Skip to content

Commit a857223

Browse files
committed
feat(json-crdt-extensions): 🎸 lazily construct slice registry on first use
1 parent 8434117 commit a857223

File tree

5 files changed

+128
-132
lines changed

5 files changed

+128
-132
lines changed

‎src/json-crdt-extensions/peritext/editor/Editor.ts‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {EditorSlices} from './EditorSlices';
55
import {next, prev} from 'sonic-forest/lib/util';
66
import {printTree} from 'tree-dump/lib/printTree';
77
import {SliceRegistry} from '../registry/SliceRegistry';
8-
import {registerCommon} from '../registry/registerCommon';
98
import {PersistedSlice} from '../slice/PersistedSlice';
109
import {stringify} from '../../../json-text/stringify';
1110
import {CommonSliceType, type SliceTypeSteps, type SliceType, type SliceTypeStep} from '../slice';
@@ -75,7 +74,7 @@ export class Editor<T = string> implements Printable {
7574
/**
7675
* The registry holds definitions of detailed behavior of various slice tags.
7776
*/
78-
public readonly registry: SliceRegistry;
77+
public registry: SliceRegistry | undefined;
7978

8079
/**
8180
* Formatting basic inline formatting which will be applied to the next
@@ -93,14 +92,17 @@ export class Editor<T = string> implements Printable {
9392
public readonly newSliceConfig = new ValueSyncStore<NewSliceConfig | undefined>(void 0);
9493

9594
constructor(public readonly txt: Peritext<T>) {
96-
const registry = this.registry = new SliceRegistry();
97-
registerCommon(registry); // TODO: figure out a better place to put this
98-
9995
this.saved = new EditorSlices(txt, txt.savedSlices);
10096
this.extra = new EditorSlices(txt, txt.extraSlices);
10197
this.local = new EditorSlices(txt, txt.localSlices);
10298
}
10399

100+
public getRegistry(): SliceRegistry {
101+
let registry = this.registry;
102+
if (!registry) this.registry = registry = SliceRegistry.withCommon();
103+
return registry;
104+
}
105+
104106
public text(): string {
105107
return this.txt.strApi().view();
106108
}
@@ -836,7 +838,7 @@ export class Editor<T = string> implements Printable {
836838
}
837839

838840
public getContainerPath(steps: SliceTypeSteps): SliceTypeSteps {
839-
const registry = this.registry;
841+
const registry = this.getRegistry();
840842
const length = steps.length;
841843
for (let i = length - 1; i >= 0; i--) {
842844
const step = steps[i];
@@ -859,7 +861,7 @@ export class Editor<T = string> implements Printable {
859861
const disc1 = Array.isArray(step1) ? step1[1] : 0;
860862
const disc2 = Array.isArray(step2) ? step2[1] : 0;
861863
if (tag1 !== tag2 || disc1 !== disc2) return i - 1;
862-
if (!this.registry.isContainer(tag1)) return i - 1;
864+
if (!this.getRegistry().isContainer(tag1)) return i - 1;
863865
}
864866
return min;
865867
}
@@ -1223,7 +1225,7 @@ export class Editor<T = string> implements Printable {
12231225
tab,
12241226
[...this.cursors()].map((cursor) => (tab) => cursor.toString(tab)),
12251227
),
1226-
(tab) => this.registry.toString(tab),
1228+
(tab) => this.getRegistry().toString(tab),
12271229
pending ? (() => `pending ${stringify(pendingFormatted)}`) : null,
12281230
])
12291231
);

‎src/json-crdt-extensions/peritext/registry/SliceRegistry.ts‎

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import {SliceBehavior, type SliceTypeCon} from '../slice/constants';
1+
import {s} from '../../../json-crdt-patch';
2+
import {SliceBehavior, SliceTypeCon as TAG} from '../slice/constants';
23
import {CommonSliceType} from '../slice';
34
import {SliceRegistryEntry} from './SliceRegistryEntry';
45
import {printTree} from 'tree-dump/lib/printTree';
56
import type {PeritextMlElement} from '../block/types';
67
import type {JsonMlElement} from 'very-small-parser/lib/html/json-ml/types';
78
import type {FromHtmlConverter, ToHtmlConverter} from './types';
89
import type {Printable} from 'tree-dump';
10+
import type {JsonNodeView} from '../../../json-crdt/nodes';
11+
import type {SchemaToJsonNode} from '../../../json-crdt/schema/types';
912

10-
export type TypeTag = SliceTypeCon | number | string;
13+
const undefSchema = s.con(undefined);
14+
15+
export type TypeTag = TAG | number | string;
1116

1217
/**
1318
* Slice registry contains a record of possible inline an block formatting
@@ -18,6 +23,114 @@ export type TypeTag = SliceTypeCon | number | string;
1823
* `/slices` directory.
1924
*/
2025
export class SliceRegistry implements Printable {
26+
/**
27+
* Creates a new slice registry with common tag registered.
28+
*/
29+
public static readonly withCommon = (): SliceRegistry => {
30+
const registry = new SliceRegistry();
31+
// --------------------------------------- Inline elements with "One" behavior
32+
const i0 = <Tag extends TypeTag = TypeTag>(
33+
tag: Tag,
34+
fromHtml?: SliceRegistryEntry<SliceBehavior.One, Tag, typeof undefSchema>['fromHtml'],
35+
): void => {
36+
registry.add(new SliceRegistryEntry(SliceBehavior.One, tag, undefSchema, false, void 0, fromHtml));
37+
};
38+
const i1 = <Tag extends TypeTag = TypeTag>(tag: Tag, htmlTags: string[]): void => {
39+
const fromHtml = {} as Record<any, any>;
40+
for (const htmlTag of htmlTags) fromHtml[htmlTag] = () => [tag, null];
41+
i0(tag, fromHtml);
42+
};
43+
i1(TAG.i, ['i', 'em']);
44+
i1(TAG.b, ['b', 'strong']);
45+
i1(TAG.s, ['s', 'strike']);
46+
i0(TAG.u);
47+
i0(TAG.code);
48+
i0(TAG.mark);
49+
i0(TAG.kbd);
50+
i0(TAG.del);
51+
i0(TAG.ins);
52+
i0(TAG.sup);
53+
i0(TAG.sub);
54+
i0(TAG.math);
55+
56+
// -------------------------------------- Inline elements with "Many" behavior
57+
const aSchema = s.obj({}, {
58+
href: s.str<string>(''),
59+
title: s.str<string>(''),
60+
});
61+
registry.add(
62+
new SliceRegistryEntry(SliceBehavior.Many, TAG.a, aSchema, false, void 0, {
63+
a: (jsonml) => {
64+
const attr = jsonml[1] || {};
65+
const data: JsonNodeView<SchemaToJsonNode<typeof aSchema>> = {
66+
href: attr.href ?? '',
67+
title: attr.title ?? '',
68+
};
69+
return [TAG.a, {data, inline: true}] as PeritextMlElement<TAG.a, any, true>;
70+
},
71+
}),
72+
);
73+
74+
// TODO: add more default annotations with "Many" behavior
75+
// comment = SliceTypeCon.comment,
76+
// font = SliceTypeCon.font,
77+
// col = SliceTypeCon.col,
78+
// bg = SliceTypeCon.bg,
79+
// hidden = SliceTypeCon.hidden,
80+
// footnote = SliceTypeCon.footnote,
81+
// ref = SliceTypeCon.ref,
82+
// iaside = SliceTypeCon.iaside,
83+
// iembed = SliceTypeCon.iembed,
84+
// bookmark = SliceTypeCon.bookmark,
85+
86+
// ------------------------------------- Block elements with "Marker" behavior
87+
const commonBlockSchema = s.obj(
88+
{},
89+
{
90+
indent: s.con(0),
91+
align: s.str<'left' | 'center' | 'right' | 'justify'>('left'),
92+
},
93+
);
94+
const b0 = <Tag extends TypeTag = TypeTag>(tag: Tag, container: boolean) => {
95+
registry.add(new SliceRegistryEntry(SliceBehavior.Marker, tag, commonBlockSchema, container));
96+
};
97+
b0(TAG.p, false);
98+
b0(TAG.blockquote, true);
99+
b0(TAG.codeblock, false);
100+
b0(TAG.pre, false);
101+
b0(TAG.ul, true);
102+
b0(TAG.ol, true);
103+
b0(TAG.tl, true);
104+
b0(TAG.ol, true);
105+
b0(TAG.li, true);
106+
b0(TAG.h1, false);
107+
b0(TAG.h2, false);
108+
b0(TAG.h3, false);
109+
b0(TAG.h4, false);
110+
b0(TAG.h5, false);
111+
b0(TAG.h6, false);
112+
b0(TAG.title, false);
113+
b0(TAG.subtitle, false);
114+
// b0(TAG.br, false);
115+
// b0(TAG.nl, false);
116+
// b0(TAG.hr, false);
117+
// b0(TAG.page, false);
118+
// b0(TAG.aside, true);
119+
// b0(TAG.embed, false);
120+
// b0(TAG.column, true);
121+
// b0(TAG.contents, true);
122+
// b0(TAG.table, true);
123+
// b0(TAG.row, true);
124+
// b0(TAG.cell, true);
125+
// b0(TAG.collapselist, true);
126+
// b0(TAG.collapse, true);
127+
// b0(TAG.note, true);
128+
// b0(TAG.mathblock, false);
129+
130+
return registry;
131+
};
132+
133+
21134
private map: Map<TypeTag, SliceRegistryEntry> = new Map();
22135
private _fromHtml: Map<string, [entry: SliceRegistryEntry, converter: FromHtmlConverter][]> = new Map();
23136

@@ -38,7 +151,7 @@ export class SliceRegistry implements Printable {
38151
_fromHtml.set(htmlTag, converters);
39152
}
40153
}
41-
const tagStr = CommonSliceType[tag as SliceTypeCon];
154+
const tagStr = CommonSliceType[tag as TAG];
42155
if (tagStr && typeof tagStr === 'string') _fromHtml.set(tagStr, [[entry, () => [tag, null]]]);
43156
}
44157

‎src/json-crdt-extensions/peritext/registry/registerCommon.ts‎

Lines changed: 0 additions & 117 deletions
This file was deleted.

‎src/json-crdt-extensions/peritext/transfer/PeritextDataTransfer.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export type PeritextDataTransferMarkdownExportTools = typeof import('./export-ma
1414
export type PeritextDataTransferMarkdownImportTools = typeof import('./import-markdown');
1515

1616
export interface PeritextDataTransferOpts {
17-
registry: SliceRegistry;
1817
htmlExport?: PeritextDataTransferHtmlExportTools;
1918
htmlImport?: PeritextDataTransferHtmlImportTools;
2019
mdExport?: PeritextDataTransferMarkdownExportTools;
@@ -131,7 +130,7 @@ export class PeritextDataTransfer<T = string> {
131130
}
132131

133132
private _imp<D>(pos: number, data: D, transform: (data: D, registry: SliceRegistry) => PeritextMlNode): number {
134-
const registry = this.opts.registry;
133+
const registry = this.txt.editor.getRegistry();
135134
const json = transform(data, registry);
136135
return this.fromJson(pos, json);
137136
}
@@ -190,7 +189,7 @@ export class PeritextDataTransfer<T = string> {
190189
return range.start.viewPos();
191190
};
192191
if (html) {
193-
const [view, style] = this.htmlI().importHtml(html, txt.editor.registry);
192+
const [view, style] = this.htmlI().importHtml(html, txt.editor.getRegistry());
194193
if (style) {
195194
txt.editor.importStyle(range, style);
196195
return 0;

‎src/json-crdt-extensions/peritext/transfer/create.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export const create = (txt: Peritext) => {
1111
htmlImport,
1212
mdExport,
1313
mdImport,
14-
registry: txt.editor.registry,
1514
});
1615
return transfer;
1716
};

0 commit comments

Comments
 (0)