Skip to content

Instantly share code, notes, and snippets.

@Rishabh-malhotraa
Last active September 9, 2021 10:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rishabh-malhotraa/b351aa74178274fa0321fa8821a2c2dc to your computer and use it in GitHub Desktop.
Save Rishabh-malhotraa/b351aa74178274fa0321fa8821a2c2dc to your computer and use it in GitHub Desktop.
interface OperationType {
op: string;
count?: number;
chars?: string;
}
class OperationTransformation {
private cursorPosition = 0;
/*update the cursor position*/
public skip(text: string, count: number) {
this.cursorPosition = Math.min(count + this.cursorPosition, text.length); // truncates count + cusor_position if it goes beyond text.size()
}
public insert(text: string, insertText: string) {
text =
text.substr(0, this.cursorPosition) +
insertText +
text.substr(this.cursorPosition);
this.skip(text, insertText.length);
return text;
}
public delete(text: string, count: number) {
return (
text.substr(0, this.cursorPosition) +
text.substr(this.cursorPosition + count) // substr return "" if start position > length of string
);
}
public getCursorPosition() {
return this.cursorPosition;
}
}
function isValid(stale: string, latest: string, otjson: string): boolean {
const ot = new OperationTransformation();
const operations: OperationType[] = JSON.parse(otjson);
for (const { op, chars, count } of operations) {
if (op === 'skip') {
if (ot.getCursorPosition() + count >= stale.length) return false; // if we skip past end
ot.skip(stale, count);
} else if (op === 'insert') {
stale = ot.insert(stale, chars);
} else if (op === 'delete') {
if (ot.getCursorPosition() + count >= stale.length) return false; // if we delete past end
stale = ot.delete(stale, count);
}
}
console.log(stale === latest ? 'true' : 'false');
return stale === latest;
}
/*
===================================================================================================
*/
isValid(
'This Editor uses operational transformations to keep everyone in a multiplayer repl in sync.',
'This Editor uses operational transformations.',
'[{"op": "skip", "count": 40}, {"op": "delete", "count": 47}]'
); // true
isValid(
'This Editor uses operational transformations to keep everyone in a multiplayer repl in sync.',
'This Editor uses operational transformations.',
'[{"op": "skip", "count": 45}, {"op": "delete", "count": 47}]'
); // false, delete past end
isValid(
'This Editor uses operational transformations to keep everyone in a multiplayer repl in sync.',
'This Editor uses operational transformations.',
'[{"op": "skip", "count": 40}, {"op": "delete", "count": 47}, {"op": "skip", "count": 2}]'
); // false, skip past end
isValid(
'This Editor uses operational transformations to keep everyone in a multiplayer repl in sync.',
'We use operational transformations to keep everyone in a multiplayer repl in sync.',
'[{"op": "delete", "count": 7}, {"op": "insert", "chars": "We"}, {"op": "skip", "count": 4}, {"op": "delete", "count": 1}]'
); // true
isValid(
'This Editor uses operational transformations to keep everyone in a multiplayer repl in sync.',
'This Editor uses operational transformations to keep everyone in a multiplayer repl in sync.',
'[]'
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment