loro-codemirror
Version:
A CodeMirror plugin for loro
118 lines (117 loc) • 4.38 kB
JavaScript
import { Annotation } from "@codemirror/state";
import { EditorView, ViewUpdate } from "@codemirror/view";
import { LoroDoc, LoroText, } from "loro-crdt";
export const loroSyncAnnotation = Annotation.define();
export class LoroSyncPluginValue {
constructor(view, doc, getTextFromDoc) {
this.view = view;
this.doc = doc;
this.getTextFromDoc = getTextFromDoc;
this.isInitDispatch = false;
this.onRemoteUpdate = (e) => {
if (e.by === "local") {
return;
}
if (e.by === "checkout") {
// TODO: better handle checkout
this.view.dispatch({
changes: [
{
from: 0,
to: this.view.state.doc.length,
insert: this.getTextFromDoc(this.doc).toString(),
},
],
annotations: [loroSyncAnnotation.of(this)],
});
return;
}
if (e.by === "import") {
let changes = [];
let pos = 0;
for (let { diff, target } of e.events) {
const text = this.getTextFromDoc(this.doc);
// Skip if the event is not a text event
if (diff.type !== "text")
return;
// Skip if the event is not for the current document
if (target !== text.id)
return;
const textDiff = diff.diff;
for (const delta of textDiff) {
if (delta.insert) {
changes.push({
from: pos,
to: pos,
insert: delta.insert,
});
}
else if (delta.delete) {
changes.push({
from: pos,
to: pos + delta.delete,
});
pos += delta.delete;
}
else if (delta.retain != null) {
pos += delta.retain;
}
}
this.view.dispatch({
changes,
annotations: [loroSyncAnnotation.of(this)],
});
}
}
};
this.sub = doc.subscribe(this.onRemoteUpdate);
Promise.resolve().then(() => {
this.isInitDispatch = true;
const currentText = this.view.state.doc.toString();
const text = this.getTextFromDoc(this.doc);
if (currentText === text.toString()) {
return;
}
view.dispatch({
changes: [
{
from: 0,
to: this.view.state.doc.length,
insert: text.toString(),
},
],
});
});
}
update(update) {
if (this.isInitDispatch) {
this.isInitDispatch = false;
return;
}
if (!update.docChanged ||
(update.transactions.length > 0 &&
(update.transactions[0].annotation(loroSyncAnnotation) ===
this ||
update.transactions[0].annotation(loroSyncAnnotation) ===
"undo"))) {
return;
}
let adj = 0;
update.changes.iterChanges((fromA, toA, fromB, toB, insert) => {
const insertText = insert.sliceString(0, insert.length, "\n");
if (fromA !== toA) {
this.getTextFromDoc(this.doc).delete(fromA + adj, toA - fromA);
}
if (insertText.length > 0) {
this.getTextFromDoc(this.doc).insert(fromA + adj, insertText);
}
adj += insertText.length - (toA - fromA);
});
this.doc.commit();
}
destroy() {
var _a;
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.call(this);
this.sub = undefined;
}
}