From a3db4f236ffd622c49c971063e4433db3e9ab223 Mon Sep 17 00:00:00 2001 From: gavinleroy Date: Wed, 10 Jul 2024 18:13:26 -0600 Subject: [PATCH] Quiet the IDE errors a bit. --- flake.nix | 11 +- ide/packages/extension/package.json | 5 +- ide/packages/extension/src/commands.ts | 2 +- ide/packages/extension/src/ctx.ts | 149 +++++++++++++------------ ide/packages/extension/src/errors.ts | 4 +- ide/packages/extension/src/main.ts | 15 ++- ide/packages/extension/src/setup.ts | 13 ++- ide/packages/extension/src/view.ts | 4 +- 8 files changed, 117 insertions(+), 86 deletions(-) diff --git a/flake.nix b/flake.nix index ba02225..ad9aafe 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,6 @@ pkgs = import nixpkgs { inherit system overlays; }; - toolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; depotjs = pkgs.rustPlatform.buildRustPackage rec { pname = "depot"; @@ -37,16 +36,24 @@ in { devShell = with pkgs; mkShell { buildInputs = [ + # Deployment only + vsce + cargo-workspaces + + llvmPackages_latest.llvm + llvmPackages_latest.lld guile + depotjs nodejs_22 nodePackages.pnpm - depotjs cargo-make cargo-watch toolchain ] ++ lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.SystemConfiguration ]; + + RUSTC_LINKER = "${pkgs.llvmPackages.clangUseLLVM}/bin/clang"; }; }); } diff --git a/ide/packages/extension/package.json b/ide/packages/extension/package.json index 92c0588..2e1d990 100644 --- a/ide/packages/extension/package.json +++ b/ide/packages/extension/package.json @@ -56,7 +56,10 @@ ] }, "files": [ - "dist" + "dist", + "argus-logo-128.png", + "LICENSE", + "README.md" ], "main": "./dist/argus.js", "exports": { diff --git a/ide/packages/extension/src/commands.ts b/ide/packages/extension/src/commands.ts index 5108fe0..3afaba3 100644 --- a/ide/packages/extension/src/commands.ts +++ b/ide/packages/extension/src/commands.ts @@ -57,6 +57,6 @@ export function openError(ctx: Ctx): Cmd { export function lastError(ctx: Ctx): Cmd { return async () => { trace("lastError"); - return errors.lastError(ctx.extCtx); + return errors.lastError(ctx.ictxt.extCtx); }; } diff --git a/ide/packages/extension/src/ctx.ts b/ide/packages/extension/src/ctx.ts index de28699..d41993f 100644 --- a/ide/packages/extension/src/ctx.ts +++ b/ide/packages/extension/src/ctx.ts @@ -13,14 +13,13 @@ import type { FileInfo, Filename } from "@argus/common/lib"; -import { CancelablePromise as CPromise } from "cancelable-promise"; +import type { CancelablePromise as CPromise } from "cancelable-promise"; import _ from "lodash"; import * as vscode from "vscode"; import { rangeHighlight } from "./decorate"; import { showErrorDialog } from "./errors"; import { log } from "./logging"; -import { setup } from "./setup"; import { StatusBar } from "./statusbar"; import { type RustEditor, @@ -71,109 +70,84 @@ export interface Disposable { export type Cmd = (...args: any[]) => unknown; export type CommandFactory = { - enabled: (ctx: CtxInit) => Cmd; + enabled: (ctx: Ctx) => Cmd; // disabled?: (ctx: Ctx) => Cmd; }; -// We can modify this if the initializations state changes. -export type CtxInit = Ctx; - -export class Ctx { - // Ranges used for highlighting code in the current Rust Editor. - private highlightRanges: CharRange[] = []; - private commandDisposables: Disposable[]; - private disposables: Disposable[]; - private diagnosticCollection; - private cache: BackendCache; - private view: View | undefined; - public statusBar: StatusBar; +/** + * Extension context before a system backend has been set. + */ +export class CtxInit { + readonly statusBar: StatusBar; constructor( readonly extCtx: vscode.ExtensionContext, - readonly commandFactories: Record, readonly workspace: Workspace & { kind: "workspace-folder" } ) { - this.commandDisposables = []; - this.disposables = []; - this.diagnosticCollection = - vscode.languages.createDiagnosticCollection("rust"); this.statusBar = new StatusBar(extCtx); - - this.cache = new BackendCache( - this.statusBar, - () => - new CPromise(() => { - showErrorDialog("Argus backend left uninitialized."); - return { - type: "analysis-error", - error: "Argus uninitialized." - }; - }) - ); } dispose() { - this.cache.havoc(); - this.view?.cleanup(); this.statusBar.dispose(); - _.forEach(this.commandDisposables, d => d.dispose()); - _.forEach(this.disposables, d => d.dispose()); - this.commandDisposables = []; - this.disposables = []; - } - - get activeRustEditor(): RustEditor | undefined { - const editor = vscode.window.activeTextEditor; - return editor && isRustEditor(editor) ? editor : undefined; } +} - get extensionPath(): string { - return this.extCtx.extensionPath; - } +export class Ctx { + private view?: View; + private highlightRanges: CharRange[] = []; + private commandDisposables: Disposable[] = []; + private disposables: Disposable[] = []; - get visibleEditors(): RustEditor[] { - return _.filter(vscode.window.visibleTextEditors, isRustEditor); - } + private constructor( + readonly ictxt: CtxInit, + readonly cache: BackendCache, + readonly commandFactories: Record, - // NOTE: callbacks that register events in this function should invoke - // actions on the `globals.ctx` instead of `this` to avoid issues with - // setup. Previously the setup failed but callbacks were still registered - // with `this` which caused the editor to be out of sync. - // FIXME: this probably demonstrates a flaw in the design anyways... - async setupBackend() { - log("setting up Argus backend"); - const b = await setup(this); - if (b == null) { - throw new Error("Failed to setup Argus"); - } + // Ranges used for highlighting code in the current Rust Editor. + readonly diagnosticCollection: vscode.DiagnosticCollection + ) {} + static async make( + initial: CtxInit, + backend: CallArgus, + commandFactories: Record + ) { vscode.window.showInformationMessage( "Loading Argus, this may take several minutes." ); - await b( + await backend( ["preload"], true, // Don't expect output true // Ignore a failing exit code ); - this.cache = new BackendCache(this.statusBar, b); - log("Argus backend preloaded"); + const self = new Ctx( + initial, + new BackendCache(initial.statusBar, backend), + commandFactories, + vscode.languages.createDiagnosticCollection("rust") + ); // Preload information for visible editors. - if (this.activeRustEditor) { - this.visibleEditors.forEach(e => this.openEditor(e)); + if (self.activeRustEditor) { + self.visibleEditors.forEach(e => self.openEditor(e)); } log("Caching open editor information"); // Register the commands with VSCode after the backend is setup. // NOTE: nothing should be registered before the commands... - this.updateCommands(); + self.updateCommands(); log("Updated commands"); + self.registerSubscriptionsAndDisposables(); + + return self; + } - this.extCtx.subscriptions.push(this.diagnosticCollection); + private registerSubscriptionsAndDisposables() { + this.ictxt.extCtx.subscriptions.push(this.diagnosticCollection!); this.disposables.push( vscode.languages.registerHoverProvider("rust", { provideHover: async (doc, pos, tok) => this.provideHover(doc, pos, tok) @@ -190,7 +164,7 @@ export class Ctx { event.document === editor.document && editor.document.isDirty ) { - this.statusBar.setState("unsaved"); + this.ictxt.statusBar.setState("unsaved"); } }) ); @@ -229,6 +203,38 @@ export class Ctx { log("Argus backend setup complete"); } + dispose() { + this.cache.havoc(); + this.view?.cleanup(); + this.ictxt.dispose(); + _.forEach(this.commandDisposables, d => d.dispose()); + _.forEach(this.disposables, d => d.dispose()); + this.commandDisposables = []; + this.disposables = []; + } + + get activeRustEditor(): RustEditor | undefined { + const editor = vscode.window.activeTextEditor; + return editor && isRustEditor(editor) ? editor : undefined; + } + + get extensionPath(): string { + return this.ictxt.extCtx.extensionPath; + } + + get visibleEditors(): RustEditor[] { + return _.filter(vscode.window.visibleTextEditors, isRustEditor); + } + + // NOTE: callbacks that register events in this function should invoke + // actions on the `globals.ctx` instead of `this` to avoid issues with + // setup. Previously the setup failed but callbacks were still registered + // with `this` which caused the editor to be out of sync. + // FIXME: this probably demonstrates a flaw in the design anyways... + async setupBackend() { + log("Argus backend preloaded"); + } + async inspectAt(target?: ErrorJumpTargetInfo) { if (!this.view) { log("Creating panel..."); @@ -516,7 +522,12 @@ class BackendCache { res => { if (res.type !== "output") { this.statusBar.setState("error"); - showErrorDialog(res.error); + // NOTE: we probably should, but don't, report this as a bug + // to the user and open an error dialog box. The reason for this + // is a malformed `Cargo.toml` can cause errors within `rustc_plugin`. + // This in turn causes Argus to fail but it isn't really a "failure" + // on Argus' part. + log(res.error); return; } return res.value; diff --git a/ide/packages/extension/src/errors.ts b/ide/packages/extension/src/errors.ts index 96011b2..dc6e493 100644 --- a/ide/packages/extension/src/errors.ts +++ b/ide/packages/extension/src/errors.ts @@ -34,6 +34,8 @@ export const showErrorDialog = async (err: string) => { } }; +export const ARGUS_ERR_LOG_KEY = "argus_err_log"; + export const showError = async (error: ArgusError) => { if (error.type === "build-error") { // TODO: is this how we want to show build errors? @@ -46,6 +48,6 @@ export const showError = async (error: ArgusError) => { }; export async function lastError(context: vscode.ExtensionContext) { - const error = context.workspaceState.get("err_log") as string; + const error = context.workspaceState.get(ARGUS_ERR_LOG_KEY) as string; await showError({ type: "build-error", error }); } diff --git a/ide/packages/extension/src/main.ts b/ide/packages/extension/src/main.ts index cc1bff3..c9e24cd 100644 --- a/ide/packages/extension/src/main.ts +++ b/ide/packages/extension/src/main.ts @@ -2,8 +2,9 @@ import type vscode from "vscode"; import * as commands from "./commands"; import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx"; -import { showErrorDialog } from "./errors"; +import { CtxInit } from "./ctx"; import { log } from "./logging"; +import { setup } from "./setup"; export let globals: { ctx: Ctx; @@ -19,12 +20,14 @@ export async function activate(context: vscode.ExtensionContext) { // TODO: anything that needs to get registered on the window should be done here. // Initialize backend API for the workspace. - const ctx = new Ctx(context, createCommands(), wksp); - await ctx.setupBackend().catch(err => { - showErrorDialog(`Cannot activate Argus extension: ${err.message}`); - throw err; - }); + const initialCtxt = new CtxInit(context, wksp); + log("setting up Argus backend"); + const backend = await setup(initialCtxt); + if (backend == null) { + return; + } + const ctx = await Ctx.make(initialCtxt, backend, createCommands()); log("Argus activated successfully"); context.subscriptions.push(ctx); globals = { diff --git a/ide/packages/extension/src/setup.ts b/ide/packages/extension/src/setup.ts index f5eeb0c..f2cc44d 100644 --- a/ide/packages/extension/src/setup.ts +++ b/ide/packages/extension/src/setup.ts @@ -19,7 +19,8 @@ import _ from "lodash"; import open from "open"; import vscode from "vscode"; -import type { Ctx } from "./ctx"; +import type { CtxInit } from "./ctx"; +import { ARGUS_ERR_LOG_KEY } from "./errors"; import { log } from "./logging"; import { isStatusBarState } from "./statusbar"; @@ -29,7 +30,7 @@ declare const TOOLCHAIN: { components: string[]; }; -let CTX: Ctx | undefined; +let CTX: CtxInit | undefined; // TODO: all of the calls to `showInformationMessage` should be replaced // with some automatic message on the statusBar indicator. @@ -190,7 +191,7 @@ const checkVersionAndInstall = async ( return true; }; -export async function setup(context: Ctx): Promise { +export async function setup(context: CtxInit): Promise { // FIXME: this is a hack to give the `context` dynamic scope in this file. CTX = context; @@ -257,6 +258,10 @@ export async function setup(context: Ctx): Promise { if ("Err" in outputTyped) { log("Argus failed with `Err`", outputTyped.Err); context.statusBar.setState("error", "Analysis failed"); + context.extCtx.workspaceState.update( + ARGUS_ERR_LOG_KEY, + outputTyped.Err + ); return outputTyped.Err; } @@ -268,7 +273,7 @@ export async function setup(context: Ctx): Promise { }) .catch(e => { context.statusBar.setState("error", "Build failure"); - log("Argus failed with error", e.toString()); + context.extCtx.workspaceState.update(ARGUS_ERR_LOG_KEY, e); return { type: "build-error", error: e.toString() diff --git a/ide/packages/extension/src/view.ts b/ide/packages/extension/src/view.ts index 83678c3..a80133e 100644 --- a/ide/packages/extension/src/view.ts +++ b/ide/packages/extension/src/view.ts @@ -60,12 +60,12 @@ export class View { enableScripts: true, retainContextWhenHidden: true, enableFindWidget: true, - localResourceRoots: [this.ctx.extCtx.extensionUri] + localResourceRoots: [this.ctx.ictxt.extCtx.extensionUri] }); // Set the webview's initial html content panel.webview.html = getHtmlForWebview( - this.ctx.extCtx.extensionUri, + this.ctx.ictxt.extCtx.extensionUri, panel, initialData, target