parseman 0.40.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 (54) hide show
  1. package/README.md +7 -6
  2. package/dist/combinators/expect.d.ts.map +1 -1
  3. package/dist/combinators/grammar.d.ts.map +1 -1
  4. package/dist/combinators/literal.d.ts.map +1 -1
  5. package/dist/combinators/node.d.ts.map +1 -1
  6. package/dist/combinators/regex.d.ts.map +1 -1
  7. package/dist/combinators/scanTo.d.ts.map +1 -1
  8. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  9. package/dist/compiler/codegen.d.ts +3 -0
  10. package/dist/compiler/codegen.d.ts.map +1 -1
  11. package/dist/compiler/inline-build.d.ts +1 -1
  12. package/dist/compiler/inline-build.d.ts.map +1 -1
  13. package/dist/compiler/inline-callback.d.ts +9 -0
  14. package/dist/compiler/inline-callback.d.ts.map +1 -1
  15. package/dist/compiler/line-index.d.ts +1 -15
  16. package/dist/compiler/line-index.d.ts.map +1 -1
  17. package/dist/compiler/value-usage.d.ts.map +1 -1
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/cst/trivia-entries.d.ts +52 -0
  20. package/dist/cst/trivia-entries.d.ts.map +1 -1
  21. package/dist/functional/run.d.ts +11 -2
  22. package/dist/functional/run.d.ts.map +1 -1
  23. package/dist/index.cjs +634 -169
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +634 -169
  28. package/dist/index.js.map +4 -4
  29. package/dist/language-service/index.cjs +203 -4
  30. package/dist/language-service/index.cjs.map +4 -4
  31. package/dist/language-service/index.js +203 -4
  32. package/dist/language-service/index.js.map +4 -4
  33. package/dist/line-index.d.ts +41 -0
  34. package/dist/line-index.d.ts.map +1 -0
  35. package/dist/plugin/index.cjs +454 -126
  36. package/dist/plugin/index.cjs.map +4 -4
  37. package/dist/plugin/index.js +454 -126
  38. package/dist/plugin/index.js.map +4 -4
  39. package/dist/recovery/scan.d.ts.map +1 -1
  40. package/dist/regex/first-set.d.ts.map +1 -1
  41. package/dist/run/index.cjs +202 -2
  42. package/dist/run/index.cjs.map +4 -4
  43. package/dist/run/index.d.ts +8 -4
  44. package/dist/run/index.d.ts.map +1 -1
  45. package/dist/run/index.js +202 -2
  46. package/dist/run/index.js.map +4 -4
  47. package/dist/spec/index.cjs +174 -52
  48. package/dist/spec/index.cjs.map +4 -4
  49. package/dist/spec/index.js +174 -52
  50. package/dist/spec/index.js.map +4 -4
  51. package/dist/types.d.ts +12 -0
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/version.d.ts +1 -1
  54. package/package.json +1 -1
@@ -354,9 +354,11 @@ function markUnusedValues(root) {
354
354
  visit(def.parser, consumed);
355
355
  visit(def.sentinel, false);
356
356
  return;
357
+ case "token":
358
+ visit(def.parser, false);
359
+ return;
357
360
  case "label":
358
361
  case "trivia":
359
- case "token":
360
362
  case "attempt":
361
363
  case "withCtx":
362
364
  case "expect":
@@ -475,6 +477,65 @@ function rules(a, b) {
475
477
  return cache;
476
478
  }
477
479
 
480
+ // src/line-index.ts
481
+ function createLineIndex() {
482
+ return { lineStarts: [0] };
483
+ }
484
+ function recordLineRange(index, input, start, end) {
485
+ const lineStarts = index.lineStarts;
486
+ for (let i = start; i < end; i++) {
487
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
488
+ }
489
+ }
490
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
491
+ if (!ctx.trackLines) return;
492
+ const from = ctx._lineScannedTo ?? 0;
493
+ if (end <= from) return;
494
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
495
+ if (!index) return;
496
+ recordLineRange(index, input, from, end);
497
+ ctx._lineScannedTo = end;
498
+ }
499
+ function normalizeLineIndex(index) {
500
+ const starts = index.lineStarts;
501
+ starts.sort((a, b) => a - b);
502
+ let write = 0;
503
+ for (let read = 0; read < starts.length; read++) {
504
+ const value = starts[read];
505
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
506
+ }
507
+ starts.length = write;
508
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
509
+ return index;
510
+ }
511
+ function offsetToLineCol(index, offset) {
512
+ const { lineStarts } = index;
513
+ let lo = 0;
514
+ let hi = lineStarts.length - 1;
515
+ while (lo < hi) {
516
+ const mid = lo + hi + 1 >> 1;
517
+ if (lineStarts[mid] <= offset) lo = mid;
518
+ else hi = mid - 1;
519
+ }
520
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
521
+ }
522
+ function annotateSpan(span, index) {
523
+ const s = offsetToLineCol(index, span.start);
524
+ const e = offsetToLineCol(index, span.end);
525
+ return {
526
+ ...span,
527
+ startLine: s.line,
528
+ startColumn: s.col,
529
+ endLine: e.line,
530
+ endColumn: e.col
531
+ };
532
+ }
533
+ function annotateSpanFromLineContext(span, ctx) {
534
+ if (!ctx.trackLines) return span;
535
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
536
+ return index ? annotateSpan(span, index) : span;
537
+ }
538
+
478
539
  // src/cst/capture-buffer.ts
479
540
  var EMPTY = [];
480
541
  var EMPTY_TL = [];
@@ -516,7 +577,12 @@ function endCstNodeCapture(ctx, saved) {
516
577
  return materialized;
517
578
  }
518
579
  function pushCstLeaf(ctx, leaf2) {
519
- pushCstChild(ctx, leaf2, leaf2);
580
+ if (!ctx.trackLines) {
581
+ pushCstChild(ctx, leaf2, leaf2);
582
+ return;
583
+ }
584
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
585
+ pushCstChild(ctx, annotated, annotated);
520
586
  }
521
587
  function pushCstChild(ctx, built, rawEntry) {
522
588
  const b = ctx._cstBuf;
@@ -706,7 +772,9 @@ function firstSetSentinel(fs) {
706
772
  function recoverScan(input, from, ctx, sync, expected) {
707
773
  let scanPos = from;
708
774
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
709
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
775
+ const span = { start: from, end: scanPos };
776
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
777
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
710
778
  ctx._errors?.push(error);
711
779
  return { error, end: scanPos };
712
780
  }
@@ -808,7 +876,8 @@ function expect(combinator, label2) {
808
876
  parse(input, pos, ctx) {
809
877
  const result = combinator.parse(input, pos, ctx);
810
878
  if (result.ok) return result;
811
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
879
+ const span = { start: pos, end: pos };
880
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
812
881
  ctx._errors?.push(error);
813
882
  if (ctx._tolerant) captureError(ctx, error);
814
883
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1148,6 +1217,7 @@ function literal(value, opts = {}) {
1148
1217
  canMatchNewline: value.includes("\n"),
1149
1218
  isTrivia: false
1150
1219
  };
1220
+ const canMatchNewline = meta.canMatchNewline;
1151
1221
  if (caseInsensitive) {
1152
1222
  const upper = value.toUpperCase();
1153
1223
  const lower = value.toLowerCase();
@@ -1164,6 +1234,7 @@ function literal(value, opts = {}) {
1164
1234
  return function parse2(input, pos, ctx) {
1165
1235
  if (input.charCodeAt(pos) === code) {
1166
1236
  const span = { start: pos, end: pos + 1 };
1237
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1167
1238
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1168
1239
  return { ok: true, value, span };
1169
1240
  }
@@ -1177,6 +1248,7 @@ function literal(value, opts = {}) {
1177
1248
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1178
1249
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1179
1250
  const span = { start: pos, end };
1251
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1180
1252
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1181
1253
  return { ok: true, value: matchedValue, span };
1182
1254
  }
@@ -1341,6 +1413,7 @@ function regex(pattern, flags = "") {
1341
1413
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1342
1414
  const value = input.slice(pos, scanEnd);
1343
1415
  const span2 = { start: pos, end: scanEnd };
1416
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1344
1417
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1345
1418
  return { ok: true, value, span: span2 };
1346
1419
  }
@@ -1350,6 +1423,7 @@ function regex(pattern, flags = "") {
1350
1423
  return failAt(ctx, [`/${source}/`], pos);
1351
1424
  }
1352
1425
  const span = { start: pos, end: pos + m[0].length };
1426
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1353
1427
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1354
1428
  return { ok: true, value: m[0], span };
1355
1429
  }
@@ -1500,6 +1574,18 @@ function rollbackTrivia(ctx, mark) {
1500
1574
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1501
1575
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1502
1576
  }
1577
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1578
+ const probeCtx = {
1579
+ trackLines: ctx.trackLines,
1580
+ state: ctx.state,
1581
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1582
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1583
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1584
+ };
1585
+ const result = triviaP.parse(input, cur, probeCtx);
1586
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1587
+ return result;
1588
+ }
1503
1589
  function saveLookaheadMark(ctx) {
1504
1590
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1505
1591
  }
@@ -1522,13 +1608,29 @@ function scanWithLabels(input, cur, ctx) {
1522
1608
  function advanceTrivia(input, cur, ctx) {
1523
1609
  const triviaP = ctx.trivia;
1524
1610
  if (!triviaP) return cur;
1611
+ if (!ctx.trackLines) {
1612
+ const fast2 = fastTriviaScanner(triviaP);
1613
+ if (fast2) return fast2(input, cur);
1614
+ if (ctx.triviaKindLabels) {
1615
+ const scan = scanWithLabels(input, cur, ctx);
1616
+ return scan.end;
1617
+ }
1618
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1619
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1620
+ }
1621
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1525
1622
  const fast = fastTriviaScanner(triviaP);
1526
- if (fast) return fast(input, cur);
1623
+ if (fast) {
1624
+ const end = fast(input, cur);
1625
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1626
+ return end;
1627
+ }
1527
1628
  if (ctx.triviaKindLabels) {
1528
1629
  const scan = scanWithLabels(input, cur, ctx);
1630
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1529
1631
  return scan.end;
1530
1632
  }
1531
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1633
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1532
1634
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1533
1635
  }
1534
1636
  function scanTrivia(input, cur, ctx) {
@@ -1536,18 +1638,46 @@ function scanTrivia(input, cur, ctx) {
1536
1638
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1537
1639
  const log = ctx._triviaLog;
1538
1640
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1641
+ if (!ctx.trackLines) {
1642
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1643
+ if (fast2 && log === void 0 && !captureTl) {
1644
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1645
+ }
1646
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1647
+ return scanWithLabels(input, cur, ctx);
1648
+ }
1649
+ if (log !== void 0 || captureTl) {
1650
+ const tr3 = triviaP.parse(input, cur, {
1651
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1652
+ state: ctx.state
1653
+ });
1654
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1655
+ const end = tr3.span.end;
1656
+ return {
1657
+ end,
1658
+ commit: () => {
1659
+ pushTriviaLogEntry(ctx, cur, end);
1660
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1661
+ }
1662
+ };
1663
+ }
1664
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1665
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1666
+ }
1667
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1539
1668
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1540
1669
  if (fast && log === void 0 && !captureTl) {
1541
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1670
+ const end = fast(input, cur);
1671
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1672
+ return { end, commit: NOOP_COMMIT };
1542
1673
  }
1543
1674
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1544
- return scanWithLabels(input, cur, ctx);
1675
+ const scan = scanWithLabels(input, cur, ctx);
1676
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1677
+ return scan;
1545
1678
  }
1546
1679
  if (log !== void 0 || captureTl) {
1547
- const tr2 = triviaP.parse(input, cur, {
1548
- trackLines: log !== void 0 ? false : ctx.trackLines,
1549
- state: ctx.state
1550
- });
1680
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1551
1681
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1552
1682
  const end = tr2.span.end;
1553
1683
  return {
@@ -1558,7 +1688,7 @@ function scanTrivia(input, cur, ctx) {
1558
1688
  }
1559
1689
  };
1560
1690
  }
1561
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1691
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1562
1692
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1563
1693
  }
1564
1694
  function consumeTrivia(input, cur, ctx) {
@@ -2211,7 +2341,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2211
2341
  let cur = pos;
2212
2342
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
2213
2343
  const probeCtx = {
2214
- trackLines: ctx.trackLines,
2344
+ trackLines: false,
2215
2345
  state: ctx.state,
2216
2346
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
2217
2347
  };
@@ -2224,6 +2354,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2224
2354
  while (cur < input.length) {
2225
2355
  const s = sentinel.parse(input, cur, probeCtx);
2226
2356
  if (s.ok) {
2357
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2227
2358
  emit(cur);
2228
2359
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2229
2360
  }
@@ -2239,6 +2370,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2239
2370
  if (!advanced) cur++;
2240
2371
  }
2241
2372
  if (orEOF) {
2373
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2242
2374
  emit(cur);
2243
2375
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2244
2376
  }
@@ -2256,7 +2388,7 @@ var META = new Set("()[]{}*+?|^$.".split(""));
2256
2388
  var CAP_RECORD = [
2257
2389
  ` if (_cap && _e > _pos) {`,
2258
2390
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
2259
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2391
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2260
2392
  ` }`
2261
2393
  ].join("\n");
2262
2394
 
@@ -2848,49 +2980,36 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2848
2980
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2849
2981
  const nodeType = def.type ?? missingInferredType();
2850
2982
  const cstOutput = cstOutputHost(ctx.build);
2983
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
2851
2984
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2852
- 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 };
2853
- 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 };
2985
+ 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 };
2986
+ 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 };
2854
2987
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2855
2988
  pushCstChild(ctx, built, rawEntry);
2856
2989
  }
2857
- return { ok: true, value: built, span: r.span };
2990
+ return { ok: true, value: built, span };
2858
2991
  }
2859
2992
  };
2860
2993
  }
2861
2994
 
2862
- // src/compiler/line-index.ts
2863
- function buildLineIndex(input) {
2864
- const lineStarts = [0];
2865
- for (let i = 0; i < input.length; i++) {
2866
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
2867
- }
2868
- return { lineStarts };
2869
- }
2870
- function offsetToLineCol(index, offset) {
2871
- const { lineStarts } = index;
2872
- let lo = 0;
2873
- let hi = lineStarts.length - 1;
2874
- while (lo < hi) {
2875
- const mid = lo + hi + 1 >> 1;
2876
- if (lineStarts[mid] <= offset) lo = mid;
2877
- else hi = mid - 1;
2995
+ // src/combinators/grammar.ts
2996
+ function annotateResultLines(result, index) {
2997
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2998
+ const errors = annotated.errors;
2999
+ if (errors) {
3000
+ ;
3001
+ annotated.errors = errors.map((error) => ({
3002
+ ...error,
3003
+ span: annotateSpan(error.span, index)
3004
+ }));
2878
3005
  }
2879
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3006
+ return annotated;
2880
3007
  }
2881
- function annotateSpan(span, index) {
2882
- const s = offsetToLineCol(index, span.start);
2883
- const e = offsetToLineCol(index, span.end);
2884
- return {
2885
- ...span,
2886
- startLine: s.line,
2887
- startColumn: s.col,
2888
- endLine: e.line,
2889
- endColumn: e.col
2890
- };
3008
+ function createParseLineContext(input, pos) {
3009
+ const lineIndex = createLineIndex();
3010
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3011
+ return { lineIndex, lineScannedTo: pos };
2891
3012
  }
2892
-
2893
- // src/combinators/grammar.ts
2894
3013
  function parser(opts, root) {
2895
3014
  const clearTrivia = opts.trivia === null;
2896
3015
  return {
@@ -2906,9 +3025,13 @@ function parser(opts, root) {
2906
3025
  },
2907
3026
  parse(input, pos, _ctx) {
2908
3027
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3028
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3029
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2909
3030
  const ctx = {
2910
3031
  ..._ctx,
2911
3032
  trackLines,
3033
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3034
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2912
3035
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2913
3036
  ...clearTrivia ? {
2914
3037
  trivia: void 0,
@@ -2926,11 +3049,10 @@ function parser(opts, root) {
2926
3049
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2927
3050
  };
2928
3051
  const result = root.parse(input, pos ?? 0, ctx);
2929
- if (trackLines) {
2930
- const idx = buildLineIndex(input);
2931
- return { ...result, span: annotateSpan(result.span, idx) };
3052
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3053
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2932
3054
  }
2933
- return result;
3055
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2934
3056
  }
2935
3057
  };
2936
3058
  }
@@ -3218,7 +3340,7 @@ function normalizeCstCollapse(collapse) {
3218
3340
  return typeof collapse === "function" ? collapse : void 0;
3219
3341
  }
3220
3342
  function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
3221
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
3343
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3222
3344
  }
3223
3345
  function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
3224
3346
  if (typeof typeOrOptions === "string") {