Skip to content

Commit

Permalink
Merge pull request #620 from streamich/overlay-6
Browse files Browse the repository at this point in the history
Improve iteration methods through `Overlay`
  • Loading branch information
streamich authored May 7, 2024
2 parents d846a5c + bd738ad commit fc27be3
Show file tree
Hide file tree
Showing 16 changed files with 739 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ const setup = () => {
test('can insert markers', () => {
const {peritext} = setup();
const {editor} = peritext;
expect(size(peritext.overlay.root)).toBe(0);
expect([...peritext.overlay].length).toBe(0);
editor.cursor.setAt(0);
peritext.refresh();
expect([...peritext.overlay].length).toBe(1);
editor.insMarker(['p'], '<p>');
peritext.refresh();
expect(size(peritext.overlay.root)).toBe(1);
expect(size(peritext.overlay.root)).toBe(2);
editor.cursor.setAt(9);
editor.insMarker(['p'], '<p>');
peritext.refresh();
Expand Down
51 changes: 51 additions & 0 deletions src/json-crdt-extensions/peritext/__tests__/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {s} from '../../../json-crdt-patch';
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');
const api = model.api;
const peritextApi = model.s.text.toExt();
const peritext = peritextApi.txt;
const editor = peritextApi.editor;
return {
schema,
model,
api,
peritextApi,
peritext,
editor,
};
};
15 changes: 15 additions & 0 deletions src/json-crdt-extensions/peritext/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ export const enum Chars {
BlockSplitSentinel = '\n',
}

export const enum Position {
/**
* Specifies the absolute start of the text, i.e. the position before the
* first character. In model space it is defined as string ID and "after"
* anchor.
*/
AbsStart = -1,

/**
* Specifies the absolute end of the text, i.e. the position after the last
* character. In model space it is defined as string ID and "before" anchor.
*/
AbsEnd = 9007199254740991, // Number.MAX_SAFE_INTEGER
}

export const MNEMONIC = ExtensionName[ExtensionId.peritext];

export const SCHEMA = (text: string) =>
Expand Down
5 changes: 4 additions & 1 deletion src/json-crdt-extensions/peritext/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,25 @@ export class Editor<T = string> {
return true;
}

/** @deprecated use `.saved.insStack` */
public insStackSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T> {
const range = this.cursor.range();
return this.txt.savedSlices.ins(range, SliceBehavior.Stack, type, data);
}

/** @deprecated use `.saved.insOverwrite` */
public insOverwriteSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T> {
const range = this.cursor.range();
return this.txt.savedSlices.ins(range, SliceBehavior.Overwrite, type, data);
}

/** @deprecated use `.saved.insErase` */
public insEraseSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T> {
const range = this.cursor.range();
return this.txt.savedSlices.ins(range, SliceBehavior.Erase, type, data);
}

/** @deprecated */
/** @deprecated use `.saved.insMarker` */
public insMarker(type: SliceType, data?: unknown): MarkerSlice<T> {
return this.saved.insMarker(type, data, Chars.BlockSplitSentinel)[0];
}
Expand Down
33 changes: 27 additions & 6 deletions src/json-crdt-extensions/peritext/editor/EditorSlices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,44 @@ import type {Peritext} from '../Peritext';
import type {SliceType} from '../slice/types';
import type {MarkerSlice} from '../slice/MarkerSlice';
import type {Slices} from '../slice/Slices';
import type {ITimestampStruct} from '../../../json-crdt-patch';
import type {PersistedSlice} from '../slice/PersistedSlice';
import type {Cursor} from './Cursor';

export class EditorSlices<T = string> {
constructor(
protected readonly txt: Peritext<T>,
protected readonly slices: Slices<T>,
) {}

protected insAtCursors<S extends PersistedSlice<T>>(callback: (cursor: Cursor<T>) => S): S[] {
const slices: S[] = [];
this.txt.editor.cursors((cursor) => {
const slice = callback(cursor);
slices.push(slice);
});
return slices;
}

public insStack(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T>[] {
return this.insAtCursors((cursor) => this.slices.insStack(cursor.range(), type, data));
}

public insOverwrite(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T>[] {
return this.insAtCursors((cursor) => this.slices.insOverwrite(cursor.range(), type, data));
}

public insErase(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T>[] {
return this.insAtCursors((cursor) => this.slices.insErase(cursor.range(), type, data));
}

public insMarker(type: SliceType, data?: unknown, separator?: string): MarkerSlice<T>[] {
const {txt, slices} = this;
const markers: MarkerSlice<T>[] = [];
txt.editor.cursors((cursor) => {
return this.insAtCursors((cursor) => {
cursor.collapse();
const after = cursor.start.clone();
after.refAfter();
const marker = slices.insMarkerAfter(after.id, type, data, separator);
markers.push(marker);
const marker = this.slices.insMarkerAfter(after.id, type, data, separator);
return marker;
});
return markers;
}
}
11 changes: 10 additions & 1 deletion src/json-crdt-extensions/peritext/overlay/MarkerOverlayPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ export class MarkerOverlayPoint<T = string> extends OverlayPoint<T> {
}

public toString(tab: string = '', lite?: boolean): string {
return super.toString(tab, lite) + (lite ? '' : printTree(tab, [(tab) => this.marker.toString(tab)]));
return (
this.toStringName(tab, lite) +
(lite
? ''
: printTree(tab, [
(tab) => this.marker.toString(tab),
...this.layers.map((slice) => (tab: string) => slice.toString(tab)),
...this.markers.map((slice) => (tab: string) => slice.toString(tab)),
]))
);
}
}
Loading

0 comments on commit fc27be3

Please sign in to comment.