@ai-matrx/content-ir 0.16.2 → 0.18.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/index.cjs CHANGED
@@ -5227,65 +5227,313 @@ function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
5227
5227
  return "content";
5228
5228
  }
5229
5229
 
5230
- // source/math.ts
5231
- var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
5232
- var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
5233
- var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
5234
- var FUNCTION_NAMES = /* @__PURE__ */ new Set([
5235
- "sin",
5236
- "cos",
5237
- "tan",
5238
- "sec",
5239
- "csc",
5240
- "cot",
5241
- "log",
5242
- "ln",
5243
- "exp",
5244
- "lim",
5245
- "max",
5246
- "min",
5247
- "det",
5248
- "gcd",
5249
- "mod",
5250
- "arg",
5251
- "sinh",
5252
- "cosh",
5253
- "tanh"
5254
- ]);
5255
- function isMathShape(content) {
5256
- if (!MATH_SHAPE_CHARS.test(content)) return false;
5257
- const words = content.match(/[A-Za-z]+/g);
5258
- if (!words) return false;
5259
- if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
5260
- if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
5261
- return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
5230
+ // source/code-ranges.ts
5231
+ function fenceOpenerOf(content) {
5232
+ const t = content.trimStart();
5233
+ if (t.startsWith("```")) {
5234
+ let ticks = 0;
5235
+ while (ticks < t.length && t[ticks] === "`") ticks++;
5236
+ return { char: "`", ticks, lang: parseFenceOpener(t)?.lang ?? "" };
5237
+ }
5238
+ if (content.length - t.length <= 3 && t.startsWith("~~~")) {
5239
+ const open = /^(~{3,})(.*)$/.exec(t);
5240
+ if (!open) return null;
5241
+ return { char: "~", ticks: open[1].length, lang: open[2].trim().split(/\s/)[0] ?? "" };
5242
+ }
5243
+ return null;
5262
5244
  }
5263
- function isSingleDollarMath(content, after) {
5264
- if (!content || content.length > 400) return false;
5265
- if (/^\s/.test(content) || /\s$/.test(content)) return false;
5266
- if (after !== void 0 && /[0-9]/.test(after)) return false;
5267
- if (LONE_VARIABLE.test(content)) return true;
5268
- if (/^[0-9]/.test(content)) return /\\[A-Za-z]+|[\\^_{}]/.test(content);
5269
- return TEX_SIGNAL.test(content) || isMathShape(content);
5245
+ var FenceReader = class {
5246
+ constructor(opener, options = {}) {
5247
+ this.opener = opener;
5248
+ this.isJson = opener.char === "`" && opener.lang === "json";
5249
+ this.nesting = opener.char === "`" && (options.nesting ?? fenceNestsInnerFences(opener.lang));
5250
+ this.tildeCloser = opener.char === "~" ? new RegExp(`^ {0,3}~{${opener.ticks},}[ \\t]*$`) : null;
5251
+ }
5252
+ opener;
5253
+ state = { inString: false, escaped: false };
5254
+ depth = 0;
5255
+ /** A nested fence was opened at some point. */
5256
+ sawNested = false;
5257
+ isJson;
5258
+ nesting;
5259
+ tildeCloser;
5260
+ /** How many nested fences are open right now. */
5261
+ get nestedDepth() {
5262
+ return this.depth;
5263
+ }
5264
+ feed(line) {
5265
+ if (this.tildeCloser) return this.tildeCloser.test(line);
5266
+ const trimmedLine = trimFenceLine(line);
5267
+ const at = line.indexOf("```");
5268
+ if (at === -1) {
5269
+ if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
5270
+ return false;
5271
+ }
5272
+ if (this.isJson) {
5273
+ const probe = jsonStringState(line, 0, at, this.state);
5274
+ if (probe.inString) {
5275
+ this.state = jsonStringState(line, at, line.length, probe);
5276
+ return false;
5277
+ }
5278
+ }
5279
+ if (trimmedLine.startsWith("```")) {
5280
+ const kind = classifyInnerFenceLine(trimmedLine, this.opener.ticks, this.nesting, this.depth);
5281
+ if (kind === "close-outer") return true;
5282
+ if (kind === "open-nested") {
5283
+ this.depth++;
5284
+ this.sawNested = true;
5285
+ }
5286
+ if (kind === "close-nested") this.depth--;
5287
+ } else {
5288
+ const closeTicks = backtickRunLength(line, at);
5289
+ const after = trimFenceLine(line.slice(at + closeTicks));
5290
+ if (closeTicks >= this.opener.ticks && after === "" && this.depth === 0) return true;
5291
+ }
5292
+ if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
5293
+ return false;
5294
+ }
5295
+ };
5296
+ function closeFence(src, open, opener) {
5297
+ const first = readFence(src, open, new FenceReader(opener));
5298
+ if (first.closed || !first.reader.sawNested || first.reader.nestedDepth <= 0) return { line: first.line, closed: first.closed };
5299
+ const strict = readFence(src, open, new FenceReader(opener, { nesting: false }));
5300
+ return { line: strict.line, closed: strict.closed };
5270
5301
  }
5271
- function singleDollarMathEnd(text, open, limit = text.length) {
5272
- const bound = Math.min(limit, open + 403);
5273
- let close = -1;
5274
- for (let k = open + 1; k < bound; k += 1) {
5275
- const c = text[k];
5276
- if (c === "\n") break;
5277
- if (c === "\\") {
5278
- k += 1;
5302
+ function readFence(src, open, reader) {
5303
+ for (let k = open + 1; k < src.count; k++) {
5304
+ if (src.openTail?.(k)) continue;
5305
+ const line = src.line(k);
5306
+ if (line === null) return { line: k - 1, closed: false, reader };
5307
+ if (reader.feed(line)) return { line: k, closed: true, reader };
5308
+ }
5309
+ return { line: src.count - 1, closed: false, reader };
5310
+ }
5311
+ function jsonStringState(line, from, upTo, state) {
5312
+ let { inString, escaped } = state;
5313
+ for (let idx = from; idx < upTo; idx++) {
5314
+ const ch = line[idx];
5315
+ if (escaped) {
5316
+ escaped = false;
5279
5317
  continue;
5280
5318
  }
5281
- if (c === "$") {
5282
- close = k;
5283
- break;
5319
+ if (ch === "\\" && inString) {
5320
+ escaped = true;
5321
+ continue;
5284
5322
  }
5323
+ if (ch === '"') inString = !inString;
5285
5324
  }
5286
- if (close === -1 || text[close + 1] === "$") return -1;
5287
- const content = text.slice(open + 1, close);
5288
- return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
5325
+ return { inString, escaped };
5326
+ }
5327
+ function backtickRunLength(str2, pos) {
5328
+ let n = 0;
5329
+ while (pos + n < str2.length && str2[pos + n] === "`") n++;
5330
+ return n;
5331
+ }
5332
+ function quotePrefix(line) {
5333
+ let depth = 0;
5334
+ let offset = 0;
5335
+ for (; ; ) {
5336
+ const m = /^ {0,3}> ?/.exec(line.slice(offset));
5337
+ if (!m) return { depth, offset };
5338
+ depth++;
5339
+ offset += m[0].length;
5340
+ }
5341
+ }
5342
+ function stripQuotes(line, depth) {
5343
+ let offset = 0;
5344
+ for (let d = 0; d < depth; d++) {
5345
+ const m = /^ {0,3}> ?/.exec(line.slice(offset));
5346
+ if (!m) return null;
5347
+ offset += m[0].length;
5348
+ }
5349
+ return line.slice(offset);
5350
+ }
5351
+ var LIST_ITEM = /^( *)([-*+]|\d{1,9}[.)])( +|\t|$)/;
5352
+ function leadingSpaces(s) {
5353
+ let i = 0;
5354
+ while (i < s.length && s[i] === " ") i++;
5355
+ return i;
5356
+ }
5357
+ function scan(text) {
5358
+ const starts = [];
5359
+ const ends = [];
5360
+ for (let pos = 0; ; ) {
5361
+ const nl = text.indexOf("\n", pos);
5362
+ const lineEnd = nl === -1 ? text.length : nl;
5363
+ starts.push(pos);
5364
+ ends.push(lineEnd > pos && text.charCodeAt(lineEnd - 1) === 13 ? lineEnd - 1 : lineEnd);
5365
+ if (nl === -1) break;
5366
+ pos = nl + 1;
5367
+ }
5368
+ const count = starts.length;
5369
+ const raw = (k) => text.slice(starts[k], ends[k]);
5370
+ const out = { ranges: [], paragraphs: [], endsInFence: false, pendingPara: -1 };
5371
+ const rawLines = { count, line: raw };
5372
+ let segment = 0;
5373
+ for (let k = 0; k < count; k++) {
5374
+ const opener = fenceOpenerOf(raw(k));
5375
+ if (!opener || opener.char !== "`") continue;
5376
+ prose(text, starts, ends, raw, segment, k, out);
5377
+ const end = closeFence(rawLines, k, opener);
5378
+ out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
5379
+ out.pendingPara = -1;
5380
+ if (!end.closed && end.line === count - 1) out.endsInFence = true;
5381
+ k = end.line;
5382
+ segment = k + 1;
5383
+ }
5384
+ prose(text, starts, ends, raw, segment, count, out);
5385
+ return out;
5386
+ }
5387
+ function prose(text, starts, ends, raw, from, to, out) {
5388
+ let para = -1;
5389
+ let quoteDepth = 0;
5390
+ let lists = [];
5391
+ const closePara = (at) => {
5392
+ if (para !== -1) out.paragraphs.push([para, at]);
5393
+ para = -1;
5394
+ };
5395
+ for (let k = from; k < to; k++) {
5396
+ const line = raw(k);
5397
+ const { depth, offset } = quotePrefix(line);
5398
+ if (depth !== quoteDepth) {
5399
+ quoteDepth = depth;
5400
+ lists = [];
5401
+ }
5402
+ let content = line.slice(offset);
5403
+ if (content.trim() === "") {
5404
+ closePara(starts[k]);
5405
+ if (!(k === starts.length - 1 && line === "")) out.pendingPara = -1;
5406
+ continue;
5407
+ }
5408
+ const indent = leadingSpaces(content);
5409
+ while (lists.length && indent < lists[lists.length - 1]) lists.pop();
5410
+ const item = LIST_ITEM.exec(content);
5411
+ let column = lists.length ? lists[lists.length - 1] : 0;
5412
+ if (item) {
5413
+ const gap = item[3].length;
5414
+ column = item[1].length + item[2].length + (gap >= 1 && gap <= 4 ? gap : 1);
5415
+ lists.push(column);
5416
+ content = content.slice(Math.min(column, content.length));
5417
+ } else {
5418
+ content = content.slice(Math.min(indent, column));
5419
+ }
5420
+ const opener = fenceOpenerOf(content);
5421
+ if (!opener) {
5422
+ if (para === -1) para = starts[k];
5423
+ if (out.pendingPara === -1) out.pendingPara = starts[k];
5424
+ continue;
5425
+ }
5426
+ closePara(starts[k]);
5427
+ out.pendingPara = -1;
5428
+ const col = column;
5429
+ const src = {
5430
+ count: to,
5431
+ line: (j) => {
5432
+ const inner = stripQuotes(raw(j), depth);
5433
+ if (inner === null) return null;
5434
+ if (opener.char === "~" && col > 0) {
5435
+ const ind = leadingSpaces(inner);
5436
+ if (inner.trim() !== "" && ind < col) return null;
5437
+ return inner.slice(Math.min(ind, col));
5438
+ }
5439
+ return inner;
5440
+ }
5441
+ };
5442
+ const end = closeFence(src, k, opener);
5443
+ out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
5444
+ if (!end.closed && end.line === starts.length - 1) out.endsInFence = true;
5445
+ k = end.line;
5446
+ }
5447
+ closePara(to < starts.length ? starts[to] : text.length);
5448
+ }
5449
+ var BacktickRuns = class {
5450
+ /** Every run, in order: [start, length]. */
5451
+ all = [];
5452
+ byLength = /* @__PURE__ */ new Map();
5453
+ indexOf = /* @__PURE__ */ new Map();
5454
+ constructor(text, from, to) {
5455
+ for (let k = from; k < to; ) {
5456
+ if (text.charCodeAt(k) !== 96) {
5457
+ k++;
5458
+ continue;
5459
+ }
5460
+ let len = 1;
5461
+ while (k + len < to && text.charCodeAt(k + len) === 96) len++;
5462
+ this.indexOf.set(k, this.all.length);
5463
+ this.all.push([k, len]);
5464
+ const list = this.byLength.get(len);
5465
+ if (list) list.push(k);
5466
+ else this.byLength.set(len, [k]);
5467
+ k += len;
5468
+ }
5469
+ }
5470
+ /** Start of the first run of exactly `n` backticks at or after `after`, or -1. */
5471
+ next(n, after) {
5472
+ const list = this.byLength.get(n);
5473
+ if (!list) return -1;
5474
+ let lo = 0;
5475
+ let hi = list.length;
5476
+ while (lo < hi) {
5477
+ const mid = lo + hi >>> 1;
5478
+ if (list[mid] < after) lo = mid + 1;
5479
+ else hi = mid;
5480
+ }
5481
+ return list[lo] ?? -1;
5482
+ }
5483
+ /** Index of the run starting at `start` in `all`. */
5484
+ index(start) {
5485
+ return this.indexOf.get(start) ?? -1;
5486
+ }
5487
+ };
5488
+ function codeSpans(text, from, to, out) {
5489
+ const runs = new BacktickRuns(text, from, to);
5490
+ for (let r = 0; r < runs.all.length; ) {
5491
+ const [start, n] = runs.all[r];
5492
+ const close = runs.next(n, start + n);
5493
+ if (close === -1) {
5494
+ r++;
5495
+ continue;
5496
+ }
5497
+ out.push({ start, end: close + n, kind: "span" });
5498
+ r = runs.index(close) + 1;
5499
+ }
5500
+ }
5501
+ function findCodeRanges(text) {
5502
+ const s = scan(text);
5503
+ const ranges = [...s.ranges];
5504
+ for (const [from, to] of s.paragraphs) codeSpans(text, from, to, ranges);
5505
+ return ranges.sort((a, b) => a.start - b.start);
5506
+ }
5507
+ function pendingCodeSpanStart(text) {
5508
+ const n = text.length;
5509
+ if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
5510
+ const s = scan(text);
5511
+ const para = s.pendingPara;
5512
+ if (s.endsInFence || para === -1) return -1;
5513
+ const runs = [];
5514
+ for (let k = para; k < n; ) {
5515
+ if (text.charCodeAt(k) !== 96) {
5516
+ k++;
5517
+ continue;
5518
+ }
5519
+ let len = 1;
5520
+ while (k + len < n && text.charCodeAt(k + len) === 96) len++;
5521
+ runs.push([k, len]);
5522
+ k += len;
5523
+ }
5524
+ const growing = (r) => {
5525
+ const [start, len] = runs[r];
5526
+ return start + len === n;
5527
+ };
5528
+ for (let r = 0; r < runs.length; ) {
5529
+ if (growing(r)) return runs[r][0];
5530
+ const len = runs[r][1];
5531
+ let j = r + 1;
5532
+ while (j < runs.length && (runs[j][1] !== len || growing(j))) j++;
5533
+ if (j >= runs.length) return runs[r][0];
5534
+ r = j + 1;
5535
+ }
5536
+ return -1;
5289
5537
  }
5290
5538
 
5291
5539
  // source/math-pairs.ts
@@ -5314,86 +5562,7 @@ function looksLikeDisplayMath(inner, flow = false) {
5314
5562
  return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT;
5315
5563
  }
5316
5564
  function codeRanges(text) {
5317
- const ranges = [];
5318
- const n = text.length;
5319
- let fence = null;
5320
- let para = -1;
5321
- const spans = (from, to) => {
5322
- const runs = [];
5323
- for (let k = from; k < to; ) {
5324
- if (text.charCodeAt(k) !== 96) {
5325
- k++;
5326
- continue;
5327
- }
5328
- let len = 1;
5329
- while (k + len < to && text.charCodeAt(k + len) === 96) len++;
5330
- runs.push([k, len]);
5331
- k += len;
5332
- }
5333
- const nextSame = new Array(runs.length).fill(-1);
5334
- const last = /* @__PURE__ */ new Map();
5335
- for (let r = runs.length - 1; r >= 0; r--) {
5336
- const len = runs[r][1];
5337
- nextSame[r] = last.get(len) ?? -1;
5338
- last.set(len, r);
5339
- }
5340
- for (let r = 0; r < runs.length; ) {
5341
- const j = nextSame[r];
5342
- if (j === -1) {
5343
- r++;
5344
- continue;
5345
- }
5346
- const [s0] = runs[r];
5347
- const [s1, l1] = runs[j];
5348
- ranges.push([s0, s1 + l1]);
5349
- r = j + 1;
5350
- }
5351
- };
5352
- let pos = 0;
5353
- while (pos <= n) {
5354
- const nl = text.indexOf("\n", pos);
5355
- const lineEnd = nl === -1 ? n : nl;
5356
- const line = text.slice(pos, lineEnd);
5357
- let indent = 0;
5358
- while (indent < line.length && line[indent] === " ") indent++;
5359
- const body = line.slice(indent);
5360
- if (fence) {
5361
- const trimmed = trimFenceLine(line);
5362
- let closes = false;
5363
- if (fence.char === "`") {
5364
- const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
5365
- if (kind === "open-nested") fence.depth++;
5366
- else if (kind === "close-nested") fence.depth--;
5367
- else if (kind === "close-outer") closes = indent <= 3;
5368
- } else {
5369
- let t = 0;
5370
- while (t < trimmed.length && trimmed[t] === "~") t++;
5371
- closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
5372
- }
5373
- if (closes) {
5374
- ranges.push([fence.start, lineEnd]);
5375
- fence = null;
5376
- }
5377
- } else {
5378
- const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
5379
- const info = opener ? opener[2] : "";
5380
- if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
5381
- if (para !== -1) spans(para, pos);
5382
- para = -1;
5383
- const char = opener[1][0];
5384
- const parsed = char === "`" ? parseFenceOpener(line) : null;
5385
- fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
5386
- } else if (body.trim() === "") {
5387
- if (para !== -1) spans(para, pos);
5388
- para = -1;
5389
- } else if (para === -1) para = pos;
5390
- }
5391
- if (nl === -1) break;
5392
- pos = nl + 1;
5393
- }
5394
- if (fence) ranges.push([fence.start, n]);
5395
- else if (para !== -1) spans(para, n);
5396
- return ranges.sort((a, b) => a[0] - b[0]);
5565
+ return findCodeRanges(text).map((r) => [r.start, r.end]);
5397
5566
  }
5398
5567
  function pendingDisplayMathOpener(text) {
5399
5568
  if (!text.includes("$$")) return -1;
@@ -5477,6 +5646,83 @@ function pairDisplayMath(text) {
5477
5646
  return pairs;
5478
5647
  }
5479
5648
 
5649
+ // source/math.ts
5650
+ var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
5651
+ var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
5652
+ var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
5653
+ function isSingleDollarMath(content, after) {
5654
+ if (!content || content.length > 400) return false;
5655
+ if (/^\s/.test(content) || /\s$/.test(content)) return false;
5656
+ if (after !== void 0 && /[0-9]/.test(after)) return false;
5657
+ if (/(?:^|[^\\])%/.test(content)) return false;
5658
+ if (/^[)\]}"/]/.test(content) || /[(\[{":;,/]$/.test(content)) return false;
5659
+ if (/^[0-9]/.test(content) && after !== void 0 && /[A-Za-z]/.test(after)) return false;
5660
+ if (isMathPlaceholder(content)) return false;
5661
+ return TEX_SIGNAL.test(content) || !readsAsProse(content);
5662
+ }
5663
+ var FUNCTION_NAMES = /* @__PURE__ */ new Set([
5664
+ "sin",
5665
+ "cos",
5666
+ "tan",
5667
+ "sec",
5668
+ "csc",
5669
+ "cot",
5670
+ "log",
5671
+ "ln",
5672
+ "exp",
5673
+ "lim",
5674
+ "max",
5675
+ "min",
5676
+ "det",
5677
+ "gcd",
5678
+ "mod",
5679
+ "arg",
5680
+ "sinh",
5681
+ "cosh",
5682
+ "tanh"
5683
+ ]);
5684
+ function readsAsProse(content) {
5685
+ if (/[A-Za-z]'[a-z]/.test(content)) return true;
5686
+ if (/(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return true;
5687
+ if (!/\s/.test(content)) return false;
5688
+ const words = (content.match(/[A-Za-z]{3,}/g) ?? []).filter((w) => !FUNCTION_NAMES.has(w.toLowerCase()));
5689
+ return words.length >= 2;
5690
+ }
5691
+ function looksLikeOpenInlineMath(partial) {
5692
+ if (!partial || partial.length > 400) return false;
5693
+ if (/^\s/.test(partial) || /\s$/.test(partial)) return false;
5694
+ if (LONE_VARIABLE.test(partial)) return true;
5695
+ if (/^[0-9]/.test(partial)) return /\\[A-Za-z]+|[\\^_{}]/.test(partial);
5696
+ return TEX_SIGNAL.test(partial) || isMathShape(partial);
5697
+ }
5698
+ function isMathShape(content) {
5699
+ if (!MATH_SHAPE_CHARS.test(content)) return false;
5700
+ const words = content.match(/[A-Za-z]+/g);
5701
+ if (!words) return false;
5702
+ if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
5703
+ if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
5704
+ return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
5705
+ }
5706
+ function singleDollarMathEnd(text, open, limit = text.length) {
5707
+ const bound = Math.min(limit, open + 403);
5708
+ let close = -1;
5709
+ for (let k = open + 1; k < bound; k += 1) {
5710
+ const c = text[k];
5711
+ if (c === "\n") break;
5712
+ if (c === "\\") {
5713
+ k += 1;
5714
+ continue;
5715
+ }
5716
+ if (c === "$") {
5717
+ close = k;
5718
+ break;
5719
+ }
5720
+ }
5721
+ if (close === -1 || text[close + 1] === "$") return -1;
5722
+ const content = text.slice(open + 1, close);
5723
+ return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
5724
+ }
5725
+
5480
5726
  // source/xml-tag.ts
5481
5727
  var XML_NAME_START = /[A-Za-z_]/;
5482
5728
  var XML_NAME_CHARACTER = /[\w.:-]/;
@@ -5769,27 +6015,6 @@ function isUnclosedGenericXmlOpening(source) {
5769
6015
  }
5770
6016
  return true;
5771
6017
  }
5772
- function jsonStringState(line, from, upTo, state) {
5773
- let { inString, escaped } = state;
5774
- for (let idx = from; idx < upTo; idx++) {
5775
- const ch = line[idx];
5776
- if (escaped) {
5777
- escaped = false;
5778
- continue;
5779
- }
5780
- if (ch === "\\" && inString) {
5781
- escaped = true;
5782
- continue;
5783
- }
5784
- if (ch === '"') inString = !inString;
5785
- }
5786
- return { inString, escaped };
5787
- }
5788
- function backtickRunLength(str2, pos) {
5789
- let n = 0;
5790
- while (pos + n < str2.length && str2[pos + n] === "`") n++;
5791
- return n;
5792
- }
5793
6018
  function isSplitterTreeLine(line) {
5794
6019
  return TREE_GLYPHS.test(line) || /^[\s│|]*[├└+|][\s─\-]+/.test(line);
5795
6020
  }
@@ -5805,6 +6030,14 @@ var Tokenizer = class {
5805
6030
  this.provisionalFrom = streaming ? pendingDisplayMathOpener(text) : -1;
5806
6031
  if (this.provisionalFrom === -1) this.provisionalFrom = Number.POSITIVE_INFINITY;
5807
6032
  if (streaming && text.includes("$$")) {
6033
+ const span = pendingCodeSpanStart(text);
6034
+ if (span !== -1) {
6035
+ const firstToken = text.indexOf("$$", span);
6036
+ if (firstToken !== -1) this.provisionalFrom = Math.min(this.provisionalFrom, firstToken);
6037
+ for (const [open, close] of this.mathPairs) {
6038
+ if (close >= span) this.provisionalFrom = Math.min(this.provisionalFrom, open);
6039
+ }
6040
+ }
5808
6041
  for (const pad of ["`", "``", "```", "~~~"]) {
5809
6042
  const alt = pairDisplayMath(text + pad);
5810
6043
  for (const [open, close] of this.mathPairs) {
@@ -6013,11 +6246,9 @@ var Tokenizer = class {
6013
6246
  const footnote = this.footnoteDefinition(p, li, t);
6014
6247
  if (footnote) return footnote;
6015
6248
  }
6016
- if (t.startsWith("```")) return this.backtickFence(p, li, t);
6017
- if (indent <= 3 && t.startsWith("~~~")) {
6018
- const tilde = this.tildeFence(p, li, t);
6019
- if (tilde) return tilde;
6020
- }
6249
+ const fence = fenceOpenerOf(seg);
6250
+ if (fence?.char === "`") return this.backtickFence(p, li, fence);
6251
+ if (fence?.char === "~") return this.tildeFence(p, li, fence);
6021
6252
  if (t.startsWith("$$")) {
6022
6253
  const math = this.mathBlock(p, tp);
6023
6254
  if (math) return math;
@@ -6160,8 +6391,8 @@ var Tokenizer = class {
6160
6391
  last = k;
6161
6392
  continue;
6162
6393
  }
6163
- if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
6164
6394
  if (this.isOpenTail(k)) return this.heldFootnote(p, open[1]);
6395
+ if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
6165
6396
  last = k;
6166
6397
  }
6167
6398
  const end = this.contentEnds[last];
@@ -6172,74 +6403,31 @@ var Tokenizer = class {
6172
6403
  heldFootnote(p, id) {
6173
6404
  return this.island("footnote_def", p, this.n, false, { id });
6174
6405
  }
6406
+ /** The text's lines as the one fence closer reads them (streaming: the open tail closes nothing). */
6407
+ get fenceLines() {
6408
+ return {
6409
+ count: this.lineCount,
6410
+ line: (k) => this.lineText(k),
6411
+ openTail: (k) => this.isOpenTail(k)
6412
+ };
6413
+ }
6175
6414
  /**
6176
- * Backtick fence — the renderer splitter's close rules: JSON-string aware for
6177
- * ```json, and THE nested-fence rule (source/fence-nesting.ts) for markdown
6178
- * fences, with the splitter's strict-CommonMark retry when a nested fence is
6179
- * still open at the end of the text.
6415
+ * Backtick fence — THE one closer (`closeFence`, source/code-ranges.ts): the
6416
+ * nested-fence rule for markdown fences with the strict-CommonMark retry, the
6417
+ * renderer's line-end closer, JSON-string awareness for ```json.
6180
6418
  */
6181
- backtickFence(p, li, t) {
6182
- const openTicks = backtickRunLength(t, 0);
6183
- const lang = parseFenceOpener(t)?.lang ?? "";
6419
+ backtickFence(p, li, opener) {
6420
+ const lang = opener.lang;
6184
6421
  const isJson = lang === "json";
6185
6422
  const normalized = normalizeFenceLanguage(lang);
6186
- const meta = { fence: "`", ticks: openTicks, lang };
6423
+ const meta = { fence: "`", ticks: opener.ticks, lang };
6187
6424
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
6188
- const nesting = fenceNestsInnerFences(lang);
6189
- let close = this.backtickFenceClose(li, openTicks, isJson, nesting);
6190
- if (close === "retry-strict") close = this.backtickFenceClose(li, openTicks, isJson, false);
6191
- if (typeof close === "number") {
6192
- return this.fenceIsland(p, this.contentEnds[close], true, meta, li, close, isJson);
6425
+ const end = closeFence(this.fenceLines, li, opener);
6426
+ if (end.closed) {
6427
+ return this.fenceIsland(p, this.contentEnds[end.line], true, meta, li, end.line, isJson);
6193
6428
  }
6194
6429
  return this.fenceIsland(p, this.n, false, meta, li, this.lineCount, isJson);
6195
6430
  }
6196
- /** The closing line of a backtick fence opened on line `li`, or null when it never closes. */
6197
- backtickFenceClose(li, openTicks, isJson, nesting) {
6198
- let state = { inString: false, escaped: false };
6199
- let nestedDepth = 0;
6200
- let sawNested = false;
6201
- for (let k = li + 1; k < this.lineCount; k++) {
6202
- if (this.isOpenTail(k)) continue;
6203
- const line = this.lineText(k);
6204
- const trimmedLine = trimFenceLine(line);
6205
- if (trimmedLine.startsWith("```")) {
6206
- const at2 = line.indexOf("```");
6207
- if (isJson) {
6208
- const probe = jsonStringState(line, 0, at2, state);
6209
- if (probe.inString) {
6210
- state = jsonStringState(line, at2, line.length, probe);
6211
- continue;
6212
- }
6213
- }
6214
- const kind = classifyInnerFenceLine(trimmedLine, openTicks, nesting, nestedDepth);
6215
- if (kind === "close-outer") return k;
6216
- if (kind === "open-nested") {
6217
- nestedDepth++;
6218
- sawNested = true;
6219
- }
6220
- if (kind === "close-nested") nestedDepth--;
6221
- if (isJson) state = jsonStringState(line, 0, line.length, state);
6222
- continue;
6223
- }
6224
- const at = line.indexOf("```");
6225
- if (at !== -1) {
6226
- if (isJson) {
6227
- const probe = jsonStringState(line, 0, at, state);
6228
- if (probe.inString) {
6229
- state = jsonStringState(line, at, line.length, probe);
6230
- continue;
6231
- }
6232
- }
6233
- const closeTicks = backtickRunLength(line, at);
6234
- const after = trimFenceLine(line.slice(at + closeTicks));
6235
- if (closeTicks >= openTicks && after === "" && nestedDepth === 0) return k;
6236
- if (isJson) state = jsonStringState(line, 0, line.length, state);
6237
- continue;
6238
- }
6239
- if (isJson) state = jsonStringState(line, 0, line.length, state);
6240
- }
6241
- return sawNested && nestedDepth > 0 ? "retry-strict" : null;
6242
- }
6243
6431
  fenceIsland(p, end, complete, meta, openLine, closeLine, isJson) {
6244
6432
  if (isJson && openLine + 1 < this.lineCount) {
6245
6433
  const bodyEnd = closeLine < this.lineCount ? this.lineStarts[closeLine] : this.n;
@@ -6248,22 +6436,16 @@ var Tokenizer = class {
6248
6436
  }
6249
6437
  return this.island("fence", p, end, complete, meta);
6250
6438
  }
6251
- tildeFence(p, li, t) {
6252
- const open = /^(~{3,})(.*)$/.exec(t);
6253
- if (!open) return null;
6254
- const ticks = open[1].length;
6255
- const lang = open[2].trim().split(/\s/)[0] ?? "";
6256
- const meta = { fence: "~", ticks, lang };
6439
+ tildeFence(p, li, opener) {
6440
+ const lang = opener.lang;
6441
+ const meta = { fence: "~", ticks: opener.ticks, lang };
6257
6442
  const normalized = normalizeFenceLanguage(lang);
6258
6443
  if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
6259
- const close = new RegExp(`^ {0,3}~{${ticks},}[ \\t]*$`);
6260
- for (let k = li + 1; k < this.lineCount; k++) {
6261
- if (this.isOpenTail(k)) continue;
6262
- if (close.test(this.lineText(k))) {
6263
- const tildeEnd = this.contentEnds[k];
6264
- const end = this.rendererOverflow(li + 1, k, tildeEnd);
6265
- return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
6266
- }
6444
+ const close = closeFence(this.fenceLines, li, opener);
6445
+ if (close.closed) {
6446
+ const tildeEnd = this.contentEnds[close.line];
6447
+ const end = this.rendererOverflow(li + 1, close.line, tildeEnd);
6448
+ return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
6267
6449
  }
6268
6450
  return this.island("fence", p, this.n, false, meta);
6269
6451
  }
@@ -6283,7 +6465,7 @@ var Tokenizer = class {
6283
6465
  for (let j = firstLine; j <= closeLine && j < this.lineCount; j++) {
6284
6466
  const start = this.lineStarts[j];
6285
6467
  if (this.lineIsBlank(j)) continue;
6286
- if (this.lineText(j).trimStart().startsWith("~~~")) continue;
6468
+ if (fenceOpenerOf(this.lineText(j))?.char === "~") continue;
6287
6469
  const inner = this.detectBlockUncached(start, j, false);
6288
6470
  if (!inner) continue;
6289
6471
  if (inner.end > end) {
@@ -6531,9 +6713,9 @@ var Tokenizer = class {
6531
6713
  const tree = { ...breakIsland, start: treeStart };
6532
6714
  if (treeStart > pos) {
6533
6715
  const proseEnd = this.contentEnds[root - 1];
6534
- const scan2 = this.scanInline(pos, proseEnd);
6535
- if (!scan2.breakIsland) {
6536
- this.push({ kind: "prose", start: pos, end: proseEnd, island: null, inlines: scan2.inlines });
6716
+ const scan3 = this.scanInline(pos, proseEnd);
6717
+ if (!scan3.breakIsland) {
6718
+ this.push({ kind: "prose", start: pos, end: proseEnd, island: null, inlines: scan3.inlines });
6537
6719
  this.push({ kind: "gap", start: proseEnd, end: treeStart, island: null, inlines: [] });
6538
6720
  this.push({ kind: "island", start: treeStart, end: tree.end, island: tree, inlines: [] });
6539
6721
  return tree.end;
@@ -6545,13 +6727,13 @@ var Tokenizer = class {
6545
6727
  }
6546
6728
  }
6547
6729
  }
6548
- const scan = this.scanInline(pos, end);
6549
- if (!scan.breakIsland) {
6550
- this.push({ kind: "prose", start: pos, end, island: null, inlines: scan.inlines });
6730
+ const scan2 = this.scanInline(pos, end);
6731
+ if (!scan2.breakIsland) {
6732
+ this.push({ kind: "prose", start: pos, end, island: null, inlines: scan2.inlines });
6551
6733
  return end;
6552
6734
  }
6553
- const br = scan.breakIsland;
6554
- if (scan.orphan) {
6735
+ const br = scan2.breakIsland;
6736
+ if (scan2.orphan) {
6555
6737
  let start = pos;
6556
6738
  while (this.blocks.length) {
6557
6739
  const last = this.blocks[this.blocks.length - 1];
@@ -6568,7 +6750,7 @@ var Tokenizer = class {
6568
6750
  start: pos,
6569
6751
  end: br.start,
6570
6752
  island: null,
6571
- inlines: scan.inlines.filter((inline) => inline.end <= br.start)
6753
+ inlines: scan2.inlines.filter((inline) => inline.end <= br.start)
6572
6754
  });
6573
6755
  this.push({ kind: "island", start: br.start, end: br.end, island: br, inlines: [] });
6574
6756
  return br.end;
@@ -6588,30 +6770,10 @@ var Tokenizer = class {
6588
6770
  const codeSpanClose = (n, from) => {
6589
6771
  if (!runs) {
6590
6772
  this.tick(pe - ps);
6591
- runs = /* @__PURE__ */ new Map();
6592
- for (let k = ps; k < pe; ) {
6593
- if (text.charCodeAt(k) !== 96) {
6594
- k++;
6595
- continue;
6596
- }
6597
- const len = backtickRunLength(text, k);
6598
- const list2 = runs.get(len);
6599
- if (list2) list2.push(k);
6600
- else runs.set(len, [k]);
6601
- k += len;
6602
- }
6773
+ runs = new BacktickRuns(text, ps, pe);
6603
6774
  }
6604
- const list = runs.get(n);
6605
- if (!list) return -1;
6606
- let lo = 0;
6607
- let hi = list.length;
6608
- while (lo < hi) {
6609
- const mid = lo + hi >>> 1;
6610
- if (list[mid] < from) lo = mid + 1;
6611
- else hi = mid;
6612
- }
6613
- const at = list[lo];
6614
- return at !== void 0 && at + n <= pe ? at : -1;
6775
+ const at = runs.next(n, from);
6776
+ return at !== -1 && at + n <= pe ? at : -1;
6615
6777
  };
6616
6778
  let i = ps;
6617
6779
  const scanEnd = Math.min(pe, this.provisionalFrom);
@@ -7211,6 +7373,7 @@ exports.executesAtOutputRoot = executesAtOutputRoot;
7211
7373
  exports.fenceDiscriminator = fenceDiscriminator;
7212
7374
  exports.fenceNestsInnerFences = fenceNestsInnerFences;
7213
7375
  exports.fieldsToDbPayload = fieldsToDbPayload;
7376
+ exports.findCodeRanges = findCodeRanges;
7214
7377
  exports.fingerprintText = fingerprintText;
7215
7378
  exports.formatBlockLabel = formatBlockLabel;
7216
7379
  exports.getParseSession = getParseSession;
@@ -7246,6 +7409,7 @@ exports.legacyShellUses = legacyShellUses;
7246
7409
  exports.listIslands = listIslands;
7247
7410
  exports.looksLikeDirectiveHead = looksLikeDirectiveHead;
7248
7411
  exports.looksLikeDisplayMath = looksLikeDisplayMath;
7412
+ exports.looksLikeOpenInlineMath = looksLikeOpenInlineMath;
7249
7413
  exports.makePartialKindStalenessGate = makePartialKindStalenessGate;
7250
7414
  exports.mapPosition = mapPosition;
7251
7415
  exports.mapRange = mapRange;