vscode-tmlanguage-snapshot
Version:
Take snapshots of your tmLanguage grammar.
139 lines (132 loc) • 4.62 kB
JavaScript
import { createRequire } from "node:module";
import { readFile } from "node:fs/promises";
import { dirname, extname, isAbsolute, join } from "node:path";
import * as _oniguruma from "vscode-oniguruma";
import * as _textmate from "vscode-textmate";
//#region rolldown:runtime
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region src/onigurama.ts
const oniguruma = _oniguruma.default ? _oniguruma.default : _oniguruma;
async function createOniguramaLib() {
const wasmBin = await readFile(join(dirname(__require.resolve("vscode-oniguruma")), "onig.wasm"));
await oniguruma.loadWASM(wasmBin);
return {
createOnigScanner(patterns) {
return new oniguruma.OnigScanner(patterns);
},
createOnigString(s) {
return new oniguruma.OnigString(s);
}
};
}
//#endregion
//#region src/snapshot.ts
function renderSnapshotFromLineAndTokens(lineWithTokens) {
const lines = [];
for (const { line, tokens } of lineWithTokens) lines.push(`>${line}`, ...renderTokens(tokens));
return lines.join("\n");
}
function renderTokens(tokens) {
const lines = [];
for (const token of tokens) {
let line = "#";
line += " ".repeat(token.startIndex);
line += "^".repeat(token.endIndex - token.startIndex);
line += ` ${token.scopes.join(" ")}`;
lines.push(line);
}
return lines;
}
//#endregion
//#region src/textmate.ts
const textmate = _textmate.default ? _textmate.default : _textmate;
function createTextmateRegistry(grammars, onigLib) {
const grammarMap = /* @__PURE__ */ new Map();
const injectionMap = /* @__PURE__ */ new Map();
for (const { grammar, content } of grammars) {
const rawGrammar = textmate.parseRawGrammar(content, grammar.path);
grammarMap.set(grammar.scopeName || rawGrammar.scopeName, rawGrammar);
if (grammar.injectTo) for (const injectScope of grammar.injectTo) {
let injections = injectionMap.get(injectScope);
if (!injections) injectionMap.set(injectScope, injections = []);
injections.push(grammar.scopeName);
}
}
return new textmate.Registry({
onigLib,
async loadGrammar(scope) {
if (grammarMap.has(scope)) return grammarMap.get(scope);
return null;
},
getInjections(scope) {
const splittedScope = scope.split(".");
const injections = [];
for (let i = 1; i <= splittedScope.length; i++) {
const subScopeName = splittedScope.slice(0, i).join(".");
injections.push(...injectionMap.get(subScopeName) ?? []);
}
return injections;
}
});
}
//#endregion
//#region src/token.ts
const newlineRe = /\r?\n/;
async function getLineWithTokens(registry, scope, source) {
const grammar = await registry.loadGrammar(scope);
if (!grammar) throw new Error(`Could not load scope ${scope}`);
let prevRuleStack = null;
return source.split(newlineRe).map((line) => {
const { tokens, ruleStack } = grammar.tokenizeLine(line, prevRuleStack);
prevRuleStack = ruleStack;
return {
line,
tokens
};
});
}
//#endregion
//#region src/index.ts
async function createGrammarSnapshot(packageJsonPath, options = {}) {
const grammars = [];
if (options.extraGrammarPaths) grammars.push(...options.extraGrammarPaths.map((path) => ({
path,
scopeName: ""
})));
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
const rootDir = dirname(packageJsonPath);
const contributeGrammars = packageJson.contributes?.grammars ?? [];
const contributeLanguages = packageJson.contributes?.languages ?? [];
grammars.push(...contributeGrammars.map((grammar) => {
const path = join(rootDir, grammar.path);
return {
...grammar,
path
};
}));
const languageToScope = grammars.filter((language) => !!language.language).map(({ language, scopeName }) => ({ [language]: scopeName })).reduce((a, b) => ({
...a,
...b
}), {});
const extensionToLang = contributeLanguages.filter((language) => !!language.extensions).flatMap(({ extensions, id }) => extensions.map((e) => ({ [e]: id }))).reduce((a, b) => ({
...a,
...b
}), {});
const getScope = (extension) => languageToScope[extensionToLang[extension]];
const registry = createTextmateRegistry(await Promise.all(grammars.map(async (grammar) => {
return {
grammar,
content: await readFile(grammar.path, "utf-8")
};
})), createOniguramaLib());
async function snapshot(path) {
const scope = getScope(extname(path));
if (!scope) throw new Error(`No scope found for ${path}`);
path = isAbsolute(path) ? path : join(rootDir, path);
return renderSnapshotFromLineAndTokens(await getLineWithTokens(registry, scope, await readFile(path, "utf-8")));
}
return snapshot;
}
//#endregion
export { createGrammarSnapshot };