Skip to content

Commit

Permalink
Quiet the IDE errors a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinleroy committed Jul 11, 2024
1 parent 0516c85 commit a3db4f2
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 86 deletions.
11 changes: 9 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
};
});
}
5 changes: 4 additions & 1 deletion ide/packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@
]
},
"files": [
"dist"
"dist",
"argus-logo-128.png",
"LICENSE",
"README.md"
],
"main": "./dist/argus.js",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion ide/packages/extension/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
}
149 changes: 80 additions & 69 deletions ide/packages/extension/src/ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<string, CommandFactory>,
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<string, CommandFactory>,

// 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<string, CommandFactory>
) {
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)
Expand All @@ -190,7 +164,7 @@ export class Ctx {
event.document === editor.document &&
editor.document.isDirty
) {
this.statusBar.setState("unsaved");
this.ictxt.statusBar.setState("unsaved");
}
})
);
Expand Down Expand Up @@ -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...");
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion ide/packages/extension/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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 });
}
15 changes: 9 additions & 6 deletions ide/packages/extension/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 = {
Expand Down
13 changes: 9 additions & 4 deletions ide/packages/extension/src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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.
Expand Down Expand Up @@ -190,7 +191,7 @@ const checkVersionAndInstall = async (
return true;
};

export async function setup(context: Ctx): Promise<CallArgus | null> {
export async function setup(context: CtxInit): Promise<CallArgus | null> {
// FIXME: this is a hack to give the `context` dynamic scope in this file.
CTX = context;

Expand Down Expand Up @@ -257,6 +258,10 @@ export async function setup(context: Ctx): Promise<CallArgus | null> {
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;
}

Expand All @@ -268,7 +273,7 @@ export async function setup(context: Ctx): Promise<CallArgus | null> {
})
.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()
Expand Down
4 changes: 2 additions & 2 deletions ide/packages/extension/src/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a3db4f2

Please sign in to comment.