Merge pull request #25825 from overleaf/em-accept-edit-operations

Accept all EditOperations in history-ot type

GitOrigin-RevId: b3bc710c92c4aa31dfeec16d422e42f0a6bb8cdb
This commit is contained in:
Eric Mc Sween
2025-05-22 07:56:33 -04:00
committed by Copybot
parent 6785c84ea3
commit 43586bcaee
@@ -1,6 +1,7 @@
import EventEmitter from '@/utils/EventEmitter'
import {
EditOperationBuilder,
EditOperationTransformer,
InsertOp,
RemoveOp,
RetainOp,
@@ -9,14 +10,6 @@ import {
} from 'overleaf-editor-core'
import { RawEditOperation } from 'overleaf-editor-core/lib/types'
function loadTextOperation(raw: RawEditOperation): TextOperation {
const operation = EditOperationBuilder.fromJSON(raw)
if (!(operation instanceof TextOperation)) {
throw new Error(`operation not supported: ${operation.constructor.name}`)
}
return operation
}
export class HistoryOTType extends EventEmitter {
// stub interface, these are actually on the Doc
api: HistoryOTType
@@ -29,15 +22,15 @@ export class HistoryOTType extends EventEmitter {
}
transformX(raw1: RawEditOperation[], raw2: RawEditOperation[]) {
const [a, b] = TextOperation.transform(
loadTextOperation(raw1[0]),
loadTextOperation(raw2[0])
const [a, b] = EditOperationTransformer.transform(
EditOperationBuilder.fromJSON(raw1[0]),
EditOperationBuilder.fromJSON(raw2[0])
)
return [[a.toJSON()], [b.toJSON()]]
}
apply(snapshot: StringFileData, rawEditOperation: RawEditOperation[]) {
const operation = loadTextOperation(rawEditOperation[0])
const operation = EditOperationBuilder.fromJSON(rawEditOperation[0])
const afterFile = StringFileData.fromRaw(snapshot.toRaw())
afterFile.edit(operation)
this.snapshot = afterFile
@@ -46,7 +39,9 @@ export class HistoryOTType extends EventEmitter {
compose(op1: RawEditOperation[], op2: RawEditOperation[]) {
return [
loadTextOperation(op1[0]).compose(loadTextOperation(op2[0])).toJSON(),
EditOperationBuilder.fromJSON(op1[0])
.compose(EditOperationBuilder.fromJSON(op2[0]))
.toJSON(),
]
}
@@ -88,40 +83,47 @@ export class HistoryOTType extends EventEmitter {
this.on(
'remoteop',
(rawEditOperation: RawEditOperation[], oldSnapshot: StringFileData) => {
const operation = loadTextOperation(rawEditOperation[0])
const str = oldSnapshot.getContent()
if (str.length !== operation.baseLength)
throw new TextOperation.ApplyError(
"The operation's base length must be equal to the string's length.",
operation,
str
)
let outputCursor = 0
let inputCursor = 0
for (const op of operation.ops) {
if (op instanceof RetainOp) {
inputCursor += op.length
outputCursor += op.length
} else if (op instanceof InsertOp) {
this.emit('insert', outputCursor, op.insertion, op.insertion.length)
outputCursor += op.insertion.length
} else if (op instanceof RemoveOp) {
this.emit(
'delete',
outputCursor,
str.slice(inputCursor, inputCursor + op.length)
const operation = EditOperationBuilder.fromJSON(rawEditOperation[0])
if (operation instanceof TextOperation) {
const str = oldSnapshot.getContent()
if (str.length !== operation.baseLength)
throw new TextOperation.ApplyError(
"The operation's base length must be equal to the string's length.",
operation,
str
)
inputCursor += op.length
}
}
if (inputCursor !== str.length)
throw new TextOperation.ApplyError(
"The operation didn't operate on the whole string.",
operation,
str
)
let outputCursor = 0
let inputCursor = 0
for (const op of operation.ops) {
if (op instanceof RetainOp) {
inputCursor += op.length
outputCursor += op.length
} else if (op instanceof InsertOp) {
this.emit(
'insert',
outputCursor,
op.insertion,
op.insertion.length
)
outputCursor += op.insertion.length
} else if (op instanceof RemoveOp) {
this.emit(
'delete',
outputCursor,
str.slice(inputCursor, inputCursor + op.length)
)
inputCursor += op.length
}
}
if (inputCursor !== str.length)
throw new TextOperation.ApplyError(
"The operation didn't operate on the whole string.",
operation,
str
)
}
}
)
}