@temelj/mdx 0.13.0 → 0.15.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/README.md +468 -82
- package/dist/character-entities.d.mts +4 -0
- package/dist/character-entities.d.mts.map +1 -0
- package/dist/character-entities.mjs +2132 -0
- package/dist/character-entities.mjs.map +1 -0
- package/dist/character-reference.d.mts +6 -0
- package/dist/character-reference.d.mts.map +1 -0
- package/dist/character-reference.mjs +18 -0
- package/dist/character-reference.mjs.map +1 -0
- package/dist/compile-analysis.d.mts +28 -0
- package/dist/compile-analysis.d.mts.map +1 -0
- package/dist/compile-analysis.mjs +74 -0
- package/dist/compile-analysis.mjs.map +1 -0
- package/dist/compile-emit.d.mts +9 -0
- package/dist/compile-emit.d.mts.map +1 -0
- package/dist/compile-emit.mjs +187 -0
- package/dist/compile-emit.mjs.map +1 -0
- package/dist/compile-esm.d.mts +34 -0
- package/dist/compile-esm.d.mts.map +1 -0
- package/dist/compile-esm.mjs +187 -0
- package/dist/compile-esm.mjs.map +1 -0
- package/dist/compile-names.d.mts +13 -0
- package/dist/compile-names.d.mts.map +1 -0
- package/dist/compile-names.mjs +32 -0
- package/dist/compile-names.mjs.map +1 -0
- package/dist/compiler.d.mts +69 -85
- package/dist/compiler.d.mts.map +1 -1
- package/dist/compiler.mjs +185 -418
- package/dist/compiler.mjs.map +1 -1
- package/dist/diagnostics.d.mts +37 -0
- package/dist/diagnostics.d.mts.map +1 -0
- package/dist/diagnostics.mjs +57 -0
- package/dist/diagnostics.mjs.map +1 -0
- package/dist/errors.d.mts +157 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +263 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/footnote-numbering.d.mts +27 -0
- package/dist/footnote-numbering.d.mts.map +1 -0
- package/dist/footnote-numbering.mjs +49 -0
- package/dist/footnote-numbering.mjs.map +1 -0
- package/dist/format-context.d.mts +46 -0
- package/dist/format-context.d.mts.map +1 -0
- package/dist/format-context.mjs +26 -0
- package/dist/format-context.mjs.map +1 -0
- package/dist/format-directive.d.mts +9 -0
- package/dist/format-directive.d.mts.map +1 -0
- package/dist/format-directive.mjs +99 -0
- package/dist/format-directive.mjs.map +1 -0
- package/dist/format-jsx.d.mts +8 -0
- package/dist/format-jsx.d.mts.map +1 -0
- package/dist/format-jsx.mjs +47 -0
- package/dist/format-jsx.mjs.map +1 -0
- package/dist/format-math.d.mts +9 -0
- package/dist/format-math.d.mts.map +1 -0
- package/dist/format-math.mjs +79 -0
- package/dist/format-math.mjs.map +1 -0
- package/dist/format-safe.d.mts +25 -0
- package/dist/format-safe.d.mts.map +1 -0
- package/dist/format-safe.mjs +366 -0
- package/dist/format-safe.mjs.map +1 -0
- package/dist/format-table.d.mts +8 -0
- package/dist/format-table.d.mts.map +1 -0
- package/dist/format-table.mjs +55 -0
- package/dist/format-table.mjs.map +1 -0
- package/dist/format-track.d.mts +18 -0
- package/dist/format-track.d.mts.map +1 -0
- package/dist/format-track.mjs +34 -0
- package/dist/format-track.mjs.map +1 -0
- package/dist/format.d.mts +7 -0
- package/dist/format.d.mts.map +1 -0
- package/dist/format.mjs +549 -0
- package/dist/format.mjs.map +1 -0
- package/dist/frontmatter.d.mts +15 -0
- package/dist/frontmatter.d.mts.map +1 -0
- package/dist/frontmatter.mjs +187 -0
- package/dist/frontmatter.mjs.map +1 -0
- package/dist/heading.d.mts +6 -0
- package/dist/heading.d.mts.map +1 -0
- package/dist/heading.mjs +19 -0
- package/dist/heading.mjs.map +1 -0
- package/dist/html-format.d.mts +32 -0
- package/dist/html-format.d.mts.map +1 -0
- package/dist/html-format.mjs +127 -0
- package/dist/html-format.mjs.map +1 -0
- package/dist/html-lower.d.mts +78 -0
- package/dist/html-lower.d.mts.map +1 -0
- package/dist/html-lower.mjs +583 -0
- package/dist/html-lower.mjs.map +1 -0
- package/dist/html-stream.d.mts +13 -0
- package/dist/html-stream.d.mts.map +1 -0
- package/dist/html-stream.mjs +32 -0
- package/dist/html-stream.mjs.map +1 -0
- package/dist/html.d.mts +120 -0
- package/dist/html.d.mts.map +1 -0
- package/dist/html.mjs +77 -0
- package/dist/html.mjs.map +1 -0
- package/dist/identifier.d.mts +4 -0
- package/dist/identifier.d.mts.map +1 -0
- package/dist/identifier.mjs +8 -0
- package/dist/identifier.mjs.map +1 -0
- package/dist/javascript-ast.d.mts +16 -0
- package/dist/javascript-ast.d.mts.map +1 -0
- package/dist/javascript-ast.mjs +39 -0
- package/dist/javascript-ast.mjs.map +1 -0
- package/dist/javascript-jsx.d.mts +24 -0
- package/dist/javascript-jsx.d.mts.map +1 -0
- package/dist/javascript-jsx.mjs +189 -0
- package/dist/javascript-jsx.mjs.map +1 -0
- package/dist/javascript.d.mts +9 -0
- package/dist/javascript.d.mts.map +1 -0
- package/dist/javascript.mjs +31 -0
- package/dist/javascript.mjs.map +1 -0
- package/dist/jsx-reference.d.mts +15 -0
- package/dist/jsx-reference.d.mts.map +1 -0
- package/dist/jsx-reference.mjs +31 -0
- package/dist/jsx-reference.mjs.map +1 -0
- package/dist/mod.d.mts +25 -8
- package/dist/mod.mjs +19 -7
- package/dist/model.d.mts +196 -0
- package/dist/model.d.mts.map +1 -0
- package/dist/model.mjs +113 -0
- package/dist/model.mjs.map +1 -0
- package/dist/parser/block-container-recognizers.d.mts +40 -0
- package/dist/parser/block-container-recognizers.d.mts.map +1 -0
- package/dist/parser/block-container-recognizers.mjs +90 -0
- package/dist/parser/block-container-recognizers.mjs.map +1 -0
- package/dist/parser/block-directive-recognizers.d.mts +22 -0
- package/dist/parser/block-directive-recognizers.d.mts.map +1 -0
- package/dist/parser/block-directive-recognizers.mjs +77 -0
- package/dist/parser/block-directive-recognizers.mjs.map +1 -0
- package/dist/parser/block-leaf-recognizers.d.mts +53 -0
- package/dist/parser/block-leaf-recognizers.d.mts.map +1 -0
- package/dist/parser/block-leaf-recognizers.mjs +386 -0
- package/dist/parser/block-leaf-recognizers.mjs.map +1 -0
- package/dist/parser/block-parser.d.mts +38 -0
- package/dist/parser/block-parser.d.mts.map +1 -0
- package/dist/parser/block-parser.mjs +895 -0
- package/dist/parser/block-parser.mjs.map +1 -0
- package/dist/parser/block-syntax.d.mts +240 -0
- package/dist/parser/block-syntax.d.mts.map +1 -0
- package/dist/parser/characters.d.mts +19 -0
- package/dist/parser/characters.d.mts.map +1 -0
- package/dist/parser/characters.mjs +52 -0
- package/dist/parser/characters.mjs.map +1 -0
- package/dist/parser/config.d.mts +16 -0
- package/dist/parser/config.d.mts.map +1 -0
- package/dist/parser/config.mjs +25 -0
- package/dist/parser/config.mjs.map +1 -0
- package/dist/parser/directive-attributes.d.mts +9 -0
- package/dist/parser/directive-attributes.d.mts.map +1 -0
- package/dist/parser/directive-attributes.mjs +24 -0
- package/dist/parser/directive-attributes.mjs.map +1 -0
- package/dist/parser/embedded-scanner.d.mts +18 -0
- package/dist/parser/embedded-scanner.d.mts.map +1 -0
- package/dist/parser/embedded-scanner.mjs +199 -0
- package/dist/parser/embedded-scanner.mjs.map +1 -0
- package/dist/parser/inline-autolink.d.mts +7 -0
- package/dist/parser/inline-autolink.d.mts.map +1 -0
- package/dist/parser/inline-autolink.mjs +42 -0
- package/dist/parser/inline-autolink.mjs.map +1 -0
- package/dist/parser/inline-character-reference.d.mts +7 -0
- package/dist/parser/inline-character-reference.d.mts.map +1 -0
- package/dist/parser/inline-character-reference.mjs +22 -0
- package/dist/parser/inline-character-reference.mjs.map +1 -0
- package/dist/parser/inline-code.d.mts +7 -0
- package/dist/parser/inline-code.d.mts.map +1 -0
- package/dist/parser/inline-code.mjs +29 -0
- package/dist/parser/inline-code.mjs.map +1 -0
- package/dist/parser/inline-directive.d.mts +9 -0
- package/dist/parser/inline-directive.d.mts.map +1 -0
- package/dist/parser/inline-directive.mjs +45 -0
- package/dist/parser/inline-directive.mjs.map +1 -0
- package/dist/parser/inline-embedded.d.mts +19 -0
- package/dist/parser/inline-embedded.d.mts.map +1 -0
- package/dist/parser/inline-embedded.mjs +92 -0
- package/dist/parser/inline-embedded.mjs.map +1 -0
- package/dist/parser/inline-emphasis.d.mts +11 -0
- package/dist/parser/inline-emphasis.d.mts.map +1 -0
- package/dist/parser/inline-emphasis.mjs +79 -0
- package/dist/parser/inline-emphasis.mjs.map +1 -0
- package/dist/parser/inline-input.d.mts +46 -0
- package/dist/parser/inline-input.d.mts.map +1 -0
- package/dist/parser/inline-input.mjs +137 -0
- package/dist/parser/inline-input.mjs.map +1 -0
- package/dist/parser/inline-link.d.mts +12 -0
- package/dist/parser/inline-link.d.mts.map +1 -0
- package/dist/parser/inline-link.mjs +187 -0
- package/dist/parser/inline-link.mjs.map +1 -0
- package/dist/parser/inline-math.d.mts +10 -0
- package/dist/parser/inline-math.d.mts.map +1 -0
- package/dist/parser/inline-math.mjs +46 -0
- package/dist/parser/inline-math.mjs.map +1 -0
- package/dist/parser/inline-output.d.mts +34 -0
- package/dist/parser/inline-output.d.mts.map +1 -0
- package/dist/parser/inline-output.mjs +62 -0
- package/dist/parser/inline-output.mjs.map +1 -0
- package/dist/parser/inline-parser.d.mts +14 -0
- package/dist/parser/inline-parser.d.mts.map +1 -0
- package/dist/parser/inline-parser.mjs +328 -0
- package/dist/parser/inline-parser.mjs.map +1 -0
- package/dist/parser/inline-pending.d.mts +23 -0
- package/dist/parser/inline-pending.d.mts.map +1 -0
- package/dist/parser/inline-pending.mjs +100 -0
- package/dist/parser/inline-pending.mjs.map +1 -0
- package/dist/parser/inline-syntax.d.mts +118 -0
- package/dist/parser/inline-syntax.d.mts.map +1 -0
- package/dist/parser/inline-syntax.mjs +1 -0
- package/dist/parser/javascript-boundary.d.mts +16 -0
- package/dist/parser/javascript-boundary.d.mts.map +1 -0
- package/dist/parser/javascript-boundary.mjs +42 -0
- package/dist/parser/javascript-boundary.mjs.map +1 -0
- package/dist/parser/javascript-scanner.d.mts +22 -0
- package/dist/parser/javascript-scanner.d.mts.map +1 -0
- package/dist/parser/javascript-scanner.mjs +122 -0
- package/dist/parser/javascript-scanner.mjs.map +1 -0
- package/dist/parser/materialize-cache.d.mts +34 -0
- package/dist/parser/materialize-cache.d.mts.map +1 -0
- package/dist/parser/materialize-cache.mjs +12 -0
- package/dist/parser/materialize-cache.mjs.map +1 -0
- package/dist/parser/materialize-jsx.d.mts +14 -0
- package/dist/parser/materialize-jsx.d.mts.map +1 -0
- package/dist/parser/materialize-jsx.mjs +76 -0
- package/dist/parser/materialize-jsx.mjs.map +1 -0
- package/dist/parser/materialize.d.mts +61 -0
- package/dist/parser/materialize.d.mts.map +1 -0
- package/dist/parser/materialize.mjs +736 -0
- package/dist/parser/materialize.mjs.map +1 -0
- package/dist/parser/recognizer.d.mts +20 -0
- package/dist/parser/recognizer.d.mts.map +1 -0
- package/dist/parser/recognizer.mjs +24 -0
- package/dist/parser/recognizer.mjs.map +1 -0
- package/dist/parser/session.d.mts +42 -0
- package/dist/parser/session.d.mts.map +1 -0
- package/dist/parser/session.mjs +89 -0
- package/dist/parser/session.mjs.map +1 -0
- package/dist/parser.d.mts +25 -0
- package/dist/parser.d.mts.map +1 -0
- package/dist/parser.mjs +37 -0
- package/dist/parser.mjs.map +1 -0
- package/dist/plugin-internal.d.mts +25 -0
- package/dist/plugin-internal.d.mts.map +1 -0
- package/dist/plugin-internal.mjs +99 -0
- package/dist/plugin-internal.mjs.map +1 -0
- package/dist/plugin.d.mts +37 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +1 -0
- package/dist/plugins/code-block-metadata.d.mts +42 -0
- package/dist/plugins/code-block-metadata.d.mts.map +1 -0
- package/dist/plugins/code-block-metadata.mjs +133 -0
- package/dist/plugins/code-block-metadata.mjs.map +1 -0
- package/dist/plugins/heading-id.d.mts +15 -0
- package/dist/plugins/heading-id.d.mts.map +1 -0
- package/dist/plugins/heading-id.mjs +45 -0
- package/dist/plugins/heading-id.mjs.map +1 -0
- package/dist/plugins/lower-heading.d.mts +16 -0
- package/dist/plugins/lower-heading.d.mts.map +1 -0
- package/dist/plugins/lower-heading.mjs +28 -0
- package/dist/plugins/lower-heading.mjs.map +1 -0
- package/dist/plugins/math-render.d.mts +26 -0
- package/dist/plugins/math-render.d.mts.map +1 -0
- package/dist/plugins/math-render.mjs +51 -0
- package/dist/plugins/math-render.mjs.map +1 -0
- package/dist/plugins/mod.d.mts +9 -5
- package/dist/plugins/mod.mjs +8 -5
- package/dist/plugins/remove-esm.d.mts +10 -0
- package/dist/plugins/remove-esm.d.mts.map +1 -0
- package/dist/plugins/remove-esm.mjs +12 -0
- package/dist/plugins/remove-esm.mjs.map +1 -0
- package/dist/plugins/shiki.d.mts +21 -0
- package/dist/plugins/shiki.d.mts.map +1 -0
- package/dist/plugins/shiki.mjs +144 -0
- package/dist/plugins/shiki.mjs.map +1 -0
- package/dist/plugins/syntax-highlight.d.mts +38 -0
- package/dist/plugins/syntax-highlight.d.mts.map +1 -0
- package/dist/plugins/syntax-highlight.mjs +106 -0
- package/dist/plugins/syntax-highlight.mjs.map +1 -0
- package/dist/processor.d.mts +60 -0
- package/dist/processor.d.mts.map +1 -0
- package/dist/processor.mjs +106 -0
- package/dist/processor.mjs.map +1 -0
- package/dist/ranges.d.mts +10 -0
- package/dist/ranges.d.mts.map +1 -0
- package/dist/ranges.mjs +22 -0
- package/dist/ranges.mjs.map +1 -0
- package/dist/source-map.d.mts +28 -0
- package/dist/source-map.d.mts.map +1 -0
- package/dist/source-map.mjs +90 -0
- package/dist/source-map.mjs.map +1 -0
- package/dist/source-text.d.mts +35 -0
- package/dist/source-text.d.mts.map +1 -0
- package/dist/source-text.mjs +132 -0
- package/dist/source-text.mjs.map +1 -0
- package/dist/source.d.mts +86 -0
- package/dist/source.d.mts.map +1 -0
- package/dist/source.mjs +264 -0
- package/dist/source.mjs.map +1 -0
- package/dist/stream-events.d.mts +59 -0
- package/dist/stream-events.d.mts.map +1 -0
- package/dist/stream-events.mjs +115 -0
- package/dist/stream-events.mjs.map +1 -0
- package/dist/stream-session.d.mts +79 -0
- package/dist/stream-session.d.mts.map +1 -0
- package/dist/stream-session.mjs +904 -0
- package/dist/stream-session.mjs.map +1 -0
- package/dist/stream-tail.d.mts +84 -0
- package/dist/stream-tail.d.mts.map +1 -0
- package/dist/stream-tail.mjs +359 -0
- package/dist/stream-tail.mjs.map +1 -0
- package/dist/stream-types.d.mts +89 -0
- package/dist/stream-types.d.mts.map +1 -0
- package/dist/stream-types.mjs +1 -0
- package/dist/stream.d.mts +28 -0
- package/dist/stream.d.mts.map +1 -0
- package/dist/stream.mjs +68 -0
- package/dist/stream.mjs.map +1 -0
- package/dist/syntax-options.d.mts +21 -0
- package/dist/syntax-options.d.mts.map +1 -0
- package/dist/syntax-options.mjs +26 -0
- package/dist/syntax-options.mjs.map +1 -0
- package/dist/traversal.d.mts +25 -0
- package/dist/traversal.d.mts.map +1 -0
- package/dist/traversal.mjs +136 -0
- package/dist/traversal.mjs.map +1 -0
- package/dist/tree.d.mts +18 -0
- package/dist/tree.d.mts.map +1 -0
- package/dist/tree.mjs +43 -0
- package/dist/tree.mjs.map +1 -0
- package/dist/unified/adapter.d.mts +24 -0
- package/dist/unified/adapter.d.mts.map +1 -0
- package/dist/unified/adapter.mjs +44 -0
- package/dist/unified/adapter.mjs.map +1 -0
- package/dist/unified/mod.d.mts +2 -0
- package/dist/unified/mod.mjs +1 -0
- package/dist/unified/runtime.d.mts +10 -0
- package/dist/unified/runtime.d.mts.map +1 -0
- package/dist/unified/runtime.mjs +115 -0
- package/dist/unified/runtime.mjs.map +1 -0
- package/dist/unified/tree-bridge.d.mts +63 -0
- package/dist/unified/tree-bridge.d.mts.map +1 -0
- package/dist/unified/tree-bridge.mjs +876 -0
- package/dist/unified/tree-bridge.mjs.map +1 -0
- package/dist/uri.d.mts +5 -0
- package/dist/uri.d.mts.map +1 -0
- package/dist/uri.mjs +32 -0
- package/dist/uri.mjs.map +1 -0
- package/package.json +56 -27
- package/dist/plugins/heading-id/plugin.d.mts +0 -17
- package/dist/plugins/heading-id/plugin.d.mts.map +0 -1
- package/dist/plugins/heading-id/plugin.mjs +0 -23
- package/dist/plugins/heading-id/plugin.mjs.map +0 -1
- package/dist/plugins/remove-imports-exports.d.mts +0 -10
- package/dist/plugins/remove-imports-exports.d.mts.map +0 -1
- package/dist/plugins/remove-imports-exports.mjs +0 -15
- package/dist/plugins/remove-imports-exports.mjs.map +0 -1
- package/dist/plugins/syntax-highlight/code-meta.d.mts +0 -22
- package/dist/plugins/syntax-highlight/code-meta.d.mts.map +0 -1
- package/dist/plugins/syntax-highlight/code-meta.mjs +0 -38
- package/dist/plugins/syntax-highlight/code-meta.mjs.map +0 -1
- package/dist/plugins/syntax-highlight/plugin.d.mts +0 -57
- package/dist/plugins/syntax-highlight/plugin.d.mts.map +0 -1
- package/dist/plugins/syntax-highlight/plugin.mjs +0 -93
- package/dist/plugins/syntax-highlight/plugin.mjs.map +0 -1
- package/dist/plugins/tree-processor/plugin.d.mts +0 -19
- package/dist/plugins/tree-processor/plugin.d.mts.map +0 -1
- package/dist/plugins/tree-processor/plugin.mjs +0 -21
- package/dist/plugins/tree-processor/plugin.mjs.map +0 -1
- package/dist/types.d.mts +0 -2
- /package/dist/{types.mjs → parser/block-syntax.mjs} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { encodeBase64 } from "@temelj/string";
|
|
2
|
+
//#region src/source-map.ts
|
|
3
|
+
/** Private generated-text builder that records original MDX locations. */
|
|
4
|
+
var CodeWriter = class {
|
|
5
|
+
chunks = [];
|
|
6
|
+
points = [];
|
|
7
|
+
column = 0;
|
|
8
|
+
line = 0;
|
|
9
|
+
size = 0;
|
|
10
|
+
sourceFile;
|
|
11
|
+
constructor(sourceFile) {
|
|
12
|
+
this.sourceFile = sourceFile;
|
|
13
|
+
}
|
|
14
|
+
get length() {
|
|
15
|
+
return this.size;
|
|
16
|
+
}
|
|
17
|
+
/** Writes generated code. `origin` maps it to a range in the source file. */
|
|
18
|
+
write(value, origin) {
|
|
19
|
+
if (value.length === 0) return;
|
|
20
|
+
if (origin !== void 0 && this.sourceFile !== void 0) {
|
|
21
|
+
const location = this.sourceFile.location(origin.start);
|
|
22
|
+
this.points.push({
|
|
23
|
+
generatedColumn: this.column,
|
|
24
|
+
generatedLine: this.line,
|
|
25
|
+
originalColumn: location.column - 1,
|
|
26
|
+
originalLine: location.line - 1
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
this.chunks.push(value);
|
|
30
|
+
this.size += value.length;
|
|
31
|
+
for (let index = 0; index < value.length; index++) if (value.charCodeAt(index) === 10) {
|
|
32
|
+
this.line++;
|
|
33
|
+
this.column = 0;
|
|
34
|
+
} else this.column++;
|
|
35
|
+
}
|
|
36
|
+
toString() {
|
|
37
|
+
return this.chunks.join("");
|
|
38
|
+
}
|
|
39
|
+
sourceMap(sourceName, outputName) {
|
|
40
|
+
const source = this.sourceFile;
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
version: 3,
|
|
43
|
+
file: outputName,
|
|
44
|
+
names: Object.freeze([]),
|
|
45
|
+
sources: Object.freeze([sourceName]),
|
|
46
|
+
sourcesContent: Object.freeze([source?.text ?? ""]),
|
|
47
|
+
mappings: encodeMappings(this.points)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function encodeMappings(points) {
|
|
52
|
+
if (points.length === 0) return "";
|
|
53
|
+
const lines = [];
|
|
54
|
+
let generatedLine = 0;
|
|
55
|
+
let previousOriginalColumn = 0;
|
|
56
|
+
let previousOriginalLine = 0;
|
|
57
|
+
let previousSource = 0;
|
|
58
|
+
let index = 0;
|
|
59
|
+
while (index < points.length) {
|
|
60
|
+
const segments = [];
|
|
61
|
+
let previousGeneratedColumn = 0;
|
|
62
|
+
while (index < points.length && points[index].generatedLine === generatedLine) {
|
|
63
|
+
const point = points[index];
|
|
64
|
+
segments.push(encodeVlq(point.generatedColumn - previousGeneratedColumn) + encodeVlq(0 - previousSource) + encodeVlq(point.originalLine - previousOriginalLine) + encodeVlq(point.originalColumn - previousOriginalColumn));
|
|
65
|
+
previousGeneratedColumn = point.generatedColumn;
|
|
66
|
+
previousSource = 0;
|
|
67
|
+
previousOriginalLine = point.originalLine;
|
|
68
|
+
previousOriginalColumn = point.originalColumn;
|
|
69
|
+
index++;
|
|
70
|
+
}
|
|
71
|
+
lines.push(segments.join(","));
|
|
72
|
+
generatedLine++;
|
|
73
|
+
}
|
|
74
|
+
return lines.join(";");
|
|
75
|
+
}
|
|
76
|
+
function encodeVlq(value) {
|
|
77
|
+
let encoded = "";
|
|
78
|
+
let remaining = value < 0 ? -value << 1 | 1 : value << 1;
|
|
79
|
+
do {
|
|
80
|
+
let digit = remaining & 31;
|
|
81
|
+
remaining >>>= 5;
|
|
82
|
+
if (remaining > 0) digit |= 32;
|
|
83
|
+
encoded += encodeBase64(Uint8Array.of(digit << 2))[0];
|
|
84
|
+
} while (remaining > 0);
|
|
85
|
+
return encoded;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { CodeWriter };
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=source-map.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-map.mjs","names":[],"sources":["../src/source-map.ts"],"sourcesContent":["import { encodeBase64 } from \"@temelj/string\";\n\nimport type { SourceRange } from \"./ranges.ts\";\nimport type { SourceFile } from \"./source.ts\";\n\nexport interface SourceMap {\n readonly file?: string;\n readonly mappings: string;\n readonly names: readonly string[];\n readonly sources: readonly string[];\n readonly sourcesContent: readonly string[];\n readonly version: 3;\n}\n\ninterface MappingPoint {\n readonly generatedColumn: number;\n readonly generatedLine: number;\n readonly originalColumn: number;\n readonly originalLine: number;\n}\n\n/** Private generated-text builder that records original MDX locations. */\nexport class CodeWriter {\n private readonly chunks: string[] = [];\n private readonly points: MappingPoint[] = [];\n private column = 0;\n private line = 0;\n private size = 0;\n private readonly sourceFile: SourceFile | undefined;\n\n public constructor(sourceFile: SourceFile | undefined) {\n this.sourceFile = sourceFile;\n }\n\n public get length(): number {\n return this.size;\n }\n\n /** Writes generated code. `origin` maps it to a range in the source file. */\n public write(value: string, origin?: SourceRange): void {\n if (value.length === 0) {\n return;\n }\n\n if (origin !== undefined && this.sourceFile !== undefined) {\n const location = this.sourceFile.location(origin.start);\n this.points.push({\n generatedColumn: this.column,\n generatedLine: this.line,\n originalColumn: location.column - 1,\n originalLine: location.line - 1,\n });\n }\n\n this.chunks.push(value);\n this.size += value.length;\n\n for (let index = 0; index < value.length; index++) {\n const code = value.charCodeAt(index);\n\n if (code === 10) {\n this.line++;\n this.column = 0;\n } else {\n this.column++;\n }\n }\n }\n\n public toString(): string {\n return this.chunks.join(\"\");\n }\n\n public sourceMap(sourceName: string, outputName?: string): SourceMap {\n const source = this.sourceFile;\n\n return Object.freeze({\n version: 3 as const,\n file: outputName,\n names: Object.freeze([]),\n sources: Object.freeze([sourceName]),\n sourcesContent: Object.freeze([source?.text ?? \"\"]),\n mappings: encodeMappings(this.points),\n });\n }\n}\n\nfunction encodeMappings(points: readonly MappingPoint[]): string {\n if (points.length === 0) {\n return \"\";\n }\n\n const lines: string[] = [];\n let generatedLine = 0;\n let previousOriginalColumn = 0;\n let previousOriginalLine = 0;\n let previousSource = 0;\n let index = 0;\n\n while (index < points.length) {\n const segments: string[] = [];\n let previousGeneratedColumn = 0;\n\n while (index < points.length && points[index].generatedLine === generatedLine) {\n const point = points[index];\n segments.push(\n encodeVlq(point.generatedColumn - previousGeneratedColumn) +\n encodeVlq(0 - previousSource) +\n encodeVlq(point.originalLine - previousOriginalLine) +\n encodeVlq(point.originalColumn - previousOriginalColumn),\n );\n previousGeneratedColumn = point.generatedColumn;\n previousSource = 0;\n previousOriginalLine = point.originalLine;\n previousOriginalColumn = point.originalColumn;\n index++;\n }\n\n lines.push(segments.join(\",\"));\n generatedLine++;\n }\n\n return lines.join(\";\");\n}\n\nfunction encodeVlq(value: number): string {\n let encoded = \"\";\n let remaining = value < 0 ? (-value << 1) | 1 : value << 1;\n\n do {\n let digit = remaining & 31;\n remaining >>>= 5;\n\n if (remaining > 0) {\n digit |= 32;\n }\n\n encoded += encodeBase64(Uint8Array.of(digit << 2))[0];\n } while (remaining > 0);\n\n return encoded;\n}\n"],"mappings":";;;AAsBA,IAAa,aAAb,MAAwB;CACtB,SAAoC,CAAC;CACrC,SAA0C,CAAC;CAC3C,SAAiB;CACjB,OAAe;CACf,OAAe;CACf;CAEA,YAAmB,YAAoC;EACrD,KAAK,aAAa;CACpB;CAEA,IAAW,SAAiB;EAC1B,OAAO,KAAK;CACd;;CAGA,MAAa,OAAe,QAA4B;EACtD,IAAI,MAAM,WAAW,GACnB;EAGF,IAAI,WAAW,KAAA,KAAa,KAAK,eAAe,KAAA,GAAW;GACzD,MAAM,WAAW,KAAK,WAAW,SAAS,OAAO,KAAK;GACtD,KAAK,OAAO,KAAK;IACf,iBAAiB,KAAK;IACtB,eAAe,KAAK;IACpB,gBAAgB,SAAS,SAAS;IAClC,cAAc,SAAS,OAAO;GAChC,CAAC;EACH;EAEA,KAAK,OAAO,KAAK,KAAK;EACtB,KAAK,QAAQ,MAAM;EAEnB,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAGxC,IAFa,MAAM,WAAW,KAEvB,MAAM,IAAI;GACf,KAAK;GACL,KAAK,SAAS;EAChB,OACE,KAAK;CAGX;CAEA,WAA0B;EACxB,OAAO,KAAK,OAAO,KAAK,EAAE;CAC5B;CAEA,UAAiB,YAAoB,YAAgC;EACnE,MAAM,SAAS,KAAK;EAEpB,OAAO,OAAO,OAAO;GACnB,SAAS;GACT,MAAM;GACN,OAAO,OAAO,OAAO,CAAC,CAAC;GACvB,SAAS,OAAO,OAAO,CAAC,UAAU,CAAC;GACnC,gBAAgB,OAAO,OAAO,CAAC,QAAQ,QAAQ,EAAE,CAAC;GAClD,UAAU,eAAe,KAAK,MAAM;EACtC,CAAC;CACH;AACF;AAEA,SAAS,eAAe,QAAyC;CAC/D,IAAI,OAAO,WAAW,GACpB,OAAO;CAGT,MAAM,QAAkB,CAAC;CACzB,IAAI,gBAAgB;CACpB,IAAI,yBAAyB;CAC7B,IAAI,uBAAuB;CAC3B,IAAI,iBAAiB;CACrB,IAAI,QAAQ;CAEZ,OAAO,QAAQ,OAAO,QAAQ;EAC5B,MAAM,WAAqB,CAAC;EAC5B,IAAI,0BAA0B;EAE9B,OAAO,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC,kBAAkB,eAAe;GAC7E,MAAM,QAAQ,OAAO;GACrB,SAAS,KACP,UAAU,MAAM,kBAAkB,uBAAuB,IACvD,UAAU,IAAI,cAAc,IAC5B,UAAU,MAAM,eAAe,oBAAoB,IACnD,UAAU,MAAM,iBAAiB,sBAAsB,CAC3D;GACA,0BAA0B,MAAM;GAChC,iBAAiB;GACjB,uBAAuB,MAAM;GAC7B,yBAAyB,MAAM;GAC/B;EACF;EAEA,MAAM,KAAK,SAAS,KAAK,GAAG,CAAC;EAC7B;CACF;CAEA,OAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,UAAU,OAAuB;CACxC,IAAI,UAAU;CACd,IAAI,YAAY,QAAQ,IAAK,CAAC,SAAS,IAAK,IAAI,SAAS;CAEzD,GAAG;EACD,IAAI,QAAQ,YAAY;EACxB,eAAe;EAEf,IAAI,YAAY,GACd,SAAS;EAGX,WAAW,aAAa,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;CACrD,SAAS,YAAY;CAErB,OAAO;AACT"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/source-text.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Immutable text in a few flat chunks, read by absolute offset. Appending makes
|
|
4
|
+
* a new text that shares the chunks before it, so a growing document is never
|
|
5
|
+
* copied whole. Chunks merge like a binary counter, so a text has O(log n)
|
|
6
|
+
* chunks and each character is copied O(log n) times in total.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SourceText {
|
|
9
|
+
#private;
|
|
10
|
+
readonly length: number;
|
|
11
|
+
/** The text has no NUL, so the parser reads it as it is. */
|
|
12
|
+
readonly plain: boolean;
|
|
13
|
+
private constructor();
|
|
14
|
+
static of(this: void, text: string): SourceText;
|
|
15
|
+
/** The text the parser reads: NUL becomes U+FFFD. */
|
|
16
|
+
grammar(): SourceText;
|
|
17
|
+
/** The same text in one chunk, for complete-document reads. */
|
|
18
|
+
flatten(): SourceText;
|
|
19
|
+
append(text: string): SourceText;
|
|
20
|
+
/** The character at `offset`, or `undefined` outside the text. */
|
|
21
|
+
char(offset: number): string | undefined;
|
|
22
|
+
slice(start: number, end?: number): string;
|
|
23
|
+
/** The first `search` at or after `from`, scanning chunk by chunk. */
|
|
24
|
+
indexOf(search: string, from?: number): number;
|
|
25
|
+
/** Finds one matching code unit without joining the chunks. The pattern must be global. */
|
|
26
|
+
findCharacter(pattern: RegExp, from: number): number;
|
|
27
|
+
/** The last `search`, scanning chunks from the end. Matches may span chunks. */
|
|
28
|
+
lastIndexOf(search: string): number;
|
|
29
|
+
startsWith(search: string, position?: number): boolean;
|
|
30
|
+
/** The whole text. A text in several chunks joins them once. */
|
|
31
|
+
toString(): string;
|
|
32
|
+
private chunkAt;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//# sourceMappingURL=source-text.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-text.d.mts","names":[],"sources":["../src/source-text.ts"],"mappings":";;;;;;;qBAMa;;WAGK;;WAEA;UAMT;SAaO,GAAG,YAAY,eAAe;;EAOrC,WAAW;;EAKX,WAAW;EAMX,OAAO,eAAe;;EA4BtB,KAAK;EAeL,MAAM,eAAe;;EA8BrB,QAAQ,gBAAgB;;EAsBxB,cAAc,SAAS,QAAQ;;EAe/B,YAAY;EAeZ,WAAW,gBAAgB;;EAK3B;UAQC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//#region src/source-text.ts
|
|
2
|
+
/**
|
|
3
|
+
* Immutable text in a few flat chunks, read by absolute offset. Appending makes
|
|
4
|
+
* a new text that shares the chunks before it, so a growing document is never
|
|
5
|
+
* copied whole. Chunks merge like a binary counter, so a text has O(log n)
|
|
6
|
+
* chunks and each character is copied O(log n) times in total.
|
|
7
|
+
*/
|
|
8
|
+
var SourceText = class SourceText {
|
|
9
|
+
#chunks;
|
|
10
|
+
#starts;
|
|
11
|
+
length;
|
|
12
|
+
/** The text has no NUL, so the parser reads it as it is. */
|
|
13
|
+
plain;
|
|
14
|
+
#joined;
|
|
15
|
+
#single;
|
|
16
|
+
/** The chunk of the last read, since reads cluster around one offset. */
|
|
17
|
+
#hint = 0;
|
|
18
|
+
constructor(chunks, starts, length, plain) {
|
|
19
|
+
this.#chunks = chunks;
|
|
20
|
+
this.#starts = starts;
|
|
21
|
+
this.#single = chunks.length === 1 ? chunks[0] : void 0;
|
|
22
|
+
this.length = length;
|
|
23
|
+
this.plain = plain;
|
|
24
|
+
}
|
|
25
|
+
static of(text) {
|
|
26
|
+
return text.length === 0 ? new SourceText([], [], 0, true) : new SourceText([text], [0], text.length, !text.includes("\0"));
|
|
27
|
+
}
|
|
28
|
+
/** The text the parser reads: NUL becomes U+FFFD. */
|
|
29
|
+
grammar() {
|
|
30
|
+
return this.plain ? this : SourceText.of(this.toString().replaceAll("\0", "�"));
|
|
31
|
+
}
|
|
32
|
+
/** The same text in one chunk, for complete-document reads. */
|
|
33
|
+
flatten() {
|
|
34
|
+
return this.#chunks.length <= 1 ? this : new SourceText([this.toString()], [0], this.length, this.plain);
|
|
35
|
+
}
|
|
36
|
+
append(text) {
|
|
37
|
+
if (text.length === 0) return this;
|
|
38
|
+
const chunks = [...this.#chunks, text];
|
|
39
|
+
const starts = [...this.#starts, this.length];
|
|
40
|
+
while (chunks.length > 1 && chunks[chunks.length - 2].length <= chunks[chunks.length - 1].length) {
|
|
41
|
+
const last = chunks[chunks.length - 1];
|
|
42
|
+
chunks.length--;
|
|
43
|
+
starts.length--;
|
|
44
|
+
chunks[chunks.length - 1] = flat(chunks[chunks.length - 1] + last);
|
|
45
|
+
}
|
|
46
|
+
return new SourceText(chunks, starts, this.length + text.length, this.plain && !text.includes("\0"));
|
|
47
|
+
}
|
|
48
|
+
/** The character at `offset`, or `undefined` outside the text. */
|
|
49
|
+
char(offset) {
|
|
50
|
+
if (this.#single !== void 0) return this.#single[offset];
|
|
51
|
+
if (offset < 0 || offset >= this.length) return;
|
|
52
|
+
const index = this.chunkAt(offset);
|
|
53
|
+
return this.#chunks[index][offset - this.#starts[index]];
|
|
54
|
+
}
|
|
55
|
+
slice(start, end = this.length) {
|
|
56
|
+
if (this.#single !== void 0) return this.#single.slice(start, end);
|
|
57
|
+
const from = Math.max(0, Math.min(start, this.length));
|
|
58
|
+
const to = Math.max(from, Math.min(end, this.length));
|
|
59
|
+
if (from === to) return "";
|
|
60
|
+
let index = this.chunkAt(from);
|
|
61
|
+
let result = "";
|
|
62
|
+
while (index < this.#chunks.length && this.#starts[index] < to) {
|
|
63
|
+
const chunk = this.#chunks[index];
|
|
64
|
+
const chunkStart = this.#starts[index];
|
|
65
|
+
result += chunk.slice(Math.max(0, from - chunkStart), Math.min(chunk.length, to - chunkStart));
|
|
66
|
+
index++;
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
/** The first `search` at or after `from`, scanning chunk by chunk. */
|
|
71
|
+
indexOf(search, from = 0) {
|
|
72
|
+
const begin = Math.max(0, from);
|
|
73
|
+
if (begin >= this.length) return search.length === 0 ? this.length : -1;
|
|
74
|
+
for (let index = this.chunkAt(begin); index < this.#chunks.length; index++) {
|
|
75
|
+
const start = Math.max(begin, this.#starts[index]);
|
|
76
|
+
const end = this.#starts[index] + this.#chunks[index].length + search.length - 1;
|
|
77
|
+
const found = this.slice(start, end).indexOf(search);
|
|
78
|
+
if (found !== -1) return start + found;
|
|
79
|
+
}
|
|
80
|
+
return -1;
|
|
81
|
+
}
|
|
82
|
+
/** Finds one matching code unit without joining the chunks. The pattern must be global. */
|
|
83
|
+
findCharacter(pattern, from) {
|
|
84
|
+
for (let index = this.chunkAt(from); index < this.#chunks.length; index++) {
|
|
85
|
+
const start = this.#starts[index];
|
|
86
|
+
pattern.lastIndex = from - start;
|
|
87
|
+
if (pattern.test(this.#chunks[index])) return start + pattern.lastIndex - 1;
|
|
88
|
+
}
|
|
89
|
+
return this.length;
|
|
90
|
+
}
|
|
91
|
+
/** The last `search`, scanning chunks from the end. Matches may span chunks. */
|
|
92
|
+
lastIndexOf(search) {
|
|
93
|
+
for (let index = this.#chunks.length - 1; index >= 0; index--) {
|
|
94
|
+
const start = this.#starts[index];
|
|
95
|
+
const from = Math.max(0, start - search.length + 1);
|
|
96
|
+
const found = this.slice(from, start + this.#chunks[index].length).lastIndexOf(search);
|
|
97
|
+
if (found !== -1) return from + found;
|
|
98
|
+
}
|
|
99
|
+
return -1;
|
|
100
|
+
}
|
|
101
|
+
startsWith(search, position = 0) {
|
|
102
|
+
return this.slice(position, position + search.length) === search;
|
|
103
|
+
}
|
|
104
|
+
/** The whole text. A text in several chunks joins them once. */
|
|
105
|
+
toString() {
|
|
106
|
+
if (this.#joined === void 0) this.#joined = this.#chunks.length === 1 ? this.#chunks[0] : flat(this.#chunks.join(""));
|
|
107
|
+
return this.#joined;
|
|
108
|
+
}
|
|
109
|
+
chunkAt(offset) {
|
|
110
|
+
const starts = this.#starts;
|
|
111
|
+
const hint = this.#hint;
|
|
112
|
+
if (starts[hint] <= offset && (starts[hint + 1] ?? Infinity) > offset) return hint;
|
|
113
|
+
let low = 0;
|
|
114
|
+
let high = starts.length - 1;
|
|
115
|
+
while (low < high) {
|
|
116
|
+
const middle = low + high + 1 >> 1;
|
|
117
|
+
if (starts[middle] <= offset) low = middle;
|
|
118
|
+
else high = middle - 1;
|
|
119
|
+
}
|
|
120
|
+
this.#hint = low;
|
|
121
|
+
return low;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
/** Reading one character makes the engine store a concatenated string flat. */
|
|
125
|
+
function flat(value) {
|
|
126
|
+
value.charCodeAt(0);
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { SourceText };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=source-text.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-text.mjs","names":[],"sources":["../src/source-text.ts"],"sourcesContent":["/**\n * Immutable text in a few flat chunks, read by absolute offset. Appending makes\n * a new text that shares the chunks before it, so a growing document is never\n * copied whole. Chunks merge like a binary counter, so a text has O(log n)\n * chunks and each character is copied O(log n) times in total.\n */\nexport class SourceText {\n readonly #chunks: readonly string[];\n readonly #starts: readonly number[];\n public readonly length: number;\n /** The text has no NUL, so the parser reads it as it is. */\n public readonly plain: boolean;\n #joined: string | undefined;\n readonly #single: string | undefined;\n /** The chunk of the last read, since reads cluster around one offset. */\n #hint = 0;\n\n private constructor(\n chunks: readonly string[],\n starts: readonly number[],\n length: number,\n plain: boolean,\n ) {\n this.#chunks = chunks;\n this.#starts = starts;\n this.#single = chunks.length === 1 ? chunks[0] : undefined;\n this.length = length;\n this.plain = plain;\n }\n\n public static of(this: void, text: string): SourceText {\n return text.length === 0\n ? new SourceText([], [], 0, true)\n : new SourceText([text], [0], text.length, !text.includes(\"\\0\"));\n }\n\n /** The text the parser reads: NUL becomes U+FFFD. */\n public grammar(): SourceText {\n return this.plain ? this : SourceText.of(this.toString().replaceAll(\"\\0\", \"\\uFFFD\"));\n }\n\n /** The same text in one chunk, for complete-document reads. */\n public flatten(): SourceText {\n return this.#chunks.length <= 1\n ? this\n : new SourceText([this.toString()], [0], this.length, this.plain);\n }\n\n public append(text: string): SourceText {\n if (text.length === 0) {\n return this;\n }\n\n const chunks = [...this.#chunks, text];\n const starts = [...this.#starts, this.length];\n\n // Like a binary counter, a chunk merges into the one before it once it is as large.\n while (\n chunks.length > 1 &&\n chunks[chunks.length - 2].length <= chunks[chunks.length - 1].length\n ) {\n const last = chunks[chunks.length - 1];\n chunks.length--;\n starts.length--;\n chunks[chunks.length - 1] = flat(chunks[chunks.length - 1] + last);\n }\n\n return new SourceText(\n chunks,\n starts,\n this.length + text.length,\n this.plain && !text.includes(\"\\0\"),\n );\n }\n\n /** The character at `offset`, or `undefined` outside the text. */\n public char(offset: number): string | undefined {\n // A complete document is one chunk, so batch parsing reads it like a string.\n if (this.#single !== undefined) {\n return this.#single[offset];\n }\n\n if (offset < 0 || offset >= this.length) {\n return undefined;\n }\n\n const index = this.chunkAt(offset);\n\n return this.#chunks[index][offset - this.#starts[index]];\n }\n\n public slice(start: number, end: number = this.length): string {\n if (this.#single !== undefined) {\n return this.#single.slice(start, end);\n }\n\n const from = Math.max(0, Math.min(start, this.length));\n const to = Math.max(from, Math.min(end, this.length));\n\n if (from === to) {\n return \"\";\n }\n\n let index = this.chunkAt(from);\n let result = \"\";\n\n // Concatenation builds a rope, so a slice costs its chunk count until it is read.\n while (index < this.#chunks.length && this.#starts[index] < to) {\n const chunk = this.#chunks[index];\n const chunkStart = this.#starts[index];\n result += chunk.slice(\n Math.max(0, from - chunkStart),\n Math.min(chunk.length, to - chunkStart),\n );\n index++;\n }\n\n return result;\n }\n\n /** The first `search` at or after `from`, scanning chunk by chunk. */\n public indexOf(search: string, from = 0): number {\n const begin = Math.max(0, from);\n\n if (begin >= this.length) {\n return search.length === 0 ? this.length : -1;\n }\n\n for (let index = this.chunkAt(begin); index < this.#chunks.length; index++) {\n const start = Math.max(begin, this.#starts[index]);\n // Reach into the next chunk, so a match across the boundary is found.\n const end = this.#starts[index] + this.#chunks[index].length + search.length - 1;\n const found = this.slice(start, end).indexOf(search);\n\n if (found !== -1) {\n return start + found;\n }\n }\n\n return -1;\n }\n\n /** Finds one matching code unit without joining the chunks. The pattern must be global. */\n public findCharacter(pattern: RegExp, from: number): number {\n for (let index = this.chunkAt(from); index < this.#chunks.length; index++) {\n const start = this.#starts[index];\n // RegExp clamps a negative cursor to zero for later chunks.\n pattern.lastIndex = from - start;\n\n if (pattern.test(this.#chunks[index])) {\n return start + pattern.lastIndex - 1;\n }\n }\n\n return this.length;\n }\n\n /** The last `search`, scanning chunks from the end. Matches may span chunks. */\n public lastIndexOf(search: string): number {\n for (let index = this.#chunks.length - 1; index >= 0; index--) {\n const start = this.#starts[index];\n // Include the end of the previous chunk, so a match across the boundary is found.\n const from = Math.max(0, start - search.length + 1);\n const found = this.slice(from, start + this.#chunks[index].length).lastIndexOf(search);\n\n if (found !== -1) {\n return from + found;\n }\n }\n\n return -1;\n }\n\n public startsWith(search: string, position = 0): boolean {\n return this.slice(position, position + search.length) === search;\n }\n\n /** The whole text. A text in several chunks joins them once. */\n public toString(): string {\n if (this.#joined === undefined) {\n this.#joined = this.#chunks.length === 1 ? this.#chunks[0] : flat(this.#chunks.join(\"\"));\n }\n\n return this.#joined;\n }\n\n private chunkAt(offset: number): number {\n const starts = this.#starts;\n const hint = this.#hint;\n\n if (starts[hint] <= offset && (starts[hint + 1] ?? Infinity) > offset) {\n return hint;\n }\n\n let low = 0;\n let high = starts.length - 1;\n\n while (low < high) {\n const middle = (low + high + 1) >> 1;\n\n if (starts[middle] <= offset) {\n low = middle;\n } else {\n high = middle - 1;\n }\n }\n\n this.#hint = low;\n\n return low;\n }\n}\n\n/** Reading one character makes the engine store a concatenated string flat. */\nfunction flat(value: string): string {\n void value.charCodeAt(0);\n\n return value;\n}\n"],"mappings":";;;;;;;AAMA,IAAa,aAAb,MAAa,WAAW;CACtB;CACA;CACA;;CAEA;CACA;CACA;;CAEA,QAAQ;CAER,YACE,QACA,QACA,QACA,OACA;EACA,KAAK,UAAU;EACf,KAAK,UAAU;EACf,KAAK,UAAU,OAAO,WAAW,IAAI,OAAO,KAAK,KAAA;EACjD,KAAK,SAAS;EACd,KAAK,QAAQ;CACf;CAEA,OAAc,GAAe,MAA0B;EACrD,OAAO,KAAK,WAAW,IACnB,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,IAC9B,IAAI,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,KAAK,SAAS,IAAI,CAAC;CACnE;;CAGA,UAA6B;EAC3B,OAAO,KAAK,QAAQ,OAAO,WAAW,GAAG,KAAK,SAAS,CAAC,CAAC,WAAW,MAAM,GAAQ,CAAC;CACrF;;CAGA,UAA6B;EAC3B,OAAO,KAAK,QAAQ,UAAU,IAC1B,OACA,IAAI,WAAW,CAAC,KAAK,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,QAAQ,KAAK,KAAK;CACpE;CAEA,OAAc,MAA0B;EACtC,IAAI,KAAK,WAAW,GAClB,OAAO;EAGT,MAAM,SAAS,CAAC,GAAG,KAAK,SAAS,IAAI;EACrC,MAAM,SAAS,CAAC,GAAG,KAAK,SAAS,KAAK,MAAM;EAG5C,OACE,OAAO,SAAS,KAChB,OAAO,OAAO,SAAS,EAAE,CAAC,UAAU,OAAO,OAAO,SAAS,EAAE,CAAC,QAC9D;GACA,MAAM,OAAO,OAAO,OAAO,SAAS;GACpC,OAAO;GACP,OAAO;GACP,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,OAAO,SAAS,KAAK,IAAI;EACnE;EAEA,OAAO,IAAI,WACT,QACA,QACA,KAAK,SAAS,KAAK,QACnB,KAAK,SAAS,CAAC,KAAK,SAAS,IAAI,CACnC;CACF;;CAGA,KAAY,QAAoC;EAE9C,IAAI,KAAK,YAAY,KAAA,GACnB,OAAO,KAAK,QAAQ;EAGtB,IAAI,SAAS,KAAK,UAAU,KAAK,QAC/B;EAGF,MAAM,QAAQ,KAAK,QAAQ,MAAM;EAEjC,OAAO,KAAK,QAAQ,MAAM,CAAC,SAAS,KAAK,QAAQ;CACnD;CAEA,MAAa,OAAe,MAAc,KAAK,QAAgB;EAC7D,IAAI,KAAK,YAAY,KAAA,GACnB,OAAO,KAAK,QAAQ,MAAM,OAAO,GAAG;EAGtC,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;EACrD,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC;EAEpD,IAAI,SAAS,IACX,OAAO;EAGT,IAAI,QAAQ,KAAK,QAAQ,IAAI;EAC7B,IAAI,SAAS;EAGb,OAAO,QAAQ,KAAK,QAAQ,UAAU,KAAK,QAAQ,SAAS,IAAI;GAC9D,MAAM,QAAQ,KAAK,QAAQ;GAC3B,MAAM,aAAa,KAAK,QAAQ;GAChC,UAAU,MAAM,MACd,KAAK,IAAI,GAAG,OAAO,UAAU,GAC7B,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,CACxC;GACA;EACF;EAEA,OAAO;CACT;;CAGA,QAAe,QAAgB,OAAO,GAAW;EAC/C,MAAM,QAAQ,KAAK,IAAI,GAAG,IAAI;EAE9B,IAAI,SAAS,KAAK,QAChB,OAAO,OAAO,WAAW,IAAI,KAAK,SAAS;EAG7C,KAAK,IAAI,QAAQ,KAAK,QAAQ,KAAK,GAAG,QAAQ,KAAK,QAAQ,QAAQ,SAAS;GAC1E,MAAM,QAAQ,KAAK,IAAI,OAAO,KAAK,QAAQ,MAAM;GAEjD,MAAM,MAAM,KAAK,QAAQ,SAAS,KAAK,QAAQ,MAAM,CAAC,SAAS,OAAO,SAAS;GAC/E,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,MAAM;GAEnD,IAAI,UAAU,IACZ,OAAO,QAAQ;EAEnB;EAEA,OAAO;CACT;;CAGA,cAAqB,SAAiB,MAAsB;EAC1D,KAAK,IAAI,QAAQ,KAAK,QAAQ,IAAI,GAAG,QAAQ,KAAK,QAAQ,QAAQ,SAAS;GACzE,MAAM,QAAQ,KAAK,QAAQ;GAE3B,QAAQ,YAAY,OAAO;GAE3B,IAAI,QAAQ,KAAK,KAAK,QAAQ,MAAM,GAClC,OAAO,QAAQ,QAAQ,YAAY;EAEvC;EAEA,OAAO,KAAK;CACd;;CAGA,YAAmB,QAAwB;EACzC,KAAK,IAAI,QAAQ,KAAK,QAAQ,SAAS,GAAG,SAAS,GAAG,SAAS;GAC7D,MAAM,QAAQ,KAAK,QAAQ;GAE3B,MAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,OAAO,SAAS,CAAC;GAClD,MAAM,QAAQ,KAAK,MAAM,MAAM,QAAQ,KAAK,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,MAAM;GAErF,IAAI,UAAU,IACZ,OAAO,OAAO;EAElB;EAEA,OAAO;CACT;CAEA,WAAkB,QAAgB,WAAW,GAAY;EACvD,OAAO,KAAK,MAAM,UAAU,WAAW,OAAO,MAAM,MAAM;CAC5D;;CAGA,WAA0B;EACxB,IAAI,KAAK,YAAY,KAAA,GACnB,KAAK,UAAU,KAAK,QAAQ,WAAW,IAAI,KAAK,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,EAAE,CAAC;EAGzF,OAAO,KAAK;CACd;CAEA,QAAgB,QAAwB;EACtC,MAAM,SAAS,KAAK;EACpB,MAAM,OAAO,KAAK;EAElB,IAAI,OAAO,SAAS,WAAW,OAAO,OAAO,MAAM,YAAY,QAC7D,OAAO;EAGT,IAAI,MAAM;EACV,IAAI,OAAO,OAAO,SAAS;EAE3B,OAAO,MAAM,MAAM;GACjB,MAAM,SAAU,MAAM,OAAO,KAAM;GAEnC,IAAI,OAAO,WAAW,QACpB,MAAM;QAEN,OAAO,SAAS;EAEpB;EAEA,KAAK,QAAQ;EAEb,OAAO;CACT;AACF;;AAGA,SAAS,KAAK,OAAuB;CACnC,MAAW,WAAW,CAAC;CAEvB,OAAO;AACT"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SourceRange } from "./ranges.mjs";
|
|
2
|
+
import { SourceText } from "./source-text.mjs";
|
|
3
|
+
//#region src/source.d.ts
|
|
4
|
+
export interface SourceFileInput {
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
readonly text: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SourceLocation {
|
|
9
|
+
readonly offset: number;
|
|
10
|
+
readonly line: number;
|
|
11
|
+
readonly column: number;
|
|
12
|
+
}
|
|
13
|
+
/** A range in a specific source file. Diagnostics carry spans because they outlive trees. */
|
|
14
|
+
export interface SourceSpan {
|
|
15
|
+
readonly file: SourceFile;
|
|
16
|
+
readonly start: number;
|
|
17
|
+
readonly end: number;
|
|
18
|
+
}
|
|
19
|
+
export interface PhysicalLine {
|
|
20
|
+
readonly line: number;
|
|
21
|
+
readonly range: SourceRange;
|
|
22
|
+
readonly content: SourceRange;
|
|
23
|
+
readonly ending: "" | "\n" | "\r" | "\r\n";
|
|
24
|
+
}
|
|
25
|
+
/** The semantic character represented by NUL in parsed source. */
|
|
26
|
+
export declare const REPLACEMENT_CHARACTER = "�";
|
|
27
|
+
export declare function semanticCharacter(value: string): string;
|
|
28
|
+
/** The column after a tab, with four-column tab stops. */
|
|
29
|
+
export declare function nextTabColumn(column: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Immutable authored source with UTF-16 offsets and exact physical line
|
|
32
|
+
* boundaries. Files from a `SourceBuffer` share text chunks and a line index,
|
|
33
|
+
* so reading a growing document never copies it whole.
|
|
34
|
+
*/
|
|
35
|
+
export declare class SourceFile {
|
|
36
|
+
#private;
|
|
37
|
+
readonly name: string;
|
|
38
|
+
constructor(input: string | SourceFileInput);
|
|
39
|
+
/** The whole text. Prefer `slice` and `length`, which do not join a growing text. */
|
|
40
|
+
get text(): string;
|
|
41
|
+
get length(): number;
|
|
42
|
+
/**
|
|
43
|
+
* Whether `other` is known to start with this file's text: both come from
|
|
44
|
+
* the same `SourceBuffer` with only appends between them.
|
|
45
|
+
*/
|
|
46
|
+
isPrefixOf(other: SourceFile): boolean;
|
|
47
|
+
location(offset: number): SourceLocation;
|
|
48
|
+
lineStart(line: number): number | undefined;
|
|
49
|
+
get lineCount(): number;
|
|
50
|
+
line(line: number): PhysicalLine | undefined;
|
|
51
|
+
lineAt(offset: number): PhysicalLine;
|
|
52
|
+
character(offset: number): string | undefined;
|
|
53
|
+
slice(range: SourceRange): string;
|
|
54
|
+
semanticSlice(range: SourceRange): string;
|
|
55
|
+
private physicalLine;
|
|
56
|
+
private table;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Source that a stream owns and changes. Appends keep one line index, so
|
|
60
|
+
* successive files do not rescan their lines; an edit starts a new generation.
|
|
61
|
+
* Each change returns an immutable `SourceFile` for that version.
|
|
62
|
+
*/
|
|
63
|
+
export declare class SourceBuffer {
|
|
64
|
+
#private;
|
|
65
|
+
constructor(input: SourceFileInput);
|
|
66
|
+
get file(): SourceFile;
|
|
67
|
+
append(text: string): SourceFile;
|
|
68
|
+
/** Replaces `start..end` with `text`. Earlier files keep their own lines. */
|
|
69
|
+
replace(start: number, end: number, text: string): SourceFile;
|
|
70
|
+
}
|
|
71
|
+
/** The chunked text behind a file, for readers that index it by offset. */
|
|
72
|
+
export declare function sourceText(file: SourceFile): SourceText;
|
|
73
|
+
/** Incremental physical-line scanner. Each appended code unit is visited once. */
|
|
74
|
+
export declare class PhysicalLineScanner {
|
|
75
|
+
#private;
|
|
76
|
+
constructor(options?: Readonly<{
|
|
77
|
+
offset?: number;
|
|
78
|
+
line?: number;
|
|
79
|
+
}>);
|
|
80
|
+
append(text: string): readonly PhysicalLine[];
|
|
81
|
+
clone(): PhysicalLineScanner;
|
|
82
|
+
complete(): readonly PhysicalLine[];
|
|
83
|
+
private emit;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//# sourceMappingURL=source.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.mts","names":[],"sources":["../src/source.ts"],"mappings":";;;iBAQiB;WACN;WACA;;iBAGM;WACN;WACA;WACA;;;iBAIM;WACN,MAAM;WACN;WACA;;iBAGM;WACN;WACA,OAAO;WACP,SAAS;WACT;;;qBAIE;wBAEG,kBAAkB;;wBAKlB,cAAc;;;;;;qBAsCjB;;WAIK;EAchB,YAAmB,gBAAgB;;MAOxB;MAIA;;;;;EAQJ,WAAW,OAAO;EAOlB,SAAS,iBAAiB;EA2B1B,UAAU;MAIN;EAMJ,KAAK,eAAe;EAIpB,OAAO,iBAAiB;EAIxB,UAAU;EAUV,MAAM,OAAO;EAcb,cAAc,OAAO;UAIpB;UAMA;;;;;;;qBA8CG;;EAMX,YAAmB,OAAO;MAMf,QAAQ;EAIZ,OAAO,eAAe;;EAQtB,QAAQ,eAAe,aAAa,eAAe;;;wBAW5C,WAAW,MAAM,aAAa;;qBA0BjC;;EAOX,YAAmB,UAAS;IAAW;IAAiB;;EAajD,OAAO,wBAAwB;EA4C/B,SAAS;EAST,qBAAqB;UAkBpB"}
|
package/dist/source.mjs
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { InputError } from "./errors.mjs";
|
|
2
|
+
import { sourceRange } from "./ranges.mjs";
|
|
3
|
+
import { SourceText } from "./source-text.mjs";
|
|
4
|
+
import { isString } from "@temelj/value";
|
|
5
|
+
//#region src/source.ts
|
|
6
|
+
/** The semantic character represented by NUL in parsed source. */
|
|
7
|
+
const REPLACEMENT_CHARACTER = "�";
|
|
8
|
+
function semanticCharacter(value) {
|
|
9
|
+
return value === "\0" ? "�" : value;
|
|
10
|
+
}
|
|
11
|
+
/** The column after a tab, with four-column tab stops. */
|
|
12
|
+
function nextTabColumn(column) {
|
|
13
|
+
return column + (4 - (column - 1) % 4);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Physical lines of a text that only grows. The files of one buffer generation
|
|
17
|
+
* share one index, which scans each character once instead of once per file.
|
|
18
|
+
*/
|
|
19
|
+
var LineIndex = class {
|
|
20
|
+
lines = [];
|
|
21
|
+
#scanner = new PhysicalLineScanner();
|
|
22
|
+
#length = 0;
|
|
23
|
+
/** Scans `text`, which extends every text scanned before, from where the last scan ended. */
|
|
24
|
+
extend(text) {
|
|
25
|
+
if (text.length > this.#length) {
|
|
26
|
+
this.lines.push(...this.#scanner.append(text.slice(this.#length)));
|
|
27
|
+
this.#length = text.length;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
let createFile;
|
|
32
|
+
let readText;
|
|
33
|
+
/**
|
|
34
|
+
* Immutable authored source with UTF-16 offsets and exact physical line
|
|
35
|
+
* boundaries. Files from a `SourceBuffer` share text chunks and a line index,
|
|
36
|
+
* so reading a growing document never copies it whole.
|
|
37
|
+
*/
|
|
38
|
+
var SourceFile = class SourceFile {
|
|
39
|
+
#source;
|
|
40
|
+
#index;
|
|
41
|
+
#table;
|
|
42
|
+
name;
|
|
43
|
+
static {
|
|
44
|
+
createFile = (name, text, index) => {
|
|
45
|
+
const file = new SourceFile({
|
|
46
|
+
name,
|
|
47
|
+
text: ""
|
|
48
|
+
});
|
|
49
|
+
file.#source = text;
|
|
50
|
+
file.#index = index;
|
|
51
|
+
return file;
|
|
52
|
+
};
|
|
53
|
+
readText = (file) => file.#source;
|
|
54
|
+
}
|
|
55
|
+
constructor(input) {
|
|
56
|
+
this.#source = SourceText.of(isString(input) ? input : input.text);
|
|
57
|
+
this.name = isString(input) ? "source.mdx" : input.name ?? "source.mdx";
|
|
58
|
+
Object.freeze(this);
|
|
59
|
+
}
|
|
60
|
+
/** The whole text. Prefer `slice` and `length`, which do not join a growing text. */
|
|
61
|
+
get text() {
|
|
62
|
+
return this.#source.toString();
|
|
63
|
+
}
|
|
64
|
+
get length() {
|
|
65
|
+
return this.#source.length;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether `other` is known to start with this file's text: both come from
|
|
69
|
+
* the same `SourceBuffer` with only appends between them.
|
|
70
|
+
*/
|
|
71
|
+
isPrefixOf(other) {
|
|
72
|
+
return this === other || this.#index !== void 0 && this.#index === other.#index && this.length <= other.length;
|
|
73
|
+
}
|
|
74
|
+
location(offset) {
|
|
75
|
+
if (!Number.isInteger(offset) || offset < 0 || offset > this.length) throw InputError.sourceOffsetOutsideFile(offset, this.length);
|
|
76
|
+
let low = 0;
|
|
77
|
+
let high = this.lineCount;
|
|
78
|
+
while (low + 1 < high) {
|
|
79
|
+
const middle = Math.floor((low + high) / 2);
|
|
80
|
+
if (this.physicalLine(middle).range.start <= offset) low = middle;
|
|
81
|
+
else high = middle;
|
|
82
|
+
}
|
|
83
|
+
let column = 1;
|
|
84
|
+
for (let index = this.physicalLine(low).range.start; index < offset; index++) column = this.#source.char(index) === " " ? nextTabColumn(column) : column + 1;
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
offset,
|
|
87
|
+
line: low + 1,
|
|
88
|
+
column
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
lineStart(line) {
|
|
92
|
+
return this.line(line)?.range.start;
|
|
93
|
+
}
|
|
94
|
+
get lineCount() {
|
|
95
|
+
const table = this.table();
|
|
96
|
+
return table.count + table.tail.length;
|
|
97
|
+
}
|
|
98
|
+
line(line) {
|
|
99
|
+
return line >= 1 && line <= this.lineCount ? this.physicalLine(line - 1) : void 0;
|
|
100
|
+
}
|
|
101
|
+
lineAt(offset) {
|
|
102
|
+
return this.physicalLine(this.location(offset).line - 1);
|
|
103
|
+
}
|
|
104
|
+
character(offset) {
|
|
105
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > this.length) throw InputError.sourceOffsetOutsideFile(offset, this.length);
|
|
106
|
+
const value = this.#source.char(offset);
|
|
107
|
+
return value === void 0 ? void 0 : semanticCharacter(value);
|
|
108
|
+
}
|
|
109
|
+
slice(range) {
|
|
110
|
+
if (!Number.isSafeInteger(range.start) || !Number.isSafeInteger(range.end) || range.start < 0 || range.end < range.start || range.end > this.length) throw InputError.replacementRangeOutsideSource(range.start, range.end, this.length);
|
|
111
|
+
return this.#source.slice(range.start, range.end);
|
|
112
|
+
}
|
|
113
|
+
semanticSlice(range) {
|
|
114
|
+
return this.slice(range).replaceAll("\0", "�");
|
|
115
|
+
}
|
|
116
|
+
physicalLine(index) {
|
|
117
|
+
const table = this.table();
|
|
118
|
+
return index < table.count ? table.complete[index] : table.tail[index - table.count];
|
|
119
|
+
}
|
|
120
|
+
table() {
|
|
121
|
+
if (this.#table !== void 0) return this.#table;
|
|
122
|
+
const index = this.#index;
|
|
123
|
+
if (index === void 0) {
|
|
124
|
+
const lines = Object.freeze(indexLines(this.text));
|
|
125
|
+
this.#table = {
|
|
126
|
+
complete: lines,
|
|
127
|
+
count: lines.length,
|
|
128
|
+
tail: []
|
|
129
|
+
};
|
|
130
|
+
return this.#table;
|
|
131
|
+
}
|
|
132
|
+
index.extend(this.#source);
|
|
133
|
+
const { lines } = index;
|
|
134
|
+
let count = lines.length;
|
|
135
|
+
while (count > 0 && lines[count - 1].range.end > this.length) count--;
|
|
136
|
+
const last = lines[count - 1];
|
|
137
|
+
const scanner = new PhysicalLineScanner({
|
|
138
|
+
offset: last?.range.end ?? 0,
|
|
139
|
+
line: (last?.line ?? 0) + 1
|
|
140
|
+
});
|
|
141
|
+
const tail = Object.freeze([...scanner.append(this.#source.slice(last?.range.end ?? 0)), ...scanner.complete()]);
|
|
142
|
+
this.#table = {
|
|
143
|
+
complete: lines,
|
|
144
|
+
count,
|
|
145
|
+
tail
|
|
146
|
+
};
|
|
147
|
+
return this.#table;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Source that a stream owns and changes. Appends keep one line index, so
|
|
152
|
+
* successive files do not rescan their lines; an edit starts a new generation.
|
|
153
|
+
* Each change returns an immutable `SourceFile` for that version.
|
|
154
|
+
*/
|
|
155
|
+
var SourceBuffer = class {
|
|
156
|
+
#name;
|
|
157
|
+
#index = new LineIndex();
|
|
158
|
+
#text;
|
|
159
|
+
#file;
|
|
160
|
+
constructor(input) {
|
|
161
|
+
this.#name = input.name ?? "source.mdx";
|
|
162
|
+
this.#text = SourceText.of(input.text);
|
|
163
|
+
this.#file = createFile(this.#name, this.#text, this.#index);
|
|
164
|
+
}
|
|
165
|
+
get file() {
|
|
166
|
+
return this.#file;
|
|
167
|
+
}
|
|
168
|
+
append(text) {
|
|
169
|
+
this.#text = this.#text.append(text);
|
|
170
|
+
this.#file = createFile(this.#name, this.#text, this.#index);
|
|
171
|
+
return this.#file;
|
|
172
|
+
}
|
|
173
|
+
/** Replaces `start..end` with `text`. Earlier files keep their own lines. */
|
|
174
|
+
replace(start, end, text) {
|
|
175
|
+
const previous = this.#text;
|
|
176
|
+
this.#text = SourceText.of(previous.slice(0, start) + text + previous.slice(end));
|
|
177
|
+
this.#index = new LineIndex();
|
|
178
|
+
this.#file = createFile(this.#name, this.#text, this.#index);
|
|
179
|
+
return this.#file;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
/** The chunked text behind a file, for readers that index it by offset. */
|
|
183
|
+
function sourceText(file) {
|
|
184
|
+
return readText(file);
|
|
185
|
+
}
|
|
186
|
+
function indexLines(source) {
|
|
187
|
+
const scanner = new PhysicalLineScanner();
|
|
188
|
+
return [...scanner.append(source), ...scanner.complete()];
|
|
189
|
+
}
|
|
190
|
+
function physicalLine(line, start, contentEnd, end, ending) {
|
|
191
|
+
return Object.freeze({
|
|
192
|
+
line,
|
|
193
|
+
range: sourceRange(start, end),
|
|
194
|
+
content: sourceRange(start, contentEnd),
|
|
195
|
+
ending
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** Incremental physical-line scanner. Each appended code unit is visited once. */
|
|
199
|
+
var PhysicalLineScanner = class PhysicalLineScanner {
|
|
200
|
+
#complete = false;
|
|
201
|
+
#length;
|
|
202
|
+
#line;
|
|
203
|
+
#lineStart;
|
|
204
|
+
#pendingCarriageReturn;
|
|
205
|
+
constructor(options = {}) {
|
|
206
|
+
const offset = options.offset ?? 0;
|
|
207
|
+
const line = options.line ?? 1;
|
|
208
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(line) || line < 1) throw new RangeError("Invalid physical-line scanner origin");
|
|
209
|
+
this.#length = offset;
|
|
210
|
+
this.#lineStart = offset;
|
|
211
|
+
this.#line = line;
|
|
212
|
+
}
|
|
213
|
+
append(text) {
|
|
214
|
+
if (this.#complete) throw new Error("Cannot append to a completed physical-line scanner");
|
|
215
|
+
const lines = [];
|
|
216
|
+
const endings = /\r\n|[\r\n]/gu;
|
|
217
|
+
if (text.length > 0 && this.#pendingCarriageReturn !== void 0) {
|
|
218
|
+
const offset = this.#pendingCarriageReturn;
|
|
219
|
+
if (text[0] === "\n") {
|
|
220
|
+
lines.push(this.emit(offset, this.#length + 1, "\r\n"));
|
|
221
|
+
endings.lastIndex = 1;
|
|
222
|
+
} else lines.push(this.emit(offset, offset + 1, "\r"));
|
|
223
|
+
this.#pendingCarriageReturn = void 0;
|
|
224
|
+
}
|
|
225
|
+
for (let match = endings.exec(text); match !== null; match = endings.exec(text)) {
|
|
226
|
+
const offset = this.#length + match.index;
|
|
227
|
+
const ending = match[0];
|
|
228
|
+
if (ending === "\r" && match.index === text.length - 1) this.#pendingCarriageReturn = offset;
|
|
229
|
+
else lines.push(this.emit(offset, offset + ending.length, ending === "\r\n" ? "\r\n" : ending === "\r" ? "\r" : "\n"));
|
|
230
|
+
}
|
|
231
|
+
this.#length += text.length;
|
|
232
|
+
return Object.freeze(lines);
|
|
233
|
+
}
|
|
234
|
+
clone() {
|
|
235
|
+
const copy = new PhysicalLineScanner({
|
|
236
|
+
offset: this.#length,
|
|
237
|
+
line: this.#line
|
|
238
|
+
});
|
|
239
|
+
copy.#complete = this.#complete;
|
|
240
|
+
copy.#lineStart = this.#lineStart;
|
|
241
|
+
copy.#pendingCarriageReturn = this.#pendingCarriageReturn;
|
|
242
|
+
return copy;
|
|
243
|
+
}
|
|
244
|
+
complete() {
|
|
245
|
+
if (this.#complete) throw new Error("Physical-line scanner is already complete");
|
|
246
|
+
this.#complete = true;
|
|
247
|
+
const lines = [];
|
|
248
|
+
if (this.#pendingCarriageReturn !== void 0) {
|
|
249
|
+
lines.push(this.emit(this.#pendingCarriageReturn, this.#pendingCarriageReturn + 1, "\r"));
|
|
250
|
+
this.#pendingCarriageReturn = void 0;
|
|
251
|
+
}
|
|
252
|
+
lines.push(physicalLine(this.#line, this.#lineStart, this.#length, this.#length, ""));
|
|
253
|
+
return Object.freeze(lines);
|
|
254
|
+
}
|
|
255
|
+
emit(contentEnd, end, ending) {
|
|
256
|
+
const line = physicalLine(this.#line++, this.#lineStart, contentEnd, end, ending);
|
|
257
|
+
this.#lineStart = end;
|
|
258
|
+
return line;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
//#endregion
|
|
262
|
+
export { PhysicalLineScanner, REPLACEMENT_CHARACTER, SourceBuffer, SourceFile, nextTabColumn, semanticCharacter, sourceText };
|
|
263
|
+
|
|
264
|
+
//# sourceMappingURL=source.mjs.map
|