parseman 0.41.0 → 0.42.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.
Files changed (46) hide show
  1. package/dist/combinators/expect.d.ts.map +1 -1
  2. package/dist/combinators/grammar.d.ts.map +1 -1
  3. package/dist/combinators/literal.d.ts.map +1 -1
  4. package/dist/combinators/node.d.ts.map +1 -1
  5. package/dist/combinators/regex.d.ts.map +1 -1
  6. package/dist/combinators/scanTo.d.ts.map +1 -1
  7. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  8. package/dist/compiler/codegen.d.ts +3 -0
  9. package/dist/compiler/codegen.d.ts.map +1 -1
  10. package/dist/compiler/inline-build.d.ts +1 -1
  11. package/dist/compiler/inline-build.d.ts.map +1 -1
  12. package/dist/compiler/line-index.d.ts +1 -15
  13. package/dist/compiler/line-index.d.ts.map +1 -1
  14. package/dist/cst/capture-buffer.d.ts.map +1 -1
  15. package/dist/index.cjs +364 -99
  16. package/dist/index.cjs.map +4 -4
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +364 -99
  20. package/dist/index.js.map +4 -4
  21. package/dist/language-service/index.cjs +48 -2
  22. package/dist/language-service/index.cjs.map +3 -3
  23. package/dist/language-service/index.js +48 -2
  24. package/dist/language-service/index.js.map +3 -3
  25. package/dist/line-index.d.ts +41 -0
  26. package/dist/line-index.d.ts.map +1 -0
  27. package/dist/plugin/index.cjs +334 -93
  28. package/dist/plugin/index.cjs.map +4 -4
  29. package/dist/plugin/index.js +334 -93
  30. package/dist/plugin/index.js.map +4 -4
  31. package/dist/recovery/scan.d.ts.map +1 -1
  32. package/dist/regex/first-set.d.ts.map +1 -1
  33. package/dist/run/index.cjs +47 -1
  34. package/dist/run/index.cjs.map +3 -3
  35. package/dist/run/index.d.ts +3 -2
  36. package/dist/run/index.d.ts.map +1 -1
  37. package/dist/run/index.js +47 -1
  38. package/dist/run/index.js.map +3 -3
  39. package/dist/spec/index.cjs +170 -50
  40. package/dist/spec/index.cjs.map +4 -4
  41. package/dist/spec/index.js +170 -50
  42. package/dist/spec/index.js.map +4 -4
  43. package/dist/types.d.ts +12 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/version.d.ts +1 -1
  46. package/package.json +1 -1
@@ -443,6 +443,65 @@ function rules(a, b) {
443
443
  return cache;
444
444
  }
445
445
 
446
+ // src/line-index.ts
447
+ function createLineIndex() {
448
+ return { lineStarts: [0] };
449
+ }
450
+ function recordLineRange(index, input, start, end) {
451
+ const lineStarts = index.lineStarts;
452
+ for (let i = start; i < end; i++) {
453
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
454
+ }
455
+ }
456
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
457
+ if (!ctx.trackLines) return;
458
+ const from = ctx._lineScannedTo ?? 0;
459
+ if (end <= from) return;
460
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
461
+ if (!index) return;
462
+ recordLineRange(index, input, from, end);
463
+ ctx._lineScannedTo = end;
464
+ }
465
+ function normalizeLineIndex(index) {
466
+ const starts = index.lineStarts;
467
+ starts.sort((a, b) => a - b);
468
+ let write = 0;
469
+ for (let read = 0; read < starts.length; read++) {
470
+ const value = starts[read];
471
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
472
+ }
473
+ starts.length = write;
474
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
475
+ return index;
476
+ }
477
+ function offsetToLineCol(index, offset) {
478
+ const { lineStarts } = index;
479
+ let lo = 0;
480
+ let hi = lineStarts.length - 1;
481
+ while (lo < hi) {
482
+ const mid = lo + hi + 1 >> 1;
483
+ if (lineStarts[mid] <= offset) lo = mid;
484
+ else hi = mid - 1;
485
+ }
486
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
487
+ }
488
+ function annotateSpan(span, index) {
489
+ const s = offsetToLineCol(index, span.start);
490
+ const e = offsetToLineCol(index, span.end);
491
+ return {
492
+ ...span,
493
+ startLine: s.line,
494
+ startColumn: s.col,
495
+ endLine: e.line,
496
+ endColumn: e.col
497
+ };
498
+ }
499
+ function annotateSpanFromLineContext(span, ctx) {
500
+ if (!ctx.trackLines) return span;
501
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
502
+ return index ? annotateSpan(span, index) : span;
503
+ }
504
+
446
505
  // src/cst/capture-buffer.ts
447
506
  var EMPTY = [];
448
507
  var EMPTY_TL = [];
@@ -484,7 +543,12 @@ function endCstNodeCapture(ctx, saved) {
484
543
  return materialized;
485
544
  }
486
545
  function pushCstLeaf(ctx, leaf2) {
487
- pushCstChild(ctx, leaf2, leaf2);
546
+ if (!ctx.trackLines) {
547
+ pushCstChild(ctx, leaf2, leaf2);
548
+ return;
549
+ }
550
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
551
+ pushCstChild(ctx, annotated, annotated);
488
552
  }
489
553
  function pushCstChild(ctx, built, rawEntry) {
490
554
  const b = ctx._cstBuf;
@@ -674,7 +738,9 @@ function firstSetSentinel(fs) {
674
738
  function recoverScan(input, from, ctx, sync, expected) {
675
739
  let scanPos = from;
676
740
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
677
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
741
+ const span = { start: from, end: scanPos };
742
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
743
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
678
744
  ctx._errors?.push(error);
679
745
  return { error, end: scanPos };
680
746
  }
@@ -776,7 +842,8 @@ function expect(combinator, label2) {
776
842
  parse(input, pos, ctx) {
777
843
  const result = combinator.parse(input, pos, ctx);
778
844
  if (result.ok) return result;
779
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
845
+ const span = { start: pos, end: pos };
846
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
780
847
  ctx._errors?.push(error);
781
848
  if (ctx._tolerant) captureError(ctx, error);
782
849
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1116,6 +1183,7 @@ function literal(value, opts = {}) {
1116
1183
  canMatchNewline: value.includes("\n"),
1117
1184
  isTrivia: false
1118
1185
  };
1186
+ const canMatchNewline = meta.canMatchNewline;
1119
1187
  if (caseInsensitive) {
1120
1188
  const upper = value.toUpperCase();
1121
1189
  const lower = value.toLowerCase();
@@ -1132,6 +1200,7 @@ function literal(value, opts = {}) {
1132
1200
  return function parse2(input, pos, ctx) {
1133
1201
  if (input.charCodeAt(pos) === code) {
1134
1202
  const span = { start: pos, end: pos + 1 };
1203
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1135
1204
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1136
1205
  return { ok: true, value, span };
1137
1206
  }
@@ -1145,6 +1214,7 @@ function literal(value, opts = {}) {
1145
1214
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1146
1215
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1147
1216
  const span = { start: pos, end };
1217
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1148
1218
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1149
1219
  return { ok: true, value: matchedValue, span };
1150
1220
  }
@@ -1309,6 +1379,7 @@ function regex(pattern, flags = "") {
1309
1379
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1310
1380
  const value = input.slice(pos, scanEnd);
1311
1381
  const span2 = { start: pos, end: scanEnd };
1382
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1312
1383
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1313
1384
  return { ok: true, value, span: span2 };
1314
1385
  }
@@ -1318,6 +1389,7 @@ function regex(pattern, flags = "") {
1318
1389
  return failAt(ctx, [`/${source}/`], pos);
1319
1390
  }
1320
1391
  const span = { start: pos, end: pos + m[0].length };
1392
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1321
1393
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1322
1394
  return { ok: true, value: m[0], span };
1323
1395
  }
@@ -1468,6 +1540,18 @@ function rollbackTrivia(ctx, mark) {
1468
1540
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1469
1541
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1470
1542
  }
1543
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1544
+ const probeCtx = {
1545
+ trackLines: ctx.trackLines,
1546
+ state: ctx.state,
1547
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1548
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1549
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1550
+ };
1551
+ const result = triviaP.parse(input, cur, probeCtx);
1552
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1553
+ return result;
1554
+ }
1471
1555
  function saveLookaheadMark(ctx) {
1472
1556
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1473
1557
  }
@@ -1490,13 +1574,29 @@ function scanWithLabels(input, cur, ctx) {
1490
1574
  function advanceTrivia(input, cur, ctx) {
1491
1575
  const triviaP = ctx.trivia;
1492
1576
  if (!triviaP) return cur;
1577
+ if (!ctx.trackLines) {
1578
+ const fast2 = fastTriviaScanner(triviaP);
1579
+ if (fast2) return fast2(input, cur);
1580
+ if (ctx.triviaKindLabels) {
1581
+ const scan = scanWithLabels(input, cur, ctx);
1582
+ return scan.end;
1583
+ }
1584
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1585
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1586
+ }
1587
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1493
1588
  const fast = fastTriviaScanner(triviaP);
1494
- if (fast) return fast(input, cur);
1589
+ if (fast) {
1590
+ const end = fast(input, cur);
1591
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1592
+ return end;
1593
+ }
1495
1594
  if (ctx.triviaKindLabels) {
1496
1595
  const scan = scanWithLabels(input, cur, ctx);
1596
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1497
1597
  return scan.end;
1498
1598
  }
1499
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1599
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1500
1600
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1501
1601
  }
1502
1602
  function scanTrivia(input, cur, ctx) {
@@ -1504,18 +1604,46 @@ function scanTrivia(input, cur, ctx) {
1504
1604
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1505
1605
  const log = ctx._triviaLog;
1506
1606
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1607
+ if (!ctx.trackLines) {
1608
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1609
+ if (fast2 && log === void 0 && !captureTl) {
1610
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1611
+ }
1612
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1613
+ return scanWithLabels(input, cur, ctx);
1614
+ }
1615
+ if (log !== void 0 || captureTl) {
1616
+ const tr3 = triviaP.parse(input, cur, {
1617
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1618
+ state: ctx.state
1619
+ });
1620
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1621
+ const end = tr3.span.end;
1622
+ return {
1623
+ end,
1624
+ commit: () => {
1625
+ pushTriviaLogEntry(ctx, cur, end);
1626
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1627
+ }
1628
+ };
1629
+ }
1630
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1631
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1632
+ }
1633
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1507
1634
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1508
1635
  if (fast && log === void 0 && !captureTl) {
1509
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1636
+ const end = fast(input, cur);
1637
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1638
+ return { end, commit: NOOP_COMMIT };
1510
1639
  }
1511
1640
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1512
- return scanWithLabels(input, cur, ctx);
1641
+ const scan = scanWithLabels(input, cur, ctx);
1642
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1643
+ return scan;
1513
1644
  }
1514
1645
  if (log !== void 0 || captureTl) {
1515
- const tr2 = triviaP.parse(input, cur, {
1516
- trackLines: log !== void 0 ? false : ctx.trackLines,
1517
- state: ctx.state
1518
- });
1646
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1519
1647
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1520
1648
  const end = tr2.span.end;
1521
1649
  return {
@@ -1526,7 +1654,7 @@ function scanTrivia(input, cur, ctx) {
1526
1654
  }
1527
1655
  };
1528
1656
  }
1529
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1657
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1530
1658
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1531
1659
  }
1532
1660
  function consumeTrivia(input, cur, ctx) {
@@ -2179,7 +2307,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2179
2307
  let cur = pos;
2180
2308
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
2181
2309
  const probeCtx = {
2182
- trackLines: ctx.trackLines,
2310
+ trackLines: false,
2183
2311
  state: ctx.state,
2184
2312
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
2185
2313
  };
@@ -2192,6 +2320,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2192
2320
  while (cur < input.length) {
2193
2321
  const s = sentinel.parse(input, cur, probeCtx);
2194
2322
  if (s.ok) {
2323
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2195
2324
  emit(cur);
2196
2325
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2197
2326
  }
@@ -2207,6 +2336,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2207
2336
  if (!advanced) cur++;
2208
2337
  }
2209
2338
  if (orEOF) {
2339
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2210
2340
  emit(cur);
2211
2341
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2212
2342
  }
@@ -2816,49 +2946,36 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2816
2946
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2817
2947
  const nodeType = def.type ?? missingInferredType();
2818
2948
  const cstOutput = cstOutputHost(ctx.build);
2949
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
2819
2950
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2820
- const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : build(children, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
2821
- const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span: r.span };
2951
+ const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState) : build(children, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children };
2952
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
2822
2953
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2823
2954
  pushCstChild(ctx, built, rawEntry);
2824
2955
  }
2825
- return { ok: true, value: built, span: r.span };
2956
+ return { ok: true, value: built, span };
2826
2957
  }
2827
2958
  };
2828
2959
  }
2829
2960
 
2830
- // src/compiler/line-index.ts
2831
- function buildLineIndex(input) {
2832
- const lineStarts = [0];
2833
- for (let i = 0; i < input.length; i++) {
2834
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
2835
- }
2836
- return { lineStarts };
2837
- }
2838
- function offsetToLineCol(index, offset) {
2839
- const { lineStarts } = index;
2840
- let lo = 0;
2841
- let hi = lineStarts.length - 1;
2842
- while (lo < hi) {
2843
- const mid = lo + hi + 1 >> 1;
2844
- if (lineStarts[mid] <= offset) lo = mid;
2845
- else hi = mid - 1;
2961
+ // src/combinators/grammar.ts
2962
+ function annotateResultLines(result, index) {
2963
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2964
+ const errors = annotated.errors;
2965
+ if (errors) {
2966
+ ;
2967
+ annotated.errors = errors.map((error) => ({
2968
+ ...error,
2969
+ span: annotateSpan(error.span, index)
2970
+ }));
2846
2971
  }
2847
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
2972
+ return annotated;
2848
2973
  }
2849
- function annotateSpan(span, index) {
2850
- const s = offsetToLineCol(index, span.start);
2851
- const e = offsetToLineCol(index, span.end);
2852
- return {
2853
- ...span,
2854
- startLine: s.line,
2855
- startColumn: s.col,
2856
- endLine: e.line,
2857
- endColumn: e.col
2858
- };
2974
+ function createParseLineContext(input, pos) {
2975
+ const lineIndex = createLineIndex();
2976
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2977
+ return { lineIndex, lineScannedTo: pos };
2859
2978
  }
2860
-
2861
- // src/combinators/grammar.ts
2862
2979
  function parser(opts, root) {
2863
2980
  const clearTrivia = opts.trivia === null;
2864
2981
  return {
@@ -2874,9 +2991,13 @@ function parser(opts, root) {
2874
2991
  },
2875
2992
  parse(input, pos, _ctx) {
2876
2993
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2994
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2995
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2877
2996
  const ctx = {
2878
2997
  ..._ctx,
2879
2998
  trackLines,
2999
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3000
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2880
3001
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2881
3002
  ...clearTrivia ? {
2882
3003
  trivia: void 0,
@@ -2894,11 +3015,10 @@ function parser(opts, root) {
2894
3015
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2895
3016
  };
2896
3017
  const result = root.parse(input, pos ?? 0, ctx);
2897
- if (trackLines) {
2898
- const idx = buildLineIndex(input);
2899
- return { ...result, span: annotateSpan(result.span, idx) };
3018
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3019
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2900
3020
  }
2901
- return result;
3021
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2902
3022
  }
2903
3023
  };
2904
3024
  }
@@ -3186,7 +3306,7 @@ function normalizeCstCollapse(collapse) {
3186
3306
  return typeof collapse === "function" ? collapse : void 0;
3187
3307
  }
3188
3308
  function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
3189
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
3309
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3190
3310
  }
3191
3311
  function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
3192
3312
  if (typeof typeOrOptions === "string") {