@ai-react-markdown/engine 2.4.3 → 2.4.5
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/README.md +95 -0
- package/dist/index.cjs +116 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.dev.cjs +116 -33
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +116 -33
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +116 -33
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/dist/index.d.cts
CHANGED
|
@@ -425,6 +425,49 @@ interface FreezeScanCheckpoint {
|
|
|
425
425
|
* tagBalance but not yet confirmed by a later `>` — reverted at the next
|
|
426
426
|
* blank line (a tag cannot span one). See TRUNCATED_TAG_RE. */
|
|
427
427
|
pendingTruncatedTags: string[];
|
|
428
|
+
/** Line-truncated CLOSING tags (`</div` + EOL, no `>`) inside an html-flow
|
|
429
|
+
* run, waiting for a `>` on a later line of the same run. Unlike opens
|
|
430
|
+
* they are NOT counted up front: a close tag cannot carry attributes, so
|
|
431
|
+
* `para </style` (prose to micromark, still-open element to parse5 —
|
|
432
|
+
* RAWTEXT waits for the `>`) must not zero the balance (2026-08-19
|
|
433
|
+
* review P1 — the boundary crossed an open `<style>`). Confirmed and
|
|
434
|
+
* applied only when a later line of the run brings the `>` (parse5
|
|
435
|
+
* completes the end tag; micromark's block ends at the blank anyway);
|
|
436
|
+
* dropped unapplied at the blank (element stays counted — over-block).
|
|
437
|
+
* Paragraph-context truncated closes are never pended: a `>` at block
|
|
438
|
+
* indent on the next line is a blockquote to micromark, and the one
|
|
439
|
+
* shape that would complete the inline close (`</b\n >`, a 4+-space
|
|
440
|
+
* lazy continuation) is not modelled — the element stays counted,
|
|
441
|
+
* over-block. */
|
|
442
|
+
pendingTruncatedCloses: string[];
|
|
443
|
+
/** An html-flow line ended inside a tag (`<div`, `</div`, `<br` — open,
|
|
444
|
+
* close or void, with or without attributes): parse5's tokenizer is
|
|
445
|
+
* still in that tag, so on the following lines everything up to the
|
|
446
|
+
* FIRST `>` is attribute garbage — `</div>` there does NOT close
|
|
447
|
+
* anything (oracle review of 2.4.4: `<div>\n<div>\n</div\n</div>` froze
|
|
448
|
+
* past the still-open outer div; pre-existing, 1-char slices). While
|
|
449
|
+
* set: a line without `>` gets no tag scan at all; the line with the
|
|
450
|
+
* `>` completes the pending close (if any), then only its text after
|
|
451
|
+
* that `>` is scanned. Cleared there and at the blank line. */
|
|
452
|
+
tagAcrossLines: boolean;
|
|
453
|
+
/** Indent of the line that set `tagAcrossLines`. A following line that
|
|
454
|
+
* DE-INDENTS below it may have left the container (a list item's html
|
|
455
|
+
* block ends there; hast-util-raw resets the tokenizer at the li/ul
|
|
456
|
+
* boundary, so a `<div>` on that line is a real start tag, not garbage)
|
|
457
|
+
* — or may still be the same html block (root-level ` </div\n</div>`,
|
|
458
|
+
* still garbage). Unknowable here → poison (oracle 3rd pass). */
|
|
459
|
+
tagAcrossLinesIndent: number;
|
|
460
|
+
/** The html-flow run since the last blank REALLY started as a micromark
|
|
461
|
+
* html block (type 6 / type 1 / a paragraph-not-interrupting type 7) —
|
|
462
|
+
* as opposed to `htmlFlowSinceBlank`, which any `<tag` / `</tag` line
|
|
463
|
+
* start sets (over-approximation, fine for its over-blocking uses). Only
|
|
464
|
+
* in a real run are the bytes raw to parse5 across line endings; the
|
|
465
|
+
* cross-line-tag garbage model (`tagAcrossLines`, pended closes) is
|
|
466
|
+
* gated on it — in a paragraph starting `</i` the next line's `<div>` /
|
|
467
|
+
* `<!--` are REAL blocks (oracle re-check of 2.4.4: gating on
|
|
468
|
+
* htmlFlowSinceBlank swallowed them — a new under-block). Sticky to the
|
|
469
|
+
* blank; a type 6/1 start on a later line of a non-real run promotes it. */
|
|
470
|
+
htmlFlowReal: boolean;
|
|
428
471
|
}
|
|
429
472
|
declare function computeFreezeBoundary(text: string, options: FreezeBoundaryOptions, resume?: FreezeScanCheckpoint | null): FreezeScanResult;
|
|
430
473
|
|
|
@@ -1902,6 +1945,20 @@ declare function preprocessLaTeX(str: string): string;
|
|
|
1902
1945
|
*/
|
|
1903
1946
|
declare function createIncrementalLatexPreprocessor(options?: {
|
|
1904
1947
|
freezeThreshold?: number;
|
|
1948
|
+
/** Failure backoff (default on). Tests that rely on `freezeThreshold: 0`
|
|
1949
|
+
* to attempt a freeze on EVERY call turn it off, so 1-char chunkings keep
|
|
1950
|
+
* exercising the cut rules instead of skipping most attempts; backoff
|
|
1951
|
+
* can only freeze LESS, so passing without it implies passing with it.
|
|
1952
|
+
* @internal */
|
|
1953
|
+
backoff?: boolean;
|
|
1954
|
+
/** Test hook: called once per freeze ATTEMPT (a scan of the active
|
|
1955
|
+
* region) with the region's length and how many source bytes the
|
|
1956
|
+
* attempt froze (0 = failed), so the backoff bound and freeze progress
|
|
1957
|
+
* can be pinned. @internal */
|
|
1958
|
+
onAttempt?: (info: {
|
|
1959
|
+
activeLength: number;
|
|
1960
|
+
frozenBytes: number;
|
|
1961
|
+
}) => void;
|
|
1905
1962
|
}): (content: string) => string;
|
|
1906
1963
|
|
|
1907
1964
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -425,6 +425,49 @@ interface FreezeScanCheckpoint {
|
|
|
425
425
|
* tagBalance but not yet confirmed by a later `>` — reverted at the next
|
|
426
426
|
* blank line (a tag cannot span one). See TRUNCATED_TAG_RE. */
|
|
427
427
|
pendingTruncatedTags: string[];
|
|
428
|
+
/** Line-truncated CLOSING tags (`</div` + EOL, no `>`) inside an html-flow
|
|
429
|
+
* run, waiting for a `>` on a later line of the same run. Unlike opens
|
|
430
|
+
* they are NOT counted up front: a close tag cannot carry attributes, so
|
|
431
|
+
* `para </style` (prose to micromark, still-open element to parse5 —
|
|
432
|
+
* RAWTEXT waits for the `>`) must not zero the balance (2026-08-19
|
|
433
|
+
* review P1 — the boundary crossed an open `<style>`). Confirmed and
|
|
434
|
+
* applied only when a later line of the run brings the `>` (parse5
|
|
435
|
+
* completes the end tag; micromark's block ends at the blank anyway);
|
|
436
|
+
* dropped unapplied at the blank (element stays counted — over-block).
|
|
437
|
+
* Paragraph-context truncated closes are never pended: a `>` at block
|
|
438
|
+
* indent on the next line is a blockquote to micromark, and the one
|
|
439
|
+
* shape that would complete the inline close (`</b\n >`, a 4+-space
|
|
440
|
+
* lazy continuation) is not modelled — the element stays counted,
|
|
441
|
+
* over-block. */
|
|
442
|
+
pendingTruncatedCloses: string[];
|
|
443
|
+
/** An html-flow line ended inside a tag (`<div`, `</div`, `<br` — open,
|
|
444
|
+
* close or void, with or without attributes): parse5's tokenizer is
|
|
445
|
+
* still in that tag, so on the following lines everything up to the
|
|
446
|
+
* FIRST `>` is attribute garbage — `</div>` there does NOT close
|
|
447
|
+
* anything (oracle review of 2.4.4: `<div>\n<div>\n</div\n</div>` froze
|
|
448
|
+
* past the still-open outer div; pre-existing, 1-char slices). While
|
|
449
|
+
* set: a line without `>` gets no tag scan at all; the line with the
|
|
450
|
+
* `>` completes the pending close (if any), then only its text after
|
|
451
|
+
* that `>` is scanned. Cleared there and at the blank line. */
|
|
452
|
+
tagAcrossLines: boolean;
|
|
453
|
+
/** Indent of the line that set `tagAcrossLines`. A following line that
|
|
454
|
+
* DE-INDENTS below it may have left the container (a list item's html
|
|
455
|
+
* block ends there; hast-util-raw resets the tokenizer at the li/ul
|
|
456
|
+
* boundary, so a `<div>` on that line is a real start tag, not garbage)
|
|
457
|
+
* — or may still be the same html block (root-level ` </div\n</div>`,
|
|
458
|
+
* still garbage). Unknowable here → poison (oracle 3rd pass). */
|
|
459
|
+
tagAcrossLinesIndent: number;
|
|
460
|
+
/** The html-flow run since the last blank REALLY started as a micromark
|
|
461
|
+
* html block (type 6 / type 1 / a paragraph-not-interrupting type 7) —
|
|
462
|
+
* as opposed to `htmlFlowSinceBlank`, which any `<tag` / `</tag` line
|
|
463
|
+
* start sets (over-approximation, fine for its over-blocking uses). Only
|
|
464
|
+
* in a real run are the bytes raw to parse5 across line endings; the
|
|
465
|
+
* cross-line-tag garbage model (`tagAcrossLines`, pended closes) is
|
|
466
|
+
* gated on it — in a paragraph starting `</i` the next line's `<div>` /
|
|
467
|
+
* `<!--` are REAL blocks (oracle re-check of 2.4.4: gating on
|
|
468
|
+
* htmlFlowSinceBlank swallowed them — a new under-block). Sticky to the
|
|
469
|
+
* blank; a type 6/1 start on a later line of a non-real run promotes it. */
|
|
470
|
+
htmlFlowReal: boolean;
|
|
428
471
|
}
|
|
429
472
|
declare function computeFreezeBoundary(text: string, options: FreezeBoundaryOptions, resume?: FreezeScanCheckpoint | null): FreezeScanResult;
|
|
430
473
|
|
|
@@ -1902,6 +1945,20 @@ declare function preprocessLaTeX(str: string): string;
|
|
|
1902
1945
|
*/
|
|
1903
1946
|
declare function createIncrementalLatexPreprocessor(options?: {
|
|
1904
1947
|
freezeThreshold?: number;
|
|
1948
|
+
/** Failure backoff (default on). Tests that rely on `freezeThreshold: 0`
|
|
1949
|
+
* to attempt a freeze on EVERY call turn it off, so 1-char chunkings keep
|
|
1950
|
+
* exercising the cut rules instead of skipping most attempts; backoff
|
|
1951
|
+
* can only freeze LESS, so passing without it implies passing with it.
|
|
1952
|
+
* @internal */
|
|
1953
|
+
backoff?: boolean;
|
|
1954
|
+
/** Test hook: called once per freeze ATTEMPT (a scan of the active
|
|
1955
|
+
* region) with the region's length and how many source bytes the
|
|
1956
|
+
* attempt froze (0 = failed), so the backoff bound and freeze progress
|
|
1957
|
+
* can be pinned. @internal */
|
|
1958
|
+
onAttempt?: (info: {
|
|
1959
|
+
activeLength: number;
|
|
1960
|
+
frozenBytes: number;
|
|
1961
|
+
}) => void;
|
|
1905
1962
|
}): (content: string) => string;
|
|
1906
1963
|
|
|
1907
1964
|
/**
|
package/dist/index.dev.cjs
CHANGED
|
@@ -269,6 +269,11 @@ var defaultUrlTransform = (value) => {
|
|
|
269
269
|
var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name");
|
|
270
270
|
var import_micromark_util_normalize_identifier = require("micromark-util-normalize-identifier");
|
|
271
271
|
var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
|
|
272
|
+
var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
|
|
273
|
+
var TYPE1_NAMES = /* @__PURE__ */ new Set(["script", "pre", "style", "textarea"]);
|
|
274
|
+
var TYPE6_START_RE = /^<\/?([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r]|\/?>|$)/;
|
|
275
|
+
var TYPE1_START_RE = /^<(script|pre|style|textarea)(?:[ \t\r]|>|$)/i;
|
|
276
|
+
var TYPE7_LINE_RE = /^<(\/?)([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r][^>]*|\/)?>[ \t\r]*$/;
|
|
272
277
|
var VOID_TAGS = /* @__PURE__ */ new Set([
|
|
273
278
|
"area",
|
|
274
279
|
"base",
|
|
@@ -407,7 +412,11 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
|
|
|
407
412
|
htmlFlowSinceBlank: false,
|
|
408
413
|
htmlSeamPending: false,
|
|
409
414
|
phasePoisonedAt: Infinity,
|
|
410
|
-
pendingTruncatedTags: []
|
|
415
|
+
pendingTruncatedTags: [],
|
|
416
|
+
pendingTruncatedCloses: [],
|
|
417
|
+
tagAcrossLines: false,
|
|
418
|
+
tagAcrossLinesIndent: 0,
|
|
419
|
+
htmlFlowReal: false
|
|
411
420
|
};
|
|
412
421
|
}
|
|
413
422
|
function isPlausibleLinkDefRest(rest) {
|
|
@@ -704,6 +713,8 @@ function processConfirmedLine(cp, ln, text) {
|
|
|
704
713
|
for (const tag of cp.pendingTruncatedTags) applyTag(tag, true);
|
|
705
714
|
cp.pendingTruncatedTags = [];
|
|
706
715
|
}
|
|
716
|
+
cp.pendingTruncatedCloses = [];
|
|
717
|
+
cp.tagAcrossLines = false;
|
|
707
718
|
cp.blankRun += 1;
|
|
708
719
|
cp.lastBlankStart = ln.start;
|
|
709
720
|
cp.candidates.push({
|
|
@@ -717,6 +728,7 @@ function processConfirmedLine(cp, ln, text) {
|
|
|
717
728
|
cp.paragraphHasUnpairedRun = false;
|
|
718
729
|
cp.openBracket = null;
|
|
719
730
|
cp.htmlFlowSinceBlank = false;
|
|
731
|
+
cp.htmlFlowReal = false;
|
|
720
732
|
cp.prevLineBlank = true;
|
|
721
733
|
cp.prevLineWasText = false;
|
|
722
734
|
cp.prevLineWasValidDef = false;
|
|
@@ -732,6 +744,16 @@ function processConfirmedLine(cp, ln, text) {
|
|
|
732
744
|
if (tagStart) {
|
|
733
745
|
cp.htmlFlowSinceBlank = true;
|
|
734
746
|
if (!TYPE6_NAMES.has(tagStart[1].toLowerCase())) cp.hazardVerdict = true;
|
|
747
|
+
if (!cp.htmlFlowReal) {
|
|
748
|
+
const t = mdTrimStart(ln.text);
|
|
749
|
+
const t6 = TYPE6_START_RE.exec(t);
|
|
750
|
+
const t7 = TYPE7_LINE_RE.exec(t);
|
|
751
|
+
if (t6 !== null && TYPE6_NAMES.has(t6[1].toLowerCase()) || TYPE1_START_RE.test(t) || // Type 7 cannot interrupt a paragraph, and excludes the raw-text
|
|
752
|
+
// names (those are type 1 as start tags, paragraph as end tags).
|
|
753
|
+
t7 !== null && !cp.prevLineWasText && !TYPE1_NAMES.has(t7[2].toLowerCase())) {
|
|
754
|
+
cp.htmlFlowReal = true;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
735
757
|
}
|
|
736
758
|
const inRawText = cp.htmlFlowSinceBlank || rawOpenAtLineStart;
|
|
737
759
|
const rawFlowStart = ln.indent <= 3 && /^<(?:!--|\?|![A-Za-z]|!\[CDATA\[)/.test(mdTrimStart(ln.text));
|
|
@@ -872,7 +894,21 @@ ${cont(scanText)}` };
|
|
|
872
894
|
for (const [from, to] of rawSpans) {
|
|
873
895
|
tagText = tagText.slice(0, from) + " ".repeat(to - from) + tagText.slice(to);
|
|
874
896
|
}
|
|
875
|
-
|
|
897
|
+
let skipTagScan = false;
|
|
898
|
+
if (cp.tagAcrossLines) {
|
|
899
|
+
if (ln.indent < cp.tagAcrossLinesIndent) poisonRawDivergence();
|
|
900
|
+
const gt = ln.text.indexOf(">");
|
|
901
|
+
if (gt === -1) {
|
|
902
|
+
skipTagScan = true;
|
|
903
|
+
} else {
|
|
904
|
+
if (/["']/.test(ln.text.slice(0, gt))) poisonRawDivergence();
|
|
905
|
+
for (const tag of cp.pendingTruncatedCloses) applyTag(tag, true);
|
|
906
|
+
cp.pendingTruncatedCloses = [];
|
|
907
|
+
cp.tagAcrossLines = false;
|
|
908
|
+
tagText = " ".repeat(gt + 1) + tagText.slice(gt + 1);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (!skipTagScan) {
|
|
876
912
|
TAG_OR_COMMENT_RE.lastIndex = 0;
|
|
877
913
|
let m;
|
|
878
914
|
let lastCommentOpenerIdx = -1;
|
|
@@ -902,6 +938,7 @@ ${cont(scanText)}` };
|
|
|
902
938
|
if (cp.commentOpen) continue;
|
|
903
939
|
const closing = m[1] === "/";
|
|
904
940
|
const tag = m[2].toLowerCase();
|
|
941
|
+
if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + m.index);
|
|
905
942
|
const selfClosing = m[3] !== void 0 && /\/\s*$/.test(m[3]);
|
|
906
943
|
if (VOID_TAGS.has(tag) || selfClosing) continue;
|
|
907
944
|
applyTag(tag, closing);
|
|
@@ -922,6 +959,7 @@ ${cont(scanText)}` };
|
|
|
922
959
|
if (startMasked || wholeVisible || inRaw(mr.index) || cp.commentOpen) continue;
|
|
923
960
|
const closing = mr[1] === "/";
|
|
924
961
|
const tag = mr[2].toLowerCase();
|
|
962
|
+
if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + mr.index);
|
|
925
963
|
const selfClosing = mr[3] !== void 0 && /\/\s*$/.test(mr[3]);
|
|
926
964
|
if (VOID_TAGS.has(tag) || selfClosing) continue;
|
|
927
965
|
applyTag(tag, closing);
|
|
@@ -937,7 +975,14 @@ ${cont(scanText)}` };
|
|
|
937
975
|
if (m2) {
|
|
938
976
|
const closing = m2[1] === "/";
|
|
939
977
|
const tag = m2[2].toLowerCase();
|
|
940
|
-
if (
|
|
978
|
+
if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastLt);
|
|
979
|
+
if (cp.htmlFlowReal) {
|
|
980
|
+
cp.tagAcrossLines = true;
|
|
981
|
+
cp.tagAcrossLinesIndent = ln.indent;
|
|
982
|
+
}
|
|
983
|
+
if (closing) {
|
|
984
|
+
if (!VOID_TAGS.has(tag) && cp.htmlFlowReal) cp.pendingTruncatedCloses.push(tag);
|
|
985
|
+
} else if (!VOID_TAGS.has(tag)) {
|
|
941
986
|
applyTag(tag, closing);
|
|
942
987
|
const rawLastLt = ln.text.lastIndexOf("<");
|
|
943
988
|
const rawTruncated = rawLastLt !== -1 && !ln.text.includes(">", rawLastLt);
|
|
@@ -1480,6 +1525,9 @@ function countTrailingNewlines(value) {
|
|
|
1480
1525
|
function countNewlines(text, end = text.length) {
|
|
1481
1526
|
let count = 0;
|
|
1482
1527
|
for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
|
|
1528
|
+
for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
|
|
1529
|
+
if (text.charCodeAt(i + 1) !== 10) count += 1;
|
|
1530
|
+
}
|
|
1483
1531
|
return count;
|
|
1484
1532
|
}
|
|
1485
1533
|
|
|
@@ -1599,7 +1647,7 @@ function normalizeId(s) {
|
|
|
1599
1647
|
return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
|
|
1600
1648
|
}
|
|
1601
1649
|
function normalizeForMatch(s) {
|
|
1602
|
-
return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s.replace(/\\(
|
|
1650
|
+
return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s.replace(/\\([!-/:-@[-`{-~])/g, "$1"));
|
|
1603
1651
|
}
|
|
1604
1652
|
|
|
1605
1653
|
// src/components/collectDefLabels.ts
|
|
@@ -4037,10 +4085,16 @@ function lineIndentBefore(content, pos) {
|
|
|
4037
4085
|
function findClosingBacktickRun(content, start, n) {
|
|
4038
4086
|
let i = start;
|
|
4039
4087
|
while (i < content.length) {
|
|
4040
|
-
|
|
4088
|
+
const ch = content[i];
|
|
4089
|
+
if (ch === "`") {
|
|
4041
4090
|
const runLen = getRepeatedMarkerLength(content, i, "`");
|
|
4042
4091
|
if (runLen === n) return i;
|
|
4043
4092
|
i += runLen;
|
|
4093
|
+
} else if (ch === "\n") {
|
|
4094
|
+
let j = i + 1;
|
|
4095
|
+
while (j < content.length && (content[j] === " " || content[j] === " " || content[j] === "\r")) j += 1;
|
|
4096
|
+
if (j >= content.length || content[j] === "\n") return -1;
|
|
4097
|
+
i += 1;
|
|
4044
4098
|
} else {
|
|
4045
4099
|
i += 1;
|
|
4046
4100
|
}
|
|
@@ -4162,16 +4216,20 @@ function escapeCurrencyDollarSigns(text) {
|
|
|
4162
4216
|
currentLineProcessed += segment;
|
|
4163
4217
|
}
|
|
4164
4218
|
let needEscape = true;
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4219
|
+
const restStart = match.index + 1;
|
|
4220
|
+
const restEnd = i < currencyMatches.length - 1 ? currencyMatches[i + 1].index : text.length;
|
|
4221
|
+
let firstLineBeforeNextMatch = "";
|
|
4222
|
+
if (restEnd - restStart > 0) {
|
|
4223
|
+
let eol = restEnd;
|
|
4224
|
+
for (let k = restStart; k < restEnd; k++) {
|
|
4225
|
+
const c = text.charCodeAt(k);
|
|
4226
|
+
if (c === 10 || c === 13) {
|
|
4227
|
+
eol = k;
|
|
4228
|
+
break;
|
|
4229
|
+
}
|
|
4170
4230
|
}
|
|
4171
|
-
|
|
4172
|
-
restBeforeNextMatchOrEnd = text.substring(match.index + 1);
|
|
4231
|
+
firstLineBeforeNextMatch = text.substring(restStart, eol);
|
|
4173
4232
|
}
|
|
4174
|
-
const firstLineBeforeNextMatch = restBeforeNextMatchOrEnd.split(/\r\n|\r|\n/g)[0];
|
|
4175
4233
|
if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
|
|
4176
4234
|
const wholeLineBeforeNextMatchWithoutCurrentDollar = currentLineProcessed + firstLineBeforeNextMatch;
|
|
4177
4235
|
if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
|
|
@@ -4337,39 +4395,49 @@ function hasUnclosedTextCommand(text) {
|
|
|
4337
4395
|
return false;
|
|
4338
4396
|
}
|
|
4339
4397
|
var RESIDUAL_OPEN_BRACKET_RE = /(?<!!)\\\[/;
|
|
4340
|
-
|
|
4398
|
+
var LEADING_DOUBLE_DOLLAR_RE = /^\s*\$\$/;
|
|
4399
|
+
function processSliceInstrumented(slice, probe = true) {
|
|
4341
4400
|
const segments = splitByProtectedRegions(slice);
|
|
4342
|
-
|
|
4401
|
+
const parts = [];
|
|
4343
4402
|
let quiescent = true;
|
|
4344
4403
|
let truncatedAtSeamStart = false;
|
|
4345
4404
|
for (let index = 0; index < segments.length; index++) {
|
|
4346
4405
|
const segment = segments[index];
|
|
4347
4406
|
if (segment.isCode) {
|
|
4348
|
-
|
|
4407
|
+
parts.push(segment.text);
|
|
4349
4408
|
continue;
|
|
4350
4409
|
}
|
|
4351
4410
|
let text = segment.text;
|
|
4352
4411
|
text = escapeMhchemCommands(text);
|
|
4353
4412
|
text = escapeCurrencyDollarSigns(text);
|
|
4354
4413
|
text = convertLatexDelimiters(text);
|
|
4355
|
-
if (RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
|
|
4414
|
+
if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
|
|
4356
4415
|
text = escapeLatexPipes(text);
|
|
4357
|
-
if (findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
|
|
4416
|
+
if (probe && findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
|
|
4358
4417
|
text = escapeLatexPipesInUnclosed(text);
|
|
4359
|
-
if (hasUnclosedTextCommand(text)) quiescent = false;
|
|
4418
|
+
if (probe && hasUnclosedTextCommand(text)) quiescent = false;
|
|
4360
4419
|
text = escapeTextUnderscores(text);
|
|
4361
4420
|
text = convertSingleToDoubleDollar(text);
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4421
|
+
if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
|
|
4422
|
+
const unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
|
|
4423
|
+
if (unclosedDouble !== -1) {
|
|
4424
|
+
quiescent = false;
|
|
4425
|
+
if (index === 0 && text.slice(0, unclosedDouble).trim() === "") {
|
|
4426
|
+
truncatedAtSeamStart = true;
|
|
4427
|
+
}
|
|
4367
4428
|
}
|
|
4368
4429
|
}
|
|
4369
4430
|
text = truncateUnclosedLatexBlock(text);
|
|
4370
|
-
|
|
4431
|
+
parts.push(text);
|
|
4371
4432
|
}
|
|
4372
|
-
return { out, quiescent, truncatedAtSeamStart };
|
|
4433
|
+
return { out: parts.join(""), quiescent, truncatedAtSeamStart };
|
|
4434
|
+
}
|
|
4435
|
+
function isBlankRawLine(text, from, to) {
|
|
4436
|
+
for (let i = from; i < to; i++) {
|
|
4437
|
+
const c = text.charCodeAt(i);
|
|
4438
|
+
if (c !== 32 && c !== 9 && c !== 13) return false;
|
|
4439
|
+
}
|
|
4440
|
+
return true;
|
|
4373
4441
|
}
|
|
4374
4442
|
function findRawSafeCut(active) {
|
|
4375
4443
|
const segments = splitByProtectedRegions(active);
|
|
@@ -4384,10 +4452,12 @@ function findRawSafeCut(active) {
|
|
|
4384
4452
|
continue;
|
|
4385
4453
|
}
|
|
4386
4454
|
const text = segment.text;
|
|
4455
|
+
let atLineStart = offset === 0 || active.charCodeAt(offset - 1) === 10;
|
|
4387
4456
|
let lineStart = 0;
|
|
4388
4457
|
while (lineStart <= text.length) {
|
|
4389
4458
|
const nl = text.indexOf("\n", lineStart);
|
|
4390
4459
|
const lineEnd = nl === -1 ? text.length : nl;
|
|
4460
|
+
if (atLineStart && nl !== -1 && isBlankRawLine(text, lineStart, lineEnd)) backtickHazard = false;
|
|
4391
4461
|
for (let i = lineStart; i < lineEnd; i++) {
|
|
4392
4462
|
const ch = text[i];
|
|
4393
4463
|
if (ch === "`") backtickHazard = true;
|
|
@@ -4400,6 +4470,7 @@ function findRawSafeCut(active) {
|
|
|
4400
4470
|
if (nl === -1) break;
|
|
4401
4471
|
if (!backtickHazard && !latentLt) lastCut = offset + nl + 1;
|
|
4402
4472
|
lineStart = nl + 1;
|
|
4473
|
+
atLineStart = true;
|
|
4403
4474
|
}
|
|
4404
4475
|
offset += text.length;
|
|
4405
4476
|
}
|
|
@@ -4408,11 +4479,14 @@ function findRawSafeCut(active) {
|
|
|
4408
4479
|
var DEFAULT_FREEZE_ATTEMPT_THRESHOLD = 512;
|
|
4409
4480
|
function createIncrementalLatexPreprocessor(options) {
|
|
4410
4481
|
const freezeThreshold = options?.freezeThreshold ?? DEFAULT_FREEZE_ATTEMPT_THRESHOLD;
|
|
4482
|
+
const onAttempt = options?.onAttempt;
|
|
4483
|
+
const backoff = options?.backoff ?? true;
|
|
4411
4484
|
let prevSource = "";
|
|
4412
4485
|
let prevOutput = "";
|
|
4413
4486
|
let frozenSrcEnd = 0;
|
|
4414
4487
|
let frozenOut = "";
|
|
4415
4488
|
let triggered = false;
|
|
4489
|
+
let nextAttemptLen = 0;
|
|
4416
4490
|
return function incrementalPreprocessLaTeX(source) {
|
|
4417
4491
|
if (source === prevSource) return prevOutput;
|
|
4418
4492
|
const isAppend = source.length > prevSource.length && source.startsWith(prevSource);
|
|
@@ -4420,6 +4494,7 @@ function createIncrementalLatexPreprocessor(options) {
|
|
|
4420
4494
|
frozenSrcEnd = 0;
|
|
4421
4495
|
frozenOut = "";
|
|
4422
4496
|
triggered = false;
|
|
4497
|
+
nextAttemptLen = 0;
|
|
4423
4498
|
}
|
|
4424
4499
|
if (!triggered) {
|
|
4425
4500
|
const checkFrom = isAppend ? Math.max(0, prevSource.length - 1) : 0;
|
|
@@ -4431,18 +4506,26 @@ function createIncrementalLatexPreprocessor(options) {
|
|
|
4431
4506
|
triggered = true;
|
|
4432
4507
|
}
|
|
4433
4508
|
let active = source.slice(frozenSrcEnd);
|
|
4434
|
-
if (active.length > freezeThreshold) {
|
|
4509
|
+
if (active.length > freezeThreshold && active.length >= nextAttemptLen) {
|
|
4510
|
+
const activeLength = active.length;
|
|
4511
|
+
let advanced = false;
|
|
4512
|
+
let frozenBytes = 0;
|
|
4513
|
+
const freeze = (cut2, slice) => {
|
|
4514
|
+
frozenOut += slice.out;
|
|
4515
|
+
frozenSrcEnd += cut2;
|
|
4516
|
+
active = source.slice(frozenSrcEnd);
|
|
4517
|
+
advanced = true;
|
|
4518
|
+
frozenBytes = cut2;
|
|
4519
|
+
};
|
|
4435
4520
|
const cut = findRawSafeCut(active);
|
|
4436
4521
|
if (cut > 0) {
|
|
4437
4522
|
const candidate = processSliceInstrumented(active.slice(0, cut));
|
|
4438
|
-
if (candidate.quiescent)
|
|
4439
|
-
frozenOut += candidate.out;
|
|
4440
|
-
frozenSrcEnd += cut;
|
|
4441
|
-
active = source.slice(frozenSrcEnd);
|
|
4442
|
-
}
|
|
4523
|
+
if (candidate.quiescent) freeze(cut, candidate);
|
|
4443
4524
|
}
|
|
4525
|
+
nextAttemptLen = advanced || !backoff ? 0 : active.length * 2;
|
|
4526
|
+
onAttempt?.({ activeLength, frozenBytes });
|
|
4444
4527
|
}
|
|
4445
|
-
const tail = processSliceInstrumented(active);
|
|
4528
|
+
const tail = processSliceInstrumented(active, false);
|
|
4446
4529
|
const head = tail.truncatedAtSeamStart ? frozenOut.replace(/\s+$/, "") : frozenOut;
|
|
4447
4530
|
const out = head + tail.out;
|
|
4448
4531
|
prevSource = source;
|