Merge pull request #25952 from overleaf/em-split-editor-facade

Split EditorFacade functionality for history OT (2nd attempt)

GitOrigin-RevId: 2bc6d6c54a9f336fd4a69f0eb548dd06b9f06f5f
This commit is contained in:
Eric Mc Sween
2025-05-30 07:34:24 -04:00
committed by Copybot
parent d701d0e887
commit 8a1e5a3555
2 changed files with 57 additions and 18 deletions

View File

@@ -365,7 +365,7 @@ export class ShareJsDoc extends EventEmitter {
attachToCM6(cm6: EditorFacade) {
this.attachToEditor(cm6, () => {
cm6.attachShareJs(this._doc, getMeta('ol-maxDocLength'))
cm6.attachShareJs(this._doc, getMeta('ol-maxDocLength'), this.type)
})
}

View File

@@ -5,6 +5,7 @@ import RangesTracker from '@overleaf/ranges-tracker'
import { ShareDoc } from '../../../../../types/share-doc'
import { debugConsole } from '@/utils/debugging'
import { DocumentContainer } from '@/features/ide-react/editor/document-container'
import { OTType } from '@/features/ide-react/editor/share-js-doc'
/*
* Integrate CodeMirror 6 with the real-time system, via ShareJS.
@@ -76,15 +77,22 @@ export const realtime = (
return Prec.highest([realtimePlugin, ensureRealtimePlugin])
}
type OTAdapter = {
handleUpdateFromCM(
transactions: readonly Transaction[],
ranges?: RangesTracker
): void
attachShareJs(): void
}
export class EditorFacade extends EventEmitter {
public shareDoc: ShareDoc | null
private otAdapter: OTAdapter | null
public events: EventEmitter
private maxDocLength?: number
constructor(public view: EditorView) {
super()
this.view = view
this.shareDoc = null
this.otAdapter = null
this.events = new EventEmitter()
}
@@ -118,23 +126,56 @@ export class EditorFacade extends EventEmitter {
this.cmChange({ from: position, to: position + text.length }, origin)
}
attachShareJs(shareDoc: ShareDoc, maxDocLength?: number, type?: OTType) {
this.otAdapter =
type === 'history-ot'
? new HistoryOTAdapter(this, shareDoc, maxDocLength)
: new ShareLatexOTAdapter(this, shareDoc, maxDocLength)
this.otAdapter.attachShareJs()
}
detachShareJs() {
this.otAdapter = null
}
handleUpdateFromCM(
transactions: readonly Transaction[],
ranges?: RangesTracker
) {
if (this.otAdapter == null) {
throw new Error('Trying to process updates with no otAdapter')
}
this.otAdapter.handleUpdateFromCM(transactions, ranges)
}
}
class ShareLatexOTAdapter {
constructor(
public editor: EditorFacade,
private shareDoc: ShareDoc,
private maxDocLength?: number
) {
this.editor = editor
this.shareDoc = shareDoc
this.maxDocLength = maxDocLength
}
// Connect to ShareJS, passing changes to the CodeMirror view
// as new transactions.
// This is a broad immitation of helper functions supplied in
// the sharejs library. (See vendor/libs/sharejs, in particular
// the 'attach_ace' helper)
attachShareJs(shareDoc: ShareDoc, maxDocLength?: number) {
this.shareDoc = shareDoc
this.maxDocLength = maxDocLength
attachShareJs() {
const shareDoc = this.shareDoc
const check = () => {
// run in a timeout so it checks the editor content once this update has been applied
window.setTimeout(() => {
const editorText = this.getValue()
const editorText = this.editor.getValue()
const otText = shareDoc.getText()
if (editorText !== otText) {
shareDoc.emit('error', 'Text does not match in CodeMirror 6')
this.shareDoc.emit('error', 'Text does not match in CodeMirror 6')
debugConsole.error('Text does not match!')
debugConsole.error('editor: ' + editorText)
debugConsole.error('ot: ' + otText)
@@ -143,12 +184,12 @@ export class EditorFacade extends EventEmitter {
}
const onInsert = (pos: number, text: string) => {
this.cmInsert(pos, text, 'remote')
this.editor.cmInsert(pos, text, 'remote')
check()
}
const onDelete = (pos: number, text: string) => {
this.cmDelete(pos, text, 'remote')
this.editor.cmDelete(pos, text, 'remote')
check()
}
@@ -161,7 +202,7 @@ export class EditorFacade extends EventEmitter {
shareDoc.removeListener('insert', onInsert)
shareDoc.removeListener('delete', onDelete)
delete shareDoc.detach_cm6
this.shareDoc = null
this.editor.detachShareJs()
}
}
@@ -175,10 +216,6 @@ export class EditorFacade extends EventEmitter {
const trackedDeletesLength =
ranges != null ? ranges.getTrackedDeletesLength() : 0
if (!shareDoc) {
throw new Error('Trying to process updates with no shareDoc')
}
for (const transaction of transactions) {
if (transaction.docChanged) {
const origin = chooseOrigin(transaction)
@@ -234,7 +271,7 @@ export class EditorFacade extends EventEmitter {
removed,
}
this.emit('change', this, changeDescription)
this.editor.emit('change', this.editor, changeDescription)
}
)
}
@@ -242,6 +279,8 @@ export class EditorFacade extends EventEmitter {
}
}
class HistoryOTAdapter extends ShareLatexOTAdapter {}
export const trackChangesAnnotation = Annotation.define()
const chooseOrigin = (transaction: Transaction) => {