mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2025-07-27 17:00:06 +02:00
100 lines
2.8 KiB
JavaScript
100 lines
2.8 KiB
JavaScript
const OError = require('@overleaf/o-error')
|
|
const DMP = require('diff-match-patch')
|
|
const { TextOperation } = require('overleaf-editor-core')
|
|
const dmp = new DMP()
|
|
|
|
// Do not attempt to produce a diff for more than 100ms
|
|
dmp.Diff_Timeout = 0.1
|
|
|
|
module.exports = {
|
|
ADDED: 1,
|
|
REMOVED: -1,
|
|
UNCHANGED: 0,
|
|
|
|
diffAsShareJsOp(before, after) {
|
|
const diffs = dmp.diff_main(before.join('\n'), after.join('\n'))
|
|
dmp.diff_cleanupSemantic(diffs)
|
|
|
|
const ops = []
|
|
let position = 0
|
|
for (const diff of diffs) {
|
|
const [type, content] = diff
|
|
if (type === this.ADDED) {
|
|
ops.push({
|
|
i: content,
|
|
p: position,
|
|
})
|
|
position += content.length
|
|
} else if (type === this.REMOVED) {
|
|
ops.push({
|
|
d: content,
|
|
p: position,
|
|
})
|
|
} else if (type === this.UNCHANGED) {
|
|
position += content.length
|
|
} else {
|
|
throw new Error('Unknown type')
|
|
}
|
|
}
|
|
return ops
|
|
},
|
|
|
|
/**
|
|
* @param {import("overleaf-editor-core").StringFileData} file
|
|
* @param {string} after
|
|
* @return {TextOperation}
|
|
*/
|
|
diffAsHistoryOTEditOperation(file, after) {
|
|
const beforeWithoutTrackedDeletes = file.getContent({
|
|
filterTrackedDeletes: true,
|
|
})
|
|
const diffs = dmp.diff_main(beforeWithoutTrackedDeletes, after)
|
|
dmp.diff_cleanupSemantic(diffs)
|
|
|
|
const trackedChanges = file.trackedChanges.asSorted()
|
|
let nextTc = trackedChanges.shift()
|
|
|
|
const op = new TextOperation()
|
|
for (const diff of diffs) {
|
|
let [type, content] = diff
|
|
if (type === this.ADDED) {
|
|
op.insert(content)
|
|
} else if (type === this.REMOVED || type === this.UNCHANGED) {
|
|
while (op.baseLength + content.length > nextTc?.range.start) {
|
|
if (nextTc.tracking.type === 'delete') {
|
|
const untilRange = nextTc.range.start - op.baseLength
|
|
if (type === this.REMOVED) {
|
|
op.remove(untilRange)
|
|
} else if (type === this.UNCHANGED) {
|
|
op.retain(untilRange)
|
|
}
|
|
op.retain(nextTc.range.end - nextTc.range.start)
|
|
content = content.slice(untilRange)
|
|
}
|
|
nextTc = trackedChanges.shift()
|
|
}
|
|
if (type === this.REMOVED) {
|
|
op.remove(content.length)
|
|
} else if (type === this.UNCHANGED) {
|
|
op.retain(content.length)
|
|
}
|
|
} else {
|
|
throw new Error('Unknown type')
|
|
}
|
|
}
|
|
while (nextTc) {
|
|
if (
|
|
nextTc.tracking.type !== 'delete' ||
|
|
nextTc.range.start !== op.baseLength
|
|
) {
|
|
throw new OError(
|
|
'StringFileData.trackedChanges out of sync: unexpected range after end of diff',
|
|
{ nextTc, baseLength: op.baseLength }
|
|
)
|
|
}
|
|
op.retain(nextTc.range.end - nextTc.range.start)
|
|
nextTc = trackedChanges.shift()
|
|
}
|
|
return op
|
|
},
|
|
}
|