Skip to content

Commit

Permalink
feat(json-crdt-extensions): 馃幐 improve Overlay.pairs() iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed May 8, 2024
1 parent 557d0b2 commit b095301
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 169 deletions.
114 changes: 79 additions & 35 deletions src/json-crdt-extensions/peritext/__tests__/setup.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,18 @@
import {s} from '../../../json-crdt-patch';
import {Model} from '../../../json-crdt/model';
import {SchemaToJsonNode} from '../../../json-crdt/schema/types';
import {ModelWithExt, ext} from '../../ModelWithExt';

/**
* Creates a Peritext instance with text "0123456789", with single-char and
* block-wise chunks, as well as with plenty of tombstones.
*/
export const setupNumbersWithTombstones = () => {
const schema = s.obj({
text: ext.peritext.new('1234'),
});
const model = ModelWithExt.create(schema);
const str = model.s.text.toExt().text();
str.ins(1, '234');
str.ins(2, '345');
str.ins(3, '456');
str.ins(4, '567');
str.ins(5, '678');
str.ins(6, '789');
str.del(7, 1);
str.del(8, 1);
str.ins(0, '0');
str.del(1, 4);
str.del(2, 1);
str.ins(1, '1');
str.del(0, 1);
str.ins(0, '0');
str.ins(2, '234');
str.del(4, 7);
str.del(4, 2);
str.del(7, 3);
str.ins(6, '6789');
str.del(7, 2);
str.ins(7, '78');
str.del(10, 2);
str.del(2, 3);
str.ins(2, '234');
if (str.view() !== '0123456789') throw new Error('Invalid text');
export type Schema = ReturnType<typeof schema>;
export type Kit = ReturnType<typeof setupKit>;

const schema = (text: string) => s.obj({
text: ext.peritext.new(text),
});

export const setupKit = (initialText: string = '', edits: (model: Model<SchemaToJsonNode<Schema>>) => void = () => {}) => {
const model = ModelWithExt.create(schema(initialText));
edits(model);
const api = model.api;
const peritextApi = model.s.text.toExt();
const peritext = peritextApi.txt;
Expand All @@ -49,3 +26,70 @@ export const setupNumbersWithTombstones = () => {
editor,
};
};

export const setupHelloWorldKit = (): Kit => {
return setupKit('', (model) => {
const str = model.s.text.toExt().text();
str.ins(0, 'hello world');
if (str.view() !== 'hello world') throw new Error('Invalid text');
});
};

export const setupHelloWorldWithFewEditsKit = (): Kit => {
return setupKit('', (model) => {
const str = model.s.text.toExt().text();
str.ins(0, 'wworld');
str.ins(0, 'helo ');
str.ins(2, 'l');
str.del(7, 1);
if (str.view() !== 'hello world') throw new Error('Invalid text');
});
};

/**
* Creates a Peritext instance with text "0123456789", no edits.
*/
export const setupNumbersKit = (): Kit => {
return setupKit('', (model) => {
const str = model.s.text.toExt().text();
str.ins(0, '0123456789');
if (str.view() !== '0123456789') throw new Error('Invalid text');
});
};

/**
* Creates a Peritext instance with text "0123456789", with single-char and
* block-wise chunks, as well as with plenty of tombstones.
*/
export const setupNumbersWithTombstonesKit = (): Kit => {
return setupKit('1234', (model) => {
const str = model.s.text.toExt().text();
str.ins(0, '234');
str.ins(1, '234');
str.ins(2, '345');
str.ins(3, '456');
str.ins(4, '567');
str.ins(5, '678');
str.ins(6, '789');
str.del(7, 1);
str.del(8, 1);
str.ins(0, '0');
str.del(1, 4);
str.del(2, 1);
str.ins(1, '1');
str.del(0, 1);
str.ins(0, '0');
str.ins(2, '234');
str.del(4, 7);
str.del(4, 2);
str.del(7, 3);
str.ins(6, '6789');
str.del(7, 2);
str.ins(7, '78');
str.del(10, 2);
str.del(2, 3);
str.ins(2, '234');
str.del(10, 3);
if (str.view() !== '0123456789') throw new Error('Invalid text');
});
};
4 changes: 4 additions & 0 deletions src/json-crdt-extensions/peritext/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import type {MarkerSlice} from '../slice/MarkerSlice';

export class Editor<T = string> {
public readonly saved: EditorSlices<T>;
public readonly extra: EditorSlices<T>;
public readonly local: EditorSlices<T>;

constructor(public readonly txt: Peritext<T>) {
this.saved = new EditorSlices(txt, txt.savedSlices);
this.extra = new EditorSlices(txt, txt.extraSlices);
this.local = new EditorSlices(txt, txt.localSlices);
}

public firstCursor(): Cursor<T> | undefined {
Expand Down
25 changes: 24 additions & 1 deletion src/json-crdt-extensions/peritext/overlay/Overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,35 @@ export class Overlay<T = string> implements Printable, Stateful {
}

public pairs0(after: undefined | OverlayPoint<T>): UndefIterator<OverlayPair<T>> {
const isEmpty = !this.root;
if (isEmpty) {
let closed = false;
return () => {
if (closed) return;
closed = true;
return [undefined, undefined];
}
}
let p1: OverlayPoint<T> | undefined;
let p2: OverlayPoint<T> | undefined;
const iterator = this.points0(after);
return () => {
const next = iterator();
const isEnd = !next;
if (isEnd) {
if (!p2 || p2.isAbsEnd()) return;
p1 = p2;
p2 = undefined;
return [p1, p2];
}
p1 = p2;
p2 = iterator();
p2 = next;
if (!p1) {
if (p2 && p2.isAbsStart()) {
p1 = p2;
p2 = iterator();
}
}
return (p1 || p2) ? [p1, p2] : undefined;
};
}
Expand Down

0 comments on commit b095301

Please sign in to comment.