squiggle/packages/vscode-ext/client/src/highlight.ts

97 lines
2.9 KiB
TypeScript
Raw Normal View History

import * as vscode from "vscode";
import { parse } from "@quri/squiggle-lang";
import { AnyPeggyNode } from "@quri/squiggle-lang/dist/src/rescript/Reducer/Reducer_Peggy/helpers";
2022-06-26 08:42:56 +00:00
const tokenTypes = ["enum", "function", "variable", "property"];
const tokenModifiers = ["declaration", "documentation"];
const legend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
const convertRange = (
range: Extract<AnyPeggyNode, { type: "Identifier" }>["location"]
) =>
new vscode.Range(
new vscode.Position(range.start.line - 1, range.start.column - 1),
new vscode.Position(range.end.line - 1, range.end.column - 1)
);
const populateTokensBuilder = (
tokensBuilder: vscode.SemanticTokensBuilder,
node: AnyPeggyNode
// bindings: { [key: string]: boolean }
) => {
switch (node.type) {
case "Expression":
for (const child of node.nodes) {
populateTokensBuilder(tokensBuilder, child);
}
break;
case "Block":
for (const child of node.statements) {
populateTokensBuilder(tokensBuilder, child);
}
break;
case "LetStatement":
tokensBuilder.push(
convertRange(node.variable.location),
node.value.type === "Lambda" ? "function" : "variable",
["declaration"]
);
populateTokensBuilder(tokensBuilder, node.value);
break;
case "Lambda":
for (const arg of node.args) {
populateTokensBuilder(tokensBuilder, arg);
}
populateTokensBuilder(tokensBuilder, node.body);
break;
case "Ternary":
populateTokensBuilder(tokensBuilder, node.condition);
populateTokensBuilder(tokensBuilder, node.trueExpression);
populateTokensBuilder(tokensBuilder, node.falseExpression);
break;
case "KeyValue":
2022-06-26 08:42:56 +00:00
if (node.key.type === "String" && node.key.location) {
tokensBuilder.push(convertRange(node.key.location), "property", [
"declaration",
]);
} else {
populateTokensBuilder(tokensBuilder, node.key);
}
populateTokensBuilder(tokensBuilder, node.value);
break;
case "Identifier":
2022-06-26 08:42:56 +00:00
tokensBuilder.push(convertRange(node.location), "variable");
break;
}
};
export const registerSemanticHighlight = () => {
const provider: vscode.DocumentSemanticTokensProvider = {
provideDocumentSemanticTokens(
document: vscode.TextDocument
): vscode.ProviderResult<vscode.SemanticTokens> {
const parseResult = parse(document.getText());
const tokensBuilder = new vscode.SemanticTokensBuilder(legend);
if (parseResult.tag === "Ok") {
populateTokensBuilder(
tokensBuilder,
parseResult.value
// {}
);
}
return tokensBuilder.build();
},
};
const selector = { language: "squiggle", scheme: "file" };
vscode.languages.registerDocumentSemanticTokensProvider(
selector,
provider,
legend
);
};