From 032bab1ec3fc65d20f5670cc7566b34bb457c93b Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 2 May 2024 19:24:02 +0200 Subject: [PATCH] =?UTF-8?q?feat(json-crdt-extensions):=20=F0=9F=8E=B8=20de?= =?UTF-8?q?fine=20Peritext=20extension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-extensions/constants.ts | 7 +++ src/json-crdt-extensions/index.ts | 1 + .../peritext/PeritextApi.ts | 6 +++ .../peritext/PeritextNode.ts | 43 +++++++++++++++++++ .../peritext/constants.ts | 26 +++++++++++ .../peritext/editor/Editor.ts | 2 +- src/json-crdt-extensions/peritext/index.ts | 15 +++++++ .../peritext/slice/Slices.ts | 1 + src/json-crdt-extensions/peritext/types.ts | 8 ++++ src/json-crdt/extensions/index.ts | 5 ++- 10 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 src/json-crdt-extensions/peritext/PeritextApi.ts create mode 100644 src/json-crdt-extensions/peritext/PeritextNode.ts create mode 100644 src/json-crdt-extensions/peritext/index.ts diff --git a/src/json-crdt-extensions/constants.ts b/src/json-crdt-extensions/constants.ts index 2364d76b94..cc81ad74f0 100644 --- a/src/json-crdt-extensions/constants.ts +++ b/src/json-crdt-extensions/constants.ts @@ -4,3 +4,10 @@ export const enum ExtensionId { peritext = 2, quill = 3, } + +export enum ExtensionName { + mval = ExtensionId.mval, + cnt = ExtensionId.cnt, + peritext = ExtensionId.peritext, + quill = ExtensionId.quill, +} diff --git a/src/json-crdt-extensions/index.ts b/src/json-crdt-extensions/index.ts index 745c3b1ab5..955c7056c2 100644 --- a/src/json-crdt-extensions/index.ts +++ b/src/json-crdt-extensions/index.ts @@ -1,2 +1,3 @@ export * from './mval'; export * from './cnt'; +export * from './peritext'; diff --git a/src/json-crdt-extensions/peritext/PeritextApi.ts b/src/json-crdt-extensions/peritext/PeritextApi.ts new file mode 100644 index 0000000000..57deb7345e --- /dev/null +++ b/src/json-crdt-extensions/peritext/PeritextApi.ts @@ -0,0 +1,6 @@ +import {NodeApi} from '../../json-crdt/model/api/nodes'; +import type {PeritextNode} from './PeritextNode'; +import type {ExtensionApi} from '../../json-crdt'; + +export class PeritextApi extends NodeApi implements ExtensionApi { +} diff --git a/src/json-crdt-extensions/peritext/PeritextNode.ts b/src/json-crdt-extensions/peritext/PeritextNode.ts new file mode 100644 index 0000000000..170b72232a --- /dev/null +++ b/src/json-crdt-extensions/peritext/PeritextNode.ts @@ -0,0 +1,43 @@ +import {printTree} from 'tree-dump/lib/printTree'; +import {MNEMONIC} from './constants'; +import type {ITimestampStruct} from '../../json-crdt-patch/clock'; +import type {ExtensionJsonNode, JsonNode} from '../../json-crdt'; +import type {Printable} from 'tree-dump/lib/types'; +import type {PeritextDataNode} from './types'; + +export class PeritextNode implements ExtensionJsonNode, Printable { + public readonly id: ITimestampStruct; + + constructor(public readonly data: PeritextDataNode) { + this.id = data.id; + } + + // -------------------------------------------------------- ExtensionJsonNode + + public name(): string { + return MNEMONIC; + } + + public view(): string { + const str = this.data.get(0)!; + return str.view(); + } + + public children(callback: (node: JsonNode) => void): void {} + + public child?(): PeritextDataNode { + return this.data; + } + + public container(): JsonNode | undefined { + return this.data.container(); + } + + public api: undefined | unknown = undefined; + + // ---------------------------------------------------------------- Printable + + public toString(tab?: string): string { + return this.name() + printTree(tab, [(tab) => this.data.toString(tab)]); + } +} diff --git a/src/json-crdt-extensions/peritext/constants.ts b/src/json-crdt-extensions/peritext/constants.ts index 05532030bb..a6191aa6c3 100644 --- a/src/json-crdt-extensions/peritext/constants.ts +++ b/src/json-crdt-extensions/peritext/constants.ts @@ -1,3 +1,29 @@ +import {nodes, s} from "../../json-crdt-patch"; +import {ExtensionId, ExtensionName} from "../constants"; +import {SliceSchema} from "./slice/types"; + export const enum Chars { BlockSplitSentinel = '\n', } + +export const MNEMONIC = ExtensionName[ExtensionId.peritext]; + +export const BUILD_SCHEMA = (text: string) => ( + s.vec<[ + /** + * The text of the node. All rich-text textual data is stored in this node. + */ + str: nodes.str, + + /** + * The slices of the node. All rich-text annotations are stored in this + * node. + */ + slices: nodes.arr, + ]>( + s.str(text), + s.arr([]), + ) +); + +export const SCHEMA = BUILD_SCHEMA(''); diff --git a/src/json-crdt-extensions/peritext/editor/Editor.ts b/src/json-crdt-extensions/peritext/editor/Editor.ts index d66f1e2adc..d20ceee1da 100644 --- a/src/json-crdt-extensions/peritext/editor/Editor.ts +++ b/src/json-crdt-extensions/peritext/editor/Editor.ts @@ -13,7 +13,7 @@ import type {MarkerSlice} from '../slice/MarkerSlice'; import type {Slices} from '../slice/Slices'; /** - * Rename to `PeritextApi`. + * @todo Rename to `PeritextApi`. */ export class Editor implements Printable { /** diff --git a/src/json-crdt-extensions/peritext/index.ts b/src/json-crdt-extensions/peritext/index.ts new file mode 100644 index 0000000000..05d1166503 --- /dev/null +++ b/src/json-crdt-extensions/peritext/index.ts @@ -0,0 +1,15 @@ +import {ext} from '../../json-crdt/extensions'; +import {ExtensionId} from '../constants'; +import {PeritextNode} from './PeritextNode'; +import {PeritextApi} from './PeritextApi'; +import {BUILD_SCHEMA, MNEMONIC} from './constants'; +import type {PeritextDataNode} from './types'; +import type {ExtensionDefinition} from '../../json-crdt'; + +export const PeritextExt: ExtensionDefinition = { + id: ExtensionId.peritext, + name: MNEMONIC, + new: (text: string) => ext(ExtensionId.peritext, BUILD_SCHEMA(text)), + Node: PeritextNode, + Api: PeritextApi, +}; diff --git a/src/json-crdt-extensions/peritext/slice/Slices.ts b/src/json-crdt-extensions/peritext/slice/Slices.ts index bf83f539be..4997c73cb2 100644 --- a/src/json-crdt-extensions/peritext/slice/Slices.ts +++ b/src/json-crdt-extensions/peritext/slice/Slices.ts @@ -62,6 +62,7 @@ export class Slices implements Stateful, Printable { if (data !== undefined) tupleKeysUpdate.push([SliceTupleIndex.Data, builder.json(data)]); builder.insVec(tupleId, tupleKeysUpdate); const chunkId = builder.insArr(set.id, set.id, [tupleId]); + // TODO: Consider using `s` schema here. api.apply(); const tuple = model.index.get(tupleId) as VecNode; const chunk = set.findById(chunkId)!; diff --git a/src/json-crdt-extensions/peritext/types.ts b/src/json-crdt-extensions/peritext/types.ts index 44f873dd52..b400bd91b0 100644 --- a/src/json-crdt-extensions/peritext/types.ts +++ b/src/json-crdt-extensions/peritext/types.ts @@ -1,5 +1,10 @@ +import type {SchemaToJsonNode} from "../../json-crdt/schema/types"; +import type {SCHEMA} from "./constants"; + /** * Represents an object which state can change over time. + * + * @todo Move to /src/utils. */ export interface Stateful { /** @@ -13,3 +18,6 @@ export interface Stateful { */ refresh(): number; } + +export type PeritextDataNodeSchema = typeof SCHEMA; +export type PeritextDataNode = SchemaToJsonNode; diff --git a/src/json-crdt/extensions/index.ts b/src/json-crdt/extensions/index.ts index 2b5446622d..ea3cbe3ff6 100644 --- a/src/json-crdt/extensions/index.ts +++ b/src/json-crdt/extensions/index.ts @@ -1,8 +1,11 @@ import {PatchBuilder} from '../../json-crdt-patch/PatchBuilder'; import {NodeBuilder} from '../../json-crdt-patch/builder/DelayedValueBuilder'; import {konst} from '../../json-crdt-patch/builder/Konst'; -import {ITimestampStruct} from '../../json-crdt-patch/clock'; +import type {ITimestampStruct} from '../../json-crdt-patch/clock'; +/** + * @todo Replace this by `s` builder. + */ export const ext = (extensionId: number, nodeBuilder: NodeBuilder) => new NodeBuilder((builder: PatchBuilder): ITimestampStruct => { // Extension tuple starts with a 3-byte header: