Skip to content

Commit

Permalink
feat(json-crdt-extensions): 🎸 add markers only overlay tree
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed May 9, 2024
1 parent 636a166 commit 9f090f7
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {printTree} from 'tree-dump/lib/printTree';
import {OverlayPoint} from './OverlayPoint';
import type {HeadlessNode2} from 'sonic-forest/lib/types2';
import type {SliceType} from '../slice/types';
import type {Anchor} from '../rga/constants';
import type {AbstractRga} from '../../../json-crdt/nodes/rga';
import type {ITimestampStruct} from '../../../json-crdt-patch/clock';
import type {MarkerSlice} from '../slice/MarkerSlice';

export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> {
export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> implements HeadlessNode2 {
/**
* Hash value of the following text contents, up until the next marker.
*/
Expand Down Expand Up @@ -57,4 +58,10 @@ export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> {
]))
);
}

// ---------------------------------------------------------------- Printable

public p2: MarkerOverlayPoint<T> | undefined;
public l2: MarkerOverlayPoint<T> | undefined;
public r2: MarkerOverlayPoint<T> | undefined;
}
41 changes: 31 additions & 10 deletions src/json-crdt-extensions/peritext/overlay/Overlay.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {printTree} from 'tree-dump/lib/printTree';
import {printBinary} from 'tree-dump/lib/printBinary';
import {first, insertLeft, insertRight, last, next, prev, remove} from 'sonic-forest/lib/util';
import {first2, insert2, next2, remove2} from 'sonic-forest/lib/util2';
import {splay} from 'sonic-forest/lib/splay/util';
import {Anchor} from '../rga/constants';
import {Point} from '../rga/Point';
Expand All @@ -19,6 +20,9 @@ import type {Printable} from 'tree-dump/lib/types';
import type {MutableSlice, Slice} from '../slice/types';
import type {Slices} from '../slice/Slices';
import type {OverlayPair, OverlayTuple} from './types';
import type {Comparator} from 'sonic-forest/lib/types';

const spatialComparator: Comparator<OverlayPoint> = (a: OverlayPoint, b: OverlayPoint) => a.cmpSpatial(b);

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

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

public markers0(): UndefIterator<MarkerOverlayPoint<T>> {
let curr = this.first();
public markers0(after: undefined | MarkerOverlayPoint<T>): UndefIterator<MarkerOverlayPoint<T>> {
let curr = after ? next2(after) : first2(this.root2);
return () => {
while (curr) {
const ret = curr;
if (curr) curr = next(curr);
if (ret instanceof MarkerOverlayPoint) return ret;
}
return;
const ret = curr;
if (curr) curr = next2(curr);
return ret;
};
}

public markers(): IterableIterator<MarkerOverlayPoint<T>> {
return new UndefEndIter(this.markers0());
return new UndefEndIter(this.markers0(undefined));
}

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

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

private delPoint(point: OverlayPoint<T>): void {
if (point instanceof MarkerOverlayPoint)
this.root2 = remove2(this.root2, point);
this.root = remove(this.root, point);
}

Expand Down Expand Up @@ -519,9 +528,21 @@ export class Overlay<T = string> implements Printable, Stateful {
])
);
};
const printMarkerPoint = (tab: string, point: MarkerOverlayPoint<T>): string => {
return (
point.toString(tab) +
printBinary(tab, [
!point.l2 ? null : (tab) => printMarkerPoint(tab, point.l2!),
!point.r2 ? null : (tab) => printMarkerPoint(tab, point.r2!),
])
);
};
return (
`${this.constructor.name} #${this.hash.toString(36)}` +
printTree(tab, [!this.root ? null : (tab) => printPoint(tab, this.root!)])
printTree(tab, [
!this.root ? null : (tab) => printPoint(tab, this.root!),
!this.root2 ? null : (tab) => printMarkerPoint(tab, this.root2!),
])
);
}
}
14 changes: 9 additions & 5 deletions src/json-crdt-extensions/peritext/overlay/OverlayPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {OverlayRef, OverlayRefSliceEnd, OverlayRefSliceStart} from './refs';
import {printTree} from 'tree-dump/lib/printTree';
import type {MarkerSlice} from '../slice/MarkerSlice';
import type {HeadlessNode} from 'sonic-forest/lib/types';
import type {Printable} from 'tree-dump/lib/types';
import type {PrintChild, Printable} from 'tree-dump/lib/types';
import type {Slice} from '../slice/types';

/**
Expand Down Expand Up @@ -218,12 +218,16 @@ export class OverlayPoint<T = string> extends Point<T> implements Printable, Hea
const refs = lite ? '' : `, refs = ${this.refs.length}`;
const header = this.toStringName(tab, lite) + refs;
if (lite) return header;
const children: PrintChild[] = [];
const layers = this.layers;
const layerLength = layers.length;
for (let i = 0; i < layerLength; i++) children.push((tab) => layers[i].toString(tab));
const markers = this.markers;
const markerLength = markers.length;
for (let i = 0; i < markerLength; i++) children.push((tab) => markers[i].toString(tab));
return (
header +
printTree(
tab,
this.layers.map((slice) => (tab) => slice.toString(tab)),
)
printTree(tab, children)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,32 @@ const runMarkersTests = (setup: () => Kit) => {
peritext.overlay.refresh();
expect([...peritext.overlay.markers()].length).toBe(0);
});

test('can add marker at the start of text', () => {
const {peritext, editor} = setup();
editor.cursor.setAt(0);
const [marker] = editor.extra.insMarker(0);
peritext.overlay.refresh();
const list = [...peritext.overlay.markers()];
expect(list.length).toBe(1);
expect(list[0].marker).toBe(marker);
editor.extra.del(marker);
peritext.overlay.refresh();
expect([...peritext.overlay.markers()].length).toBe(0);
});

test('can add marker at the end of text', () => {
const {peritext, editor} = setup();
editor.cursor.set(peritext.pointEnd()!);
const [marker] = editor.extra.insMarker('0');
peritext.overlay.refresh();
const list = [...peritext.overlay.markers()];
expect(list.length).toBe(1);
expect(list[0].marker).toBe(marker);
editor.extra.del(marker);
peritext.overlay.refresh();
expect([...peritext.overlay.markers()].length).toBe(0);
});
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const setup = () => {

const markerCount = (peritext: Peritext): number => {
const overlay = peritext.overlay;
const iterator = overlay.markers0();
const iterator = overlay.markers0(void 0);
let count = 0;
for (let split = iterator(); split; split = iterator()) {
count++;
Expand Down Expand Up @@ -106,7 +106,7 @@ describe('markers', () => {
expect(markerCount(peritext)).toBe(2);
const points = [];
let point;
for (const iterator = peritext.overlay.markers0(); (point = iterator()); ) points.push(point);
for (const iterator = peritext.overlay.markers0(void 0); (point = iterator()); ) points.push(point);
expect(points.length).toBe(2);
expect(points[0].pos()).toBe(2);
expect(points[1].pos()).toBe(11);
Expand Down

0 comments on commit 9f090f7

Please sign in to comment.