Skip to content

Commit

Permalink
Group tools by file. Open tool definition on click.
Browse files Browse the repository at this point in the history
  • Loading branch information
hverlin committed Nov 11, 2024
1 parent 05c9921 commit bd63607
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 14 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Mise VSCode",
"publisher": "hverlin",
"description": "VSCode extension for mise (manged dev tools, tasks and environment variables)",
"version": "0.0.4",
"version": "0.0.5",
"repository": {
"type": "git",
"url": "https://github.com/hverlin/mise-vscode"
Expand Down Expand Up @@ -63,6 +63,10 @@
{
"command": "mise.runTask",
"title": "Run Mise Task"
},
{
"command": "mise.openToolDefinition",
"title": "Open Tool Definition"
}
],
"menus": {
Expand Down
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
MiseTasksProvider,
registerMiseCommands,
} from "./providers/tasksProvider";
import { MiseToolsProvider } from "./providers/toolsProvider";
import { MiseToolsProvider, registerCommands } from "./providers/toolsProvider";

let statusBarItem: vscode.StatusBarItem;

Expand All @@ -25,6 +25,7 @@ export function activate(context: vscode.ExtensionContext) {
statusBarItem.text = "$(tools) Mise";
statusBarItem.tooltip = "Click to refresh Mise";
registerMiseCommands(context, tasksProvider);
registerCommands(context);

vscode.window.registerTreeDataProvider("miseTasksView", tasksProvider);
vscode.window.registerTreeDataProvider("miseToolsView", toolsProvider);
Expand Down
1 change: 1 addition & 0 deletions src/miseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class MiseService {
active: tool.active,
installed: tool.installed,
install_path: tool.install_path,
source: tool.source,
} satisfies MiseTool;
});
});
Expand Down
1 change: 0 additions & 1 deletion src/providers/tasksProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ class TaskItem extends vscode.TreeItem {
this.tooltip = `Task: ${task.name}\nSource: ${task.source}\nDescription: ${task.description}`;
this.iconPath = new vscode.ThemeIcon("play");

// Add command to run the task
this.command = {
title: "Run Task",
command: "mise.runTask",
Expand Down
125 changes: 114 additions & 11 deletions src/providers/toolsProvider.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,141 @@
import * as path from "node:path";
import * as vscode from "vscode";
import type { MiseService } from "../miseService";

export class MiseToolsProvider implements vscode.TreeDataProvider<ToolItem> {
private _onDidChangeTreeData: vscode.EventEmitter<
ToolItem | undefined | null | void
> = new vscode.EventEmitter<ToolItem | undefined | null | void>();
readonly onDidChangeTreeData: vscode.Event<
ToolItem | undefined | null | void
> = this._onDidChangeTreeData.event;
type TreeItem = SourceItem | ToolItem;

export class MiseToolsProvider implements vscode.TreeDataProvider<TreeItem> {
private _onDidChangeTreeData = new vscode.EventEmitter<
TreeItem | undefined | null | void
>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;

constructor(private miseService: MiseService) {}

refresh(): void {
this._onDidChangeTreeData.fire();
}

getTreeItem(element: ToolItem): vscode.TreeItem {
getTreeItem(element: TreeItem): vscode.TreeItem {
return element;
}

async getChildren(): Promise<ToolItem[]> {
const tools = await this.miseService.getTools();
return tools.map((tool) => new ToolItem(tool));
async getChildren(element?: TreeItem): Promise<TreeItem[]> {
if (!element) {
const tools = await this.miseService.getTools();
const toolsBySource = this.groupToolsBySource(tools);

return Object.entries(toolsBySource).map(
([source, tools]) => new SourceItem(source, tools),
);
}

if (element instanceof SourceItem) {
return element.tools.map((tool) => new ToolItem(tool));
}

return [];
}

private groupToolsBySource(tools: MiseTool[]): Record<string, MiseTool[]> {
return tools.reduce((acc: Record<string, MiseTool[]>, tool: MiseTool) => {
const source = tool.source?.path || "Unknown";
if (!acc[source]) {
acc[source] = [];
}
acc[source].push(tool);
return acc;
}, {});
}
}

class SourceItem extends vscode.TreeItem {
constructor(
public readonly source: string,
public readonly tools: MiseTool[],
) {
super(path.basename(source), vscode.TreeItemCollapsibleState.Expanded);

this.tooltip = `Source: ${source}
Number of tools: ${tools.length}`;

this.iconPath = new vscode.ThemeIcon("folder");
this.contextValue = "source";
this.description = `(${tools.length} tools)`;
}
}

class ToolItem extends vscode.TreeItem {
constructor(tool: MiseTool) {
super(`${tool.name} ${tool.version}`, vscode.TreeItemCollapsibleState.None);

this.tooltip = `Tool: ${tool.name}
Version: ${tool.version}
Requested Version: ${tool.requested_version}
Source: ${tool.source?.path || "Unknown"}
Activated: ${tool.active}
Installed: ${tool.installed}
Install Path: ${tool.install_path}`;

this.iconPath = this.getToolIcon(tool);

if (tool.source?.path) {
this.command = {
command: "mise.openToolDefinition",
title: "Open Tool Definition",
arguments: [tool],
};
}
}

private getToolIcon(tool: MiseTool): vscode.ThemeIcon {
if (!tool.installed) {
return new vscode.ThemeIcon("circle-outline");
}
if (tool.active) {
return new vscode.ThemeIcon("check");
}
return new vscode.ThemeIcon("circle-filled");
}
}

export function registerCommands(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand(
"mise.openToolDefinition",
async (tool: MiseTool) => {
if (!tool.source?.path) {
return;
}

const document = await vscode.workspace.openTextDocument(
tool.source.path,
);
const editor = await vscode.window.showTextDocument(document);

let line = 0;
for (let i = 0; i < editor.document.getText().split("\n").length; i++) {
const l = editor.document.getText().split("\n")[i];
const [firstWord] = l.replace(/\s/g, "").replace(/"/g, "").split("=");
if (firstWord === tool.name) {
line = i + 1;
break;
}
}

if (line) {
const position = new vscode.Position(Math.max(0, line - 1), 0);
const position2 = new vscode.Position(
Math.max(0, line - 1),
tool.name.includes(":") ? tool.name.length + 2 : tool.name.length,
);
editor.selection = new vscode.Selection(position, position2);
editor.revealRange(
new vscode.Range(position, position2),
vscode.TextEditorRevealType.InCenter,
);
}
},
),
);
}
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ type MiseTask = {
description: string;
};

type MiseToolSource = {
type: string;
path: string;
};

type MiseTool = {
name: string;
version: string;
source?: MiseToolSource;
requested_version: string;
installed: boolean;
active: boolean;
Expand Down

0 comments on commit bd63607

Please sign in to comment.