parseman 0.46.0 → 0.47.1
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/README.md +8 -3
- package/dist/analysis/choice-cost-render.d.ts +12 -4
- package/dist/analysis/choice-cost-render.d.ts.map +1 -1
- package/dist/analysis/choice-cost.d.ts +2 -2
- package/dist/analysis/choice-cost.d.ts.map +1 -1
- package/dist/analysis/commitment.d.ts +131 -0
- package/dist/analysis/commitment.d.ts.map +1 -0
- package/dist/analysis/diagnostics.cjs +10339 -7787
- package/dist/analysis/diagnostics.cjs.map +3 -4
- package/dist/analysis/diagnostics.d.ts +2 -2
- package/dist/analysis/diagnostics.js +10339 -7787
- package/dist/analysis/diagnostics.js.map +3 -4
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/fix.d.ts +3 -3
- package/dist/analysis/fix.d.ts.map +1 -1
- package/dist/analysis/format-number.d.ts +23 -0
- package/dist/analysis/format-number.d.ts.map +1 -0
- package/dist/analysis/gating.d.ts +39 -2
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/analysis/rebuild.d.ts.map +1 -1
- package/dist/analysis/terminal.d.ts +5 -2
- package/dist/analysis/terminal.d.ts.map +1 -1
- package/dist/cli/index.cjs +10924 -8506
- package/dist/cli/index.cjs.map +3 -4
- package/dist/cli/index.js +10924 -8506
- package/dist/cli/index.js.map +3 -4
- package/dist/combinators/adjacency.d.ts +100 -0
- package/dist/combinators/adjacency.d.ts.map +1 -0
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/completions.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +8 -1
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/gate.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts +22 -0
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts +8 -0
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/map.d.ts +0 -1
- package/dist/combinators/map.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +13 -3
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/ref.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts +1 -16
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts +30 -2
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts +71 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/token.d.ts.map +1 -1
- package/dist/combinators/trivia-boundary.d.ts +26 -0
- package/dist/combinators/trivia-boundary.d.ts.map +1 -0
- package/dist/combinators/trivia-skip.d.ts +45 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/combinators/withCtx.d.ts.map +1 -1
- package/dist/compiler/compile-linkable-table.d.ts +104 -0
- package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
- package/dist/compiler/degradation.d.ts +0 -2
- package/dist/compiler/degradation.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts +13 -0
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +47 -53
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +81 -0
- package/dist/compiler/token-alphabet.d.ts.map +1 -0
- package/dist/compiler/token-dispatch.d.ts +95 -0
- package/dist/compiler/token-dispatch.d.ts.map +1 -0
- package/dist/compiler/token-scanner.d.ts +53 -0
- package/dist/compiler/token-scanner.d.ts.map +1 -0
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/cst/capture-buffer.d.ts +35 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/reflection-attach.d.ts +8 -0
- package/dist/cst/reflection-attach.d.ts.map +1 -0
- package/dist/cst/reflection-symbols.d.ts +10 -0
- package/dist/cst/reflection-symbols.d.ts.map +1 -0
- package/dist/cst/reflection.d.ts +2 -4
- package/dist/cst/reflection.d.ts.map +1 -1
- package/dist/cst/root-trivia-scope.d.ts +26 -0
- package/dist/cst/root-trivia-scope.d.ts.map +1 -0
- package/dist/cst/trivia-charscan.d.ts +18 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts +19 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/functional/run-tabled.d.ts +12 -0
- package/dist/functional/run-tabled.d.ts.map +1 -0
- package/dist/functional/run.d.ts +25 -42
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/grammar-metadata.d.ts +3 -0
- package/dist/grammar-metadata.d.ts.map +1 -0
- package/dist/index.cjs +9290 -7181
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9300 -7191
- package/dist/index.js.map +3 -4
- package/dist/language-service/index.cjs +313 -121
- package/dist/language-service/index.cjs.map +3 -4
- package/dist/language-service/index.js +313 -121
- package/dist/language-service/index.js.map +3 -4
- package/dist/oracle/index.cjs.map +0 -1
- package/dist/oracle/index.js.map +0 -1
- package/dist/parse-context.d.ts +49 -0
- package/dist/parse-context.d.ts.map +1 -0
- package/dist/plugin/evaluator.d.ts +40 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +11175 -9534
- package/dist/plugin/index.cjs.map +3 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +11175 -9534
- package/dist/plugin/index.js.map +3 -4
- package/dist/regex/classes.d.ts +28 -0
- package/dist/regex/classes.d.ts.map +1 -1
- package/dist/regex/first-set.d.ts +14 -0
- package/dist/regex/first-set.d.ts.map +1 -1
- package/dist/run/index.cjs +136 -51
- package/dist/run/index.cjs.map +2 -3
- package/dist/run/index.js +136 -51
- package/dist/run/index.js.map +2 -3
- package/dist/spec/index.cjs +2934 -1611
- package/dist/spec/index.cjs.map +3 -4
- package/dist/spec/index.js +2934 -1611
- package/dist/spec/index.js.map +3 -4
- package/dist/spec/model.d.ts +8 -0
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/table/assemble.d.ts +307 -0
- package/dist/table/assemble.d.ts.map +1 -0
- package/dist/table/cell.d.ts +42 -0
- package/dist/table/cell.d.ts.map +1 -0
- package/dist/table/child-slots.d.ts +30 -0
- package/dist/table/child-slots.d.ts.map +1 -0
- package/dist/table/compile-rule-map.d.ts +136 -0
- package/dist/table/compile-rule-map.d.ts.map +1 -0
- package/dist/table/compile.d.ts +56 -0
- package/dist/table/compile.d.ts.map +1 -0
- package/dist/table/duplication-hook.d.ts +32 -0
- package/dist/table/duplication-hook.d.ts.map +1 -0
- package/dist/table/emit-assembly.d.ts +139 -0
- package/dist/table/emit-assembly.d.ts.map +1 -0
- package/dist/table/emit.d.ts +113 -0
- package/dist/table/emit.d.ts.map +1 -0
- package/dist/table/encode-baseline.d.ts +16 -0
- package/dist/table/encode-baseline.d.ts.map +1 -0
- package/dist/table/encode.d.ts +71 -0
- package/dist/table/encode.d.ts.map +1 -0
- package/dist/table/exec-baseline.d.ts +9 -0
- package/dist/table/exec-baseline.d.ts.map +1 -0
- package/dist/table/exec.d.ts +72 -0
- package/dist/table/exec.d.ts.map +1 -0
- package/dist/table/fold.d.ts +25 -0
- package/dist/table/fold.d.ts.map +1 -0
- package/dist/table/index.cjs +11463 -0
- package/dist/table/index.cjs.map +6 -0
- package/dist/table/index.d.ts +100 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +11440 -0
- package/dist/table/index.js.map +6 -0
- package/dist/table/inspect.d.ts +17 -0
- package/dist/table/inspect.d.ts.map +1 -0
- package/dist/table/ops.d.ts +453 -0
- package/dist/table/ops.d.ts.map +1 -0
- package/dist/table/program.d.ts +496 -0
- package/dist/table/program.d.ts.map +1 -0
- package/dist/table/run-support.d.ts +38 -0
- package/dist/table/run-support.d.ts.map +1 -0
- package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
- package/dist/table/scan-shapes.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts +58 -0
- package/dist/table/site-labels.d.ts.map +1 -0
- package/dist/table/stamp.d.ts +70 -0
- package/dist/table/stamp.d.ts.map +1 -0
- package/dist/types.d.ts +80 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +26 -2
- package/src/analysis/choice-cost-gate.ts +359 -0
- package/src/analysis/choice-cost-render.ts +293 -0
- package/src/analysis/choice-cost.ts +1008 -0
- package/src/analysis/commitment.ts +557 -0
- package/src/analysis/corpus.ts +130 -0
- package/src/analysis/diagnose-render.ts +625 -0
- package/src/analysis/diagnose.ts +317 -0
- package/src/analysis/diagnostics.ts +33 -0
- package/src/analysis/duplication.ts +1955 -0
- package/src/analysis/fix-render.ts +217 -0
- package/src/analysis/fix.ts +669 -0
- package/src/analysis/format-number.ts +31 -0
- package/src/analysis/gating.ts +792 -0
- package/src/analysis/grammar.ts +72 -0
- package/src/analysis/rebuild.ts +287 -0
- package/src/analysis/terminal.ts +336 -0
- package/src/cli/index.ts +413 -0
- package/src/combinators/adjacency.ts +171 -0
- package/src/combinators/attempt.ts +41 -0
- package/src/combinators/case-fold.ts +90 -0
- package/src/combinators/choice.ts +466 -0
- package/src/combinators/completions.ts +64 -0
- package/src/combinators/dispatch.ts +358 -0
- package/src/combinators/expect.ts +159 -0
- package/src/combinators/first-set.ts +348 -0
- package/src/combinators/gate.ts +50 -0
- package/src/combinators/grammar.ts +272 -0
- package/src/combinators/keywords.ts +145 -0
- package/src/combinators/literal.ts +102 -0
- package/src/combinators/map.ts +106 -0
- package/src/combinators/node.ts +345 -0
- package/src/combinators/not.ts +55 -0
- package/src/combinators/parser.ts +266 -0
- package/src/combinators/peek.ts +63 -0
- package/src/combinators/probe.ts +25 -0
- package/src/combinators/ref.ts +109 -0
- package/src/combinators/regex.ts +170 -0
- package/src/combinators/repeat.ts +561 -0
- package/src/combinators/scanTo.ts +393 -0
- package/src/combinators/sequence.ts +237 -0
- package/src/combinators/token.ts +120 -0
- package/src/combinators/trivia-boundary.ts +48 -0
- package/src/combinators/trivia-skip.ts +852 -0
- package/src/combinators/withCtx.ts +45 -0
- package/src/compiler/build-arity.ts +330 -0
- package/src/compiler/compile-linkable-table.ts +209 -0
- package/src/compiler/degradation.ts +266 -0
- package/src/compiler/fields.ts +191 -0
- package/src/compiler/grammar-coverage-ids.ts +129 -0
- package/src/compiler/ir-serialize.ts +615 -0
- package/src/compiler/linker.ts +954 -0
- package/src/compiler/token-alphabet.ts +209 -0
- package/src/compiler/token-dispatch.ts +353 -0
- package/src/compiler/token-scanner.ts +318 -0
- package/src/compiler/value-usage.ts +147 -0
- package/src/coverage.ts +383 -0
- package/src/cst/capture-buffer.ts +297 -0
- package/src/cst/host-mode.ts +69 -0
- package/src/cst/reflection-attach.ts +11 -0
- package/src/cst/reflection-symbols.ts +9 -0
- package/src/cst/reflection.ts +149 -0
- package/src/cst/relative-spans.ts +207 -0
- package/src/cst/root-trivia-scope.ts +30 -0
- package/src/cst/trivia-charscan.ts +312 -0
- package/src/cst/trivia-entries.ts +285 -0
- package/src/cst/trivia-index.ts +131 -0
- package/src/cst/trivia-kinds.ts +358 -0
- package/src/cst/types.ts +76 -0
- package/src/cst/walk.ts +101 -0
- package/src/functional/doc.ts +959 -0
- package/src/functional/run-tabled.ts +153 -0
- package/src/functional/run.ts +558 -0
- package/src/grammar-metadata.ts +2 -0
- package/src/index.ts +189 -0
- package/src/language-service/index.ts +221 -0
- package/src/line-index.ts +131 -0
- package/src/oracle/digest.ts +620 -0
- package/src/oracle/index.ts +55 -0
- package/src/parse-context.ts +91 -0
- package/src/plugin/direct-builder-static.ts +102 -0
- package/src/plugin/evaluator.ts +1505 -0
- package/src/plugin/free-identifiers.ts +338 -0
- package/src/plugin/index.ts +2281 -0
- package/src/plugin/reducer-resolver.ts +606 -0
- package/src/recovery/scan.ts +155 -0
- package/src/regex/classes.ts +183 -0
- package/src/regex/first-set.ts +316 -0
- package/src/run/index.ts +31 -0
- package/src/spec/ebnf.ts +128 -0
- package/src/spec/index.ts +54 -0
- package/src/spec/model.ts +636 -0
- package/src/spec/railroad-lib.ts +559 -0
- package/src/spec/railroad.ts +256 -0
- package/src/table/assemble.ts +3006 -0
- package/src/table/cell.ts +42 -0
- package/src/table/child-slots.ts +113 -0
- package/src/table/compile-rule-map.ts +336 -0
- package/src/table/compile.ts +244 -0
- package/src/table/duplication-hook.ts +89 -0
- package/src/table/emit-assembly.ts +1777 -0
- package/src/table/emit.ts +429 -0
- package/src/table/encode-baseline.ts +340 -0
- package/src/table/encode.ts +1569 -0
- package/src/table/exec-baseline.ts +537 -0
- package/src/table/exec.ts +1680 -0
- package/src/table/fold.ts +38 -0
- package/src/table/index.ts +108 -0
- package/src/table/inspect.ts +52 -0
- package/src/table/ops.ts +467 -0
- package/src/table/program.ts +876 -0
- package/src/table/run-support.ts +66 -0
- package/src/table/scan-shapes.ts +1578 -0
- package/src/table/site-labels.ts +228 -0
- package/src/table/stamp.ts +229 -0
- package/src/types.ts +563 -0
- package/src/version.ts +16 -0
- package/dist/compiler/codegen.d.ts +0 -334
- package/dist/compiler/codegen.d.ts.map +0 -1
- package/dist/compiler/inline-build.d.ts +0 -11
- package/dist/compiler/inline-build.d.ts.map +0 -1
- package/dist/compiler/inline-callback.d.ts +0 -21
- package/dist/compiler/inline-callback.d.ts.map +0 -1
- package/dist/compiler/line-index.d.ts +0 -2
- package/dist/compiler/line-index.d.ts.map +0 -1
- package/dist/compiler/module-hoist.d.ts +0 -106
- package/dist/compiler/module-hoist.d.ts.map +0 -1
- package/dist/compiler/scannable-run.d.ts.map +0 -1
- package/dist/compiler/scannable-terminal.d.ts +0 -23
- package/dist/compiler/scannable-terminal.d.ts.map +0 -1
- package/dist/compiler/trivia-fast-path.d.ts +0 -38
- package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE LABELLED-TRIVIA SCANNER, AT CHARACTER LEVEL.
|
|
3
|
+
*
|
|
4
|
+
* `fastTriviaScanner` (`../combinators/trivia-skip.ts`) already lowers UNLABELLED
|
|
5
|
+
* trivia to a char loop. Labelled trivia had no equivalent, so every gap between
|
|
6
|
+
* two sequence terms in a `classifiedTrivia()` grammar ran the arms as actual
|
|
7
|
+
* COMBINATORS — `matchArmAt` → `startsFirstSet` → a detached `ParseContext` → the
|
|
8
|
+
* regex combinator's `ParseResult`. On `benchmark.less`, one parse takes that path
|
|
9
|
+
* 31,758 times, tries 113,091 arms, and enters 13,696 of them, to classify 11,963
|
|
10
|
+
* chunks covering 33,981 characters — about 2.8 characters per chunk, for which the
|
|
11
|
+
* combinator machinery is pure overhead.
|
|
12
|
+
*
|
|
13
|
+
* The arms are static grammar data (`encode.ts`'s `triviaSpecOf` already lowers
|
|
14
|
+
* them to `[label, source, flags]`), so they can be classified ONCE, per spec, into
|
|
15
|
+
* per-arm character matchers. Anything that does not classify yields `null` and the
|
|
16
|
+
* combinator path stands — there is no fast path that is only usually right.
|
|
17
|
+
*
|
|
18
|
+
* A matcher returns the END of its match, or `pos` for "no match". That is exactly
|
|
19
|
+
* `matchArmAt`'s contract (`!r.ok || r.span.end <= pos` → null), so a lowered arm
|
|
20
|
+
* and a combinator arm are interchangeable inside the scan loops.
|
|
21
|
+
*
|
|
22
|
+
* The parser `state` `matchArmAt` threads into its detached context is not carried
|
|
23
|
+
* here, and does not need to be: only a plain `regex` arm lowers, and a regex
|
|
24
|
+
* terminal never reads state. An arm that could is exactly an arm that fails to
|
|
25
|
+
* classify.
|
|
26
|
+
*/
|
|
27
|
+
import type { Combinator } from '../types.ts'
|
|
28
|
+
import { parseClassRanges } from '../regex/classes.ts'
|
|
29
|
+
import type { LabeledTriviaSpec } from './trivia-kinds.ts'
|
|
30
|
+
|
|
31
|
+
export type TriviaArmMatcher = (input: string, pos: number) => number
|
|
32
|
+
|
|
33
|
+
type Ranges = Array<[number, number]>
|
|
34
|
+
|
|
35
|
+
function inRanges(cp: number, ranges: Ranges): boolean {
|
|
36
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
37
|
+
const r = ranges[i]!
|
|
38
|
+
if (cp >= r[0] && cp <= r[1]) return true
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A run of LITERAL characters as code points, or null.
|
|
45
|
+
*
|
|
46
|
+
* Only punctuation may be backslash-escaped: `\n`/`\d`/`\w` are not literals, and
|
|
47
|
+
* decoding them as one would silently accept a different language.
|
|
48
|
+
*/
|
|
49
|
+
function literalCodes(src: string): number[] | null {
|
|
50
|
+
const codes: number[] = []
|
|
51
|
+
for (let i = 0; i < src.length; i++) {
|
|
52
|
+
let c = src[i]!
|
|
53
|
+
if (c === '\\') {
|
|
54
|
+
c = src[i + 1] ?? ''
|
|
55
|
+
if (c === '' || /[A-Za-z0-9]/.test(c)) return null
|
|
56
|
+
i++
|
|
57
|
+
} else if (/[[\]()|*+?{}^$.]/.test(c)) {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
codes.push(c.charCodeAt(0))
|
|
61
|
+
}
|
|
62
|
+
return codes.length > 0 ? codes : null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* ONE character as it appears inside a character class, where `*` and friends are
|
|
67
|
+
* ordinary characters. `\x`-style escapes still have to be punctuation.
|
|
68
|
+
*/
|
|
69
|
+
function classLiteralCode(src: string): number | null {
|
|
70
|
+
if (src.length === 1) return src.charCodeAt(0)
|
|
71
|
+
if (src.length === 2 && src[0] === '\\' && !/[A-Za-z0-9]/.test(src[1]!)) return src.charCodeAt(1)
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `[cls]+` / `[cls]*` — a positive character-class run. */
|
|
76
|
+
function classifyClassRun(src: string): TriviaArmMatcher | null {
|
|
77
|
+
const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(src)
|
|
78
|
+
if (!m) return null
|
|
79
|
+
const ranges = parseClassRanges(m[1]!)
|
|
80
|
+
if (!ranges) return null
|
|
81
|
+
// `*` can match empty, but an empty match is "no match" to every caller here
|
|
82
|
+
// (`matchArmAt` rejects `end <= pos`), so both quantifiers scan the same.
|
|
83
|
+
return (input, pos) => {
|
|
84
|
+
let p = pos
|
|
85
|
+
while (p < input.length && inRanges(input.charCodeAt(p), ranges)) p++
|
|
86
|
+
return p
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** `//[^\n\r]*` — a literal leader run to (not including) the line break. */
|
|
91
|
+
function classifyLineRun(src: string): TriviaArmMatcher | null {
|
|
92
|
+
const m = /^(.*?)\[\^(?:\\n\\r|\\r\\n)\]\*$/.exec(src)
|
|
93
|
+
if (!m) return null
|
|
94
|
+
const codes = literalCodes(m[1]!)
|
|
95
|
+
if (!codes) return null
|
|
96
|
+
const n = codes.length
|
|
97
|
+
return (input, pos) => {
|
|
98
|
+
for (let i = 0; i < n; i++) if (input.charCodeAt(pos + i) !== codes[i]) return pos
|
|
99
|
+
let p = pos + n
|
|
100
|
+
while (p < input.length) {
|
|
101
|
+
const c = input.charCodeAt(p)
|
|
102
|
+
if (c === 10 || c === 13) break
|
|
103
|
+
p++
|
|
104
|
+
}
|
|
105
|
+
return p
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `/*(?:[^*]|\*(?!\/))*\*\/` — a literal-delimited run, i.e. a block comment.
|
|
111
|
+
*
|
|
112
|
+
* The body alternation can never consume the closing pair, so the greedy regex
|
|
113
|
+
* stops at the FIRST close: an `indexOf` is not an approximation of it, it is the
|
|
114
|
+
* same position. An unterminated open fails in both, since the close is required.
|
|
115
|
+
*/
|
|
116
|
+
function classifyDelimited(src: string): TriviaArmMatcher | null {
|
|
117
|
+
const m = /^(\\?.)(\\?.)\(\?:\[\^(\\?.)\]\|(\\?.)\(\?!(\\?.)\)\)\*(\\?.)(\\?.)$/.exec(src)
|
|
118
|
+
if (!m) return null
|
|
119
|
+
const open = literalCodes(m[1]! + m[2]!)
|
|
120
|
+
const close = literalCodes(m[6]! + m[7]!)
|
|
121
|
+
// `m[3]` sits inside `[^…]`, where a regex metacharacter stands for itself and
|
|
122
|
+
// needs no backslash — so it is read with the class-body rule, not the strict one.
|
|
123
|
+
const bodyExcluded = classLiteralCode(m[3]!)
|
|
124
|
+
const bodyGuard = literalCodes(m[4]!)
|
|
125
|
+
const guardNext = literalCodes(m[5]!)
|
|
126
|
+
if (!open || !close || bodyExcluded === null || !bodyGuard || !guardNext) return null
|
|
127
|
+
// The body must exclude exactly the close pair and nothing else — the shape
|
|
128
|
+
// `(?:[^C0]|C0(?!C1))*` with `C0C1` the close.
|
|
129
|
+
if (bodyExcluded !== close[0] || bodyGuard[0] !== close[0] || guardNext[0] !== close[1]) return null
|
|
130
|
+
const closeStr = String.fromCharCode(...close)
|
|
131
|
+
const openLen = open.length
|
|
132
|
+
const closeLen = closeStr.length
|
|
133
|
+
return (input, pos) => {
|
|
134
|
+
for (let i = 0; i < openLen; i++) if (input.charCodeAt(pos + i) !== open[i]) return pos
|
|
135
|
+
const hit = input.indexOf(closeStr, pos + openLen)
|
|
136
|
+
return hit < 0 ? pos : hit + closeLen
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Split a regex body on top-level `|`, tracking group depth and classes. */
|
|
141
|
+
function splitAlts(body: string): string[] {
|
|
142
|
+
const out: string[] = []
|
|
143
|
+
let depth = 0
|
|
144
|
+
let inClass = false
|
|
145
|
+
let start = 0
|
|
146
|
+
for (let i = 0; i < body.length; i++) {
|
|
147
|
+
const c = body[i]
|
|
148
|
+
if (c === '\\') { i++; continue }
|
|
149
|
+
if (inClass) { if (c === ']') inClass = false; continue }
|
|
150
|
+
if (c === '[') inClass = true
|
|
151
|
+
else if (c === '(') depth++
|
|
152
|
+
else if (c === ')') depth--
|
|
153
|
+
else if (c === '|' && depth === 0) { out.push(body.slice(start, i)); start = i + 1 }
|
|
154
|
+
}
|
|
155
|
+
out.push(body.slice(start))
|
|
156
|
+
return out
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Strip one full-span `(?:…)` wrapper. */
|
|
160
|
+
function unwrapGroup(src: string): string {
|
|
161
|
+
if (!src.startsWith('(?:') || !src.endsWith(')')) return src
|
|
162
|
+
let depth = 0
|
|
163
|
+
for (let i = 0; i < src.length; i++) {
|
|
164
|
+
const c = src[i]
|
|
165
|
+
if (c === '\\') { i++; continue }
|
|
166
|
+
if (c === '(') depth++
|
|
167
|
+
else if (c === ')') { depth--; if (depth === 0) return i === src.length - 1 ? src.slice(3, -1) : src }
|
|
168
|
+
}
|
|
169
|
+
return src
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* `(?:A|B|C)+` — one arm that is itself a whole trivia alternation (the shape
|
|
174
|
+
* `classifiedTrivia({ whitespace: /…|…|…/ })` produces when a grammar gives every
|
|
175
|
+
* form the SAME label).
|
|
176
|
+
*
|
|
177
|
+
* ORDERED-GREEDY IS THE REGEX RESULT, not an approximation of it. A greedy `+`
|
|
178
|
+
* only reconsiders an alternative when the CONTINUATION fails, and here the
|
|
179
|
+
* continuation is "more repetitions, then end of pattern", which cannot fail once
|
|
180
|
+
* one repetition has matched. So the engine keeps the first alternative that
|
|
181
|
+
* matches at each position, exactly as this loop does — no disjointness needed
|
|
182
|
+
* (`//` and `/*` share a leading `/` and still agree). An alternative that matches
|
|
183
|
+
* empty terminates both, as `end > p` requires progress.
|
|
184
|
+
*/
|
|
185
|
+
function classifyAltRun(src: string): TriviaArmMatcher | null {
|
|
186
|
+
const m = /^\((?:\?:)?(.*)\)[*+]$/.exec(src)
|
|
187
|
+
if (!m) return null
|
|
188
|
+
const parts = splitAlts(m[1]!)
|
|
189
|
+
if (parts.length < 2) return null
|
|
190
|
+
const matchers: TriviaArmMatcher[] = []
|
|
191
|
+
for (const part of parts) {
|
|
192
|
+
const sub = classifyArm(unwrapGroup(part))
|
|
193
|
+
if (!sub) return null
|
|
194
|
+
matchers.push(sub)
|
|
195
|
+
}
|
|
196
|
+
const n = matchers.length
|
|
197
|
+
return (input, pos) => {
|
|
198
|
+
let p = pos
|
|
199
|
+
scan: while (p < input.length) {
|
|
200
|
+
for (let i = 0; i < n; i++) {
|
|
201
|
+
const end = matchers[i]!(input, p)
|
|
202
|
+
if (end > p) { p = end; continue scan }
|
|
203
|
+
}
|
|
204
|
+
break
|
|
205
|
+
}
|
|
206
|
+
return p
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function classifyArm(src: string): TriviaArmMatcher | null {
|
|
211
|
+
return classifyClassRun(src)
|
|
212
|
+
?? classifyLineRun(src)
|
|
213
|
+
?? classifyDelimited(src)
|
|
214
|
+
?? classifyAltRun(src)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function classifyParser(p: Combinator<unknown>): TriviaArmMatcher | null {
|
|
218
|
+
const d = p._def
|
|
219
|
+
if (d.tag !== 'regex' || d.flags) return null
|
|
220
|
+
return classifyArm(d.source)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let armSpec0: LabeledTriviaSpec | undefined
|
|
224
|
+
let armSpec1: LabeledTriviaSpec | undefined
|
|
225
|
+
let armSpec2: LabeledTriviaSpec | undefined
|
|
226
|
+
let armSpec3: LabeledTriviaSpec | undefined
|
|
227
|
+
let armValue0: readonly TriviaArmMatcher[] | null | undefined
|
|
228
|
+
let armValue1: readonly TriviaArmMatcher[] | null | undefined
|
|
229
|
+
let armValue2: readonly TriviaArmMatcher[] | null | undefined
|
|
230
|
+
let armValue3: readonly TriviaArmMatcher[] | null | undefined
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* The character-level matchers for a spec's arms — index-parallel to `spec.arms`,
|
|
234
|
+
* so a matcher's index IS its kind index — or `null` when any arm has no lowering.
|
|
235
|
+
*
|
|
236
|
+
* `spec.minRepeats > 1` is excluded: the loops below report only an END, and a run
|
|
237
|
+
* that fails a minimum of two must report NOTHING. `classifiedTrivia()` never
|
|
238
|
+
* produces one, and the combinator path still handles it if a host writes one.
|
|
239
|
+
*/
|
|
240
|
+
export function charArmsFor(spec: LabeledTriviaSpec): readonly TriviaArmMatcher[] | null {
|
|
241
|
+
if (spec === armSpec0) return armValue0!
|
|
242
|
+
if (spec === armSpec1) return armValue1!
|
|
243
|
+
if (spec === armSpec2) return armValue2!
|
|
244
|
+
if (spec === armSpec3) return armValue3!
|
|
245
|
+
let arms: TriviaArmMatcher[] | null = []
|
|
246
|
+
if (spec.minRepeats > 1) {
|
|
247
|
+
arms = null
|
|
248
|
+
} else {
|
|
249
|
+
for (const arm of spec.arms) {
|
|
250
|
+
const c = classifyParser(arm.parser)
|
|
251
|
+
if (!c) { arms = null; break }
|
|
252
|
+
arms.push(c)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
armSpec3 = armSpec2
|
|
256
|
+
armValue3 = armValue2
|
|
257
|
+
armSpec2 = armSpec1
|
|
258
|
+
armValue2 = armValue1
|
|
259
|
+
armSpec1 = armSpec0
|
|
260
|
+
armValue1 = armValue0
|
|
261
|
+
armSpec0 = spec
|
|
262
|
+
armValue0 = arms
|
|
263
|
+
return arms
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** End of the trivia run at `cur`, or `cur` when nothing matched. */
|
|
267
|
+
export function charTriviaEnd(input: string, cur: number, arms: readonly TriviaArmMatcher[]): number {
|
|
268
|
+
const n = arms.length
|
|
269
|
+
let pos = cur
|
|
270
|
+
scan: while (pos < input.length) {
|
|
271
|
+
for (let i = 0; i < n; i++) {
|
|
272
|
+
const end = arms[i]!(input, pos)
|
|
273
|
+
if (end > pos) { pos = end; continue scan }
|
|
274
|
+
}
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
return pos
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** As `charTriviaEnd`, reporting each chunk with the kind index that matched it. */
|
|
281
|
+
export function charTriviaVisit(
|
|
282
|
+
input: string,
|
|
283
|
+
cur: number,
|
|
284
|
+
arms: readonly TriviaArmMatcher[],
|
|
285
|
+
visit: (start: number, end: number, kindIndex: number) => void,
|
|
286
|
+
): number {
|
|
287
|
+
const n = arms.length
|
|
288
|
+
let pos = cur
|
|
289
|
+
scan: while (pos < input.length) {
|
|
290
|
+
for (let i = 0; i < n; i++) {
|
|
291
|
+
const end = arms[i]!(input, pos)
|
|
292
|
+
if (end > pos) { visit(pos, end, i); pos = end; continue scan }
|
|
293
|
+
}
|
|
294
|
+
break
|
|
295
|
+
}
|
|
296
|
+
return pos
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** The `1 << kindIndex` mask of the categories in the run at `cur`. */
|
|
300
|
+
export function charTriviaKindMask(input: string, cur: number, arms: readonly TriviaArmMatcher[]): number {
|
|
301
|
+
const n = arms.length
|
|
302
|
+
let pos = cur
|
|
303
|
+
let mask = 0
|
|
304
|
+
scan: while (pos < input.length) {
|
|
305
|
+
for (let i = 0; i < n; i++) {
|
|
306
|
+
const end = arms[i]!(input, pos)
|
|
307
|
+
if (end > pos) { mask |= 1 << i; pos = end; continue scan }
|
|
308
|
+
}
|
|
309
|
+
break
|
|
310
|
+
}
|
|
311
|
+
return mask
|
|
312
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/** View over a flat trivia log (`_triviaLog` or per-node `triviaLog`). */
|
|
2
|
+
export type TriviaEntriesView = {
|
|
3
|
+
readonly length: number
|
|
4
|
+
readonly labels: readonly string[] | undefined
|
|
5
|
+
readonly stride: number
|
|
6
|
+
start(i: number): number
|
|
7
|
+
end(i: number): number
|
|
8
|
+
/** Raw-child insertion boundary; defined only for a per-node trivia log. */
|
|
9
|
+
insertIndex(i: number): number | undefined
|
|
10
|
+
kindIndex(i: number): number | undefined
|
|
11
|
+
kind(i: number): string | undefined
|
|
12
|
+
text(i: number, input: string): string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type RootTriviaGap = {
|
|
16
|
+
/** Start offset of the contiguous trivia gap. */
|
|
17
|
+
readonly start: number
|
|
18
|
+
/** End offset of the contiguous trivia gap. */
|
|
19
|
+
readonly end: number
|
|
20
|
+
/** Entry indices into `entries` that make up this gap. */
|
|
21
|
+
readonly entryIndices: readonly number[]
|
|
22
|
+
/** True when any entry in the gap has the requested labeled trivia kind. */
|
|
23
|
+
hasKind(kind: string): boolean
|
|
24
|
+
/** Slice the full contiguous gap text from the original input. */
|
|
25
|
+
text(input: string): string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type RootTriviaIndex = {
|
|
29
|
+
/** Lazy entry-level view over sparse selected-root rows. */
|
|
30
|
+
readonly entries: TriviaEntriesView
|
|
31
|
+
/** Selected-root label table; entry kind indices refer to this array. */
|
|
32
|
+
readonly labels: readonly string[]
|
|
33
|
+
/**
|
|
34
|
+
* Contiguous trivia gaps keyed by the following content offset. Values are
|
|
35
|
+
* entry indices into `entries`, not materialized `{ value, span }` objects.
|
|
36
|
+
*/
|
|
37
|
+
readonly before: ReadonlyMap<number, readonly number[]>
|
|
38
|
+
/**
|
|
39
|
+
* Contiguous trivia gaps keyed by the preceding content offset. Values are
|
|
40
|
+
* entry indices into `entries`, not materialized `{ value, span }` objects.
|
|
41
|
+
*/
|
|
42
|
+
readonly after: ReadonlyMap<number, readonly number[]>
|
|
43
|
+
entryIndicesBefore(offset: number): readonly number[]
|
|
44
|
+
entryIndicesAfter(offset: number): readonly number[]
|
|
45
|
+
/**
|
|
46
|
+
* Contiguous trivia gap immediately before `offset`, keyed by the following
|
|
47
|
+
* content offset. Returns undefined when no gap sits at that boundary.
|
|
48
|
+
*/
|
|
49
|
+
gapBefore(offset: number): RootTriviaGap | undefined
|
|
50
|
+
/**
|
|
51
|
+
* Contiguous trivia gap immediately after `offset`, keyed by the preceding
|
|
52
|
+
* content offset. Returns undefined when no gap sits at that boundary.
|
|
53
|
+
*/
|
|
54
|
+
gapAfter(offset: number): RootTriviaGap | undefined
|
|
55
|
+
/** All contiguous trivia gaps in source order. */
|
|
56
|
+
gaps(): readonly RootTriviaGap[]
|
|
57
|
+
/** Source-ordered gaps containing at least one entry with the given label. */
|
|
58
|
+
gapsWithKind(kind: string | readonly string[]): readonly RootTriviaGap[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const EMPTY_INDICES: readonly number[] = Object.freeze([])
|
|
62
|
+
const EMPTY_GAPS: readonly RootTriviaGap[] = Object.freeze([])
|
|
63
|
+
/** Number of packed fields in one selected-root trivia row. */
|
|
64
|
+
export const SELECTED_ROOT_STRIDE = 5
|
|
65
|
+
|
|
66
|
+
type RootTriviaMaps = {
|
|
67
|
+
before: Map<number, number[]>
|
|
68
|
+
after: Map<number, number[]>
|
|
69
|
+
beforeGaps: Map<number, RootTriviaGap>
|
|
70
|
+
afterGaps: Map<number, RootTriviaGap>
|
|
71
|
+
gaps: RootTriviaGap[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function entryOffset(i: number, stride: number): number {
|
|
75
|
+
return i * stride
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Wrap a flat trivia log. Stride is inferred from `labels`:
|
|
80
|
+
* - root `_triviaLog`: 2 (start, end) or 3 (+ kindIndex)
|
|
81
|
+
* - node `triviaLog`: 3 (start, end, insertIdx) or 4 (+ kindIndex)
|
|
82
|
+
*/
|
|
83
|
+
export function triviaEntries(
|
|
84
|
+
log: readonly number[],
|
|
85
|
+
labels?: readonly string[],
|
|
86
|
+
opts?: { nodeLog?: boolean },
|
|
87
|
+
): TriviaEntriesView {
|
|
88
|
+
const baseStride = opts?.nodeLog ? 3 : 2
|
|
89
|
+
const stride = labels ? baseStride + 1 : baseStride
|
|
90
|
+
const length = Math.floor(log.length / stride)
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
length,
|
|
94
|
+
labels,
|
|
95
|
+
stride,
|
|
96
|
+
start(i) {
|
|
97
|
+
return log[entryOffset(i, stride)]!
|
|
98
|
+
},
|
|
99
|
+
end(i) {
|
|
100
|
+
return log[entryOffset(i, stride) + 1]!
|
|
101
|
+
},
|
|
102
|
+
insertIndex(i) {
|
|
103
|
+
return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : undefined
|
|
104
|
+
},
|
|
105
|
+
kindIndex(i) {
|
|
106
|
+
if (!labels) return undefined
|
|
107
|
+
return log[entryOffset(i, stride) + baseStride]
|
|
108
|
+
},
|
|
109
|
+
kind(i) {
|
|
110
|
+
const ki = labels ? log[entryOffset(i, stride) + baseStride] : undefined
|
|
111
|
+
return ki !== undefined ? labels![ki] : undefined
|
|
112
|
+
},
|
|
113
|
+
text(i, input) {
|
|
114
|
+
const s = log[entryOffset(i, stride)]!
|
|
115
|
+
const e = log[entryOffset(i, stride) + 1]!
|
|
116
|
+
return input.slice(s, e)
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Build a root view over the sparse selected-label rows captured as
|
|
123
|
+
* `[ownedRangeStart, ownedRangeEnd, markerStart, markerEnd, kindIndex]`.
|
|
124
|
+
*
|
|
125
|
+
* This is intentionally a different input shape from `triviaLog`: each row is
|
|
126
|
+
* one selected marker, while the first pair names the complete committed trivia
|
|
127
|
+
* range around it. Thus a mixed trivia run costs one five-number row, not three
|
|
128
|
+
* legacy trivia entries. Rows are source ordered and equal gap pairs are
|
|
129
|
+
* contiguous, which lets singleton boundary lookups binary-search the sparse
|
|
130
|
+
* rows without constructing document-wide maps.
|
|
131
|
+
*/
|
|
132
|
+
export function buildRootTriviaIndex(
|
|
133
|
+
log: readonly number[],
|
|
134
|
+
labels: readonly string[],
|
|
135
|
+
): RootTriviaIndex {
|
|
136
|
+
const length = Math.floor(log.length / SELECTED_ROOT_STRIDE)
|
|
137
|
+
const markerOffset = (i: number) => i * SELECTED_ROOT_STRIDE
|
|
138
|
+
const ownedRangeStart = (i: number) => log[markerOffset(i)]!
|
|
139
|
+
const ownedRangeEnd = (i: number) => log[markerOffset(i) + 1]!
|
|
140
|
+
const markerStart = (i: number) => log[markerOffset(i) + 2]!
|
|
141
|
+
const markerEnd = (i: number) => log[markerOffset(i) + 3]!
|
|
142
|
+
const markerKind = (i: number) => log[markerOffset(i) + 4]!
|
|
143
|
+
|
|
144
|
+
const entries: TriviaEntriesView = {
|
|
145
|
+
length,
|
|
146
|
+
labels,
|
|
147
|
+
stride: SELECTED_ROOT_STRIDE,
|
|
148
|
+
start: markerStart,
|
|
149
|
+
end: markerEnd,
|
|
150
|
+
insertIndex: () => undefined,
|
|
151
|
+
kindIndex: markerKind,
|
|
152
|
+
kind(i) {
|
|
153
|
+
return labels[markerKind(i)]
|
|
154
|
+
},
|
|
155
|
+
text(i, input) {
|
|
156
|
+
return input.slice(markerStart(i), markerEnd(i))
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const lowerBound = (offset: number, by: (i: number) => number): number => {
|
|
161
|
+
let lo = 0
|
|
162
|
+
let hi = length
|
|
163
|
+
while (lo < hi) {
|
|
164
|
+
const mid = (lo + hi) >>> 1
|
|
165
|
+
if (by(mid) < offset) lo = mid + 1
|
|
166
|
+
else hi = mid
|
|
167
|
+
}
|
|
168
|
+
return lo
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const gapRowsAt = (offset: number, direction: 'before' | 'after'): [number, number] | undefined => {
|
|
172
|
+
const edge = direction === 'before' ? ownedRangeEnd : ownedRangeStart
|
|
173
|
+
let first = lowerBound(offset, edge)
|
|
174
|
+
if (first === length || edge(first) !== offset) return undefined
|
|
175
|
+
const start = ownedRangeStart(first)
|
|
176
|
+
const end = ownedRangeEnd(first)
|
|
177
|
+
while (first > 0 && ownedRangeStart(first - 1) === start && ownedRangeEnd(first - 1) === end) first--
|
|
178
|
+
let last = first + 1
|
|
179
|
+
while (last < length && ownedRangeStart(last) === start && ownedRangeEnd(last) === end) last++
|
|
180
|
+
return [first, last]
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const indices = (first: number, last: number): readonly number[] => {
|
|
184
|
+
const out: number[] = []
|
|
185
|
+
for (let i = first; i < last; i++) out.push(i)
|
|
186
|
+
return out
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const makeGap = (first: number, last: number): RootTriviaGap => {
|
|
190
|
+
const start = ownedRangeStart(first)
|
|
191
|
+
const end = ownedRangeEnd(first)
|
|
192
|
+
let entryIndices: readonly number[] | undefined
|
|
193
|
+
return {
|
|
194
|
+
start,
|
|
195
|
+
end,
|
|
196
|
+
get entryIndices() {
|
|
197
|
+
return entryIndices ??= indices(first, last)
|
|
198
|
+
},
|
|
199
|
+
hasKind(kind) {
|
|
200
|
+
for (let i = first; i < last; i++) {
|
|
201
|
+
if (labels[markerKind(i)] === kind) return true
|
|
202
|
+
}
|
|
203
|
+
return false
|
|
204
|
+
},
|
|
205
|
+
text(input) {
|
|
206
|
+
return input.slice(start, end)
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let maps: RootTriviaMaps | undefined
|
|
212
|
+
const getMaps = () => {
|
|
213
|
+
if (maps !== undefined) return maps
|
|
214
|
+
const before = new Map<number, number[]>()
|
|
215
|
+
const after = new Map<number, number[]>()
|
|
216
|
+
const beforeGaps = new Map<number, RootTriviaGap>()
|
|
217
|
+
const afterGaps = new Map<number, RootTriviaGap>()
|
|
218
|
+
const gaps: RootTriviaGap[] = []
|
|
219
|
+
for (let first = 0; first < length;) {
|
|
220
|
+
let last = first + 1
|
|
221
|
+
while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++
|
|
222
|
+
const gap = makeGap(first, last)
|
|
223
|
+
gaps.push(gap)
|
|
224
|
+
afterGaps.set(gap.start, gap)
|
|
225
|
+
beforeGaps.set(gap.end, gap)
|
|
226
|
+
after.set(gap.start, [...gap.entryIndices])
|
|
227
|
+
before.set(gap.end, [...gap.entryIndices])
|
|
228
|
+
first = last
|
|
229
|
+
}
|
|
230
|
+
maps = { before, after, beforeGaps, afterGaps, gaps }
|
|
231
|
+
return maps
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
entries,
|
|
236
|
+
labels,
|
|
237
|
+
get before() { return getMaps().before },
|
|
238
|
+
get after() { return getMaps().after },
|
|
239
|
+
entryIndicesBefore(offset) {
|
|
240
|
+
const rows = gapRowsAt(offset, 'before')
|
|
241
|
+
return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES
|
|
242
|
+
},
|
|
243
|
+
entryIndicesAfter(offset) {
|
|
244
|
+
const rows = gapRowsAt(offset, 'after')
|
|
245
|
+
return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES
|
|
246
|
+
},
|
|
247
|
+
gapBefore(offset) {
|
|
248
|
+
const rows = gapRowsAt(offset, 'before')
|
|
249
|
+
return rows ? makeGap(rows[0], rows[1]) : undefined
|
|
250
|
+
},
|
|
251
|
+
gapAfter(offset) {
|
|
252
|
+
const rows = gapRowsAt(offset, 'after')
|
|
253
|
+
return rows ? makeGap(rows[0], rows[1]) : undefined
|
|
254
|
+
},
|
|
255
|
+
gaps() {
|
|
256
|
+
return getMaps().gaps
|
|
257
|
+
},
|
|
258
|
+
gapsWithKind(kind) {
|
|
259
|
+
const kinds = typeof kind === 'string' ? [kind] : kind
|
|
260
|
+
const matches: RootTriviaGap[] = []
|
|
261
|
+
// Do not materialize the document-wide boundary maps for a sparse query.
|
|
262
|
+
// The primary consumer selects comments: it needs only comment-bearing gaps,
|
|
263
|
+
// not an object/Map for every whitespace-owned boundary in the document.
|
|
264
|
+
for (let first = 0; first < length;) {
|
|
265
|
+
let last = first + 1
|
|
266
|
+
while (last < length
|
|
267
|
+
&& ownedRangeStart(last) === ownedRangeStart(first)
|
|
268
|
+
&& ownedRangeEnd(last) === ownedRangeEnd(first)) last++
|
|
269
|
+
let matched = false
|
|
270
|
+
for (let i = first; i < last && !matched; i++) {
|
|
271
|
+
const label = labels[markerKind(i)]
|
|
272
|
+
for (const kind of kinds) {
|
|
273
|
+
if (label === kind) {
|
|
274
|
+
matched = true
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (matched) matches.push(makeGap(first, last))
|
|
280
|
+
first = last
|
|
281
|
+
}
|
|
282
|
+
return matches.length === 0 ? EMPTY_GAPS : matches
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
}
|