postext 1.22.2 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/bundle.test.js +1 -1
- package/dist/__tests__/codeBlocks.test.d.ts +2 -0
- package/dist/__tests__/codeBlocks.test.d.ts.map +1 -0
- package/dist/__tests__/codeBlocks.test.js +550 -0
- package/dist/__tests__/codeBlocks.test.js.map +1 -0
- package/dist/__tests__/exports.test.js +19 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/hardBreaks.test.d.ts +2 -0
- package/dist/__tests__/hardBreaks.test.d.ts.map +1 -0
- package/dist/__tests__/hardBreaks.test.js +226 -0
- package/dist/__tests__/hardBreaks.test.js.map +1 -0
- package/dist/__tests__/headingInlineMarks.test.js +1 -1
- package/dist/__tests__/headingLetterSpacing.test.js +1 -1
- package/dist/__tests__/headingLetterSpacing.test.js.map +1 -1
- package/dist/__tests__/lineNumbers.test.d.ts +2 -0
- package/dist/__tests__/lineNumbers.test.d.ts.map +1 -0
- package/dist/__tests__/lineNumbers.test.js +304 -0
- package/dist/__tests__/lineNumbers.test.js.map +1 -0
- package/dist/__tests__/mathSizeMigration.test.js +2 -2
- package/dist/__tests__/paragraphDropCap.test.d.ts +2 -0
- package/dist/__tests__/paragraphDropCap.test.d.ts.map +1 -0
- package/dist/__tests__/paragraphDropCap.test.js +442 -0
- package/dist/__tests__/paragraphDropCap.test.js.map +1 -0
- package/dist/__tests__/paragraphStyleIndent.test.js +26 -2
- package/dist/__tests__/paragraphStyleIndent.test.js.map +1 -1
- package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js +1 -1
- package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js +1 -1
- package/dist/__tests__/pipeline/colonListRoom.test.js +1 -1
- package/dist/__tests__/pipeline/compoundBreaks.test.js +1 -1
- package/dist/__tests__/pipeline/dashBreaks.test.js +1 -1
- package/dist/__tests__/pipeline/headingSplitOrphans.test.js +1 -1
- package/dist/__tests__/pipeline/inlineFigureSpace.test.js +1 -1
- package/dist/__tests__/pipeline/paragraphContainerTail.test.js +1 -1
- package/dist/__tests__/pipeline/paragraphStyleFaces.test.js +7 -1
- package/dist/__tests__/pipeline/paragraphStyleFaces.test.js.map +1 -1
- package/dist/__tests__/pipeline/raggedOptimal.test.js +1 -1
- package/dist/__tests__/tabStops.test.d.ts +2 -0
- package/dist/__tests__/tabStops.test.d.ts.map +1 -0
- package/dist/__tests__/tabStops.test.js +394 -0
- package/dist/__tests__/tabStops.test.js.map +1 -0
- package/dist/__tests__/verseLines.test.d.ts +2 -0
- package/dist/__tests__/verseLines.test.d.ts.map +1 -0
- package/dist/__tests__/verseLines.test.js +375 -0
- package/dist/__tests__/verseLines.test.js.map +1 -0
- package/dist/bundle/configVersion.d.ts +77 -7
- package/dist/bundle/configVersion.d.ts.map +1 -1
- package/dist/bundle/configVersion.js +239 -5
- package/dist/bundle/configVersion.js.map +1 -1
- package/dist/bundle/index.d.ts +1 -1
- package/dist/bundle/index.d.ts.map +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/index.js.map +1 -1
- package/dist/canvas-backend/blockRender.d.ts.map +1 -1
- package/dist/canvas-backend/blockRender.js +39 -5
- package/dist/canvas-backend/blockRender.js.map +1 -1
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +3 -0
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/cjkMarks.js +1 -1
- package/dist/cjkMarks.js.map +1 -1
- package/dist/code/highlight.d.ts +34 -0
- package/dist/code/highlight.d.ts.map +1 -0
- package/dist/code/highlight.js +406 -0
- package/dist/code/highlight.js.map +1 -0
- package/dist/configWarnings.d.ts.map +1 -1
- package/dist/configWarnings.js +192 -3
- package/dist/configWarnings.js.map +1 -1
- package/dist/defaults/bodyText.d.ts.map +1 -1
- package/dist/defaults/bodyText.js +25 -0
- package/dist/defaults/bodyText.js.map +1 -1
- package/dist/defaults/calloutStyles.d.ts.map +1 -1
- package/dist/defaults/calloutStyles.js +7 -0
- package/dist/defaults/calloutStyles.js.map +1 -1
- package/dist/defaults/codeStyle.d.ts +29 -0
- package/dist/defaults/codeStyle.d.ts.map +1 -0
- package/dist/defaults/codeStyle.js +284 -0
- package/dist/defaults/codeStyle.js.map +1 -0
- package/dist/defaults/dropCap.d.ts +49 -0
- package/dist/defaults/dropCap.d.ts.map +1 -0
- package/dist/defaults/dropCap.js +66 -0
- package/dist/defaults/dropCap.js.map +1 -0
- package/dist/defaults/headings.d.ts +1 -1
- package/dist/defaults/headings.d.ts.map +1 -1
- package/dist/defaults/headings.js +11 -0
- package/dist/defaults/headings.js.map +1 -1
- package/dist/defaults/index.d.ts +3 -0
- package/dist/defaults/index.d.ts.map +1 -1
- package/dist/defaults/index.js +19 -0
- package/dist/defaults/index.js.map +1 -1
- package/dist/defaults/lineNumbers.d.ts +19 -0
- package/dist/defaults/lineNumbers.d.ts.map +1 -0
- package/dist/defaults/lineNumbers.js +101 -0
- package/dist/defaults/lineNumbers.js.map +1 -0
- package/dist/defaults/paragraphStyles.d.ts.map +1 -1
- package/dist/defaults/paragraphStyles.js +20 -0
- package/dist/defaults/paragraphStyles.js.map +1 -1
- package/dist/defaults/tabStops.d.ts +29 -0
- package/dist/defaults/tabStops.d.ts.map +1 -0
- package/dist/defaults/tabStops.js +94 -0
- package/dist/defaults/tabStops.js.map +1 -0
- package/dist/defaults/verse.d.ts +10 -0
- package/dist/defaults/verse.d.ts.map +1 -0
- package/dist/defaults/verse.js +52 -0
- package/dist/defaults/verse.js.map +1 -0
- package/dist/fonts/usage.d.ts +7 -0
- package/dist/fonts/usage.d.ts.map +1 -1
- package/dist/fonts/usage.js +71 -0
- package/dist/fonts/usage.js.map +1 -1
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +85 -11
- package/dist/html-backend.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/measure/bidiLines.js +2 -2
- package/dist/measure/bidiLines.js.map +1 -1
- package/dist/measure/cache.js +2 -2
- package/dist/measure/cache.js.map +1 -1
- package/dist/measure/cjkCompose.d.ts.map +1 -1
- package/dist/measure/cjkCompose.js +5 -5
- package/dist/measure/cjkCompose.js.map +1 -1
- package/dist/measure/hardBreaks.d.ts +46 -0
- package/dist/measure/hardBreaks.d.ts.map +1 -0
- package/dist/measure/hardBreaks.js +216 -0
- package/dist/measure/hardBreaks.js.map +1 -0
- package/dist/measure/index.d.ts +1 -0
- package/dist/measure/index.d.ts.map +1 -1
- package/dist/measure/index.js +1 -0
- package/dist/measure/index.js.map +1 -1
- package/dist/measure/leader.d.ts +20 -0
- package/dist/measure/leader.d.ts.map +1 -0
- package/dist/measure/leader.js +46 -0
- package/dist/measure/leader.js.map +1 -0
- package/dist/measure/links.js +1 -1
- package/dist/measure/links.js.map +1 -1
- package/dist/measure/plain.d.ts.map +1 -1
- package/dist/measure/plain.js +18 -25
- package/dist/measure/plain.js.map +1 -1
- package/dist/measure/rich.d.ts +19 -0
- package/dist/measure/rich.d.ts.map +1 -1
- package/dist/measure/rich.js +211 -38
- package/dist/measure/rich.js.map +1 -1
- package/dist/measure/tabs.d.ts +103 -0
- package/dist/measure/tabs.d.ts.map +1 -0
- package/dist/measure/tabs.js +123 -0
- package/dist/measure/tabs.js.map +1 -0
- package/dist/measure/types.d.ts +26 -2
- package/dist/measure/types.d.ts.map +1 -1
- package/dist/measure/types.js +23 -3
- package/dist/measure/types.js.map +1 -1
- package/dist/parse/blockParser.d.ts +13 -6
- package/dist/parse/blockParser.d.ts.map +1 -1
- package/dist/parse/blockParser.js +457 -87
- package/dist/parse/blockParser.js.map +1 -1
- package/dist/parse/codeFence.d.ts +18 -0
- package/dist/parse/codeFence.d.ts.map +1 -0
- package/dist/parse/codeFence.js +52 -0
- package/dist/parse/codeFence.js.map +1 -0
- package/dist/parse/index.d.ts +1 -1
- package/dist/parse/index.d.ts.map +1 -1
- package/dist/parse/index.js.map +1 -1
- package/dist/parse/indexMarks.d.ts +3 -1
- package/dist/parse/indexMarks.d.ts.map +1 -1
- package/dist/parse/indexMarks.js +22 -2
- package/dist/parse/indexMarks.js.map +1 -1
- package/dist/parse/inlineFormatting.d.ts +50 -0
- package/dist/parse/inlineFormatting.d.ts.map +1 -1
- package/dist/parse/inlineFormatting.js +94 -0
- package/dist/parse/inlineFormatting.js.map +1 -1
- package/dist/parse/sourceMapping.d.ts.map +1 -1
- package/dist/parse/sourceMapping.js +65 -1
- package/dist/parse/sourceMapping.js.map +1 -1
- package/dist/parse/types.d.ts +144 -9
- package/dist/parse/types.d.ts.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +143 -18
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/buildBlockKind.d.ts.map +1 -1
- package/dist/pipeline/buildBlockKind.js +60 -1
- package/dist/pipeline/buildBlockKind.js.map +1 -1
- package/dist/pipeline/buildHelpers.d.ts +3 -0
- package/dist/pipeline/buildHelpers.d.ts.map +1 -1
- package/dist/pipeline/buildHelpers.js +7 -2
- package/dist/pipeline/buildHelpers.js.map +1 -1
- package/dist/pipeline/calloutLayout.d.ts.map +1 -1
- package/dist/pipeline/calloutLayout.js +33 -2
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/pipeline/chips.d.ts.map +1 -1
- package/dist/pipeline/chips.js +4 -0
- package/dist/pipeline/chips.js.map +1 -1
- package/dist/pipeline/codeBlocks.d.ts +38 -0
- package/dist/pipeline/codeBlocks.d.ts.map +1 -0
- package/dist/pipeline/codeBlocks.js +146 -0
- package/dist/pipeline/codeBlocks.js.map +1 -0
- package/dist/pipeline/codeInline.d.ts +22 -0
- package/dist/pipeline/codeInline.d.ts.map +1 -0
- package/dist/pipeline/codeInline.js +93 -0
- package/dist/pipeline/codeInline.js.map +1 -0
- package/dist/pipeline/codeLines.d.ts +91 -0
- package/dist/pipeline/codeLines.d.ts.map +1 -0
- package/dist/pipeline/codeLines.js +399 -0
- package/dist/pipeline/codeLines.js.map +1 -0
- package/dist/pipeline/config.d.ts.map +1 -1
- package/dist/pipeline/config.js +8 -1
- package/dist/pipeline/config.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts +9 -0
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +60 -2
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/pipeline/continuation.d.ts.map +1 -1
- package/dist/pipeline/continuation.js +9 -2
- package/dist/pipeline/continuation.js.map +1 -1
- package/dist/pipeline/dropCap.d.ts +128 -0
- package/dist/pipeline/dropCap.d.ts.map +1 -0
- package/dist/pipeline/dropCap.js +412 -0
- package/dist/pipeline/dropCap.js.map +1 -0
- package/dist/pipeline/hardBreaks.d.ts +19 -0
- package/dist/pipeline/hardBreaks.d.ts.map +1 -0
- package/dist/pipeline/hardBreaks.js +36 -0
- package/dist/pipeline/hardBreaks.js.map +1 -0
- package/dist/pipeline/headerFooter.d.ts.map +1 -1
- package/dist/pipeline/headerFooter.js +4 -1
- package/dist/pipeline/headerFooter.js.map +1 -1
- package/dist/pipeline/lineNumbers.d.ts +37 -0
- package/dist/pipeline/lineNumbers.d.ts.map +1 -0
- package/dist/pipeline/lineNumbers.js +441 -0
- package/dist/pipeline/lineNumbers.js.map +1 -0
- package/dist/pipeline/measureContentBlock.d.ts +31 -5
- package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
- package/dist/pipeline/measureContentBlock.js +274 -68
- package/dist/pipeline/measureContentBlock.js.map +1 -1
- package/dist/pipeline/partPalette.d.ts +1 -1
- package/dist/pipeline/partPalette.d.ts.map +1 -1
- package/dist/pipeline/partPalette.js +24 -2
- package/dist/pipeline/partPalette.js.map +1 -1
- package/dist/pipeline/styles.d.ts +18 -2
- package/dist/pipeline/styles.d.ts.map +1 -1
- package/dist/pipeline/styles.js +15 -3
- package/dist/pipeline/styles.js.map +1 -1
- package/dist/pipeline/tabs.d.ts +35 -0
- package/dist/pipeline/tabs.d.ts.map +1 -0
- package/dist/pipeline/tabs.js +99 -0
- package/dist/pipeline/tabs.js.map +1 -0
- package/dist/pipeline/toc.d.ts.map +1 -1
- package/dist/pipeline/toc.js +3 -38
- package/dist/pipeline/toc.js.map +1 -1
- package/dist/pipeline/verseLines.d.ts +78 -0
- package/dist/pipeline/verseLines.d.ts.map +1 -0
- package/dist/pipeline/verseLines.js +245 -0
- package/dist/pipeline/verseLines.js.map +1 -0
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +1 -0
- package/dist/preflight.js.map +1 -1
- package/dist/types.d.ts +560 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +240 -7
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js +6 -0
- package/dist/vdt.js.map +1 -1
- package/dist/word/docxRead.d.ts.map +1 -1
- package/dist/word/docxRead.js +15 -0
- package/dist/word/docxRead.js.map +1 -1
- package/dist/word/inline.d.ts +6 -1
- package/dist/word/inline.d.ts.map +1 -1
- package/dist/word/inline.js +35 -1
- package/dist/word/inline.js.map +1 -1
- package/dist/word/model.d.ts +14 -0
- package/dist/word/model.d.ts.map +1 -1
- package/dist/word/model.js.map +1 -1
- package/dist/word/template.d.ts +12 -3
- package/dist/word/template.d.ts.map +1 -1
- package/dist/word/template.js +13 -3
- package/dist/word/template.js.map +1 -1
- package/dist/word/toDocx.d.ts.map +1 -1
- package/dist/word/toDocx.js +52 -8
- package/dist/word/toDocx.js.map +1 -1
- package/dist/word/toMarkdown.d.ts +5 -1
- package/dist/word/toMarkdown.d.ts.map +1 -1
- package/dist/word/toMarkdown.js +117 -6
- package/dist/word/toMarkdown.js.map +1 -1
- package/dist/word/word.test.js +124 -2
- package/dist/word/word.test.js.map +1 -1
- package/dist/worker/fonts.d.ts +2 -1
- package/dist/worker/fonts.d.ts.map +1 -1
- package/dist/worker/fonts.js +6 -2
- package/dist/worker/fonts.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { attachEquationAnchors } from './equationLabels';
|
|
8
8
|
import { parseAttrBlobStrict, parseDirectiveAttrs } from './attrs';
|
|
9
9
|
import { extractInlineMath, fixMathSourceMap, injectMathSpans } from './inlineMath';
|
|
10
|
-
import { BREAK_PLACEHOLDER, TITLE_BREAK_RE, extractInlineChips, extractInlineFootnotes, extractInlineRefs, injectFootnoteSpans, extractInlineSwatches, injectChipSpans, injectRefSpans, injectSwatchSpans, parseInlineFormatting, protectCodeSpans, titleBreakIndices, trimSpans } from './inlineFormatting';
|
|
10
|
+
import { BREAK_PLACEHOLDER, TITLE_BREAK_RE, extractInlineChips, extractInlineFootnotes, extractInlineRefs, injectFootnoteSpans, extractInlineSwatches, injectChipSpans, injectRefSpans, injectSwatchSpans, parseInlineFormatting, protectCodeSpans, replaceBodyBreaks, BODY_BREAK_MARK, bodyBreakSpans, titleBreakIndices, trimSpans, extractInlineTabs, injectTabSpans } from './inlineFormatting';
|
|
11
11
|
import { buildBlockMapping } from './sourceMapping';
|
|
12
12
|
import { extractInlineCitations, injectCitationSpans } from './citations';
|
|
13
13
|
import { attachIndexMarks, extractIndexMarks, remapParseOffsets } from './indexMarks';
|
|
@@ -15,13 +15,18 @@ import { joinEastAsianLines } from './softBreaks';
|
|
|
15
15
|
import { asciiDigits } from '../arabicNumerals';
|
|
16
16
|
import { indentColumn, listDepth, nestListItem } from './listNesting';
|
|
17
17
|
import { parseComicFence } from '../comics/page';
|
|
18
|
+
import { closesCodeFence, codeFenceOpen } from './codeFence';
|
|
18
19
|
export { parseDirectiveAttrs, spaceDirectiveLines, MAX_SPACE_LINES } from './attrs';
|
|
20
|
+
export { codeFenceOpen } from './codeFence';
|
|
19
21
|
const HEADING_RE = /^(#{1,6})\s+(.+)$/;
|
|
20
22
|
/** `:::name` or `:::name{attrs}` on its own line. Shared by single-line
|
|
21
23
|
* directives and container opening fences. */
|
|
22
24
|
const DIRECTIVE_RE = /^:::\s*([a-z][a-z0-9-]*)\s*(?:\{([^}]*)\})?\s*$/;
|
|
23
25
|
/** A bare `:::` line: closes the innermost open container. */
|
|
24
26
|
const CONTAINER_CLOSE_RE = /^:::\s*$/;
|
|
27
|
+
/** A bayt's hemistichs cut at a `\\` with a space on each side (the
|
|
28
|
+
* Wikisource markup, #378). */
|
|
29
|
+
const HEMISTICH_WIKI_RE = /\s\\\\\s/;
|
|
25
30
|
/** Set of directive names recognized today. Unknown names fall through to
|
|
26
31
|
* paragraph-parsing and downstream warnings flag them. */
|
|
27
32
|
export const KNOWN_DIRECTIVES = new Set(['pagebreak', 'numbering', 'columnbreak', 'space', 'toc', 'index', 'bibliography', 'references', 'verse', 'page', 'strip']);
|
|
@@ -74,28 +79,81 @@ const INTERRUPTING_MATH_SINGLE_RE = /^\s*\$\$((?:(?!\$\$)[\s\S])+)\$\$\s*$/;
|
|
|
74
79
|
/** `::resource{id="..."}` block embed on its own line. Malformed variants
|
|
75
80
|
* (missing/empty id, extra attrs) fall through to paragraph parsing. */
|
|
76
81
|
const RESOURCE_DIRECTIVE_RE = /^::resource\s*\{id="([^"]+)"\}\s*$/;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
/** A fence's info string read (#624): the first word is the language
|
|
83
|
+
* (lower-cased); a `{…}` block after it, or `key=value` pairs, are
|
|
84
|
+
* attributes in the directive grammar; anything else after the language
|
|
85
|
+
* is the title (```` ```console Terminal ````). */
|
|
86
|
+
export function readCodeInfo(info) {
|
|
87
|
+
let rest = info.trim();
|
|
88
|
+
let lang;
|
|
89
|
+
const first = /^([^\s{]+)\s*/.exec(rest);
|
|
90
|
+
if (first) {
|
|
91
|
+
lang = first[1].toLowerCase();
|
|
92
|
+
rest = rest.slice(first[0].length);
|
|
93
|
+
}
|
|
94
|
+
let attrs = {};
|
|
95
|
+
const brace = /\{([^{}]*)\}\s*$/.exec(rest);
|
|
96
|
+
if (brace) {
|
|
97
|
+
attrs = parseDirectiveAttrs(brace[1]);
|
|
98
|
+
rest = rest.slice(0, brace.index).trim();
|
|
99
|
+
}
|
|
100
|
+
else if (/[=\uFF1D]/.test(rest)) {
|
|
101
|
+
attrs = parseDirectiveAttrs(rest);
|
|
102
|
+
rest = '';
|
|
103
|
+
}
|
|
104
|
+
const title = attrs.title !== undefined ? attrs.title : rest !== '' ? rest : undefined;
|
|
105
|
+
return { ...(lang ? { lang } : {}), attrs, ...(title !== undefined && title.trim() !== '' ? { title: title.trim() } : {}) };
|
|
106
|
+
}
|
|
107
|
+
/** How far a line of indented code is indented (#624): the length of the
|
|
108
|
+
* leading whitespace that reaches column 4 (a tab to the next multiple of
|
|
109
|
+
* 4), or -1 when it does not reach it. */
|
|
110
|
+
function indentedCodeCut(line) {
|
|
111
|
+
let col = 0;
|
|
112
|
+
for (let i = 0; i < line.length; i++) {
|
|
113
|
+
const c = line[i];
|
|
114
|
+
if (c === ' ')
|
|
115
|
+
col += 1;
|
|
116
|
+
else if (c === '\t')
|
|
117
|
+
col = col - (col % 4) + 4;
|
|
118
|
+
else
|
|
119
|
+
return -1;
|
|
120
|
+
if (col >= 4)
|
|
121
|
+
return i + 1;
|
|
122
|
+
}
|
|
123
|
+
return -1;
|
|
124
|
+
}
|
|
125
|
+
// Small LRU memo used by parseMarkdownMemo, keyed by the input string (one
|
|
126
|
+
// per way of reading it, see `ParseOptions`). The returned array and its
|
|
127
|
+
// ContentBlocks are treated as read-only by the rest of the pipeline. A few
|
|
128
|
+
// slots (not one) because a book is counted chapter by chapter —
|
|
129
|
+
// continuation, outline, warnings and the layout itself each parse the same
|
|
130
|
+
// few chapters in turn, and a single slot thrashed between them.
|
|
83
131
|
const PARSE_MEMO_SLOTS = 8;
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
132
|
+
const _parseMemos = new Map();
|
|
133
|
+
/** The options as one key: the default reading is `''`. */
|
|
134
|
+
function optionsKey(options) {
|
|
135
|
+
const fences = options?.fences !== false;
|
|
136
|
+
const indented = options?.indentedCode === true;
|
|
137
|
+
return fences && !indented ? '' : `${fences ? 'f' : 'n'}${indented ? 'i' : ''}`;
|
|
138
|
+
}
|
|
139
|
+
function parseMemoLookup(markdown, options) {
|
|
140
|
+
const key = optionsKey(options);
|
|
141
|
+
let memo = _parseMemos.get(key);
|
|
142
|
+
if (!memo)
|
|
143
|
+
_parseMemos.set(key, (memo = new Map()));
|
|
144
|
+
const hit = memo.get(markdown);
|
|
87
145
|
if (hit) {
|
|
88
146
|
// Refresh recency: a Map iterates in insertion order.
|
|
89
|
-
|
|
90
|
-
|
|
147
|
+
memo.delete(markdown);
|
|
148
|
+
memo.set(markdown, hit);
|
|
91
149
|
return hit;
|
|
92
150
|
}
|
|
93
|
-
const result = parseMarkdownWithIssues(markdown);
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
const oldest =
|
|
151
|
+
const result = parseMarkdownWithIssues(markdown, options);
|
|
152
|
+
memo.set(markdown, result);
|
|
153
|
+
if (memo.size > PARSE_MEMO_SLOTS) {
|
|
154
|
+
const oldest = memo.keys().next().value;
|
|
97
155
|
if (oldest !== undefined)
|
|
98
|
-
|
|
156
|
+
memo.delete(oldest);
|
|
99
157
|
}
|
|
100
158
|
return result;
|
|
101
159
|
}
|
|
@@ -107,30 +165,58 @@ export const PARSE_MEMO_CAPACITY = PARSE_MEMO_SLOTS;
|
|
|
107
165
|
* This avoids reparsing a document on each keystroke when upstream
|
|
108
166
|
* recomputes only because a sibling state changed.
|
|
109
167
|
*/
|
|
110
|
-
export function parseMarkdownMemo(markdown) {
|
|
111
|
-
return parseMemoLookup(markdown).blocks;
|
|
168
|
+
export function parseMarkdownMemo(markdown, options) {
|
|
169
|
+
return parseMemoLookup(markdown, options).blocks;
|
|
112
170
|
}
|
|
113
|
-
export function parseMarkdownWithIssuesMemo(markdown) {
|
|
114
|
-
return parseMemoLookup(markdown);
|
|
171
|
+
export function parseMarkdownWithIssuesMemo(markdown, options) {
|
|
172
|
+
return parseMemoLookup(markdown, options);
|
|
115
173
|
}
|
|
116
|
-
export function parseMarkdown(markdown) {
|
|
117
|
-
return parseMarkdownWithIssues(markdown).blocks;
|
|
174
|
+
export function parseMarkdown(markdown, options) {
|
|
175
|
+
return parseMarkdownWithIssues(markdown, options).blocks;
|
|
118
176
|
}
|
|
119
177
|
/**
|
|
120
178
|
* Merge consecutive blockquote lines into a single block, and consecutive
|
|
121
179
|
* non-blank, non-special lines into paragraphs. Index marks (`:index…`)
|
|
122
180
|
* are taken out first and attached to the blocks holding them
|
|
123
181
|
* (`ContentBlock.indexMarks`); every source offset still points into
|
|
124
|
-
* `markdown`.
|
|
182
|
+
* `markdown`. `options` picks how code is read (#624): fences on, indented
|
|
183
|
+
* code off by default.
|
|
125
184
|
*/
|
|
126
|
-
export function parseMarkdownWithIssues(markdown) {
|
|
127
|
-
const marks = extractIndexMarks(markdown);
|
|
185
|
+
export function parseMarkdownWithIssues(markdown, options) {
|
|
186
|
+
const marks = extractIndexMarks(markdown, { fences: options?.fences !== false });
|
|
128
187
|
if (!marks)
|
|
129
|
-
return attachDirections(attachContainerAnchors(attachEquationAnchors(parseBlocks(markdown), markdown)));
|
|
130
|
-
const result = parseBlocks(marks.text);
|
|
188
|
+
return attachInlineCode(attachDirections(attachContainerAnchors(attachEquationAnchors(parseBlocks(markdown, options), markdown))), markdown);
|
|
189
|
+
const result = parseBlocks(marks.text, options);
|
|
131
190
|
attachIndexMarks(result.blocks, marks.marks);
|
|
132
191
|
remapParseOffsets(result, marks.toOriginal);
|
|
133
|
-
return attachDirections(attachContainerAnchors(attachEquationAnchors(result, markdown)));
|
|
192
|
+
return attachInlineCode(attachDirections(attachContainerAnchors(attachEquationAnchors(result, markdown))), markdown);
|
|
193
|
+
}
|
|
194
|
+
/** Inline code (`` `x` ``, outside an escape) between two backticks on
|
|
195
|
+
* one line, as `protectCodeSpans` reads it. */
|
|
196
|
+
const INLINE_CODE_RE = /(?<!\\)`([^`\n]+?)`/g;
|
|
197
|
+
/** Text blocks whose inline code a layout may set in the code face. */
|
|
198
|
+
const INLINE_CODE_HOSTS = new Set(['heading', 'paragraph', 'blockquote', 'listItem']);
|
|
199
|
+
/**
|
|
200
|
+
* The inline code of each text block (#624): the source ranges of the text
|
|
201
|
+
* between the backticks of its code spans (`ContentBlock.inlineCode`), read
|
|
202
|
+
* from the block's own source. Blocks without a backtick are left alone.
|
|
203
|
+
*/
|
|
204
|
+
function attachInlineCode(result, markdown) {
|
|
205
|
+
for (const b of result.blocks) {
|
|
206
|
+
if (!INLINE_CODE_HOSTS.has(b.type) || b.verse || b.sourceEnd <= b.sourceStart)
|
|
207
|
+
continue;
|
|
208
|
+
const source = markdown.slice(b.sourceStart, b.sourceEnd);
|
|
209
|
+
if (!source.includes('`'))
|
|
210
|
+
continue;
|
|
211
|
+
const ranges = [];
|
|
212
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
213
|
+
for (let m = INLINE_CODE_RE.exec(source); m; m = INLINE_CODE_RE.exec(source)) {
|
|
214
|
+
ranges.push({ start: b.sourceStart + m.index + 1, end: b.sourceStart + m.index + 1 + m[1].length });
|
|
215
|
+
}
|
|
216
|
+
if (ranges.length > 0)
|
|
217
|
+
b.inlineCode = ranges;
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
134
220
|
}
|
|
135
221
|
/** The direction a `dir` attribute names (`{dir=rtl}`, any case), or
|
|
136
222
|
* undefined for none or a value other than `ltr` / `rtl`. */
|
|
@@ -179,8 +265,10 @@ function attachDirections(result) {
|
|
|
179
265
|
const d = own ?? inherited;
|
|
180
266
|
if (d)
|
|
181
267
|
b.direction = d;
|
|
182
|
-
|
|
183
|
-
|
|
268
|
+
// A poem's own `lang` (`:::verse{dir=ltr lang=en}`, #620).
|
|
269
|
+
const lang = (b.verse ? langAttr(b.verse.attrs) : undefined) ?? inheritedLang;
|
|
270
|
+
if (lang)
|
|
271
|
+
b.lang = lang;
|
|
184
272
|
}
|
|
185
273
|
return result;
|
|
186
274
|
}
|
|
@@ -220,7 +308,9 @@ function joinedLines(markdown, mapping) {
|
|
|
220
308
|
fixMathSourceMap(mapping.text, mapping.spans, mapping.sourceMap);
|
|
221
309
|
return joinEastAsianLines(markdown, mapping.text, mapping.spans, mapping.sourceMap) ?? mapping;
|
|
222
310
|
}
|
|
223
|
-
function parseBlocks(markdown) {
|
|
311
|
+
function parseBlocks(markdown, options) {
|
|
312
|
+
const fences = options?.fences !== false;
|
|
313
|
+
const indentedCode = options?.indentedCode === true;
|
|
224
314
|
const blocks = [];
|
|
225
315
|
const issues = [];
|
|
226
316
|
const rawLines = markdown.split('\n');
|
|
@@ -239,31 +329,82 @@ function parseBlocks(markdown) {
|
|
|
239
329
|
/** The spans, plain text and source map of a run of inline Markdown
|
|
240
330
|
* (`raw`, whose source runs from `srcStart` to `srcEnd`): chips,
|
|
241
331
|
* footnote markers, references, citations, swatches, maths and the
|
|
242
|
-
* inline formatting, as a paragraph's text is read.
|
|
243
|
-
|
|
332
|
+
* inline formatting, as a paragraph's text is read. With `hardBreaks`
|
|
333
|
+
* its forced line breaks (#620) become `BREAK_PLACEHOLDER` (see
|
|
334
|
+
* `replaceBodyBreaks`; `raw` joins its lines with a line feed after a
|
|
335
|
+
* line that ends in a backslash). Maths issues are reported once, by
|
|
336
|
+
* the reading with `report`. */
|
|
337
|
+
const inlineRun = (raw, srcStart, srcEnd, hardBreaks = false, report = true) => {
|
|
244
338
|
const chipExtract = extractInlineChips(protectCodeSpans(raw), srcStart);
|
|
245
339
|
const fnExtract = extractInlineFootnotes(chipExtract.cleaned, srcStart);
|
|
246
340
|
const refExtract = extractInlineRefs(fnExtract.cleaned, srcStart);
|
|
247
341
|
const citeExtract = extractInlineCitations(refExtract.cleaned, srcStart);
|
|
248
342
|
const swExtract = extractInlineSwatches(citeExtract.cleaned, srcStart);
|
|
249
343
|
const mathExtract = extractInlineMath(swExtract.cleaned, null, srcStart, srcEnd);
|
|
250
|
-
|
|
251
|
-
|
|
344
|
+
if (report)
|
|
345
|
+
issues.push(...mathExtract.issues);
|
|
346
|
+
// Tabs (#622): `:tab` and tab characters, outside code and maths.
|
|
347
|
+
const tabExtract = extractInlineTabs(mathExtract.cleaned);
|
|
348
|
+
const formatted = hardBreaks
|
|
349
|
+
? bodyBreakSpans(parseInlineFormatting(replaceBodyBreaks(tabExtract.cleaned, BODY_BREAK_MARK)))
|
|
350
|
+
: parseInlineFormatting(tabExtract.cleaned);
|
|
351
|
+
const rawSpans = injectChipSpans(injectFootnoteSpans(injectRefSpans(injectCitationSpans(injectSwatchSpans(injectMathSpans(injectTabSpans(formatted, tabExtract.tabs), mathExtract.maths), swExtract.swatches), citeExtract.citations), refExtract.refs), fnExtract.markers), chipExtract.chips);
|
|
252
352
|
return joinedLines(markdown, buildBlockMapping(markdown, srcStart, srcEnd, rawSpans));
|
|
253
353
|
};
|
|
254
354
|
/**
|
|
255
|
-
* A
|
|
256
|
-
* line
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
355
|
+
* A block's lines (trimmed) read as one run of inline Markdown with its
|
|
356
|
+
* forced line breaks (#620): joined with a space, or with a line feed
|
|
357
|
+
* after a line that ends in a backslash, for `replaceBodyBreaks`. When
|
|
358
|
+
* the block has a break, `literalBreaks` is the reading postext 1.22
|
|
359
|
+
* made of it (every line joined with a space, the backslashes printed).
|
|
360
|
+
*/
|
|
361
|
+
const breakingRun = (lines, srcStart, srcEnd) => {
|
|
362
|
+
let raw = '';
|
|
363
|
+
lines.forEach((l, k) => {
|
|
364
|
+
if (k > 0)
|
|
365
|
+
raw += lines[k - 1].endsWith('\\') ? '\n' : ' ';
|
|
366
|
+
raw += l;
|
|
367
|
+
});
|
|
368
|
+
if (!raw.includes('\\'))
|
|
369
|
+
return inlineRun(raw, srcStart, srcEnd);
|
|
370
|
+
const mapping = inlineRun(raw, srcStart, srcEnd, true);
|
|
371
|
+
if (!mapping.text.includes(BREAK_PLACEHOLDER))
|
|
372
|
+
return mapping;
|
|
373
|
+
const literal = inlineRun(lines.join(' '), srcStart, srcEnd, false, false);
|
|
374
|
+
return { ...mapping, literalBreaks: { text: literal.text, spans: literal.spans, sourceMap: literal.sourceMap } };
|
|
375
|
+
};
|
|
376
|
+
/** Poems in the line layout read so far (`VerseStanza.poem`). */
|
|
377
|
+
let poemCount = 0;
|
|
378
|
+
/**
|
|
379
|
+
* A `:::verse` poem whose fence is line `start` (#378, #620), up to the
|
|
380
|
+
* closing `:::` (an unclosed poem runs to the end of the text). The fence's
|
|
381
|
+
* `layout` picks how its lines are read: `bayt` (see {@link parseBayts}),
|
|
382
|
+
* `lines` (see {@link parseVerseLines}), or, unset or `auto`, `bayt` when
|
|
383
|
+
* any line carries a hemistich separator (`||`, a spaced `\\`) and
|
|
384
|
+
* `lines` otherwise.
|
|
264
385
|
*/
|
|
265
386
|
const parseVerse = (start, attrsRaw) => {
|
|
266
|
-
|
|
387
|
+
const attrs = parseDirectiveAttrs(attrsRaw);
|
|
388
|
+
let end = start + 1;
|
|
389
|
+
while (end < rawLines.length && !CONTAINER_CLOSE_RE.test(rawLines[end].trim()))
|
|
390
|
+
end++;
|
|
391
|
+
const layout = attrs.layout?.trim().toLowerCase();
|
|
392
|
+
const separated = rawLines.slice(start + 1, end).some((raw) => raw.includes('||') || HEMISTICH_WIKI_RE.test(raw));
|
|
393
|
+
if (layout === 'bayt' || (layout !== 'lines' && separated))
|
|
394
|
+
return parseBayts(start, end, attrs);
|
|
395
|
+
return parseVerseLines(start, end, attrs, layout !== 'lines');
|
|
396
|
+
};
|
|
397
|
+
/**
|
|
398
|
+
* A poem in the bayt layout (#378): every non-blank line is a bayt, its
|
|
399
|
+
* two hemistichs split at the first `||` (or a `\\` with a space on
|
|
400
|
+
* each side, the Wikisource markup); a line without one is a single
|
|
401
|
+
* hemistich. The poem is one paragraph block (`ContentBlock.verse`) whose
|
|
402
|
+
* text joins the hemistichs with a tab and the bayts with a line feed;
|
|
403
|
+
* each hemistich is read as inline Markdown on its own, and the tab maps
|
|
404
|
+
* to the separator in the source, the line feed to the line's end. A
|
|
405
|
+
* poem with no line is dropped.
|
|
406
|
+
*/
|
|
407
|
+
const parseBayts = (start, end, attrs) => {
|
|
267
408
|
let text = '';
|
|
268
409
|
const spans = [];
|
|
269
410
|
const sourceMap = [];
|
|
@@ -284,17 +425,15 @@ function parseBlocks(markdown) {
|
|
|
284
425
|
sourceMap.push(...run.sourceMap);
|
|
285
426
|
};
|
|
286
427
|
let lastLine = start;
|
|
287
|
-
for (; k <
|
|
428
|
+
for (let k = start + 1; k < end; k++) {
|
|
288
429
|
const raw = rawLines[k];
|
|
289
|
-
if (CONTAINER_CLOSE_RE.test(raw.trim()))
|
|
290
|
-
break;
|
|
291
430
|
if (raw.trim() === '')
|
|
292
431
|
continue;
|
|
293
432
|
if (text !== '')
|
|
294
433
|
sep('\n', lineOffsets[k] - 1);
|
|
295
434
|
const lineStart = lineOffsets[k];
|
|
296
435
|
const bar = raw.indexOf('||');
|
|
297
|
-
const wiki = bar < 0 ?
|
|
436
|
+
const wiki = bar < 0 ? HEMISTICH_WIKI_RE.exec(raw) : null;
|
|
298
437
|
const cut = bar >= 0 ? bar : wiki ? wiki.index + 1 : -1;
|
|
299
438
|
if (cut >= 0) {
|
|
300
439
|
hemistich(lineStart, lineStart + cut);
|
|
@@ -306,24 +445,157 @@ function parseBlocks(markdown) {
|
|
|
306
445
|
}
|
|
307
446
|
lastLine = k;
|
|
308
447
|
}
|
|
309
|
-
const closed =
|
|
310
|
-
const next = closed ?
|
|
448
|
+
const closed = end < rawLines.length;
|
|
449
|
+
const next = closed ? end + 1 : end;
|
|
311
450
|
if (text === '')
|
|
312
|
-
return { next };
|
|
313
|
-
const attrs = parseDirectiveAttrs(attrsRaw);
|
|
451
|
+
return { blocks: [], next };
|
|
314
452
|
return {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
453
|
+
blocks: [{
|
|
454
|
+
type: 'paragraph',
|
|
455
|
+
text,
|
|
456
|
+
spans,
|
|
457
|
+
verse: { attrs },
|
|
458
|
+
sourceStart: lineOffsets[start],
|
|
459
|
+
sourceEnd: closed ? lineEndOffset(end) : lineEndOffset(lastLine),
|
|
460
|
+
sourceMap,
|
|
461
|
+
}],
|
|
324
462
|
next,
|
|
325
463
|
};
|
|
326
464
|
};
|
|
465
|
+
/**
|
|
466
|
+
* A poem in the line layout (#620): every non-blank line is a line of
|
|
467
|
+
* verse, read as inline Markdown on its own; a run of blank lines ends a
|
|
468
|
+
* stanza. Each stanza is a paragraph block (`VerseInfo.stanza`) whose text
|
|
469
|
+
* joins its lines with a line feed, mapped to the source's line end. A
|
|
470
|
+
* line's leading whitespace is its indent (`VerseLineInfo.indent`), kept
|
|
471
|
+
* out of the text; a line written `+ …` is a stepped line. With
|
|
472
|
+
* `keepSpaces` on the fence, a run of two or more spaces inside a line (a
|
|
473
|
+
* caesura) is kept as a fixed space of its width. The first stanza opens
|
|
474
|
+
* on the fence line, the last closes on the closing fence.
|
|
475
|
+
*/
|
|
476
|
+
const parseVerseLines = (start, end, attrs, auto) => {
|
|
477
|
+
const closed = end < rawLines.length;
|
|
478
|
+
const next = closed ? end + 1 : end;
|
|
479
|
+
const keepSpaces = attrs.keepSpaces !== undefined && attrs.keepSpaces.trim().toLowerCase() !== 'false';
|
|
480
|
+
// The stanzas, as runs of source line indices.
|
|
481
|
+
const groups = [];
|
|
482
|
+
let run = [];
|
|
483
|
+
for (let k = start + 1; k < end; k++) {
|
|
484
|
+
if (rawLines[k].trim() === '') {
|
|
485
|
+
if (run.length > 0)
|
|
486
|
+
groups.push(run);
|
|
487
|
+
run = [];
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
run.push(k);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (run.length > 0)
|
|
494
|
+
groups.push(run);
|
|
495
|
+
const stanzas = [];
|
|
496
|
+
for (const rows of groups) {
|
|
497
|
+
const st = { text: '', spans: [], sourceMap: [], lines: [], from: rows[0], to: rows[0] };
|
|
498
|
+
for (const k of rows) {
|
|
499
|
+
const raw = rawLines[k];
|
|
500
|
+
const lineStart = lineOffsets[k];
|
|
501
|
+
let p = 0;
|
|
502
|
+
let indent = 0;
|
|
503
|
+
for (; p < raw.length; p++) {
|
|
504
|
+
const c = raw[p];
|
|
505
|
+
if (c === ' ')
|
|
506
|
+
indent += 1;
|
|
507
|
+
else if (c === '\t')
|
|
508
|
+
indent += 4;
|
|
509
|
+
else if (c === '\u3000')
|
|
510
|
+
indent += 2;
|
|
511
|
+
else
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
let stepped = false;
|
|
515
|
+
// `\+ …`: a line that opens with a plus sign, not a stepped one.
|
|
516
|
+
if (raw[p] === '\\' && raw[p + 1] === '+')
|
|
517
|
+
p++;
|
|
518
|
+
else if (raw[p] === '+' && (raw[p + 1] === ' ' || raw[p + 1] === '\t')) {
|
|
519
|
+
stepped = true;
|
|
520
|
+
p++;
|
|
521
|
+
while (raw[p] === ' ' || raw[p] === '\t')
|
|
522
|
+
p++;
|
|
523
|
+
}
|
|
524
|
+
const bodyEnd = raw.trimEnd().length;
|
|
525
|
+
// The line's pieces of text, and the caesura gaps between them.
|
|
526
|
+
const pieces = [];
|
|
527
|
+
if (keepSpaces) {
|
|
528
|
+
const gapRe = / {2,}/g;
|
|
529
|
+
let at = p;
|
|
530
|
+
for (let m = gapRe.exec(raw); m && m.index < bodyEnd; m = gapRe.exec(raw)) {
|
|
531
|
+
if (m.index < p)
|
|
532
|
+
continue;
|
|
533
|
+
if (m.index > at)
|
|
534
|
+
pieces.push({ from: at, to: m.index, gap: false });
|
|
535
|
+
pieces.push({ from: m.index, to: m.index + m[0].length, gap: true });
|
|
536
|
+
at = m.index + m[0].length;
|
|
537
|
+
}
|
|
538
|
+
if (at < bodyEnd)
|
|
539
|
+
pieces.push({ from: at, to: bodyEnd, gap: false });
|
|
540
|
+
}
|
|
541
|
+
else if (p < bodyEnd) {
|
|
542
|
+
pieces.push({ from: p, to: bodyEnd, gap: false });
|
|
543
|
+
}
|
|
544
|
+
let text = '';
|
|
545
|
+
const spans = [];
|
|
546
|
+
const sourceMap = [];
|
|
547
|
+
for (const piece of pieces) {
|
|
548
|
+
if (piece.gap) {
|
|
549
|
+
if (text === '')
|
|
550
|
+
continue;
|
|
551
|
+
const width = piece.to - piece.from;
|
|
552
|
+
text += ' '.repeat(width);
|
|
553
|
+
spans.push({ text: ' '.repeat(width), bold: false, italic: false, fixedSpace: true });
|
|
554
|
+
for (let c = piece.from; c < piece.to; c++)
|
|
555
|
+
sourceMap.push(lineStart + c);
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
const r = inlineRun(raw.slice(piece.from, piece.to), lineStart + piece.from, lineStart + piece.to);
|
|
559
|
+
text += r.text;
|
|
560
|
+
spans.push(...r.spans);
|
|
561
|
+
sourceMap.push(...r.sourceMap);
|
|
562
|
+
}
|
|
563
|
+
if (text.trim() === '')
|
|
564
|
+
continue;
|
|
565
|
+
if (st.text !== '') {
|
|
566
|
+
st.text += '\n';
|
|
567
|
+
st.spans.push({ text: '\n', bold: false, italic: false });
|
|
568
|
+
st.sourceMap.push(lineStart - 1);
|
|
569
|
+
}
|
|
570
|
+
st.text += text;
|
|
571
|
+
st.spans.push(...spans);
|
|
572
|
+
st.sourceMap.push(...sourceMap);
|
|
573
|
+
st.lines.push({ indent, ...(stepped ? { stepped: true } : {}) });
|
|
574
|
+
st.to = k;
|
|
575
|
+
}
|
|
576
|
+
if (st.lines.length > 0)
|
|
577
|
+
stanzas.push(st);
|
|
578
|
+
}
|
|
579
|
+
if (stanzas.length === 0)
|
|
580
|
+
return { blocks: [], next };
|
|
581
|
+
const poem = poemCount++;
|
|
582
|
+
let firstLine = 0;
|
|
583
|
+
const blocks = stanzas.map((st, index) => {
|
|
584
|
+
const last = index === stanzas.length - 1;
|
|
585
|
+
const stanza = { poem, index, last, firstLine, lines: st.lines, ...(auto ? { auto: true } : {}) };
|
|
586
|
+
firstLine += st.lines.length;
|
|
587
|
+
return {
|
|
588
|
+
type: 'paragraph',
|
|
589
|
+
text: st.text,
|
|
590
|
+
spans: st.spans,
|
|
591
|
+
verse: { attrs, stanza },
|
|
592
|
+
sourceStart: index === 0 ? lineOffsets[start] : lineOffsets[st.from],
|
|
593
|
+
sourceEnd: last && closed ? lineEndOffset(end) : lineEndOffset(st.to),
|
|
594
|
+
sourceMap: st.sourceMap,
|
|
595
|
+
};
|
|
596
|
+
});
|
|
597
|
+
return { blocks, next };
|
|
598
|
+
};
|
|
327
599
|
// Open fenced containers, innermost last. Each entry remembers what it
|
|
328
600
|
// needs to emit the matching `containerEnd` (or an `unclosedContainer`
|
|
329
601
|
// issue pointing at the opening line when EOF arrives first).
|
|
@@ -360,6 +632,98 @@ function parseBlocks(markdown) {
|
|
|
360
632
|
* lines (the pipeline numbers adjacent items as one list); any other
|
|
361
633
|
* block closes it. */
|
|
362
634
|
let openListItems = [];
|
|
635
|
+
/**
|
|
636
|
+
* A code block (#624) of source lines `from` … `to - 1`, each cut at
|
|
637
|
+
* `cuts[k]` characters from its start (the fence's indentation, the four
|
|
638
|
+
* columns of indented code); the block's range runs from `srcStart` to
|
|
639
|
+
* `srcEnd`. The lines are kept as written (a carriage return before the
|
|
640
|
+
* line feed dropped), joined with a line feed mapped to the source's
|
|
641
|
+
* line end.
|
|
642
|
+
*/
|
|
643
|
+
const codeBlock = (from, to, cuts, srcStart, srcEnd, info) => {
|
|
644
|
+
const lines = [];
|
|
645
|
+
const lineStarts = [];
|
|
646
|
+
let text = '';
|
|
647
|
+
const sourceMap = [];
|
|
648
|
+
for (let k = from; k < to; k++) {
|
|
649
|
+
const raw = rawLines[k];
|
|
650
|
+
const cut = Math.min(cuts[k - from] ?? 0, raw.length);
|
|
651
|
+
const body = raw.slice(cut).replace(/\r$/, '');
|
|
652
|
+
const at = lineOffsets[k] + cut;
|
|
653
|
+
if (k > from) {
|
|
654
|
+
text += '\n';
|
|
655
|
+
sourceMap.push(lineEndOffset(k - 1));
|
|
656
|
+
}
|
|
657
|
+
lines.push(body);
|
|
658
|
+
lineStarts.push(at);
|
|
659
|
+
text += body;
|
|
660
|
+
for (let j = 0; j < body.length; j++)
|
|
661
|
+
sourceMap.push(at + j);
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
type: 'code',
|
|
665
|
+
text,
|
|
666
|
+
spans: text.length > 0 ? [{ text, bold: false, italic: false }] : [],
|
|
667
|
+
code: { ...info, lines, lineStarts },
|
|
668
|
+
sourceStart: srcStart,
|
|
669
|
+
sourceEnd: srcEnd,
|
|
670
|
+
sourceMap,
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
/** A fenced code block (#624) opening at line `start`, up to its closing
|
|
674
|
+
* fence; a fence left open runs to the end of the text and is reported
|
|
675
|
+
* (`unclosedCodeBlock`). */
|
|
676
|
+
const parseCodeFence = (start, open) => {
|
|
677
|
+
let end = start + 1;
|
|
678
|
+
while (end < rawLines.length && !closesCodeFence(rawLines[end], open.marker))
|
|
679
|
+
end++;
|
|
680
|
+
const closed = end < rawLines.length;
|
|
681
|
+
const cuts = [];
|
|
682
|
+
for (let k = start + 1; k < end; k++) {
|
|
683
|
+
// The fence's indentation comes off each line, as far as it has
|
|
684
|
+
// leading spaces.
|
|
685
|
+
const lead = /^ */.exec(rawLines[k])[0].length;
|
|
686
|
+
cuts.push(Math.min(open.indent, lead));
|
|
687
|
+
}
|
|
688
|
+
const fence = open.marker[0] === '`' ? '```' : '~~~';
|
|
689
|
+
const info = readCodeInfo(open.info);
|
|
690
|
+
const srcStart = lineOffsets[start];
|
|
691
|
+
const srcEnd = closed ? lineEndOffset(end) : lineEndOffset(rawLines.length - 1);
|
|
692
|
+
blocks.push(codeBlock(start + 1, end, cuts, srcStart, srcEnd, { ...info, info: open.info, fence }));
|
|
693
|
+
if (!closed) {
|
|
694
|
+
issues.push({
|
|
695
|
+
kind: 'unclosedCodeBlock',
|
|
696
|
+
delimiter: fence,
|
|
697
|
+
...(info.lang ? { lang: info.lang } : {}),
|
|
698
|
+
sourceStart: srcStart,
|
|
699
|
+
sourceEnd: lineEndOffset(start),
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
return closed ? end + 1 : end;
|
|
703
|
+
};
|
|
704
|
+
/** Indented code (#624, `codeStyle.indentedCode`): the run of lines from
|
|
705
|
+
* `start` indented four columns or blank, its trailing blank lines left
|
|
706
|
+
* out. */
|
|
707
|
+
const parseIndentedCode = (start) => {
|
|
708
|
+
let end = start;
|
|
709
|
+
let last = start;
|
|
710
|
+
while (end < rawLines.length) {
|
|
711
|
+
const raw = rawLines[end];
|
|
712
|
+
if (raw.trim() === '') {
|
|
713
|
+
end++;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (indentedCodeCut(raw) < 0)
|
|
717
|
+
break;
|
|
718
|
+
last = end;
|
|
719
|
+
end++;
|
|
720
|
+
}
|
|
721
|
+
const cuts = [];
|
|
722
|
+
for (let k = start; k <= last; k++)
|
|
723
|
+
cuts.push(Math.max(0, indentedCodeCut(rawLines[k])));
|
|
724
|
+
blocks.push(codeBlock(start, last + 1, cuts, lineOffsets[start], lineEndOffset(last), { info: '', attrs: {}, fence: 'indent' }));
|
|
725
|
+
return last + 1;
|
|
726
|
+
};
|
|
363
727
|
let i = 0;
|
|
364
728
|
while (i < rawLines.length) {
|
|
365
729
|
const line = rawLines[i];
|
|
@@ -369,6 +733,22 @@ function parseBlocks(markdown) {
|
|
|
369
733
|
i++;
|
|
370
734
|
continue;
|
|
371
735
|
}
|
|
736
|
+
// A code block (#624): a ``` or ~~~ fence, everything up to its
|
|
737
|
+
// closing fence kept as written.
|
|
738
|
+
const fenceOpen = fences ? codeFenceOpen(line) : undefined;
|
|
739
|
+
if (fenceOpen) {
|
|
740
|
+
i = parseCodeFence(i, fenceOpen);
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
// Indented code, when the reading asks for it: four columns in, after
|
|
744
|
+
// a blank line (or at the start), and not a list item under a list.
|
|
745
|
+
if (indentedCode
|
|
746
|
+
&& (i === 0 || rawLines[i - 1].trim() === '')
|
|
747
|
+
&& indentedCodeCut(line) >= 0
|
|
748
|
+
&& !(blocks[blocks.length - 1]?.type === 'listItem' && (TASK_ITEM_RE.test(line) || ORDERED_LIST_ITEM_RE.test(line) || LIST_ITEM_RE.test(line)))) {
|
|
749
|
+
i = parseIndentedCode(i);
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
372
752
|
// Display math — single-line `$$ ... $$`
|
|
373
753
|
const singleMath = line.match(BLOCK_MATH_SINGLE_RE);
|
|
374
754
|
if (singleMath) {
|
|
@@ -518,11 +898,11 @@ function parseBlocks(markdown) {
|
|
|
518
898
|
i++;
|
|
519
899
|
continue;
|
|
520
900
|
}
|
|
521
|
-
// A poem (#378): `:::verse{attrs}` … `:::`,
|
|
901
|
+
// A poem (#378, #620): `:::verse{attrs}` … `:::`, a bayt or a line of
|
|
902
|
+
// verse a line.
|
|
522
903
|
if (refsMatch && refsMatch[1] === 'verse') {
|
|
523
904
|
const verse = parseVerse(i, refsMatch[2] ?? '');
|
|
524
|
-
|
|
525
|
-
blocks.push(verse.block);
|
|
905
|
+
blocks.push(...verse.blocks);
|
|
526
906
|
i = verse.next;
|
|
527
907
|
continue;
|
|
528
908
|
}
|
|
@@ -686,20 +1066,13 @@ function parseBlocks(markdown) {
|
|
|
686
1066
|
}
|
|
687
1067
|
const contentOffset = leading + markerLength;
|
|
688
1068
|
const itemSrcStart = srcStart + contentOffset;
|
|
689
|
-
|
|
690
|
-
const
|
|
691
|
-
const refExtract = extractInlineRefs(fnExtract.cleaned, itemSrcStart);
|
|
692
|
-
const citeExtract = extractInlineCitations(refExtract.cleaned, itemSrcStart);
|
|
693
|
-
const swExtract = extractInlineSwatches(citeExtract.cleaned, itemSrcStart);
|
|
694
|
-
const mathExtract = extractInlineMath(swExtract.cleaned, null, itemSrcStart, srcEnd);
|
|
695
|
-
issues.push(...mathExtract.issues);
|
|
696
|
-
const rawSpans = injectChipSpans(injectFootnoteSpans(injectRefSpans(injectCitationSpans(injectSwatchSpans(injectMathSpans(parseInlineFormatting(mathExtract.cleaned), mathExtract.maths), swExtract.swatches), citeExtract.citations), refExtract.refs), fnExtract.markers), chipExtract.chips);
|
|
697
|
-
const mapping = buildBlockMapping(markdown, itemSrcStart, srcEnd, rawSpans);
|
|
698
|
-
fixMathSourceMap(mapping.text, mapping.spans, mapping.sourceMap);
|
|
1069
|
+
// A `\\` in the item is a forced line break (#620).
|
|
1070
|
+
const mapping = breakingRun([itemText], itemSrcStart, srcEnd);
|
|
699
1071
|
const block = {
|
|
700
1072
|
type: 'listItem',
|
|
701
1073
|
text: mapping.text,
|
|
702
1074
|
spans: mapping.spans.length > 0 ? mapping.spans : [{ text: '', bold: false, italic: false }],
|
|
1075
|
+
...(mapping.literalBreaks ? { literalBreaks: mapping.literalBreaks } : {}),
|
|
703
1076
|
depth,
|
|
704
1077
|
listKind,
|
|
705
1078
|
sourceStart: srcStart,
|
|
@@ -742,17 +1115,9 @@ function parseBlocks(markdown) {
|
|
|
742
1115
|
}
|
|
743
1116
|
const srcStart = lineOffsets[startIdx];
|
|
744
1117
|
const srcEnd = lineEndOffset(lastIdx);
|
|
745
|
-
const chipExtract = extractInlineChips(protectCodeSpans(quoteLines.join(' ')), srcStart);
|
|
746
|
-
const fnExtract = extractInlineFootnotes(chipExtract.cleaned, srcStart);
|
|
747
|
-
const refExtract = extractInlineRefs(fnExtract.cleaned, srcStart);
|
|
748
|
-
const citeExtract = extractInlineCitations(refExtract.cleaned, srcStart);
|
|
749
|
-
const swExtract = extractInlineSwatches(citeExtract.cleaned, srcStart);
|
|
750
|
-
const mathExtract = extractInlineMath(swExtract.cleaned, null, srcStart, srcEnd);
|
|
751
|
-
issues.push(...mathExtract.issues);
|
|
752
|
-
const rawSpans = injectChipSpans(injectFootnoteSpans(injectRefSpans(injectCitationSpans(injectSwatchSpans(injectMathSpans(parseInlineFormatting(mathExtract.cleaned), mathExtract.maths), swExtract.swatches), citeExtract.citations), refExtract.refs), fnExtract.markers), chipExtract.chips);
|
|
753
1118
|
// Lines join with a space, except between Chinese or Japanese
|
|
754
|
-
// characters (#181).
|
|
755
|
-
const mapping =
|
|
1119
|
+
// characters (#181) and at a forced line break (#620).
|
|
1120
|
+
const mapping = breakingRun(quoteLines, srcStart, srcEnd);
|
|
756
1121
|
blocks.push({
|
|
757
1122
|
type: 'blockquote',
|
|
758
1123
|
text: mapping.text,
|
|
@@ -760,6 +1125,7 @@ function parseBlocks(markdown) {
|
|
|
760
1125
|
sourceStart: srcStart,
|
|
761
1126
|
sourceEnd: srcEnd,
|
|
762
1127
|
sourceMap: mapping.sourceMap,
|
|
1128
|
+
...(mapping.literalBreaks ? { literalBreaks: mapping.literalBreaks } : {}),
|
|
763
1129
|
});
|
|
764
1130
|
continue;
|
|
765
1131
|
}
|
|
@@ -777,6 +1143,9 @@ function parseBlocks(markdown) {
|
|
|
777
1143
|
break;
|
|
778
1144
|
if (i > startIdx && isFenceLine(pl))
|
|
779
1145
|
break;
|
|
1146
|
+
// A code fence interrupts the paragraph (#624).
|
|
1147
|
+
if (i > startIdx && fences && codeFenceOpen(rawLine))
|
|
1148
|
+
break;
|
|
780
1149
|
// A footnote definition opens a paragraph of its own.
|
|
781
1150
|
if (i > startIdx && FOOTNOTE_DEF_RE.test(pl))
|
|
782
1151
|
break;
|
|
@@ -804,11 +1173,12 @@ function parseBlocks(markdown) {
|
|
|
804
1173
|
srcStart += rawLines[startIdx].indexOf(def[0]) + def[0].length;
|
|
805
1174
|
}
|
|
806
1175
|
const srcEnd = lineEndOffset(lastIdx);
|
|
807
|
-
const mapping =
|
|
1176
|
+
const mapping = breakingRun(paraLines, srcStart, srcEnd);
|
|
808
1177
|
blocks.push({
|
|
809
1178
|
type: 'paragraph',
|
|
810
1179
|
text: mapping.text,
|
|
811
1180
|
spans: mapping.spans,
|
|
1181
|
+
...(mapping.literalBreaks ? { literalBreaks: mapping.literalBreaks } : {}),
|
|
812
1182
|
...(startIdx === lastDisplayEnd + 1 && !def ? { continuesParagraph: true } : {}),
|
|
813
1183
|
...(def ? { footnoteDef: def[1] } : {}),
|
|
814
1184
|
sourceStart: srcStart,
|