@thomasfosterau/effect-richtext 0.0.0 → 0.1.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 +48 -0
- package/README.md +117 -1
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/commands/commands.d.ts +323 -0
- package/dist/commands/commands.js +722 -0
- package/dist/commands/internal.js +43 -0
- package/dist/commands/keymaps.d.ts +46 -0
- package/dist/commands/keymaps.js +64 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +3 -0
- package/dist/compat/commands.d.ts +250 -0
- package/dist/compat/commands.js +275 -0
- package/dist/compat/internal/core.d.ts +20 -0
- package/dist/compat/internal/core.js +148 -0
- package/dist/compat/internal/props.js +82 -0
- package/dist/compat/keymap.d.ts +21 -0
- package/dist/compat/keymap.js +38 -0
- package/dist/compat/model.d.ts +893 -0
- package/dist/compat/model.js +1212 -0
- package/dist/compat/orderedmap.d.ts +74 -0
- package/dist/compat/orderedmap.js +124 -0
- package/dist/compat/schema-basic.d.ts +38 -0
- package/dist/compat/schema-basic.js +238 -0
- package/dist/compat/schema-list.d.ts +81 -0
- package/dist/compat/schema-list.js +156 -0
- package/dist/compat/state.d.ts +415 -0
- package/dist/compat/state.js +689 -0
- package/dist/compat/test-builder.d.ts +98 -0
- package/dist/compat/test-builder.js +197 -0
- package/dist/compat/transform.d.ts +458 -0
- package/dist/compat/transform.js +803 -0
- package/dist/compat/view.d.ts +411 -0
- package/dist/compat/view.js +409 -0
- package/dist/dropcursor.d.ts +46 -0
- package/dist/dropcursor.js +176 -0
- package/dist/extensions/defaults.d.ts +716 -0
- package/dist/extensions/defaults.js +209 -0
- package/dist/extensions/editor.d.ts +246 -0
- package/dist/extensions/editor.js +271 -0
- package/dist/extensions/extension.d.ts +375 -0
- package/dist/extensions/extension.js +233 -0
- package/dist/extensions.d.ts +4 -0
- package/dist/extensions.js +4 -0
- package/dist/gapcursor.d.ts +66 -0
- package/dist/gapcursor.js +273 -0
- package/dist/history.d.ts +178 -0
- package/dist/history.js +412 -0
- package/dist/html.d.ts +158 -0
- package/dist/html.js +164 -0
- package/dist/inputrules.d.ts +271 -0
- package/dist/inputrules.js +321 -0
- package/dist/internal/attempt.js +15 -0
- package/dist/internal/json.js +29 -0
- package/dist/keymap/keymap.d.ts +130 -0
- package/dist/keymap/keymap.js +159 -0
- package/dist/keymap.d.ts +2 -0
- package/dist/keymap.js +1 -0
- package/dist/markdown/errors.d.ts +23 -0
- package/dist/markdown/errors.js +26 -0
- package/dist/markdown/from_markdown.d.ts +185 -0
- package/dist/markdown/from_markdown.js +266 -0
- package/dist/markdown/schema.d.ts +522 -0
- package/dist/markdown/schema.js +292 -0
- package/dist/markdown/to_markdown.d.ts +251 -0
- package/dist/markdown/to_markdown.js +413 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +5 -0
- package/dist/model/attrsCodec.js +48 -0
- package/dist/model/comparedeep.js +17 -0
- package/dist/model/content.d.ts +232 -0
- package/dist/model/content.js +630 -0
- package/dist/model/diff.js +66 -0
- package/dist/model/dom.d.ts +3 -0
- package/dist/model/errors.d.ts +90 -0
- package/dist/model/errors.js +76 -0
- package/dist/model/fragment.d.ts +131 -0
- package/dist/model/fragment.js +262 -0
- package/dist/model/from_dom.d.ts +299 -0
- package/dist/model/from_dom.js +618 -0
- package/dist/model/json.d.ts +37 -0
- package/dist/model/json.js +126 -0
- package/dist/model/mark.d.ts +73 -0
- package/dist/model/mark.js +121 -0
- package/dist/model/node.d.ts +267 -0
- package/dist/model/node.js +434 -0
- package/dist/model/replace.d.ts +78 -0
- package/dist/model/replace.js +207 -0
- package/dist/model/resolvedpos.d.ts +196 -0
- package/dist/model/resolvedpos.js +305 -0
- package/dist/model/schema.d.ts +740 -0
- package/dist/model/schema.js +451 -0
- package/dist/model/to_dom.d.ts +169 -0
- package/dist/model/to_dom.js +238 -0
- package/dist/model.d.ts +12 -0
- package/dist/model.js +12 -0
- package/dist/placeholder.d.ts +53 -0
- package/dist/placeholder.js +79 -0
- package/dist/reactive/observer.d.ts +54 -0
- package/dist/reactive/observer.js +61 -0
- package/dist/reactive/selectors.d.ts +22 -0
- package/dist/reactive/selectors.js +61 -0
- package/dist/reactive/stateref.d.ts +72 -0
- package/dist/reactive/stateref.js +71 -0
- package/dist/reactive/streams.d.ts +83 -0
- package/dist/reactive/streams.js +85 -0
- package/dist/reactive.d.ts +16 -0
- package/dist/reactive.js +26 -0
- package/dist/schema-basic.d.ts +448 -0
- package/dist/schema-basic.js +269 -0
- package/dist/schema-list/commands.d.ts +66 -0
- package/dist/schema-list/commands.js +203 -0
- package/dist/schema-list.d.ts +92 -0
- package/dist/schema-list.js +100 -0
- package/dist/state/errors.d.ts +39 -0
- package/dist/state/errors.js +32 -0
- package/dist/state/plugin.d.ts +232 -0
- package/dist/state/plugin.js +82 -0
- package/dist/state/selection.d.ts +297 -0
- package/dist/state/selection.js +486 -0
- package/dist/state/state.d.ts +212 -0
- package/dist/state/state.js +489 -0
- package/dist/state/transaction.d.ts +145 -0
- package/dist/state/transaction.js +195 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.js +6 -0
- package/dist/test-builder.d.ts +3582 -0
- package/dist/test-builder.js +239 -0
- package/dist/transform/attr_step.d.ts +60 -0
- package/dist/transform/attr_step.js +105 -0
- package/dist/transform/errors.d.ts +50 -0
- package/dist/transform/errors.js +35 -0
- package/dist/transform/map.d.ts +182 -0
- package/dist/transform/map.js +280 -0
- package/dist/transform/mark.js +98 -0
- package/dist/transform/mark_set_step.js +69 -0
- package/dist/transform/mark_step.d.ts +113 -0
- package/dist/transform/mark_step.js +225 -0
- package/dist/transform/replace.d.ts +18 -0
- package/dist/transform/replace.js +381 -0
- package/dist/transform/replace_step.d.ts +109 -0
- package/dist/transform/replace_step.js +175 -0
- package/dist/transform/step.d.ts +157 -0
- package/dist/transform/step.js +168 -0
- package/dist/transform/structure.d.ts +89 -0
- package/dist/transform/structure.js +350 -0
- package/dist/transform/transform.d.ts +248 -0
- package/dist/transform/transform.js +373 -0
- package/dist/transform.d.ts +10 -0
- package/dist/transform.js +10 -0
- package/dist/view/browser.js +39 -0
- package/dist/view/capturekeys.js +282 -0
- package/dist/view/clipboard.d.ts +8 -0
- package/dist/view/clipboard.js +245 -0
- package/dist/view/decoration.d.ts +339 -0
- package/dist/view/decoration.js +566 -0
- package/dist/view/dom.d.ts +18 -0
- package/dist/view/dom.js +140 -0
- package/dist/view/domchange.js +295 -0
- package/dist/view/domcoords.d.ts +22 -0
- package/dist/view/domcoords.js +448 -0
- package/dist/view/domobserver.d.ts +47 -0
- package/dist/view/domobserver.js +328 -0
- package/dist/view/errors.d.ts +18 -0
- package/dist/view/errors.js +21 -0
- package/dist/view/input.d.ts +58 -0
- package/dist/view/input.js +704 -0
- package/dist/view/selection.js +198 -0
- package/dist/view/view.d.ts +699 -0
- package/dist/view/view.js +623 -0
- package/dist/view/viewdesc.d.ts +236 -0
- package/dist/view/viewdesc.js +1205 -0
- package/dist/view-css.d.ts +43 -0
- package/dist/view-css.js +124 -0
- package/dist/view.d.ts +6 -0
- package/dist/view.js +4 -0
- package/package.json +200 -4
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { Mark } from "../model/mark.js";
|
|
2
|
+
import { MarkdownError } from "./errors.js";
|
|
3
|
+
import * as Arr from "effect/Array";
|
|
4
|
+
import * as Order from "effect/Order";
|
|
5
|
+
// src/markdown/to_markdown.ts
|
|
6
|
+
/**
|
|
7
|
+
* Serializing documents to Markdown.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.1.0
|
|
10
|
+
*/
|
|
11
|
+
const blankMark = {
|
|
12
|
+
open: "",
|
|
13
|
+
close: "",
|
|
14
|
+
mixable: true
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A specification for serializing a document as Markdown/CommonMark
|
|
18
|
+
* text.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
* @category models
|
|
22
|
+
*/
|
|
23
|
+
var MarkdownSerializer = class {
|
|
24
|
+
/**
|
|
25
|
+
* Construct a serializer with the given configuration. The `nodes`
|
|
26
|
+
* object should map node names in a given schema to function that
|
|
27
|
+
* take a serializer state and such a node, and serialize the node.
|
|
28
|
+
*/
|
|
29
|
+
constructor(nodes, marks, options = {}) {
|
|
30
|
+
this.nodes = nodes;
|
|
31
|
+
this.marks = marks;
|
|
32
|
+
this.options = options;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Serialize the content of the given node to
|
|
36
|
+
* [CommonMark](http://commonmark.org/).
|
|
37
|
+
*
|
|
38
|
+
* Serialization can't fail for nodes that the serializer knows; in
|
|
39
|
+
* strict mode (the default) an unknown node or mark type is a
|
|
40
|
+
* programming error and throws a `MarkdownError`.
|
|
41
|
+
*/
|
|
42
|
+
serialize(content, options = {}) {
|
|
43
|
+
const state = new MarkdownSerializerState(this.nodes, this.marks, {
|
|
44
|
+
...this.options,
|
|
45
|
+
...options
|
|
46
|
+
});
|
|
47
|
+
state.renderContent(content);
|
|
48
|
+
return state.out;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function backticksFor(node, side) {
|
|
52
|
+
const ticks = /`+/g;
|
|
53
|
+
let m, len = 0;
|
|
54
|
+
if (node.isText) while (m = ticks.exec(node.text)) len = Math.max(len, m[0].length);
|
|
55
|
+
let result = len > 0 && side > 0 ? " `" : "`";
|
|
56
|
+
for (let i = 0; i < len; i++) result += "`";
|
|
57
|
+
if (len > 0 && side < 0) result += " ";
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
function isPlainURL(link, parent, index) {
|
|
61
|
+
if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) return false;
|
|
62
|
+
const content = parent.childUnsafe(index);
|
|
63
|
+
if (!content.isText || content.text != link.attrs.href || content.marks[content.marks.length - 1] != link) return false;
|
|
64
|
+
return index == parent.childCount - 1 || !link.isInSet(parent.childUnsafe(index + 1).marks);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A serializer for the Markdown schema (or any schema using the same
|
|
68
|
+
* node and mark names).
|
|
69
|
+
*
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
* @category serializers
|
|
72
|
+
*/
|
|
73
|
+
const defaultMarkdownSerializer = new MarkdownSerializer({
|
|
74
|
+
blockquote(state, node) {
|
|
75
|
+
state.wrapBlock("> ", null, node, () => state.renderContent(node));
|
|
76
|
+
},
|
|
77
|
+
code_block(state, node) {
|
|
78
|
+
const backticks = node.textContent.match(/`{3,}/gm);
|
|
79
|
+
const fence = backticks ? Arr.sort(backticks, Order.String).slice(-1)[0] + "`" : "```";
|
|
80
|
+
state.write(fence + (node.attrs.params || "") + "\n");
|
|
81
|
+
state.text(node.textContent, false);
|
|
82
|
+
state.write("\n");
|
|
83
|
+
state.write(fence);
|
|
84
|
+
state.closeBlock(node);
|
|
85
|
+
},
|
|
86
|
+
heading(state, node) {
|
|
87
|
+
state.write(state.repeat("#", node.attrs.level) + " ");
|
|
88
|
+
state.renderInline(node, false);
|
|
89
|
+
state.closeBlock(node);
|
|
90
|
+
},
|
|
91
|
+
horizontal_rule(state, node) {
|
|
92
|
+
state.write(node.attrs.markup || "---");
|
|
93
|
+
state.closeBlock(node);
|
|
94
|
+
},
|
|
95
|
+
bullet_list(state, node) {
|
|
96
|
+
state.renderList(node, " ", () => (node.attrs.bullet || "*") + " ");
|
|
97
|
+
},
|
|
98
|
+
ordered_list(state, node) {
|
|
99
|
+
const start = node.attrs.order ?? 1;
|
|
100
|
+
const maxW = String(start + node.childCount - 1).length;
|
|
101
|
+
const space = state.repeat(" ", maxW + 2);
|
|
102
|
+
state.renderList(node, space, (i) => {
|
|
103
|
+
const nStr = String(start + i);
|
|
104
|
+
return state.repeat(" ", maxW - nStr.length) + nStr + ". ";
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
list_item(state, node) {
|
|
108
|
+
state.renderContent(node);
|
|
109
|
+
},
|
|
110
|
+
paragraph(state, node) {
|
|
111
|
+
state.renderInline(node);
|
|
112
|
+
state.closeBlock(node);
|
|
113
|
+
},
|
|
114
|
+
image(state, node) {
|
|
115
|
+
state.write("]/g, "\\$&") + (node.attrs.title ? " \"" + node.attrs.title.replace(/"/g, "\\\"") + "\"" : "") + ")");
|
|
116
|
+
},
|
|
117
|
+
hard_break(state, node, parent, index) {
|
|
118
|
+
for (let i = index + 1; i < parent.childCount; i++) if (parent.childUnsafe(i).type != node.type) {
|
|
119
|
+
state.write("\\\n");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
text(state, node) {
|
|
124
|
+
state.text(node.text, !state.inAutolink);
|
|
125
|
+
}
|
|
126
|
+
}, {
|
|
127
|
+
em: {
|
|
128
|
+
open: "*",
|
|
129
|
+
close: "*",
|
|
130
|
+
mixable: true,
|
|
131
|
+
expelEnclosingWhitespace: true
|
|
132
|
+
},
|
|
133
|
+
strong: {
|
|
134
|
+
open: "**",
|
|
135
|
+
close: "**",
|
|
136
|
+
mixable: true,
|
|
137
|
+
expelEnclosingWhitespace: true
|
|
138
|
+
},
|
|
139
|
+
link: {
|
|
140
|
+
open(state, mark, parent, index) {
|
|
141
|
+
state.inAutolink = isPlainURL(mark, parent, index);
|
|
142
|
+
return state.inAutolink ? "<" : "[";
|
|
143
|
+
},
|
|
144
|
+
close(state, mark) {
|
|
145
|
+
const { inAutolink } = state;
|
|
146
|
+
state.inAutolink = void 0;
|
|
147
|
+
return inAutolink ? ">" : "](" + mark.attrs.href.replace(/[()"]/g, "\\$&") + (mark.attrs.title ? ` "${mark.attrs.title.replace(/"/g, "\\\"")}"` : "") + ")";
|
|
148
|
+
},
|
|
149
|
+
mixable: true
|
|
150
|
+
},
|
|
151
|
+
code: {
|
|
152
|
+
open(_state, _mark, parent, index) {
|
|
153
|
+
return backticksFor(parent.childUnsafe(index), -1);
|
|
154
|
+
},
|
|
155
|
+
close(_state, _mark, parent, index) {
|
|
156
|
+
return backticksFor(parent.childUnsafe(index - 1), 1);
|
|
157
|
+
},
|
|
158
|
+
escape: false
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
/**
|
|
162
|
+
* This is an object used to track state and expose methods related to
|
|
163
|
+
* markdown serialization. Instances are passed to node and mark
|
|
164
|
+
* serialization methods.
|
|
165
|
+
*
|
|
166
|
+
* @since 0.1.0
|
|
167
|
+
* @category models
|
|
168
|
+
*/
|
|
169
|
+
var MarkdownSerializerState = class {
|
|
170
|
+
/** @internal */
|
|
171
|
+
constructor(nodes, marks, options) {
|
|
172
|
+
this.nodes = nodes;
|
|
173
|
+
this.marks = marks;
|
|
174
|
+
this.delim = "";
|
|
175
|
+
this.out = "";
|
|
176
|
+
this.closed = null;
|
|
177
|
+
this.inAutolink = void 0;
|
|
178
|
+
this.atBlockStart = false;
|
|
179
|
+
this.inTightList = false;
|
|
180
|
+
this.options = {
|
|
181
|
+
...options,
|
|
182
|
+
tightLists: options.tightLists ?? false,
|
|
183
|
+
hardBreakNodeName: options.hardBreakNodeName ?? "hard_break"
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** @internal */
|
|
187
|
+
flushClose(size = 2) {
|
|
188
|
+
if (this.closed) {
|
|
189
|
+
if (!this.atBlank()) this.out += "\n";
|
|
190
|
+
if (size > 1) {
|
|
191
|
+
let delimMin = this.delim;
|
|
192
|
+
const trim = /\s+$/.exec(delimMin);
|
|
193
|
+
if (trim) delimMin = delimMin.slice(0, delimMin.length - trim[0].length);
|
|
194
|
+
for (let i = 1; i < size; i++) this.out += delimMin + "\n";
|
|
195
|
+
}
|
|
196
|
+
this.closed = null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** @internal */
|
|
200
|
+
getMark(name) {
|
|
201
|
+
let info = this.marks[name];
|
|
202
|
+
if (!info) {
|
|
203
|
+
if (this.options.strict !== false) throw new MarkdownError({ message: `Mark type \`${name}\` not supported by Markdown renderer` });
|
|
204
|
+
info = blankMark;
|
|
205
|
+
}
|
|
206
|
+
return info;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Render a block, prefixing each line with `delim`, and the first line
|
|
210
|
+
* in `firstDelim`. `node` should be the node that is closed at the end
|
|
211
|
+
* of the block, and `f` is a function that renders the content of the
|
|
212
|
+
* block.
|
|
213
|
+
*/
|
|
214
|
+
wrapBlock(delim, firstDelim, node, f) {
|
|
215
|
+
const old = this.delim;
|
|
216
|
+
this.write(firstDelim != null ? firstDelim : delim);
|
|
217
|
+
this.delim += delim;
|
|
218
|
+
f();
|
|
219
|
+
this.delim = old;
|
|
220
|
+
this.closeBlock(node);
|
|
221
|
+
}
|
|
222
|
+
/** @internal */
|
|
223
|
+
atBlank() {
|
|
224
|
+
return !this.out || this.out[this.out.length - 1] == "\n";
|
|
225
|
+
}
|
|
226
|
+
/** Ensure the current content ends with a newline. */
|
|
227
|
+
ensureNewLine() {
|
|
228
|
+
if (!this.atBlank()) this.out += "\n";
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Prepare the state for writing output (closing closed paragraphs,
|
|
232
|
+
* adding delimiters, and so on), and then optionally add content
|
|
233
|
+
* (unescaped) to the output.
|
|
234
|
+
*/
|
|
235
|
+
write(content) {
|
|
236
|
+
this.flushClose();
|
|
237
|
+
if (this.delim && this.atBlank()) this.out += this.delim;
|
|
238
|
+
if (content) this.out += content;
|
|
239
|
+
}
|
|
240
|
+
/** Close the block for the given node. */
|
|
241
|
+
closeBlock(node) {
|
|
242
|
+
this.closed = node;
|
|
243
|
+
}
|
|
244
|
+
/** Add the given text to the document. When escape is not `false`, it will be escaped. */
|
|
245
|
+
text(text, escape = true) {
|
|
246
|
+
const lines = text.split("\n");
|
|
247
|
+
for (let i = 0; i < lines.length; i++) {
|
|
248
|
+
this.write();
|
|
249
|
+
if (!escape && lines[i][0] == "[" && /(^|[^\\])!$/.test(this.out.slice(-2))) this.out = this.out.slice(0, this.out.length - 1) + "\\!";
|
|
250
|
+
this.out += escape ? this.esc(lines[i], this.atBlockStart) : lines[i];
|
|
251
|
+
if (i != lines.length - 1) this.out += "\n";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Render the given node as a block. In strict mode, throws a
|
|
256
|
+
* `MarkdownError` for node types without a serializer.
|
|
257
|
+
*/
|
|
258
|
+
render(node, parent, index) {
|
|
259
|
+
const serializer = this.nodes[node.type.name];
|
|
260
|
+
if (serializer) serializer(this, node, parent, index);
|
|
261
|
+
else if (this.options.strict !== false) throw new MarkdownError({ message: "Token type `" + node.type.name + "` not supported by Markdown renderer" });
|
|
262
|
+
else if (!node.type.isLeaf) {
|
|
263
|
+
if (node.type.inlineContent) this.renderInline(node);
|
|
264
|
+
else this.renderContent(node);
|
|
265
|
+
if (node.isBlock) this.closeBlock(node);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/** Render the contents of `parent` as block nodes. */
|
|
269
|
+
renderContent(parent) {
|
|
270
|
+
parent.forEach((node, _, i) => this.render(node, parent, i));
|
|
271
|
+
}
|
|
272
|
+
/** Render the contents of `parent` as inline content. */
|
|
273
|
+
renderInline(parent, fromBlockStart = true) {
|
|
274
|
+
this.atBlockStart = fromBlockStart;
|
|
275
|
+
const active = [];
|
|
276
|
+
let trailing = "";
|
|
277
|
+
const progress = (node, _offset, index) => {
|
|
278
|
+
let marks = node ? node.marks : [];
|
|
279
|
+
if (node && node.type.name === this.options.hardBreakNodeName) marks = marks.filter((m) => {
|
|
280
|
+
if (index + 1 == parent.childCount) return false;
|
|
281
|
+
const next = parent.childUnsafe(index + 1);
|
|
282
|
+
return m.isInSet(next.marks) && (!next.isText || /\S/.test(next.text));
|
|
283
|
+
});
|
|
284
|
+
const inner = marks.length ? marks[marks.length - 1] : null;
|
|
285
|
+
const noEsc = inner && this.getMark(inner.type.name).escape === false;
|
|
286
|
+
const len = marks.length - (noEsc ? 1 : 0);
|
|
287
|
+
outer: for (let i = 0; i < len; i++) {
|
|
288
|
+
const mark = marks[i];
|
|
289
|
+
if (!this.getMark(mark.type.name).mixable) break;
|
|
290
|
+
for (let j = 0; j < active.length; j++) {
|
|
291
|
+
const other = active[j];
|
|
292
|
+
if (!this.getMark(other.type.name).mixable) break;
|
|
293
|
+
if (mark.eq(other)) {
|
|
294
|
+
if (i > j) marks = marks.slice(0, j).concat(mark).concat(marks.slice(j, i)).concat(marks.slice(i + 1, len));
|
|
295
|
+
else if (j > i) marks = marks.slice(0, i).concat(marks.slice(i + 1, j)).concat(mark).concat(marks.slice(j, len));
|
|
296
|
+
continue outer;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
let keep = 0;
|
|
301
|
+
while (keep < Math.min(active.length, len) && marks[keep].eq(active[keep])) ++keep;
|
|
302
|
+
let leading = trailing;
|
|
303
|
+
trailing = "";
|
|
304
|
+
if (node && node.isText && marks.some((mark) => {
|
|
305
|
+
const info = this.getMark(mark.type.name);
|
|
306
|
+
return info && info.expelEnclosingWhitespace && !active.some((m, i) => i < keep && m.eq(mark));
|
|
307
|
+
})) {
|
|
308
|
+
const [_, lead, rest] = /^(\s*)(.*)$/m.exec(node.text);
|
|
309
|
+
if (lead) {
|
|
310
|
+
leading += lead;
|
|
311
|
+
node = rest ? node.withText(rest) : null;
|
|
312
|
+
if (!node) marks = active;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (node && node.isText && marks.some((mark, i) => {
|
|
316
|
+
const info = this.getMark(mark.type.name);
|
|
317
|
+
return info && info.expelEnclosingWhitespace && !this.isMarkAhead(parent, index + 1, marks.slice(0, i + 1));
|
|
318
|
+
})) {
|
|
319
|
+
const [_, rest, trail] = /^(.*?)(\s*)$/m.exec(node.text);
|
|
320
|
+
if (trail) {
|
|
321
|
+
trailing = trail;
|
|
322
|
+
node = rest ? node.withText(rest) : null;
|
|
323
|
+
if (!node) marks = active;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (node || index == parent.childCount) while (keep < active.length) this.text(this.markString(active.pop(), false, parent, index), false);
|
|
327
|
+
if (leading) this.text(leading);
|
|
328
|
+
if (node) {
|
|
329
|
+
while (active.length < len) {
|
|
330
|
+
const add = marks[active.length];
|
|
331
|
+
active.push(add);
|
|
332
|
+
this.text(this.markString(add, true, parent, index), false);
|
|
333
|
+
this.atBlockStart = false;
|
|
334
|
+
}
|
|
335
|
+
if (noEsc && node.isText) this.text(this.markString(inner, true, parent, index) + node.text + this.markString(inner, false, parent, index + 1), false);
|
|
336
|
+
else this.render(node, parent, index);
|
|
337
|
+
this.atBlockStart = false;
|
|
338
|
+
if (node.isText && node.nodeSize > 0) this.atBlockStart = false;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
parent.forEach(progress);
|
|
342
|
+
progress(null, 0, parent.childCount);
|
|
343
|
+
this.atBlockStart = false;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Render a node's content as a list. `delim` should be the extra
|
|
347
|
+
* indentation added to all lines except the first in an item,
|
|
348
|
+
* `firstDelim` is a function going from an item index to a delimiter
|
|
349
|
+
* for the first line of the item.
|
|
350
|
+
*/
|
|
351
|
+
renderList(node, delim, firstDelim) {
|
|
352
|
+
if (this.closed && this.closed.type == node.type) this.flushClose(3);
|
|
353
|
+
else if (this.inTightList) this.flushClose(1);
|
|
354
|
+
const attrs = node.attrs;
|
|
355
|
+
const isTight = typeof attrs.tight != "undefined" ? attrs.tight : this.options.tightLists;
|
|
356
|
+
const prevTight = this.inTightList;
|
|
357
|
+
this.inTightList = isTight;
|
|
358
|
+
node.forEach((child, _, i) => {
|
|
359
|
+
if (i && isTight) this.flushClose(1);
|
|
360
|
+
this.wrapBlock(delim, firstDelim(i), node, () => this.render(child, node, i));
|
|
361
|
+
});
|
|
362
|
+
this.inTightList = prevTight;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Escape the given string so that it can safely appear in Markdown
|
|
366
|
+
* content. If `startOfLine` is true, also escape characters that have
|
|
367
|
+
* special meaning only at the start of the line.
|
|
368
|
+
*/
|
|
369
|
+
esc(str, startOfLine = false) {
|
|
370
|
+
str = str.replace(/[`*\\~[\]_]/g, (m, i) => m == "_" && i > 0 && i + 1 < str.length && str[i - 1].match(/\w/) && str[i + 1].match(/\w/) ? m : "\\" + m);
|
|
371
|
+
if (startOfLine) str = str.replace(/^(\+[ ]|[-*>])/, "\\$&").replace(/^(\s*)(#{1,6})(\s|$)/, "$1\\$2$3").replace(/^(\s*\d+)\.\s/, "$1\\. ");
|
|
372
|
+
if (this.options.escapeExtraCharacters) str = str.replace(this.options.escapeExtraCharacters, "\\$&");
|
|
373
|
+
return str;
|
|
374
|
+
}
|
|
375
|
+
/** @internal */
|
|
376
|
+
quote(str) {
|
|
377
|
+
const wrap = str.indexOf("\"") == -1 ? "\"\"" : str.indexOf("'") == -1 ? "''" : "()";
|
|
378
|
+
return wrap[0] + str + wrap[1];
|
|
379
|
+
}
|
|
380
|
+
/** Repeat the given string `n` times. */
|
|
381
|
+
repeat(str, n) {
|
|
382
|
+
let out = "";
|
|
383
|
+
for (let i = 0; i < n; i++) out += str;
|
|
384
|
+
return out;
|
|
385
|
+
}
|
|
386
|
+
/** Get the markdown string for a given opening or closing mark. */
|
|
387
|
+
markString(mark, open, parent, index) {
|
|
388
|
+
const info = this.getMark(mark.type.name);
|
|
389
|
+
const value = open ? info.open : info.close;
|
|
390
|
+
return typeof value == "string" ? value : value(this, mark, parent, index);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get leading and trailing whitespace from a string. Values of leading
|
|
394
|
+
* or trailing property of the return object will be undefined if there
|
|
395
|
+
* is no match.
|
|
396
|
+
*/
|
|
397
|
+
getEnclosingWhitespace(text) {
|
|
398
|
+
return {
|
|
399
|
+
leading: (text.match(/^(\s+)/) || [void 0])[0],
|
|
400
|
+
trailing: (text.match(/(\s+)$/) || [void 0])[0]
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/** @internal */
|
|
404
|
+
isMarkAhead(parent, index, marks) {
|
|
405
|
+
for (;; index++) {
|
|
406
|
+
if (index >= parent.childCount) return false;
|
|
407
|
+
const next = parent.childUnsafe(index);
|
|
408
|
+
if (next.type.name != this.options.hardBreakNodeName) return next.marks.length >= marks.length && Mark.sameSet(next.marks.slice(0, marks.length), marks);
|
|
409
|
+
index++;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
export { MarkdownSerializer, MarkdownSerializerState, defaultMarkdownSerializer };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MarkdownError, isMarkdownError } from "./markdown/errors.js";
|
|
2
|
+
import { MarkdownParseState, MarkdownParser, ParseSpec, TokenHandler, defaultMarkdownParser } from "./markdown/from_markdown.js";
|
|
3
|
+
import { MarkdownSchema, marks, nodes, schema } from "./markdown/schema.js";
|
|
4
|
+
import { MarkSerializerSpec, MarkdownSerializeOptions, MarkdownSerializer, MarkdownSerializerOptions, MarkdownSerializerState, MarkdownSerializerStateOptions, NodeSerializer, defaultMarkdownSerializer } from "./markdown/to_markdown.js";
|
|
5
|
+
export { MarkSerializerSpec, MarkdownError, MarkdownParseState, MarkdownParser, MarkdownSchema, MarkdownSerializeOptions, MarkdownSerializer, MarkdownSerializerOptions, MarkdownSerializerState, MarkdownSerializerStateOptions, NodeSerializer, ParseSpec, TokenHandler, defaultMarkdownParser, defaultMarkdownSerializer, isMarkdownError, marks, nodes, schema };
|
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MarkdownError, isMarkdownError } from "./markdown/errors.js";
|
|
2
|
+
import { MarkdownParseState, MarkdownParser, defaultMarkdownParser } from "./markdown/from_markdown.js";
|
|
3
|
+
import { marks, nodes, schema } from "./markdown/schema.js";
|
|
4
|
+
import { MarkdownSerializer, MarkdownSerializerState, defaultMarkdownSerializer } from "./markdown/to_markdown.js";
|
|
5
|
+
export { MarkdownError, MarkdownParseState, MarkdownParser, MarkdownSerializer, MarkdownSerializerState, defaultMarkdownParser, defaultMarkdownSerializer, isMarkdownError, marks, nodes, schema };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
|
+
// src/model/attrsCodec.ts
|
|
3
|
+
const encoderCache = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
const decoderCache = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
const encoder = (schema) => {
|
|
6
|
+
let enc = encoderCache.get(schema);
|
|
7
|
+
if (!enc) {
|
|
8
|
+
enc = S.encodeUnknownSync(schema);
|
|
9
|
+
encoderCache.set(schema, enc);
|
|
10
|
+
}
|
|
11
|
+
return enc;
|
|
12
|
+
};
|
|
13
|
+
const decoder = (schema) => {
|
|
14
|
+
let dec = decoderCache.get(schema);
|
|
15
|
+
if (!dec) {
|
|
16
|
+
dec = S.decodeUnknownSync(schema);
|
|
17
|
+
decoderCache.set(schema, dec);
|
|
18
|
+
}
|
|
19
|
+
return dec;
|
|
20
|
+
};
|
|
21
|
+
const mapAttrs = (defs, attrs, decode) => {
|
|
22
|
+
let out;
|
|
23
|
+
for (const name in attrs) {
|
|
24
|
+
const def = defs[name];
|
|
25
|
+
if (!def?.schema || def.identityCodec && (!decode || def.is !== void 0 && def.is(attrs[name]))) continue;
|
|
26
|
+
const value = (decode ? decoder : encoder)(def.schema)(attrs[name]);
|
|
27
|
+
if (value !== attrs[name]) {
|
|
28
|
+
if (!out) out = { ...attrs };
|
|
29
|
+
out[name] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out ?? attrs;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Encode attribute values with their schemas, for JSON output. Returns
|
|
36
|
+
* the input object when nothing changed.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
const encodeAttrs = (defs, attrs) => mapAttrs(defs, attrs, false);
|
|
41
|
+
/**
|
|
42
|
+
* Decode attribute values from JSON with their schemas. Throws the
|
|
43
|
+
* schema error on failure.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
const decodeAttrs = (defs, attrs) => mapAttrs(defs, attrs, true);
|
|
48
|
+
export { decodeAttrs, encodeAttrs };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
// src/model/comparedeep.ts
|
|
3
|
+
/** @internal */
|
|
4
|
+
function compareDeep(a, b) {
|
|
5
|
+
if (a === b) return true;
|
|
6
|
+
if (!Predicate.isObjectOrArray(a) || !Predicate.isObjectOrArray(b)) return false;
|
|
7
|
+
if (Array.isArray(a)) {
|
|
8
|
+
if (!Array.isArray(b) || a.length !== b.length) return false;
|
|
9
|
+
for (let i = 0; i < a.length; i++) if (!compareDeep(a[i], b[i])) return false;
|
|
10
|
+
} else {
|
|
11
|
+
if (Array.isArray(b)) return false;
|
|
12
|
+
for (const p in a) if (!(p in b) || !compareDeep(a[p], b[p])) return false;
|
|
13
|
+
for (const p in b) if (!(p in a)) return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
export { compareDeep };
|