This commit is contained in:
呈祥
2026-01-06 17:31:56 +08:00
commit 210d948353
21 changed files with 4215 additions and 0 deletions
+155
View File
@@ -0,0 +1,155 @@
// 简单文本操作的实现
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();