Skip to content

Commit a88f9d8

Browse files
committed
feat(json-crdt-extensions): 🎸 support absolut positions in higher/lower iteration
1 parent fdf0744 commit a88f9d8

File tree

2 files changed

+124
-26
lines changed

2 files changed

+124
-26
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ export class Overlay<T = string> implements Printable, Stateful {
8282
* Retrieve overlay point or the previous one, measured in spacial dimension.
8383
*/
8484
public getOrNextLower(point: Point<T>): OverlayPoint<T> | undefined {
85+
if (point.isAbsStart()) {
86+
const first = this.first();
87+
if (!first) return;
88+
if (first.isAbsStart()) return first;
89+
point = first;
90+
} else if (point.isAbsEnd()) return this.last();
8591
let curr: OverlayPoint<T> | undefined = this.root;
8692
let result: OverlayPoint<T> | undefined = undefined;
8793
while (curr) {
@@ -107,7 +113,7 @@ export class Overlay<T = string> implements Printable, Stateful {
107113
if (!last) return;
108114
if (last.isAbsEnd()) return last;
109115
point = last;
110-
}
116+
} else if (point.isAbsStart()) return this.first();
111117
let curr: OverlayPoint<T> | undefined = this.root;
112118
let result: OverlayPoint<T> | undefined = undefined;
113119
while (curr) {
@@ -117,7 +123,7 @@ export class Overlay<T = string> implements Printable, Stateful {
117123
else {
118124
const next = curr.l;
119125
result = curr;
120-
if (!next) return;
126+
if (!next) return result;
121127
curr = next;
122128
}
123129
}

‎src/json-crdt-extensions/peritext/overlay/__tests__/Overlay.getOrNextLH.spec.ts‎

Lines changed: 116 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,99 @@ describe('.getOrNextLower()', () => {
5252
const cnt = size(peritext.overlay.root);
5353
expect(cnt).toBe(3);
5454
});
55+
56+
test('can iterate through all character points', () => {
57+
const model = Model.create();
58+
const api = model.api;
59+
api.root({
60+
text: '1234',
61+
slices: [],
62+
});
63+
const peritext = new Peritext(model, api.str(['text']).node, api.arr(['slices']).node);
64+
const editor = peritext.editor;
65+
editor.cursor.setAt(0, 1);
66+
const [slice1] = editor.saved.insStack(1);
67+
editor.cursor.setAt(1, 1);
68+
const [slice2] = editor.saved.insStack(2);
69+
editor.cursor.setAt(2, 1);
70+
const [slice3] = editor.saved.insStack(3);
71+
editor.cursor.setAt(3, 1);
72+
const [slice4] = editor.saved.insStack(4);
73+
peritext.refresh();
74+
let overlayPoint = peritext.overlay.getOrNextLower(slice1.start)!;
75+
expect(overlayPoint.layers.length).toBe(1);
76+
expect(overlayPoint.layers[0]).toBe(slice1);
77+
overlayPoint = peritext.overlay.getOrNextLower(slice1.end)!;
78+
expect(overlayPoint.layers.length).toBe(0);
79+
overlayPoint = peritext.overlay.getOrNextLower(slice2.start)!;
80+
expect(overlayPoint.layers.length).toBe(1);
81+
expect(overlayPoint.layers[0]).toBe(slice2);
82+
overlayPoint = peritext.overlay.getOrNextLower(slice2.end)!;
83+
expect(overlayPoint.layers.length).toBe(0);
84+
overlayPoint = peritext.overlay.getOrNextLower(slice3.start)!;
85+
expect(overlayPoint.layers.length).toBe(1);
86+
expect(overlayPoint.layers[0]).toBe(slice3);
87+
overlayPoint = peritext.overlay.getOrNextLower(slice3.end)!;
88+
expect(overlayPoint.layers.length).toBe(0);
89+
overlayPoint = peritext.overlay.getOrNextLower(slice4.start)!;
90+
expect(overlayPoint.layers.length).toBe(2);
91+
overlayPoint = peritext.overlay.getOrNextLower(slice4.end)!;
92+
expect(overlayPoint.layers.length).toBe(0);
93+
});
94+
95+
describe('when all text selected, using relative range', () => {
96+
test('can select the starting point', () => {
97+
const {peritext, editor} = setupNumbersWithTombstones();
98+
const range = peritext.range(peritext.pointStart()!, peritext.pointEnd()!);
99+
editor.cursor.setRange(range);
100+
peritext.refresh();
101+
const overlayPoint = peritext.overlay.getOrNextLower(peritext.pointAbsStart())!;
102+
expect(overlayPoint).toBeInstanceOf(OverlayPoint);
103+
expect(overlayPoint.refs.length).toBe(1);
104+
expect(overlayPoint.refs[0]).toEqual(new OverlayRefSliceEnd(editor.cursor));
105+
expect(overlayPoint.layers.length).toBe(1);
106+
expect(overlayPoint.layers[0]).toEqual(editor.cursor);
107+
});
108+
109+
test('can select the ending point', () => {
110+
const {peritext, editor} = setupNumbersWithTombstones();
111+
const range = peritext.range(peritext.pointStart()!, peritext.pointEnd()!);
112+
editor.cursor.setRange(range);
113+
peritext.refresh();
114+
const overlayPoint = peritext.overlay.getOrNextLower(peritext.pointAbsEnd())!;
115+
expect(overlayPoint).toBeInstanceOf(OverlayPoint);
116+
expect(overlayPoint.refs.length).toBe(1);
117+
expect(overlayPoint.refs[0]).toEqual(new OverlayRefSliceStart(editor.cursor));
118+
});
119+
});
120+
121+
describe('when all text selected, using absolute range', () => {
122+
test('can select the starting point', () => {
123+
const {peritext, editor} = setupNumbersWithTombstones();
124+
const range = peritext.range(peritext.pointAbsStart(), peritext.pointAbsEnd());
125+
editor.cursor.setRange(range);
126+
peritext.refresh();
127+
const overlayPoint = peritext.overlay.getOrNextLower(peritext.pointAbsStart())!;
128+
expect(overlayPoint).toBeInstanceOf(OverlayPoint);
129+
expect(overlayPoint.refs.length).toBe(1);
130+
expect(overlayPoint.refs[0]).toEqual(new OverlayRefSliceEnd(editor.cursor));
131+
expect(overlayPoint.layers.length).toBe(1);
132+
expect(overlayPoint.layers[0]).toEqual(editor.cursor);
133+
});
134+
135+
test('can select the end point', () => {
136+
const {peritext, editor} = setupNumbersWithTombstones();
137+
const range = peritext.range(peritext.pointAbsStart(), peritext.pointAbsEnd());
138+
editor.cursor.setRange(range);
139+
peritext.refresh();
140+
const overlayPoint = peritext.overlay.getOrNextHigher(peritext.pointAbsStart()!)!;
141+
expect(overlayPoint).toBeInstanceOf(OverlayPoint);
142+
expect(overlayPoint.refs.length).toBe(1);
143+
expect(overlayPoint.refs[0]).toEqual(new OverlayRefSliceStart(editor.cursor));
144+
expect(overlayPoint.layers.length).toBe(1);
145+
expect(overlayPoint.layers[0]).toEqual(editor.cursor);
146+
});
147+
});
55148
});
56149

57150
describe('.getOrNextHigher()', () => {
@@ -94,31 +187,30 @@ describe('.getOrNextHigher()', () => {
94187
expect(overlayPoint.layers[0]).toBe(slice1);
95188
});
96189

97-
test('can find points at the relative end', () => {
98-
const {peritext, editor} = setupNumbersWithTombstones();
99-
editor.cursor.setAt(9, 1);
100-
peritext.refresh();
101-
let overlayPoint = peritext.overlay.getOrNextHigher(editor.cursor.end)!;
102-
expect(overlayPoint.layers.length).toBe(0);
103-
overlayPoint = peritext.overlay.getOrNextHigher(editor.cursor.start)!;
104-
expect(overlayPoint.layers.length).toBe(1);
105-
expect(overlayPoint.layers[0]).toBe(editor.cursor);
106-
});
107-
108-
test('can find points at the relative end, when absolute end provided', () => {
109-
const {peritext, editor} = setupNumbersWithTombstones();
110-
editor.cursor.setAt(9, 1);
111-
peritext.refresh();
112-
const overlayPoint = peritext.overlay.getOrNextHigher(peritext.pointAbsEnd())!;
113-
expect(overlayPoint.layers.length).toBe(0);
114-
});
190+
describe('when all text selected, using relative range', () => {
191+
test('can select the ending point', () => {
192+
const {peritext, editor} = setupNumbersWithTombstones();
193+
const range = peritext.range(peritext.pointStart()!, peritext.pointEnd()!);
194+
editor.cursor.setRange(range);
195+
peritext.refresh();
196+
const overlayPoint = peritext.overlay.getOrNextHigher(peritext.pointAbsEnd())!;
197+
expect(overlayPoint).toBeInstanceOf(OverlayPoint);
198+
expect(overlayPoint.refs.length).toBe(1);
199+
expect(overlayPoint.refs[0]).toEqual(new OverlayRefSliceEnd(editor.cursor));
200+
});
115201

116-
test('returns undefined, when absolute start provided', () => {
117-
const {peritext, editor} = setupNumbersWithTombstones();
118-
editor.cursor.setAt(9, 1);
119-
peritext.refresh();
120-
const overlayPoint = peritext.overlay.getOrNextHigher(peritext.pointAbsStart())!;
121-
expect(overlayPoint).toBe(undefined);
202+
test('can select the start point', () => {
203+
const {peritext, editor} = setupNumbersWithTombstones();
204+
const range = peritext.range(peritext.pointStart()!, peritext.pointEnd()!);
205+
editor.cursor.setRange(range);
206+
peritext.refresh();
207+
const overlayPoint = peritext.overlay.getOrNextHigher(peritext.pointAbsStart()!)!;
208+
expect(overlayPoint).toBeInstanceOf(OverlayPoint);
209+
expect(overlayPoint.refs.length).toBe(1);
210+
expect(overlayPoint.refs[0]).toEqual(new OverlayRefSliceStart(editor.cursor));
211+
expect(overlayPoint.layers.length).toBe(1);
212+
expect(overlayPoint.layers[0]).toEqual(editor.cursor);
213+
});
122214
});
123215

124216
describe('when all text selected, using absolute range', () => {

0 commit comments

Comments
 (0)