Skip to content

Commit 9f090f7

Browse files
committed
feat(json-crdt-extensions): 🎸 add markers only overlay tree
1 parent 636a166 commit 9f090f7

File tree

5 files changed

+76
-18
lines changed

5 files changed

+76
-18
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import {printTree} from 'tree-dump/lib/printTree';
22
import {OverlayPoint} from './OverlayPoint';
3+
import type {HeadlessNode2} from 'sonic-forest/lib/types2';
34
import type {SliceType} from '../slice/types';
45
import type {Anchor} from '../rga/constants';
56
import type {AbstractRga} from '../../../json-crdt/nodes/rga';
67
import type {ITimestampStruct} from '../../../json-crdt-patch/clock';
78
import type {MarkerSlice} from '../slice/MarkerSlice';
89

9-
export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> {
10+
export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> implements HeadlessNode2 {
1011
/**
1112
* Hash value of the following text contents, up until the next marker.
1213
*/
@@ -57,4 +58,10 @@ export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> {
5758
]))
5859
);
5960
}
61+
62+
// ---------------------------------------------------------------- Printable
63+
64+
public p2: MarkerOverlayPoint<T> | undefined;
65+
public l2: MarkerOverlayPoint<T> | undefined;
66+
public r2: MarkerOverlayPoint<T> | undefined;
6067
}

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

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {printTree} from 'tree-dump/lib/printTree';
22
import {printBinary} from 'tree-dump/lib/printBinary';
33
import {first, insertLeft, insertRight, last, next, prev, remove} from 'sonic-forest/lib/util';
4+
import {first2, insert2, next2, remove2} from 'sonic-forest/lib/util2';
45
import {splay} from 'sonic-forest/lib/splay/util';
56
import {Anchor} from '../rga/constants';
67
import {Point} from '../rga/Point';
@@ -19,6 +20,9 @@ import type {Printable} from 'tree-dump/lib/types';
1920
import type {MutableSlice, Slice} from '../slice/types';
2021
import type {Slices} from '../slice/Slices';
2122
import type {OverlayPair, OverlayTuple} from './types';
23+
import type {Comparator} from 'sonic-forest/lib/types';
24+
25+
const spatialComparator: Comparator<OverlayPoint> = (a: OverlayPoint, b: OverlayPoint) => a.cmpSpatial(b);
2226

2327
/**
2428
* Overlay is a tree structure that represents all the intersections of slices
@@ -29,6 +33,7 @@ import type {OverlayPair, OverlayTuple} from './types';
2933
*/
3034
export class Overlay<T = string> implements Printable, Stateful {
3135
public root: OverlayPoint<T> | undefined = undefined;
36+
public root2: MarkerOverlayPoint<T> | undefined = undefined;
3237

3338
/** A virtual absolute start point, used when the absolute start is missing. */
3439
public readonly START: OverlayPoint<T>;
@@ -171,20 +176,17 @@ export class Overlay<T = string> implements Printable, Stateful {
171176
return new UndefEndIter(this.points0(after));
172177
}
173178

174-
public markers0(): UndefIterator<MarkerOverlayPoint<T>> {
175-
let curr = this.first();
179+
public markers0(after: undefined | MarkerOverlayPoint<T>): UndefIterator<MarkerOverlayPoint<T>> {
180+
let curr = after ? next2(after) : first2(this.root2);
176181
return () => {
177-
while (curr) {
178-
const ret = curr;
179-
if (curr) curr = next(curr);
180-
if (ret instanceof MarkerOverlayPoint) return ret;
181-
}
182-
return;
182+
const ret = curr;
183+
if (curr) curr = next2(curr);
184+
return ret;
183185
};
184186
}
185187

186188
public markers(): IterableIterator<MarkerOverlayPoint<T>> {
187-
return new UndefEndIter(this.markers0());
189+
return new UndefEndIter(this.markers0(undefined));
188190
}
189191

190192
public pairs0(after: undefined | OverlayPoint<T>): UndefIterator<OverlayPair<T>> {
@@ -404,6 +406,7 @@ export class Overlay<T = string> implements Printable, Stateful {
404406
}
405407

406408
private insMarker(slice: MarkerSlice<T>): [start: OverlayPoint<T>, end: OverlayPoint<T>] {
409+
// TODO: When marker is at rel start, and cursor too, the overlay point should have reference to the cursor.
407410
const point = this.mPoint(slice, Anchor.Before);
408411
const pivot = this.insPoint(point);
409412
if (!pivot) {
@@ -445,6 +448,10 @@ export class Overlay<T = string> implements Printable, Stateful {
445448
* @returns Returns the existing point if it was already in the tree.
446449
*/
447450
private insPoint(point: OverlayPoint<T>): OverlayPoint<T> | undefined {
451+
if (point instanceof MarkerOverlayPoint) {
452+
this.root2 = insert2(this.root2, point, spatialComparator);
453+
// if (this.root2 !== point) this.root2 = splay2(this.root2!, point, 10);
454+
}
448455
let pivot = this.getOrNextLower(point);
449456
if (!pivot) pivot = first(this.root);
450457
if (!pivot) {
@@ -461,6 +468,8 @@ export class Overlay<T = string> implements Printable, Stateful {
461468
}
462469

463470
private delPoint(point: OverlayPoint<T>): void {
471+
if (point instanceof MarkerOverlayPoint)
472+
this.root2 = remove2(this.root2, point);
464473
this.root = remove(this.root, point);
465474
}
466475

@@ -519,9 +528,21 @@ export class Overlay<T = string> implements Printable, Stateful {
519528
])
520529
);
521530
};
531+
const printMarkerPoint = (tab: string, point: MarkerOverlayPoint<T>): string => {
532+
return (
533+
point.toString(tab) +
534+
printBinary(tab, [
535+
!point.l2 ? null : (tab) => printMarkerPoint(tab, point.l2!),
536+
!point.r2 ? null : (tab) => printMarkerPoint(tab, point.r2!),
537+
])
538+
);
539+
};
522540
return (
523541
`${this.constructor.name} #${this.hash.toString(36)}` +
524-
printTree(tab, [!this.root ? null : (tab) => printPoint(tab, this.root!)])
542+
printTree(tab, [
543+
!this.root ? null : (tab) => printPoint(tab, this.root!),
544+
!this.root2 ? null : (tab) => printMarkerPoint(tab, this.root2!),
545+
])
525546
);
526547
}
527548
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {OverlayRef, OverlayRefSliceEnd, OverlayRefSliceStart} from './refs';
44
import {printTree} from 'tree-dump/lib/printTree';
55
import type {MarkerSlice} from '../slice/MarkerSlice';
66
import type {HeadlessNode} from 'sonic-forest/lib/types';
7-
import type {Printable} from 'tree-dump/lib/types';
7+
import type {PrintChild, Printable} from 'tree-dump/lib/types';
88
import type {Slice} from '../slice/types';
99

1010
/**
@@ -218,12 +218,16 @@ export class OverlayPoint<T = string> extends Point<T> implements Printable, Hea
218218
const refs = lite ? '' : `, refs = ${this.refs.length}`;
219219
const header = this.toStringName(tab, lite) + refs;
220220
if (lite) return header;
221+
const children: PrintChild[] = [];
222+
const layers = this.layers;
223+
const layerLength = layers.length;
224+
for (let i = 0; i < layerLength; i++) children.push((tab) => layers[i].toString(tab));
225+
const markers = this.markers;
226+
const markerLength = markers.length;
227+
for (let i = 0; i < markerLength; i++) children.push((tab) => markers[i].toString(tab));
221228
return (
222229
header +
223-
printTree(
224-
tab,
225-
this.layers.map((slice) => (tab) => slice.toString(tab)),
226-
)
230+
printTree(tab, children)
227231
);
228232
}
229233

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,32 @@ const runMarkersTests = (setup: () => Kit) => {
7373
peritext.overlay.refresh();
7474
expect([...peritext.overlay.markers()].length).toBe(0);
7575
});
76+
77+
test('can add marker at the start of text', () => {
78+
const {peritext, editor} = setup();
79+
editor.cursor.setAt(0);
80+
const [marker] = editor.extra.insMarker(0);
81+
peritext.overlay.refresh();
82+
const list = [...peritext.overlay.markers()];
83+
expect(list.length).toBe(1);
84+
expect(list[0].marker).toBe(marker);
85+
editor.extra.del(marker);
86+
peritext.overlay.refresh();
87+
expect([...peritext.overlay.markers()].length).toBe(0);
88+
});
89+
90+
test('can add marker at the end of text', () => {
91+
const {peritext, editor} = setup();
92+
editor.cursor.set(peritext.pointEnd()!);
93+
const [marker] = editor.extra.insMarker('0');
94+
peritext.overlay.refresh();
95+
const list = [...peritext.overlay.markers()];
96+
expect(list.length).toBe(1);
97+
expect(list[0].marker).toBe(marker);
98+
editor.extra.del(marker);
99+
peritext.overlay.refresh();
100+
expect([...peritext.overlay.markers()].length).toBe(0);
101+
});
76102
});
77103
};
78104

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const setup = () => {
2121

2222
const markerCount = (peritext: Peritext): number => {
2323
const overlay = peritext.overlay;
24-
const iterator = overlay.markers0();
24+
const iterator = overlay.markers0(void 0);
2525
let count = 0;
2626
for (let split = iterator(); split; split = iterator()) {
2727
count++;
@@ -106,7 +106,7 @@ describe('markers', () => {
106106
expect(markerCount(peritext)).toBe(2);
107107
const points = [];
108108
let point;
109-
for (const iterator = peritext.overlay.markers0(); (point = iterator()); ) points.push(point);
109+
for (const iterator = peritext.overlay.markers0(void 0); (point = iterator()); ) points.push(point);
110110
expect(points.length).toBe(2);
111111
expect(points[0].pos()).toBe(2);
112112
expect(points[1].pos()).toBe(11);

0 commit comments

Comments
 (0)