Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
1'use strict';
2
3var common = require('@lezer/common');
4var highlight = require('@lezer/highlight');
5
6class CompositeBlock {
7 static create(type, value, from, parentHash, end) {
8 let hash = (parentHash + (parentHash << 8) + type + (value << 4)) | 0;
9 return new CompositeBlock(type, value, from, hash, end, [], []);
10 }
11 constructor(type,
12 // Used for indentation in list items, markup character in lists
13 value, from, hash, end, children, positions) {
14 this.type = type;
15 this.value = value;
16 this.from = from;
17 this.hash = hash;
18 this.end = end;
19 this.children = children;
20 this.positions = positions;
21 this.hashProp = [[common.NodeProp.contextHash, hash]];
22 }
23 addChild(child, pos) {
24 if (child.prop(common.NodeProp.contextHash) != this.hash)
25 child = new common.Tree(child.type, child.children, child.positions, child.length, this.hashProp);
26 this.children.push(child);
27 this.positions.push(pos);
28 }
29 toTree(nodeSet, end = this.end) {
30 let last = this.children.length - 1;
31 if (last >= 0)
32 end = Math.max(end, this.positions[last] + this.children[last].length + this.from);
33 return new common.Tree(nodeSet.types[this.type], this.children, this.positions, end - this.from).balance({
34 makeTree: (children, positions, length) => new common.Tree(common.NodeType.none, children, positions, length, this.hashProp)
35 });
36 }
37}
38var Type;
39(function (Type) {
40 Type[Type["Document"] = 1] = "Document";
41 Type[Type["CodeBlock"] = 2] = "CodeBlock";
42 Type[Type["FencedCode"] = 3] = "FencedCode";
43 Type[Type["Blockquote"] = 4] = "Blockquote";
44 Type[Type["HorizontalRule"] = 5] = "HorizontalRule";
45 Type[Type["BulletList"] = 6] = "BulletList";
46 Type[Type["OrderedList"] = 7] = "OrderedList";
47 Type[Type["ListItem"] = 8] = "ListItem";
48 Type[Type["ATXHeading1"] = 9] = "ATXHeading1";
49 Type[Type["ATXHeading2"] = 10] = "ATXHeading2";
50 Type[Type["ATXHeading3"] = 11] = "ATXHeading3";
51 Type[Type["ATXHeading4"] = 12] = "ATXHeading4";
52 Type[Type["ATXHeading5"] = 13] = "ATXHeading5";
53 Type[Type["ATXHeading6"] = 14] = "ATXHeading6";
54 Type[Type["SetextHeading1"] = 15] = "SetextHeading1";
55 Type[Type["SetextHeading2"] = 16] = "SetextHeading2";
56 Type[Type["HTMLBlock"] = 17] = "HTMLBlock";
57 Type[Type["LinkReference"] = 18] = "LinkReference";
58 Type[Type["Paragraph"] = 19] = "Paragraph";
59 Type[Type["CommentBlock"] = 20] = "CommentBlock";
60 Type[Type["ProcessingInstructionBlock"] = 21] = "ProcessingInstructionBlock";
61 // Inline
62 Type[Type["Escape"] = 22] = "Escape";
63 Type[Type["Entity"] = 23] = "Entity";
64 Type[Type["HardBreak"] = 24] = "HardBreak";
65 Type[Type["Emphasis"] = 25] = "Emphasis";
66 Type[Type["StrongEmphasis"] = 26] = "StrongEmphasis";
67 Type[Type["Link"] = 27] = "Link";
68 Type[Type["Image"] = 28] = "Image";
69 Type[Type["InlineCode"] = 29] = "InlineCode";
70 Type[Type["HTMLTag"] = 30] = "HTMLTag";
71 Type[Type["Comment"] = 31] = "Comment";
72 Type[Type["ProcessingInstruction"] = 32] = "ProcessingInstruction";
73 Type[Type["Autolink"] = 33] = "Autolink";
74 // Smaller tokens
75 Type[Type["HeaderMark"] = 34] = "HeaderMark";
76 Type[Type["QuoteMark"] = 35] = "QuoteMark";
77 Type[Type["ListMark"] = 36] = "ListMark";
78 Type[Type["LinkMark"] = 37] = "LinkMark";
79 Type[Type["EmphasisMark"] = 38] = "EmphasisMark";
80 Type[Type["CodeMark"] = 39] = "CodeMark";
81 Type[Type["CodeText"] = 40] = "CodeText";
82 Type[Type["CodeInfo"] = 41] = "CodeInfo";
83 Type[Type["LinkTitle"] = 42] = "LinkTitle";
84 Type[Type["LinkLabel"] = 43] = "LinkLabel";
85 Type[Type["URL"] = 44] = "URL";
86})(Type || (Type = {}));
87/**
88Data structure used to accumulate a block's content during [leaf
89block parsing](#BlockParser.leaf).
90*/
91class LeafBlock {
92 /**
93 @internal
94 */
95 constructor(
96 /**
97 The start position of the block.
98 */
99 start,
100 /**
101 The block's text content.
102 */
103 content) {
104 this.start = start;
105 this.content = content;
106 /**
107 @internal
108 */
109 this.marks = [];
110 /**
111 The block parsers active for this block.
112 */
113 this.parsers = [];
114 }
115}
116/**
117Data structure used during block-level per-line parsing.
118*/
119class Line {
120 constructor() {
121 /**
122 The line's full text.
123 */
124 this.text = "";
125 /**
126 The base indent provided by the composite contexts (that have
127 been handled so far).
128 */
129 this.baseIndent = 0;
130 /**
131 The string position corresponding to the base indent.
132 */
133 this.basePos = 0;
134 /**
135 The number of contexts handled @internal
136 */
137 this.depth = 0;
138 /**
139 Any markers (i.e. block quote markers) parsed for the contexts. @internal
140 */
141 this.markers = [];
142 /**
143 The position of the next non-whitespace character beyond any
144 list, blockquote, or other composite block markers.
145 */
146 this.pos = 0;
147 /**
148 The column of the next non-whitespace character.
149 */
150 this.indent = 0;
151 /**
152 The character code of the character after `pos`.
153 */
154 this.next = -1;
155 }
156 /**
157 @internal
158 */
159 forward() {
160 if (this.basePos > this.pos)
161 this.forwardInner();
162 }
163 /**
164 @internal
165 */
166 forwardInner() {
167 let newPos = this.skipSpace(this.basePos);
168 this.indent = this.countIndent(newPos, this.pos, this.indent);
169 this.pos = newPos;
170 this.next = newPos == this.text.length ? -1 : this.text.charCodeAt(newPos);
171 }
172 /**
173 Skip whitespace after the given position, return the position of
174 the next non-space character or the end of the line if there's
175 only space after `from`.
176 */
177 skipSpace(from) { return skipSpace(this.text, from); }
178 /**
179 @internal
180 */
181 reset(text) {
182 this.text = text;
183 this.baseIndent = this.basePos = this.pos = this.indent = 0;
184 this.forwardInner();
185 this.depth = 1;
186 while (this.markers.length)
187 this.markers.pop();
188 }
189 /**
190 Move the line's base position forward to the given position.
191 This should only be called by composite [block
192 parsers](#BlockParser.parse) or [markup skipping
193 functions](#NodeSpec.composite).
194 */
195 moveBase(to) {
196 this.basePos = to;
197 this.baseIndent = this.countIndent(to, this.pos, this.indent);
198 }
199 /**
200 Move the line's base position forward to the given _column_.
201 */
202 moveBaseColumn(indent) {
203 this.baseIndent = indent;
204 this.basePos = this.findColumn(indent);
205 }
206 /**
207 Store a composite-block-level marker. Should be called from
208 [markup skipping functions](#NodeSpec.composite) when they
209 consume any non-whitespace characters.
210 */
211 addMarker(elt) {
212 this.markers.push(elt);
213 }
214 /**
215 Find the column position at `to`, optionally starting at a given
216 position and column.
217 */
218 countIndent(to, from = 0, indent = 0) {
219 for (let i = from; i < to; i++)
220 indent += this.text.charCodeAt(i) == 9 ? 4 - indent % 4 : 1;
221 return indent;
222 }
223 /**
224 Find the position corresponding to the given column.
225 */
226 findColumn(goal) {
227 let i = 0;
228 for (let indent = 0; i < this.text.length && indent < goal; i++)
229 indent += this.text.charCodeAt(i) == 9 ? 4 - indent % 4 : 1;
230 return i;
231 }
232 /**
233 @internal
234 */
235 scrub() {
236 if (!this.baseIndent)
237 return this.text;
238 let result = "";
239 for (let i = 0; i < this.basePos; i++)
240 result += " ";
241 return result + this.text.slice(this.basePos);
242 }
243}
244function skipForList(bl, cx, line) {
245 if (line.pos == line.text.length ||
246 (bl != cx.block && line.indent >= cx.stack[line.depth + 1].value + line.baseIndent))
247 return true;
248 if (line.indent >= line.baseIndent + 4)
249 return false;
250 let size = (bl.type == Type.OrderedList ? isOrderedList : isBulletList)(line, cx, false);
251 return size > 0 &&
252 (bl.type != Type.BulletList || isHorizontalRule(line, cx, false) < 0) &&
253 line.text.charCodeAt(line.pos + size - 1) == bl.value;
254}
255const DefaultSkipMarkup = {
256 [Type.Blockquote](bl, cx, line) {
257 if (line.next != 62 /* '>' */)
258 return false;
259 line.markers.push(elt(Type.QuoteMark, cx.lineStart + line.pos, cx.lineStart + line.pos + 1));
260 line.moveBase(line.pos + (space(line.text.charCodeAt(line.pos + 1)) ? 2 : 1));
261 bl.end = cx.lineStart + line.text.length;
262 return true;
263 },
264 [Type.ListItem](bl, _cx, line) {
265 if (line.indent < line.baseIndent + bl.value && line.next > -1)
266 return false;
267 line.moveBaseColumn(line.baseIndent + bl.value);
268 return true;
269 },
270 [Type.OrderedList]: skipForList,
271 [Type.BulletList]: skipForList,
272 [Type.Document]() { return true; }
273};
274function space(ch) { return ch == 32 || ch == 9 || ch == 10 || ch == 13; }
275function skipSpace(line, i = 0) {
276 while (i < line.length && space(line.charCodeAt(i)))
277 i++;
278 return i;
279}
280function skipSpaceBack(line, i, to) {
281 while (i > to && space(line.charCodeAt(i - 1)))
282 i--;
283 return i;
284}
285function isFencedCode(line) {
286 if (line.next != 96 && line.next != 126 /* '`~' */)
287 return -1;
288 let pos = line.pos + 1;
289 while (pos < line.text.length && line.text.charCodeAt(pos) == line.next)
290 pos++;
291 if (pos < line.pos + 3)
292 return -1;
293 if (line.next == 96)
294 for (let i = pos; i < line.text.length; i++)
295 if (line.text.charCodeAt(i) == 96)
296 return -1;
297 return pos;
298}
299function isBlockquote(line) {
300 return line.next != 62 /* '>' */ ? -1 : line.text.charCodeAt(line.pos + 1) == 32 ? 2 : 1;
301}
302function isHorizontalRule(line, cx, breaking) {
303 if (line.next != 42 && line.next != 45 && line.next != 95 /* '_-*' */)
304 return -1;
305 let count = 1;
306 for (let pos = line.pos + 1; pos < line.text.length; pos++) {
307 let ch = line.text.charCodeAt(pos);
308 if (ch == line.next)
309 count++;
310 else if (!space(ch))
311 return -1;
312 }
313 // Setext headers take precedence
314 if (breaking && line.next == 45 && isSetextUnderline(line) > -1 && line.depth == cx.stack.length &&
315 cx.parser.leafBlockParsers.indexOf(DefaultLeafBlocks.SetextHeading) > -1)
316 return -1;
317 return count < 3 ? -1 : 1;
318}
319function inList(cx, type) {
320 for (let i = cx.stack.length - 1; i >= 0; i--)
321 if (cx.stack[i].type == type)
322 return true;
323 return false;
324}
325function isBulletList(line, cx, breaking) {
326 return (line.next == 45 || line.next == 43 || line.next == 42 /* '-+*' */) &&
327 (line.pos == line.text.length - 1 || space(line.text.charCodeAt(line.pos + 1))) &&
328 (!breaking || inList(cx, Type.BulletList) || line.skipSpace(line.pos + 2) < line.text.length) ? 1 : -1;
329}
330function isOrderedList(line, cx, breaking) {
331 let pos = line.pos, next = line.next;
332 for (;;) {
333 if (next >= 48 && next <= 57 /* '0-9' */)
334 pos++;
335 else
336 break;
337 if (pos == line.text.length)
338 return -1;
339 next = line.text.charCodeAt(pos);
340 }
341 if (pos == line.pos || pos > line.pos + 9 ||
342 (next != 46 && next != 41 /* '.)' */) ||
343 (pos < line.text.length - 1 && !space(line.text.charCodeAt(pos + 1))) ||
344 breaking && !inList(cx, Type.OrderedList) &&
345 (line.skipSpace(pos + 1) == line.text.length || pos > line.pos + 1 || line.next != 49 /* '1' */))
346 return -1;
347 return pos + 1 - line.pos;
348}
349function isAtxHeading(line) {
350 if (line.next != 35 /* '#' */)
351 return -1;
352 let pos = line.pos + 1;
353 while (pos < line.text.length && line.text.charCodeAt(pos) == 35)
354 pos++;
355 if (pos < line.text.length && line.text.charCodeAt(pos) != 32)
356 return -1;
357 let size = pos - line.pos;
358 return size > 6 ? -1 : size;
359}
360function isSetextUnderline(line) {
361 if (line.next != 45 && line.next != 61 /* '-=' */ || line.indent >= line.baseIndent + 4)
362 return -1;
363 let pos = line.pos + 1;
364 while (pos < line.text.length && line.text.charCodeAt(pos) == line.next)
365 pos++;
366 let end = pos;
367 while (pos < line.text.length && space(line.text.charCodeAt(pos)))
368 pos++;
369 return pos == line.text.length ? end : -1;
370}
371const EmptyLine = /^[ \t]*$/, CommentEnd = /-->/, ProcessingEnd = /\?>/;
372const HTMLBlockStyle = [
373 [/^<(?:script|pre|style)(?:\s|>|$)/i, /<\/(?:script|pre|style)>/i],
374 [/^\s*<!--/, CommentEnd],
375 [/^\s*<\?/, ProcessingEnd],
376 [/^\s*<![A-Z]/, />/],
377 [/^\s*<!\[CDATA\[/, /\]\]>/],
378 [/^\s*<\/?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?:\s|\/?>|$)/i, EmptyLine],
379 [/^\s*(?:<\/[a-z][\w-]*\s*>|<[a-z][\w-]*(\s+[a-z:_][\w-.]*(?:\s*=\s*(?:[^\s"'=<>`]+|'[^']*'|"[^"]*"))?)*\s*>)\s*$/i, EmptyLine]
380];
381function isHTMLBlock(line, _cx, breaking) {
382 if (line.next != 60 /* '<' */)
383 return -1;
384 let rest = line.text.slice(line.pos);
385 for (let i = 0, e = HTMLBlockStyle.length - (breaking ? 1 : 0); i < e; i++)
386 if (HTMLBlockStyle[i][0].test(rest))
387 return i;
388 return -1;
389}
390function getListIndent(line, pos) {
391 let indentAfter = line.countIndent(pos, line.pos, line.indent);
392 let indented = line.countIndent(line.skipSpace(pos), pos, indentAfter);
393 return indented >= indentAfter + 5 ? indentAfter + 1 : indented;
394}
395function addCodeText(marks, from, to) {
396 let last = marks.length - 1;
397 if (last >= 0 && marks[last].to == from && marks[last].type == Type.CodeText)
398 marks[last].to = to;
399 else
400 marks.push(elt(Type.CodeText, from, to));
401}
402// Rules for parsing blocks. A return value of false means the rule
403// doesn't apply here, true means it does. When true is returned and
404// `p.line` has been updated, the rule is assumed to have consumed a
405// leaf block. Otherwise, it is assumed to have opened a context.
406const DefaultBlockParsers = {
407 LinkReference: undefined,
408 IndentedCode(cx, line) {
409 let base = line.baseIndent + 4;
410 if (line.indent < base)
411 return false;
412 let start = line.findColumn(base);
413 let from = cx.lineStart + start, to = cx.lineStart + line.text.length;
414 let marks = [], pendingMarks = [];
415 addCodeText(marks, from, to);
416 while (cx.nextLine() && line.depth >= cx.stack.length) {
417 if (line.pos == line.text.length) { // Empty
418 addCodeText(pendingMarks, cx.lineStart - 1, cx.lineStart);
419 for (let m of line.markers)
420 pendingMarks.push(m);
421 }
422 else if (line.indent < base) {
423 break;
424 }
425 else {
426 if (pendingMarks.length) {
427 for (let m of pendingMarks) {
428 if (m.type == Type.CodeText)
429 addCodeText(marks, m.from, m.to);
430 else
431 marks.push(m);
432 }
433 pendingMarks = [];
434 }
435 addCodeText(marks, cx.lineStart - 1, cx.lineStart);
436 for (let m of line.markers)
437 marks.push(m);
438 to = cx.lineStart + line.text.length;
439 let codeStart = cx.lineStart + line.findColumn(line.baseIndent + 4);
440 if (codeStart < to)
441 addCodeText(marks, codeStart, to);
442 }
443 }
444 if (pendingMarks.length) {
445 pendingMarks = pendingMarks.filter(m => m.type != Type.CodeText);
446 if (pendingMarks.length)
447 line.markers = pendingMarks.concat(line.markers);
448 }
449 cx.addNode(cx.buffer.writeElements(marks, -from).finish(Type.CodeBlock, to - from), from);
450 return true;
451 },
452 FencedCode(cx, line) {
453 let fenceEnd = isFencedCode(line);
454 if (fenceEnd < 0)
455 return false;
456 let from = cx.lineStart + line.pos, ch = line.next, len = fenceEnd - line.pos;
457 let infoFrom = line.skipSpace(fenceEnd), infoTo = skipSpaceBack(line.text, line.text.length, infoFrom);
458 let marks = [elt(Type.CodeMark, from, from + len)];
459 if (infoFrom < infoTo)
460 marks.push(elt(Type.CodeInfo, cx.lineStart + infoFrom, cx.lineStart + infoTo));
461 for (let first = true, empty = true, hasLine = false; cx.nextLine() && line.depth >= cx.stack.length; first = false) {
462 let i = line.pos;
463 if (line.indent - line.baseIndent < 4)
464 while (i < line.text.length && line.text.charCodeAt(i) == ch)
465 i++;
466 if (i - line.pos >= len && line.skipSpace(i) == line.text.length) {
467 for (let m of line.markers)
468 marks.push(m);
469 if (empty && hasLine)
470 addCodeText(marks, cx.lineStart - 1, cx.lineStart);
471 marks.push(elt(Type.CodeMark, cx.lineStart + line.pos, cx.lineStart + i));
472 cx.nextLine();
473 break;
474 }
475 else {
476 hasLine = true;
477 if (!first) {
478 addCodeText(marks, cx.lineStart - 1, cx.lineStart);
479 empty = false;
480 }
481 for (let m of line.markers)
482 marks.push(m);
483 let textStart = cx.lineStart + line.basePos, textEnd = cx.lineStart + line.text.length;
484 if (textStart < textEnd) {
485 addCodeText(marks, textStart, textEnd);
486 empty = false;
487 }
488 }
489 }
490 cx.addNode(cx.buffer.writeElements(marks, -from)
491 .finish(Type.FencedCode, cx.prevLineEnd() - from), from);
492 return true;
493 },
494 Blockquote(cx, line) {
495 let size = isBlockquote(line);
496 if (size < 0)
497 return false;
498 cx.startContext(Type.Blockquote, line.pos);
499 cx.addNode(Type.QuoteMark, cx.lineStart + line.pos, cx.lineStart + line.pos + 1);
500 line.moveBase(line.pos + size);
501 return null;
502 },
503 HorizontalRule(cx, line) {
504 if (isHorizontalRule(line, cx, false) < 0)
505 return false;
506 let from = cx.lineStart + line.pos;
507 cx.nextLine();
508 cx.addNode(Type.HorizontalRule, from);
509 return true;
510 },
511 BulletList(cx, line) {
512 let size = isBulletList(line, cx, false);
513 if (size < 0)
514 return false;
515 if (cx.block.type != Type.BulletList)
516 cx.startContext(Type.BulletList, line.basePos, line.next);
517 let newBase = getListIndent(line, line.pos + 1);
518 cx.startContext(Type.ListItem, line.basePos, newBase - line.baseIndent);
519 cx.addNode(Type.ListMark, cx.lineStart + line.pos, cx.lineStart + line.pos + size);
520 line.moveBaseColumn(newBase);
521 return null;
522 },
523 OrderedList(cx, line) {
524 let size = isOrderedList(line, cx, false);
525 if (size < 0)
526 return false;
527 if (cx.block.type != Type.OrderedList)
528 cx.startContext(Type.OrderedList, line.basePos, line.text.charCodeAt(line.pos + size - 1));
529 let newBase = getListIndent(line, line.pos + size);
530 cx.startContext(Type.ListItem, line.basePos, newBase - line.baseIndent);
531 cx.addNode(Type.ListMark, cx.lineStart + line.pos, cx.lineStart + line.pos + size);
532 line.moveBaseColumn(newBase);
533 return null;
534 },
535 ATXHeading(cx, line) {
536 let size = isAtxHeading(line);
537 if (size < 0)
538 return false;
539 let off = line.pos, from = cx.lineStart + off;
540 let endOfSpace = skipSpaceBack(line.text, line.text.length, off), after = endOfSpace;
541 while (after > off && line.text.charCodeAt(after - 1) == line.next)
542 after--;
543 if (after == endOfSpace || after == off || !space(line.text.charCodeAt(after - 1)))
544 after = line.text.length;
545 let buf = cx.buffer
546 .write(Type.HeaderMark, 0, size)
547 .writeElements(cx.parser.parseInline(line.text.slice(off + size + 1, after), from + size + 1), -from);
548 if (after < line.text.length)
549 buf.write(Type.HeaderMark, after - off, endOfSpace - off);
550 let node = buf.finish(Type.ATXHeading1 - 1 + size, line.text.length - off);
551 cx.nextLine();
552 cx.addNode(node, from);
553 return true;
554 },
555 HTMLBlock(cx, line) {
556 let type = isHTMLBlock(line, cx, false);
557 if (type < 0)
558 return false;
559 let from = cx.lineStart + line.pos, end = HTMLBlockStyle[type][1];
560 let marks = [], trailing = end != EmptyLine;
561 while (!end.test(line.text) && cx.nextLine()) {
562 if (line.depth < cx.stack.length) {
563 trailing = false;
564 break;
565 }
566 for (let m of line.markers)
567 marks.push(m);
568 }
569 if (trailing)
570 cx.nextLine();
571 let nodeType = end == CommentEnd ? Type.CommentBlock : end == ProcessingEnd ? Type.ProcessingInstructionBlock : Type.HTMLBlock;
572 let to = cx.prevLineEnd();
573 cx.addNode(cx.buffer.writeElements(marks, -from).finish(nodeType, to - from), from);
574 return true;
575 },
576 SetextHeading: undefined // Specifies relative precedence for block-continue function
577};
578// This implements a state machine that incrementally parses link references. At each
579// next line, it looks ahead to see if the line continues the reference or not. If it
580// doesn't and a valid link is available ending before that line, it finishes that.
581// Similarly, on `finish` (when the leaf is terminated by external circumstances), it
582// creates a link reference if there's a valid reference up to the current point.
583class LinkReferenceParser {
584 constructor(leaf) {
585 this.stage = 0 /* RefStage.Start */;
586 this.elts = [];
587 this.pos = 0;
588 this.start = leaf.start;
589 this.advance(leaf.content);
590 }
591 nextLine(cx, line, leaf) {
592 if (this.stage == -1 /* RefStage.Failed */)
593 return false;
594 let content = leaf.content + "\n" + line.scrub();
595 let finish = this.advance(content);
596 if (finish > -1 && finish < content.length)
597 return this.complete(cx, leaf, finish);
598 return false;
599 }
600 finish(cx, leaf) {
601 if ((this.stage == 2 /* RefStage.Link */ || this.stage == 3 /* RefStage.Title */) && skipSpace(leaf.content, this.pos) == leaf.content.length)
602 return this.complete(cx, leaf, leaf.content.length);
603 return false;
604 }
605 complete(cx, leaf, len) {
606 cx.addLeafElement(leaf, elt(Type.LinkReference, this.start, this.start + len, this.elts));
607 return true;
608 }
609 nextStage(elt) {
610 if (elt) {
611 this.pos = elt.to - this.start;
612 this.elts.push(elt);
613 this.stage++;
614 return true;
615 }
616 if (elt === false)
617 this.stage = -1 /* RefStage.Failed */;
618 return false;
619 }
620 advance(content) {
621 for (;;) {
622 if (this.stage == -1 /* RefStage.Failed */) {
623 return -1;
624 }
625 else if (this.stage == 0 /* RefStage.Start */) {
626 if (!this.nextStage(parseLinkLabel(content, this.pos, this.start, true)))
627 return -1;
628 if (content.charCodeAt(this.pos) != 58 /* ':' */)
629 return this.stage = -1 /* RefStage.Failed */;
630 this.elts.push(elt(Type.LinkMark, this.pos + this.start, this.pos + this.start + 1));
631 this.pos++;
632 }
633 else if (this.stage == 1 /* RefStage.Label */) {
634 if (!this.nextStage(parseURL(content, skipSpace(content, this.pos), this.start)))
635 return -1;
636 }
637 else if (this.stage == 2 /* RefStage.Link */) {
638 let skip = skipSpace(content, this.pos), end = 0;
639 if (skip > this.pos) {
640 let title = parseLinkTitle(content, skip, this.start);
641 if (title) {
642 let titleEnd = lineEnd(content, title.to - this.start);
643 if (titleEnd > 0) {
644 this.nextStage(title);
645 end = titleEnd;
646 }
647 }
648 }
649 if (!end)
650 end = lineEnd(content, this.pos);
651 return end > 0 && end < content.length ? end : -1;
652 }
653 else { // RefStage.Title
654 return lineEnd(content, this.pos);
655 }
656 }
657 }
658}
659function lineEnd(text, pos) {
660 for (; pos < text.length; pos++) {
661 let next = text.charCodeAt(pos);
662 if (next == 10)
663 break;
664 if (!space(next))
665 return -1;
666 }
667 return pos;
668}
669class SetextHeadingParser {
670 nextLine(cx, line, leaf) {
671 let underline = line.depth < cx.stack.length ? -1 : isSetextUnderline(line);
672 let next = line.next;
673 if (underline < 0)
674 return false;
675 let underlineMark = elt(Type.HeaderMark, cx.lineStart + line.pos, cx.lineStart + underline);
676 cx.nextLine();
677 cx.addLeafElement(leaf, elt(next == 61 ? Type.SetextHeading1 : Type.SetextHeading2, leaf.start, cx.prevLineEnd(), [
678 ...cx.parser.parseInline(leaf.content, leaf.start),
679 underlineMark
680 ]));
681 return true;
682 }
683 finish() {
684 return false;
685 }
686}
687const DefaultLeafBlocks = {
688 LinkReference(_, leaf) { return leaf.content.charCodeAt(0) == 91 /* '[' */ ? new LinkReferenceParser(leaf) : null; },
689 SetextHeading() { return new SetextHeadingParser; }
690};
691const DefaultEndLeaf = [
692 (_, line) => isAtxHeading(line) >= 0,
693 (_, line) => isFencedCode(line) >= 0,
694 (_, line) => isBlockquote(line) >= 0,
695 (p, line) => isBulletList(line, p, true) >= 0,
696 (p, line) => isOrderedList(line, p, true) >= 0,
697 (p, line) => isHorizontalRule(line, p, true) >= 0,
698 (p, line) => isHTMLBlock(line, p, true) >= 0
699];
700const scanLineResult = { text: "", end: 0 };
701/**
702Block-level parsing functions get access to this context object.
703*/
704class BlockContext {
705 /**
706 @internal
707 */
708 constructor(
709 /**
710 The parser configuration used.
711 */
712 parser,
713 /**
714 @internal
715 */
716 input, fragments,
717 /**
718 @internal
719 */
720 ranges) {
721 this.parser = parser;
722 this.input = input;
723 this.ranges = ranges;
724 this.line = new Line();
725 this.atEnd = false;
726 /**
727 For reused nodes on gaps, we can't directly put the original
728 node into the tree, since that may be bigger than its parent.
729 When this happens, we create a dummy tree that is replaced by
730 the proper node in `injectGaps` @internal
731 */
732 this.reusePlaceholders = new Map;
733 this.stoppedAt = null;
734 /**
735 The range index that absoluteLineStart points into @internal
736 */
737 this.rangeI = 0;
738 this.to = ranges[ranges.length - 1].to;
739 this.lineStart = this.absoluteLineStart = this.absoluteLineEnd = ranges[0].from;
740 this.block = CompositeBlock.create(Type.Document, 0, this.lineStart, 0, 0);
741 this.stack = [this.block];
742 this.fragments = fragments.length ? new FragmentCursor(fragments, input) : null;
743 this.readLine();
744 }
745 get parsedPos() {
746 return this.absoluteLineStart;
747 }
748 advance() {
749 if (this.stoppedAt != null && this.absoluteLineStart > this.stoppedAt)
750 return this.finish();
751 let { line } = this;
752 for (;;) {
753 for (let markI = 0;;) {
754 let next = line.depth < this.stack.length ? this.stack[this.stack.length - 1] : null;
755 while (markI < line.markers.length && (!next || line.markers[markI].from < next.end)) {
756 let mark = line.markers[markI++];
757 this.addNode(mark.type, mark.from, mark.to);
758 }
759 if (!next)
760 break;
761 this.finishContext();
762 }
763 if (line.pos < line.text.length)
764 break;
765 // Empty line
766 if (!this.nextLine())
767 return this.finish();
768 }
769 if (this.fragments && this.reuseFragment(line.basePos))
770 return null;
771 start: for (;;) {
772 for (let type of this.parser.blockParsers)
773 if (type) {
774 let result = type(this, line);
775 if (result != false) {
776 if (result == true)
777 return null;
778 line.forward();
779 continue start;
780 }
781 }
782 break;
783 }
784 let leaf = new LeafBlock(this.lineStart + line.pos, line.text.slice(line.pos));
785 for (let parse of this.parser.leafBlockParsers)
786 if (parse) {
787 let parser = parse(this, leaf);
788 if (parser)
789 leaf.parsers.push(parser);
790 }
791 lines: while (this.nextLine()) {
792 if (line.pos == line.text.length)
793 break;
794 if (line.indent < line.baseIndent + 4) {
795 for (let stop of this.parser.endLeafBlock)
796 if (stop(this, line, leaf))
797 break lines;
798 }
799 for (let parser of leaf.parsers)
800 if (parser.nextLine(this, line, leaf))
801 return null;
802 leaf.content += "\n" + line.scrub();
803 for (let m of line.markers)
804 leaf.marks.push(m);
805 }
806 this.finishLeaf(leaf);
807 return null;
808 }
809 stopAt(pos) {
810 if (this.stoppedAt != null && this.stoppedAt < pos)
811 throw new RangeError("Can't move stoppedAt forward");
812 this.stoppedAt = pos;
813 }
814 reuseFragment(start) {
815 if (!this.fragments.moveTo(this.absoluteLineStart + start, this.absoluteLineStart) ||
816 !this.fragments.matches(this.block.hash))
817 return false;
818 let taken = this.fragments.takeNodes(this);
819 if (!taken)
820 return false;
821 this.absoluteLineStart += taken;
822 this.lineStart = toRelative(this.absoluteLineStart, this.ranges);
823 this.moveRangeI();
824 if (this.absoluteLineStart < this.to) {
825 this.lineStart++;
826 this.absoluteLineStart++;
827 this.readLine();
828 }
829 else {
830 this.atEnd = true;
831 this.readLine();
832 }
833 return true;
834 }
835 /**
836 The number of parent blocks surrounding the current block.
837 */
838 get depth() {
839 return this.stack.length;
840 }
841 /**
842 Get the type of the parent block at the given depth. When no
843 depth is passed, return the type of the innermost parent.
844 */
845 parentType(depth = this.depth - 1) {
846 return this.parser.nodeSet.types[this.stack[depth].type];
847 }
848 /**
849 Move to the next input line. This should only be called by
850 (non-composite) [block parsers](#BlockParser.parse) that consume
851 the line directly, or leaf block parser
852 [`nextLine`](#LeafBlockParser.nextLine) methods when they
853 consume the current line (and return true).
854 */
855 nextLine() {
856 this.lineStart += this.line.text.length;
857 if (this.absoluteLineEnd >= this.to) {
858 this.absoluteLineStart = this.absoluteLineEnd;
859 this.atEnd = true;
860 this.readLine();
861 return false;
862 }
863 else {
864 this.lineStart++;
865 this.absoluteLineStart = this.absoluteLineEnd + 1;
866 this.moveRangeI();
867 this.readLine();
868 return true;
869 }
870 }
871 /**
872 Retrieve the text of the line after the current one, without
873 actually moving the context's current line forward.
874 */
875 peekLine() {
876 return this.scanLine(this.absoluteLineEnd + 1).text;
877 }
878 moveRangeI() {
879 while (this.rangeI < this.ranges.length - 1 && this.absoluteLineStart >= this.ranges[this.rangeI].to) {
880 this.rangeI++;
881 this.absoluteLineStart = Math.max(this.absoluteLineStart, this.ranges[this.rangeI].from);
882 }
883 }
884 /**
885 @internal
886 Collect the text for the next line.
887 */
888 scanLine(start) {
889 let r = scanLineResult;
890 r.end = start;
891 if (start >= this.to) {
892 r.text = "";
893 }
894 else {
895 r.text = this.lineChunkAt(start);
896 r.end += r.text.length;
897 if (this.ranges.length > 1) {
898 let textOffset = this.absoluteLineStart, rangeI = this.rangeI;
899 while (this.ranges[rangeI].to < r.end) {
900 rangeI++;
901 let nextFrom = this.ranges[rangeI].from;
902 let after = this.lineChunkAt(nextFrom);
903 r.end = nextFrom + after.length;
904 r.text = r.text.slice(0, this.ranges[rangeI - 1].to - textOffset) + after;
905 textOffset = r.end - r.text.length;
906 }
907 }
908 }
909 return r;
910 }
911 /**
912 @internal
913 Populate this.line with the content of the next line. Skip
914 leading characters covered by composite blocks.
915 */
916 readLine() {
917 let { line } = this, { text, end } = this.scanLine(this.absoluteLineStart);
918 this.absoluteLineEnd = end;
919 line.reset(text);
920 for (; line.depth < this.stack.length; line.depth++) {
921 let cx = this.stack[line.depth], handler = this.parser.skipContextMarkup[cx.type];
922 if (!handler)
923 throw new Error("Unhandled block context " + Type[cx.type]);
924 let marks = this.line.markers.length;
925 if (!handler(cx, this, line)) {
926 if (this.line.markers.length > marks)
927 cx.end = this.line.markers[this.line.markers.length - 1].to;
928 line.forward();
929 break;
930 }
931 line.forward();
932 }
933 }
934 lineChunkAt(pos) {
935 let next = this.input.chunk(pos), text;
936 if (!this.input.lineChunks) {
937 let eol = next.indexOf("\n");
938 text = eol < 0 ? next : next.slice(0, eol);
939 }
940 else {
941 text = next == "\n" ? "" : next;
942 }
943 return pos + text.length > this.to ? text.slice(0, this.to - pos) : text;
944 }
945 /**
946 The end position of the previous line.
947 */
948 prevLineEnd() { return this.atEnd ? this.lineStart : this.lineStart - 1; }
949 /**
950 @internal
951 */
952 startContext(type, start, value = 0) {
953 this.block = CompositeBlock.create(type, value, this.lineStart + start, this.block.hash, this.lineStart + this.line.text.length);
954 this.stack.push(this.block);
955 }
956 /**
957 Start a composite block. Should only be called from [block
958 parser functions](#BlockParser.parse) that return null.
959 */
960 startComposite(type, start, value = 0) {
961 this.startContext(this.parser.getNodeType(type), start, value);
962 }
963 /**
964 @internal
965 */
966 addNode(block, from, to) {
967 if (typeof block == "number")
968 block = new common.Tree(this.parser.nodeSet.types[block], none, none, (to !== null && to !== void 0 ? to : this.prevLineEnd()) - from);
969 this.block.addChild(block, from - this.block.from);
970 }
971 /**
972 Add a block element. Can be called by [block
973 parsers](#BlockParser.parse).
974 */
975 addElement(elt) {
976 this.block.addChild(elt.toTree(this.parser.nodeSet), elt.from - this.block.from);
977 }
978 /**
979 Add a block element from a [leaf parser](#LeafBlockParser). This
980 makes sure any extra composite block markup (such as blockquote
981 markers) inside the block are also added to the syntax tree.
982 */
983 addLeafElement(leaf, elt) {
984 this.addNode(this.buffer
985 .writeElements(injectMarks(elt.children, leaf.marks), -elt.from)
986 .finish(elt.type, elt.to - elt.from), elt.from);
987 }
988 /**
989 @internal
990 */
991 finishContext() {
992 let cx = this.stack.pop();
993 let top = this.stack[this.stack.length - 1];
994 top.addChild(cx.toTree(this.parser.nodeSet), cx.from - top.from);
995 this.block = top;
996 }
997 finish() {
998 while (this.stack.length > 1)
999 this.finishContext();
1000 return this.addGaps(this.block.toTree(this.parser.nodeSet, this.lineStart));
1001 }
1002 addGaps(tree) {
1003 return this.ranges.length > 1 ?
1004 injectGaps(this.ranges, 0, tree.topNode, this.ranges[0].from, this.reusePlaceholders) : tree;
1005 }
1006 /**
1007 @internal
1008 */
1009 finishLeaf(leaf) {
1010 for (let parser of leaf.parsers)
1011 if (parser.finish(this, leaf))
1012 return;
1013 let inline = injectMarks(this.parser.parseInline(leaf.content, leaf.start), leaf.marks);
1014 this.addNode(this.buffer
1015 .writeElements(inline, -leaf.start)
1016 .finish(Type.Paragraph, leaf.content.length), leaf.start);
1017 }
1018 elt(type, from, to, children) {
1019 if (typeof type == "string")
1020 return elt(this.parser.getNodeType(type), from, to, children);
1021 return new TreeElement(type, from);
1022 }
1023 /**
1024 @internal
1025 */
1026 get buffer() { return new Buffer(this.parser.nodeSet); }
1027}
1028function injectGaps(ranges, rangeI, tree, offset, dummies) {
1029 let rangeEnd = ranges[rangeI].to;
1030 let children = [], positions = [], start = tree.from + offset;
1031 function movePastNext(upto, inclusive) {
1032 while (inclusive ? upto >= rangeEnd : upto > rangeEnd) {
1033 let size = ranges[rangeI + 1].from - rangeEnd;
1034 offset += size;
1035 upto += size;
1036 rangeI++;
1037 rangeEnd = ranges[rangeI].to;
1038 }
1039 }
1040 for (let ch = tree.firstChild; ch; ch = ch.nextSibling) {
1041 movePastNext(ch.from + offset, true);
1042 let from = ch.from + offset, node, reuse = dummies.get(ch.tree);
1043 if (reuse) {
1044 node = reuse;
1045 }
1046 else if (ch.to + offset > rangeEnd) {
1047 node = injectGaps(ranges, rangeI, ch, offset, dummies);
1048 movePastNext(ch.to + offset, false);
1049 }
1050 else {
1051 node = ch.toTree();
1052 }
1053 children.push(node);
1054 positions.push(from - start);
1055 }
1056 movePastNext(tree.to + offset, false);
1057 return new common.Tree(tree.type, children, positions, tree.to + offset - start, tree.tree ? tree.tree.propValues : undefined);
1058}
1059/**
1060A Markdown parser configuration.
1061*/
1062class MarkdownParser extends common.Parser {
1063 /**
1064 @internal
1065 */
1066 constructor(
1067 /**
1068 The parser's syntax [node
1069 types](https://lezer.codemirror.net/docs/ref/#common.NodeSet).
1070 */
1071 nodeSet,
1072 /**
1073 @internal
1074 */
1075 blockParsers,
1076 /**
1077 @internal
1078 */
1079 leafBlockParsers,
1080 /**
1081 @internal
1082 */
1083 blockNames,
1084 /**
1085 @internal
1086 */
1087 endLeafBlock,
1088 /**
1089 @internal
1090 */
1091 skipContextMarkup,
1092 /**
1093 @internal
1094 */
1095 inlineParsers,
1096 /**
1097 @internal
1098 */
1099 inlineNames,
1100 /**
1101 @internal
1102 */
1103 wrappers) {
1104 super();
1105 this.nodeSet = nodeSet;
1106 this.blockParsers = blockParsers;
1107 this.leafBlockParsers = leafBlockParsers;
1108 this.blockNames = blockNames;
1109 this.endLeafBlock = endLeafBlock;
1110 this.skipContextMarkup = skipContextMarkup;
1111 this.inlineParsers = inlineParsers;
1112 this.inlineNames = inlineNames;
1113 this.wrappers = wrappers;
1114 /**
1115 @internal
1116 */
1117 this.nodeTypes = Object.create(null);
1118 for (let t of nodeSet.types)
1119 this.nodeTypes[t.name] = t.id;
1120 }
1121 createParse(input, fragments, ranges) {
1122 let parse = new BlockContext(this, input, fragments, ranges);
1123 for (let w of this.wrappers)
1124 parse = w(parse, input, fragments, ranges);
1125 return parse;
1126 }
1127 /**
1128 Reconfigure the parser.
1129 */
1130 configure(spec) {
1131 let config = resolveConfig(spec);
1132 if (!config)
1133 return this;
1134 let { nodeSet, skipContextMarkup } = this;
1135 let blockParsers = this.blockParsers.slice(), leafBlockParsers = this.leafBlockParsers.slice(), blockNames = this.blockNames.slice(), inlineParsers = this.inlineParsers.slice(), inlineNames = this.inlineNames.slice(), endLeafBlock = this.endLeafBlock.slice(), wrappers = this.wrappers;
1136 if (nonEmpty(config.defineNodes)) {
1137 skipContextMarkup = Object.assign({}, skipContextMarkup);
1138 let nodeTypes = nodeSet.types.slice(), styles;
1139 for (let s of config.defineNodes) {
1140 let { name, block, composite, style } = typeof s == "string" ? { name: s } : s;
1141 if (nodeTypes.some(t => t.name == name))
1142 continue;
1143 if (composite)
1144 skipContextMarkup[nodeTypes.length] =
1145 (bl, cx, line) => composite(cx, line, bl.value);
1146 let id = nodeTypes.length;
1147 let group = composite ? ["Block", "BlockContext"] : !block ? undefined
1148 : id >= Type.ATXHeading1 && id <= Type.SetextHeading2 ? ["Block", "LeafBlock", "Heading"] : ["Block", "LeafBlock"];
1149 nodeTypes.push(common.NodeType.define({
1150 id,
1151 name,
1152 props: group && [[common.NodeProp.group, group]]
1153 }));
1154 if (style) {
1155 if (!styles)
1156 styles = {};
1157 if (Array.isArray(style) || style instanceof highlight.Tag)
1158 styles[name] = style;
1159 else
1160 Object.assign(styles, style);
1161 }
1162 }
1163 nodeSet = new common.NodeSet(nodeTypes);
1164 if (styles)
1165 nodeSet = nodeSet.extend(highlight.styleTags(styles));
1166 }
1167 if (nonEmpty(config.props))
1168 nodeSet = nodeSet.extend(...config.props);
1169 if (nonEmpty(config.remove)) {
1170 for (let rm of config.remove) {
1171 let block = this.blockNames.indexOf(rm), inline = this.inlineNames.indexOf(rm);
1172 if (block > -1)
1173 blockParsers[block] = leafBlockParsers[block] = undefined;
1174 if (inline > -1)
1175 inlineParsers[inline] = undefined;
1176 }
1177 }
1178 if (nonEmpty(config.parseBlock)) {
1179 for (let spec of config.parseBlock) {
1180 let found = blockNames.indexOf(spec.name);
1181 if (found > -1) {
1182 blockParsers[found] = spec.parse;
1183 leafBlockParsers[found] = spec.leaf;
1184 }
1185 else {
1186 let pos = spec.before ? findName(blockNames, spec.before)
1187 : spec.after ? findName(blockNames, spec.after) + 1 : blockNames.length - 1;
1188 blockParsers.splice(pos, 0, spec.parse);
1189 leafBlockParsers.splice(pos, 0, spec.leaf);
1190 blockNames.splice(pos, 0, spec.name);
1191 }
1192 if (spec.endLeaf)
1193 endLeafBlock.push(spec.endLeaf);
1194 }
1195 }
1196 if (nonEmpty(config.parseInline)) {
1197 for (let spec of config.parseInline) {
1198 let found = inlineNames.indexOf(spec.name);
1199 if (found > -1) {
1200 inlineParsers[found] = spec.parse;
1201 }
1202 else {
1203 let pos = spec.before ? findName(inlineNames, spec.before)
1204 : spec.after ? findName(inlineNames, spec.after) + 1 : inlineNames.length - 1;
1205 inlineParsers.splice(pos, 0, spec.parse);
1206 inlineNames.splice(pos, 0, spec.name);
1207 }
1208 }
1209 }
1210 if (config.wrap)
1211 wrappers = wrappers.concat(config.wrap);
1212 return new MarkdownParser(nodeSet, blockParsers, leafBlockParsers, blockNames, endLeafBlock, skipContextMarkup, inlineParsers, inlineNames, wrappers);
1213 }
1214 /**
1215 @internal
1216 */
1217 getNodeType(name) {
1218 let found = this.nodeTypes[name];
1219 if (found == null)
1220 throw new RangeError(`Unknown node type '${name}'`);
1221 return found;
1222 }
1223 /**
1224 Parse the given piece of inline text at the given offset,
1225 returning an array of [`Element`](#Element) objects representing
1226 the inline content.
1227 */
1228 parseInline(text, offset) {
1229 let cx = new InlineContext(this, text, offset);
1230 outer: for (let pos = offset; pos < cx.end;) {
1231 let next = cx.char(pos);
1232 for (let token of this.inlineParsers)
1233 if (token) {
1234 let result = token(cx, next, pos);
1235 if (result >= 0) {
1236 pos = result;
1237 continue outer;
1238 }
1239 }
1240 pos++;
1241 }
1242 return cx.resolveMarkers(0);
1243 }
1244}
1245function nonEmpty(a) {
1246 return a != null && a.length > 0;
1247}
1248function resolveConfig(spec) {
1249 if (!Array.isArray(spec))
1250 return spec;
1251 if (spec.length == 0)
1252 return null;
1253 let conf = resolveConfig(spec[0]);
1254 if (spec.length == 1)
1255 return conf;
1256 let rest = resolveConfig(spec.slice(1));
1257 if (!rest || !conf)
1258 return conf || rest;
1259 let conc = (a, b) => (a || none).concat(b || none);
1260 let wrapA = conf.wrap, wrapB = rest.wrap;
1261 return {
1262 props: conc(conf.props, rest.props),
1263 defineNodes: conc(conf.defineNodes, rest.defineNodes),
1264 parseBlock: conc(conf.parseBlock, rest.parseBlock),
1265 parseInline: conc(conf.parseInline, rest.parseInline),
1266 remove: conc(conf.remove, rest.remove),
1267 wrap: !wrapA ? wrapB : !wrapB ? wrapA :
1268 (inner, input, fragments, ranges) => wrapA(wrapB(inner, input, fragments, ranges), input, fragments, ranges)
1269 };
1270}
1271function findName(names, name) {
1272 let found = names.indexOf(name);
1273 if (found < 0)
1274 throw new RangeError(`Position specified relative to unknown parser ${name}`);
1275 return found;
1276}
1277let nodeTypes = [common.NodeType.none];
1278for (let i = 1, name; name = Type[i]; i++) {
1279 nodeTypes[i] = common.NodeType.define({
1280 id: i,
1281 name,
1282 props: i >= Type.Escape ? [] : [[common.NodeProp.group, i in DefaultSkipMarkup ? ["Block", "BlockContext"] : ["Block", "LeafBlock"]]],
1283 top: name == "Document"
1284 });
1285}
1286const none = [];
1287class Buffer {
1288 constructor(nodeSet) {
1289 this.nodeSet = nodeSet;
1290 this.content = [];
1291 this.nodes = [];
1292 }
1293 write(type, from, to, children = 0) {
1294 this.content.push(type, from, to, 4 + children * 4);
1295 return this;
1296 }
1297 writeElements(elts, offset = 0) {
1298 for (let e of elts)
1299 e.writeTo(this, offset);
1300 return this;
1301 }
1302 finish(type, length) {
1303 return common.Tree.build({
1304 buffer: this.content,
1305 nodeSet: this.nodeSet,
1306 reused: this.nodes,
1307 topID: type,
1308 length
1309 });
1310 }
1311}
1312/**
1313Elements are used to compose syntax nodes during parsing.
1314*/
1315class Element {
1316 /**
1317 @internal
1318 */
1319 constructor(
1320 /**
1321 The node's
1322 [id](https://lezer.codemirror.net/docs/ref/#common.NodeType.id).
1323 */
1324 type,
1325 /**
1326 The start of the node, as an offset from the start of the document.
1327 */
1328 from,
1329 /**
1330 The end of the node.
1331 */
1332 to,
1333 /**
1334 The node's child nodes @internal
1335 */
1336 children = none) {
1337 this.type = type;
1338 this.from = from;
1339 this.to = to;
1340 this.children = children;
1341 }
1342 /**
1343 @internal
1344 */
1345 writeTo(buf, offset) {
1346 let startOff = buf.content.length;
1347 buf.writeElements(this.children, offset);
1348 buf.content.push(this.type, this.from + offset, this.to + offset, buf.content.length + 4 - startOff);
1349 }
1350 /**
1351 @internal
1352 */
1353 toTree(nodeSet) {
1354 return new Buffer(nodeSet).writeElements(this.children, -this.from).finish(this.type, this.to - this.from);
1355 }
1356}
1357class TreeElement {
1358 constructor(tree, from) {
1359 this.tree = tree;
1360 this.from = from;
1361 }
1362 get to() { return this.from + this.tree.length; }
1363 get type() { return this.tree.type.id; }
1364 get children() { return none; }
1365 writeTo(buf, offset) {
1366 buf.nodes.push(this.tree);
1367 buf.content.push(buf.nodes.length - 1, this.from + offset, this.to + offset, -1);
1368 }
1369 toTree() { return this.tree; }
1370}
1371function elt(type, from, to, children) {
1372 return new Element(type, from, to, children);
1373}
1374const EmphasisUnderscore = { resolve: "Emphasis", mark: "EmphasisMark" };
1375const EmphasisAsterisk = { resolve: "Emphasis", mark: "EmphasisMark" };
1376const LinkStart = {}, ImageStart = {};
1377class InlineDelimiter {
1378 constructor(type, from, to, side) {
1379 this.type = type;
1380 this.from = from;
1381 this.to = to;
1382 this.side = side;
1383 }
1384}
1385const Escapable = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
1386let Punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\u2010-\u2027]/;
1387try {
1388 Punctuation = new RegExp("[\\p{S}|\\p{P}]", "u");
1389}
1390catch (_) { }
1391const DefaultInline = {
1392 Escape(cx, next, start) {
1393 if (next != 92 /* '\\' */ || start == cx.end - 1)
1394 return -1;
1395 let escaped = cx.char(start + 1);
1396 for (let i = 0; i < Escapable.length; i++)
1397 if (Escapable.charCodeAt(i) == escaped)
1398 return cx.append(elt(Type.Escape, start, start + 2));
1399 return -1;
1400 },
1401 Entity(cx, next, start) {
1402 if (next != 38 /* '&' */)
1403 return -1;
1404 let m = /^(?:#\d+|#x[a-f\d]+|\w+);/i.exec(cx.slice(start + 1, start + 31));
1405 return m ? cx.append(elt(Type.Entity, start, start + 1 + m[0].length)) : -1;
1406 },
1407 InlineCode(cx, next, start) {
1408 if (next != 96 /* '`' */ || start && cx.char(start - 1) == 96)
1409 return -1;
1410 let pos = start + 1;
1411 while (pos < cx.end && cx.char(pos) == 96)
1412 pos++;
1413 let size = pos - start, curSize = 0;
1414 for (; pos < cx.end; pos++) {
1415 if (cx.char(pos) == 96) {
1416 curSize++;
1417 if (curSize == size && cx.char(pos + 1) != 96)
1418 return cx.append(elt(Type.InlineCode, start, pos + 1, [
1419 elt(Type.CodeMark, start, start + size),
1420 elt(Type.CodeMark, pos + 1 - size, pos + 1)
1421 ]));
1422 }
1423 else {
1424 curSize = 0;
1425 }
1426 }
1427 return -1;
1428 },
1429 HTMLTag(cx, next, start) {
1430 if (next != 60 /* '<' */ || start == cx.end - 1)
1431 return -1;
1432 let after = cx.slice(start + 1, cx.end);
1433 let url = /^(?:[a-z][-\w+.]+:[^\s>]+|[a-z\d.!#$%&'*+/=?^_`{|}~-]+@[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?(?:\.[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?)*)>/i.exec(after);
1434 if (url) {
1435 return cx.append(elt(Type.Autolink, start, start + 1 + url[0].length, [
1436 elt(Type.LinkMark, start, start + 1),
1437 // url[0] includes the closing bracket, so exclude it from this slice
1438 elt(Type.URL, start + 1, start + url[0].length),
1439 elt(Type.LinkMark, start + url[0].length, start + 1 + url[0].length)
1440 ]));
1441 }
1442 let comment = /^!--[^>](?:-[^-]|[^-])*?-->/i.exec(after);
1443 if (comment)
1444 return cx.append(elt(Type.Comment, start, start + 1 + comment[0].length));
1445 let procInst = /^\?[^]*?\?>/.exec(after);
1446 if (procInst)
1447 return cx.append(elt(Type.ProcessingInstruction, start, start + 1 + procInst[0].length));
1448 let m = /^(?:![A-Z][^]*?>|!\[CDATA\[[^]*?\]\]>|\/\s*[a-zA-Z][\w-]*\s*>|\s*[a-zA-Z][\w-]*(\s+[a-zA-Z:_][\w-.:]*(?:\s*=\s*(?:[^\s"'=<>`]+|'[^']*'|"[^"]*"))?)*\s*(\/\s*)?>)/.exec(after);
1449 if (!m)
1450 return -1;
1451 return cx.append(elt(Type.HTMLTag, start, start + 1 + m[0].length));
1452 },
1453 Emphasis(cx, next, start) {
1454 if (next != 95 && next != 42)
1455 return -1;
1456 let pos = start + 1;
1457 while (cx.char(pos) == next)
1458 pos++;
1459 let before = cx.slice(start - 1, start), after = cx.slice(pos, pos + 1);
1460 let pBefore = Punctuation.test(before), pAfter = Punctuation.test(after);
1461 let sBefore = /\s|^$/.test(before), sAfter = /\s|^$/.test(after);
1462 let leftFlanking = !sAfter && (!pAfter || sBefore || pBefore);
1463 let rightFlanking = !sBefore && (!pBefore || sAfter || pAfter);
1464 let canOpen = leftFlanking && (next == 42 || !rightFlanking || pBefore);
1465 let canClose = rightFlanking && (next == 42 || !leftFlanking || pAfter);
1466 return cx.append(new InlineDelimiter(next == 95 ? EmphasisUnderscore : EmphasisAsterisk, start, pos, (canOpen ? 1 /* Mark.Open */ : 0 /* Mark.None */) | (canClose ? 2 /* Mark.Close */ : 0 /* Mark.None */)));
1467 },
1468 HardBreak(cx, next, start) {
1469 if (next == 92 /* '\\' */ && cx.char(start + 1) == 10 /* '\n' */)
1470 return cx.append(elt(Type.HardBreak, start, start + 2));
1471 if (next == 32) {
1472 let pos = start + 1;
1473 while (cx.char(pos) == 32)
1474 pos++;
1475 if (cx.char(pos) == 10 && pos >= start + 2)
1476 return cx.append(elt(Type.HardBreak, start, pos + 1));
1477 }
1478 return -1;
1479 },
1480 Link(cx, next, start) {
1481 return next == 91 /* '[' */ ? cx.append(new InlineDelimiter(LinkStart, start, start + 1, 1 /* Mark.Open */)) : -1;
1482 },
1483 Image(cx, next, start) {
1484 return next == 33 /* '!' */ && cx.char(start + 1) == 91 /* '[' */
1485 ? cx.append(new InlineDelimiter(ImageStart, start, start + 2, 1 /* Mark.Open */)) : -1;
1486 },
1487 LinkEnd(cx, next, start) {
1488 if (next != 93 /* ']' */)
1489 return -1;
1490 // Scanning back to the next link/image start marker
1491 for (let i = cx.parts.length - 1; i >= 0; i--) {
1492 let part = cx.parts[i];
1493 if (part instanceof InlineDelimiter && (part.type == LinkStart || part.type == ImageStart)) {
1494 // If this one has been set invalid (because it would produce
1495 // a nested link) or there's no valid link here ignore both.
1496 if (!part.side || cx.skipSpace(part.to) == start && !/[(\[]/.test(cx.slice(start + 1, start + 2))) {
1497 cx.parts[i] = null;
1498 return -1;
1499 }
1500 // Finish the content and replace the entire range in
1501 // this.parts with the link/image node.
1502 let content = cx.takeContent(i);
1503 let link = cx.parts[i] = finishLink(cx, content, part.type == LinkStart ? Type.Link : Type.Image, part.from, start + 1);
1504 // Set any open-link markers before this link to invalid.
1505 if (part.type == LinkStart)
1506 for (let j = 0; j < i; j++) {
1507 let p = cx.parts[j];
1508 if (p instanceof InlineDelimiter && p.type == LinkStart)
1509 p.side = 0 /* Mark.None */;
1510 }
1511 return link.to;
1512 }
1513 }
1514 return -1;
1515 }
1516};
1517function finishLink(cx, content, type, start, startPos) {
1518 let { text } = cx, next = cx.char(startPos), endPos = startPos;
1519 content.unshift(elt(Type.LinkMark, start, start + (type == Type.Image ? 2 : 1)));
1520 content.push(elt(Type.LinkMark, startPos - 1, startPos));
1521 if (next == 40 /* '(' */) {
1522 let pos = cx.skipSpace(startPos + 1);
1523 let dest = parseURL(text, pos - cx.offset, cx.offset), title;
1524 if (dest) {
1525 pos = cx.skipSpace(dest.to);
1526 // The destination and title must be separated by whitespace
1527 if (pos != dest.to) {
1528 title = parseLinkTitle(text, pos - cx.offset, cx.offset);
1529 if (title)
1530 pos = cx.skipSpace(title.to);
1531 }
1532 }
1533 if (cx.char(pos) == 41 /* ')' */) {
1534 content.push(elt(Type.LinkMark, startPos, startPos + 1));
1535 endPos = pos + 1;
1536 if (dest)
1537 content.push(dest);
1538 if (title)
1539 content.push(title);
1540 content.push(elt(Type.LinkMark, pos, endPos));
1541 }
1542 }
1543 else if (next == 91 /* '[' */) {
1544 let label = parseLinkLabel(text, startPos - cx.offset, cx.offset, false);
1545 if (label) {
1546 content.push(label);
1547 endPos = label.to;
1548 }
1549 }
1550 return elt(type, start, endPos, content);
1551}
1552// These return `null` when falling off the end of the input, `false`
1553// when parsing fails otherwise (for use in the incremental link
1554// reference parser).
1555function parseURL(text, start, offset) {
1556 let next = text.charCodeAt(start);
1557 if (next == 60 /* '<' */) {
1558 for (let pos = start + 1; pos < text.length; pos++) {
1559 let ch = text.charCodeAt(pos);
1560 if (ch == 62 /* '>' */)
1561 return elt(Type.URL, start + offset, pos + 1 + offset);
1562 if (ch == 60 || ch == 10 /* '<\n' */)
1563 return false;
1564 }
1565 return null;
1566 }
1567 else {
1568 let depth = 0, pos = start;
1569 for (let escaped = false; pos < text.length; pos++) {
1570 let ch = text.charCodeAt(pos);
1571 if (space(ch)) {
1572 break;
1573 }
1574 else if (escaped) {
1575 escaped = false;
1576 }
1577 else if (ch == 40 /* '(' */) {
1578 depth++;
1579 }
1580 else if (ch == 41 /* ')' */) {
1581 if (!depth)
1582 break;
1583 depth--;
1584 }
1585 else if (ch == 92 /* '\\' */) {
1586 escaped = true;
1587 }
1588 }
1589 return pos > start ? elt(Type.URL, start + offset, pos + offset) : pos == text.length ? null : false;
1590 }
1591}
1592function parseLinkTitle(text, start, offset) {
1593 let next = text.charCodeAt(start);
1594 if (next != 39 && next != 34 && next != 40 /* '"\'(' */)
1595 return false;
1596 let end = next == 40 ? 41 : next;
1597 for (let pos = start + 1, escaped = false; pos < text.length; pos++) {
1598 let ch = text.charCodeAt(pos);
1599 if (escaped)
1600 escaped = false;
1601 else if (ch == end)
1602 return elt(Type.LinkTitle, start + offset, pos + 1 + offset);
1603 else if (ch == 92 /* '\\' */)
1604 escaped = true;
1605 }
1606 return null;
1607}
1608function parseLinkLabel(text, start, offset, requireNonWS) {
1609 for (let escaped = false, pos = start + 1, end = Math.min(text.length, pos + 999); pos < end; pos++) {
1610 let ch = text.charCodeAt(pos);
1611 if (escaped)
1612 escaped = false;
1613 else if (ch == 93 /* ']' */)
1614 return requireNonWS ? false : elt(Type.LinkLabel, start + offset, pos + 1 + offset);
1615 else {
1616 if (requireNonWS && !space(ch))
1617 requireNonWS = false;
1618 if (ch == 91 /* '[' */)
1619 return false;
1620 else if (ch == 92 /* '\\' */)
1621 escaped = true;
1622 }
1623 }
1624 return null;
1625}
1626/**
1627Inline parsing functions get access to this context, and use it to
1628read the content and emit syntax nodes.
1629*/
1630class InlineContext {
1631 /**
1632 @internal
1633 */
1634 constructor(
1635 /**
1636 The parser that is being used.
1637 */
1638 parser,
1639 /**
1640 The text of this inline section.
1641 */
1642 text,
1643 /**
1644 The starting offset of the section in the document.
1645 */
1646 offset) {
1647 this.parser = parser;
1648 this.text = text;
1649 this.offset = offset;
1650 /**
1651 @internal
1652 */
1653 this.parts = [];
1654 }
1655 /**
1656 Get the character code at the given (document-relative)
1657 position.
1658 */
1659 char(pos) { return pos >= this.end ? -1 : this.text.charCodeAt(pos - this.offset); }
1660 /**
1661 The position of the end of this inline section.
1662 */
1663 get end() { return this.offset + this.text.length; }
1664 /**
1665 Get a substring of this inline section. Again uses
1666 document-relative positions.
1667 */
1668 slice(from, to) { return this.text.slice(from - this.offset, to - this.offset); }
1669 /**
1670 @internal
1671 */
1672 append(elt) {
1673 this.parts.push(elt);
1674 return elt.to;
1675 }
1676 /**
1677 Add a [delimiter](#DelimiterType) at this given position. `open`
1678 and `close` indicate whether this delimiter is opening, closing,
1679 or both. Returns the end of the delimiter, for convenient
1680 returning from [parse functions](#InlineParser.parse).
1681 */
1682 addDelimiter(type, from, to, open, close) {
1683 return this.append(new InlineDelimiter(type, from, to, (open ? 1 /* Mark.Open */ : 0 /* Mark.None */) | (close ? 2 /* Mark.Close */ : 0 /* Mark.None */)));
1684 }
1685 /**
1686 Returns true when there is an unmatched link or image opening
1687 token before the current position.
1688 */
1689 get hasOpenLink() {
1690 for (let i = this.parts.length - 1; i >= 0; i--) {
1691 let part = this.parts[i];
1692 if (part instanceof InlineDelimiter && (part.type == LinkStart || part.type == ImageStart))
1693 return true;
1694 }
1695 return false;
1696 }
1697 /**
1698 Add an inline element. Returns the end of the element.
1699 */
1700 addElement(elt) {
1701 return this.append(elt);
1702 }
1703 /**
1704 Resolve markers between this.parts.length and from, wrapping matched markers in the
1705 appropriate node and updating the content of this.parts. @internal
1706 */
1707 resolveMarkers(from) {
1708 // Scan forward, looking for closing tokens
1709 for (let i = from; i < this.parts.length; i++) {
1710 let close = this.parts[i];
1711 if (!(close instanceof InlineDelimiter && close.type.resolve && (close.side & 2 /* Mark.Close */)))
1712 continue;
1713 let emp = close.type == EmphasisUnderscore || close.type == EmphasisAsterisk;
1714 let closeSize = close.to - close.from;
1715 let open, j = i - 1;
1716 // Continue scanning for a matching opening token
1717 for (; j >= from; j--) {
1718 let part = this.parts[j];
1719 if (part instanceof InlineDelimiter && (part.side & 1 /* Mark.Open */) && part.type == close.type &&
1720 // Ignore emphasis delimiters where the character count doesn't match
1721 !(emp && ((close.side & 1 /* Mark.Open */) || (part.side & 2 /* Mark.Close */)) &&
1722 (part.to - part.from + closeSize) % 3 == 0 && ((part.to - part.from) % 3 || closeSize % 3))) {
1723 open = part;
1724 break;
1725 }
1726 }
1727 if (!open)
1728 continue;
1729 let type = close.type.resolve, content = [];
1730 let start = open.from, end = close.to;
1731 // Emphasis marker effect depends on the character count. Size consumed is minimum of the two
1732 // markers.
1733 if (emp) {
1734 let size = Math.min(2, open.to - open.from, closeSize);
1735 start = open.to - size;
1736 end = close.from + size;
1737 type = size == 1 ? "Emphasis" : "StrongEmphasis";
1738 }
1739 // Move the covered region into content, optionally adding marker nodes
1740 if (open.type.mark)
1741 content.push(this.elt(open.type.mark, start, open.to));
1742 for (let k = j + 1; k < i; k++) {
1743 if (this.parts[k] instanceof Element)
1744 content.push(this.parts[k]);
1745 this.parts[k] = null;
1746 }
1747 if (close.type.mark)
1748 content.push(this.elt(close.type.mark, close.from, end));
1749 let element = this.elt(type, start, end, content);
1750 // If there are leftover emphasis marker characters, shrink the close/open markers. Otherwise, clear them.
1751 this.parts[j] = emp && open.from != start ? new InlineDelimiter(open.type, open.from, start, open.side) : null;
1752 let keep = this.parts[i] = emp && close.to != end ? new InlineDelimiter(close.type, end, close.to, close.side) : null;
1753 // Insert the new element in this.parts
1754 if (keep)
1755 this.parts.splice(i, 0, element);
1756 else
1757 this.parts[i] = element;
1758 }
1759 // Collect the elements remaining in this.parts into an array.
1760 let result = [];
1761 for (let i = from; i < this.parts.length; i++) {
1762 let part = this.parts[i];
1763 if (part instanceof Element)
1764 result.push(part);
1765 }
1766 return result;
1767 }
1768 /**
1769 Find an opening delimiter of the given type. Returns `null` if
1770 no delimiter is found, or an index that can be passed to
1771 [`takeContent`](#InlineContext.takeContent) otherwise.
1772 */
1773 findOpeningDelimiter(type) {
1774 for (let i = this.parts.length - 1; i >= 0; i--) {
1775 let part = this.parts[i];
1776 if (part instanceof InlineDelimiter && part.type == type && (part.side & 1 /* Mark.Open */))
1777 return i;
1778 }
1779 return null;
1780 }
1781 /**
1782 Remove all inline elements and delimiters starting from the
1783 given index (which you should get from
1784 [`findOpeningDelimiter`](#InlineContext.findOpeningDelimiter),
1785 resolve delimiters inside of them, and return them as an array
1786 of elements.
1787 */
1788 takeContent(startIndex) {
1789 let content = this.resolveMarkers(startIndex);
1790 this.parts.length = startIndex;
1791 return content;
1792 }
1793 /**
1794 Return the delimiter at the given index. Mostly useful to get
1795 additional info out of a delimiter index returned by
1796 [`findOpeningDelimiter`](#InlineContext.findOpeningDelimiter).
1797 Returns null if there is no delimiter at this index.
1798 */
1799 getDelimiterAt(index) {
1800 let part = this.parts[index];
1801 return part instanceof InlineDelimiter ? part : null;
1802 }
1803 /**
1804 Skip space after the given (document) position, returning either
1805 the position of the next non-space character or the end of the
1806 section.
1807 */
1808 skipSpace(from) { return skipSpace(this.text, from - this.offset) + this.offset; }
1809 elt(type, from, to, children) {
1810 if (typeof type == "string")
1811 return elt(this.parser.getNodeType(type), from, to, children);
1812 return new TreeElement(type, from);
1813 }
1814}
1815/**
1816The opening delimiter type used by the standard link parser.
1817*/
1818InlineContext.linkStart = LinkStart;
1819/**
1820Opening delimiter type used for standard images.
1821*/
1822InlineContext.imageStart = ImageStart;
1823function injectMarks(elements, marks) {
1824 if (!marks.length)
1825 return elements;
1826 if (!elements.length)
1827 return marks;
1828 let elts = elements.slice(), eI = 0;
1829 for (let mark of marks) {
1830 while (eI < elts.length && elts[eI].to < mark.to)
1831 eI++;
1832 if (eI < elts.length && elts[eI].from < mark.from) {
1833 let e = elts[eI];
1834 if (e instanceof Element)
1835 elts[eI] = new Element(e.type, e.from, e.to, injectMarks(e.children, [mark]));
1836 }
1837 else {
1838 elts.splice(eI++, 0, mark);
1839 }
1840 }
1841 return elts;
1842}
1843// These are blocks that can span blank lines, and should thus only be
1844// reused if their next sibling is also being reused.
1845const NotLast = [Type.CodeBlock, Type.ListItem, Type.OrderedList, Type.BulletList];
1846class FragmentCursor {
1847 constructor(fragments, input) {
1848 this.fragments = fragments;
1849 this.input = input;
1850 // Index into fragment array
1851 this.i = 0;
1852 // Active fragment
1853 this.fragment = null;
1854 this.fragmentEnd = -1;
1855 // Cursor into the current fragment, if any. When `moveTo` returns
1856 // true, this points at the first block after `pos`.
1857 this.cursor = null;
1858 if (fragments.length)
1859 this.fragment = fragments[this.i++];
1860 }
1861 nextFragment() {
1862 this.fragment = this.i < this.fragments.length ? this.fragments[this.i++] : null;
1863 this.cursor = null;
1864 this.fragmentEnd = -1;
1865 }
1866 moveTo(pos, lineStart) {
1867 while (this.fragment && this.fragment.to <= pos)
1868 this.nextFragment();
1869 if (!this.fragment || this.fragment.from > (pos ? pos - 1 : 0))
1870 return false;
1871 if (this.fragmentEnd < 0) {
1872 let end = this.fragment.to;
1873 while (end > 0 && this.input.read(end - 1, end) != "\n")
1874 end--;
1875 this.fragmentEnd = end ? end - 1 : 0;
1876 }
1877 let c = this.cursor;
1878 if (!c) {
1879 c = this.cursor = this.fragment.tree.cursor();
1880 c.firstChild();
1881 }
1882 let rPos = pos + this.fragment.offset;
1883 while (c.to <= rPos)
1884 if (!c.parent())
1885 return false;
1886 for (;;) {
1887 if (c.from >= rPos)
1888 return this.fragment.from <= lineStart;
1889 if (!c.childAfter(rPos))
1890 return false;
1891 }
1892 }
1893 matches(hash) {
1894 let tree = this.cursor.tree;
1895 return tree && tree.prop(common.NodeProp.contextHash) == hash;
1896 }
1897 takeNodes(cx) {
1898 let cur = this.cursor, off = this.fragment.offset, fragEnd = this.fragmentEnd - (this.fragment.openEnd ? 1 : 0);
1899 let start = cx.absoluteLineStart, end = start, blockI = cx.block.children.length;
1900 let prevEnd = end, prevI = blockI;
1901 for (;;) {
1902 if (cur.to - off > fragEnd) {
1903 if (cur.type.isAnonymous && cur.firstChild())
1904 continue;
1905 break;
1906 }
1907 let pos = toRelative(cur.from - off, cx.ranges);
1908 if (cur.to - off <= cx.ranges[cx.rangeI].to) { // Fits in current range
1909 cx.addNode(cur.tree, pos);
1910 }
1911 else {
1912 let dummy = new common.Tree(cx.parser.nodeSet.types[Type.Paragraph], [], [], 0, cx.block.hashProp);
1913 cx.reusePlaceholders.set(dummy, cur.tree);
1914 cx.addNode(dummy, pos);
1915 }
1916 // Taken content must always end in a block, because incremental
1917 // parsing happens on block boundaries. Never stop directly
1918 // after an indented code block, since those can continue after
1919 // any number of blank lines.
1920 if (cur.type.is("Block")) {
1921 if (NotLast.indexOf(cur.type.id) < 0) {
1922 end = cur.to - off;
1923 blockI = cx.block.children.length;
1924 }
1925 else {
1926 end = prevEnd;
1927 blockI = prevI;
1928 }
1929 prevEnd = cur.to - off;
1930 prevI = cx.block.children.length;
1931 }
1932 if (!cur.nextSibling())
1933 break;
1934 }
1935 while (cx.block.children.length > blockI) {
1936 cx.block.children.pop();
1937 cx.block.positions.pop();
1938 }
1939 return end - start;
1940 }
1941}
1942// Convert an input-stream-relative position to a
1943// Markdown-doc-relative position by subtracting the size of all input
1944// gaps before `abs`.
1945function toRelative(abs, ranges) {
1946 let pos = abs;
1947 for (let i = 1; i < ranges.length; i++) {
1948 let gapFrom = ranges[i - 1].to, gapTo = ranges[i].from;
1949 if (gapFrom < abs)
1950 pos -= gapTo - gapFrom;
1951 }
1952 return pos;
1953}
1954const markdownHighlighting = highlight.styleTags({
1955 "Blockquote/...": highlight.tags.quote,
1956 HorizontalRule: highlight.tags.contentSeparator,
1957 "ATXHeading1/... SetextHeading1/...": highlight.tags.heading1,
1958 "ATXHeading2/... SetextHeading2/...": highlight.tags.heading2,
1959 "ATXHeading3/...": highlight.tags.heading3,
1960 "ATXHeading4/...": highlight.tags.heading4,
1961 "ATXHeading5/...": highlight.tags.heading5,
1962 "ATXHeading6/...": highlight.tags.heading6,
1963 "Comment CommentBlock": highlight.tags.comment,
1964 Escape: highlight.tags.escape,
1965 Entity: highlight.tags.character,
1966 "Emphasis/...": highlight.tags.emphasis,
1967 "StrongEmphasis/...": highlight.tags.strong,
1968 "Link/... Image/...": highlight.tags.link,
1969 "OrderedList/... BulletList/...": highlight.tags.list,
1970 "BlockQuote/...": highlight.tags.quote,
1971 "InlineCode CodeText": highlight.tags.monospace,
1972 "URL Autolink": highlight.tags.url,
1973 "HeaderMark HardBreak QuoteMark ListMark LinkMark EmphasisMark CodeMark": highlight.tags.processingInstruction,
1974 "CodeInfo LinkLabel": highlight.tags.labelName,
1975 LinkTitle: highlight.tags.string,
1976 Paragraph: highlight.tags.content
1977});
1978/**
1979The default CommonMark parser.
1980*/
1981const parser = new MarkdownParser(new common.NodeSet(nodeTypes).extend(markdownHighlighting), Object.keys(DefaultBlockParsers).map(n => DefaultBlockParsers[n]), Object.keys(DefaultBlockParsers).map(n => DefaultLeafBlocks[n]), Object.keys(DefaultBlockParsers), DefaultEndLeaf, DefaultSkipMarkup, Object.keys(DefaultInline).map(n => DefaultInline[n]), Object.keys(DefaultInline), []);
1982
1983function leftOverSpace(node, from, to) {
1984 let ranges = [];
1985 for (let n = node.firstChild, pos = from;; n = n.nextSibling) {
1986 let nextPos = n ? n.from : to;
1987 if (nextPos > pos)
1988 ranges.push({ from: pos, to: nextPos });
1989 if (!n)
1990 break;
1991 pos = n.to;
1992 }
1993 return ranges;
1994}
1995/**
1996Create a Markdown extension to enable nested parsing on code
1997blocks and/or embedded HTML.
1998*/
1999function parseCode(config) {
2000 let { codeParser, htmlParser } = config;
2001 let wrap = common.parseMixed((node, input) => {
2002 let id = node.type.id;
2003 if (codeParser && (id == Type.CodeBlock || id == Type.FencedCode)) {
2004 let info = "";
2005 if (id == Type.FencedCode) {
2006 let infoNode = node.node.getChild(Type.CodeInfo);
2007 if (infoNode)
2008 info = input.read(infoNode.from, infoNode.to);
2009 }
2010 let parser = codeParser(info);
2011 if (parser)
2012 return { parser, overlay: node => node.type.id == Type.CodeText, bracketed: id == Type.FencedCode };
2013 }
2014 else if (htmlParser && (id == Type.HTMLBlock || id == Type.HTMLTag || id == Type.CommentBlock)) {
2015 return { parser: htmlParser, overlay: leftOverSpace(node.node, node.from, node.to) };
2016 }
2017 return null;
2018 });
2019 return { wrap };
2020}
2021
2022const StrikethroughDelim = { resolve: "Strikethrough", mark: "StrikethroughMark" };
2023/**
2024An extension that implements
2025[GFM-style](https://github.github.com/gfm/#strikethrough-extension-)
2026Strikethrough syntax using `~~` delimiters.
2027*/
2028const Strikethrough = {
2029 defineNodes: [{
2030 name: "Strikethrough",
2031 style: { "Strikethrough/...": highlight.tags.strikethrough }
2032 }, {
2033 name: "StrikethroughMark",
2034 style: highlight.tags.processingInstruction
2035 }],
2036 parseInline: [{
2037 name: "Strikethrough",
2038 parse(cx, next, pos) {
2039 if (next != 126 /* '~' */ || cx.char(pos + 1) != 126 || cx.char(pos + 2) == 126)
2040 return -1;
2041 let before = cx.slice(pos - 1, pos), after = cx.slice(pos + 2, pos + 3);
2042 let sBefore = /\s|^$/.test(before), sAfter = /\s|^$/.test(after);
2043 let pBefore = Punctuation.test(before), pAfter = Punctuation.test(after);
2044 return cx.addDelimiter(StrikethroughDelim, pos, pos + 2, !sAfter && (!pAfter || sBefore || pBefore), !sBefore && (!pBefore || sAfter || pAfter));
2045 },
2046 after: "Emphasis"
2047 }]
2048};
2049// Parse a line as a table row and return the row count. When `elts`
2050// is given, push syntax elements for the content onto it.
2051function parseRow(cx, line, startI = 0, elts, offset = 0) {
2052 let count = 0, first = true, cellStart = -1, cellEnd = -1, esc = false;
2053 let parseCell = () => {
2054 elts.push(cx.elt("TableCell", offset + cellStart, offset + cellEnd, cx.parser.parseInline(line.slice(cellStart, cellEnd), offset + cellStart)));
2055 };
2056 for (let i = startI; i < line.length; i++) {
2057 let next = line.charCodeAt(i);
2058 if (next == 124 /* '|' */ && !esc) {
2059 if (!first || cellStart > -1)
2060 count++;
2061 first = false;
2062 if (elts) {
2063 if (cellStart > -1)
2064 parseCell();
2065 elts.push(cx.elt("TableDelimiter", i + offset, i + offset + 1));
2066 }
2067 cellStart = cellEnd = -1;
2068 }
2069 else if (esc || next != 32 && next != 9) {
2070 if (cellStart < 0)
2071 cellStart = i;
2072 cellEnd = i + 1;
2073 }
2074 esc = !esc && next == 92;
2075 }
2076 if (cellStart > -1) {
2077 count++;
2078 if (elts)
2079 parseCell();
2080 }
2081 return count;
2082}
2083function hasPipe(str, start) {
2084 for (let i = start; i < str.length; i++) {
2085 let next = str.charCodeAt(i);
2086 if (next == 124 /* '|' */)
2087 return true;
2088 if (next == 92 /* '\\' */)
2089 i++;
2090 }
2091 return false;
2092}
2093const delimiterLine = /^\|?(\s*:?-+:?\s*\|)+(\s*:?-+:?\s*)?$/;
2094class TableParser {
2095 constructor() {
2096 // Null means we haven't seen the second line yet, false means this
2097 // isn't a table, and an array means this is a table and we've
2098 // parsed the given rows so far.
2099 this.rows = null;
2100 }
2101 nextLine(cx, line, leaf) {
2102 if (this.rows == null) { // Second line
2103 this.rows = false;
2104 let lineText;
2105 if ((line.next == 45 || line.next == 58 || line.next == 124 /* '-:|' */) &&
2106 delimiterLine.test(lineText = line.text.slice(line.pos))) {
2107 let firstRow = [], firstCount = parseRow(cx, leaf.content, 0, firstRow, leaf.start);
2108 if (firstCount == parseRow(cx, lineText, line.pos))
2109 this.rows = [cx.elt("TableHeader", leaf.start, leaf.start + leaf.content.length, firstRow),
2110 cx.elt("TableDelimiter", cx.lineStart + line.pos, cx.lineStart + line.text.length)];
2111 }
2112 }
2113 else if (this.rows) { // Line after the second
2114 let content = [];
2115 parseRow(cx, line.text, line.pos, content, cx.lineStart);
2116 this.rows.push(cx.elt("TableRow", cx.lineStart + line.pos, cx.lineStart + line.text.length, content));
2117 }
2118 return false;
2119 }
2120 finish(cx, leaf) {
2121 if (!this.rows)
2122 return false;
2123 cx.addLeafElement(leaf, cx.elt("Table", leaf.start, leaf.start + leaf.content.length, this.rows));
2124 return true;
2125 }
2126}
2127/**
2128This extension provides
2129[GFM-style](https://github.github.com/gfm/#tables-extension-)
2130tables, using syntax like this:
2131
2132```
2133| head 1 | head 2 |
2134| --- | --- |
2135| cell 1 | cell 2 |
2136```
2137*/
2138const Table = {
2139 defineNodes: [
2140 { name: "Table", block: true },
2141 { name: "TableHeader", style: { "TableHeader/...": highlight.tags.heading } },
2142 "TableRow",
2143 { name: "TableCell", style: highlight.tags.content },
2144 { name: "TableDelimiter", style: highlight.tags.processingInstruction },
2145 ],
2146 parseBlock: [{
2147 name: "Table",
2148 leaf(_, leaf) { return hasPipe(leaf.content, 0) ? new TableParser : null; },
2149 endLeaf(cx, line, leaf) {
2150 if (leaf.parsers.some(p => p instanceof TableParser) || !hasPipe(line.text, line.basePos))
2151 return false;
2152 let next = cx.peekLine();
2153 return delimiterLine.test(next) && parseRow(cx, line.text, line.basePos) == parseRow(cx, next, line.basePos);
2154 },
2155 before: "SetextHeading"
2156 }]
2157};
2158class TaskParser {
2159 nextLine() { return false; }
2160 finish(cx, leaf) {
2161 cx.addLeafElement(leaf, cx.elt("Task", leaf.start, leaf.start + leaf.content.length, [
2162 cx.elt("TaskMarker", leaf.start, leaf.start + 3),
2163 ...cx.parser.parseInline(leaf.content.slice(3), leaf.start + 3)
2164 ]));
2165 return true;
2166 }
2167}
2168/**
2169Extension providing
2170[GFM-style](https://github.github.com/gfm/#task-list-items-extension-)
2171task list items, where list items can be prefixed with `[ ]` or
2172`[x]` to add a checkbox.
2173*/
2174const TaskList = {
2175 defineNodes: [
2176 { name: "Task", block: true, style: highlight.tags.list },
2177 { name: "TaskMarker", style: highlight.tags.atom }
2178 ],
2179 parseBlock: [{
2180 name: "TaskList",
2181 leaf(cx, leaf) {
2182 return /^\[[ xX]\][ \t]/.test(leaf.content) && cx.parentType().name == "ListItem" ? new TaskParser : null;
2183 },
2184 after: "SetextHeading"
2185 }]
2186};
2187const autolinkRE = /(www\.)|(https?:\/\/)|([\w.+-]{1,100}@)|(mailto:|xmpp:)/gy;
2188const urlRE = /[\w-]+(\.[\w-]+)+(\/[^\s<]*)?/gy;
2189const lastTwoDomainWords = /[\w-]+\.[\w-]+($|\/)/;
2190const emailRE = /[\w.+-]+@[\w-]+(\.[\w.-]+)+/gy;
2191const xmppResourceRE = /\/[a-zA-Z\d@.]+/gy;
2192function count(str, from, to, ch) {
2193 let result = 0;
2194 for (let i = from; i < to; i++)
2195 if (str[i] == ch)
2196 result++;
2197 return result;
2198}
2199function autolinkURLEnd(text, from) {
2200 urlRE.lastIndex = from;
2201 let m = urlRE.exec(text);
2202 if (!m || lastTwoDomainWords.exec(m[0])[0].indexOf("_") > -1)
2203 return -1;
2204 let end = from + m[0].length;
2205 for (;;) {
2206 let last = text[end - 1], m;
2207 if (/[?!.,:*_~]/.test(last) ||
2208 last == ")" && count(text, from, end, ")") > count(text, from, end, "("))
2209 end--;
2210 else if (last == ";" && (m = /&(?:#\d+|#x[a-f\d]+|\w+);$/.exec(text.slice(from, end))))
2211 end = from + m.index;
2212 else
2213 break;
2214 }
2215 return end;
2216}
2217function autolinkEmailEnd(text, from) {
2218 emailRE.lastIndex = from;
2219 let m = emailRE.exec(text);
2220 if (!m)
2221 return -1;
2222 let last = m[0][m[0].length - 1];
2223 return last == "_" || last == "-" ? -1 : from + m[0].length - (last == "." ? 1 : 0);
2224}
2225/**
2226Extension that implements autolinking for
2227`www.`/`http://`/`https://`/`mailto:`/`xmpp:` URLs and email
2228addresses.
2229*/
2230const Autolink = {
2231 parseInline: [{
2232 name: "Autolink",
2233 parse(cx, next, absPos) {
2234 let pos = absPos - cx.offset;
2235 if (pos && /\w/.test(cx.text[pos - 1]))
2236 return -1;
2237 autolinkRE.lastIndex = pos;
2238 let m = autolinkRE.exec(cx.text), end = -1;
2239 if (!m)
2240 return -1;
2241 if (m[1] || m[2]) { // www., http://
2242 end = autolinkURLEnd(cx.text, pos + m[0].length);
2243 if (end > -1 && cx.hasOpenLink) {
2244 let noBracket = /([^\[\]]|\[[^\]]*\])*/.exec(cx.text.slice(pos, end));
2245 end = pos + noBracket[0].length;
2246 }
2247 }
2248 else if (m[3]) { // email address
2249 end = autolinkEmailEnd(cx.text, pos);
2250 }
2251 else { // mailto:/xmpp:
2252 end = autolinkEmailEnd(cx.text, pos + m[0].length);
2253 if (end > -1 && m[0] == "xmpp:") {
2254 xmppResourceRE.lastIndex = end;
2255 m = xmppResourceRE.exec(cx.text);
2256 if (m)
2257 end = m.index + m[0].length;
2258 }
2259 }
2260 if (end < 0)
2261 return -1;
2262 cx.addElement(cx.elt("URL", absPos, end + cx.offset));
2263 return end + cx.offset;
2264 }
2265 }]
2266};
2267/**
2268Extension bundle containing [`Table`](#Table),
2269[`TaskList`](#TaskList), [`Strikethrough`](#Strikethrough), and
2270[`Autolink`](#Autolink).
2271*/
2272const GFM = [Table, TaskList, Strikethrough, Autolink];
2273function parseSubSuper(ch, node, mark) {
2274 return (cx, next, pos) => {
2275 if (next != ch || cx.char(pos + 1) == ch)
2276 return -1;
2277 let elts = [cx.elt(mark, pos, pos + 1)];
2278 for (let i = pos + 1; i < cx.end; i++) {
2279 let next = cx.char(i);
2280 if (next == ch)
2281 return cx.addElement(cx.elt(node, pos, i + 1, elts.concat(cx.elt(mark, i, i + 1))));
2282 if (next == 92 /* '\\' */)
2283 elts.push(cx.elt("Escape", i, i++ + 2));
2284 if (space(next))
2285 break;
2286 }
2287 return -1;
2288 };
2289}
2290/**
2291Extension providing
2292[Pandoc-style](https://pandoc.org/MANUAL.html#superscripts-and-subscripts)
2293superscript using `^` markers.
2294*/
2295const Superscript = {
2296 defineNodes: [
2297 { name: "Superscript", style: highlight.tags.special(highlight.tags.content) },
2298 { name: "SuperscriptMark", style: highlight.tags.processingInstruction }
2299 ],
2300 parseInline: [{
2301 name: "Superscript",
2302 parse: parseSubSuper(94 /* '^' */, "Superscript", "SuperscriptMark")
2303 }]
2304};
2305/**
2306Extension providing
2307[Pandoc-style](https://pandoc.org/MANUAL.html#superscripts-and-subscripts)
2308subscript using `~` markers.
2309*/
2310const Subscript = {
2311 defineNodes: [
2312 { name: "Subscript", style: highlight.tags.special(highlight.tags.content) },
2313 { name: "SubscriptMark", style: highlight.tags.processingInstruction }
2314 ],
2315 parseInline: [{
2316 name: "Subscript",
2317 parse: parseSubSuper(126 /* '~' */, "Subscript", "SubscriptMark")
2318 }]
2319};
2320/**
2321Extension that parses two colons with only letters, underscores,
2322and numbers between them as `Emoji` nodes.
2323*/
2324const Emoji = {
2325 defineNodes: [{ name: "Emoji", style: highlight.tags.character }],
2326 parseInline: [{
2327 name: "Emoji",
2328 parse(cx, next, pos) {
2329 let match;
2330 if (next != 58 /* ':' */ || !(match = /^[a-zA-Z_0-9]+:/.exec(cx.slice(pos + 1, cx.end))))
2331 return -1;
2332 return cx.addElement(cx.elt("Emoji", pos, pos + 1 + match[0].length));
2333 }
2334 }]
2335};
2336
2337exports.Autolink = Autolink;
2338exports.BlockContext = BlockContext;
2339exports.Element = Element;
2340exports.Emoji = Emoji;
2341exports.GFM = GFM;
2342exports.InlineContext = InlineContext;
2343exports.LeafBlock = LeafBlock;
2344exports.Line = Line;
2345exports.MarkdownParser = MarkdownParser;
2346exports.Strikethrough = Strikethrough;
2347exports.Subscript = Subscript;
2348exports.Superscript = Superscript;
2349exports.Table = Table;
2350exports.TaskList = TaskList;
2351exports.parseCode = parseCode;
2352exports.parser = parser;