summaryrefslogtreecommitdiff
path: root/src/renderer/src/extensions/otSyncExtension.ts
blob: 7ff22033dab204fd05f63433701bd2e12253c3fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// CM6 extension for OT sync: ViewPlugin + annotation to prevent echo loops
import { Annotation } from '@codemirror/state'
import { ViewPlugin, type ViewUpdate } from '@codemirror/view'
import type { OverleafDocSync } from '../ot/overleafSync'

/** Annotation used to mark transactions that come from remote OT updates */
export const remoteUpdateAnnotation = Annotation.define<boolean>()

/**
 * Creates a CM6 extension that intercepts local doc changes
 * and feeds them to the OT orchestrator. Skips changes tagged
 * with remoteUpdateAnnotation to prevent echo loops.
 */
export function otSyncExtension(sync: OverleafDocSync) {
  return ViewPlugin.fromClass(
    class {
      constructor() {
        // nothing to initialize
      }

      update(update: ViewUpdate) {
        if (!update.docChanged) return

        // Skip if this change was from a remote OT update
        for (const tr of update.transactions) {
          if (tr.annotation(remoteUpdateAnnotation)) return
        }

        // Feed local changes to OT orchestrator
        // We need the old doc (before changes) — it's the startState.doc
        sync.onLocalChange(update.changes, update.startState.doc)
      }

      destroy() {
        // nothing to clean up
      }
    }
  )
}