@orkestrel/markdown 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/core/index.cjs +1174 -1254
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +211 -2
- package/dist/src/core/index.d.ts +211 -2
- package/dist/src/core/index.js +1157 -1255
- package/dist/src/core/index.js.map +1 -1
- package/package.json +15 -15
package/dist/src/core/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let _orkestrel_contract = require("@orkestrel/contract");
|
|
3
3
|
let _orkestrel_html = require("@orkestrel/html");
|
|
4
|
-
|
|
4
|
+
// src/core/constants.ts
|
|
5
5
|
/**
|
|
6
6
|
* Caps the recursion depth the parse pipeline (`parseDocument` and its
|
|
7
7
|
* `parsers.ts` helpers), the `helpers.ts` traversal / projection functions
|
|
@@ -29,8 +29,7 @@ var EMPTY_PROJECTION = Object.freeze({
|
|
|
29
29
|
cells: Object.freeze([]),
|
|
30
30
|
rows: Object.freeze([])
|
|
31
31
|
});
|
|
32
|
-
|
|
33
|
-
//#region src/core/validators.ts
|
|
32
|
+
// src/core/validators.ts
|
|
34
33
|
/**
|
|
35
34
|
* Determines whether a node is a heading block.
|
|
36
35
|
*
|
|
@@ -359,8 +358,26 @@ var isMarkdownDocument = (0, _orkestrel_contract.recordOf)({
|
|
|
359
358
|
element: (0, _orkestrel_contract.literalOf)("document"),
|
|
360
359
|
children: (0, _orkestrel_contract.arrayOf)(isBlockNode)
|
|
361
360
|
});
|
|
362
|
-
|
|
363
|
-
|
|
361
|
+
// src/core/MarkdownLineIterator.ts
|
|
362
|
+
/** Keeps a block parse's line cursor and its unconsumed lookahead. */
|
|
363
|
+
var MarkdownLineIterator = class extends Iterator {
|
|
364
|
+
#indices;
|
|
365
|
+
#lookahead;
|
|
366
|
+
constructor(indices) {
|
|
367
|
+
super();
|
|
368
|
+
this.#indices = indices;
|
|
369
|
+
this.#lookahead = indices.next();
|
|
370
|
+
}
|
|
371
|
+
get value() {
|
|
372
|
+
return this.#lookahead.done ? void 0 : this.#lookahead.value;
|
|
373
|
+
}
|
|
374
|
+
next() {
|
|
375
|
+
const result = this.#lookahead;
|
|
376
|
+
if (!result.done) this.#lookahead = this.#indices.next();
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
// src/core/parsers.ts
|
|
364
381
|
/**
|
|
365
382
|
* Parses a run of markdown lines into a block AST, recursing into nested
|
|
366
383
|
* blockquotes, list items, and depth-capped degrade paragraphs.
|
|
@@ -397,41 +414,39 @@ function parseBlocks(lines, depth, spans = /* @__PURE__ */ new Map(), end) {
|
|
|
397
414
|
return [paragraph];
|
|
398
415
|
}
|
|
399
416
|
const blocks = [];
|
|
400
|
-
|
|
401
|
-
|
|
417
|
+
const indices = new MarkdownLineIterator(lines.keys());
|
|
418
|
+
indices.reduce((index, step) => {
|
|
419
|
+
if (step < index) return index;
|
|
402
420
|
const line = text[index] ?? "";
|
|
403
|
-
if (isBlankLine(line))
|
|
404
|
-
index += 1;
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
421
|
+
if (isBlankLine(line)) return index + 1;
|
|
407
422
|
const fence = extractFence(line);
|
|
408
423
|
if (fence) {
|
|
409
424
|
const start = index;
|
|
410
425
|
const body = [];
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
closed = true;
|
|
420
|
-
index += 1;
|
|
426
|
+
for (const cursor of indices) {
|
|
427
|
+
const closed = isFenceClose(text[cursor] ?? "", fence.marker);
|
|
428
|
+
body.push([
|
|
429
|
+
cursor,
|
|
430
|
+
closed,
|
|
431
|
+
closed ? void 0 : lines[cursor]
|
|
432
|
+
]);
|
|
433
|
+
if (closed) break;
|
|
421
434
|
}
|
|
435
|
+
const closed = body.at(-1)?.[1] ?? false;
|
|
436
|
+
const next = (body.at(-1)?.[0] ?? index) + 1;
|
|
422
437
|
const node = {
|
|
423
438
|
element: "codeBlock",
|
|
424
439
|
...fence.lang === void 0 ? {} : { lang: fence.lang },
|
|
425
|
-
code: joinSources(body, "\n").text
|
|
440
|
+
code: joinSources(body.flatMap(([, , source]) => source === void 0 ? [] : [source]), "\n").text
|
|
426
441
|
};
|
|
427
|
-
const source = joinSources(lines.slice(start,
|
|
442
|
+
const source = joinSources(lines.slice(start, next), "\n");
|
|
428
443
|
const span = projectSpan(source, 0, source.text.length);
|
|
429
444
|
if (span !== void 0) spans.set(node, !closed && end !== void 0 ? {
|
|
430
445
|
start: span.start,
|
|
431
446
|
end
|
|
432
447
|
} : span);
|
|
433
448
|
blocks.push(node);
|
|
434
|
-
|
|
449
|
+
return next;
|
|
435
450
|
}
|
|
436
451
|
if (isThematicBreak(line)) {
|
|
437
452
|
const node = { element: "thematicBreak" };
|
|
@@ -439,8 +454,7 @@ function parseBlocks(lines, depth, spans = /* @__PURE__ */ new Map(), end) {
|
|
|
439
454
|
const span = source === void 0 ? void 0 : projectSpan(source, 0, source.text.length);
|
|
440
455
|
if (span !== void 0) spans.set(node, span);
|
|
441
456
|
blocks.push(node);
|
|
442
|
-
index
|
|
443
|
-
continue;
|
|
457
|
+
return index + 1;
|
|
444
458
|
}
|
|
445
459
|
const heading = extractHeading(line);
|
|
446
460
|
if (heading) {
|
|
@@ -457,57 +471,60 @@ function parseBlocks(lines, depth, spans = /* @__PURE__ */ new Map(), end) {
|
|
|
457
471
|
const span = source === void 0 ? void 0 : projectSpan(source, 0, source.text.length);
|
|
458
472
|
if (span !== void 0) spans.set(node, span);
|
|
459
473
|
blocks.push(node);
|
|
460
|
-
index
|
|
461
|
-
continue;
|
|
474
|
+
return index + 1;
|
|
462
475
|
}
|
|
463
476
|
if (isQuote(line)) {
|
|
464
477
|
const start = index;
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
478
|
+
const first = lines[index];
|
|
479
|
+
const quoted = first === void 0 ? [] : [[index, stripQuote(first)]];
|
|
480
|
+
while (indices.value !== void 0 && isQuote(text[indices.value] ?? "")) {
|
|
481
|
+
const cursor = indices.next().value;
|
|
482
|
+
if (cursor === void 0) break;
|
|
483
|
+
const quotedLine = lines[cursor];
|
|
468
484
|
if (quotedLine === void 0) break;
|
|
469
|
-
quoted.push(stripQuote(quotedLine));
|
|
470
|
-
index += 1;
|
|
485
|
+
quoted.push([cursor, stripQuote(quotedLine)]);
|
|
471
486
|
}
|
|
472
|
-
const
|
|
487
|
+
const next = quoted.length === 0 ? index : (quoted.at(-1)?.[0] ?? index) + 1;
|
|
488
|
+
const source = joinSources(lines.slice(start, next), "\n");
|
|
473
489
|
const span = projectSpan(source, 0, source.text.length);
|
|
474
490
|
const node = {
|
|
475
491
|
element: "blockquote",
|
|
476
|
-
children: parseBlocks(quoted, depth + 1, spans,
|
|
492
|
+
children: parseBlocks(quoted.map(([, quotedLine]) => quotedLine), depth + 1, spans, next === lines.length && end !== void 0 ? end : span?.end)
|
|
477
493
|
};
|
|
478
494
|
if (span !== void 0) spans.set(node, span);
|
|
479
495
|
blocks.push(node);
|
|
480
|
-
|
|
496
|
+
return next;
|
|
481
497
|
}
|
|
482
498
|
if (isTableStart(line, text[index + 1])) {
|
|
483
499
|
const table = collectTable(lines, index, spans);
|
|
484
500
|
blocks.push(table.node);
|
|
485
|
-
|
|
486
|
-
continue;
|
|
501
|
+
return table.next;
|
|
487
502
|
}
|
|
488
503
|
if (extractListItem(line)) {
|
|
489
504
|
const list = collectList(lines, index, depth, spans, end);
|
|
490
505
|
blocks.push(list.node);
|
|
491
|
-
|
|
492
|
-
continue;
|
|
506
|
+
return list.next;
|
|
493
507
|
}
|
|
494
508
|
const start = index;
|
|
495
|
-
const paragraph = [];
|
|
496
|
-
while (
|
|
497
|
-
const
|
|
498
|
-
if (
|
|
499
|
-
|
|
509
|
+
const paragraph = [[index, lines[index]]];
|
|
510
|
+
while (indices.value !== void 0 && !isBlankLine(text[indices.value] ?? "") && !startsBlock(text, indices.value)) {
|
|
511
|
+
const cursor = indices.next().value;
|
|
512
|
+
if (cursor === void 0) break;
|
|
513
|
+
paragraph.push([cursor, lines[cursor]]);
|
|
500
514
|
}
|
|
501
|
-
const
|
|
515
|
+
const next = paragraph.length === 0 ? index : (paragraph.at(-1)?.[0] ?? index) + 1;
|
|
516
|
+
const content = paragraph.flatMap(([, source]) => source === void 0 ? [] : [source]);
|
|
517
|
+
const source = joinSources(content.map((paragraphLine, position) => normalizeParagraphLine(paragraphLine, position < content.length - 1)), "\n");
|
|
502
518
|
const node = {
|
|
503
519
|
element: "paragraph",
|
|
504
520
|
children: coalesceText(scanInlineSource(source, 0, source.text.length, spans), spans)
|
|
505
521
|
};
|
|
506
|
-
const region = joinSources(lines.slice(start,
|
|
522
|
+
const region = joinSources(lines.slice(start, next), "\n");
|
|
507
523
|
const span = projectSpan(region, 0, region.text.length);
|
|
508
524
|
if (span !== void 0) spans.set(node, span);
|
|
509
525
|
blocks.push(node);
|
|
510
|
-
|
|
526
|
+
return next;
|
|
527
|
+
}, 0);
|
|
511
528
|
return blocks;
|
|
512
529
|
}
|
|
513
530
|
/**
|
|
@@ -534,6 +551,10 @@ function parseDocument(markdown) {
|
|
|
534
551
|
* @param markdown - The markdown source to parse.
|
|
535
552
|
* @returns The parsed document and its node-identity span map.
|
|
536
553
|
*
|
|
554
|
+
* @remarks
|
|
555
|
+
* Parsing inserts into a scratch memo. The returned map contains only nodes of the
|
|
556
|
+
* final document, enumerated without the public walk's depth cap.
|
|
557
|
+
*
|
|
537
558
|
* @example
|
|
538
559
|
* ```ts
|
|
539
560
|
* const [document, spans] = parseProvenance('# Hi')
|
|
@@ -541,16 +562,19 @@ function parseDocument(markdown) {
|
|
|
541
562
|
* ```
|
|
542
563
|
*/
|
|
543
564
|
function parseProvenance(markdown) {
|
|
544
|
-
const
|
|
565
|
+
const memo = /* @__PURE__ */ new Map();
|
|
545
566
|
const document = {
|
|
546
567
|
element: "document",
|
|
547
|
-
children: parseBlocks(splitLines(markdown), 0,
|
|
568
|
+
children: parseBlocks(splitLines(markdown), 0, memo, markdown.length)
|
|
548
569
|
};
|
|
549
|
-
|
|
570
|
+
memo.set(document, {
|
|
550
571
|
start: 0,
|
|
551
572
|
end: markdown.length
|
|
552
573
|
});
|
|
553
|
-
|
|
574
|
+
const nodes = [document];
|
|
575
|
+
for (const node of nodes) for (const child of listChildren(node)) nodes.push(child);
|
|
576
|
+
const present = new Set(nodes);
|
|
577
|
+
return [document, new Map([...memo].filter(([node]) => present.has(node)))];
|
|
554
578
|
}
|
|
555
579
|
/**
|
|
556
580
|
* Parses inline markdown text (emphasis, code spans, links, images, and hard
|
|
@@ -568,8 +592,7 @@ function parseProvenance(markdown) {
|
|
|
568
592
|
function parseInline(text) {
|
|
569
593
|
return coalesceText(scanInline(text, 0, text.length));
|
|
570
594
|
}
|
|
571
|
-
|
|
572
|
-
//#region src/core/helpers.ts
|
|
595
|
+
// src/core/helpers.ts
|
|
573
596
|
/**
|
|
574
597
|
* Splits a markdown document into offset-bearing lines while normalizing CRLF and
|
|
575
598
|
* bare CR terminators at the line boundary. A single trailing terminator does not
|
|
@@ -584,36 +607,17 @@ function parseInline(text) {
|
|
|
584
607
|
* ```
|
|
585
608
|
*/
|
|
586
609
|
function splitLines(markdown) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const character = markdown[index];
|
|
592
|
-
if (character !== "\r" && character !== "\n") {
|
|
593
|
-
index += 1;
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
lines.push({
|
|
597
|
-
text: markdown.slice(start, index),
|
|
610
|
+
return Iterator.from(markdown.matchAll(/([^\r\n]*)(?:\r\n|\r|\n|$)/g)).filter((match) => match.index < markdown.length || markdown.length === 0).map((match) => {
|
|
611
|
+
const text = match[1] ?? "";
|
|
612
|
+
return {
|
|
613
|
+
text,
|
|
598
614
|
segments: [{
|
|
599
615
|
offset: 0,
|
|
600
|
-
start,
|
|
601
|
-
end: index
|
|
616
|
+
start: match.index,
|
|
617
|
+
end: match.index + text.length
|
|
602
618
|
}]
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
start = index;
|
|
606
|
-
}
|
|
607
|
-
lines.push({
|
|
608
|
-
text: markdown.slice(start),
|
|
609
|
-
segments: [{
|
|
610
|
-
offset: 0,
|
|
611
|
-
start,
|
|
612
|
-
end: markdown.length
|
|
613
|
-
}]
|
|
614
|
-
});
|
|
615
|
-
if (lines.length > 1 && lines[lines.length - 1]?.text === "") lines.pop();
|
|
616
|
-
return lines;
|
|
619
|
+
};
|
|
620
|
+
}).toArray();
|
|
617
621
|
}
|
|
618
622
|
/**
|
|
619
623
|
* Slices derived markdown text and narrows each intersecting source segment to the
|
|
@@ -633,24 +637,23 @@ function splitLines(markdown) {
|
|
|
633
637
|
function sliceSource(source, from, to) {
|
|
634
638
|
const start = Math.max(0, Math.min(from, source.text.length));
|
|
635
639
|
const end = Math.max(start, Math.min(to, source.text.length));
|
|
636
|
-
const segments =
|
|
637
|
-
for (let index = 0; index < source.segments.length; index += 1) {
|
|
640
|
+
const segments = source.segments.keys().flatMap((index) => {
|
|
638
641
|
const segment = source.segments[index];
|
|
639
|
-
if (segment === void 0)
|
|
642
|
+
if (segment === void 0) return [];
|
|
640
643
|
const next = source.segments[index + 1];
|
|
641
644
|
const limit = Math.min(segment.offset + (segment.end - segment.start), next === void 0 ? source.text.length : next.offset);
|
|
642
645
|
const overlapStart = Math.max(start, segment.offset);
|
|
643
646
|
const overlapEnd = Math.min(end, limit);
|
|
644
647
|
const empty = segment.offset === limit && overlapStart === segment.offset;
|
|
645
|
-
if (overlapStart >= overlapEnd && !empty)
|
|
648
|
+
if (overlapStart >= overlapEnd && !empty) return [];
|
|
646
649
|
const originalStart = overlapStart === limit ? segment.end : Math.min(segment.end, segment.start + overlapStart - segment.offset);
|
|
647
650
|
const originalEnd = overlapEnd === limit ? segment.end : Math.min(segment.end, segment.start + overlapEnd - segment.offset);
|
|
648
|
-
|
|
651
|
+
return [{
|
|
649
652
|
offset: overlapStart - start,
|
|
650
653
|
start: originalStart,
|
|
651
654
|
end: originalEnd
|
|
652
|
-
}
|
|
653
|
-
}
|
|
655
|
+
}];
|
|
656
|
+
}).toArray();
|
|
654
657
|
return {
|
|
655
658
|
text: source.text.slice(start, end),
|
|
656
659
|
segments
|
|
@@ -671,31 +674,34 @@ function sliceSource(source, from, to) {
|
|
|
671
674
|
* ```
|
|
672
675
|
*/
|
|
673
676
|
function joinSources(sources, separator) {
|
|
674
|
-
|
|
677
|
+
const parts = [];
|
|
675
678
|
const segments = [];
|
|
676
|
-
|
|
679
|
+
sources.keys().reduce((offset, index) => {
|
|
677
680
|
const source = sources[index];
|
|
678
|
-
if (source === void 0)
|
|
681
|
+
if (source === void 0) return offset;
|
|
679
682
|
if (index > 0) {
|
|
680
683
|
const previous = sources[index - 1];
|
|
681
684
|
const left = previous?.segments[previous.segments.length - 1];
|
|
682
685
|
const right = source.segments[0];
|
|
683
686
|
if (separator.length > 0 && left !== void 0 && right !== void 0 && left.end < right.start) segments.push({
|
|
684
|
-
offset
|
|
687
|
+
offset,
|
|
685
688
|
start: left.end,
|
|
686
689
|
end: right.start
|
|
687
690
|
});
|
|
688
|
-
|
|
691
|
+
parts.push(separator);
|
|
689
692
|
}
|
|
693
|
+
const start = offset + (index > 0 ? separator.length : 0);
|
|
690
694
|
for (const segment of source.segments) segments.push({
|
|
691
|
-
offset:
|
|
695
|
+
offset: start + segment.offset,
|
|
692
696
|
start: segment.start,
|
|
693
697
|
end: segment.end
|
|
694
698
|
});
|
|
695
|
-
text
|
|
696
|
-
|
|
699
|
+
const text = source.text;
|
|
700
|
+
parts.push(text);
|
|
701
|
+
return start + text.length;
|
|
702
|
+
}, 0);
|
|
697
703
|
return {
|
|
698
|
-
text,
|
|
704
|
+
text: parts.join(""),
|
|
699
705
|
segments
|
|
700
706
|
};
|
|
701
707
|
}
|
|
@@ -716,27 +722,46 @@ function joinSources(sources, separator) {
|
|
|
716
722
|
*/
|
|
717
723
|
function projectSpan(source, from, to) {
|
|
718
724
|
if (from < 0 || to < from || to > source.text.length) return void 0;
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
725
|
+
const [spanStart, spanEnd] = source.segments.keys().reduce(([start, end, found], index) => {
|
|
726
|
+
if (found) return [
|
|
727
|
+
start,
|
|
728
|
+
end,
|
|
729
|
+
found
|
|
730
|
+
];
|
|
722
731
|
const segment = source.segments[index];
|
|
723
|
-
if (segment === void 0)
|
|
732
|
+
if (segment === void 0) return [
|
|
733
|
+
start,
|
|
734
|
+
end,
|
|
735
|
+
false
|
|
736
|
+
];
|
|
724
737
|
const next = source.segments[index + 1];
|
|
725
738
|
const limit = Math.min(segment.offset + (segment.end - segment.start), next === void 0 ? source.text.length : next.offset);
|
|
726
739
|
if (from === to && from >= segment.offset && from <= limit) {
|
|
727
|
-
if (next !== void 0 && from === next.offset)
|
|
740
|
+
if (next !== void 0 && from === next.offset) return [
|
|
741
|
+
start,
|
|
742
|
+
end,
|
|
743
|
+
false
|
|
744
|
+
];
|
|
728
745
|
const position = from === limit ? segment.end : Math.min(segment.end, segment.start + from - segment.offset);
|
|
729
|
-
return
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
746
|
+
return [
|
|
747
|
+
position,
|
|
748
|
+
position,
|
|
749
|
+
true
|
|
750
|
+
];
|
|
733
751
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
752
|
+
return [
|
|
753
|
+
start === void 0 && from >= segment.offset && from < limit ? segment.start + from - segment.offset : start,
|
|
754
|
+
to > segment.offset && to <= limit ? to === limit ? segment.end : Math.min(segment.end, segment.start + to - segment.offset) : end,
|
|
755
|
+
false
|
|
756
|
+
];
|
|
757
|
+
}, [
|
|
758
|
+
void 0,
|
|
759
|
+
void 0,
|
|
760
|
+
false
|
|
761
|
+
]);
|
|
762
|
+
return spanStart === void 0 || spanEnd === void 0 ? void 0 : {
|
|
763
|
+
start: spanStart,
|
|
764
|
+
end: spanEnd
|
|
740
765
|
};
|
|
741
766
|
}
|
|
742
767
|
/**
|
|
@@ -797,10 +822,7 @@ function normalizeParagraphLine(source, breaks) {
|
|
|
797
822
|
* ```
|
|
798
823
|
*/
|
|
799
824
|
function countIndent(line) {
|
|
800
|
-
|
|
801
|
-
for (const character of line) if (character === " " || character === " ") count += 1;
|
|
802
|
-
else break;
|
|
803
|
-
return count;
|
|
825
|
+
return line.match(/^[ \t]*/)?.[0].length ?? 0;
|
|
804
826
|
}
|
|
805
827
|
/**
|
|
806
828
|
* Checks whether `character` is whitespace under the emphasis flanking rule — a space, a
|
|
@@ -880,16 +902,8 @@ function isQuote(line) {
|
|
|
880
902
|
*/
|
|
881
903
|
function isFenceClose(line, marker) {
|
|
882
904
|
const character = marker[0] === "~" ? "~" : "`";
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
let run = 0;
|
|
886
|
-
while (index < line.length && line[index] === character) {
|
|
887
|
-
run++;
|
|
888
|
-
index++;
|
|
889
|
-
}
|
|
890
|
-
if (run < marker.length) return false;
|
|
891
|
-
while (index < line.length && isFenceWhitespace(line[index])) index++;
|
|
892
|
-
return index === line.length;
|
|
905
|
+
const trimmed = line.replace(/^[ \t\n\r\f\v]+|[ \t\n\r\f\v]+$/g, "");
|
|
906
|
+
return trimmed.length >= marker.length && Iterator.from(trimmed).every((value) => value === character);
|
|
893
907
|
}
|
|
894
908
|
/**
|
|
895
909
|
* Checks whether `character` is a regex-`\s`-equivalent whitespace character — the
|
|
@@ -1095,12 +1109,11 @@ function splitTableRow(row) {
|
|
|
1095
1109
|
function splitTableSources(row) {
|
|
1096
1110
|
const source = trimSource(row);
|
|
1097
1111
|
const cells = [];
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
if (
|
|
1103
|
-
pieces.push(sliceSource(source, start, index));
|
|
1112
|
+
const pieces = [];
|
|
1113
|
+
const [tailStart, tailOffset] = Iterator.from(source.text.matchAll(/\\\||\|/g)).reduce(([start, offset], match) => {
|
|
1114
|
+
const index = match.index;
|
|
1115
|
+
pieces.push(sliceSource(source, start, index));
|
|
1116
|
+
if (match[0] === "\\|") {
|
|
1104
1117
|
const span = projectSpan(source, index, index + 2);
|
|
1105
1118
|
pieces.push({
|
|
1106
1119
|
text: "|",
|
|
@@ -1110,21 +1123,16 @@ function splitTableSources(row) {
|
|
|
1110
1123
|
end: span.end
|
|
1111
1124
|
}]
|
|
1112
1125
|
});
|
|
1113
|
-
index
|
|
1114
|
-
start = index + 1;
|
|
1115
|
-
continue;
|
|
1126
|
+
return [index + 2, offset];
|
|
1116
1127
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
cells.
|
|
1125
|
-
if ((0, _orkestrel_contract.isNonEmptyArray)(cells) && (0, _orkestrel_contract.isEmptyString)((cells[0]?.text ?? "").trim())) cells.shift();
|
|
1126
|
-
if ((0, _orkestrel_contract.isNonEmptyArray)(cells) && (0, _orkestrel_contract.isEmptyString)((cells[cells.length - 1]?.text ?? "").trim())) cells.pop();
|
|
1127
|
-
return cells;
|
|
1128
|
+
cells.push(joinSources(pieces.slice(offset), ""));
|
|
1129
|
+
return [index + 1, pieces.length];
|
|
1130
|
+
}, [0, 0]);
|
|
1131
|
+
pieces.push(sliceSource(source, tailStart, source.text.length));
|
|
1132
|
+
cells.push(joinSources(pieces.slice(tailOffset), ""));
|
|
1133
|
+
const first = (0, _orkestrel_contract.isEmptyString)((cells[0]?.text ?? "").trim()) ? 1 : 0;
|
|
1134
|
+
const end = (0, _orkestrel_contract.isEmptyString)((cells.at(-1)?.text ?? "").trim()) ? cells.length - 1 : cells.length;
|
|
1135
|
+
return cells.slice(first, end);
|
|
1128
1136
|
}
|
|
1129
1137
|
/**
|
|
1130
1138
|
* Derives the per-column {@link TableAlign} list from a GFM delimiter row — `:---`
|
|
@@ -1183,22 +1191,15 @@ function startsBlock(lines, index) {
|
|
|
1183
1191
|
* ```
|
|
1184
1192
|
*/
|
|
1185
1193
|
function unescapeText(text) {
|
|
1186
|
-
|
|
1187
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
1188
|
-
const character = text[index] ?? "";
|
|
1189
|
-
if (character === "\\" && isEscapable(text[index + 1] ?? "")) {
|
|
1190
|
-
out += text[index + 1] ?? "";
|
|
1191
|
-
index += 1;
|
|
1192
|
-
} else out += character;
|
|
1193
|
-
}
|
|
1194
|
-
return out;
|
|
1194
|
+
return text.replace(/\\([\s\S])/g, (match, character) => isEscapable(character) ? character : match);
|
|
1195
1195
|
}
|
|
1196
1196
|
/**
|
|
1197
1197
|
* Merges adjacent text nodes into one — the inline scanner emits a text node per
|
|
1198
1198
|
* unrecognized character, so coalescing keeps the AST clean and assertion-friendly.
|
|
1199
1199
|
*
|
|
1200
1200
|
* @param nodes - The inline nodes (possibly with adjacent text runs)
|
|
1201
|
-
* @param spans - The optional operation-owned node
|
|
1201
|
+
* @param spans - The optional operation-owned span memo; inserts merged-node entries
|
|
1202
|
+
* without deleting or replacing constituent entries
|
|
1202
1203
|
* @returns The nodes with consecutive text nodes concatenated
|
|
1203
1204
|
*
|
|
1204
1205
|
* @example
|
|
@@ -1208,28 +1209,53 @@ function unescapeText(text) {
|
|
|
1208
1209
|
* ```
|
|
1209
1210
|
*/
|
|
1210
1211
|
function coalesceText(nodes, spans) {
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1212
|
+
const result = Iterator.from(nodes).reduce(({ first, last, value, complete, log }, node) => {
|
|
1213
|
+
if (node.element === "text" && first?.element === "text") return {
|
|
1214
|
+
first,
|
|
1215
|
+
last: node,
|
|
1216
|
+
value: (value ?? first.value) + node.value,
|
|
1217
|
+
complete: complete && (spans?.has(node) ?? false),
|
|
1218
|
+
log
|
|
1219
|
+
};
|
|
1220
|
+
if (first !== void 0) {
|
|
1221
|
+
const merged = value === void 0 ? first : {
|
|
1216
1222
|
element: "text",
|
|
1217
|
-
value
|
|
1223
|
+
value
|
|
1218
1224
|
};
|
|
1219
|
-
const left = spans?.get(
|
|
1220
|
-
const right = spans?.get(
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1225
|
+
const left = spans?.get(first);
|
|
1226
|
+
const right = last === void 0 ? void 0 : spans?.get(last);
|
|
1227
|
+
if (value !== void 0 && complete && left !== void 0 && right !== void 0) spans?.set(merged, {
|
|
1228
|
+
start: left.start,
|
|
1229
|
+
end: right.end
|
|
1230
|
+
});
|
|
1231
|
+
log.push(merged);
|
|
1232
|
+
}
|
|
1233
|
+
return {
|
|
1234
|
+
first: node,
|
|
1235
|
+
last: node,
|
|
1236
|
+
value: void 0,
|
|
1237
|
+
complete: spans?.has(node) ?? false,
|
|
1238
|
+
log
|
|
1239
|
+
};
|
|
1240
|
+
}, {
|
|
1241
|
+
first: void 0,
|
|
1242
|
+
last: void 0,
|
|
1243
|
+
value: void 0,
|
|
1244
|
+
complete: false,
|
|
1245
|
+
log: []
|
|
1246
|
+
});
|
|
1247
|
+
if (result.first === void 0) return result.log;
|
|
1248
|
+
const merged = result.value === void 0 ? result.first : {
|
|
1249
|
+
element: "text",
|
|
1250
|
+
value: result.value
|
|
1251
|
+
};
|
|
1252
|
+
const left = spans?.get(result.first);
|
|
1253
|
+
const right = result.last === void 0 ? void 0 : spans?.get(result.last);
|
|
1254
|
+
if (result.value !== void 0 && result.complete && left !== void 0 && right !== void 0) spans?.set(merged, {
|
|
1255
|
+
start: left.start,
|
|
1256
|
+
end: right.end
|
|
1257
|
+
});
|
|
1258
|
+
return [...result.log, merged];
|
|
1233
1259
|
}
|
|
1234
1260
|
/**
|
|
1235
1261
|
* Scans an inline code span at `start` (a `` ` ``-run … a matching `` ` ``-run of the
|
|
@@ -1248,23 +1274,26 @@ function coalesceText(nodes, spans) {
|
|
|
1248
1274
|
* ```
|
|
1249
1275
|
*/
|
|
1250
1276
|
function scanCode(source, start, to) {
|
|
1251
|
-
|
|
1252
|
-
while (start + run < to && source[start + run] === "`") run += 1;
|
|
1277
|
+
const run = source[start] === "`" ? countMarkdownRun(source, start, to) : 0;
|
|
1253
1278
|
const open = "`".repeat(run);
|
|
1254
|
-
|
|
1255
|
-
|
|
1279
|
+
const attempts = walkMarkdownCharacters(source, 0, source.length + 2);
|
|
1280
|
+
const [, result] = attempts.reduce(([search]) => {
|
|
1256
1281
|
const closeAt = source.indexOf(open, search);
|
|
1257
|
-
if (closeAt === -1 || closeAt + run > to)
|
|
1282
|
+
if (closeAt === -1 || closeAt + run > to) {
|
|
1283
|
+
attempts.return(void 0);
|
|
1284
|
+
return [search, void 0];
|
|
1285
|
+
}
|
|
1258
1286
|
if (source[closeAt - 1] !== "`" && source[closeAt + run] !== "`") {
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
return {
|
|
1262
|
-
value,
|
|
1287
|
+
attempts.return(void 0);
|
|
1288
|
+
const raw = source.slice(start + run, closeAt);
|
|
1289
|
+
return [search, {
|
|
1290
|
+
value: raw.length > 2 && raw.startsWith(" ") && raw.endsWith(" ") && raw.trim().length > 0 ? raw.slice(1, -1) : raw,
|
|
1263
1291
|
end: closeAt + run
|
|
1264
|
-
};
|
|
1292
|
+
}];
|
|
1265
1293
|
}
|
|
1266
|
-
|
|
1267
|
-
}
|
|
1294
|
+
return [closeAt + 1, void 0];
|
|
1295
|
+
}, [start + run, void 0]);
|
|
1296
|
+
return result;
|
|
1268
1297
|
}
|
|
1269
1298
|
/**
|
|
1270
1299
|
* Locates a link `[text](href)` at `start` — the text runs to a balanced `]`, then `(`
|
|
@@ -1283,45 +1312,12 @@ function scanCode(source, start, to) {
|
|
|
1283
1312
|
* ```
|
|
1284
1313
|
*/
|
|
1285
1314
|
function locateLink(source, start, to) {
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
if (character === "\\") {
|
|
1291
|
-
index += 1;
|
|
1292
|
-
continue;
|
|
1293
|
-
}
|
|
1294
|
-
if (character === "[") bracketDepth += 1;
|
|
1295
|
-
else if (character === "]") {
|
|
1296
|
-
bracketDepth -= 1;
|
|
1297
|
-
if (bracketDepth === 0) {
|
|
1298
|
-
close = index;
|
|
1299
|
-
break;
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
if (close === -1 || source[close + 1] !== "(") return void 0;
|
|
1304
|
-
let parenDepth = 0;
|
|
1305
|
-
let parenClose = -1;
|
|
1306
|
-
for (let index = close + 1; index < to; index += 1) {
|
|
1307
|
-
const character = source[index] ?? "";
|
|
1308
|
-
if (character === "\\") {
|
|
1309
|
-
index += 1;
|
|
1310
|
-
continue;
|
|
1311
|
-
}
|
|
1312
|
-
if (character === "(") parenDepth += 1;
|
|
1313
|
-
else if (character === ")") {
|
|
1314
|
-
parenDepth -= 1;
|
|
1315
|
-
if (parenDepth === 0) {
|
|
1316
|
-
parenClose = index;
|
|
1317
|
-
break;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
if (parenClose === -1) return void 0;
|
|
1322
|
-
return {
|
|
1315
|
+
const close = locateMarkdownDelimiter(source, start, to, "[", "]");
|
|
1316
|
+
if (close === void 0 || source[close + 1] !== "(") return void 0;
|
|
1317
|
+
const end = locateMarkdownDelimiter(source, close + 1, to, "(", ")");
|
|
1318
|
+
return end === void 0 ? void 0 : {
|
|
1323
1319
|
close,
|
|
1324
|
-
end:
|
|
1320
|
+
end: end + 1
|
|
1325
1321
|
};
|
|
1326
1322
|
}
|
|
1327
1323
|
/**
|
|
@@ -1377,44 +1373,38 @@ function scanLink(source, start, to, depth = 0) {
|
|
|
1377
1373
|
*/
|
|
1378
1374
|
function locateEmphasis(source, start, to) {
|
|
1379
1375
|
const marker = source[start] ?? "";
|
|
1380
|
-
|
|
1381
|
-
while (start + run < to && source[start + run] === marker && run < 2) run += 1;
|
|
1376
|
+
const run = Math.min(2, countMarkdownRun(source, start, to));
|
|
1382
1377
|
const strong = run === 2;
|
|
1383
1378
|
const openEnd = start + run;
|
|
1384
1379
|
if (openEnd >= to || isFlankingWhitespace(source[openEnd] ?? "")) return void 0;
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
if (character === "\\")
|
|
1389
|
-
index += 2;
|
|
1390
|
-
continue;
|
|
1391
|
-
}
|
|
1380
|
+
const characters = walkMarkdownCharacters(source, openEnd, to);
|
|
1381
|
+
const [, result] = characters.reduce(([next], [index, character]) => {
|
|
1382
|
+
if (index < next) return [next, void 0];
|
|
1383
|
+
if (character === "\\") return [index + 2, void 0];
|
|
1392
1384
|
if (character === "`") {
|
|
1393
1385
|
const span = scanCode(source, index, to);
|
|
1394
|
-
|
|
1395
|
-
continue;
|
|
1386
|
+
return [span ? span.end : index + 1, void 0];
|
|
1396
1387
|
}
|
|
1397
1388
|
if ((character === "*" || character === "_") && character !== marker) {
|
|
1398
1389
|
const nested = locateEmphasis(source, index, to);
|
|
1399
|
-
if (nested !== void 0)
|
|
1400
|
-
index = nested.end;
|
|
1401
|
-
continue;
|
|
1402
|
-
}
|
|
1390
|
+
if (nested !== void 0) return [nested.end, void 0];
|
|
1403
1391
|
}
|
|
1404
1392
|
if (character === marker) {
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1393
|
+
const closeRun = countMarkdownRun(source, index, to);
|
|
1394
|
+
if (closeRun >= run && !isFlankingWhitespace(source[index - 1] ?? "")) {
|
|
1395
|
+
characters.return(void 0);
|
|
1396
|
+
return [index + run, {
|
|
1397
|
+
strong,
|
|
1398
|
+
open: openEnd,
|
|
1399
|
+
close: index,
|
|
1400
|
+
end: index + run
|
|
1401
|
+
}];
|
|
1402
|
+
}
|
|
1403
|
+
return [index + closeRun, void 0];
|
|
1415
1404
|
}
|
|
1416
|
-
index
|
|
1417
|
-
}
|
|
1405
|
+
return [index + 1, void 0];
|
|
1406
|
+
}, [openEnd, void 0]);
|
|
1407
|
+
return result;
|
|
1418
1408
|
}
|
|
1419
1409
|
/**
|
|
1420
1410
|
* Scans an emphasis run at `start` (`*` / `_`, doubled for strong) — finds the nearest
|
|
@@ -1518,116 +1508,45 @@ function scanInlineSource(source, from, to, spans, depth = 0) {
|
|
|
1518
1508
|
return [node];
|
|
1519
1509
|
} else return [];
|
|
1520
1510
|
const nodes = [];
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1511
|
+
const parts = [];
|
|
1512
|
+
const [end, pendingStart, partOffset] = walkMarkdownCharacters(source.text, from, to).reduce(([next, start, offset], [index, character]) => {
|
|
1513
|
+
if (index < next) return [
|
|
1514
|
+
next,
|
|
1515
|
+
start,
|
|
1516
|
+
offset
|
|
1517
|
+
];
|
|
1526
1518
|
if (character === "\\" && index + 1 < to && isEscapable(source.text[index + 1] ?? "")) {
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
let spaceEnd = index;
|
|
1534
|
-
while (spaceEnd < to && source.text[spaceEnd] === " ") spaceEnd += 1;
|
|
1535
|
-
if (spaceEnd - index >= 2 && source.text[spaceEnd] === "\n") {
|
|
1536
|
-
if (pending.length > 0) {
|
|
1537
|
-
const node = {
|
|
1538
|
-
element: "text",
|
|
1539
|
-
value: pending
|
|
1540
|
-
};
|
|
1541
|
-
const span = projectSpan(source, pendingStart, index);
|
|
1542
|
-
if (span !== void 0) spans.set(node, span);
|
|
1543
|
-
nodes.push(node);
|
|
1544
|
-
pending = "";
|
|
1545
|
-
}
|
|
1546
|
-
const node = { element: "break" };
|
|
1547
|
-
const span = projectSpan(source, index, spaceEnd + 1);
|
|
1548
|
-
if (span !== void 0) spans.set(node, span);
|
|
1549
|
-
nodes.push(node);
|
|
1550
|
-
index = spaceEnd + 1;
|
|
1551
|
-
pendingStart = index;
|
|
1552
|
-
continue;
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
let scanned;
|
|
1556
|
-
let end = index;
|
|
1557
|
-
if (character === "`") {
|
|
1558
|
-
const span = scanCode(source.text, index, to);
|
|
1559
|
-
if (span) {
|
|
1560
|
-
scanned = {
|
|
1561
|
-
element: "codeSpan",
|
|
1562
|
-
value: span.value
|
|
1563
|
-
};
|
|
1564
|
-
end = span.end;
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
if (character === "!" && source.text[index + 1] === "[") {
|
|
1568
|
-
const link = locateLink(source.text, index + 1, to);
|
|
1569
|
-
if (link !== void 0) {
|
|
1570
|
-
scanned = {
|
|
1571
|
-
element: "image",
|
|
1572
|
-
src: unescapeText(source.text.slice(link.close + 2, link.end - 1).trim()),
|
|
1573
|
-
children: coalesceText(scanInlineSource(source, index + 2, link.close, spans, depth + 1), spans)
|
|
1574
|
-
};
|
|
1575
|
-
end = link.end;
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
if (character === "[") {
|
|
1579
|
-
const link = locateLink(source.text, index, to);
|
|
1580
|
-
if (link !== void 0) {
|
|
1581
|
-
scanned = {
|
|
1582
|
-
element: "link",
|
|
1583
|
-
href: unescapeText(source.text.slice(link.close + 2, link.end - 1).trim()),
|
|
1584
|
-
children: coalesceText(scanInlineSource(source, index + 1, link.close, spans, depth + 1), spans)
|
|
1585
|
-
};
|
|
1586
|
-
end = link.end;
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
if (character === "*" || character === "_") {
|
|
1590
|
-
const emphasis = locateEmphasis(source.text, index, to);
|
|
1591
|
-
if (emphasis !== void 0) {
|
|
1592
|
-
scanned = {
|
|
1593
|
-
element: "emphasis",
|
|
1594
|
-
strong: emphasis.strong,
|
|
1595
|
-
children: coalesceText(scanInlineSource(source, emphasis.open, emphasis.close, spans, depth + 1), spans)
|
|
1596
|
-
};
|
|
1597
|
-
end = emphasis.end;
|
|
1598
|
-
}
|
|
1519
|
+
parts.push(source.text[index + 1] ?? "");
|
|
1520
|
+
return [
|
|
1521
|
+
index + 2,
|
|
1522
|
+
start,
|
|
1523
|
+
offset
|
|
1524
|
+
];
|
|
1599
1525
|
}
|
|
1526
|
+
const scanned = scanMarkdownInline(source, index, to, spans, depth);
|
|
1600
1527
|
if (scanned !== void 0) {
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
}
|
|
1611
|
-
const span = projectSpan(source, index, end);
|
|
1612
|
-
if (span !== void 0) spans.set(scanned, span);
|
|
1613
|
-
nodes.push(scanned);
|
|
1614
|
-
index = end;
|
|
1615
|
-
pendingStart = index;
|
|
1616
|
-
continue;
|
|
1528
|
+
recordMarkdownText(source, parts.slice(offset).join(""), start, index, spans, nodes);
|
|
1529
|
+
const span = projectSpan(source, index, scanned.end);
|
|
1530
|
+
if (span !== void 0) spans.set(scanned.node, span);
|
|
1531
|
+
nodes.push(scanned.node);
|
|
1532
|
+
return [
|
|
1533
|
+
scanned.end,
|
|
1534
|
+
scanned.end,
|
|
1535
|
+
parts.length
|
|
1536
|
+
];
|
|
1617
1537
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
}
|
|
1538
|
+
parts.push(character);
|
|
1539
|
+
return [
|
|
1540
|
+
index + 1,
|
|
1541
|
+
start,
|
|
1542
|
+
offset
|
|
1543
|
+
];
|
|
1544
|
+
}, [
|
|
1545
|
+
from,
|
|
1546
|
+
from,
|
|
1547
|
+
0
|
|
1548
|
+
]);
|
|
1549
|
+
recordMarkdownText(source, parts.slice(partOffset).join(""), pendingStart, end, spans, nodes);
|
|
1631
1550
|
return nodes;
|
|
1632
1551
|
}
|
|
1633
1552
|
/**
|
|
@@ -1655,30 +1574,28 @@ function collectTable(lines, start, spans = /* @__PURE__ */ new Map()) {
|
|
|
1655
1574
|
return coalesceText(scanInlineSource(source, 0, source.text.length, spans), spans);
|
|
1656
1575
|
});
|
|
1657
1576
|
const align = delimiterToAlignments(lines[start + 1]?.text ?? "");
|
|
1658
|
-
const padded = [];
|
|
1659
|
-
|
|
1660
|
-
const
|
|
1661
|
-
|
|
1662
|
-
while (index < lines.length && !isBlankLine(lines[index]?.text ?? "") && (lines[index]?.text ?? "").includes("|")) {
|
|
1577
|
+
const padded = Array.from({ length: columns }, (_, column) => align[column] ?? null);
|
|
1578
|
+
const entries = [];
|
|
1579
|
+
for (const index of walkMarkdownLines(lines, start + 2)) {
|
|
1580
|
+
if (isBlankLine(lines[index]?.text ?? "") || !(lines[index]?.text ?? "").includes("|")) break;
|
|
1663
1581
|
const cells = splitTableSources(lines[index] ?? {
|
|
1664
1582
|
text: "",
|
|
1665
1583
|
segments: []
|
|
1666
1584
|
});
|
|
1667
|
-
const row =
|
|
1668
|
-
for (let column = 0; column < columns; column += 1) {
|
|
1585
|
+
const row = Array.from({ length: columns }, (_, column) => {
|
|
1669
1586
|
const source = trimSource(cells[column] ?? {
|
|
1670
1587
|
text: "",
|
|
1671
1588
|
segments: []
|
|
1672
1589
|
});
|
|
1673
|
-
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
index += 1;
|
|
1590
|
+
return coalesceText(scanInlineSource(source, 0, source.text.length, spans), spans);
|
|
1591
|
+
});
|
|
1592
|
+
entries.push([index, row]);
|
|
1677
1593
|
}
|
|
1594
|
+
const index = entries.length === 0 ? start + 2 : (entries.at(-1)?.[0] ?? start + 1) + 1;
|
|
1678
1595
|
const node = {
|
|
1679
1596
|
element: "table",
|
|
1680
1597
|
header,
|
|
1681
|
-
rows,
|
|
1598
|
+
rows: entries.map(([, row]) => row),
|
|
1682
1599
|
align: padded
|
|
1683
1600
|
};
|
|
1684
1601
|
const source = joinSources(lines.slice(start, index), "\n");
|
|
@@ -1713,23 +1630,20 @@ function collectList(lines, start, depth, spans = /* @__PURE__ */ new Map(), end
|
|
|
1713
1630
|
const topIndent = first?.indent ?? 0;
|
|
1714
1631
|
const items = [];
|
|
1715
1632
|
const chain = [];
|
|
1716
|
-
|
|
1717
|
-
for (let cursor = start; cursor < lines.length; cursor += 1) {
|
|
1633
|
+
const nested = walkMarkdownLines(lines, start).every((cursor) => {
|
|
1718
1634
|
const parsed = extractListItem(text[cursor] ?? "");
|
|
1719
1635
|
const previous = chain[chain.length - 1];
|
|
1720
|
-
if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker))
|
|
1721
|
-
nested = false;
|
|
1722
|
-
break;
|
|
1723
|
-
}
|
|
1636
|
+
if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker)) return false;
|
|
1724
1637
|
chain.push(parsed);
|
|
1725
|
-
|
|
1638
|
+
return true;
|
|
1639
|
+
});
|
|
1726
1640
|
const remaining = 64 - depth;
|
|
1727
1641
|
if (nested && remaining > 0 && chain.length > remaining) {
|
|
1728
1642
|
const terminal = chain[remaining - 1];
|
|
1729
1643
|
const terminalLine = lines[start + remaining - 1];
|
|
1730
1644
|
if (terminal !== void 0 && terminalLine !== void 0) {
|
|
1731
1645
|
const sources = [sliceSource(terminalLine, terminal.marker, terminalLine.text.length)];
|
|
1732
|
-
for (
|
|
1646
|
+
for (const cursor of walkMarkdownLines(lines, start + remaining)) {
|
|
1733
1647
|
const line = lines[cursor];
|
|
1734
1648
|
if (line !== void 0) sources.push(sliceSource(line, terminal.marker, line.text.length));
|
|
1735
1649
|
}
|
|
@@ -1747,16 +1661,16 @@ function collectList(lines, start, depth, spans = /* @__PURE__ */ new Map(), end
|
|
|
1747
1661
|
spans.set(textNode, residualSpan);
|
|
1748
1662
|
spans.set(paragraph, residualSpan);
|
|
1749
1663
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1664
|
+
const node = Array.from({ length: Math.ceil(remaining) }, (_, step) => step).reduce((child, step) => {
|
|
1665
|
+
const cursor = remaining - 1 - step;
|
|
1666
|
+
const children = child === void 0 ? [paragraph] : [child];
|
|
1753
1667
|
const parsed = chain[cursor];
|
|
1754
|
-
if (parsed === void 0)
|
|
1668
|
+
if (parsed === void 0) return child;
|
|
1755
1669
|
const item = {
|
|
1756
1670
|
element: "listItem",
|
|
1757
1671
|
children
|
|
1758
1672
|
};
|
|
1759
|
-
|
|
1673
|
+
const parent = {
|
|
1760
1674
|
element: "list",
|
|
1761
1675
|
ordered: parsed.ordered,
|
|
1762
1676
|
start: parsed.start,
|
|
@@ -1766,72 +1680,78 @@ function collectList(lines, start, depth, spans = /* @__PURE__ */ new Map(), end
|
|
|
1766
1680
|
const span = projectSpan(region, 0, region.text.length);
|
|
1767
1681
|
if (span !== void 0) {
|
|
1768
1682
|
spans.set(item, span);
|
|
1769
|
-
spans.set(
|
|
1683
|
+
spans.set(parent, span);
|
|
1770
1684
|
}
|
|
1771
|
-
|
|
1772
|
-
}
|
|
1685
|
+
return parent;
|
|
1686
|
+
}, void 0);
|
|
1773
1687
|
if (node !== void 0) return {
|
|
1774
1688
|
node,
|
|
1775
1689
|
next: lines.length
|
|
1776
1690
|
};
|
|
1777
1691
|
}
|
|
1778
1692
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1693
|
+
const positions = walkMarkdownLines(lines, start);
|
|
1694
|
+
const finish = positions.reduce((index, position) => {
|
|
1695
|
+
if (position < index) return index;
|
|
1781
1696
|
const parsed = extractListItem(text[index] ?? "");
|
|
1782
|
-
if (!parsed || parsed.indent > topIndent || parsed.ordered !== ordered)
|
|
1697
|
+
if (!parsed || parsed.indent > topIndent || parsed.ordered !== ordered) {
|
|
1698
|
+
positions.return(void 0);
|
|
1699
|
+
return index;
|
|
1700
|
+
}
|
|
1783
1701
|
const itemStart = index;
|
|
1784
1702
|
const itemLine = lines[index];
|
|
1785
|
-
if (itemLine === void 0)
|
|
1786
|
-
|
|
1703
|
+
if (itemLine === void 0) {
|
|
1704
|
+
positions.return(void 0);
|
|
1705
|
+
return index;
|
|
1706
|
+
}
|
|
1707
|
+
const entries = [[index, sliceSource(itemLine, parsed.marker, itemLine.text.length)]];
|
|
1787
1708
|
const continuation = parsed.marker;
|
|
1788
|
-
index
|
|
1789
|
-
|
|
1790
|
-
const nextSource = lines[index];
|
|
1709
|
+
for (const cursor of walkMarkdownLines(lines, index + 1)) {
|
|
1710
|
+
const nextSource = lines[cursor];
|
|
1791
1711
|
if (nextSource === void 0) break;
|
|
1792
1712
|
const next = nextSource.text;
|
|
1793
1713
|
if (isBlankLine(next)) {
|
|
1794
|
-
const after = lines[
|
|
1795
|
-
if (
|
|
1796
|
-
|
|
1797
|
-
index += 1;
|
|
1714
|
+
const after = lines[cursor + 1]?.text ?? "";
|
|
1715
|
+
if (cursor + 1 < lines.length && !isBlankLine(after) && countIndent(after) >= continuation) {
|
|
1716
|
+
entries.push([cursor, sliceSource(nextSource, 0, 0)]);
|
|
1798
1717
|
continue;
|
|
1799
1718
|
}
|
|
1800
1719
|
break;
|
|
1801
1720
|
}
|
|
1802
1721
|
if (countIndent(next) >= continuation) {
|
|
1803
|
-
|
|
1804
|
-
index += 1;
|
|
1722
|
+
entries.push([cursor, sliceSource(nextSource, continuation, next.length)]);
|
|
1805
1723
|
continue;
|
|
1806
1724
|
}
|
|
1807
|
-
if (extractListItem(next) || startsBlock(text,
|
|
1808
|
-
|
|
1809
|
-
index += 1;
|
|
1725
|
+
if (extractListItem(next) || startsBlock(text, cursor)) break;
|
|
1726
|
+
entries.push([cursor, trimSource(nextSource)]);
|
|
1810
1727
|
}
|
|
1728
|
+
const next = (entries.at(-1)?.[0] ?? index) + 1;
|
|
1729
|
+
const itemLines = entries.map(([, line]) => line);
|
|
1811
1730
|
const tail = itemLines[itemLines.length - 1];
|
|
1812
1731
|
const segment = tail?.segments[tail.segments.length - 1];
|
|
1813
|
-
const itemEnd =
|
|
1732
|
+
const itemEnd = next === lines.length && end !== void 0 ? end : segment?.end;
|
|
1814
1733
|
const item = {
|
|
1815
1734
|
element: "listItem",
|
|
1816
1735
|
children: parseBlocks(itemLines, depth + 1, spans, itemEnd)
|
|
1817
1736
|
};
|
|
1818
|
-
const source = joinSources(lines.slice(itemStart,
|
|
1737
|
+
const source = joinSources(lines.slice(itemStart, next), "\n");
|
|
1819
1738
|
const span = projectSpan(source, 0, source.text.length);
|
|
1820
1739
|
if (span !== void 0) spans.set(item, span);
|
|
1821
1740
|
items.push(item);
|
|
1822
|
-
|
|
1741
|
+
return next;
|
|
1742
|
+
}, start);
|
|
1823
1743
|
const node = {
|
|
1824
1744
|
element: "list",
|
|
1825
1745
|
ordered,
|
|
1826
1746
|
start: startOrdinal,
|
|
1827
1747
|
items
|
|
1828
1748
|
};
|
|
1829
|
-
const source = joinSources(lines.slice(start,
|
|
1749
|
+
const source = joinSources(lines.slice(start, finish), "\n");
|
|
1830
1750
|
const span = projectSpan(source, 0, source.text.length);
|
|
1831
1751
|
if (span !== void 0) spans.set(node, span);
|
|
1832
1752
|
return {
|
|
1833
1753
|
node,
|
|
1834
|
-
next:
|
|
1754
|
+
next: finish
|
|
1835
1755
|
};
|
|
1836
1756
|
}
|
|
1837
1757
|
/**
|
|
@@ -1874,7 +1794,7 @@ function markdownToHTML(node) {
|
|
|
1874
1794
|
continue;
|
|
1875
1795
|
}
|
|
1876
1796
|
const children = [];
|
|
1877
|
-
|
|
1797
|
+
const depth = frame.depth + (current.element === "document" ? 0 : current.element === "table" ? 4 : 1);
|
|
1878
1798
|
switch (current.element) {
|
|
1879
1799
|
case "document":
|
|
1880
1800
|
for (const child of current.children) if (child !== void 0) children.push(child);
|
|
@@ -1883,24 +1803,20 @@ function markdownToHTML(node) {
|
|
|
1883
1803
|
case "paragraph":
|
|
1884
1804
|
case "blockquote":
|
|
1885
1805
|
for (const child of current.children) if (child !== void 0) children.push(child);
|
|
1886
|
-
depth += 1;
|
|
1887
1806
|
break;
|
|
1888
1807
|
case "listItem": {
|
|
1889
1808
|
const only = current.children[0];
|
|
1890
1809
|
if (current.children.length === 1 && only !== void 0 && only.element === "paragraph") {
|
|
1891
1810
|
for (const child of only.children) if (child !== void 0) children.push(child);
|
|
1892
1811
|
} else for (const child of current.children) if (child !== void 0) children.push(child);
|
|
1893
|
-
depth += 1;
|
|
1894
1812
|
break;
|
|
1895
1813
|
}
|
|
1896
1814
|
case "emphasis":
|
|
1897
1815
|
case "link":
|
|
1898
1816
|
for (const child of current.children) if (child !== void 0) children.push(child);
|
|
1899
|
-
depth += 1;
|
|
1900
1817
|
break;
|
|
1901
1818
|
case "list":
|
|
1902
1819
|
for (const child of current.items) if (child !== void 0) children.push(child);
|
|
1903
|
-
depth += 1;
|
|
1904
1820
|
break;
|
|
1905
1821
|
case "table":
|
|
1906
1822
|
if (frame.depth + 4 > 64) {
|
|
@@ -1915,7 +1831,6 @@ function markdownToHTML(node) {
|
|
|
1915
1831
|
for (const child of cell) if (child !== void 0) children.push(child);
|
|
1916
1832
|
}
|
|
1917
1833
|
}
|
|
1918
|
-
depth += 4;
|
|
1919
1834
|
}
|
|
1920
1835
|
if (current.element === "codeBlock" && frame.depth + 2 > 64) {
|
|
1921
1836
|
values.push(void 0);
|
|
@@ -1926,7 +1841,7 @@ function markdownToHTML(node) {
|
|
|
1926
1841
|
expanded: true,
|
|
1927
1842
|
count: children.length
|
|
1928
1843
|
});
|
|
1929
|
-
for (
|
|
1844
|
+
for (const index of children.keys().toArray().toReversed()) {
|
|
1930
1845
|
const child = children[index];
|
|
1931
1846
|
if (child !== void 0) stack.push({
|
|
1932
1847
|
node: child,
|
|
@@ -1937,231 +1852,207 @@ function markdownToHTML(node) {
|
|
|
1937
1852
|
}
|
|
1938
1853
|
continue;
|
|
1939
1854
|
}
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
1855
|
+
const value = projectMarkdownNode(current, frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count));
|
|
1856
|
+
values.push(value);
|
|
1857
|
+
}
|
|
1858
|
+
const projected = values[0];
|
|
1859
|
+
if (projected?.category === "document") return projected;
|
|
1860
|
+
return {
|
|
1861
|
+
category: "document",
|
|
1862
|
+
children: projected === void 0 ? [] : [projected]
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Projects one markdown node from its already projected children.
|
|
1867
|
+
*
|
|
1868
|
+
* @example
|
|
1869
|
+
* ```ts
|
|
1870
|
+
* projectMarkdownNode({ element: 'text', value: 'hi' }, []) // { category: 'text', value: 'hi' }
|
|
1871
|
+
* ```
|
|
1872
|
+
*/
|
|
1873
|
+
function projectMarkdownNode(current, children) {
|
|
1874
|
+
const projected = [];
|
|
1875
|
+
for (const child of children) if (child !== void 0) projected.push(child);
|
|
1876
|
+
switch (current.element) {
|
|
1877
|
+
case "document": return {
|
|
1878
|
+
category: "document",
|
|
1879
|
+
children: projected
|
|
1880
|
+
};
|
|
1881
|
+
case "heading": return {
|
|
1882
|
+
category: "element",
|
|
1883
|
+
name: `h${current.level}`,
|
|
1884
|
+
attributes: [],
|
|
1885
|
+
children: projected
|
|
1886
|
+
};
|
|
1887
|
+
case "paragraph": return {
|
|
1888
|
+
category: "element",
|
|
1889
|
+
name: "p",
|
|
1890
|
+
attributes: [],
|
|
1891
|
+
children: projected
|
|
1892
|
+
};
|
|
1893
|
+
case "thematicBreak": return {
|
|
1894
|
+
category: "element",
|
|
1895
|
+
name: "hr",
|
|
1896
|
+
attributes: [],
|
|
1897
|
+
children: []
|
|
1898
|
+
};
|
|
1899
|
+
case "blockquote": return {
|
|
1900
|
+
category: "element",
|
|
1901
|
+
name: "blockquote",
|
|
1902
|
+
attributes: [],
|
|
1903
|
+
children: projected
|
|
1904
|
+
};
|
|
1905
|
+
case "codeBlock": return {
|
|
1906
|
+
category: "element",
|
|
1907
|
+
name: "pre",
|
|
1908
|
+
attributes: [],
|
|
1909
|
+
children: [{
|
|
1910
|
+
category: "element",
|
|
1911
|
+
name: "code",
|
|
1912
|
+
attributes: current.lang === void 0 ? [] : [{
|
|
1913
|
+
name: "class",
|
|
1914
|
+
value: `language-${current.lang}`
|
|
1915
|
+
}],
|
|
1916
|
+
children: [{
|
|
1917
|
+
category: "text",
|
|
1918
|
+
value: current.code
|
|
1919
|
+
}]
|
|
1920
|
+
}]
|
|
1921
|
+
};
|
|
1922
|
+
case "list": return {
|
|
1923
|
+
category: "element",
|
|
1924
|
+
name: current.ordered ? "ol" : "ul",
|
|
1925
|
+
attributes: current.ordered && current.start !== 1 ? [{
|
|
1926
|
+
name: "start",
|
|
1927
|
+
value: String(current.start)
|
|
1928
|
+
}] : [],
|
|
1929
|
+
children: projected
|
|
1930
|
+
};
|
|
1931
|
+
case "listItem": return {
|
|
1932
|
+
category: "element",
|
|
1933
|
+
name: "li",
|
|
1934
|
+
attributes: [],
|
|
1935
|
+
children: projected
|
|
1936
|
+
};
|
|
1937
|
+
case "table": {
|
|
1938
|
+
const outputs = children.values();
|
|
1939
|
+
const header = [];
|
|
1940
|
+
for (const [column, cell] of current.header.entries()) {
|
|
1941
|
+
if (cell === void 0) continue;
|
|
1942
|
+
const align = current.align[column];
|
|
1943
|
+
const attributes = align === "left" || align === "right" || align === "center" ? [{
|
|
1944
|
+
name: "align",
|
|
1945
|
+
value: align
|
|
1946
|
+
}] : [];
|
|
1947
|
+
const count = Iterator.from(cell).filter((child) => child !== void 0).reduce((sum) => sum + 1, 0);
|
|
1948
|
+
const cellChildren = [];
|
|
1949
|
+
for (const child of outputs.take(count).toArray()) if (child !== void 0) cellChildren.push(child);
|
|
1950
|
+
header.push({
|
|
2015
1951
|
category: "element",
|
|
2016
|
-
name: "
|
|
2017
|
-
attributes
|
|
2018
|
-
children:
|
|
2019
|
-
};
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
const
|
|
2024
|
-
for (const [column, cell] of
|
|
1952
|
+
name: "th",
|
|
1953
|
+
attributes,
|
|
1954
|
+
children: cellChildren
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
const rows = [];
|
|
1958
|
+
for (const row of current.rows) {
|
|
1959
|
+
const cells = [];
|
|
1960
|
+
for (const [column, cell] of row.entries()) {
|
|
2025
1961
|
if (cell === void 0) continue;
|
|
2026
1962
|
const align = current.align[column];
|
|
2027
1963
|
const attributes = align === "left" || align === "right" || align === "center" ? [{
|
|
2028
1964
|
name: "align",
|
|
2029
1965
|
value: align
|
|
2030
1966
|
}] : [];
|
|
2031
|
-
|
|
2032
|
-
for (const child of cell) if (child !== void 0) count += 1;
|
|
1967
|
+
const count = Iterator.from(cell).filter((child) => child !== void 0).reduce((sum) => sum + 1, 0);
|
|
2033
1968
|
const cellChildren = [];
|
|
2034
|
-
for (const child of
|
|
2035
|
-
|
|
1969
|
+
for (const child of outputs.take(count).toArray()) if (child !== void 0) cellChildren.push(child);
|
|
1970
|
+
cells.push({
|
|
2036
1971
|
category: "element",
|
|
2037
|
-
name: "
|
|
1972
|
+
name: "td",
|
|
2038
1973
|
attributes,
|
|
2039
1974
|
children: cellChildren
|
|
2040
1975
|
});
|
|
2041
|
-
offset += count;
|
|
2042
|
-
}
|
|
2043
|
-
const rows = [];
|
|
2044
|
-
for (const row of current.rows) {
|
|
2045
|
-
const cells = [];
|
|
2046
|
-
for (const [column, cell] of row.entries()) {
|
|
2047
|
-
if (cell === void 0) continue;
|
|
2048
|
-
const align = current.align[column];
|
|
2049
|
-
const attributes = align === "left" || align === "right" || align === "center" ? [{
|
|
2050
|
-
name: "align",
|
|
2051
|
-
value: align
|
|
2052
|
-
}] : [];
|
|
2053
|
-
let count = 0;
|
|
2054
|
-
for (const child of cell) if (child !== void 0) count += 1;
|
|
2055
|
-
const cellChildren = [];
|
|
2056
|
-
for (const child of children.slice(offset, offset + count)) if (child !== void 0) cellChildren.push(child);
|
|
2057
|
-
cells.push({
|
|
2058
|
-
category: "element",
|
|
2059
|
-
name: "td",
|
|
2060
|
-
attributes,
|
|
2061
|
-
children: cellChildren
|
|
2062
|
-
});
|
|
2063
|
-
offset += count;
|
|
2064
|
-
}
|
|
2065
|
-
rows.push({
|
|
2066
|
-
category: "element",
|
|
2067
|
-
name: "tr",
|
|
2068
|
-
attributes: [],
|
|
2069
|
-
children: cells
|
|
2070
|
-
});
|
|
2071
1976
|
}
|
|
2072
|
-
|
|
2073
|
-
category: "element",
|
|
2074
|
-
name: "thead",
|
|
2075
|
-
attributes: [],
|
|
2076
|
-
children: [{
|
|
2077
|
-
category: "element",
|
|
2078
|
-
name: "tr",
|
|
2079
|
-
attributes: [],
|
|
2080
|
-
children: header
|
|
2081
|
-
}]
|
|
2082
|
-
}];
|
|
2083
|
-
if ((0, _orkestrel_contract.isNonEmptyArray)(current.rows)) tableChildren.push({
|
|
1977
|
+
rows.push({
|
|
2084
1978
|
category: "element",
|
|
2085
|
-
name: "
|
|
1979
|
+
name: "tr",
|
|
2086
1980
|
attributes: [],
|
|
2087
|
-
children:
|
|
1981
|
+
children: cells
|
|
2088
1982
|
});
|
|
2089
|
-
value = {
|
|
2090
|
-
category: "element",
|
|
2091
|
-
name: "table",
|
|
2092
|
-
attributes: [],
|
|
2093
|
-
children: tableChildren
|
|
2094
|
-
};
|
|
2095
|
-
break;
|
|
2096
1983
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
break;
|
|
2103
|
-
case "emphasis":
|
|
2104
|
-
value = {
|
|
2105
|
-
category: "element",
|
|
2106
|
-
name: current.strong ? "strong" : "em",
|
|
2107
|
-
attributes: [],
|
|
2108
|
-
children: projected
|
|
2109
|
-
};
|
|
2110
|
-
break;
|
|
2111
|
-
case "codeSpan":
|
|
2112
|
-
value = {
|
|
2113
|
-
category: "element",
|
|
2114
|
-
name: "code",
|
|
2115
|
-
attributes: [],
|
|
2116
|
-
children: [{
|
|
2117
|
-
category: "text",
|
|
2118
|
-
value: current.value
|
|
2119
|
-
}]
|
|
2120
|
-
};
|
|
2121
|
-
break;
|
|
2122
|
-
case "link":
|
|
2123
|
-
value = {
|
|
2124
|
-
category: "element",
|
|
2125
|
-
name: "a",
|
|
2126
|
-
attributes: [{
|
|
2127
|
-
name: "href",
|
|
2128
|
-
value: current.href
|
|
2129
|
-
}],
|
|
2130
|
-
children: projected
|
|
2131
|
-
};
|
|
2132
|
-
break;
|
|
2133
|
-
case "image":
|
|
2134
|
-
value = {
|
|
2135
|
-
category: "element",
|
|
2136
|
-
name: "img",
|
|
2137
|
-
attributes: [{
|
|
2138
|
-
name: "src",
|
|
2139
|
-
value: current.src
|
|
2140
|
-
}, {
|
|
2141
|
-
name: "alt",
|
|
2142
|
-
value: flattenText(current)
|
|
2143
|
-
}],
|
|
2144
|
-
children: []
|
|
2145
|
-
};
|
|
2146
|
-
break;
|
|
2147
|
-
case "break":
|
|
2148
|
-
value = {
|
|
1984
|
+
const tableChildren = [{
|
|
1985
|
+
category: "element",
|
|
1986
|
+
name: "thead",
|
|
1987
|
+
attributes: [],
|
|
1988
|
+
children: [{
|
|
2149
1989
|
category: "element",
|
|
2150
|
-
name: "
|
|
1990
|
+
name: "tr",
|
|
2151
1991
|
attributes: [],
|
|
2152
|
-
children:
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
|
|
1992
|
+
children: header
|
|
1993
|
+
}]
|
|
1994
|
+
}];
|
|
1995
|
+
if ((0, _orkestrel_contract.isNonEmptyArray)(current.rows)) tableChildren.push({
|
|
1996
|
+
category: "element",
|
|
1997
|
+
name: "tbody",
|
|
1998
|
+
attributes: [],
|
|
1999
|
+
children: rows
|
|
2000
|
+
});
|
|
2001
|
+
return {
|
|
2002
|
+
category: "element",
|
|
2003
|
+
name: "table",
|
|
2004
|
+
attributes: [],
|
|
2005
|
+
children: tableChildren
|
|
2006
|
+
};
|
|
2156
2007
|
}
|
|
2157
|
-
|
|
2008
|
+
case "text": return {
|
|
2009
|
+
category: "text",
|
|
2010
|
+
value: current.value
|
|
2011
|
+
};
|
|
2012
|
+
case "emphasis": return {
|
|
2013
|
+
category: "element",
|
|
2014
|
+
name: current.strong ? "strong" : "em",
|
|
2015
|
+
attributes: [],
|
|
2016
|
+
children: projected
|
|
2017
|
+
};
|
|
2018
|
+
case "codeSpan": return {
|
|
2019
|
+
category: "element",
|
|
2020
|
+
name: "code",
|
|
2021
|
+
attributes: [],
|
|
2022
|
+
children: [{
|
|
2023
|
+
category: "text",
|
|
2024
|
+
value: current.value
|
|
2025
|
+
}]
|
|
2026
|
+
};
|
|
2027
|
+
case "link": return {
|
|
2028
|
+
category: "element",
|
|
2029
|
+
name: "a",
|
|
2030
|
+
attributes: [{
|
|
2031
|
+
name: "href",
|
|
2032
|
+
value: current.href
|
|
2033
|
+
}],
|
|
2034
|
+
children: projected
|
|
2035
|
+
};
|
|
2036
|
+
case "image": return {
|
|
2037
|
+
category: "element",
|
|
2038
|
+
name: "img",
|
|
2039
|
+
attributes: [{
|
|
2040
|
+
name: "src",
|
|
2041
|
+
value: current.src
|
|
2042
|
+
}, {
|
|
2043
|
+
name: "alt",
|
|
2044
|
+
value: flattenText(current)
|
|
2045
|
+
}],
|
|
2046
|
+
children: []
|
|
2047
|
+
};
|
|
2048
|
+
case "break": return {
|
|
2049
|
+
category: "element",
|
|
2050
|
+
name: "br",
|
|
2051
|
+
attributes: [],
|
|
2052
|
+
children: []
|
|
2053
|
+
};
|
|
2054
|
+
default: return;
|
|
2158
2055
|
}
|
|
2159
|
-
const projected = values[0];
|
|
2160
|
-
if (projected?.category === "document") return projected;
|
|
2161
|
-
return {
|
|
2162
|
-
category: "document",
|
|
2163
|
-
children: projected === void 0 ? [] : [projected]
|
|
2164
|
-
};
|
|
2165
2056
|
}
|
|
2166
2057
|
/**
|
|
2167
2058
|
* Renders a {@link MarkdownNode} to its canonical markdown source — the inverse
|
|
@@ -2200,7 +2091,7 @@ function renderMarkdown(node) {
|
|
|
2200
2091
|
count: 0,
|
|
2201
2092
|
escaped: "",
|
|
2202
2093
|
escapeBang: false,
|
|
2203
|
-
|
|
2094
|
+
emphasis: 0
|
|
2204
2095
|
}];
|
|
2205
2096
|
const values = [];
|
|
2206
2097
|
while (stack.length > 0) {
|
|
@@ -2208,51 +2099,14 @@ function renderMarkdown(node) {
|
|
|
2208
2099
|
if (frame === void 0) continue;
|
|
2209
2100
|
const current = frame.node;
|
|
2210
2101
|
if (!frame.expanded) {
|
|
2211
|
-
|
|
2212
|
-
if ((frame.depth >= 64 || current.element === "text") && "value" in current && (0, _orkestrel_contract.isString)(current.value)) for (let index = 0; index < current.value.length; index += 1) {
|
|
2213
|
-
const character = current.value[index] ?? "";
|
|
2214
|
-
const atLineStart = index === 0 || current.value[index - 1] === "\n";
|
|
2215
|
-
if (current.element === "text" && character === "!" && index === current.value.length - 1 && frame.escapeBang) {
|
|
2216
|
-
escaped += "\\!";
|
|
2217
|
-
continue;
|
|
2218
|
-
}
|
|
2219
|
-
if (character === "\\" || character === "*" || character === "_" || character === "`" || character === "[" || character === "]") {
|
|
2220
|
-
escaped += `\\${character}`;
|
|
2221
|
-
continue;
|
|
2222
|
-
}
|
|
2223
|
-
if (atLineStart) {
|
|
2224
|
-
if (character === "#" || character === ">") {
|
|
2225
|
-
escaped += `\\${character}`;
|
|
2226
|
-
continue;
|
|
2227
|
-
}
|
|
2228
|
-
if ((character === "-" || character === "~") && current.value[index + 1] === character && current.value[index + 2] === character) {
|
|
2229
|
-
escaped += `\\${character}`;
|
|
2230
|
-
continue;
|
|
2231
|
-
}
|
|
2232
|
-
if ((character === "-" || character === "+") && (current.value[index + 1] ?? " ") === " ") {
|
|
2233
|
-
escaped += `\\${character}`;
|
|
2234
|
-
continue;
|
|
2235
|
-
}
|
|
2236
|
-
if (/[0-9]/.test(character)) {
|
|
2237
|
-
let end = index;
|
|
2238
|
-
while (end < current.value.length && /[0-9]/.test(current.value[end] ?? "")) end += 1;
|
|
2239
|
-
const marker = current.value[end];
|
|
2240
|
-
if ((marker === "." || marker === ")") && current.value[end + 1] === " ") {
|
|
2241
|
-
escaped += `${current.value.slice(index, end)}\\${marker}`;
|
|
2242
|
-
index = end;
|
|
2243
|
-
continue;
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
escaped += character;
|
|
2248
|
-
}
|
|
2102
|
+
const escaped = (frame.depth >= 64 || current.element === "text") && "value" in current && (0, _orkestrel_contract.isString)(current.value) ? escapeMarkdownText(current.value, current.element === "text" && frame.escapeBang) : "";
|
|
2249
2103
|
if (frame.depth >= 64) {
|
|
2250
2104
|
values.push(escaped);
|
|
2251
2105
|
continue;
|
|
2252
2106
|
}
|
|
2253
2107
|
const groups = [];
|
|
2254
2108
|
const adjacent = [];
|
|
2255
|
-
|
|
2109
|
+
const depth = frame.depth + (current.element === "table" ? 2 : 1);
|
|
2256
2110
|
switch (current.element) {
|
|
2257
2111
|
case "document":
|
|
2258
2112
|
case "blockquote":
|
|
@@ -2279,7 +2133,7 @@ function renderMarkdown(node) {
|
|
|
2279
2133
|
}
|
|
2280
2134
|
for (const row of current.rows) {
|
|
2281
2135
|
if (row === void 0) continue;
|
|
2282
|
-
for (
|
|
2136
|
+
for (const column of current.header.keys()) {
|
|
2283
2137
|
const cell = row[column];
|
|
2284
2138
|
if (cell !== void 0) {
|
|
2285
2139
|
groups.push(cell);
|
|
@@ -2287,26 +2141,17 @@ function renderMarkdown(node) {
|
|
|
2287
2141
|
}
|
|
2288
2142
|
}
|
|
2289
2143
|
}
|
|
2290
|
-
depth += 1;
|
|
2291
2144
|
}
|
|
2292
2145
|
const children = [];
|
|
2293
2146
|
const escapeBangs = [];
|
|
2294
|
-
for (
|
|
2147
|
+
for (const groupIndex of groups.keys()) {
|
|
2295
2148
|
const group = groups[groupIndex];
|
|
2296
2149
|
if (group === void 0) continue;
|
|
2297
|
-
|
|
2298
|
-
|
|
2150
|
+
const siblings = group.filter((child) => child !== void 0);
|
|
2151
|
+
for (const position of siblings.keys()) {
|
|
2152
|
+
const child = siblings[position];
|
|
2299
2153
|
if (child === void 0) continue;
|
|
2300
|
-
|
|
2301
|
-
if (adjacent[groupIndex] === true) {
|
|
2302
|
-
let nextPosition = position + 1;
|
|
2303
|
-
let next = group[nextPosition];
|
|
2304
|
-
while (next === void 0 && nextPosition < group.length) {
|
|
2305
|
-
nextPosition += 1;
|
|
2306
|
-
next = group[nextPosition];
|
|
2307
|
-
}
|
|
2308
|
-
escapeBang = next?.element === "link";
|
|
2309
|
-
}
|
|
2154
|
+
const escapeBang = (adjacent[groupIndex] === true ? siblings[position + 1] : void 0)?.element === "link";
|
|
2310
2155
|
children.push(child);
|
|
2311
2156
|
escapeBangs.push(escapeBang);
|
|
2312
2157
|
}
|
|
@@ -2317,8 +2162,8 @@ function renderMarkdown(node) {
|
|
|
2317
2162
|
count: children.length,
|
|
2318
2163
|
escaped
|
|
2319
2164
|
});
|
|
2320
|
-
const
|
|
2321
|
-
for (
|
|
2165
|
+
const emphasis = current.element === "emphasis" ? frame.emphasis + 1 : frame.emphasis;
|
|
2166
|
+
for (const index of children.keys().toArray().toReversed()) {
|
|
2322
2167
|
const child = children[index];
|
|
2323
2168
|
if (child !== void 0) stack.push({
|
|
2324
2169
|
node: child,
|
|
@@ -2327,136 +2172,112 @@ function renderMarkdown(node) {
|
|
|
2327
2172
|
count: 0,
|
|
2328
2173
|
escaped: "",
|
|
2329
2174
|
escapeBang: escapeBangs[index] === true && depth < 64,
|
|
2330
|
-
|
|
2175
|
+
emphasis
|
|
2331
2176
|
});
|
|
2332
2177
|
}
|
|
2333
2178
|
continue;
|
|
2334
2179
|
}
|
|
2335
|
-
const
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2180
|
+
const value = renderMarkdownNode(current, frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count), frame.escaped, frame.emphasis);
|
|
2181
|
+
if (stack.length === 0) return value;
|
|
2182
|
+
values.push(value);
|
|
2183
|
+
}
|
|
2184
|
+
return "";
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Renders one markdown node from its rendered children and escaped literal text.
|
|
2188
|
+
*
|
|
2189
|
+
* @example
|
|
2190
|
+
* ```ts
|
|
2191
|
+
* renderMarkdownNode({ element: 'thematicBreak' }, [], '', 0) // '---'
|
|
2192
|
+
* ```
|
|
2193
|
+
*/
|
|
2194
|
+
function renderMarkdownNode(current, children, literal, emphasis) {
|
|
2195
|
+
switch (current.element) {
|
|
2196
|
+
case "codeBlock":
|
|
2197
|
+
case "codeSpan": {
|
|
2198
|
+
const body = current.element === "codeBlock" ? current.code : current.value;
|
|
2199
|
+
const longest = Iterator.from(body.matchAll(/`+/g)).reduce((maximum, match) => Math.max(maximum, match[0].length), 0);
|
|
2200
|
+
const fence = "`".repeat(Math.max(current.element === "codeBlock" ? 3 : 1, longest + 1));
|
|
2201
|
+
if (current.element === "codeBlock") return `${fence}${current.lang === void 0 ? "" : current.lang}\n${current.code}\n${fence}`;
|
|
2202
|
+
const pad = current.value.startsWith("`") || current.value.endsWith("`") ? " " : "";
|
|
2203
|
+
return `${fence}${pad}${current.value}${pad}${fence}`;
|
|
2204
|
+
}
|
|
2205
|
+
case "break": return " \n";
|
|
2206
|
+
case "document": return children.join("\n\n");
|
|
2207
|
+
case "heading": {
|
|
2208
|
+
const escaped = children.join("").replace(/(^|[^\\])(#+)$/, (_match, before, hashes) => {
|
|
2209
|
+
return `${before}\\${hashes[0] ?? ""}${hashes.slice(1)}`;
|
|
2210
|
+
});
|
|
2211
|
+
return `${"#".repeat(current.level)} ${escaped}`;
|
|
2212
|
+
}
|
|
2213
|
+
case "paragraph": return children.join("");
|
|
2214
|
+
case "thematicBreak": return "---";
|
|
2215
|
+
case "blockquote": return children.join("\n\n").split("\n").map((line) => line === "" ? ">" : `> ${line}`).join("\n");
|
|
2216
|
+
case "list": {
|
|
2217
|
+
const items = [];
|
|
2218
|
+
children.entries().reduce((ordinal, [position, body]) => {
|
|
2219
|
+
const marker = current.ordered ? `${ordinal}. ` : "- ";
|
|
2220
|
+
const pad = " ".repeat(marker.length);
|
|
2221
|
+
if (current.items[position]?.children[0]?.element === "table") {
|
|
2222
|
+
items.push(`${marker}\n${body.split("\n").map((line) => pad + line).join("\n")}`);
|
|
2223
|
+
return ordinal + 1;
|
|
2351
2224
|
}
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
});
|
|
2366
|
-
value = `${"#".repeat(current.level)} ${escaped}`;
|
|
2367
|
-
break;
|
|
2368
|
-
}
|
|
2369
|
-
case "paragraph":
|
|
2370
|
-
value = children.join("");
|
|
2371
|
-
break;
|
|
2372
|
-
case "thematicBreak":
|
|
2373
|
-
value = "---";
|
|
2374
|
-
break;
|
|
2375
|
-
case "blockquote":
|
|
2376
|
-
value = children.join("\n\n").split("\n").map((line) => line === "" ? ">" : `> ${line}`).join("\n");
|
|
2377
|
-
break;
|
|
2378
|
-
case "list": {
|
|
2379
|
-
const items = [];
|
|
2380
|
-
let ordinal = current.start;
|
|
2381
|
-
for (const [position, body] of children.entries()) {
|
|
2382
|
-
const marker = current.ordered ? `${ordinal}. ` : "- ";
|
|
2383
|
-
ordinal += 1;
|
|
2384
|
-
const pad = " ".repeat(marker.length);
|
|
2385
|
-
if (current.items[position]?.children[0]?.element === "table") {
|
|
2386
|
-
items.push(`${marker}\n${body.split("\n").map((line) => pad + line).join("\n")}`);
|
|
2387
|
-
continue;
|
|
2388
|
-
}
|
|
2389
|
-
items.push(body.split("\n").map((line, index) => index === 0 ? marker + line : line === "" ? "" : pad + line).join("\n"));
|
|
2225
|
+
items.push(body.split("\n").map((line, index) => index === 0 ? marker + line : line === "" ? "" : pad + line).join("\n"));
|
|
2226
|
+
return ordinal + 1;
|
|
2227
|
+
}, current.start);
|
|
2228
|
+
return items.join("\n");
|
|
2229
|
+
}
|
|
2230
|
+
case "listItem": return children.join("\n\n");
|
|
2231
|
+
case "table": {
|
|
2232
|
+
const outputs = children.values();
|
|
2233
|
+
const header = [];
|
|
2234
|
+
for (const cell of current.header) {
|
|
2235
|
+
if (cell === void 0) {
|
|
2236
|
+
header.push("");
|
|
2237
|
+
continue;
|
|
2390
2238
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2239
|
+
const count = Iterator.from(cell).filter((child) => child !== void 0).reduce((sum) => sum + 1, 0);
|
|
2240
|
+
header.push(outputs.take(count).toArray().join("").replace(/\|/g, "\\|"));
|
|
2393
2241
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2242
|
+
const delimiter = current.align.map((align) => {
|
|
2243
|
+
if (align === null) return "---";
|
|
2244
|
+
if (align === "left") return ":---";
|
|
2245
|
+
if (align === "right") return "---:";
|
|
2246
|
+
if (align === "center") return ":---:";
|
|
2247
|
+
return "---";
|
|
2248
|
+
});
|
|
2249
|
+
const rows = [];
|
|
2250
|
+
for (const row of current.rows) {
|
|
2251
|
+
const cells = [];
|
|
2252
|
+
for (const column of current.header.keys()) {
|
|
2253
|
+
const cell = row[column];
|
|
2401
2254
|
if (cell === void 0) {
|
|
2402
|
-
|
|
2255
|
+
cells.push("");
|
|
2403
2256
|
continue;
|
|
2404
2257
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
header.push(children.slice(offset, offset + count).join("").replace(/\|/g, "\\|"));
|
|
2408
|
-
offset += count;
|
|
2409
|
-
}
|
|
2410
|
-
const delimiter = current.align.map((align) => {
|
|
2411
|
-
if (align === null) return "---";
|
|
2412
|
-
if (align === "left") return ":---";
|
|
2413
|
-
if (align === "right") return "---:";
|
|
2414
|
-
if (align === "center") return ":---:";
|
|
2415
|
-
return "---";
|
|
2416
|
-
});
|
|
2417
|
-
const rows = [];
|
|
2418
|
-
for (const row of current.rows) {
|
|
2419
|
-
const cells = [];
|
|
2420
|
-
for (let column = 0; column < current.header.length; column += 1) {
|
|
2421
|
-
const cell = row[column];
|
|
2422
|
-
if (cell === void 0) {
|
|
2423
|
-
cells.push("");
|
|
2424
|
-
continue;
|
|
2425
|
-
}
|
|
2426
|
-
let count = 0;
|
|
2427
|
-
for (const child of cell) if (child !== void 0) count += 1;
|
|
2428
|
-
cells.push(children.slice(offset, offset + count).join("").replace(/\|/g, "\\|"));
|
|
2429
|
-
offset += count;
|
|
2430
|
-
}
|
|
2431
|
-
rows.push(`| ${cells.join(" | ")} |`);
|
|
2258
|
+
const count = Iterator.from(cell).filter((child) => child !== void 0).reduce((sum) => sum + 1, 0);
|
|
2259
|
+
cells.push(outputs.take(count).toArray().join("").replace(/\|/g, "\\|"));
|
|
2432
2260
|
}
|
|
2433
|
-
|
|
2434
|
-
`| ${header.join(" | ")} |`,
|
|
2435
|
-
`| ${delimiter.join(" | ")} |`,
|
|
2436
|
-
...rows
|
|
2437
|
-
].join("\n");
|
|
2438
|
-
break;
|
|
2439
|
-
}
|
|
2440
|
-
case "text":
|
|
2441
|
-
value = frame.escaped;
|
|
2442
|
-
break;
|
|
2443
|
-
case "emphasis": {
|
|
2444
|
-
const marker = frame.nesting % 2 === 0 ? current.strong ? "**" : "*" : current.strong ? "__" : "_";
|
|
2445
|
-
value = `${marker}${children.join("")}${marker}`;
|
|
2446
|
-
break;
|
|
2261
|
+
rows.push(`| ${cells.join(" | ")} |`);
|
|
2447
2262
|
}
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
}
|
|
2454
|
-
default: value = "";
|
|
2263
|
+
return [
|
|
2264
|
+
`| ${header.join(" | ")} |`,
|
|
2265
|
+
`| ${delimiter.join(" | ")} |`,
|
|
2266
|
+
...rows
|
|
2267
|
+
].join("\n");
|
|
2455
2268
|
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2269
|
+
case "text": return literal;
|
|
2270
|
+
case "emphasis": {
|
|
2271
|
+
const marker = emphasis % 2 === 0 ? current.strong ? "**" : "*" : current.strong ? "__" : "_";
|
|
2272
|
+
return `${marker}${children.join("")}${marker}`;
|
|
2273
|
+
}
|
|
2274
|
+
case "link":
|
|
2275
|
+
case "image": {
|
|
2276
|
+
const escaped = (current.element === "link" ? current.href : current.src).replace(/[\\()]/g, (character) => `\\${character}`);
|
|
2277
|
+
return `${current.element === "image" ? "!" : ""}[${children.join("")}](${escaped})`;
|
|
2278
|
+
}
|
|
2279
|
+
default: return "";
|
|
2458
2280
|
}
|
|
2459
|
-
return "";
|
|
2460
2281
|
}
|
|
2461
2282
|
/**
|
|
2462
2283
|
* Builds an HTML-to-markdown projection with absent fields defaulted from
|
|
@@ -2507,27 +2328,7 @@ function createProjection(parts = {}) {
|
|
|
2507
2328
|
* ```
|
|
2508
2329
|
*/
|
|
2509
2330
|
function trimInlines(nodes) {
|
|
2510
|
-
|
|
2511
|
-
for (const node of nodes) if (node !== void 0) out.push(node);
|
|
2512
|
-
const first = out[0];
|
|
2513
|
-
if (first !== void 0 && first.element === "text") {
|
|
2514
|
-
const value = first.value.replace(/^\s+/, "");
|
|
2515
|
-
if ((0, _orkestrel_contract.isEmptyString)(value)) out.shift();
|
|
2516
|
-
else out[0] = {
|
|
2517
|
-
element: "text",
|
|
2518
|
-
value
|
|
2519
|
-
};
|
|
2520
|
-
}
|
|
2521
|
-
const last = out[out.length - 1];
|
|
2522
|
-
if (last !== void 0 && last.element === "text") {
|
|
2523
|
-
const value = last.value.replace(/\s+$/, "");
|
|
2524
|
-
if ((0, _orkestrel_contract.isEmptyString)(value)) out.pop();
|
|
2525
|
-
else out[out.length - 1] = {
|
|
2526
|
-
element: "text",
|
|
2527
|
-
value
|
|
2528
|
-
};
|
|
2529
|
-
}
|
|
2530
|
-
return out;
|
|
2331
|
+
return trimTrailing(trimLeading(nodes));
|
|
2531
2332
|
}
|
|
2532
2333
|
/**
|
|
2533
2334
|
* Reduces an inline run to the shape markdown can actually write back: adjacent text
|
|
@@ -2554,45 +2355,18 @@ function trimInlines(nodes) {
|
|
|
2554
2355
|
* ```
|
|
2555
2356
|
*/
|
|
2556
2357
|
function normalizeInlines(nodes, breaks) {
|
|
2557
|
-
const spent =
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
const previous = out[out.length - 1];
|
|
2570
|
-
if (node.element === "text") {
|
|
2571
|
-
const value = previous?.element === "break" ? node.value.replace(/^\s+/, "") : node.value;
|
|
2572
|
-
if (!(0, _orkestrel_contract.isEmptyString)(value)) out.push({
|
|
2573
|
-
element: "text",
|
|
2574
|
-
value
|
|
2575
|
-
});
|
|
2576
|
-
continue;
|
|
2577
|
-
}
|
|
2578
|
-
if (node.element === "break") {
|
|
2579
|
-
if (previous === void 0 || previous.element === "break") continue;
|
|
2580
|
-
if (previous.element === "text") {
|
|
2581
|
-
const value = previous.value.replace(/\s+$/, "");
|
|
2582
|
-
if ((0, _orkestrel_contract.isEmptyString)(value)) out.pop();
|
|
2583
|
-
else out[out.length - 1] = {
|
|
2584
|
-
element: "text",
|
|
2585
|
-
value
|
|
2586
|
-
};
|
|
2587
|
-
}
|
|
2588
|
-
if (out.length === 0) continue;
|
|
2589
|
-
out.push(node);
|
|
2590
|
-
continue;
|
|
2591
|
-
}
|
|
2592
|
-
out.push(node);
|
|
2593
|
-
}
|
|
2594
|
-
while (out.length > 0 && out[out.length - 1]?.element === "break") out.pop();
|
|
2595
|
-
return coalesceText(out);
|
|
2358
|
+
const spent = coalesceText(Iterator.from(nodes).filter((node) => node !== void 0).map((node) => node.element === "break" && !breaks ? {
|
|
2359
|
+
element: "text",
|
|
2360
|
+
value: " "
|
|
2361
|
+
} : node).toArray());
|
|
2362
|
+
const starts = [0, ...spent.entries().flatMap(([index, node]) => node.element === "break" ? [index + 1] : [])];
|
|
2363
|
+
const segments = starts.map((start, index) => {
|
|
2364
|
+
const end = starts[index + 1];
|
|
2365
|
+
const segment = spent.slice(start, end === void 0 ? spent.length : end - 1).filter((node) => node.element !== "text" || !(0, _orkestrel_contract.isEmptyString)(node.value));
|
|
2366
|
+
return index < starts.length - 1 ? trimTrailing(segment) : segment;
|
|
2367
|
+
});
|
|
2368
|
+
const first = segments.findIndex((segment) => segment.length > 0);
|
|
2369
|
+
return coalesceText(segments.map((segment, index) => index > first ? trimLeading(segment) : segment).filter((segment) => segment.length > 0).flatMap((segment, index) => index === 0 ? segment : [{ element: "break" }, ...segment]));
|
|
2596
2370
|
}
|
|
2597
2371
|
/**
|
|
2598
2372
|
* Combines the projections of one node's children into the projection of that node —
|
|
@@ -2622,63 +2396,72 @@ function normalizeInlines(nodes, breaks) {
|
|
|
2622
2396
|
*/
|
|
2623
2397
|
function mergeProjections(children) {
|
|
2624
2398
|
const blocks = [];
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2399
|
+
const text = [];
|
|
2400
|
+
const [tail, remainingCells, remainingRows] = Iterator.from(children).reduce(([pending, cells, rows], child) => {
|
|
2401
|
+
if (child === void 0) return [
|
|
2402
|
+
pending,
|
|
2403
|
+
cells,
|
|
2404
|
+
rows
|
|
2405
|
+
];
|
|
2406
|
+
text.push(child.text);
|
|
2632
2407
|
if ((0, _orkestrel_contract.isNonEmptyArray)(child.blocks)) {
|
|
2633
2408
|
const flushed = trimInlines(normalizeInlines(pending, true));
|
|
2634
2409
|
if ((0, _orkestrel_contract.isNonEmptyArray)(flushed)) blocks.push({
|
|
2635
2410
|
element: "paragraph",
|
|
2636
2411
|
children: flushed
|
|
2637
2412
|
});
|
|
2638
|
-
pending = [];
|
|
2639
2413
|
for (const row of rows) for (const cell of row) if (cell !== void 0 && (0, _orkestrel_contract.isNonEmptyArray)(cell.inlines)) blocks.push({
|
|
2640
2414
|
element: "paragraph",
|
|
2641
2415
|
children: cell.inlines
|
|
2642
2416
|
});
|
|
2643
|
-
rows.length = 0;
|
|
2644
2417
|
for (const cell of cells) if (cell !== void 0 && (0, _orkestrel_contract.isNonEmptyArray)(cell.inlines)) blocks.push({
|
|
2645
2418
|
element: "paragraph",
|
|
2646
2419
|
children: cell.inlines
|
|
2647
2420
|
});
|
|
2648
|
-
cells.length = 0;
|
|
2649
2421
|
for (const block of projectionToBlocks(child)) blocks.push(block);
|
|
2650
|
-
|
|
2422
|
+
return [
|
|
2423
|
+
[],
|
|
2424
|
+
[],
|
|
2425
|
+
[]
|
|
2426
|
+
];
|
|
2651
2427
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
cells.length = 0;
|
|
2656
|
-
}
|
|
2428
|
+
const rowed = (0, _orkestrel_contract.isNonEmptyArray)(child.rows);
|
|
2429
|
+
if (rowed) {
|
|
2430
|
+
if ((0, _orkestrel_contract.isNonEmptyArray)(cells)) rows.push(cells);
|
|
2657
2431
|
for (const row of child.rows) if (row !== void 0) rows.push(row);
|
|
2658
2432
|
}
|
|
2659
|
-
|
|
2433
|
+
const nextCells = rowed ? [] : cells;
|
|
2434
|
+
for (const cell of child.cells) if (cell !== void 0) nextCells.push(cell);
|
|
2660
2435
|
for (const inline of child.inlines) if (inline !== void 0) pending.push(inline);
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2436
|
+
return [
|
|
2437
|
+
pending,
|
|
2438
|
+
nextCells,
|
|
2439
|
+
rows
|
|
2440
|
+
];
|
|
2441
|
+
}, [
|
|
2442
|
+
[],
|
|
2443
|
+
[],
|
|
2444
|
+
[]
|
|
2445
|
+
]);
|
|
2446
|
+
const rowed = (0, _orkestrel_contract.isNonEmptyArray)(remainingRows) && (0, _orkestrel_contract.isNonEmptyArray)(remainingCells);
|
|
2447
|
+
const finalRows = rowed ? [...remainingRows, [...remainingCells]] : remainingRows;
|
|
2448
|
+
const finalCells = rowed ? [] : remainingCells;
|
|
2666
2449
|
if (!(0, _orkestrel_contract.isNonEmptyArray)(blocks)) return createProjection({
|
|
2667
|
-
inlines: coalesceText(
|
|
2668
|
-
text,
|
|
2669
|
-
cells,
|
|
2670
|
-
rows
|
|
2450
|
+
inlines: coalesceText(tail),
|
|
2451
|
+
text: text.join(""),
|
|
2452
|
+
cells: finalCells,
|
|
2453
|
+
rows: finalRows
|
|
2671
2454
|
});
|
|
2672
|
-
const flushed = trimInlines(normalizeInlines(
|
|
2455
|
+
const flushed = trimInlines(normalizeInlines(tail, true));
|
|
2673
2456
|
if ((0, _orkestrel_contract.isNonEmptyArray)(flushed)) blocks.push({
|
|
2674
2457
|
element: "paragraph",
|
|
2675
2458
|
children: flushed
|
|
2676
2459
|
});
|
|
2677
2460
|
return createProjection({
|
|
2678
2461
|
blocks,
|
|
2679
|
-
text,
|
|
2680
|
-
cells,
|
|
2681
|
-
rows
|
|
2462
|
+
text: text.join(""),
|
|
2463
|
+
cells: finalCells,
|
|
2464
|
+
rows: finalRows
|
|
2682
2465
|
});
|
|
2683
2466
|
}
|
|
2684
2467
|
/**
|
|
@@ -2893,21 +2676,11 @@ function projectHTMLNode(node, children) {
|
|
|
2893
2676
|
});
|
|
2894
2677
|
}
|
|
2895
2678
|
case "pre": {
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
position = index;
|
|
2900
|
-
break;
|
|
2901
|
-
}
|
|
2902
|
-
const source = position === -1 ? void 0 : node.children[position];
|
|
2903
|
-
const projected = position === -1 ? void 0 : children[position];
|
|
2679
|
+
const position = node.children.entries().find(([, child]) => child?.category === "element")?.[0];
|
|
2680
|
+
const source = position === void 0 ? void 0 : node.children[position];
|
|
2681
|
+
const projected = position === void 0 ? void 0 : children[position];
|
|
2904
2682
|
if (source?.category === "element" && source.name === "code" && projected !== void 0) {
|
|
2905
|
-
|
|
2906
|
-
for (const token of ((0, _orkestrel_html.attributeOf)(source, "class") ?? "").split(/\s+/)) {
|
|
2907
|
-
if (!token.startsWith("language-") || token.length <= 9 || token.includes("`")) continue;
|
|
2908
|
-
lang = token.slice(9);
|
|
2909
|
-
break;
|
|
2910
|
-
}
|
|
2683
|
+
const lang = ((0, _orkestrel_html.attributeOf)(source, "class") ?? "").split(/\s+/).find((token) => token.startsWith("language-") && token.length > 9 && !token.includes("`"))?.slice(9);
|
|
2911
2684
|
return createProjection({
|
|
2912
2685
|
blocks: [{
|
|
2913
2686
|
element: "codeBlock",
|
|
@@ -3019,24 +2792,21 @@ function projectHTMLNode(node, children) {
|
|
|
3019
2792
|
if (source === void 0) continue;
|
|
3020
2793
|
if (source.index >= source.children.length) continue;
|
|
3021
2794
|
const child = source.children[source.index];
|
|
3022
|
-
|
|
3023
|
-
|
|
2795
|
+
sources.push({
|
|
2796
|
+
...source,
|
|
2797
|
+
index: source.index + 1,
|
|
2798
|
+
direct: child?.category === "element" ? child.name === "th" || child.name === "td" : source.direct
|
|
2799
|
+
});
|
|
3024
2800
|
if (child?.category !== "element") continue;
|
|
3025
2801
|
if (child.name === "th" || child.name === "td") {
|
|
3026
2802
|
if (!source.direct) {
|
|
3027
2803
|
headings.push(false);
|
|
3028
2804
|
rowed.push(false);
|
|
3029
2805
|
}
|
|
3030
|
-
source.direct = true;
|
|
3031
2806
|
continue;
|
|
3032
2807
|
}
|
|
3033
|
-
source.direct = false;
|
|
3034
2808
|
if (child.name === "tr") {
|
|
3035
|
-
|
|
3036
|
-
for (const cell of child.children) if (cell?.category === "element" && cell.name === "th") {
|
|
3037
|
-
heading = true;
|
|
3038
|
-
break;
|
|
3039
|
-
}
|
|
2809
|
+
const heading = Iterator.from(child.children).some((cell) => cell?.category === "element" && cell.name === "th");
|
|
3040
2810
|
headings.push(heading);
|
|
3041
2811
|
rowed.push(true);
|
|
3042
2812
|
continue;
|
|
@@ -3047,35 +2817,21 @@ function projectHTMLNode(node, children) {
|
|
|
3047
2817
|
direct: false
|
|
3048
2818
|
});
|
|
3049
2819
|
}
|
|
3050
|
-
|
|
3051
|
-
for (const [index, heading] of headings.entries()) {
|
|
3052
|
-
if (!heading) continue;
|
|
3053
|
-
position = index;
|
|
3054
|
-
break;
|
|
3055
|
-
}
|
|
3056
|
-
if (position === void 0) for (const [index, structural] of rowed.entries()) {
|
|
3057
|
-
if (!structural) continue;
|
|
3058
|
-
position = index;
|
|
3059
|
-
break;
|
|
3060
|
-
}
|
|
2820
|
+
const position = headings.entries().find(([, heading]) => heading)?.[0] ?? rowed.entries().find(([, structural]) => structural)?.[0];
|
|
3061
2821
|
const headerRow = position === void 0 ? void 0 : rows[position];
|
|
3062
2822
|
const columns = headerRow?.length ?? rows[0]?.length ?? 0;
|
|
3063
2823
|
if (columns === 0) return createProjection({
|
|
3064
2824
|
blocks: projectionToBlocks(merged),
|
|
3065
2825
|
text: merged.text
|
|
3066
2826
|
});
|
|
3067
|
-
const
|
|
3068
|
-
const
|
|
3069
|
-
|
|
3070
|
-
const cell = headerRow?.[column];
|
|
3071
|
-
header.push(cell?.inlines ?? []);
|
|
3072
|
-
align.push(cell?.align ?? null);
|
|
3073
|
-
}
|
|
2827
|
+
const headerCells = Array.from({ length: columns }, (_, column) => headerRow?.[column]);
|
|
2828
|
+
const header = headerCells.map((cell) => cell?.inlines ?? []);
|
|
2829
|
+
const align = headerCells.map((cell) => cell?.align ?? null);
|
|
3074
2830
|
const body = [];
|
|
3075
2831
|
for (const [index, row] of rows.entries()) {
|
|
3076
2832
|
if (row === void 0 || index === position) continue;
|
|
3077
2833
|
const cells = [];
|
|
3078
|
-
for (
|
|
2834
|
+
for (const column of header.keys()) cells.push(row[column]?.inlines ?? []);
|
|
3079
2835
|
body.push(cells);
|
|
3080
2836
|
}
|
|
3081
2837
|
return createProjection({
|
|
@@ -3176,32 +2932,8 @@ function* walkNodes(node) {
|
|
|
3176
2932
|
if (frame === void 0) continue;
|
|
3177
2933
|
yield frame.node;
|
|
3178
2934
|
if (frame.depth >= 64) continue;
|
|
3179
|
-
const children =
|
|
3180
|
-
|
|
3181
|
-
case "document":
|
|
3182
|
-
case "heading":
|
|
3183
|
-
case "paragraph":
|
|
3184
|
-
case "blockquote":
|
|
3185
|
-
case "listItem":
|
|
3186
|
-
case "emphasis":
|
|
3187
|
-
case "link":
|
|
3188
|
-
case "image":
|
|
3189
|
-
for (const child of frame.node.children) if (child !== void 0) children.push(child);
|
|
3190
|
-
break;
|
|
3191
|
-
case "list":
|
|
3192
|
-
for (const child of frame.node.items) if (child !== void 0) children.push(child);
|
|
3193
|
-
break;
|
|
3194
|
-
case "table":
|
|
3195
|
-
for (const cell of frame.node.header) if (cell !== void 0) {
|
|
3196
|
-
for (const child of cell) if (child !== void 0) children.push(child);
|
|
3197
|
-
}
|
|
3198
|
-
for (const row of frame.node.rows) if (row !== void 0) {
|
|
3199
|
-
for (const cell of row) if (cell !== void 0) {
|
|
3200
|
-
for (const child of cell) if (child !== void 0) children.push(child);
|
|
3201
|
-
}
|
|
3202
|
-
}
|
|
3203
|
-
}
|
|
3204
|
-
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
2935
|
+
const children = listChildren(frame.node);
|
|
2936
|
+
for (const index of children.keys().toArray().toReversed()) {
|
|
3205
2937
|
const child = children[index];
|
|
3206
2938
|
if (child !== void 0) stack.push({
|
|
3207
2939
|
node: child,
|
|
@@ -3283,7 +3015,7 @@ function foldNode(node, handlers, depth) {
|
|
|
3283
3015
|
expanded: true,
|
|
3284
3016
|
count: children.length
|
|
3285
3017
|
});
|
|
3286
|
-
for (
|
|
3018
|
+
for (const index of children.keys().toArray().toReversed()) {
|
|
3287
3019
|
const child = children[index];
|
|
3288
3020
|
if (child !== void 0) stack.push({
|
|
3289
3021
|
node: child,
|
|
@@ -3295,71 +3027,37 @@ function foldNode(node, handlers, depth) {
|
|
|
3295
3027
|
continue;
|
|
3296
3028
|
}
|
|
3297
3029
|
const children = frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count);
|
|
3298
|
-
|
|
3299
|
-
switch (frame.node.element) {
|
|
3300
|
-
case "document":
|
|
3301
|
-
value = handlers.document(frame.node, children);
|
|
3302
|
-
break;
|
|
3303
|
-
case "heading":
|
|
3304
|
-
value = handlers.heading(frame.node, children);
|
|
3305
|
-
break;
|
|
3306
|
-
case "paragraph":
|
|
3307
|
-
value = handlers.paragraph(frame.node, children);
|
|
3308
|
-
break;
|
|
3309
|
-
case "thematicBreak":
|
|
3310
|
-
value = handlers.thematicBreak(frame.node, children);
|
|
3311
|
-
break;
|
|
3312
|
-
case "blockquote":
|
|
3313
|
-
value = handlers.blockquote(frame.node, children);
|
|
3314
|
-
break;
|
|
3315
|
-
case "codeBlock":
|
|
3316
|
-
value = handlers.codeBlock(frame.node, children);
|
|
3317
|
-
break;
|
|
3318
|
-
case "list":
|
|
3319
|
-
value = handlers.list(frame.node, children);
|
|
3320
|
-
break;
|
|
3321
|
-
case "listItem":
|
|
3322
|
-
value = handlers.listItem(frame.node, children);
|
|
3323
|
-
break;
|
|
3324
|
-
case "table":
|
|
3325
|
-
value = handlers.table(frame.node, children);
|
|
3326
|
-
break;
|
|
3327
|
-
case "text":
|
|
3328
|
-
value = handlers.text(frame.node, children);
|
|
3329
|
-
break;
|
|
3330
|
-
case "emphasis":
|
|
3331
|
-
value = handlers.emphasis(frame.node, children);
|
|
3332
|
-
break;
|
|
3333
|
-
case "codeSpan":
|
|
3334
|
-
value = handlers.codeSpan(frame.node, children);
|
|
3335
|
-
break;
|
|
3336
|
-
case "break":
|
|
3337
|
-
value = handlers.break(frame.node, children);
|
|
3338
|
-
break;
|
|
3339
|
-
case "link":
|
|
3340
|
-
value = handlers.link(frame.node, children);
|
|
3341
|
-
break;
|
|
3342
|
-
case "image": value = handlers.image(frame.node, children);
|
|
3343
|
-
}
|
|
3030
|
+
const value = foldMarkdownNode(frame.node, handlers, children);
|
|
3344
3031
|
if (stack.length === 0) return value;
|
|
3345
3032
|
values.push(value);
|
|
3346
3033
|
}
|
|
3034
|
+
return foldMarkdownNode(node, handlers, []);
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* Dispatches one markdown node and its folded children to the matching handler.
|
|
3038
|
+
*
|
|
3039
|
+
* @example
|
|
3040
|
+
* ```ts
|
|
3041
|
+
* foldMarkdownNode({ element: 'text', value: 'hi' }, countHandlers, []) // 1
|
|
3042
|
+
* ```
|
|
3043
|
+
*/
|
|
3044
|
+
function foldMarkdownNode(node, handlers, children) {
|
|
3347
3045
|
switch (node.element) {
|
|
3348
|
-
case "document": return handlers.document(node,
|
|
3349
|
-
case "heading": return handlers.heading(node,
|
|
3350
|
-
case "paragraph": return handlers.paragraph(node,
|
|
3351
|
-
case "thematicBreak": return handlers.thematicBreak(node,
|
|
3352
|
-
case "blockquote": return handlers.blockquote(node,
|
|
3353
|
-
case "codeBlock": return handlers.codeBlock(node,
|
|
3354
|
-
case "list": return handlers.list(node,
|
|
3355
|
-
case "listItem": return handlers.listItem(node,
|
|
3356
|
-
case "table": return handlers.table(node,
|
|
3357
|
-
case "text": return handlers.text(node,
|
|
3358
|
-
case "emphasis": return handlers.emphasis(node,
|
|
3359
|
-
case "codeSpan": return handlers.codeSpan(node,
|
|
3360
|
-
case "break": return handlers.break(node,
|
|
3361
|
-
case "link": return handlers.link(node,
|
|
3362
|
-
case "image": return handlers.image(node,
|
|
3046
|
+
case "document": return handlers.document(node, children);
|
|
3047
|
+
case "heading": return handlers.heading(node, children);
|
|
3048
|
+
case "paragraph": return handlers.paragraph(node, children);
|
|
3049
|
+
case "thematicBreak": return handlers.thematicBreak(node, children);
|
|
3050
|
+
case "blockquote": return handlers.blockquote(node, children);
|
|
3051
|
+
case "codeBlock": return handlers.codeBlock(node, children);
|
|
3052
|
+
case "list": return handlers.list(node, children);
|
|
3053
|
+
case "listItem": return handlers.listItem(node, children);
|
|
3054
|
+
case "table": return handlers.table(node, children);
|
|
3055
|
+
case "text": return handlers.text(node, children);
|
|
3056
|
+
case "emphasis": return handlers.emphasis(node, children);
|
|
3057
|
+
case "codeSpan": return handlers.codeSpan(node, children);
|
|
3058
|
+
case "break": return handlers.break(node, children);
|
|
3059
|
+
case "link": return handlers.link(node, children);
|
|
3060
|
+
case "image": return handlers.image(node, children);
|
|
3363
3061
|
}
|
|
3364
3062
|
}
|
|
3365
3063
|
/**
|
|
@@ -3403,7 +3101,7 @@ function rewriteDocument(document, rewrite) {
|
|
|
3403
3101
|
count: 0
|
|
3404
3102
|
}];
|
|
3405
3103
|
const values = [];
|
|
3406
|
-
const derivations =
|
|
3104
|
+
const derivations = [];
|
|
3407
3105
|
while (stack.length > 0) {
|
|
3408
3106
|
const frame = stack.pop();
|
|
3409
3107
|
if (frame === void 0) continue;
|
|
@@ -3444,7 +3142,7 @@ function rewriteDocument(document, rewrite) {
|
|
|
3444
3142
|
count: children.length
|
|
3445
3143
|
});
|
|
3446
3144
|
const depth = current.element === "document" ? 0 : frame.depth + 1;
|
|
3447
|
-
for (
|
|
3145
|
+
for (const index of children.keys().toArray().toReversed()) {
|
|
3448
3146
|
const child = children[index];
|
|
3449
3147
|
if (child !== void 0) stack.push({
|
|
3450
3148
|
node: child,
|
|
@@ -3455,197 +3153,138 @@ function rewriteDocument(document, rewrite) {
|
|
|
3455
3153
|
}
|
|
3456
3154
|
continue;
|
|
3457
3155
|
}
|
|
3458
|
-
const
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
let offset = 0;
|
|
3465
|
-
for (const block of current.children) {
|
|
3466
|
-
if (block === void 0) continue;
|
|
3467
|
-
const child = children[offset];
|
|
3468
|
-
const accepted = child !== void 0 && isBlockNode(child) ? child : block;
|
|
3469
|
-
blocks.push(accepted);
|
|
3470
|
-
if (accepted !== block) changed = true;
|
|
3471
|
-
offset += 1;
|
|
3472
|
-
}
|
|
3473
|
-
if (changed) rebuilt = {
|
|
3474
|
-
element: "document",
|
|
3475
|
-
children: blocks
|
|
3476
|
-
};
|
|
3477
|
-
break;
|
|
3478
|
-
}
|
|
3479
|
-
case "heading":
|
|
3480
|
-
case "paragraph": {
|
|
3481
|
-
const inlines = [];
|
|
3482
|
-
let offset = 0;
|
|
3483
|
-
for (const inline of current.children) {
|
|
3484
|
-
if (inline === void 0) continue;
|
|
3485
|
-
const child = children[offset];
|
|
3486
|
-
const accepted = child !== void 0 && isInlineNode(child) ? child : inline;
|
|
3487
|
-
inlines.push(accepted);
|
|
3488
|
-
if (accepted !== inline) changed = true;
|
|
3489
|
-
offset += 1;
|
|
3490
|
-
}
|
|
3491
|
-
if (changed) rebuilt = {
|
|
3492
|
-
...current,
|
|
3493
|
-
children: inlines
|
|
3494
|
-
};
|
|
3495
|
-
break;
|
|
3496
|
-
}
|
|
3497
|
-
case "blockquote": {
|
|
3498
|
-
const blocks = [];
|
|
3499
|
-
let offset = 0;
|
|
3500
|
-
for (const block of current.children) {
|
|
3501
|
-
if (block === void 0) continue;
|
|
3502
|
-
const child = children[offset];
|
|
3503
|
-
const accepted = child !== void 0 && isBlockNode(child) ? child : block;
|
|
3504
|
-
blocks.push(accepted);
|
|
3505
|
-
if (accepted !== block) changed = true;
|
|
3506
|
-
offset += 1;
|
|
3507
|
-
}
|
|
3508
|
-
if (changed) rebuilt = {
|
|
3509
|
-
...current,
|
|
3510
|
-
children: blocks
|
|
3511
|
-
};
|
|
3512
|
-
break;
|
|
3513
|
-
}
|
|
3514
|
-
case "listItem": {
|
|
3515
|
-
const blocks = [];
|
|
3516
|
-
let offset = 0;
|
|
3517
|
-
for (const block of current.children) {
|
|
3518
|
-
if (block === void 0) continue;
|
|
3519
|
-
const child = children[offset];
|
|
3520
|
-
const accepted = child !== void 0 && isBlockNode(child) ? child : block;
|
|
3521
|
-
blocks.push(accepted);
|
|
3522
|
-
if (accepted !== block) changed = true;
|
|
3523
|
-
offset += 1;
|
|
3524
|
-
}
|
|
3525
|
-
if (changed) rebuilt = {
|
|
3526
|
-
element: "listItem",
|
|
3527
|
-
children: blocks
|
|
3528
|
-
};
|
|
3529
|
-
break;
|
|
3530
|
-
}
|
|
3531
|
-
case "emphasis":
|
|
3532
|
-
case "link":
|
|
3533
|
-
case "image": {
|
|
3534
|
-
const inlines = [];
|
|
3535
|
-
let offset = 0;
|
|
3536
|
-
for (const inline of current.children) {
|
|
3537
|
-
if (inline === void 0) continue;
|
|
3538
|
-
const child = children[offset];
|
|
3539
|
-
const accepted = child !== void 0 && isInlineNode(child) ? child : inline;
|
|
3540
|
-
inlines.push(accepted);
|
|
3541
|
-
if (accepted !== inline) changed = true;
|
|
3542
|
-
offset += 1;
|
|
3543
|
-
}
|
|
3544
|
-
if (changed) rebuilt = {
|
|
3545
|
-
...current,
|
|
3546
|
-
children: inlines
|
|
3547
|
-
};
|
|
3548
|
-
break;
|
|
3549
|
-
}
|
|
3550
|
-
case "list": {
|
|
3551
|
-
const items = [];
|
|
3552
|
-
let offset = 0;
|
|
3553
|
-
for (const item of current.items) {
|
|
3554
|
-
if (item === void 0) continue;
|
|
3555
|
-
const child = children[offset];
|
|
3556
|
-
const accepted = child?.element === "listItem" ? child : item;
|
|
3557
|
-
items.push(accepted);
|
|
3558
|
-
if (accepted !== item) changed = true;
|
|
3559
|
-
offset += 1;
|
|
3560
|
-
}
|
|
3561
|
-
if (changed) rebuilt = {
|
|
3562
|
-
...current,
|
|
3563
|
-
items
|
|
3564
|
-
};
|
|
3565
|
-
break;
|
|
3566
|
-
}
|
|
3567
|
-
case "table": {
|
|
3568
|
-
let offset = 0;
|
|
3569
|
-
const header = [];
|
|
3570
|
-
for (const cell of current.header) {
|
|
3571
|
-
if (cell === void 0) continue;
|
|
3572
|
-
const inlines = [];
|
|
3573
|
-
for (const inline of cell) {
|
|
3574
|
-
if (inline === void 0) continue;
|
|
3575
|
-
const child = children[offset];
|
|
3576
|
-
const accepted = child !== void 0 && isInlineNode(child) ? child : inline;
|
|
3577
|
-
inlines.push(accepted);
|
|
3578
|
-
if (accepted !== inline) changed = true;
|
|
3579
|
-
offset += 1;
|
|
3580
|
-
}
|
|
3581
|
-
header.push(inlines);
|
|
3582
|
-
}
|
|
3583
|
-
const rows = [];
|
|
3584
|
-
for (const row of current.rows) {
|
|
3585
|
-
if (row === void 0) continue;
|
|
3586
|
-
const cells = [];
|
|
3587
|
-
for (const cell of row) {
|
|
3588
|
-
if (cell === void 0) continue;
|
|
3589
|
-
const inlines = [];
|
|
3590
|
-
for (const inline of cell) {
|
|
3591
|
-
if (inline === void 0) continue;
|
|
3592
|
-
const child = children[offset];
|
|
3593
|
-
const accepted = child !== void 0 && isInlineNode(child) ? child : inline;
|
|
3594
|
-
inlines.push(accepted);
|
|
3595
|
-
if (accepted !== inline) changed = true;
|
|
3596
|
-
offset += 1;
|
|
3597
|
-
}
|
|
3598
|
-
cells.push(inlines);
|
|
3599
|
-
}
|
|
3600
|
-
rows.push(cells);
|
|
3601
|
-
}
|
|
3602
|
-
if (changed) rebuilt = {
|
|
3603
|
-
...current,
|
|
3604
|
-
header,
|
|
3605
|
-
rows
|
|
3606
|
-
};
|
|
3607
|
-
break;
|
|
3608
|
-
}
|
|
3609
|
-
}
|
|
3610
|
-
if (rebuilt !== current) derivations.set(rebuilt, current);
|
|
3156
|
+
const rebuilt = rebuildMarkdownNode(current, frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count));
|
|
3157
|
+
if (rebuilt !== current) derivations.push([
|
|
3158
|
+
rebuilt,
|
|
3159
|
+
current,
|
|
3160
|
+
true
|
|
3161
|
+
]);
|
|
3611
3162
|
if (current.element === "document") {
|
|
3612
3163
|
const result = rebuilt.element === "document" ? rebuilt : current;
|
|
3613
3164
|
const output = new Set(walkNodes(result));
|
|
3614
|
-
|
|
3615
|
-
for (const [node, source] of derivations) if (output.has(node)) retained.set(node, source);
|
|
3616
|
-
return [result, retained];
|
|
3617
|
-
}
|
|
3618
|
-
const result = rewrite(rebuilt);
|
|
3619
|
-
let accepted = rebuilt;
|
|
3620
|
-
switch (current.element) {
|
|
3621
|
-
case "text":
|
|
3622
|
-
case "emphasis":
|
|
3623
|
-
case "codeSpan":
|
|
3624
|
-
case "break":
|
|
3625
|
-
case "link":
|
|
3626
|
-
case "image":
|
|
3627
|
-
if (isInlineNode(result)) accepted = result;
|
|
3628
|
-
break;
|
|
3629
|
-
case "heading":
|
|
3630
|
-
case "paragraph":
|
|
3631
|
-
case "list":
|
|
3632
|
-
case "table":
|
|
3633
|
-
case "codeBlock":
|
|
3634
|
-
case "blockquote":
|
|
3635
|
-
case "thematicBreak":
|
|
3636
|
-
if (isBlockNode(result)) accepted = result;
|
|
3637
|
-
break;
|
|
3638
|
-
case "listItem": if (result.element === "listItem") accepted = result;
|
|
3639
|
-
}
|
|
3640
|
-
if (accepted !== rebuilt && accepted !== current) {
|
|
3641
|
-
if (derivations.has(accepted) && derivations.get(accepted) !== current) derivations.set(accepted, void 0);
|
|
3642
|
-
else derivations.set(accepted, current);
|
|
3165
|
+
return [result, new Map(Map.groupBy(derivations, ([node]) => node).entries().filter(([node]) => output.has(node)).map(([node, steps]) => [node, steps.reduce((source, [, input, direct], index) => index === 0 || direct || source === input ? input : void 0, void 0)]))];
|
|
3643
3166
|
}
|
|
3167
|
+
const accepted = acceptMarkdownNode(current, rebuilt, rewrite(rebuilt));
|
|
3168
|
+
if (accepted !== rebuilt && accepted !== current) derivations.push([
|
|
3169
|
+
accepted,
|
|
3170
|
+
current,
|
|
3171
|
+
false
|
|
3172
|
+
]);
|
|
3644
3173
|
values.push(accepted);
|
|
3645
3174
|
}
|
|
3646
3175
|
return [document, /* @__PURE__ */ new Map()];
|
|
3647
3176
|
}
|
|
3648
3177
|
/**
|
|
3178
|
+
* Rebuilds one markdown node when accepted child replacements change its identity.
|
|
3179
|
+
*
|
|
3180
|
+
* @example
|
|
3181
|
+
* ```ts
|
|
3182
|
+
* const original: ParagraphNode = { element: 'paragraph', children: [] }
|
|
3183
|
+
* rebuildMarkdownNode(original, []) === original // true
|
|
3184
|
+
* ```
|
|
3185
|
+
*/
|
|
3186
|
+
function rebuildMarkdownNode(current, children) {
|
|
3187
|
+
const outputs = children.values();
|
|
3188
|
+
switch (current.element) {
|
|
3189
|
+
case "document":
|
|
3190
|
+
case "blockquote":
|
|
3191
|
+
case "listItem": {
|
|
3192
|
+
const pairs = Iterator.from(current.children).filter((child) => child !== void 0).map((block) => {
|
|
3193
|
+
const child = outputs.next().value;
|
|
3194
|
+
return [block, child !== void 0 && isBlockNode(child) ? child : block];
|
|
3195
|
+
}).toArray();
|
|
3196
|
+
if (!pairs.some(([input, output]) => input !== output)) return current;
|
|
3197
|
+
const blocks = pairs.map(([, output]) => output);
|
|
3198
|
+
return current.element === "blockquote" ? {
|
|
3199
|
+
...current,
|
|
3200
|
+
children: blocks
|
|
3201
|
+
} : {
|
|
3202
|
+
element: current.element,
|
|
3203
|
+
children: blocks
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
case "heading":
|
|
3207
|
+
case "paragraph":
|
|
3208
|
+
case "emphasis":
|
|
3209
|
+
case "link":
|
|
3210
|
+
case "image": {
|
|
3211
|
+
const pairs = Iterator.from(current.children).filter((child) => child !== void 0).map((inline) => {
|
|
3212
|
+
const child = outputs.next().value;
|
|
3213
|
+
return [inline, child !== void 0 && isInlineNode(child) ? child : inline];
|
|
3214
|
+
}).toArray();
|
|
3215
|
+
return pairs.some(([input, output]) => input !== output) ? {
|
|
3216
|
+
...current,
|
|
3217
|
+
children: pairs.map(([, output]) => output)
|
|
3218
|
+
} : current;
|
|
3219
|
+
}
|
|
3220
|
+
case "list": {
|
|
3221
|
+
const pairs = Iterator.from(current.items).filter((item) => item !== void 0).map((item) => {
|
|
3222
|
+
const child = outputs.next().value;
|
|
3223
|
+
return [item, child?.element === "listItem" ? child : item];
|
|
3224
|
+
}).toArray();
|
|
3225
|
+
return pairs.some(([input, output]) => input !== output) ? {
|
|
3226
|
+
...current,
|
|
3227
|
+
items: pairs.map(([, output]) => output)
|
|
3228
|
+
} : current;
|
|
3229
|
+
}
|
|
3230
|
+
case "table": {
|
|
3231
|
+
const pairs = [];
|
|
3232
|
+
const header = Iterator.from(current.header).filter((cell) => cell !== void 0).map((cell) => rebuildMarkdownCell(cell, outputs, pairs)).toArray();
|
|
3233
|
+
const rows = Iterator.from(current.rows).filter((row) => row !== void 0).map((row) => Iterator.from(row).filter((cell) => cell !== void 0).map((cell) => rebuildMarkdownCell(cell, outputs, pairs)).toArray()).toArray();
|
|
3234
|
+
return pairs.some(([input, output]) => input !== output) ? {
|
|
3235
|
+
...current,
|
|
3236
|
+
header,
|
|
3237
|
+
rows
|
|
3238
|
+
} : current;
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
return current;
|
|
3242
|
+
}
|
|
3243
|
+
/**
|
|
3244
|
+
* Rebuilds a table cell and records each input and accepted output pair.
|
|
3245
|
+
*
|
|
3246
|
+
* @example
|
|
3247
|
+
* ```ts
|
|
3248
|
+
* rebuildMarkdownCell([], [].values(), []) // []
|
|
3249
|
+
* ```
|
|
3250
|
+
*/
|
|
3251
|
+
function rebuildMarkdownCell(cell, outputs, pairs) {
|
|
3252
|
+
return Iterator.from(cell).filter((inline) => inline !== void 0).map((inline) => {
|
|
3253
|
+
const child = outputs.next().value;
|
|
3254
|
+
const accepted = child !== void 0 && isInlineNode(child) ? child : inline;
|
|
3255
|
+
pairs.push([inline, accepted]);
|
|
3256
|
+
return accepted;
|
|
3257
|
+
}).toArray();
|
|
3258
|
+
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Accepts a rewritten node only when it fits the original node slot.
|
|
3261
|
+
*
|
|
3262
|
+
* @example
|
|
3263
|
+
* ```ts
|
|
3264
|
+
* const original: TextNode = { element: 'text', value: 'hi' }
|
|
3265
|
+
* acceptMarkdownNode(original, original, { element: 'thematicBreak' }) === original // true
|
|
3266
|
+
* ```
|
|
3267
|
+
*/
|
|
3268
|
+
function acceptMarkdownNode(current, rebuilt, result) {
|
|
3269
|
+
switch (current.element) {
|
|
3270
|
+
case "text":
|
|
3271
|
+
case "emphasis":
|
|
3272
|
+
case "codeSpan":
|
|
3273
|
+
case "break":
|
|
3274
|
+
case "link":
|
|
3275
|
+
case "image": return isInlineNode(result) ? result : rebuilt;
|
|
3276
|
+
case "heading":
|
|
3277
|
+
case "paragraph":
|
|
3278
|
+
case "list":
|
|
3279
|
+
case "table":
|
|
3280
|
+
case "codeBlock":
|
|
3281
|
+
case "blockquote":
|
|
3282
|
+
case "thematicBreak": return isBlockNode(result) ? result : rebuilt;
|
|
3283
|
+
case "listItem": return result.element === "listItem" ? result : rebuilt;
|
|
3284
|
+
}
|
|
3285
|
+
return rebuilt;
|
|
3286
|
+
}
|
|
3287
|
+
/**
|
|
3649
3288
|
* Concatenates the `value` / `code` content of every descendant text / code-span /
|
|
3650
3289
|
* code-block node under `node`, including image alternative content, in walk order —
|
|
3651
3290
|
* the plain-text projection of an AST (search indexing, word counts, a text-only
|
|
@@ -3672,43 +3311,19 @@ function flattenText(node) {
|
|
|
3672
3311
|
node,
|
|
3673
3312
|
depth: 0
|
|
3674
3313
|
}];
|
|
3675
|
-
|
|
3314
|
+
const parts = [];
|
|
3676
3315
|
while (stack.length > 0) {
|
|
3677
3316
|
const frame = stack.pop();
|
|
3678
3317
|
if (frame === void 0 || frame.depth >= 64) continue;
|
|
3679
|
-
const children = [];
|
|
3680
3318
|
switch (frame.node.element) {
|
|
3681
3319
|
case "text":
|
|
3682
3320
|
case "codeSpan":
|
|
3683
|
-
|
|
3684
|
-
break;
|
|
3685
|
-
case "codeBlock":
|
|
3686
|
-
value += frame.node.code;
|
|
3321
|
+
parts.push(frame.node.value);
|
|
3687
3322
|
break;
|
|
3688
|
-
case "
|
|
3689
|
-
case "heading":
|
|
3690
|
-
case "paragraph":
|
|
3691
|
-
case "blockquote":
|
|
3692
|
-
case "listItem":
|
|
3693
|
-
case "emphasis":
|
|
3694
|
-
case "link":
|
|
3695
|
-
case "image":
|
|
3696
|
-
for (const child of frame.node.children) if (child !== void 0) children.push(child);
|
|
3697
|
-
break;
|
|
3698
|
-
case "list":
|
|
3699
|
-
for (const child of frame.node.items) if (child !== void 0) children.push(child);
|
|
3700
|
-
break;
|
|
3701
|
-
case "table":
|
|
3702
|
-
for (const cell of frame.node.header) if (cell !== void 0) {
|
|
3703
|
-
for (const child of cell) if (child !== void 0) children.push(child);
|
|
3704
|
-
}
|
|
3705
|
-
for (const row of frame.node.rows) if (row !== void 0) {
|
|
3706
|
-
for (const cell of row) if (cell !== void 0) {
|
|
3707
|
-
for (const child of cell) if (child !== void 0) children.push(child);
|
|
3708
|
-
}
|
|
3709
|
-
}
|
|
3323
|
+
case "codeBlock": parts.push(frame.node.code);
|
|
3710
3324
|
}
|
|
3711
|
-
|
|
3325
|
+
const children = listChildren(frame.node);
|
|
3326
|
+
for (const index of children.keys().toArray().toReversed()) {
|
|
3712
3327
|
const child = children[index];
|
|
3713
3328
|
if (child !== void 0) stack.push({
|
|
3714
3329
|
node: child,
|
|
@@ -3716,10 +3331,310 @@ function flattenText(node) {
|
|
|
3716
3331
|
});
|
|
3717
3332
|
}
|
|
3718
3333
|
}
|
|
3719
|
-
return
|
|
3334
|
+
return parts.join("");
|
|
3335
|
+
}
|
|
3336
|
+
/**
|
|
3337
|
+
* Yields UTF-16 offsets and characters within a markdown scan window.
|
|
3338
|
+
*
|
|
3339
|
+
* @example
|
|
3340
|
+
* ```ts
|
|
3341
|
+
* Array.from(walkMarkdownCharacters('ab', 0, 2)) // [[0, 'a'], [1, 'b']]
|
|
3342
|
+
* ```
|
|
3343
|
+
*/
|
|
3344
|
+
function* walkMarkdownCharacters(source, from, to) {
|
|
3345
|
+
const positions = Array.prototype.keys.call({ length: Math.max(0, Math.ceil(to - from)) });
|
|
3346
|
+
for (const position of positions) {
|
|
3347
|
+
const index = from + position;
|
|
3348
|
+
yield [index, source[index] ?? ""];
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
/**
|
|
3352
|
+
* Counts consecutive copies of the character at the start of a scan window.
|
|
3353
|
+
*
|
|
3354
|
+
* @example
|
|
3355
|
+
* ```ts
|
|
3356
|
+
* countMarkdownRun('***x', 0, 4) // 3
|
|
3357
|
+
* ```
|
|
3358
|
+
*/
|
|
3359
|
+
function countMarkdownRun(source, start, to) {
|
|
3360
|
+
if (start >= to || source[start] === void 0) return 0;
|
|
3361
|
+
return /^([\s\S])\1*/.exec(source.slice(start, Math.ceil(to)))?.[0].length ?? 0;
|
|
3362
|
+
}
|
|
3363
|
+
/**
|
|
3364
|
+
* Finds the next defined markdown node at or after an array offset.
|
|
3365
|
+
*
|
|
3366
|
+
* @example
|
|
3367
|
+
* ```ts
|
|
3368
|
+
* findFollowingNode([], 0) // undefined
|
|
3369
|
+
* ```
|
|
3370
|
+
*/
|
|
3371
|
+
function findFollowingNode(nodes, start) {
|
|
3372
|
+
return nodes.keys().drop(start).map((index) => nodes[index]).find((node) => node !== void 0) ?? nodes[Math.max(start, nodes.length)];
|
|
3373
|
+
}
|
|
3374
|
+
/**
|
|
3375
|
+
* Escapes literal text so markdown syntax remains literal when rendered.
|
|
3376
|
+
*
|
|
3377
|
+
* @example
|
|
3378
|
+
* ```ts
|
|
3379
|
+
* escapeMarkdownText('*hi*', false) // '\\*hi\\*'
|
|
3380
|
+
* ```
|
|
3381
|
+
*/
|
|
3382
|
+
function escapeMarkdownText(value, bang) {
|
|
3383
|
+
const parts = [];
|
|
3384
|
+
walkMarkdownCharacters(value, 0, value.length).reduce((next, [index, character]) => {
|
|
3385
|
+
if (index < next) return next;
|
|
3386
|
+
const atLineStart = index === 0 || value[index - 1] === "\n";
|
|
3387
|
+
if (character === "!" && index === value.length - 1 && bang) {
|
|
3388
|
+
parts.push("\\!");
|
|
3389
|
+
return index + 1;
|
|
3390
|
+
}
|
|
3391
|
+
if ("\\*_`[]".includes(character) || atLineStart && (character === "#" || character === ">") || atLineStart && (character === "-" || character === "~") && value[index + 1] === character && value[index + 2] === character || atLineStart && (character === "-" || character === "+") && (value[index + 1] ?? " ") === " ") {
|
|
3392
|
+
parts.push("\\" + character);
|
|
3393
|
+
return index + 1;
|
|
3394
|
+
}
|
|
3395
|
+
if (atLineStart && /[0-9]/.test(character)) {
|
|
3396
|
+
const end = index + (/^[0-9]+/.exec(value.slice(index))?.[0].length ?? 0);
|
|
3397
|
+
const marker = value[end];
|
|
3398
|
+
if ((marker === "." || marker === ")") && value[end + 1] === " ") {
|
|
3399
|
+
parts.push(value.slice(index, end) + "\\" + marker);
|
|
3400
|
+
return end + 1;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
parts.push(character);
|
|
3404
|
+
return index + 1;
|
|
3405
|
+
}, 0);
|
|
3406
|
+
return parts.join("");
|
|
3407
|
+
}
|
|
3408
|
+
/**
|
|
3409
|
+
* Locates a balanced closing delimiter while skipping backslash escapes.
|
|
3410
|
+
*
|
|
3411
|
+
* @example
|
|
3412
|
+
* ```ts
|
|
3413
|
+
* locateMarkdownDelimiter('[a]', 0, 3, '[', ']') // 2
|
|
3414
|
+
* ```
|
|
3415
|
+
*/
|
|
3416
|
+
function locateMarkdownDelimiter(source, start, to, open, close) {
|
|
3417
|
+
const characters = walkMarkdownCharacters(source, start, to);
|
|
3418
|
+
const [, , result] = characters.reduce(([depth, next], [index, character]) => {
|
|
3419
|
+
if (index < next) return [
|
|
3420
|
+
depth,
|
|
3421
|
+
next,
|
|
3422
|
+
void 0
|
|
3423
|
+
];
|
|
3424
|
+
if (character === "\\") return [
|
|
3425
|
+
depth,
|
|
3426
|
+
index + 2,
|
|
3427
|
+
void 0
|
|
3428
|
+
];
|
|
3429
|
+
if (character === open) return [
|
|
3430
|
+
depth + 1,
|
|
3431
|
+
index + 1,
|
|
3432
|
+
void 0
|
|
3433
|
+
];
|
|
3434
|
+
if (character !== close) return [
|
|
3435
|
+
depth,
|
|
3436
|
+
index + 1,
|
|
3437
|
+
void 0
|
|
3438
|
+
];
|
|
3439
|
+
if (depth === 1) {
|
|
3440
|
+
characters.return(void 0);
|
|
3441
|
+
return [
|
|
3442
|
+
0,
|
|
3443
|
+
index + 1,
|
|
3444
|
+
index
|
|
3445
|
+
];
|
|
3446
|
+
}
|
|
3447
|
+
return [
|
|
3448
|
+
depth - 1,
|
|
3449
|
+
index + 1,
|
|
3450
|
+
void 0
|
|
3451
|
+
];
|
|
3452
|
+
}, [
|
|
3453
|
+
0,
|
|
3454
|
+
start,
|
|
3455
|
+
void 0
|
|
3456
|
+
]);
|
|
3457
|
+
return result;
|
|
3458
|
+
}
|
|
3459
|
+
/**
|
|
3460
|
+
* Appends a nonempty text node and records its original source span.
|
|
3461
|
+
*
|
|
3462
|
+
* @example
|
|
3463
|
+
* ```ts
|
|
3464
|
+
* const nodes: InlineNode[] = []
|
|
3465
|
+
* recordMarkdownText({ text: 'a', segments: [] }, 'a', 0, 1, new Map(), nodes)
|
|
3466
|
+
* nodes // [{ element: 'text', value: 'a' }]
|
|
3467
|
+
* ```
|
|
3468
|
+
*/
|
|
3469
|
+
function recordMarkdownText(source, value, start, end, spans, nodes) {
|
|
3470
|
+
if (value.length === 0) return;
|
|
3471
|
+
const node = {
|
|
3472
|
+
element: "text",
|
|
3473
|
+
value
|
|
3474
|
+
};
|
|
3475
|
+
const span = projectSpan(source, start, end);
|
|
3476
|
+
if (span !== void 0) spans.set(node, span);
|
|
3477
|
+
nodes.push(node);
|
|
3478
|
+
}
|
|
3479
|
+
/**
|
|
3480
|
+
* Scans one inline construct and records the spans of its nested children.
|
|
3481
|
+
*
|
|
3482
|
+
* @example
|
|
3483
|
+
* ```ts
|
|
3484
|
+
* scanMarkdownInline({ text: '`x`', segments: [] }, 0, 3, new Map(), 0)?.end // 3
|
|
3485
|
+
* ```
|
|
3486
|
+
*/
|
|
3487
|
+
function scanMarkdownInline(source, index, to, spans, depth) {
|
|
3488
|
+
const character = source.text[index] ?? "";
|
|
3489
|
+
if (character === " ") {
|
|
3490
|
+
const end = index + countMarkdownRun(source.text, index, to);
|
|
3491
|
+
if (end - index >= 2 && source.text[end] === "\n") return {
|
|
3492
|
+
node: { element: "break" },
|
|
3493
|
+
end: end + 1
|
|
3494
|
+
};
|
|
3495
|
+
}
|
|
3496
|
+
if (character === "`") {
|
|
3497
|
+
const span = scanCode(source.text, index, to);
|
|
3498
|
+
if (span) return {
|
|
3499
|
+
node: {
|
|
3500
|
+
element: "codeSpan",
|
|
3501
|
+
value: span.value
|
|
3502
|
+
},
|
|
3503
|
+
end: span.end
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
3506
|
+
if (character === "!" && source.text[index + 1] === "[") {
|
|
3507
|
+
const link = locateLink(source.text, index + 1, to);
|
|
3508
|
+
if (link !== void 0) return {
|
|
3509
|
+
node: {
|
|
3510
|
+
element: "image",
|
|
3511
|
+
src: unescapeText(source.text.slice(link.close + 2, link.end - 1).trim()),
|
|
3512
|
+
children: coalesceText(scanInlineSource(source, index + 2, link.close, spans, depth + 1), spans)
|
|
3513
|
+
},
|
|
3514
|
+
end: link.end
|
|
3515
|
+
};
|
|
3516
|
+
}
|
|
3517
|
+
if (character === "[") {
|
|
3518
|
+
const link = locateLink(source.text, index, to);
|
|
3519
|
+
if (link !== void 0) return {
|
|
3520
|
+
node: {
|
|
3521
|
+
element: "link",
|
|
3522
|
+
href: unescapeText(source.text.slice(link.close + 2, link.end - 1).trim()),
|
|
3523
|
+
children: coalesceText(scanInlineSource(source, index + 1, link.close, spans, depth + 1), spans)
|
|
3524
|
+
},
|
|
3525
|
+
end: link.end
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
if (character === "*" || character === "_") {
|
|
3529
|
+
const emphasis = locateEmphasis(source.text, index, to);
|
|
3530
|
+
if (emphasis !== void 0) return {
|
|
3531
|
+
node: {
|
|
3532
|
+
element: "emphasis",
|
|
3533
|
+
strong: emphasis.strong,
|
|
3534
|
+
children: coalesceText(scanInlineSource(source, emphasis.open, emphasis.close, spans, depth + 1), spans)
|
|
3535
|
+
},
|
|
3536
|
+
end: emphasis.end
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
/**
|
|
3541
|
+
* Yields line offsets through a closable markdown scan iterator.
|
|
3542
|
+
*
|
|
3543
|
+
* @example
|
|
3544
|
+
* ```ts
|
|
3545
|
+
* Array.from(walkMarkdownLines(splitLines('a\nb'), 1)) // [1]
|
|
3546
|
+
* ```
|
|
3547
|
+
*/
|
|
3548
|
+
function* walkMarkdownLines(lines, start) {
|
|
3549
|
+
const positions = Array.prototype.keys.call({ get length() {
|
|
3550
|
+
return Math.max(0, Math.ceil(lines.length - start));
|
|
3551
|
+
} });
|
|
3552
|
+
for (const position of positions) yield start + position;
|
|
3553
|
+
}
|
|
3554
|
+
/**
|
|
3555
|
+
* Splits inline nodes into adjacent text runs and singleton non-text runs.
|
|
3556
|
+
*
|
|
3557
|
+
* @param nodes - The inline nodes in source order
|
|
3558
|
+
* @returns The runs with their original node identities
|
|
3559
|
+
*
|
|
3560
|
+
* @example
|
|
3561
|
+
* ```ts
|
|
3562
|
+
* splitTextRuns([{ element: 'text', value: 'a' }]).length // 1
|
|
3563
|
+
* ```
|
|
3564
|
+
*/
|
|
3565
|
+
function splitTextRuns(nodes) {
|
|
3566
|
+
const starts = nodes.entries().flatMap(([index, node]) => index === 0 || node.element !== "text" || nodes[index - 1]?.element !== "text" ? [index] : []).toArray();
|
|
3567
|
+
return starts.map((start, index) => nodes.slice(start, starts[index + 1]));
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* Lists the direct children of a markdown node in traversal order.
|
|
3571
|
+
*
|
|
3572
|
+
* @param node - The node whose direct children to list
|
|
3573
|
+
* @returns The children, with table cells flattened in header then body order
|
|
3574
|
+
*
|
|
3575
|
+
* @example
|
|
3576
|
+
* ```ts
|
|
3577
|
+
* listChildren({ element: 'document', children: [] }) // []
|
|
3578
|
+
* ```
|
|
3579
|
+
*/
|
|
3580
|
+
function listChildren(node) {
|
|
3581
|
+
switch (node.element) {
|
|
3582
|
+
case "document":
|
|
3583
|
+
case "heading":
|
|
3584
|
+
case "paragraph":
|
|
3585
|
+
case "blockquote":
|
|
3586
|
+
case "listItem":
|
|
3587
|
+
case "emphasis":
|
|
3588
|
+
case "link":
|
|
3589
|
+
case "image": return Iterator.from(node.children).filter((child) => child !== void 0).toArray();
|
|
3590
|
+
case "list": return Iterator.from(node.items).filter((child) => child !== void 0).toArray();
|
|
3591
|
+
case "table": return [...Iterator.from(node.header).filter((cell) => cell !== void 0).flatMap((cell) => Iterator.from(cell).filter((child) => child !== void 0)), ...Iterator.from(node.rows).filter((row) => row !== void 0).flatMap((row) => Iterator.from(row).filter((cell) => cell !== void 0)).flatMap((cell) => Iterator.from(cell).filter((child) => child !== void 0))];
|
|
3592
|
+
default: return [];
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
/**
|
|
3596
|
+
* Trims leading whitespace when an inline run starts with a text node.
|
|
3597
|
+
*
|
|
3598
|
+
* @param nodes - The inline run to trim
|
|
3599
|
+
* @returns The run with its leading text trimmed or removed when empty
|
|
3600
|
+
*
|
|
3601
|
+
* @example
|
|
3602
|
+
* ```ts
|
|
3603
|
+
* trimLeading([{ element: 'text', value: ' a ' }]) // [{ element: 'text', value: 'a ' }]
|
|
3604
|
+
* ```
|
|
3605
|
+
*/
|
|
3606
|
+
function trimLeading(nodes) {
|
|
3607
|
+
const present = Iterator.from(nodes).filter((node) => node !== void 0).toArray();
|
|
3608
|
+
const first = present[0];
|
|
3609
|
+
if (first?.element !== "text") return present;
|
|
3610
|
+
const value = first.value.replace(/^\s+/, "");
|
|
3611
|
+
return (0, _orkestrel_contract.isEmptyString)(value) ? present.slice(1) : [{
|
|
3612
|
+
element: "text",
|
|
3613
|
+
value
|
|
3614
|
+
}, ...present.slice(1)];
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* Trims trailing whitespace when an inline run ends with a text node.
|
|
3618
|
+
*
|
|
3619
|
+
* @param nodes - The inline run to trim
|
|
3620
|
+
* @returns The run with its trailing text trimmed or removed when empty
|
|
3621
|
+
*
|
|
3622
|
+
* @example
|
|
3623
|
+
* ```ts
|
|
3624
|
+
* trimTrailing([{ element: 'text', value: ' a ' }]) // [{ element: 'text', value: ' a' }]
|
|
3625
|
+
* ```
|
|
3626
|
+
*/
|
|
3627
|
+
function trimTrailing(nodes) {
|
|
3628
|
+
const present = Iterator.from(nodes).filter((node) => node !== void 0).toArray();
|
|
3629
|
+
const last = present.at(-1);
|
|
3630
|
+
if (last?.element !== "text") return present;
|
|
3631
|
+
const value = last.value.replace(/\s+$/, "");
|
|
3632
|
+
return (0, _orkestrel_contract.isEmptyString)(value) ? present.slice(0, -1) : [...present.slice(0, -1), {
|
|
3633
|
+
element: "text",
|
|
3634
|
+
value
|
|
3635
|
+
}];
|
|
3720
3636
|
}
|
|
3721
|
-
|
|
3722
|
-
//#region src/core/compilers.ts
|
|
3637
|
+
// src/core/compilers.ts
|
|
3723
3638
|
/**
|
|
3724
3639
|
* Renders a {@link MarkdownNode} to sanitized canonical HTML.
|
|
3725
3640
|
*
|
|
@@ -3745,8 +3660,7 @@ function flattenText(node) {
|
|
|
3745
3660
|
function renderHTML(node) {
|
|
3746
3661
|
return (0, _orkestrel_html.renderHTML)(new _orkestrel_html.HTML(markdownToHTML(node)).sanitize({ attributes: [..._orkestrel_html.SAFE_ATTRIBUTES, "src"] }).document);
|
|
3747
3662
|
}
|
|
3748
|
-
|
|
3749
|
-
//#region src/core/shapers.ts
|
|
3663
|
+
// src/core/shapers.ts
|
|
3750
3664
|
/**
|
|
3751
3665
|
* Describes the shape of a {@link TextNode} — a plain-text leaf inline run.
|
|
3752
3666
|
*
|
|
@@ -3867,8 +3781,7 @@ var listItemMatchShape = (0, _orkestrel_contract.objectShape)({
|
|
|
3867
3781
|
indent: (0, _orkestrel_contract.integerShape)(),
|
|
3868
3782
|
marker: (0, _orkestrel_contract.integerShape)()
|
|
3869
3783
|
});
|
|
3870
|
-
|
|
3871
|
-
//#region src/core/Markdown.ts
|
|
3784
|
+
// src/core/Markdown.ts
|
|
3872
3785
|
/**
|
|
3873
3786
|
* Wraps a typed {@link MarkdownDocument} AST as a stateful, parsed markdown document
|
|
3874
3787
|
* with the query (`find` / `filter` / `reduce` / iteration), rewrite (`map`), fold, and
|
|
@@ -3974,9 +3887,7 @@ var Markdown = class Markdown {
|
|
|
3974
3887
|
for (const node of this.walk()) if (predicate(node)) return node;
|
|
3975
3888
|
}
|
|
3976
3889
|
filter(predicate) {
|
|
3977
|
-
|
|
3978
|
-
for (const node of this.walk()) if (predicate(node)) out.push(node);
|
|
3979
|
-
return out;
|
|
3890
|
+
return this.walk().filter((node) => predicate(node)).toArray();
|
|
3980
3891
|
}
|
|
3981
3892
|
/**
|
|
3982
3893
|
* Rewrites the AST bottom-up (copy-on-write) and returns a new {@link Markdown},
|
|
@@ -3993,9 +3904,7 @@ var Markdown = class Markdown {
|
|
|
3993
3904
|
}
|
|
3994
3905
|
/** Folds the AST depth-first, pre-order into an accumulator. */
|
|
3995
3906
|
reduce(callback, initial) {
|
|
3996
|
-
|
|
3997
|
-
for (const node of this.walk()) accumulator = callback(accumulator, node);
|
|
3998
|
-
return accumulator;
|
|
3907
|
+
return this.walk().reduce((accumulator, node) => callback(accumulator, node), initial);
|
|
3999
3908
|
}
|
|
4000
3909
|
/** Runs a total catamorphism over the document using a {@link MarkdownHandlerMap} table. */
|
|
4001
3910
|
fold(handlers) {
|
|
@@ -4012,7 +3921,9 @@ var Markdown = class Markdown {
|
|
|
4012
3921
|
* ```ts
|
|
4013
3922
|
* // universal - works in every ReadableStream-supporting environment
|
|
4014
3923
|
* const reader = markdown.stream().getReader()
|
|
4015
|
-
* for (
|
|
3924
|
+
* for (;;) {
|
|
3925
|
+
* const result = await reader.read()
|
|
3926
|
+
* if (result.done) break
|
|
4016
3927
|
* console.log(result.value) // one BlockNode
|
|
4017
3928
|
* }
|
|
4018
3929
|
*
|
|
@@ -4024,18 +3935,11 @@ var Markdown = class Markdown {
|
|
|
4024
3935
|
* ```
|
|
4025
3936
|
*/
|
|
4026
3937
|
stream() {
|
|
4027
|
-
const blocks = this.#document.children;
|
|
4028
|
-
let index = 0;
|
|
3938
|
+
const blocks = this.#document.children.values();
|
|
4029
3939
|
return new ReadableStream({ pull(controller) {
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
controller.close();
|
|
4034
|
-
return;
|
|
4035
|
-
}
|
|
4036
|
-
controller.enqueue(block);
|
|
4037
|
-
index += 1;
|
|
4038
|
-
} else controller.close();
|
|
3940
|
+
const block = blocks.next();
|
|
3941
|
+
if (!block.done && block.value !== void 0) controller.enqueue(block.value);
|
|
3942
|
+
else controller.close();
|
|
4039
3943
|
} });
|
|
4040
3944
|
}
|
|
4041
3945
|
#derive(document, derivations) {
|
|
@@ -4054,8 +3958,7 @@ var Markdown = class Markdown {
|
|
|
4054
3958
|
return derived;
|
|
4055
3959
|
}
|
|
4056
3960
|
};
|
|
4057
|
-
|
|
4058
|
-
//#region src/core/factories.ts
|
|
3961
|
+
// src/core/factories.ts
|
|
4059
3962
|
/**
|
|
4060
3963
|
* Creates a stateful markdown handle from a markdown string or an already-parsed
|
|
4061
3964
|
* {@link MarkdownDocument} — a typed AST plus the query, rewrite, and fold operations
|
|
@@ -4173,16 +4076,17 @@ function createCodeBlockContract() {
|
|
|
4173
4076
|
function createThematicBreakContract() {
|
|
4174
4077
|
return (0, _orkestrel_contract.createContract)(thematicBreakShape);
|
|
4175
4078
|
}
|
|
4176
|
-
//#endregion
|
|
4177
4079
|
exports.EMPTY_PROJECTION = EMPTY_PROJECTION;
|
|
4178
4080
|
exports.MAX_DEPTH = MAX_DEPTH;
|
|
4179
4081
|
exports.Markdown = Markdown;
|
|
4082
|
+
exports.acceptMarkdownNode = acceptMarkdownNode;
|
|
4180
4083
|
exports.coalesceText = coalesceText;
|
|
4181
4084
|
exports.codeBlockShape = codeBlockShape;
|
|
4182
4085
|
exports.codeSpanShape = codeSpanShape;
|
|
4183
4086
|
exports.collectList = collectList;
|
|
4184
4087
|
exports.collectTable = collectTable;
|
|
4185
4088
|
exports.countIndent = countIndent;
|
|
4089
|
+
exports.countMarkdownRun = countMarkdownRun;
|
|
4186
4090
|
exports.createCodeBlockContract = createCodeBlockContract;
|
|
4187
4091
|
exports.createCodeSpanContract = createCodeSpanContract;
|
|
4188
4092
|
exports.createLineBreakContract = createLineBreakContract;
|
|
@@ -4191,10 +4095,13 @@ exports.createProjection = createProjection;
|
|
|
4191
4095
|
exports.createTextContract = createTextContract;
|
|
4192
4096
|
exports.createThematicBreakContract = createThematicBreakContract;
|
|
4193
4097
|
exports.delimiterToAlignments = delimiterToAlignments;
|
|
4098
|
+
exports.escapeMarkdownText = escapeMarkdownText;
|
|
4194
4099
|
exports.extractFence = extractFence;
|
|
4195
4100
|
exports.extractHeading = extractHeading;
|
|
4196
4101
|
exports.extractListItem = extractListItem;
|
|
4102
|
+
exports.findFollowingNode = findFollowingNode;
|
|
4197
4103
|
exports.flattenText = flattenText;
|
|
4104
|
+
exports.foldMarkdownNode = foldMarkdownNode;
|
|
4198
4105
|
exports.foldNode = foldNode;
|
|
4199
4106
|
exports.htmlToMarkdown = htmlToMarkdown;
|
|
4200
4107
|
exports.isBlankLine = isBlankLine;
|
|
@@ -4224,9 +4131,11 @@ exports.isThematicBreak = isThematicBreak;
|
|
|
4224
4131
|
exports.isThematicBreakNode = isThematicBreakNode;
|
|
4225
4132
|
exports.joinSources = joinSources;
|
|
4226
4133
|
exports.lineBreakShape = lineBreakShape;
|
|
4134
|
+
exports.listChildren = listChildren;
|
|
4227
4135
|
exports.listItemMatchShape = listItemMatchShape;
|
|
4228
4136
|
exports.locateEmphasis = locateEmphasis;
|
|
4229
4137
|
exports.locateLink = locateLink;
|
|
4138
|
+
exports.locateMarkdownDelimiter = locateMarkdownDelimiter;
|
|
4230
4139
|
exports.markdownToHTML = markdownToHTML;
|
|
4231
4140
|
exports.mergeProjections = mergeProjections;
|
|
4232
4141
|
exports.normalizeInlines = normalizeInlines;
|
|
@@ -4237,29 +4146,40 @@ exports.parseInline = parseInline;
|
|
|
4237
4146
|
exports.parseProvenance = parseProvenance;
|
|
4238
4147
|
exports.projectHTMLLeaf = projectHTMLLeaf;
|
|
4239
4148
|
exports.projectHTMLNode = projectHTMLNode;
|
|
4149
|
+
exports.projectMarkdownNode = projectMarkdownNode;
|
|
4240
4150
|
exports.projectSpan = projectSpan;
|
|
4241
4151
|
exports.projectionToBlocks = projectionToBlocks;
|
|
4242
4152
|
exports.projectionToInlines = projectionToInlines;
|
|
4153
|
+
exports.rebuildMarkdownCell = rebuildMarkdownCell;
|
|
4154
|
+
exports.rebuildMarkdownNode = rebuildMarkdownNode;
|
|
4155
|
+
exports.recordMarkdownText = recordMarkdownText;
|
|
4243
4156
|
exports.renderHTML = renderHTML;
|
|
4244
4157
|
exports.renderMarkdown = renderMarkdown;
|
|
4158
|
+
exports.renderMarkdownNode = renderMarkdownNode;
|
|
4245
4159
|
exports.rewriteDocument = rewriteDocument;
|
|
4246
4160
|
exports.scanCode = scanCode;
|
|
4247
4161
|
exports.scanEmphasis = scanEmphasis;
|
|
4248
4162
|
exports.scanInline = scanInline;
|
|
4249
4163
|
exports.scanInlineSource = scanInlineSource;
|
|
4250
4164
|
exports.scanLink = scanLink;
|
|
4165
|
+
exports.scanMarkdownInline = scanMarkdownInline;
|
|
4251
4166
|
exports.sliceSource = sliceSource;
|
|
4252
4167
|
exports.splitLines = splitLines;
|
|
4253
4168
|
exports.splitTableRow = splitTableRow;
|
|
4254
4169
|
exports.splitTableSources = splitTableSources;
|
|
4170
|
+
exports.splitTextRuns = splitTextRuns;
|
|
4255
4171
|
exports.startsBlock = startsBlock;
|
|
4256
4172
|
exports.stripQuote = stripQuote;
|
|
4257
4173
|
exports.tableAlignShape = tableAlignShape;
|
|
4258
4174
|
exports.textShape = textShape;
|
|
4259
4175
|
exports.thematicBreakShape = thematicBreakShape;
|
|
4260
4176
|
exports.trimInlines = trimInlines;
|
|
4177
|
+
exports.trimLeading = trimLeading;
|
|
4261
4178
|
exports.trimSource = trimSource;
|
|
4179
|
+
exports.trimTrailing = trimTrailing;
|
|
4262
4180
|
exports.unescapeText = unescapeText;
|
|
4181
|
+
exports.walkMarkdownCharacters = walkMarkdownCharacters;
|
|
4182
|
+
exports.walkMarkdownLines = walkMarkdownLines;
|
|
4263
4183
|
exports.walkNodes = walkNodes;
|
|
4264
4184
|
|
|
4265
4185
|
//# sourceMappingURL=index.cjs.map
|