Implement text diffing for proper CRDT operations
This commit is contained in:
parent
925c7a3c0d
commit
4d6ecf78cd
1 changed files with 33 additions and 5 deletions
|
|
@ -61,19 +61,47 @@ function connect(roomName: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compute minimal edit operations using LCS-based diff
|
||||||
|
function computeDiff(oldText: string, newText: string) {
|
||||||
|
// find common prefix
|
||||||
|
let prefixLen = 0;
|
||||||
|
while (prefixLen < oldText.length && prefixLen < newText.length &&
|
||||||
|
oldText[prefixLen] === newText[prefixLen]) {
|
||||||
|
prefixLen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find common suffix
|
||||||
|
let suffixLen = 0;
|
||||||
|
while (suffixLen < oldText.length - prefixLen &&
|
||||||
|
suffixLen < newText.length - prefixLen &&
|
||||||
|
oldText[oldText.length - 1 - suffixLen] === newText[newText.length - 1 - suffixLen]) {
|
||||||
|
suffixLen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteStart = prefixLen;
|
||||||
|
const deleteLen = oldText.length - prefixLen - suffixLen;
|
||||||
|
const insertText = newText.slice(prefixLen, newText.length - suffixLen);
|
||||||
|
|
||||||
|
return { deleteStart, deleteLen, insertText };
|
||||||
|
}
|
||||||
|
|
||||||
function setContent(newContent: string) {
|
function setContent(newContent: string) {
|
||||||
if (!doc || !text || !ws) return;
|
if (!doc || !text || !ws) return;
|
||||||
|
|
||||||
const oldContent = text.toString();
|
const oldContent = text.toString();
|
||||||
if (oldContent === newContent) return;
|
if (oldContent === newContent) return;
|
||||||
|
|
||||||
// compute diff and apply
|
// compute minimal diff and apply
|
||||||
// simple approach: delete all, insert all
|
const { deleteStart, deleteLen, insertText } = computeDiff(oldContent, newContent);
|
||||||
// TODO: proper diff for efficiency
|
|
||||||
const t = text;
|
const t = text;
|
||||||
doc.transact(() => {
|
doc.transact(() => {
|
||||||
t.delete(0, t.length);
|
if (deleteLen > 0) {
|
||||||
t.insert(0, newContent);
|
t.delete(deleteStart, deleteLen);
|
||||||
|
}
|
||||||
|
if (insertText.length > 0) {
|
||||||
|
t.insert(deleteStart, insertText);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// send update to daemon
|
// send update to daemon
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue