From d8e6fdf54a2bf3451f765f8a2a49bcaed624277d Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Tue, 2 May 2023 11:26:18 -0300 Subject: [PATCH 01/18] feat: semantic highlighting --- client/scripts/bundle.js | 12 + package-lock.json | 226 ++++++++++++++++++ server/package.json | 3 +- server/scripts/bundle.js | 7 +- server/src/server.ts | 2 + .../services/initialization/onInitialize.ts | 10 +- .../semanticHighlight/HighlightVisitor.ts | 13 + .../SemanticTokensBuilder.ts | 43 ++++ .../ContractDefinitionHighlighter.ts | 17 ++ .../highlighters/CustomTypeHighlighter.ts | 17 ++ .../highlighters/ElementaryTypeHighlighter.ts | 13 + .../EventDefinitionHighlighter.ts | 17 ++ .../highlighters/EventEmissionHighlighter.ts | 17 ++ .../highlighters/FunctionCallHighlighter.ts | 18 ++ .../FunctionDefinitionHighlighter.ts | 17 ++ .../InterfaceDefinitionHighlighter.ts | 17 ++ .../highlighters/KeywordHighlighter.ts | 98 ++++++++ .../highlighters/LoggerVisitor.ts | 34 +++ .../highlighters/NumberHighlighter.ts | 19 ++ .../highlighters/StringHighlighter.ts | 23 ++ .../StructDefinitionHighlighter.ts | 17 ++ .../semanticHighlight/onSemanticTokensFull.ts | 111 +++++++++ .../semanticHighlight/slangHelpers.ts | 24 ++ .../services/semanticHighlight/tokenTypes.ts | 29 +++ server/test/helpers/setupMockConnection.ts | 5 + .../semanticTokens/full/SemanticTokens.sol | 54 +++++ test/protocol/src/TestLanguageClient.ts | 12 + .../initialize/data/initializeResult.json | 8 + .../textDocument/semanticTokens/full.test.ts | 40 ++++ 29 files changed, 920 insertions(+), 3 deletions(-) create mode 100644 server/src/services/semanticHighlight/HighlightVisitor.ts create mode 100644 server/src/services/semanticHighlight/SemanticTokensBuilder.ts create mode 100644 server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts create mode 100644 server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/StringHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/onSemanticTokensFull.ts create mode 100644 server/src/services/semanticHighlight/slangHelpers.ts create mode 100644 server/src/services/semanticHighlight/tokenTypes.ts create mode 100644 test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol create mode 100644 test/protocol/test/textDocument/semanticTokens/full.test.ts diff --git a/client/scripts/bundle.js b/client/scripts/bundle.js index 0d274115..2df7e7fc 100755 --- a/client/scripts/bundle.js +++ b/client/scripts/bundle.js @@ -160,6 +160,7 @@ async function main() { external: [ "vscode", "@nomicfoundation/solidity-analyzer", + "@nomicfoundation/slang", "fsevents", "mocha", ], @@ -198,6 +199,17 @@ async function main() { "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.1", "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.1", "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.1", + + "@nomicfoundation/slang": "0.4.0", + "@nomicfoundation/slang-darwin-arm64": "0.4.0", + "@nomicfoundation/slang-win32-arm64-msvc": "0.4.0", + "@nomicfoundation/slang-linux-arm64-gnu": "0.4.0", + "@nomicfoundation/slang-linux-arm64-musl": "0.4.0", + "@nomicfoundation/slang-win32-ia32-msvc": "0.4.0", + "@nomicfoundation/slang-darwin-x64": "0.4.0", + "@nomicfoundation/slang-win32-x64-msvc": "0.4.0", + "@nomicfoundation/slang-linux-x64-gnu": "0.4.0", + "@nomicfoundation/slang-linux-x64-musl": "0.4.0", }, }) ); diff --git a/package-lock.json b/package-lock.json index 4c49173f..fe88b5ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2661,6 +2661,160 @@ "setimmediate": "^1.0.5" } }, + "node_modules/@nomicfoundation/slang": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.4.0.tgz", + "integrity": "sha512-cA6E4RialoC1x44PPK1SQ0RWqhnPSy1NMp5/LUvWk4SXzOzOgMdCW/GWy7/E17aY+4mbVC6AjTJb+aKvV57Uqw==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@nomicfoundation/slang-darwin-arm64": "0.4.0", + "@nomicfoundation/slang-darwin-x64": "0.4.0", + "@nomicfoundation/slang-linux-arm64-gnu": "0.4.0", + "@nomicfoundation/slang-linux-arm64-musl": "0.4.0", + "@nomicfoundation/slang-linux-x64-gnu": "0.4.0", + "@nomicfoundation/slang-linux-x64-musl": "0.4.0", + "@nomicfoundation/slang-win32-arm64-msvc": "0.4.0", + "@nomicfoundation/slang-win32-ia32-msvc": "0.4.0", + "@nomicfoundation/slang-win32-x64-msvc": "0.4.0" + } + }, + "node_modules/@nomicfoundation/slang-darwin-arm64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-arm64/-/slang-darwin-arm64-0.4.0.tgz", + "integrity": "sha512-HPE+Whj93rfR9DlhQjqlCpAgKCflxW/7VAtB4X2TNRepfF28E2Mvyfg2GXzGzf2FNH9GCv8/c2vLsxyrm/v+MQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-darwin-x64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-x64/-/slang-darwin-x64-0.4.0.tgz", + "integrity": "sha512-/Lo8Pg5K9qJXku5bCvwXt3axy2wRnAffd71n8CKzkCXIul5KwFFYY3fg0/o/WjWgLw+PodvPqh1g3Yrdf0/RCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-linux-arm64-gnu": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-gnu/-/slang-linux-arm64-gnu-0.4.0.tgz", + "integrity": "sha512-ZE5d/Cp+viPD3dtLkTz2kTzSD5E1cZiKvZA8qI5UsqH8oX2eW/I/2SOavmQ4toSUCcqgqnYJUhvjQc1k/oiVeg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-linux-arm64-musl": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-musl/-/slang-linux-arm64-musl-0.4.0.tgz", + "integrity": "sha512-wk4lfa4ap+Pzz09W8HSA56ZsxdFpNOdE0KfBAPgbE7/6pHhyMt6IqsI+TDsHQSzRJ00+17//e5LrZGkV9+mw0A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-linux-x64-gnu": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-gnu/-/slang-linux-x64-gnu-0.4.0.tgz", + "integrity": "sha512-VVTk06QpWCtfsF727AvNBXelg6ObwATkjiOnNRIiM2CVZi9+UlcqxdnoVnxL5mkoAhXS59xArDQ6C+x4qWCL2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-linux-x64-musl": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-musl/-/slang-linux-x64-musl-0.4.0.tgz", + "integrity": "sha512-6RjyR7prrIMOp1378OiU73HD8wwvvyR850eKgkTRqJyNvFXd4pELc4ERE+BNUlwt2Ab86T+cMMbcP1T0yeRorg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-win32-arm64-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-arm64-msvc/-/slang-win32-arm64-msvc-0.4.0.tgz", + "integrity": "sha512-9UlbedxBV+Btjku6m09kJ/pR3pxvw2b+gQthklGdvVPUzPn2EdWttiAMlMLgTNM93Lf1KfjAfxkYA3zSgkoTXg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-win32-ia32-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-ia32-msvc/-/slang-win32-ia32-msvc-0.4.0.tgz", + "integrity": "sha512-mJSEW8YL4cJu+Zv/S98pkdFsHKmvpXSz7glrLLH6REiyUyjnDpu35HESpeQXImcIC0C5uBerkTJFabzwKchwRw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/slang-win32-x64-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-x64-msvc/-/slang-win32-x64-msvc-0.4.0.tgz", + "integrity": "sha512-yi7SF4/qoNqPOqJjypAG2CqcOjqNe069nBCYReoXny1TDWBy/pVnxoLdUrIpALUIbMW0uYycBpgqQkvoRnYBcQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nomicfoundation/solidity-analyzer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", @@ -11808,6 +11962,7 @@ "version": "0.7.2", "license": "MIT", "dependencies": { + "@nomicfoundation/slang": "^0.4.0", "@nomicfoundation/solidity-analyzer": "0.1.1" }, "bin": { @@ -14561,6 +14716,76 @@ } } }, + "@nomicfoundation/slang": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.4.0.tgz", + "integrity": "sha512-cA6E4RialoC1x44PPK1SQ0RWqhnPSy1NMp5/LUvWk4SXzOzOgMdCW/GWy7/E17aY+4mbVC6AjTJb+aKvV57Uqw==", + "requires": { + "@nomicfoundation/slang-darwin-arm64": "0.4.0", + "@nomicfoundation/slang-darwin-x64": "0.4.0", + "@nomicfoundation/slang-linux-arm64-gnu": "0.4.0", + "@nomicfoundation/slang-linux-arm64-musl": "0.4.0", + "@nomicfoundation/slang-linux-x64-gnu": "0.4.0", + "@nomicfoundation/slang-linux-x64-musl": "0.4.0", + "@nomicfoundation/slang-win32-arm64-msvc": "0.4.0", + "@nomicfoundation/slang-win32-ia32-msvc": "0.4.0", + "@nomicfoundation/slang-win32-x64-msvc": "0.4.0" + } + }, + "@nomicfoundation/slang-darwin-arm64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-arm64/-/slang-darwin-arm64-0.4.0.tgz", + "integrity": "sha512-HPE+Whj93rfR9DlhQjqlCpAgKCflxW/7VAtB4X2TNRepfF28E2Mvyfg2GXzGzf2FNH9GCv8/c2vLsxyrm/v+MQ==", + "optional": true + }, + "@nomicfoundation/slang-darwin-x64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-x64/-/slang-darwin-x64-0.4.0.tgz", + "integrity": "sha512-/Lo8Pg5K9qJXku5bCvwXt3axy2wRnAffd71n8CKzkCXIul5KwFFYY3fg0/o/WjWgLw+PodvPqh1g3Yrdf0/RCw==", + "optional": true + }, + "@nomicfoundation/slang-linux-arm64-gnu": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-gnu/-/slang-linux-arm64-gnu-0.4.0.tgz", + "integrity": "sha512-ZE5d/Cp+viPD3dtLkTz2kTzSD5E1cZiKvZA8qI5UsqH8oX2eW/I/2SOavmQ4toSUCcqgqnYJUhvjQc1k/oiVeg==", + "optional": true + }, + "@nomicfoundation/slang-linux-arm64-musl": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-musl/-/slang-linux-arm64-musl-0.4.0.tgz", + "integrity": "sha512-wk4lfa4ap+Pzz09W8HSA56ZsxdFpNOdE0KfBAPgbE7/6pHhyMt6IqsI+TDsHQSzRJ00+17//e5LrZGkV9+mw0A==", + "optional": true + }, + "@nomicfoundation/slang-linux-x64-gnu": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-gnu/-/slang-linux-x64-gnu-0.4.0.tgz", + "integrity": "sha512-VVTk06QpWCtfsF727AvNBXelg6ObwATkjiOnNRIiM2CVZi9+UlcqxdnoVnxL5mkoAhXS59xArDQ6C+x4qWCL2Q==", + "optional": true + }, + "@nomicfoundation/slang-linux-x64-musl": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-musl/-/slang-linux-x64-musl-0.4.0.tgz", + "integrity": "sha512-6RjyR7prrIMOp1378OiU73HD8wwvvyR850eKgkTRqJyNvFXd4pELc4ERE+BNUlwt2Ab86T+cMMbcP1T0yeRorg==", + "optional": true + }, + "@nomicfoundation/slang-win32-arm64-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-arm64-msvc/-/slang-win32-arm64-msvc-0.4.0.tgz", + "integrity": "sha512-9UlbedxBV+Btjku6m09kJ/pR3pxvw2b+gQthklGdvVPUzPn2EdWttiAMlMLgTNM93Lf1KfjAfxkYA3zSgkoTXg==", + "optional": true + }, + "@nomicfoundation/slang-win32-ia32-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-ia32-msvc/-/slang-win32-ia32-msvc-0.4.0.tgz", + "integrity": "sha512-mJSEW8YL4cJu+Zv/S98pkdFsHKmvpXSz7glrLLH6REiyUyjnDpu35HESpeQXImcIC0C5uBerkTJFabzwKchwRw==", + "optional": true + }, + "@nomicfoundation/slang-win32-x64-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-x64-msvc/-/slang-win32-x64-msvc-0.4.0.tgz", + "integrity": "sha512-yi7SF4/qoNqPOqJjypAG2CqcOjqNe069nBCYReoXny1TDWBy/pVnxoLdUrIpALUIbMW0uYycBpgqQkvoRnYBcQ==", + "optional": true + }, "@nomicfoundation/solidity-analyzer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", @@ -14640,6 +14865,7 @@ "version": "file:server", "requires": { "@istanbuljs/nyc-config-typescript": "1.0.2", + "@nomicfoundation/slang": "^0.4.0", "@nomicfoundation/solidity-analyzer": "0.1.1", "@sentry/node": "7.32.1", "@sentry/tracing": "7.32.1", diff --git a/server/package.json b/server/package.json index ba5d4b09..1bd52a95 100644 --- a/server/package.json +++ b/server/package.json @@ -56,6 +56,7 @@ "@types/qs": "^6.9.7", "@types/semver": "^7.3.12", "@types/sinon": "10.0.6", + "@types/yaml": "^1.9.7", "c3-linearization": "0.3.0", "chai": "4.3.4", "codecov": "3.8.3", @@ -84,10 +85,10 @@ "vscode-languageserver-textdocument": "1.0.8", "vscode-languageserver-types": "3.17.3", "vscode-uri": "3.0.7", - "@types/yaml": "^1.9.7", "yaml": "^2.2.1" }, "dependencies": { + "@nomicfoundation/slang": "^0.4.0", "@nomicfoundation/solidity-analyzer": "0.1.1" } } diff --git a/server/scripts/bundle.js b/server/scripts/bundle.js index fab71131..dba87171 100644 --- a/server/scripts/bundle.js +++ b/server/scripts/bundle.js @@ -86,7 +86,12 @@ async function main() { minifyWhitespace: true, minifyIdentifiers: false, minifySyntax: true, - external: ["@nomicfoundation/solidity-analyzer", "fsevents", "mocha"], + external: [ + "@nomicfoundation/solidity-analyzer", + "@nomicfoundation/slang", + "fsevents", + "mocha", + ], platform: "node", outdir: ".", logLevel: "info", diff --git a/server/src/server.ts b/server/src/server.ts index 8663d436..d0956241 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -19,6 +19,7 @@ import { Telemetry } from "./telemetry/types"; import { attachDocumentHooks } from "./services/documents/attachDocumentHooks"; import { availableVersions } from "./services/initialization/updateAvailableSolcVersions"; import { onDocumentFormatting } from "./services/formatting/onDocumentFormatting"; +import { onSemanticTokensFull } from "./services/semanticHighlight/onSemanticTokensFull"; export default function setupServer( connection: Connection, @@ -98,6 +99,7 @@ function attachLanguageServerCommandHooks(serverState: ServerState) { connection.onCodeAction(onCodeAction(serverState)); connection.onHover(onHover(serverState)); connection.onDocumentFormatting(onDocumentFormatting(serverState)); + connection.languages.semanticTokens.on(onSemanticTokensFull(serverState)); } function attachCustomHooks(serverState: ServerState) { diff --git a/server/src/services/initialization/onInitialize.ts b/server/src/services/initialization/onInitialize.ts index 3a194af9..889bf55b 100644 --- a/server/src/services/initialization/onInitialize.ts +++ b/server/src/services/initialization/onInitialize.ts @@ -5,6 +5,7 @@ import { WorkspaceFolder, } from "vscode-languageserver/node"; import { ServerState } from "../../types"; +import { tokensTypes } from "../semanticHighlight/tokenTypes"; import { indexWorkspaceFolders } from "./indexWorkspaceFolders"; import { updateAvailableSolcVersions } from "./updateAvailableSolcVersions"; @@ -79,7 +80,14 @@ export const onInitialize = (serverState: ServerState) => { codeActionProvider: true, hoverProvider: true, documentFormattingProvider: true, - + semanticTokensProvider: { + legend: { + tokenTypes: tokensTypes, + tokenModifiers: [], + }, + range: false, + full: true, + }, workspace: { workspaceFolders: { supported: false, diff --git a/server/src/services/semanticHighlight/HighlightVisitor.ts b/server/src/services/semanticHighlight/HighlightVisitor.ts new file mode 100644 index 00000000..73fff4de --- /dev/null +++ b/server/src/services/semanticHighlight/HighlightVisitor.ts @@ -0,0 +1,13 @@ +import { TextDocument } from "vscode-languageserver-textdocument"; +import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; +import { SlangNode } from "./slangHelpers"; + +// Abstraction for a visitor that wants to highlight tokens +export abstract class HighlightVisitor { + constructor( + public document: TextDocument, + public tokenBuilder: SemanticTokensBuilder + ) {} + + public abstract visit(node: SlangNode, ancestors: SlangNode[]): void; +} diff --git a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts new file mode 100644 index 00000000..da1ebf8a --- /dev/null +++ b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { TextDocument } from "vscode-languageserver-textdocument"; +import { getTokenTypeIndex } from "./tokenTypes"; +import { SlangNode } from "./slangHelpers"; + +// Helps building a SemanticTokens response by providing slang nodes and supported token types +export class SemanticTokensBuilder { + private tokenData: number[] = []; + private lastTokenLine = 0; + private lastTokenChar = 0; + + constructor(private document: TextDocument) {} + + public addToken(node: SlangNode, type: SemanticTokenTypes, modifiers = 0) { + const offset = node.charRange[0]; + const length = node.charRange[1] - node.charRange[0]; + + const position = this.document.positionAt(offset); + + // Calculate character and line difference to last token + const lineDelta = position.line - this.lastTokenLine; + const charDelta = + lineDelta === 0 + ? position.character - this.lastTokenChar + : position.character; + + this.lastTokenLine = position.line; + this.lastTokenChar = position.character; + + this.tokenData.push( + lineDelta, + charDelta, + length, + getTokenTypeIndex(type), + modifiers + ); + } + + public getTokenData() { + return this.tokenData; + } +} diff --git a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts new file mode 100644 index 00000000..6c732ceb --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights contract definitions +export class ContractDefinitionHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 1]?.kind === RuleKind.ContractDefinition + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts new file mode 100644 index 00000000..dc4ed48b --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights custom type names +export class CustomTypeHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 3]?.kind === RuleKind.TypeName + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts new file mode 100644 index 00000000..6855fe2e --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts @@ -0,0 +1,13 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights elementary type names +export class ElementaryTypeHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if (node.type === NodeType.Rule && node.kind === RuleKind.ElementaryType) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts new file mode 100644 index 00000000..473201b3 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights event definitions +export class EventDefinitionHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 1]?.kind === RuleKind.EventDefinition + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts new file mode 100644 index 00000000..6fa266a4 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights event emissions +export class EventEmissionHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 3]?.kind === RuleKind.EmitStatement + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts new file mode 100644 index 00000000..b8516e70 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts @@ -0,0 +1,18 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights function calls +export class FunctionCallHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 2]?.kind === + RuleKind.FunctionCallExpression + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.function); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts new file mode 100644 index 00000000..15c95d48 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights function definitions +export class FunctionDefinitionHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 1]?.kind === RuleKind.FunctionDefinition + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.function); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts new file mode 100644 index 00000000..4035abb1 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights interface definitions +export class InterfaceDefinitionHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 1]?.kind === RuleKind.InterfaceDefinition + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts new file mode 100644 index 00000000..d793ae35 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts @@ -0,0 +1,98 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +const keywordKinds = new Set([ + TokenKind.AbicoderKeyword, + TokenKind.AbstractKeyword, + TokenKind.AddressKeyword, + TokenKind.AnonymousKeyword, + TokenKind.AsKeyword, + TokenKind.AssemblyKeyword, + TokenKind.BoolKeyword, + TokenKind.BreakKeyword, + TokenKind.CalldataKeyword, + TokenKind.CaseKeyword, + TokenKind.CatchKeyword, + TokenKind.ConstantKeyword, + TokenKind.ConstructorKeyword, + TokenKind.ContinueKeyword, + TokenKind.ContractKeyword, + TokenKind.DaysKeyword, + TokenKind.DefaultKeyword, + TokenKind.DeleteKeyword, + TokenKind.DoKeyword, + TokenKind.ElseKeyword, + TokenKind.EmitKeyword, + TokenKind.EnumKeyword, + TokenKind.ErrorKeyword, + TokenKind.EtherKeyword, + TokenKind.EventKeyword, + TokenKind.ExperimentalKeyword, + TokenKind.ExternalKeyword, + TokenKind.FallbackKeyword, + TokenKind.FalseKeyword, + TokenKind.FinneyKeyword, + TokenKind.ForKeyword, + TokenKind.FromKeyword, + TokenKind.FunctionKeyword, + TokenKind.GlobalKeyword, + TokenKind.GweiKeyword, + TokenKind.HoursKeyword, + TokenKind.IfKeyword, + TokenKind.ImmutableKeyword, + TokenKind.ImportKeyword, + TokenKind.IndexedKeyword, + TokenKind.InterfaceKeyword, + TokenKind.InternalKeyword, + TokenKind.IsKeyword, + TokenKind.LeaveKeyword, + TokenKind.LetKeyword, + TokenKind.LibraryKeyword, + TokenKind.MappingKeyword, + TokenKind.MemoryKeyword, + TokenKind.MinutesKeyword, + TokenKind.ModifierKeyword, + TokenKind.NewKeyword, + TokenKind.OverrideKeyword, + TokenKind.PayableKeyword, + TokenKind.PragmaKeyword, + TokenKind.PrivateKeyword, + TokenKind.PublicKeyword, + TokenKind.PureKeyword, + TokenKind.ReceiveKeyword, + TokenKind.ReturnKeyword, + TokenKind.ReturnsKeyword, + TokenKind.RevertKeyword, + TokenKind.SecondsKeyword, + TokenKind.SolidityKeyword, + TokenKind.StorageKeyword, + TokenKind.StringKeyword, + TokenKind.StructKeyword, + TokenKind.SwitchKeyword, + TokenKind.SzaboKeyword, + TokenKind.ThrowKeyword, + TokenKind.TrueKeyword, + TokenKind.TryKeyword, + TokenKind.TypeKeyword, + TokenKind.UncheckedKeyword, + TokenKind.UsingKeyword, + TokenKind.ViewKeyword, + TokenKind.VirtualKeyword, + TokenKind.WeeksKeyword, + TokenKind.WeiKeyword, + TokenKind.WhileKeyword, + TokenKind.YearsKeyword, + TokenKind.YulKeyword, + TokenKind.YulReservedKeyword, +]); + +// Highlights keywords +export class KeywordHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if (node.type === NodeType.Token && keywordKinds.has(node.kind)) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.keyword); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts b/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts new file mode 100644 index 00000000..e5309e19 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts @@ -0,0 +1,34 @@ +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import _ from "lodash"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +const nodeTypeMap = _.invert(NodeType); +const ruleKindMap = _.invert(RuleKind); +const tokenKindMap = _.invert(TokenKind); + +// Visitor that logs the tree as indented text +export class Logger extends HighlightVisitor { + public visit(node: SlangNode, ancestors: SlangNode[]): void { + const nodeText = JSON.stringify( + _.truncate( + this.document + .getText() + .slice(Number(node.charRange[0]), Number(node.charRange[1])), + { + length: 20, + } + ) + ); + + const indentation = " ".repeat(ancestors.length * 2); + const kindMap = node.type === NodeType.Rule ? ruleKindMap : tokenKindMap; + + // eslint-disable-next-line no-console + console.log( + `${indentation}${nodeTypeMap[node.type]}: ${ + kindMap[node.kind] + } ${nodeText}` + ); + } +} diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts new file mode 100644 index 00000000..5e03a300 --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts @@ -0,0 +1,19 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +const numberKinds = new Set([ + TokenKind.HexLiteral, + TokenKind.YulHexLiteral, + TokenKind.DecimalLiteral, +]); + +// Highlights numbers +export class NumberHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if (node.type === NodeType.Token && numberKinds.has(node.kind)) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.number); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts new file mode 100644 index 00000000..c2b6445e --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts @@ -0,0 +1,23 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +const stringKinds = new Set([ + TokenKind.HexStringLiteral, + TokenKind.AsciiStringLiteral, + TokenKind.UnicodeStringLiteral, + TokenKind.DoubleQuotedAsciiStringLiteral, + TokenKind.SingleQuotedAsciiStringLiteral, + TokenKind.DoubleQuotedUnicodeStringLiteral, + TokenKind.SingleQuotedUnicodeStringLiteral, +]); + +// Highlights strings +export class StringHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if (node.type === NodeType.Token && stringKinds.has(node.kind)) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.string); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts new file mode 100644 index 00000000..8a79ae0d --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts @@ -0,0 +1,17 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNode } from "../slangHelpers"; + +// Highlights struct definitions +export class StructDefinitionHighlighter extends HighlightVisitor { + public visit(node: SlangNode, _ancestors: SlangNode[]): void { + if ( + node.type === NodeType.Token && + node.kind === TokenKind.Identifier && + _ancestors[_ancestors.length - 1]?.kind === RuleKind.StructDefinition + ) { + this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts new file mode 100644 index 00000000..a0de6749 --- /dev/null +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -0,0 +1,111 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { + SemanticTokens, + SemanticTokensParams, +} from "vscode-languageserver-protocol"; +import { Language, ProductionKind } from "@nomicfoundation/slang"; +import _ from "lodash"; +import { analyze } from "@nomicfoundation/solidity-analyzer"; +import semver from "semver"; +import { ServerState } from "../../types"; +import { onCommand } from "../../utils/onCommand"; +import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; +import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; +import { KeywordHighlighter } from "./highlighters/KeywordHighlighter"; +import { ElementaryTypeHighlighter } from "./highlighters/ElementaryTypeHighlighter"; +import { NumberHighlighter } from "./highlighters/NumberHighlighter"; +import { StringHighlighter } from "./highlighters/StringHighlighter"; +import { FunctionDefinitionHighlighter } from "./highlighters/FunctionDefinitionHighlighter"; +import { FunctionCallHighlighter } from "./highlighters/FunctionCallHighlighter"; +import { EventEmissionHighlighter } from "./highlighters/EventEmissionHighlighter"; +import { EventDefinitionHighlighter } from "./highlighters/EventDefinitionHighlighter"; +import { ContractDefinitionHighlighter } from "./highlighters/ContractDefinitionHighlighter"; +import { InterfaceDefinitionHighlighter } from "./highlighters/InterfaceDefinitionHighlighter"; +import { StructDefinitionHighlighter } from "./highlighters/StructDefinitionHighlighter"; +import { walk } from "./slangHelpers"; + +const emptyResponse: SemanticTokens = { data: [] }; + +export function onSemanticTokensFull(serverState: ServerState) { + return (params: SemanticTokensParams): SemanticTokens => { + return ( + onCommand( + serverState, + "onSemanticTokensFull", + params.textDocument.uri, + () => { + const { uri } = params.textDocument; + const { logger, solcVersions } = serverState; + + // Find the file in the documents collection + const document = serverState.documents.get(uri); + + if (document === undefined) { + throw new Error(`Document not found: ${uri}`); + } + + const text = document.getText(); + + // Get the document's solidity version + const { versionPragmas } = analyze(text); + const solcVersion = + semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || + _.last(solcVersions); + + try { + // Parse using slang + const language = new Language(solcVersion!); + + const parseOutput = language.parse( + ProductionKind.SourceUnit, + document.getText() + ); + + const parseTree = parseOutput.parseTree; + + if (parseTree === null) { + logger.trace("Slang parsing error"); + const strings = parseOutput.errors.map((e) => + e.toErrorReport(uri, text, false) + ); + logger.trace(strings.join("")); + + return emptyResponse; + } + + // Register visitors + const builder = new SemanticTokensBuilder(document); + + const visitors = [ + new CustomTypeHighlighter(document, builder), + new KeywordHighlighter(document, builder), + new ElementaryTypeHighlighter(document, builder), + new NumberHighlighter(document, builder), + new StringHighlighter(document, builder), + new FunctionDefinitionHighlighter(document, builder), + new FunctionCallHighlighter(document, builder), + new EventEmissionHighlighter(document, builder), + new EventDefinitionHighlighter(document, builder), + new ContractDefinitionHighlighter(document, builder), + new InterfaceDefinitionHighlighter(document, builder), + new StructDefinitionHighlighter(document, builder), + ]; + + // Visit the CST + walk(parseTree, (node, ancestors) => { + for (const visitor of visitors) { + visitor.visit(node, ancestors); + } + }); + + return { data: builder.getTokenData() }; + } catch (error) { + serverState.logger.error(error); + return emptyResponse; + } + } + ) || emptyResponse + ); + }; +} diff --git a/server/src/services/semanticHighlight/slangHelpers.ts b/server/src/services/semanticHighlight/slangHelpers.ts new file mode 100644 index 00000000..ef26a6fc --- /dev/null +++ b/server/src/services/semanticHighlight/slangHelpers.ts @@ -0,0 +1,24 @@ +import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang"; +import _ from "lodash"; + +export type SlangNode = RuleNode | TokenNode; +export type NodeCallback = (node: SlangNode, ancestors: SlangNode[]) => void; + +export function walk( + node: SlangNode, + onNode: NodeCallback, + ancestors: SlangNode[] = [] +) { + onNode(node, ancestors); + + ancestors.push(node); + + const children: SlangNode[] = + node.type === NodeType.Rule ? node.children : []; + + for (const child of children) { + walk(child, onNode, ancestors); + } + + ancestors.pop(); +} diff --git a/server/src/services/semanticHighlight/tokenTypes.ts b/server/src/services/semanticHighlight/tokenTypes.ts new file mode 100644 index 00000000..23c8b18f --- /dev/null +++ b/server/src/services/semanticHighlight/tokenTypes.ts @@ -0,0 +1,29 @@ +import { SemanticTokenTypes } from "vscode-languageserver-types"; + +// Semantic token types that we support +export const tokensTypes = [ + SemanticTokenTypes.keyword, + SemanticTokenTypes.number, + SemanticTokenTypes.type, + SemanticTokenTypes.string, + SemanticTokenTypes.function, +]; + +const tokenTypesMap = tokensTypes.reduce( + (map: Record, tokenType, index) => { + map[tokenType] = index; + return map; + }, + {} +); + +// Helper function to get the index of our supported token types list +export function getTokenTypeIndex(token: string) { + const tokenType = tokenTypesMap[token]; + + if (tokenType === undefined) { + throw new Error(`Invalid token type requested: ${token}`); + } + + return tokenType; +} diff --git a/server/test/helpers/setupMockConnection.ts b/server/test/helpers/setupMockConnection.ts index 7731f2ff..b53d6747 100644 --- a/server/test/helpers/setupMockConnection.ts +++ b/server/test/helpers/setupMockConnection.ts @@ -26,6 +26,11 @@ export function setupMockConnection() { sendNotification: sinon.spy(), onCodeAction: sinon.spy(), onDocumentFormatting: sinon.spy(), + languages: { + semanticTokens: { + on: sinon.spy(), + }, + }, onNotification: sinon.fake( ( _method: string, diff --git a/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol b/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol new file mode 100644 index 00000000..443abb91 --- /dev/null +++ b/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.8; + +interface TestInterface {} + +struct TestStruct { + uint256 aNumber; + string aString; + address anAddress; +} + +contract testContract { + event TestEvent( + testContract contractAsEventParam, TestInterface interfaceAsEventParam, TestStruct structAsEventParam + ); + + testContract contractAsMember; + + TestInterface interfaceAsMember; + + TestStruct structAsMember; + + uint256 aNumber = 0x12 + 123; + + string aString = "asdf"; + + function testFunction( + testContract contractAsFuncParam, + TestInterface interfaceAsFuncParam, + TestStruct memory structAsFuncParam + ) public { + testContract contractAsLocalVar; + + TestInterface interfaceAsLocalVar; + TestStruct memory structAsLocalVar; + contractAsLocalVar; + contractAsFuncParam; + interfaceAsFuncParam; + interfaceAsLocalVar; + structAsFuncParam; + structAsLocalVar; + + emit TestEvent(contractAsLocalVar, interfaceAsLocalVar, structAsLocalVar); + + //ñññññ + + TestStruct memory afterUTF8; + afterUTF8; + anotherFunction(); + } + + function anotherFunction() public pure {} +} diff --git a/test/protocol/src/TestLanguageClient.ts b/test/protocol/src/TestLanguageClient.ts index 37bacf66..1fb5b3e7 100644 --- a/test/protocol/src/TestLanguageClient.ts +++ b/test/protocol/src/TestLanguageClient.ts @@ -34,6 +34,8 @@ import { ReferencesRequest, RenameParams, RenameRequest, + SemanticTokensParams, + SemanticTokensRequest, TypeDefinitionParams, TypeDefinitionRequest, } from 'vscode-languageserver-protocol/node' @@ -342,6 +344,16 @@ export class TestLanguageClient { return this.connection!.sendRequest(RenameRequest.type, params) } + public async getSemanticTokensFull(uri: string) { + const params: SemanticTokensParams = { + textDocument: { + uri, + }, + } + + return this.connection!.sendRequest(SemanticTokensRequest.type, params) + } + public async formatDocument(uri: string) { const params: DocumentFormattingParams = { textDocument: { diff --git a/test/protocol/test/initialize/data/initializeResult.json b/test/protocol/test/initialize/data/initializeResult.json index 4d37991d..1b18a5a5 100644 --- a/test/protocol/test/initialize/data/initializeResult.json +++ b/test/protocol/test/initialize/data/initializeResult.json @@ -18,6 +18,14 @@ "renameProvider": true, "codeActionProvider": true, "hoverProvider": true, + "semanticTokensProvider": { + "full": true, + "legend": { + "tokenModifiers": [], + "tokenTypes": ["keyword", "number", "type", "string", "function"] + }, + "range": false + }, "workspace": { "workspaceFolders": { "supported": true, diff --git a/test/protocol/test/textDocument/semanticTokens/full.test.ts b/test/protocol/test/textDocument/semanticTokens/full.test.ts new file mode 100644 index 00000000..cb5da1cf --- /dev/null +++ b/test/protocol/test/textDocument/semanticTokens/full.test.ts @@ -0,0 +1,40 @@ +import { expect } from 'chai' +import { test } from 'mocha' +import { TestLanguageClient } from '../../../src/TestLanguageClient' +import { getInitializedClient } from '../../client' +import { getProjectPath } from '../../helpers' +import { toUri } from '../../../src/helpers' + +let client!: TestLanguageClient + +describe('[hardhat] semanticTokens/full', () => { + let testPath: string + + before(async () => { + client = await getInitializedClient() + + testPath = getProjectPath('hardhat/contracts/semanticTokens/full/SemanticTokens.sol') + + await client.openDocument(testPath) + }) + + after(async () => { + client.closeAllDocuments() + }) + + test('provides highlighting for types, keywords, functions, numbers and strings', async function () { + const semanticTokens = await client.getSemanticTokensFull(toUri(testPath)) + + expect(semanticTokens).to.deep.equal({ + data: [ + 2, 0, 6, 0, 0, 0, 7, 8, 0, 0, 2, 0, 9, 0, 0, 0, 10, 13, 2, 0, 2, 0, 6, 0, 0, 0, 7, 10, 2, 0, 1, 4, 7, 2, 0, 1, + 4, 6, 2, 0, 0, 0, 6, 0, 0, 1, 0, 11, 2, 0, 0, 4, 7, 0, 0, 3, 0, 8, 0, 0, 0, 9, 12, 2, 0, 1, 4, 5, 0, 0, 0, 6, 9, + 2, 0, 1, 8, 12, 2, 0, 0, 35, 13, 2, 0, 0, 37, 10, 2, 0, 3, 4, 12, 2, 0, 2, 4, 13, 2, 0, 2, 4, 10, 2, 0, 2, 4, 7, + 2, 0, 0, 18, 4, 1, 0, 0, 7, 3, 1, 0, 2, 4, 6, 2, 0, 0, 0, 6, 0, 0, 0, 17, 6, 3, 0, 2, 4, 8, 0, 0, 0, 9, 12, 4, + 0, 1, 8, 12, 2, 0, 1, 8, 13, 2, 0, 1, 8, 10, 2, 0, 0, 11, 6, 0, 0, 1, 6, 6, 0, 0, 1, 8, 12, 2, 0, 2, 8, 13, 2, + 0, 1, 8, 10, 2, 0, 0, 11, 6, 0, 0, 8, 8, 4, 0, 0, 0, 5, 9, 2, 0, 4, 8, 10, 2, 0, 0, 11, 6, 0, 0, 2, 8, 15, 4, 0, + 3, 4, 8, 0, 0, 0, 9, 15, 4, 0, 0, 18, 6, 0, 0, 0, 7, 4, 0, 0, + ], + }) + }) +}) From 6db7e3db0c35f72b85b2e1a770433e87dc31daa9 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Tue, 23 May 2023 13:13:12 -0300 Subject: [PATCH 02/18] feat: documentSymbol - project outline --- package.json | 2 +- server/src/parser/slangHelpers.ts | 43 + server/src/server.ts | 2 + .../documentSymbol/SymbolTreeBuilder.ts | 31 + .../services/documentSymbol/SymbolVisitor.ts | 16 + .../documentSymbol/onDocumentSymbol.ts | 125 +++ .../visitors/ConstantDefinition.ts | 8 + .../visitors/ConstructorDefinition.ts | 18 + .../visitors/ContractDefinition.ts | 8 + .../visitors/DefinitionVisitor.ts | 52 ++ .../documentSymbol/visitors/EnumDefinition.ts | 8 + .../visitors/ErrorDefinition.ts | 8 + .../visitors/EventDefinition.ts | 8 + .../visitors/FallbackFunctionDefinition.ts | 18 + .../visitors/FunctionDefinition.ts | 8 + .../visitors/InterfaceDefinition.ts | 8 + .../visitors/LibraryDefinition.ts | 8 + .../visitors/ModifierDefinition.ts | 8 + .../visitors/ReceiveFunctionDefinition.ts | 18 + .../visitors/StateVariableDeclaration.ts | 8 + .../visitors/StructDefinition.ts | 8 + .../documentSymbol/visitors/StructMember.ts | 8 + .../UserDefinedValueTypeDefinition.ts | 8 + .../visitors/VariableDeclarationStatement.ts | 8 + .../services/initialization/onInitialize.ts | 1 + .../semanticHighlight/HighlightVisitor.ts | 7 +- .../SemanticTokensBuilder.ts | 2 +- .../ContractDefinitionHighlighter.ts | 4 +- .../highlighters/CustomTypeHighlighter.ts | 4 +- .../highlighters/ElementaryTypeHighlighter.ts | 4 +- .../EventDefinitionHighlighter.ts | 4 +- .../highlighters/EventEmissionHighlighter.ts | 4 +- .../highlighters/FunctionCallHighlighter.ts | 4 +- .../FunctionDefinitionHighlighter.ts | 4 +- .../InterfaceDefinitionHighlighter.ts | 4 +- .../highlighters/KeywordHighlighter.ts | 4 +- .../highlighters/LoggerVisitor.ts | 4 +- .../highlighters/NumberHighlighter.ts | 4 +- .../highlighters/StringHighlighter.ts | 4 +- .../StructDefinitionHighlighter.ts | 4 +- .../semanticHighlight/onSemanticTokensFull.ts | 18 +- .../semanticHighlight/slangHelpers.ts | 24 - server/test/helpers/setupMockConnection.ts | 1 + .../documentSymbol/DocumentSymbols.sol | 88 ++ test/protocol/src/TestLanguageClient.ts | 12 + .../initialize/data/initializeResult.json | 1 + .../documentSymbol/documentSymbol.test.ts | 862 ++++++++++++++++++ 47 files changed, 1446 insertions(+), 59 deletions(-) create mode 100644 server/src/parser/slangHelpers.ts create mode 100644 server/src/services/documentSymbol/SymbolTreeBuilder.ts create mode 100644 server/src/services/documentSymbol/SymbolVisitor.ts create mode 100644 server/src/services/documentSymbol/onDocumentSymbol.ts create mode 100644 server/src/services/documentSymbol/visitors/ConstantDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/ConstructorDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/ContractDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/DefinitionVisitor.ts create mode 100644 server/src/services/documentSymbol/visitors/EnumDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/ErrorDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/EventDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/FunctionDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/InterfaceDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/LibraryDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/ModifierDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts create mode 100644 server/src/services/documentSymbol/visitors/StructDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/StructMember.ts create mode 100644 server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts create mode 100644 server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts delete mode 100644 server/src/services/semanticHighlight/slangHelpers.ts create mode 100644 test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol create mode 100644 test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts diff --git a/package.json b/package.json index 1db18def..56e59d7a 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "url": "https://github.com/NomicFoundation/hardhat-vscode/issues" }, "scripts": { - "postinstall": "npm install --no-save --ignore-scripts --force @nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1", + "postinstall": "npm install --no-save --ignore-scripts --force @nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1 @nomicfoundation/slang-win32-ia32-msvc@0.4.0", "build": "tsc -b ./client/tsconfig.json && tsc -b ./server/tsconfig.build.json && tsc -b ./coc/tsconfig.json && tsc -b", "watch": "concurrently -n client,server \"tsc -b -w ./client/tsconfig.json\" \"tsc -b -w ./server/tsconfig.build.json\"", "test:unit": "npm -w server run test", diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts new file mode 100644 index 00000000..9f2d2d9d --- /dev/null +++ b/server/src/parser/slangHelpers.ts @@ -0,0 +1,43 @@ +import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang"; +import _ from "lodash"; +import { TextDocument } from "vscode-languageserver-textdocument"; +import { Range } from "vscode-languageserver-types"; + +export type SlangNode = RuleNode | TokenNode; +export type NodeCallback = (node: SlangNode, ancestors: SlangNode[]) => void; + +export function walk( + node: SlangNode, + onEnter: NodeCallback, + onExit: NodeCallback, + ancestors: SlangNode[] = [] +) { + onEnter(node, ancestors); + + ancestors.push(node); + + const children: SlangNode[] = + node.type === NodeType.Rule ? node.children : []; + + for (const child of children) { + walk(child, onEnter, onExit, ancestors); + } + + ancestors.pop(); + + onExit(node, ancestors); +} + +export function slangToVSCodeRange( + doc: TextDocument, + slangRange: number[] +): Range { + if (slangRange.length !== 2) { + throw new Error(`Invalid slang rage: ${slangRange}`); + } + + return { + start: doc.positionAt(slangRange[0]), + end: doc.positionAt(slangRange[1]), + }; +} diff --git a/server/src/server.ts b/server/src/server.ts index d0956241..c2e78b46 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -20,6 +20,7 @@ import { attachDocumentHooks } from "./services/documents/attachDocumentHooks"; import { availableVersions } from "./services/initialization/updateAvailableSolcVersions"; import { onDocumentFormatting } from "./services/formatting/onDocumentFormatting"; import { onSemanticTokensFull } from "./services/semanticHighlight/onSemanticTokensFull"; +import { onDocumentSymbol } from "./services/documentSymbol/onDocumentSymbol"; export default function setupServer( connection: Connection, @@ -99,6 +100,7 @@ function attachLanguageServerCommandHooks(serverState: ServerState) { connection.onCodeAction(onCodeAction(serverState)); connection.onHover(onHover(serverState)); connection.onDocumentFormatting(onDocumentFormatting(serverState)); + connection.onDocumentSymbol(onDocumentSymbol(serverState)); connection.languages.semanticTokens.on(onSemanticTokensFull(serverState)); } diff --git a/server/src/services/documentSymbol/SymbolTreeBuilder.ts b/server/src/services/documentSymbol/SymbolTreeBuilder.ts new file mode 100644 index 00000000..ba3a3bb6 --- /dev/null +++ b/server/src/services/documentSymbol/SymbolTreeBuilder.ts @@ -0,0 +1,31 @@ +import { DocumentSymbol } from "vscode-languageserver-types"; +import _ from "lodash"; + +export class SymbolTreeBuilder { + public symbols: DocumentSymbol[] = []; + public currentPath: Array> = []; + + public openSymbol(params: Partial) { + const symbol = { + children: [], + ...params, + }; + + this.currentPath.push(symbol); + } + + public closeSymbol() { + const symbol = this.currentPath.pop() as DocumentSymbol; + if (this.currentPath.length === 0) { + this.symbols.push(symbol); + } else { + const lastSymbol = _.last(this.currentPath); + + if (!lastSymbol) { + throw new Error("Attempting to close a symbol but none is open"); + } + + lastSymbol.children?.push(symbol); + } + } +} diff --git a/server/src/services/documentSymbol/SymbolVisitor.ts b/server/src/services/documentSymbol/SymbolVisitor.ts new file mode 100644 index 00000000..51b3176a --- /dev/null +++ b/server/src/services/documentSymbol/SymbolVisitor.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { TextDocument } from "vscode-languageserver-textdocument"; +import { SlangNode } from "../../parser/slangHelpers"; +import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; + +// Abstraction for a visitor that wants to build part of the document symbol tree +export abstract class SymbolVisitor { + constructor( + public document: TextDocument, + public symbolBuilder: SymbolTreeBuilder + ) {} + + public enter(node: SlangNode, ancestors: SlangNode[]): void {} + public exit(node: SlangNode, ancestors: SlangNode[]): void {} +} diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts new file mode 100644 index 00000000..77fc5b80 --- /dev/null +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -0,0 +1,125 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { DocumentSymbolParams } from "vscode-languageserver/node"; +import { DocumentSymbol, SymbolInformation } from "vscode-languageserver-types"; +import { analyze } from "@nomicfoundation/solidity-analyzer"; +import semver from "semver"; +import { Language, ProductionKind } from "@nomicfoundation/slang"; +import _ from "lodash"; +import { ServerState } from "../../types"; +import { walk } from "../../parser/slangHelpers"; +import { onCommand } from "../../utils/onCommand"; +import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; +import { StructDefinition } from "./visitors/StructDefinition"; +import { SymbolVisitor } from "./SymbolVisitor"; +import { StructMember } from "./visitors/StructMember"; +import { InterfaceDefinition } from "./visitors/InterfaceDefinition"; +import { FunctionDefinition } from "./visitors/FunctionDefinition"; +import { ContractDefinition } from "./visitors/ContractDefinition"; +import { EventDefinition } from "./visitors/EventDefinition"; +import { StateVariableDeclaration } from "./visitors/StateVariableDeclaration"; +import { VariableDeclarationStatement } from "./visitors/VariableDeclarationStatement"; +import { ConstantDefinition } from "./visitors/ConstantDefinition"; +import { ConstructorDefinition } from "./visitors/ConstructorDefinition"; +import { EnumDefinition } from "./visitors/EnumDefinition"; +import { ErrorDefinition } from "./visitors/ErrorDefinition"; +import { FallbackFunctionDefinition } from "./visitors/FallbackFunctionDefinition"; +import { LibraryDefinition } from "./visitors/LibraryDefinition"; +import { ModifierDefinition } from "./visitors/ModifierDefinition"; +import { ReceiveFunctionDefinition } from "./visitors/ReceiveFunctionDefinition"; +import { UserDefinedValueTypeDefinition } from "./visitors/UserDefinedValueTypeDefinition"; + +export function onDocumentSymbol(serverState: ServerState) { + return async ( + params: DocumentSymbolParams + ): Promise => { + return onCommand( + serverState, + "onDocumentSymbol", + params.textDocument.uri, + () => { + const { uri } = params.textDocument; + + const { logger, solcVersions } = serverState; + + // Find the file in the documents collection + const document = serverState.documents.get(uri); + + if (document === undefined) { + throw new Error(`Document not found: ${uri}`); + } + + const text = document.getText(); + + // Get the document's solidity version + const { versionPragmas } = analyze(text); + const solcVersion = + semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || + _.last(solcVersions); + + try { + // Parse using slang + const language = new Language(solcVersion!); + + const parseOutput = language.parse( + ProductionKind.SourceUnit, + document.getText() + ); + + const parseTree = parseOutput.parseTree; + + if (parseTree === null) { + logger.trace("Slang parsing error"); + const strings = parseOutput.errors.map((e) => + e.toErrorReport(uri, text, false) + ); + logger.trace(strings.join("")); + + return null; + } + + const builder = new SymbolTreeBuilder(); + + const visitors: SymbolVisitor[] = [ + new StructDefinition(document, builder), + new StructMember(document, builder), + new InterfaceDefinition(document, builder), + new FunctionDefinition(document, builder), + new ContractDefinition(document, builder), + new EventDefinition(document, builder), + new StateVariableDeclaration(document, builder), + new VariableDeclarationStatement(document, builder), + new ConstantDefinition(document, builder), + new ConstructorDefinition(document, builder), + new EnumDefinition(document, builder), + new ErrorDefinition(document, builder), + new FallbackFunctionDefinition(document, builder), + new LibraryDefinition(document, builder), + new ModifierDefinition(document, builder), + new ReceiveFunctionDefinition(document, builder), + new UserDefinedValueTypeDefinition(document, builder), + ]; + + walk( + parseTree, + (node, ancestors) => { + for (const visitor of visitors) { + visitor.enter(node, ancestors); + } + }, + (node, ancestors) => { + for (const visitor of visitors) { + visitor.exit(node, ancestors); + } + } + ); + + return builder.symbols; + } catch (error) { + logger.error(error); + return null; + } + } + ); + }; +} diff --git a/server/src/services/documentSymbol/visitors/ConstantDefinition.ts b/server/src/services/documentSymbol/visitors/ConstantDefinition.ts new file mode 100644 index 00000000..3a0677dd --- /dev/null +++ b/server/src/services/documentSymbol/visitors/ConstantDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class ConstantDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.ConstantDefinition; + public symbolKind = SymbolKind.Constant; +} diff --git a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts new file mode 100644 index 00000000..5434ae84 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts @@ -0,0 +1,18 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class ConstructorDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.ConstructorDefinition; + public symbolKind = SymbolKind.Constructor; + + protected getSymbolAttributes(node: SlangNode) { + return { + name: "constructor", + range: slangToVSCodeRange(this.document, node.charRange), + selectionRange: slangToVSCodeRange(this.document, node.charRange), + kind: this.symbolKind, + }; + } +} diff --git a/server/src/services/documentSymbol/visitors/ContractDefinition.ts b/server/src/services/documentSymbol/visitors/ContractDefinition.ts new file mode 100644 index 00000000..3084d372 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/ContractDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class ContractDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.ContractDefinition; + public symbolKind = SymbolKind.Class; +} diff --git a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts new file mode 100644 index 00000000..d1408a3f --- /dev/null +++ b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { DocumentSymbol, SymbolKind } from "vscode-languageserver-types"; +import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import _ from "lodash"; +import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { SymbolVisitor } from "../SymbolVisitor"; + +export abstract class DefinitionVisitor extends SymbolVisitor { + public abstract ruleKind: RuleKind; + public abstract symbolKind: SymbolKind; + + public enter(node: SlangNode, ancestors: SlangNode[]): void { + // Open a new symbol node on the DocumentSymbol tree on matching rules + if (node.type === NodeType.Rule && node.kind === this.ruleKind) { + this.symbolBuilder.openSymbol(this.getSymbolAttributes(node)); + } + + // Set the symbol node's range and name when finding the related identifier + if (node.type === NodeType.Token && node.kind === TokenKind.Identifier) { + const parent = _.last(ancestors)!; + + if (parent.type !== NodeType.Rule || parent.kind !== this.ruleKind) { + return; + } + + const lastSymbol = _.last(this.symbolBuilder.currentPath); + + if (lastSymbol === undefined) { + return; + } + + const identifierRange = slangToVSCodeRange(this.document, node.charRange); + + lastSymbol.name = this.document.getText(identifierRange); + lastSymbol.selectionRange = identifierRange; + } + } + + protected getSymbolAttributes(node: SlangNode): Partial { + return { + range: slangToVSCodeRange(this.document, node.charRange), + selectionRange: slangToVSCodeRange(this.document, node.charRange), + kind: this.symbolKind, + }; + } + + public exit(node: SlangNode): void { + if (node.type === NodeType.Rule && node.kind === this.ruleKind) { + this.symbolBuilder.closeSymbol(); + } + } +} diff --git a/server/src/services/documentSymbol/visitors/EnumDefinition.ts b/server/src/services/documentSymbol/visitors/EnumDefinition.ts new file mode 100644 index 00000000..e5ac7808 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/EnumDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class EnumDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.EnumDefinition; + public symbolKind = SymbolKind.Enum; +} diff --git a/server/src/services/documentSymbol/visitors/ErrorDefinition.ts b/server/src/services/documentSymbol/visitors/ErrorDefinition.ts new file mode 100644 index 00000000..610b2270 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/ErrorDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class ErrorDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.ErrorDefinition; + public symbolKind = SymbolKind.Event; +} diff --git a/server/src/services/documentSymbol/visitors/EventDefinition.ts b/server/src/services/documentSymbol/visitors/EventDefinition.ts new file mode 100644 index 00000000..6b18f1bf --- /dev/null +++ b/server/src/services/documentSymbol/visitors/EventDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class EventDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.EventDefinition; + public symbolKind = SymbolKind.Event; +} diff --git a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts new file mode 100644 index 00000000..64404a96 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts @@ -0,0 +1,18 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class FallbackFunctionDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.FallbackFunctionDefinition; + public symbolKind = SymbolKind.Function; + + protected getSymbolAttributes(node: SlangNode) { + return { + name: "fallback", + range: slangToVSCodeRange(this.document, node.charRange), + selectionRange: slangToVSCodeRange(this.document, node.charRange), + kind: this.symbolKind, + }; + } +} diff --git a/server/src/services/documentSymbol/visitors/FunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FunctionDefinition.ts new file mode 100644 index 00000000..a981d64b --- /dev/null +++ b/server/src/services/documentSymbol/visitors/FunctionDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class FunctionDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.FunctionDefinition; + public symbolKind = SymbolKind.Function; +} diff --git a/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts b/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts new file mode 100644 index 00000000..93980fac --- /dev/null +++ b/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class InterfaceDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.InterfaceDefinition; + public symbolKind = SymbolKind.Interface; +} diff --git a/server/src/services/documentSymbol/visitors/LibraryDefinition.ts b/server/src/services/documentSymbol/visitors/LibraryDefinition.ts new file mode 100644 index 00000000..6232b846 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/LibraryDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class LibraryDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.LibraryDefinition; + public symbolKind = SymbolKind.Class; +} diff --git a/server/src/services/documentSymbol/visitors/ModifierDefinition.ts b/server/src/services/documentSymbol/visitors/ModifierDefinition.ts new file mode 100644 index 00000000..bac07f48 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/ModifierDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class ModifierDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.ModifierDefinition; + public symbolKind = SymbolKind.Function; +} diff --git a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts new file mode 100644 index 00000000..78c5bc45 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts @@ -0,0 +1,18 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class ReceiveFunctionDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.ReceiveFunctionDefinition; + public symbolKind = SymbolKind.Function; + + protected getSymbolAttributes(node: SlangNode) { + return { + name: "receive", + range: slangToVSCodeRange(this.document, node.charRange), + selectionRange: slangToVSCodeRange(this.document, node.charRange), + kind: this.symbolKind, + }; + } +} diff --git a/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts b/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts new file mode 100644 index 00000000..7f89033a --- /dev/null +++ b/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class StateVariableDeclaration extends DefinitionVisitor { + public ruleKind = RuleKind.StateVariableDeclaration; + public symbolKind = SymbolKind.Property; +} diff --git a/server/src/services/documentSymbol/visitors/StructDefinition.ts b/server/src/services/documentSymbol/visitors/StructDefinition.ts new file mode 100644 index 00000000..0b064799 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/StructDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class StructDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.StructDefinition; + public symbolKind = SymbolKind.Struct; +} diff --git a/server/src/services/documentSymbol/visitors/StructMember.ts b/server/src/services/documentSymbol/visitors/StructMember.ts new file mode 100644 index 00000000..febf343b --- /dev/null +++ b/server/src/services/documentSymbol/visitors/StructMember.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class StructMember extends DefinitionVisitor { + public ruleKind = RuleKind.StructMember; + public symbolKind = SymbolKind.Property; +} diff --git a/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts b/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts new file mode 100644 index 00000000..e2270962 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class UserDefinedValueTypeDefinition extends DefinitionVisitor { + public ruleKind = RuleKind.UserDefinedValueTypeDefinition; + public symbolKind = SymbolKind.TypeParameter; +} diff --git a/server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts b/server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts new file mode 100644 index 00000000..2a895995 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class VariableDeclarationStatement extends DefinitionVisitor { + public ruleKind = RuleKind.VariableDeclarationStatement; + public symbolKind = SymbolKind.Variable; +} diff --git a/server/src/services/initialization/onInitialize.ts b/server/src/services/initialization/onInitialize.ts index 889bf55b..c3874968 100644 --- a/server/src/services/initialization/onInitialize.ts +++ b/server/src/services/initialization/onInitialize.ts @@ -88,6 +88,7 @@ export const onInitialize = (serverState: ServerState) => { range: false, full: true, }, + documentSymbolProvider: true, workspace: { workspaceFolders: { supported: false, diff --git a/server/src/services/semanticHighlight/HighlightVisitor.ts b/server/src/services/semanticHighlight/HighlightVisitor.ts index 73fff4de..25c0e10d 100644 --- a/server/src/services/semanticHighlight/HighlightVisitor.ts +++ b/server/src/services/semanticHighlight/HighlightVisitor.ts @@ -1,6 +1,8 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { TextDocument } from "vscode-languageserver-textdocument"; +import { SlangNode } from "../../parser/slangHelpers"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; -import { SlangNode } from "./slangHelpers"; // Abstraction for a visitor that wants to highlight tokens export abstract class HighlightVisitor { @@ -9,5 +11,6 @@ export abstract class HighlightVisitor { public tokenBuilder: SemanticTokensBuilder ) {} - public abstract visit(node: SlangNode, ancestors: SlangNode[]): void; + public enter(node: SlangNode, ancestors: SlangNode[]): void {} + public exit(node: SlangNode, ancestors: SlangNode[]): void {} } diff --git a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts index da1ebf8a..ed5844a0 100644 --- a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts +++ b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TextDocument } from "vscode-languageserver-textdocument"; +import { SlangNode } from "../../parser/slangHelpers"; import { getTokenTypeIndex } from "./tokenTypes"; -import { SlangNode } from "./slangHelpers"; // Helps building a SemanticTokens response by providing slang nodes and supported token types export class SemanticTokensBuilder { diff --git a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts index 6c732ceb..8de45379 100644 --- a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights contract definitions export class ContractDefinitionHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts index dc4ed48b..f92751d6 100644 --- a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights custom type names export class CustomTypeHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts index 6855fe2e..0cce0d2a 100644 --- a/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights elementary type names export class ElementaryTypeHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if (node.type === NodeType.Rule && node.kind === RuleKind.ElementaryType) { this.tokenBuilder.addToken(node, SemanticTokenTypes.type); } diff --git a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts index 473201b3..b3999740 100644 --- a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights event definitions export class EventDefinitionHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts index 6fa266a4..aab48d73 100644 --- a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights event emissions export class EventEmissionHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts index b8516e70..4a0e21d2 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights function calls export class FunctionCallHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts index 15c95d48..6191d154 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights function definitions export class FunctionDefinitionHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts index 4035abb1..e516d55a 100644 --- a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights interface definitions export class InterfaceDefinitionHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts index d793ae35..aaf533a8 100644 --- a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts @@ -1,7 +1,7 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; const keywordKinds = new Set([ TokenKind.AbicoderKeyword, @@ -90,7 +90,7 @@ const keywordKinds = new Set([ // Highlights keywords export class KeywordHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if (node.type === NodeType.Token && keywordKinds.has(node.kind)) { this.tokenBuilder.addToken(node, SemanticTokenTypes.keyword); } diff --git a/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts b/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts index e5309e19..5b3b40f0 100644 --- a/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts +++ b/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts @@ -1,7 +1,7 @@ import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import _ from "lodash"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; const nodeTypeMap = _.invert(NodeType); const ruleKindMap = _.invert(RuleKind); @@ -9,7 +9,7 @@ const tokenKindMap = _.invert(TokenKind); // Visitor that logs the tree as indented text export class Logger extends HighlightVisitor { - public visit(node: SlangNode, ancestors: SlangNode[]): void { + public enter(node: SlangNode, ancestors: SlangNode[]): void { const nodeText = JSON.stringify( _.truncate( this.document diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts index 5e03a300..c11522c2 100644 --- a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts @@ -1,7 +1,7 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; const numberKinds = new Set([ TokenKind.HexLiteral, @@ -11,7 +11,7 @@ const numberKinds = new Set([ // Highlights numbers export class NumberHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if (node.type === NodeType.Token && numberKinds.has(node.kind)) { this.tokenBuilder.addToken(node, SemanticTokenTypes.number); } diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts index c2b6445e..8f4713c4 100644 --- a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts @@ -1,7 +1,7 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; const stringKinds = new Set([ TokenKind.HexStringLiteral, @@ -15,7 +15,7 @@ const stringKinds = new Set([ // Highlights strings export class StringHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if (node.type === NodeType.Token && stringKinds.has(node.kind)) { this.tokenBuilder.addToken(node, SemanticTokenTypes.string); } diff --git a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts index 8a79ae0d..c3f6c370 100644 --- a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts @@ -1,11 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../slangHelpers"; +import { SlangNode } from "../../../parser/slangHelpers"; // Highlights struct definitions export class StructDefinitionHighlighter extends HighlightVisitor { - public visit(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(node: SlangNode, _ancestors: SlangNode[]): void { if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index a0de6749..d2015daf 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -10,6 +10,7 @@ import { analyze } from "@nomicfoundation/solidity-analyzer"; import semver from "semver"; import { ServerState } from "../../types"; import { onCommand } from "../../utils/onCommand"; +import { walk } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; import { KeywordHighlighter } from "./highlighters/KeywordHighlighter"; @@ -23,7 +24,6 @@ import { EventDefinitionHighlighter } from "./highlighters/EventDefinitionHighli import { ContractDefinitionHighlighter } from "./highlighters/ContractDefinitionHighlighter"; import { InterfaceDefinitionHighlighter } from "./highlighters/InterfaceDefinitionHighlighter"; import { StructDefinitionHighlighter } from "./highlighters/StructDefinitionHighlighter"; -import { walk } from "./slangHelpers"; const emptyResponse: SemanticTokens = { data: [] }; @@ -93,11 +93,19 @@ export function onSemanticTokensFull(serverState: ServerState) { ]; // Visit the CST - walk(parseTree, (node, ancestors) => { - for (const visitor of visitors) { - visitor.visit(node, ancestors); + walk( + parseTree, + (node, ancestors) => { + for (const visitor of visitors) { + visitor.enter(node, ancestors); + } + }, + (node, ancestors) => { + for (const visitor of visitors) { + visitor.exit(node, ancestors); + } } - }); + ); return { data: builder.getTokenData() }; } catch (error) { diff --git a/server/src/services/semanticHighlight/slangHelpers.ts b/server/src/services/semanticHighlight/slangHelpers.ts deleted file mode 100644 index ef26a6fc..00000000 --- a/server/src/services/semanticHighlight/slangHelpers.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang"; -import _ from "lodash"; - -export type SlangNode = RuleNode | TokenNode; -export type NodeCallback = (node: SlangNode, ancestors: SlangNode[]) => void; - -export function walk( - node: SlangNode, - onNode: NodeCallback, - ancestors: SlangNode[] = [] -) { - onNode(node, ancestors); - - ancestors.push(node); - - const children: SlangNode[] = - node.type === NodeType.Rule ? node.children : []; - - for (const child of children) { - walk(child, onNode, ancestors); - } - - ancestors.pop(); -} diff --git a/server/test/helpers/setupMockConnection.ts b/server/test/helpers/setupMockConnection.ts index b53d6747..767abde2 100644 --- a/server/test/helpers/setupMockConnection.ts +++ b/server/test/helpers/setupMockConnection.ts @@ -26,6 +26,7 @@ export function setupMockConnection() { sendNotification: sinon.spy(), onCodeAction: sinon.spy(), onDocumentFormatting: sinon.spy(), + onDocumentSymbol: sinon.spy(), languages: { semanticTokens: { on: sinon.spy(), diff --git a/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol b/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol new file mode 100644 index 00000000..d09e073e --- /dev/null +++ b/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.8; + +type CustomType is uint256; + +interface TestInterface { + function interfaceFunction(uint256 param) external returns (string memory); +} + +struct TestStruct { + uint256 aNumber; + string aString; + address anAddress; +} + +struct TestStruct2 { + uint256 aNumber; + string aString; + address anAddress; +} + +uint256 constant aConstant = 1234; + +enum Name { + A, + B +} + +error CustomError(); + +library TestLibrary {} + +contract testContract { + constructor() { + uint256 local = aConstant; + local; + } + + modifier testModifier() { + _; + } + + event TestEvent( + testContract contractAsEventParam, TestInterface interfaceAsEventParam, TestStruct structAsEventParam + ); + + testContract contractAsMember; + + TestInterface interfaceAsMember; + + TestStruct structAsMember; + + uint256 aNumber = 0x12 + 123; + + string aString = "asdf"; + + function testFunction( + testContract contractAsFuncParam, + TestInterface interfaceAsFuncParam, + TestStruct memory structAsFuncParam + ) public { + testContract contractAsLocalVar; + + TestInterface interfaceAsLocalVar; + TestStruct memory structAsLocalVar; + contractAsLocalVar; + contractAsFuncParam; + interfaceAsFuncParam; + interfaceAsLocalVar; + structAsFuncParam; + structAsLocalVar; + + emit TestEvent(contractAsLocalVar, interfaceAsLocalVar, structAsLocalVar); + + //ñññññ + + TestStruct memory afterUTF8; + afterUTF8; + anotherFunction(); + } + + function anotherFunction() public pure {} + + fallback() external {} + + receive() external payable {} +} diff --git a/test/protocol/src/TestLanguageClient.ts b/test/protocol/src/TestLanguageClient.ts index 1fb5b3e7..15df0780 100644 --- a/test/protocol/src/TestLanguageClient.ts +++ b/test/protocol/src/TestLanguageClient.ts @@ -21,6 +21,8 @@ import { DidOpenTextDocumentParams, DocumentFormattingParams, DocumentFormattingRequest, + DocumentSymbolParams, + DocumentSymbolRequest, ImplementationParams, ImplementationRequest, InitializedNotification, @@ -354,6 +356,16 @@ export class TestLanguageClient { return this.connection!.sendRequest(SemanticTokensRequest.type, params) } + public async getDocumentSymbols(uri: string) { + const params: DocumentSymbolParams = { + textDocument: { + uri, + }, + } + + return this.connection!.sendRequest(DocumentSymbolRequest.type, params) + } + public async formatDocument(uri: string) { const params: DocumentFormattingParams = { textDocument: { diff --git a/test/protocol/test/initialize/data/initializeResult.json b/test/protocol/test/initialize/data/initializeResult.json index 1b18a5a5..aaa1cf6c 100644 --- a/test/protocol/test/initialize/data/initializeResult.json +++ b/test/protocol/test/initialize/data/initializeResult.json @@ -18,6 +18,7 @@ "renameProvider": true, "codeActionProvider": true, "hoverProvider": true, + "documentSymbolProvider": true, "semanticTokensProvider": { "full": true, "legend": { diff --git a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts new file mode 100644 index 00000000..54d05694 --- /dev/null +++ b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts @@ -0,0 +1,862 @@ +import { expect } from 'chai' +import { test } from 'mocha' +import { TestLanguageClient } from '../../../src/TestLanguageClient' +import { getInitializedClient } from '../../client' +import { getProjectPath } from '../../helpers' +import { toUri } from '../../../src/helpers' + +let client!: TestLanguageClient + +describe('[hardhat] documentSymbol', () => { + let testPath: string + + before(async () => { + client = await getInitializedClient() + + testPath = getProjectPath('hardhat/contracts/documentSymbol/DocumentSymbols.sol') + + await client.openDocument(testPath) + }) + + after(async () => { + client.closeAllDocuments() + }) + + test('provides all the document symbols', async function () { + const symbols = await client.getDocumentSymbols(toUri(testPath)) + + expect(symbols).to.deep.equal([ + { + kind: 26, + children: [], + range: { + start: { + line: 3, + character: 0, + }, + end: { + line: 5, + character: 0, + }, + }, + selectionRange: { + start: { + line: 4, + character: 5, + }, + end: { + line: 4, + character: 15, + }, + }, + name: 'CustomType', + }, + { + kind: 11, + children: [ + { + kind: 12, + children: [], + range: { + start: { + line: 7, + character: 0, + }, + end: { + line: 8, + character: 0, + }, + }, + selectionRange: { + start: { + line: 7, + character: 13, + }, + end: { + line: 7, + character: 30, + }, + }, + name: 'interfaceFunction', + }, + ], + range: { + start: { + line: 5, + character: 0, + }, + end: { + line: 9, + character: 0, + }, + }, + selectionRange: { + start: { + line: 6, + character: 10, + }, + end: { + line: 6, + character: 23, + }, + }, + name: 'TestInterface', + }, + { + kind: 23, + children: [ + { + kind: 7, + children: [], + range: { + start: { + line: 11, + character: 4, + }, + end: { + line: 12, + character: 0, + }, + }, + selectionRange: { + start: { + line: 11, + character: 12, + }, + end: { + line: 11, + character: 19, + }, + }, + name: 'aNumber', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 12, + character: 4, + }, + end: { + line: 13, + character: 0, + }, + }, + selectionRange: { + start: { + line: 12, + character: 11, + }, + end: { + line: 12, + character: 18, + }, + }, + name: 'aString', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 13, + character: 0, + }, + end: { + line: 14, + character: 0, + }, + }, + selectionRange: { + start: { + line: 13, + character: 12, + }, + end: { + line: 13, + character: 21, + }, + }, + name: 'anAddress', + }, + ], + range: { + start: { + line: 9, + character: 0, + }, + end: { + line: 15, + character: 0, + }, + }, + selectionRange: { + start: { + line: 10, + character: 7, + }, + end: { + line: 10, + character: 17, + }, + }, + name: 'TestStruct', + }, + { + kind: 23, + children: [ + { + kind: 7, + children: [], + range: { + start: { + line: 17, + character: 4, + }, + end: { + line: 18, + character: 0, + }, + }, + selectionRange: { + start: { + line: 17, + character: 12, + }, + end: { + line: 17, + character: 19, + }, + }, + name: 'aNumber', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 18, + character: 4, + }, + end: { + line: 19, + character: 0, + }, + }, + selectionRange: { + start: { + line: 18, + character: 11, + }, + end: { + line: 18, + character: 18, + }, + }, + name: 'aString', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 19, + character: 0, + }, + end: { + line: 20, + character: 0, + }, + }, + selectionRange: { + start: { + line: 19, + character: 12, + }, + end: { + line: 19, + character: 21, + }, + }, + name: 'anAddress', + }, + ], + range: { + start: { + line: 15, + character: 0, + }, + end: { + line: 21, + character: 0, + }, + }, + selectionRange: { + start: { + line: 16, + character: 7, + }, + end: { + line: 16, + character: 18, + }, + }, + name: 'TestStruct2', + }, + { + kind: 14, + children: [], + range: { + start: { + line: 22, + character: 0, + }, + end: { + line: 23, + character: 0, + }, + }, + selectionRange: { + start: { + line: 22, + character: 17, + }, + end: { + line: 22, + character: 26, + }, + }, + name: 'aConstant', + }, + { + kind: 10, + children: [], + range: { + start: { + line: 23, + character: 0, + }, + end: { + line: 28, + character: 0, + }, + }, + selectionRange: { + start: { + line: 24, + character: 5, + }, + end: { + line: 24, + character: 9, + }, + }, + name: 'Name', + }, + { + kind: 24, + children: [], + range: { + start: { + line: 28, + character: 0, + }, + end: { + line: 30, + character: 0, + }, + }, + selectionRange: { + start: { + line: 29, + character: 6, + }, + end: { + line: 29, + character: 17, + }, + }, + name: 'CustomError', + }, + { + kind: 5, + children: [], + range: { + start: { + line: 30, + character: 0, + }, + end: { + line: 32, + character: 0, + }, + }, + selectionRange: { + start: { + line: 31, + character: 8, + }, + end: { + line: 31, + character: 19, + }, + }, + name: 'TestLibrary', + }, + { + kind: 5, + children: [ + { + kind: 9, + children: [ + { + kind: 13, + children: [], + range: { + start: { + line: 35, + character: 8, + }, + end: { + line: 36, + character: 0, + }, + }, + selectionRange: { + start: { + line: 35, + character: 16, + }, + end: { + line: 35, + character: 21, + }, + }, + name: 'local', + }, + ], + name: 'constructor', + range: { + start: { + line: 34, + character: 0, + }, + end: { + line: 38, + character: 0, + }, + }, + selectionRange: { + start: { + line: 34, + character: 0, + }, + end: { + line: 38, + character: 0, + }, + }, + }, + { + kind: 12, + children: [], + range: { + start: { + line: 38, + character: 0, + }, + end: { + line: 42, + character: 0, + }, + }, + selectionRange: { + start: { + line: 39, + character: 13, + }, + end: { + line: 39, + character: 25, + }, + }, + name: 'testModifier', + }, + { + kind: 24, + children: [], + range: { + start: { + line: 42, + character: 0, + }, + end: { + line: 46, + character: 0, + }, + }, + selectionRange: { + start: { + line: 43, + character: 10, + }, + end: { + line: 43, + character: 19, + }, + }, + name: 'TestEvent', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 46, + character: 0, + }, + end: { + line: 48, + character: 0, + }, + }, + selectionRange: { + start: { + line: 47, + character: 17, + }, + end: { + line: 47, + character: 33, + }, + }, + name: 'contractAsMember', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 48, + character: 0, + }, + end: { + line: 50, + character: 0, + }, + }, + selectionRange: { + start: { + line: 49, + character: 18, + }, + end: { + line: 49, + character: 35, + }, + }, + name: 'interfaceAsMember', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 50, + character: 0, + }, + end: { + line: 52, + character: 0, + }, + }, + selectionRange: { + start: { + line: 51, + character: 15, + }, + end: { + line: 51, + character: 29, + }, + }, + name: 'structAsMember', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 53, + character: 4, + }, + end: { + line: 54, + character: 0, + }, + }, + selectionRange: { + start: { + line: 53, + character: 12, + }, + end: { + line: 53, + character: 19, + }, + }, + name: 'aNumber', + }, + { + kind: 7, + children: [], + range: { + start: { + line: 55, + character: 4, + }, + end: { + line: 56, + character: 0, + }, + }, + selectionRange: { + start: { + line: 55, + character: 11, + }, + end: { + line: 55, + character: 18, + }, + }, + name: 'aString', + }, + { + kind: 12, + children: [ + { + kind: 13, + children: [], + range: { + start: { + line: 62, + character: 0, + }, + end: { + line: 63, + character: 0, + }, + }, + selectionRange: { + start: { + line: 62, + character: 21, + }, + end: { + line: 62, + character: 39, + }, + }, + name: 'contractAsLocalVar', + }, + { + kind: 13, + children: [], + range: { + start: { + line: 63, + character: 0, + }, + end: { + line: 65, + character: 0, + }, + }, + selectionRange: { + start: { + line: 64, + character: 22, + }, + end: { + line: 64, + character: 41, + }, + }, + name: 'interfaceAsLocalVar', + }, + { + kind: 13, + children: [], + range: { + start: { + line: 65, + character: 0, + }, + end: { + line: 66, + character: 0, + }, + }, + selectionRange: { + start: { + line: 65, + character: 26, + }, + end: { + line: 65, + character: 42, + }, + }, + name: 'structAsLocalVar', + }, + { + kind: 13, + children: [], + range: { + start: { + line: 74, + character: 0, + }, + end: { + line: 78, + character: 0, + }, + }, + selectionRange: { + start: { + line: 77, + character: 26, + }, + end: { + line: 77, + character: 35, + }, + }, + name: 'afterUTF8', + }, + ], + range: { + start: { + line: 56, + character: 0, + }, + end: { + line: 81, + character: 0, + }, + }, + selectionRange: { + start: { + line: 57, + character: 13, + }, + end: { + line: 57, + character: 25, + }, + }, + name: 'testFunction', + }, + { + kind: 12, + children: [], + range: { + start: { + line: 81, + character: 0, + }, + end: { + line: 83, + character: 0, + }, + }, + selectionRange: { + start: { + line: 82, + character: 13, + }, + end: { + line: 82, + character: 28, + }, + }, + name: 'anotherFunction', + }, + { + kind: 12, + children: [], + name: 'fallback', + range: { + start: { + line: 83, + character: 0, + }, + end: { + line: 85, + character: 0, + }, + }, + selectionRange: { + start: { + line: 83, + character: 0, + }, + end: { + line: 85, + character: 0, + }, + }, + }, + { + kind: 12, + children: [], + name: 'receive', + range: { + start: { + line: 85, + character: 0, + }, + end: { + line: 87, + character: 0, + }, + }, + selectionRange: { + start: { + line: 85, + character: 0, + }, + end: { + line: 87, + character: 0, + }, + }, + }, + ], + range: { + start: { + line: 32, + character: 0, + }, + end: { + line: 88, + character: 0, + }, + }, + selectionRange: { + start: { + line: 33, + character: 9, + }, + end: { + line: 33, + character: 21, + }, + }, + name: 'testContract', + }, + ]) + }) +}) From 59d1055afd2e5526df7713538bfbdf8f791f1d6e Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Tue, 24 Oct 2023 09:24:21 -0300 Subject: [PATCH 03/18] chore: migrate to slang 0.10.1 semantic highlight --- package-lock.json | 160 +++++++++--------- package.json | 2 +- server/package.json | 2 +- server/src/parser/slangHelpers.ts | 41 ++--- .../services/documentSymbol/SymbolVisitor.ts | 9 +- .../documentSymbol/onDocumentSymbol.ts | 20 ++- .../visitors/ConstantDefinition.ts | 2 +- .../visitors/ConstructorDefinition.ts | 11 +- .../visitors/ContractDefinition.ts | 2 +- .../visitors/DefinitionVisitor.ts | 27 ++- .../documentSymbol/visitors/EnumDefinition.ts | 2 +- .../visitors/ErrorDefinition.ts | 2 +- .../visitors/EventDefinition.ts | 2 +- .../visitors/FallbackFunctionDefinition.ts | 11 +- .../visitors/FunctionDefinition.ts | 2 +- .../visitors/InterfaceDefinition.ts | 2 +- .../visitors/LibraryDefinition.ts | 2 +- .../visitors/ModifierDefinition.ts | 2 +- .../visitors/ReceiveFunctionDefinition.ts | 11 +- .../visitors/StateVariableDeclaration.ts | 4 +- .../visitors/StructDefinition.ts | 2 +- .../documentSymbol/visitors/StructMember.ts | 2 +- .../UserDefinedValueTypeDefinition.ts | 2 +- .../visitors/VariableDeclaration.ts | 8 + .../visitors/VariableDeclarationStatement.ts | 8 - .../semanticHighlight/HighlightVisitor.ts | 6 +- .../SemanticTokensBuilder.ts | 8 +- .../ContractDefinitionHighlighter.ts | 13 +- .../highlighters/CustomTypeHighlighter.ts | 13 +- .../highlighters/ElementaryTypeHighlighter.ts | 13 -- .../EventDefinitionHighlighter.ts | 13 +- .../highlighters/EventEmissionHighlighter.ts | 13 +- .../highlighters/FunctionCallHighlighter.ts | 14 +- .../FunctionDefinitionHighlighter.ts | 13 +- .../InterfaceDefinitionHighlighter.ts | 13 +- .../highlighters/KeywordHighlighter.ts | 15 +- .../highlighters/LoggerVisitor.ts | 34 ---- .../highlighters/NumberHighlighter.ts | 10 +- .../highlighters/StringHighlighter.ts | 14 +- .../StructDefinitionHighlighter.ts | 13 +- .../semanticHighlight/onSemanticTokensFull.ts | 18 +- .../documentSymbol/documentSymbol.test.ts | 106 ++++++------ .../textDocument/semanticTokens/full.test.ts | 14 +- 43 files changed, 334 insertions(+), 347 deletions(-) create mode 100644 server/src/services/documentSymbol/visitors/VariableDeclaration.ts delete mode 100644 server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts delete mode 100644 server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts delete mode 100644 server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts diff --git a/package-lock.json b/package-lock.json index fe88b5ef..316bd954 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2662,28 +2662,28 @@ } }, "node_modules/@nomicfoundation/slang": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.4.0.tgz", - "integrity": "sha512-cA6E4RialoC1x44PPK1SQ0RWqhnPSy1NMp5/LUvWk4SXzOzOgMdCW/GWy7/E17aY+4mbVC6AjTJb+aKvV57Uqw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.10.1.tgz", + "integrity": "sha512-qU9eHCExF6Hix4KPXHv1oQN1vWYGeOrHJXZ+uxroCMZ4Tf5P/kSrxVI2SqnVmwpSdxrwJdDQ9cI8Fe6PV7YDCA==", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@nomicfoundation/slang-darwin-arm64": "0.4.0", - "@nomicfoundation/slang-darwin-x64": "0.4.0", - "@nomicfoundation/slang-linux-arm64-gnu": "0.4.0", - "@nomicfoundation/slang-linux-arm64-musl": "0.4.0", - "@nomicfoundation/slang-linux-x64-gnu": "0.4.0", - "@nomicfoundation/slang-linux-x64-musl": "0.4.0", - "@nomicfoundation/slang-win32-arm64-msvc": "0.4.0", - "@nomicfoundation/slang-win32-ia32-msvc": "0.4.0", - "@nomicfoundation/slang-win32-x64-msvc": "0.4.0" + "@nomicfoundation/slang-darwin-arm64": "0.10.1", + "@nomicfoundation/slang-darwin-x64": "0.10.1", + "@nomicfoundation/slang-linux-arm64-gnu": "0.10.1", + "@nomicfoundation/slang-linux-arm64-musl": "0.10.1", + "@nomicfoundation/slang-linux-x64-gnu": "0.10.1", + "@nomicfoundation/slang-linux-x64-musl": "0.10.1", + "@nomicfoundation/slang-win32-arm64-msvc": "0.10.1", + "@nomicfoundation/slang-win32-ia32-msvc": "0.10.1", + "@nomicfoundation/slang-win32-x64-msvc": "0.10.1" } }, "node_modules/@nomicfoundation/slang-darwin-arm64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-arm64/-/slang-darwin-arm64-0.4.0.tgz", - "integrity": "sha512-HPE+Whj93rfR9DlhQjqlCpAgKCflxW/7VAtB4X2TNRepfF28E2Mvyfg2GXzGzf2FNH9GCv8/c2vLsxyrm/v+MQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-arm64/-/slang-darwin-arm64-0.10.1.tgz", + "integrity": "sha512-lvXEs9qQS5Qm0vUhy5NwTlvkTMH7cbm2//Z80jhaBqIJlQFUW/0A4JqK/9RAji8ZuOtGC2QT9LnOk2VoNQGiwg==", "cpu": [ "arm64" ], @@ -2696,9 +2696,9 @@ } }, "node_modules/@nomicfoundation/slang-darwin-x64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-x64/-/slang-darwin-x64-0.4.0.tgz", - "integrity": "sha512-/Lo8Pg5K9qJXku5bCvwXt3axy2wRnAffd71n8CKzkCXIul5KwFFYY3fg0/o/WjWgLw+PodvPqh1g3Yrdf0/RCw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-x64/-/slang-darwin-x64-0.10.1.tgz", + "integrity": "sha512-YV8OZyA++MZebNc5j62bXvQKKT8x6jg5kR2O332HHLZb/5kYWJ0PQYw61W9/DwnFFhBwVT65AZ+cJdM0LQJxog==", "cpu": [ "x64" ], @@ -2711,9 +2711,9 @@ } }, "node_modules/@nomicfoundation/slang-linux-arm64-gnu": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-gnu/-/slang-linux-arm64-gnu-0.4.0.tgz", - "integrity": "sha512-ZE5d/Cp+viPD3dtLkTz2kTzSD5E1cZiKvZA8qI5UsqH8oX2eW/I/2SOavmQ4toSUCcqgqnYJUhvjQc1k/oiVeg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-gnu/-/slang-linux-arm64-gnu-0.10.1.tgz", + "integrity": "sha512-6vJmGMa6yvrf5PvO9lxnd5mfFtrP+1eTVhNBjbAazaSFKfpDrjR4b29JyLwREcialj/+L3prvEUjXRchIj1Gqg==", "cpu": [ "arm64" ], @@ -2726,9 +2726,9 @@ } }, "node_modules/@nomicfoundation/slang-linux-arm64-musl": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-musl/-/slang-linux-arm64-musl-0.4.0.tgz", - "integrity": "sha512-wk4lfa4ap+Pzz09W8HSA56ZsxdFpNOdE0KfBAPgbE7/6pHhyMt6IqsI+TDsHQSzRJ00+17//e5LrZGkV9+mw0A==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-musl/-/slang-linux-arm64-musl-0.10.1.tgz", + "integrity": "sha512-cPX5ybj6A46mb9pros8Kwpzv/Lpkp2q+gS+s8Zg62NkogvSqswDZDlBAZ7FUZTzGpMfhZJn75itPgGUCAoImUA==", "cpu": [ "arm64" ], @@ -2741,9 +2741,9 @@ } }, "node_modules/@nomicfoundation/slang-linux-x64-gnu": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-gnu/-/slang-linux-x64-gnu-0.4.0.tgz", - "integrity": "sha512-VVTk06QpWCtfsF727AvNBXelg6ObwATkjiOnNRIiM2CVZi9+UlcqxdnoVnxL5mkoAhXS59xArDQ6C+x4qWCL2Q==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-gnu/-/slang-linux-x64-gnu-0.10.1.tgz", + "integrity": "sha512-sZvtSgb5LencgjqfLkFlhrYQgM8QoSy2t0IcCG1JLA8MXWlYRN/F3nDm3zKSmuOwmtFIzvbc7X/wkkGCNsSQ9g==", "cpu": [ "x64" ], @@ -2756,9 +2756,9 @@ } }, "node_modules/@nomicfoundation/slang-linux-x64-musl": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-musl/-/slang-linux-x64-musl-0.4.0.tgz", - "integrity": "sha512-6RjyR7prrIMOp1378OiU73HD8wwvvyR850eKgkTRqJyNvFXd4pELc4ERE+BNUlwt2Ab86T+cMMbcP1T0yeRorg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-musl/-/slang-linux-x64-musl-0.10.1.tgz", + "integrity": "sha512-S2WnY4tMCWBPaAQcPoRDZ6M01hKgE1R22n7sqnF7KI2o0Khsm9fnlAZ/2ct4ioe2Je4hyHrNhms8/534P8dQuw==", "cpu": [ "x64" ], @@ -2771,9 +2771,9 @@ } }, "node_modules/@nomicfoundation/slang-win32-arm64-msvc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-arm64-msvc/-/slang-win32-arm64-msvc-0.4.0.tgz", - "integrity": "sha512-9UlbedxBV+Btjku6m09kJ/pR3pxvw2b+gQthklGdvVPUzPn2EdWttiAMlMLgTNM93Lf1KfjAfxkYA3zSgkoTXg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-arm64-msvc/-/slang-win32-arm64-msvc-0.10.1.tgz", + "integrity": "sha512-diu8P/UdSdn1p+XKQK5NgZV2RTZWsL2kHLO+n0bZ3Z4lTojL8drDhMXIXzMcwRUuJdXv/NkO97dsBwM+CyhGiw==", "cpu": [ "arm64" ], @@ -2786,9 +2786,9 @@ } }, "node_modules/@nomicfoundation/slang-win32-ia32-msvc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-ia32-msvc/-/slang-win32-ia32-msvc-0.4.0.tgz", - "integrity": "sha512-mJSEW8YL4cJu+Zv/S98pkdFsHKmvpXSz7glrLLH6REiyUyjnDpu35HESpeQXImcIC0C5uBerkTJFabzwKchwRw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-ia32-msvc/-/slang-win32-ia32-msvc-0.10.1.tgz", + "integrity": "sha512-oRNDHADhWFq6D2l8UacJ+ExxJO7sYKOMn4IlYGulyp2KGzjnk4fl2vCpYVUVJ+y9pF4W2G1G06nxxddaPk/2mg==", "cpu": [ "ia32" ], @@ -2801,9 +2801,9 @@ } }, "node_modules/@nomicfoundation/slang-win32-x64-msvc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-x64-msvc/-/slang-win32-x64-msvc-0.4.0.tgz", - "integrity": "sha512-yi7SF4/qoNqPOqJjypAG2CqcOjqNe069nBCYReoXny1TDWBy/pVnxoLdUrIpALUIbMW0uYycBpgqQkvoRnYBcQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-x64-msvc/-/slang-win32-x64-msvc-0.10.1.tgz", + "integrity": "sha512-1a6ip/OkkJ8rMaMupgmbon0Rc2rgZ4e+rC7rDA3E2VbDjZsCd2bHAOop9bQCO2rUA9pIYkzwueWJkJZGTKs82w==", "cpu": [ "x64" ], @@ -11962,7 +11962,7 @@ "version": "0.7.2", "license": "MIT", "dependencies": { - "@nomicfoundation/slang": "^0.4.0", + "@nomicfoundation/slang": "^0.10.1", "@nomicfoundation/solidity-analyzer": "0.1.1" }, "bin": { @@ -14717,73 +14717,73 @@ } }, "@nomicfoundation/slang": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.4.0.tgz", - "integrity": "sha512-cA6E4RialoC1x44PPK1SQ0RWqhnPSy1NMp5/LUvWk4SXzOzOgMdCW/GWy7/E17aY+4mbVC6AjTJb+aKvV57Uqw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.10.1.tgz", + "integrity": "sha512-qU9eHCExF6Hix4KPXHv1oQN1vWYGeOrHJXZ+uxroCMZ4Tf5P/kSrxVI2SqnVmwpSdxrwJdDQ9cI8Fe6PV7YDCA==", "requires": { - "@nomicfoundation/slang-darwin-arm64": "0.4.0", - "@nomicfoundation/slang-darwin-x64": "0.4.0", - "@nomicfoundation/slang-linux-arm64-gnu": "0.4.0", - "@nomicfoundation/slang-linux-arm64-musl": "0.4.0", - "@nomicfoundation/slang-linux-x64-gnu": "0.4.0", - "@nomicfoundation/slang-linux-x64-musl": "0.4.0", - "@nomicfoundation/slang-win32-arm64-msvc": "0.4.0", - "@nomicfoundation/slang-win32-ia32-msvc": "0.4.0", - "@nomicfoundation/slang-win32-x64-msvc": "0.4.0" + "@nomicfoundation/slang-darwin-arm64": "0.10.1", + "@nomicfoundation/slang-darwin-x64": "0.10.1", + "@nomicfoundation/slang-linux-arm64-gnu": "0.10.1", + "@nomicfoundation/slang-linux-arm64-musl": "0.10.1", + "@nomicfoundation/slang-linux-x64-gnu": "0.10.1", + "@nomicfoundation/slang-linux-x64-musl": "0.10.1", + "@nomicfoundation/slang-win32-arm64-msvc": "0.10.1", + "@nomicfoundation/slang-win32-ia32-msvc": "0.10.1", + "@nomicfoundation/slang-win32-x64-msvc": "0.10.1" } }, "@nomicfoundation/slang-darwin-arm64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-arm64/-/slang-darwin-arm64-0.4.0.tgz", - "integrity": "sha512-HPE+Whj93rfR9DlhQjqlCpAgKCflxW/7VAtB4X2TNRepfF28E2Mvyfg2GXzGzf2FNH9GCv8/c2vLsxyrm/v+MQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-arm64/-/slang-darwin-arm64-0.10.1.tgz", + "integrity": "sha512-lvXEs9qQS5Qm0vUhy5NwTlvkTMH7cbm2//Z80jhaBqIJlQFUW/0A4JqK/9RAji8ZuOtGC2QT9LnOk2VoNQGiwg==", "optional": true }, "@nomicfoundation/slang-darwin-x64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-x64/-/slang-darwin-x64-0.4.0.tgz", - "integrity": "sha512-/Lo8Pg5K9qJXku5bCvwXt3axy2wRnAffd71n8CKzkCXIul5KwFFYY3fg0/o/WjWgLw+PodvPqh1g3Yrdf0/RCw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-darwin-x64/-/slang-darwin-x64-0.10.1.tgz", + "integrity": "sha512-YV8OZyA++MZebNc5j62bXvQKKT8x6jg5kR2O332HHLZb/5kYWJ0PQYw61W9/DwnFFhBwVT65AZ+cJdM0LQJxog==", "optional": true }, "@nomicfoundation/slang-linux-arm64-gnu": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-gnu/-/slang-linux-arm64-gnu-0.4.0.tgz", - "integrity": "sha512-ZE5d/Cp+viPD3dtLkTz2kTzSD5E1cZiKvZA8qI5UsqH8oX2eW/I/2SOavmQ4toSUCcqgqnYJUhvjQc1k/oiVeg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-gnu/-/slang-linux-arm64-gnu-0.10.1.tgz", + "integrity": "sha512-6vJmGMa6yvrf5PvO9lxnd5mfFtrP+1eTVhNBjbAazaSFKfpDrjR4b29JyLwREcialj/+L3prvEUjXRchIj1Gqg==", "optional": true }, "@nomicfoundation/slang-linux-arm64-musl": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-musl/-/slang-linux-arm64-musl-0.4.0.tgz", - "integrity": "sha512-wk4lfa4ap+Pzz09W8HSA56ZsxdFpNOdE0KfBAPgbE7/6pHhyMt6IqsI+TDsHQSzRJ00+17//e5LrZGkV9+mw0A==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-arm64-musl/-/slang-linux-arm64-musl-0.10.1.tgz", + "integrity": "sha512-cPX5ybj6A46mb9pros8Kwpzv/Lpkp2q+gS+s8Zg62NkogvSqswDZDlBAZ7FUZTzGpMfhZJn75itPgGUCAoImUA==", "optional": true }, "@nomicfoundation/slang-linux-x64-gnu": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-gnu/-/slang-linux-x64-gnu-0.4.0.tgz", - "integrity": "sha512-VVTk06QpWCtfsF727AvNBXelg6ObwATkjiOnNRIiM2CVZi9+UlcqxdnoVnxL5mkoAhXS59xArDQ6C+x4qWCL2Q==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-gnu/-/slang-linux-x64-gnu-0.10.1.tgz", + "integrity": "sha512-sZvtSgb5LencgjqfLkFlhrYQgM8QoSy2t0IcCG1JLA8MXWlYRN/F3nDm3zKSmuOwmtFIzvbc7X/wkkGCNsSQ9g==", "optional": true }, "@nomicfoundation/slang-linux-x64-musl": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-musl/-/slang-linux-x64-musl-0.4.0.tgz", - "integrity": "sha512-6RjyR7prrIMOp1378OiU73HD8wwvvyR850eKgkTRqJyNvFXd4pELc4ERE+BNUlwt2Ab86T+cMMbcP1T0yeRorg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-linux-x64-musl/-/slang-linux-x64-musl-0.10.1.tgz", + "integrity": "sha512-S2WnY4tMCWBPaAQcPoRDZ6M01hKgE1R22n7sqnF7KI2o0Khsm9fnlAZ/2ct4ioe2Je4hyHrNhms8/534P8dQuw==", "optional": true }, "@nomicfoundation/slang-win32-arm64-msvc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-arm64-msvc/-/slang-win32-arm64-msvc-0.4.0.tgz", - "integrity": "sha512-9UlbedxBV+Btjku6m09kJ/pR3pxvw2b+gQthklGdvVPUzPn2EdWttiAMlMLgTNM93Lf1KfjAfxkYA3zSgkoTXg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-arm64-msvc/-/slang-win32-arm64-msvc-0.10.1.tgz", + "integrity": "sha512-diu8P/UdSdn1p+XKQK5NgZV2RTZWsL2kHLO+n0bZ3Z4lTojL8drDhMXIXzMcwRUuJdXv/NkO97dsBwM+CyhGiw==", "optional": true }, "@nomicfoundation/slang-win32-ia32-msvc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-ia32-msvc/-/slang-win32-ia32-msvc-0.4.0.tgz", - "integrity": "sha512-mJSEW8YL4cJu+Zv/S98pkdFsHKmvpXSz7glrLLH6REiyUyjnDpu35HESpeQXImcIC0C5uBerkTJFabzwKchwRw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-ia32-msvc/-/slang-win32-ia32-msvc-0.10.1.tgz", + "integrity": "sha512-oRNDHADhWFq6D2l8UacJ+ExxJO7sYKOMn4IlYGulyp2KGzjnk4fl2vCpYVUVJ+y9pF4W2G1G06nxxddaPk/2mg==", "optional": true }, "@nomicfoundation/slang-win32-x64-msvc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-x64-msvc/-/slang-win32-x64-msvc-0.4.0.tgz", - "integrity": "sha512-yi7SF4/qoNqPOqJjypAG2CqcOjqNe069nBCYReoXny1TDWBy/pVnxoLdUrIpALUIbMW0uYycBpgqQkvoRnYBcQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang-win32-x64-msvc/-/slang-win32-x64-msvc-0.10.1.tgz", + "integrity": "sha512-1a6ip/OkkJ8rMaMupgmbon0Rc2rgZ4e+rC7rDA3E2VbDjZsCd2bHAOop9bQCO2rUA9pIYkzwueWJkJZGTKs82w==", "optional": true }, "@nomicfoundation/solidity-analyzer": { @@ -14865,7 +14865,7 @@ "version": "file:server", "requires": { "@istanbuljs/nyc-config-typescript": "1.0.2", - "@nomicfoundation/slang": "^0.4.0", + "@nomicfoundation/slang": "^0.10.1", "@nomicfoundation/solidity-analyzer": "0.1.1", "@sentry/node": "7.32.1", "@sentry/tracing": "7.32.1", diff --git a/package.json b/package.json index 56e59d7a..083a528b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "url": "https://github.com/NomicFoundation/hardhat-vscode/issues" }, "scripts": { - "postinstall": "npm install --no-save --ignore-scripts --force @nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1 @nomicfoundation/slang-win32-ia32-msvc@0.4.0", + "postinstall": "npm install --no-save --ignore-scripts --force @nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1 @nomicfoundation/slang-win32-ia32-msvc@0.10.1", "build": "tsc -b ./client/tsconfig.json && tsc -b ./server/tsconfig.build.json && tsc -b ./coc/tsconfig.json && tsc -b", "watch": "concurrently -n client,server \"tsc -b -w ./client/tsconfig.json\" \"tsc -b -w ./server/tsconfig.build.json\"", "test:unit": "npm -w server run test", diff --git a/server/package.json b/server/package.json index 1bd52a95..3b63cdbb 100644 --- a/server/package.json +++ b/server/package.json @@ -88,7 +88,7 @@ "yaml": "^2.2.1" }, "dependencies": { - "@nomicfoundation/slang": "^0.4.0", + "@nomicfoundation/slang": "^0.10.1", "@nomicfoundation/solidity-analyzer": "0.1.1" } } diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index 9f2d2d9d..43892378 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -1,43 +1,36 @@ -import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang"; +import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { TextRange } from "@nomicfoundation/slang/text_index"; import _ from "lodash"; import { TextDocument } from "vscode-languageserver-textdocument"; import { Range } from "vscode-languageserver-types"; export type SlangNode = RuleNode | TokenNode; -export type NodeCallback = (node: SlangNode, ancestors: SlangNode[]) => void; +export type NodeCallback = (cursor: Cursor) => void; export function walk( - node: SlangNode, + cursor: Cursor, onEnter: NodeCallback, - onExit: NodeCallback, - ancestors: SlangNode[] = [] + onExit: NodeCallback ) { - onEnter(node, ancestors); + onEnter(cursor); - ancestors.push(node); - - const children: SlangNode[] = - node.type === NodeType.Rule ? node.children : []; - - for (const child of children) { - walk(child, onEnter, onExit, ancestors); + if (cursor.node.type === NodeType.Rule) { + for (let i = 0; i < cursor.node.children.length; i++) { + cursor.goToNthChild(i); + walk(cursor, onEnter, onExit); + } } - - ancestors.pop(); - - onExit(node, ancestors); + onExit(cursor); + cursor.goToParent(); } export function slangToVSCodeRange( doc: TextDocument, - slangRange: number[] + slangRange: TextRange ): Range { - if (slangRange.length !== 2) { - throw new Error(`Invalid slang rage: ${slangRange}`); - } - return { - start: doc.positionAt(slangRange[0]), - end: doc.positionAt(slangRange[1]), + start: doc.positionAt(slangRange.start.utf16), + end: doc.positionAt(slangRange.end.utf16), }; } diff --git a/server/src/services/documentSymbol/SymbolVisitor.ts b/server/src/services/documentSymbol/SymbolVisitor.ts index 51b3176a..2c646908 100644 --- a/server/src/services/documentSymbol/SymbolVisitor.ts +++ b/server/src/services/documentSymbol/SymbolVisitor.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { TextDocument } from "vscode-languageserver-textdocument"; -import { SlangNode } from "../../parser/slangHelpers"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; // Abstraction for a visitor that wants to build part of the document symbol tree @@ -11,6 +11,9 @@ export abstract class SymbolVisitor { public symbolBuilder: SymbolTreeBuilder ) {} - public enter(node: SlangNode, ancestors: SlangNode[]): void {} - public exit(node: SlangNode, ancestors: SlangNode[]): void {} + // public enter(node: SlangNode, ancestors: SlangNode[]): void {} + // public exit(node: SlangNode, ancestors: SlangNode[]): void {} + + public enter(cursor: Cursor): void {} + public exit(cursor: Cursor): void {} } diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 77fc5b80..033be064 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -1,11 +1,13 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ import { DocumentSymbolParams } from "vscode-languageserver/node"; import { DocumentSymbol, SymbolInformation } from "vscode-languageserver-types"; import { analyze } from "@nomicfoundation/solidity-analyzer"; import semver from "semver"; -import { Language, ProductionKind } from "@nomicfoundation/slang"; import _ from "lodash"; +import { Language } from "@nomicfoundation/slang/language"; +import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { ServerState } from "../../types"; import { walk } from "../../parser/slangHelpers"; import { onCommand } from "../../utils/onCommand"; @@ -18,7 +20,7 @@ import { FunctionDefinition } from "./visitors/FunctionDefinition"; import { ContractDefinition } from "./visitors/ContractDefinition"; import { EventDefinition } from "./visitors/EventDefinition"; import { StateVariableDeclaration } from "./visitors/StateVariableDeclaration"; -import { VariableDeclarationStatement } from "./visitors/VariableDeclarationStatement"; +import { VariableDeclaration } from "./visitors/VariableDeclaration"; import { ConstantDefinition } from "./visitors/ConstantDefinition"; import { ConstructorDefinition } from "./visitors/ConstructorDefinition"; import { EnumDefinition } from "./visitors/EnumDefinition"; @@ -70,7 +72,7 @@ export function onDocumentSymbol(serverState: ServerState) { if (parseTree === null) { logger.trace("Slang parsing error"); - const strings = parseOutput.errors.map((e) => + const strings = parseOutput.errors.map((e: any) => e.toErrorReport(uri, text, false) ); logger.trace(strings.join("")); @@ -88,7 +90,7 @@ export function onDocumentSymbol(serverState: ServerState) { new ContractDefinition(document, builder), new EventDefinition(document, builder), new StateVariableDeclaration(document, builder), - new VariableDeclarationStatement(document, builder), + new VariableDeclaration(document, builder), new ConstantDefinition(document, builder), new ConstructorDefinition(document, builder), new EnumDefinition(document, builder), @@ -101,15 +103,15 @@ export function onDocumentSymbol(serverState: ServerState) { ]; walk( - parseTree, - (node, ancestors) => { + parseTree.cursor, + (cursor) => { for (const visitor of visitors) { - visitor.enter(node, ancestors); + visitor.enter(cursor); } }, - (node, ancestors) => { + (cursor) => { for (const visitor of visitors) { - visitor.exit(node, ancestors); + visitor.exit(cursor); } } ); diff --git a/server/src/services/documentSymbol/visitors/ConstantDefinition.ts b/server/src/services/documentSymbol/visitors/ConstantDefinition.ts index 3a0677dd..ca8df7c4 100644 --- a/server/src/services/documentSymbol/visitors/ConstantDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ConstantDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ConstantDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts index 5434ae84..860e6b6f 100644 --- a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts @@ -1,17 +1,18 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; -import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { slangToVSCodeRange } from "../../../parser/slangHelpers"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ConstructorDefinition extends DefinitionVisitor { public ruleKind = RuleKind.ConstructorDefinition; public symbolKind = SymbolKind.Constructor; - protected getSymbolAttributes(node: SlangNode) { + protected getSymbolAttributes(cursor: Cursor) { return { name: "constructor", - range: slangToVSCodeRange(this.document, node.charRange), - selectionRange: slangToVSCodeRange(this.document, node.charRange), + range: slangToVSCodeRange(this.document, cursor.textRange), + selectionRange: slangToVSCodeRange(this.document, cursor.textRange), kind: this.symbolKind, }; } diff --git a/server/src/services/documentSymbol/visitors/ContractDefinition.ts b/server/src/services/documentSymbol/visitors/ContractDefinition.ts index 3084d372..0a0a00ca 100644 --- a/server/src/services/documentSymbol/visitors/ContractDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ContractDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ContractDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts index d1408a3f..a22455df 100644 --- a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts +++ b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts @@ -1,18 +1,23 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { DocumentSymbol, SymbolKind } from "vscode-languageserver-types"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; import _ from "lodash"; -import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { slangToVSCodeRange } from "../../../parser/slangHelpers"; import { SymbolVisitor } from "../SymbolVisitor"; export abstract class DefinitionVisitor extends SymbolVisitor { public abstract ruleKind: RuleKind; public abstract symbolKind: SymbolKind; - public enter(node: SlangNode, ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; + // Open a new symbol node on the DocumentSymbol tree on matching rules if (node.type === NodeType.Rule && node.kind === this.ruleKind) { - this.symbolBuilder.openSymbol(this.getSymbolAttributes(node)); + this.symbolBuilder.openSymbol(this.getSymbolAttributes(cursor)); } // Set the symbol node's range and name when finding the related identifier @@ -29,22 +34,26 @@ export abstract class DefinitionVisitor extends SymbolVisitor { return; } - const identifierRange = slangToVSCodeRange(this.document, node.charRange); + const identifierRange = slangToVSCodeRange( + this.document, + cursor.textRange + ); lastSymbol.name = this.document.getText(identifierRange); lastSymbol.selectionRange = identifierRange; } } - protected getSymbolAttributes(node: SlangNode): Partial { + protected getSymbolAttributes(cursor: Cursor): Partial { return { - range: slangToVSCodeRange(this.document, node.charRange), - selectionRange: slangToVSCodeRange(this.document, node.charRange), + range: slangToVSCodeRange(this.document, cursor.textRange), + selectionRange: slangToVSCodeRange(this.document, cursor.textRange), kind: this.symbolKind, }; } - public exit(node: SlangNode): void { + public exit(cursor: Cursor): void { + const node = cursor.node; if (node.type === NodeType.Rule && node.kind === this.ruleKind) { this.symbolBuilder.closeSymbol(); } diff --git a/server/src/services/documentSymbol/visitors/EnumDefinition.ts b/server/src/services/documentSymbol/visitors/EnumDefinition.ts index e5ac7808..25c01682 100644 --- a/server/src/services/documentSymbol/visitors/EnumDefinition.ts +++ b/server/src/services/documentSymbol/visitors/EnumDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class EnumDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/ErrorDefinition.ts b/server/src/services/documentSymbol/visitors/ErrorDefinition.ts index 610b2270..4cd985a9 100644 --- a/server/src/services/documentSymbol/visitors/ErrorDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ErrorDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ErrorDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/EventDefinition.ts b/server/src/services/documentSymbol/visitors/EventDefinition.ts index 6b18f1bf..d14182e8 100644 --- a/server/src/services/documentSymbol/visitors/EventDefinition.ts +++ b/server/src/services/documentSymbol/visitors/EventDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class EventDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts index 64404a96..c9826d2c 100644 --- a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts @@ -1,17 +1,18 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; -import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { slangToVSCodeRange } from "../../../parser/slangHelpers"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class FallbackFunctionDefinition extends DefinitionVisitor { public ruleKind = RuleKind.FallbackFunctionDefinition; public symbolKind = SymbolKind.Function; - protected getSymbolAttributes(node: SlangNode) { + protected getSymbolAttributes(cursor: Cursor) { return { name: "fallback", - range: slangToVSCodeRange(this.document, node.charRange), - selectionRange: slangToVSCodeRange(this.document, node.charRange), + range: slangToVSCodeRange(this.document, cursor.textRange), + selectionRange: slangToVSCodeRange(this.document, cursor.textRange), kind: this.symbolKind, }; } diff --git a/server/src/services/documentSymbol/visitors/FunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FunctionDefinition.ts index a981d64b..7dde5b02 100644 --- a/server/src/services/documentSymbol/visitors/FunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/FunctionDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class FunctionDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts b/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts index 93980fac..9ebefd0b 100644 --- a/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts +++ b/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class InterfaceDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/LibraryDefinition.ts b/server/src/services/documentSymbol/visitors/LibraryDefinition.ts index 6232b846..87df2314 100644 --- a/server/src/services/documentSymbol/visitors/LibraryDefinition.ts +++ b/server/src/services/documentSymbol/visitors/LibraryDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class LibraryDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/ModifierDefinition.ts b/server/src/services/documentSymbol/visitors/ModifierDefinition.ts index bac07f48..5354612f 100644 --- a/server/src/services/documentSymbol/visitors/ModifierDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ModifierDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ModifierDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts index 78c5bc45..56f92cc8 100644 --- a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts @@ -1,17 +1,18 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; -import { SlangNode, slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { slangToVSCodeRange } from "../../../parser/slangHelpers"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ReceiveFunctionDefinition extends DefinitionVisitor { public ruleKind = RuleKind.ReceiveFunctionDefinition; public symbolKind = SymbolKind.Function; - protected getSymbolAttributes(node: SlangNode) { + protected getSymbolAttributes(cursor: Cursor) { return { name: "receive", - range: slangToVSCodeRange(this.document, node.charRange), - selectionRange: slangToVSCodeRange(this.document, node.charRange), + range: slangToVSCodeRange(this.document, cursor.textRange), + selectionRange: slangToVSCodeRange(this.document, cursor.textRange), kind: this.symbolKind, }; } diff --git a/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts b/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts index 7f89033a..bf7e8ba7 100644 --- a/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts +++ b/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts @@ -1,8 +1,8 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class StateVariableDeclaration extends DefinitionVisitor { - public ruleKind = RuleKind.StateVariableDeclaration; + public ruleKind = RuleKind.StateVariableDefinition; public symbolKind = SymbolKind.Property; } diff --git a/server/src/services/documentSymbol/visitors/StructDefinition.ts b/server/src/services/documentSymbol/visitors/StructDefinition.ts index 0b064799..06d752a8 100644 --- a/server/src/services/documentSymbol/visitors/StructDefinition.ts +++ b/server/src/services/documentSymbol/visitors/StructDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class StructDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/StructMember.ts b/server/src/services/documentSymbol/visitors/StructMember.ts index febf343b..20547bb2 100644 --- a/server/src/services/documentSymbol/visitors/StructMember.ts +++ b/server/src/services/documentSymbol/visitors/StructMember.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class StructMember extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts b/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts index e2270962..b0b69b9f 100644 --- a/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts +++ b/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts @@ -1,5 +1,5 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class UserDefinedValueTypeDefinition extends DefinitionVisitor { diff --git a/server/src/services/documentSymbol/visitors/VariableDeclaration.ts b/server/src/services/documentSymbol/visitors/VariableDeclaration.ts new file mode 100644 index 00000000..9a5f9a5d --- /dev/null +++ b/server/src/services/documentSymbol/visitors/VariableDeclaration.ts @@ -0,0 +1,8 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind } from "@nomicfoundation/slang/kinds"; +import { DefinitionVisitor } from "./DefinitionVisitor"; + +export class VariableDeclaration extends DefinitionVisitor { + public ruleKind = RuleKind.VariableDeclaration; + public symbolKind = SymbolKind.Variable; +} diff --git a/server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts b/server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts deleted file mode 100644 index 2a895995..00000000 --- a/server/src/services/documentSymbol/visitors/VariableDeclarationStatement.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang"; -import { DefinitionVisitor } from "./DefinitionVisitor"; - -export class VariableDeclarationStatement extends DefinitionVisitor { - public ruleKind = RuleKind.VariableDeclarationStatement; - public symbolKind = SymbolKind.Variable; -} diff --git a/server/src/services/semanticHighlight/HighlightVisitor.ts b/server/src/services/semanticHighlight/HighlightVisitor.ts index 25c0e10d..1c72d019 100644 --- a/server/src/services/semanticHighlight/HighlightVisitor.ts +++ b/server/src/services/semanticHighlight/HighlightVisitor.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { TextDocument } from "vscode-languageserver-textdocument"; -import { SlangNode } from "../../parser/slangHelpers"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; // Abstraction for a visitor that wants to highlight tokens @@ -11,6 +11,6 @@ export abstract class HighlightVisitor { public tokenBuilder: SemanticTokensBuilder ) {} - public enter(node: SlangNode, ancestors: SlangNode[]): void {} - public exit(node: SlangNode, ancestors: SlangNode[]): void {} + public enter(cursor: Cursor): void {} + public exit(cursor: Cursor): void {} } diff --git a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts index ed5844a0..f533d2ff 100644 --- a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts +++ b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TextDocument } from "vscode-languageserver-textdocument"; -import { SlangNode } from "../../parser/slangHelpers"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { getTokenTypeIndex } from "./tokenTypes"; // Helps building a SemanticTokens response by providing slang nodes and supported token types @@ -12,9 +12,9 @@ export class SemanticTokensBuilder { constructor(private document: TextDocument) {} - public addToken(node: SlangNode, type: SemanticTokenTypes, modifiers = 0) { - const offset = node.charRange[0]; - const length = node.charRange[1] - node.charRange[0]; + public addToken(cursor: Cursor, type: SemanticTokenTypes, modifiers = 0) { + const offset = cursor.textRange.start.utf16; + const length = cursor.textRange.end.utf16 - cursor.textRange.start.utf16; const position = this.document.positionAt(offset); diff --git a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts index 8de45379..698555a0 100644 --- a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights contract definitions export class ContractDefinitionHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 1]?.kind === RuleKind.ContractDefinition + ancestors[ancestors.length - 1]?.kind === RuleKind.ContractDefinition ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts index f92751d6..d56aad8f 100644 --- a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights custom type names export class CustomTypeHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 3]?.kind === RuleKind.TypeName + ancestors[ancestors.length - 2]?.kind === RuleKind.TypeName ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts deleted file mode 100644 index 0cce0d2a..00000000 --- a/server/src/services/semanticHighlight/highlighters/ElementaryTypeHighlighter.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind } from "@nomicfoundation/slang"; -import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; - -// Highlights elementary type names -export class ElementaryTypeHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { - if (node.type === NodeType.Rule && node.kind === RuleKind.ElementaryType) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); - } - } -} diff --git a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts index b3999740..56c29def 100644 --- a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights event definitions export class EventDefinitionHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 1]?.kind === RuleKind.EventDefinition + ancestors[ancestors.length - 1]?.kind === RuleKind.EventDefinition ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts index aab48d73..f408fec4 100644 --- a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights event emissions export class EventEmissionHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 3]?.kind === RuleKind.EmitStatement + ancestors[ancestors.length - 2]?.kind === RuleKind.EmitStatement ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts index 4a0e21d2..86b5337d 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts @@ -1,18 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights function calls export class FunctionCallHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 2]?.kind === - RuleKind.FunctionCallExpression + ancestors[ancestors.length - 2]?.kind === RuleKind.FunctionCallExpression ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.function); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.function); } } } diff --git a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts index 6191d154..13251552 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights function definitions export class FunctionDefinitionHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 1]?.kind === RuleKind.FunctionDefinition + ancestors[ancestors.length - 1]?.kind === RuleKind.FunctionDefinition ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.function); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.function); } } } diff --git a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts index e516d55a..3863588c 100644 --- a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights interface definitions export class InterfaceDefinitionHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 1]?.kind === RuleKind.InterfaceDefinition + ancestors[ancestors.length - 1]?.kind === RuleKind.InterfaceDefinition ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts index aaf533a8..0aafdad1 100644 --- a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts @@ -1,10 +1,11 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenKind } from "@nomicfoundation/slang"; +import { TokenKind } from "@nomicfoundation/slang/kinds"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; const keywordKinds = new Set([ - TokenKind.AbicoderKeyword, + TokenKind.ABICoderKeyword, TokenKind.AbstractKeyword, TokenKind.AddressKeyword, TokenKind.AnonymousKeyword, @@ -77,6 +78,7 @@ const keywordKinds = new Set([ TokenKind.TryKeyword, TokenKind.TypeKeyword, TokenKind.UncheckedKeyword, + TokenKind.UnsignedIntegerType, TokenKind.UsingKeyword, TokenKind.ViewKeyword, TokenKind.VirtualKeyword, @@ -84,15 +86,14 @@ const keywordKinds = new Set([ TokenKind.WeiKeyword, TokenKind.WhileKeyword, TokenKind.YearsKeyword, - TokenKind.YulKeyword, - TokenKind.YulReservedKeyword, ]); // Highlights keywords export class KeywordHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; if (node.type === NodeType.Token && keywordKinds.has(node.kind)) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.keyword); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.keyword); } } } diff --git a/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts b/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts deleted file mode 100644 index 5b3b40f0..00000000 --- a/server/src/services/semanticHighlight/highlighters/LoggerVisitor.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; -import _ from "lodash"; -import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; - -const nodeTypeMap = _.invert(NodeType); -const ruleKindMap = _.invert(RuleKind); -const tokenKindMap = _.invert(TokenKind); - -// Visitor that logs the tree as indented text -export class Logger extends HighlightVisitor { - public enter(node: SlangNode, ancestors: SlangNode[]): void { - const nodeText = JSON.stringify( - _.truncate( - this.document - .getText() - .slice(Number(node.charRange[0]), Number(node.charRange[1])), - { - length: 20, - } - ) - ); - - const indentation = " ".repeat(ancestors.length * 2); - const kindMap = node.type === NodeType.Rule ? ruleKindMap : tokenKindMap; - - // eslint-disable-next-line no-console - console.log( - `${indentation}${nodeTypeMap[node.type]}: ${ - kindMap[node.kind] - } ${nodeText}` - ); - } -} diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts index c11522c2..3cfe1073 100644 --- a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts @@ -1,7 +1,8 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenKind } from "@nomicfoundation/slang"; +import { TokenKind } from "@nomicfoundation/slang/kinds"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; const numberKinds = new Set([ TokenKind.HexLiteral, @@ -11,9 +12,10 @@ const numberKinds = new Set([ // Highlights numbers export class NumberHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; if (node.type === NodeType.Token && numberKinds.has(node.kind)) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.number); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.number); } } } diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts index 8f4713c4..766d01f6 100644 --- a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts @@ -1,23 +1,21 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenKind } from "@nomicfoundation/slang"; +import { TokenKind } from "@nomicfoundation/slang/kinds"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; const stringKinds = new Set([ TokenKind.HexStringLiteral, TokenKind.AsciiStringLiteral, TokenKind.UnicodeStringLiteral, - TokenKind.DoubleQuotedAsciiStringLiteral, - TokenKind.SingleQuotedAsciiStringLiteral, - TokenKind.DoubleQuotedUnicodeStringLiteral, - TokenKind.SingleQuotedUnicodeStringLiteral, ]); // Highlights strings export class StringHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; if (node.type === NodeType.Token && stringKinds.has(node.kind)) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.string); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.string); } } } diff --git a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts index c3f6c370..526537e4 100644 --- a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts @@ -1,17 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, RuleKind, TokenKind } from "@nomicfoundation/slang"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNode } from "../../../parser/slangHelpers"; // Highlights struct definitions export class StructDefinitionHighlighter extends HighlightVisitor { - public enter(node: SlangNode, _ancestors: SlangNode[]): void { + public enter(cursor: Cursor): void { + const node = cursor.node; + const ancestors = cursor.pathRuleNodes; if ( node.type === NodeType.Token && node.kind === TokenKind.Identifier && - _ancestors[_ancestors.length - 1]?.kind === RuleKind.StructDefinition + ancestors[ancestors.length - 1]?.kind === RuleKind.StructDefinition ) { - this.tokenBuilder.addToken(node, SemanticTokenTypes.type); + this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index d2015daf..9b05eccd 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -1,20 +1,21 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ import { SemanticTokens, SemanticTokensParams, } from "vscode-languageserver-protocol"; -import { Language, ProductionKind } from "@nomicfoundation/slang"; import _ from "lodash"; import { analyze } from "@nomicfoundation/solidity-analyzer"; import semver from "semver"; +import { Language } from "@nomicfoundation/slang/language"; +import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { ServerState } from "../../types"; import { onCommand } from "../../utils/onCommand"; import { walk } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; import { KeywordHighlighter } from "./highlighters/KeywordHighlighter"; -import { ElementaryTypeHighlighter } from "./highlighters/ElementaryTypeHighlighter"; import { NumberHighlighter } from "./highlighters/NumberHighlighter"; import { StringHighlighter } from "./highlighters/StringHighlighter"; import { FunctionDefinitionHighlighter } from "./highlighters/FunctionDefinitionHighlighter"; @@ -66,7 +67,7 @@ export function onSemanticTokensFull(serverState: ServerState) { if (parseTree === null) { logger.trace("Slang parsing error"); - const strings = parseOutput.errors.map((e) => + const strings = parseOutput.errors.map((e: any) => e.toErrorReport(uri, text, false) ); logger.trace(strings.join("")); @@ -80,7 +81,6 @@ export function onSemanticTokensFull(serverState: ServerState) { const visitors = [ new CustomTypeHighlighter(document, builder), new KeywordHighlighter(document, builder), - new ElementaryTypeHighlighter(document, builder), new NumberHighlighter(document, builder), new StringHighlighter(document, builder), new FunctionDefinitionHighlighter(document, builder), @@ -94,15 +94,15 @@ export function onSemanticTokensFull(serverState: ServerState) { // Visit the CST walk( - parseTree, - (node, ancestors) => { + parseTree.cursor, + (cursor) => { for (const visitor of visitors) { - visitor.enter(node, ancestors); + visitor.enter(cursor); } }, - (node, ancestors) => { + (cursor) => { for (const visitor of visitors) { - visitor.exit(node, ancestors); + visitor.exit(cursor); } } ); diff --git a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts index 54d05694..0910faef 100644 --- a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts +++ b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts @@ -27,7 +27,6 @@ describe('[hardhat] documentSymbol', () => { expect(symbols).to.deep.equal([ { - kind: 26, children: [], range: { start: { @@ -49,13 +48,12 @@ describe('[hardhat] documentSymbol', () => { character: 15, }, }, + kind: 26, name: 'CustomType', }, { - kind: 11, children: [ { - kind: 12, children: [], range: { start: { @@ -77,6 +75,7 @@ describe('[hardhat] documentSymbol', () => { character: 30, }, }, + kind: 12, name: 'interfaceFunction', }, ], @@ -100,18 +99,17 @@ describe('[hardhat] documentSymbol', () => { character: 23, }, }, + kind: 11, name: 'TestInterface', }, { - kind: 23, children: [ { - kind: 7, children: [], range: { start: { line: 11, - character: 4, + character: 0, }, end: { line: 12, @@ -128,15 +126,15 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, + kind: 7, name: 'aNumber', }, { - kind: 7, children: [], range: { start: { line: 12, - character: 4, + character: 0, }, end: { line: 13, @@ -153,10 +151,10 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, + kind: 7, name: 'aString', }, { - kind: 7, children: [], range: { start: { @@ -178,6 +176,7 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, + kind: 7, name: 'anAddress', }, ], @@ -201,18 +200,17 @@ describe('[hardhat] documentSymbol', () => { character: 17, }, }, + kind: 23, name: 'TestStruct', }, { - kind: 23, children: [ { - kind: 7, children: [], range: { start: { line: 17, - character: 4, + character: 0, }, end: { line: 18, @@ -229,15 +227,15 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, + kind: 7, name: 'aNumber', }, { - kind: 7, children: [], range: { start: { line: 18, - character: 4, + character: 0, }, end: { line: 19, @@ -254,10 +252,10 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, + kind: 7, name: 'aString', }, { - kind: 7, children: [], range: { start: { @@ -279,6 +277,7 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, + kind: 7, name: 'anAddress', }, ], @@ -302,14 +301,14 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, + kind: 23, name: 'TestStruct2', }, { - kind: 14, children: [], range: { start: { - line: 22, + line: 21, character: 0, }, end: { @@ -327,10 +326,10 @@ describe('[hardhat] documentSymbol', () => { character: 26, }, }, + kind: 14, name: 'aConstant', }, { - kind: 10, children: [], range: { start: { @@ -352,10 +351,10 @@ describe('[hardhat] documentSymbol', () => { character: 9, }, }, + kind: 10, name: 'Name', }, { - kind: 24, children: [], range: { start: { @@ -377,10 +376,10 @@ describe('[hardhat] documentSymbol', () => { character: 17, }, }, + kind: 24, name: 'CustomError', }, { - kind: 5, children: [], range: { start: { @@ -402,25 +401,23 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, + kind: 5, name: 'TestLibrary', }, { - kind: 5, children: [ { - kind: 9, children: [ { - kind: 13, children: [], range: { start: { line: 35, - character: 8, + character: 0, }, end: { - line: 36, - character: 0, + line: 35, + character: 21, }, }, selectionRange: { @@ -433,6 +430,7 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, + kind: 13, name: 'local', }, ], @@ -457,9 +455,9 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, }, + kind: 9, }, { - kind: 12, children: [], range: { start: { @@ -481,10 +479,10 @@ describe('[hardhat] documentSymbol', () => { character: 25, }, }, + kind: 12, name: 'testModifier', }, { - kind: 24, children: [], range: { start: { @@ -506,10 +504,10 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, + kind: 24, name: 'TestEvent', }, { - kind: 7, children: [], range: { start: { @@ -531,10 +529,10 @@ describe('[hardhat] documentSymbol', () => { character: 33, }, }, + kind: 7, name: 'contractAsMember', }, { - kind: 7, children: [], range: { start: { @@ -556,10 +554,10 @@ describe('[hardhat] documentSymbol', () => { character: 35, }, }, + kind: 7, name: 'interfaceAsMember', }, { - kind: 7, children: [], range: { start: { @@ -581,15 +579,15 @@ describe('[hardhat] documentSymbol', () => { character: 29, }, }, + kind: 7, name: 'structAsMember', }, { - kind: 7, children: [], range: { start: { - line: 53, - character: 4, + line: 52, + character: 0, }, end: { line: 54, @@ -606,15 +604,15 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, + kind: 7, name: 'aNumber', }, { - kind: 7, children: [], range: { start: { - line: 55, - character: 4, + line: 54, + character: 0, }, end: { line: 56, @@ -631,13 +629,12 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, + kind: 7, name: 'aString', }, { - kind: 12, children: [ { - kind: 13, children: [], range: { start: { @@ -645,8 +642,8 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, end: { - line: 63, - character: 0, + line: 62, + character: 39, }, }, selectionRange: { @@ -659,10 +656,10 @@ describe('[hardhat] documentSymbol', () => { character: 39, }, }, + kind: 13, name: 'contractAsLocalVar', }, { - kind: 13, children: [], range: { start: { @@ -670,8 +667,8 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, end: { - line: 65, - character: 0, + line: 64, + character: 41, }, }, selectionRange: { @@ -684,10 +681,10 @@ describe('[hardhat] documentSymbol', () => { character: 41, }, }, + kind: 13, name: 'interfaceAsLocalVar', }, { - kind: 13, children: [], range: { start: { @@ -695,8 +692,8 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, end: { - line: 66, - character: 0, + line: 65, + character: 42, }, }, selectionRange: { @@ -709,10 +706,10 @@ describe('[hardhat] documentSymbol', () => { character: 42, }, }, + kind: 13, name: 'structAsLocalVar', }, { - kind: 13, children: [], range: { start: { @@ -720,8 +717,8 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, end: { - line: 78, - character: 0, + line: 77, + character: 35, }, }, selectionRange: { @@ -734,6 +731,7 @@ describe('[hardhat] documentSymbol', () => { character: 35, }, }, + kind: 13, name: 'afterUTF8', }, ], @@ -757,10 +755,10 @@ describe('[hardhat] documentSymbol', () => { character: 25, }, }, + kind: 12, name: 'testFunction', }, { - kind: 12, children: [], range: { start: { @@ -782,10 +780,10 @@ describe('[hardhat] documentSymbol', () => { character: 28, }, }, + kind: 12, name: 'anotherFunction', }, { - kind: 12, children: [], name: 'fallback', range: { @@ -808,9 +806,9 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, }, + kind: 12, }, { - kind: 12, children: [], name: 'receive', range: { @@ -833,6 +831,7 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, }, + kind: 12, }, ], range: { @@ -855,6 +854,7 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, + kind: 5, name: 'testContract', }, ]) diff --git a/test/protocol/test/textDocument/semanticTokens/full.test.ts b/test/protocol/test/textDocument/semanticTokens/full.test.ts index cb5da1cf..6b52b58d 100644 --- a/test/protocol/test/textDocument/semanticTokens/full.test.ts +++ b/test/protocol/test/textDocument/semanticTokens/full.test.ts @@ -27,13 +27,13 @@ describe('[hardhat] semanticTokens/full', () => { expect(semanticTokens).to.deep.equal({ data: [ - 2, 0, 6, 0, 0, 0, 7, 8, 0, 0, 2, 0, 9, 0, 0, 0, 10, 13, 2, 0, 2, 0, 6, 0, 0, 0, 7, 10, 2, 0, 1, 4, 7, 2, 0, 1, - 4, 6, 2, 0, 0, 0, 6, 0, 0, 1, 0, 11, 2, 0, 0, 4, 7, 0, 0, 3, 0, 8, 0, 0, 0, 9, 12, 2, 0, 1, 4, 5, 0, 0, 0, 6, 9, - 2, 0, 1, 8, 12, 2, 0, 0, 35, 13, 2, 0, 0, 37, 10, 2, 0, 3, 4, 12, 2, 0, 2, 4, 13, 2, 0, 2, 4, 10, 2, 0, 2, 4, 7, - 2, 0, 0, 18, 4, 1, 0, 0, 7, 3, 1, 0, 2, 4, 6, 2, 0, 0, 0, 6, 0, 0, 0, 17, 6, 3, 0, 2, 4, 8, 0, 0, 0, 9, 12, 4, - 0, 1, 8, 12, 2, 0, 1, 8, 13, 2, 0, 1, 8, 10, 2, 0, 0, 11, 6, 0, 0, 1, 6, 6, 0, 0, 1, 8, 12, 2, 0, 2, 8, 13, 2, - 0, 1, 8, 10, 2, 0, 0, 11, 6, 0, 0, 8, 8, 4, 0, 0, 0, 5, 9, 2, 0, 4, 8, 10, 2, 0, 0, 11, 6, 0, 0, 2, 8, 15, 4, 0, - 3, 4, 8, 0, 0, 0, 9, 15, 4, 0, 0, 18, 6, 0, 0, 0, 7, 4, 0, 0, + 2, 0, 6, 0, 0, 0, 7, 8, 0, 0, 2, 0, 9, 0, 0, 0, 10, 13, 2, 0, 2, 0, 6, 0, 0, 0, 7, 10, 2, 0, 1, 4, 7, 0, 0, 1, + 4, 6, 0, 0, 1, 4, 7, 0, 0, 3, 0, 8, 0, 0, 0, 9, 12, 2, 0, 1, 4, 5, 0, 0, 0, 6, 9, 2, 0, 1, 8, 12, 2, 0, 0, 35, + 13, 2, 0, 0, 37, 10, 2, 0, 3, 4, 12, 2, 0, 2, 4, 13, 2, 0, 2, 4, 10, 2, 0, 2, 4, 7, 0, 0, 0, 18, 4, 1, 0, 0, 7, + 3, 1, 0, 2, 4, 6, 0, 0, 0, 17, 6, 3, 0, 2, 4, 8, 0, 0, 0, 9, 12, 4, 0, 1, 8, 12, 2, 0, 1, 8, 13, 2, 0, 1, 8, 10, + 2, 0, 0, 11, 6, 0, 0, 1, 6, 6, 0, 0, 1, 8, 12, 2, 0, 2, 8, 13, 2, 0, 1, 8, 10, 2, 0, 0, 11, 6, 0, 0, 8, 8, 4, 0, + 0, 0, 5, 9, 2, 0, 4, 8, 10, 2, 0, 0, 11, 6, 0, 0, 2, 8, 15, 4, 0, 3, 4, 8, 0, 0, 0, 9, 15, 4, 0, 0, 18, 6, 0, 0, + 0, 7, 4, 0, 0, ], }) }) From 2305c70d9ba4e5b959569b52d0fc8285f99877de Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Fri, 27 Oct 2023 08:40:27 -0300 Subject: [PATCH 04/18] track performance spans --- .../src/services/documentSymbol/onDocumentSymbol.ts | 9 ++++++++- .../semanticHighlight/onSemanticTokensFull.ts | 8 +++++++- server/src/utils/onCommand.ts | 12 ++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 033be064..834dcc02 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -39,7 +39,7 @@ export function onDocumentSymbol(serverState: ServerState) { serverState, "onDocumentSymbol", params.textDocument.uri, - () => { + (_analyzer, _document, transaction) => { const { uri } = params.textDocument; const { logger, solcVersions } = serverState; @@ -54,13 +54,16 @@ export function onDocumentSymbol(serverState: ServerState) { const text = document.getText(); // Get the document's solidity version + let span = transaction.startChild({ op: "solidity-analyzer" }); const { versionPragmas } = analyze(text); + span.finish(); const solcVersion = semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || _.last(solcVersions); try { // Parse using slang + span = transaction.startChild({ op: "slang-parsing" }); const language = new Language(solcVersion!); const parseOutput = language.parse( @@ -69,6 +72,7 @@ export function onDocumentSymbol(serverState: ServerState) { ); const parseTree = parseOutput.parseTree; + span.finish(); if (parseTree === null) { logger.trace("Slang parsing error"); @@ -102,6 +106,8 @@ export function onDocumentSymbol(serverState: ServerState) { new UserDefinedValueTypeDefinition(document, builder), ]; + // Walk the tree and generate symbol nodes + span = transaction.startChild({ op: "walk-generate-symbols" }); walk( parseTree.cursor, (cursor) => { @@ -115,6 +121,7 @@ export function onDocumentSymbol(serverState: ServerState) { } } ); + span.finish(); return builder.symbols; } catch (error) { diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 9b05eccd..f3e92b67 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -35,7 +35,7 @@ export function onSemanticTokensFull(serverState: ServerState) { serverState, "onSemanticTokensFull", params.textDocument.uri, - () => { + (_analyzer, _document, transaction) => { const { uri } = params.textDocument; const { logger, solcVersions } = serverState; @@ -49,13 +49,16 @@ export function onSemanticTokensFull(serverState: ServerState) { const text = document.getText(); // Get the document's solidity version + let span = transaction.startChild({ op: "solidity-analyzer" }); const { versionPragmas } = analyze(text); + span.finish(); const solcVersion = semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || _.last(solcVersions); try { // Parse using slang + span = transaction.startChild({ op: "slang-parsing" }); const language = new Language(solcVersion!); const parseOutput = language.parse( @@ -64,6 +67,7 @@ export function onSemanticTokensFull(serverState: ServerState) { ); const parseTree = parseOutput.parseTree; + span.finish(); if (parseTree === null) { logger.trace("Slang parsing error"); @@ -93,6 +97,7 @@ export function onSemanticTokensFull(serverState: ServerState) { ]; // Visit the CST + span = transaction.startChild({ op: "walk-generate-symbols" }); walk( parseTree.cursor, (cursor) => { @@ -106,6 +111,7 @@ export function onSemanticTokensFull(serverState: ServerState) { } } ); + span.finish(); return { data: builder.getTokenData() }; } catch (error) { diff --git a/server/src/utils/onCommand.ts b/server/src/utils/onCommand.ts index 33d56b2a..afca0984 100644 --- a/server/src/utils/onCommand.ts +++ b/server/src/utils/onCommand.ts @@ -1,5 +1,6 @@ import { ISolFileEntry } from "@common/types"; import { TextDocument } from "vscode-languageserver-textdocument"; +import { Transaction } from "@sentry/types"; import { addFrameworkTag } from "../telemetry/tags"; import { ServerState } from "../types"; import { lookupEntryForUri } from "./lookupEntryForUri"; @@ -8,7 +9,11 @@ export function onCommand( serverState: ServerState, commandName: string, uri: string, - action: (documentAnalyzer: ISolFileEntry, document: TextDocument) => T + action: ( + documentAnalyzer: ISolFileEntry, + document: TextDocument, + transaction: Transaction + ) => T ) { const { logger, telemetry } = serverState; @@ -28,6 +33,9 @@ export function onCommand( addFrameworkTag(transaction, documentAnalyzer.project); - return { status: "ok", result: action(documentAnalyzer, document) }; + return { + status: "ok", + result: action(documentAnalyzer, document, transaction), + }; }); } From 5bd248d66aebf49014ef950a9baf62d5f8310fb8 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Fri, 27 Oct 2023 09:49:23 -0300 Subject: [PATCH 05/18] error tolerance for document symbols --- .../documentSymbol/SymbolTreeBuilder.ts | 5 + .../documentSymbol/onDocumentSymbol.ts | 190 ++++++++++-------- .../visitors/DefinitionVisitor.ts | 2 +- 3 files changed, 114 insertions(+), 83 deletions(-) diff --git a/server/src/services/documentSymbol/SymbolTreeBuilder.ts b/server/src/services/documentSymbol/SymbolTreeBuilder.ts index ba3a3bb6..32bd59b8 100644 --- a/server/src/services/documentSymbol/SymbolTreeBuilder.ts +++ b/server/src/services/documentSymbol/SymbolTreeBuilder.ts @@ -16,6 +16,11 @@ export class SymbolTreeBuilder { public closeSymbol() { const symbol = this.currentPath.pop() as DocumentSymbol; + + if (symbol.name === undefined) { + return; + } + if (this.currentPath.length === 0) { this.symbols.push(symbol); } else { diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 834dcc02..8bd87bf1 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -10,7 +10,6 @@ import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { ServerState } from "../../types"; import { walk } from "../../parser/slangHelpers"; -import { onCommand } from "../../utils/onCommand"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; import { StructDefinition } from "./visitors/StructDefinition"; import { SymbolVisitor } from "./SymbolVisitor"; @@ -35,100 +34,127 @@ export function onDocumentSymbol(serverState: ServerState) { return async ( params: DocumentSymbolParams ): Promise => { - return onCommand( - serverState, - "onDocumentSymbol", - params.textDocument.uri, - (_analyzer, _document, transaction) => { - const { uri } = params.textDocument; + const { telemetry, logger, solcVersions } = serverState; + return telemetry.trackTimingSync("onDocumentSymbol", (transaction) => { + const { uri } = params.textDocument; - const { logger, solcVersions } = serverState; + // Find the file in the documents collection + const document = serverState.documents.get(uri); - // Find the file in the documents collection - const document = serverState.documents.get(uri); + if (document === undefined) { + logger.error("document not found in collection"); + return { + status: "internal_error", + result: null, + }; + } - if (document === undefined) { - throw new Error(`Document not found: ${uri}`); - } + const text = document.getText(); + + // Get the document's solidity version + let span = transaction.startChild({ op: "solidity-analyzer" }); + const { versionPragmas } = analyze(text); + span.finish(); + const solcVersion = + semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || + _.last(solcVersions); + + try { + // Parse using slang + span = transaction.startChild({ op: "slang-parsing" }); + const language = new Language(solcVersion!); - const text = document.getText(); + const parseOutput = language.parse( + ProductionKind.SourceUnit, + document.getText() + ); - // Get the document's solidity version - let span = transaction.startChild({ op: "solidity-analyzer" }); - const { versionPragmas } = analyze(text); + const parseTree = parseOutput.parseTree; span.finish(); - const solcVersion = - semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || - _.last(solcVersions); - - try { - // Parse using slang - span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(solcVersion!); - - const parseOutput = language.parse( - ProductionKind.SourceUnit, - document.getText() + + if (parseTree === null) { + logger.error("Slang parsing error"); + const strings = parseOutput.errors.map((e: any) => + e.toErrorReport(uri, text, false) ); + logger.error(strings.join("")); - const parseTree = parseOutput.parseTree; - span.finish(); + return { + status: "internal_error", + result: null, + }; + } - if (parseTree === null) { - logger.trace("Slang parsing error"); - const strings = parseOutput.errors.map((e: any) => - e.toErrorReport(uri, text, false) - ); - logger.trace(strings.join("")); + // let count = 0; + // // const start = performance.now(); + // const kursor: Cursor = parseTree.cursor.clone(); + // do { + // console.log( + // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}: ${ + // kursor.node?.text + // }` + // ); + // } while (kursor.goToNext()); + // const end = performance.now(); + // const elapsed = end - start; + // console.log({ elapsed }); + // console.log({ count }); - return null; - } + // const myobj = { foo: { bar: 123 } }; + // const start = performance.now(); + // for (let index = 0; index < 1_000_000; index++) { + // myobj.foo.bar; + // } + // const end = performance.now(); + // const elapsed = end - start; + // console.log({ elapsed }); - const builder = new SymbolTreeBuilder(); - - const visitors: SymbolVisitor[] = [ - new StructDefinition(document, builder), - new StructMember(document, builder), - new InterfaceDefinition(document, builder), - new FunctionDefinition(document, builder), - new ContractDefinition(document, builder), - new EventDefinition(document, builder), - new StateVariableDeclaration(document, builder), - new VariableDeclaration(document, builder), - new ConstantDefinition(document, builder), - new ConstructorDefinition(document, builder), - new EnumDefinition(document, builder), - new ErrorDefinition(document, builder), - new FallbackFunctionDefinition(document, builder), - new LibraryDefinition(document, builder), - new ModifierDefinition(document, builder), - new ReceiveFunctionDefinition(document, builder), - new UserDefinedValueTypeDefinition(document, builder), - ]; - - // Walk the tree and generate symbol nodes - span = transaction.startChild({ op: "walk-generate-symbols" }); - walk( - parseTree.cursor, - (cursor) => { - for (const visitor of visitors) { - visitor.enter(cursor); - } - }, - (cursor) => { - for (const visitor of visitors) { - visitor.exit(cursor); - } + const builder = new SymbolTreeBuilder(); + + const visitors: SymbolVisitor[] = [ + new StructDefinition(document, builder), + new StructMember(document, builder), + new InterfaceDefinition(document, builder), + new FunctionDefinition(document, builder), + new ContractDefinition(document, builder), + new EventDefinition(document, builder), + new StateVariableDeclaration(document, builder), + new VariableDeclaration(document, builder), + new ConstantDefinition(document, builder), + new ConstructorDefinition(document, builder), + new EnumDefinition(document, builder), + new ErrorDefinition(document, builder), + new FallbackFunctionDefinition(document, builder), + new LibraryDefinition(document, builder), + new ModifierDefinition(document, builder), + new ReceiveFunctionDefinition(document, builder), + new UserDefinedValueTypeDefinition(document, builder), + ]; + + // Walk the tree and generate symbol nodes + span = transaction.startChild({ op: "walk-generate-symbols" }); + walk( + parseTree.cursor, + (cursor) => { + for (const visitor of visitors) { + visitor.enter(cursor); } - ); - span.finish(); + }, + (cursor) => { + for (const visitor of visitors) { + visitor.exit(cursor); + } + } + ); + span.finish(); + // console.log("ALL GOOD"); + // console.log(JSON.stringify(builder.symbols, null, 2)); - return builder.symbols; - } catch (error) { - logger.error(error); - return null; - } + return { status: "ok", result: builder.symbols }; + } catch (error) { + logger.error(error); + return { status: "internal_error", result: null }; } - ); + }); }; } diff --git a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts index a22455df..ecaa9b97 100644 --- a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts +++ b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts @@ -39,7 +39,7 @@ export abstract class DefinitionVisitor extends SymbolVisitor { cursor.textRange ); - lastSymbol.name = this.document.getText(identifierRange); + lastSymbol.name = node.text; lastSymbol.selectionRange = identifierRange; } } From 2677dbcce23fc9a2fa9c830ae4827c5e040e4149 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Fri, 27 Oct 2023 09:59:27 -0300 Subject: [PATCH 06/18] decouple onSemanticTokens from solidity-parser --- .../semanticHighlight/onSemanticTokensFull.ts | 160 +++++++++--------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index f3e92b67..f1912b4d 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -11,7 +11,6 @@ import semver from "semver"; import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { ServerState } from "../../types"; -import { onCommand } from "../../utils/onCommand"; import { walk } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; @@ -30,96 +29,99 @@ const emptyResponse: SemanticTokens = { data: [] }; export function onSemanticTokensFull(serverState: ServerState) { return (params: SemanticTokensParams): SemanticTokens => { + const { telemetry, logger, solcVersions } = serverState; + return ( - onCommand( - serverState, - "onSemanticTokensFull", - params.textDocument.uri, - (_analyzer, _document, transaction) => { - const { uri } = params.textDocument; - const { logger, solcVersions } = serverState; - - // Find the file in the documents collection - const document = serverState.documents.get(uri); - - if (document === undefined) { - throw new Error(`Document not found: ${uri}`); - } + telemetry.trackTimingSync("onSemanticTokensFull", (transaction) => { + const { uri } = params.textDocument; + + // Find the file in the documents collection + const document = serverState.documents.get(uri); + + if (document === undefined) { + logger.error("document not found in collection"); + return { + status: "internal_error", + result: emptyResponse, + }; + } + + const text = document.getText(); + + // Get the document's solidity version + let span = transaction.startChild({ op: "solidity-analyzer" }); + const { versionPragmas } = analyze(text); + span.finish(); + const solcVersion = + semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || + _.last(solcVersions); + + try { + // Parse using slang + span = transaction.startChild({ op: "slang-parsing" }); + const language = new Language(solcVersion!); - const text = document.getText(); + const parseOutput = language.parse( + ProductionKind.SourceUnit, + document.getText() + ); - // Get the document's solidity version - let span = transaction.startChild({ op: "solidity-analyzer" }); - const { versionPragmas } = analyze(text); + const parseTree = parseOutput.parseTree; span.finish(); - const solcVersion = - semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || - _.last(solcVersions); - - try { - // Parse using slang - span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(solcVersion!); - - const parseOutput = language.parse( - ProductionKind.SourceUnit, - document.getText() + + if (parseTree === null) { + logger.error("Slang parsing error"); + const strings = parseOutput.errors.map((e: any) => + e.toErrorReport(uri, text, false) ); + logger.error(strings.join("")); - const parseTree = parseOutput.parseTree; - span.finish(); + return { + status: "internal_error", + result: emptyResponse, + }; + } - if (parseTree === null) { - logger.trace("Slang parsing error"); - const strings = parseOutput.errors.map((e: any) => - e.toErrorReport(uri, text, false) - ); - logger.trace(strings.join("")); + // Register visitors + const builder = new SemanticTokensBuilder(document); - return emptyResponse; - } + const visitors = [ + new CustomTypeHighlighter(document, builder), + new KeywordHighlighter(document, builder), + new NumberHighlighter(document, builder), + new StringHighlighter(document, builder), + new FunctionDefinitionHighlighter(document, builder), + new FunctionCallHighlighter(document, builder), + new EventEmissionHighlighter(document, builder), + new EventDefinitionHighlighter(document, builder), + new ContractDefinitionHighlighter(document, builder), + new InterfaceDefinitionHighlighter(document, builder), + new StructDefinitionHighlighter(document, builder), + ]; - // Register visitors - const builder = new SemanticTokensBuilder(document); - - const visitors = [ - new CustomTypeHighlighter(document, builder), - new KeywordHighlighter(document, builder), - new NumberHighlighter(document, builder), - new StringHighlighter(document, builder), - new FunctionDefinitionHighlighter(document, builder), - new FunctionCallHighlighter(document, builder), - new EventEmissionHighlighter(document, builder), - new EventDefinitionHighlighter(document, builder), - new ContractDefinitionHighlighter(document, builder), - new InterfaceDefinitionHighlighter(document, builder), - new StructDefinitionHighlighter(document, builder), - ]; - - // Visit the CST - span = transaction.startChild({ op: "walk-generate-symbols" }); - walk( - parseTree.cursor, - (cursor) => { - for (const visitor of visitors) { - visitor.enter(cursor); - } - }, - (cursor) => { - for (const visitor of visitors) { - visitor.exit(cursor); - } + // Visit the CST + span = transaction.startChild({ op: "walk-highlight-tokens" }); + walk( + parseTree.cursor, + (cursor) => { + for (const visitor of visitors) { + visitor.enter(cursor); } - ); - span.finish(); + }, + (cursor) => { + for (const visitor of visitors) { + visitor.exit(cursor); + } + } + ); + span.finish(); - return { data: builder.getTokenData() }; - } catch (error) { - serverState.logger.error(error); - return emptyResponse; - } + return { status: "ok", result: { data: builder.getTokenData() } }; + } catch (error) { + logger.error(error); + return { status: "internal_error", result: emptyResponse }; } - ) || emptyResponse + }) || emptyResponse ); }; } From fea4b12746b3bf7bcc817f128e6f0bbe2c2224a3 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Mon, 30 Oct 2023 07:51:28 -0300 Subject: [PATCH 07/18] optimize walking by storing references --- server/src/parser/slangHelpers.ts | 30 ++++++++++--- .../services/documentSymbol/SymbolVisitor.ts | 9 ++-- .../documentSymbol/onDocumentSymbol.ts | 13 ++++-- .../visitors/ConstructorDefinition.ts | 11 +++-- .../visitors/DefinitionVisitor.ts | 45 +++++++++++-------- .../visitors/FallbackFunctionDefinition.ts | 11 +++-- .../visitors/ReceiveFunctionDefinition.ts | 11 +++-- .../semanticHighlight/HighlightVisitor.ts | 6 +-- .../SemanticTokensBuilder.ts | 13 ++++-- .../ContractDefinitionHighlighter.ts | 13 +++--- .../highlighters/CustomTypeHighlighter.ts | 13 +++--- .../EventDefinitionHighlighter.ts | 13 +++--- .../highlighters/EventEmissionHighlighter.ts | 13 +++--- .../highlighters/FunctionCallHighlighter.ts | 13 +++--- .../FunctionDefinitionHighlighter.ts | 13 +++--- .../InterfaceDefinitionHighlighter.ts | 13 +++--- .../highlighters/KeywordHighlighter.ts | 11 +++-- .../highlighters/NumberHighlighter.ts | 12 ++--- .../highlighters/StringHighlighter.ts | 12 ++--- .../StructDefinitionHighlighter.ts | 13 +++--- .../semanticHighlight/onSemanticTokensFull.ts | 8 ++-- 21 files changed, 170 insertions(+), 126 deletions(-) diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index 43892378..f962961c 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -1,27 +1,47 @@ import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang/cst"; import { Cursor } from "@nomicfoundation/slang/cursor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { TextRange } from "@nomicfoundation/slang/text_index"; import _ from "lodash"; import { TextDocument } from "vscode-languageserver-textdocument"; import { Range } from "vscode-languageserver-types"; export type SlangNode = RuleNode | TokenNode; -export type NodeCallback = (cursor: Cursor) => void; +export type NodeKind = RuleKind | TokenKind; +export type NodeCallback = (node: SlangNodeWrapper) => void; + +export interface SlangNodeWrapper { + textRange: TextRange; + type: NodeType; + kind: NodeKind; + text: string; + pathRuleNodes: SlangNode[]; +} export function walk( cursor: Cursor, onEnter: NodeCallback, onExit: NodeCallback ) { - onEnter(cursor); + const node = cursor.node; + + const nodeWrapper: SlangNodeWrapper = { + textRange: cursor.textRange, + type: node.type, + kind: node.kind, + text: node.text, + pathRuleNodes: cursor.pathRuleNodes, + }; + + onEnter(nodeWrapper); - if (cursor.node.type === NodeType.Rule) { - for (let i = 0; i < cursor.node.children.length; i++) { + if (nodeWrapper.type === NodeType.Rule) { + for (let i = 0; i < node.children.length; i++) { cursor.goToNthChild(i); walk(cursor, onEnter, onExit); } } - onExit(cursor); + onExit(nodeWrapper); cursor.goToParent(); } diff --git a/server/src/services/documentSymbol/SymbolVisitor.ts b/server/src/services/documentSymbol/SymbolVisitor.ts index 2c646908..d29ec812 100644 --- a/server/src/services/documentSymbol/SymbolVisitor.ts +++ b/server/src/services/documentSymbol/SymbolVisitor.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { TextDocument } from "vscode-languageserver-textdocument"; -import { Cursor } from "@nomicfoundation/slang/cursor"; +import { SlangNodeWrapper } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; // Abstraction for a visitor that wants to build part of the document symbol tree @@ -11,9 +11,6 @@ export abstract class SymbolVisitor { public symbolBuilder: SymbolTreeBuilder ) {} - // public enter(node: SlangNode, ancestors: SlangNode[]): void {} - // public exit(node: SlangNode, ancestors: SlangNode[]): void {} - - public enter(cursor: Cursor): void {} - public exit(cursor: Cursor): void {} + public enter(nodeWrapper: SlangNodeWrapper): void {} + public exit(nodeWrapper: SlangNodeWrapper): void {} } diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 8bd87bf1..2c40f697 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -8,6 +8,8 @@ import semver from "semver"; import _ from "lodash"; import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { performance } from "perf_hooks"; import { ServerState } from "../../types"; import { walk } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; @@ -101,9 +103,12 @@ export function onDocumentSymbol(serverState: ServerState) { // console.log({ count }); // const myobj = { foo: { bar: 123 } }; + // const node = kursor.node; // const start = performance.now(); // for (let index = 0; index < 1_000_000; index++) { + // // eslint-disable-next-line @typescript-eslint/no-unused-expressions // myobj.foo.bar; + // // node.type; // } // const end = performance.now(); // const elapsed = end - start; @@ -135,14 +140,14 @@ export function onDocumentSymbol(serverState: ServerState) { span = transaction.startChild({ op: "walk-generate-symbols" }); walk( parseTree.cursor, - (cursor) => { + (nodeWrapper) => { for (const visitor of visitors) { - visitor.enter(cursor); + visitor.enter(nodeWrapper); } }, - (cursor) => { + (nodeWrapper) => { for (const visitor of visitors) { - visitor.exit(cursor); + visitor.exit(nodeWrapper); } } ); diff --git a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts index 860e6b6f..7cd931e0 100644 --- a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts @@ -1,18 +1,21 @@ import { SymbolKind } from "vscode-languageserver-types"; import { RuleKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; -import { slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { + SlangNodeWrapper, + slangToVSCodeRange, +} from "../../../parser/slangHelpers"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ConstructorDefinition extends DefinitionVisitor { public ruleKind = RuleKind.ConstructorDefinition; public symbolKind = SymbolKind.Constructor; - protected getSymbolAttributes(cursor: Cursor) { + protected getSymbolAttributes(nodeWrapper: SlangNodeWrapper) { return { name: "constructor", - range: slangToVSCodeRange(this.document, cursor.textRange), - selectionRange: slangToVSCodeRange(this.document, cursor.textRange), + range: slangToVSCodeRange(this.document, nodeWrapper.textRange), + selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), kind: this.symbolKind, }; } diff --git a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts index ecaa9b97..31dc32fb 100644 --- a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts +++ b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts @@ -3,26 +3,31 @@ import { DocumentSymbol, SymbolKind } from "vscode-languageserver-types"; import _ from "lodash"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { NodeType } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; -import { slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { + SlangNodeWrapper, + slangToVSCodeRange, +} from "../../../parser/slangHelpers"; import { SymbolVisitor } from "../SymbolVisitor"; export abstract class DefinitionVisitor extends SymbolVisitor { public abstract ruleKind: RuleKind; public abstract symbolKind: SymbolKind; - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; - + public enter(nodeWrapper: SlangNodeWrapper): void { // Open a new symbol node on the DocumentSymbol tree on matching rules - if (node.type === NodeType.Rule && node.kind === this.ruleKind) { - this.symbolBuilder.openSymbol(this.getSymbolAttributes(cursor)); + if ( + nodeWrapper.type === NodeType.Rule && + nodeWrapper.kind === this.ruleKind + ) { + this.symbolBuilder.openSymbol(this.getSymbolAttributes(nodeWrapper)); } // Set the symbol node's range and name when finding the related identifier - if (node.type === NodeType.Token && node.kind === TokenKind.Identifier) { - const parent = _.last(ancestors)!; + if ( + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier + ) { + const parent = _.last(nodeWrapper.pathRuleNodes)!; if (parent.type !== NodeType.Rule || parent.kind !== this.ruleKind) { return; @@ -36,25 +41,29 @@ export abstract class DefinitionVisitor extends SymbolVisitor { const identifierRange = slangToVSCodeRange( this.document, - cursor.textRange + nodeWrapper.textRange ); - lastSymbol.name = node.text; + lastSymbol.name = nodeWrapper.text; lastSymbol.selectionRange = identifierRange; } } - protected getSymbolAttributes(cursor: Cursor): Partial { + protected getSymbolAttributes( + nodeWrapper: SlangNodeWrapper + ): Partial { return { - range: slangToVSCodeRange(this.document, cursor.textRange), - selectionRange: slangToVSCodeRange(this.document, cursor.textRange), + range: slangToVSCodeRange(this.document, nodeWrapper.textRange), + selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), kind: this.symbolKind, }; } - public exit(cursor: Cursor): void { - const node = cursor.node; - if (node.type === NodeType.Rule && node.kind === this.ruleKind) { + public exit(nodeWrapper: SlangNodeWrapper): void { + if ( + nodeWrapper.type === NodeType.Rule && + nodeWrapper.kind === this.ruleKind + ) { this.symbolBuilder.closeSymbol(); } } diff --git a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts index c9826d2c..485e0c24 100644 --- a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts @@ -1,18 +1,21 @@ import { SymbolKind } from "vscode-languageserver-types"; import { RuleKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; -import { slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { + SlangNodeWrapper, + slangToVSCodeRange, +} from "../../../parser/slangHelpers"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class FallbackFunctionDefinition extends DefinitionVisitor { public ruleKind = RuleKind.FallbackFunctionDefinition; public symbolKind = SymbolKind.Function; - protected getSymbolAttributes(cursor: Cursor) { + protected getSymbolAttributes(nodeWrapper: SlangNodeWrapper) { return { name: "fallback", - range: slangToVSCodeRange(this.document, cursor.textRange), - selectionRange: slangToVSCodeRange(this.document, cursor.textRange), + range: slangToVSCodeRange(this.document, nodeWrapper.textRange), + selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), kind: this.symbolKind, }; } diff --git a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts index 56f92cc8..99063e92 100644 --- a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts @@ -1,18 +1,21 @@ import { SymbolKind } from "vscode-languageserver-types"; import { RuleKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; -import { slangToVSCodeRange } from "../../../parser/slangHelpers"; +import { + SlangNodeWrapper, + slangToVSCodeRange, +} from "../../../parser/slangHelpers"; import { DefinitionVisitor } from "./DefinitionVisitor"; export class ReceiveFunctionDefinition extends DefinitionVisitor { public ruleKind = RuleKind.ReceiveFunctionDefinition; public symbolKind = SymbolKind.Function; - protected getSymbolAttributes(cursor: Cursor) { + protected getSymbolAttributes(nodeWrapper: SlangNodeWrapper) { return { name: "receive", - range: slangToVSCodeRange(this.document, cursor.textRange), - selectionRange: slangToVSCodeRange(this.document, cursor.textRange), + range: slangToVSCodeRange(this.document, nodeWrapper.textRange), + selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), kind: this.symbolKind, }; } diff --git a/server/src/services/semanticHighlight/HighlightVisitor.ts b/server/src/services/semanticHighlight/HighlightVisitor.ts index 1c72d019..09794d70 100644 --- a/server/src/services/semanticHighlight/HighlightVisitor.ts +++ b/server/src/services/semanticHighlight/HighlightVisitor.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { TextDocument } from "vscode-languageserver-textdocument"; -import { Cursor } from "@nomicfoundation/slang/cursor"; +import { SlangNodeWrapper } from "../../parser/slangHelpers"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; // Abstraction for a visitor that wants to highlight tokens @@ -11,6 +11,6 @@ export abstract class HighlightVisitor { public tokenBuilder: SemanticTokensBuilder ) {} - public enter(cursor: Cursor): void {} - public exit(cursor: Cursor): void {} + public enter(nodeWrapper: SlangNodeWrapper): void {} + public exit(nodeWrapper: SlangNodeWrapper): void {} } diff --git a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts index f533d2ff..166577fa 100644 --- a/server/src/services/semanticHighlight/SemanticTokensBuilder.ts +++ b/server/src/services/semanticHighlight/SemanticTokensBuilder.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TextDocument } from "vscode-languageserver-textdocument"; -import { Cursor } from "@nomicfoundation/slang/cursor"; +import { SlangNodeWrapper } from "../../parser/slangHelpers"; import { getTokenTypeIndex } from "./tokenTypes"; // Helps building a SemanticTokens response by providing slang nodes and supported token types @@ -12,9 +12,14 @@ export class SemanticTokensBuilder { constructor(private document: TextDocument) {} - public addToken(cursor: Cursor, type: SemanticTokenTypes, modifiers = 0) { - const offset = cursor.textRange.start.utf16; - const length = cursor.textRange.end.utf16 - cursor.textRange.start.utf16; + public addToken( + nodeWrapper: SlangNodeWrapper, + type: SemanticTokenTypes, + modifiers = 0 + ) { + const offset = nodeWrapper.textRange.start.utf16; + const length = + nodeWrapper.textRange.end.utf16 - nodeWrapper.textRange.start.utf16; const position = this.document.positionAt(offset); diff --git a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts index 698555a0..3c7a52ac 100644 --- a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights contract definitions export class ContractDefinitionHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 1]?.kind === RuleKind.ContractDefinition ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts index d56aad8f..79f951f6 100644 --- a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights custom type names export class CustomTypeHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 2]?.kind === RuleKind.TypeName ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts index 56c29def..9501a0ab 100644 --- a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights event definitions export class EventDefinitionHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 1]?.kind === RuleKind.EventDefinition ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts index f408fec4..e292386f 100644 --- a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights event emissions export class EventEmissionHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 2]?.kind === RuleKind.EmitStatement ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts index 86b5337d..646cef83 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights function calls export class FunctionCallHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 2]?.kind === RuleKind.FunctionCallExpression ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.function); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.function); } } } diff --git a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts index 13251552..7e29b382 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights function definitions export class FunctionDefinitionHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 1]?.kind === RuleKind.FunctionDefinition ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.function); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.function); } } } diff --git a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts index 3863588c..9b5f6e68 100644 --- a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights interface definitions export class InterfaceDefinitionHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 1]?.kind === RuleKind.InterfaceDefinition ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts index 0aafdad1..56e4e8b2 100644 --- a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts @@ -3,6 +3,7 @@ import { TokenKind } from "@nomicfoundation/slang/kinds"; import { NodeType } from "@nomicfoundation/slang/cst"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; const keywordKinds = new Set([ TokenKind.ABICoderKeyword, @@ -90,10 +91,12 @@ const keywordKinds = new Set([ // Highlights keywords export class KeywordHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - if (node.type === NodeType.Token && keywordKinds.has(node.kind)) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.keyword); + public enter(nodeWrapper: SlangNodeWrapper): void { + if ( + nodeWrapper.type === NodeType.Token && + keywordKinds.has(nodeWrapper.kind as TokenKind) + ) { + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.keyword); } } } diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts index 3cfe1073..647b31b6 100644 --- a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts @@ -1,8 +1,8 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; import { NodeType } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; const numberKinds = new Set([ TokenKind.HexLiteral, @@ -12,10 +12,12 @@ const numberKinds = new Set([ // Highlights numbers export class NumberHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - if (node.type === NodeType.Token && numberKinds.has(node.kind)) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.number); + public enter(nodeWrapper: SlangNodeWrapper): void { + if ( + nodeWrapper.type === NodeType.Token && + numberKinds.has(nodeWrapper.kind as TokenKind) + ) { + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.number); } } } diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts index 766d01f6..89addfba 100644 --- a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts @@ -1,8 +1,8 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; import { NodeType } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; const stringKinds = new Set([ TokenKind.HexStringLiteral, @@ -12,10 +12,12 @@ const stringKinds = new Set([ // Highlights strings export class StringHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - if (node.type === NodeType.Token && stringKinds.has(node.kind)) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.string); + public enter(nodeWrapper: SlangNodeWrapper): void { + if ( + nodeWrapper.type === NodeType.Token && + stringKinds.has(nodeWrapper.kind as TokenKind) + ) { + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.string); } } } diff --git a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts index 526537e4..35cd4256 100644 --- a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts @@ -1,20 +1,19 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights struct definitions export class StructDefinitionHighlighter extends HighlightVisitor { - public enter(cursor: Cursor): void { - const node = cursor.node; - const ancestors = cursor.pathRuleNodes; + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( - node.type === NodeType.Token && - node.kind === TokenKind.Identifier && + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && ancestors[ancestors.length - 1]?.kind === RuleKind.StructDefinition ) { - this.tokenBuilder.addToken(cursor, SemanticTokenTypes.type); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index f1912b4d..8e8fa2de 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -103,14 +103,14 @@ export function onSemanticTokensFull(serverState: ServerState) { span = transaction.startChild({ op: "walk-highlight-tokens" }); walk( parseTree.cursor, - (cursor) => { + (nodeWrapper) => { for (const visitor of visitors) { - visitor.enter(cursor); + visitor.enter(nodeWrapper); } }, - (cursor) => { + (nodeWrapper) => { for (const visitor of visitors) { - visitor.exit(cursor); + visitor.exit(nodeWrapper); } } ); From 4fe2396d736f8a2e8f5d23ebb0e506077fec5ef8 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Mon, 30 Oct 2023 08:05:35 -0300 Subject: [PATCH 08/18] leverage trackTimingSync error handling --- .../documentSymbol/onDocumentSymbol.ts | 186 ++++++++---------- 1 file changed, 85 insertions(+), 101 deletions(-) diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 2c40f697..07419052 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -8,8 +8,6 @@ import semver from "semver"; import _ from "lodash"; import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; -import { performance } from "perf_hooks"; import { ServerState } from "../../types"; import { walk } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; @@ -36,7 +34,7 @@ export function onDocumentSymbol(serverState: ServerState) { return async ( params: DocumentSymbolParams ): Promise => { - const { telemetry, logger, solcVersions } = serverState; + const { telemetry, solcVersions } = serverState; return telemetry.trackTimingSync("onDocumentSymbol", (transaction) => { const { uri } = params.textDocument; @@ -44,11 +42,7 @@ export function onDocumentSymbol(serverState: ServerState) { const document = serverState.documents.get(uri); if (document === undefined) { - logger.error("document not found in collection"); - return { - status: "internal_error", - result: null, - }; + throw new Error(`${uri} not found in documents`); } const text = document.getText(); @@ -61,105 +55,95 @@ export function onDocumentSymbol(serverState: ServerState) { semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || _.last(solcVersions); - try { - // Parse using slang - span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(solcVersion!); + // Parse using slang + span = transaction.startChild({ op: "slang-parsing" }); + const language = new Language(solcVersion!); - const parseOutput = language.parse( - ProductionKind.SourceUnit, - document.getText() - ); + const parseOutput = language.parse( + ProductionKind.SourceUnit, + document.getText() + ); - const parseTree = parseOutput.parseTree; - span.finish(); + const parseTree = parseOutput.parseTree; + span.finish(); - if (parseTree === null) { - logger.error("Slang parsing error"); - const strings = parseOutput.errors.map((e: any) => - e.toErrorReport(uri, text, false) - ); - logger.error(strings.join("")); + if (parseTree === null) { + const strings = parseOutput.errors.map((e: any) => + e.toErrorReport(uri, text, false) + ); - return { - status: "internal_error", - result: null, - }; - } + throw new Error(`Slang parsing error: ${strings.join("")}`); + } - // let count = 0; - // // const start = performance.now(); - // const kursor: Cursor = parseTree.cursor.clone(); - // do { - // console.log( - // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}: ${ - // kursor.node?.text - // }` - // ); - // } while (kursor.goToNext()); - // const end = performance.now(); - // const elapsed = end - start; - // console.log({ elapsed }); - // console.log({ count }); - - // const myobj = { foo: { bar: 123 } }; - // const node = kursor.node; - // const start = performance.now(); - // for (let index = 0; index < 1_000_000; index++) { - // // eslint-disable-next-line @typescript-eslint/no-unused-expressions - // myobj.foo.bar; - // // node.type; - // } - // const end = performance.now(); - // const elapsed = end - start; - // console.log({ elapsed }); - - const builder = new SymbolTreeBuilder(); - - const visitors: SymbolVisitor[] = [ - new StructDefinition(document, builder), - new StructMember(document, builder), - new InterfaceDefinition(document, builder), - new FunctionDefinition(document, builder), - new ContractDefinition(document, builder), - new EventDefinition(document, builder), - new StateVariableDeclaration(document, builder), - new VariableDeclaration(document, builder), - new ConstantDefinition(document, builder), - new ConstructorDefinition(document, builder), - new EnumDefinition(document, builder), - new ErrorDefinition(document, builder), - new FallbackFunctionDefinition(document, builder), - new LibraryDefinition(document, builder), - new ModifierDefinition(document, builder), - new ReceiveFunctionDefinition(document, builder), - new UserDefinedValueTypeDefinition(document, builder), - ]; - - // Walk the tree and generate symbol nodes - span = transaction.startChild({ op: "walk-generate-symbols" }); - walk( - parseTree.cursor, - (nodeWrapper) => { - for (const visitor of visitors) { - visitor.enter(nodeWrapper); - } - }, - (nodeWrapper) => { - for (const visitor of visitors) { - visitor.exit(nodeWrapper); - } + // let count = 0; + // // const start = performance.now(); + // const kursor: Cursor = parseTree.cursor.clone(); + // do { + // console.log( + // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}: ${ + // kursor.node?.text + // }` + // ); + // } while (kursor.goToNext()); + // const end = performance.now(); + // const elapsed = end - start; + // console.log({ elapsed }); + // console.log({ count }); + + // const myobj = { foo: { bar: 123 } }; + // const node = kursor.node; + // const start = performance.now(); + // for (let index = 0; index < 1_000_000; index++) { + // // eslint-disable-next-line @typescript-eslint/no-unused-expressions + // myobj.foo.bar; + // // node.type; + // } + // const end = performance.now(); + // const elapsed = end - start; + // console.log({ elapsed }); + + const builder = new SymbolTreeBuilder(); + + const visitors: SymbolVisitor[] = [ + new StructDefinition(document, builder), + new StructMember(document, builder), + new InterfaceDefinition(document, builder), + new FunctionDefinition(document, builder), + new ContractDefinition(document, builder), + new EventDefinition(document, builder), + new StateVariableDeclaration(document, builder), + new VariableDeclaration(document, builder), + new ConstantDefinition(document, builder), + new ConstructorDefinition(document, builder), + new EnumDefinition(document, builder), + new ErrorDefinition(document, builder), + new FallbackFunctionDefinition(document, builder), + new LibraryDefinition(document, builder), + new ModifierDefinition(document, builder), + new ReceiveFunctionDefinition(document, builder), + new UserDefinedValueTypeDefinition(document, builder), + ]; + + // Walk the tree and generate symbol nodes + span = transaction.startChild({ op: "walk-generate-symbols" }); + walk( + parseTree.cursor, + (nodeWrapper) => { + for (const visitor of visitors) { + visitor.enter(nodeWrapper); } - ); - span.finish(); - // console.log("ALL GOOD"); - // console.log(JSON.stringify(builder.symbols, null, 2)); - - return { status: "ok", result: builder.symbols }; - } catch (error) { - logger.error(error); - return { status: "internal_error", result: null }; - } + }, + (nodeWrapper) => { + for (const visitor of visitors) { + visitor.exit(nodeWrapper); + } + } + ); + span.finish(); + // console.log("ALL GOOD"); + // console.log(JSON.stringify(builder.symbols, null, 2)); + + return { status: "ok", result: builder.symbols }; }); }; } From 2d9db70ce571e34990fcdebb68dc35af728cf4ac Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Mon, 30 Oct 2023 12:49:14 -0300 Subject: [PATCH 09/18] enable documentSymbol and semanticHighlight only on slang-supported platforms --- server/src/parser/slangHelpers.ts | 16 ++++++++++++++++ .../src/services/initialization/onInitialize.ts | 7 +++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index f962961c..7557e600 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -5,6 +5,7 @@ import { TextRange } from "@nomicfoundation/slang/text_index"; import _ from "lodash"; import { TextDocument } from "vscode-languageserver-textdocument"; import { Range } from "vscode-languageserver-types"; +import os from "os"; export type SlangNode = RuleNode | TokenNode; export type NodeKind = RuleKind | TokenKind; @@ -54,3 +55,18 @@ export function slangToVSCodeRange( end: doc.positionAt(slangRange.end.utf16), }; } + +const SUPPORTED_PLATFORMS = [ + "darwin-arm64", + "darwin-x64", + "linux-arm64", + "linux-x64", + "win32-arm64", + "win32-ia32", + "win32-x64", +]; +export function isSlangSupported() { + const currentPlatform = `${os.platform()}-${os.arch()}`; + + return SUPPORTED_PLATFORMS.includes(currentPlatform); +} diff --git a/server/src/services/initialization/onInitialize.ts b/server/src/services/initialization/onInitialize.ts index c3874968..a0986b0f 100644 --- a/server/src/services/initialization/onInitialize.ts +++ b/server/src/services/initialization/onInitialize.ts @@ -6,6 +6,7 @@ import { } from "vscode-languageserver/node"; import { ServerState } from "../../types"; import { tokensTypes } from "../semanticHighlight/tokenTypes"; +import { isSlangSupported } from "../../parser/slangHelpers"; import { indexWorkspaceFolders } from "./indexWorkspaceFolders"; import { updateAvailableSolcVersions } from "./updateAvailableSolcVersions"; @@ -58,6 +59,8 @@ export const onInitialize = (serverState: ServerState) => { return { status: "ok", result: null }; }); + const slangSupported = isSlangSupported(); + // Build and return InitializeResult const result: InitializeResult = { serverInfo: { @@ -86,9 +89,9 @@ export const onInitialize = (serverState: ServerState) => { tokenModifiers: [], }, range: false, - full: true, + full: slangSupported, }, - documentSymbolProvider: true, + documentSymbolProvider: slangSupported, workspace: { workspaceFolders: { supported: false, From efed2dfcf18afab62f931e2625203c742e4a7b84 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Thu, 2 Nov 2023 12:52:33 -0300 Subject: [PATCH 10/18] feature flags --- server/src/parser/slangHelpers.ts | 5 +- .../documentSymbol/onDocumentSymbol.ts | 3 + .../services/initialization/featureFlags.ts | 71 +++++++++++++++++++ .../services/initialization/onInitialize.ts | 46 ++++++++---- .../semanticHighlight/onSemanticTokensFull.ts | 3 + server/src/utils/operatingSystem.ts | 4 ++ 6 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 server/src/services/initialization/featureFlags.ts diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index 7557e600..3963236b 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -5,7 +5,7 @@ import { TextRange } from "@nomicfoundation/slang/text_index"; import _ from "lodash"; import { TextDocument } from "vscode-languageserver-textdocument"; import { Range } from "vscode-languageserver-types"; -import os from "os"; +import { getPlatform } from "../utils/operatingSystem"; export type SlangNode = RuleNode | TokenNode; export type NodeKind = RuleKind | TokenKind; @@ -65,8 +65,9 @@ const SUPPORTED_PLATFORMS = [ "win32-ia32", "win32-x64", ]; + export function isSlangSupported() { - const currentPlatform = `${os.platform()}-${os.arch()}`; + const currentPlatform = getPlatform(); return SUPPORTED_PLATFORMS.includes(currentPlatform); } diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 07419052..e56cf919 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -51,6 +51,9 @@ export function onDocumentSymbol(serverState: ServerState) { let span = transaction.startChild({ op: "solidity-analyzer" }); const { versionPragmas } = analyze(text); span.finish(); + + versionPragmas.push("<= 0.8.19"); // latest supported by slang + const solcVersion = semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || _.last(solcVersions); diff --git a/server/src/services/initialization/featureFlags.ts b/server/src/services/initialization/featureFlags.ts new file mode 100644 index 00000000..e264074f --- /dev/null +++ b/server/src/services/initialization/featureFlags.ts @@ -0,0 +1,71 @@ +import got from "got"; +import crypto from "crypto"; +import { ServerState } from "../../types"; + +export interface FeatureFlag { + percent: number; +} + +export interface FeatureFlags { + semanticHighlighting: FeatureFlag; + documentSymbol: FeatureFlag; +} + +const DEFAULT_FLAGS: FeatureFlags = { + semanticHighlighting: { + percent: 0, + }, + documentSymbol: { + percent: 0, + }, +}; + +export async function fetchFeatureFlags( + state: ServerState +): Promise { + state.logger.info("Fetching feature flags"); + + try { + return await got + .get( + "https://raw.githubusercontent.com/antico5/hardhat-vscode/flags/flags.json", + { + timeout: 2000, + } + ) + .json(); + } catch (error) { + state.telemetry.captureException(error); + return DEFAULT_FLAGS; + } +} + +export function isFeatureEnabled( + { logger }: ServerState, + flags: FeatureFlags, + feature: keyof FeatureFlags, + machineId?: string +): boolean { + const flag = flags[feature]; + + if (machineId === undefined) { + logger.info(`MachineId is undefined, turning feature flags off`); + return false; + } + + if (flag === undefined) { + throw new Error(`Feature flag not found: ${feature}`); + } + + // hash the machineId to get an evenly distributed value + const machineIdHash = crypto.createHash("md5"); + machineIdHash.update(machineId); + const digest = machineIdHash.digest("hex").slice(-4); // get last 2 bytes + + // check what percentile the current machineId is in + const numberDigest = parseInt(digest, 16); + const percentile = numberDigest / 65536; + const enabled = percentile < flag.percent; + + return enabled; +} diff --git a/server/src/services/initialization/onInitialize.ts b/server/src/services/initialization/onInitialize.ts index a0986b0f..342ed876 100644 --- a/server/src/services/initialization/onInitialize.ts +++ b/server/src/services/initialization/onInitialize.ts @@ -7,8 +7,10 @@ import { import { ServerState } from "../../types"; import { tokensTypes } from "../semanticHighlight/tokenTypes"; import { isSlangSupported } from "../../parser/slangHelpers"; +import { getPlatform } from "../../utils/operatingSystem"; import { indexWorkspaceFolders } from "./indexWorkspaceFolders"; import { updateAvailableSolcVersions } from "./updateAvailableSolcVersions"; +import { fetchFeatureFlags, isFeatureEnabled } from "./featureFlags"; export const onInitialize = (serverState: ServerState) => { const { logger } = serverState; @@ -42,22 +44,29 @@ export const onInitialize = (serverState: ServerState) => { workspaceFolders, }); - // fetch available solidity versions - await updateAvailableSolcVersions(serverState); + // fetch available solidity versions and feature flags + const [flags] = await Promise.all([ + fetchFeatureFlags(serverState), + updateAvailableSolcVersions(serverState), + ]); logger.info("Language server ready"); // Index and analysis - await serverState.telemetry.trackTiming("indexing", async (transaction) => { - await indexWorkspaceFolders( - serverState, - serverState.workspaceFileRetriever, - workspaceFolders, - transaction - ); - - return { status: "ok", result: null }; - }); + await serverState.telemetry.trackTiming( + "indexing", + async (transaction) => { + await indexWorkspaceFolders( + serverState, + serverState.workspaceFileRetriever, + workspaceFolders, + transaction + ); + + return { status: "ok", result: null }; + }, + { platform: getPlatform() } + ); const slangSupported = isSlangSupported(); @@ -89,9 +98,18 @@ export const onInitialize = (serverState: ServerState) => { tokenModifiers: [], }, range: false, - full: slangSupported, + full: + slangSupported && + isFeatureEnabled( + serverState, + flags, + "semanticHighlighting", + machineId + ), }, - documentSymbolProvider: slangSupported, + documentSymbolProvider: + slangSupported && + isFeatureEnabled(serverState, flags, "documentSymbol", machineId), workspace: { workspaceFolders: { supported: false, diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 8e8fa2de..51c00c84 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -52,6 +52,9 @@ export function onSemanticTokensFull(serverState: ServerState) { let span = transaction.startChild({ op: "solidity-analyzer" }); const { versionPragmas } = analyze(text); span.finish(); + + versionPragmas.push("<= 0.8.19"); // latest supported by slang + const solcVersion = semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || _.last(solcVersions); diff --git a/server/src/utils/operatingSystem.ts b/server/src/utils/operatingSystem.ts index 0537cc3b..a62ccdc4 100644 --- a/server/src/utils/operatingSystem.ts +++ b/server/src/utils/operatingSystem.ts @@ -16,3 +16,7 @@ export async function runCmd(cmd: string, cwd?: string): Promise { }); }); } + +export function getPlatform() { + return `${os.platform()}-${os.arch()}`; +} From 6a8dd2c792645baf87c2f7cf31360c5f02f27da1 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Thu, 2 Nov 2023 16:45:23 -0300 Subject: [PATCH 11/18] use finders for walking the cst --- .../documentSymbol/SymbolTreeBuilder.ts | 19 ++- .../services/documentSymbol/SymbolVisitor.ts | 65 +++++++- .../documentSymbol/onDocumentSymbol.ts | 59 +++---- .../visitors/ConstantDefinition.ts | 7 +- .../visitors/ConstructorDefinition.ts | 21 +-- .../visitors/ContractDefinition.ts | 7 +- .../visitors/DefinitionVisitor.ts | 70 --------- .../documentSymbol/visitors/EnumDefinition.ts | 7 +- .../visitors/ErrorDefinition.ts | 7 +- .../visitors/EventDefinition.ts | 7 +- .../visitors/FallbackFunctionDefinition.ts | 21 +-- .../visitors/FunctionDefinition.ts | 7 +- .../visitors/InterfaceDefinition.ts | 7 +- .../visitors/LibraryDefinition.ts | 7 +- .../visitors/ModifierDefinition.ts | 7 +- .../visitors/ReceiveFunctionDefinition.ts | 21 +-- .../visitors/StateVariableDeclaration.ts | 7 +- .../visitors/StructDefinition.ts | 7 +- .../documentSymbol/visitors/StructMember.ts | 7 +- .../UserDefinedValueTypeDefinition.ts | 7 +- .../visitors/VariableDeclaration.ts | 7 +- server/src/telemetry/SentryServerTelemetry.ts | 2 + .../documentSymbol/documentSymbol.test.ts | 148 +++++++++--------- 23 files changed, 238 insertions(+), 286 deletions(-) delete mode 100644 server/src/services/documentSymbol/visitors/DefinitionVisitor.ts diff --git a/server/src/services/documentSymbol/SymbolTreeBuilder.ts b/server/src/services/documentSymbol/SymbolTreeBuilder.ts index 32bd59b8..bf4aee01 100644 --- a/server/src/services/documentSymbol/SymbolTreeBuilder.ts +++ b/server/src/services/documentSymbol/SymbolTreeBuilder.ts @@ -2,8 +2,8 @@ import { DocumentSymbol } from "vscode-languageserver-types"; import _ from "lodash"; export class SymbolTreeBuilder { - public symbols: DocumentSymbol[] = []; - public currentPath: Array> = []; + private symbols: DocumentSymbol[] = []; + private currentPath: Array> = []; public openSymbol(params: Partial) { const symbol = { @@ -24,7 +24,7 @@ export class SymbolTreeBuilder { if (this.currentPath.length === 0) { this.symbols.push(symbol); } else { - const lastSymbol = _.last(this.currentPath); + const lastSymbol = this.lastOpenSymbol(); if (!lastSymbol) { throw new Error("Attempting to close a symbol but none is open"); @@ -33,4 +33,17 @@ export class SymbolTreeBuilder { lastSymbol.children?.push(symbol); } } + + public lastOpenSymbol() { + return _.last(this.currentPath); + } + + public getSymbols() { + // Close any left open symbols + while (this.lastOpenSymbol()) { + this.closeSymbol(); + } + + return this.symbols; + } } diff --git a/server/src/services/documentSymbol/SymbolVisitor.ts b/server/src/services/documentSymbol/SymbolVisitor.ts index d29ec812..8461b354 100644 --- a/server/src/services/documentSymbol/SymbolVisitor.ts +++ b/server/src/services/documentSymbol/SymbolVisitor.ts @@ -1,16 +1,69 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { SymbolKind } from "vscode-languageserver-types"; +import _ from "lodash"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { RuleNode, TokenNode } from "@nomicfoundation/slang/cst"; import { TextDocument } from "vscode-languageserver-textdocument"; -import { SlangNodeWrapper } from "../../parser/slangHelpers"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { slangToVSCodeRange } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; -// Abstraction for a visitor that wants to build part of the document symbol tree export abstract class SymbolVisitor { + public abstract ruleKind: RuleKind; + public abstract symbolKind: SymbolKind; + public abstract nameTokenKind: TokenKind; + public nameTokenSkips = 0; + constructor( public document: TextDocument, public symbolBuilder: SymbolTreeBuilder ) {} - public enter(nodeWrapper: SlangNodeWrapper): void {} - public exit(nodeWrapper: SlangNodeWrapper): void {} + public onRuleNode(node: RuleNode, cursor: Cursor): void { + const range = slangToVSCodeRange(this.document, cursor.textRange); + + let symbolName = "-"; + let selectionRange = slangToVSCodeRange(this.document, cursor.textRange); + + // Find identifier + const childCursor = cursor.spawn(); + + do { + const identifierNode: TokenNode | null = childCursor.findTokenWithKind([ + this.nameTokenKind, + ]); + + if ( + identifierNode && + _.last(childCursor.pathRuleNodes).kind === this.ruleKind + ) { + symbolName = identifierNode.text; + selectionRange = slangToVSCodeRange( + this.document, + childCursor.textRange + ); + break; + } + } while (childCursor.goToNext()); + + let lastOpenSymbol; + + while ((lastOpenSymbol = this.symbolBuilder.lastOpenSymbol())) { + const lastEndOffset = this.document.offsetAt(lastOpenSymbol.range!.end); + const currentEndOffset = this.document.offsetAt(range.end); + + if (lastEndOffset < currentEndOffset) { + this.symbolBuilder.closeSymbol(); + } else { + break; + } + } + + this.symbolBuilder.openSymbol({ + kind: this.symbolKind, + name: symbolName, + range, + selectionRange, + }); + } } diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index e56cf919..4621e143 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -8,11 +8,11 @@ import semver from "semver"; import _ from "lodash"; import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { RuleNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; -import { walk } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; import { StructDefinition } from "./visitors/StructDefinition"; -import { SymbolVisitor } from "./SymbolVisitor"; import { StructMember } from "./visitors/StructMember"; import { InterfaceDefinition } from "./visitors/InterfaceDefinition"; import { FunctionDefinition } from "./visitors/FunctionDefinition"; @@ -29,6 +29,7 @@ import { LibraryDefinition } from "./visitors/LibraryDefinition"; import { ModifierDefinition } from "./visitors/ModifierDefinition"; import { ReceiveFunctionDefinition } from "./visitors/ReceiveFunctionDefinition"; import { UserDefinedValueTypeDefinition } from "./visitors/UserDefinedValueTypeDefinition"; +import { SymbolVisitor } from "./SymbolVisitor"; export function onDocumentSymbol(serverState: ServerState) { return async ( @@ -78,32 +79,14 @@ export function onDocumentSymbol(serverState: ServerState) { throw new Error(`Slang parsing error: ${strings.join("")}`); } - // let count = 0; - // // const start = performance.now(); // const kursor: Cursor = parseTree.cursor.clone(); // do { // console.log( - // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}: ${ - // kursor.node?.text - // }` + // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}(${ + // ["R", "T"][kursor.node.type] + // }): ${kursor.node?.text ?? ""}` // ); // } while (kursor.goToNext()); - // const end = performance.now(); - // const elapsed = end - start; - // console.log({ elapsed }); - // console.log({ count }); - - // const myobj = { foo: { bar: 123 } }; - // const node = kursor.node; - // const start = performance.now(); - // for (let index = 0; index < 1_000_000; index++) { - // // eslint-disable-next-line @typescript-eslint/no-unused-expressions - // myobj.foo.bar; - // // node.type; - // } - // const end = performance.now(); - // const elapsed = end - start; - // console.log({ elapsed }); const builder = new SymbolTreeBuilder(); @@ -127,26 +110,22 @@ export function onDocumentSymbol(serverState: ServerState) { new UserDefinedValueTypeDefinition(document, builder), ]; - // Walk the tree and generate symbol nodes + const indexedVisitors = _.keyBy(visitors, "ruleKind"); + + const cursor: Cursor = parseTree.cursor; + const ruleKinds = visitors.map((v) => v.ruleKind); + let node: RuleNode; + span = transaction.startChild({ op: "walk-generate-symbols" }); - walk( - parseTree.cursor, - (nodeWrapper) => { - for (const visitor of visitors) { - visitor.enter(nodeWrapper); - } - }, - (nodeWrapper) => { - for (const visitor of visitors) { - visitor.exit(nodeWrapper); - } - } - ); + while ((node = cursor.findRuleWithKind(ruleKinds)) !== null) { + const visitor: SymbolVisitor = indexedVisitors[node.kind]; + visitor.onRuleNode(node, cursor); + + cursor.goToNext(); + } span.finish(); - // console.log("ALL GOOD"); - // console.log(JSON.stringify(builder.symbols, null, 2)); - return { status: "ok", result: builder.symbols }; + return { status: "ok", result: builder.getSymbols() }; }); }; } diff --git a/server/src/services/documentSymbol/visitors/ConstantDefinition.ts b/server/src/services/documentSymbol/visitors/ConstantDefinition.ts index ca8df7c4..a83013d5 100644 --- a/server/src/services/documentSymbol/visitors/ConstantDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ConstantDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class ConstantDefinition extends DefinitionVisitor { +export class ConstantDefinition extends SymbolVisitor { public ruleKind = RuleKind.ConstantDefinition; public symbolKind = SymbolKind.Constant; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts index 7cd931e0..c1faaee9 100644 --- a/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ConstructorDefinition.ts @@ -1,22 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; -import { - SlangNodeWrapper, - slangToVSCodeRange, -} from "../../../parser/slangHelpers"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class ConstructorDefinition extends DefinitionVisitor { +export class ConstructorDefinition extends SymbolVisitor { public ruleKind = RuleKind.ConstructorDefinition; public symbolKind = SymbolKind.Constructor; - - protected getSymbolAttributes(nodeWrapper: SlangNodeWrapper) { - return { - name: "constructor", - range: slangToVSCodeRange(this.document, nodeWrapper.textRange), - selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), - kind: this.symbolKind, - }; - } + public nameTokenKind = TokenKind.ConstructorKeyword; } diff --git a/server/src/services/documentSymbol/visitors/ContractDefinition.ts b/server/src/services/documentSymbol/visitors/ContractDefinition.ts index 0a0a00ca..e5f0a9d3 100644 --- a/server/src/services/documentSymbol/visitors/ContractDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ContractDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class ContractDefinition extends DefinitionVisitor { +export class ContractDefinition extends SymbolVisitor { public ruleKind = RuleKind.ContractDefinition; public symbolKind = SymbolKind.Class; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts b/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts deleted file mode 100644 index 31dc32fb..00000000 --- a/server/src/services/documentSymbol/visitors/DefinitionVisitor.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { DocumentSymbol, SymbolKind } from "vscode-languageserver-types"; -import _ from "lodash"; -import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType } from "@nomicfoundation/slang/cst"; -import { - SlangNodeWrapper, - slangToVSCodeRange, -} from "../../../parser/slangHelpers"; -import { SymbolVisitor } from "../SymbolVisitor"; - -export abstract class DefinitionVisitor extends SymbolVisitor { - public abstract ruleKind: RuleKind; - public abstract symbolKind: SymbolKind; - - public enter(nodeWrapper: SlangNodeWrapper): void { - // Open a new symbol node on the DocumentSymbol tree on matching rules - if ( - nodeWrapper.type === NodeType.Rule && - nodeWrapper.kind === this.ruleKind - ) { - this.symbolBuilder.openSymbol(this.getSymbolAttributes(nodeWrapper)); - } - - // Set the symbol node's range and name when finding the related identifier - if ( - nodeWrapper.type === NodeType.Token && - nodeWrapper.kind === TokenKind.Identifier - ) { - const parent = _.last(nodeWrapper.pathRuleNodes)!; - - if (parent.type !== NodeType.Rule || parent.kind !== this.ruleKind) { - return; - } - - const lastSymbol = _.last(this.symbolBuilder.currentPath); - - if (lastSymbol === undefined) { - return; - } - - const identifierRange = slangToVSCodeRange( - this.document, - nodeWrapper.textRange - ); - - lastSymbol.name = nodeWrapper.text; - lastSymbol.selectionRange = identifierRange; - } - } - - protected getSymbolAttributes( - nodeWrapper: SlangNodeWrapper - ): Partial { - return { - range: slangToVSCodeRange(this.document, nodeWrapper.textRange), - selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), - kind: this.symbolKind, - }; - } - - public exit(nodeWrapper: SlangNodeWrapper): void { - if ( - nodeWrapper.type === NodeType.Rule && - nodeWrapper.kind === this.ruleKind - ) { - this.symbolBuilder.closeSymbol(); - } - } -} diff --git a/server/src/services/documentSymbol/visitors/EnumDefinition.ts b/server/src/services/documentSymbol/visitors/EnumDefinition.ts index 25c01682..e27f715f 100644 --- a/server/src/services/documentSymbol/visitors/EnumDefinition.ts +++ b/server/src/services/documentSymbol/visitors/EnumDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class EnumDefinition extends DefinitionVisitor { +export class EnumDefinition extends SymbolVisitor { public ruleKind = RuleKind.EnumDefinition; public symbolKind = SymbolKind.Enum; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/ErrorDefinition.ts b/server/src/services/documentSymbol/visitors/ErrorDefinition.ts index 4cd985a9..7f607faa 100644 --- a/server/src/services/documentSymbol/visitors/ErrorDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ErrorDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class ErrorDefinition extends DefinitionVisitor { +export class ErrorDefinition extends SymbolVisitor { public ruleKind = RuleKind.ErrorDefinition; public symbolKind = SymbolKind.Event; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/EventDefinition.ts b/server/src/services/documentSymbol/visitors/EventDefinition.ts index d14182e8..32bb90eb 100644 --- a/server/src/services/documentSymbol/visitors/EventDefinition.ts +++ b/server/src/services/documentSymbol/visitors/EventDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class EventDefinition extends DefinitionVisitor { +export class EventDefinition extends SymbolVisitor { public ruleKind = RuleKind.EventDefinition; public symbolKind = SymbolKind.Event; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts index 485e0c24..379b94ce 100644 --- a/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/FallbackFunctionDefinition.ts @@ -1,22 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; -import { - SlangNodeWrapper, - slangToVSCodeRange, -} from "../../../parser/slangHelpers"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class FallbackFunctionDefinition extends DefinitionVisitor { +export class FallbackFunctionDefinition extends SymbolVisitor { public ruleKind = RuleKind.FallbackFunctionDefinition; public symbolKind = SymbolKind.Function; - - protected getSymbolAttributes(nodeWrapper: SlangNodeWrapper) { - return { - name: "fallback", - range: slangToVSCodeRange(this.document, nodeWrapper.textRange), - selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), - kind: this.symbolKind, - }; - } + public nameTokenKind = TokenKind.FallbackKeyword; } diff --git a/server/src/services/documentSymbol/visitors/FunctionDefinition.ts b/server/src/services/documentSymbol/visitors/FunctionDefinition.ts index 7dde5b02..4f336bec 100644 --- a/server/src/services/documentSymbol/visitors/FunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/FunctionDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class FunctionDefinition extends DefinitionVisitor { +export class FunctionDefinition extends SymbolVisitor { public ruleKind = RuleKind.FunctionDefinition; public symbolKind = SymbolKind.Function; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts b/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts index 9ebefd0b..7bf9ae13 100644 --- a/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts +++ b/server/src/services/documentSymbol/visitors/InterfaceDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class InterfaceDefinition extends DefinitionVisitor { +export class InterfaceDefinition extends SymbolVisitor { public ruleKind = RuleKind.InterfaceDefinition; public symbolKind = SymbolKind.Interface; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/LibraryDefinition.ts b/server/src/services/documentSymbol/visitors/LibraryDefinition.ts index 87df2314..2d54d91d 100644 --- a/server/src/services/documentSymbol/visitors/LibraryDefinition.ts +++ b/server/src/services/documentSymbol/visitors/LibraryDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class LibraryDefinition extends DefinitionVisitor { +export class LibraryDefinition extends SymbolVisitor { public ruleKind = RuleKind.LibraryDefinition; public symbolKind = SymbolKind.Class; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/ModifierDefinition.ts b/server/src/services/documentSymbol/visitors/ModifierDefinition.ts index 5354612f..0456ab4b 100644 --- a/server/src/services/documentSymbol/visitors/ModifierDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ModifierDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class ModifierDefinition extends DefinitionVisitor { +export class ModifierDefinition extends SymbolVisitor { public ruleKind = RuleKind.ModifierDefinition; public symbolKind = SymbolKind.Function; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts index 99063e92..8927c6b0 100644 --- a/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts +++ b/server/src/services/documentSymbol/visitors/ReceiveFunctionDefinition.ts @@ -1,22 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; -import { - SlangNodeWrapper, - slangToVSCodeRange, -} from "../../../parser/slangHelpers"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class ReceiveFunctionDefinition extends DefinitionVisitor { +export class ReceiveFunctionDefinition extends SymbolVisitor { public ruleKind = RuleKind.ReceiveFunctionDefinition; public symbolKind = SymbolKind.Function; - - protected getSymbolAttributes(nodeWrapper: SlangNodeWrapper) { - return { - name: "receive", - range: slangToVSCodeRange(this.document, nodeWrapper.textRange), - selectionRange: slangToVSCodeRange(this.document, nodeWrapper.textRange), - kind: this.symbolKind, - }; - } + public nameTokenKind = TokenKind.ReceiveKeyword; } diff --git a/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts b/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts index bf7e8ba7..7f6b17e9 100644 --- a/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts +++ b/server/src/services/documentSymbol/visitors/StateVariableDeclaration.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class StateVariableDeclaration extends DefinitionVisitor { +export class StateVariableDeclaration extends SymbolVisitor { public ruleKind = RuleKind.StateVariableDefinition; public symbolKind = SymbolKind.Property; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/StructDefinition.ts b/server/src/services/documentSymbol/visitors/StructDefinition.ts index 06d752a8..5f9b69a5 100644 --- a/server/src/services/documentSymbol/visitors/StructDefinition.ts +++ b/server/src/services/documentSymbol/visitors/StructDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class StructDefinition extends DefinitionVisitor { +export class StructDefinition extends SymbolVisitor { public ruleKind = RuleKind.StructDefinition; public symbolKind = SymbolKind.Struct; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/StructMember.ts b/server/src/services/documentSymbol/visitors/StructMember.ts index 20547bb2..b3f6e734 100644 --- a/server/src/services/documentSymbol/visitors/StructMember.ts +++ b/server/src/services/documentSymbol/visitors/StructMember.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class StructMember extends DefinitionVisitor { +export class StructMember extends SymbolVisitor { public ruleKind = RuleKind.StructMember; public symbolKind = SymbolKind.Property; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts b/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts index b0b69b9f..7067bd18 100644 --- a/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts +++ b/server/src/services/documentSymbol/visitors/UserDefinedValueTypeDefinition.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class UserDefinedValueTypeDefinition extends DefinitionVisitor { +export class UserDefinedValueTypeDefinition extends SymbolVisitor { public ruleKind = RuleKind.UserDefinedValueTypeDefinition; public symbolKind = SymbolKind.TypeParameter; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/services/documentSymbol/visitors/VariableDeclaration.ts b/server/src/services/documentSymbol/visitors/VariableDeclaration.ts index 9a5f9a5d..ad8af4b4 100644 --- a/server/src/services/documentSymbol/visitors/VariableDeclaration.ts +++ b/server/src/services/documentSymbol/visitors/VariableDeclaration.ts @@ -1,8 +1,9 @@ import { SymbolKind } from "vscode-languageserver-types"; -import { RuleKind } from "@nomicfoundation/slang/kinds"; -import { DefinitionVisitor } from "./DefinitionVisitor"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; -export class VariableDeclaration extends DefinitionVisitor { +export class VariableDeclaration extends SymbolVisitor { public ruleKind = RuleKind.VariableDeclaration; public symbolKind = SymbolKind.Variable; + public nameTokenKind = TokenKind.Identifier; } diff --git a/server/src/telemetry/SentryServerTelemetry.ts b/server/src/telemetry/SentryServerTelemetry.ts index 34917058..20e2a4d6 100644 --- a/server/src/telemetry/SentryServerTelemetry.ts +++ b/server/src/telemetry/SentryServerTelemetry.ts @@ -131,6 +131,8 @@ export class SentryServerTelemetry implements Telemetry { return null; } finally { transaction.finish(); + // const elapsed = transaction.endTimestamp! - transaction.startTimestamp; + // console.log(`${taskName}: ${elapsed * 1000} ms`); } } diff --git a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts index 0910faef..f8c63c05 100644 --- a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts +++ b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts @@ -28,6 +28,8 @@ describe('[hardhat] documentSymbol', () => { expect(symbols).to.deep.equal([ { children: [], + kind: 26, + name: 'CustomType', range: { start: { line: 3, @@ -48,13 +50,13 @@ describe('[hardhat] documentSymbol', () => { character: 15, }, }, - kind: 26, - name: 'CustomType', }, { children: [ { children: [], + kind: 12, + name: 'interfaceFunction', range: { start: { line: 7, @@ -75,10 +77,10 @@ describe('[hardhat] documentSymbol', () => { character: 30, }, }, - kind: 12, - name: 'interfaceFunction', }, ], + kind: 11, + name: 'TestInterface', range: { start: { line: 5, @@ -99,13 +101,13 @@ describe('[hardhat] documentSymbol', () => { character: 23, }, }, - kind: 11, - name: 'TestInterface', }, { children: [ { children: [], + kind: 7, + name: 'aNumber', range: { start: { line: 11, @@ -126,11 +128,11 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, - kind: 7, - name: 'aNumber', }, { children: [], + kind: 7, + name: 'aString', range: { start: { line: 12, @@ -151,11 +153,11 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, - kind: 7, - name: 'aString', }, { children: [], + kind: 7, + name: 'anAddress', range: { start: { line: 13, @@ -176,10 +178,10 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, - kind: 7, - name: 'anAddress', }, ], + kind: 23, + name: 'TestStruct', range: { start: { line: 9, @@ -200,13 +202,13 @@ describe('[hardhat] documentSymbol', () => { character: 17, }, }, - kind: 23, - name: 'TestStruct', }, { children: [ { children: [], + kind: 7, + name: 'aNumber', range: { start: { line: 17, @@ -227,11 +229,11 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, - kind: 7, - name: 'aNumber', }, { children: [], + kind: 7, + name: 'aString', range: { start: { line: 18, @@ -252,11 +254,11 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, - kind: 7, - name: 'aString', }, { children: [], + kind: 7, + name: 'anAddress', range: { start: { line: 19, @@ -277,10 +279,10 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, - kind: 7, - name: 'anAddress', }, ], + kind: 23, + name: 'TestStruct2', range: { start: { line: 15, @@ -301,11 +303,11 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, - kind: 23, - name: 'TestStruct2', }, { children: [], + kind: 14, + name: 'aConstant', range: { start: { line: 21, @@ -326,11 +328,11 @@ describe('[hardhat] documentSymbol', () => { character: 26, }, }, - kind: 14, - name: 'aConstant', }, { children: [], + kind: 10, + name: 'Name', range: { start: { line: 23, @@ -351,11 +353,11 @@ describe('[hardhat] documentSymbol', () => { character: 9, }, }, - kind: 10, - name: 'Name', }, { children: [], + kind: 24, + name: 'CustomError', range: { start: { line: 28, @@ -376,11 +378,11 @@ describe('[hardhat] documentSymbol', () => { character: 17, }, }, - kind: 24, - name: 'CustomError', }, { children: [], + kind: 5, + name: 'TestLibrary', range: { start: { line: 30, @@ -401,8 +403,6 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, - kind: 5, - name: 'TestLibrary', }, { children: [ @@ -410,6 +410,8 @@ describe('[hardhat] documentSymbol', () => { children: [ { children: [], + kind: 13, + name: 'local', range: { start: { line: 35, @@ -430,10 +432,9 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, - kind: 13, - name: 'local', }, ], + kind: 9, name: 'constructor', range: { start: { @@ -448,17 +449,18 @@ describe('[hardhat] documentSymbol', () => { selectionRange: { start: { line: 34, - character: 0, + character: 4, }, end: { - line: 38, - character: 0, + line: 34, + character: 15, }, }, - kind: 9, }, { children: [], + kind: 12, + name: 'testModifier', range: { start: { line: 38, @@ -479,11 +481,11 @@ describe('[hardhat] documentSymbol', () => { character: 25, }, }, - kind: 12, - name: 'testModifier', }, { children: [], + kind: 24, + name: 'TestEvent', range: { start: { line: 42, @@ -504,11 +506,11 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, - kind: 24, - name: 'TestEvent', }, { children: [], + kind: 7, + name: 'contractAsMember', range: { start: { line: 46, @@ -529,11 +531,11 @@ describe('[hardhat] documentSymbol', () => { character: 33, }, }, - kind: 7, - name: 'contractAsMember', }, { children: [], + kind: 7, + name: 'interfaceAsMember', range: { start: { line: 48, @@ -554,11 +556,11 @@ describe('[hardhat] documentSymbol', () => { character: 35, }, }, - kind: 7, - name: 'interfaceAsMember', }, { children: [], + kind: 7, + name: 'structAsMember', range: { start: { line: 50, @@ -579,11 +581,11 @@ describe('[hardhat] documentSymbol', () => { character: 29, }, }, - kind: 7, - name: 'structAsMember', }, { children: [], + kind: 7, + name: 'aNumber', range: { start: { line: 52, @@ -604,11 +606,11 @@ describe('[hardhat] documentSymbol', () => { character: 19, }, }, - kind: 7, - name: 'aNumber', }, { children: [], + kind: 7, + name: 'aString', range: { start: { line: 54, @@ -629,13 +631,13 @@ describe('[hardhat] documentSymbol', () => { character: 18, }, }, - kind: 7, - name: 'aString', }, { children: [ { children: [], + kind: 13, + name: 'contractAsLocalVar', range: { start: { line: 62, @@ -656,11 +658,11 @@ describe('[hardhat] documentSymbol', () => { character: 39, }, }, - kind: 13, - name: 'contractAsLocalVar', }, { children: [], + kind: 13, + name: 'interfaceAsLocalVar', range: { start: { line: 63, @@ -681,11 +683,11 @@ describe('[hardhat] documentSymbol', () => { character: 41, }, }, - kind: 13, - name: 'interfaceAsLocalVar', }, { children: [], + kind: 13, + name: 'structAsLocalVar', range: { start: { line: 65, @@ -706,11 +708,11 @@ describe('[hardhat] documentSymbol', () => { character: 42, }, }, - kind: 13, - name: 'structAsLocalVar', }, { children: [], + kind: 13, + name: 'afterUTF8', range: { start: { line: 74, @@ -731,10 +733,10 @@ describe('[hardhat] documentSymbol', () => { character: 35, }, }, - kind: 13, - name: 'afterUTF8', }, ], + kind: 12, + name: 'testFunction', range: { start: { line: 56, @@ -755,11 +757,11 @@ describe('[hardhat] documentSymbol', () => { character: 25, }, }, - kind: 12, - name: 'testFunction', }, { children: [], + kind: 12, + name: 'anotherFunction', range: { start: { line: 81, @@ -780,11 +782,10 @@ describe('[hardhat] documentSymbol', () => { character: 28, }, }, - kind: 12, - name: 'anotherFunction', }, { children: [], + kind: 12, name: 'fallback', range: { start: { @@ -798,18 +799,18 @@ describe('[hardhat] documentSymbol', () => { }, selectionRange: { start: { - line: 83, - character: 0, + line: 84, + character: 4, }, end: { - line: 85, - character: 0, + line: 84, + character: 12, }, }, - kind: 12, }, { children: [], + kind: 12, name: 'receive', range: { start: { @@ -823,17 +824,18 @@ describe('[hardhat] documentSymbol', () => { }, selectionRange: { start: { - line: 85, - character: 0, + line: 86, + character: 4, }, end: { - line: 87, - character: 0, + line: 86, + character: 11, }, }, - kind: 12, }, ], + kind: 5, + name: 'testContract', range: { start: { line: 32, @@ -854,8 +856,6 @@ describe('[hardhat] documentSymbol', () => { character: 21, }, }, - kind: 5, - name: 'testContract', }, ]) }) From d995a5db074e939829e6121378cbdfe37108f5f8 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Sat, 11 Nov 2023 11:33:22 -0300 Subject: [PATCH 12/18] rework semantic highlighting to use cursor finders --- server/src/parser/slangHelpers.ts | 28 ---------- .../semanticHighlight/HighlightVisitor.ts | 4 +- .../ContractDefinitionHighlighter.ts | 5 +- .../highlighters/CustomTypeHighlighter.ts | 5 +- .../EventDefinitionHighlighter.ts | 5 +- .../highlighters/EventEmissionHighlighter.ts | 5 +- .../highlighters/FunctionCallHighlighter.ts | 5 +- .../FunctionDefinitionHighlighter.ts | 5 +- .../InterfaceDefinitionHighlighter.ts | 5 +- .../highlighters/KeywordHighlighter.ts | 4 +- .../highlighters/NumberHighlighter.ts | 5 +- .../highlighters/StringHighlighter.ts | 5 +- .../StructDefinitionHighlighter.ts | 5 +- .../semanticHighlight/onSemanticTokensFull.ts | 54 +++++++++++++------ 14 files changed, 85 insertions(+), 55 deletions(-) diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index 3963236b..905bcc7b 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -1,5 +1,4 @@ import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { TextRange } from "@nomicfoundation/slang/text_index"; import _ from "lodash"; @@ -19,33 +18,6 @@ export interface SlangNodeWrapper { pathRuleNodes: SlangNode[]; } -export function walk( - cursor: Cursor, - onEnter: NodeCallback, - onExit: NodeCallback -) { - const node = cursor.node; - - const nodeWrapper: SlangNodeWrapper = { - textRange: cursor.textRange, - type: node.type, - kind: node.kind, - text: node.text, - pathRuleNodes: cursor.pathRuleNodes, - }; - - onEnter(nodeWrapper); - - if (nodeWrapper.type === NodeType.Rule) { - for (let i = 0; i < node.children.length; i++) { - cursor.goToNthChild(i); - walk(cursor, onEnter, onExit); - } - } - onExit(nodeWrapper); - cursor.goToParent(); -} - export function slangToVSCodeRange( doc: TextDocument, slangRange: TextRange diff --git a/server/src/services/semanticHighlight/HighlightVisitor.ts b/server/src/services/semanticHighlight/HighlightVisitor.ts index 09794d70..14a6e91a 100644 --- a/server/src/services/semanticHighlight/HighlightVisitor.ts +++ b/server/src/services/semanticHighlight/HighlightVisitor.ts @@ -1,16 +1,18 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { TextDocument } from "vscode-languageserver-textdocument"; +import { TokenKind } from "@nomicfoundation/slang/kinds"; import { SlangNodeWrapper } from "../../parser/slangHelpers"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; // Abstraction for a visitor that wants to highlight tokens export abstract class HighlightVisitor { + public abstract tokenKinds: Set; + constructor( public document: TextDocument, public tokenBuilder: SemanticTokensBuilder ) {} public enter(nodeWrapper: SlangNodeWrapper): void {} - public exit(nodeWrapper: SlangNodeWrapper): void {} } diff --git a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts index 3c7a52ac..60e1d7ad 100644 --- a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights contract definitions export class ContractDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts index 79f951f6..9c001779 100644 --- a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights custom type names export class CustomTypeHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts index 9501a0ab..ac26961e 100644 --- a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights event definitions export class EventDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts index e292386f..e3d37c31 100644 --- a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights event emissions export class EventEmissionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts index 646cef83..b2d74ed7 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights function calls export class FunctionCallHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts index 7e29b382..3c92b430 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights function definitions export class FunctionDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts index 9b5f6e68..5b425951 100644 --- a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights interface definitions export class InterfaceDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts index 56e4e8b2..f63ebf83 100644 --- a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts @@ -1,6 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; @@ -91,6 +91,8 @@ const keywordKinds = new Set([ // Highlights keywords export class KeywordHighlighter extends HighlightVisitor { + public tokenKinds = keywordKinds; + public enter(nodeWrapper: SlangNodeWrapper): void { if ( nodeWrapper.type === NodeType.Token && diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts index 647b31b6..0be44882 100644 --- a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts @@ -1,6 +1,7 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; @@ -12,6 +13,8 @@ const numberKinds = new Set([ // Highlights numbers export class NumberHighlighter extends HighlightVisitor { + public tokenKinds = numberKinds; + public enter(nodeWrapper: SlangNodeWrapper): void { if ( nodeWrapper.type === NodeType.Token && diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts index 89addfba..4bf3ec2a 100644 --- a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts @@ -1,6 +1,7 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; @@ -12,6 +13,8 @@ const stringKinds = new Set([ // Highlights strings export class StringHighlighter extends HighlightVisitor { + public tokenKinds = stringKinds; + public enter(nodeWrapper: SlangNodeWrapper): void { if ( nodeWrapper.type === NodeType.Token && diff --git a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts index 35cd4256..dcb205da 100644 --- a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts @@ -1,11 +1,14 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType } from "@nomicfoundation/slang/cst"; +import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; // Highlights struct definitions export class StructDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + public enter(nodeWrapper: SlangNodeWrapper): void { const ancestors = nodeWrapper.pathRuleNodes; if ( diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 51c00c84..46b1ec9b 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -5,13 +5,14 @@ import { SemanticTokens, SemanticTokensParams, } from "vscode-languageserver-protocol"; -import _ from "lodash"; +import _, { Dictionary } from "lodash"; import { analyze } from "@nomicfoundation/solidity-analyzer"; import semver from "semver"; import { Language } from "@nomicfoundation/slang/language"; -import { ProductionKind } from "@nomicfoundation/slang/kinds"; +import { ProductionKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { Cursor } from "@nomicfoundation/slang/cursor"; +import { TokenNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; -import { walk } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; import { KeywordHighlighter } from "./highlighters/KeywordHighlighter"; @@ -24,6 +25,7 @@ import { EventDefinitionHighlighter } from "./highlighters/EventDefinitionHighli import { ContractDefinitionHighlighter } from "./highlighters/ContractDefinitionHighlighter"; import { InterfaceDefinitionHighlighter } from "./highlighters/InterfaceDefinitionHighlighter"; import { StructDefinitionHighlighter } from "./highlighters/StructDefinitionHighlighter"; +import { HighlightVisitor } from "./HighlightVisitor"; const emptyResponse: SemanticTokens = { data: [] }; @@ -103,20 +105,42 @@ export function onSemanticTokensFull(serverState: ServerState) { ]; // Visit the CST - span = transaction.startChild({ op: "walk-highlight-tokens" }); - walk( - parseTree.cursor, - (nodeWrapper) => { - for (const visitor of visitors) { - visitor.enter(nodeWrapper); - } - }, - (nodeWrapper) => { - for (const visitor of visitors) { - visitor.exit(nodeWrapper); + const indexedVisitors: Dictionary = {}; + const registeredTokenKinds: TokenKind[] = []; + + for (const visitor of visitors) { + for (const tokenKind of visitor.tokenKinds) { + indexedVisitors[tokenKind] ||= []; + indexedVisitors[tokenKind].push(visitor); + + if (!registeredTokenKinds.includes(tokenKind)) { + registeredTokenKinds.push(tokenKind); } } - ); + } + + const cursor: Cursor = parseTree.cursor; + let node: TokenNode; + + span = transaction.startChild({ op: "walk-highlight-tokens" }); + while ( + (node = cursor.findTokenWithKind(registeredTokenKinds)) !== null + ) { + const nodeWrapper = { + kind: node.kind, + pathRuleNodes: cursor.pathRuleNodes, + text: node.text, + textRange: cursor.textRange, + type: node.type, + }; + + const registeredVisitors = indexedVisitors[nodeWrapper.kind]; + for (const visitor of registeredVisitors) { + visitor.enter(nodeWrapper); + } + + cursor.goToNext(); + } span.finish(); return { status: "ok", result: { data: builder.getTokenData() } }; From c5dcf84c775bca929757e71b7d8cc1524721332d Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Sat, 11 Nov 2023 12:37:56 -0300 Subject: [PATCH 13/18] fix linter --- client/scripts/bundle.js | 20 +++++----- .../services/documentSymbol/SymbolVisitor.ts | 1 - .../documentSymbol/onDocumentSymbol.ts | 9 ----- .../services/initialization/featureFlags.ts | 5 +++ .../services/initialization/onInitialize.ts | 37 ++++++++++++------- .../ContractDefinitionHighlighter.ts | 3 +- .../highlighters/CustomTypeHighlighter.ts | 3 +- .../EventDefinitionHighlighter.ts | 3 +- .../highlighters/EventEmissionHighlighter.ts | 3 +- .../highlighters/FunctionCallHighlighter.ts | 3 +- .../FunctionDefinitionHighlighter.ts | 3 +- .../InterfaceDefinitionHighlighter.ts | 3 +- .../highlighters/KeywordHighlighter.ts | 3 +- .../highlighters/NumberHighlighter.ts | 3 +- .../highlighters/StringHighlighter.ts | 3 +- .../StructDefinitionHighlighter.ts | 3 +- .../semanticHighlight/onSemanticTokensFull.ts | 2 +- server/src/telemetry/SentryServerTelemetry.ts | 2 - 18 files changed, 50 insertions(+), 59 deletions(-) diff --git a/client/scripts/bundle.js b/client/scripts/bundle.js index 2df7e7fc..d7cc443c 100755 --- a/client/scripts/bundle.js +++ b/client/scripts/bundle.js @@ -200,16 +200,16 @@ async function main() { "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.1", "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.1", - "@nomicfoundation/slang": "0.4.0", - "@nomicfoundation/slang-darwin-arm64": "0.4.0", - "@nomicfoundation/slang-win32-arm64-msvc": "0.4.0", - "@nomicfoundation/slang-linux-arm64-gnu": "0.4.0", - "@nomicfoundation/slang-linux-arm64-musl": "0.4.0", - "@nomicfoundation/slang-win32-ia32-msvc": "0.4.0", - "@nomicfoundation/slang-darwin-x64": "0.4.0", - "@nomicfoundation/slang-win32-x64-msvc": "0.4.0", - "@nomicfoundation/slang-linux-x64-gnu": "0.4.0", - "@nomicfoundation/slang-linux-x64-musl": "0.4.0", + "@nomicfoundation/slang": "0.10.1", + "@nomicfoundation/slang-darwin-arm64": "0.10.1", + "@nomicfoundation/slang-win32-arm64-msvc": "0.10.1", + "@nomicfoundation/slang-linux-arm64-gnu": "0.10.1", + "@nomicfoundation/slang-linux-arm64-musl": "0.10.1", + "@nomicfoundation/slang-win32-ia32-msvc": "0.10.1", + "@nomicfoundation/slang-darwin-x64": "0.10.1", + "@nomicfoundation/slang-win32-x64-msvc": "0.10.1", + "@nomicfoundation/slang-linux-x64-gnu": "0.10.1", + "@nomicfoundation/slang-linux-x64-musl": "0.10.1", }, }) ); diff --git a/server/src/services/documentSymbol/SymbolVisitor.ts b/server/src/services/documentSymbol/SymbolVisitor.ts index 8461b354..8965cc22 100644 --- a/server/src/services/documentSymbol/SymbolVisitor.ts +++ b/server/src/services/documentSymbol/SymbolVisitor.ts @@ -12,7 +12,6 @@ export abstract class SymbolVisitor { public abstract ruleKind: RuleKind; public abstract symbolKind: SymbolKind; public abstract nameTokenKind: TokenKind; - public nameTokenSkips = 0; constructor( public document: TextDocument, diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 4621e143..7f2fde40 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -79,15 +79,6 @@ export function onDocumentSymbol(serverState: ServerState) { throw new Error(`Slang parsing error: ${strings.join("")}`); } - // const kursor: Cursor = parseTree.cursor.clone(); - // do { - // console.log( - // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}(${ - // ["R", "T"][kursor.node.type] - // }): ${kursor.node?.text ?? ""}` - // ); - // } while (kursor.goToNext()); - const builder = new SymbolTreeBuilder(); const visitors: SymbolVisitor[] = [ diff --git a/server/src/services/initialization/featureFlags.ts b/server/src/services/initialization/featureFlags.ts index e264074f..3d7a7b1c 100644 --- a/server/src/services/initialization/featureFlags.ts +++ b/server/src/services/initialization/featureFlags.ts @@ -1,6 +1,7 @@ import got from "got"; import crypto from "crypto"; import { ServerState } from "../../types"; +import { isTestMode } from "../../utils"; export interface FeatureFlag { percent: number; @@ -57,6 +58,10 @@ export function isFeatureEnabled( throw new Error(`Feature flag not found: ${feature}`); } + if (isTestMode()) { + return true; + } + // hash the machineId to get an evenly distributed value const machineIdHash = crypto.createHash("md5"); machineIdHash.update(machineId); diff --git a/server/src/services/initialization/onInitialize.ts b/server/src/services/initialization/onInitialize.ts index 342ed876..62413bd2 100644 --- a/server/src/services/initialization/onInitialize.ts +++ b/server/src/services/initialization/onInitialize.ts @@ -52,6 +52,21 @@ export const onInitialize = (serverState: ServerState) => { logger.info("Language server ready"); + const slangSupported = isSlangSupported(); + + const semanticTokensEnabled = isFeatureEnabled( + serverState, + flags, + "semanticHighlighting", + machineId + ); + + const documentSymbolsEnabled = isFeatureEnabled( + serverState, + flags, + "documentSymbol", + machineId + ); // Index and analysis await serverState.telemetry.trackTiming( "indexing", @@ -65,11 +80,14 @@ export const onInitialize = (serverState: ServerState) => { return { status: "ok", result: null }; }, - { platform: getPlatform() } + { + platform: getPlatform(), + slangSupported, + semanticTokensEnabled, + documentSymbolsEnabled, + } ); - const slangSupported = isSlangSupported(); - // Build and return InitializeResult const result: InitializeResult = { serverInfo: { @@ -98,18 +116,9 @@ export const onInitialize = (serverState: ServerState) => { tokenModifiers: [], }, range: false, - full: - slangSupported && - isFeatureEnabled( - serverState, - flags, - "semanticHighlighting", - machineId - ), + full: slangSupported && semanticTokensEnabled, }, - documentSymbolProvider: - slangSupported && - isFeatureEnabled(serverState, flags, "documentSymbol", machineId), + documentSymbolProvider: slangSupported && documentSymbolsEnabled, workspace: { workspaceFolders: { supported: false, diff --git a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts index 60e1d7ad..a0449c91 100644 --- a/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts index 9c001779..7336ec19 100644 --- a/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts index ac26961e..beeda201 100644 --- a/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts index e3d37c31..96bc1611 100644 --- a/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts index b2d74ed7..52b8b7f9 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts index 3c92b430..bce240a5 100644 --- a/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/FunctionDefinitionHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts index 5b425951..3c2e12d8 100644 --- a/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/InterfaceDefinitionHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts index f63ebf83..e44e40c4 100644 --- a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts index 0be44882..09852474 100644 --- a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts index 4bf3ec2a..9937e472 100644 --- a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; -import { Cursor } from "@nomicfoundation/slang/cursor"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts index dcb205da..1a31047a 100644 --- a/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/StructDefinitionHighlighter.ts @@ -1,7 +1,6 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { NodeType, TokenNode } from "@nomicfoundation/slang/cst"; +import { NodeType } from "@nomicfoundation/slang/cst"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { Cursor } from "@nomicfoundation/slang/cursor"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 46b1ec9b..943bf6e8 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -145,7 +145,7 @@ export function onSemanticTokensFull(serverState: ServerState) { return { status: "ok", result: { data: builder.getTokenData() } }; } catch (error) { - logger.error(error); + logger.error(`Slang parsing error: ${error}`); return { status: "internal_error", result: emptyResponse }; } }) || emptyResponse diff --git a/server/src/telemetry/SentryServerTelemetry.ts b/server/src/telemetry/SentryServerTelemetry.ts index 20e2a4d6..34917058 100644 --- a/server/src/telemetry/SentryServerTelemetry.ts +++ b/server/src/telemetry/SentryServerTelemetry.ts @@ -131,8 +131,6 @@ export class SentryServerTelemetry implements Telemetry { return null; } finally { transaction.finish(); - // const elapsed = transaction.endTimestamp! - transaction.startTimestamp; - // console.log(`${taskName}: ${elapsed * 1000} ms`); } } From a3573528f6bf4bae77eb9f6d6d55e621d5ba38ea Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Fri, 8 Dec 2023 13:36:41 -0300 Subject: [PATCH 14/18] Changes based on PR feedback (part 1) --- flags.json | 9 ++ server/src/parser/slangHelpers.ts | 1 - .../documentSymbol/SymbolTreeBuilder.ts | 3 +- .../services/documentSymbol/SymbolVisitor.ts | 13 +-- .../documentSymbol/onDocumentSymbol.ts | 37 +++++-- .../visitors/YulFunctionDefinition.ts | 9 ++ .../services/initialization/featureFlags.ts | 2 +- .../services/initialization/onInitialize.ts | 2 +- ...ighter.ts => EnumDefinitionHighlighter.ts} | 19 ++-- ...ghter.ts => ErrorDefinitionHighlighter.ts} | 19 ++-- .../highlighters/KeywordHighlighter.ts | 103 ------------------ .../LibraryDefinitionHighlighter.ts | 20 ++++ ...inedValueTypeDefinitionHighlighter copy.ts | 21 ++++ .../semanticHighlight/onSemanticTokensFull.ts | 41 +++++-- .../documentSymbol/DocumentSymbols.sol | 6 + .../semanticTokens/full/SemanticTokens.sol | 12 ++ .../documentSymbol/documentSymbol.test.ts | 53 ++++++--- .../textDocument/semanticTokens/full.test.ts | 11 +- 18 files changed, 202 insertions(+), 179 deletions(-) create mode 100644 flags.json create mode 100644 server/src/services/documentSymbol/visitors/YulFunctionDefinition.ts rename server/src/services/semanticHighlight/highlighters/{NumberHighlighter.ts => EnumDefinitionHighlighter.ts} (54%) rename server/src/services/semanticHighlight/highlighters/{StringHighlighter.ts => ErrorDefinitionHighlighter.ts} (54%) delete mode 100644 server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/LibraryDefinitionHighlighter.ts create mode 100644 server/src/services/semanticHighlight/highlighters/UserDefinedValueTypeDefinitionHighlighter copy.ts diff --git a/flags.json b/flags.json new file mode 100644 index 00000000..b5627fc5 --- /dev/null +++ b/flags.json @@ -0,0 +1,9 @@ +{ + "documentSymbol": { + "percent": 0.1 + }, + + "semanticHighlighting": { + "percent": 0.1 + } +} diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index 905bcc7b..29c77d27 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -8,7 +8,6 @@ import { getPlatform } from "../utils/operatingSystem"; export type SlangNode = RuleNode | TokenNode; export type NodeKind = RuleKind | TokenKind; -export type NodeCallback = (node: SlangNodeWrapper) => void; export interface SlangNodeWrapper { textRange: TextRange; diff --git a/server/src/services/documentSymbol/SymbolTreeBuilder.ts b/server/src/services/documentSymbol/SymbolTreeBuilder.ts index bf4aee01..b777a87d 100644 --- a/server/src/services/documentSymbol/SymbolTreeBuilder.ts +++ b/server/src/services/documentSymbol/SymbolTreeBuilder.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { DocumentSymbol } from "vscode-languageserver-types"; import _ from "lodash"; @@ -30,7 +31,7 @@ export class SymbolTreeBuilder { throw new Error("Attempting to close a symbol but none is open"); } - lastSymbol.children?.push(symbol); + lastSymbol.children!.push(symbol); } } diff --git a/server/src/services/documentSymbol/SymbolVisitor.ts b/server/src/services/documentSymbol/SymbolVisitor.ts index 8965cc22..2b8d1dff 100644 --- a/server/src/services/documentSymbol/SymbolVisitor.ts +++ b/server/src/services/documentSymbol/SymbolVisitor.ts @@ -2,7 +2,7 @@ import { SymbolKind } from "vscode-languageserver-types"; import _ from "lodash"; import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; -import { RuleNode, TokenNode } from "@nomicfoundation/slang/cst"; +import { TokenNode } from "@nomicfoundation/slang/cst"; import { TextDocument } from "vscode-languageserver-textdocument"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { slangToVSCodeRange } from "../../parser/slangHelpers"; @@ -18,7 +18,7 @@ export abstract class SymbolVisitor { public symbolBuilder: SymbolTreeBuilder ) {} - public onRuleNode(node: RuleNode, cursor: Cursor): void { + public onRuleNode(cursor: Cursor): void { const range = slangToVSCodeRange(this.document, cursor.textRange); let symbolName = "-"; @@ -28,15 +28,12 @@ export abstract class SymbolVisitor { const childCursor = cursor.spawn(); do { - const identifierNode: TokenNode | null = childCursor.findTokenWithKind([ + const nameToken: TokenNode | null = childCursor.findTokenWithKind([ this.nameTokenKind, ]); - if ( - identifierNode && - _.last(childCursor.pathRuleNodes).kind === this.ruleKind - ) { - symbolName = identifierNode.text; + if (nameToken && childCursor.pathRuleNodes.length === 1) { + symbolName = nameToken.text; selectionRange = slangToVSCodeRange( this.document, childCursor.textRange diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 7f2fde40..cf8e49ac 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -30,12 +30,13 @@ import { ModifierDefinition } from "./visitors/ModifierDefinition"; import { ReceiveFunctionDefinition } from "./visitors/ReceiveFunctionDefinition"; import { UserDefinedValueTypeDefinition } from "./visitors/UserDefinedValueTypeDefinition"; import { SymbolVisitor } from "./SymbolVisitor"; +import { YulFunctionDefinition } from "./visitors/YulFunctionDefinition"; export function onDocumentSymbol(serverState: ServerState) { return async ( params: DocumentSymbolParams ): Promise => { - const { telemetry, solcVersions } = serverState; + const { telemetry } = serverState; return telemetry.trackTimingSync("onDocumentSymbol", (transaction) => { const { uri } = params.textDocument; @@ -53,11 +54,22 @@ export function onDocumentSymbol(serverState: ServerState) { const { versionPragmas } = analyze(text); span.finish(); - versionPragmas.push("<= 0.8.19"); // latest supported by slang + const versions = Language.supportedVersions(); + versionPragmas.push( + `>= ${versions[0]}`, + `<= ${versions[versions.length - 1]}` + ); + + const solcVersion = semver.maxSatisfying( + Language.supportedVersions(), + versionPragmas.join(" ") + ); - const solcVersion = - semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || - _.last(solcVersions); + if (solcVersion === null) { + throw new Error( + `No supported solidity version found. Supported versions: ${Language.supportedVersions()}, pragma directives: ${versionPragmas}` + ); + } // Parse using slang span = transaction.startChild({ op: "slang-parsing" }); @@ -76,7 +88,7 @@ export function onDocumentSymbol(serverState: ServerState) { e.toErrorReport(uri, text, false) ); - throw new Error(`Slang parsing error: ${strings.join("")}`); + throw new Error(`Slang parsing error:\n${strings.join("\n")}`); } const builder = new SymbolTreeBuilder(); @@ -99,6 +111,7 @@ export function onDocumentSymbol(serverState: ServerState) { new ModifierDefinition(document, builder), new ReceiveFunctionDefinition(document, builder), new UserDefinedValueTypeDefinition(document, builder), + new YulFunctionDefinition(document, builder), ]; const indexedVisitors = _.keyBy(visitors, "ruleKind"); @@ -107,10 +120,20 @@ export function onDocumentSymbol(serverState: ServerState) { const ruleKinds = visitors.map((v) => v.ruleKind); let node: RuleNode; + // Useful to keep this here for development + // const kursor: Cursor = parseTree.cursor.clone(); + // do { + // console.log( + // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}(${ + // ["R", "T"][kursor.node.type] + // }): ${kursor.node?.text ?? ""}` + // ); + // } while (kursor.goToNext()); + span = transaction.startChild({ op: "walk-generate-symbols" }); while ((node = cursor.findRuleWithKind(ruleKinds)) !== null) { const visitor: SymbolVisitor = indexedVisitors[node.kind]; - visitor.onRuleNode(node, cursor); + visitor.onRuleNode(cursor); cursor.goToNext(); } diff --git a/server/src/services/documentSymbol/visitors/YulFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/YulFunctionDefinition.ts new file mode 100644 index 00000000..e1565c83 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/YulFunctionDefinition.ts @@ -0,0 +1,9 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; + +export class YulFunctionDefinition extends SymbolVisitor { + public ruleKind = RuleKind.YulFunctionDefinition; + public symbolKind = SymbolKind.Function; + public nameTokenKind = TokenKind.YulIdentifier; +} diff --git a/server/src/services/initialization/featureFlags.ts b/server/src/services/initialization/featureFlags.ts index 3d7a7b1c..0058f801 100644 --- a/server/src/services/initialization/featureFlags.ts +++ b/server/src/services/initialization/featureFlags.ts @@ -29,7 +29,7 @@ export async function fetchFeatureFlags( try { return await got .get( - "https://raw.githubusercontent.com/antico5/hardhat-vscode/flags/flags.json", + "https://raw.githubusercontent.com/NomicFoundation/hardhat-vscode/development/flags.json", { timeout: 2000, } diff --git a/server/src/services/initialization/onInitialize.ts b/server/src/services/initialization/onInitialize.ts index 62413bd2..cba45bcc 100644 --- a/server/src/services/initialization/onInitialize.ts +++ b/server/src/services/initialization/onInitialize.ts @@ -45,7 +45,7 @@ export const onInitialize = (serverState: ServerState) => { }); // fetch available solidity versions and feature flags - const [flags] = await Promise.all([ + const [flags, _] = await Promise.all([ fetchFeatureFlags(serverState), updateAvailableSolcVersions(serverState), ]); diff --git a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts b/server/src/services/semanticHighlight/highlighters/EnumDefinitionHighlighter.ts similarity index 54% rename from server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts rename to server/src/services/semanticHighlight/highlighters/EnumDefinitionHighlighter.ts index 09852474..ff239114 100644 --- a/server/src/services/semanticHighlight/highlighters/NumberHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/EnumDefinitionHighlighter.ts @@ -1,25 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { TokenKind } from "@nomicfoundation/slang/kinds"; import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; -const numberKinds = new Set([ - TokenKind.HexLiteral, - TokenKind.YulHexLiteral, - TokenKind.DecimalLiteral, -]); - -// Highlights numbers -export class NumberHighlighter extends HighlightVisitor { - public tokenKinds = numberKinds; +export class EnumDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( nodeWrapper.type === NodeType.Token && - numberKinds.has(nodeWrapper.kind as TokenKind) + nodeWrapper.kind === TokenKind.Identifier && + ancestors[ancestors.length - 1]?.kind === RuleKind.EnumDefinition ) { - this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.number); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts b/server/src/services/semanticHighlight/highlighters/ErrorDefinitionHighlighter.ts similarity index 54% rename from server/src/services/semanticHighlight/highlighters/StringHighlighter.ts rename to server/src/services/semanticHighlight/highlighters/ErrorDefinitionHighlighter.ts index 9937e472..76b46d8c 100644 --- a/server/src/services/semanticHighlight/highlighters/StringHighlighter.ts +++ b/server/src/services/semanticHighlight/highlighters/ErrorDefinitionHighlighter.ts @@ -1,25 +1,20 @@ import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { TokenKind } from "@nomicfoundation/slang/kinds"; import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { HighlightVisitor } from "../HighlightVisitor"; import { SlangNodeWrapper } from "../../../parser/slangHelpers"; -const stringKinds = new Set([ - TokenKind.HexStringLiteral, - TokenKind.AsciiStringLiteral, - TokenKind.UnicodeStringLiteral, -]); - -// Highlights strings -export class StringHighlighter extends HighlightVisitor { - public tokenKinds = stringKinds; +export class ErrorDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; if ( nodeWrapper.type === NodeType.Token && - stringKinds.has(nodeWrapper.kind as TokenKind) + nodeWrapper.kind === TokenKind.Identifier && + ancestors[ancestors.length - 1]?.kind === RuleKind.ErrorDefinition ) { - this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.string); + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); } } } diff --git a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts b/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts deleted file mode 100644 index e44e40c4..00000000 --- a/server/src/services/semanticHighlight/highlighters/KeywordHighlighter.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { SemanticTokenTypes } from "vscode-languageserver-protocol"; -import { TokenKind } from "@nomicfoundation/slang/kinds"; -import { NodeType } from "@nomicfoundation/slang/cst"; -import { HighlightVisitor } from "../HighlightVisitor"; -import { SlangNodeWrapper } from "../../../parser/slangHelpers"; - -const keywordKinds = new Set([ - TokenKind.ABICoderKeyword, - TokenKind.AbstractKeyword, - TokenKind.AddressKeyword, - TokenKind.AnonymousKeyword, - TokenKind.AsKeyword, - TokenKind.AssemblyKeyword, - TokenKind.BoolKeyword, - TokenKind.BreakKeyword, - TokenKind.CalldataKeyword, - TokenKind.CaseKeyword, - TokenKind.CatchKeyword, - TokenKind.ConstantKeyword, - TokenKind.ConstructorKeyword, - TokenKind.ContinueKeyword, - TokenKind.ContractKeyword, - TokenKind.DaysKeyword, - TokenKind.DefaultKeyword, - TokenKind.DeleteKeyword, - TokenKind.DoKeyword, - TokenKind.ElseKeyword, - TokenKind.EmitKeyword, - TokenKind.EnumKeyword, - TokenKind.ErrorKeyword, - TokenKind.EtherKeyword, - TokenKind.EventKeyword, - TokenKind.ExperimentalKeyword, - TokenKind.ExternalKeyword, - TokenKind.FallbackKeyword, - TokenKind.FalseKeyword, - TokenKind.FinneyKeyword, - TokenKind.ForKeyword, - TokenKind.FromKeyword, - TokenKind.FunctionKeyword, - TokenKind.GlobalKeyword, - TokenKind.GweiKeyword, - TokenKind.HoursKeyword, - TokenKind.IfKeyword, - TokenKind.ImmutableKeyword, - TokenKind.ImportKeyword, - TokenKind.IndexedKeyword, - TokenKind.InterfaceKeyword, - TokenKind.InternalKeyword, - TokenKind.IsKeyword, - TokenKind.LeaveKeyword, - TokenKind.LetKeyword, - TokenKind.LibraryKeyword, - TokenKind.MappingKeyword, - TokenKind.MemoryKeyword, - TokenKind.MinutesKeyword, - TokenKind.ModifierKeyword, - TokenKind.NewKeyword, - TokenKind.OverrideKeyword, - TokenKind.PayableKeyword, - TokenKind.PragmaKeyword, - TokenKind.PrivateKeyword, - TokenKind.PublicKeyword, - TokenKind.PureKeyword, - TokenKind.ReceiveKeyword, - TokenKind.ReturnKeyword, - TokenKind.ReturnsKeyword, - TokenKind.RevertKeyword, - TokenKind.SecondsKeyword, - TokenKind.SolidityKeyword, - TokenKind.StorageKeyword, - TokenKind.StringKeyword, - TokenKind.StructKeyword, - TokenKind.SwitchKeyword, - TokenKind.SzaboKeyword, - TokenKind.ThrowKeyword, - TokenKind.TrueKeyword, - TokenKind.TryKeyword, - TokenKind.TypeKeyword, - TokenKind.UncheckedKeyword, - TokenKind.UnsignedIntegerType, - TokenKind.UsingKeyword, - TokenKind.ViewKeyword, - TokenKind.VirtualKeyword, - TokenKind.WeeksKeyword, - TokenKind.WeiKeyword, - TokenKind.WhileKeyword, - TokenKind.YearsKeyword, -]); - -// Highlights keywords -export class KeywordHighlighter extends HighlightVisitor { - public tokenKinds = keywordKinds; - - public enter(nodeWrapper: SlangNodeWrapper): void { - if ( - nodeWrapper.type === NodeType.Token && - keywordKinds.has(nodeWrapper.kind as TokenKind) - ) { - this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.keyword); - } - } -} diff --git a/server/src/services/semanticHighlight/highlighters/LibraryDefinitionHighlighter.ts b/server/src/services/semanticHighlight/highlighters/LibraryDefinitionHighlighter.ts new file mode 100644 index 00000000..5cc2ec6c --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/LibraryDefinitionHighlighter.ts @@ -0,0 +1,20 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; + +export class LibraryDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; + if ( + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && + ancestors[ancestors.length - 1]?.kind === RuleKind.LibraryDefinition + ) { + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/highlighters/UserDefinedValueTypeDefinitionHighlighter copy.ts b/server/src/services/semanticHighlight/highlighters/UserDefinedValueTypeDefinitionHighlighter copy.ts new file mode 100644 index 00000000..96a6f7bf --- /dev/null +++ b/server/src/services/semanticHighlight/highlighters/UserDefinedValueTypeDefinitionHighlighter copy.ts @@ -0,0 +1,21 @@ +import { SemanticTokenTypes } from "vscode-languageserver-protocol"; +import { NodeType } from "@nomicfoundation/slang/cst"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { HighlightVisitor } from "../HighlightVisitor"; +import { SlangNodeWrapper } from "../../../parser/slangHelpers"; + +export class UserDefinedValueTypeDefinitionHighlighter extends HighlightVisitor { + public tokenKinds = new Set([TokenKind.Identifier]); + + public enter(nodeWrapper: SlangNodeWrapper): void { + const ancestors = nodeWrapper.pathRuleNodes; + if ( + nodeWrapper.type === NodeType.Token && + nodeWrapper.kind === TokenKind.Identifier && + ancestors[ancestors.length - 1]?.kind === + RuleKind.UserDefinedValueTypeDefinition + ) { + this.tokenBuilder.addToken(nodeWrapper, SemanticTokenTypes.type); + } + } +} diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 943bf6e8..700ca7c4 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -15,9 +15,6 @@ import { TokenNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; -import { KeywordHighlighter } from "./highlighters/KeywordHighlighter"; -import { NumberHighlighter } from "./highlighters/NumberHighlighter"; -import { StringHighlighter } from "./highlighters/StringHighlighter"; import { FunctionDefinitionHighlighter } from "./highlighters/FunctionDefinitionHighlighter"; import { FunctionCallHighlighter } from "./highlighters/FunctionCallHighlighter"; import { EventEmissionHighlighter } from "./highlighters/EventEmissionHighlighter"; @@ -26,12 +23,16 @@ import { ContractDefinitionHighlighter } from "./highlighters/ContractDefinition import { InterfaceDefinitionHighlighter } from "./highlighters/InterfaceDefinitionHighlighter"; import { StructDefinitionHighlighter } from "./highlighters/StructDefinitionHighlighter"; import { HighlightVisitor } from "./HighlightVisitor"; +import { UserDefinedValueTypeDefinitionHighlighter } from "./highlighters/UserDefinedValueTypeDefinitionHighlighter copy"; +import { EnumDefinitionHighlighter } from "./highlighters/EnumDefinitionHighlighter"; +import { ErrorDefinitionHighlighter } from "./highlighters/ErrorDefinitionHighlighter"; +import { LibraryDefinitionHighlighter } from "./highlighters/LibraryDefinitionHighlighter"; const emptyResponse: SemanticTokens = { data: [] }; export function onSemanticTokensFull(serverState: ServerState) { return (params: SemanticTokensParams): SemanticTokens => { - const { telemetry, logger, solcVersions } = serverState; + const { telemetry, logger } = serverState; return ( telemetry.trackTimingSync("onSemanticTokensFull", (transaction) => { @@ -55,11 +56,26 @@ export function onSemanticTokensFull(serverState: ServerState) { const { versionPragmas } = analyze(text); span.finish(); - versionPragmas.push("<= 0.8.19"); // latest supported by slang + const versions = Language.supportedVersions(); + versionPragmas.push( + `>= ${versions[0]}`, + `<= ${versions[versions.length - 1]}` + ); - const solcVersion = - semver.maxSatisfying(solcVersions, versionPragmas.join(" ")) || - _.last(solcVersions); + const solcVersion = semver.maxSatisfying( + Language.supportedVersions(), + versionPragmas.join(" ") + ); + + if (solcVersion === null) { + logger.error( + `No supported solidity version found. Supported versions: ${Language.supportedVersions()}, pragma directives: ${versionPragmas}` + ); + return { + status: "internal_error", + result: emptyResponse, + }; + } try { // Parse using slang @@ -79,7 +95,7 @@ export function onSemanticTokensFull(serverState: ServerState) { const strings = parseOutput.errors.map((e: any) => e.toErrorReport(uri, text, false) ); - logger.error(strings.join("")); + logger.error(`Slang parsing error:\n${strings.join("\n")}`); return { status: "internal_error", @@ -92,9 +108,6 @@ export function onSemanticTokensFull(serverState: ServerState) { const visitors = [ new CustomTypeHighlighter(document, builder), - new KeywordHighlighter(document, builder), - new NumberHighlighter(document, builder), - new StringHighlighter(document, builder), new FunctionDefinitionHighlighter(document, builder), new FunctionCallHighlighter(document, builder), new EventEmissionHighlighter(document, builder), @@ -102,6 +115,10 @@ export function onSemanticTokensFull(serverState: ServerState) { new ContractDefinitionHighlighter(document, builder), new InterfaceDefinitionHighlighter(document, builder), new StructDefinitionHighlighter(document, builder), + new UserDefinedValueTypeDefinitionHighlighter(document, builder), + new EnumDefinitionHighlighter(document, builder), + new ErrorDefinitionHighlighter(document, builder), + new LibraryDefinitionHighlighter(document, builder), ]; // Visit the CST diff --git a/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol b/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol index d09e073e..97cd7179 100644 --- a/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol +++ b/test/protocol/projects/hardhat/contracts/documentSymbol/DocumentSymbols.sol @@ -78,6 +78,12 @@ contract testContract { TestStruct memory afterUTF8; afterUTF8; anotherFunction(); + + assembly { + function mult(a, b) -> result { + result := mul(a, b) + } + } } function anotherFunction() public pure {} diff --git a/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol b/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol index 443abb91..59a8773b 100644 --- a/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol +++ b/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.8; +type CustomType is uint256; + interface TestInterface {} struct TestStruct { @@ -10,6 +12,16 @@ struct TestStruct { address anAddress; } +enum TestEnum { + A, + B, + C +} + +error CustomError(); + +library MyLibrary {} + contract testContract { event TestEvent( testContract contractAsEventParam, TestInterface interfaceAsEventParam, TestStruct structAsEventParam diff --git a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts index f8c63c05..53ef7cde 100644 --- a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts +++ b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts @@ -734,6 +734,31 @@ describe('[hardhat] documentSymbol', () => { }, }, }, + { + children: [], + kind: 12, + name: 'mult', + range: { + start: { + line: 82, + character: 0, + }, + end: { + line: 85, + character: 0, + }, + }, + selectionRange: { + start: { + line: 82, + character: 21, + }, + end: { + line: 82, + character: 25, + }, + }, + }, ], kind: 12, name: 'testFunction', @@ -743,7 +768,7 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, end: { - line: 81, + line: 87, character: 0, }, }, @@ -764,21 +789,21 @@ describe('[hardhat] documentSymbol', () => { name: 'anotherFunction', range: { start: { - line: 81, + line: 87, character: 0, }, end: { - line: 83, + line: 89, character: 0, }, }, selectionRange: { start: { - line: 82, + line: 88, character: 13, }, end: { - line: 82, + line: 88, character: 28, }, }, @@ -789,21 +814,21 @@ describe('[hardhat] documentSymbol', () => { name: 'fallback', range: { start: { - line: 83, + line: 89, character: 0, }, end: { - line: 85, + line: 91, character: 0, }, }, selectionRange: { start: { - line: 84, + line: 90, character: 4, }, end: { - line: 84, + line: 90, character: 12, }, }, @@ -814,21 +839,21 @@ describe('[hardhat] documentSymbol', () => { name: 'receive', range: { start: { - line: 85, + line: 91, character: 0, }, end: { - line: 87, + line: 93, character: 0, }, }, selectionRange: { start: { - line: 86, + line: 92, character: 4, }, end: { - line: 86, + line: 92, character: 11, }, }, @@ -842,7 +867,7 @@ describe('[hardhat] documentSymbol', () => { character: 0, }, end: { - line: 88, + line: 94, character: 0, }, }, diff --git a/test/protocol/test/textDocument/semanticTokens/full.test.ts b/test/protocol/test/textDocument/semanticTokens/full.test.ts index 6b52b58d..1a3bf9a2 100644 --- a/test/protocol/test/textDocument/semanticTokens/full.test.ts +++ b/test/protocol/test/textDocument/semanticTokens/full.test.ts @@ -27,13 +27,10 @@ describe('[hardhat] semanticTokens/full', () => { expect(semanticTokens).to.deep.equal({ data: [ - 2, 0, 6, 0, 0, 0, 7, 8, 0, 0, 2, 0, 9, 0, 0, 0, 10, 13, 2, 0, 2, 0, 6, 0, 0, 0, 7, 10, 2, 0, 1, 4, 7, 0, 0, 1, - 4, 6, 0, 0, 1, 4, 7, 0, 0, 3, 0, 8, 0, 0, 0, 9, 12, 2, 0, 1, 4, 5, 0, 0, 0, 6, 9, 2, 0, 1, 8, 12, 2, 0, 0, 35, - 13, 2, 0, 0, 37, 10, 2, 0, 3, 4, 12, 2, 0, 2, 4, 13, 2, 0, 2, 4, 10, 2, 0, 2, 4, 7, 0, 0, 0, 18, 4, 1, 0, 0, 7, - 3, 1, 0, 2, 4, 6, 0, 0, 0, 17, 6, 3, 0, 2, 4, 8, 0, 0, 0, 9, 12, 4, 0, 1, 8, 12, 2, 0, 1, 8, 13, 2, 0, 1, 8, 10, - 2, 0, 0, 11, 6, 0, 0, 1, 6, 6, 0, 0, 1, 8, 12, 2, 0, 2, 8, 13, 2, 0, 1, 8, 10, 2, 0, 0, 11, 6, 0, 0, 8, 8, 4, 0, - 0, 0, 5, 9, 2, 0, 4, 8, 10, 2, 0, 0, 11, 6, 0, 0, 2, 8, 15, 4, 0, 3, 4, 8, 0, 0, 0, 9, 15, 4, 0, 0, 18, 6, 0, 0, - 0, 7, 4, 0, 0, + 4, 5, 10, 2, 0, 2, 10, 13, 2, 0, 2, 7, 10, 2, 0, 6, 5, 8, 2, 0, 6, 6, 11, 2, 0, 2, 8, 9, 2, 0, 2, 9, 12, 2, 0, + 1, 10, 9, 2, 0, 1, 8, 12, 2, 0, 0, 35, 13, 2, 0, 0, 37, 10, 2, 0, 3, 4, 12, 2, 0, 2, 4, 13, 2, 0, 2, 4, 10, 2, + 0, 6, 13, 12, 4, 0, 1, 8, 12, 2, 0, 1, 8, 13, 2, 0, 1, 8, 10, 2, 0, 2, 8, 12, 2, 0, 2, 8, 13, 2, 0, 1, 8, 10, 2, + 0, 8, 13, 9, 2, 0, 4, 8, 10, 2, 0, 2, 8, 15, 4, 0, 3, 13, 15, 4, 0, ], }) }) From f3095bd21b9aeb189acf6fcda16654da22145f24 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Fri, 8 Dec 2023 14:14:24 -0300 Subject: [PATCH 15/18] Support unnamed function definition on documentSymbols --- package.json | 1 - .../documentSymbol/onDocumentSymbol.ts | 13 +--- .../visitors/UnnamedFunctionDefinition.ts | 9 +++ .../semanticHighlight/onSemanticTokensFull.ts | 16 +--- .../src/documentSymbol/UnnamedFunction.sol | 7 ++ .../documentSymbol/documentSymbol.test.ts | 74 +++++++++++++++++++ 6 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 server/src/services/documentSymbol/visitors/UnnamedFunctionDefinition.ts create mode 100644 test/protocol/projects/projectless/src/documentSymbol/UnnamedFunction.sol diff --git a/package.json b/package.json index 083a528b..5a151564 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "url": "https://github.com/NomicFoundation/hardhat-vscode/issues" }, "scripts": { - "postinstall": "npm install --no-save --ignore-scripts --force @nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1 @nomicfoundation/slang-win32-ia32-msvc@0.10.1", "build": "tsc -b ./client/tsconfig.json && tsc -b ./server/tsconfig.build.json && tsc -b ./coc/tsconfig.json && tsc -b", "watch": "concurrently -n client,server \"tsc -b -w ./client/tsconfig.json\" \"tsc -b -w ./server/tsconfig.build.json\"", "test:unit": "npm -w server run test", diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index cf8e49ac..69c0cd83 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -11,6 +11,7 @@ import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { RuleNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; +import { SlangNode } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; import { StructDefinition } from "./visitors/StructDefinition"; import { StructMember } from "./visitors/StructMember"; @@ -31,6 +32,7 @@ import { ReceiveFunctionDefinition } from "./visitors/ReceiveFunctionDefinition" import { UserDefinedValueTypeDefinition } from "./visitors/UserDefinedValueTypeDefinition"; import { SymbolVisitor } from "./SymbolVisitor"; import { YulFunctionDefinition } from "./visitors/YulFunctionDefinition"; +import { UnnamedFunctionDefinition } from "./visitors/UnnamedFunctionDefinition"; export function onDocumentSymbol(serverState: ServerState) { return async ( @@ -80,17 +82,9 @@ export function onDocumentSymbol(serverState: ServerState) { document.getText() ); - const parseTree = parseOutput.parseTree; + const parseTree: SlangNode = parseOutput.parseTree; span.finish(); - if (parseTree === null) { - const strings = parseOutput.errors.map((e: any) => - e.toErrorReport(uri, text, false) - ); - - throw new Error(`Slang parsing error:\n${strings.join("\n")}`); - } - const builder = new SymbolTreeBuilder(); const visitors: SymbolVisitor[] = [ @@ -112,6 +106,7 @@ export function onDocumentSymbol(serverState: ServerState) { new ReceiveFunctionDefinition(document, builder), new UserDefinedValueTypeDefinition(document, builder), new YulFunctionDefinition(document, builder), + new UnnamedFunctionDefinition(document, builder), ]; const indexedVisitors = _.keyBy(visitors, "ruleKind"); diff --git a/server/src/services/documentSymbol/visitors/UnnamedFunctionDefinition.ts b/server/src/services/documentSymbol/visitors/UnnamedFunctionDefinition.ts new file mode 100644 index 00000000..54bdcfc5 --- /dev/null +++ b/server/src/services/documentSymbol/visitors/UnnamedFunctionDefinition.ts @@ -0,0 +1,9 @@ +import { SymbolKind } from "vscode-languageserver-types"; +import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; +import { SymbolVisitor } from "../SymbolVisitor"; + +export class UnnamedFunctionDefinition extends SymbolVisitor { + public ruleKind = RuleKind.UnnamedFunctionDefinition; + public symbolKind = SymbolKind.Function; + public nameTokenKind = TokenKind.FunctionKeyword; +} diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 700ca7c4..9f98f969 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -13,6 +13,7 @@ import { ProductionKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { TokenNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; +import { SlangNode } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; import { FunctionDefinitionHighlighter } from "./highlighters/FunctionDefinitionHighlighter"; @@ -87,22 +88,9 @@ export function onSemanticTokensFull(serverState: ServerState) { document.getText() ); - const parseTree = parseOutput.parseTree; + const parseTree: SlangNode = parseOutput.parseTree; span.finish(); - if (parseTree === null) { - logger.error("Slang parsing error"); - const strings = parseOutput.errors.map((e: any) => - e.toErrorReport(uri, text, false) - ); - logger.error(`Slang parsing error:\n${strings.join("\n")}`); - - return { - status: "internal_error", - result: emptyResponse, - }; - } - // Register visitors const builder = new SemanticTokensBuilder(document); diff --git a/test/protocol/projects/projectless/src/documentSymbol/UnnamedFunction.sol b/test/protocol/projects/projectless/src/documentSymbol/UnnamedFunction.sol new file mode 100644 index 00000000..ab9e6fd1 --- /dev/null +++ b/test/protocol/projects/projectless/src/documentSymbol/UnnamedFunction.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.4.11; + +contract UnnamedTest { + function() payable {} +} diff --git a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts index 53ef7cde..14216fca 100644 --- a/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts +++ b/test/protocol/test/textDocument/documentSymbol/documentSymbol.test.ts @@ -885,3 +885,77 @@ describe('[hardhat] documentSymbol', () => { ]) }) }) + +describe('[projectless] documentSymbol', () => { + let testPath: string + + before(async () => { + client = await getInitializedClient() + + testPath = getProjectPath('projectless/src/documentSymbol/UnnamedFunction.sol') + + await client.openDocument(testPath) + }) + + after(async () => { + client.closeAllDocuments() + }) + + test('supports unnamed function definition', async function () { + const symbols = await client.getDocumentSymbols(toUri(testPath)) + + expect(symbols).to.deep.equal([ + { + children: [ + { + children: [], + kind: 12, + name: 'function', + range: { + start: { + line: 5, + character: 0, + }, + end: { + line: 6, + character: 0, + }, + }, + selectionRange: { + start: { + line: 5, + character: 4, + }, + end: { + line: 5, + character: 12, + }, + }, + }, + ], + kind: 5, + name: 'UnnamedTest', + range: { + start: { + line: 3, + character: 0, + }, + end: { + line: 7, + character: 0, + }, + }, + selectionRange: { + start: { + line: 4, + character: 9, + }, + end: { + line: 4, + character: 20, + }, + }, + }, + ]) + }) +}) From 6d58175cea67fd137aa8c44d4b934f6903e69c39 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Sat, 16 Dec 2023 12:26:53 -0300 Subject: [PATCH 16/18] refactor based on feedback --- flags.json | 4 ++-- .../services/documentSymbol/onDocumentSymbol.ts | 13 ++++++------- .../semanticHighlight/onSemanticTokensFull.ts | 15 +++++++-------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/flags.json b/flags.json index b5627fc5..be52c528 100644 --- a/flags.json +++ b/flags.json @@ -1,9 +1,9 @@ { "documentSymbol": { - "percent": 0.1 + "percent": 0 }, "semanticHighlighting": { - "percent": 0.1 + "percent": 0 } } diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 69c0cd83..0a69e444 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -11,7 +11,6 @@ import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { RuleNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; -import { SlangNode } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; import { StructDefinition } from "./visitors/StructDefinition"; import { StructMember } from "./visitors/StructMember"; @@ -62,27 +61,27 @@ export function onDocumentSymbol(serverState: ServerState) { `<= ${versions[versions.length - 1]}` ); - const solcVersion = semver.maxSatisfying( - Language.supportedVersions(), + const slangVersion = semver.maxSatisfying( + versions, versionPragmas.join(" ") ); - if (solcVersion === null) { + if (slangVersion === null) { throw new Error( - `No supported solidity version found. Supported versions: ${Language.supportedVersions()}, pragma directives: ${versionPragmas}` + `No supported solidity version found. Supported versions: ${versions}, pragma directives: ${versionPragmas}` ); } // Parse using slang span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(solcVersion!); + const language = new Language(slangVersion!); const parseOutput = language.parse( ProductionKind.SourceUnit, document.getText() ); - const parseTree: SlangNode = parseOutput.parseTree; + const parseTree = parseOutput.parseTree; span.finish(); const builder = new SymbolTreeBuilder(); diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index 9f98f969..e239800a 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -13,7 +13,6 @@ import { ProductionKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { TokenNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; -import { SlangNode } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; import { FunctionDefinitionHighlighter } from "./highlighters/FunctionDefinitionHighlighter"; @@ -63,14 +62,14 @@ export function onSemanticTokensFull(serverState: ServerState) { `<= ${versions[versions.length - 1]}` ); - const solcVersion = semver.maxSatisfying( - Language.supportedVersions(), + const slangVersion = semver.maxSatisfying( + versions, versionPragmas.join(" ") ); - if (solcVersion === null) { + if (slangVersion === null) { logger.error( - `No supported solidity version found. Supported versions: ${Language.supportedVersions()}, pragma directives: ${versionPragmas}` + `No supported solidity version found. Supported versions: ${versions}, pragma directives: ${versionPragmas}` ); return { status: "internal_error", @@ -81,14 +80,14 @@ export function onSemanticTokensFull(serverState: ServerState) { try { // Parse using slang span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(solcVersion!); + const language = new Language(slangVersion!); const parseOutput = language.parse( ProductionKind.SourceUnit, document.getText() ); - const parseTree: SlangNode = parseOutput.parseTree; + const parseTree = parseOutput.parseTree; span.finish(); // Register visitors @@ -150,7 +149,7 @@ export function onSemanticTokensFull(serverState: ServerState) { return { status: "ok", result: { data: builder.getTokenData() } }; } catch (error) { - logger.error(`Slang parsing error: ${error}`); + logger.error(`Semantic Highlighting Error: ${error}`); return { status: "internal_error", result: emptyResponse }; } }) || emptyResponse From ea7fa83f67af60c86696fadd19a363647bf4f23e Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Sat, 6 Jan 2024 10:12:32 -0300 Subject: [PATCH 17/18] moving version selection to slangHelpers --- package-lock.json | 1 - server/src/parser/slangHelpers.ts | 18 +++ .../documentSymbol/onDocumentSymbol.ts | 142 ++++++++---------- .../semanticHighlight/onSemanticTokensFull.ts | 25 +-- 4 files changed, 85 insertions(+), 101 deletions(-) diff --git a/package-lock.json b/package-lock.json index 316bd954..777ee3b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "packages": { "": { "name": "solidity-language-server-monorepo", - "hasInstallScript": true, "workspaces": [ "client", "server", diff --git a/server/src/parser/slangHelpers.ts b/server/src/parser/slangHelpers.ts index 29c77d27..00da758d 100644 --- a/server/src/parser/slangHelpers.ts +++ b/server/src/parser/slangHelpers.ts @@ -4,6 +4,8 @@ import { TextRange } from "@nomicfoundation/slang/text_index"; import _ from "lodash"; import { TextDocument } from "vscode-languageserver-textdocument"; import { Range } from "vscode-languageserver-types"; +import { Language } from "@nomicfoundation/slang/language"; +import semver from "semver"; import { getPlatform } from "../utils/operatingSystem"; export type SlangNode = RuleNode | TokenNode; @@ -42,3 +44,19 @@ export function isSlangSupported() { return SUPPORTED_PLATFORMS.includes(currentPlatform); } + +export function getLanguage(versionPragmas: string[]): Language { + const supportedVersions = Language.supportedVersions(); + + const slangVersion = semver.maxSatisfying( + supportedVersions, + versionPragmas.join(" ") + ); + + if (slangVersion === null) { + throw new Error( + `No supported solidity version found. Supported versions: ${supportedVersions}, pragma directives: ${versionPragmas}` + ); + } + return new Language(slangVersion); +} diff --git a/server/src/services/documentSymbol/onDocumentSymbol.ts b/server/src/services/documentSymbol/onDocumentSymbol.ts index 0a69e444..48b77321 100644 --- a/server/src/services/documentSymbol/onDocumentSymbol.ts +++ b/server/src/services/documentSymbol/onDocumentSymbol.ts @@ -4,13 +4,12 @@ import { DocumentSymbolParams } from "vscode-languageserver/node"; import { DocumentSymbol, SymbolInformation } from "vscode-languageserver-types"; import { analyze } from "@nomicfoundation/solidity-analyzer"; -import semver from "semver"; import _ from "lodash"; -import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { RuleNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; +import { getLanguage } from "../../parser/slangHelpers"; import { SymbolTreeBuilder } from "./SymbolTreeBuilder"; import { StructDefinition } from "./visitors/StructDefinition"; import { StructMember } from "./visitors/StructMember"; @@ -37,7 +36,7 @@ export function onDocumentSymbol(serverState: ServerState) { return async ( params: DocumentSymbolParams ): Promise => { - const { telemetry } = serverState; + const { telemetry, logger } = serverState; return telemetry.trackTimingSync("onDocumentSymbol", (transaction) => { const { uri } = params.textDocument; @@ -55,85 +54,74 @@ export function onDocumentSymbol(serverState: ServerState) { const { versionPragmas } = analyze(text); span.finish(); - const versions = Language.supportedVersions(); - versionPragmas.push( - `>= ${versions[0]}`, - `<= ${versions[versions.length - 1]}` - ); + try { + const language = getLanguage(versionPragmas); - const slangVersion = semver.maxSatisfying( - versions, - versionPragmas.join(" ") - ); + // Parse using slang + span = transaction.startChild({ op: "slang-parsing" }); - if (slangVersion === null) { - throw new Error( - `No supported solidity version found. Supported versions: ${versions}, pragma directives: ${versionPragmas}` + const parseOutput = language.parse( + ProductionKind.SourceUnit, + document.getText() ); - } - - // Parse using slang - span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(slangVersion!); - const parseOutput = language.parse( - ProductionKind.SourceUnit, - document.getText() - ); - - const parseTree = parseOutput.parseTree; - span.finish(); - - const builder = new SymbolTreeBuilder(); - - const visitors: SymbolVisitor[] = [ - new StructDefinition(document, builder), - new StructMember(document, builder), - new InterfaceDefinition(document, builder), - new FunctionDefinition(document, builder), - new ContractDefinition(document, builder), - new EventDefinition(document, builder), - new StateVariableDeclaration(document, builder), - new VariableDeclaration(document, builder), - new ConstantDefinition(document, builder), - new ConstructorDefinition(document, builder), - new EnumDefinition(document, builder), - new ErrorDefinition(document, builder), - new FallbackFunctionDefinition(document, builder), - new LibraryDefinition(document, builder), - new ModifierDefinition(document, builder), - new ReceiveFunctionDefinition(document, builder), - new UserDefinedValueTypeDefinition(document, builder), - new YulFunctionDefinition(document, builder), - new UnnamedFunctionDefinition(document, builder), - ]; - - const indexedVisitors = _.keyBy(visitors, "ruleKind"); - - const cursor: Cursor = parseTree.cursor; - const ruleKinds = visitors.map((v) => v.ruleKind); - let node: RuleNode; - - // Useful to keep this here for development - // const kursor: Cursor = parseTree.cursor.clone(); - // do { - // console.log( - // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}(${ - // ["R", "T"][kursor.node.type] - // }): ${kursor.node?.text ?? ""}` - // ); - // } while (kursor.goToNext()); - - span = transaction.startChild({ op: "walk-generate-symbols" }); - while ((node = cursor.findRuleWithKind(ruleKinds)) !== null) { - const visitor: SymbolVisitor = indexedVisitors[node.kind]; - visitor.onRuleNode(cursor); - - cursor.goToNext(); + const parseTree = parseOutput.parseTree; + span.finish(); + + const builder = new SymbolTreeBuilder(); + + const visitors: SymbolVisitor[] = [ + new StructDefinition(document, builder), + new StructMember(document, builder), + new InterfaceDefinition(document, builder), + new FunctionDefinition(document, builder), + new ContractDefinition(document, builder), + new EventDefinition(document, builder), + new StateVariableDeclaration(document, builder), + new VariableDeclaration(document, builder), + new ConstantDefinition(document, builder), + new ConstructorDefinition(document, builder), + new EnumDefinition(document, builder), + new ErrorDefinition(document, builder), + new FallbackFunctionDefinition(document, builder), + new LibraryDefinition(document, builder), + new ModifierDefinition(document, builder), + new ReceiveFunctionDefinition(document, builder), + new UserDefinedValueTypeDefinition(document, builder), + new YulFunctionDefinition(document, builder), + new UnnamedFunctionDefinition(document, builder), + ]; + + const indexedVisitors = _.keyBy(visitors, "ruleKind"); + + const cursor: Cursor = parseTree.cursor; + const ruleKinds = visitors.map((v) => v.ruleKind); + let node: RuleNode; + + // Useful to keep this here for development + // const kursor: Cursor = parseTree.cursor.clone(); + // do { + // console.log( + // `${" ".repeat(kursor.pathRuleNodes.length)}${kursor.node.kind}(${ + // ["R", "T"][kursor.node.type] + // }): ${kursor.node?.text ?? ""}` + // ); + // } while (kursor.goToNext()); + + span = transaction.startChild({ op: "walk-generate-symbols" }); + while ((node = cursor.findRuleWithKind(ruleKinds)) !== null) { + const visitor: SymbolVisitor = indexedVisitors[node.kind]; + visitor.onRuleNode(cursor); + + cursor.goToNext(); + } + span.finish(); + + return { status: "ok", result: builder.getSymbols() }; + } catch (error) { + logger.error(`Document Symbol Error: ${error}`); + return { status: "internal_error", result: null }; } - span.finish(); - - return { status: "ok", result: builder.getSymbols() }; }); }; } diff --git a/server/src/services/semanticHighlight/onSemanticTokensFull.ts b/server/src/services/semanticHighlight/onSemanticTokensFull.ts index e239800a..f997bf2e 100644 --- a/server/src/services/semanticHighlight/onSemanticTokensFull.ts +++ b/server/src/services/semanticHighlight/onSemanticTokensFull.ts @@ -7,12 +7,11 @@ import { } from "vscode-languageserver-protocol"; import _, { Dictionary } from "lodash"; import { analyze } from "@nomicfoundation/solidity-analyzer"; -import semver from "semver"; -import { Language } from "@nomicfoundation/slang/language"; import { ProductionKind, TokenKind } from "@nomicfoundation/slang/kinds"; import { Cursor } from "@nomicfoundation/slang/cursor"; import { TokenNode } from "@nomicfoundation/slang/cst"; import { ServerState } from "../../types"; +import { getLanguage } from "../../parser/slangHelpers"; import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter"; import { SemanticTokensBuilder } from "./SemanticTokensBuilder"; import { FunctionDefinitionHighlighter } from "./highlighters/FunctionDefinitionHighlighter"; @@ -56,31 +55,11 @@ export function onSemanticTokensFull(serverState: ServerState) { const { versionPragmas } = analyze(text); span.finish(); - const versions = Language.supportedVersions(); - versionPragmas.push( - `>= ${versions[0]}`, - `<= ${versions[versions.length - 1]}` - ); - - const slangVersion = semver.maxSatisfying( - versions, - versionPragmas.join(" ") - ); - - if (slangVersion === null) { - logger.error( - `No supported solidity version found. Supported versions: ${versions}, pragma directives: ${versionPragmas}` - ); - return { - status: "internal_error", - result: emptyResponse, - }; - } + const language = getLanguage(versionPragmas); try { // Parse using slang span = transaction.startChild({ op: "slang-parsing" }); - const language = new Language(slangVersion!); const parseOutput = language.parse( ProductionKind.SourceUnit, From 76b210f45d040d584ccfe404973e92dcb8200bd2 Mon Sep 17 00:00:00 2001 From: Armando Andini Date: Sat, 6 Jan 2024 11:41:09 -0300 Subject: [PATCH 18/18] add comment to semantic tokens test --- .../hardhat/contracts/semanticTokens/full/SemanticTokens.sol | 2 ++ test/protocol/test/textDocument/semanticTokens/full.test.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol b/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol index 59a8773b..fee819fd 100644 --- a/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol +++ b/test/protocol/projects/hardhat/contracts/semanticTokens/full/SemanticTokens.sol @@ -46,6 +46,8 @@ contract testContract { TestInterface interfaceAsLocalVar; TestStruct memory structAsLocalVar; + + // The following expression statements should not be highlighted contractAsLocalVar; contractAsFuncParam; interfaceAsFuncParam; diff --git a/test/protocol/test/textDocument/semanticTokens/full.test.ts b/test/protocol/test/textDocument/semanticTokens/full.test.ts index 1a3bf9a2..812c1211 100644 --- a/test/protocol/test/textDocument/semanticTokens/full.test.ts +++ b/test/protocol/test/textDocument/semanticTokens/full.test.ts @@ -30,7 +30,7 @@ describe('[hardhat] semanticTokens/full', () => { 4, 5, 10, 2, 0, 2, 10, 13, 2, 0, 2, 7, 10, 2, 0, 6, 5, 8, 2, 0, 6, 6, 11, 2, 0, 2, 8, 9, 2, 0, 2, 9, 12, 2, 0, 1, 10, 9, 2, 0, 1, 8, 12, 2, 0, 0, 35, 13, 2, 0, 0, 37, 10, 2, 0, 3, 4, 12, 2, 0, 2, 4, 13, 2, 0, 2, 4, 10, 2, 0, 6, 13, 12, 4, 0, 1, 8, 12, 2, 0, 1, 8, 13, 2, 0, 1, 8, 10, 2, 0, 2, 8, 12, 2, 0, 2, 8, 13, 2, 0, 1, 8, 10, 2, - 0, 8, 13, 9, 2, 0, 4, 8, 10, 2, 0, 2, 8, 15, 4, 0, 3, 13, 15, 4, 0, + 0, 10, 13, 9, 2, 0, 4, 8, 10, 2, 0, 2, 8, 15, 4, 0, 3, 13, 15, 4, 0, ], }) })