Skip to content

Commit b095301

Browse files
committed
feat(json-crdt-extensions): 🎸 improve Overlay.pairs() iterator
1 parent 557d0b2 commit b095301

File tree

4 files changed

+240
-169
lines changed

4 files changed

+240
-169
lines changed
Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,18 @@
11
import {s} from '../../../json-crdt-patch';
2+
import {Model} from '../../../json-crdt/model';
3+
import {SchemaToJsonNode} from '../../../json-crdt/schema/types';
24
import {ModelWithExt, ext} from '../../ModelWithExt';
35

4-
/**
5-
* Creates a Peritext instance with text "0123456789", with single-char and
6-
* block-wise chunks, as well as with plenty of tombstones.
7-
*/
8-
export const setupNumbersWithTombstones = () => {
9-
const schema = s.obj({
10-
text: ext.peritext.new('1234'),
11-
});
12-
const model = ModelWithExt.create(schema);
13-
const str = model.s.text.toExt().text();
14-
str.ins(1, '234');
15-
str.ins(2, '345');
16-
str.ins(3, '456');
17-
str.ins(4, '567');
18-
str.ins(5, '678');
19-
str.ins(6, '789');
20-
str.del(7, 1);
21-
str.del(8, 1);
22-
str.ins(0, '0');
23-
str.del(1, 4);
24-
str.del(2, 1);
25-
str.ins(1, '1');
26-
str.del(0, 1);
27-
str.ins(0, '0');
28-
str.ins(2, '234');
29-
str.del(4, 7);
30-
str.del(4, 2);
31-
str.del(7, 3);
32-
str.ins(6, '6789');
33-
str.del(7, 2);
34-
str.ins(7, '78');
35-
str.del(10, 2);
36-
str.del(2, 3);
37-
str.ins(2, '234');
38-
if (str.view() !== '0123456789') throw new Error('Invalid text');
6+
export type Schema = ReturnType<typeof schema>;
7+
export type Kit = ReturnType<typeof setupKit>;
8+
9+
const schema = (text: string) => s.obj({
10+
text: ext.peritext.new(text),
11+
});
12+
13+
export const setupKit = (initialText: string = '', edits: (model: Model<SchemaToJsonNode<Schema>>) => void = () => {}) => {
14+
const model = ModelWithExt.create(schema(initialText));
15+
edits(model);
3916
const api = model.api;
4017
const peritextApi = model.s.text.toExt();
4118
const peritext = peritextApi.txt;
@@ -49,3 +26,70 @@ export const setupNumbersWithTombstones = () => {
4926
editor,
5027
};
5128
};
29+
30+
export const setupHelloWorldKit = (): Kit => {
31+
return setupKit('', (model) => {
32+
const str = model.s.text.toExt().text();
33+
str.ins(0, 'hello world');
34+
if (str.view() !== 'hello world') throw new Error('Invalid text');
35+
});
36+
};
37+
38+
export const setupHelloWorldWithFewEditsKit = (): Kit => {
39+
return setupKit('', (model) => {
40+
const str = model.s.text.toExt().text();
41+
str.ins(0, 'wworld');
42+
str.ins(0, 'helo ');
43+
str.ins(2, 'l');
44+
str.del(7, 1);
45+
if (str.view() !== 'hello world') throw new Error('Invalid text');
46+
});
47+
};
48+
49+
/**
50+
* Creates a Peritext instance with text "0123456789", no edits.
51+
*/
52+
export const setupNumbersKit = (): Kit => {
53+
return setupKit('', (model) => {
54+
const str = model.s.text.toExt().text();
55+
str.ins(0, '0123456789');
56+
if (str.view() !== '0123456789') throw new Error('Invalid text');
57+
});
58+
};
59+
60+
/**
61+
* Creates a Peritext instance with text "0123456789", with single-char and
62+
* block-wise chunks, as well as with plenty of tombstones.
63+
*/
64+
export const setupNumbersWithTombstonesKit = (): Kit => {
65+
return setupKit('1234', (model) => {
66+
const str = model.s.text.toExt().text();
67+
str.ins(0, '234');
68+
str.ins(1, '234');
69+
str.ins(2, '345');
70+
str.ins(3, '456');
71+
str.ins(4, '567');
72+
str.ins(5, '678');
73+
str.ins(6, '789');
74+
str.del(7, 1);
75+
str.del(8, 1);
76+
str.ins(0, '0');
77+
str.del(1, 4);
78+
str.del(2, 1);
79+
str.ins(1, '1');
80+
str.del(0, 1);
81+
str.ins(0, '0');
82+
str.ins(2, '234');
83+
str.del(4, 7);
84+
str.del(4, 2);
85+
str.del(7, 3);
86+
str.ins(6, '6789');
87+
str.del(7, 2);
88+
str.ins(7, '78');
89+
str.del(10, 2);
90+
str.del(2, 3);
91+
str.ins(2, '234');
92+
str.del(10, 3);
93+
if (str.view() !== '0123456789') throw new Error('Invalid text');
94+
});
95+
};

src/json-crdt-extensions/peritext/editor/Editor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import type {MarkerSlice} from '../slice/MarkerSlice';
1010

1111
export class Editor<T = string> {
1212
public readonly saved: EditorSlices<T>;
13+
public readonly extra: EditorSlices<T>;
14+
public readonly local: EditorSlices<T>;
1315

1416
constructor(public readonly txt: Peritext<T>) {
1517
this.saved = new EditorSlices(txt, txt.savedSlices);
18+
this.extra = new EditorSlices(txt, txt.extraSlices);
19+
this.local = new EditorSlices(txt, txt.localSlices);
1620
}
1721

1822
public firstCursor(): Cursor<T> | undefined {

src/json-crdt-extensions/peritext/overlay/Overlay.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,35 @@ export class Overlay<T = string> implements Printable, Stateful {
196196
}
197197

198198
public pairs0(after: undefined | OverlayPoint<T>): UndefIterator<OverlayPair<T>> {
199+
const isEmpty = !this.root;
200+
if (isEmpty) {
201+
let closed = false;
202+
return () => {
203+
if (closed) return;
204+
closed = true;
205+
return [undefined, undefined];
206+
}
207+
}
199208
let p1: OverlayPoint<T> | undefined;
200209
let p2: OverlayPoint<T> | undefined;
201210
const iterator = this.points0(after);
202211
return () => {
212+
const next = iterator();
213+
const isEnd = !next;
214+
if (isEnd) {
215+
if (!p2 || p2.isAbsEnd()) return;
216+
p1 = p2;
217+
p2 = undefined;
218+
return [p1, p2];
219+
}
203220
p1 = p2;
204-
p2 = iterator();
221+
p2 = next;
222+
if (!p1) {
223+
if (p2 && p2.isAbsStart()) {
224+
p1 = p2;
225+
p2 = iterator();
226+
}
227+
}
205228
return (p1 || p2) ? [p1, p2] : undefined;
206229
};
207230
}

0 commit comments

Comments
 (0)