@descent-vtt/spec-graph 0.7.0 → 0.9.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/CHANGELOG.md +477 -0
- package/README.md +109 -17
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +38 -19
- package/dist/cli.js.map +1 -1
- package/dist/directives.d.ts +9 -2
- package/dist/directives.js +15 -4
- package/dist/directives.js.map +1 -1
- package/dist/extract.d.ts +1 -1
- package/dist/extract.js +50 -16
- package/dist/extract.js.map +1 -1
- package/dist/glob.d.ts +44 -44
- package/dist/glob.js +152 -116
- package/dist/glob.js.map +1 -1
- package/dist/identity.d.ts +12 -4
- package/dist/identity.js +32 -8
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.js +10 -2
- package/dist/lifecycle.js.map +1 -1
- package/dist/markdown.d.ts +53 -161
- package/dist/markdown.js +42 -864
- package/dist/markdown.js.map +1 -1
- package/dist/report.d.ts +23 -0
- package/dist/report.js +80 -0
- package/dist/report.js.map +1 -1
- package/dist/rules.js +48 -14
- package/dist/rules.js.map +1 -1
- package/dist/sections.d.ts +1 -1
- package/dist/sections.js +18 -4
- package/dist/sections.js.map +1 -1
- package/dist/select.d.ts +2 -2
- package/dist/select.js +3 -3
- package/dist/select.js.map +1 -1
- package/dist/source.d.ts +6 -19
- package/dist/source.js +4 -70
- package/dist/source.js.map +1 -1
- package/dist/types.d.ts +23 -3
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- 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/{regex.d.ts → vendor/spec-core/pattern/regex.d.ts} +20 -29
- package/dist/{regex.js → vendor/spec-core/pattern/regex.js} +40 -49
- 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/yaml.d.ts +44 -12
- package/dist/yaml.js +115 -206
- package/dist/yaml.js.map +1 -1
- package/package.json +3 -2
- package/src/vendor/spec-core/LICENSE +21 -0
- package/src/vendor/spec-core/README.md +8 -0
- package/dist/regex.js.map +0 -1
|
@@ -0,0 +1,439 @@
|
|
|
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 { createLineIndex, inRanges, maskRanges, mergeRanges, splitLines, stripBom } from '../text/index.js';
|
|
29
|
+
import { frontMatterCloses, frontMatterKind } from './frontmatter.js';
|
|
30
|
+
import { findAtxHeadings, findHeadings } from './headings.js';
|
|
31
|
+
import { findLinks } from './links.js';
|
|
32
|
+
import { findListItems } from './lists.js';
|
|
33
|
+
import { atxLevel, fenceCloses, fenceOpen, hasRawTextClose, isRawTextOpen, isThematicBreak, leadIndex, listMarker, measureIndent, opensComment, setextUnderline, stripQuotes, } from './syntax.js';
|
|
34
|
+
import { findTables } from './tables.js';
|
|
35
|
+
const BACKTICK = 0x60;
|
|
36
|
+
const BACKSLASH = 0x5c;
|
|
37
|
+
/** Scans a Markdown document. Pure, and linear in the length of the text. */
|
|
38
|
+
export function scanMarkdown(source) {
|
|
39
|
+
const text = stripBom(source);
|
|
40
|
+
const index = createLineIndex(text);
|
|
41
|
+
const frontMatter = readFrontMatterBlock(text, index);
|
|
42
|
+
const bodyStart = frontMatter === null ? 0 : frontMatter.bodyStart;
|
|
43
|
+
const core = scanCore(text, index, frontMatter === null ? 0 : frontMatter.closeLine);
|
|
44
|
+
// Without front matter this range is empty, and merging drops it.
|
|
45
|
+
const front = [{ start: 0, end: bodyStart }];
|
|
46
|
+
const ranges = {
|
|
47
|
+
structure: mergeRanges([...front, ...core.blocks, ...core.spans, ...core.comments]),
|
|
48
|
+
prose: mergeRanges([...front, ...core.comments]),
|
|
49
|
+
directives: mergeRanges([...front, ...core.blocks, ...core.spans]),
|
|
50
|
+
};
|
|
51
|
+
const masks = {
|
|
52
|
+
structure: maskRanges(text, ranges.structure),
|
|
53
|
+
prose: maskRanges(text, ranges.prose),
|
|
54
|
+
directives: maskRanges(text, ranges.directives),
|
|
55
|
+
};
|
|
56
|
+
const layout = {
|
|
57
|
+
text,
|
|
58
|
+
index,
|
|
59
|
+
lines: core.lines,
|
|
60
|
+
blocks: core.blocks,
|
|
61
|
+
comments: core.comments,
|
|
62
|
+
structure: masks.structure,
|
|
63
|
+
prose: masks.prose,
|
|
64
|
+
covered: core.covered,
|
|
65
|
+
continues: core.continues,
|
|
66
|
+
};
|
|
67
|
+
const atx = findAtxHeadings(layout);
|
|
68
|
+
const tables = findTables(layout, atx);
|
|
69
|
+
const headings = findHeadings(layout, atx, tables);
|
|
70
|
+
return {
|
|
71
|
+
text,
|
|
72
|
+
bom: source.length - text.length,
|
|
73
|
+
index,
|
|
74
|
+
lines: core.lines,
|
|
75
|
+
frontMatter,
|
|
76
|
+
bodyStart,
|
|
77
|
+
blocks: core.blocks,
|
|
78
|
+
codeSpans: core.spans,
|
|
79
|
+
comments: core.comments,
|
|
80
|
+
masks,
|
|
81
|
+
headings,
|
|
82
|
+
listItems: findListItems(layout, headings),
|
|
83
|
+
links: findLinks(layout),
|
|
84
|
+
tables,
|
|
85
|
+
isMasked: (offset, mask = 'structure') => inRanges(ranges[mask], offset),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The lines of the text or of one of its masks, without terminators, as
|
|
90
|
+
* `splitLines` gives them: a final terminator leaves a last, empty line.
|
|
91
|
+
* Every view has the same number of lines, each the same length.
|
|
92
|
+
*/
|
|
93
|
+
export function linesOf(scan, view = 'text') {
|
|
94
|
+
return splitLines(view === 'text' ? scan.text : scan.masks[view]);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* YAML between `---` lines, closed by `---` or `...`, or TOML between `+++`
|
|
98
|
+
* lines. The opening line is the document's first, exactly. An opening line
|
|
99
|
+
* that is never closed opens nothing: it is a thematic break.
|
|
100
|
+
*/
|
|
101
|
+
function readFrontMatterBlock(text, index) {
|
|
102
|
+
const kind = frontMatterKind(index.lineText(1));
|
|
103
|
+
if (kind === null)
|
|
104
|
+
return null;
|
|
105
|
+
for (let line = 2; line <= index.lineCount; line += 1) {
|
|
106
|
+
if (!frontMatterCloses(index.lineText(line), kind))
|
|
107
|
+
continue;
|
|
108
|
+
const start = index.lineStart(2);
|
|
109
|
+
const end = index.lineStart(line);
|
|
110
|
+
const bodyStart = line < index.lineCount ? index.lineStart(line + 1) : text.length;
|
|
111
|
+
return { kind, raw: text.slice(start, end), start, end, bodyStart, closeLine: line };
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The block pass, with the inline sweep for code spans and comments run
|
|
117
|
+
* inside it line by line.
|
|
118
|
+
*
|
|
119
|
+
* The two cannot be separate passes. Whether a line opens a fence depends on
|
|
120
|
+
* whether it lies inside a comment, and whether a comment opens depends on
|
|
121
|
+
* whether its `<!--` lies inside a code span.
|
|
122
|
+
*/
|
|
123
|
+
function scanCore(text, index, frontLines) {
|
|
124
|
+
const count = index.lineCount;
|
|
125
|
+
const lines = [];
|
|
126
|
+
const blocks = [];
|
|
127
|
+
const spans = [];
|
|
128
|
+
const comments = [];
|
|
129
|
+
const covered = new Set();
|
|
130
|
+
for (let i = 0; i < frontLines; i += 1) {
|
|
131
|
+
const start = index.lineStart(i + 1);
|
|
132
|
+
const end = index.lineEnd(i + 1);
|
|
133
|
+
lines.push(record(i, start, end, start, text.slice(start, end), 0, { frontMatter: true, code: false, html: false }));
|
|
134
|
+
}
|
|
135
|
+
// One shape too many, for a line past the last, would never be read.
|
|
136
|
+
const shapes = [];
|
|
137
|
+
for (let i = frontLines; i < count; i += 1) {
|
|
138
|
+
const start = index.lineStart(i + 1);
|
|
139
|
+
const end = index.lineEnd(i + 1);
|
|
140
|
+
const q = stripQuotes(text, start, Number.POSITIVE_INFINITY);
|
|
141
|
+
const content = text.slice(q.contentStart, end);
|
|
142
|
+
const indent = measureIndent(content);
|
|
143
|
+
const blank = leadIndex(content) === content.length;
|
|
144
|
+
const atx = atxLevel(content) > 0;
|
|
145
|
+
const previous = shapes[shapes.length - 1];
|
|
146
|
+
const interrupts = atx ||
|
|
147
|
+
isThematicBreak(content) ||
|
|
148
|
+
setextUnderline(content) !== null ||
|
|
149
|
+
opensComment(content) ||
|
|
150
|
+
listMarker(content) !== null ||
|
|
151
|
+
fenceOpen(content) !== null ||
|
|
152
|
+
isRawTextOpen(content) ||
|
|
153
|
+
(previous !== undefined && q.depth > previous.depth);
|
|
154
|
+
shapes.push({ contentStart: q.contentStart, depth: q.depth, indent, blank, atx, interrupts });
|
|
155
|
+
}
|
|
156
|
+
const shapeOf = (i) => shapes[i - frontLines];
|
|
157
|
+
// A line continues the paragraph above it when it is not blank, opens
|
|
158
|
+
// nothing of its own, and the line above is not a one-line heading. After a
|
|
159
|
+
// blank line there is no paragraph to continue, and no search asks. Each
|
|
160
|
+
// body line's paragraph ends at `stops`, walked from the bottom up.
|
|
161
|
+
const continues = new Set();
|
|
162
|
+
const stops = new Map();
|
|
163
|
+
for (let i = count - 1; i >= frontLines; i -= 1) {
|
|
164
|
+
const shape = shapeOf(i);
|
|
165
|
+
const above = shapes[i - frontLines - 1];
|
|
166
|
+
if (above !== undefined && !above.atx && !shape.blank && !shape.interrupts)
|
|
167
|
+
continues.add(i + 1);
|
|
168
|
+
stops.set(i, continues.has(i + 2) ? stops.get(i + 1) : index.lineEnd(i + 1));
|
|
169
|
+
}
|
|
170
|
+
/* ------------------------------------------------------------ inline sweep */
|
|
171
|
+
// Where the last failed search for a closing backtick run stopped, and the
|
|
172
|
+
// last run of each length it passed. Openers come in order, so a later one
|
|
173
|
+
// before that stop lies inside the stretch searched, and closes only if a
|
|
174
|
+
// run of its length lies after it there, which is a lookup.
|
|
175
|
+
let seen = null;
|
|
176
|
+
/** Where the span opened by a run ending at `from` closes, if it does. */
|
|
177
|
+
const closeRun = (from, length, line) => {
|
|
178
|
+
// A run of a length the search never saw - one an escape split in two -
|
|
179
|
+
// has no closer either. Reading its absence as any number below `from`
|
|
180
|
+
// gives that answer, and one that is not below it gives it by searching.
|
|
181
|
+
// No run starts at `from`, which follows one, and an opener at `seen.end`
|
|
182
|
+
// finds nothing either way: the comparisons are equivalent at equality.
|
|
183
|
+
if (seen !== null && from < seen.end && (seen.last.get(length) ?? -1) < from)
|
|
184
|
+
return undefined;
|
|
185
|
+
const stop = stops.get(line);
|
|
186
|
+
const last = new Map();
|
|
187
|
+
let at = text.indexOf('`', from);
|
|
188
|
+
while (at >= 0 && at < stop) {
|
|
189
|
+
const run = at;
|
|
190
|
+
while (at < stop && text.charCodeAt(at) === BACKTICK)
|
|
191
|
+
at += 1;
|
|
192
|
+
if (at - run === length)
|
|
193
|
+
return at;
|
|
194
|
+
last.set(at - run, run);
|
|
195
|
+
at = text.indexOf('`', at);
|
|
196
|
+
}
|
|
197
|
+
seen = { end: stop, last };
|
|
198
|
+
return undefined;
|
|
199
|
+
};
|
|
200
|
+
// Once one `<!--` has no `-->` after it, no later one has either.
|
|
201
|
+
let closable = true;
|
|
202
|
+
/** Where the comment opened at `at` closes, if it does. */
|
|
203
|
+
const commentEnd = (at) => {
|
|
204
|
+
// CommonMark 0.31: `<!-->` and `<!--->` are whole, empty comments.
|
|
205
|
+
if (text.startsWith('<!-->', at))
|
|
206
|
+
return at + 5;
|
|
207
|
+
if (text.startsWith('<!--->', at))
|
|
208
|
+
return at + 6;
|
|
209
|
+
if (!closable)
|
|
210
|
+
return undefined;
|
|
211
|
+
const close = text.indexOf('-->', at + 4);
|
|
212
|
+
closable = close !== -1;
|
|
213
|
+
return closable ? close + 3 : undefined;
|
|
214
|
+
};
|
|
215
|
+
const pushComment = (start, end, closed) => {
|
|
216
|
+
comments.push({
|
|
217
|
+
start,
|
|
218
|
+
end,
|
|
219
|
+
// An empty comment's `-->` overlaps its `<!--`, and the slice is empty.
|
|
220
|
+
inner: text.slice(start + 4, closed ? end - 3 : end),
|
|
221
|
+
innerStart: start + 4,
|
|
222
|
+
line: index.positionAt(start).line,
|
|
223
|
+
closed,
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Sweeps one line from `from`, returning where the next line's sweep may
|
|
228
|
+
* begin: past a span or comment that runs onto a later line, or the line's
|
|
229
|
+
* end. `lead` is the offset at which a `<!--` would open the line, or the
|
|
230
|
+
* end of the line when none can: a sweep never stops there.
|
|
231
|
+
*/
|
|
232
|
+
const sweep = (from, end, line, lead) => {
|
|
233
|
+
let at = from;
|
|
234
|
+
while (at < end) {
|
|
235
|
+
const ch = text.charCodeAt(at);
|
|
236
|
+
if (ch === BACKSLASH) {
|
|
237
|
+
// An escaped backtick opens no span, and an escaped `<` no comment.
|
|
238
|
+
at += 2;
|
|
239
|
+
}
|
|
240
|
+
else if (ch === BACKTICK) {
|
|
241
|
+
let run = at + 1;
|
|
242
|
+
while (run < end && text.charCodeAt(run) === BACKTICK)
|
|
243
|
+
run += 1;
|
|
244
|
+
const close = closeRun(run, run - at, line);
|
|
245
|
+
if (close === undefined) {
|
|
246
|
+
at = run;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
spans.push({ start: at, end: close });
|
|
250
|
+
at = close;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (text.startsWith('<!--', at)) {
|
|
254
|
+
const close = commentEnd(at);
|
|
255
|
+
if (close !== undefined) {
|
|
256
|
+
pushComment(at, close, true);
|
|
257
|
+
at = close;
|
|
258
|
+
}
|
|
259
|
+
else if (at === lead) {
|
|
260
|
+
// CommonMark's HTML block: a comment opening a line runs to its
|
|
261
|
+
// `-->`, and one never closed runs to the end of the document.
|
|
262
|
+
pushComment(at, text.length, false);
|
|
263
|
+
return text.length;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
at += 4;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
at += 1;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return at;
|
|
274
|
+
};
|
|
275
|
+
let fence = null;
|
|
276
|
+
let raw = null;
|
|
277
|
+
let indented = null;
|
|
278
|
+
let previousBlank = true;
|
|
279
|
+
// The content column of each list item open around the line, innermost last.
|
|
280
|
+
const items = [];
|
|
281
|
+
let pos = 0;
|
|
282
|
+
const openBlock = (kind, i, info) => {
|
|
283
|
+
const block = { kind, start: index.lineStart(i + 1), end: index.lineEnd(i + 1), line: i + 1, endLine: i + 1, info, closed: kind === 'indented' };
|
|
284
|
+
blocks.push(block);
|
|
285
|
+
return block;
|
|
286
|
+
};
|
|
287
|
+
const extend = (block, i) => {
|
|
288
|
+
block.end = index.lineEnd(i + 1);
|
|
289
|
+
block.endLine = i + 1;
|
|
290
|
+
};
|
|
291
|
+
// Blank lines after an indented block were marked as code in case more
|
|
292
|
+
// code followed; the block ends at its last code line.
|
|
293
|
+
const endIndented = (block, next) => {
|
|
294
|
+
for (let i = block.endLine; i < next; i += 1)
|
|
295
|
+
lines[i].code = false;
|
|
296
|
+
};
|
|
297
|
+
/** Classifies one line and sweeps it; the caller records whether it was blank. */
|
|
298
|
+
const scanLine = (i) => {
|
|
299
|
+
const start = index.lineStart(i + 1);
|
|
300
|
+
const end = index.lineEnd(i + 1);
|
|
301
|
+
const shape = shapeOf(i);
|
|
302
|
+
const content = text.slice(shape.contentStart, end);
|
|
303
|
+
const push = (kind) => {
|
|
304
|
+
lines.push(record(i, start, end, shape.contentStart, content, shape.depth, kind));
|
|
305
|
+
};
|
|
306
|
+
if (pos > start) {
|
|
307
|
+
// Inside a comment or span opened above: nothing opens a block here.
|
|
308
|
+
covered.add(i + 1);
|
|
309
|
+
push(NONE);
|
|
310
|
+
pos = sweep(pos, end, i, end);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const container = fence ?? raw;
|
|
314
|
+
if (container !== null) {
|
|
315
|
+
// A fenced block or raw-text block ends with its block quote.
|
|
316
|
+
const q = stripQuotes(text, start, container.depth);
|
|
317
|
+
if (q.depth === container.depth) {
|
|
318
|
+
const inside = text.slice(q.contentStart, end);
|
|
319
|
+
lines.push(record(i, start, end, q.contentStart, inside, q.depth, fence === null ? HTML : CODE));
|
|
320
|
+
extend(container.block, i);
|
|
321
|
+
if (fence !== null ? fenceCloses(inside, fence.open) : hasRawTextClose(inside)) {
|
|
322
|
+
container.block.closed = true;
|
|
323
|
+
fence = null;
|
|
324
|
+
raw = null;
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
fence = null;
|
|
329
|
+
raw = null;
|
|
330
|
+
}
|
|
331
|
+
// After a blank line, a line left of an item's content is outside it.
|
|
332
|
+
if (!shape.blank && previousBlank) {
|
|
333
|
+
while (items.length > 0 && items[items.length - 1] > shape.indent)
|
|
334
|
+
items.pop();
|
|
335
|
+
}
|
|
336
|
+
// Indented code: four columns past the item the line is in, or past the
|
|
337
|
+
// margin outside a list, and never interrupting a paragraph. Four columns
|
|
338
|
+
// inside an item are its text, not code.
|
|
339
|
+
const margin = items[items.length - 1] ?? 0;
|
|
340
|
+
const deep = shape.indent >= margin + 4;
|
|
341
|
+
if (!shape.blank && deep && (previousBlank || indented !== null)) {
|
|
342
|
+
if (indented === null)
|
|
343
|
+
indented = openBlock('indented', i, '');
|
|
344
|
+
else
|
|
345
|
+
extend(indented, i);
|
|
346
|
+
push(CODE);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (shape.blank) {
|
|
350
|
+
push(indented === null ? NONE : CODE);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (indented !== null) {
|
|
354
|
+
endIndented(indented, i);
|
|
355
|
+
indented = null;
|
|
356
|
+
}
|
|
357
|
+
// A fence as deep as indented code continues a paragraph instead.
|
|
358
|
+
const open = deep ? null : fenceOpen(content);
|
|
359
|
+
if (open !== null) {
|
|
360
|
+
fence = { block: openBlock('fenced', i, open.info), depth: shape.depth, open };
|
|
361
|
+
push(CODE);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (isRawTextOpen(content)) {
|
|
365
|
+
const block = openBlock('html', i, '');
|
|
366
|
+
push(HTML);
|
|
367
|
+
if (hasRawTextClose(content))
|
|
368
|
+
block.closed = true;
|
|
369
|
+
else
|
|
370
|
+
raw = { block, depth: shape.depth };
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
push(NONE);
|
|
374
|
+
const marker = listMarker(content);
|
|
375
|
+
if (marker !== null) {
|
|
376
|
+
// A marker left of an item's content starts a sibling or an outer item.
|
|
377
|
+
while (items.length > 0 && items[items.length - 1] > shape.indent)
|
|
378
|
+
items.pop();
|
|
379
|
+
items.push(itemColumn(shape.indent, marker));
|
|
380
|
+
}
|
|
381
|
+
// A heading ends a list whatever its indentation.
|
|
382
|
+
if (shape.atx)
|
|
383
|
+
items.length = 0;
|
|
384
|
+
const lead = shape.indent <= 3 ? shape.contentStart + leadIndex(content) : end;
|
|
385
|
+
pos = sweep(shape.contentStart, end, i, lead);
|
|
386
|
+
};
|
|
387
|
+
for (let i = frontLines; i < count; i += 1) {
|
|
388
|
+
scanLine(i);
|
|
389
|
+
// Whether the line was blank to look at, whatever it was classified as:
|
|
390
|
+
// indented code starts only after one.
|
|
391
|
+
previousBlank = shapeOf(i).blank;
|
|
392
|
+
}
|
|
393
|
+
if (indented !== null)
|
|
394
|
+
endIndented(indented, count);
|
|
395
|
+
markCommentLines(lines, comments);
|
|
396
|
+
return { lines, blocks, spans, comments, covered, continues };
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* The column an item's text starts at: past its marker and the one to four
|
|
400
|
+
* spaces after it. With none, or five or more, the text starts one column
|
|
401
|
+
* past the marker, and anything further is indented code.
|
|
402
|
+
*/
|
|
403
|
+
function itemColumn(indent, marker) {
|
|
404
|
+
const spaces = marker.width - marker.marker.length;
|
|
405
|
+
return indent + marker.marker.length + (spaces >= 1 && spaces <= 4 ? spaces : 1);
|
|
406
|
+
}
|
|
407
|
+
const NONE = { frontMatter: false, code: false, html: false };
|
|
408
|
+
const CODE = { frontMatter: false, code: true, html: false };
|
|
409
|
+
const HTML = { frontMatter: false, code: false, html: true };
|
|
410
|
+
function record(i, start, end, contentStart, content, quoteDepth, kinds) {
|
|
411
|
+
return {
|
|
412
|
+
line: i + 1,
|
|
413
|
+
start,
|
|
414
|
+
end,
|
|
415
|
+
contentStart,
|
|
416
|
+
content,
|
|
417
|
+
indent: measureIndent(content),
|
|
418
|
+
blank: leadIndex(content) === content.length,
|
|
419
|
+
quoteDepth,
|
|
420
|
+
...kinds,
|
|
421
|
+
comment: false,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/** Marks each line whose first non-blank character lies inside a comment. */
|
|
425
|
+
function markCommentLines(lines, comments) {
|
|
426
|
+
let c = 0;
|
|
427
|
+
for (const line of lines) {
|
|
428
|
+
if (line.blank)
|
|
429
|
+
continue;
|
|
430
|
+
const lead = line.contentStart + leadIndex(line.content);
|
|
431
|
+
// No comment ends exactly at a line's first non-blank character: a
|
|
432
|
+
// comment ends in `>`, so one that ends before it ends on an earlier line.
|
|
433
|
+
while (c < comments.length && comments[c].end <= lead)
|
|
434
|
+
c += 1;
|
|
435
|
+
const comment = comments[c];
|
|
436
|
+
line.comment = comment !== undefined && comment.start <= lead;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../../src/vendor/spec-core/markdown/scan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A structural Markdown scanner.\n *\n * Deliberately not a CommonMark parser: nothing here renders. What it must get\n * exactly right is what is code, what is a comment, and what is neither,\n * because every reader downstream asks that question first. A heading inside\n * a fenced block is not a section, a template hint inside `<!-- -->` is not\n * content, and a link in a code sample is not a reference.\n *\n * The scan works on offsets. One pass over the lines finds front matter, code\n * blocks and raw-text HTML, and inside everything else it finds code spans and\n * comments in a single sweep from left to right, as CommonMark does: whichever\n * of the two opens first wins, so `` `<!--` `` is code that mentions a comment,\n * and a backtick inside a comment is a character. Three masks are built from\n * the result, and headings, list items, links and tables are read from them.\n *\n * Every pass is linear in the text. No search runs to the end of the document\n * once per opener: a code span looks for its closer only to the end of its\n * paragraph and remembers what it saw there, and a comment with no `-->`\n * after it tells every later one that there is none.\n *\n * Loops here that stop at a line's end compare against it, and the character\n * there is the line's terminator, which none of them acts on. A bound moved\n * to read one character further changes no answer; mutation testing reports\n * such mutants as surviving, and they are equivalent. So is a search for `-->`\n * compared as `close <= 0` rather than `close < 0`: it starts past `<!--`.\n */\n\nimport { createLineIndex, inRanges, maskRanges, mergeRanges, splitLines, stripBom, type LineIndex, type Range } from '../text/index.js';\nimport { frontMatterCloses, frontMatterKind } from './frontmatter.js';\nimport { findAtxHeadings, findHeadings } from './headings.js';\nimport type { Layout } from './layout.js';\nimport { findLinks } from './links.js';\nimport { findListItems } from './lists.js';\nimport {\n atxLevel,\n fenceCloses,\n fenceOpen,\n hasRawTextClose,\n isRawTextOpen,\n isThematicBreak,\n leadIndex,\n listMarker,\n measureIndent,\n opensComment,\n setextUnderline,\n stripQuotes,\n type FenceOpen,\n type ListMarker,\n} from './syntax.js';\nimport { findTables } from './tables.js';\nimport type { Block, BlockKind, FrontMatterBlock, HtmlComment, MarkdownScan, MaskKind, ScannedLine } from './types.js';\n\nconst BACKTICK = 0x60;\nconst BACKSLASH = 0x5c;\n\n/** Scans a Markdown document. Pure, and linear in the length of the text. */\nexport function scanMarkdown(source: string): MarkdownScan {\n const text = stripBom(source);\n const index = createLineIndex(text);\n const frontMatter = readFrontMatterBlock(text, index);\n const bodyStart = frontMatter === null ? 0 : frontMatter.bodyStart;\n const core = scanCore(text, index, frontMatter === null ? 0 : frontMatter.closeLine);\n\n // Without front matter this range is empty, and merging drops it.\n const front: Range[] = [{ start: 0, end: bodyStart }];\n const ranges: Record<MaskKind, Range[]> = {\n structure: mergeRanges([...front, ...core.blocks, ...core.spans, ...core.comments]),\n prose: mergeRanges([...front, ...core.comments]),\n directives: mergeRanges([...front, ...core.blocks, ...core.spans]),\n };\n const masks = {\n structure: maskRanges(text, ranges.structure),\n prose: maskRanges(text, ranges.prose),\n directives: maskRanges(text, ranges.directives),\n };\n\n const layout: Layout = {\n text,\n index,\n lines: core.lines,\n blocks: core.blocks,\n comments: core.comments,\n structure: masks.structure,\n prose: masks.prose,\n covered: core.covered,\n continues: core.continues,\n };\n const atx = findAtxHeadings(layout);\n const tables = findTables(layout, atx);\n const headings = findHeadings(layout, atx, tables);\n\n return {\n text,\n bom: source.length - text.length,\n index,\n lines: core.lines,\n frontMatter,\n bodyStart,\n blocks: core.blocks,\n codeSpans: core.spans,\n comments: core.comments,\n masks,\n headings,\n listItems: findListItems(layout, headings),\n links: findLinks(layout),\n tables,\n isMasked: (offset, mask = 'structure') => inRanges(ranges[mask], offset),\n };\n}\n\n/**\n * The lines of the text or of one of its masks, without terminators, as\n * `splitLines` gives them: a final terminator leaves a last, empty line.\n * Every view has the same number of lines, each the same length.\n */\nexport function linesOf(scan: MarkdownScan, view: MaskKind | 'text' = 'text'): string[] {\n return splitLines(view === 'text' ? scan.text : scan.masks[view]);\n}\n\n/**\n * YAML between `---` lines, closed by `---` or `...`, or TOML between `+++`\n * lines. The opening line is the document's first, exactly. An opening line\n * that is never closed opens nothing: it is a thematic break.\n */\nfunction readFrontMatterBlock(text: string, index: LineIndex): FrontMatterBlock | null {\n const kind = frontMatterKind(index.lineText(1));\n if (kind === null) return null;\n for (let line = 2; line <= index.lineCount; line += 1) {\n if (!frontMatterCloses(index.lineText(line), kind)) continue;\n const start = index.lineStart(2);\n const end = index.lineStart(line);\n const bodyStart = line < index.lineCount ? index.lineStart(line + 1) : text.length;\n return { kind, raw: text.slice(start, end), start, end, bodyStart, closeLine: line };\n }\n return null;\n}\n\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] };\n\ninterface Core {\n readonly lines: ScannedLine[];\n readonly blocks: Block[];\n readonly spans: Range[];\n readonly comments: HtmlComment[];\n readonly covered: ReadonlySet<number>;\n readonly continues: ReadonlySet<number>;\n}\n\n/** What each body line is on its own, before anything around it is known. */\ninterface Shape {\n readonly contentStart: number;\n readonly depth: number;\n readonly indent: number;\n readonly blank: boolean;\n /** An ATX heading, which is a paragraph of one line. */\n readonly atx: boolean;\n /** Opens a block that ends a paragraph above it. */\n readonly interrupts: boolean;\n}\n\n/**\n * The block pass, with the inline sweep for code spans and comments run\n * inside it line by line.\n *\n * The two cannot be separate passes. Whether a line opens a fence depends on\n * whether it lies inside a comment, and whether a comment opens depends on\n * whether its `<!--` lies inside a code span.\n */\nfunction scanCore(text: string, index: LineIndex, frontLines: number): Core {\n const count = index.lineCount;\n const lines: Mutable<ScannedLine>[] = [];\n const blocks: Mutable<Block>[] = [];\n const spans: Range[] = [];\n const comments: HtmlComment[] = [];\n const covered = new Set<number>();\n\n for (let i = 0; i < frontLines; i += 1) {\n const start = index.lineStart(i + 1);\n const end = index.lineEnd(i + 1);\n lines.push(record(i, start, end, start, text.slice(start, end), 0, { frontMatter: true, code: false, html: false }));\n }\n\n // One shape too many, for a line past the last, would never be read.\n const shapes: Shape[] = [];\n for (let i = frontLines; i < count; i += 1) {\n const start = index.lineStart(i + 1);\n const end = index.lineEnd(i + 1);\n const q = stripQuotes(text, start, Number.POSITIVE_INFINITY);\n const content = text.slice(q.contentStart, end);\n const indent = measureIndent(content);\n const blank = leadIndex(content) === content.length;\n const atx = atxLevel(content) > 0;\n const previous = shapes[shapes.length - 1];\n const interrupts =\n atx ||\n isThematicBreak(content) ||\n setextUnderline(content) !== null ||\n opensComment(content) ||\n listMarker(content) !== null ||\n fenceOpen(content) !== null ||\n isRawTextOpen(content) ||\n (previous !== undefined && q.depth > previous.depth);\n shapes.push({ contentStart: q.contentStart, depth: q.depth, indent, blank, atx, interrupts });\n }\n const shapeOf = (i: number): Shape => shapes[i - frontLines] as Shape;\n\n // A line continues the paragraph above it when it is not blank, opens\n // nothing of its own, and the line above is not a one-line heading. After a\n // blank line there is no paragraph to continue, and no search asks. Each\n // body line's paragraph ends at `stops`, walked from the bottom up.\n const continues = new Set<number>();\n const stops = new Map<number, number>();\n for (let i = count - 1; i >= frontLines; i -= 1) {\n const shape = shapeOf(i);\n const above = shapes[i - frontLines - 1];\n if (above !== undefined && !above.atx && !shape.blank && !shape.interrupts) continues.add(i + 1);\n stops.set(i, continues.has(i + 2) ? (stops.get(i + 1) as number) : index.lineEnd(i + 1));\n }\n\n /* ------------------------------------------------------------ inline sweep */\n\n // Where the last failed search for a closing backtick run stopped, and the\n // last run of each length it passed. Openers come in order, so a later one\n // before that stop lies inside the stretch searched, and closes only if a\n // run of its length lies after it there, which is a lookup.\n let seen: { readonly end: number; readonly last: ReadonlyMap<number, number> } | null = null;\n\n /** Where the span opened by a run ending at `from` closes, if it does. */\n const closeRun = (from: number, length: number, line: number): number | undefined => {\n // A run of a length the search never saw - one an escape split in two -\n // has no closer either. Reading its absence as any number below `from`\n // gives that answer, and one that is not below it gives it by searching.\n // No run starts at `from`, which follows one, and an opener at `seen.end`\n // finds nothing either way: the comparisons are equivalent at equality.\n if (seen !== null && from < seen.end && (seen.last.get(length) ?? -1) < from) return undefined;\n const stop = stops.get(line) as number;\n const last = new Map<number, number>();\n let at = text.indexOf('`', from);\n while (at >= 0 && at < stop) {\n const run = at;\n while (at < stop && text.charCodeAt(at) === BACKTICK) at += 1;\n if (at - run === length) return at;\n last.set(at - run, run);\n at = text.indexOf('`', at);\n }\n seen = { end: stop, last };\n return undefined;\n };\n\n // Once one `<!--` has no `-->` after it, no later one has either.\n let closable = true;\n\n /** Where the comment opened at `at` closes, if it does. */\n const commentEnd = (at: number): number | undefined => {\n // CommonMark 0.31: `<!-->` and `<!--->` are whole, empty comments.\n if (text.startsWith('<!-->', at)) return at + 5;\n if (text.startsWith('<!--->', at)) return at + 6;\n if (!closable) return undefined;\n const close = text.indexOf('-->', at + 4);\n closable = close !== -1;\n return closable ? close + 3 : undefined;\n };\n\n const pushComment = (start: number, end: number, closed: boolean): void => {\n comments.push({\n start,\n end,\n // An empty comment's `-->` overlaps its `<!--`, and the slice is empty.\n inner: text.slice(start + 4, closed ? end - 3 : end),\n innerStart: start + 4,\n line: index.positionAt(start).line,\n closed,\n });\n };\n /**\n * Sweeps one line from `from`, returning where the next line's sweep may\n * begin: past a span or comment that runs onto a later line, or the line's\n * end. `lead` is the offset at which a `<!--` would open the line, or the\n * end of the line when none can: a sweep never stops there.\n */\n const sweep = (from: number, end: number, line: number, lead: number): number => {\n let at = from;\n while (at < end) {\n const ch = text.charCodeAt(at);\n if (ch === BACKSLASH) {\n // An escaped backtick opens no span, and an escaped `<` no comment.\n at += 2;\n } else if (ch === BACKTICK) {\n let run = at + 1;\n while (run < end && text.charCodeAt(run) === BACKTICK) run += 1;\n const close = closeRun(run, run - at, line);\n if (close === undefined) {\n at = run;\n } else {\n spans.push({ start: at, end: close });\n at = close;\n }\n } else if (text.startsWith('<!--', at)) {\n const close = commentEnd(at);\n if (close !== undefined) {\n pushComment(at, close, true);\n at = close;\n } else if (at === lead) {\n // CommonMark's HTML block: a comment opening a line runs to its\n // `-->`, and one never closed runs to the end of the document.\n pushComment(at, text.length, false);\n return text.length;\n } else {\n at += 4;\n }\n } else {\n at += 1;\n }\n }\n return at;\n };\n\n /* ------------------------------------------------------------- block pass */\n\n interface Open {\n readonly block: Mutable<Block>;\n readonly depth: number;\n }\n let fence: (Open & { readonly open: FenceOpen }) | null = null;\n let raw: Open | null = null;\n let indented: Mutable<Block> | null = null;\n let previousBlank = true;\n // The content column of each list item open around the line, innermost last.\n const items: number[] = [];\n let pos = 0;\n\n const openBlock = (kind: BlockKind, i: number, info: string): Mutable<Block> => {\n const block = { kind, start: index.lineStart(i + 1), end: index.lineEnd(i + 1), line: i + 1, endLine: i + 1, info, closed: kind === 'indented' };\n blocks.push(block);\n return block;\n };\n const extend = (block: Mutable<Block>, i: number): void => {\n block.end = index.lineEnd(i + 1);\n block.endLine = i + 1;\n };\n // Blank lines after an indented block were marked as code in case more\n // code followed; the block ends at its last code line.\n const endIndented = (block: Mutable<Block>, next: number): void => {\n for (let i = block.endLine; i < next; i += 1) (lines[i] as Mutable<ScannedLine>).code = false;\n };\n\n /** Classifies one line and sweeps it; the caller records whether it was blank. */\n const scanLine = (i: number): void => {\n const start = index.lineStart(i + 1);\n const end = index.lineEnd(i + 1);\n const shape = shapeOf(i);\n const content = text.slice(shape.contentStart, end);\n const push = (kind: Kinds): void => {\n lines.push(record(i, start, end, shape.contentStart, content, shape.depth, kind));\n };\n\n if (pos > start) {\n // Inside a comment or span opened above: nothing opens a block here.\n covered.add(i + 1);\n push(NONE);\n pos = sweep(pos, end, i, end);\n return;\n }\n\n const container = fence ?? raw;\n if (container !== null) {\n // A fenced block or raw-text block ends with its block quote.\n const q = stripQuotes(text, start, container.depth);\n if (q.depth === container.depth) {\n const inside = text.slice(q.contentStart, end);\n lines.push(record(i, start, end, q.contentStart, inside, q.depth, fence === null ? HTML : CODE));\n extend(container.block, i);\n if (fence !== null ? fenceCloses(inside, fence.open) : hasRawTextClose(inside)) {\n container.block.closed = true;\n fence = null;\n raw = null;\n }\n return;\n }\n fence = null;\n raw = null;\n }\n\n // After a blank line, a line left of an item's content is outside it.\n if (!shape.blank && previousBlank) {\n while (items.length > 0 && (items[items.length - 1] as number) > shape.indent) items.pop();\n }\n // Indented code: four columns past the item the line is in, or past the\n // margin outside a list, and never interrupting a paragraph. Four columns\n // inside an item are its text, not code.\n const margin = items[items.length - 1] ?? 0;\n const deep = shape.indent >= margin + 4;\n if (!shape.blank && deep && (previousBlank || indented !== null)) {\n if (indented === null) indented = openBlock('indented', i, '');\n else extend(indented, i);\n push(CODE);\n return;\n }\n if (shape.blank) {\n push(indented === null ? NONE : CODE);\n return;\n }\n if (indented !== null) {\n endIndented(indented, i);\n indented = null;\n }\n\n // A fence as deep as indented code continues a paragraph instead.\n const open = deep ? null : fenceOpen(content);\n if (open !== null) {\n fence = { block: openBlock('fenced', i, open.info), depth: shape.depth, open };\n push(CODE);\n return;\n }\n if (isRawTextOpen(content)) {\n const block = openBlock('html', i, '');\n push(HTML);\n if (hasRawTextClose(content)) block.closed = true;\n else raw = { block, depth: shape.depth };\n return;\n }\n\n push(NONE);\n const marker = listMarker(content);\n if (marker !== null) {\n // A marker left of an item's content starts a sibling or an outer item.\n while (items.length > 0 && (items[items.length - 1] as number) > shape.indent) items.pop();\n items.push(itemColumn(shape.indent, marker));\n }\n // A heading ends a list whatever its indentation.\n if (shape.atx) items.length = 0;\n const lead = shape.indent <= 3 ? shape.contentStart + leadIndex(content) : end;\n pos = sweep(shape.contentStart, end, i, lead);\n };\n\n for (let i = frontLines; i < count; i += 1) {\n scanLine(i);\n // Whether the line was blank to look at, whatever it was classified as:\n // indented code starts only after one.\n previousBlank = shapeOf(i).blank;\n }\n if (indented !== null) endIndented(indented, count);\n\n markCommentLines(lines, comments);\n return { lines, blocks, spans, comments, covered, continues };\n}\n\n/**\n * The column an item's text starts at: past its marker and the one to four\n * spaces after it. With none, or five or more, the text starts one column\n * past the marker, and anything further is indented code.\n */\nfunction itemColumn(indent: number, marker: ListMarker): number {\n const spaces = marker.width - marker.marker.length;\n return indent + marker.marker.length + (spaces >= 1 && spaces <= 4 ? spaces : 1);\n}\n\ntype Kinds = Pick<ScannedLine, 'frontMatter' | 'code' | 'html'>;\nconst NONE: Kinds = { frontMatter: false, code: false, html: false };\nconst CODE: Kinds = { frontMatter: false, code: true, html: false };\nconst HTML: Kinds = { frontMatter: false, code: false, html: true };\n\nfunction record(\n i: number,\n start: number,\n end: number,\n contentStart: number,\n content: string,\n quoteDepth: number,\n kinds: Kinds,\n): Mutable<ScannedLine> {\n return {\n line: i + 1,\n start,\n end,\n contentStart,\n content,\n indent: measureIndent(content),\n blank: leadIndex(content) === content.length,\n quoteDepth,\n ...kinds,\n comment: false,\n };\n}\n\n/** Marks each line whose first non-blank character lies inside a comment. */\nfunction markCommentLines(lines: readonly Mutable<ScannedLine>[], comments: readonly HtmlComment[]): void {\n let c = 0;\n for (const line of lines) {\n if (line.blank) continue;\n const lead = line.contentStart + leadIndex(line.content);\n // No comment ends exactly at a line's first non-blank character: a\n // comment ends in `>`, so one that ends before it ends on an earlier line.\n while (c < comments.length && (comments[c] as HtmlComment).end <= lead) c += 1;\n const comment = comments[c];\n line.comment = comment !== undefined && comment.start <= lead;\n }\n}\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognisers for what one line of Markdown opens.
|
|
3
|
+
*
|
|
4
|
+
* Each takes a line's content - after any block-quote markers, without its
|
|
5
|
+
* terminator - and answers in time linear in the line. They are written as
|
|
6
|
+
* loops where a regular expression would have to backtrack: a line of forty
|
|
7
|
+
* thousand spaces is a document someone can commit.
|
|
8
|
+
*/
|
|
9
|
+
/** Columns of leading whitespace, a tab reaching the next multiple of four. */
|
|
10
|
+
export declare function measureIndent(content: string): number;
|
|
11
|
+
/** Columns of whitespace from an offset, stopping at the first other character or at `to`. */
|
|
12
|
+
export declare function measureIndentAt(text: string, from: number, to: number): number;
|
|
13
|
+
/** Index of the first character that is not a space or a tab; the length when there is none. */
|
|
14
|
+
export declare function leadIndex(content: string): number;
|
|
15
|
+
/**
|
|
16
|
+
* Takes up to `maxDepth` block-quote markers off the line starting at
|
|
17
|
+
* `start`: up to three spaces, a `>`, and one optional space, as many times as
|
|
18
|
+
* they repeat. The line ends at a terminator or at the end of the text, and
|
|
19
|
+
* neither is a space or a `>`, so nothing here reads past it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function stripQuotes(text: string, start: number, maxDepth: number): {
|
|
22
|
+
readonly contentStart: number;
|
|
23
|
+
readonly depth: number;
|
|
24
|
+
};
|
|
25
|
+
export interface FenceOpen {
|
|
26
|
+
/** `` ` `` or `~`. */
|
|
27
|
+
readonly char: string;
|
|
28
|
+
readonly length: number;
|
|
29
|
+
/** Columns before the fence. */
|
|
30
|
+
readonly indent: number;
|
|
31
|
+
readonly info: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A code fence opening the line, at any indentation: a fence inside a list
|
|
35
|
+
* item is indented with the item.
|
|
36
|
+
*/
|
|
37
|
+
export declare function fenceOpen(content: string): FenceOpen | null;
|
|
38
|
+
/**
|
|
39
|
+
* Whether a line closes an open fence: the same character, at least as many
|
|
40
|
+
* of it, nothing but whitespace after, and no more than three columns deeper
|
|
41
|
+
* than the opening fence.
|
|
42
|
+
*/
|
|
43
|
+
export declare function fenceCloses(content: string, fence: FenceOpen): boolean;
|
|
44
|
+
/** The level of an ATX heading opening the line, or 0. */
|
|
45
|
+
export declare function atxLevel(content: string): number;
|
|
46
|
+
/** Three or more of one of `*`, `-` or `_`, with nothing but spaces and tabs between. */
|
|
47
|
+
export declare function isThematicBreak(content: string): boolean;
|
|
48
|
+
/** `=` or `-` for a line that could underline a setext heading, else `null`. */
|
|
49
|
+
export declare function setextUnderline(content: string): '=' | '-' | null;
|
|
50
|
+
export interface ListMarker {
|
|
51
|
+
/** Characters of whitespace before the marker. */
|
|
52
|
+
readonly offset: number;
|
|
53
|
+
readonly marker: string;
|
|
54
|
+
/** Characters from the marker's start to the item's text. */
|
|
55
|
+
readonly width: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A list marker opening the line, at any indentation. A thematic break that
|
|
59
|
+
* could also be read as one (`- - -`) is a thematic break.
|
|
60
|
+
*/
|
|
61
|
+
export declare function listMarker(content: string): ListMarker | null;
|
|
62
|
+
/**
|
|
63
|
+
* `<script>`, `<pre>`, `<style>` or `<textarea>` opening the line.
|
|
64
|
+
*
|
|
65
|
+
* These four are the HTML elements whose content CommonMark does not read as
|
|
66
|
+
* Markdown. A page explaining a directive puts an example inside one, and the
|
|
67
|
+
* example must not be read as the directive. `<div>` and `<details>` are not
|
|
68
|
+
* on the list: their content is Markdown, and a decision written inside a
|
|
69
|
+
* collapsed section is still a decision.
|
|
70
|
+
*/
|
|
71
|
+
export declare function isRawTextOpen(content: string): boolean;
|
|
72
|
+
/** Whether a line holds the close tag of any of the four raw-text elements. */
|
|
73
|
+
export declare function hasRawTextClose(content: string): boolean;
|
|
74
|
+
/** `<!--` opening the line after up to three spaces. */
|
|
75
|
+
export declare function opensComment(content: string): boolean;
|