| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- 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
- },
- }
|