diff --git a/src/json-crdt-extensions/constants.ts b/src/json-crdt-extensions/constants.ts index fd071bca51..2364d76b94 100644 --- a/src/json-crdt-extensions/constants.ts +++ b/src/json-crdt-extensions/constants.ts @@ -4,7 +4,3 @@ export const enum ExtensionId { peritext = 2, quill = 3, } - -export const enum Chars { - BlockSplitSentinel = '\n', -} diff --git a/src/json-crdt-extensions/peritext/Peritext.ts b/src/json-crdt-extensions/peritext/Peritext.ts index aa31a19424..7b5b56778f 100644 --- a/src/json-crdt-extensions/peritext/Peritext.ts +++ b/src/json-crdt-extensions/peritext/Peritext.ts @@ -1,14 +1,18 @@ +import {printTree} from 'sonic-forest/lib/print/printTree'; import {Anchor} from './rga/constants'; import {Point} from './rga/Point'; import {Range} from './rga/Range'; import {Editor} from './editor/Editor'; -import {printTree} from '../../util/print/printTree'; import {ArrNode, StrNode} from '../../json-crdt/nodes'; import {Slices} from './slice/Slices'; -import {type ITimestampStruct} from '../../json-crdt-patch/clock'; +import {Overlay} from './overlay/Overlay'; +import {Chars} from './constants'; +import type {ITimestampStruct} from '../../json-crdt-patch/clock'; import type {Model} from '../../json-crdt/model'; import type {Printable} from '../../util/print/types'; import type {StringChunk} from './util/types'; +import type {SliceType} from './types'; +import type {MarkerSlice} from './slice/MarkerSlice'; /** * Context for a Peritext instance. Contains all the data and methods needed to @@ -17,6 +21,7 @@ import type {StringChunk} from './util/types'; export class Peritext implements Printable { public readonly slices: Slices; public readonly editor: Editor; + public readonly overlay = new Overlay(this); constructor( public readonly model: Model, @@ -43,6 +48,17 @@ export class Peritext implements Printable { return curr; } + /** Select a single character before a point. */ + public findCharBefore(point: Point): Range | undefined { + if (point.anchor === Anchor.After) { + const chunk = point.chunk(); + if (chunk && !chunk.del) return this.range(this.point(point.id, Anchor.Before), point); + } + const id = point.prevId(); + if (!id) return; + return this.range(this.point(id, Anchor.Before), this.point(id, Anchor.After)); + } + // ------------------------------------------------------------------- points /** @@ -159,15 +175,19 @@ export class Peritext implements Printable { return textId; } - /** Select a single character before a point. */ - public findCharBefore(point: Point): Range | undefined { - if (point.anchor === Anchor.After) { - const chunk = point.chunk(); - if (chunk && !chunk.del) return this.range(this.point(point.id, Anchor.Before), point); - } - const id = point.prevId(); - if (!id) return; - return this.range(this.point(id, Anchor.Before), this.point(id, Anchor.After)); + public insMarker(after: ITimestampStruct, type: SliceType, data?: unknown, char: string = Chars.BlockSplitSentinel): MarkerSlice { + const api = this.model.api; + const builder = api.builder; + const str = this.str; + /** + * We skip one clock cycle to prevent Block-wise RGA from merging adjacent + * characters. We want the marker chunk to always be its own distinct chunk. + */ + builder.nop(1); + const textId = builder.insStr(str.id, after, char[0]); + const point = this.point(textId, Anchor.Before); + const range = this.range(point, point); + return this.slices.insMarker(range, type, data); } // ---------------------------------------------------------------- Printable diff --git a/src/json-crdt-extensions/peritext/constants.ts b/src/json-crdt-extensions/peritext/constants.ts new file mode 100644 index 0000000000..05532030bb --- /dev/null +++ b/src/json-crdt-extensions/peritext/constants.ts @@ -0,0 +1,3 @@ +export const enum Chars { + BlockSplitSentinel = '\n', +} diff --git a/src/json-crdt-extensions/peritext/editor/Editor.ts b/src/json-crdt-extensions/peritext/editor/Editor.ts index de975f2133..5f5f654d99 100644 --- a/src/json-crdt-extensions/peritext/editor/Editor.ts +++ b/src/json-crdt-extensions/peritext/editor/Editor.ts @@ -3,11 +3,13 @@ import {Anchor} from '../rga/constants'; import {SliceBehavior} from '../slice/constants'; import {tick, type ITimestampStruct} from '../../../json-crdt-patch/clock'; import {PersistedSlice} from '../slice/PersistedSlice'; +import {Chars} from '../constants'; import type {Range} from '../rga/Range'; import type {Peritext} from '../Peritext'; import type {Printable} from '../../../util/print/types'; import type {Point} from '../rga/Point'; import type {SliceType} from '../types'; +import type {MarkerSlice} from '../slice/MarkerSlice'; export class Editor implements Printable { /** @@ -132,4 +134,9 @@ export class Editor implements Printable { public insertEraseSlice(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice { return this.txt.slices.ins(this.cursor, SliceBehavior.Erase, type, data); } + + public insMarker(type: SliceType, data?: unknown): MarkerSlice { + const after = this.collapseSelection(); + return this.txt.insMarker(after, type, data, Chars.BlockSplitSentinel); + } }