@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/CHANGELOG.md +95 -1
- package/README.md +24 -0
- package/dist/index.cjs +431 -267
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +430 -268
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +431 -267
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +56 -14
- package/dist/source.d.ts +56 -14
- package/dist/source.js +430 -268
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
package/dist/source.cjs
CHANGED
|
@@ -254,65 +254,313 @@ function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
|
|
|
254
254
|
return "content";
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
// source/
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
"lim",
|
|
272
|
-
"max",
|
|
273
|
-
"min",
|
|
274
|
-
"det",
|
|
275
|
-
"gcd",
|
|
276
|
-
"mod",
|
|
277
|
-
"arg",
|
|
278
|
-
"sinh",
|
|
279
|
-
"cosh",
|
|
280
|
-
"tanh"
|
|
281
|
-
]);
|
|
282
|
-
function isMathShape(content) {
|
|
283
|
-
if (!MATH_SHAPE_CHARS.test(content)) return false;
|
|
284
|
-
const words = content.match(/[A-Za-z]+/g);
|
|
285
|
-
if (!words) return false;
|
|
286
|
-
if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
|
|
287
|
-
if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
|
|
288
|
-
return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
|
|
257
|
+
// source/code-ranges.ts
|
|
258
|
+
function fenceOpenerOf(content) {
|
|
259
|
+
const t = content.trimStart();
|
|
260
|
+
if (t.startsWith("```")) {
|
|
261
|
+
let ticks = 0;
|
|
262
|
+
while (ticks < t.length && t[ticks] === "`") ticks++;
|
|
263
|
+
return { char: "`", ticks, lang: parseFenceOpener(t)?.lang ?? "" };
|
|
264
|
+
}
|
|
265
|
+
if (content.length - t.length <= 3 && t.startsWith("~~~")) {
|
|
266
|
+
const open = /^(~{3,})(.*)$/.exec(t);
|
|
267
|
+
if (!open) return null;
|
|
268
|
+
return { char: "~", ticks: open[1].length, lang: open[2].trim().split(/\s/)[0] ?? "" };
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
289
271
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
272
|
+
var FenceReader = class {
|
|
273
|
+
constructor(opener, options = {}) {
|
|
274
|
+
this.opener = opener;
|
|
275
|
+
this.isJson = opener.char === "`" && opener.lang === "json";
|
|
276
|
+
this.nesting = opener.char === "`" && (options.nesting ?? fenceNestsInnerFences(opener.lang));
|
|
277
|
+
this.tildeCloser = opener.char === "~" ? new RegExp(`^ {0,3}~{${opener.ticks},}[ \\t]*$`) : null;
|
|
278
|
+
}
|
|
279
|
+
opener;
|
|
280
|
+
state = { inString: false, escaped: false };
|
|
281
|
+
depth = 0;
|
|
282
|
+
/** A nested fence was opened at some point. */
|
|
283
|
+
sawNested = false;
|
|
284
|
+
isJson;
|
|
285
|
+
nesting;
|
|
286
|
+
tildeCloser;
|
|
287
|
+
/** How many nested fences are open right now. */
|
|
288
|
+
get nestedDepth() {
|
|
289
|
+
return this.depth;
|
|
290
|
+
}
|
|
291
|
+
feed(line) {
|
|
292
|
+
if (this.tildeCloser) return this.tildeCloser.test(line);
|
|
293
|
+
const trimmedLine = trimFenceLine(line);
|
|
294
|
+
const at = line.indexOf("```");
|
|
295
|
+
if (at === -1) {
|
|
296
|
+
if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
if (this.isJson) {
|
|
300
|
+
const probe = jsonStringState(line, 0, at, this.state);
|
|
301
|
+
if (probe.inString) {
|
|
302
|
+
this.state = jsonStringState(line, at, line.length, probe);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (trimmedLine.startsWith("```")) {
|
|
307
|
+
const kind = classifyInnerFenceLine(trimmedLine, this.opener.ticks, this.nesting, this.depth);
|
|
308
|
+
if (kind === "close-outer") return true;
|
|
309
|
+
if (kind === "open-nested") {
|
|
310
|
+
this.depth++;
|
|
311
|
+
this.sawNested = true;
|
|
312
|
+
}
|
|
313
|
+
if (kind === "close-nested") this.depth--;
|
|
314
|
+
} else {
|
|
315
|
+
const closeTicks = backtickRunLength(line, at);
|
|
316
|
+
const after = trimFenceLine(line.slice(at + closeTicks));
|
|
317
|
+
if (closeTicks >= this.opener.ticks && after === "" && this.depth === 0) return true;
|
|
318
|
+
}
|
|
319
|
+
if (this.isJson) this.state = jsonStringState(line, 0, line.length, this.state);
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
function closeFence(src, open, opener) {
|
|
324
|
+
const first = readFence(src, open, new FenceReader(opener));
|
|
325
|
+
if (first.closed || !first.reader.sawNested || first.reader.nestedDepth <= 0) return { line: first.line, closed: first.closed };
|
|
326
|
+
const strict = readFence(src, open, new FenceReader(opener, { nesting: false }));
|
|
327
|
+
return { line: strict.line, closed: strict.closed };
|
|
297
328
|
}
|
|
298
|
-
function
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
329
|
+
function readFence(src, open, reader) {
|
|
330
|
+
for (let k = open + 1; k < src.count; k++) {
|
|
331
|
+
if (src.openTail?.(k)) continue;
|
|
332
|
+
const line = src.line(k);
|
|
333
|
+
if (line === null) return { line: k - 1, closed: false, reader };
|
|
334
|
+
if (reader.feed(line)) return { line: k, closed: true, reader };
|
|
335
|
+
}
|
|
336
|
+
return { line: src.count - 1, closed: false, reader };
|
|
337
|
+
}
|
|
338
|
+
function jsonStringState(line, from, upTo, state) {
|
|
339
|
+
let { inString, escaped } = state;
|
|
340
|
+
for (let idx = from; idx < upTo; idx++) {
|
|
341
|
+
const ch = line[idx];
|
|
342
|
+
if (escaped) {
|
|
343
|
+
escaped = false;
|
|
306
344
|
continue;
|
|
307
345
|
}
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
346
|
+
if (ch === "\\" && inString) {
|
|
347
|
+
escaped = true;
|
|
348
|
+
continue;
|
|
311
349
|
}
|
|
350
|
+
if (ch === '"') inString = !inString;
|
|
312
351
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
352
|
+
return { inString, escaped };
|
|
353
|
+
}
|
|
354
|
+
function backtickRunLength(str, pos) {
|
|
355
|
+
let n = 0;
|
|
356
|
+
while (pos + n < str.length && str[pos + n] === "`") n++;
|
|
357
|
+
return n;
|
|
358
|
+
}
|
|
359
|
+
function quotePrefix(line) {
|
|
360
|
+
let depth = 0;
|
|
361
|
+
let offset = 0;
|
|
362
|
+
for (; ; ) {
|
|
363
|
+
const m = /^ {0,3}> ?/.exec(line.slice(offset));
|
|
364
|
+
if (!m) return { depth, offset };
|
|
365
|
+
depth++;
|
|
366
|
+
offset += m[0].length;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function stripQuotes(line, depth) {
|
|
370
|
+
let offset = 0;
|
|
371
|
+
for (let d = 0; d < depth; d++) {
|
|
372
|
+
const m = /^ {0,3}> ?/.exec(line.slice(offset));
|
|
373
|
+
if (!m) return null;
|
|
374
|
+
offset += m[0].length;
|
|
375
|
+
}
|
|
376
|
+
return line.slice(offset);
|
|
377
|
+
}
|
|
378
|
+
var LIST_ITEM = /^( *)([-*+]|\d{1,9}[.)])( +|\t|$)/;
|
|
379
|
+
function leadingSpaces(s) {
|
|
380
|
+
let i = 0;
|
|
381
|
+
while (i < s.length && s[i] === " ") i++;
|
|
382
|
+
return i;
|
|
383
|
+
}
|
|
384
|
+
function scan(text) {
|
|
385
|
+
const starts = [];
|
|
386
|
+
const ends = [];
|
|
387
|
+
for (let pos = 0; ; ) {
|
|
388
|
+
const nl = text.indexOf("\n", pos);
|
|
389
|
+
const lineEnd = nl === -1 ? text.length : nl;
|
|
390
|
+
starts.push(pos);
|
|
391
|
+
ends.push(lineEnd > pos && text.charCodeAt(lineEnd - 1) === 13 ? lineEnd - 1 : lineEnd);
|
|
392
|
+
if (nl === -1) break;
|
|
393
|
+
pos = nl + 1;
|
|
394
|
+
}
|
|
395
|
+
const count = starts.length;
|
|
396
|
+
const raw = (k) => text.slice(starts[k], ends[k]);
|
|
397
|
+
const out = { ranges: [], paragraphs: [], endsInFence: false, pendingPara: -1 };
|
|
398
|
+
const rawLines = { count, line: raw };
|
|
399
|
+
let segment = 0;
|
|
400
|
+
for (let k = 0; k < count; k++) {
|
|
401
|
+
const opener = fenceOpenerOf(raw(k));
|
|
402
|
+
if (!opener || opener.char !== "`") continue;
|
|
403
|
+
prose(text, starts, ends, raw, segment, k, out);
|
|
404
|
+
const end = closeFence(rawLines, k, opener);
|
|
405
|
+
out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
|
|
406
|
+
out.pendingPara = -1;
|
|
407
|
+
if (!end.closed && end.line === count - 1) out.endsInFence = true;
|
|
408
|
+
k = end.line;
|
|
409
|
+
segment = k + 1;
|
|
410
|
+
}
|
|
411
|
+
prose(text, starts, ends, raw, segment, count, out);
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
function prose(text, starts, ends, raw, from, to, out) {
|
|
415
|
+
let para = -1;
|
|
416
|
+
let quoteDepth = 0;
|
|
417
|
+
let lists = [];
|
|
418
|
+
const closePara = (at) => {
|
|
419
|
+
if (para !== -1) out.paragraphs.push([para, at]);
|
|
420
|
+
para = -1;
|
|
421
|
+
};
|
|
422
|
+
for (let k = from; k < to; k++) {
|
|
423
|
+
const line = raw(k);
|
|
424
|
+
const { depth, offset } = quotePrefix(line);
|
|
425
|
+
if (depth !== quoteDepth) {
|
|
426
|
+
quoteDepth = depth;
|
|
427
|
+
lists = [];
|
|
428
|
+
}
|
|
429
|
+
let content = line.slice(offset);
|
|
430
|
+
if (content.trim() === "") {
|
|
431
|
+
closePara(starts[k]);
|
|
432
|
+
if (!(k === starts.length - 1 && line === "")) out.pendingPara = -1;
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const indent = leadingSpaces(content);
|
|
436
|
+
while (lists.length && indent < lists[lists.length - 1]) lists.pop();
|
|
437
|
+
const item = LIST_ITEM.exec(content);
|
|
438
|
+
let column = lists.length ? lists[lists.length - 1] : 0;
|
|
439
|
+
if (item) {
|
|
440
|
+
const gap = item[3].length;
|
|
441
|
+
column = item[1].length + item[2].length + (gap >= 1 && gap <= 4 ? gap : 1);
|
|
442
|
+
lists.push(column);
|
|
443
|
+
content = content.slice(Math.min(column, content.length));
|
|
444
|
+
} else {
|
|
445
|
+
content = content.slice(Math.min(indent, column));
|
|
446
|
+
}
|
|
447
|
+
const opener = fenceOpenerOf(content);
|
|
448
|
+
if (!opener) {
|
|
449
|
+
if (para === -1) para = starts[k];
|
|
450
|
+
if (out.pendingPara === -1) out.pendingPara = starts[k];
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
closePara(starts[k]);
|
|
454
|
+
out.pendingPara = -1;
|
|
455
|
+
const col = column;
|
|
456
|
+
const src = {
|
|
457
|
+
count: to,
|
|
458
|
+
line: (j) => {
|
|
459
|
+
const inner = stripQuotes(raw(j), depth);
|
|
460
|
+
if (inner === null) return null;
|
|
461
|
+
if (opener.char === "~" && col > 0) {
|
|
462
|
+
const ind = leadingSpaces(inner);
|
|
463
|
+
if (inner.trim() !== "" && ind < col) return null;
|
|
464
|
+
return inner.slice(Math.min(ind, col));
|
|
465
|
+
}
|
|
466
|
+
return inner;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
const end = closeFence(src, k, opener);
|
|
470
|
+
out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
|
|
471
|
+
if (!end.closed && end.line === starts.length - 1) out.endsInFence = true;
|
|
472
|
+
k = end.line;
|
|
473
|
+
}
|
|
474
|
+
closePara(to < starts.length ? starts[to] : text.length);
|
|
475
|
+
}
|
|
476
|
+
var BacktickRuns = class {
|
|
477
|
+
/** Every run, in order: [start, length]. */
|
|
478
|
+
all = [];
|
|
479
|
+
byLength = /* @__PURE__ */ new Map();
|
|
480
|
+
indexOf = /* @__PURE__ */ new Map();
|
|
481
|
+
constructor(text, from, to) {
|
|
482
|
+
for (let k = from; k < to; ) {
|
|
483
|
+
if (text.charCodeAt(k) !== 96) {
|
|
484
|
+
k++;
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
let len = 1;
|
|
488
|
+
while (k + len < to && text.charCodeAt(k + len) === 96) len++;
|
|
489
|
+
this.indexOf.set(k, this.all.length);
|
|
490
|
+
this.all.push([k, len]);
|
|
491
|
+
const list = this.byLength.get(len);
|
|
492
|
+
if (list) list.push(k);
|
|
493
|
+
else this.byLength.set(len, [k]);
|
|
494
|
+
k += len;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/** Start of the first run of exactly `n` backticks at or after `after`, or -1. */
|
|
498
|
+
next(n, after) {
|
|
499
|
+
const list = this.byLength.get(n);
|
|
500
|
+
if (!list) return -1;
|
|
501
|
+
let lo = 0;
|
|
502
|
+
let hi = list.length;
|
|
503
|
+
while (lo < hi) {
|
|
504
|
+
const mid = lo + hi >>> 1;
|
|
505
|
+
if (list[mid] < after) lo = mid + 1;
|
|
506
|
+
else hi = mid;
|
|
507
|
+
}
|
|
508
|
+
return list[lo] ?? -1;
|
|
509
|
+
}
|
|
510
|
+
/** Index of the run starting at `start` in `all`. */
|
|
511
|
+
index(start) {
|
|
512
|
+
return this.indexOf.get(start) ?? -1;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
function codeSpans(text, from, to, out) {
|
|
516
|
+
const runs = new BacktickRuns(text, from, to);
|
|
517
|
+
for (let r = 0; r < runs.all.length; ) {
|
|
518
|
+
const [start, n] = runs.all[r];
|
|
519
|
+
const close = runs.next(n, start + n);
|
|
520
|
+
if (close === -1) {
|
|
521
|
+
r++;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
out.push({ start, end: close + n, kind: "span" });
|
|
525
|
+
r = runs.index(close) + 1;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function findCodeRanges(text) {
|
|
529
|
+
const s = scan(text);
|
|
530
|
+
const ranges = [...s.ranges];
|
|
531
|
+
for (const [from, to] of s.paragraphs) codeSpans(text, from, to, ranges);
|
|
532
|
+
return ranges.sort((a, b) => a.start - b.start);
|
|
533
|
+
}
|
|
534
|
+
function pendingCodeSpanStart(text) {
|
|
535
|
+
const n = text.length;
|
|
536
|
+
if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
|
|
537
|
+
const s = scan(text);
|
|
538
|
+
const para = s.pendingPara;
|
|
539
|
+
if (s.endsInFence || para === -1) return -1;
|
|
540
|
+
const runs = [];
|
|
541
|
+
for (let k = para; k < n; ) {
|
|
542
|
+
if (text.charCodeAt(k) !== 96) {
|
|
543
|
+
k++;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
let len = 1;
|
|
547
|
+
while (k + len < n && text.charCodeAt(k + len) === 96) len++;
|
|
548
|
+
runs.push([k, len]);
|
|
549
|
+
k += len;
|
|
550
|
+
}
|
|
551
|
+
const growing = (r) => {
|
|
552
|
+
const [start, len] = runs[r];
|
|
553
|
+
return start + len === n;
|
|
554
|
+
};
|
|
555
|
+
for (let r = 0; r < runs.length; ) {
|
|
556
|
+
if (growing(r)) return runs[r][0];
|
|
557
|
+
const len = runs[r][1];
|
|
558
|
+
let j = r + 1;
|
|
559
|
+
while (j < runs.length && (runs[j][1] !== len || growing(j))) j++;
|
|
560
|
+
if (j >= runs.length) return runs[r][0];
|
|
561
|
+
r = j + 1;
|
|
562
|
+
}
|
|
563
|
+
return -1;
|
|
316
564
|
}
|
|
317
565
|
|
|
318
566
|
// source/math-pairs.ts
|
|
@@ -341,86 +589,7 @@ function looksLikeDisplayMath(inner, flow = false) {
|
|
|
341
589
|
return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT;
|
|
342
590
|
}
|
|
343
591
|
function codeRanges(text) {
|
|
344
|
-
|
|
345
|
-
const n = text.length;
|
|
346
|
-
let fence = null;
|
|
347
|
-
let para = -1;
|
|
348
|
-
const spans = (from, to) => {
|
|
349
|
-
const runs = [];
|
|
350
|
-
for (let k = from; k < to; ) {
|
|
351
|
-
if (text.charCodeAt(k) !== 96) {
|
|
352
|
-
k++;
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
let len = 1;
|
|
356
|
-
while (k + len < to && text.charCodeAt(k + len) === 96) len++;
|
|
357
|
-
runs.push([k, len]);
|
|
358
|
-
k += len;
|
|
359
|
-
}
|
|
360
|
-
const nextSame = new Array(runs.length).fill(-1);
|
|
361
|
-
const last = /* @__PURE__ */ new Map();
|
|
362
|
-
for (let r = runs.length - 1; r >= 0; r--) {
|
|
363
|
-
const len = runs[r][1];
|
|
364
|
-
nextSame[r] = last.get(len) ?? -1;
|
|
365
|
-
last.set(len, r);
|
|
366
|
-
}
|
|
367
|
-
for (let r = 0; r < runs.length; ) {
|
|
368
|
-
const j = nextSame[r];
|
|
369
|
-
if (j === -1) {
|
|
370
|
-
r++;
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
const [s0] = runs[r];
|
|
374
|
-
const [s1, l1] = runs[j];
|
|
375
|
-
ranges.push([s0, s1 + l1]);
|
|
376
|
-
r = j + 1;
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
let pos = 0;
|
|
380
|
-
while (pos <= n) {
|
|
381
|
-
const nl = text.indexOf("\n", pos);
|
|
382
|
-
const lineEnd = nl === -1 ? n : nl;
|
|
383
|
-
const line = text.slice(pos, lineEnd);
|
|
384
|
-
let indent = 0;
|
|
385
|
-
while (indent < line.length && line[indent] === " ") indent++;
|
|
386
|
-
const body = line.slice(indent);
|
|
387
|
-
if (fence) {
|
|
388
|
-
const trimmed = trimFenceLine(line);
|
|
389
|
-
let closes = false;
|
|
390
|
-
if (fence.char === "`") {
|
|
391
|
-
const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
|
|
392
|
-
if (kind === "open-nested") fence.depth++;
|
|
393
|
-
else if (kind === "close-nested") fence.depth--;
|
|
394
|
-
else if (kind === "close-outer") closes = indent <= 3;
|
|
395
|
-
} else {
|
|
396
|
-
let t = 0;
|
|
397
|
-
while (t < trimmed.length && trimmed[t] === "~") t++;
|
|
398
|
-
closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
|
|
399
|
-
}
|
|
400
|
-
if (closes) {
|
|
401
|
-
ranges.push([fence.start, lineEnd]);
|
|
402
|
-
fence = null;
|
|
403
|
-
}
|
|
404
|
-
} else {
|
|
405
|
-
const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
|
|
406
|
-
const info = opener ? opener[2] : "";
|
|
407
|
-
if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
|
|
408
|
-
if (para !== -1) spans(para, pos);
|
|
409
|
-
para = -1;
|
|
410
|
-
const char = opener[1][0];
|
|
411
|
-
const parsed = char === "`" ? parseFenceOpener(line) : null;
|
|
412
|
-
fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
|
|
413
|
-
} else if (body.trim() === "") {
|
|
414
|
-
if (para !== -1) spans(para, pos);
|
|
415
|
-
para = -1;
|
|
416
|
-
} else if (para === -1) para = pos;
|
|
417
|
-
}
|
|
418
|
-
if (nl === -1) break;
|
|
419
|
-
pos = nl + 1;
|
|
420
|
-
}
|
|
421
|
-
if (fence) ranges.push([fence.start, n]);
|
|
422
|
-
else if (para !== -1) spans(para, n);
|
|
423
|
-
return ranges.sort((a, b) => a[0] - b[0]);
|
|
592
|
+
return findCodeRanges(text).map((r) => [r.start, r.end]);
|
|
424
593
|
}
|
|
425
594
|
function pendingDisplayMathOpener(text) {
|
|
426
595
|
if (!text.includes("$$")) return -1;
|
|
@@ -504,6 +673,83 @@ function pairDisplayMath(text) {
|
|
|
504
673
|
return pairs;
|
|
505
674
|
}
|
|
506
675
|
|
|
676
|
+
// source/math.ts
|
|
677
|
+
var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
|
|
678
|
+
var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
|
|
679
|
+
var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
|
|
680
|
+
function isSingleDollarMath(content, after) {
|
|
681
|
+
if (!content || content.length > 400) return false;
|
|
682
|
+
if (/^\s/.test(content) || /\s$/.test(content)) return false;
|
|
683
|
+
if (after !== void 0 && /[0-9]/.test(after)) return false;
|
|
684
|
+
if (/(?:^|[^\\])%/.test(content)) return false;
|
|
685
|
+
if (/^[)\]}"/]/.test(content) || /[(\[{":;,/]$/.test(content)) return false;
|
|
686
|
+
if (/^[0-9]/.test(content) && after !== void 0 && /[A-Za-z]/.test(after)) return false;
|
|
687
|
+
if (isMathPlaceholder(content)) return false;
|
|
688
|
+
return TEX_SIGNAL.test(content) || !readsAsProse(content);
|
|
689
|
+
}
|
|
690
|
+
var FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
691
|
+
"sin",
|
|
692
|
+
"cos",
|
|
693
|
+
"tan",
|
|
694
|
+
"sec",
|
|
695
|
+
"csc",
|
|
696
|
+
"cot",
|
|
697
|
+
"log",
|
|
698
|
+
"ln",
|
|
699
|
+
"exp",
|
|
700
|
+
"lim",
|
|
701
|
+
"max",
|
|
702
|
+
"min",
|
|
703
|
+
"det",
|
|
704
|
+
"gcd",
|
|
705
|
+
"mod",
|
|
706
|
+
"arg",
|
|
707
|
+
"sinh",
|
|
708
|
+
"cosh",
|
|
709
|
+
"tanh"
|
|
710
|
+
]);
|
|
711
|
+
function readsAsProse(content) {
|
|
712
|
+
if (/[A-Za-z]'[a-z]/.test(content)) return true;
|
|
713
|
+
if (/(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return true;
|
|
714
|
+
if (!/\s/.test(content)) return false;
|
|
715
|
+
const words = (content.match(/[A-Za-z]{3,}/g) ?? []).filter((w) => !FUNCTION_NAMES.has(w.toLowerCase()));
|
|
716
|
+
return words.length >= 2;
|
|
717
|
+
}
|
|
718
|
+
function looksLikeOpenInlineMath(partial) {
|
|
719
|
+
if (!partial || partial.length > 400) return false;
|
|
720
|
+
if (/^\s/.test(partial) || /\s$/.test(partial)) return false;
|
|
721
|
+
if (LONE_VARIABLE.test(partial)) return true;
|
|
722
|
+
if (/^[0-9]/.test(partial)) return /\\[A-Za-z]+|[\\^_{}]/.test(partial);
|
|
723
|
+
return TEX_SIGNAL.test(partial) || isMathShape(partial);
|
|
724
|
+
}
|
|
725
|
+
function isMathShape(content) {
|
|
726
|
+
if (!MATH_SHAPE_CHARS.test(content)) return false;
|
|
727
|
+
const words = content.match(/[A-Za-z]+/g);
|
|
728
|
+
if (!words) return false;
|
|
729
|
+
if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
|
|
730
|
+
if (/[A-Za-z]'+[A-Za-z]/.test(content) || /(?:^|[\s(,])[A-Za-z]\.(?:[A-Za-z]\.|\s|$)/.test(content)) return false;
|
|
731
|
+
return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
|
|
732
|
+
}
|
|
733
|
+
function singleDollarMathEnd(text, open, limit = text.length) {
|
|
734
|
+
const bound = Math.min(limit, open + 403);
|
|
735
|
+
let close = -1;
|
|
736
|
+
for (let k = open + 1; k < bound; k += 1) {
|
|
737
|
+
const c = text[k];
|
|
738
|
+
if (c === "\n") break;
|
|
739
|
+
if (c === "\\") {
|
|
740
|
+
k += 1;
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if (c === "$") {
|
|
744
|
+
close = k;
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (close === -1 || text[close + 1] === "$") return -1;
|
|
749
|
+
const content = text.slice(open + 1, close);
|
|
750
|
+
return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
|
|
751
|
+
}
|
|
752
|
+
|
|
507
753
|
// source/xml-tag.ts
|
|
508
754
|
var XML_NAME_START = /[A-Za-z_]/;
|
|
509
755
|
var XML_NAME_CHARACTER = /[\w.:-]/;
|
|
@@ -796,27 +1042,6 @@ function isUnclosedGenericXmlOpening(source) {
|
|
|
796
1042
|
}
|
|
797
1043
|
return true;
|
|
798
1044
|
}
|
|
799
|
-
function jsonStringState(line, from, upTo, state) {
|
|
800
|
-
let { inString, escaped } = state;
|
|
801
|
-
for (let idx = from; idx < upTo; idx++) {
|
|
802
|
-
const ch = line[idx];
|
|
803
|
-
if (escaped) {
|
|
804
|
-
escaped = false;
|
|
805
|
-
continue;
|
|
806
|
-
}
|
|
807
|
-
if (ch === "\\" && inString) {
|
|
808
|
-
escaped = true;
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
if (ch === '"') inString = !inString;
|
|
812
|
-
}
|
|
813
|
-
return { inString, escaped };
|
|
814
|
-
}
|
|
815
|
-
function backtickRunLength(str, pos) {
|
|
816
|
-
let n = 0;
|
|
817
|
-
while (pos + n < str.length && str[pos + n] === "`") n++;
|
|
818
|
-
return n;
|
|
819
|
-
}
|
|
820
1045
|
function isSplitterTreeLine(line) {
|
|
821
1046
|
return TREE_GLYPHS.test(line) || /^[\s│|]*[├└+|][\s─\-]+/.test(line);
|
|
822
1047
|
}
|
|
@@ -832,6 +1057,14 @@ var Tokenizer = class {
|
|
|
832
1057
|
this.provisionalFrom = streaming ? pendingDisplayMathOpener(text) : -1;
|
|
833
1058
|
if (this.provisionalFrom === -1) this.provisionalFrom = Number.POSITIVE_INFINITY;
|
|
834
1059
|
if (streaming && text.includes("$$")) {
|
|
1060
|
+
const span = pendingCodeSpanStart(text);
|
|
1061
|
+
if (span !== -1) {
|
|
1062
|
+
const firstToken = text.indexOf("$$", span);
|
|
1063
|
+
if (firstToken !== -1) this.provisionalFrom = Math.min(this.provisionalFrom, firstToken);
|
|
1064
|
+
for (const [open, close] of this.mathPairs) {
|
|
1065
|
+
if (close >= span) this.provisionalFrom = Math.min(this.provisionalFrom, open);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
835
1068
|
for (const pad of ["`", "``", "```", "~~~"]) {
|
|
836
1069
|
const alt = pairDisplayMath(text + pad);
|
|
837
1070
|
for (const [open, close] of this.mathPairs) {
|
|
@@ -1040,11 +1273,9 @@ var Tokenizer = class {
|
|
|
1040
1273
|
const footnote = this.footnoteDefinition(p, li, t);
|
|
1041
1274
|
if (footnote) return footnote;
|
|
1042
1275
|
}
|
|
1043
|
-
|
|
1044
|
-
if (
|
|
1045
|
-
|
|
1046
|
-
if (tilde) return tilde;
|
|
1047
|
-
}
|
|
1276
|
+
const fence = fenceOpenerOf(seg);
|
|
1277
|
+
if (fence?.char === "`") return this.backtickFence(p, li, fence);
|
|
1278
|
+
if (fence?.char === "~") return this.tildeFence(p, li, fence);
|
|
1048
1279
|
if (t.startsWith("$$")) {
|
|
1049
1280
|
const math = this.mathBlock(p, tp);
|
|
1050
1281
|
if (math) return math;
|
|
@@ -1187,8 +1418,8 @@ var Tokenizer = class {
|
|
|
1187
1418
|
last = k;
|
|
1188
1419
|
continue;
|
|
1189
1420
|
}
|
|
1190
|
-
if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
|
|
1191
1421
|
if (this.isOpenTail(k)) return this.heldFootnote(p, open[1]);
|
|
1422
|
+
if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
|
|
1192
1423
|
last = k;
|
|
1193
1424
|
}
|
|
1194
1425
|
const end = this.contentEnds[last];
|
|
@@ -1199,74 +1430,31 @@ var Tokenizer = class {
|
|
|
1199
1430
|
heldFootnote(p, id) {
|
|
1200
1431
|
return this.island("footnote_def", p, this.n, false, { id });
|
|
1201
1432
|
}
|
|
1433
|
+
/** The text's lines as the one fence closer reads them (streaming: the open tail closes nothing). */
|
|
1434
|
+
get fenceLines() {
|
|
1435
|
+
return {
|
|
1436
|
+
count: this.lineCount,
|
|
1437
|
+
line: (k) => this.lineText(k),
|
|
1438
|
+
openTail: (k) => this.isOpenTail(k)
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1202
1441
|
/**
|
|
1203
|
-
* Backtick fence —
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
* still open at the end of the text.
|
|
1442
|
+
* Backtick fence — THE one closer (`closeFence`, source/code-ranges.ts): the
|
|
1443
|
+
* nested-fence rule for markdown fences with the strict-CommonMark retry, the
|
|
1444
|
+
* renderer's line-end closer, JSON-string awareness for ```json.
|
|
1207
1445
|
*/
|
|
1208
|
-
backtickFence(p, li,
|
|
1209
|
-
const
|
|
1210
|
-
const lang = parseFenceOpener(t)?.lang ?? "";
|
|
1446
|
+
backtickFence(p, li, opener) {
|
|
1447
|
+
const lang = opener.lang;
|
|
1211
1448
|
const isJson = lang === "json";
|
|
1212
1449
|
const normalized = normalizeFenceLanguage(lang);
|
|
1213
|
-
const meta = { fence: "`", ticks:
|
|
1450
|
+
const meta = { fence: "`", ticks: opener.ticks, lang };
|
|
1214
1451
|
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
if (typeof close === "number") {
|
|
1219
|
-
return this.fenceIsland(p, this.contentEnds[close], true, meta, li, close, isJson);
|
|
1452
|
+
const end = closeFence(this.fenceLines, li, opener);
|
|
1453
|
+
if (end.closed) {
|
|
1454
|
+
return this.fenceIsland(p, this.contentEnds[end.line], true, meta, li, end.line, isJson);
|
|
1220
1455
|
}
|
|
1221
1456
|
return this.fenceIsland(p, this.n, false, meta, li, this.lineCount, isJson);
|
|
1222
1457
|
}
|
|
1223
|
-
/** The closing line of a backtick fence opened on line `li`, or null when it never closes. */
|
|
1224
|
-
backtickFenceClose(li, openTicks, isJson, nesting) {
|
|
1225
|
-
let state = { inString: false, escaped: false };
|
|
1226
|
-
let nestedDepth = 0;
|
|
1227
|
-
let sawNested = false;
|
|
1228
|
-
for (let k = li + 1; k < this.lineCount; k++) {
|
|
1229
|
-
if (this.isOpenTail(k)) continue;
|
|
1230
|
-
const line = this.lineText(k);
|
|
1231
|
-
const trimmedLine = trimFenceLine(line);
|
|
1232
|
-
if (trimmedLine.startsWith("```")) {
|
|
1233
|
-
const at2 = line.indexOf("```");
|
|
1234
|
-
if (isJson) {
|
|
1235
|
-
const probe = jsonStringState(line, 0, at2, state);
|
|
1236
|
-
if (probe.inString) {
|
|
1237
|
-
state = jsonStringState(line, at2, line.length, probe);
|
|
1238
|
-
continue;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
const kind = classifyInnerFenceLine(trimmedLine, openTicks, nesting, nestedDepth);
|
|
1242
|
-
if (kind === "close-outer") return k;
|
|
1243
|
-
if (kind === "open-nested") {
|
|
1244
|
-
nestedDepth++;
|
|
1245
|
-
sawNested = true;
|
|
1246
|
-
}
|
|
1247
|
-
if (kind === "close-nested") nestedDepth--;
|
|
1248
|
-
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
1249
|
-
continue;
|
|
1250
|
-
}
|
|
1251
|
-
const at = line.indexOf("```");
|
|
1252
|
-
if (at !== -1) {
|
|
1253
|
-
if (isJson) {
|
|
1254
|
-
const probe = jsonStringState(line, 0, at, state);
|
|
1255
|
-
if (probe.inString) {
|
|
1256
|
-
state = jsonStringState(line, at, line.length, probe);
|
|
1257
|
-
continue;
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
const closeTicks = backtickRunLength(line, at);
|
|
1261
|
-
const after = trimFenceLine(line.slice(at + closeTicks));
|
|
1262
|
-
if (closeTicks >= openTicks && after === "" && nestedDepth === 0) return k;
|
|
1263
|
-
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
1264
|
-
continue;
|
|
1265
|
-
}
|
|
1266
|
-
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
1267
|
-
}
|
|
1268
|
-
return sawNested && nestedDepth > 0 ? "retry-strict" : null;
|
|
1269
|
-
}
|
|
1270
1458
|
fenceIsland(p, end, complete, meta, openLine, closeLine, isJson) {
|
|
1271
1459
|
if (isJson && openLine + 1 < this.lineCount) {
|
|
1272
1460
|
const bodyEnd = closeLine < this.lineCount ? this.lineStarts[closeLine] : this.n;
|
|
@@ -1275,22 +1463,16 @@ var Tokenizer = class {
|
|
|
1275
1463
|
}
|
|
1276
1464
|
return this.island("fence", p, end, complete, meta);
|
|
1277
1465
|
}
|
|
1278
|
-
tildeFence(p, li,
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
const ticks = open[1].length;
|
|
1282
|
-
const lang = open[2].trim().split(/\s/)[0] ?? "";
|
|
1283
|
-
const meta = { fence: "~", ticks, lang };
|
|
1466
|
+
tildeFence(p, li, opener) {
|
|
1467
|
+
const lang = opener.lang;
|
|
1468
|
+
const meta = { fence: "~", ticks: opener.ticks, lang };
|
|
1284
1469
|
const normalized = normalizeFenceLanguage(lang);
|
|
1285
1470
|
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
1286
|
-
const close =
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const end = this.rendererOverflow(li + 1, k, tildeEnd);
|
|
1292
|
-
return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
|
|
1293
|
-
}
|
|
1471
|
+
const close = closeFence(this.fenceLines, li, opener);
|
|
1472
|
+
if (close.closed) {
|
|
1473
|
+
const tildeEnd = this.contentEnds[close.line];
|
|
1474
|
+
const end = this.rendererOverflow(li + 1, close.line, tildeEnd);
|
|
1475
|
+
return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
|
|
1294
1476
|
}
|
|
1295
1477
|
return this.island("fence", p, this.n, false, meta);
|
|
1296
1478
|
}
|
|
@@ -1310,7 +1492,7 @@ var Tokenizer = class {
|
|
|
1310
1492
|
for (let j = firstLine; j <= closeLine && j < this.lineCount; j++) {
|
|
1311
1493
|
const start = this.lineStarts[j];
|
|
1312
1494
|
if (this.lineIsBlank(j)) continue;
|
|
1313
|
-
if (this.lineText(j)
|
|
1495
|
+
if (fenceOpenerOf(this.lineText(j))?.char === "~") continue;
|
|
1314
1496
|
const inner = this.detectBlockUncached(start, j, false);
|
|
1315
1497
|
if (!inner) continue;
|
|
1316
1498
|
if (inner.end > end) {
|
|
@@ -1558,9 +1740,9 @@ var Tokenizer = class {
|
|
|
1558
1740
|
const tree = { ...breakIsland, start: treeStart };
|
|
1559
1741
|
if (treeStart > pos) {
|
|
1560
1742
|
const proseEnd = this.contentEnds[root - 1];
|
|
1561
|
-
const
|
|
1562
|
-
if (!
|
|
1563
|
-
this.push({ kind: "prose", start: pos, end: proseEnd, island: null, inlines:
|
|
1743
|
+
const scan3 = this.scanInline(pos, proseEnd);
|
|
1744
|
+
if (!scan3.breakIsland) {
|
|
1745
|
+
this.push({ kind: "prose", start: pos, end: proseEnd, island: null, inlines: scan3.inlines });
|
|
1564
1746
|
this.push({ kind: "gap", start: proseEnd, end: treeStart, island: null, inlines: [] });
|
|
1565
1747
|
this.push({ kind: "island", start: treeStart, end: tree.end, island: tree, inlines: [] });
|
|
1566
1748
|
return tree.end;
|
|
@@ -1572,13 +1754,13 @@ var Tokenizer = class {
|
|
|
1572
1754
|
}
|
|
1573
1755
|
}
|
|
1574
1756
|
}
|
|
1575
|
-
const
|
|
1576
|
-
if (!
|
|
1577
|
-
this.push({ kind: "prose", start: pos, end, island: null, inlines:
|
|
1757
|
+
const scan2 = this.scanInline(pos, end);
|
|
1758
|
+
if (!scan2.breakIsland) {
|
|
1759
|
+
this.push({ kind: "prose", start: pos, end, island: null, inlines: scan2.inlines });
|
|
1578
1760
|
return end;
|
|
1579
1761
|
}
|
|
1580
|
-
const br =
|
|
1581
|
-
if (
|
|
1762
|
+
const br = scan2.breakIsland;
|
|
1763
|
+
if (scan2.orphan) {
|
|
1582
1764
|
let start = pos;
|
|
1583
1765
|
while (this.blocks.length) {
|
|
1584
1766
|
const last = this.blocks[this.blocks.length - 1];
|
|
@@ -1595,7 +1777,7 @@ var Tokenizer = class {
|
|
|
1595
1777
|
start: pos,
|
|
1596
1778
|
end: br.start,
|
|
1597
1779
|
island: null,
|
|
1598
|
-
inlines:
|
|
1780
|
+
inlines: scan2.inlines.filter((inline) => inline.end <= br.start)
|
|
1599
1781
|
});
|
|
1600
1782
|
this.push({ kind: "island", start: br.start, end: br.end, island: br, inlines: [] });
|
|
1601
1783
|
return br.end;
|
|
@@ -1615,30 +1797,10 @@ var Tokenizer = class {
|
|
|
1615
1797
|
const codeSpanClose = (n, from) => {
|
|
1616
1798
|
if (!runs) {
|
|
1617
1799
|
this.tick(pe - ps);
|
|
1618
|
-
runs =
|
|
1619
|
-
for (let k = ps; k < pe; ) {
|
|
1620
|
-
if (text.charCodeAt(k) !== 96) {
|
|
1621
|
-
k++;
|
|
1622
|
-
continue;
|
|
1623
|
-
}
|
|
1624
|
-
const len = backtickRunLength(text, k);
|
|
1625
|
-
const list2 = runs.get(len);
|
|
1626
|
-
if (list2) list2.push(k);
|
|
1627
|
-
else runs.set(len, [k]);
|
|
1628
|
-
k += len;
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
const list = runs.get(n);
|
|
1632
|
-
if (!list) return -1;
|
|
1633
|
-
let lo = 0;
|
|
1634
|
-
let hi = list.length;
|
|
1635
|
-
while (lo < hi) {
|
|
1636
|
-
const mid = lo + hi >>> 1;
|
|
1637
|
-
if (list[mid] < from) lo = mid + 1;
|
|
1638
|
-
else hi = mid;
|
|
1800
|
+
runs = new BacktickRuns(text, ps, pe);
|
|
1639
1801
|
}
|
|
1640
|
-
const at =
|
|
1641
|
-
return at !==
|
|
1802
|
+
const at = runs.next(n, from);
|
|
1803
|
+
return at !== -1 && at + n <= pe ? at : -1;
|
|
1642
1804
|
};
|
|
1643
1805
|
let i = ps;
|
|
1644
1806
|
const scanEnd = Math.min(pe, this.provisionalFrom);
|
|
@@ -2187,12 +2349,14 @@ exports.blockRangeEdit = blockRangeEdit;
|
|
|
2187
2349
|
exports.buildCodePointIndex = buildCodePointIndex;
|
|
2188
2350
|
exports.classifyInnerFenceLine = classifyInnerFenceLine;
|
|
2189
2351
|
exports.fenceNestsInnerFences = fenceNestsInnerFences;
|
|
2352
|
+
exports.findCodeRanges = findCodeRanges;
|
|
2190
2353
|
exports.isMathPlaceholder = isMathPlaceholder;
|
|
2191
2354
|
exports.isSingleDollarMath = isSingleDollarMath;
|
|
2192
2355
|
exports.islandEdit = islandEdit;
|
|
2193
2356
|
exports.joinSource = joinSource;
|
|
2194
2357
|
exports.listIslands = listIslands;
|
|
2195
2358
|
exports.looksLikeDisplayMath = looksLikeDisplayMath;
|
|
2359
|
+
exports.looksLikeOpenInlineMath = looksLikeOpenInlineMath;
|
|
2196
2360
|
exports.mapPosition = mapPosition;
|
|
2197
2361
|
exports.mapRange = mapRange;
|
|
2198
2362
|
exports.pairDisplayMath = pairDisplayMath;
|