@descent-vtt/spec-harness 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/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +17 -0
- package/.mcp.json +12 -0
- package/CHANGELOG.md +46 -0
- package/README.md +127 -9
- package/bin/spec-harness.js +19 -0
- package/dist/audit.d.ts +69 -0
- package/dist/audit.js +93 -0
- package/dist/audit.js.map +1 -0
- package/dist/branch.d.ts +26 -0
- package/dist/branch.js +100 -0
- package/dist/branch.js.map +1 -0
- package/dist/briefs.d.ts +49 -0
- package/dist/briefs.js +111 -0
- package/dist/briefs.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +245 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +18 -0
- package/dist/commands.js +224 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.js +167 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +56 -0
- package/dist/context.js +125 -0
- package/dist/context.js.map +1 -0
- package/dist/document.d.ts +33 -0
- package/dist/document.js +9 -0
- package/dist/document.js.map +1 -0
- package/dist/fs.d.ts +29 -0
- package/dist/fs.js +85 -0
- package/dist/fs.js.map +1 -0
- package/dist/git.d.ts +57 -0
- package/dist/git.js +129 -0
- package/dist/git.js.map +1 -0
- package/dist/guard.d.ts +43 -0
- package/dist/guard.js +120 -0
- package/dist/guard.js.map +1 -0
- package/dist/hooks.d.ts +50 -0
- package/dist/hooks.js +109 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/junit.d.ts +30 -0
- package/dist/junit.js +166 -0
- package/dist/junit.js.map +1 -0
- package/dist/manifests.d.ts +52 -0
- package/dist/manifests.js +346 -0
- package/dist/manifests.js.map +1 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +79 -0
- package/dist/plugin.js.map +1 -0
- package/dist/premises.d.ts +16 -0
- package/dist/premises.js +71 -0
- package/dist/premises.js.map +1 -0
- package/dist/probe.d.ts +108 -0
- package/dist/probe.js +203 -0
- package/dist/probe.js.map +1 -0
- package/dist/reader.d.ts +8 -0
- package/dist/reader.js +105 -0
- package/dist/reader.js.map +1 -0
- package/dist/round.d.ts +68 -0
- package/dist/round.js +339 -0
- package/dist/round.js.map +1 -0
- package/dist/rulings.d.ts +92 -0
- package/dist/rulings.js +160 -0
- package/dist/rulings.js.map +1 -0
- package/dist/sandbox.d.ts +28 -0
- package/dist/sandbox.js +110 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +212 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +32 -0
- package/dist/setup.js +218 -0
- package/dist/setup.js.map +1 -0
- package/dist/siblings.d.ts +66 -0
- package/dist/siblings.js +134 -0
- package/dist/siblings.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vendor/spec-core/jsonrpc/index.d.ts +2 -0
- package/dist/vendor/spec-core/jsonrpc/index.js +2 -0
- package/dist/vendor/spec-core/jsonrpc/index.js.map +1 -0
- package/dist/vendor/spec-core/jsonrpc/mcp.d.ts +157 -0
- package/dist/vendor/spec-core/jsonrpc/mcp.js +353 -0
- package/dist/vendor/spec-core/jsonrpc/mcp.js.map +1 -0
- package/dist/vendor/spec-core/markdown/frontmatter.d.ts +103 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
- package/dist/vendor/spec-core/markdown/headings.d.ts +45 -0
- package/dist/vendor/spec-core/markdown/headings.js +199 -0
- package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
- package/dist/vendor/spec-core/markdown/index.d.ts +5 -0
- package/dist/vendor/spec-core/markdown/index.js +4 -0
- package/dist/vendor/spec-core/markdown/index.js.map +1 -0
- package/dist/vendor/spec-core/markdown/layout.d.ts +39 -0
- package/dist/vendor/spec-core/markdown/layout.js +28 -0
- package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
- package/dist/vendor/spec-core/markdown/links.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/links.js +364 -0
- package/dist/vendor/spec-core/markdown/links.js.map +1 -0
- package/dist/vendor/spec-core/markdown/lists.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/lists.js +127 -0
- package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
- package/dist/vendor/spec-core/markdown/scan.d.ts +36 -0
- package/dist/vendor/spec-core/markdown/scan.js +439 -0
- package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
- package/dist/vendor/spec-core/markdown/syntax.d.ts +75 -0
- package/dist/vendor/spec-core/markdown/syntax.js +181 -0
- package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
- package/dist/vendor/spec-core/markdown/tables.d.ts +18 -0
- package/dist/vendor/spec-core/markdown/tables.js +86 -0
- package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
- package/dist/vendor/spec-core/markdown/types.d.ts +287 -0
- package/dist/vendor/spec-core/markdown/types.js +9 -0
- package/dist/vendor/spec-core/markdown/types.js.map +1 -0
- package/dist/vendor/spec-core/path/index.d.ts +1 -0
- package/dist/vendor/spec-core/path/index.js +2 -0
- package/dist/vendor/spec-core/path/index.js.map +1 -0
- package/dist/vendor/spec-core/path/posix.d.ts +83 -0
- package/dist/vendor/spec-core/path/posix.js +150 -0
- package/dist/vendor/spec-core/path/posix.js.map +1 -0
- package/dist/vendor/spec-core/pattern/automaton.d.ts +129 -0
- package/dist/vendor/spec-core/pattern/automaton.js +340 -0
- package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
- package/dist/vendor/spec-core/pattern/charset.d.ts +58 -0
- package/dist/vendor/spec-core/pattern/charset.js +116 -0
- package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
- package/dist/vendor/spec-core/pattern/glob.d.ts +121 -0
- package/dist/vendor/spec-core/pattern/glob.js +481 -0
- package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
- package/dist/vendor/spec-core/pattern/index.d.ts +6 -0
- package/dist/vendor/spec-core/pattern/index.js +4 -0
- package/dist/vendor/spec-core/pattern/index.js.map +1 -0
- package/dist/vendor/spec-core/pattern/regex.d.ts +67 -0
- package/dist/vendor/spec-core/pattern/regex.js +813 -0
- package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
- package/dist/vendor/spec-core/text/index.d.ts +2 -0
- package/dist/vendor/spec-core/text/index.js +2 -0
- package/dist/vendor/spec-core/text/index.js.map +1 -0
- package/dist/vendor/spec-core/text/lines.d.ts +72 -0
- package/dist/vendor/spec-core/text/lines.js +156 -0
- package/dist/vendor/spec-core/text/lines.js.map +1 -0
- package/dist/versions.d.ts +42 -0
- package/dist/versions.js +57 -0
- package/dist/versions.js.map +1 -0
- package/dist/workspace.d.ts +77 -0
- package/dist/workspace.js +138 -0
- package/dist/workspace.js.map +1 -0
- package/hooks/hooks.json +28 -0
- package/package.json +86 -6
- package/skills/close-round/SKILL.md +38 -0
- package/skills/draft-brief/SKILL.md +64 -0
- package/skills/run-round/SKILL.md +46 -0
- package/skills/split-goal/SKILL.md +42 -0
- package/src/vendor/spec-core/LICENSE +21 -0
- package/src/vendor/spec-core/README.md +8 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links, images and reference definitions, read from the structure mask so
|
|
3
|
+
* that nothing in code or a comment is one.
|
|
4
|
+
*
|
|
5
|
+
* Six forms: inline `[text](dest "title")`, full and collapsed reference
|
|
6
|
+
* `[text][label]` and `[label][]`, shortcut `[label]`, autolink `<https://x>`,
|
|
7
|
+
* wiki `[[target|text]]`, and the definition `[label]: dest` the reference
|
|
8
|
+
* forms are read through. Each may be an image, written with a leading `!`.
|
|
9
|
+
*
|
|
10
|
+
* Brackets are paired once per paragraph with a stack, and the tables that
|
|
11
|
+
* say where a destination or a title ends are built once per paragraph, so
|
|
12
|
+
* no bracket searches the text again for its partner. A document of nothing
|
|
13
|
+
* but brackets costs its length.
|
|
14
|
+
*
|
|
15
|
+
* A paragraph ends at `to`, and the character there is a line terminator,
|
|
16
|
+
* which no loop here acts on; nor is any lookup made at the first three
|
|
17
|
+
* offsets of a paragraph, before the shortest `[](` could end. Mutants that
|
|
18
|
+
* move a bound onto those places change no answer, and are equivalent.
|
|
19
|
+
*/
|
|
20
|
+
import { inRanges, mergeRanges } from '../text/index.js';
|
|
21
|
+
import { isMarkdown, isSpaceOrTab, visibleLead } from './layout.js';
|
|
22
|
+
const BANG = 33;
|
|
23
|
+
const DOUBLE_QUOTE = 34;
|
|
24
|
+
const SINGLE_QUOTE = 39;
|
|
25
|
+
const OPEN_PAREN = 40;
|
|
26
|
+
const CLOSE_PAREN = 41;
|
|
27
|
+
const LESS_THAN = 60;
|
|
28
|
+
const GREATER_THAN = 62;
|
|
29
|
+
const OPEN_BRACKET = 91;
|
|
30
|
+
const BACKSLASH = 92;
|
|
31
|
+
const CLOSE_BRACKET = 93;
|
|
32
|
+
const LF = 10;
|
|
33
|
+
const CR = 13;
|
|
34
|
+
/** CommonMark: a link label holds at most 999 characters. */
|
|
35
|
+
const MAX_LABEL = 999;
|
|
36
|
+
const DEFINITION = /^ {0,3}\[(?!\^)([^\]]+)\]:[ \t]*(<[^<>]*>|\S+)/;
|
|
37
|
+
/** What may follow a definition's destination: nothing, or a title after whitespace. */
|
|
38
|
+
const TITLE_ONLY = /^(?:[ \t]+(?:"[^"]*"|'[^']*'|\([^)]*\)))?[ \t]*$/;
|
|
39
|
+
const AUTOLINK = /<((?:https?|ftp|mailto):[^<>\s]+)>/g;
|
|
40
|
+
export function findLinks(layout) {
|
|
41
|
+
const definitions = new Map();
|
|
42
|
+
const defined = readDefinitions(layout, definitions);
|
|
43
|
+
const bracketed = readBrackets(layout, definitions, new Set(defined.map((d) => d.line)));
|
|
44
|
+
// `<https://x>` as a link's destination or text is part of that link.
|
|
45
|
+
const taken = mergeRanges([...defined, ...bracketed]);
|
|
46
|
+
const autolinks = readAutolinks(layout).filter((link) => !inRanges(taken, link.start));
|
|
47
|
+
return [...defined, ...bracketed, ...autolinks].sort((a, b) => a.start - b.start);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* How labels compare: trimmed, whitespace collapsed, and case folded as
|
|
51
|
+
* CommonMark's reference implementations fold it, down and then up, so that
|
|
52
|
+
* `ẞ` and `SS` are one label.
|
|
53
|
+
*/
|
|
54
|
+
function normalizeLabel(label) {
|
|
55
|
+
return label.trim().replace(/\s+/g, ' ').toLowerCase().toUpperCase();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Definitions come first: the reference forms are read through them, and a
|
|
59
|
+
* definition is conventionally written at the foot of the file, after the
|
|
60
|
+
* links that use it. The first definition of a label wins.
|
|
61
|
+
*
|
|
62
|
+
* `[Note]: this matters` is prose that looks like one, so a definition has
|
|
63
|
+
* nothing after its destination but a title. `[^1]: text` is a footnote.
|
|
64
|
+
*/
|
|
65
|
+
function readDefinitions(layout, definitions) {
|
|
66
|
+
const { text, structure } = layout;
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const line of layout.lines) {
|
|
69
|
+
if (!isMarkdown(line) || !visibleLead(layout, line))
|
|
70
|
+
continue;
|
|
71
|
+
const masked = structure.slice(line.contentStart, line.end);
|
|
72
|
+
const match = DEFINITION.exec(masked);
|
|
73
|
+
if (match === null || !TITLE_ONLY.test(masked.slice(match[0].length)))
|
|
74
|
+
continue;
|
|
75
|
+
const start = line.contentStart + match[0].indexOf('[');
|
|
76
|
+
const label = text.slice(start + 1, start + 1 + match[1].length).trim();
|
|
77
|
+
if (label.length === 0)
|
|
78
|
+
continue;
|
|
79
|
+
const written = match[2];
|
|
80
|
+
const end = line.contentStart + match[0].length;
|
|
81
|
+
const angle = written.charCodeAt(0) === LESS_THAN ? 1 : 0;
|
|
82
|
+
const targetStart = end - written.length + angle;
|
|
83
|
+
const targetEnd = end - angle;
|
|
84
|
+
const target = text.slice(targetStart, targetEnd);
|
|
85
|
+
const key = normalizeLabel(label);
|
|
86
|
+
if (!definitions.has(key))
|
|
87
|
+
definitions.set(key, { target, targetStart, targetEnd });
|
|
88
|
+
out.push({ form: 'definition', image: false, text: label, target, label, start, end, targetStart, targetEnd, line: line.line });
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
function readAutolinks(layout) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const match of layout.structure.matchAll(AUTOLINK)) {
|
|
95
|
+
const start = match.index;
|
|
96
|
+
const end = start + match[0].length;
|
|
97
|
+
out.push({
|
|
98
|
+
form: 'autolink',
|
|
99
|
+
image: false,
|
|
100
|
+
text: '',
|
|
101
|
+
target: match[1],
|
|
102
|
+
label: null,
|
|
103
|
+
start,
|
|
104
|
+
end,
|
|
105
|
+
targetStart: start + 1,
|
|
106
|
+
targetEnd: end - 1,
|
|
107
|
+
line: layout.index.positionAt(start).line,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/** Pairs `[` with `]` inside a paragraph, innermost first, skipping escaped ones. */
|
|
113
|
+
function pairBrackets(structure, from, to) {
|
|
114
|
+
const pairs = new Map();
|
|
115
|
+
const open = [];
|
|
116
|
+
for (let at = from; at < to; at += 1) {
|
|
117
|
+
const ch = structure.charCodeAt(at);
|
|
118
|
+
if (ch === BACKSLASH)
|
|
119
|
+
at += 1;
|
|
120
|
+
else if (ch === OPEN_BRACKET)
|
|
121
|
+
open.push(at);
|
|
122
|
+
else if (ch === CLOSE_BRACKET) {
|
|
123
|
+
// A `]` with nothing to close pairs with nothing; recorded under
|
|
124
|
+
// `undefined` it would not be found either, so this test changes no
|
|
125
|
+
// answer, only what the map holds.
|
|
126
|
+
const opened = open.pop();
|
|
127
|
+
if (opened !== undefined)
|
|
128
|
+
pairs.set(opened, at);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return pairs;
|
|
132
|
+
}
|
|
133
|
+
function endsOf(structure, from, to) {
|
|
134
|
+
const length = to - from;
|
|
135
|
+
const escaped = new Uint8Array(length + 1);
|
|
136
|
+
for (let x = 0; x < length; x += 1) {
|
|
137
|
+
if (escaped[x] === 0 && structure.charCodeAt(from + x) === BACKSLASH)
|
|
138
|
+
escaped[x + 1] = 1;
|
|
139
|
+
}
|
|
140
|
+
// Two past the last offset: a `(` whose `)` is the paragraph's last
|
|
141
|
+
// character looks one further for the next unmatched `)`, and finds none.
|
|
142
|
+
const make = () => new Int32Array(length + 2).fill(to);
|
|
143
|
+
const ends = { space: make(), paren: make(), doubleQuote: make(), singleQuote: make(), closeParen: make(), openParen: make() };
|
|
144
|
+
for (let x = length - 1; x >= 0; x -= 1) {
|
|
145
|
+
const at = from + x;
|
|
146
|
+
const ch = structure.charCodeAt(at);
|
|
147
|
+
const plain = escaped[x] === 0;
|
|
148
|
+
ends.space[x] = ch <= 32 || ch === 127 ? at : ends.space[x + 1];
|
|
149
|
+
// From a `(`, the first unmatched `)` is the first one after the `)` that
|
|
150
|
+
// closes it, which is the first unmatched `)` after the `(`.
|
|
151
|
+
const unmatched = ends.paren[x + 1];
|
|
152
|
+
if (plain && ch === CLOSE_PAREN)
|
|
153
|
+
ends.paren[x] = at;
|
|
154
|
+
else if (plain && ch === OPEN_PAREN)
|
|
155
|
+
ends.paren[x] = ends.paren[unmatched + 1 - from];
|
|
156
|
+
else
|
|
157
|
+
ends.paren[x] = unmatched;
|
|
158
|
+
ends.doubleQuote[x] = plain && ch === DOUBLE_QUOTE ? at : ends.doubleQuote[x + 1];
|
|
159
|
+
ends.singleQuote[x] = plain && ch === SINGLE_QUOTE ? at : ends.singleQuote[x + 1];
|
|
160
|
+
ends.closeParen[x] = plain && ch === CLOSE_PAREN ? at : ends.closeParen[x + 1];
|
|
161
|
+
ends.openParen[x] = plain && ch === OPEN_PAREN ? at : ends.openParen[x + 1];
|
|
162
|
+
}
|
|
163
|
+
return ends;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The bracket forms, a paragraph at a time. A paragraph here is a run of
|
|
167
|
+
* Markdown lines, none blank, each continuing the one above: a bracket never
|
|
168
|
+
* pairs across a blank line, a heading, or the start of another block.
|
|
169
|
+
*/
|
|
170
|
+
function readBrackets(layout, definitions, defined) {
|
|
171
|
+
const { lines, continues, structure, text } = layout;
|
|
172
|
+
const out = [];
|
|
173
|
+
// The first `]]` at or after the last place one was looked for; the cursor
|
|
174
|
+
// only moves forward, so each is found once.
|
|
175
|
+
let doubleClose = -2;
|
|
176
|
+
const lookup = (from, to) => to - from > MAX_LABEL ? undefined : definitions.get(normalizeLabel(text.slice(from, to)));
|
|
177
|
+
// A blank or code line let into a paragraph would bring only characters the
|
|
178
|
+
// structure mask blanks, so this test shapes the runs and changes no link.
|
|
179
|
+
const inParagraph = (i) => {
|
|
180
|
+
const line = lines[i];
|
|
181
|
+
return line !== undefined && isMarkdown(line) && !line.blank;
|
|
182
|
+
};
|
|
183
|
+
for (let first = 0; first < lines.length; first += 1) {
|
|
184
|
+
if (!inParagraph(first))
|
|
185
|
+
continue;
|
|
186
|
+
let last = first;
|
|
187
|
+
while (inParagraph(last + 1) && continues.has(last + 2))
|
|
188
|
+
last += 1;
|
|
189
|
+
const from = lines[first].start;
|
|
190
|
+
const to = lines[last].end;
|
|
191
|
+
const pairs = pairBrackets(structure, from, to);
|
|
192
|
+
let ends = null;
|
|
193
|
+
// Spaces and tabs, and at most one line ending inside the paragraph. The
|
|
194
|
+
// character at `to` ends the paragraph's last line and is none of these.
|
|
195
|
+
const skipSpace = (start) => {
|
|
196
|
+
let at = start;
|
|
197
|
+
while (isSpaceOrTab(structure, at))
|
|
198
|
+
at += 1;
|
|
199
|
+
if (at < to) {
|
|
200
|
+
if (structure.charCodeAt(at) === CR)
|
|
201
|
+
at += 1;
|
|
202
|
+
if (structure.charCodeAt(at) === LF)
|
|
203
|
+
at += 1;
|
|
204
|
+
while (isSpaceOrTab(structure, at))
|
|
205
|
+
at += 1;
|
|
206
|
+
}
|
|
207
|
+
return at;
|
|
208
|
+
};
|
|
209
|
+
const destination = (open) => {
|
|
210
|
+
const at = skipSpace(open);
|
|
211
|
+
let targetStart;
|
|
212
|
+
let targetEnd;
|
|
213
|
+
let after;
|
|
214
|
+
if (structure.charCodeAt(at) === LESS_THAN) {
|
|
215
|
+
// `<dest>`: no line ending and no unescaped `<` inside.
|
|
216
|
+
let close = at + 1;
|
|
217
|
+
for (;;) {
|
|
218
|
+
if (close >= to)
|
|
219
|
+
return null;
|
|
220
|
+
const ch = structure.charCodeAt(close);
|
|
221
|
+
if (ch === GREATER_THAN)
|
|
222
|
+
break;
|
|
223
|
+
if (ch === LESS_THAN || ch === LF || ch === CR)
|
|
224
|
+
return null;
|
|
225
|
+
close += ch === BACKSLASH ? 2 : 1;
|
|
226
|
+
}
|
|
227
|
+
targetStart = at + 1;
|
|
228
|
+
targetEnd = close;
|
|
229
|
+
after = close + 1;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
ends ??= endsOf(structure, from, to);
|
|
233
|
+
const space = ends.space[at - from];
|
|
234
|
+
const paren = ends.paren[at - from];
|
|
235
|
+
targetStart = at;
|
|
236
|
+
// The two are equal only when neither is found, at `to`.
|
|
237
|
+
targetEnd = paren < space ? paren : space;
|
|
238
|
+
after = targetEnd;
|
|
239
|
+
// An empty destination points nowhere a reader can follow.
|
|
240
|
+
if (targetEnd === at)
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
let k = skipSpace(after);
|
|
244
|
+
if (structure.charCodeAt(k) === CLOSE_PAREN)
|
|
245
|
+
return { targetStart, targetEnd, end: k + 1 };
|
|
246
|
+
// A title is separated from the destination by whitespace, and quoted.
|
|
247
|
+
if (k === after)
|
|
248
|
+
return null;
|
|
249
|
+
ends ??= endsOf(structure, from, to);
|
|
250
|
+
const x = k + 1 - from;
|
|
251
|
+
const quote = structure.charCodeAt(k);
|
|
252
|
+
let close;
|
|
253
|
+
if (quote === DOUBLE_QUOTE)
|
|
254
|
+
close = ends.doubleQuote[x];
|
|
255
|
+
else if (quote === SINGLE_QUOTE)
|
|
256
|
+
close = ends.singleQuote[x];
|
|
257
|
+
// A title in parentheses may not hold an unescaped `(`.
|
|
258
|
+
else if (quote === OPEN_PAREN)
|
|
259
|
+
close = ends.openParen[x] < ends.closeParen[x] ? to : ends.closeParen[x];
|
|
260
|
+
else
|
|
261
|
+
return null;
|
|
262
|
+
// Equal ends are `to`, where the title fails either way. Past `to` the
|
|
263
|
+
// next line opens a block of its own, and none opens with `)`.
|
|
264
|
+
if (close === to)
|
|
265
|
+
return null;
|
|
266
|
+
k = skipSpace(close + 1);
|
|
267
|
+
return structure.charCodeAt(k) === CLOSE_PAREN ? { targetStart, targetEnd, end: k + 1 } : null;
|
|
268
|
+
};
|
|
269
|
+
const wiki = (at, start, image, line) => {
|
|
270
|
+
if (structure.charCodeAt(at + 1) !== OPEN_BRACKET)
|
|
271
|
+
return null;
|
|
272
|
+
if (doubleClose !== -1 && doubleClose < at + 2)
|
|
273
|
+
doubleClose = structure.indexOf(']]', at + 2);
|
|
274
|
+
const close = doubleClose;
|
|
275
|
+
if (close < 0 || close + 2 > line.end)
|
|
276
|
+
return null;
|
|
277
|
+
const inner = text.slice(at + 2, close);
|
|
278
|
+
const written = inner.trim();
|
|
279
|
+
const pipe = written.indexOf('|');
|
|
280
|
+
const target = (pipe < 0 ? written : written.slice(0, pipe)).trim();
|
|
281
|
+
// `[[ ]]` and `[[|text]]` name nothing to link to.
|
|
282
|
+
if (target.length === 0)
|
|
283
|
+
return null;
|
|
284
|
+
const targetStart = at + 2 + inner.length - inner.trimStart().length;
|
|
285
|
+
return {
|
|
286
|
+
form: 'wiki',
|
|
287
|
+
image,
|
|
288
|
+
text: written,
|
|
289
|
+
target,
|
|
290
|
+
label: null,
|
|
291
|
+
start,
|
|
292
|
+
end: close + 2,
|
|
293
|
+
targetStart,
|
|
294
|
+
targetEnd: targetStart + target.length,
|
|
295
|
+
line: line.line,
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
const bracket = (at, start, image, line) => {
|
|
299
|
+
const close = pairs.get(at);
|
|
300
|
+
if (close === undefined)
|
|
301
|
+
return null;
|
|
302
|
+
const label = () => text.slice(at + 1, close).trim();
|
|
303
|
+
if (structure.charCodeAt(close + 1) === OPEN_PAREN) {
|
|
304
|
+
const dest = destination(close + 2);
|
|
305
|
+
if (dest !== null) {
|
|
306
|
+
const target = text.slice(dest.targetStart, dest.targetEnd);
|
|
307
|
+
return { form: 'inline', image, text: label(), target, label: null, start, line: line.line, ...dest };
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// Only a `[` is paired, so a second label follows exactly when this finds one.
|
|
312
|
+
const refClose = pairs.get(close + 1);
|
|
313
|
+
if (refClose !== undefined) {
|
|
314
|
+
// `[text][]` is read through its text; `[text][label]` through its label.
|
|
315
|
+
const collapsed = refClose - close - 2 <= MAX_LABEL && text.slice(close + 2, refClose).trim() === '';
|
|
316
|
+
const found = collapsed ? lookup(at + 1, close) : lookup(close + 2, refClose);
|
|
317
|
+
if (found === undefined)
|
|
318
|
+
return null;
|
|
319
|
+
const written = collapsed ? label() : text.slice(close + 2, refClose).trim();
|
|
320
|
+
return { form: 'reference', image, text: label(), label: written, start, end: refClose + 1, line: line.line, ...found };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// `[text](not a destination)` is a shortcut followed by text, as CommonMark reads it.
|
|
324
|
+
const found = lookup(at + 1, close);
|
|
325
|
+
if (found === undefined)
|
|
326
|
+
return null;
|
|
327
|
+
return { form: 'shortcut', image, text: label(), label: label(), start, end: close + 1, line: line.line, ...found };
|
|
328
|
+
};
|
|
329
|
+
let escaped = -1;
|
|
330
|
+
let at = from;
|
|
331
|
+
while (at < to) {
|
|
332
|
+
const ch = structure.charCodeAt(at);
|
|
333
|
+
if (ch === BACKSLASH) {
|
|
334
|
+
escaped = at + 1;
|
|
335
|
+
at += 2;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (ch !== OPEN_BRACKET) {
|
|
339
|
+
at += 1;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const line = lines[layout.index.positionAt(at).line - 1];
|
|
343
|
+
// A definition's line holds its label, its destination and its title,
|
|
344
|
+
// and none of them is a link.
|
|
345
|
+
if (defined.has(line.line)) {
|
|
346
|
+
at = line.end;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const image = structure.charCodeAt(at - 1) === BANG && escaped !== at - 1;
|
|
350
|
+
const start = image ? at - 1 : at;
|
|
351
|
+
const link = wiki(at, start, image, line) ?? bracket(at, start, image, line);
|
|
352
|
+
if (link === null) {
|
|
353
|
+
at += 1;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
out.push(link);
|
|
357
|
+
at = link.end;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
first = last;
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../../../src/vendor/spec-core/markdown/links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC;AAGjF,MAAM,IAAI,GAAG,EAAE,CAAC;AAChB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,WAAW,GAAG,EAAE,CAAC;AACvB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,EAAE,GAAG,EAAE,CAAC;AACd,MAAM,EAAE,GAAG,EAAE,CAAC;AAEd,6DAA6D;AAC7D,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,MAAM,UAAU,GAAG,gDAAgD,CAAC;AACpE,wFAAwF;AACxF,MAAM,UAAU,GAAG,kDAAkD,CAAC;AACtE,MAAM,QAAQ,GAAG,qCAAqC,CAAC;AAQvD,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAClD,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzF,sEAAsE;IACtE,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,SAAS,EAAE,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,MAAc,EAAE,WAAoC;IAC3E,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IACnC,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC;YAAE,SAAS;QAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAAE,SAAS;QAChF,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,GAAI,KAAK,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACpF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAChD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,WAAW,GAAG,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;QACjD,MAAM,SAAS,GAAG,GAAG,GAAG,KAAK,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;QACpF,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAClI,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACpC,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,EAAE;YACR,MAAM,EAAE,KAAK,CAAC,CAAC,CAAW;YAC1B,KAAK,EAAE,IAAI;YACX,KAAK;YACL,GAAG;YACH,WAAW,EAAE,KAAK,GAAG,CAAC;YACtB,SAAS,EAAE,GAAG,GAAG,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI;SAC1C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,qFAAqF;AACrF,SAAS,YAAY,CAAC,SAAiB,EAAE,IAAY,EAAE,EAAU;IAC/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,EAAE,KAAK,SAAS;YAAE,EAAE,IAAI,CAAC,CAAC;aACzB,IAAI,EAAE,KAAK,YAAY;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aACvC,IAAI,EAAE,KAAK,aAAa,EAAE,CAAC;YAC9B,iEAAiE;YACjE,oEAAoE;YACpE,mCAAmC;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC1B,IAAI,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAkBD,SAAS,MAAM,CAAC,SAAiB,EAAE,IAAY,EAAE,EAAU;IACzD,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3F,CAAC;IACD,oEAAoE;IACpE,0EAA0E;IAC1E,MAAM,IAAI,GAAG,GAAe,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,IAAI,GAAS,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;IACrI,KAAK,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;QACpB,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC;QAC5E,0EAA0E;QAC1E,6DAA6D;QAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAW,CAAC;QAC9C,IAAI,KAAK,IAAI,EAAE,KAAK,WAAW;YAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;aAC/C,IAAI,KAAK,IAAI,EAAE,KAAK,UAAU;YAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,CAAW,CAAC;;YAC3F,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;QAC/B,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC;QAC9F,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC;QAC9F,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC;QAC3F,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC;IAC1F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AASD;;;;GAIG;AACH,SAAS,YAAY,CAAC,MAAc,EAAE,WAA4C,EAAE,OAA4B;IAC9G,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;IACrD,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,2EAA2E;IAC3E,6CAA6C;IAC7C,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;IAErB,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAU,EAA0B,EAAE,CAClE,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAE5F,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,WAAW,GAAG,CAAC,CAAS,EAAW,EAAE;QACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,OAAO,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IAC/D,CAAC,CAAC;IAEF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,OAAO,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;YAAE,IAAI,IAAI,CAAC,CAAC;QACnE,MAAM,IAAI,GAAI,KAAK,CAAC,KAAK,CAAiB,CAAC,KAAK,CAAC;QACjD,MAAM,EAAE,GAAI,KAAK,CAAC,IAAI,CAAiB,CAAC,GAAG,CAAC;QAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAChD,IAAI,IAAI,GAAgB,IAAI,CAAC;QAE7B,yEAAyE;QACzE,yEAAyE;QACzE,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE;YAC1C,IAAI,EAAE,GAAG,KAAK,CAAC;YACf,OAAO,YAAY,CAAC,SAAS,EAAE,EAAE,CAAC;gBAAE,EAAE,IAAI,CAAC,CAAC;YAC5C,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;gBACZ,IAAI,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;oBAAE,EAAE,IAAI,CAAC,CAAC;gBAC7C,IAAI,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;oBAAE,EAAE,IAAI,CAAC,CAAC;gBAC7C,OAAO,YAAY,CAAC,SAAS,EAAE,EAAE,CAAC;oBAAE,EAAE,IAAI,CAAC,CAAC;YAC9C,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QAEF,MAAM,WAAW,GAAG,CAAC,IAAY,EAAsB,EAAE;YACvD,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,WAAmB,CAAC;YACxB,IAAI,SAAiB,CAAC;YACtB,IAAI,KAAa,CAAC;YAClB,IAAI,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC3C,wDAAwD;gBACxD,IAAI,KAAK,GAAG,EAAE,GAAG,CAAC,CAAC;gBACnB,SAAS,CAAC;oBACR,IAAI,KAAK,IAAI,EAAE;wBAAE,OAAO,IAAI,CAAC;oBAC7B,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;oBACvC,IAAI,EAAE,KAAK,YAAY;wBAAE,MAAM;oBAC/B,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;wBAAE,OAAO,IAAI,CAAC;oBAC5D,KAAK,IAAI,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpC,CAAC;gBACD,WAAW,GAAG,EAAE,GAAG,CAAC,CAAC;gBACrB,SAAS,GAAG,KAAK,CAAC;gBAClB,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACN,IAAI,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAW,CAAC;gBAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAW,CAAC;gBAC9C,WAAW,GAAG,EAAE,CAAC;gBACjB,yDAAyD;gBACzD,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;gBAC1C,KAAK,GAAG,SAAS,CAAC;gBAClB,2DAA2D;gBAC3D,IAAI,SAAS,KAAK,EAAE;oBAAE,OAAO,IAAI,CAAC;YACpC,CAAC;YACD,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,WAAW;gBAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3F,uEAAuE;YACvE,IAAI,CAAC,KAAK,KAAK;gBAAE,OAAO,IAAI,CAAC;YAC7B,IAAI,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YACvB,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,KAAa,CAAC;YAClB,IAAI,KAAK,KAAK,YAAY;gBAAE,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAW,CAAC;iBAC7D,IAAI,KAAK,KAAK,YAAY;gBAAE,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAW,CAAC;YACvE,wDAAwD;iBACnD,IAAI,KAAK,KAAK,UAAU;gBAAE,KAAK,GAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAY,GAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAY,CAAC;;gBACvI,OAAO,IAAI,CAAC;YACjB,uEAAuE;YACvE,+DAA+D;YAC/D,IAAI,KAAK,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAC;YAC9B,CAAC,GAAG,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YACzB,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjG,CAAC,CAAC;QAEF,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,KAAa,EAAE,KAAc,EAAE,IAAiB,EAAe,EAAE;YACzF,IAAI,SAAS,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,YAAY;gBAAE,OAAO,IAAI,CAAC;YAC/D,IAAI,WAAW,KAAK,CAAC,CAAC,IAAI,WAAW,GAAG,EAAE,GAAG,CAAC;gBAAE,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;YAC9F,MAAM,KAAK,GAAG,WAAW,CAAC;YAC1B,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACpE,mDAAmD;YACnD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACrC,MAAM,WAAW,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC;YACrE,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,KAAK;gBACL,IAAI,EAAE,OAAO;gBACb,MAAM;gBACN,KAAK,EAAE,IAAI;gBACX,KAAK;gBACL,GAAG,EAAE,KAAK,GAAG,CAAC;gBACd,WAAW;gBACX,SAAS,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM;gBACtC,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC;QACJ,CAAC,CAAC;QAEF,MAAM,OAAO,GAAG,CAAC,EAAU,EAAE,KAAa,EAAE,KAAc,EAAE,IAAiB,EAAe,EAAE;YAC5F,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5B,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACrC,MAAM,KAAK,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7D,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;gBACnD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;gBACpC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC5D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;gBACxG,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,+EAA+E;gBAC/E,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;gBACtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,0EAA0E;oBAC1E,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,GAAG,CAAC,IAAI,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;oBACrG,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;oBAC9E,IAAI,KAAK,KAAK,SAAS;wBAAE,OAAO,IAAI,CAAC;oBACrC,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC7E,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;gBAC1H,CAAC;YACH,CAAC;YACD,sFAAsF;YACtF,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;YACpC,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;QACtH,CAAC,CAAC;QAEF,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC;QACjB,IAAI,EAAE,GAAG,IAAI,CAAC;QACd,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;YACf,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACpC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;gBACrB,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC;gBACjB,EAAE,IAAI,CAAC,CAAC;gBACR,SAAS;YACX,CAAC;YACD,IAAI,EAAE,KAAK,YAAY,EAAE,CAAC;gBACxB,EAAE,IAAI,CAAC,CAAC;gBACR,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAgB,CAAC;YACxE,sEAAsE;YACtE,8BAA8B;YAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;gBACd,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,OAAO,KAAK,EAAE,GAAG,CAAC,CAAC;YAC1E,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;YAC7E,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,EAAE,IAAI,CAAC,CAAC;YACV,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACf,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;YAChB,CAAC;QACH,CAAC;QACD,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * Links, images and reference definitions, read from the structure mask so\n * that nothing in code or a comment is one.\n *\n * Six forms: inline `[text](dest \"title\")`, full and collapsed reference\n * `[text][label]` and `[label][]`, shortcut `[label]`, autolink `<https://x>`,\n * wiki `[[target|text]]`, and the definition `[label]: dest` the reference\n * forms are read through. Each may be an image, written with a leading `!`.\n *\n * Brackets are paired once per paragraph with a stack, and the tables that\n * say where a destination or a title ends are built once per paragraph, so\n * no bracket searches the text again for its partner. A document of nothing\n * but brackets costs its length.\n *\n * A paragraph ends at `to`, and the character there is a line terminator,\n * which no loop here acts on; nor is any lookup made at the first three\n * offsets of a paragraph, before the shortest `[](` could end. Mutants that\n * move a bound onto those places change no answer, and are equivalent.\n */\n\nimport { inRanges, mergeRanges } from '../text/index.js';\nimport { isMarkdown, isSpaceOrTab, visibleLead, type Layout } from './layout.js';\nimport type { Link, ScannedLine } from './types.js';\n\nconst BANG = 33;\nconst DOUBLE_QUOTE = 34;\nconst SINGLE_QUOTE = 39;\nconst OPEN_PAREN = 40;\nconst CLOSE_PAREN = 41;\nconst LESS_THAN = 60;\nconst GREATER_THAN = 62;\nconst OPEN_BRACKET = 91;\nconst BACKSLASH = 92;\nconst CLOSE_BRACKET = 93;\nconst LF = 10;\nconst CR = 13;\n\n/** CommonMark: a link label holds at most 999 characters. */\nconst MAX_LABEL = 999;\n\nconst DEFINITION = /^ {0,3}\\[(?!\\^)([^\\]]+)\\]:[ \\t]*(<[^<>]*>|\\S+)/;\n/** What may follow a definition's destination: nothing, or a title after whitespace. */\nconst TITLE_ONLY = /^(?:[ \\t]+(?:\"[^\"]*\"|'[^']*'|\\([^)]*\\)))?[ \\t]*$/;\nconst AUTOLINK = /<((?:https?|ftp|mailto):[^<>\\s]+)>/g;\n\ninterface Definition {\n readonly target: string;\n readonly targetStart: number;\n readonly targetEnd: number;\n}\n\nexport function findLinks(layout: Layout): Link[] {\n const definitions = new Map<string, Definition>();\n const defined = readDefinitions(layout, definitions);\n const bracketed = readBrackets(layout, definitions, new Set(defined.map((d) => d.line)));\n // `<https://x>` as a link's destination or text is part of that link.\n const taken = mergeRanges([...defined, ...bracketed]);\n const autolinks = readAutolinks(layout).filter((link) => !inRanges(taken, link.start));\n return [...defined, ...bracketed, ...autolinks].sort((a, b) => a.start - b.start);\n}\n\n/**\n * How labels compare: trimmed, whitespace collapsed, and case folded as\n * CommonMark's reference implementations fold it, down and then up, so that\n * `ẞ` and `SS` are one label.\n */\nfunction normalizeLabel(label: string): string {\n return label.trim().replace(/\\s+/g, ' ').toLowerCase().toUpperCase();\n}\n\n/**\n * Definitions come first: the reference forms are read through them, and a\n * definition is conventionally written at the foot of the file, after the\n * links that use it. The first definition of a label wins.\n *\n * `[Note]: this matters` is prose that looks like one, so a definition has\n * nothing after its destination but a title. `[^1]: text` is a footnote.\n */\nfunction readDefinitions(layout: Layout, definitions: Map<string, Definition>): Link[] {\n const { text, structure } = layout;\n const out: Link[] = [];\n for (const line of layout.lines) {\n if (!isMarkdown(line) || !visibleLead(layout, line)) continue;\n const masked = structure.slice(line.contentStart, line.end);\n const match = DEFINITION.exec(masked);\n if (match === null || !TITLE_ONLY.test(masked.slice(match[0].length))) continue;\n const start = line.contentStart + match[0].indexOf('[');\n const label = text.slice(start + 1, start + 1 + (match[1] as string).length).trim();\n if (label.length === 0) continue;\n const written = match[2] as string;\n const end = line.contentStart + match[0].length;\n const angle = written.charCodeAt(0) === LESS_THAN ? 1 : 0;\n const targetStart = end - written.length + angle;\n const targetEnd = end - angle;\n const target = text.slice(targetStart, targetEnd);\n const key = normalizeLabel(label);\n if (!definitions.has(key)) definitions.set(key, { target, targetStart, targetEnd });\n out.push({ form: 'definition', image: false, text: label, target, label, start, end, targetStart, targetEnd, line: line.line });\n }\n return out;\n}\n\nfunction readAutolinks(layout: Layout): Link[] {\n const out: Link[] = [];\n for (const match of layout.structure.matchAll(AUTOLINK)) {\n const start = match.index;\n const end = start + match[0].length;\n out.push({\n form: 'autolink',\n image: false,\n text: '',\n target: match[1] as string,\n label: null,\n start,\n end,\n targetStart: start + 1,\n targetEnd: end - 1,\n line: layout.index.positionAt(start).line,\n });\n }\n return out;\n}\n\n/** Pairs `[` with `]` inside a paragraph, innermost first, skipping escaped ones. */\nfunction pairBrackets(structure: string, from: number, to: number): Map<number, number> {\n const pairs = new Map<number, number>();\n const open: number[] = [];\n for (let at = from; at < to; at += 1) {\n const ch = structure.charCodeAt(at);\n if (ch === BACKSLASH) at += 1;\n else if (ch === OPEN_BRACKET) open.push(at);\n else if (ch === CLOSE_BRACKET) {\n // A `]` with nothing to close pairs with nothing; recorded under\n // `undefined` it would not be found either, so this test changes no\n // answer, only what the map holds.\n const opened = open.pop();\n if (opened !== undefined) pairs.set(opened, at);\n }\n }\n return pairs;\n}\n\n/**\n * For every offset of a paragraph, where what starts there ends, or the end\n * of the paragraph when nothing does: a bare destination at the next space or\n * control character, or at the first `)` its own parentheses leave unmatched;\n * a title at its next unescaped closing quote or parenthesis. Filled from the\n * right in one pass, so every lookup is a read.\n */\ninterface Ends {\n readonly space: Int32Array;\n readonly paren: Int32Array;\n readonly doubleQuote: Int32Array;\n readonly singleQuote: Int32Array;\n readonly closeParen: Int32Array;\n readonly openParen: Int32Array;\n}\n\nfunction endsOf(structure: string, from: number, to: number): Ends {\n const length = to - from;\n const escaped = new Uint8Array(length + 1);\n for (let x = 0; x < length; x += 1) {\n if (escaped[x] === 0 && structure.charCodeAt(from + x) === BACKSLASH) escaped[x + 1] = 1;\n }\n // Two past the last offset: a `(` whose `)` is the paragraph's last\n // character looks one further for the next unmatched `)`, and finds none.\n const make = (): Int32Array => new Int32Array(length + 2).fill(to);\n const ends: Ends = { space: make(), paren: make(), doubleQuote: make(), singleQuote: make(), closeParen: make(), openParen: make() };\n for (let x = length - 1; x >= 0; x -= 1) {\n const at = from + x;\n const ch = structure.charCodeAt(at);\n const plain = escaped[x] === 0;\n ends.space[x] = ch <= 32 || ch === 127 ? at : (ends.space[x + 1] as number);\n // From a `(`, the first unmatched `)` is the first one after the `)` that\n // closes it, which is the first unmatched `)` after the `(`.\n const unmatched = ends.paren[x + 1] as number;\n if (plain && ch === CLOSE_PAREN) ends.paren[x] = at;\n else if (plain && ch === OPEN_PAREN) ends.paren[x] = ends.paren[unmatched + 1 - from] as number;\n else ends.paren[x] = unmatched;\n ends.doubleQuote[x] = plain && ch === DOUBLE_QUOTE ? at : (ends.doubleQuote[x + 1] as number);\n ends.singleQuote[x] = plain && ch === SINGLE_QUOTE ? at : (ends.singleQuote[x + 1] as number);\n ends.closeParen[x] = plain && ch === CLOSE_PAREN ? at : (ends.closeParen[x + 1] as number);\n ends.openParen[x] = plain && ch === OPEN_PAREN ? at : (ends.openParen[x + 1] as number);\n }\n return ends;\n}\n\ninterface Destination {\n readonly targetStart: number;\n readonly targetEnd: number;\n /** Offset just past the closing `)`. */\n readonly end: number;\n}\n\n/**\n * The bracket forms, a paragraph at a time. A paragraph here is a run of\n * Markdown lines, none blank, each continuing the one above: a bracket never\n * pairs across a blank line, a heading, or the start of another block.\n */\nfunction readBrackets(layout: Layout, definitions: ReadonlyMap<string, Definition>, defined: ReadonlySet<number>): Link[] {\n const { lines, continues, structure, text } = layout;\n const out: Link[] = [];\n // The first `]]` at or after the last place one was looked for; the cursor\n // only moves forward, so each is found once.\n let doubleClose = -2;\n\n const lookup = (from: number, to: number): Definition | undefined =>\n to - from > MAX_LABEL ? undefined : definitions.get(normalizeLabel(text.slice(from, to)));\n\n // A blank or code line let into a paragraph would bring only characters the\n // structure mask blanks, so this test shapes the runs and changes no link.\n const inParagraph = (i: number): boolean => {\n const line = lines[i];\n return line !== undefined && isMarkdown(line) && !line.blank;\n };\n\n for (let first = 0; first < lines.length; first += 1) {\n if (!inParagraph(first)) continue;\n let last = first;\n while (inParagraph(last + 1) && continues.has(last + 2)) last += 1;\n const from = (lines[first] as ScannedLine).start;\n const to = (lines[last] as ScannedLine).end;\n const pairs = pairBrackets(structure, from, to);\n let ends: Ends | null = null;\n\n // Spaces and tabs, and at most one line ending inside the paragraph. The\n // character at `to` ends the paragraph's last line and is none of these.\n const skipSpace = (start: number): number => {\n let at = start;\n while (isSpaceOrTab(structure, at)) at += 1;\n if (at < to) {\n if (structure.charCodeAt(at) === CR) at += 1;\n if (structure.charCodeAt(at) === LF) at += 1;\n while (isSpaceOrTab(structure, at)) at += 1;\n }\n return at;\n };\n\n const destination = (open: number): Destination | null => {\n const at = skipSpace(open);\n let targetStart: number;\n let targetEnd: number;\n let after: number;\n if (structure.charCodeAt(at) === LESS_THAN) {\n // `<dest>`: no line ending and no unescaped `<` inside.\n let close = at + 1;\n for (;;) {\n if (close >= to) return null;\n const ch = structure.charCodeAt(close);\n if (ch === GREATER_THAN) break;\n if (ch === LESS_THAN || ch === LF || ch === CR) return null;\n close += ch === BACKSLASH ? 2 : 1;\n }\n targetStart = at + 1;\n targetEnd = close;\n after = close + 1;\n } else {\n ends ??= endsOf(structure, from, to);\n const space = ends.space[at - from] as number;\n const paren = ends.paren[at - from] as number;\n targetStart = at;\n // The two are equal only when neither is found, at `to`.\n targetEnd = paren < space ? paren : space;\n after = targetEnd;\n // An empty destination points nowhere a reader can follow.\n if (targetEnd === at) return null;\n }\n let k = skipSpace(after);\n if (structure.charCodeAt(k) === CLOSE_PAREN) return { targetStart, targetEnd, end: k + 1 };\n // A title is separated from the destination by whitespace, and quoted.\n if (k === after) return null;\n ends ??= endsOf(structure, from, to);\n const x = k + 1 - from;\n const quote = structure.charCodeAt(k);\n let close: number;\n if (quote === DOUBLE_QUOTE) close = ends.doubleQuote[x] as number;\n else if (quote === SINGLE_QUOTE) close = ends.singleQuote[x] as number;\n // A title in parentheses may not hold an unescaped `(`.\n else if (quote === OPEN_PAREN) close = (ends.openParen[x] as number) < (ends.closeParen[x] as number) ? to : (ends.closeParen[x] as number);\n else return null;\n // Equal ends are `to`, where the title fails either way. Past `to` the\n // next line opens a block of its own, and none opens with `)`.\n if (close === to) return null;\n k = skipSpace(close + 1);\n return structure.charCodeAt(k) === CLOSE_PAREN ? { targetStart, targetEnd, end: k + 1 } : null;\n };\n\n const wiki = (at: number, start: number, image: boolean, line: ScannedLine): Link | null => {\n if (structure.charCodeAt(at + 1) !== OPEN_BRACKET) return null;\n if (doubleClose !== -1 && doubleClose < at + 2) doubleClose = structure.indexOf(']]', at + 2);\n const close = doubleClose;\n if (close < 0 || close + 2 > line.end) return null;\n const inner = text.slice(at + 2, close);\n const written = inner.trim();\n const pipe = written.indexOf('|');\n const target = (pipe < 0 ? written : written.slice(0, pipe)).trim();\n // `[[ ]]` and `[[|text]]` name nothing to link to.\n if (target.length === 0) return null;\n const targetStart = at + 2 + inner.length - inner.trimStart().length;\n return {\n form: 'wiki',\n image,\n text: written,\n target,\n label: null,\n start,\n end: close + 2,\n targetStart,\n targetEnd: targetStart + target.length,\n line: line.line,\n };\n };\n\n const bracket = (at: number, start: number, image: boolean, line: ScannedLine): Link | null => {\n const close = pairs.get(at);\n if (close === undefined) return null;\n const label = (): string => text.slice(at + 1, close).trim();\n if (structure.charCodeAt(close + 1) === OPEN_PAREN) {\n const dest = destination(close + 2);\n if (dest !== null) {\n const target = text.slice(dest.targetStart, dest.targetEnd);\n return { form: 'inline', image, text: label(), target, label: null, start, line: line.line, ...dest };\n }\n } else {\n // Only a `[` is paired, so a second label follows exactly when this finds one.\n const refClose = pairs.get(close + 1);\n if (refClose !== undefined) {\n // `[text][]` is read through its text; `[text][label]` through its label.\n const collapsed = refClose - close - 2 <= MAX_LABEL && text.slice(close + 2, refClose).trim() === '';\n const found = collapsed ? lookup(at + 1, close) : lookup(close + 2, refClose);\n if (found === undefined) return null;\n const written = collapsed ? label() : text.slice(close + 2, refClose).trim();\n return { form: 'reference', image, text: label(), label: written, start, end: refClose + 1, line: line.line, ...found };\n }\n }\n // `[text](not a destination)` is a shortcut followed by text, as CommonMark reads it.\n const found = lookup(at + 1, close);\n if (found === undefined) return null;\n return { form: 'shortcut', image, text: label(), label: label(), start, end: close + 1, line: line.line, ...found };\n };\n\n let escaped = -1;\n let at = from;\n while (at < to) {\n const ch = structure.charCodeAt(at);\n if (ch === BACKSLASH) {\n escaped = at + 1;\n at += 2;\n continue;\n }\n if (ch !== OPEN_BRACKET) {\n at += 1;\n continue;\n }\n const line = lines[layout.index.positionAt(at).line - 1] as ScannedLine;\n // A definition's line holds its label, its destination and its title,\n // and none of them is a link.\n if (defined.has(line.line)) {\n at = line.end;\n continue;\n }\n const image = structure.charCodeAt(at - 1) === BANG && escaped !== at - 1;\n const start = image ? at - 1 : at;\n const link = wiki(at, start, image, line) ?? bracket(at, start, image, line);\n if (link === null) {\n at += 1;\n } else {\n out.push(link);\n at = link.end;\n }\n }\n first = last;\n }\n return out;\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List items, as blocks rather than lines.
|
|
3
|
+
*
|
|
4
|
+
* An item owns what is nested under it - anything indented past its marker -
|
|
5
|
+
* and its lazy continuation lines: text directly under it, with no blank line
|
|
6
|
+
* between, that opens nothing of its own. So a resolution written on the
|
|
7
|
+
* second line of a bullet is found instead of missed, and a fenced example
|
|
8
|
+
* nested under an item is the item's.
|
|
9
|
+
*
|
|
10
|
+
* The items open at a line are kept on a stack, innermost last, and each line
|
|
11
|
+
* closes items from the top. Every item is opened and closed once, so the
|
|
12
|
+
* pass is linear however deep the nesting.
|
|
13
|
+
*/
|
|
14
|
+
import { type Layout } from './layout.js';
|
|
15
|
+
import type { Heading, ListItem } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Items end where CommonMark's do, near enough: at a heading; at a line no
|
|
18
|
+
* deeper than the marker after a blank line; and at a line no deeper than the
|
|
19
|
+
* marker that opens something - another item, a block quote, a thematic
|
|
20
|
+
* break, a fence. A block of code is owned whole or not at all.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findListItems(layout: Layout, headings: readonly Heading[]): ListItem[];
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List items, as blocks rather than lines.
|
|
3
|
+
*
|
|
4
|
+
* An item owns what is nested under it - anything indented past its marker -
|
|
5
|
+
* and its lazy continuation lines: text directly under it, with no blank line
|
|
6
|
+
* between, that opens nothing of its own. So a resolution written on the
|
|
7
|
+
* second line of a bullet is found instead of missed, and a fenced example
|
|
8
|
+
* nested under an item is the item's.
|
|
9
|
+
*
|
|
10
|
+
* The items open at a line are kept on a stack, innermost last, and each line
|
|
11
|
+
* closes items from the top. Every item is opened and closed once, so the
|
|
12
|
+
* pass is linear however deep the nesting.
|
|
13
|
+
*/
|
|
14
|
+
import { visibleLead } from './layout.js';
|
|
15
|
+
import { isThematicBreak, listMarker, measureIndentAt, stripQuotes } from './syntax.js';
|
|
16
|
+
const CHECKBOX = /^\[([ xX~\-?!*/+])\](?=[ \t]|$)/;
|
|
17
|
+
/**
|
|
18
|
+
* Items end where CommonMark's do, near enough: at a heading; at a line no
|
|
19
|
+
* deeper than the marker after a blank line; and at a line no deeper than the
|
|
20
|
+
* marker that opens something - another item, a block quote, a thematic
|
|
21
|
+
* break, a fence. A block of code is owned whole or not at all.
|
|
22
|
+
*/
|
|
23
|
+
export function findListItems(layout, headings) {
|
|
24
|
+
const { lines, text, structure, blocks } = layout;
|
|
25
|
+
const heading = new Set();
|
|
26
|
+
for (const h of headings)
|
|
27
|
+
for (let line = h.line; line <= h.endLine; line += 1)
|
|
28
|
+
heading.add(line);
|
|
29
|
+
const items = [];
|
|
30
|
+
const stack = [];
|
|
31
|
+
// Whether a blank line came since the last line read. Its first value is
|
|
32
|
+
// never asked for: no item is open before a marker line, which clears it.
|
|
33
|
+
let sawBlank = false;
|
|
34
|
+
// The last line every open item owns: each owns every line it has not been closed by.
|
|
35
|
+
let lastEnd = 0;
|
|
36
|
+
let lastLine = 0;
|
|
37
|
+
let block = 0;
|
|
38
|
+
const closeWhile = (closes) => {
|
|
39
|
+
for (let top = stack[stack.length - 1]; top !== undefined && closes(top); top = stack[stack.length - 1]) {
|
|
40
|
+
top.end = lastEnd;
|
|
41
|
+
top.endLine = lastLine;
|
|
42
|
+
stack.pop();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
// Front matter is blank in the structure mask, so no marker is visible in it.
|
|
46
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
47
|
+
const line = lines[i];
|
|
48
|
+
// A blank line inside a block of code is skipped with the block.
|
|
49
|
+
if (line.blank) {
|
|
50
|
+
sawBlank = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// No item is open after a heading, so whether a blank line came before it
|
|
54
|
+
// is never asked.
|
|
55
|
+
if (heading.has(line.line)) {
|
|
56
|
+
closeWhile(() => true);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (line.code || line.html) {
|
|
60
|
+
while (blocks[block].endLine < line.line)
|
|
61
|
+
block += 1;
|
|
62
|
+
const owned = blocks[block];
|
|
63
|
+
closeWhile((item) => indentFor(text, line, item) <= item.indent);
|
|
64
|
+
lastEnd = owned.end;
|
|
65
|
+
lastLine = owned.endLine;
|
|
66
|
+
i = owned.endLine - 1;
|
|
67
|
+
sawBlank = false;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const marker = visibleLead(layout, line) ? listMarker(line.content) : null;
|
|
71
|
+
const opens = marker !== null || (visibleLead(layout, line) && isThematicBreak(line.content));
|
|
72
|
+
closeWhile((item) => {
|
|
73
|
+
if (indentFor(text, line, item) > item.indent)
|
|
74
|
+
return false;
|
|
75
|
+
return sawBlank || opens || line.quoteDepth > item.quoteDepth;
|
|
76
|
+
});
|
|
77
|
+
lastEnd = line.end;
|
|
78
|
+
lastLine = line.line;
|
|
79
|
+
sawBlank = false;
|
|
80
|
+
if (marker === null)
|
|
81
|
+
continue;
|
|
82
|
+
const markerStart = line.contentStart + marker.offset;
|
|
83
|
+
let textStart = markerStart + marker.width;
|
|
84
|
+
const box = CHECKBOX.exec(structure.slice(textStart, line.end));
|
|
85
|
+
const checkboxStart = box === null ? null : textStart;
|
|
86
|
+
if (box !== null) {
|
|
87
|
+
// The character at the end of the line is its terminator, not a space.
|
|
88
|
+
textStart += 3;
|
|
89
|
+
while (text.charCodeAt(textStart) === 32 || text.charCodeAt(textStart) === 9)
|
|
90
|
+
textStart += 1;
|
|
91
|
+
}
|
|
92
|
+
const item = {
|
|
93
|
+
start: line.contentStart,
|
|
94
|
+
end: line.end,
|
|
95
|
+
markerStart,
|
|
96
|
+
marker: marker.marker,
|
|
97
|
+
textStart,
|
|
98
|
+
checkbox: box === null ? null : box[1],
|
|
99
|
+
checkboxStart,
|
|
100
|
+
firstLine: text.slice(textStart, line.end).trim(),
|
|
101
|
+
line: line.line,
|
|
102
|
+
endLine: line.line,
|
|
103
|
+
depth: stack.length,
|
|
104
|
+
indent: line.indent,
|
|
105
|
+
quoteDepth: line.quoteDepth,
|
|
106
|
+
};
|
|
107
|
+
items.push(item);
|
|
108
|
+
stack.push(item);
|
|
109
|
+
}
|
|
110
|
+
closeWhile(() => true);
|
|
111
|
+
return items.map((item) => ({
|
|
112
|
+
...item,
|
|
113
|
+
body: text.slice(item.textStart, item.end),
|
|
114
|
+
maskedBody: structure.slice(item.textStart, item.end),
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A line's indentation as an item sees it: measured inside the item's block
|
|
119
|
+
* quote. A line quoted deeper than the item is measured up to its extra `>`,
|
|
120
|
+
* so a quote nested under an item is nested, and one at the item's own
|
|
121
|
+
* indentation opens a block quote beside it.
|
|
122
|
+
*/
|
|
123
|
+
function indentFor(text, line, item) {
|
|
124
|
+
const q = stripQuotes(text, line.start, item.quoteDepth);
|
|
125
|
+
return measureIndentAt(text, q.contentStart, line.end);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=lists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lists.js","sourceRoot":"","sources":["../../../../src/vendor/spec-core/markdown/lists.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * List items, as blocks rather than lines.\n *\n * An item owns what is nested under it - anything indented past its marker -\n * and its lazy continuation lines: text directly under it, with no blank line\n * between, that opens nothing of its own. So a resolution written on the\n * second line of a bullet is found instead of missed, and a fenced example\n * nested under an item is the item's.\n *\n * The items open at a line are kept on a stack, innermost last, and each line\n * closes items from the top. Every item is opened and closed once, so the\n * pass is linear however deep the nesting.\n */\n\nimport { visibleLead, type Layout } from './layout.js';\nimport { isThematicBreak, listMarker, measureIndentAt, stripQuotes } from './syntax.js';\nimport type { Block, Heading, ListItem, ScannedLine } from './types.js';\n\nconst CHECKBOX = /^\\[([ xX~\\-?!*/+])\\](?=[ \\t]|$)/;\n\ntype Draft = { -readonly [K in Exclude<keyof ListItem, 'body' | 'maskedBody'>]: ListItem[K] };\n\n/**\n * Items end where CommonMark's do, near enough: at a heading; at a line no\n * deeper than the marker after a blank line; and at a line no deeper than the\n * marker that opens something - another item, a block quote, a thematic\n * break, a fence. A block of code is owned whole or not at all.\n */\nexport function findListItems(layout: Layout, headings: readonly Heading[]): ListItem[] {\n const { lines, text, structure, blocks } = layout;\n const heading = new Set<number>();\n for (const h of headings) for (let line = h.line; line <= h.endLine; line += 1) heading.add(line);\n\n const items: Draft[] = [];\n const stack: Draft[] = [];\n // Whether a blank line came since the last line read. Its first value is\n // never asked for: no item is open before a marker line, which clears it.\n let sawBlank = false;\n // The last line every open item owns: each owns every line it has not been closed by.\n let lastEnd = 0;\n let lastLine = 0;\n let block = 0;\n\n const closeWhile = (closes: (item: Draft) => boolean): void => {\n for (let top = stack[stack.length - 1]; top !== undefined && closes(top); top = stack[stack.length - 1]) {\n top.end = lastEnd;\n top.endLine = lastLine;\n stack.pop();\n }\n };\n\n // Front matter is blank in the structure mask, so no marker is visible in it.\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i] as ScannedLine;\n // A blank line inside a block of code is skipped with the block.\n if (line.blank) {\n sawBlank = true;\n continue;\n }\n // No item is open after a heading, so whether a blank line came before it\n // is never asked.\n if (heading.has(line.line)) {\n closeWhile(() => true);\n continue;\n }\n\n if (line.code || line.html) {\n while ((blocks[block] as Block).endLine < line.line) block += 1;\n const owned = blocks[block] as Block;\n closeWhile((item) => indentFor(text, line, item) <= item.indent);\n lastEnd = owned.end;\n lastLine = owned.endLine;\n i = owned.endLine - 1;\n sawBlank = false;\n continue;\n }\n\n const marker = visibleLead(layout, line) ? listMarker(line.content) : null;\n const opens = marker !== null || (visibleLead(layout, line) && isThematicBreak(line.content));\n closeWhile((item) => {\n if (indentFor(text, line, item) > item.indent) return false;\n return sawBlank || opens || line.quoteDepth > item.quoteDepth;\n });\n lastEnd = line.end;\n lastLine = line.line;\n sawBlank = false;\n if (marker === null) continue;\n\n const markerStart = line.contentStart + marker.offset;\n let textStart = markerStart + marker.width;\n const box = CHECKBOX.exec(structure.slice(textStart, line.end));\n const checkboxStart = box === null ? null : textStart;\n if (box !== null) {\n // The character at the end of the line is its terminator, not a space.\n textStart += 3;\n while (text.charCodeAt(textStart) === 32 || text.charCodeAt(textStart) === 9) textStart += 1;\n }\n const item: Draft = {\n start: line.contentStart,\n end: line.end,\n markerStart,\n marker: marker.marker,\n textStart,\n checkbox: box === null ? null : (box[1] as string),\n checkboxStart,\n firstLine: text.slice(textStart, line.end).trim(),\n line: line.line,\n endLine: line.line,\n depth: stack.length,\n indent: line.indent,\n quoteDepth: line.quoteDepth,\n };\n items.push(item);\n stack.push(item);\n }\n closeWhile(() => true);\n\n return items.map((item) => ({\n ...item,\n body: text.slice(item.textStart, item.end),\n maskedBody: structure.slice(item.textStart, item.end),\n }));\n}\n\n/**\n * A line's indentation as an item sees it: measured inside the item's block\n * quote. A line quoted deeper than the item is measured up to its extra `>`,\n * so a quote nested under an item is nested, and one at the item's own\n * indentation opens a block quote beside it.\n */\nfunction indentFor(text: string, line: ScannedLine, item: Draft): number {\n const q = stripQuotes(text, line.start, item.quoteDepth);\n return measureIndentAt(text, q.contentStart, line.end);\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A structural Markdown scanner.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately not a CommonMark parser: nothing here renders. What it must get
|
|
5
|
+
* exactly right is what is code, what is a comment, and what is neither,
|
|
6
|
+
* because every reader downstream asks that question first. A heading inside
|
|
7
|
+
* a fenced block is not a section, a template hint inside `<!-- -->` is not
|
|
8
|
+
* content, and a link in a code sample is not a reference.
|
|
9
|
+
*
|
|
10
|
+
* The scan works on offsets. One pass over the lines finds front matter, code
|
|
11
|
+
* blocks and raw-text HTML, and inside everything else it finds code spans and
|
|
12
|
+
* comments in a single sweep from left to right, as CommonMark does: whichever
|
|
13
|
+
* of the two opens first wins, so `` `<!--` `` is code that mentions a comment,
|
|
14
|
+
* and a backtick inside a comment is a character. Three masks are built from
|
|
15
|
+
* the result, and headings, list items, links and tables are read from them.
|
|
16
|
+
*
|
|
17
|
+
* Every pass is linear in the text. No search runs to the end of the document
|
|
18
|
+
* once per opener: a code span looks for its closer only to the end of its
|
|
19
|
+
* paragraph and remembers what it saw there, and a comment with no `-->`
|
|
20
|
+
* after it tells every later one that there is none.
|
|
21
|
+
*
|
|
22
|
+
* Loops here that stop at a line's end compare against it, and the character
|
|
23
|
+
* there is the line's terminator, which none of them acts on. A bound moved
|
|
24
|
+
* to read one character further changes no answer; mutation testing reports
|
|
25
|
+
* such mutants as surviving, and they are equivalent. So is a search for `-->`
|
|
26
|
+
* compared as `close <= 0` rather than `close < 0`: it starts past `<!--`.
|
|
27
|
+
*/
|
|
28
|
+
import type { MarkdownScan, MaskKind } from './types.js';
|
|
29
|
+
/** Scans a Markdown document. Pure, and linear in the length of the text. */
|
|
30
|
+
export declare function scanMarkdown(source: string): MarkdownScan;
|
|
31
|
+
/**
|
|
32
|
+
* The lines of the text or of one of its masks, without terminators, as
|
|
33
|
+
* `splitLines` gives them: a final terminator leaves a last, empty line.
|
|
34
|
+
* Every view has the same number of lines, each the same length.
|
|
35
|
+
*/
|
|
36
|
+
export declare function linesOf(scan: MarkdownScan, view?: MaskKind | 'text'): string[];
|