|
|
@@ -5,7 +5,6 @@ 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.
|
|
|
@@ -77,22 +76,15 @@ export const realtime = (
|
|
|
return Prec.highest([realtimePlugin, ensureRealtimePlugin])
|
|
|
}
|
|
|
|
|
|
-type OTAdapter = {
|
|
|
- handleUpdateFromCM(
|
|
|
- transactions: readonly Transaction[],
|
|
|
- ranges?: RangesTracker
|
|
|
- ): void
|
|
|
- attachShareJs(): void
|
|
|
-}
|
|
|
-
|
|
|
export class EditorFacade extends EventEmitter {
|
|
|
- private otAdapter: OTAdapter | null
|
|
|
+ public shareDoc: ShareDoc | null
|
|
|
public events: EventEmitter
|
|
|
+ private maxDocLength?: number
|
|
|
|
|
|
constructor(public view: EditorView) {
|
|
|
super()
|
|
|
this.view = view
|
|
|
- this.otAdapter = null
|
|
|
+ this.shareDoc = null
|
|
|
this.events = new EventEmitter()
|
|
|
}
|
|
|
|
|
|
@@ -126,56 +118,23 @@ 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() {
|
|
|
- const shareDoc = this.shareDoc
|
|
|
+ attachShareJs(shareDoc: ShareDoc, maxDocLength?: number) {
|
|
|
+ this.shareDoc = shareDoc
|
|
|
+ this.maxDocLength = maxDocLength
|
|
|
+
|
|
|
const check = () => {
|
|
|
// run in a timeout so it checks the editor content once this update has been applied
|
|
|
window.setTimeout(() => {
|
|
|
- const editorText = this.editor.getValue()
|
|
|
+ const editorText = this.getValue()
|
|
|
const otText = shareDoc.getText()
|
|
|
|
|
|
if (editorText !== otText) {
|
|
|
- this.shareDoc.emit('error', 'Text does not match in CodeMirror 6')
|
|
|
+ shareDoc.emit('error', 'Text does not match in CodeMirror 6')
|
|
|
debugConsole.error('Text does not match!')
|
|
|
debugConsole.error('editor: ' + editorText)
|
|
|
debugConsole.error('ot: ' + otText)
|
|
|
@@ -184,12 +143,12 @@ class ShareLatexOTAdapter {
|
|
|
}
|
|
|
|
|
|
const onInsert = (pos: number, text: string) => {
|
|
|
- this.editor.cmInsert(pos, text, 'remote')
|
|
|
+ this.cmInsert(pos, text, 'remote')
|
|
|
check()
|
|
|
}
|
|
|
|
|
|
const onDelete = (pos: number, text: string) => {
|
|
|
- this.editor.cmDelete(pos, text, 'remote')
|
|
|
+ this.cmDelete(pos, text, 'remote')
|
|
|
check()
|
|
|
}
|
|
|
|
|
|
@@ -202,7 +161,7 @@ class ShareLatexOTAdapter {
|
|
|
shareDoc.removeListener('insert', onInsert)
|
|
|
shareDoc.removeListener('delete', onDelete)
|
|
|
delete shareDoc.detach_cm6
|
|
|
- this.editor.detachShareJs()
|
|
|
+ this.shareDoc = null
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -216,6 +175,10 @@ class ShareLatexOTAdapter {
|
|
|
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)
|
|
|
@@ -271,7 +234,7 @@ class ShareLatexOTAdapter {
|
|
|
removed,
|
|
|
}
|
|
|
|
|
|
- this.editor.emit('change', this, changeDescription)
|
|
|
+ this.emit('change', this, changeDescription)
|
|
|
}
|
|
|
)
|
|
|
}
|
|
|
@@ -279,8 +242,6 @@ class ShareLatexOTAdapter {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-class HistoryOTAdapter extends ShareLatexOTAdapter {}
|
|
|
-
|
|
|
export const trackChangesAnnotation = Annotation.define()
|
|
|
|
|
|
const chooseOrigin = (transaction: Transaction) => {
|