@agentshouse/mdruntime 0.1.0-ce131cb435f8b → 0.1.0-ce9f29a01a271
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/dist/adapter.d.ts +12 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/change-set.d.ts +5 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -1
- package/dist/chat/list.d.ts +2 -1
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +2 -2
- package/dist/chat/markdown.d.ts +1 -0
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +78 -20
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +9 -2
- package/dist/chat/messages.d.ts.map +1 -1
- package/dist/chat/messages.js +36 -16
- package/dist/chat/overlay.d.ts +1 -0
- package/dist/chat/overlay.d.ts.map +1 -1
- package/dist/chat/overlay.js +10 -0
- package/dist/chat/pieces.d.ts +62 -0
- package/dist/chat/pieces.d.ts.map +1 -0
- package/dist/chat/pieces.js +1110 -0
- package/dist/chat/provider.d.ts +1 -0
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +12 -3
- package/dist/chat/ready.d.ts +1 -1
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +1 -1
- package/dist/chat/steady.d.ts +3 -0
- package/dist/chat/steady.d.ts.map +1 -0
- package/dist/chat/steady.js +26 -0
- package/dist/chat.d.ts +2 -2
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +45 -30
- package/dist/conversation.d.ts +5 -0
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +35 -1
- package/dist/host.d.ts +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1 -1
- package/dist/overlay.d.ts +3 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +178 -141
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +4 -2
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +34 -15
- package/dist/replica.d.ts +2 -1
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +18 -15
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +8 -9
- package/dist/save.d.ts +1 -1
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +154 -92
- package/dist/shared.d.ts +1 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +5 -1
- package/dist/store.d.ts +2 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +387 -339
- package/dist/turn.d.ts +1 -1
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +4 -4
- package/package.json +2 -2
|
@@ -0,0 +1,1110 @@
|
|
|
1
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
2
|
+
import { gfmFromMarkdown } from 'mdast-util-gfm';
|
|
3
|
+
import { gfm } from 'micromark-extension-gfm';
|
|
4
|
+
export function parseMarkdown(text) {
|
|
5
|
+
return fromMarkdown(text, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] });
|
|
6
|
+
}
|
|
7
|
+
const VIEW_CHARS = 16_384;
|
|
8
|
+
const WINDOW_CHARS = 2 * VIEW_CHARS;
|
|
9
|
+
const WINDOW_LINES = 2_048;
|
|
10
|
+
const SPAN_CHARS = WINDOW_CHARS;
|
|
11
|
+
const TRACKS = 32;
|
|
12
|
+
const FENCE = /^(`{3,}|~{3,})(.*)$/;
|
|
13
|
+
const CLOSING = /^(`{3,}|~{3,})[ \t]*$/;
|
|
14
|
+
const RAW = [
|
|
15
|
+
[/^<(?:script|pre|style|textarea)(?:[ \t>]|$)/i, /<\/(?:script|pre|style|textarea)>/i],
|
|
16
|
+
[/^<!--/, /-->/],
|
|
17
|
+
[/^<\?/, /\?>/],
|
|
18
|
+
[/^<![A-Za-z]/, />/],
|
|
19
|
+
[/^<!\[CDATA\[/, /\]\]>/],
|
|
20
|
+
];
|
|
21
|
+
const TAG = /^<[A-Za-z/]/;
|
|
22
|
+
const ATX = /^#{1,6}(?:[ \t]|$)/;
|
|
23
|
+
const THEMATIC = /^(?:(?:\*[ \t]*){3,}|(?:-[ \t]*){3,}|(?:_[ \t]*){3,})$/;
|
|
24
|
+
const UNDERLINE = /^(?:=+|-+)[ \t]*$/;
|
|
25
|
+
const ITEM = /^(?:[-+*]|(\d{1,9})[.)])(?=[ \t]|$)/;
|
|
26
|
+
const UNRESOLVED = /^\[(?:[^\\[\]]|\\.)+\]:[ \t]*$/;
|
|
27
|
+
const DELIMITER_ROW = /^\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
|
|
28
|
+
const CANDIDATE = /^[ \t>]*(?:(?:[-+*]|\d{1,9}[.)])[ \t]+[ \t>]*)*\[/;
|
|
29
|
+
const DEFINED = /\[((?:[^\\[\]]|\\[\s\S]){1,999})\]:/y;
|
|
30
|
+
const LABEL = /\[((?:[^\\[\]]|\\[\s\S]){1,999})\]/g;
|
|
31
|
+
const QUOTE_BREAK = /^[ \t>]*$/;
|
|
32
|
+
const BLOCK_START = /[\s#>+*=_`~<|-]/;
|
|
33
|
+
const ORDERED = /\d{1,9}[.)](?=[ \t\r\n]|$)/y;
|
|
34
|
+
const SPACE = /\s/u;
|
|
35
|
+
const PUNCTUATION = /[\p{P}\p{S}]/u;
|
|
36
|
+
const ESCAPABLE = /[!-/:-@[-`{-~]/;
|
|
37
|
+
const TAG_START = /[A-Za-z!?/]/;
|
|
38
|
+
const LINE_ENDING = /\r\n?|\n/g;
|
|
39
|
+
const QUOTE_MARKER = /^ {0,3}> ?/;
|
|
40
|
+
const MARKER = /^(?:[-+*]|\d{1,9}[.)])/;
|
|
41
|
+
const TASK = /^\[[ \txX]\]/;
|
|
42
|
+
const NOTE = /^\[\^(?:[^\\\]]|\\.){1,999}\]:/;
|
|
43
|
+
const SPACES = /^[ \t]*$/;
|
|
44
|
+
const START = {
|
|
45
|
+
fence: null,
|
|
46
|
+
list: null,
|
|
47
|
+
empty: false,
|
|
48
|
+
para: false,
|
|
49
|
+
table: false,
|
|
50
|
+
quote: false,
|
|
51
|
+
unresolved: false,
|
|
52
|
+
};
|
|
53
|
+
export function identifierOf(label) {
|
|
54
|
+
return label
|
|
55
|
+
.replace(/(?:\r\n?|\n)[ \t>]*/g, '\n')
|
|
56
|
+
.replace(/[\t\n\r ]+/g, ' ')
|
|
57
|
+
.replace(/^ | $/g, '')
|
|
58
|
+
.toLowerCase()
|
|
59
|
+
.toUpperCase()
|
|
60
|
+
.toLowerCase();
|
|
61
|
+
}
|
|
62
|
+
function lineOf(long, at) {
|
|
63
|
+
let low = 0;
|
|
64
|
+
let high = long.starts.length - 2;
|
|
65
|
+
while (low < high) {
|
|
66
|
+
const middle = (low + high + 1) >> 1;
|
|
67
|
+
if (long.starts[middle] <= at)
|
|
68
|
+
low = middle;
|
|
69
|
+
else
|
|
70
|
+
high = middle - 1;
|
|
71
|
+
}
|
|
72
|
+
return low;
|
|
73
|
+
}
|
|
74
|
+
function lineEnd(starts, text, index) {
|
|
75
|
+
return Math.min(starts[index + 1] - 1, text.length);
|
|
76
|
+
}
|
|
77
|
+
function lineText(starts, text, index) {
|
|
78
|
+
const end = lineEnd(starts, text, index);
|
|
79
|
+
return text.slice(starts[index], text[end - 1] === '\r' ? end - 1 : end);
|
|
80
|
+
}
|
|
81
|
+
function lineStartOf(long, at) {
|
|
82
|
+
return long.starts[lineOf(long, at)];
|
|
83
|
+
}
|
|
84
|
+
function lineFacts(raw) {
|
|
85
|
+
let indent = 0;
|
|
86
|
+
let index = 0;
|
|
87
|
+
while (index < raw.length && (raw[index] === ' ' || raw[index] === '\t')) {
|
|
88
|
+
indent += raw[index] === '\t' ? 4 - (indent % 4) : 1;
|
|
89
|
+
index += 1;
|
|
90
|
+
}
|
|
91
|
+
const content = raw.slice(index);
|
|
92
|
+
return { raw, indent, content, blank: content === '' };
|
|
93
|
+
}
|
|
94
|
+
function itemOf(line) {
|
|
95
|
+
if (line.indent > 3 || THEMATIC.test(line.content))
|
|
96
|
+
return null;
|
|
97
|
+
const marker = ITEM.exec(line.content);
|
|
98
|
+
if (marker === null)
|
|
99
|
+
return null;
|
|
100
|
+
const width = marker[0].length;
|
|
101
|
+
let columns = line.indent + width;
|
|
102
|
+
let index = width;
|
|
103
|
+
while (index < line.content.length && /[ \t]/.test(line.content[index])) {
|
|
104
|
+
columns += line.content[index] === '\t' ? 4 - (columns % 4) : 1;
|
|
105
|
+
index += 1;
|
|
106
|
+
}
|
|
107
|
+
const empty = index === line.content.length;
|
|
108
|
+
const spacing = columns - line.indent - width;
|
|
109
|
+
return {
|
|
110
|
+
indent: empty || spacing >= 5 ? line.indent + width + 1 : columns,
|
|
111
|
+
empty,
|
|
112
|
+
interrupts: !empty && (marker[1] === undefined || Number(marker[1]) === 1),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function fenceOf(line) {
|
|
116
|
+
if (line.indent > 3)
|
|
117
|
+
return null;
|
|
118
|
+
const fence = FENCE.exec(line.content);
|
|
119
|
+
if (fence === null || (fence[1][0] === '`' && fence[2].includes('`')))
|
|
120
|
+
return null;
|
|
121
|
+
return `${fence[1][0]}${fence[1].length}`;
|
|
122
|
+
}
|
|
123
|
+
function opens(line) {
|
|
124
|
+
if (line.indent > 3)
|
|
125
|
+
return false;
|
|
126
|
+
return (fenceOf(line) !== null ||
|
|
127
|
+
RAW.some(([start]) => start.test(line.content)) ||
|
|
128
|
+
line.content.startsWith('>') ||
|
|
129
|
+
ATX.test(line.content) ||
|
|
130
|
+
THEMATIC.test(line.content) ||
|
|
131
|
+
itemOf(line) !== null);
|
|
132
|
+
}
|
|
133
|
+
function fenced(track, line) {
|
|
134
|
+
const fence = track.fence;
|
|
135
|
+
if (fence === 'html')
|
|
136
|
+
return line.blank ? { ...track, fence: null } : track;
|
|
137
|
+
if (fence.startsWith('raw')) {
|
|
138
|
+
return RAW[Number(fence.slice(3))][1].test(line.raw) ? { ...track, fence: null } : track;
|
|
139
|
+
}
|
|
140
|
+
const close = line.indent <= 3 ? CLOSING.exec(line.content) : null;
|
|
141
|
+
const closes = close !== null && close[1][0] === fence[0] && close[1].length >= Number(fence.slice(1));
|
|
142
|
+
return closes ? { ...track, fence: null } : track;
|
|
143
|
+
}
|
|
144
|
+
function prose(track, plain, line) {
|
|
145
|
+
if (track.table)
|
|
146
|
+
return [{ ...plain, para: false }];
|
|
147
|
+
const paragraph = { ...plain, para: true, unresolved: UNRESOLVED.test(line.content) };
|
|
148
|
+
return track.quote ? [{ ...plain, para: false, quote: true }, paragraph] : [paragraph];
|
|
149
|
+
}
|
|
150
|
+
function topLevel(track, line) {
|
|
151
|
+
const plain = { ...track, list: null, empty: false, quote: false, unresolved: false };
|
|
152
|
+
if (line.indent >= 4)
|
|
153
|
+
return [{ ...plain, para: track.para }];
|
|
154
|
+
const fence = fenceOf(line);
|
|
155
|
+
if (fence !== null) {
|
|
156
|
+
const opened = { ...plain, fence, para: false, table: false };
|
|
157
|
+
return track.table || track.unresolved ? [opened, { ...plain, para: false }] : [opened];
|
|
158
|
+
}
|
|
159
|
+
const raw = RAW.findIndex(([start]) => start.test(line.content));
|
|
160
|
+
if (raw !== -1) {
|
|
161
|
+
const [start, end] = RAW[raw];
|
|
162
|
+
const closed = end.test(line.content.replace(start, ''));
|
|
163
|
+
const opened = { ...plain, fence: closed ? null : `raw${raw}`, para: false, table: false };
|
|
164
|
+
return track.table ? [opened, { ...plain, para: false }] : [opened];
|
|
165
|
+
}
|
|
166
|
+
if (TAG.test(line.content)) {
|
|
167
|
+
return [{ ...plain, fence: 'html', para: false, table: false }, ...prose(track, plain, line)];
|
|
168
|
+
}
|
|
169
|
+
if (line.content.startsWith('>'))
|
|
170
|
+
return [{ ...plain, para: false, table: false, quote: true }];
|
|
171
|
+
if (ATX.test(line.content) || THEMATIC.test(line.content)) {
|
|
172
|
+
return [{ ...plain, para: false, table: false }];
|
|
173
|
+
}
|
|
174
|
+
const item = itemOf(line);
|
|
175
|
+
if (item !== null) {
|
|
176
|
+
const opened = { ...plain, list: item.indent, empty: item.empty, para: false, table: false };
|
|
177
|
+
if (!item.interrupts && track.para)
|
|
178
|
+
return [{ ...plain, para: true }];
|
|
179
|
+
return track.table || (!item.interrupts && track.quote)
|
|
180
|
+
? [opened, ...prose(track, plain, line)]
|
|
181
|
+
: [opened];
|
|
182
|
+
}
|
|
183
|
+
if (track.para && UNDERLINE.test(line.content))
|
|
184
|
+
return [{ ...plain, para: false, table: false }];
|
|
185
|
+
if (track.para && DELIMITER_ROW.test(line.content)) {
|
|
186
|
+
return [
|
|
187
|
+
{ ...plain, para: false, table: true },
|
|
188
|
+
{ ...plain, para: true },
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
return prose(track, plain, line);
|
|
192
|
+
}
|
|
193
|
+
function advance(track, line, blankBefore) {
|
|
194
|
+
if (track.fence !== null)
|
|
195
|
+
return [fenced(track, line)];
|
|
196
|
+
if (line.blank) {
|
|
197
|
+
const blank = { ...track, para: false, table: false, quote: false, unresolved: false };
|
|
198
|
+
return track.empty
|
|
199
|
+
? [
|
|
200
|
+
{ ...blank, empty: false },
|
|
201
|
+
{ ...blank, list: null, empty: false },
|
|
202
|
+
]
|
|
203
|
+
: [blank];
|
|
204
|
+
}
|
|
205
|
+
if (track.list === null)
|
|
206
|
+
return topLevel(track, line);
|
|
207
|
+
if (line.indent >= track.list) {
|
|
208
|
+
return [{ ...track, empty: false, para: false, table: false, quote: false, unresolved: false }];
|
|
209
|
+
}
|
|
210
|
+
const closed = topLevel({ ...track, list: null, empty: false, para: false, table: false, quote: false }, line);
|
|
211
|
+
if (blankBefore || opens(line))
|
|
212
|
+
return closed;
|
|
213
|
+
return [{ ...track, empty: false, para: false, quote: false, unresolved: false }, ...closed];
|
|
214
|
+
}
|
|
215
|
+
function trackKey(track) {
|
|
216
|
+
return `${track.fence}|${track.list}|${track.empty}|${track.para}|${track.table}|${track.quote}|${track.unresolved}`;
|
|
217
|
+
}
|
|
218
|
+
function candidatesOf(text, start, raw, found) {
|
|
219
|
+
const lead = CANDIDATE.exec(raw);
|
|
220
|
+
if (lead === null)
|
|
221
|
+
return;
|
|
222
|
+
let at = start + lead[0].length - 1;
|
|
223
|
+
for (;;) {
|
|
224
|
+
DEFINED.lastIndex = at;
|
|
225
|
+
const label = DEFINED.exec(text);
|
|
226
|
+
if (label === null)
|
|
227
|
+
return;
|
|
228
|
+
const key = identifierOf(label[1]);
|
|
229
|
+
const held = found.get(key);
|
|
230
|
+
if (held === undefined)
|
|
231
|
+
found.set(key, [at]);
|
|
232
|
+
else
|
|
233
|
+
held.push(at);
|
|
234
|
+
if (!label[1].startsWith('^'))
|
|
235
|
+
return;
|
|
236
|
+
at = DEFINED.lastIndex;
|
|
237
|
+
while (text[at] === ' ' || text[at] === '\t')
|
|
238
|
+
at += 1;
|
|
239
|
+
if (text[at] !== '[')
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function breaksOf(flags) {
|
|
244
|
+
const breaks = new Int32Array(flags.length + 1);
|
|
245
|
+
breaks[flags.length] = flags.length;
|
|
246
|
+
for (let index = flags.length - 1; index >= 0; index -= 1) {
|
|
247
|
+
breaks[index] = flags[index] === 1 ? index : breaks[index + 1];
|
|
248
|
+
}
|
|
249
|
+
return breaks;
|
|
250
|
+
}
|
|
251
|
+
export function longOf(text) {
|
|
252
|
+
const offsets = [];
|
|
253
|
+
let at = 0;
|
|
254
|
+
while (at < text.length) {
|
|
255
|
+
offsets.push(at);
|
|
256
|
+
LINE_ENDING.lastIndex = at;
|
|
257
|
+
at = LINE_ENDING.test(text) ? LINE_ENDING.lastIndex : text.length + 1;
|
|
258
|
+
}
|
|
259
|
+
offsets.push(at);
|
|
260
|
+
const starts = Int32Array.from(offsets);
|
|
261
|
+
const count = starts.length - 1;
|
|
262
|
+
const blank = new Uint8Array(count);
|
|
263
|
+
const quoteBlank = new Uint8Array(count);
|
|
264
|
+
const anchors = [0];
|
|
265
|
+
const candidates = new Map();
|
|
266
|
+
let tracks = [START];
|
|
267
|
+
let lost = false;
|
|
268
|
+
for (let index = 0; index < count; index += 1) {
|
|
269
|
+
const line = lineFacts(lineText(starts, text, index));
|
|
270
|
+
const before = index === 0 || blank[index - 1] === 1;
|
|
271
|
+
blank[index] = line.blank ? 1 : 0;
|
|
272
|
+
quoteBlank[index] = QUOTE_BREAK.test(line.raw) ? 1 : 0;
|
|
273
|
+
if (index > 0 && !lost && !line.blank && before && line.indent <= 1) {
|
|
274
|
+
if (tracks.every((track) => track.fence === null))
|
|
275
|
+
anchors.push(starts[index]);
|
|
276
|
+
}
|
|
277
|
+
candidatesOf(text, starts[index], line.raw, candidates);
|
|
278
|
+
if (lost)
|
|
279
|
+
continue;
|
|
280
|
+
const advanced = new Map();
|
|
281
|
+
for (const track of tracks) {
|
|
282
|
+
for (const one of advance(track, line, before))
|
|
283
|
+
advanced.set(trackKey(one), one);
|
|
284
|
+
}
|
|
285
|
+
tracks = [...advanced.values()];
|
|
286
|
+
lost = tracks.length > TRACKS;
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
text,
|
|
290
|
+
starts,
|
|
291
|
+
breaks: breaksOf(blank),
|
|
292
|
+
quoteBreaks: breaksOf(quoteBlank),
|
|
293
|
+
anchors,
|
|
294
|
+
candidates,
|
|
295
|
+
pieces: [],
|
|
296
|
+
next: { start: 0, path: [], join: '', carry: '', levels: [] },
|
|
297
|
+
urls: new Map(),
|
|
298
|
+
spans: new Map(),
|
|
299
|
+
discoveries: new Map(),
|
|
300
|
+
regions: new Map(),
|
|
301
|
+
spreads: new Map(),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function offsetOf(node) {
|
|
305
|
+
return node.position.start.offset;
|
|
306
|
+
}
|
|
307
|
+
function endOf(node) {
|
|
308
|
+
return node.position.end.offset;
|
|
309
|
+
}
|
|
310
|
+
function definedIn(nodes, shift, found = []) {
|
|
311
|
+
for (const node of nodes) {
|
|
312
|
+
if (node.type === 'definition') {
|
|
313
|
+
found.push({ key: node.identifier, at: offsetOf(node) + shift, url: node.url });
|
|
314
|
+
}
|
|
315
|
+
else if (node.type === 'footnoteDefinition') {
|
|
316
|
+
found.push({ key: `^${node.identifier}`, at: offsetOf(node) + shift, url: '' });
|
|
317
|
+
}
|
|
318
|
+
if (node.type === 'blockquote' ||
|
|
319
|
+
node.type === 'list' ||
|
|
320
|
+
node.type === 'listItem' ||
|
|
321
|
+
node.type === 'footnoteDefinition') {
|
|
322
|
+
definedIn(node.children, shift, found);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return found;
|
|
326
|
+
}
|
|
327
|
+
function windowOf(long, from, end, prefix) {
|
|
328
|
+
const root = parseMarkdown(`${prefix}${from.carry}${long.text.slice(from.start, end)}`);
|
|
329
|
+
const lead = prefix.length + from.carry.length;
|
|
330
|
+
const nodes = root.children.filter((node) => offsetOf(node) >= prefix.length);
|
|
331
|
+
return {
|
|
332
|
+
start: from.start,
|
|
333
|
+
end,
|
|
334
|
+
lead,
|
|
335
|
+
prefix,
|
|
336
|
+
carry: from.carry,
|
|
337
|
+
levels: from.levels,
|
|
338
|
+
nodes,
|
|
339
|
+
defined: definedIn(nodes, from.start - lead),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function spanned(long, anchor, stop) {
|
|
343
|
+
const key = `${anchor}:${stop}`;
|
|
344
|
+
const held = long.spans.get(key);
|
|
345
|
+
if (held !== undefined)
|
|
346
|
+
return held;
|
|
347
|
+
const found = definedIn(parseMarkdown(long.text.slice(anchor, stop)).children, anchor);
|
|
348
|
+
long.spans.set(key, found);
|
|
349
|
+
return found;
|
|
350
|
+
}
|
|
351
|
+
function discovered(long, anchor, stop) {
|
|
352
|
+
let discovery = long.discoveries.get(anchor);
|
|
353
|
+
if (discovery === undefined) {
|
|
354
|
+
discovery = { found: [], next: { start: anchor, path: [], join: '', carry: '', levels: [] } };
|
|
355
|
+
long.discoveries.set(anchor, discovery);
|
|
356
|
+
}
|
|
357
|
+
while (discovery.next !== null && discovery.next.start < stop) {
|
|
358
|
+
const from = discovery.next;
|
|
359
|
+
const { pane, cut } = cutWindow(long, from, 'discover', (end) => windowOf(long, from, end, ''));
|
|
360
|
+
const end = cut?.at ?? long.text.length;
|
|
361
|
+
discovery.found.push(...pane.defined.filter((one) => one.at < end));
|
|
362
|
+
discovery.next = cut === null ? null : nextOf(cut);
|
|
363
|
+
}
|
|
364
|
+
return discovery.found;
|
|
365
|
+
}
|
|
366
|
+
function anchorBefore(long, at) {
|
|
367
|
+
let low = 0;
|
|
368
|
+
let high = long.anchors.length - 1;
|
|
369
|
+
while (low < high) {
|
|
370
|
+
const middle = (low + high + 1) >> 1;
|
|
371
|
+
if (long.anchors[middle] <= at)
|
|
372
|
+
low = middle;
|
|
373
|
+
else
|
|
374
|
+
high = middle - 1;
|
|
375
|
+
}
|
|
376
|
+
return long.anchors[low];
|
|
377
|
+
}
|
|
378
|
+
function verified(long, at, key) {
|
|
379
|
+
const line = lineOf(long, at);
|
|
380
|
+
const anchor = anchorBefore(long, long.starts[line]);
|
|
381
|
+
const stop = Math.min(long.starts[long.breaks[line]], long.text.length);
|
|
382
|
+
const found = stop - anchor <= SPAN_CHARS ? spanned(long, anchor, stop) : discovered(long, anchor, stop);
|
|
383
|
+
return found.find((one) => one.key === key && one.at === at)?.url ?? null;
|
|
384
|
+
}
|
|
385
|
+
function realFrom(long, key, position) {
|
|
386
|
+
for (const at of long.candidates.get(key) ?? []) {
|
|
387
|
+
if (at < position)
|
|
388
|
+
continue;
|
|
389
|
+
const url = verified(long, at, key);
|
|
390
|
+
if (url !== null)
|
|
391
|
+
return url;
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
function dummy(key) {
|
|
396
|
+
const label = key.startsWith('^') ? `^${key.slice(1)}` : key;
|
|
397
|
+
const trailing = /\\+$/.exec(label)?.[0].length ?? 0;
|
|
398
|
+
return `[${label}${trailing % 2 === 1 ? ' ' : ''}]: x`;
|
|
399
|
+
}
|
|
400
|
+
function prefixOf(keys) {
|
|
401
|
+
return keys.length === 0 ? '' : `${keys.map(dummy).join('\n')}\n\n***\n\n`;
|
|
402
|
+
}
|
|
403
|
+
function renderWindow(long, from, end) {
|
|
404
|
+
const known = [];
|
|
405
|
+
const deferred = [];
|
|
406
|
+
const seen = new Set();
|
|
407
|
+
for (const found of `${from.carry}${long.text.slice(from.start, end)}`.matchAll(LABEL)) {
|
|
408
|
+
const key = identifierOf(found[1]);
|
|
409
|
+
if (seen.has(key))
|
|
410
|
+
continue;
|
|
411
|
+
seen.add(key);
|
|
412
|
+
if (long.urls.has(key)) {
|
|
413
|
+
known.push(key);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
const first = long.candidates.get(key)?.find((at) => at >= from.start);
|
|
417
|
+
if (first === undefined)
|
|
418
|
+
continue;
|
|
419
|
+
if (first < end) {
|
|
420
|
+
deferred.push(key);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const url = realFrom(long, key, end);
|
|
424
|
+
if (url === null)
|
|
425
|
+
continue;
|
|
426
|
+
long.urls.set(key, url);
|
|
427
|
+
known.push(key);
|
|
428
|
+
}
|
|
429
|
+
const pane = windowOf(long, from, end, prefixOf(known));
|
|
430
|
+
const later = [];
|
|
431
|
+
for (const key of deferred) {
|
|
432
|
+
const native = pane.defined.find((one) => one.key === key);
|
|
433
|
+
if (native !== undefined) {
|
|
434
|
+
long.urls.set(key, native.url);
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const url = realFrom(long, key, from.start);
|
|
438
|
+
if (url === null)
|
|
439
|
+
continue;
|
|
440
|
+
long.urls.set(key, url);
|
|
441
|
+
later.push(key);
|
|
442
|
+
}
|
|
443
|
+
return later.length === 0 ? pane : windowOf(long, from, end, prefixOf([...known, ...later]));
|
|
444
|
+
}
|
|
445
|
+
function windowEnd(long, start, size) {
|
|
446
|
+
const at = start + size;
|
|
447
|
+
if (at >= long.text.length)
|
|
448
|
+
return long.text.length;
|
|
449
|
+
const end = lineEnd(long.starts, long.text, lineOf(long, at));
|
|
450
|
+
return end - at <= VIEW_CHARS ? Math.min(end + 1, long.text.length) : at;
|
|
451
|
+
}
|
|
452
|
+
function nextOf(cut) {
|
|
453
|
+
return { start: cut.at, path: cut.path, join: cut.join, carry: cut.carry, levels: cut.levels };
|
|
454
|
+
}
|
|
455
|
+
function quotesOf(levels) {
|
|
456
|
+
return levels.filter((level) => level.kind === 'quote').length;
|
|
457
|
+
}
|
|
458
|
+
function itemed(levels) {
|
|
459
|
+
return levels.some((level) => level.kind !== 'quote');
|
|
460
|
+
}
|
|
461
|
+
function stepsOf(levels) {
|
|
462
|
+
return levels.flatMap((level) => level.kind === 'quote'
|
|
463
|
+
? ['blockquote']
|
|
464
|
+
: level.kind === 'item'
|
|
465
|
+
? ['list', 'item']
|
|
466
|
+
: ['footnote']);
|
|
467
|
+
}
|
|
468
|
+
function carryOf(levels) {
|
|
469
|
+
return levels.map((level) => level.carry).join('');
|
|
470
|
+
}
|
|
471
|
+
function columnOn(long, index, levels) {
|
|
472
|
+
const raw = lineText(long.starts, long.text, index);
|
|
473
|
+
let at = 0;
|
|
474
|
+
for (const level of levels) {
|
|
475
|
+
if (level.line === index) {
|
|
476
|
+
at = level.column;
|
|
477
|
+
if (at < 0)
|
|
478
|
+
return -1;
|
|
479
|
+
}
|
|
480
|
+
else if (level.kind === 'quote') {
|
|
481
|
+
const marker = QUOTE_MARKER.exec(raw.slice(at));
|
|
482
|
+
if (marker === null)
|
|
483
|
+
return -1;
|
|
484
|
+
at += marker[0].length;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
for (let column = 0; column < level.size; column += 1) {
|
|
488
|
+
if (raw[at + column] !== ' ')
|
|
489
|
+
return -1;
|
|
490
|
+
}
|
|
491
|
+
at += level.size;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return at;
|
|
495
|
+
}
|
|
496
|
+
function withinOf(raw, levels) {
|
|
497
|
+
let rest = raw;
|
|
498
|
+
for (const [index, level] of levels.entries()) {
|
|
499
|
+
if (level.kind === 'quote') {
|
|
500
|
+
const marker = QUOTE_MARKER.exec(rest);
|
|
501
|
+
if (marker === null)
|
|
502
|
+
return SPACES.test(rest) ? 'gone' : 'line';
|
|
503
|
+
rest = rest.slice(marker[0].length);
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (SPACES.test(rest))
|
|
507
|
+
return quotesOf(levels.slice(index)) > 0 ? 'gone' : 'blank';
|
|
508
|
+
if (!rest.startsWith(' '.repeat(level.size)))
|
|
509
|
+
return 'line';
|
|
510
|
+
rest = rest.slice(level.size);
|
|
511
|
+
}
|
|
512
|
+
return SPACES.test(rest) ? 'blank' : 'line';
|
|
513
|
+
}
|
|
514
|
+
function plain(facts) {
|
|
515
|
+
return (!opens(facts) &&
|
|
516
|
+
!TAG.test(facts.content) &&
|
|
517
|
+
!DELIMITER_ROW.test(facts.content) &&
|
|
518
|
+
!UNDERLINE.test(facts.content));
|
|
519
|
+
}
|
|
520
|
+
function spreadScan(long, line, column, size, parents) {
|
|
521
|
+
const { text, starts } = long;
|
|
522
|
+
const lines = starts.length - 1;
|
|
523
|
+
const quotes = quotesOf(parents);
|
|
524
|
+
const breaks = quotes > 0 ? long.quoteBreaks : long.breaks;
|
|
525
|
+
const kind = (index) => withinOf(lineText(starts, text, index), parents);
|
|
526
|
+
let blank = breaks[line + 1];
|
|
527
|
+
while (blank < lines && kind(blank) === 'line')
|
|
528
|
+
blank = breaks[blank + 1];
|
|
529
|
+
if (blank >= lines || kind(blank) === 'gone')
|
|
530
|
+
return false;
|
|
531
|
+
let next = blank + 1;
|
|
532
|
+
while (next < lines && kind(next) === 'blank')
|
|
533
|
+
next += 1;
|
|
534
|
+
if (next >= lines || kind(next) === 'gone')
|
|
535
|
+
return false;
|
|
536
|
+
const at = columnOn(long, next, parents);
|
|
537
|
+
if (at < 0)
|
|
538
|
+
return false;
|
|
539
|
+
const lead = /^[ \t]*/.exec(lineText(starts, text, next).slice(at))[0];
|
|
540
|
+
if (lead.includes('\t'))
|
|
541
|
+
return null;
|
|
542
|
+
if (lead.length < size)
|
|
543
|
+
return false;
|
|
544
|
+
const leads = new RegExp(`^(?: {0,3}> ?){0,${quotes}}`);
|
|
545
|
+
if (!plain(lineFacts(lineText(starts, text, line).slice(column))))
|
|
546
|
+
return null;
|
|
547
|
+
for (let index = line + 1; index < blank; index += 1) {
|
|
548
|
+
const raw = lineText(starts, text, index);
|
|
549
|
+
const held = columnOn(long, index, parents);
|
|
550
|
+
const rest = held < 0 ? raw.replace(leads, '') : raw.slice(held);
|
|
551
|
+
const indent = /^[ \t]*/.exec(rest)[0];
|
|
552
|
+
if (indent.includes('\t'))
|
|
553
|
+
return null;
|
|
554
|
+
const inner = indent.length >= size ? lineFacts(rest.slice(size)) : lineFacts(rest);
|
|
555
|
+
if (indent.length >= size && inner.indent >= 4)
|
|
556
|
+
return null;
|
|
557
|
+
if (!plain(inner))
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
function spreadOf(long, line, column, size, parents) {
|
|
563
|
+
const key = long.starts[line] + column;
|
|
564
|
+
const held = long.spreads.get(key);
|
|
565
|
+
if (held !== undefined)
|
|
566
|
+
return held;
|
|
567
|
+
const spread = spreadScan(long, line, column, size, parents);
|
|
568
|
+
long.spreads.set(key, spread);
|
|
569
|
+
return spread;
|
|
570
|
+
}
|
|
571
|
+
function itemLevel(long, pane, item, outer) {
|
|
572
|
+
const { text, starts } = long;
|
|
573
|
+
const marker = offsetOf(item) + pane.start - pane.lead;
|
|
574
|
+
const line = lineOf(long, marker);
|
|
575
|
+
const start = starts[line];
|
|
576
|
+
const base = columnOn(long, line, outer);
|
|
577
|
+
if (base < 0 || marker < start + base || !/^ *$/.test(text.slice(start + base, marker))) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
const sign = MARKER.exec(text.slice(marker, marker + 11))?.[0];
|
|
581
|
+
if (sign === undefined)
|
|
582
|
+
return null;
|
|
583
|
+
let after = marker + sign.length;
|
|
584
|
+
while (text[after] === ' ')
|
|
585
|
+
after += 1;
|
|
586
|
+
const spaces = after - marker - sign.length;
|
|
587
|
+
if (spaces < 1 || spaces > 4 || after >= lineEnd(starts, text, line))
|
|
588
|
+
return null;
|
|
589
|
+
if (/[\t\r\n]/.test(text[after]))
|
|
590
|
+
return null;
|
|
591
|
+
const column = after - start;
|
|
592
|
+
const size = column - base;
|
|
593
|
+
const spread = item.spread === true
|
|
594
|
+
? true
|
|
595
|
+
: spreadOf(long, line, column, size, outer);
|
|
596
|
+
if (spread === null)
|
|
597
|
+
return null;
|
|
598
|
+
long.spreads.set(start + column, spread);
|
|
599
|
+
const carry = `${' '.repeat(marker - start - base)}${sign}${' '.repeat(spaces)}`;
|
|
600
|
+
return { kind: 'item', line, column, size, carry, spread };
|
|
601
|
+
}
|
|
602
|
+
function noteLevel(long, pane, note, outer) {
|
|
603
|
+
const { text, starts } = long;
|
|
604
|
+
const marker = offsetOf(note) + pane.start - pane.lead;
|
|
605
|
+
const line = lineOf(long, marker);
|
|
606
|
+
const start = starts[line];
|
|
607
|
+
const base = columnOn(long, line, outer);
|
|
608
|
+
if (base < 0 || marker < start + base || !/^ *$/.test(text.slice(start + base, marker))) {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
const label = NOTE.exec(text.slice(marker, marker + 1_004))?.[0];
|
|
612
|
+
if (label === undefined)
|
|
613
|
+
return null;
|
|
614
|
+
let after = marker + label.length;
|
|
615
|
+
while (text[after] === ' ')
|
|
616
|
+
after += 1;
|
|
617
|
+
if (text[after] === '\t')
|
|
618
|
+
return null;
|
|
619
|
+
const column = after - start;
|
|
620
|
+
return { kind: 'note', line, column, size: 4, carry: `${label} `, spread: false };
|
|
621
|
+
}
|
|
622
|
+
function quoteLevel(long, pane, quote) {
|
|
623
|
+
const { text, starts } = long;
|
|
624
|
+
const marker = offsetOf(quote) + pane.start - pane.lead;
|
|
625
|
+
const line = lineOf(long, marker);
|
|
626
|
+
const known = text[marker] === '>' && text[marker + 1] !== '\t';
|
|
627
|
+
const after = text[marker + 1] === ' ' ? marker + 2 : marker + 1;
|
|
628
|
+
return {
|
|
629
|
+
kind: 'quote',
|
|
630
|
+
line,
|
|
631
|
+
column: known ? after - starts[line] : -1,
|
|
632
|
+
size: 0,
|
|
633
|
+
carry: '> ',
|
|
634
|
+
spread: false,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function regionOf(long, line, levels) {
|
|
638
|
+
const quotes = quotesOf(levels);
|
|
639
|
+
const lined = !itemed(levels);
|
|
640
|
+
const stop = (quotes > 0 ? long.quoteBreaks : long.breaks)[line];
|
|
641
|
+
const markers = markersOf(quotes);
|
|
642
|
+
const leads = new RegExp(`^(?: {0,3}> ?){0,${quotes}}`);
|
|
643
|
+
const name = `${levels.map((level) => (level.kind === 'quote' ? '>' : level.size)).join(' ')}:${stop}`;
|
|
644
|
+
const held = long.regions.get(name);
|
|
645
|
+
if (held !== undefined && held.from <= line + 1)
|
|
646
|
+
return held;
|
|
647
|
+
const { text, starts } = long;
|
|
648
|
+
const looks = [];
|
|
649
|
+
for (let index = line + 1; index < stop; index += 1) {
|
|
650
|
+
const raw = lineText(starts, text, index);
|
|
651
|
+
const column = lined ? (markers.exec(raw)?.[0].length ?? -1) : columnOn(long, index, levels);
|
|
652
|
+
const facts = lineFacts(column < 0 ? raw.replace(lined ? markers : leads, '') : raw.slice(column));
|
|
653
|
+
if (facts.indent <= 3 && UNDERLINE.test(facts.content))
|
|
654
|
+
looks.push(index);
|
|
655
|
+
}
|
|
656
|
+
const from = starts[line];
|
|
657
|
+
const to = Math.min(starts[stop], text.length);
|
|
658
|
+
const last = new Map();
|
|
659
|
+
for (const one of ['*', '_', '~', ']', '>'])
|
|
660
|
+
last.set(one, text.lastIndexOf(one, to - 1));
|
|
661
|
+
const runs = new Map();
|
|
662
|
+
for (let at = text.indexOf('`', from); at !== -1 && at < to;) {
|
|
663
|
+
let end = at;
|
|
664
|
+
while (text[end] === '`')
|
|
665
|
+
end += 1;
|
|
666
|
+
runs.set(end - at, at);
|
|
667
|
+
at = text.indexOf('`', end);
|
|
668
|
+
}
|
|
669
|
+
const region = { from: line + 1, looks, last, runs };
|
|
670
|
+
long.regions.set(name, region);
|
|
671
|
+
return region;
|
|
672
|
+
}
|
|
673
|
+
function after(lines, line) {
|
|
674
|
+
let low = 0;
|
|
675
|
+
let high = lines.length;
|
|
676
|
+
while (low < high) {
|
|
677
|
+
const middle = (low + high) >> 1;
|
|
678
|
+
if (lines[middle] <= line)
|
|
679
|
+
low = middle + 1;
|
|
680
|
+
else
|
|
681
|
+
high = middle;
|
|
682
|
+
}
|
|
683
|
+
return lines[low] ?? Infinity;
|
|
684
|
+
}
|
|
685
|
+
const CODE = 1;
|
|
686
|
+
const LINKED = 2;
|
|
687
|
+
const EMPHASIZED = 4;
|
|
688
|
+
function marked(node, flags, shift, start, text) {
|
|
689
|
+
if (!('children' in node))
|
|
690
|
+
return;
|
|
691
|
+
for (const child of node.children) {
|
|
692
|
+
const from = Math.max(offsetOf(child) + shift - start, 0);
|
|
693
|
+
const to = Math.min(endOf(child) + shift - start, flags.length);
|
|
694
|
+
const flag = child.type === 'inlineCode' || child.type === 'html'
|
|
695
|
+
? CODE
|
|
696
|
+
: child.type === 'link'
|
|
697
|
+
? text[offsetOf(child) + shift] === '<'
|
|
698
|
+
? CODE
|
|
699
|
+
: LINKED
|
|
700
|
+
: child.type === 'image' ||
|
|
701
|
+
child.type === 'linkReference' ||
|
|
702
|
+
child.type === 'imageReference' ||
|
|
703
|
+
child.type === 'footnoteReference'
|
|
704
|
+
? LINKED
|
|
705
|
+
: child.type === 'emphasis' || child.type === 'strong' || child.type === 'delete'
|
|
706
|
+
? EMPHASIZED
|
|
707
|
+
: 0;
|
|
708
|
+
for (let at = from; flag !== 0 && at < to; at += 1)
|
|
709
|
+
flags[at] |= flag;
|
|
710
|
+
marked(child, flags, shift, start, text);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function opener(text, from, to, paragraph) {
|
|
714
|
+
const before = from === paragraph ? ' ' : (text[from - 1] ?? ' ');
|
|
715
|
+
const after = text[to] ?? ' ';
|
|
716
|
+
const space = (one) => SPACE.test(one);
|
|
717
|
+
const mark = (one) => PUNCTUATION.test(one);
|
|
718
|
+
const left = !space(after) && (!mark(after) || space(before) || mark(before));
|
|
719
|
+
if (text[from] !== '_')
|
|
720
|
+
return left;
|
|
721
|
+
const right = !space(before) && (!mark(before) || space(after) || mark(after));
|
|
722
|
+
return left && (!right || mark(before));
|
|
723
|
+
}
|
|
724
|
+
function pendingAt(long, pane, paragraph, start, region) {
|
|
725
|
+
const { text } = long;
|
|
726
|
+
const shift = pane.start - pane.lead;
|
|
727
|
+
const flags = new Uint8Array(pane.end - start);
|
|
728
|
+
marked(paragraph, flags, shift, start, text);
|
|
729
|
+
for (let at = start; at < pane.end; at += 1) {
|
|
730
|
+
if ((flags[at - start] & CODE) !== 0)
|
|
731
|
+
continue;
|
|
732
|
+
const one = text[at];
|
|
733
|
+
if (one === '\\' && ESCAPABLE.test(text[at + 1] ?? '')) {
|
|
734
|
+
at += 1;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (one === '`') {
|
|
738
|
+
let end = at;
|
|
739
|
+
while (text[end] === '`')
|
|
740
|
+
end += 1;
|
|
741
|
+
if ((region.runs.get(end - at) ?? -1) > at)
|
|
742
|
+
return at;
|
|
743
|
+
at = end - 1;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (one === '<' && TAG_START.test(text[at + 1] ?? '') && region.last.get('>') > at)
|
|
747
|
+
return at;
|
|
748
|
+
const settled = flags[at - start];
|
|
749
|
+
if (one === '[' && (settled & LINKED) === 0 && region.last.get(']') > at)
|
|
750
|
+
return at;
|
|
751
|
+
if ((one === '*' || one === '_' || one === '~') && (settled & (LINKED | EMPHASIZED)) === 0) {
|
|
752
|
+
let end = at;
|
|
753
|
+
while (text[end] === one)
|
|
754
|
+
end += 1;
|
|
755
|
+
if (opener(text, at, end, start) && region.last.get(one) >= end)
|
|
756
|
+
return at;
|
|
757
|
+
at = end - 1;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return Infinity;
|
|
761
|
+
}
|
|
762
|
+
function restartable(text, at) {
|
|
763
|
+
const first = text[at];
|
|
764
|
+
ORDERED.lastIndex = at;
|
|
765
|
+
if (first === undefined || BLOCK_START.test(first) || ORDERED.test(text))
|
|
766
|
+
return false;
|
|
767
|
+
DEFINED.lastIndex = at;
|
|
768
|
+
return first !== '[' || !DEFINED.test(text);
|
|
769
|
+
}
|
|
770
|
+
function inside(spans, at) {
|
|
771
|
+
return spans.some(([from, to]) => from < at && at < to);
|
|
772
|
+
}
|
|
773
|
+
function markersOf(depth) {
|
|
774
|
+
return new RegExp(`^(?: {0,3}> ?){${depth}}`);
|
|
775
|
+
}
|
|
776
|
+
function spreadAlong(nodes, levels) {
|
|
777
|
+
let node = nodes.at(-1);
|
|
778
|
+
for (const level of levels) {
|
|
779
|
+
if (node === undefined || !('children' in node))
|
|
780
|
+
break;
|
|
781
|
+
if (level.kind !== 'item') {
|
|
782
|
+
node = node.children.at(-1);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
const item = node.children.at(-1);
|
|
786
|
+
if (node.type !== 'list' || item?.type !== 'listItem')
|
|
787
|
+
break;
|
|
788
|
+
item.spread = level.spread;
|
|
789
|
+
node = item.children.at(-1);
|
|
790
|
+
}
|
|
791
|
+
return nodes;
|
|
792
|
+
}
|
|
793
|
+
function openable(long, line, cut, begun, frame) {
|
|
794
|
+
const { text, starts } = long;
|
|
795
|
+
if (!/^ *$/.test(text.slice(cut, begun)))
|
|
796
|
+
return false;
|
|
797
|
+
const content = lineText(starts, text, line).slice(cut - starts[line]);
|
|
798
|
+
const inner = frame.at(-1).kind;
|
|
799
|
+
if (inner !== 'quote' && cut !== begun)
|
|
800
|
+
return false;
|
|
801
|
+
if (inner === 'item' && TASK.test(content))
|
|
802
|
+
return false;
|
|
803
|
+
return frame.every((_, index) => {
|
|
804
|
+
const facts = lineFacts(`${carryOf(frame.slice(index))}${content}`);
|
|
805
|
+
return facts.indent > 3 || !THEMATIC.test(facts.content);
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
function paragraphCuts(long, pane, paragraph, levels, mode) {
|
|
809
|
+
const { text, starts } = long;
|
|
810
|
+
const quotes = quotesOf(levels);
|
|
811
|
+
const lined = !itemed(levels);
|
|
812
|
+
const markers = markersOf(quotes);
|
|
813
|
+
const leads = new RegExp(`^(?: {0,3}> ?){0,${quotes}}`);
|
|
814
|
+
const carry = carryOf(levels);
|
|
815
|
+
const shift = pane.start - pane.lead;
|
|
816
|
+
const start = Math.max(offsetOf(paragraph) + shift, pane.start);
|
|
817
|
+
const first = lineOf(long, start);
|
|
818
|
+
const region = regionOf(long, first, levels);
|
|
819
|
+
if (after(region.looks, first) !== Infinity)
|
|
820
|
+
return [];
|
|
821
|
+
const pending = mode === 'render' ? pendingAt(long, pane, paragraph, start, region) : Infinity;
|
|
822
|
+
const spans = mode === 'render'
|
|
823
|
+
? paragraph.children
|
|
824
|
+
.filter((child) => child.type !== 'text')
|
|
825
|
+
.map((child) => [offsetOf(child) + shift, endOf(child) + shift])
|
|
826
|
+
: [];
|
|
827
|
+
const last = lineOf(long, pane.end - 1);
|
|
828
|
+
const lines = starts.length - 1;
|
|
829
|
+
const path = [...stepsOf(levels), 'paragraph'];
|
|
830
|
+
const reparsed = (at) => () => spreadAlong(windowOf(long, { start: pane.start, path: [], join: '', carry: pane.carry, levels: pane.levels }, at, pane.prefix).nodes, levels);
|
|
831
|
+
const cuts = [];
|
|
832
|
+
const settled = (at) => at > start && at < pending && !inside(spans, at);
|
|
833
|
+
const restOf = (index) => {
|
|
834
|
+
const raw = lineText(starts, text, index);
|
|
835
|
+
const column = lined ? -1 : columnOn(long, index, levels);
|
|
836
|
+
return column < 0 ? raw.replace(lined ? markers : leads, '') : raw.slice(column);
|
|
837
|
+
};
|
|
838
|
+
const unbroken = (index) => index >= lines || !DELIMITER_ROW.test(lineFacts(restOf(index)).content);
|
|
839
|
+
const contentOf = (index) => {
|
|
840
|
+
const raw = lineText(starts, text, index);
|
|
841
|
+
const column = lined ? -1 : columnOn(long, index, levels);
|
|
842
|
+
return starts[index] + (column < 0 ? leads.exec(raw)[0].length : column);
|
|
843
|
+
};
|
|
844
|
+
const fresh = (at) => levels.at(-1)?.kind !== 'item' || !TASK.test(text.slice(at, at + 3));
|
|
845
|
+
for (let index = first + 1; index <= last; index += 1) {
|
|
846
|
+
const line = starts[index];
|
|
847
|
+
if (line >= pending)
|
|
848
|
+
break;
|
|
849
|
+
const whole = lined && markers.test(lineText(starts, text, index));
|
|
850
|
+
const content = contentOf(index);
|
|
851
|
+
const at = whole ? line : content;
|
|
852
|
+
const previous = lineText(starts, text, index - 1);
|
|
853
|
+
if (!restartable(text, content) || !fresh(content) || /[ \t\\]$/.test(previous))
|
|
854
|
+
continue;
|
|
855
|
+
if (!settled(at) || !unbroken(index + 1))
|
|
856
|
+
continue;
|
|
857
|
+
const join = text.slice(starts[index - 1] + previous.length, line);
|
|
858
|
+
cuts.push({ at, path, join, carry: whole ? '' : carry, levels, nodes: reparsed(at) });
|
|
859
|
+
}
|
|
860
|
+
if (cuts.length > 0)
|
|
861
|
+
return cuts;
|
|
862
|
+
for (let index = first; index <= last; index += 1) {
|
|
863
|
+
const content = contentOf(index);
|
|
864
|
+
const end = Math.min(lineEnd(starts, text, index), pane.end);
|
|
865
|
+
for (let at = Math.max(content + 1, start + 1); at < end; at += 1) {
|
|
866
|
+
if (at >= pending)
|
|
867
|
+
return cuts;
|
|
868
|
+
if (!/[ \t]/.test(text[at - 1]) || !restartable(text, at) || !settled(at))
|
|
869
|
+
continue;
|
|
870
|
+
if (!fresh(at))
|
|
871
|
+
continue;
|
|
872
|
+
if (!unbroken(index + 1))
|
|
873
|
+
break;
|
|
874
|
+
let from = at - 1;
|
|
875
|
+
while (from > content && /[ \t]/.test(text[from - 1]))
|
|
876
|
+
from -= 1;
|
|
877
|
+
if (from === content)
|
|
878
|
+
continue;
|
|
879
|
+
cuts.push({ at, path, join: text.slice(from, at), carry, levels, nodes: reparsed(at) });
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return cuts;
|
|
883
|
+
}
|
|
884
|
+
function cutsOf(long, pane, mode) {
|
|
885
|
+
const { nodes } = pane;
|
|
886
|
+
const shift = pane.start - pane.lead;
|
|
887
|
+
const at = (node) => lineStartOf(long, offsetOf(node) + shift);
|
|
888
|
+
const cuts = [];
|
|
889
|
+
for (let index = 1; index < nodes.length; index += 1) {
|
|
890
|
+
const start = at(nodes[index]);
|
|
891
|
+
if (start > pane.start) {
|
|
892
|
+
const taken = nodes.slice(0, index);
|
|
893
|
+
cuts.push({ at: start, path: [], join: '', carry: '', levels: [], nodes: () => taken });
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
const last = nodes.at(-1);
|
|
897
|
+
if (last === undefined)
|
|
898
|
+
return cuts;
|
|
899
|
+
const before = nodes.slice(0, -1);
|
|
900
|
+
const chain = [];
|
|
901
|
+
const levels = [];
|
|
902
|
+
const spreads = new Map();
|
|
903
|
+
const within = (parent, path, frame, header) => {
|
|
904
|
+
const held = [...chain, parent];
|
|
905
|
+
const lined = !itemed(frame);
|
|
906
|
+
const marked = markersOf(quotesOf(frame));
|
|
907
|
+
for (let index = 1; index < parent.children.length; index += 1) {
|
|
908
|
+
const begun = offsetOf(parent.children[index]) + shift;
|
|
909
|
+
const line = lineOf(long, begun);
|
|
910
|
+
let cut = long.starts[line];
|
|
911
|
+
if (lined) {
|
|
912
|
+
if (!marked.test(lineText(long.starts, long.text, line)))
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
const column = columnOn(long, line, frame);
|
|
917
|
+
if (column < 0)
|
|
918
|
+
continue;
|
|
919
|
+
if (header === null) {
|
|
920
|
+
cut += column;
|
|
921
|
+
if (!openable(long, line, cut, begun, frame))
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (cut <= pane.start)
|
|
926
|
+
continue;
|
|
927
|
+
const taken = [...before, truncated(held, index, spreads)];
|
|
928
|
+
const carry = header ?? (lined ? '' : carryOf(frame));
|
|
929
|
+
cuts.push({ at: cut, path, join: '', carry, levels: frame, nodes: () => taken });
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
const carried = (node) => offsetOf(node) < pane.lead;
|
|
933
|
+
let node = last;
|
|
934
|
+
while (node !== undefined) {
|
|
935
|
+
if (node.type === 'paragraph') {
|
|
936
|
+
cuts.push(...paragraphCuts(long, pane, node, levels, mode));
|
|
937
|
+
}
|
|
938
|
+
if (node.type === 'table' && node.children.length > 1) {
|
|
939
|
+
const begun = offsetOf(node) + shift;
|
|
940
|
+
const lined = !itemed(levels);
|
|
941
|
+
const header = begun < pane.start
|
|
942
|
+
? pane.carry
|
|
943
|
+
: lined
|
|
944
|
+
? long.text.slice(at(node), at(node.children[1]))
|
|
945
|
+
: long.text[begun] === ' '
|
|
946
|
+
? null
|
|
947
|
+
: `${carryOf(levels)}${long.text.slice(begun, at(node.children[1]))}`;
|
|
948
|
+
if (header !== null)
|
|
949
|
+
within(node, [...stepsOf(levels), 'table'], levels, header);
|
|
950
|
+
}
|
|
951
|
+
if (node.type === 'list') {
|
|
952
|
+
within(node, [...stepsOf(levels), 'list'], levels, null);
|
|
953
|
+
const item = node.children.at(-1);
|
|
954
|
+
if (item === undefined)
|
|
955
|
+
break;
|
|
956
|
+
const level = carried(item)
|
|
957
|
+
? pane.levels[levels.length]
|
|
958
|
+
: itemLevel(long, pane, item, levels);
|
|
959
|
+
if (level === undefined || level === null || level.kind !== 'item')
|
|
960
|
+
break;
|
|
961
|
+
chain.push(node);
|
|
962
|
+
levels.push(level);
|
|
963
|
+
spreads.set(item, level.spread);
|
|
964
|
+
within(item, stepsOf(levels), levels, null);
|
|
965
|
+
chain.push(item);
|
|
966
|
+
node = item.children.at(-1);
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
if (node.type === 'footnoteDefinition') {
|
|
970
|
+
const level = carried(node)
|
|
971
|
+
? pane.levels[levels.length]
|
|
972
|
+
: noteLevel(long, pane, node, levels);
|
|
973
|
+
if (level === undefined || level === null || level.kind !== 'note')
|
|
974
|
+
break;
|
|
975
|
+
levels.push(level);
|
|
976
|
+
within(node, stepsOf(levels), levels, null);
|
|
977
|
+
chain.push(node);
|
|
978
|
+
node = node.children.at(-1);
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (node.type !== 'blockquote')
|
|
982
|
+
break;
|
|
983
|
+
const level = carried(node) ? pane.levels[levels.length] : quoteLevel(long, pane, node);
|
|
984
|
+
if (level === undefined || level.kind !== 'quote')
|
|
985
|
+
break;
|
|
986
|
+
levels.push(level);
|
|
987
|
+
within(node, stepsOf(levels), levels, null);
|
|
988
|
+
chain.push(node);
|
|
989
|
+
node = node.children.at(-1);
|
|
990
|
+
}
|
|
991
|
+
return cuts;
|
|
992
|
+
}
|
|
993
|
+
function truncated(chain, index, spreads) {
|
|
994
|
+
const [outer, ...inner] = chain;
|
|
995
|
+
const spread = spreads.get(outer);
|
|
996
|
+
const kept = spread === undefined ? {} : { spread };
|
|
997
|
+
if (inner.length === 0) {
|
|
998
|
+
return { ...outer, ...kept, children: outer.children.slice(0, index) };
|
|
999
|
+
}
|
|
1000
|
+
const children = outer.children.slice(0, -1);
|
|
1001
|
+
return {
|
|
1002
|
+
...outer,
|
|
1003
|
+
...kept,
|
|
1004
|
+
children: [...children, truncated(inner, index, spreads)],
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
function chosen(cuts, target) {
|
|
1008
|
+
let best = null;
|
|
1009
|
+
for (const cut of cuts) {
|
|
1010
|
+
if (best === null)
|
|
1011
|
+
best = cut;
|
|
1012
|
+
else if (cut.at >= target) {
|
|
1013
|
+
if (best.at < target || cut.at < best.at)
|
|
1014
|
+
best = cut;
|
|
1015
|
+
}
|
|
1016
|
+
else if (best.at < target && cut.at > best.at)
|
|
1017
|
+
best = cut;
|
|
1018
|
+
}
|
|
1019
|
+
return best;
|
|
1020
|
+
}
|
|
1021
|
+
function closingOf(long, pane) {
|
|
1022
|
+
const last = pane.nodes.at(-1);
|
|
1023
|
+
if (last?.type !== 'code')
|
|
1024
|
+
return 0;
|
|
1025
|
+
const { text, starts } = long;
|
|
1026
|
+
const line = lineOf(long, offsetOf(last) + pane.start - pane.lead);
|
|
1027
|
+
const fence = fenceOf(lineFacts(lineText(starts, text, line)));
|
|
1028
|
+
if (fence === null)
|
|
1029
|
+
return 0;
|
|
1030
|
+
const open = { ...START, fence };
|
|
1031
|
+
for (let index = line + 1; index < starts.length - 1; index += 1) {
|
|
1032
|
+
if (fenced(open, lineFacts(lineText(starts, text, index))).fence === null) {
|
|
1033
|
+
return windowEnd(long, lineEnd(starts, text, index) + 1, WINDOW_CHARS);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return text.length;
|
|
1037
|
+
}
|
|
1038
|
+
function cutWindow(long, from, mode, open) {
|
|
1039
|
+
const line = Math.min(lineOf(long, from.start) + WINDOW_LINES, long.starts.length - 1);
|
|
1040
|
+
let end = windowEnd(long, from.start, Math.min(WINDOW_CHARS, long.starts[line] - from.start));
|
|
1041
|
+
for (;;) {
|
|
1042
|
+
const pane = open(end);
|
|
1043
|
+
if (end >= long.text.length)
|
|
1044
|
+
return { pane, cut: null };
|
|
1045
|
+
const cut = chosen(cutsOf(long, pane, mode), from.start + VIEW_CHARS);
|
|
1046
|
+
if (cut !== null)
|
|
1047
|
+
return { pane, cut };
|
|
1048
|
+
const grown = windowEnd(long, from.start, 2 * (end - from.start));
|
|
1049
|
+
const whole = 2 * grown - from.start >= long.text.length;
|
|
1050
|
+
end = Math.max(whole ? long.text.length : grown, closingOf(long, pane));
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
export function pieceAt(long, index) {
|
|
1054
|
+
while (long.pieces.length <= index && long.next !== null) {
|
|
1055
|
+
const from = long.next;
|
|
1056
|
+
const { pane, cut } = cutWindow(long, from, 'render', (end) => renderWindow(long, from, end));
|
|
1057
|
+
const end = cut?.at ?? long.text.length;
|
|
1058
|
+
for (const one of pane.defined) {
|
|
1059
|
+
if (one.at < end && !long.urls.has(one.key))
|
|
1060
|
+
long.urls.set(one.key, one.url);
|
|
1061
|
+
}
|
|
1062
|
+
const nodes = cut === null ? pane.nodes : cut.nodes();
|
|
1063
|
+
long.pieces.push({ nodes, path: from.path, join: from.join });
|
|
1064
|
+
long.next = cut === null ? null : nextOf(cut);
|
|
1065
|
+
}
|
|
1066
|
+
return long.pieces[index] ?? null;
|
|
1067
|
+
}
|
|
1068
|
+
function opened(node, made) {
|
|
1069
|
+
if (made.has(node))
|
|
1070
|
+
return node;
|
|
1071
|
+
const copy = { ...node, children: [...node.children] };
|
|
1072
|
+
made.add(copy);
|
|
1073
|
+
return copy;
|
|
1074
|
+
}
|
|
1075
|
+
function continued(into, more, path, join, made) {
|
|
1076
|
+
const [step, ...deeper] = path;
|
|
1077
|
+
const tail = (step === 'table' ? more.children.slice(1) : more.children);
|
|
1078
|
+
const merged = opened(into, made);
|
|
1079
|
+
const held = merged.children;
|
|
1080
|
+
if (step === 'paragraph')
|
|
1081
|
+
held.push({ type: 'text', value: join }, ...tail);
|
|
1082
|
+
else if (deeper.length === 0)
|
|
1083
|
+
held.push(...tail);
|
|
1084
|
+
else {
|
|
1085
|
+
const [head, ...rest] = tail;
|
|
1086
|
+
held.push(continued(held.pop(), head, deeper, join, made), ...rest);
|
|
1087
|
+
}
|
|
1088
|
+
return merged;
|
|
1089
|
+
}
|
|
1090
|
+
export function drawn(long, count) {
|
|
1091
|
+
const nodes = [];
|
|
1092
|
+
const made = new Set();
|
|
1093
|
+
for (let index = 0; index < count; index += 1) {
|
|
1094
|
+
const piece = pieceAt(long, index);
|
|
1095
|
+
if (piece === null)
|
|
1096
|
+
break;
|
|
1097
|
+
const [first, ...rest] = piece.nodes;
|
|
1098
|
+
const last = nodes.at(-1);
|
|
1099
|
+
if (piece.path.length === 0 || first === undefined || last === undefined) {
|
|
1100
|
+
nodes.push(...piece.nodes);
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
nodes.pop();
|
|
1104
|
+
nodes.push(continued(last, first, piece.path, piece.join, made), ...rest);
|
|
1105
|
+
}
|
|
1106
|
+
return nodes;
|
|
1107
|
+
}
|
|
1108
|
+
export function remaining(long, count) {
|
|
1109
|
+
return long.pieces.length > count || (long.pieces.length === count && long.next !== null);
|
|
1110
|
+
}
|