Skip to content

Commit 9994f2a

Browse files
committed
feat(json-crdt-extensions): 🎸 add initial Inline class implementation
1 parent 2325bf6 commit 9994f2a

File tree

1 file changed

+124
-0
lines changed
  • src/json-crdt-extensions/peritext/block

1 file changed

+124
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import {printTree} from 'tree-dump/lib/printTree';
2+
import {OverlayPoint} from '../overlay/OverlayPoint';
3+
import {stringify} from '../../../json-text/stringify';
4+
import {SliceBehavior} from '../slice/constants';
5+
import {Range} from '../rga/Range';
6+
import type {AbstractRga} from '../../../json-crdt/nodes/rga';
7+
import type {ChunkSlice} from '../util/ChunkSlice';
8+
import type {Printable} from 'tree-dump/lib/types';
9+
import type {PathStep} from '../../../json-pointer';
10+
import type {Slice} from '../slice/types';
11+
12+
export type Marks = Record<string | number, unknown>;
13+
14+
export class Inline extends Range implements Printable {
15+
constructor(
16+
rga: AbstractRga<string>,
17+
public start: OverlayPoint,
18+
public end: OverlayPoint,
19+
20+
/**
21+
* @todo PERF: for performance reasons, we should consider not passing in
22+
* this array. Maybe pass in just the initial chunk and the offset. However,
23+
* maybe even the just is not necessary, as the `.start` point should have
24+
* its chunk cached, or will have it cached after the first access.
25+
*/
26+
public readonly texts: ChunkSlice[],
27+
) {
28+
super(rga, start, end);
29+
}
30+
31+
/**
32+
* @returns A stable unique identifier of this *inline* within a list of other
33+
* inlines of the parent block. Can be used for UI libraries to track the
34+
* identity of the inline across renders.
35+
*/
36+
public key(): number | string {
37+
const start = this.start;
38+
const startId = this.start.id;
39+
const endId = this.end.id;
40+
const key = startId.sid.toString(36) + start.anchor + startId.time.toString(36) + endId.time.toString(36);
41+
return key;
42+
}
43+
44+
public str(): string {
45+
let str = '';
46+
for (const slice of this.texts) str += slice.view();
47+
return str;
48+
}
49+
50+
public marks(): Marks {
51+
const marks: Marks = {};
52+
const point = this.start as OverlayPoint;
53+
const slices: Slice[] = this.texts.length ? point.layers : point.markers;
54+
const length = slices.length;
55+
for (let i = 0; i < length; i++) {
56+
const slice = slices[i];
57+
const type = slice.type as PathStep;
58+
switch (slice.behavior) {
59+
case SliceBehavior.Stack: {
60+
let dataList: unknown[] = (marks[type] as unknown[]) || (marks[type] = []);
61+
if (!Array.isArray(dataList)) dataList = marks[type] = [dataList];
62+
let data = slice.data();
63+
if (data === undefined) data = 1;
64+
dataList.push(data);
65+
break;
66+
}
67+
case SliceBehavior.Overwrite: {
68+
let data = slice.data();
69+
if (data === undefined) data = 1;
70+
marks[type] = data;
71+
break;
72+
}
73+
case SliceBehavior.Erase: {
74+
delete marks[type];
75+
break;
76+
}
77+
}
78+
}
79+
return marks;
80+
}
81+
82+
public pos(): number {
83+
const chunkSlice = this.texts[0];
84+
if (!chunkSlice) return -1;
85+
const chunk = chunkSlice.chunk;
86+
const pos = this.rga.pos(chunk);
87+
return pos + chunkSlice.off;
88+
}
89+
90+
// ---------------------------------------------------------------- Printable
91+
92+
public toString(tab: string = ''): string {
93+
const str = this.str();
94+
const truncate = str.length > 32;
95+
const text = JSON.stringify(truncate ? str.slice(0, 32) : str) + (truncate ? ' …' : '');
96+
const startFormatted = this.start.toString(tab, true);
97+
const range =
98+
this.start.cmp(this.end) === 0 ? startFormatted : `${startFormatted}${this.end.toString(tab, true)}`;
99+
const header = `${this.constructor.name} ${range} ${text}`;
100+
const marks = this.marks();
101+
const markKeys = Object.keys(marks);
102+
return (
103+
header +
104+
printTree(tab, [
105+
!marks
106+
? null
107+
: (tab) =>
108+
'attributes' +
109+
printTree(
110+
tab,
111+
markKeys.map((key) => () => key + ' = ' + stringify(marks[key])),
112+
),
113+
!this.texts.length
114+
? null
115+
: (tab) =>
116+
'texts' +
117+
printTree(
118+
tab,
119+
this.texts.map((text) => (tab) => text.toString(tab)),
120+
),
121+
])
122+
);
123+
}
124+
}

0 commit comments

Comments
 (0)