diff --git a/package.json b/package.json index 2f593c1..3281237 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,11 @@ "type": "object", "description": "List of detectors which are hidden in the explorer", "default": [] + }, + "slither.slitherLspPath": { + "type":"string", + "markdownDescription": "Path to the `slither-lsp` executable", + "default": "slither-lsp" } } } diff --git a/src/extension.ts b/src/extension.ts index 12168e5..feadaa7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,6 +5,7 @@ import { SettingsViewProvider } from "./views/settings/settingsViewProvider"; import * as state from "./state"; import { Configuration } from "./types/configTypes"; import { isDebuggingExtension } from "./utils/common"; +import * as cp from "child_process" // Properties export let analysis_key: number | null = null; @@ -14,6 +15,40 @@ export let finishedActivation: boolean = false; let slitherSettingsProvider: SettingsViewProvider; +const slitherLspRegex = /^\s*usage:\s+slither-lsp/gm; + +function checkSlitherLsp(path: string): Promise { + return new Promise((resolve, reject) => { + const data: Buffer[] = []; + + const child = cp.spawn(path, ["--help"]); + child.stdout.on("data", chunk => { + data.push(chunk) + }) + + child.on("error", err => { + reject(err); + }) + + child.on("close", code => { + const stdout = Buffer.concat(data).toString("utf-8").trim(); + + if (code) { + reject(`Invalid \`slither-lsp\` executable. Process terminated with code ${code}`); + Logger.error(`slither-lsp output: ${stdout}`); + return; + } + + if (!slitherLspRegex.test(stdout)) { + reject(`The specified executable was not recognized as a valid \`slither-lsp\` executable`); + return; + } + + resolve(); + }); + }); +} + // Functions export async function activate(context: vscode.ExtensionContext) { // Set our slither panel to visible @@ -58,8 +93,43 @@ export async function activate(context: vscode.ExtensionContext) { Logger.debug("Started in console mode"); } + const slitherLspPath = vscode.workspace.getConfiguration("slither").get("slitherLspPath", "slither-lsp"); + + vscode.workspace.onDidChangeConfiguration(async event => { + let affected = event.affectsConfiguration("slither.slitherLspPath"); + if (!affected) { + return; + } + + const action = await vscode.window + .showInformationMessage( + `Reload window in order for changes in the configuration of Contract Explorer to take effect.`, + "Reload" + ); + if (action === "Reload") { + await vscode.commands.executeCommand('workbench.action.reloadWindow'); + } + }); + + try { + await checkSlitherLsp(slitherLspPath); + } catch (err) { + let msg = ""; + if (typeof (err) === "string") { + msg = err; + } else if (err instanceof Error) { + msg = err.toString(); + } + const action = await vscode.window.showErrorMessage(msg, "Edit path"); + + if (action === "Edit path") { + await vscode.commands.executeCommand("workbench.action.openSettings", "slither.slitherLspPath"); + } + return; + } + // Initialize the language server - let slitherLanguageClient = new SlitherLanguageClient(port); + let slitherLanguageClient = new SlitherLanguageClient(slitherLspPath, port); // When the language server is ready, we'll want to start fetching some state variables. slitherLanguageClient.start(async () => { diff --git a/src/slitherLanguageClient.ts b/src/slitherLanguageClient.ts index fb22ac8..2de7f78 100644 --- a/src/slitherLanguageClient.ts +++ b/src/slitherLanguageClient.ts @@ -14,26 +14,23 @@ import { } from "./types/languageServerTypes"; import { AnalysisRequestParams } from "./types/analysisTypes"; -// The name of the language server executable -const lsp_executable_name = "slither-lsp"; - export class SlitherLanguageClient { public languageClient: LanguageClient; private socket: Socket | null = null; - constructor(port: integer | null) { + constructor(slitherLspPath: string, port: integer | null) { // Define server options. let serverOptions: ServerOptions; if (port == null) { // If we weren't given a port, we use stdio. We keep the process attached so when we exit, it exits. serverOptions = { run: { - command: lsp_executable_name, + command: slitherLspPath, args: [], options: { detached: false }, }, debug: { - command: lsp_executable_name, + command: slitherLspPath, args: [], options: { detached: false }, },