semantic highlight based on squiggle-lang AST

This commit is contained in:
Vyacheslav Matyukhin 2022-06-26 11:15:25 +03:00
parent 5a2b5b2a4d
commit 96c2a982cb
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
3 changed files with 97 additions and 10 deletions

View File

@ -4,6 +4,7 @@ import * as vscode from "vscode";
import { startClient, stopClient } from "./client";
import { SquiggleEditorProvider } from "./editor";
import { registerSemanticHighlight } from "./highlight";
import { registerPreviewCommand } from "./preview";
// this method is called when your extension is activated
@ -13,6 +14,8 @@ export function activate(context: vscode.ExtensionContext) {
registerPreviewCommand(context);
registerSemanticHighlight();
startClient(context);
}

View File

@ -0,0 +1,90 @@
import * as vscode from "vscode";
import { parse } from "@quri/squiggle-lang";
import { AnyPeggyNode } from "@quri/squiggle-lang/dist/src/rescript/Reducer/Reducer_Peggy/helpers";
const tokenTypes = ["class", "interface", "enum", "function", "variable"];
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":
populateTokensBuilder(tokensBuilder, node.key);
populateTokensBuilder(tokensBuilder, node.value);
break;
case "Identifier":
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
);
};

View File

@ -13,6 +13,10 @@ import { parse } from "@quri/squiggle-lang";
import { TextDocument } from "vscode-languageserver-textdocument";
// Documentation:
// - https://code.visualstudio.com/api/language-extensions/language-server-extension-guide
// - https://microsoft.github.io/language-server-protocol/specifications/specification-current
// Create a connection for the server, using Node's IPC as a transport.
// Also include all preview / proposed LSP features.
let connection = createConnection(ProposedFeatures.all);
@ -23,17 +27,7 @@ documents.onDidChangeContent((change) => {
validateSquiggleDocument(change.document);
});
let hasDiagnosticRelatedInformationCapability = false;
connection.onInitialize((params: InitializeParams) => {
const capabilities = params.capabilities;
hasDiagnosticRelatedInformationCapability = !!(
capabilities.textDocument &&
capabilities.textDocument.publishDiagnostics &&
capabilities.textDocument.publishDiagnostics.relatedInformation
);
const result: InitializeResult = {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,