156 lines
4.8 KiB
TypeScript
156 lines
4.8 KiB
TypeScript
// 简单文本操作的实现
|
|
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();
|
|
|