rapier-markdown-kit 1.2.11 → 1.2.12

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.
@@ -0,0 +1,323 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ const historyBoundary = (source, at) => Number.isSafeInteger(at) && at >= 0 && !Object.is(at, -0) && at <= source.length &&
4
+ !(at > 0 && at < source.length && (source.charCodeAt(at - 1) & 0xFC00) === 0xD800 &&
5
+ (source.charCodeAt(at) & 0xFC00) === 0xDC00);
6
+
7
+ const sourceLeaf = (text, from = 0, to = text.length) => {
8
+ if (typeof text !== 'string') throw new TypeError('source string required');
9
+ return to > from ? {text, from, length: to - from, height: 1} : null;
10
+ };
11
+ const sourceLeafText = node => node.from === 0 && node.length === node.text.length
12
+ ? node.text : node.text.slice(node.from, node.from + node.length);
13
+ const SOURCE_LEAF_LIMIT = 4096;
14
+ const sourceHeight = node => node?.height || 0;
15
+ const sourceNode = (left, right) => !left ? right : !right ? left
16
+ : {left, right, length: left.length + right.length, height: 1 + Math.max(left.height, right.height)};
17
+ function sourceBalance(left, right) {
18
+ if (sourceHeight(left) > sourceHeight(right) + 1) {
19
+ if (sourceHeight(left.left) >= sourceHeight(left.right)) return sourceNode(left.left, sourceNode(left.right, right));
20
+ return sourceNode(sourceNode(left.left, left.right.left), sourceNode(left.right.right, right));
21
+ }
22
+ if (sourceHeight(right) > sourceHeight(left) + 1) {
23
+ if (sourceHeight(right.right) >= sourceHeight(right.left)) return sourceNode(sourceNode(left, right.left), right.right);
24
+ return sourceNode(sourceNode(left, right.left.left), sourceNode(right.left.right, right.right));
25
+ }
26
+ return sourceNode(left, right);
27
+ }
28
+ function sourceJoin(left, right) {
29
+ if (!left || !right) return left || right;
30
+ if (left.text !== undefined && right.text !== undefined && left.length + right.length <= SOURCE_LEAF_LIMIT)
31
+ return sourceLeaf(sourceLeafText(left) + sourceLeafText(right));
32
+ if (left.height > right.height + 1) return sourceBalance(left.left, sourceJoin(left.right, right));
33
+ if (right.height > left.height + 1) return sourceBalance(sourceJoin(left, right.left), right.right);
34
+ return sourceNode(left, right);
35
+ }
36
+ function sourceSplit(node, at) {
37
+ if (!node || at === 0) return [null, node];
38
+ if (at === node.length) return [node, null];
39
+ if (node.text !== undefined) return [sourceLeaf(node.text, node.from, node.from + at), sourceLeaf(node.text, node.from + at, node.from + node.length)];
40
+ if (at < node.left.length) {
41
+ const [left, right] = sourceSplit(node.left, at);
42
+ return [left, sourceJoin(right, node.right)];
43
+ }
44
+ const [left, right] = sourceSplit(node.right, at - node.left.length);
45
+ return [sourceJoin(node.left, left), right];
46
+ }
47
+ function* sourceParts(node, from, to) {
48
+ if (!node || from >= to) return;
49
+ if (node.text !== undefined) {
50
+ yield from === 0 && to === node.length ? sourceString(node) : node.text.slice(node.from + from, node.from + to); return;
51
+ }
52
+ if (from < node.left.length) yield* sourceParts(node.left, from, Math.min(to, node.left.length));
53
+ if (to > node.left.length) yield* sourceParts(node.right, Math.max(0, from - node.left.length), to - node.left.length);
54
+ }
55
+ function* sourceLeaves(node) {
56
+ if (!node) return;
57
+ if (node.text !== undefined) { yield node; return; }
58
+ yield* sourceLeaves(node.left); yield* sourceLeaves(node.right);
59
+ }
60
+ const nodeTexts = new WeakMap();
61
+ function sourceString(node) {
62
+ if (!node) return '';
63
+ if (node.text !== undefined && node.length <= SOURCE_LEAF_LIMIT) return sourceLeafText(node);
64
+ let text = nodeTexts.get(node);
65
+ if (text === undefined) {
66
+ text = node.text !== undefined ? sourceLeafText(node) : sourceString(node.left) + sourceString(node.right);
67
+ nodeTexts.set(node, text);
68
+ }
69
+ return text;
70
+ }
71
+ const replacements = new WeakMap(), texts = new WeakMap(), edits = new WeakMap();
72
+ export const sourceEdit = source => edits.get(source) || null;
73
+ export class HistorySource {
74
+ #root;
75
+ constructor(text) { this.#root = sourceLeaf(text); Object.freeze(this); }
76
+ static owns(value) { return value !== null && typeof value === 'object' && #root in value; }
77
+ static #fromRoot(root) { const source = new HistorySource(''); source.#root = root; return source; }
78
+ static fromParts(parts) {
79
+ const leaves = Array.from(parts, text => sourceLeaf(text)).filter(Boolean);
80
+ const build = (from, to) => from === to ? null : to - from === 1 ? leaves[from]
81
+ : sourceNode(build(from, Math.floor((from + to) / 2)), build(Math.floor((from + to) / 2), to));
82
+ return HistorySource.#fromRoot(build(0, leaves.length));
83
+ }
84
+ get length() { return this.#root?.length || 0; }
85
+ charCodeAt(at) {
86
+ if (at < 0 || at >= this.length) return NaN;
87
+ let node = this.#root;
88
+ while (node.text === undefined) {
89
+ if (at < node.left.length) node = node.left;
90
+ else { at -= node.left.length; node = node.right; }
91
+ }
92
+ return node.text.charCodeAt(node.from + at);
93
+ }
94
+ parts(from = 0, to = this.length) { return sourceParts(this.#root, from, to); }
95
+ slice(from = 0, to = this.length) {
96
+ const root = this.#root;
97
+ if (!root || from >= to) return '';
98
+ if (from === 0 && to === this.length) return sourceString(root);
99
+ if (root.text !== undefined) return root.from + from === 0 && root.from + to === root.text.length
100
+ ? root.text : root.text.slice(root.from + from, root.from + to);
101
+ return Array.from(this.parts(from, to)).join('');
102
+ }
103
+ matches(from, text) {
104
+ if (from < 0 || from + text.length > this.length) return false;
105
+ let at = 0;
106
+ for (const part of this.parts(from, from + text.length)) {
107
+ if (!text.startsWith(part, at)) return false;
108
+ at += part.length;
109
+ }
110
+ return at === text.length;
111
+ }
112
+ sameParts(parts) {
113
+ const leaves = sourceLeaves(this.#root);
114
+ let leaf = leaves.next().value, offset = 0;
115
+ for (const text of parts) for (let at = 0; at < text.length;) {
116
+ if (!leaf) return false;
117
+ const length = Math.min(leaf.length - offset, text.length - at), from = leaf.from + offset;
118
+ if ((leaf.text !== text || from !== at) && leaf.text.slice(from, from + length) !== text.slice(at, at + length)) return false;
119
+ offset += length; at += length;
120
+ if (offset === leaf.length) { leaf = leaves.next().value; offset = 0; }
121
+ }
122
+ return !leaf;
123
+ }
124
+ sameSource(other) {
125
+ if (!(other instanceof HistorySource) || this.length !== other.length) return false;
126
+ if (this.#root === other.#root) return true;
127
+ const left = sourceLeaves(this.#root), right = sourceLeaves(other.#root);
128
+ let a = left.next().value, b = right.next().value, i = 0, j = 0;
129
+ while (a && b) {
130
+ const count = Math.min(a.length - i, b.length - j), from = a.from + i, to = b.from + j;
131
+ if ((a.text !== b.text || from !== to) && a.text.slice(from, from + count) !== b.text.slice(to, to + count)) return false;
132
+ i += count; j += count;
133
+ if (i === a.length) { a = left.next().value; i = 0; }
134
+ if (j === b.length) { b = right.next().value; j = 0; }
135
+ }
136
+ return !a && !b;
137
+ }
138
+ replace(pos, length, text, retain = true) {
139
+ if (!Number.isSafeInteger(pos) || pos < 0 || Object.is(pos, -0) || !Number.isSafeInteger(length) ||
140
+ length < 0 || Object.is(length, -0) || pos + length > this.length || typeof text !== 'string') throw new RangeError('source splice out of range');
141
+ const held = retain && replacements.get(this);
142
+ if (held && held.pos === pos && held.length === length && held.text === text) {
143
+ const source = held.source.deref();
144
+ if (source) { retainEditFacts(this, source, pos, length, text); return source; }
145
+ }
146
+ const [left, rest] = sourceSplit(this.#root, pos), [, right] = sourceSplit(rest, length);
147
+ const source = HistorySource.#fromRoot(sourceJoin(sourceJoin(left, sourceLeaf(text)), right));
148
+ if (retain) {
149
+ edits.set(source, Object.freeze({before: new WeakRef(this), pos, removed: length, inserted: text.length, text}));
150
+ retainEditFacts(this, source, pos, length, text);
151
+ replacements.set(this, {pos, length, text, source: new WeakRef(source)});
152
+ }
153
+ return source;
154
+ }
155
+ before(splices) {
156
+ if (this.length <= SOURCE_LEAF_LIMIT) {
157
+ let text = this.slice();
158
+ for (let index = splices.length - 1; index >= 0; index--) {
159
+ const row = splices[index], pos = row?.pos, removed = row?.removed, inserted = row?.inserted;
160
+ if (!Number.isSafeInteger(pos) || pos < 0 || Object.is(pos, -0) || typeof removed !== 'string' || typeof inserted !== 'string' ||
161
+ /[\uD800-\uDFFF]/u.test(removed) || /[\uD800-\uDFFF]/u.test(inserted) || pos + inserted.length > text.length || !text.startsWith(inserted, pos)) return null;
162
+ text = text.slice(0, pos) + removed + text.slice(pos + inserted.length);
163
+ if (!historyBoundary(text, pos) || !historyBoundary(text, pos + removed.length)) return null;
164
+ if (text.length > SOURCE_LEAF_LIMIT) return new HistorySource(text).before(splices.slice(0, index));
165
+ }
166
+ return new HistorySource(text);
167
+ }
168
+ let source = this;
169
+ for (let index = splices.length - 1; index >= 0; index--) {
170
+ const row = splices[index], pos = row?.pos, removed = row?.removed, inserted = row?.inserted;
171
+ if (!Number.isSafeInteger(pos) || pos < 0 || Object.is(pos, -0) ||
172
+ typeof removed !== 'string' || typeof inserted !== 'string' ||
173
+ /[\uD800-\uDFFF]/u.test(removed) || /[\uD800-\uDFFF]/u.test(inserted) || !source.matches(pos, inserted)) return null;
174
+ source = source.replace(pos, inserted.length, removed, false);
175
+ if (!historyBoundary(source, pos) || !historyBoundary(source, pos + removed.length)) return null;
176
+ }
177
+ return source;
178
+ }
179
+ apply(splices, inverse = false) { return applySource(this, splices, inverse); }
180
+ }
181
+
182
+ const recent = [], MAX_CHARS = 32 * 1024 * 1024;
183
+ function retainView(text, source, admitted = false) {
184
+ const at = recent.findIndex(row => row.source === source);
185
+ const previous = at >= 0 ? recent.splice(at, 1)[0] : null;
186
+ if (admitted && previous?.admission !== 2) for (const row of recent) row.admission = Math.max(0, row.admission - 1);
187
+ recent.push({text, source, admission: admitted ? 2 : previous?.admission || 0});
188
+ let chars = recent.reduce((sum, row) => sum + row.text.length, 0);
189
+ while (recent.length > 1 && (recent.length > 4 || chars > MAX_CHARS)) {
190
+ let remove = recent.findIndex(row => row.admission === 0);
191
+ if (remove < 0) remove = recent.findIndex(row => row.admission === 1);
192
+ chars -= recent.splice(remove < 0 ? 0 : remove, 1)[0].text.length;
193
+ }
194
+ return source;
195
+ }
196
+
197
+ export function sourceView(text, hint = null) {
198
+ if (typeof text !== 'string') throw new TypeError('source string required');
199
+ const held = recentView(text, hint);
200
+ if (held) return held;
201
+ const source = new HistorySource(text);
202
+ texts.set(source, text);
203
+ return retainView(text, source);
204
+ }
205
+
206
+ function recentView(text, hint = null) {
207
+ if (hint) for (let at = recent.length - 1; at >= 0; at--) {
208
+ const row = recent[at], held = replacements.get(row.source);
209
+ if (held && held.pos === hint.pos && held.length === hint.length && held.text === hint.text && row.text === text) return row.source;
210
+ }
211
+ for (let at = recent.length - 1; at >= 0; at--) if (recent[at].text === text) return recent[at].source;
212
+ return null;
213
+ }
214
+ export const heldSourceView = text => recentView(text);
215
+
216
+ const facts = new WeakMap();
217
+ function measureSource(text) {
218
+ let controls = 0, lone = 0, utf8 = 0, data = 0, jsonExtra = 0;
219
+ const markers = [];
220
+ for (let at = 0; at < text.length; at++) {
221
+ const code = text.charCodeAt(at);
222
+ if (code === 34 || code === 92 || code === 8 || code === 9 || code === 10 || code === 12 || code === 13) jsonExtra++;
223
+ else if (code < 32) jsonExtra += 5;
224
+ if (code < 32 && code !== 9 && code !== 10 && code !== 13 || code === 127) controls++;
225
+ if (code < 0x80) utf8++;
226
+ else if (code < 0x800) utf8 += 2;
227
+ else if (code >= 0xd800 && code <= 0xdbff && text.charCodeAt(at + 1) >= 0xdc00 && text.charCodeAt(at + 1) <= 0xdfff) {utf8 += 4; at++;}
228
+ else {utf8 += 3; if (code >= 0xd800 && code <= 0xdfff) lone++;}
229
+ if ((code | 32) === 100 && (text.charCodeAt(at + 1) | 32) === 97 &&
230
+ (text.charCodeAt(at + 2) | 32) === 116 && (text.charCodeAt(at + 3) | 32) === 97 && text.charCodeAt(at + 4) === 58) { data++; markers.push(at); }
231
+ }
232
+ return {controls, lone, utf8, data, markers: Object.freeze(markers), jsonChars: text.length + jsonExtra + lone * 5, jsonBytes: utf8 + jsonExtra + lone * 3};
233
+ }
234
+
235
+ export function heldSourceFacts(source) {
236
+ return facts.get(typeof source === 'string' ? recentView(source) : source) || null;
237
+ }
238
+
239
+ export function sourceFacts(text) {
240
+ const source = sourceView(text), held = facts.get(source);
241
+ retainView(text, source, true);
242
+ if (held) return held;
243
+ const measured = Object.freeze(measureSource(text));
244
+ facts.set(source, measured);
245
+ return measured;
246
+ }
247
+
248
+ function retainEditFacts(before, after, pos, length, inserted) {
249
+ const held = facts.get(before), present = facts.get(after);
250
+ if (present || !held) return present;
251
+ const left = before.slice(Math.max(0, pos - 4), pos), removed = before.slice(pos, pos + length),
252
+ right = before.slice(pos + length, Math.min(before.length, pos + length + 4));
253
+ const was = measureSource(left + removed + right), now = measureSource(left + inserted + right), next = {...held};
254
+ for (const key of ['controls', 'lone', 'utf8', 'data', 'jsonChars', 'jsonBytes']) next[key] += now[key] - was[key];
255
+ const start = pos - left.length, end = pos + length, delta = inserted.length - length;
256
+ next.markers = Object.freeze([
257
+ ...held.markers.filter(at => at < start),
258
+ ...now.markers.filter(at => at < left.length + inserted.length).map(at => at + start),
259
+ ...held.markers.filter(at => at >= end).map(at => at + delta),
260
+ ]);
261
+ const measured = Object.freeze(next);
262
+ facts.set(after, measured);
263
+ return measured;
264
+ }
265
+
266
+ export function sourceText(source) {
267
+ if (!HistorySource.owns(source) || Object.getPrototypeOf(source) !== HistorySource.prototype) throw new TypeError('immutable source required');
268
+ let text = texts.get(source);
269
+ if (text === undefined) {
270
+ text = source.slice();
271
+ texts.set(source, text);
272
+ }
273
+ retainView(text, source);
274
+ return text;
275
+ }
276
+
277
+ function applySource(source, splices, inverse) {
278
+ if (!Array.isArray(splices)) return null;
279
+ for (let index = 0; index < splices.length; index++) {
280
+ const row = splices[inverse ? splices.length - 1 - index : index];
281
+ const pos = row?.pos, removed = inverse ? row?.inserted : row?.removed, inserted = inverse ? row?.removed : row?.inserted;
282
+ if (typeof removed !== 'string' || typeof inserted !== 'string' ||
283
+ /[\uD800-\uDFFF]/u.test(removed) || /[\uD800-\uDFFF]/u.test(inserted)) return null;
284
+ if (!historyBoundary(source, pos) || !historyBoundary(source, pos + removed.length) ||
285
+ !source.matches(pos, removed)) return null;
286
+ source = source.replace(pos, removed.length, inserted);
287
+ }
288
+ return source;
289
+ }
290
+
291
+ export function transformText(text, splices, inverse = false) {
292
+ if (typeof text !== 'string') throw new TypeError('source string required');
293
+ if (!Array.isArray(splices)) return null;
294
+ for (let index = 0; index < splices.length; index++) {
295
+ const row = splices[inverse ? splices.length - 1 - index : index];
296
+ const pos = row?.pos, removed = inverse ? row?.inserted : row?.removed, inserted = inverse ? row?.removed : row?.inserted;
297
+ if (typeof removed !== 'string' || typeof inserted !== 'string' ||
298
+ /[\uD800-\uDFFF]/u.test(removed) || /[\uD800-\uDFFF]/u.test(inserted)) return null;
299
+ if (text.length > SOURCE_LEAF_LIMIT) {
300
+ const source = applySource(sourceView(text, {pos, length: removed.length, text: inserted}),
301
+ inverse ? splices.slice(0, splices.length - index) : splices.slice(index), inverse);
302
+ return source === null ? null : sourceText(source);
303
+ }
304
+ if (!historyBoundary(text, pos) || !historyBoundary(text, pos + removed.length) || !text.startsWith(removed, pos)) return null;
305
+ text = text.slice(0, pos) + inserted + text.slice(pos + removed.length);
306
+ }
307
+ return text;
308
+ }
309
+
310
+ export function replaceText(text, rows) {
311
+ if (typeof text !== 'string') throw new TypeError('source string required');
312
+ let source = text;
313
+ for (const row of rows) {
314
+ const pos = row?.pos, length = row?.remove, inserted = row?.insert;
315
+ if (!Number.isSafeInteger(pos) || pos < 0 || Object.is(pos, -0) ||
316
+ !Number.isSafeInteger(length) || length < 0 || Object.is(length, -0) ||
317
+ pos + length > source.length || typeof inserted !== 'string') throw new RangeError('source splice out of range');
318
+ if (typeof source === 'string') source = sourceView(source, {pos, length, text: inserted});
319
+ source = source.replace(pos, length, inserted);
320
+ }
321
+ return sourceText(typeof source === 'string' ? sourceView(source) : source);
322
+ }
323
+