stream-markdown-parser 0.0.65 → 0.0.67

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/index.js CHANGED
@@ -601,7 +601,7 @@ var require_markdown_it_task_checkbox = /* @__PURE__ */ __commonJS({ "../../node
601
601
  }) });
602
602
 
603
603
  //#endregion
604
- //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.4/node_modules/markdown-it-ts/dist/chunk-Bp6m_JJh.js
604
+ //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.5/node_modules/markdown-it-ts/dist/chunk-Bp6m_JJh.js
605
605
  var import_markdown_it_task_checkbox = /* @__PURE__ */ __toESM(require_markdown_it_task_checkbox(), 1);
606
606
  var __defProp = Object.defineProperty;
607
607
  var __export = (all) => {
@@ -2187,7 +2187,7 @@ var require_punycode = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/pu
2187
2187
  }) });
2188
2188
 
2189
2189
  //#endregion
2190
- //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.4/node_modules/markdown-it-ts/dist/index.js
2190
+ //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.5/node_modules/markdown-it-ts/dist/index.js
2191
2191
  var import_punycode = /* @__PURE__ */ __toESM(require_punycode(), 1);
2192
2192
  var utils_exports = /* @__PURE__ */ __export({
2193
2193
  arrayReplaceAt: () => arrayReplaceAt,
@@ -2199,6 +2199,7 @@ var utils_exports = /* @__PURE__ */ __export({
2199
2199
  has: () => has,
2200
2200
  isMdAsciiPunct: () => isMdAsciiPunct,
2201
2201
  isPunctChar: () => isPunctChar,
2202
+ isPunctCode: () => isPunctCode,
2202
2203
  isSpace: () => isSpace$7,
2203
2204
  isString: () => isString,
2204
2205
  isValidEntityCode: () => isValidEntityCode$1,
@@ -2253,6 +2254,15 @@ function isWhiteSpace(code$1) {
2253
2254
  function isPunctChar(ch) {
2254
2255
  return regex_default$3.test(ch) || regex_default$5.test(ch);
2255
2256
  }
2257
+ const PUNCT_CHAR_CACHE = /* @__PURE__ */ new Map();
2258
+ function isPunctCode(ch) {
2259
+ if (isMdAsciiPunct(ch)) return true;
2260
+ const cached = PUNCT_CHAR_CACHE.get(ch);
2261
+ if (cached !== void 0) return cached;
2262
+ const value = isPunctChar(String.fromCharCode(ch));
2263
+ PUNCT_CHAR_CACHE.set(ch, value);
2264
+ return value;
2265
+ }
2256
2266
  function isMdAsciiPunct(ch) {
2257
2267
  switch (ch) {
2258
2268
  case 33:
@@ -2780,12 +2790,31 @@ function inline(state) {
2780
2790
  }
2781
2791
  }
2782
2792
  }
2793
+ const CJK_CHAR_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
2794
+ const ASCII_DOMAIN_START_RE = /[0-9a-z]/i;
2783
2795
  function isLinkOpen$1(str) {
2784
2796
  return /^<a[>\s]/i.test(str);
2785
2797
  }
2786
2798
  function isLinkClose$1(str) {
2787
2799
  return /^<\/a\s*>/i.test(str);
2788
2800
  }
2801
+ function trimCjkPrefixFromFuzzyLink(linkify$2, link$1) {
2802
+ if (link$1.schema || link$1.index !== 0 || !link$1.raw) return link$1;
2803
+ for (let offset = 1; offset < link$1.raw.length; offset++) {
2804
+ const prevChar = link$1.raw[offset - 1];
2805
+ const nextChar = link$1.raw[offset];
2806
+ if (!CJK_CHAR_RE.test(prevChar) || !ASCII_DOMAIN_START_RE.test(nextChar)) continue;
2807
+ const suffix = link$1.raw.slice(offset);
2808
+ const candidate = linkify$2.match(suffix)?.[0];
2809
+ if (!candidate || candidate.index !== 0 || candidate.lastIndex !== suffix.length) continue;
2810
+ return {
2811
+ ...candidate,
2812
+ index: link$1.index + offset,
2813
+ lastIndex: link$1.index + offset + candidate.lastIndex
2814
+ };
2815
+ }
2816
+ return link$1;
2817
+ }
2789
2818
  function linkify(state) {
2790
2819
  const blockTokens = state.tokens;
2791
2820
  if (!state.md?.options?.linkify) return;
@@ -2812,7 +2841,7 @@ function linkify(state) {
2812
2841
  if (htmlLinkLevel > 0) continue;
2813
2842
  if (currentToken.type !== "text" || !state.md.linkify.test(currentToken.content)) continue;
2814
2843
  const text$1 = currentToken.content;
2815
- let links = state.md.linkify.match(text$1) || [];
2844
+ let links = (state.md.linkify.match(text$1) || []).map((link$1) => trimCjkPrefixFromFuzzyLink(state.md.linkify, link$1));
2816
2845
  if (links.length === 0) continue;
2817
2846
  const nodes = [];
2818
2847
  let level = currentToken.level;
@@ -3121,26 +3150,23 @@ function smartquotes(state) {
3121
3150
  }
3122
3151
  }
3123
3152
  function text_join(state) {
3124
- (state.tokens || []).forEach((tk) => {
3125
- if (tk.type === "inline" && Array.isArray(tk.children)) {
3126
- const out = [];
3127
- for (let i = 0; i < tk.children.length; i++) {
3128
- const ch = tk.children[i];
3129
- if (ch.type === "text") {
3130
- let content = ch.content || "";
3131
- while (i + 1 < tk.children.length && tk.children[i + 1].type === "text") {
3132
- i++;
3133
- content += tk.children[i].content || "";
3134
- }
3135
- const textToken$1 = new Token("text", "", 0);
3136
- textToken$1.content = content;
3137
- textToken$1.level = ch.level;
3138
- out.push(textToken$1);
3139
- } else out.push(ch);
3140
- }
3141
- tk.children = out;
3153
+ const blockTokens = state.tokens || [];
3154
+ const length = blockTokens.length;
3155
+ for (let j = 0; j < length; j++) {
3156
+ const blockToken = blockTokens[j];
3157
+ if (blockToken.type !== "inline" || !Array.isArray(blockToken.children)) continue;
3158
+ const tokens = blockToken.children;
3159
+ const max = tokens.length;
3160
+ for (let curr$1 = 0; curr$1 < max; curr$1++) if (tokens[curr$1].type === "text_special") tokens[curr$1].type = "text";
3161
+ let last = 0;
3162
+ let curr = 0;
3163
+ for (; curr < max; curr++) if (tokens[curr].type === "text" && curr + 1 < max && tokens[curr + 1].type === "text") tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content;
3164
+ else {
3165
+ if (curr !== last) tokens[last] = tokens[curr];
3166
+ last++;
3142
3167
  }
3143
- });
3168
+ if (curr !== last) tokens.length = last;
3169
+ }
3144
3170
  }
3145
3171
  function isSpace$6(code$1) {
3146
3172
  switch (code$1) {
@@ -5271,7 +5297,6 @@ var StateInline = class {
5271
5297
  token.content = this.pending;
5272
5298
  token.level = this.pendingLevel;
5273
5299
  this.tokens.push(token);
5274
- this.tokens_meta.push(null);
5275
5300
  this.pending = "";
5276
5301
  return token;
5277
5302
  }
@@ -5281,7 +5306,6 @@ var StateInline = class {
5281
5306
  push(type, tag, nesting) {
5282
5307
  if (this.pending) this.pushPending();
5283
5308
  const token = new Token(type, tag, nesting);
5284
- token.level = this.level;
5285
5309
  let token_meta = null;
5286
5310
  if (nesting < 0) {
5287
5311
  this.level--;
@@ -5308,13 +5332,12 @@ var StateInline = class {
5308
5332
  let pos = start;
5309
5333
  while (pos < posMax && src.charCodeAt(pos) === marker) pos++;
5310
5334
  const count = pos - start;
5311
- if (count < 1) return null;
5312
5335
  const lastChar = start > 0 ? src.charCodeAt(start - 1) : 32;
5313
5336
  const nextChar = pos < posMax ? src.charCodeAt(pos) : 32;
5314
- const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
5315
- const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
5316
5337
  const isLastWhiteSpace = isWhiteSpace(lastChar);
5317
5338
  const isNextWhiteSpace = isWhiteSpace(nextChar);
5339
+ const isLastPunctChar = isPunctCode(lastChar);
5340
+ const isNextPunctChar = isPunctCode(nextChar);
5318
5341
  const left_flanking = !isNextWhiteSpace && (!isNextPunctChar || isLastWhiteSpace || isLastPunctChar);
5319
5342
  const right_flanking = !isLastWhiteSpace && (!isLastPunctChar || isNextWhiteSpace || isNextPunctChar);
5320
5343
  return {
@@ -5506,8 +5529,7 @@ var ParserCore = class {
5506
5529
  return new State(src, this.resolveParser(md), env);
5507
5530
  }
5508
5531
  process(state) {
5509
- if (!this.cachedCoreRules) this.cachedCoreRules = this.ruler.getRules("");
5510
- const rules = this.cachedCoreRules;
5532
+ const rules = this.cachedCoreRules ?? (this.cachedCoreRules = this.ruler.getRules(""));
5511
5533
  for (let i = 0; i < rules.length; i++) rules[i](state);
5512
5534
  }
5513
5535
  parse(src, env = {}, md) {
@@ -5617,21 +5639,64 @@ function ensureSyncResult(value, ruleName) {
5617
5639
  return value;
5618
5640
  }
5619
5641
  const resolveResult = (value) => isPromiseLike(value) ? value : Promise.resolve(value);
5642
+ function parseFenceInfo(info) {
5643
+ if (!info) return {
5644
+ langName: "",
5645
+ langAttrs: ""
5646
+ };
5647
+ let markerEnd = 0;
5648
+ while (markerEnd < info.length) {
5649
+ const ch = info.charCodeAt(markerEnd);
5650
+ if (ch === 32 || ch === 9 || ch === 10) break;
5651
+ markerEnd++;
5652
+ }
5653
+ if (markerEnd >= info.length) return {
5654
+ langName: info,
5655
+ langAttrs: ""
5656
+ };
5657
+ let attrsStart = markerEnd;
5658
+ while (attrsStart < info.length) {
5659
+ const ch = info.charCodeAt(attrsStart);
5660
+ if (ch !== 32 && ch !== 9 && ch !== 10) break;
5661
+ attrsStart++;
5662
+ }
5663
+ return {
5664
+ langName: info.slice(0, markerEnd),
5665
+ langAttrs: attrsStart < info.length ? info.slice(attrsStart) : ""
5666
+ };
5667
+ }
5620
5668
  function renderFence(token, highlighted, info, langName, options, self) {
5621
5669
  if (highlighted.startsWith("<pre")) return `${highlighted}\n`;
5622
5670
  if (info) {
5623
- const classIndex = typeof token.attrIndex === "function" ? token.attrIndex("class") : -1;
5624
- const tmpAttrs = token.attrs ? token.attrs.map((attr) => attr.slice()) : [];
5671
+ const classIndex = token.attrIndex("class");
5672
+ const tmpAttrs = token.attrs ? token.attrs.slice() : [];
5625
5673
  const langClass = `${options.langPrefix || "language-"}${langName}`;
5626
5674
  if (classIndex < 0) tmpAttrs.push(["class", langClass]);
5627
- else tmpAttrs[classIndex][1] += ` ${langClass}`;
5675
+ else tmpAttrs[classIndex] = tmpAttrs[classIndex].slice();
5676
+ if (classIndex >= 0) tmpAttrs[classIndex][1] += ` ${langClass}`;
5628
5677
  const tmpToken = {
5629
5678
  ...token,
5630
5679
  attrs: tmpAttrs
5631
5680
  };
5632
- return `<pre><code${self.renderAttrs(tmpToken)}>${highlighted}</code></pre>\n`;
5681
+ return `<pre><code${tmpAttrs.length > 0 ? self.renderAttrs(tmpToken) : ""}>${highlighted}</code></pre>\n`;
5633
5682
  }
5634
- return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`;
5683
+ return `<pre><code${token.attrs && token.attrs.length > 0 ? self.renderAttrs(token) : ""}>${highlighted}</code></pre>\n`;
5684
+ }
5685
+ function renderCodeInlineToken(token, self) {
5686
+ return `<code${token.attrs && token.attrs.length > 0 ? self.renderAttrs(token) : ""}>${escapeHtml(token.content)}</code>`;
5687
+ }
5688
+ function renderCodeBlockToken(token, self) {
5689
+ return `<pre${token.attrs && token.attrs.length > 0 ? self.renderAttrs(token) : ""}><code>${escapeHtml(token.content)}</code></pre>\n`;
5690
+ }
5691
+ function renderFenceSyncToken(token, options, self) {
5692
+ const info = token.info ? unescapeAll(token.info).trim() : "";
5693
+ const { langName, langAttrs } = parseFenceInfo(info);
5694
+ const highlight = options.highlight;
5695
+ const fallback = escapeHtml(token.content);
5696
+ if (!highlight) return renderFence(token, fallback, info, langName, options, self);
5697
+ const highlighted = highlight(token.content, langName, langAttrs);
5698
+ if (isPromiseLike(highlighted)) throw new TypeError("Renderer rule \"fence\" returned a Promise. Use renderAsync() instead.");
5699
+ return renderFence(token, highlighted || fallback, info, langName, options, self);
5635
5700
  }
5636
5701
  const DEFAULT_RENDERER_OPTIONS = {
5637
5702
  langPrefix: "language-",
@@ -5641,39 +5706,29 @@ const DEFAULT_RENDERER_OPTIONS = {
5641
5706
  const hasOwn = Object.prototype.hasOwnProperty;
5642
5707
  const defaultRules = {
5643
5708
  code_inline(tokens, idx, _options, _env, self) {
5644
- const token = tokens[idx];
5645
- return `<code${self.renderAttrs(token)}>${escapeHtml(token.content)}</code>`;
5709
+ return renderCodeInlineToken(tokens[idx], self);
5646
5710
  },
5647
5711
  code_block(tokens, idx, _options, _env, self) {
5648
- const token = tokens[idx];
5649
- return `<pre${self.renderAttrs(token)}><code>${escapeHtml(token.content)}</code></pre>\n`;
5712
+ return renderCodeBlockToken(tokens[idx], self);
5650
5713
  },
5651
5714
  fence(tokens, idx, options, _env, self) {
5652
5715
  const token = tokens[idx];
5653
5716
  const info = token.info ? unescapeAll(token.info).trim() : "";
5654
- let langName = "";
5655
- let langAttrs = "";
5656
- if (info) {
5657
- const parts = info.split(/(\s+)/g);
5658
- langName = parts[0];
5659
- langAttrs = parts.slice(2).join("");
5660
- }
5717
+ const { langName, langAttrs } = parseFenceInfo(info);
5661
5718
  const highlight = options.highlight;
5662
- const fallback = () => escapeHtml(token.content);
5663
- if (!highlight) return renderFence(token, fallback(), info, langName, options, self);
5719
+ const fallback = escapeHtml(token.content);
5720
+ if (!highlight) return renderFence(token, fallback, info, langName, options, self);
5664
5721
  const highlighted = highlight(token.content, langName, langAttrs);
5665
- if (isPromiseLike(highlighted)) return highlighted.then((res) => renderFence(token, res || fallback(), info, langName, options, self));
5666
- return renderFence(token, highlighted || fallback(), info, langName, options, self);
5722
+ if (isPromiseLike(highlighted)) return highlighted.then((res) => renderFence(token, res || fallback, info, langName, options, self));
5723
+ return renderFence(token, highlighted || fallback, info, langName, options, self);
5667
5724
  },
5668
5725
  image(tokens, idx, options, env, self) {
5669
5726
  const token = tokens[idx];
5670
5727
  const altText = self.renderInlineAsText(token.children || [], options, env);
5671
- if (typeof token.attrIndex === "function") {
5672
- const altIndex = token.attrIndex("alt");
5673
- if (altIndex >= 0 && token.attrs) token.attrs[altIndex][1] = altText;
5674
- else if (token.attrs) token.attrs.push(["alt", altText]);
5675
- else token.attrs = [["alt", altText]];
5676
- }
5728
+ const altIndex = token.attrIndex("alt");
5729
+ if (altIndex >= 0 && token.attrs) token.attrs[altIndex][1] = altText;
5730
+ else if (token.attrs) token.attrs.push(["alt", altText]);
5731
+ else token.attrs = [["alt", altText]];
5677
5732
  return self.renderToken(tokens, idx, options);
5678
5733
  },
5679
5734
  hardbreak(_tokens, _idx, options) {
@@ -5717,36 +5772,65 @@ var Renderer = class {
5717
5772
  const merged = this.mergeOptions(options);
5718
5773
  const envRef = env ?? {};
5719
5774
  const rules = this.rules;
5720
- const out = new Array(tokens.length);
5775
+ const codeBlockRule = rules.code_block;
5776
+ const fenceRule = rules.fence;
5777
+ const htmlBlockRule = rules.html_block;
5778
+ let result = "";
5721
5779
  for (let i = 0; i < tokens.length; i++) {
5722
5780
  const token = tokens[i];
5723
5781
  if (token.type === "inline") {
5724
- out[i] = this.renderInlineTokens(token.children || [], merged, envRef);
5782
+ result += this.renderInlineTokens(token.children || [], merged, envRef);
5725
5783
  continue;
5726
5784
  }
5785
+ switch (token.type) {
5786
+ case "code_block":
5787
+ if (codeBlockRule === defaultRules.code_block) {
5788
+ result += renderCodeBlockToken(token, this);
5789
+ continue;
5790
+ }
5791
+ break;
5792
+ case "fence":
5793
+ if (fenceRule === defaultRules.fence) {
5794
+ result += renderFenceSyncToken(token, merged, this);
5795
+ continue;
5796
+ }
5797
+ break;
5798
+ case "html_block":
5799
+ if (htmlBlockRule === defaultRules.html_block) {
5800
+ result += token.content;
5801
+ continue;
5802
+ }
5803
+ break;
5804
+ default: break;
5805
+ }
5727
5806
  const rule = rules[token.type];
5728
- if (rule) out[i] = ensureSyncResult(rule(tokens, i, merged, envRef, this), token.type);
5729
- else out[i] = this.renderToken(tokens, i, merged);
5807
+ if (!rule) {
5808
+ result += this.renderToken(tokens, i, merged);
5809
+ continue;
5810
+ }
5811
+ const rendered = rule(tokens, i, merged, envRef, this);
5812
+ if (typeof rendered === "string") result += rendered;
5813
+ else result += ensureSyncResult(rendered, token.type);
5730
5814
  }
5731
- return out.join("");
5815
+ return result;
5732
5816
  }
5733
5817
  async renderAsync(tokens, options, env) {
5734
5818
  if (!Array.isArray(tokens)) throw new TypeError("render expects token array as first argument");
5735
5819
  const merged = this.mergeOptions(options);
5736
5820
  const envRef = env ?? {};
5737
5821
  const rules = this.rules;
5738
- const out = new Array(tokens.length);
5822
+ let result = "";
5739
5823
  for (let i = 0; i < tokens.length; i++) {
5740
5824
  const token = tokens[i];
5741
5825
  if (token.type === "inline") {
5742
- out[i] = await this.renderInlineTokensAsync(token.children || [], merged, envRef);
5826
+ result += await this.renderInlineTokensAsync(token.children || [], merged, envRef);
5743
5827
  continue;
5744
5828
  }
5745
5829
  const rule = rules[token.type];
5746
- if (rule) out[i] = await resolveResult(rule(tokens, i, merged, envRef, this));
5747
- else out[i] = this.renderToken(tokens, i, merged);
5830
+ if (rule) result += await resolveResult(rule(tokens, i, merged, envRef, this));
5831
+ else result += this.renderToken(tokens, i, merged);
5748
5832
  }
5749
- return out.join("");
5833
+ return result;
5750
5834
  }
5751
5835
  renderInline(tokens, options, env) {
5752
5836
  const merged = this.mergeOptions(options);
@@ -5778,7 +5862,7 @@ var Renderer = class {
5778
5862
  let result = "";
5779
5863
  if (token.block && token.nesting !== -1 && idx > 0 && tokens[idx - 1].hidden) result += "\n";
5780
5864
  result += token.nesting === -1 ? `</${token.tag}` : `<${token.tag}`;
5781
- result += this.renderAttrs(token);
5865
+ if (token.attrs && token.attrs.length > 0) result += this.renderAttrs(token);
5782
5866
  if (token.nesting === 0 && options.xhtmlOut) result += " /";
5783
5867
  let needLineFeed = false;
5784
5868
  if (token.block) {
@@ -5824,25 +5908,77 @@ var Renderer = class {
5824
5908
  renderInlineTokens(tokens, options, env) {
5825
5909
  if (!tokens || tokens.length === 0) return "";
5826
5910
  const rules = this.rules;
5827
- const out = new Array(tokens.length);
5911
+ const textRule = rules.text;
5912
+ const textSpecialRule = rules.text_special;
5913
+ const softbreakRule = rules.softbreak;
5914
+ const hardbreakRule = rules.hardbreak;
5915
+ const htmlInlineRule = rules.html_inline;
5916
+ const codeInlineRule = rules.code_inline;
5917
+ const inlineBreak = options.xhtmlOut ? "<br />\n" : "<br>\n";
5918
+ const softbreak = options.breaks ? inlineBreak : "\n";
5919
+ let result = "";
5828
5920
  for (let i = 0; i < tokens.length; i++) {
5829
5921
  const token = tokens[i];
5922
+ switch (token.type) {
5923
+ case "text":
5924
+ if (textRule === defaultRules.text) {
5925
+ result += escapeHtml(token.content);
5926
+ continue;
5927
+ }
5928
+ break;
5929
+ case "text_special":
5930
+ if (textSpecialRule === defaultRules.text_special) {
5931
+ result += escapeHtml(token.content);
5932
+ continue;
5933
+ }
5934
+ break;
5935
+ case "softbreak":
5936
+ if (softbreakRule === defaultRules.softbreak) {
5937
+ result += softbreak;
5938
+ continue;
5939
+ }
5940
+ break;
5941
+ case "hardbreak":
5942
+ if (hardbreakRule === defaultRules.hardbreak) {
5943
+ result += inlineBreak;
5944
+ continue;
5945
+ }
5946
+ break;
5947
+ case "html_inline":
5948
+ if (htmlInlineRule === defaultRules.html_inline) {
5949
+ result += token.content;
5950
+ continue;
5951
+ }
5952
+ break;
5953
+ case "code_inline":
5954
+ if (codeInlineRule === defaultRules.code_inline) {
5955
+ result += renderCodeInlineToken(token, this);
5956
+ continue;
5957
+ }
5958
+ break;
5959
+ default: break;
5960
+ }
5830
5961
  const rule = rules[token.type];
5831
- if (rule) out[i] = ensureSyncResult(rule(tokens, i, options, env, this), token.type);
5832
- else out[i] = this.renderToken(tokens, i, options);
5962
+ if (!rule) {
5963
+ result += this.renderToken(tokens, i, options);
5964
+ continue;
5965
+ }
5966
+ const rendered = rule(tokens, i, options, env, this);
5967
+ if (typeof rendered === "string") result += rendered;
5968
+ else result += ensureSyncResult(rendered, token.type);
5833
5969
  }
5834
- return out.join("");
5970
+ return result;
5835
5971
  }
5836
5972
  async renderInlineTokensAsync(tokens, options, env) {
5837
5973
  if (!tokens || tokens.length === 0) return "";
5838
5974
  const rules = this.rules;
5839
- const out = new Array(tokens.length);
5975
+ let result = "";
5840
5976
  for (let i = 0; i < tokens.length; i++) {
5841
5977
  const rule = rules[tokens[i].type];
5842
- if (rule) out[i] = await resolveResult(rule(tokens, i, options, env, this));
5843
- else out[i] = this.renderToken(tokens, i, options);
5978
+ if (rule) result += await resolveResult(rule(tokens, i, options, env, this));
5979
+ else result += this.renderToken(tokens, i, options);
5844
5980
  }
5845
- return out.join("");
5981
+ return result;
5846
5982
  }
5847
5983
  renderInlineAsTextInternal(tokens, options, env) {
5848
5984
  if (!tokens || tokens.length === 0) return "";
@@ -5999,6 +6135,7 @@ function makeEmptyStats() {
5999
6135
  total: 0,
6000
6136
  cacheHits: 0,
6001
6137
  appendHits: 0,
6138
+ tailHits: 0,
6002
6139
  fullParses: 0,
6003
6140
  resets: 0,
6004
6141
  chunkedParses: 0,
@@ -6014,6 +6151,10 @@ var StreamParser = class {
6014
6151
  DEFAULT_SKIP_CACHE_LINES = 1e4;
6015
6152
  MAX_CHUNKS_FOR_FALLBACK = 24;
6016
6153
  MAX_CHUNKED_DOC_CHARS = 12e4;
6154
+ MIN_LIST_LINES_FOR_MERGE = 80;
6155
+ MIN_LIST_CHARS_FOR_MERGE = 800;
6156
+ MIN_TABLE_LINES_FOR_MERGE = 48;
6157
+ MIN_TABLE_CHARS_FOR_MERGE = 1200;
6017
6158
  constructor(core) {
6018
6159
  this.core = core;
6019
6160
  }
@@ -6086,8 +6227,10 @@ var StreamParser = class {
6086
6227
  src,
6087
6228
  tokens: tokens$1,
6088
6229
  env: workingEnv,
6089
- lineCount: srcLineCount
6230
+ lineCount: srcLineCount,
6231
+ lastSegment: void 0
6090
6232
  };
6233
+ this.updateCacheLineCount(this.cache, srcLineCount);
6091
6234
  this.stats.total += 1;
6092
6235
  this.stats.chunkedParses = (this.stats.chunkedParses || 0) + 1;
6093
6236
  this.stats.lastMode = "chunked";
@@ -6099,8 +6242,10 @@ var StreamParser = class {
6099
6242
  src,
6100
6243
  tokens,
6101
6244
  env: workingEnv,
6102
- lineCount: srcLineCount
6245
+ lineCount: srcLineCount,
6246
+ lastSegment: void 0
6103
6247
  };
6248
+ this.updateCacheLineCount(this.cache, srcLineCount);
6104
6249
  this.stats.total += 1;
6105
6250
  this.stats.fullParses += 1;
6106
6251
  this.stats.lastMode = "full";
@@ -6116,19 +6261,22 @@ var StreamParser = class {
6116
6261
  if (cached.src.length < threshold && src.length < threshold * 1.5 && !src.startsWith(cached.src)) {
6117
6262
  const fallbackEnv$1 = envProvided ?? cached.env;
6118
6263
  const nextTokens$1 = this.core.parse(src, fallbackEnv$1, md).tokens;
6264
+ const lineCount = countLines(src);
6119
6265
  this.cache = {
6120
6266
  src,
6121
6267
  tokens: nextTokens$1,
6122
6268
  env: fallbackEnv$1,
6123
- lineCount: countLines(src)
6269
+ lineCount,
6270
+ lastSegment: void 0
6124
6271
  };
6272
+ this.updateCacheLineCount(this.cache, lineCount);
6125
6273
  this.stats.total += 1;
6126
6274
  this.stats.fullParses += 1;
6127
6275
  this.stats.lastMode = "full";
6128
6276
  return nextTokens$1;
6129
6277
  }
6130
6278
  const appended = this.getAppendedSegment(cached.src, src);
6131
- if (appended) {
6279
+ if (appended && !this.shouldPreferTailReparseForAppend(cached)) {
6132
6280
  const cachedLineCount = cached.lineCount ?? countLines(cached.src);
6133
6281
  let ctxLines = 3;
6134
6282
  if (appended.length > 5e3) ctxLines = 8;
@@ -6233,8 +6381,9 @@ var StreamParser = class {
6233
6381
  if (appendedLineCount === null) appendedLineCount = countLines(appended);
6234
6382
  return appendedLineCount;
6235
6383
  };
6384
+ const canDirectParseAppend = this.canDirectlyParseAppend(cached);
6236
6385
  let shouldAttemptContext = false;
6237
- switch (ctxStrategy) {
6386
+ if (!canDirectParseAppend) switch (ctxStrategy) {
6238
6387
  case "lines":
6239
6388
  shouldAttemptContext = getAppendedLineCount() >= CONTEXT_PARSE_MIN_LINES;
6240
6389
  break;
@@ -6287,6 +6436,7 @@ var StreamParser = class {
6287
6436
  }
6288
6437
  } catch {}
6289
6438
  }
6439
+ const appendStart = cached.tokens.length;
6290
6440
  if (appendedState.tokens.length > 0) {
6291
6441
  const cachedTail = cached.tokens;
6292
6442
  const a = appendedState.tokens;
@@ -6318,12 +6468,30 @@ var StreamParser = class {
6318
6468
  }
6319
6469
  cached.src = src;
6320
6470
  cached.lineCount = cachedLineCount + (appendedLineCount ?? countLines(appended));
6471
+ if (cached.tokens.length > appendStart) {
6472
+ const appendedLastSegment = this.getLastSegment(cached.tokens.slice(appendStart), src);
6473
+ if (appendedLastSegment) cached.lastSegment = {
6474
+ tokenStart: appendStart + appendedLastSegment.tokenStart,
6475
+ tokenEnd: appendStart + appendedLastSegment.tokenEnd,
6476
+ lineStart: appendedLastSegment.lineStart,
6477
+ lineEnd: appendedLastSegment.lineEnd,
6478
+ srcOffset: appendedLastSegment.srcOffset
6479
+ };
6480
+ else cached.lastSegment = void 0;
6481
+ } else cached.lastSegment = void 0;
6321
6482
  this.stats.total += 1;
6322
6483
  this.stats.appendHits += 1;
6323
6484
  this.stats.lastMode = "append";
6324
6485
  return cached.tokens;
6325
6486
  }
6326
6487
  const fallbackEnv = envProvided ?? cached.env;
6488
+ const tailReparsed = this.tryTailSegmentReparse(src, cached, fallbackEnv, md);
6489
+ if (tailReparsed) {
6490
+ this.stats.total += 1;
6491
+ this.stats.tailHits += 1;
6492
+ this.stats.lastMode = "tail";
6493
+ return tailReparsed;
6494
+ }
6327
6495
  const chunkedEnabled = !!md.options?.streamChunkedFallback;
6328
6496
  const chunkAdaptive = md.options?.streamChunkAdaptive !== false;
6329
6497
  const targetChunks = md.options?.streamChunkTargetChunks ?? 8;
@@ -6365,8 +6533,10 @@ var StreamParser = class {
6365
6533
  src,
6366
6534
  tokens,
6367
6535
  env: fallbackEnv,
6368
- lineCount: srcLineCount2
6536
+ lineCount: srcLineCount2,
6537
+ lastSegment: void 0
6369
6538
  };
6539
+ this.updateCacheLineCount(this.cache, srcLineCount2);
6370
6540
  this.stats.total += 1;
6371
6541
  this.stats.chunkedParses = (this.stats.chunkedParses || 0) + 1;
6372
6542
  this.stats.lastMode = "chunked";
@@ -6378,8 +6548,10 @@ var StreamParser = class {
6378
6548
  src,
6379
6549
  tokens: nextTokens,
6380
6550
  env: fallbackEnv,
6381
- lineCount: srcLineCount2
6551
+ lineCount: srcLineCount2,
6552
+ lastSegment: void 0
6382
6553
  };
6554
+ this.updateCacheLineCount(this.cache, srcLineCount2);
6383
6555
  this.stats.total += 1;
6384
6556
  this.stats.fullParses += 1;
6385
6557
  this.stats.lastMode = "full";
@@ -6408,8 +6580,46 @@ var StreamParser = class {
6408
6580
  if (prevWithoutTrailingNewline.slice(lastBreak + 1).trim().length > 0) return null;
6409
6581
  }
6410
6582
  if (this.endsInsideOpenFence(prev)) return null;
6583
+ if (this.mayContainReferenceDefinition(segment)) return null;
6411
6584
  return segment;
6412
6585
  }
6586
+ tryTailSegmentReparse(src, cached, env, md) {
6587
+ const lastSegment = this.ensureLastSegment(cached);
6588
+ if (!lastSegment) return null;
6589
+ if (lastSegment.srcOffset <= 0 && lastSegment.tokenStart <= 0) return null;
6590
+ const stablePrefix = cached.src.slice(0, lastSegment.srcOffset);
6591
+ if (!src.startsWith(stablePrefix)) return null;
6592
+ const prevTail = cached.src.slice(lastSegment.srcOffset);
6593
+ const nextTail = src.slice(lastSegment.srcOffset);
6594
+ if (nextTail === prevTail) return null;
6595
+ const appended = src.startsWith(cached.src) ? src.slice(cached.src.length) : null;
6596
+ if (appended) {
6597
+ const merged = this.tryContainerTailAppendMerge(src, cached, env, md, lastSegment, appended);
6598
+ if (merged) return merged;
6599
+ }
6600
+ if (this.mayContainReferenceDefinition(prevTail) || this.mayContainReferenceDefinition(nextTail)) return null;
6601
+ try {
6602
+ const tailState = this.core.parse(nextTail, env, md);
6603
+ const localLastSegment = this.getLastSegment(tailState.tokens, nextTail);
6604
+ if (lastSegment.lineStart > 0) this.shiftTokenLines(tailState.tokens, lastSegment.lineStart);
6605
+ cached.src = src;
6606
+ cached.env = env;
6607
+ cached.tokens.length = lastSegment.tokenStart;
6608
+ cached.tokens.push(...tailState.tokens);
6609
+ cached.lineCount = countLines(src);
6610
+ if (localLastSegment) cached.lastSegment = {
6611
+ tokenStart: lastSegment.tokenStart + localLastSegment.tokenStart,
6612
+ tokenEnd: lastSegment.tokenStart + localLastSegment.tokenEnd,
6613
+ lineStart: lastSegment.lineStart + localLastSegment.lineStart,
6614
+ lineEnd: lastSegment.lineStart + localLastSegment.lineEnd,
6615
+ srcOffset: lastSegment.srcOffset + localLastSegment.srcOffset
6616
+ };
6617
+ else cached.lastSegment = null;
6618
+ return cached.tokens;
6619
+ } catch {
6620
+ return null;
6621
+ }
6622
+ }
6413
6623
  getTailLines(src, lineCount) {
6414
6624
  if (lineCount <= 0) return "";
6415
6625
  let remaining = lineCount;
@@ -6461,6 +6671,293 @@ var StreamParser = class {
6461
6671
  getStats() {
6462
6672
  return { ...this.stats };
6463
6673
  }
6674
+ updateCacheLineCount(cache, lineCount) {
6675
+ cache.lineCount = lineCount ?? countLines(cache.src);
6676
+ cache.lastSegment = void 0;
6677
+ }
6678
+ ensureLastSegment(cache) {
6679
+ if (cache.lastSegment !== void 0) return cache.lastSegment;
6680
+ cache.lastSegment = this.getLastSegment(cache.tokens, cache.src);
6681
+ return cache.lastSegment;
6682
+ }
6683
+ getLastSegment(tokens, src) {
6684
+ if (tokens.length === 0) return null;
6685
+ let lineStart = Number.POSITIVE_INFINITY;
6686
+ let lineEnd = -1;
6687
+ let depth = 0;
6688
+ for (let i = tokens.length - 1; i >= 0; i--) {
6689
+ const token = tokens[i];
6690
+ if (token.map) {
6691
+ if (token.map[0] < lineStart) lineStart = token.map[0];
6692
+ if (token.map[1] > lineEnd) lineEnd = token.map[1];
6693
+ }
6694
+ if (token.nesting < 0) {
6695
+ depth += -token.nesting;
6696
+ continue;
6697
+ }
6698
+ if (token.nesting > 0) {
6699
+ depth -= token.nesting;
6700
+ if (token.level === 0 && depth <= 0) {
6701
+ const resolvedStart = Number.isFinite(lineStart) ? lineStart : token.map?.[0] ?? 0;
6702
+ const resolvedEnd = lineEnd >= resolvedStart ? lineEnd : token.map?.[1] ?? resolvedStart;
6703
+ return {
6704
+ tokenStart: i,
6705
+ tokenEnd: tokens.length,
6706
+ lineStart: resolvedStart,
6707
+ lineEnd: resolvedEnd,
6708
+ srcOffset: this.getLineStartOffset(src, resolvedStart)
6709
+ };
6710
+ }
6711
+ continue;
6712
+ }
6713
+ if (token.level === 0 && depth === 0) {
6714
+ const resolvedStart = Number.isFinite(lineStart) ? lineStart : token.map?.[0] ?? 0;
6715
+ const resolvedEnd = lineEnd >= resolvedStart ? lineEnd : token.map?.[1] ?? resolvedStart;
6716
+ return {
6717
+ tokenStart: i,
6718
+ tokenEnd: tokens.length,
6719
+ lineStart: resolvedStart,
6720
+ lineEnd: resolvedEnd,
6721
+ srcOffset: this.getLineStartOffset(src, resolvedStart)
6722
+ };
6723
+ }
6724
+ }
6725
+ return null;
6726
+ }
6727
+ getLineStartOffset(src, line) {
6728
+ if (line <= 0) return 0;
6729
+ let remaining = line;
6730
+ let pos = -1;
6731
+ while (remaining > 0) {
6732
+ pos = src.indexOf("\n", pos + 1);
6733
+ if (pos === -1) return src.length;
6734
+ remaining--;
6735
+ }
6736
+ return pos + 1;
6737
+ }
6738
+ mayContainReferenceDefinition(src) {
6739
+ if (!src.includes("]:")) return false;
6740
+ return /(?:^|\n)[ \t]{0,3}\[[^\]\n]+\]:/.test(src);
6741
+ }
6742
+ canDirectlyParseAppend(cache) {
6743
+ if (!this.endsWithBlankLine(cache.src)) return false;
6744
+ const lastSegment = this.ensureLastSegment(cache);
6745
+ if (!lastSegment) return false;
6746
+ switch (cache.tokens[lastSegment.tokenStart]?.type) {
6747
+ case "paragraph_open":
6748
+ case "heading_open":
6749
+ case "fence":
6750
+ case "code_block":
6751
+ case "html_block":
6752
+ case "hr":
6753
+ case "table_open": return true;
6754
+ default: return false;
6755
+ }
6756
+ }
6757
+ tryContainerTailAppendMerge(src, cached, env, md, lastSegment, appended) {
6758
+ if (!appended || this.mayContainReferenceDefinition(appended)) return null;
6759
+ const lastToken = cached.tokens[lastSegment.tokenStart];
6760
+ switch (lastToken?.type) {
6761
+ case "bullet_list_open":
6762
+ case "ordered_list_open": return this.tryListTailAppendMerge(src, cached, env, md, lastSegment, appended, lastToken);
6763
+ case "table_open": return this.tryTableTailAppendMerge(src, cached, env, md, lastSegment, appended, lastToken);
6764
+ default: return null;
6765
+ }
6766
+ }
6767
+ tryListTailAppendMerge(src, cached, env, md, lastSegment, appended, listOpen) {
6768
+ if (cached.src.length === 0 || cached.src.charCodeAt(cached.src.length - 1) !== 10) return null;
6769
+ const segmentLineSpan = lastSegment.lineEnd - lastSegment.lineStart;
6770
+ const segmentChars = cached.src.length - lastSegment.srcOffset;
6771
+ if (segmentLineSpan < this.MIN_LIST_LINES_FOR_MERGE && segmentChars < this.MIN_LIST_CHARS_FOR_MERGE) return null;
6772
+ const closeType = listOpen.type === "bullet_list_open" ? "bullet_list_close" : "ordered_list_close";
6773
+ let parsed;
6774
+ try {
6775
+ parsed = this.core.parse(appended, env, md).tokens;
6776
+ } catch {
6777
+ return null;
6778
+ }
6779
+ if (!this.isSingleTopLevelContainer(parsed, listOpen.type, closeType, listOpen.markup)) return null;
6780
+ const inserted = parsed.slice(1, -1);
6781
+ if (inserted.length === 0) return null;
6782
+ const lineOffset = cached.lineCount ?? countLines(cached.src);
6783
+ if (lineOffset > 0) this.shiftTokenLines(inserted, lineOffset);
6784
+ const existingMode = this.getListParagraphMode(cached.tokens, lastSegment.tokenStart, cached.tokens.length, listOpen.level);
6785
+ const appendedMode = this.getListParagraphMode(parsed, 0, parsed.length, 0);
6786
+ if (existingMode === "loose" || appendedMode === "loose" || this.endsWithBlankLine(cached.src) || (parsed[0]?.map?.[0] ?? 0) > 0) {
6787
+ this.setListParagraphVisibility(cached.tokens, lastSegment.tokenStart, cached.tokens.length, listOpen.level, false);
6788
+ this.setListParagraphVisibility(inserted, 0, inserted.length, listOpen.level, false);
6789
+ }
6790
+ cached.tokens.splice(cached.tokens.length - 1, 0, ...inserted);
6791
+ cached.src = src;
6792
+ cached.env = env;
6793
+ cached.lineCount = countLines(src);
6794
+ if (listOpen.map) listOpen.map[1] = this.getDocLineCount(src);
6795
+ cached.lastSegment = {
6796
+ tokenStart: lastSegment.tokenStart,
6797
+ tokenEnd: cached.tokens.length,
6798
+ lineStart: lastSegment.lineStart,
6799
+ lineEnd: this.getDocLineCount(src),
6800
+ srcOffset: lastSegment.srcOffset
6801
+ };
6802
+ return cached.tokens;
6803
+ }
6804
+ tryTableTailAppendMerge(src, cached, env, md, lastSegment, appended, tableOpen) {
6805
+ if (cached.src.length === 0 || cached.src.charCodeAt(cached.src.length - 1) !== 10) return null;
6806
+ if (/(?:^|\n)[ \t]*\n/.test(appended)) return null;
6807
+ const segmentLineSpan = lastSegment.lineEnd - lastSegment.lineStart;
6808
+ const segmentChars = cached.src.length - lastSegment.srcOffset;
6809
+ if (segmentLineSpan < this.MIN_TABLE_LINES_FOR_MERGE && segmentChars < this.MIN_TABLE_CHARS_FOR_MERGE) return null;
6810
+ const tableContext = this.getTableHeaderContext(cached.src.slice(lastSegment.srcOffset));
6811
+ if (!tableContext) return null;
6812
+ const syntheticSrc = `${tableContext}${appended}`;
6813
+ let parsed;
6814
+ try {
6815
+ parsed = this.core.parse(syntheticSrc, env, md).tokens;
6816
+ } catch {
6817
+ return null;
6818
+ }
6819
+ if (!this.isSingleTopLevelContainer(parsed, "table_open", "table_close")) return null;
6820
+ if ((parsed[0]?.map?.[1] ?? -1) !== this.getDocLineCount(syntheticSrc)) return null;
6821
+ const parsedSection = this.getTableBodySection(parsed, 0, parsed.length, 0);
6822
+ const cachedSection = this.getTableBodySection(cached.tokens, lastSegment.tokenStart, cached.tokens.length, tableOpen.level);
6823
+ if (!parsedSection || !cachedSection || parsedSection.tbodyOpenIndex < 0 || parsedSection.tbodyCloseIndex < 0) return null;
6824
+ const inserted = cachedSection.tbodyOpenIndex >= 0 ? parsed.slice(parsedSection.tbodyOpenIndex + 1, parsedSection.tbodyCloseIndex) : parsed.slice(parsedSection.tbodyOpenIndex, parsedSection.tbodyCloseIndex + 1);
6825
+ if (inserted.length === 0) return null;
6826
+ const lineOffset = lastSegment.lineEnd - 2;
6827
+ if (lineOffset !== 0) this.shiftTokenLines(inserted, lineOffset);
6828
+ const insertAt = cachedSection.tbodyCloseIndex >= 0 ? cachedSection.tbodyCloseIndex : cachedSection.tableCloseIndex;
6829
+ cached.tokens.splice(insertAt, 0, ...inserted);
6830
+ cached.src = src;
6831
+ cached.env = env;
6832
+ cached.lineCount = countLines(src);
6833
+ const nextDocLineCount = this.getDocLineCount(src);
6834
+ if (tableOpen.map) tableOpen.map[1] = nextDocLineCount;
6835
+ if (cachedSection.tbodyOpenIndex >= 0) {
6836
+ const tbodyOpen = cached.tokens[cachedSection.tbodyOpenIndex];
6837
+ if (tbodyOpen?.map) tbodyOpen.map[1] = nextDocLineCount;
6838
+ }
6839
+ cached.lastSegment = {
6840
+ tokenStart: lastSegment.tokenStart,
6841
+ tokenEnd: cached.tokens.length,
6842
+ lineStart: lastSegment.lineStart,
6843
+ lineEnd: nextDocLineCount,
6844
+ srcOffset: lastSegment.srcOffset
6845
+ };
6846
+ return cached.tokens;
6847
+ }
6848
+ getTableHeaderContext(src) {
6849
+ const firstBreak = src.indexOf("\n");
6850
+ if (firstBreak < 0) return null;
6851
+ const secondBreak = src.indexOf("\n", firstBreak + 1);
6852
+ if (secondBreak < 0) return null;
6853
+ return src.slice(0, secondBreak + 1);
6854
+ }
6855
+ getTableBodySection(tokens, start, end, tableLevel) {
6856
+ if (start < 0 || start >= end || tokens[start]?.type !== "table_open") return null;
6857
+ let tableCloseIndex = -1;
6858
+ for (let i = end - 1; i > start; i--) {
6859
+ const token = tokens[i];
6860
+ if (token.type === "table_close" && token.level === tableLevel) {
6861
+ tableCloseIndex = i;
6862
+ break;
6863
+ }
6864
+ }
6865
+ if (tableCloseIndex < 0) return null;
6866
+ let tbodyOpenIndex = -1;
6867
+ let tbodyCloseIndex = -1;
6868
+ for (let i = start + 1; i < tableCloseIndex; i++) {
6869
+ const token = tokens[i];
6870
+ if (token.type === "tbody_open" && token.level === tableLevel + 1) {
6871
+ tbodyOpenIndex = i;
6872
+ break;
6873
+ }
6874
+ }
6875
+ if (tbodyOpenIndex >= 0) {
6876
+ for (let i = tableCloseIndex - 1; i > tbodyOpenIndex; i--) {
6877
+ const token = tokens[i];
6878
+ if (token.type === "tbody_close" && token.level === tableLevel + 1) {
6879
+ tbodyCloseIndex = i;
6880
+ break;
6881
+ }
6882
+ }
6883
+ if (tbodyCloseIndex < 0) return null;
6884
+ }
6885
+ return {
6886
+ tableCloseIndex,
6887
+ tbodyOpenIndex,
6888
+ tbodyCloseIndex
6889
+ };
6890
+ }
6891
+ isSingleTopLevelContainer(tokens, openType, closeType, markup) {
6892
+ if (tokens.length < 2) return false;
6893
+ const first = tokens[0];
6894
+ const last = tokens[tokens.length - 1];
6895
+ if (first.type !== openType || last.type !== closeType || first.level !== 0 || last.level !== 0) return false;
6896
+ if (markup !== void 0 && first.markup !== markup) return false;
6897
+ let depth = 0;
6898
+ for (let i = 0; i < tokens.length; i++) {
6899
+ const token = tokens[i];
6900
+ if (token.level === 0 && i > 0 && i < tokens.length - 1 && depth === 0) return false;
6901
+ if (token.nesting > 0) depth += token.nesting;
6902
+ else if (token.nesting < 0) depth += token.nesting;
6903
+ }
6904
+ return depth === 0;
6905
+ }
6906
+ getListParagraphMode(tokens, start, end, listLevel) {
6907
+ let sawHidden = false;
6908
+ let sawVisible = false;
6909
+ const paragraphLevel = listLevel + 2;
6910
+ for (let i = start; i < end; i++) {
6911
+ const token = tokens[i];
6912
+ if (token.type !== "paragraph_open" || token.level !== paragraphLevel) continue;
6913
+ if (token.hidden) sawHidden = true;
6914
+ else sawVisible = true;
6915
+ if (sawHidden && sawVisible) return "loose";
6916
+ }
6917
+ if (sawVisible) return "loose";
6918
+ if (sawHidden) return "tight";
6919
+ return "none";
6920
+ }
6921
+ setListParagraphVisibility(tokens, start, end, listLevel, hidden) {
6922
+ const paragraphLevel = listLevel + 2;
6923
+ for (let i = start; i < end; i++) {
6924
+ const token = tokens[i];
6925
+ if ((token.type === "paragraph_open" || token.type === "paragraph_close") && token.level === paragraphLevel) token.hidden = hidden;
6926
+ }
6927
+ }
6928
+ shouldPreferTailReparseForAppend(cache) {
6929
+ const lastSegment = this.ensureLastSegment(cache);
6930
+ if (!lastSegment) return false;
6931
+ switch (cache.tokens[lastSegment.tokenStart]?.type) {
6932
+ case "bullet_list_open":
6933
+ case "ordered_list_open":
6934
+ case "blockquote_open":
6935
+ case "table_open": return true;
6936
+ case "paragraph_open":
6937
+ case "code_block":
6938
+ case "html_block": return !this.endsWithBlankLine(cache.src);
6939
+ default: return false;
6940
+ }
6941
+ }
6942
+ endsWithBlankLine(src) {
6943
+ const len = src.length;
6944
+ if (len < 2 || src.charCodeAt(len - 1) !== 10) return false;
6945
+ let pos = len - 2;
6946
+ while (pos >= 0) {
6947
+ const ch = src.charCodeAt(pos);
6948
+ if (ch === 32 || ch === 9) {
6949
+ pos--;
6950
+ continue;
6951
+ }
6952
+ return ch === 10;
6953
+ }
6954
+ return true;
6955
+ }
6956
+ getDocLineCount(src) {
6957
+ const lines = countLines(src);
6958
+ if (src.length === 0) return 0;
6959
+ return src.charCodeAt(src.length - 1) === 10 ? lines : lines + 1;
6960
+ }
6464
6961
  shiftTokenLines(tokens, offset) {
6465
6962
  if (offset === 0) return;
6466
6963
  const stack = [...tokens];
@@ -6746,6 +7243,7 @@ function markdownIt(presetName, options) {
6746
7243
  total: 0,
6747
7244
  cacheHits: 0,
6748
7245
  appendHits: 0,
7246
+ tailHits: 0,
6749
7247
  fullParses: 0,
6750
7248
  resets: 0,
6751
7249
  chunkedParses: 0,
@@ -10247,6 +10745,7 @@ function parseTextToken(token) {
10247
10745
  const STRONG_PAIR_RE = /\*\*([\s\S]*?)\*\*/;
10248
10746
  const STRIKETHROUGH_RE = /[^~]*~{2,}[^~]+/;
10249
10747
  const HAS_STRONG_RE = /\*\*/;
10748
+ const ESCAPED_PUNCTUATION_RE = /\\([\\()[\]`$|*_\-!])/g;
10250
10749
  function countUnescapedAsterisks(str) {
10251
10750
  let count = 0;
10252
10751
  let i = 0;
@@ -10597,6 +11096,17 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
10597
11096
  result.push(currentTextNode);
10598
11097
  }
10599
11098
  }
11099
+ function hasEscapedMarkup(token, escapedPrefix) {
11100
+ return String(token.markup ?? "").startsWith(escapedPrefix);
11101
+ }
11102
+ function stripTrailingMidStateMarker(content, token) {
11103
+ let nextContent = content;
11104
+ const rawTokenContent = String(token.content ?? "");
11105
+ if (nextContent.endsWith("\\") && !hasEscapedMarkup(token, "\\\\") && !rawTokenContent.endsWith("\\\\")) nextContent = nextContent.slice(0, -1);
11106
+ if (nextContent.endsWith("(") && !hasEscapedMarkup(token, "\\(") && !rawTokenContent.endsWith("\\(")) nextContent = nextContent.slice(0, -1);
11107
+ if (/\*+$/.test(nextContent) && !hasEscapedMarkup(token, "\\*") && !rawTokenContent.endsWith("\\*")) nextContent = nextContent.replace(/\*+$/, "");
11108
+ return nextContent;
11109
+ }
10600
11110
  while (i < tokens.length) {
10601
11111
  const token = tokens[i];
10602
11112
  handleToken(token);
@@ -10791,12 +11301,12 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
10791
11301
  content
10792
11302
  });
10793
11303
  if (currentTextNode) {
10794
- currentTextNode.content += textNode.content.replace(/(\*+|\(|\\)$/, "");
11304
+ currentTextNode.content += stripTrailingMidStateMarker(textNode.content, token);
10795
11305
  currentTextNode.raw += textNode.raw;
10796
11306
  return;
10797
11307
  }
10798
11308
  const maybeMath = preToken?.tag === "br" && tokens[i - 2]?.content === "[";
10799
- if (!nextToken) textNode.content = textNode.content.replace(/(\*+|\(|\\)$/, "");
11309
+ if (!nextToken) textNode.content = stripTrailingMidStateMarker(textNode.content, token);
10800
11310
  currentTextNode = textNode;
10801
11311
  currentTextNode.center = maybeMath;
10802
11312
  result.push(currentTextNode);
@@ -10804,8 +11314,7 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
10804
11314
  function handleTextToken(token) {
10805
11315
  let index = result.length - 1;
10806
11316
  const rawContent = String(token.content ?? "");
10807
- let content = rawContent;
10808
- if (rawContent.includes("\\")) content = rawContent.replace(/\\/g, "");
11317
+ let content = rawContent.replace(ESCAPED_PUNCTUATION_RE, "$1");
10809
11318
  if (token.content === "<" || content === "1" && tokens[i - 1]?.tag === "br") {
10810
11319
  i++;
10811
11320
  return;
@@ -10829,7 +11338,7 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
10829
11338
  i++;
10830
11339
  return;
10831
11340
  }
10832
- if (content === "`" || content === "|" || content === "$" || /^\*+$/.test(content)) {
11341
+ if ((content === "`" || content === "|" || content === "$") && !hasEscapedMarkup(token, `\\${content}`) || /^\*+$/.test(content) && !hasEscapedMarkup(token, "\\*")) {
10833
11342
  i++;
10834
11343
  return;
10835
11344
  }