From 19d4b00a83ba5496e4d82ebef49539b1b80725bc Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 09:34:43 -0700 Subject: [PATCH 01/13] remove emoji Signed-off-by: Jade Abraham --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fbf6f0..ec3748b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ - Generic Instantiations: inspect generic code with helpful annotations - Dead Code: highlight dead code that will never execute -> **_:warning: CAUTION:_** +> **_CAUTION:_** > These features use a work-in-progress resolver for Chapel called Dyno to further > inspect your code. To enable these features, use Dyno by setting > `chapel.chpl-language-server.resolver` to `true`. Enabling the Dyno resolver From 5db8b3ea3e28f7a197d4b2111a1cd376f0baf0db Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 14:19:36 -0700 Subject: [PATCH 02/13] add contributor docs Signed-off-by: Jade Abraham --- DEVELOPER.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 DEVELOPER.md diff --git a/DEVELOPER.md b/DEVELOPER.md new file mode 100644 index 0000000..e4d1ba8 --- /dev/null +++ b/DEVELOPER.md @@ -0,0 +1,9 @@ +# Documentation for Extension Developers + +This document is intended for developers who want to contribute to the Chapel extension for Visual Studio Code. + +## Building locally + +To build and test the extension locally in debug mode, you can use the Run/Debug tab to launch the extension in a new VSCode window (or press `F5`). + +For some debugging purposes, it may be useful to build a local binary of the extension using `vsce`. To do this, run `vsce package -o bin/chapel.vsix` from the root of the repository. This can then be installed in VSCode by selecting "Install from VSIX" in the Extensions view, or by running `code --install-extension bin/chapel.vsix`. From e3bbce9f6061e72f927c89dcf7465945e921a1f8 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 15:18:25 -0700 Subject: [PATCH 03/13] use more portable delimeter Signed-off-by: Jade Abraham --- src/ChplPaths.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ChplPaths.ts b/src/ChplPaths.ts index fda7756..ad6e355 100644 --- a/src/ChplPaths.ts +++ b/src/ChplPaths.ts @@ -97,7 +97,7 @@ export function findPossibleChplHomes(): string[] { // as a best effort, we find chpl in the PATH and check if chplhome is the parent directory const PATH = process.env["PATH"]; - const paths_to_check = PATH?.split(":") ?? []; + const paths_to_check = PATH?.split(path.delimiter) ?? []; for (const p of paths_to_check) { const chpl_path = path.join(p, "chpl"); if (fs.existsSync(chpl_path) && fs.statSync(chpl_path).isFile()) { From 84477750ed2fc3488a36c43f533bb0579feeffc9 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 15:25:34 -0700 Subject: [PATCH 04/13] add note about vscode bug Signed-off-by: Jade Abraham --- src/configuration.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/configuration.ts b/src/configuration.ts index 31d8a45..1317dd3 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -43,7 +43,12 @@ export function getChplHome(): string { export function setChplHome(chplhome: string) { const config = vscode.workspace.getConfiguration(configScope); - // when updating CHPL_HOME, we should update the workspace config if its been overriden, otherwise set it globally for all users + // an unfortunate aspect of the vscode API is that it doesn't allow us to + // distinguish between global local and global remote settings. This means + // this code may edit the wrong config file, because it will prefer the + // global local settings over the global remote settings. This is a known + // issue with the vscode API: + // https://github.com/microsoft/vscode/issues/182696 if(config.inspect("CHPL_HOME")?.workspaceValue !== undefined) { config.update("CHPL_HOME", chplhome, vscode.ConfigurationTarget.Workspace); } else { From 568ad05382df3a6ebddeb72ead9ce8c0185d8cce Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 15:25:40 -0700 Subject: [PATCH 05/13] fix permissions issues Signed-off-by: Jade Abraham --- src/ChplPaths.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ChplPaths.ts b/src/ChplPaths.ts index ad6e355..433d9b4 100644 --- a/src/ChplPaths.ts +++ b/src/ChplPaths.ts @@ -28,14 +28,14 @@ export function checkChplHome( return "CHPL_HOME is not set"; } - if (!path.isAbsolute(chplhome)) { - return `CHPL_HOME (${chplhome}) is not an absolute path`; - } - if (!fs.existsSync(chplhome) || !fs.statSync(chplhome).isDirectory()) { return `CHPL_HOME (${chplhome}) does not exist`; } + if (!path.isAbsolute(chplhome)) { + return `CHPL_HOME (${chplhome}) is not an absolute path`; + } + const subdirs = [ "util", "compiler", @@ -75,6 +75,12 @@ function searchDirectoryForChplHome(dir: string, depth: number = 1): string[] { if (checkChplHome(dir) === undefined) { chplhomes.push(dir); } else if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) { + try { + // sadly this is the only way synchronous way to check if the directory is readable + fs.accessSync(dir, fs.constants.R_OK); + } catch(err) { + return chplhomes; + } fs.readdirSync(dir).forEach((subdir) => { const subdir_path = path.join(dir, subdir); chplhomes.push(...searchDirectoryForChplHome(subdir_path, depth - 1)); From e521411cef65a2f9a6aeea48326427fb022b6a64 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 16:55:18 -0700 Subject: [PATCH 06/13] support chapel format strings Signed-off-by: Jade Abraham --- syntaxes/chapel.tmLanguage.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/syntaxes/chapel.tmLanguage.json b/syntaxes/chapel.tmLanguage.json index c0dcb17..74eabc9 100644 --- a/syntaxes/chapel.tmLanguage.json +++ b/syntaxes/chapel.tmLanguage.json @@ -129,11 +129,15 @@ "string_format": { "patterns": [ { - "match": "%%", + "match": "(%[{]([#]+|[#]*[.][#]+)[}])", "name": "constant.other.placeholder.chapel" }, { - "match": "%[0-9]+\\.[0-9]*(i|di|u|du|bi|bu||@bu|oi|ou)", + "match": "(%(@|\\+|0|<|>|\\^)*(\\*|[0-9]+)?(\\.[0-9]*)?[dxXobjh'\"]?[eE]?([niurmzsSc]|([{](.S|\\*S|.S.)[}])|(/.*/)|([{]/.*/[a-zA-Z]+[}]))?)", + "name": "constant.other.placeholder.chapel" + }, + { + "match": "(%%)", "name": "constant.other.placeholder.chapel" } ] From 1aee936a9ebcea7b8e14656027131bf929771944 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 17:17:15 -0700 Subject: [PATCH 07/13] fix floating points Signed-off-by: Jade Abraham --- syntaxes/chapel.tmLanguage.json | 46 ++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/syntaxes/chapel.tmLanguage.json b/syntaxes/chapel.tmLanguage.json index 74eabc9..768a808 100644 --- a/syntaxes/chapel.tmLanguage.json +++ b/syntaxes/chapel.tmLanguage.json @@ -80,20 +80,58 @@ "match": "\\b(true|false|nil)\\b", "name": "constant.language.chapel" }, + { "include": "#integers" }, + { "include": "#floats" } + ] + }, + "integers": { + "patterns": [ + { + "match": "\\b([0-9][0-9_]*)i?\\b", + "name": "constant.numeric.chapel" + }, + { + "match": "\\b(0[xX][0-9a-fA-F][0-9a-fA-F_]*)i?\\b", + "name": "constant.numeric.chapel" + }, + { + "match": "\\b(0[bB][01][01_]*)i?\\b", + "name": "constant.numeric.chapel" + }, + { + "match": "\\b(0[oO][0-7][0-7_]*)i?\\b", + "name": "constant.numeric.chapel" + } + ] + }, + "floats": { + "patterns": [ + { + "match": "\\.([0-9][0-9_]*)([Ee](\\+|-)?([0-9][0-9_]*))?i?\\b", + "name": "constant.numeric.chapel" + }, + { + "match": "\\b([0-9][0-9_]*)\\.([0-9][0-9_]*)([Ee](\\+|-)?([0-9][0-9_]*))?i?\\b", + "name": "constant.numeric.chapel" + }, + { + "match": "\\b([0-9][0-9_]*)\\.([Ee](\\+|-)?([0-9][0-9_]*))i?\\b", + "name": "constant.numeric.chapel" + }, { - "match": "\\b([0-9][0-9_]*)\\b", + "match": "\\b([0-9][0-9_]*)([Ee](\\+|-)?([0-9][0-9_]*))i?\\b", "name": "constant.numeric.chapel" }, { - "match": "\\b(0[xX][0-9a-fA-F][0-9a-fA-F_]*)\\b", + "match": "\\b0[xX]([0-9a-fA-F][0-9a-fA-F_]*)?\\.([0-9a-fA-F][0-9a-fA-F_]*)([Pp](\\+|-)?([0-9a-fA-F][0-9a-fA-F_]*))?i?\\b", "name": "constant.numeric.chapel" }, { - "match": "\\b(0[bB][01][01_]*)\\b", + "match": "\\b0[xX]([0-9a-fA-F][0-9a-fA-F_]*)\\.([Pp](\\+|-)?([0-9a-fA-F][0-9a-fA-F_]*))i?\\b", "name": "constant.numeric.chapel" }, { - "match": "\\b(0[oO][0-7][0-7_]*)\\b", + "match": "\\b0[xX]([0-9a-fA-F][0-9a-fA-F_]*)([Pp](\\+|-)?([0-9a-fA-F][0-9a-fA-F_]*))i?\\b", "name": "constant.numeric.chapel" } ] From 4c53afae98cc45db0ae4bb12cbb6e651dfd704b6 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 8 May 2024 17:18:50 -0700 Subject: [PATCH 08/13] fix count operator Signed-off-by: Jade Abraham --- syntaxes/chapel.tmLanguage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntaxes/chapel.tmLanguage.json b/syntaxes/chapel.tmLanguage.json index 768a808..26c9a2d 100644 --- a/syntaxes/chapel.tmLanguage.json +++ b/syntaxes/chapel.tmLanguage.json @@ -57,7 +57,7 @@ "name": "keyword.operator.assignment.augmented.chapel" }, { - "match": "\\+|-|\\*|\\*\\*|/|%|<<|>>|&|(\\|)|\\^|~|<=>|\\.\\.<|\\.\\.#|\\.\\.\\.|\\.\\.", + "match": "\\+|-|\\*|\\*\\*|/|%|<<|>>|&|(\\|)|\\^|~|<=>|\\.\\.<|#|\\.\\.\\.|\\.\\.", "name": "keyword.operator.arithmetic.chapel" }, { From dc42c825a215a2ee582595faaf98e271cb630410 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 22 May 2024 15:24:14 -0700 Subject: [PATCH 09/13] reduce reliance on CHPL_HOME and allow prefix installs Signed-off-by: Jade Abraham --- package.json | 21 ++++--- src/ChapelLanguageClient.ts | 72 ++++++++++++++--------- src/ChplPaths.ts | 114 +++++++++++++++++++++++++++--------- src/configuration.ts | 7 ++- src/extension.ts | 66 ++++++++------------- 5 files changed, 171 insertions(+), 109 deletions(-) diff --git a/package.json b/package.json index 51dbcd0..5c2c4c6 100644 --- a/package.json +++ b/package.json @@ -82,13 +82,8 @@ ], "commands": [ { - "command": "chapel.findChpl", - "title": "Find Chapel", - "category": "chapel" - }, - { - "command": "chapel.buildTools", - "title": "Build chpl-language-server and chplcheck", + "command": "chapel.findChplHome", + "title": "Find CHPL_HOME", "category": "chapel" }, { @@ -114,7 +109,7 @@ "chapel.CHPL_HOME": { "scope": "window", "type": "string", - "description": "CHPL_HOME" + "description": "The path to CHPL_HOME. If not provided, the extension may be able to find it automatically if `chpl`, `chplcheck`, and `chpl-language-server` are in PATH." }, "chapel.CHPL_DEVELOPER": { "scope": "window", @@ -128,6 +123,11 @@ "default": true, "description": "Enable chplcheck" }, + "chapel.chplcheck.path": { + "scope": "window", + "type": "string", + "description": "An explicit path to the chplcheck executable. If not provided, the extension will look for chplcheck in PATH and in CHPL_HOME" + }, "chapel.chplcheck.args": { "scope": "window", "type": "array", @@ -143,6 +143,11 @@ "default": true, "description": "Enable chpl-language-server" }, + "chapel.chpl-language-server.path": { + "scope": "window", + "type": "string", + "description": "An explicit path to the chpl-language-server executable. If not provided, the extension will look for chpl-language-server in PATH and in CHPL_HOME" + }, "chapel.chpl-language-server.resolver": { "scope": "window", "type": "boolean", diff --git a/src/ChapelLanguageClient.ts b/src/ChapelLanguageClient.ts index 61b3520..49f7404 100644 --- a/src/ChapelLanguageClient.ts +++ b/src/ChapelLanguageClient.ts @@ -17,11 +17,19 @@ * limitations under the License. */ -import { ToolConfig, getChplDeveloper } from "./configuration"; +import { ToolConfig, getChplDeveloper, getChplHome } from "./configuration"; import * as fs from "fs"; import * as vscode from "vscode"; import * as vlc from "vscode-languageclient/node"; -import { checkToolPath, checkChplHome, cloneEnv } from "./ChplPaths"; +import { + checkToolPath, + checkChplHome, + cloneEnv, + findToolPath, + findPossibleChplHomes, + getWorkspaceFolder, +} from "./ChplPaths"; +import {showChplHomeMissingError} from "./extension"; import * as path from "path"; export enum LanguageClientState { @@ -62,7 +70,6 @@ class ErrorHandlingClient extends vlc.LanguageClient { } export abstract class ChapelLanguageClient { - chplhome: string; protected config_: ToolConfig; name: string; state: LanguageClientState; @@ -71,12 +78,10 @@ export abstract class ChapelLanguageClient { logger: vscode.LogOutputChannel; constructor( - chplhome: string, config: ToolConfig, name: string, logger: vscode.LogOutputChannel ) { - this.chplhome = chplhome; this.config_ = config; this.name = name; this.state = this.config_.enable @@ -92,9 +97,8 @@ export abstract class ChapelLanguageClient { get config(): ToolConfig { return this.config_; } - async resetConfig(chplhome: string, config: ToolConfig) { + async resetConfig(config: ToolConfig) { await this.stop(); - this.chplhome = chplhome; this.config_ = config; this.state = this.config_.enable ? LanguageClientState.STOPPED @@ -116,17 +120,20 @@ export abstract class ChapelLanguageClient { // if missing tool path, warn user that we can't find it, tell them to not override the path or upgrade their chapel version // otherwise, its likely the tools arent built, so prompt the user to build them - if (checkChplHome(this.chplhome) !== undefined) { + if (this.tool_path === "") { vscode.window .showErrorMessage( - "CHPL_HOME is either missing or incorrect, make sure the path is correct", - "Find CHPL_HOME", + `Could not determine the path for ${this.name}. Make sure it is installed. If it is, you may need to set the path manually.`, + `Set ${this.name} Path`, + "Set CHPL_HOME", "Show Log", "Ok" ) .then((value) => { - if (value === "Find CHPL_HOME") { - vscode.commands.executeCommand("chapel.findChpl"); + if (value === `Set ${this.name} Path`) { + // TODO + } else if (value === "Set CHPL_HOME") { + vscode.commands.executeCommand("chapel.findChplHome"); } else if (value === "Show Log") { this.logger.show(); } @@ -134,7 +141,7 @@ export abstract class ChapelLanguageClient { } else if (checkToolPath(this.tool_path) !== undefined) { vscode.window .showErrorMessage( - `${this.name} does not exist in the CHPL_HOME directory, make sure you are using the correct version of Chapel`, + `${this.name} is missing at the path '${this.tool_path}'. If you set the path manually, make sure it is correct. If it is, you may need to upgrade your Chapel version.`, "Show Log", "Ok" ) @@ -146,15 +153,12 @@ export abstract class ChapelLanguageClient { } else { vscode.window .showErrorMessage( - `${this.name} encountered an error, this is likely because ${this.name} is not installed. Double check that ${this.name} is built.`, - "Build Tools", + `${this.name} encountered an error. You may need to rebuild ${this.name}.`, "Show Log", "Ok" ) .then((value) => { - if (value === "Build Tools") { - vscode.commands.executeCommand(`chapel.buildTools`, this.chplhome); - } else if (value === "Show Log") { + if (value === "Show Log") { this.logger.show(); } }); @@ -177,7 +181,16 @@ export abstract class ChapelLanguageClient { } let env = cloneEnv(); - env.CHPL_HOME = this.chplhome; + const chplhome = getChplHome(); + if (chplhome !== undefined && chplhome !== "") { + this.logger.info(`Using CHPL_HOME: ${chplhome}`); + const chplHomeError = checkChplHome(chplhome); + if (chplHomeError !== undefined) { + showChplHomeMissingError(chplHomeError); + } else { + env.CHPL_HOME = chplhome; + } + } env.CHPL_DEVELOPER = getChplDeveloper() ? "1" : "0"; let args = this.alwaysArguments(); @@ -190,7 +203,7 @@ export abstract class ChapelLanguageClient { command: this.tool_path, args: args, options: { - cwd: this.chplhome, + cwd: getWorkspaceFolder()?.uri.fsPath, env: env, }, }; @@ -337,7 +350,12 @@ export abstract class ChapelLanguageClient { export class ChplCheckClient extends ChapelLanguageClient { getToolPath(): string { - return path.join(this.chplhome, "tools", "chplcheck", "chplcheck"); + const path = findToolPath("chplcheck", getChplHome()); + if (path === undefined) { + this.setErrorState(); + return ""; + } + return path; } alwaysArguments(): Array { return ["--lsp"]; @@ -345,12 +363,12 @@ export class ChplCheckClient extends ChapelLanguageClient { } export class CLSClient extends ChapelLanguageClient { getToolPath(): string { - return path.join( - this.chplhome, - "tools", - "chpl-language-server", - "chpl-language-server" - ); + const path = findToolPath("chpl-language-server", getChplHome()); + if (path === undefined) { + this.setErrorState(); + return ""; + } + return path; } alwaysArguments(): Array { let args = []; diff --git a/src/ChplPaths.ts b/src/ChplPaths.ts index 433d9b4..db84949 100644 --- a/src/ChplPaths.ts +++ b/src/ChplPaths.ts @@ -20,6 +20,8 @@ import * as path from "path"; import * as fs from "fs"; import * as vscode from "vscode"; +import * as cfg from "./configuration"; +import assert from "assert"; export function checkChplHome( chplhome: string | undefined @@ -62,6 +64,22 @@ export function checkChplHome( return undefined; } +// take a callback to be called on each file found +// if the callback returns true, stop searching +function searchPATH(file: string, callback: (file_path: string) => boolean | void) { + const PATH = process.env["PATH"]; + const paths_to_check = PATH?.split(path.delimiter) ?? []; + for (const p of paths_to_check) { + const file_path = path.join(p, file); + if (fs.existsSync(file_path) && fs.statSync(file_path).isFile()) { + const r = callback(file_path); + if (r !== undefined && r === true) { + break; + } + } + } +} + export function checkToolPath(tool_path: string): string | undefined { if (!fs.existsSync(tool_path) || !fs.statSync(tool_path).isFile()) { return `${tool_path} does not exist`; @@ -69,6 +87,40 @@ export function checkToolPath(tool_path: string): string | undefined { return undefined; } +export function findToolPath( + tool_name: string, + chplhome: string | undefined +): string | undefined { + assert(tool_name === "chplcheck" || tool_name === "chpl-language-server"); + + // 1. if there is a path in config, use that. + // 2. if there is a chplhome, use that + // 3. otherwise, search PATH + + const cfg_tool_path = tool_name === "chplcheck" ? cfg.getChplCheckConfig().path : cfg.getCLSConfig().path; + if (cfg_tool_path !== undefined) { + const error = checkToolPath(cfg_tool_path); + if (error === undefined) { + return cfg_tool_path; + } + } + + if (chplhome !== undefined && checkChplHome(chplhome) === undefined) { + const tool_path = path.join(chplhome, "tools", tool_name, tool_name); + const error = checkToolPath(tool_path); + if (error === undefined) { + return tool_path; + } + } + + let tool_path: string | undefined = undefined; + searchPATH(tool_name, (file_path) => { + tool_path = file_path; + return true; + }); + return tool_path; +} + function searchDirectoryForChplHome(dir: string, depth: number = 1): string[] { let chplhomes: string[] = []; if (depth > 0) { @@ -102,20 +154,15 @@ export function findPossibleChplHomes(): string[] { // but we cannot execute shell commands and get their result with the vscode api // as a best effort, we find chpl in the PATH and check if chplhome is the parent directory - const PATH = process.env["PATH"]; - const paths_to_check = PATH?.split(path.delimiter) ?? []; - for (const p of paths_to_check) { - const chpl_path = path.join(p, "chpl"); - if (fs.existsSync(chpl_path) && fs.statSync(chpl_path).isFile()) { - const chplhome = path.dirname(path.dirname(chpl_path)); - if ( - checkChplHome(chplhome) === undefined && - possibleChplHomes.indexOf(chplhome) === -1 - ) { - possibleChplHomes.push(chplhome); - } + searchPATH("chpl", (chpl_path) => { + const chplhome = path.dirname(path.dirname(chpl_path)); + if ( + checkChplHome(chplhome) === undefined && + possibleChplHomes.indexOf(chplhome) === -1 + ) { + possibleChplHomes.push(chplhome); } - } + }); // as a best effort, we can also check `/opt` and `/opt/homebrew/Cellar/chapel` for chplhome, searching to a depth of 3 const dirs_to_check = ["/opt", "/opt/homebrew/Cellar/chapel/"]; for (const dir of dirs_to_check) { @@ -134,20 +181,29 @@ export function cloneEnv() { return env; } -export function buildTools(chplhome: string) { - let env = cloneEnv(); - const term = vscode.window.createTerminal({ cwd: chplhome, env: env }); - term.sendText(`make chpl-language-server || exit 1 && make chplcheck || exit 1 && exit 0`); - term.show(); - vscode.window.onDidChangeTerminalState((e) => { - if (e === term && e.exitStatus !== undefined) { - if (e.exitStatus.code === 0) { - vscode.window.showInformationMessage("Build complete"); - vscode.commands.executeCommand("chplcheck.restart"); - vscode.commands.executeCommand("chpl-language-server.restart"); - } else { - vscode.window.showWarningMessage(`Build failed, try running 'export CHPL_HOME=${chplhome} && make chpl-language-server && make chplcheck' in the CHPL_HOME directory to see the error message.`); - } - } - }) +// export function buildTools(chplhome: string) { +// let env = cloneEnv(); +// const term = vscode.window.createTerminal({ cwd: chplhome, env: env }); +// term.sendText(`make chpl-language-server || exit 1 && make chplcheck || exit 1 && exit 0`); +// term.show(); +// vscode.window.onDidChangeTerminalState((e) => { +// if (e === term && e.exitStatus !== undefined) { +// if (e.exitStatus.code === 0) { +// vscode.window.showInformationMessage("Build complete"); +// vscode.commands.executeCommand("chplcheck.restart"); +// vscode.commands.executeCommand("chpl-language-server.restart"); +// } else { +// vscode.window.showWarningMessage(`Build failed, try running 'export CHPL_HOME=${chplhome} && make chpl-language-server && make chplcheck' in the CHPL_HOME directory to see the error message.`); +// } +// } +// }) +// } + +export function getWorkspaceFolder(): vscode.WorkspaceFolder | undefined { + const editor = vscode.window.activeTextEditor; + if (editor === undefined) { + return undefined; + } + const doc = editor.document; + return vscode.workspace.getWorkspaceFolder(doc.uri); } diff --git a/src/configuration.ts b/src/configuration.ts index 1317dd3..7827cd9 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -22,8 +22,9 @@ import * as vscode from "vscode"; export interface ToolConfig { args: Array; enable: boolean; + path: string | undefined; } -const ToolConfigDefault: ToolConfig = {args: [], enable: false}; +const ToolConfigDefault: ToolConfig = {args: [], enable: false, path: undefined}; export type ChplCheckConfig = ToolConfig; const ChplCheckConfigDefault: ChplCheckConfig = {...ToolConfigDefault }; @@ -35,10 +36,10 @@ const CLSConfigDefault: CLSConfig = {...ToolConfigDefault, resolver: false}; const configScope = "chapel"; -export function getChplHome(): string { +export function getChplHome(): string | undefined { const config = vscode.workspace.getConfiguration(configScope); const chplhome = config.get("CHPL_HOME"); - return chplhome ?? ""; + return chplhome ?? undefined; } export function setChplHome(chplhome: string) { const config = vscode.workspace.getConfiguration(configScope); diff --git a/src/extension.ts b/src/extension.ts index ea23ff0..fe3556c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,26 +25,26 @@ import { setChplHome, } from "./configuration"; import { ChplCheckClient, CLSClient } from "./ChapelLanguageClient"; -import { buildTools, checkChplHome, findPossibleChplHomes } from "./ChplPaths"; +import { checkChplHome, findPossibleChplHomes } from "./ChplPaths"; let chplcheckClient: ChplCheckClient; let clsClient: CLSClient; let logger: vscode.LogOutputChannel; -function showChplHomeMissingError(errorString?: string) { +export function showChplHomeMissingError(errorString?: string) { if (errorString) { logger.error(errorString); } vscode.window .showErrorMessage( - "CHPL_HOME is either missing or incorrect, make sure the path is correct", + "CHPL_HOME is incorrect, make sure the path is correct", "Find CHPL_HOME", "Show Log", "Ok" ) .then((value) => { if (value === "Find CHPL_HOME") { - vscode.commands.executeCommand("chapel.findChpl"); + vscode.commands.executeCommand("chapel.findChplHome"); } else if (value === "Show Log") { logger.show(); } @@ -84,7 +84,7 @@ export function activate(context: vscode.ExtensionContext) { logger.info("Chapel extension activated"); context.subscriptions.push( - vscode.commands.registerCommand("chapel.findChpl", async () => { + vscode.commands.registerCommand("chapel.findChplHome", async () => { // show a selection to let the user select the proper CHPL_HOME let choices = findPossibleChplHomes(); if (choices.length === 0) { @@ -94,48 +94,35 @@ export function activate(context: vscode.ExtensionContext) { } }) ); - context.subscriptions.push( - vscode.commands.registerCommand( - "chapel.buildTools", - async (chplhome?: string) => { - if (chplhome === undefined) { - chplhome = getChplHome(); - } - if (checkChplHome(chplhome) === undefined) { - buildTools(chplhome); - } else { - vscode.window.showWarningMessage( - `Unable to build automatically, please build manually` - ); - } - } - ) - ); - - const chplhome = getChplHome(); - const chplHomeError = checkChplHome(chplhome); - if (chplHomeError !== undefined) { - showChplHomeMissingError(chplHomeError); - } + // context.subscriptions.push( + // vscode.commands.registerCommand( + // "chapel.buildTools", + // async (chplhome?: string) => { + // if (chplhome === undefined) { + // chplhome = getChplHome(); + // } + // if (checkChplHome(chplhome) === undefined) { + // buildTools(chplhome); + // } else { + // vscode.window.showWarningMessage( + // `Unable to build automatically, please build manually` + // ); + // } + // } + // ) + // ); chplcheckClient = new ChplCheckClient( - chplhome, getChplCheckConfig(), "chplcheck", logger ); clsClient = new CLSClient( - chplhome, getCLSConfig(), "chpl-language-server", logger ); - if (chplHomeError !== undefined) { - chplcheckClient.setErrorState(); - clsClient.setErrorState(); - } - // Restart language server command context.subscriptions.push( vscode.commands.registerCommand("chplcheck.restart", async () => { @@ -155,14 +142,9 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(async (e) => { if (e.affectsConfiguration("chapel")) { - const chplhome = getChplHome(); - const chplHomeError = checkChplHome(chplhome); - if (chplHomeError !== undefined) { - showChplHomeMissingError(chplHomeError); - } Promise.all([ - chplcheckClient.resetConfig(chplhome, getChplCheckConfig()), - clsClient.resetConfig(chplhome, getCLSConfig()), + chplcheckClient.resetConfig(getChplCheckConfig()), + clsClient.resetConfig(getCLSConfig()), ]); } }) From b87b591386d2697f39b9684ea762befaba81e1fb Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 22 May 2024 17:19:26 -0700 Subject: [PATCH 10/13] improve log message Signed-off-by: Jade Abraham --- src/ChplPaths.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ChplPaths.ts b/src/ChplPaths.ts index db84949..82f34c7 100644 --- a/src/ChplPaths.ts +++ b/src/ChplPaths.ts @@ -81,6 +81,9 @@ function searchPATH(file: string, callback: (file_path: string) => boolean | voi } export function checkToolPath(tool_path: string): string | undefined { + if (tool_path === "") { + return "Path is empty"; + } if (!fs.existsSync(tool_path) || !fs.statSync(tool_path).isFile()) { return `${tool_path} does not exist`; } From 70b2e9374b651e82a742655657d45ca80f4e96a5 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 22 May 2024 17:21:09 -0700 Subject: [PATCH 11/13] remove build tools Signed-off-by: Jade Abraham --- src/ChplPaths.ts | 18 ------------------ src/extension.ts | 17 ----------------- 2 files changed, 35 deletions(-) diff --git a/src/ChplPaths.ts b/src/ChplPaths.ts index 82f34c7..5f88f23 100644 --- a/src/ChplPaths.ts +++ b/src/ChplPaths.ts @@ -184,24 +184,6 @@ export function cloneEnv() { return env; } -// export function buildTools(chplhome: string) { -// let env = cloneEnv(); -// const term = vscode.window.createTerminal({ cwd: chplhome, env: env }); -// term.sendText(`make chpl-language-server || exit 1 && make chplcheck || exit 1 && exit 0`); -// term.show(); -// vscode.window.onDidChangeTerminalState((e) => { -// if (e === term && e.exitStatus !== undefined) { -// if (e.exitStatus.code === 0) { -// vscode.window.showInformationMessage("Build complete"); -// vscode.commands.executeCommand("chplcheck.restart"); -// vscode.commands.executeCommand("chpl-language-server.restart"); -// } else { -// vscode.window.showWarningMessage(`Build failed, try running 'export CHPL_HOME=${chplhome} && make chpl-language-server && make chplcheck' in the CHPL_HOME directory to see the error message.`); -// } -// } -// }) -// } - export function getWorkspaceFolder(): vscode.WorkspaceFolder | undefined { const editor = vscode.window.activeTextEditor; if (editor === undefined) { diff --git a/src/extension.ts b/src/extension.ts index fe3556c..37f6732 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -94,23 +94,6 @@ export function activate(context: vscode.ExtensionContext) { } }) ); - // context.subscriptions.push( - // vscode.commands.registerCommand( - // "chapel.buildTools", - // async (chplhome?: string) => { - // if (chplhome === undefined) { - // chplhome = getChplHome(); - // } - // if (checkChplHome(chplhome) === undefined) { - // buildTools(chplhome); - // } else { - // vscode.window.showWarningMessage( - // `Unable to build automatically, please build manually` - // ); - // } - // } - // ) - // ); chplcheckClient = new ChplCheckClient( getChplCheckConfig(), From abf17ae59e22cb808bb1aad9af3370a852f251c3 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 22 May 2024 17:55:03 -0700 Subject: [PATCH 12/13] improve error handling and add status bar icons Signed-off-by: Jade Abraham --- src/ChapelLanguageClient.ts | 24 +++++++++++++++++++++--- src/ChplPaths.ts | 20 ++++++++++++-------- src/configuration.ts | 15 ++++++++++----- src/extension.ts | 30 ++++++++++++++++++++++++------ 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/ChapelLanguageClient.ts b/src/ChapelLanguageClient.ts index 49f7404..8f0c8aa 100644 --- a/src/ChapelLanguageClient.ts +++ b/src/ChapelLanguageClient.ts @@ -29,7 +29,7 @@ import { findPossibleChplHomes, getWorkspaceFolder, } from "./ChplPaths"; -import {showChplHomeMissingError} from "./extension"; +import { showChplHomeMissingError } from "./extension"; import * as path from "path"; export enum LanguageClientState { @@ -76,6 +76,7 @@ export abstract class ChapelLanguageClient { tool_path: string; client: ErrorHandlingClient | undefined; logger: vscode.LogOutputChannel; + statusBarItem: vscode.StatusBarItem; constructor( config: ToolConfig, @@ -90,6 +91,21 @@ export abstract class ChapelLanguageClient { this.tool_path = this.getToolPath(); this.client = undefined; this.logger = logger; + this.statusBarItem = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Right, + 1000 + ); + // render the text using vscode codicons + this.statusBarItem.text = `$(error) ${this.name}`; + this.statusBarItem.tooltip = `${this.name} is stopped. Click to restart.`; + this.statusBarItem.color = new vscode.ThemeColor( + "statusBarItem.errorForeground" + ); + this.statusBarItem.backgroundColor = new vscode.ThemeColor( + "statusBarItem.errorBackground" + ); + this.statusBarItem.command = `${this.name}.restart`; + this.statusBarItem.hide(); } protected abstract getToolPath(): string; @@ -109,6 +125,7 @@ export abstract class ChapelLanguageClient { setErrorState() { this.state = LanguageClientState.ERRORED; + this.statusBarItem.show(); } clearError(): void { @@ -116,6 +133,7 @@ export abstract class ChapelLanguageClient { } private errorFindTools() { + this.setErrorState(); // if invalid chplhome, prompt user to set it // if missing tool path, warn user that we can't find it, tell them to not override the path or upgrade their chapel version // otherwise, its likely the tools arent built, so prompt the user to build them @@ -172,11 +190,11 @@ export abstract class ChapelLanguageClient { return Promise.resolve(); } this.state = LanguageClientState.STARTING; + this.statusBarItem.hide(); let toolPathError = checkToolPath(this.tool_path); if (toolPathError !== undefined) { this.logger.error(toolPathError); this.errorFindTools(); - this.state = LanguageClientState.STOPPED; return Promise.reject(); } @@ -251,7 +269,7 @@ export abstract class ChapelLanguageClient { } this.stop().finally(() => { - this.state = LanguageClientState.ERRORED; + this.setErrorState(); vscode.window .showErrorMessage( diff --git a/src/ChplPaths.ts b/src/ChplPaths.ts index 5f88f23..0ca76b6 100644 --- a/src/ChplPaths.ts +++ b/src/ChplPaths.ts @@ -21,6 +21,7 @@ import * as path from "path"; import * as fs from "fs"; import * as vscode from "vscode"; import * as cfg from "./configuration"; +import { showInvalidPathWarning } from "./extension"; import assert from "assert"; export function checkChplHome( @@ -66,7 +67,10 @@ export function checkChplHome( // take a callback to be called on each file found // if the callback returns true, stop searching -function searchPATH(file: string, callback: (file_path: string) => boolean | void) { +function searchPATH( + file: string, + callback: (file_path: string) => boolean | void +) { const PATH = process.env["PATH"]; const paths_to_check = PATH?.split(path.delimiter) ?? []; for (const p of paths_to_check) { @@ -100,12 +104,12 @@ export function findToolPath( // 2. if there is a chplhome, use that // 3. otherwise, search PATH - const cfg_tool_path = tool_name === "chplcheck" ? cfg.getChplCheckConfig().path : cfg.getCLSConfig().path; - if (cfg_tool_path !== undefined) { - const error = checkToolPath(cfg_tool_path); - if (error === undefined) { - return cfg_tool_path; - } + const cfg_tool_path = + tool_name === "chplcheck" + ? cfg.getChplCheckConfig().path + : cfg.getCLSConfig().path; + if (cfg_tool_path !== undefined && cfg_tool_path !== "") { + return cfg_tool_path; } if (chplhome !== undefined && checkChplHome(chplhome) === undefined) { @@ -133,7 +137,7 @@ function searchDirectoryForChplHome(dir: string, depth: number = 1): string[] { try { // sadly this is the only way synchronous way to check if the directory is readable fs.accessSync(dir, fs.constants.R_OK); - } catch(err) { + } catch (err) { return chplhomes; } fs.readdirSync(dir).forEach((subdir) => { diff --git a/src/configuration.ts b/src/configuration.ts index 7827cd9..742d826 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -24,15 +24,19 @@ export interface ToolConfig { enable: boolean; path: string | undefined; } -const ToolConfigDefault: ToolConfig = {args: [], enable: false, path: undefined}; +const ToolConfigDefault: ToolConfig = { + args: [], + enable: false, + path: undefined, +}; export type ChplCheckConfig = ToolConfig; -const ChplCheckConfigDefault: ChplCheckConfig = {...ToolConfigDefault }; +const ChplCheckConfigDefault: ChplCheckConfig = { ...ToolConfigDefault }; export interface CLSConfig extends ToolConfig { resolver: boolean; } -const CLSConfigDefault: CLSConfig = {...ToolConfigDefault, resolver: false}; +const CLSConfigDefault: CLSConfig = { ...ToolConfigDefault, resolver: false }; const configScope = "chapel"; @@ -50,7 +54,7 @@ export function setChplHome(chplhome: string) { // global local settings over the global remote settings. This is a known // issue with the vscode API: // https://github.com/microsoft/vscode/issues/182696 - if(config.inspect("CHPL_HOME")?.workspaceValue !== undefined) { + if (config.inspect("CHPL_HOME")?.workspaceValue !== undefined) { config.update("CHPL_HOME", chplhome, vscode.ConfigurationTarget.Workspace); } else { config.update("CHPL_HOME", chplhome, vscode.ConfigurationTarget.Global); @@ -64,7 +68,8 @@ export function getChplDeveloper(): boolean { export function getChplCheckConfig(): ChplCheckConfig { const config = vscode.workspace.getConfiguration(configScope); - const chplcheck = config.get("chplcheck") ?? ChplCheckConfigDefault; + const chplcheck = + config.get("chplcheck") ?? ChplCheckConfigDefault; return chplcheck; } diff --git a/src/extension.ts b/src/extension.ts index 37f6732..e4f045b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,6 +31,27 @@ let chplcheckClient: ChplCheckClient; let clsClient: CLSClient; let logger: vscode.LogOutputChannel; +export function showInvalidPathWarning( + tool: string, + path: string, + errorString?: string +) { + if (errorString) { + logger.warn(errorString); + } + + let msg = `The path '${path}' for ${tool} is invalid, errors may occur. Please double check that this is the correct path.`; + if (path === "") { + msg = `The path for ${tool} is not set, errors may occur. Please either set the path or remove the empty path.`; + } + + vscode.window.showWarningMessage(msg, "Show Log", "Ok").then((value) => { + if (value === "Show Log") { + logger.show(); + } + }); +} + export function showChplHomeMissingError(errorString?: string) { if (errorString) { logger.error(errorString); @@ -54,7 +75,8 @@ export function showChplHomeMissingError(errorString?: string) { function pickMyOwnChplHome() { vscode.window .showInputBox({ - placeHolder: "Enter the path to CHPL_HOME (possibly run `chpl --print-chpl-home` in the terminal to find it)", + placeHolder: + "Enter the path to CHPL_HOME (possibly run `chpl --print-chpl-home` in the terminal to find it)", }) .then((selection) => { if (selection !== undefined) { @@ -100,11 +122,7 @@ export function activate(context: vscode.ExtensionContext) { "chplcheck", logger ); - clsClient = new CLSClient( - getCLSConfig(), - "chpl-language-server", - logger - ); + clsClient = new CLSClient(getCLSConfig(), "chpl-language-server", logger); // Restart language server command context.subscriptions.push( From 78cf1c129050cfd204375b051907bacd1046c6bc Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Thu, 23 May 2024 08:53:51 -0700 Subject: [PATCH 13/13] fix lock file Signed-off-by: Jade Abraham --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11e44b5..c4ca20c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chapel-vscode", - "version": "0.0.1", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chapel-vscode", - "version": "0.0.1", + "version": "0.0.3", "license": "SEE LICENSE IN LICENSE", "dependencies": { "vscode-languageclient": "^8.0.2"