Skip to content

Commit

Permalink
Added VSCode StatusBar, floating subtree SVG
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinleroy committed Feb 19, 2024
1 parent dbe19bc commit 8380be5
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 38 deletions.
14 changes: 4 additions & 10 deletions ide/packages/common/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,16 @@ export type WebViewToExtensionMsg = CommonData & { type: FROM_WV } & (
// serde-compatible type
export type Result<T> = { Ok: T } | { Err: ArgusError };

export type BuildError = {
type: "build-error";
error: string;
};

export type ArgusError = {
type: "analysis-error";
error: string;
};
export type ArgusError =
| { type: "analysis-error"; error: string }
| { type: "build-error"; error: string };

export interface ArgusOutput<T> {
type: "output";
value: T;
}

export type ArgusResult<T> = ArgusOutput<T> | ArgusError | BuildError;
export type ArgusResult<T> = ArgusOutput<T> | ArgusError;

// TODO: what we really want here is dependent typing ... it
// might be achievable with TS, but too tired rn to think about that.
Expand Down
8 changes: 6 additions & 2 deletions ide/packages/extension/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
{
"name": "@argus/extension",
"displayName": "Argus Extension",
"description": "Something with types, probably?",
"description": "A trait debugger for Rust",
"version": "0.0.1",
"engines": {
"vscode": "^1.22.0"
},
"bugs": {
"url": "https://github.com/gavinleroy/argus/issues",
"email": "gavinleroy6@gmail.com"
},
"categories": [
"Other"
"Programming Languages"
],
"activationEvents": [
"onLanguage:rust"
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 @@ -3,7 +3,7 @@ import { Filename } from "@argus/common/lib";

import { Cmd, Ctx } from "./ctx";

export function launchArgus(ctx: Ctx): Cmd {
export function inspect(ctx: Ctx): Cmd {
return async () => {
ctx.createOrShowView();
};
Expand Down
17 changes: 16 additions & 1 deletion ide/packages/extension/src/ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import {
traitErrorDecorate,
} from "./decorate";
import { showErrorDialog } from "./errors";
import { globals } from "./main";
import { setup } from "./setup";
import { StatusBar } from "./statusbar";
import {
RustEditor,
isDocumentInWorkspace,
Expand Down Expand Up @@ -132,7 +134,7 @@ export class Ctx {
}

async setupBackend() {
let b = await setup(this);
const b = await setup(this);
if (b == null) {
showErrorDialog("Failed to setup Argus");
return;
Expand All @@ -145,6 +147,19 @@ export class Ctx {
// Register the commands with VSCode after the backend is setup.
this.updateCommands();

vscode.workspace.onDidChangeTextDocument(event => {
const editor = vscode.window.activeTextEditor!;
if (
editor &&
isRustEditor(editor) &&
isDocumentInWorkspace(editor.document) &&
event.document === editor.document &&
editor.document.isDirty
) {
globals.statusBar.setState("unsaved");
}
});

vscode.window.onDidChangeActiveTextEditor(async editor => {
if (
editor &&
Expand Down
17 changes: 17 additions & 0 deletions ide/packages/extension/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ArgusError } from "@argus/common/lib";
import cp from "child_process";
import newGithubIssueUrl from "new-github-issue-url";
import os from "os";
Expand Down Expand Up @@ -45,3 +46,19 @@ ${logText}`,
open(url);
}
};

export const showError = async (error: ArgusError) => {
if (error.type === "build-error") {
// TODO: is this how we want to show build errors?
await showErrorDialog(error.error);
} else if (error.type == "analysis-error") {
await showErrorDialog(error.error);
} else {
await showErrorDialog("Unknown error");
}
};

export async function last_error(context: vscode.ExtensionContext) {
const error = context.workspaceState.get("err_log") as string;
await showError({ type: "build-error", error });
}
11 changes: 10 additions & 1 deletion ide/packages/extension/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ import * as commands from "./commands";
import { CommandFactory, Ctx, fetchWorkspace } from "./ctx";
import { showErrorDialog } from "./errors";
import { log } from "./logging";
import { StatusBar } from "./statusbar";

export let globals: {
ctx: Ctx;
statusBar: StatusBar;
};

// This method is called when your extension is activated
export async function activate(context: vscode.ExtensionContext) {
globals = {
ctx: undefined as any,
statusBar: new StatusBar(context),
};

log("Activating Argus ...");

const ctx = new Ctx(context, createCommands(), fetchWorkspace());
const api = await activateBackend(ctx).catch(err => {
showErrorDialog(`Cannot activate Argus extension: ${err.message}`);
throw err;
});

globals = {
...globals,
ctx: api,
};
}
Expand All @@ -40,7 +49,7 @@ async function activateBackend(ctx: Ctx): Promise<Ctx> {
function createCommands(): Record<string, CommandFactory> {
return {
// Public commands that appear in the command palette and thus need to be listed in package.json.
inspectWorkspace: { enabled: commands.launchArgus },
inspectWorkspace: { enabled: commands.inspect },

// Private commands used internally, these should not appear in the command palette.
openError: { enabled: commands.openError },
Expand Down
7 changes: 6 additions & 1 deletion ide/packages/extension/src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import vscode from "vscode";

import { Ctx } from "./ctx";
import { log } from "./logging";
import { globals } from "./main";

declare const VERSION: string;
declare const TOOLCHAIN: {
Expand Down Expand Up @@ -68,7 +69,7 @@ export const getArgusOpts = async (cwd: string) => {
const execNotifyBinary = async (
cmd: string,
args: string[],
_title: string,
title: string,
opts?: any
): Promise<Buffer> => {
log("Running command: ", cmd, args, opts);
Expand All @@ -87,15 +88,19 @@ const execNotifyBinary = async (
stderrChunks.push(data);
});

globals.statusBar.setState("loading", title);

return new Promise<Buffer>((resolve, reject) => {
proc.addListener("close", _ => {
globals.statusBar.setState("idle");
if (proc.exitCode !== 0) {
reject(stderrChunks.join(""));
} else {
resolve(Buffer.concat(stdoutChunks));
}
});
proc.addListener("error", e => {
globals.statusBar.setState("idle");
reject(e.toString());
});
});
Expand Down
89 changes: 89 additions & 0 deletions ide/packages/extension/src/statusbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import vscode from "vscode";

export type StatusBarState =
| "active"
| "unsaved"
| "idle"
| "error"
| "loading"
| "notfound";

interface StatusBarConfig {
foreground: string;
background: string;
icon?: string;
command: string;
tooltip?: string;
}

const defaultConfigs: Record<StatusBarState, StatusBarConfig> = {
active: {
foreground: "statusBarItem.warningForeground",
background: "statusBarItem.warningBackground",
icon: "check",
command: "argus.inspectWorkspace",
},
unsaved: {
foreground: "statusBarItem.foreground",
background: "statusBarItem.background",
icon: "circle-slash",
command: "argus.inspectWorkspace",
},
idle: {
foreground: "statusBarItem.foreground",
background: "statusBarItem.background",
command: "argus.inspectWorkspace",
},
error: {
foreground: "statusBarItem.errorForeground",
background: "statusBarItem.errorBackground",
icon: "x",
command: "argus.lastError",
},
loading: {
foreground: "statusBarItem.foreground",
background: "statusBarItem.background",
icon: "sync~spin",
command: "argus.inspectWorkspace",
},
notfound: {
foreground: "statusBarItem.foreground",
background: "statusBarItem.background",
icon: "question",
command: "argus.inspectWorkspace",
tooltip:
"Argus could not get Cargo to find this file (this is probably a Argus bug)",
},
};

export class StatusBar {
bar: vscode.StatusBarItem;
state: StatusBarState = "loading";

constructor(
context: vscode.ExtensionContext,
readonly name: string = "argus",
readonly configs: Record<StatusBarState, StatusBarConfig> = defaultConfigs
) {
this.bar = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Left
);
context.subscriptions.push(this.bar);
this.bar.show();
}

setState(state: StatusBarState, tooltip: string = "") {
this.state = state;
this.bar.tooltip = tooltip;
this.render();
}

render() {
const config = this.configs[this.state];
this.bar.color = config.foreground;
this.bar.backgroundColor = new vscode.ThemeColor(config.background);
this.bar.text = `$(${config.icon}) ${this.name}`;
this.bar.command = config.command;
this.bar.tooltip = config.tooltip;
}
}
4 changes: 4 additions & 0 deletions ide/packages/panoptes/src/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ export const IcoLoop = () => <i className="codicon codicon-sync" />;
export const IcoNote = () => <i className="codicon codicon-note" />;

export const IcoMegaphone = () => <i className="codicon codicon-megaphone" />;

export const IcoTreeDown = () => (
<i className="codicon codicon-type-hierarchy-sub" />
);
10 changes: 9 additions & 1 deletion ide/packages/panoptes/src/TreeView/Directory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,26 @@ export const CollapsibleElement = ({
);
};

type InfoWrapper = React.FC<{ n: ProofNodeIdx; Child: React.FC }>;

export const DirNode = ({
idx,
styleEdge,
Children,
Wrapper = ({ n: _, Child }) => <Child />,
}: {
idx: number;
styleEdge: boolean;
Children: React.FC | null;
Wrapper: InfoWrapper;
}) => {
const tree = useContext(TreeContext)!;
const node = tree.node(idx);

const arrows: ElementPair = [<IcoTriangleDown />, <IcoTriangleRight />];
const dots: ElementPair = [<IcoDot />, <IcoDot />];
const icons = "Result" in node ? dots : arrows;
const info = <Node node={node} />;
const info = <Wrapper n={idx} Child={() => <Node node={node} />} />;

return (
<CollapsibleElement
Expand All @@ -93,10 +97,12 @@ export const DirRecursive = ({
level,
getNext,
styleEdges,
Wrapper = ({ n: _, Child }) => <Child />,
}: {
level: ProofNodeIdx[];
getNext: (idx: ProofNodeIdx) => ProofNodeIdx[];
styleEdges: boolean;
Wrapper?: InfoWrapper;
}) => {
const tree = useContext(TreeContext)!;
const node = tree.node(level[0]);
Expand All @@ -115,13 +121,15 @@ export const DirRecursive = ({
key={i}
idx={current}
styleEdge={styleEdges}
Wrapper={Wrapper}
Children={
next.length > 0
? () => (
<DirRecursive
level={next}
getNext={getNext}
styleEdges={styleEdges}
Wrapper={Wrapper}
/>
)
: null
Expand Down
1 change: 0 additions & 1 deletion ide/packages/panoptes/src/TreeView/Graph.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
width: 100%;
height: 100vh;
grid-area: tree-interact;
border-right: solid 1px #ccc;
}

span.foreign-wrapper {
Expand Down
11 changes: 6 additions & 5 deletions ide/packages/panoptes/src/TreeView/Graph.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TreeTopology } from "@argus/common/bindings";
import { ProofNodeIdx, TreeTopology } from "@argus/common/bindings";
import _ from "lodash";
import React, {
MouseEventHandler,
Expand Down Expand Up @@ -52,6 +52,7 @@ const TreeNode = ({
const treeInfo = useContext(TreeContext)!;
const ref = useRef<HTMLDivElement>(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const padding = 5;

const idx = nodeDatum.name as number;
const node = treeInfo.node(idx);
Expand All @@ -72,8 +73,8 @@ const TreeNode = ({
data-sidx={idx}
x={-dimensions.width / 2}
y={-dimensions.height / 2}
width={dimensions.width}
height={dimensions.height}
width={dimensions.width + padding}
height={dimensions.height + padding}
strokeWidth="0.5"
rx="3"
ry="3"
Expand Down Expand Up @@ -114,12 +115,12 @@ const topologyToTreeData = (
return obj;
};

const Graph = ({}) => {
const Graph = ({ root }: { root: ProofNodeIdx }) => {
const treeInfo = useContext(TreeContext)!;
const [translate, containerRef] = useCenteredTree();

const topology = treeInfo.topology;
const data = topologyToTreeData(topology, treeInfo.root);
const data = topologyToTreeData(topology, root);

const customRender = (rd3tProps: any) => {
return <TreeNode {...rd3tProps} />;
Expand Down
Loading

0 comments on commit 8380be5

Please sign in to comment.