drop zero-length retain operations in project-history (#34427)

GitOrigin-RevId: ec3069a3ea3b7d487548ba181a2dde9772e308e5
This commit is contained in:
Brian Gough
2026-06-11 08:05:33 +00:00
committed by Copybot
parent da85973cca
commit 57d76da263
2 changed files with 57 additions and 0 deletions
@@ -449,6 +449,10 @@ class OperationsBuilder {
* @param {TrackingDirective} [opts.tracking]
*/
retain(length, opts = {}) {
// A zero-length retain is a no-op, they are dropped in overleaf-editor-core.
if (length === 0) {
return
}
if (opts.tracking) {
this.textOperation.push({ r: length, ...opts })
} else {
@@ -672,6 +672,59 @@ describe('UpdateTranslator', function () {
])
})
it('drops zero-length retains carrying tracking data', function () {
// A zero-length retain (e.g. an empty `r` string produced by the resync
// mechanism) must not be emitted as a zero-length tracked delete.
const updates = [
{
update: {
doc: this.doc_id,
op: [
{
p: 3,
r: '',
tracking: { type: 'delete', userId: this.user_id, ts: 0 },
},
],
v: this.version,
meta: {
user_id: this.user_id,
ts: new Date(this.timestamp).getTime(),
pathname: '/main.tex',
doc_length: 20,
},
},
},
]
const changes = this.UpdateTranslator.convertToChanges(
this.project_id,
updates
).map(change => change.toRaw())
// The empty tracked retain is dropped, leaving a plain retain of the
// whole document.
expect(changes).to.deep.equal([
{
authors: [],
operations: [
{
pathname: 'main.tex',
textOperation: [20],
},
],
v2Authors: [this.user_id],
timestamp: this.timestamp,
v2DocVersions: {
'59bfd450e3028c4d40a1e9ab': {
pathname: 'main.tex',
v: 0,
},
},
},
])
})
it('can translate insertions at the start and end (with zero retained)', function () {
const updates = [
{