From abf17ae59e22cb808bb1aad9af3370a852f251c3 Mon Sep 17 00:00:00 2001 From: Jade Abraham Date: Wed, 22 May 2024 17:55:03 -0700 Subject: [PATCH] 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(