Merge pull request #35731 from overleaf/mg-historyot-delete-at-end

Fix History OT base length when a tracked delete is in the document

GitOrigin-RevId: 414349aedd498c2635bb3067388dcc7291e32ee6
This commit is contained in:
Malik Glossop
2026-09-17 11:37:22 +00:00
committed by Copybot
parent e4bbf74476
commit 7e69642c01
3 changed files with 286 additions and 1 deletions
@@ -251,8 +251,11 @@ const updateSender = EditorState.transactionExtender.of(tr => {
const trackingUserId = tr.startState.field(trackChangesUserIdState)
const trackedDeletes = trackedDeletesFromState(tr.startState)
const startDoc = tr.startState.doc
// Seed the builder with the full snapshot length (visible length plus all
// tracked deletes). Mapping the CM length through toSnapshot under-counts
// when a tracked delete sits at the end of the document.
const opBuilder = new OperationBuilder(
trackedDeletes.toSnapshot(startDoc.length)
startDoc.length + trackedDeletes.totalLength
)
if (trackingUserId == null) {
@@ -13,6 +13,12 @@ export class TrackedDeletes {
toSnapshot: OffsetTable
}
/**
* Total length of tracked deletes, i.e. the difference between the snapshot
* length and the visible CodeMirror length.
*/
readonly totalLength: number
constructor(trackedChanges: TrackedChangeList) {
this.offsets = {
toCM6: [{ pos: 0, map: pos => pos }],
@@ -42,12 +48,20 @@ export class TrackedDeletes {
offset = newOffset
}
}
this.totalLength = offset
}
toCodeMirror(snapshotPos: number) {
return this.mapPos(snapshotPos, this.offsets.toCM6)
}
/**
* Map a CodeMirror position to a snapshot position, for placing an edit. At a
* tracked-delete boundary this deliberately maps to *before* the delete (so
* typing lands before the struck-through text), which means it under-counts
* for a delete at the end of the document. Use `totalLength` for the
* whole-document length, not `toSnapshot(docLength)`.
*/
toSnapshot(cm6Pos: number) {
return this.mapPos(cm6Pos, this.offsets.toSnapshot)
}