@ai-matrx/content-ir 0.17.0 → 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 +40 -1
- package/dist/index.cjs +377 -295
- 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 +377 -296
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +377 -295
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +44 -27
- package/dist/source.d.ts +44 -27
- package/dist/source.js +377 -296
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
package/dist/source.cjs
CHANGED
|
@@ -254,75 +254,231 @@ 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
|
-
const content = text.slice(open + 1, close);
|
|
315
|
-
return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
|
|
352
|
+
return { inString, escaped };
|
|
316
353
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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) {
|
|
323
415
|
let para = -1;
|
|
324
|
-
|
|
325
|
-
|
|
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) {
|
|
326
482
|
for (let k = from; k < to; ) {
|
|
327
483
|
if (text.charCodeAt(k) !== 96) {
|
|
328
484
|
k++;
|
|
@@ -330,117 +486,57 @@ function findCodeRanges(text) {
|
|
|
330
486
|
}
|
|
331
487
|
let len = 1;
|
|
332
488
|
while (k + len < to && text.charCodeAt(k + len) === 96) len++;
|
|
333
|
-
|
|
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]);
|
|
334
494
|
k += len;
|
|
335
495
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
const [s0] = runs[r];
|
|
350
|
-
const [s1, l1] = runs[j];
|
|
351
|
-
ranges.push({ start: s0, end: s1 + l1, kind: "span" });
|
|
352
|
-
r = j + 1;
|
|
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;
|
|
353
507
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
else if (kind === "close-nested") fence.depth--;
|
|
370
|
-
else if (kind === "close-outer") closes = indent <= 3;
|
|
371
|
-
} else {
|
|
372
|
-
let t = 0;
|
|
373
|
-
while (t < trimmed.length && trimmed[t] === "~") t++;
|
|
374
|
-
closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
|
|
375
|
-
}
|
|
376
|
-
if (closes) {
|
|
377
|
-
ranges.push({ start: fence.start, end: lineEnd, kind: "fence" });
|
|
378
|
-
fence = null;
|
|
379
|
-
}
|
|
380
|
-
} else {
|
|
381
|
-
const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
|
|
382
|
-
const info = opener ? opener[2] : "";
|
|
383
|
-
if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
|
|
384
|
-
if (para !== -1) spans(para, pos);
|
|
385
|
-
para = -1;
|
|
386
|
-
const char = opener[1][0];
|
|
387
|
-
const parsed = char === "`" ? parseFenceOpener(line) : null;
|
|
388
|
-
fence = { char, len: opener[1].length, start: pos, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
|
|
389
|
-
} else if (body.trim() === "") {
|
|
390
|
-
if (para !== -1) spans(para, pos);
|
|
391
|
-
para = -1;
|
|
392
|
-
} else if (para === -1) para = pos;
|
|
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;
|
|
393
523
|
}
|
|
394
|
-
|
|
395
|
-
|
|
524
|
+
out.push({ start, end: close + n, kind: "span" });
|
|
525
|
+
r = runs.index(close) + 1;
|
|
396
526
|
}
|
|
397
|
-
|
|
398
|
-
|
|
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);
|
|
399
532
|
return ranges.sort((a, b) => a.start - b.start);
|
|
400
533
|
}
|
|
401
534
|
function pendingCodeSpanStart(text) {
|
|
402
535
|
const n = text.length;
|
|
403
536
|
if (n === 0 || /\n[ \t]*\n[ \t]*$/.test(text)) return -1;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
while (pos <= n) {
|
|
408
|
-
const nl = text.indexOf("\n", pos);
|
|
409
|
-
const lineEnd = nl === -1 ? n : nl;
|
|
410
|
-
const line = text.slice(pos, lineEnd);
|
|
411
|
-
let indent = 0;
|
|
412
|
-
while (indent < line.length && line[indent] === " ") indent++;
|
|
413
|
-
const body = line.slice(indent);
|
|
414
|
-
if (fence) {
|
|
415
|
-
const trimmed = trimFenceLine(line);
|
|
416
|
-
let closes = false;
|
|
417
|
-
if (fence.char === "`") {
|
|
418
|
-
const kind = classifyInnerFenceLine(trimmed, fence.len, fence.nests, fence.depth);
|
|
419
|
-
if (kind === "open-nested") fence.depth++;
|
|
420
|
-
else if (kind === "close-nested") fence.depth--;
|
|
421
|
-
else if (kind === "close-outer") closes = indent <= 3;
|
|
422
|
-
} else {
|
|
423
|
-
let t = 0;
|
|
424
|
-
while (t < trimmed.length && trimmed[t] === "~") t++;
|
|
425
|
-
closes = indent <= 3 && t >= fence.len && trimFenceLine(trimmed.slice(t)) === "";
|
|
426
|
-
}
|
|
427
|
-
if (closes) fence = null;
|
|
428
|
-
} else {
|
|
429
|
-
const opener = indent <= 3 ? /^(`{3,}|~{3,})(.*)$/.exec(body) : null;
|
|
430
|
-
const info = opener ? opener[2] : "";
|
|
431
|
-
if (opener && !(opener[1][0] === "`" && info.includes("`"))) {
|
|
432
|
-
para = -1;
|
|
433
|
-
const char = opener[1][0];
|
|
434
|
-
const parsed = char === "`" ? parseFenceOpener(line) : null;
|
|
435
|
-
fence = { char, len: opener[1].length, nests: fenceNestsInnerFences(parsed?.lang), depth: 0 };
|
|
436
|
-
} else if (body.trim() === "") {
|
|
437
|
-
if (!(nl === -1 && line === "")) para = -1;
|
|
438
|
-
} else if (para === -1) para = pos;
|
|
439
|
-
}
|
|
440
|
-
if (nl === -1) break;
|
|
441
|
-
pos = nl + 1;
|
|
442
|
-
}
|
|
443
|
-
if (fence || para === -1) return -1;
|
|
537
|
+
const s = scan(text);
|
|
538
|
+
const para = s.pendingPara;
|
|
539
|
+
if (s.endsInFence || para === -1) return -1;
|
|
444
540
|
const runs = [];
|
|
445
541
|
for (let k = para; k < n; ) {
|
|
446
542
|
if (text.charCodeAt(k) !== 96) {
|
|
@@ -577,6 +673,83 @@ function pairDisplayMath(text) {
|
|
|
577
673
|
return pairs;
|
|
578
674
|
}
|
|
579
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
|
+
|
|
580
753
|
// source/xml-tag.ts
|
|
581
754
|
var XML_NAME_START = /[A-Za-z_]/;
|
|
582
755
|
var XML_NAME_CHARACTER = /[\w.:-]/;
|
|
@@ -869,27 +1042,6 @@ function isUnclosedGenericXmlOpening(source) {
|
|
|
869
1042
|
}
|
|
870
1043
|
return true;
|
|
871
1044
|
}
|
|
872
|
-
function jsonStringState(line, from, upTo, state) {
|
|
873
|
-
let { inString, escaped } = state;
|
|
874
|
-
for (let idx = from; idx < upTo; idx++) {
|
|
875
|
-
const ch = line[idx];
|
|
876
|
-
if (escaped) {
|
|
877
|
-
escaped = false;
|
|
878
|
-
continue;
|
|
879
|
-
}
|
|
880
|
-
if (ch === "\\" && inString) {
|
|
881
|
-
escaped = true;
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
if (ch === '"') inString = !inString;
|
|
885
|
-
}
|
|
886
|
-
return { inString, escaped };
|
|
887
|
-
}
|
|
888
|
-
function backtickRunLength(str, pos) {
|
|
889
|
-
let n = 0;
|
|
890
|
-
while (pos + n < str.length && str[pos + n] === "`") n++;
|
|
891
|
-
return n;
|
|
892
|
-
}
|
|
893
1045
|
function isSplitterTreeLine(line) {
|
|
894
1046
|
return TREE_GLYPHS.test(line) || /^[\s│|]*[├└+|][\s─\-]+/.test(line);
|
|
895
1047
|
}
|
|
@@ -1121,11 +1273,9 @@ var Tokenizer = class {
|
|
|
1121
1273
|
const footnote = this.footnoteDefinition(p, li, t);
|
|
1122
1274
|
if (footnote) return footnote;
|
|
1123
1275
|
}
|
|
1124
|
-
|
|
1125
|
-
if (
|
|
1126
|
-
|
|
1127
|
-
if (tilde) return tilde;
|
|
1128
|
-
}
|
|
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);
|
|
1129
1279
|
if (t.startsWith("$$")) {
|
|
1130
1280
|
const math = this.mathBlock(p, tp);
|
|
1131
1281
|
if (math) return math;
|
|
@@ -1280,74 +1430,31 @@ var Tokenizer = class {
|
|
|
1280
1430
|
heldFootnote(p, id) {
|
|
1281
1431
|
return this.island("footnote_def", p, this.n, false, { id });
|
|
1282
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
|
+
}
|
|
1283
1441
|
/**
|
|
1284
|
-
* Backtick fence —
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
* 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.
|
|
1288
1445
|
*/
|
|
1289
|
-
backtickFence(p, li,
|
|
1290
|
-
const
|
|
1291
|
-
const lang = parseFenceOpener(t)?.lang ?? "";
|
|
1446
|
+
backtickFence(p, li, opener) {
|
|
1447
|
+
const lang = opener.lang;
|
|
1292
1448
|
const isJson = lang === "json";
|
|
1293
1449
|
const normalized = normalizeFenceLanguage(lang);
|
|
1294
|
-
const meta = { fence: "`", ticks:
|
|
1450
|
+
const meta = { fence: "`", ticks: opener.ticks, lang };
|
|
1295
1451
|
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
if (typeof close === "number") {
|
|
1300
|
-
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);
|
|
1301
1455
|
}
|
|
1302
1456
|
return this.fenceIsland(p, this.n, false, meta, li, this.lineCount, isJson);
|
|
1303
1457
|
}
|
|
1304
|
-
/** The closing line of a backtick fence opened on line `li`, or null when it never closes. */
|
|
1305
|
-
backtickFenceClose(li, openTicks, isJson, nesting) {
|
|
1306
|
-
let state = { inString: false, escaped: false };
|
|
1307
|
-
let nestedDepth = 0;
|
|
1308
|
-
let sawNested = false;
|
|
1309
|
-
for (let k = li + 1; k < this.lineCount; k++) {
|
|
1310
|
-
if (this.isOpenTail(k)) continue;
|
|
1311
|
-
const line = this.lineText(k);
|
|
1312
|
-
const trimmedLine = trimFenceLine(line);
|
|
1313
|
-
if (trimmedLine.startsWith("```")) {
|
|
1314
|
-
const at2 = line.indexOf("```");
|
|
1315
|
-
if (isJson) {
|
|
1316
|
-
const probe = jsonStringState(line, 0, at2, state);
|
|
1317
|
-
if (probe.inString) {
|
|
1318
|
-
state = jsonStringState(line, at2, line.length, probe);
|
|
1319
|
-
continue;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
const kind = classifyInnerFenceLine(trimmedLine, openTicks, nesting, nestedDepth);
|
|
1323
|
-
if (kind === "close-outer") return k;
|
|
1324
|
-
if (kind === "open-nested") {
|
|
1325
|
-
nestedDepth++;
|
|
1326
|
-
sawNested = true;
|
|
1327
|
-
}
|
|
1328
|
-
if (kind === "close-nested") nestedDepth--;
|
|
1329
|
-
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
1330
|
-
continue;
|
|
1331
|
-
}
|
|
1332
|
-
const at = line.indexOf("```");
|
|
1333
|
-
if (at !== -1) {
|
|
1334
|
-
if (isJson) {
|
|
1335
|
-
const probe = jsonStringState(line, 0, at, state);
|
|
1336
|
-
if (probe.inString) {
|
|
1337
|
-
state = jsonStringState(line, at, line.length, probe);
|
|
1338
|
-
continue;
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
const closeTicks = backtickRunLength(line, at);
|
|
1342
|
-
const after = trimFenceLine(line.slice(at + closeTicks));
|
|
1343
|
-
if (closeTicks >= openTicks && after === "" && nestedDepth === 0) return k;
|
|
1344
|
-
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
1345
|
-
continue;
|
|
1346
|
-
}
|
|
1347
|
-
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
1348
|
-
}
|
|
1349
|
-
return sawNested && nestedDepth > 0 ? "retry-strict" : null;
|
|
1350
|
-
}
|
|
1351
1458
|
fenceIsland(p, end, complete, meta, openLine, closeLine, isJson) {
|
|
1352
1459
|
if (isJson && openLine + 1 < this.lineCount) {
|
|
1353
1460
|
const bodyEnd = closeLine < this.lineCount ? this.lineStarts[closeLine] : this.n;
|
|
@@ -1356,22 +1463,16 @@ var Tokenizer = class {
|
|
|
1356
1463
|
}
|
|
1357
1464
|
return this.island("fence", p, end, complete, meta);
|
|
1358
1465
|
}
|
|
1359
|
-
tildeFence(p, li,
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
const ticks = open[1].length;
|
|
1363
|
-
const lang = open[2].trim().split(/\s/)[0] ?? "";
|
|
1364
|
-
const meta = { fence: "~", ticks, lang };
|
|
1466
|
+
tildeFence(p, li, opener) {
|
|
1467
|
+
const lang = opener.lang;
|
|
1468
|
+
const meta = { fence: "~", ticks: opener.ticks, lang };
|
|
1365
1469
|
const normalized = normalizeFenceLanguage(lang);
|
|
1366
1470
|
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
1367
|
-
const close =
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
const end = this.rendererOverflow(li + 1, k, tildeEnd);
|
|
1373
|
-
return this.island("fence", p, end, end === this.n ? this.overflowComplete : true, meta);
|
|
1374
|
-
}
|
|
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);
|
|
1375
1476
|
}
|
|
1376
1477
|
return this.island("fence", p, this.n, false, meta);
|
|
1377
1478
|
}
|
|
@@ -1391,7 +1492,7 @@ var Tokenizer = class {
|
|
|
1391
1492
|
for (let j = firstLine; j <= closeLine && j < this.lineCount; j++) {
|
|
1392
1493
|
const start = this.lineStarts[j];
|
|
1393
1494
|
if (this.lineIsBlank(j)) continue;
|
|
1394
|
-
if (this.lineText(j)
|
|
1495
|
+
if (fenceOpenerOf(this.lineText(j))?.char === "~") continue;
|
|
1395
1496
|
const inner = this.detectBlockUncached(start, j, false);
|
|
1396
1497
|
if (!inner) continue;
|
|
1397
1498
|
if (inner.end > end) {
|
|
@@ -1639,9 +1740,9 @@ var Tokenizer = class {
|
|
|
1639
1740
|
const tree = { ...breakIsland, start: treeStart };
|
|
1640
1741
|
if (treeStart > pos) {
|
|
1641
1742
|
const proseEnd = this.contentEnds[root - 1];
|
|
1642
|
-
const
|
|
1643
|
-
if (!
|
|
1644
|
-
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 });
|
|
1645
1746
|
this.push({ kind: "gap", start: proseEnd, end: treeStart, island: null, inlines: [] });
|
|
1646
1747
|
this.push({ kind: "island", start: treeStart, end: tree.end, island: tree, inlines: [] });
|
|
1647
1748
|
return tree.end;
|
|
@@ -1653,13 +1754,13 @@ var Tokenizer = class {
|
|
|
1653
1754
|
}
|
|
1654
1755
|
}
|
|
1655
1756
|
}
|
|
1656
|
-
const
|
|
1657
|
-
if (!
|
|
1658
|
-
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 });
|
|
1659
1760
|
return end;
|
|
1660
1761
|
}
|
|
1661
|
-
const br =
|
|
1662
|
-
if (
|
|
1762
|
+
const br = scan2.breakIsland;
|
|
1763
|
+
if (scan2.orphan) {
|
|
1663
1764
|
let start = pos;
|
|
1664
1765
|
while (this.blocks.length) {
|
|
1665
1766
|
const last = this.blocks[this.blocks.length - 1];
|
|
@@ -1676,7 +1777,7 @@ var Tokenizer = class {
|
|
|
1676
1777
|
start: pos,
|
|
1677
1778
|
end: br.start,
|
|
1678
1779
|
island: null,
|
|
1679
|
-
inlines:
|
|
1780
|
+
inlines: scan2.inlines.filter((inline) => inline.end <= br.start)
|
|
1680
1781
|
});
|
|
1681
1782
|
this.push({ kind: "island", start: br.start, end: br.end, island: br, inlines: [] });
|
|
1682
1783
|
return br.end;
|
|
@@ -1696,30 +1797,10 @@ var Tokenizer = class {
|
|
|
1696
1797
|
const codeSpanClose = (n, from) => {
|
|
1697
1798
|
if (!runs) {
|
|
1698
1799
|
this.tick(pe - ps);
|
|
1699
|
-
runs =
|
|
1700
|
-
for (let k = ps; k < pe; ) {
|
|
1701
|
-
if (text.charCodeAt(k) !== 96) {
|
|
1702
|
-
k++;
|
|
1703
|
-
continue;
|
|
1704
|
-
}
|
|
1705
|
-
const len = backtickRunLength(text, k);
|
|
1706
|
-
const list2 = runs.get(len);
|
|
1707
|
-
if (list2) list2.push(k);
|
|
1708
|
-
else runs.set(len, [k]);
|
|
1709
|
-
k += len;
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
const list = runs.get(n);
|
|
1713
|
-
if (!list) return -1;
|
|
1714
|
-
let lo = 0;
|
|
1715
|
-
let hi = list.length;
|
|
1716
|
-
while (lo < hi) {
|
|
1717
|
-
const mid = lo + hi >>> 1;
|
|
1718
|
-
if (list[mid] < from) lo = mid + 1;
|
|
1719
|
-
else hi = mid;
|
|
1800
|
+
runs = new BacktickRuns(text, ps, pe);
|
|
1720
1801
|
}
|
|
1721
|
-
const at =
|
|
1722
|
-
return at !==
|
|
1802
|
+
const at = runs.next(n, from);
|
|
1803
|
+
return at !== -1 && at + n <= pe ? at : -1;
|
|
1723
1804
|
};
|
|
1724
1805
|
let i = ps;
|
|
1725
1806
|
const scanEnd = Math.min(pe, this.provisionalFrom);
|
|
@@ -2275,6 +2356,7 @@ exports.islandEdit = islandEdit;
|
|
|
2275
2356
|
exports.joinSource = joinSource;
|
|
2276
2357
|
exports.listIslands = listIslands;
|
|
2277
2358
|
exports.looksLikeDisplayMath = looksLikeDisplayMath;
|
|
2359
|
+
exports.looksLikeOpenInlineMath = looksLikeOpenInlineMath;
|
|
2278
2360
|
exports.mapPosition = mapPosition;
|
|
2279
2361
|
exports.mapRange = mapRange;
|
|
2280
2362
|
exports.pairDisplayMath = pairDisplayMath;
|