fix(highlight): vendor pure-Lezer Nix and C# parsers
The Nix grammar package imports @codemirror/autocomplete at the top level for its keyword snippets, but only declares it as a peer dependency. electron-builder packs node_modules by walking declared production dependencies, so it omitted the package from app.asar and the desktop daemon died on startup with ERR_MODULE_NOT_FOUND. Dev and CI never saw it because the workspace hoists everything. This is the same failure that just shipped for Svelte, and the previous fix removed @codemirror/autocomplete on the assumption Svelte was its only consumer. Vendoring both remaining grammars removes the class of bug rather than the current instance: these packages are interactive editor extensions, so importing one drags a DOM editor runtime into a headless token highlighter. The vendored language modules keep upstream's indent and fold props. The web file editor consumes getLanguageForFile().extension, so dropping them would have silently degraded editing for .nix and .cs. The new dependency-closure test replicates electron-builder's traversal statically and would have failed both PRs. It is scoped to @getpaseo/highlight because that tree is small and pure; the same walk over the server tree is dominated by false positives from optional dependencies loaded behind try/catch.
This commit is contained in:
@@ -124,6 +124,16 @@ PASEO_DESKTOP_SMOKE_ARTIFACT_DIR=/tmp/paseo-desktop-smoke \
|
||||
npm run build:desktop -- --publish never --linux --x64 --dir
|
||||
```
|
||||
|
||||
### Undeclared peer dependencies break app.asar
|
||||
|
||||
electron-builder packs `node_modules` by walking declared production `dependencies`. A package that imports something it only lists as a `peerDependency` resolves fine in this hoisted workspace, passes every test, and then throws `ERR_MODULE_NOT_FOUND` inside `app.asar` — killing the desktop daemon at startup. That shipped twice from `@replit/codemirror-lang-*` grammars, which are interactive editor extensions published as if they were bare parsers.
|
||||
|
||||
The packaged smoke catches it but only runs when a PR touches the `desktop` filter in `.github/ci-paths.yml`. Both offenders landed under `packages/highlight/**`, which maps to `sdk`.
|
||||
|
||||
`packages/highlight/src/__tests__/dependency-closure.test.ts` replicates the packer's traversal statically and runs with the normal unit tests. It is scoped to `@getpaseo/highlight` on purpose: that tree is small and pure, so the check is exact. Running the same walk over `@getpaseo/server` produces dozens of false positives from optional dependencies loaded behind `try`/`catch`.
|
||||
|
||||
Prefer a `@lezer/*` grammar. When a language only ships inside an editor extension, vendor the grammar into `packages/highlight/src/<lang>/` — see `svelte/`, `nix/`, and `csharp/`.
|
||||
|
||||
### Desktop browser regression
|
||||
|
||||
The desktop browser E2E launches an isolated real daemon, Metro, and Electron app. It forces workspace LRU eviction to reparent the original tab and replace its guest `WebContents`, then makes one MCP call each for tab listing, snapshot, and click against that original browser id. A final MCP wait proves the real target page received the click.
|
||||
|
||||
Generated
-45
@@ -2549,19 +2549,6 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz",
|
||||
"integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz",
|
||||
@@ -11140,36 +11127,6 @@
|
||||
"nanoid": "^3.3.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-lang-csharp": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-lang-csharp/-/codemirror-lang-csharp-6.2.0.tgz",
|
||||
"integrity": "sha512-6utbaWkoymhoAXj051mkRp+VIJlpwUgCX9Toevz3YatiZsz512fw3OVCedXQx+WcR0wb6zVHjChnuxqfCLtFVQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-lang-nix": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-lang-nix/-/codemirror-lang-nix-6.0.1.tgz",
|
||||
"integrity": "sha512-lvzjoYn9nfJzBD5qdm3Ut6G3+Or2wEacYIDJ49h9+19WSChVnxv4ojf+rNmQ78ncuxIt/bfbMvDLMeMP0xze6g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-vim": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.3.0.tgz",
|
||||
@@ -38660,8 +38617,6 @@
|
||||
"@lezer/rust": "^1.0.2",
|
||||
"@lezer/xml": "^1.0.6",
|
||||
"@lezer/yaml": "^1.0.4",
|
||||
"@replit/codemirror-lang-csharp": "^6.2.0",
|
||||
"@replit/codemirror-lang-nix": "^6.0.1",
|
||||
"lezer-elixir": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -45,8 +45,6 @@
|
||||
"@lezer/rust": "^1.0.2",
|
||||
"@lezer/xml": "^1.0.6",
|
||||
"@lezer/yaml": "^1.0.4",
|
||||
"@replit/codemirror-lang-csharp": "^6.2.0",
|
||||
"@replit/codemirror-lang-nix": "^6.0.1",
|
||||
"lezer-elixir": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
import { builtinModules } from "node:module";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// electron-builder packs node_modules by walking declared production
|
||||
// `dependencies` only. A package that imports something it lists as a peer
|
||||
// dependency resolves fine in this hoisted workspace and then throws
|
||||
// ERR_MODULE_NOT_FOUND inside app.asar, taking the desktop daemon down at
|
||||
// startup. That shipped twice from @replit/codemirror-lang-* grammars, which
|
||||
// are editor extensions published as if they were bare parsers.
|
||||
//
|
||||
// This replicates the packer's traversal statically: build the closure of
|
||||
// declared dependencies reachable from @getpaseo/highlight, walk each package's
|
||||
// real entry points, and assert every bare specifier they reach lands inside
|
||||
// the closure.
|
||||
|
||||
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const repoRoot = path.resolve(packageRoot, "../..");
|
||||
|
||||
const builtins = new Set(builtinModules);
|
||||
|
||||
interface Manifest {
|
||||
dependencies?: Record<string, string>;
|
||||
main?: string;
|
||||
module?: string;
|
||||
exports?: unknown;
|
||||
}
|
||||
|
||||
function packageNameOf(specifier: string): string {
|
||||
const parts = specifier.split("/");
|
||||
return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
||||
}
|
||||
|
||||
function resolvePackageDir(name: string): string | null {
|
||||
for (const base of [packageRoot, repoRoot]) {
|
||||
const dir = path.join(base, "node_modules", name);
|
||||
if (fs.existsSync(path.join(dir, "package.json"))) return dir;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function readManifest(dir: string): Manifest {
|
||||
return JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
|
||||
}
|
||||
|
||||
function collectClosure(): Map<string, string> {
|
||||
const closure = new Map<string, string>();
|
||||
const queue = Object.keys(readManifest(packageRoot).dependencies ?? {});
|
||||
|
||||
while (queue.length > 0) {
|
||||
const name = queue.shift() as string;
|
||||
if (closure.has(name)) continue;
|
||||
const dir = resolvePackageDir(name);
|
||||
expect(dir, `${name} is declared as a dependency but is not installed`).not.toBeNull();
|
||||
closure.set(name, dir as string);
|
||||
queue.push(...Object.keys(readManifest(dir as string).dependencies ?? {}));
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
/** Every string leaf of an `exports` map is a file the package can be entered through. */
|
||||
function collectExportTargets(node: unknown, into: string[]): void {
|
||||
if (typeof node === "string") {
|
||||
into.push(node);
|
||||
} else if (Array.isArray(node)) {
|
||||
for (const child of node) collectExportTargets(child, into);
|
||||
} else if (node && typeof node === "object") {
|
||||
for (const child of Object.values(node)) collectExportTargets(child, into);
|
||||
}
|
||||
}
|
||||
|
||||
function entryFiles(dir: string, manifest: Manifest): string[] {
|
||||
const targets: string[] = [];
|
||||
collectExportTargets(manifest.exports, targets);
|
||||
if (manifest.main) targets.push(manifest.main);
|
||||
if (manifest.module) targets.push(manifest.module);
|
||||
if (targets.length === 0) targets.push("index.js");
|
||||
|
||||
return targets
|
||||
.filter((target) => !target.includes("*"))
|
||||
.map((target) => resolveFile(path.resolve(dir, target)))
|
||||
.filter((file): file is string => file !== null);
|
||||
}
|
||||
|
||||
/** Node's file resolution, minus the parts no dependency here relies on. */
|
||||
function resolveFile(candidate: string): string | null {
|
||||
const attempts = [
|
||||
candidate,
|
||||
`${candidate}.js`,
|
||||
`${candidate}.mjs`,
|
||||
`${candidate}.cjs`,
|
||||
path.join(candidate, "index.js"),
|
||||
];
|
||||
for (const attempt of attempts) {
|
||||
if (fs.existsSync(attempt) && fs.statSync(attempt).isFile()) return attempt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Specifiers never contain whitespace, which keeps `from`/`import` inside
|
||||
// grammar strings (styleTags keys list keywords) from matching.
|
||||
const IMPORT_PATTERN =
|
||||
/(?:\bfrom\s*|\bimport\s*|\brequire\s*\(\s*|\bimport\s*\(\s*)["']([^"'\s]+)["']/g;
|
||||
|
||||
/** Bare specifiers reachable from a package's entry points, following its own relative imports. */
|
||||
function reachableImports(dir: string, manifest: Manifest): Map<string, string> {
|
||||
const bare = new Map<string, string>();
|
||||
const seen = new Set<string>();
|
||||
const queue = entryFiles(dir, manifest);
|
||||
|
||||
while (queue.length > 0) {
|
||||
const file = queue.shift() as string;
|
||||
if (seen.has(file)) continue;
|
||||
seen.add(file);
|
||||
|
||||
const source = fs.readFileSync(file, "utf8");
|
||||
for (const match of source.matchAll(IMPORT_PATTERN)) {
|
||||
const specifier = match[1];
|
||||
if (specifier.startsWith(".")) {
|
||||
const target = resolveFile(path.resolve(path.dirname(file), specifier));
|
||||
if (target) queue.push(target);
|
||||
} else if (!specifier.startsWith("node:") && !builtins.has(specifier)) {
|
||||
bare.set(packageNameOf(specifier), path.relative(repoRoot, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bare;
|
||||
}
|
||||
|
||||
function collectSourceFiles(dir: string): string[] {
|
||||
const files: string[] = [];
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
// Tests import devDependencies, which are never packaged.
|
||||
if (entry.name === "__tests__") continue;
|
||||
files.push(...collectSourceFiles(full));
|
||||
} else if (entry.name.endsWith(".ts")) {
|
||||
files.push(full);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
describe("packaged dependency closure", () => {
|
||||
const closure = collectClosure();
|
||||
|
||||
it("resolves every declared dependency", () => {
|
||||
expect(closure.size).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("imports nothing this package does not declare", () => {
|
||||
const undeclared = new Map<string, string>();
|
||||
|
||||
for (const file of collectSourceFiles(path.join(packageRoot, "src"))) {
|
||||
for (const match of fs.readFileSync(file, "utf8").matchAll(IMPORT_PATTERN)) {
|
||||
const specifier = match[1];
|
||||
if (specifier.startsWith(".") || specifier.startsWith("node:")) continue;
|
||||
if (builtins.has(specifier)) continue;
|
||||
const name = packageNameOf(specifier);
|
||||
if (!closure.has(name)) undeclared.set(name, path.relative(repoRoot, file));
|
||||
}
|
||||
}
|
||||
|
||||
expect([...undeclared].map(([name, file]) => `${name} (imported by ${file})`)).toEqual([]);
|
||||
});
|
||||
|
||||
it.each([...closure.keys()].sort())("%s imports nothing outside the closure", (name) => {
|
||||
const dir = closure.get(name) as string;
|
||||
const undeclared = [...reachableImports(dir, readManifest(dir))]
|
||||
.filter(([specifier]) => specifier !== name && !closure.has(specifier))
|
||||
.map(([specifier, file]) => `${specifier} (imported by ${file})`);
|
||||
|
||||
expect(
|
||||
undeclared,
|
||||
`${name} imports packages it does not declare as dependencies; electron-builder omits those from app.asar`,
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { styleTags, tags as t } from "@lezer/highlight";
|
||||
|
||||
export const csharpHighlighting = styleTags({
|
||||
"Keyword ContextualKeyword SimpleType": t.keyword,
|
||||
"NullLiteral BooleanLiteral": t.bool,
|
||||
IntegerLiteral: t.integer,
|
||||
RealLiteral: t.float,
|
||||
'StringLiteral CharacterLiteral InterpolatedRegularString InterpolatedVerbatimString $" @$" $@"':
|
||||
t.string,
|
||||
"LineComment BlockComment": t.comment,
|
||||
". .. : Astrisk Slash % + - ++ -- Not ~ << & | ^ && || < > <= >= == NotEq = += -= *= SlashEq %= &= |= ^= ? ?? ??= =>":
|
||||
t.operator,
|
||||
PP_Directive: t.keyword,
|
||||
TypeIdentifier: t.typeName,
|
||||
"ArgumentName AttrsNamedArg": t.variableName,
|
||||
ConstName: t.constant(t.variableName),
|
||||
MethodName: t.function(t.variableName),
|
||||
ParamName: [t.emphasis, t.variableName],
|
||||
VarName: t.variableName,
|
||||
"FieldName PropertyName": t.propertyName,
|
||||
"( )": t.paren,
|
||||
"{ }": t.brace,
|
||||
"[ ]": t.squareBracket,
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
continuedIndent,
|
||||
foldInside,
|
||||
foldNodeProp,
|
||||
indentNodeProp,
|
||||
LRLanguage,
|
||||
} from "@codemirror/language";
|
||||
import { parser } from "./parser.js";
|
||||
|
||||
export const csharpLanguage = LRLanguage.define({
|
||||
name: "C#",
|
||||
parser: parser.configure({
|
||||
props: [
|
||||
indentNodeProp.add({
|
||||
Delim: continuedIndent({ except: /^\s*(?:case\b|default:)/ }),
|
||||
}),
|
||||
foldNodeProp.add({
|
||||
Delim: foldInside,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
languageData: {
|
||||
commentTokens: { line: "//", block: { open: "/*", close: "*/" } },
|
||||
closeBrackets: { brackets: ["(", "[", "{", '"', "'"] },
|
||||
indentOnInput: /^\s*((\)|\]|\})$|(else|else\s+if|catch|finally|case)\b|default:)/,
|
||||
},
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
// Generated by lezer-generator, taken from @replit/codemirror-lang-csharp@6.2.0.
|
||||
// Vendored so the daemon never loads the editor-only runtime those packages
|
||||
// pull in at import time (@codemirror/autocomplete, view, state). Only pure
|
||||
// Lezer primitives are used here. See packages/highlight/src/svelte for the
|
||||
// same treatment applied to Svelte.
|
||||
export const interpStringContent = 296;
|
||||
export const interpStringBrace = 297;
|
||||
export const interpStringEnd = 298;
|
||||
export const interpVStringContent = 299;
|
||||
export const interpVStringBrace = 300;
|
||||
export const interpVStringEnd = 301;
|
||||
@@ -0,0 +1,69 @@
|
||||
// Generated by lezer-generator, taken from @replit/codemirror-lang-csharp@6.2.0.
|
||||
// Vendored so the daemon never loads the editor-only runtime those packages
|
||||
// pull in at import time (@codemirror/autocomplete, view, state). Only pure
|
||||
// Lezer primitives are used here. See packages/highlight/src/svelte for the
|
||||
// same treatment applied to Svelte.
|
||||
import { ExternalTokenizer } from "@lezer/lr";
|
||||
import {
|
||||
interpStringBrace,
|
||||
interpStringContent,
|
||||
interpStringEnd,
|
||||
interpVStringBrace,
|
||||
interpVStringContent,
|
||||
interpVStringEnd,
|
||||
} from "./terms.js";
|
||||
|
||||
const quote = 34,
|
||||
backslash = 92,
|
||||
braceL = 123,
|
||||
braceR = 125;
|
||||
export const interpString = new ExternalTokenizer((input) => {
|
||||
for (let i = 0; ; i++) {
|
||||
switch (input.next) {
|
||||
case -1:
|
||||
if (i > 0) input.acceptToken(interpStringContent);
|
||||
return;
|
||||
case quote:
|
||||
if (i > 0) input.acceptToken(interpStringContent);
|
||||
else input.acceptToken(interpStringEnd, 1);
|
||||
return;
|
||||
case braceL:
|
||||
if (input.peek(1) === braceL) input.acceptToken(interpStringContent, 2);
|
||||
else input.acceptToken(interpStringBrace);
|
||||
return;
|
||||
case braceR:
|
||||
if (input.peek(1) === braceR) input.acceptToken(interpStringContent, 2);
|
||||
return;
|
||||
case backslash:
|
||||
const next = input.peek(1);
|
||||
if (next === braceL || next === braceR) return;
|
||||
input.advance();
|
||||
// FALLTHROUGH
|
||||
default:
|
||||
input.advance();
|
||||
}
|
||||
}
|
||||
});
|
||||
export const interpVString = new ExternalTokenizer((input) => {
|
||||
for (let i = 0; ; i++) {
|
||||
switch (input.next) {
|
||||
case -1:
|
||||
if (i > 0) input.acceptToken(interpVStringContent);
|
||||
return;
|
||||
case quote:
|
||||
if (input.peek(1) === quote) input.acceptToken(interpVStringContent, 2);
|
||||
else if (i > 0) input.acceptToken(interpVStringContent);
|
||||
else input.acceptToken(interpVStringEnd, 1);
|
||||
return;
|
||||
case braceL:
|
||||
if (input.peek(1) === braceL) input.acceptToken(interpVStringContent, 2);
|
||||
else input.acceptToken(interpVStringBrace);
|
||||
return;
|
||||
case braceR:
|
||||
if (input.peek(1) === braceR) input.acceptToken(interpVStringContent, 2);
|
||||
return;
|
||||
default:
|
||||
input.advance();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { styleTags, tags as t } from "@lezer/highlight";
|
||||
|
||||
export const nixHighlighting = styleTags({
|
||||
Identifier: t.propertyName,
|
||||
Boolean: t.bool,
|
||||
String: t.string,
|
||||
IndentedString: t.string,
|
||||
LineComment: t.lineComment,
|
||||
BlockComment: t.blockComment,
|
||||
Float: t.float,
|
||||
Integer: t.integer,
|
||||
Null: t.null,
|
||||
URI: t.url,
|
||||
SPath: t.literal,
|
||||
Path: t.literal,
|
||||
"( )": t.paren,
|
||||
"{ }": t.brace,
|
||||
"[ ]": t.squareBracket,
|
||||
"if then else": t.controlKeyword,
|
||||
"import with let in rec builtins inherit assert or": t.keyword,
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
continuedIndent,
|
||||
delimitedIndent,
|
||||
foldInside,
|
||||
foldNodeProp,
|
||||
indentNodeProp,
|
||||
LRLanguage,
|
||||
} from "@codemirror/language";
|
||||
import { parser } from "./parser.js";
|
||||
|
||||
export const nixLanguage = LRLanguage.define({
|
||||
name: "Nix",
|
||||
parser: parser.configure({
|
||||
props: [
|
||||
indentNodeProp.add({
|
||||
Parenthesized: delimitedIndent({ closing: ")" }),
|
||||
AttrSet: delimitedIndent({ closing: "}" }),
|
||||
List: delimitedIndent({ closing: "]" }),
|
||||
Let: continuedIndent({ except: /^\s*in\b/ }),
|
||||
}),
|
||||
foldNodeProp.add({
|
||||
AttrSet: foldInside,
|
||||
List: foldInside,
|
||||
Let(node) {
|
||||
const first = node.getChild("let");
|
||||
const last = node.getChild("in");
|
||||
if (!first || !last) return null;
|
||||
return { from: first.to, to: last.from };
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
languageData: {
|
||||
commentTokens: { line: "#", block: { open: "/*", close: "*/" } },
|
||||
closeBrackets: { brackets: ["(", "[", "{", "''", '"'] },
|
||||
indentOnInput: /^\s*(in|\}|\)|\])$/,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
// Generated by lezer-generator, taken from @replit/codemirror-lang-nix@6.0.1.
|
||||
// Vendored so the daemon never loads the editor-only runtime those packages
|
||||
// pull in at import time (@codemirror/autocomplete, view, state). Only pure
|
||||
// Lezer primitives are used here. See packages/highlight/src/svelte for the
|
||||
// same treatment applied to Svelte.
|
||||
import { LRParser } from "@lezer/lr";
|
||||
import { nixHighlighting } from "./highlight.js";
|
||||
import { scanIndString, scanString } from "./tokens.js";
|
||||
|
||||
const spec_Identifier = new Map<string, number>([
|
||||
["assert", 22],
|
||||
["with", 26],
|
||||
["let", 30],
|
||||
["inherit", 42],
|
||||
["in", 48],
|
||||
["if", 52],
|
||||
["then", 54],
|
||||
["else", 56],
|
||||
["builtins", 70],
|
||||
["null", 214],
|
||||
["true", 216],
|
||||
["false", 216],
|
||||
["rec", 100],
|
||||
["or", 108],
|
||||
]);
|
||||
export const parser = /*@__PURE__*/ LRParser.deserialize({
|
||||
version: 14,
|
||||
states:
|
||||
"7QO]QSOOO!sQWO'#DyO#XQ`O'#EjO&QQSO'#C`O&YQTO'#CnO'lQWO'#EWO(VQSO'#C|O(VQSO'#C|OOQO'#DQ'#DQOOQO'#DT'#DTO)dQUO'#DUO*yQSO'#DcOOQO'#Ej'#EjO,XQ`O'#EiOOQO'#Ei'#EiO-wQ`O'#EXOOQO'#Eh'#EhOOQO'#EX'#EXOOQO'#EW'#EWOOQO'#Dw'#DwO]QSO'#CfO]QSO'#ChO/dQSO'#D^O]QSO'#CuO]QSO'#D[O/xQSO'#D_QOQSOOO/}QSO'#CdO0`Q`O,5:eO3XQSO,5:eO3aQSO,5:eO4sQSO'#EOOOQO'#Cm'#CmOOQO'#Df'#DfO4}QSO,59wO]QSO'#CpO5`QSO'#ClO5eQSO'#EUO]QSO,58zO5sQSO,58zO5xQSO,58zOOQP'#EQ'#EQOOQP'#Dg'#DgO5}QTO,59YOOQO,59Y,59YO]QSO'#CoO6]QSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO(VQSO,59eO6|QWO,59hO8VQSO'#D]O/xQSO'#D^OOQO,59h,59hOOQQ'#En'#EnOOQQ'#Dj'#DjO8hQUO,59pOOQO,59p,59pO]QSO'#DVOOQO'#Dk'#DkO8vQSO,59}OOQO,59},59}O8}QSO'#EiO6]QSO,59jOOQO,59i,59iO9XQSO,59QO9^QSO,59SO9cQSO,59UO]QSO,59UOOQO,59x,59xO9tQSO,59aO9yQSO,59vOOQO,59y,59yO:OQSO'#DhO;hQSO,5:jO]QSO,59OO;rQWO1G0PO;zQSO1G0POOQO1G0P1G0POOQO-E7d-E7dOOQO1G/c1G/cO<SQSO,59[O]QSO,59WOOQO'#Di'#DiO<XQSO,5:pO]QSO,5:qOOQO1G.f1G.fO<gQWO'#DyO<rQSO1G.fOOQP-E7e-E7eOOQO1G.t1G.tO<wQSO,59ZO<|Q`O'#EOOOQO1G/P1G/PO@PQWO1G/PO@WQWO1G/POAuQWO1G/POA|QWO1G/POBTQWO1G/POC^QWO1G/PODgQWO1G/POEpQWO1G/POFyQWO1G/POHSQWO1G/POI]QWO1G/POJfQWO1G/POKoQWO1G/POOQO,59w,59wOOQQ-E7h-E7hOOQO1G/[1G/[OLYQSO,59qOOQO-E7i-E7iOOQO1G/i1G/iOMfQ`O1G/UO]QSO1G.lO]QSO1G.nO]QSO1G.pOOQO1G.p1G.pO]QSO1G.{OOQO1G/b1G/bOOQO,5:S,5:SOOQO-E7f-E7fOOQO1G.j1G.jO! UQSO'#CdOOQO,5:P,5:PO! aQSO7+%kO! fQWO7+%kOOQO-E7c-E7cOOQO7+%k7+%kOOQO1G.v1G.vO! nQSO1G.rOOQO-E7g-E7gOOQO1G0[1G0[O! sQSO1G0]OOQO,5:e,5:eO]QSO7+$QOOQP1G.u1G.uO! xQ`O,5:jOOQQ1G/]1G/]O!#eQSO1G/UO)rQSO7+$pOOQO7+$W7+$WOOQO7+$Y7+$YOOQO7+$[7+$[O!#oQSO7+$gOOQO<<IV<<IVO!#tQSO<<IVP!#yQSO'#DeOOQO7+$^7+$^O!$OQSO7+%wOOQO<<Gl<<GlOOQO<<H[<<H[O]QSO<<HROOQOAN>qAN>qO!$ZQSO<<IcOOQOAN=mAN=mOOQOAN>}AN>}O!$iQSO,59jO)rQSO7+$p",
|
||||
stateData:
|
||||
"!$x~O!jOSPOSQOS~OTQOUPOZdO]eO_fOfhOjgOs[Ou[Ov[Oz[O{[O|[O}[O!SiO!UZO!sSO#QVO#ZUO#_WO#`XO#aYO~OTkOVlOXnOeuO!sSO!usO~O!lvO!pwOT#^XU#^X_#^Xf#^Xn#^Xo#^Xs#^Xu#^Xv#^Xz#^X{#^X|#^X}#^X!S#^X!U#^X!`#^X!n#^X!s#^X!v#^X!|#^X!}#^X#O#^X#P#^X#Q#^X#R#^X#S#^X#T#^X#U#^X#V#^X#W#^X#X#^X#Y#^X#_#^X#`#^X#a#^X!q#^Xk#^Xg#^XV#^X!o#^Xl#^X~O!lvO!pxO~O!ayO!b}O!c|O!dyO~On!TOo!VO!n!OO!|!PO!}!PO#O!QO#P!RO#Q!SO#R!TO#S!UO#T!WO#U!XO#V!YO#W!ZO#X![O#Y!]O~O!`!zX!q!zXk!zXg!zXV!zX!o!zXl!zX~P&hOT[OU!_O_!`OfhOs[Ou[Ov[Oz[O{[O|[O}[O!SiO!UZO!sSO#QVO#ZUO#_WO#`XO#aYO~O!e!bO!f!fO!g!eO!h!bO~OT[OU!_O_!`OfhOs[Ou[Ov[Oz[O{[O|[O}[O!SiO!UZO!sSO#_WO#`XO#aYO~O!T!iO~P)rOT#]XU#]X_#]Xf#]Xs#]Xu#]Xv#]Xz#]X{#]X|#]X}#]X!S#]X!U#]X!s#]X#_#]X#`#]X#a#]X~O!v!kOn#]Xo#]X!`#]X!n#]X!|#]X!}#]X#O#]X#P#]X#Q#]X#R#]X#S#]X#T#]X#U#]X#V#]X#W#]X#X#]X#Y#]X!q#]Xk#]Xg#]XV#]X!o#]Xl#]X~P+QOn!{Xo!{X!`!{X!n!{X!|!{X!}!{X#O!{X#P!{X#Q!{X#R!{X#S!{X#T!{X#U!{X#V!{X#W!{X#X!{X#Y!{X!q!{Xk!{Xg!{XV!{X!o!{Xl!{X~P)rOToOU!_OeuOh!pO!sSO!usO~OU!_O~O!n!wO!v!uOVWX!oWX!w!rX~OT!PaU!Pa_!Paf!Pan!Pao!Pas!Pau!Pav!Paz!Pa{!Pa|!Pa}!Pa!S!Pa!U!Pa!`!Pa!l!ma!n!Pa!p!ma!s!Pa!v!Pa!|!Pa!}!Pa#O!Pa#P!Pa#Q!Pa#R!Pa#S!Pa#T!Pa#U!Pa#V!Pa#W!Pa#X!Pa#Y!Pa#_!Pa#`!Pa#a!Pa!q!Pak!Pag!PaV!Pa!o!Pal!Pa~OV!zO!o!xO~OV!zO~O!v!uOT!rXU!rX_!rXf!rXs!rXu!rXv!rXz!rX{!rX|!rX}!rX!S!rX!U!rX!W!rX!s!rX#_!rX#`!rX#a!rX~O!w!rX!T!rX~P3fOToOV!|OeuO!sSO!usO~O!w#OO~OT#POf#RO!sSO!usO~OU#TO~OT#UO~O!ayO!b}O!c#WO!dyO~OT#YO!sSO!usO~O!n!OO!|!PO!}!PO#O!QO#P!RO#Q!SO~Onpaopa!`pa#Rpa#Spa#Tpa#Upa#Vpa#Wpa#Xpa#Ypa!qpakpagpaVpa!opalpa~P6hOToOV#iOeuO!sSO!usO~O!e!bO!f!fO!g#kO!h!bO~O!T#nO~P)rO!v$mO!T#]X~P+QO!q#pO~O!q#qO~OToOeuOh#rO!sSO!usO~Ok#tO~Og#uO~OT#vO!sSO!usO~O!v!uOT!raU!ra_!raf!ras!rau!rav!raz!ra{!ra|!ra}!ra!S!ra!U!ra!W!ra!s!ra#_!ra#`!ra#a!ra~O!w!ra!T!ra~P:ZOT#yOX#{O~OV$OO!o#|O~OV$PO~OT#PO!q$SO!sSO!usO~OT#yOV$UOXnO~O!l$VO~OV$WO~On!rXo!rX!`!rX!n!rX!|!rX!}!rX#O!rX#P!rX#Q!rX#R!rX#S!rX#T!rX#U!rX#V!rX#W!rX#X!rX#Y!rX!q!rXk!rXg!rXV!rX!o!rXl!rX~P3fO!n!OO!|!POnmiomi!`mi#Omi#Pmi#Qmi#Rmi#Smi#Tmi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~O!}mi~P>iO!}!PO~P>iO!n!OO!|!PO!}!PO#O!QOnmiomi!`mi#Qmi#Rmi#Smi#Tmi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~O#Pmi~P@_O#P!RO~P@_O#R!TOnmiomi!`mi#Smi#Tmi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TO#R!TOomi!`mi#Smi#Tmi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TO#R!TO#S!UOomi!`mi#Tmi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TOo!VO#R!TO#S!UO!`mi#Tmi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TOo!VO#R!TO#S!UO#T!WO!`mi#Umi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TOo!VO#R!TO#S!UO#T!WO#U!XO!`mi#Vmi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TOo!VO#R!TO#S!UO#T!WO#U!XO#V!YO!`mi#Wmi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hOn!TOo!VO#R!TO#S!UO#T!WO#U!XO#V!YO#W!ZO!`mi#Xmi#Ymi!qmikmigmiVmi!omilmi~P6hO!`mi!qmikmigmiVmi!omilmi~P&hOV$YO~OTriUri_rifrisriurivrizri{ri|ri}ri!Sri!Uri!sri#_ri#`ri#ari~O!W$[Onriori!`ri!nri!|ri!}ri#Ori#Pri#Qri#Rri#Sri#Tri#Uri#Vri#Wri#Xri#Yri!qrikrigriVri!orilri~PL_O!n!wOVWX!oWX~OV$aO~OT#yOX$bO~O!q$dO~Og$eO~On!rao!ra!`!ra!n!ra!|!ra!}!ra#O!ra#P!ra#Q!ra#R!ra#S!ra#T!ra#U!ra#V!ra#W!ra#X!ra#Y!ra!q!rak!rag!raV!ra!o!ral!ra~P:ZO!W$nO!Tri~PL_Ol$hO~OV$iO~OT#yO~OT#PO!sSO!usO~OT#PO!q$lO!sSO!usO~OToO!sSO!usO~Oz!v!v~",
|
||||
goto: "2g#cPPPP#dPPP#yP#dP#dP#dP$S$Z$k%{%fPPPP&PPPP&fPP&f'[(QP({PP({({)vPPPP({)z({({PPP({P*|+S+_+e+p+z,QPPPPPPPPPPP,WP-cPPPP-{P.VPPP$S$S#d.ZPPPPPPPPPPPPPP/o0e1fPPP2cwcOdeghsv}!f!p!w#O#R#p#q#r#t$V$hSmP#TV#z!x#|$cZqPfr!_!oYtPfr!_!oQ#Z!OQ#o!kR$Z$m!p[OUVZ_deghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!h!p!w#O#R#p#q#r#t$V$[$h$n[oPfr!_!o$mW#Pu#Q$e$jS#Y!O!kR#v!uTyS{wbOdeghsv}!f!p!w#O#R#p#q#r#t$V$h!gaOUVdeghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!p!w#O#R#p#q#r#t$V$h!g`OUVdeghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!p!w#O#R#p#q#r#t$V$h!q^OUVZ_deghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!h!p!w#O#R#p#q#r#t$V$[$h$n!q[OUVZ_deghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!h!p!w#O#R#p#q#r#t$V$[$h$nT!bY!d!p[OUVZ_deghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!h!p!w#O#R#p#q#r#t$V$[$h$nS!qf!`R!tiQ!ymR#}!ySrP!_Q!ofT!{r!oQ{SR#V{S!vkoS#w!v$XR$X#YQ#QuS$R#Q$jR$j$eQ!dYR#j!dQ!hZR#m!hQjOQ!mdQ!neQ!rgQ!shQ!}sQ#SvQ#X}Q#l!fQ#s!pQ#x!wQ$Q#OQ$T#RQ$]#pQ$^#qQ$_#rQ$`#tQ$f$VR$k$hvROdeghsv}!f!p!w#O#R#p#q#r#t$V$hR#UwapPfr!O!_!k!o$mTzS{vTOdeghsv}!f!p!w#O#R#p#q#r#t$V$hQ!^UQ!aVQ#[!PQ#]!QQ#^!RQ#_!SQ#`!TQ#a!UQ#b!VQ#c!WQ#d!XQ#e!YQ#f!ZQ#g![R#h!]!g_OUVdeghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!p!w#O#R#p#q#r#t$V$h!f`OUVdeghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!p!w#O#R#p#q#r#t$V$hS!gZ!hQ!l_T$g$[$n!j]OUV_deghsv}!P!Q!R!S!T!U!V!W!X!Y!Z![!]!f!p!w#O#R#p#q#r#t$V$[$hV!jZ!h$nT!cY!d",
|
||||
nodeNames:
|
||||
"⚠ LineComment BlockComment Program Function Identifier { } Formal Ellipses Assert assert With with Let let Bind AttrPath String Interpolation Interpolation inherit ( ) in IfExpr if then else BinaryExpr < > UnaryExpr App Select builtins Null Integer Float Boolean IndentedString Interpolation Path HPath SPath URI Parenthesized AttrSet LetAttrSet RecAttrSet rec ] [ List or",
|
||||
maxTerm: 110,
|
||||
nodeProps: [
|
||||
["closedBy", 6, "}", 22, ")", 52, "]"],
|
||||
["openedBy", 7, "{", 23, "(", 51, "["],
|
||||
],
|
||||
skippedNodes: [0, 1, 2],
|
||||
repeatNodeCount: 7,
|
||||
tokenData:
|
||||
">P~RtXY#cYZ#c]^#cpq#cqr#trs$Rst$Wtu$ovw$zwx%Vxy%byz%gz{%l{|%q|})]}!O)b!O!P*X!P!Q.r!Q!R0l!R![2e![!]3S!]!^3X!^!_3^!_!`5_!`!a5l!a!b5y!b!c6O!c!}6T!}#O;g#P#Q;l#R#S:u#T#o6T#o#p;q#p#q;v#q#r<R#r#s<W~#hS!j~XY#cYZ#c]^#cpq#cV#yP#ZP!_!`#|U$RO#VU~$WO!s~~$]SP~OY$WZ;'S$W;'S;=`$i<%lO$W~$lP;=`<%l$W~$rP#o#p$u~$zO!u~~$}Pvw%Q~%VO#W~~%YPwx%]~%bO#a~~%gOf~~%lOg~~%qO!}~~%vW#P~{|&`}!O&}!O!P&}!P!Q'j!Q![&}!c!}&}#R#S&}#T#o&}~&eW!|~{|&}}!O&}!O!P&}!P!Q'j!Q![&}!c!}&}#R#S&}#T#o&}T'QW{|&}}!O&}!O!P&}!P!Q'j!Q![&}!c!}&}#R#S&}#T#o&}T'mV{|(S}!O(S!O!P(S!Q![(S!c!}(S#R#S(S#T#o(ST(XWzT{|(S}!O(S!O!P(S!P!Q(q!Q![(S!c!}(S#R#S(S#T#o(ST(vVzT{|(S}!O(S!O!P(S!Q![(S!c!}(S#R#S(S#T#o(S~)bO!o~~)gX#Q~{|&}}!O&}!O!P&}!P!Q'j!Q![&}!`!a*S!c!}&}#R#S&}#T#o&}~*XO#Y~~*^W!vT{|&}}!O&}!O!P*v!P!Q'j!Q![,Q!c!}&}#R#S&}#T#o&}V*yW{|&}}!O&}!O!P+c!P!Q'j!Q![&}!c!}&}#R#S&}#T#o&}V+hWXQ{|&}}!O&}!O!P&}!P!Q'j!Q![&}!c!}&}#R#S&}#T#o&}~,V[v~{|&}}!O&}!O!P&}!P!Q'j!Q![,Q!c!g&}!g!h,{!h!}&}#R#S&}#T#X&}#X#Y,{#Y#o&}~-OW{|-h}!O-h!O!P&}!P!Q'j!Q![.T!c!}&}#R#S&}#T#o&}~-kW{|&}}!O&}!O!P&}!P!Q'j!Q![.T!c!}&}#R#S&}#T#o&}~.YWv~{|&}}!O&}!O!P&}!P!Q'j!Q![.T!c!}&}#R#S&}#T#o&}~.wX#O~z{/d{|(S}!O(S!O!P(S!P!Q0g!Q![(S!c!}(S#R#S(S#T#o(S~/iTQ~Oz/dz{/x{;'S/d;'S;=`0a<%lO/d~/{TO!P/d!P!Q0[!Q;'S/d;'S;=`0a<%lO/d~0aOQ~~0dP;=`<%l/d~0lO#R~~0qWu~{|&}}!O&}!O!P1Z!P!Q'j!Q![1v!c!}&}#R#S&}#T#o&}~1^W{|&}}!O&}!O!P&}!P!Q'j!Q![,Q!c!}&}#R#S&}#T#o&}~1{Wu~{|&}}!O&}!O!P&}!P!Q'j!Q![1v!c!}&}#R#S&}#T#o&}~2jWu~{|&}}!O&}!O!P,Q!P!Q'j!Q![2e!c!}&}#R#S&}#T#o&}~3XO!l~~3^O!q~~3cWn~{|3{}!O3{!O!P3{!Q![3{!_!`5Y!c!}3{#R#S3{#T#o3{~4OX{|3{}!O3{!O!P3{!P!Q4k!Q![3{!`!a5T!c!}3{#R#S3{#T#o3{~4nV{|3{}!O3{!O!P3{!Q![3{!c!}3{#R#S3{#T#o3{~5YO|~~5_O#S~V5dP!wP!_!`5gU5lO#UU~5qPo~!_!`5t~5yO#T~~6OO!n~~6TO!p~~6YYT~wx6x{|7a}!O6T!O!P7a!P!Q'j!Q![6T![!]8P!c!}6T#R#S:u#T#o6T~6}UT~wx6x}!O6x!Q![6x!c!}6x#R#S6x#T#o6x~7dX{|7a}!O7a!O!P7a!P!Q'j!Q![7a![!]8P!c!}7a#R#S&}#T#o7a~8Sdqr9btu9buv9bvw9bwx9bz{9b{|9b|}9b}!O9b!O!P9b!P!Q9b!Q![9b![!]9b!_!`9b!a!b9b!b!c9b!c!}9b#R#S9b#T#o9b#r#s9b~9gd}~qr9btu9buv9bvw9bwx9bz{9b{|9b|}9b}!O9b!O!P9b!P!Q9b!Q![9b![!]9b!_!`9b!a!b9b!b!c9b!c!}9b#R#S9b#T#o9b#r#s9b~:zXT~wx6x{|&}}!O:u!O!P&}!P!Q'j!Q![:u!c!}:u#R#S:u#T#o:u~;lO!U~~;qO!T~~;vOU~~;yP#p#q;|~<RO#X~~<WOV~~<ZP!P!Q<^~<aV{|<v}!O<v!O!P<v!Q![<v!c!}<v#R#S<v#T#o<v~<{W{~{|<v}!O<v!O!P<v!P!Q=e!Q![<v!c!}<v#R#S<v#T#o<v~=jV{~{|<v}!O<v!O!P<v!Q![<v!c!}<v#R#S<v#T#o<v",
|
||||
tokenizers: [scanString, scanIndString, 0, 1, 2],
|
||||
topRules: { Program: [0, 3] },
|
||||
specialized: [{ term: 5, get: (value: string) => spec_Identifier.get(value) ?? -1 }],
|
||||
propSources: [nixHighlighting],
|
||||
tokenPrec: 2290,
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// Generated by lezer-generator, taken from @replit/codemirror-lang-nix@6.0.1.
|
||||
// Vendored so the daemon never loads the editor-only runtime those packages
|
||||
// pull in at import time (@codemirror/autocomplete, view, state). Only pure
|
||||
// Lezer primitives are used here. See packages/highlight/src/svelte for the
|
||||
// same treatment applied to Svelte.
|
||||
export const strContent = 63;
|
||||
export const strDollarBrace = 64;
|
||||
export const strEnd = 65;
|
||||
export const escapeSequence = 66;
|
||||
export const indStrContent = 67;
|
||||
export const indStrDollarBrace = 68;
|
||||
export const indStrEnd = 69;
|
||||
export const indEscapeSequence = 70;
|
||||
@@ -0,0 +1,67 @@
|
||||
// Generated by lezer-generator, taken from @replit/codemirror-lang-nix@6.0.1.
|
||||
// Vendored so the daemon never loads the editor-only runtime those packages
|
||||
// pull in at import time (@codemirror/autocomplete, view, state). Only pure
|
||||
// Lezer primitives are used here. See packages/highlight/src/svelte for the
|
||||
// same treatment applied to Svelte.
|
||||
import { ExternalTokenizer } from "@lezer/lr";
|
||||
import {
|
||||
escapeSequence,
|
||||
indEscapeSequence,
|
||||
indStrContent,
|
||||
indStrDollarBrace,
|
||||
indStrEnd,
|
||||
strContent,
|
||||
strDollarBrace,
|
||||
strEnd,
|
||||
} from "./terms.js";
|
||||
|
||||
const quote = 34,
|
||||
backslack = 92,
|
||||
braceL = 123,
|
||||
dollar = 36,
|
||||
apostrophe = 39;
|
||||
export const scanString = new ExternalTokenizer((input) => {
|
||||
for (let afterDollar = false, i = 0; ; i++) {
|
||||
let { next } = input;
|
||||
if (next < 0) {
|
||||
if (i > 0) input.acceptToken(strContent);
|
||||
break;
|
||||
} else if (next === quote) {
|
||||
if (i > 0) input.acceptToken(strContent);
|
||||
else input.acceptToken(strEnd, 1);
|
||||
break;
|
||||
} else if (next === braceL && afterDollar) {
|
||||
if (i == 1) input.acceptToken(strDollarBrace, 1);
|
||||
else input.acceptToken(strContent, -1);
|
||||
break;
|
||||
} else if (next === backslack) {
|
||||
input.advance();
|
||||
input.acceptToken(escapeSequence, 1);
|
||||
}
|
||||
afterDollar = next === dollar;
|
||||
input.advance();
|
||||
}
|
||||
});
|
||||
export const scanIndString = new ExternalTokenizer((input) => {
|
||||
for (let afterDollar = false, afterApostrophe = false, i = 0; ; i++) {
|
||||
let { next } = input;
|
||||
if (next < 0) {
|
||||
if (i > 0) input.acceptToken(indStrContent);
|
||||
break;
|
||||
} else if (next === apostrophe && afterApostrophe) {
|
||||
if (i > 1) input.acceptToken(indStrContent, -1);
|
||||
else input.acceptToken(indStrEnd, 1);
|
||||
break;
|
||||
} else if (next === braceL && afterDollar) {
|
||||
if (i == 1) input.acceptToken(indStrDollarBrace, 1);
|
||||
else input.acceptToken(indStrContent, -1);
|
||||
break;
|
||||
} else if (next === backslack) {
|
||||
input.advance();
|
||||
input.acceptToken(indEscapeSequence, 1);
|
||||
}
|
||||
afterDollar = next === dollar;
|
||||
afterApostrophe = next === apostrophe;
|
||||
input.advance();
|
||||
}
|
||||
});
|
||||
@@ -14,10 +14,10 @@ import { parser as phpParser } from "@lezer/php";
|
||||
import { parser as rustParser } from "@lezer/rust";
|
||||
import { parser as xmlParser } from "@lezer/xml";
|
||||
import { parser as yamlParser } from "@lezer/yaml";
|
||||
import { csharpLanguage } from "@replit/codemirror-lang-csharp";
|
||||
import { nixLanguage } from "@replit/codemirror-lang-nix";
|
||||
import { parser as elixirParser } from "lezer-elixir";
|
||||
import type { Parser } from "@lezer/common";
|
||||
import { csharpLanguage } from "./csharp/language.js";
|
||||
import { nixLanguage } from "./nix/language.js";
|
||||
import { parser as svelteBaseParser } from "./svelte/parser.js";
|
||||
import { configureNesting, defaultNesting } from "./svelte/nesting.js";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user