@doxi/core 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/collab/port.d.ts +46 -0
- package/dist/collab/port.d.ts.map +1 -0
- package/dist/collab/port.js +11 -0
- package/dist/collab/port.js.map +1 -0
- package/dist/commands/block-commands.d.ts +62 -0
- package/dist/commands/block-commands.d.ts.map +1 -0
- package/dist/commands/block-commands.js +208 -0
- package/dist/commands/block-commands.js.map +1 -0
- package/dist/commands/command.d.ts +13 -0
- package/dist/commands/command.d.ts.map +1 -0
- package/dist/commands/command.js +13 -0
- package/dist/commands/command.js.map +1 -0
- package/dist/commands/edit-commands.d.ts +5 -0
- package/dist/commands/edit-commands.d.ts.map +1 -0
- package/dist/commands/edit-commands.js +147 -0
- package/dist/commands/edit-commands.js.map +1 -0
- package/dist/commands/image-commands.d.ts +31 -0
- package/dist/commands/image-commands.d.ts.map +1 -0
- package/dist/commands/image-commands.js +130 -0
- package/dist/commands/image-commands.js.map +1 -0
- package/dist/commands/index.d.ts +11 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +11 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/keymap.d.ts +34 -0
- package/dist/commands/keymap.d.ts.map +1 -0
- package/dist/commands/keymap.js +84 -0
- package/dist/commands/keymap.js.map +1 -0
- package/dist/commands/link-commands.d.ts +54 -0
- package/dist/commands/link-commands.d.ts.map +1 -0
- package/dist/commands/link-commands.js +151 -0
- package/dist/commands/link-commands.js.map +1 -0
- package/dist/commands/list-commands.d.ts +42 -0
- package/dist/commands/list-commands.d.ts.map +1 -0
- package/dist/commands/list-commands.js +316 -0
- package/dist/commands/list-commands.js.map +1 -0
- package/dist/commands/mark-commands.d.ts +53 -0
- package/dist/commands/mark-commands.d.ts.map +1 -0
- package/dist/commands/mark-commands.js +181 -0
- package/dist/commands/mark-commands.js.map +1 -0
- package/dist/commands/selection-commands.d.ts +3 -0
- package/dist/commands/selection-commands.d.ts.map +1 -0
- package/dist/commands/selection-commands.js +11 -0
- package/dist/commands/selection-commands.js.map +1 -0
- package/dist/commands/table-commands.d.ts +109 -0
- package/dist/commands/table-commands.d.ts.map +1 -0
- package/dist/commands/table-commands.js +884 -0
- package/dist/commands/table-commands.js.map +1 -0
- package/dist/history/history.d.ts +40 -0
- package/dist/history/history.d.ts.map +1 -0
- package/dist/history/history.js +139 -0
- package/dist/history/history.js.map +1 -0
- package/dist/history/index.d.ts +2 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +2 -0
- package/dist/history/index.js.map +1 -0
- package/dist/html/index.d.ts +3 -0
- package/dist/html/index.d.ts.map +1 -0
- package/dist/html/index.js +3 -0
- package/dist/html/index.js.map +1 -0
- package/dist/html/parse.d.ts +4 -0
- package/dist/html/parse.d.ts.map +1 -0
- package/dist/html/parse.js +0 -0
- package/dist/html/parse.js.map +1 -0
- package/dist/html/serialize.d.ts +4 -0
- package/dist/html/serialize.d.ts.map +1 -0
- package/dist/html/serialize.js +75 -0
- package/dist/html/serialize.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/index.d.ts +6 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +6 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/layout-engine.d.ts +20 -0
- package/dist/layout/layout-engine.d.ts.map +1 -0
- package/dist/layout/layout-engine.js +198 -0
- package/dist/layout/layout-engine.js.map +1 -0
- package/dist/layout/measure.d.ts +9 -0
- package/dist/layout/measure.d.ts.map +1 -0
- package/dist/layout/measure.js +37 -0
- package/dist/layout/measure.js.map +1 -0
- package/dist/layout/split-paragraph.d.ts +28 -0
- package/dist/layout/split-paragraph.d.ts.map +1 -0
- package/dist/layout/split-paragraph.js +122 -0
- package/dist/layout/split-paragraph.js.map +1 -0
- package/dist/layout/split-table.d.ts +46 -0
- package/dist/layout/split-table.d.ts.map +1 -0
- package/dist/layout/split-table.js +84 -0
- package/dist/layout/split-table.js.map +1 -0
- package/dist/layout/types.d.ts +73 -0
- package/dist/layout/types.d.ts.map +1 -0
- package/dist/layout/types.js +36 -0
- package/dist/layout/types.js.map +1 -0
- package/dist/layout/widow-orphan.d.ts +15 -0
- package/dist/layout/widow-orphan.d.ts.map +1 -0
- package/dist/layout/widow-orphan.js +14 -0
- package/dist/layout/widow-orphan.js.map +1 -0
- package/dist/model/content-expr.d.ts +32 -0
- package/dist/model/content-expr.d.ts.map +1 -0
- package/dist/model/content-expr.js +106 -0
- package/dist/model/content-expr.js.map +1 -0
- package/dist/model/fragment.d.ts +17 -0
- package/dist/model/fragment.d.ts.map +1 -0
- package/dist/model/fragment.js +44 -0
- package/dist/model/fragment.js.map +1 -0
- package/dist/model/index.d.ts +10 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +10 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/mark.d.ts +35 -0
- package/dist/model/mark.d.ts.map +1 -0
- package/dist/model/mark.js +89 -0
- package/dist/model/mark.js.map +1 -0
- package/dist/model/node-type.d.ts +36 -0
- package/dist/model/node-type.d.ts.map +1 -0
- package/dist/model/node-type.js +14 -0
- package/dist/model/node-type.js.map +1 -0
- package/dist/model/node.d.ts +36 -0
- package/dist/model/node.d.ts.map +1 -0
- package/dist/model/node.js +192 -0
- package/dist/model/node.js.map +1 -0
- package/dist/model/position.d.ts +66 -0
- package/dist/model/position.d.ts.map +1 -0
- package/dist/model/position.js +158 -0
- package/dist/model/position.js.map +1 -0
- package/dist/model/schema.d.ts +28 -0
- package/dist/model/schema.d.ts.map +1 -0
- package/dist/model/schema.js +195 -0
- package/dist/model/schema.js.map +1 -0
- package/dist/model/slice.d.ts +26 -0
- package/dist/model/slice.d.ts.map +1 -0
- package/dist/model/slice.js +56 -0
- package/dist/model/slice.js.map +1 -0
- package/dist/model/table-grid.d.ts +71 -0
- package/dist/model/table-grid.d.ts.map +1 -0
- package/dist/model/table-grid.js +130 -0
- package/dist/model/table-grid.js.map +1 -0
- package/dist/plugin/index.d.ts +3 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +3 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/plugin-key.d.ts +13 -0
- package/dist/plugin/plugin-key.d.ts.map +1 -0
- package/dist/plugin/plugin-key.js +13 -0
- package/dist/plugin/plugin-key.js.map +1 -0
- package/dist/plugin/plugin-state.d.ts +2 -0
- package/dist/plugin/plugin-state.d.ts.map +1 -0
- package/dist/plugin/plugin-state.js +3 -0
- package/dist/plugin/plugin-state.js.map +1 -0
- package/dist/plugin/plugin.d.ts +39 -0
- package/dist/plugin/plugin.d.ts.map +1 -0
- package/dist/plugin/plugin.js +10 -0
- package/dist/plugin/plugin.js.map +1 -0
- package/dist/schema/default.d.ts +163 -0
- package/dist/schema/default.d.ts.map +1 -0
- package/dist/schema/default.js +94 -0
- package/dist/schema/default.js.map +1 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/serialize/index.d.ts +2 -0
- package/dist/serialize/index.d.ts.map +1 -0
- package/dist/serialize/index.js +2 -0
- package/dist/serialize/index.js.map +1 -0
- package/dist/serialize/json.d.ts +15 -0
- package/dist/serialize/json.d.ts.map +1 -0
- package/dist/serialize/json.js +23 -0
- package/dist/serialize/json.js.map +1 -0
- package/dist/state/all-selection.d.ts +11 -0
- package/dist/state/all-selection.d.ts.map +1 -0
- package/dist/state/all-selection.js +17 -0
- package/dist/state/all-selection.js.map +1 -0
- package/dist/state/cell-selection.d.ts +30 -0
- package/dist/state/cell-selection.d.ts.map +1 -0
- package/dist/state/cell-selection.js +38 -0
- package/dist/state/cell-selection.js.map +1 -0
- package/dist/state/editor-state.d.ts +46 -0
- package/dist/state/editor-state.d.ts.map +1 -0
- package/dist/state/editor-state.js +211 -0
- package/dist/state/editor-state.js.map +1 -0
- package/dist/state/index.d.ts +7 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +7 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/node-selection.d.ts +16 -0
- package/dist/state/node-selection.d.ts.map +1 -0
- package/dist/state/node-selection.js +51 -0
- package/dist/state/node-selection.js.map +1 -0
- package/dist/state/selection.d.ts +29 -0
- package/dist/state/selection.d.ts.map +1 -0
- package/dist/state/selection.js +24 -0
- package/dist/state/selection.js.map +1 -0
- package/dist/state/text-selection.d.ts +10 -0
- package/dist/state/text-selection.d.ts.map +1 -0
- package/dist/state/text-selection.js +26 -0
- package/dist/state/text-selection.js.map +1 -0
- package/dist/transform/attr-step.d.ts +16 -0
- package/dist/transform/attr-step.d.ts.map +1 -0
- package/dist/transform/attr-step.js +98 -0
- package/dist/transform/attr-step.js.map +1 -0
- package/dist/transform/index.d.ts +10 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/index.js +10 -0
- package/dist/transform/index.js.map +1 -0
- package/dist/transform/mapping.d.ts +44 -0
- package/dist/transform/mapping.d.ts.map +1 -0
- package/dist/transform/mapping.js +101 -0
- package/dist/transform/mapping.js.map +1 -0
- package/dist/transform/mark-step.d.ts +27 -0
- package/dist/transform/mark-step.d.ts.map +1 -0
- package/dist/transform/mark-step.js +146 -0
- package/dist/transform/mark-step.js.map +1 -0
- package/dist/transform/replace-around-step.d.ts +35 -0
- package/dist/transform/replace-around-step.d.ts.map +1 -0
- package/dist/transform/replace-around-step.js +144 -0
- package/dist/transform/replace-around-step.js.map +1 -0
- package/dist/transform/replace-step.d.ts +17 -0
- package/dist/transform/replace-step.d.ts.map +1 -0
- package/dist/transform/replace-step.js +72 -0
- package/dist/transform/replace-step.js.map +1 -0
- package/dist/transform/replace.d.ts +18 -0
- package/dist/transform/replace.d.ts.map +1 -0
- package/dist/transform/replace.js +132 -0
- package/dist/transform/replace.js.map +1 -0
- package/dist/transform/set-page-meta-step.d.ts +42 -0
- package/dist/transform/set-page-meta-step.d.ts.map +1 -0
- package/dist/transform/set-page-meta-step.js +75 -0
- package/dist/transform/set-page-meta-step.js.map +1 -0
- package/dist/transform/step.d.ts +34 -0
- package/dist/transform/step.d.ts.map +1 -0
- package/dist/transform/step.js +23 -0
- package/dist/transform/step.js.map +1 -0
- package/dist/transform/transaction.d.ts +20 -0
- package/dist/transform/transaction.d.ts.map +1 -0
- package/dist/transform/transaction.js +38 -0
- package/dist/transform/transaction.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/dist/view/cell-drag.d.ts +33 -0
- package/dist/view/cell-drag.d.ts.map +1 -0
- package/dist/view/cell-drag.js +177 -0
- package/dist/view/cell-drag.js.map +1 -0
- package/dist/view/clipboard.d.ts +5 -0
- package/dist/view/clipboard.d.ts.map +1 -0
- package/dist/view/clipboard.js +97 -0
- package/dist/view/clipboard.js.map +1 -0
- package/dist/view/default-renderer.d.ts +3 -0
- package/dist/view/default-renderer.d.ts.map +1 -0
- package/dist/view/default-renderer.js +142 -0
- package/dist/view/default-renderer.js.map +1 -0
- package/dist/view/dom-spec.d.ts +11 -0
- package/dist/view/dom-spec.d.ts.map +1 -0
- package/dist/view/dom-spec.js +32 -0
- package/dist/view/dom-spec.js.map +1 -0
- package/dist/view/editor-view.d.ts +55 -0
- package/dist/view/editor-view.d.ts.map +1 -0
- package/dist/view/editor-view.js +143 -0
- package/dist/view/editor-view.js.map +1 -0
- package/dist/view/image-resize.d.ts +37 -0
- package/dist/view/image-resize.d.ts.map +1 -0
- package/dist/view/image-resize.js +191 -0
- package/dist/view/image-resize.js.map +1 -0
- package/dist/view/index.d.ts +15 -0
- package/dist/view/index.d.ts.map +1 -0
- package/dist/view/index.js +15 -0
- package/dist/view/index.js.map +1 -0
- package/dist/view/input-pipeline.d.ts +24 -0
- package/dist/view/input-pipeline.d.ts.map +1 -0
- package/dist/view/input-pipeline.js +226 -0
- package/dist/view/input-pipeline.js.map +1 -0
- package/dist/view/mutation-observer.d.ts +17 -0
- package/dist/view/mutation-observer.d.ts.map +1 -0
- package/dist/view/mutation-observer.js +62 -0
- package/dist/view/mutation-observer.js.map +1 -0
- package/dist/view/page-slots.d.ts +56 -0
- package/dist/view/page-slots.d.ts.map +1 -0
- package/dist/view/page-slots.js +230 -0
- package/dist/view/page-slots.js.map +1 -0
- package/dist/view/paginator.d.ts +17 -0
- package/dist/view/paginator.d.ts.map +1 -0
- package/dist/view/paginator.js +93 -0
- package/dist/view/paginator.js.map +1 -0
- package/dist/view/print.d.ts +42 -0
- package/dist/view/print.d.ts.map +1 -0
- package/dist/view/print.js +70 -0
- package/dist/view/print.js.map +1 -0
- package/dist/view/reconcile.d.ts +16 -0
- package/dist/view/reconcile.d.ts.map +1 -0
- package/dist/view/reconcile.js +158 -0
- package/dist/view/reconcile.js.map +1 -0
- package/dist/view/renderer.d.ts +31 -0
- package/dist/view/renderer.d.ts.map +1 -0
- package/dist/view/renderer.js +89 -0
- package/dist/view/renderer.js.map +1 -0
- package/dist/view/selection-sync.d.ts +35 -0
- package/dist/view/selection-sync.d.ts.map +1 -0
- package/dist/view/selection-sync.js +324 -0
- package/dist/view/selection-sync.js.map +1 -0
- package/dist/view/table-resize.d.ts +41 -0
- package/dist/view/table-resize.d.ts.map +1 -0
- package/dist/view/table-resize.js +216 -0
- package/dist/view/table-resize.js.map +1 -0
- package/package.json +93 -0
- package/styles/base.css +269 -0
- package/styles/dark.css +36 -0
- package/styles/light.css +13 -0
- package/styles/page.css +93 -0
- package/styles/print-a4.css +87 -0
- package/styles/print.css +88 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Fragment } from './fragment.js';
|
|
2
|
+
import { sameSet } from './mark.js';
|
|
3
|
+
export class DxNode {
|
|
4
|
+
type;
|
|
5
|
+
attrs;
|
|
6
|
+
content;
|
|
7
|
+
marks;
|
|
8
|
+
text;
|
|
9
|
+
nodeSize;
|
|
10
|
+
constructor(type, attrs, content, marks, text) {
|
|
11
|
+
this.type = type;
|
|
12
|
+
this.attrs = attrs;
|
|
13
|
+
this.content = content;
|
|
14
|
+
this.marks = marks;
|
|
15
|
+
this.text = text;
|
|
16
|
+
if (type.isText) {
|
|
17
|
+
if (text === undefined || text.length === 0) {
|
|
18
|
+
throw new RangeError(`DxNode.text: text leaves must have non-empty text`);
|
|
19
|
+
}
|
|
20
|
+
this.nodeSize = text.length;
|
|
21
|
+
}
|
|
22
|
+
else if (type.isAtom) {
|
|
23
|
+
this.nodeSize = 1;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.nodeSize = 2 + content.size;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// ---- accessors ----
|
|
30
|
+
get isText() { return this.type.isText; }
|
|
31
|
+
get isInline() { return this.type.isInline; }
|
|
32
|
+
get isBlock() { return this.type.isBlock; }
|
|
33
|
+
get isLeaf() { return this.type.isLeaf; }
|
|
34
|
+
get isAtom() { return this.type.isAtom; }
|
|
35
|
+
// ---- constructors ----
|
|
36
|
+
static text(type, text, marks = []) {
|
|
37
|
+
if (!type.isText)
|
|
38
|
+
throw new TypeError(`DxNode.text: type ${type.name} is not a text type`);
|
|
39
|
+
return new DxNode(type, {}, Fragment.empty, marks, text);
|
|
40
|
+
}
|
|
41
|
+
// ---- structural ops ----
|
|
42
|
+
copy(content = this.content) {
|
|
43
|
+
if (this.isText) {
|
|
44
|
+
throw new TypeError('DxNode.copy: cannot copy a text leaf with new content');
|
|
45
|
+
}
|
|
46
|
+
return new DxNode(this.type, this.attrs, content, this.marks);
|
|
47
|
+
}
|
|
48
|
+
mark(marks) {
|
|
49
|
+
if (sameSet(marks, this.marks))
|
|
50
|
+
return this;
|
|
51
|
+
if (this.isText)
|
|
52
|
+
return new DxNode(this.type, this.attrs, this.content, marks, this.text);
|
|
53
|
+
return new DxNode(this.type, this.attrs, this.content, marks);
|
|
54
|
+
}
|
|
55
|
+
/** Returns the slice of this node's content between [from, to). */
|
|
56
|
+
cut(from = 0, to = this.nodeSize) {
|
|
57
|
+
if (this.isText) {
|
|
58
|
+
const t = this.text;
|
|
59
|
+
const a = Math.max(0, Math.min(t.length, from));
|
|
60
|
+
const b = Math.max(a, Math.min(t.length, to));
|
|
61
|
+
if (a === 0 && b === t.length)
|
|
62
|
+
return this;
|
|
63
|
+
return new DxNode(this.type, this.attrs, this.content, this.marks, t.slice(a, b));
|
|
64
|
+
}
|
|
65
|
+
if (this.isAtom)
|
|
66
|
+
return this;
|
|
67
|
+
// For non-leaf nodes, [from, to) is in *content* coordinates if from/to are within content.size.
|
|
68
|
+
// We require: 0 <= from <= to <= content.size (callers strip the +2 for open/close).
|
|
69
|
+
const contentSize = this.content.size;
|
|
70
|
+
const a = Math.max(0, Math.min(contentSize, from));
|
|
71
|
+
const b = Math.max(a, Math.min(contentSize, to));
|
|
72
|
+
if (a === 0 && b === contentSize)
|
|
73
|
+
return this;
|
|
74
|
+
return this.copy(cutFragment(this.content, a, b));
|
|
75
|
+
}
|
|
76
|
+
eq(other) {
|
|
77
|
+
if (this === other)
|
|
78
|
+
return true;
|
|
79
|
+
if (this.type !== other.type)
|
|
80
|
+
return false;
|
|
81
|
+
if (!attrsEq(this.attrs, other.attrs))
|
|
82
|
+
return false;
|
|
83
|
+
if (!sameSet(this.marks, other.marks))
|
|
84
|
+
return false;
|
|
85
|
+
if (this.isText)
|
|
86
|
+
return this.text === other.text;
|
|
87
|
+
return fragmentEq(this.content, other.content);
|
|
88
|
+
}
|
|
89
|
+
// ---- serialization ----
|
|
90
|
+
toJSON() {
|
|
91
|
+
const json = { type: this.type.name };
|
|
92
|
+
if (Object.keys(this.attrs).length > 0)
|
|
93
|
+
json.attrs = { ...this.attrs };
|
|
94
|
+
if (this.isText)
|
|
95
|
+
json.text = this.text;
|
|
96
|
+
else if (this.content.size > 0) {
|
|
97
|
+
json.content = this.content.children.map((c) => c.toJSON());
|
|
98
|
+
}
|
|
99
|
+
if (this.marks.length > 0)
|
|
100
|
+
json.marks = this.marks.map((m) => m.toJSON());
|
|
101
|
+
return json;
|
|
102
|
+
}
|
|
103
|
+
static fromJSON(types, json) {
|
|
104
|
+
const type = types[json.type];
|
|
105
|
+
if (!type)
|
|
106
|
+
throw new Error(`DxNode.fromJSON: unknown node type '${json.type}'`);
|
|
107
|
+
const attrs = applyNodeDefaults(type.spec.attrs ?? {}, json.attrs ?? {});
|
|
108
|
+
if (type.isText) {
|
|
109
|
+
if (typeof json.text !== 'string')
|
|
110
|
+
throw new Error(`DxNode.fromJSON: text node missing text`);
|
|
111
|
+
return new DxNode(type, attrs, Fragment.empty, [], json.text);
|
|
112
|
+
}
|
|
113
|
+
const children = (json.content ?? []).map((c) => DxNode.fromJSON(types, c));
|
|
114
|
+
return new DxNode(type, attrs, Fragment.from(children), []);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function attrsEq(a, b) {
|
|
118
|
+
const ak = Object.keys(a), bk = Object.keys(b);
|
|
119
|
+
if (ak.length !== bk.length)
|
|
120
|
+
return false;
|
|
121
|
+
for (const k of ak) {
|
|
122
|
+
if (!(k in b))
|
|
123
|
+
return false;
|
|
124
|
+
if (!Object.is(a[k], b[k]))
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
function fragmentEq(a, b) {
|
|
130
|
+
if (a === b)
|
|
131
|
+
return true;
|
|
132
|
+
if (a.childCount !== b.childCount)
|
|
133
|
+
return false;
|
|
134
|
+
for (let i = 0; i < a.childCount; i++) {
|
|
135
|
+
if (!a.child(i).eq(b.child(i)))
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
function applyNodeDefaults(specs, given) {
|
|
141
|
+
const out = {};
|
|
142
|
+
for (const name of Object.keys(specs)) {
|
|
143
|
+
if (name in given)
|
|
144
|
+
out[name] = given[name];
|
|
145
|
+
else if ('default' in specs[name])
|
|
146
|
+
out[name] = specs[name].default;
|
|
147
|
+
}
|
|
148
|
+
for (const name of Object.keys(given)) {
|
|
149
|
+
if (!(name in specs))
|
|
150
|
+
out[name] = given[name];
|
|
151
|
+
}
|
|
152
|
+
return Object.freeze(out);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Cut a fragment to its content-coordinate sub-range [from, to).
|
|
156
|
+
* Walks children and trims text leaves at the boundary.
|
|
157
|
+
*/
|
|
158
|
+
function cutFragment(frag, from, to) {
|
|
159
|
+
if (from === 0 && to === frag.size)
|
|
160
|
+
return frag;
|
|
161
|
+
if (from === to)
|
|
162
|
+
return Fragment.empty;
|
|
163
|
+
const result = [];
|
|
164
|
+
let offset = 0;
|
|
165
|
+
for (let i = 0; i < frag.childCount; i++) {
|
|
166
|
+
const child = frag.child(i);
|
|
167
|
+
const childSize = child.nodeSize;
|
|
168
|
+
const childEnd = offset + childSize;
|
|
169
|
+
if (childEnd <= from) {
|
|
170
|
+
offset = childEnd;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (offset >= to)
|
|
174
|
+
break;
|
|
175
|
+
const localFrom = Math.max(0, from - offset);
|
|
176
|
+
const localTo = Math.min(childSize, to - offset);
|
|
177
|
+
if (localFrom === 0 && localTo === childSize) {
|
|
178
|
+
result.push(child);
|
|
179
|
+
}
|
|
180
|
+
else if (child.isText) {
|
|
181
|
+
result.push(child.cut(localFrom, localTo));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// Non-text inline atom: take whole-or-nothing per spec.
|
|
185
|
+
// (Non-leaf block cuts strip the open/close offsets via -1.)
|
|
186
|
+
result.push(child.cut(localFrom - 1, localTo - 1));
|
|
187
|
+
}
|
|
188
|
+
offset = childEnd;
|
|
189
|
+
}
|
|
190
|
+
return Fragment.from(result);
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/model/node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { DxNode } from './node.js';
|
|
2
|
+
/**
|
|
3
|
+
* ResolvedPos is the path-aware view of a flat integer position.
|
|
4
|
+
*
|
|
5
|
+
* Walks from doc root to the closest ancestor whose content contains pos
|
|
6
|
+
* (sticky-left: positions on a block boundary belong to the parent).
|
|
7
|
+
*
|
|
8
|
+
* Internal arrays:
|
|
9
|
+
* ancestors[0] is the root; ancestors[depth] is the closest ancestor.
|
|
10
|
+
* indices[d] is the index inside ancestors[d-1] of ancestors[d]; indices[0] is unused.
|
|
11
|
+
* bases[d] is the absolute position of the start of ancestors[d]'s content.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ResolvedPos {
|
|
14
|
+
readonly pos: number;
|
|
15
|
+
private readonly ancestors;
|
|
16
|
+
private readonly indices;
|
|
17
|
+
private readonly bases;
|
|
18
|
+
constructor(pos: number, ancestors: ReadonlyArray<DxNode>, indices: ReadonlyArray<number>, bases: ReadonlyArray<number>);
|
|
19
|
+
/** Depth of the closest ancestor whose content contains pos. */
|
|
20
|
+
get depth(): number;
|
|
21
|
+
node(depth?: number): DxNode;
|
|
22
|
+
get parent(): DxNode;
|
|
23
|
+
/**
|
|
24
|
+
* For depth 0, returns 0 (root has no parent index).
|
|
25
|
+
* For depth > 0, returns the index of ancestors[depth] inside ancestors[depth-1].
|
|
26
|
+
*/
|
|
27
|
+
index(depth?: number): number;
|
|
28
|
+
/** Offset of pos inside the parent's content. */
|
|
29
|
+
get parentOffset(): number;
|
|
30
|
+
/** Position right before the ancestor at depth d (its [open] boundary). */
|
|
31
|
+
before(depth: number): number;
|
|
32
|
+
/** Position right after the ancestor at depth d (its [close] boundary). */
|
|
33
|
+
after(depth: number): number;
|
|
34
|
+
/** First in-content position of the ancestor at depth d. */
|
|
35
|
+
start(depth?: number): number;
|
|
36
|
+
/** Last in-content position of the ancestor at depth d. */
|
|
37
|
+
end(depth?: number): number;
|
|
38
|
+
private checkDepth;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Return the depth at which the "top-level block" — the block sitting
|
|
42
|
+
* directly inside the doc root or inside a table_cell — lives. The returned
|
|
43
|
+
* depth `d` is such that `rp.node(d)` is that block and `rp.node(d - 1)`
|
|
44
|
+
* is its block container.
|
|
45
|
+
*
|
|
46
|
+
* - doc > paragraph (depth 1): returns 1.
|
|
47
|
+
* - doc > table > table_row > table_cell > paragraph (depth 4): returns 4
|
|
48
|
+
* (the paragraph is the cell's top-level block).
|
|
49
|
+
* - doc > list > list_item > paragraph (depth 3): returns 1 (the list is
|
|
50
|
+
* the top-level block; list_item is a structural wrapper).
|
|
51
|
+
* - doc > blockquote > paragraph (depth 2): returns 1 (blockquote is the
|
|
52
|
+
* top-level block, matching the depth-1-only semantics v0.3a commands
|
|
53
|
+
* had inside blockquotes).
|
|
54
|
+
*
|
|
55
|
+
* Heuristic: a node is treated as a "cell-like block container" only if its
|
|
56
|
+
* spec carries `isolating: true` (today: table_cell). The doc root always
|
|
57
|
+
* qualifies. This keeps blockquote (a block in its own right) as a top
|
|
58
|
+
* block — commands operate ON the blockquote, not on its inner paragraph —
|
|
59
|
+
* which preserves the v0.3a semantics every existing test relies on.
|
|
60
|
+
*
|
|
61
|
+
* Returns 0 only when `rp.depth === 0` (cursor sits between root children
|
|
62
|
+
* with no resolvable ancestor block) — callers must treat that as a no-op.
|
|
63
|
+
*/
|
|
64
|
+
export declare function findTopBlockDepth(rp: ResolvedPos): number;
|
|
65
|
+
export declare function resolve(root: DxNode, pos: number): ResolvedPos;
|
|
66
|
+
//# sourceMappingURL=position.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../src/model/position.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAEvC;;;;;;;;;;GAUG;AACH,qBAAa,WAAW;IAEpB,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAHb,GAAG,EAAE,MAAM,EACH,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC;IAG/C,gEAAgE;IAChE,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,CAAC,KAAK,GAAE,MAAmB,GAAG,MAAM;IAKxC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,GAAE,MAAmB,GAAG,MAAM;IAMzC,iDAAiD;IACjD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,2EAA2E;IAC3E,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQ7B,2EAA2E;IAC3E,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQ5B,4DAA4D;IAC5D,KAAK,CAAC,KAAK,GAAE,MAAmB,GAAG,MAAM;IAKzC,2DAA2D;IAC3D,GAAG,CAAC,KAAK,GAAE,MAAmB,GAAG,MAAM;IAKvC,OAAO,CAAC,UAAU;CAKnB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAazD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAuC9D"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResolvedPos is the path-aware view of a flat integer position.
|
|
3
|
+
*
|
|
4
|
+
* Walks from doc root to the closest ancestor whose content contains pos
|
|
5
|
+
* (sticky-left: positions on a block boundary belong to the parent).
|
|
6
|
+
*
|
|
7
|
+
* Internal arrays:
|
|
8
|
+
* ancestors[0] is the root; ancestors[depth] is the closest ancestor.
|
|
9
|
+
* indices[d] is the index inside ancestors[d-1] of ancestors[d]; indices[0] is unused.
|
|
10
|
+
* bases[d] is the absolute position of the start of ancestors[d]'s content.
|
|
11
|
+
*/
|
|
12
|
+
export class ResolvedPos {
|
|
13
|
+
pos;
|
|
14
|
+
ancestors;
|
|
15
|
+
indices;
|
|
16
|
+
bases;
|
|
17
|
+
constructor(pos, ancestors, indices, bases) {
|
|
18
|
+
this.pos = pos;
|
|
19
|
+
this.ancestors = ancestors;
|
|
20
|
+
this.indices = indices;
|
|
21
|
+
this.bases = bases;
|
|
22
|
+
}
|
|
23
|
+
/** Depth of the closest ancestor whose content contains pos. */
|
|
24
|
+
get depth() {
|
|
25
|
+
return this.ancestors.length - 1;
|
|
26
|
+
}
|
|
27
|
+
node(depth = this.depth) {
|
|
28
|
+
this.checkDepth(depth);
|
|
29
|
+
return this.ancestors[depth];
|
|
30
|
+
}
|
|
31
|
+
get parent() {
|
|
32
|
+
return this.node(this.depth);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* For depth 0, returns 0 (root has no parent index).
|
|
36
|
+
* For depth > 0, returns the index of ancestors[depth] inside ancestors[depth-1].
|
|
37
|
+
*/
|
|
38
|
+
index(depth = this.depth) {
|
|
39
|
+
this.checkDepth(depth);
|
|
40
|
+
if (depth === 0)
|
|
41
|
+
return 0;
|
|
42
|
+
return this.indices[depth];
|
|
43
|
+
}
|
|
44
|
+
/** Offset of pos inside the parent's content. */
|
|
45
|
+
get parentOffset() {
|
|
46
|
+
return this.pos - this.bases[this.depth];
|
|
47
|
+
}
|
|
48
|
+
/** Position right before the ancestor at depth d (its [open] boundary). */
|
|
49
|
+
before(depth) {
|
|
50
|
+
this.checkDepth(depth);
|
|
51
|
+
if (depth === 0) {
|
|
52
|
+
throw new RangeError('ResolvedPos.before: cannot take before(0) (root has no boundary)');
|
|
53
|
+
}
|
|
54
|
+
return this.bases[depth] - 1;
|
|
55
|
+
}
|
|
56
|
+
/** Position right after the ancestor at depth d (its [close] boundary). */
|
|
57
|
+
after(depth) {
|
|
58
|
+
this.checkDepth(depth);
|
|
59
|
+
if (depth === 0) {
|
|
60
|
+
throw new RangeError('ResolvedPos.after: cannot take after(0) (root has no boundary)');
|
|
61
|
+
}
|
|
62
|
+
return this.bases[depth] + this.ancestors[depth].content.size + 1;
|
|
63
|
+
}
|
|
64
|
+
/** First in-content position of the ancestor at depth d. */
|
|
65
|
+
start(depth = this.depth) {
|
|
66
|
+
this.checkDepth(depth);
|
|
67
|
+
return depth === 0 ? 0 : this.bases[depth];
|
|
68
|
+
}
|
|
69
|
+
/** Last in-content position of the ancestor at depth d. */
|
|
70
|
+
end(depth = this.depth) {
|
|
71
|
+
this.checkDepth(depth);
|
|
72
|
+
return this.start(depth) + this.ancestors[depth].content.size;
|
|
73
|
+
}
|
|
74
|
+
checkDepth(d) {
|
|
75
|
+
if (d < 0 || d > this.depth) {
|
|
76
|
+
throw new RangeError(`ResolvedPos: depth ${d} out of range [0, ${this.depth}]`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Return the depth at which the "top-level block" — the block sitting
|
|
82
|
+
* directly inside the doc root or inside a table_cell — lives. The returned
|
|
83
|
+
* depth `d` is such that `rp.node(d)` is that block and `rp.node(d - 1)`
|
|
84
|
+
* is its block container.
|
|
85
|
+
*
|
|
86
|
+
* - doc > paragraph (depth 1): returns 1.
|
|
87
|
+
* - doc > table > table_row > table_cell > paragraph (depth 4): returns 4
|
|
88
|
+
* (the paragraph is the cell's top-level block).
|
|
89
|
+
* - doc > list > list_item > paragraph (depth 3): returns 1 (the list is
|
|
90
|
+
* the top-level block; list_item is a structural wrapper).
|
|
91
|
+
* - doc > blockquote > paragraph (depth 2): returns 1 (blockquote is the
|
|
92
|
+
* top-level block, matching the depth-1-only semantics v0.3a commands
|
|
93
|
+
* had inside blockquotes).
|
|
94
|
+
*
|
|
95
|
+
* Heuristic: a node is treated as a "cell-like block container" only if its
|
|
96
|
+
* spec carries `isolating: true` (today: table_cell). The doc root always
|
|
97
|
+
* qualifies. This keeps blockquote (a block in its own right) as a top
|
|
98
|
+
* block — commands operate ON the blockquote, not on its inner paragraph —
|
|
99
|
+
* which preserves the v0.3a semantics every existing test relies on.
|
|
100
|
+
*
|
|
101
|
+
* Returns 0 only when `rp.depth === 0` (cursor sits between root children
|
|
102
|
+
* with no resolvable ancestor block) — callers must treat that as a no-op.
|
|
103
|
+
*/
|
|
104
|
+
export function findTopBlockDepth(rp) {
|
|
105
|
+
// Walk from the deepest ancestor outward, returning the first depth whose
|
|
106
|
+
// parent is a "cell-like" container — the doc root (always) or any node
|
|
107
|
+
// whose spec sets `isolating: true` (today: table_cell). That's the
|
|
108
|
+
// innermost place where a block "lives at the top level of its container".
|
|
109
|
+
for (let d = rp.depth; d >= 1; d--) {
|
|
110
|
+
const parent = rp.node(d - 1);
|
|
111
|
+
const parentSpec = parent.type.spec;
|
|
112
|
+
if (parent.type.name === 'doc' || parentSpec.isolating === true) {
|
|
113
|
+
return d;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return rp.depth >= 1 ? 1 : 0;
|
|
117
|
+
}
|
|
118
|
+
export function resolve(root, pos) {
|
|
119
|
+
if (pos < 0 || pos > root.content.size) {
|
|
120
|
+
throw new RangeError(`resolve: position ${pos} out of range [0, ${root.content.size}]`);
|
|
121
|
+
}
|
|
122
|
+
const ancestors = [root];
|
|
123
|
+
const indices = [0];
|
|
124
|
+
const bases = [0];
|
|
125
|
+
let node = root;
|
|
126
|
+
let base = 0;
|
|
127
|
+
outer: while (!node.isLeaf) {
|
|
128
|
+
const localPos = pos - base;
|
|
129
|
+
let offset = 0;
|
|
130
|
+
for (let i = 0; i < node.content.childCount; i++) {
|
|
131
|
+
const child = node.content.child(i);
|
|
132
|
+
const childSize = child.nodeSize;
|
|
133
|
+
if (localPos < offset + 1) {
|
|
134
|
+
// Position is on the boundary before this child; current node stays as parent (sticky-left).
|
|
135
|
+
break outer;
|
|
136
|
+
}
|
|
137
|
+
const insideStart = offset + 1;
|
|
138
|
+
const insideEnd = insideStart + child.content.size;
|
|
139
|
+
if (localPos < insideEnd + 1) {
|
|
140
|
+
if (child.isLeaf) {
|
|
141
|
+
// Treat leaf as terminal; parent stays as current node.
|
|
142
|
+
break outer;
|
|
143
|
+
}
|
|
144
|
+
const childBase = base + insideStart;
|
|
145
|
+
ancestors.push(child);
|
|
146
|
+
indices.push(i);
|
|
147
|
+
bases.push(childBase);
|
|
148
|
+
node = child;
|
|
149
|
+
base = childBase;
|
|
150
|
+
continue outer;
|
|
151
|
+
}
|
|
152
|
+
offset += childSize;
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
return new ResolvedPos(pos, ancestors, indices, bases);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=position.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.js","sourceRoot":"","sources":["../../src/model/position.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Fragment } from './fragment.js';
|
|
2
|
+
import type { Mark } from './mark.js';
|
|
3
|
+
import { MarkType, type MarkSpec } from './mark.js';
|
|
4
|
+
import { DxNode } from './node.js';
|
|
5
|
+
import { NodeType, type NodeSpec } from './node-type.js';
|
|
6
|
+
export interface SchemaSpec<N extends Record<string, NodeSpec> = Record<string, NodeSpec>, M extends Record<string, MarkSpec> = Record<string, MarkSpec>> {
|
|
7
|
+
nodes: N;
|
|
8
|
+
marks: M;
|
|
9
|
+
}
|
|
10
|
+
type NodeTypeMap<N extends Record<string, NodeSpec>> = {
|
|
11
|
+
readonly [K in keyof N]: NodeType;
|
|
12
|
+
};
|
|
13
|
+
type MarkTypeMap<M extends Record<string, MarkSpec>> = {
|
|
14
|
+
readonly [K in keyof M]: MarkType;
|
|
15
|
+
};
|
|
16
|
+
export declare class Schema<N extends Record<string, NodeSpec> = Record<string, NodeSpec>, M extends Record<string, MarkSpec> = Record<string, MarkSpec>> {
|
|
17
|
+
readonly nodes: NodeTypeMap<N>;
|
|
18
|
+
readonly marks: MarkTypeMap<M>;
|
|
19
|
+
private readonly compiled;
|
|
20
|
+
constructor(spec: SchemaSpec<N, M>);
|
|
21
|
+
text(text: string, marks?: ReadonlyArray<Mark>): DxNode;
|
|
22
|
+
node(name: keyof N | string, attrs: Record<string, unknown> | null, content?: ReadonlyArray<DxNode> | Fragment | null, marks?: ReadonlyArray<Mark>): DxNode;
|
|
23
|
+
/** Validates the content of a node (and recursively). Throws on mismatch. */
|
|
24
|
+
validateNode(node: DxNode): void;
|
|
25
|
+
private findTextType;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/model/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAA;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAExD,MAAM,WAAW,UAAU,CACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAC7D,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IAE7D,KAAK,EAAE,CAAC,CAAA;IACR,KAAK,EAAE,CAAC,CAAA;CACT;AAED,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ;CAAE,CAAA;AAC5F,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ;CAAE,CAAA;AAS5F,qBAAa,MAAM,CACjB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAC7D,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IAE7D,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAE9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;gBAE5C,IAAI,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;IA2ClC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,aAAa,CAAC,IAAI,CAAM,GAAG,MAAM;IAK3D,IAAI,CACF,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACrC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,QAAQ,GAAG,IAAI,EACjD,KAAK,GAAE,aAAa,CAAC,IAAI,CAAM,GAC9B,MAAM;IAWT,6EAA6E;IAC7E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAahC,OAAO,CAAC,YAAY;CAOrB"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { parseContentExpr } from './content-expr.js';
|
|
2
|
+
import { Fragment } from './fragment.js';
|
|
3
|
+
import { MarkType } from './mark.js';
|
|
4
|
+
import { DxNode } from './node.js';
|
|
5
|
+
import { NodeType } from './node-type.js';
|
|
6
|
+
export class Schema {
|
|
7
|
+
nodes;
|
|
8
|
+
marks;
|
|
9
|
+
compiled;
|
|
10
|
+
constructor(spec) {
|
|
11
|
+
const nodeTypes = {};
|
|
12
|
+
for (const name of Object.keys(spec.nodes)) {
|
|
13
|
+
nodeTypes[name] = new NodeType(name, spec.nodes[name]);
|
|
14
|
+
}
|
|
15
|
+
const groups = new Map();
|
|
16
|
+
for (const name of Object.keys(nodeTypes)) {
|
|
17
|
+
const groupSpec = nodeTypes[name].spec.group;
|
|
18
|
+
if (!groupSpec)
|
|
19
|
+
continue;
|
|
20
|
+
for (const g of groupSpec.split(/\s+/)) {
|
|
21
|
+
if (!g)
|
|
22
|
+
continue;
|
|
23
|
+
if (!groups.has(g))
|
|
24
|
+
groups.set(g, []);
|
|
25
|
+
groups.get(g).push(nodeTypes[name]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const compiled = new Map();
|
|
29
|
+
for (const name of Object.keys(nodeTypes)) {
|
|
30
|
+
const type = nodeTypes[name];
|
|
31
|
+
const source = type.spec.content ?? '';
|
|
32
|
+
const expr = parseContentExpr(source);
|
|
33
|
+
validateExprReferences(expr, nodeTypes, groups);
|
|
34
|
+
const resolveName = (n) => {
|
|
35
|
+
if (n in nodeTypes)
|
|
36
|
+
return [nodeTypes[n]];
|
|
37
|
+
const g = groups.get(n);
|
|
38
|
+
return g ? g.slice() : undefined;
|
|
39
|
+
};
|
|
40
|
+
compiled.set(name, { type, contentExpr: expr, resolveName });
|
|
41
|
+
}
|
|
42
|
+
const marks = {};
|
|
43
|
+
const markNames = Object.keys(spec.marks);
|
|
44
|
+
for (let i = 0; i < markNames.length; i++) {
|
|
45
|
+
const n = markNames[i];
|
|
46
|
+
marks[n] = new MarkType(n, i, spec.marks[n]);
|
|
47
|
+
}
|
|
48
|
+
this.nodes = nodeTypes;
|
|
49
|
+
this.marks = marks;
|
|
50
|
+
this.compiled = compiled;
|
|
51
|
+
}
|
|
52
|
+
text(text, marks = []) {
|
|
53
|
+
const t = this.findTextType();
|
|
54
|
+
return DxNode.text(t, text, marks);
|
|
55
|
+
}
|
|
56
|
+
node(name, attrs, content, marks = []) {
|
|
57
|
+
const type = this.nodes[name];
|
|
58
|
+
if (!type)
|
|
59
|
+
throw new Error(`Schema.node: unknown node type '${String(name)}'`);
|
|
60
|
+
const finalAttrs = buildAttrs(type, attrs ?? {});
|
|
61
|
+
const finalContent = content instanceof Fragment
|
|
62
|
+
? content
|
|
63
|
+
: Fragment.from(content ?? null);
|
|
64
|
+
return new DxNode(type, finalAttrs, finalContent, marks);
|
|
65
|
+
}
|
|
66
|
+
/** Validates the content of a node (and recursively). Throws on mismatch. */
|
|
67
|
+
validateNode(node) {
|
|
68
|
+
if (node.isText || node.isAtom)
|
|
69
|
+
return;
|
|
70
|
+
const compiled = this.compiled.get(node.type.name);
|
|
71
|
+
if (!compiled)
|
|
72
|
+
throw new Error(`Schema.validateNode: unknown node type '${node.type.name}'`);
|
|
73
|
+
const ok = matchExpr(compiled.contentExpr, node.content.children, 0, compiled.resolveName);
|
|
74
|
+
if (ok !== node.content.childCount) {
|
|
75
|
+
throw new Error(`Schema.validateNode: content does not match expression for '${node.type.name}'`);
|
|
76
|
+
}
|
|
77
|
+
for (const c of node.content.children)
|
|
78
|
+
this.validateNode(c);
|
|
79
|
+
}
|
|
80
|
+
findTextType() {
|
|
81
|
+
for (const name of Object.keys(this.nodes)) {
|
|
82
|
+
const t = this.nodes[name];
|
|
83
|
+
if (t.isText)
|
|
84
|
+
return t;
|
|
85
|
+
}
|
|
86
|
+
throw new Error('Schema: no text node type declared');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function buildAttrs(type, given) {
|
|
90
|
+
const specs = type.spec.attrs ?? {};
|
|
91
|
+
const out = {};
|
|
92
|
+
for (const name of Object.keys(specs)) {
|
|
93
|
+
if (name in given)
|
|
94
|
+
out[name] = given[name];
|
|
95
|
+
else if ('default' in specs[name])
|
|
96
|
+
out[name] = specs[name].default;
|
|
97
|
+
else
|
|
98
|
+
throw new Error(`Schema.node('${type.name}'): missing attribute '${name}'`);
|
|
99
|
+
}
|
|
100
|
+
for (const name of Object.keys(given)) {
|
|
101
|
+
if (!(name in specs))
|
|
102
|
+
out[name] = given[name];
|
|
103
|
+
}
|
|
104
|
+
return Object.freeze(out);
|
|
105
|
+
}
|
|
106
|
+
function validateExprReferences(expr, types, groups) {
|
|
107
|
+
switch (expr.kind) {
|
|
108
|
+
case 'empty': return;
|
|
109
|
+
case 'name':
|
|
110
|
+
if (!(expr.name in types) && !groups.has(expr.name)) {
|
|
111
|
+
throw new Error(`Schema: unknown content reference '${expr.name}'`);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
case 'seq':
|
|
115
|
+
for (const i of expr.items)
|
|
116
|
+
validateExprReferences(i, types, groups);
|
|
117
|
+
return;
|
|
118
|
+
case 'choice':
|
|
119
|
+
for (const o of expr.options)
|
|
120
|
+
validateExprReferences(o, types, groups);
|
|
121
|
+
return;
|
|
122
|
+
case 'star':
|
|
123
|
+
case 'plus':
|
|
124
|
+
case 'opt':
|
|
125
|
+
validateExprReferences(expr.inner, types, groups);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Greedy match. Returns the number of children consumed if the expression matched at index, else -1.
|
|
131
|
+
* For v0.1 we use a greedy recursive-descent matcher: sufficient for unambiguous schemas.
|
|
132
|
+
* A proper DFA is a Track-B follow-up; current schemas (only the default schema for v0.1) are unambiguous.
|
|
133
|
+
*/
|
|
134
|
+
function matchExpr(expr, children, start, resolve) {
|
|
135
|
+
switch (expr.kind) {
|
|
136
|
+
case 'empty':
|
|
137
|
+
return start === children.length ? start : start;
|
|
138
|
+
case 'name': {
|
|
139
|
+
const accepts = resolve(expr.name);
|
|
140
|
+
if (!accepts)
|
|
141
|
+
return -1;
|
|
142
|
+
const c = children[start];
|
|
143
|
+
if (!c)
|
|
144
|
+
return -1;
|
|
145
|
+
return accepts.includes(c.type) ? start + 1 : -1;
|
|
146
|
+
}
|
|
147
|
+
case 'seq': {
|
|
148
|
+
let pos = start;
|
|
149
|
+
for (const item of expr.items) {
|
|
150
|
+
const next = matchExpr(item, children, pos, resolve);
|
|
151
|
+
if (next < 0)
|
|
152
|
+
return -1;
|
|
153
|
+
pos = next;
|
|
154
|
+
}
|
|
155
|
+
return pos;
|
|
156
|
+
}
|
|
157
|
+
case 'choice': {
|
|
158
|
+
let best = -1;
|
|
159
|
+
for (const opt of expr.options) {
|
|
160
|
+
const r = matchExpr(opt, children, start, resolve);
|
|
161
|
+
if (r > best)
|
|
162
|
+
best = r;
|
|
163
|
+
}
|
|
164
|
+
return best;
|
|
165
|
+
}
|
|
166
|
+
case 'star': {
|
|
167
|
+
let pos = start;
|
|
168
|
+
while (pos < children.length) {
|
|
169
|
+
const r = matchExpr(expr.inner, children, pos, resolve);
|
|
170
|
+
if (r < 0 || r === pos)
|
|
171
|
+
break;
|
|
172
|
+
pos = r;
|
|
173
|
+
}
|
|
174
|
+
return pos;
|
|
175
|
+
}
|
|
176
|
+
case 'plus': {
|
|
177
|
+
const first = matchExpr(expr.inner, children, start, resolve);
|
|
178
|
+
if (first < 0)
|
|
179
|
+
return -1;
|
|
180
|
+
let pos = first;
|
|
181
|
+
while (pos < children.length) {
|
|
182
|
+
const r = matchExpr(expr.inner, children, pos, resolve);
|
|
183
|
+
if (r < 0 || r === pos)
|
|
184
|
+
break;
|
|
185
|
+
pos = r;
|
|
186
|
+
}
|
|
187
|
+
return pos;
|
|
188
|
+
}
|
|
189
|
+
case 'opt': {
|
|
190
|
+
const r = matchExpr(expr.inner, children, start, resolve);
|
|
191
|
+
return r < 0 ? start : r;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=schema.js.map
|