From 3ef2fe603fff4a7650aa5197f92bed9127c3dcb9 Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 8 Jun 2024 15:09:23 +0200 Subject: [PATCH] =?UTF-8?q?feat(json-crdt-extensions):=20=F0=9F=8E=B8=20im?= =?UTF-8?q?prove=20Inline=20attribute=20construction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../peritext/block/Inline.ts | 66 +++++++++++-------- .../block/__tests__/Inline.str.spec.ts | 30 +++++++++ .../peritext/slice/constants.ts | 8 ++- 3 files changed, 74 insertions(+), 30 deletions(-) diff --git a/src/json-crdt-extensions/peritext/block/Inline.ts b/src/json-crdt-extensions/peritext/block/Inline.ts index 6344d8e7d8..5e33e7587c 100644 --- a/src/json-crdt-extensions/peritext/block/Inline.ts +++ b/src/json-crdt-extensions/peritext/block/Inline.ts @@ -1,14 +1,13 @@ import {printTree} from 'tree-dump/lib/printTree'; import {OverlayPoint} from '../overlay/OverlayPoint'; import {stringify} from '../../../json-text/stringify'; -import {SliceBehavior} from '../slice/constants'; +import {SliceBehavior, SliceTypes} from '../slice/constants'; import {Range} from '../rga/Range'; import {ChunkSlice} from '../util/ChunkSlice'; import {updateNum} from '../../../json-hash'; import type {AbstractRga} from '../../../json-crdt/nodes/rga'; import type {Printable} from 'tree-dump/lib/types'; import type {PathStep} from '../../../json-pointer'; -import type {Slice} from '../slice/types'; import type {Peritext} from '../Peritext'; export type InlineAttributes = Record; @@ -57,7 +56,7 @@ export class Inline extends Range implements Printable { } /** - * @returns The position of the inline withing the text. + * @returns The position of the inline within the text. */ public pos(): number { const chunkSlice = this.texts[0]; @@ -74,34 +73,45 @@ export class Inline extends Range implements Printable { public attr(): InlineAttributes { const attr: InlineAttributes = {}; const point = this.start as OverlayPoint; - const slices: Slice[] = this.texts.length ? point.layers : point.markers; - const length = slices.length; - for (let i = 0; i < length; i++) { - const slice = slices[i]; - const type = slice.type as PathStep; - switch (slice.behavior) { - case SliceBehavior.Cursor: - case SliceBehavior.Stack: { - let dataList: unknown[] = (attr[type] as unknown[]) || (attr[type] = []); - if (!Array.isArray(dataList)) dataList = attr[type] = [dataList]; - let data = slice.data(); - if (data === undefined) data = 1; - dataList.push(data); - break; - } - case SliceBehavior.Overwrite: { - let data = slice.data(); - if (data === undefined) data = 1; - attr[type] = data; - break; - } - case SliceBehavior.Erase: { - delete attr[type]; - break; + const slices1 = point.layers; + const slices2 = point.markers; + const length1 = slices1.length; + const length2 = slices2.length; + const length3 = length1 + length2; + for (let i = 0; i < length3; i++) { + const slice = i >= length1 ? slices2[i - length1] : slices1[i]; + if (slice instanceof Range) { + const type = slice.type as PathStep; + switch (slice.behavior) { + case SliceBehavior.Cursor: { + const dataList: unknown[] = (attr[SliceTypes.Cursor] as unknown[]) || (attr[SliceTypes.Cursor] = []); + const data: unknown[] = [type]; + const cursorData = slice.data(); + if (cursorData !== void 0) data.push(cursorData); + dataList.push(data); + break; + } + case SliceBehavior.Stack: { + let dataList: unknown[] = (attr[type] as unknown[]) || (attr[type] = []); + if (!Array.isArray(dataList)) dataList = attr[type] = [dataList]; + let data = slice.data(); + if (data === undefined) data = 1; + dataList.push(data); + break; + } + case SliceBehavior.Overwrite: { + let data = slice.data(); + if (data === undefined) data = 1; + attr[type] = data; + break; + } + case SliceBehavior.Erase: { + delete attr[type]; + break; + } } } } - // TODO: Iterate over the markers... return attr; } diff --git a/src/json-crdt-extensions/peritext/block/__tests__/Inline.str.spec.ts b/src/json-crdt-extensions/peritext/block/__tests__/Inline.str.spec.ts index 04c02535bd..e97d93d96a 100644 --- a/src/json-crdt-extensions/peritext/block/__tests__/Inline.str.spec.ts +++ b/src/json-crdt-extensions/peritext/block/__tests__/Inline.str.spec.ts @@ -1,4 +1,5 @@ import {Kit, setupKit, setupNumbersKit, setupNumbersWithTombstonesKit} from '../../__tests__/setup'; +import {CursorAnchor, SliceTypes} from '../../slice/constants'; import {Inline} from '../Inline'; const runStrTests = (setup: () => Kit) => { @@ -102,6 +103,35 @@ const runStrTests = (setup: () => Kit) => { expect(attr['bold,very']).toEqual([1]); expect(attr['bold,normal']).toEqual([2]); }); + + test('returns collapsed slice (cursor) at marker position', () => { + const {peritext} = setup(); + peritext.editor.cursor.setAt(3); + const [paragraph] = peritext.editor.saved.insMarker('p'); + peritext.editor.cursor.set(paragraph.start); + peritext.refresh(); + const block = peritext.blocks.root.children[1]!; + const inline = [...block.texts()][0]; + const attr = inline.attr(); + expect(attr).toEqual({ + [SliceTypes.Cursor]: [[CursorAnchor.Start]], + }); + }); + + test('returns collapsed slice (cursor) at markup slice start', () => { + const {peritext} = setup(); + peritext.editor.cursor.setAt(2, 2); + const [slice] = peritext.editor.saved.insStack('bold', 123); + peritext.editor.cursor.set(slice.start); + peritext.refresh(); + const block = peritext.blocks.root.children[0]!; + const inline = [...block.texts()][1]; + const attr = inline.attr(); + expect(attr).toEqual({ + [SliceTypes.Cursor]: [[CursorAnchor.Start]], + bold: [123], + }); + }); }); }; diff --git a/src/json-crdt-extensions/peritext/slice/constants.ts b/src/json-crdt-extensions/peritext/slice/constants.ts index 11c016a3c5..b567131bbc 100644 --- a/src/json-crdt-extensions/peritext/slice/constants.ts +++ b/src/json-crdt-extensions/peritext/slice/constants.ts @@ -9,8 +9,12 @@ export const enum CursorAnchor { End = 1, } -export const enum Tags { - Cursor = 0, +/** + * Built-in slice types. + */ +export const enum SliceTypes { + Cursor = -1, + Paragraph = 0, } export const enum SliceHeaderMask {