// 简单文本操作的实现 import { v4 as uuidv4 } from 'uuid'; export abstract class SimpleTextOperation { id: string = uuidv4(); abstract toString(): string; abstract equals(other: SimpleTextOperation): boolean; abstract apply(doc: string): string; static transform(a: SimpleTextOperation, b: SimpleTextOperation): [SimpleTextOperation, SimpleTextOperation] { if (a instanceof Noop || b instanceof Noop) { return [a, b]; } if (a instanceof Insert && b instanceof Insert) { if (a.position < b.position || (a.position === b.position && a.str < b.str)) { return [a, new Insert(b.str, b.position + a.str.length)]; } if (a.position > b.position || (a.position === b.position && a.str > b.str)) { return [new Insert(a.str, a.position + b.str.length), b]; } return [noop, noop]; } if (a instanceof Insert && b instanceof Delete) { if (a.position <= b.position) { return [a, new Delete(b.count, b.position + a.str.length)]; } if (a.position >= b.position + b.count) { return [new Insert(a.str, a.position - b.count), b]; } // 这里,我们必须删除操作 a 插入的字符串。 // 这不能保留操作 a 的意图,但这是获得有效转换函数的唯一方法。 return [noop, new Delete(b.count + a.str.length, b.position)]; } if (a instanceof Delete && b instanceof Insert) { if (a.position >= b.position) { return [new Delete(a.count, a.position + b.str.length), b]; } if (a.position + a.count <= b.position) { return [a, new Insert(b.str, b.position - a.count)]; } // 与上面相同的问题。我们必须删除操作 b 中插入的字符串。 return [new Delete(a.count + b.str.length, a.position), noop]; } if (a instanceof Delete && b instanceof Delete) { if (a.position === b.position) { if (a.count === b.count) { return [noop, noop]; } else if (a.count < b.count) { return [noop, new Delete(b.count - a.count, b.position)]; } return [new Delete(a.count - b.count, a.position), noop]; } if (a.position < b.position) { if (a.position + a.count <= b.position) { return [a, new Delete(b.count, b.position - a.count)]; } if (a.position + a.count >= b.position + b.count) { return [new Delete(a.count - b.count, a.position), noop]; } return [ new Delete(b.position - a.position, a.position), new Delete(b.position + b.count - (a.position + a.count), a.position) ]; } if (a.position > b.position) { if (a.position >= b.position + b.count) { return [new Delete(a.count, a.position - b.count), b]; } if (a.position + a.count <= b.position + b.count) { return [noop, new Delete(b.count - a.count, b.position)]; } return [ new Delete(a.position + a.count - (b.position + b.count), b.position), new Delete(a.position - b.position, b.position) ]; } } throw new Error('Unsupported operation types for transformation'); } } // 在文档的从零开始的位置 `position` 插入字符串 `str` export class Insert extends SimpleTextOperation { public str: string; public position: number; constructor(str: string, position: number) { super(); this.str = str; this.position = position; } toString(): string { return 'Insert(' + JSON.stringify(this.str) + ', ' + this.position + ')'; } equals(other: SimpleTextOperation): boolean { return other instanceof Insert && this.str === other.str && this.position === other.position; } apply(doc: string): string { return doc.slice(0, this.position) + this.str + doc.slice(this.position); } } // 在文档的从零开始的位置 `position` 删除 `count` 个字符 export class Delete extends SimpleTextOperation { public count: number; public position: number; constructor(count: number, position: number) { super(); this.count = count; this.position = position; } toString(): string { return 'Delete(' + this.count + ', ' + this.position + ')'; } equals(other: SimpleTextOperation): boolean { return other instanceof Delete && this.count === other.count && this.position === other.position; } apply(doc: string): string { return doc.slice(0, this.position) + doc.slice(this.position + this.count); } } // 不执行任何操作的操作。这对于转换两个删除相同字符的结果是必需的 export class Noop extends SimpleTextOperation { toString(): string { return 'Noop()'; } equals(other: SimpleTextOperation): boolean { return other instanceof Noop; } apply(doc: string): string { return doc; } } const noop = new Noop();