Skip to content

Commit

Permalink
Allow running tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
hverlin committed Nov 11, 2024
1 parent 2baf640 commit 1f7ea3a
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 15 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
.vscode-test/**
node_modules
mise.local.toml
dist/
dist/
.DS_Store
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.2",
"version": "0.0.3",
"repository": {
"type": "git",
"url": "https://github.com/hverlin/mise-vscode"
Expand Down Expand Up @@ -59,6 +59,10 @@
"command": "mise.refreshEntry",
"title": "Refresh",
"icon": "$(refresh)"
},
{
"command": "mise.runTask",
"title": "Run Mise Task"
}
],
"menus": {
Expand Down
6 changes: 5 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as vscode from "vscode";
import { MiseService } from "./miseService";
import { MiseEnvsProvider } from "./providers/envProvider";
import { MiseTasksProvider } from "./providers/tasksProvider";
import {
MiseTasksProvider,
registerMiseCommands,
} from "./providers/tasksProvider";
import { MiseToolsProvider } from "./providers/toolsProvider";

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

vscode.window.registerTreeDataProvider("miseTasksView", tasksProvider);
vscode.window.registerTreeDataProvider("miseToolsView", toolsProvider);
Expand Down
46 changes: 43 additions & 3 deletions src/miseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ import { logger } from "./utils/logger";
const execAsync = promisify(exec);

export class MiseService {
private terminal: vscode.Terminal | undefined;
private readonly workspaceRoot: string | undefined;

constructor() {
this.workspaceRoot = vscode.workspace.rootPath;
}

async getTasks(): Promise<MiseTask[]> {
try {
const { stdout } = await execAsync("mise tasks ls --json", {
cwd: vscode.workspace.rootPath,
cwd: this.workspaceRoot,
});
return JSON.parse(stdout).map((task: MiseTask) => ({
name: task.name,
Expand All @@ -27,7 +34,7 @@ export class MiseService {

try {
const { stdout } = await execAsync("mise ls --current --offline --json", {
cwd: vscode.workspace.rootPath,
cwd: this.workspaceRoot,
});
logger.info(`Got stdout from mise ls 4 command ${stdout}`);
return Object.entries(JSON.parse(stdout)).flatMap(([toolName, tools]) => {
Expand All @@ -51,7 +58,7 @@ export class MiseService {
async getEnvs(): Promise<MiseEnv[]> {
try {
const { stdout } = await execAsync("mise env --json", {
cwd: vscode.workspace.rootPath,
cwd: this.workspaceRoot,
});

return Object.entries(JSON.parse(stdout)).map(([key, value]) => ({
Expand All @@ -63,4 +70,37 @@ export class MiseService {
return [];
}
}

async runTask(taskName: string): Promise<void> {
const terminal = this.getOrCreateTerminal();
terminal.show();
terminal.sendText(`mise run ${taskName}`);
}

private getOrCreateTerminal(): vscode.Terminal {
if (!this.terminal || this._isTerminalClosed(this.terminal)) {
this.terminal = vscode.window.createTerminal({
name: "Mise Tasks",
cwd: this.workspaceRoot,
});

vscode.window.onDidCloseTerminal((closedTerminal) => {
if (closedTerminal === this.terminal) {
this.terminal = undefined;
}
});
}
return this.terminal;
}

private _isTerminalClosed(terminal: vscode.Terminal): boolean {
return vscode.window.terminals.indexOf(terminal) === -1;
}

dispose() {
if (this.terminal) {
this.terminal.dispose();
this.terminal = undefined;
}
}
}
92 changes: 83 additions & 9 deletions src/providers/tasksProvider.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as vscode from "vscode";
import type { MiseService } from "../miseService";

export class MiseTasksProvider implements vscode.TreeDataProvider<TaskItem> {
export class MiseTasksProvider implements vscode.TreeDataProvider<TreeNode> {
private _onDidChangeTreeData: vscode.EventEmitter<
TaskItem | undefined | null | void
> = new vscode.EventEmitter<TaskItem | undefined | null | void>();
TreeNode | undefined | null | void
> = new vscode.EventEmitter<TreeNode | undefined | null | void>();
readonly onDidChangeTreeData: vscode.Event<
TaskItem | undefined | null | void
TreeNode | undefined | null | void
> = this._onDidChangeTreeData.event;

constructor(private miseService: MiseService) {}
Expand All @@ -15,19 +15,93 @@ export class MiseTasksProvider implements vscode.TreeDataProvider<TaskItem> {
this._onDidChangeTreeData.fire();
}

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

async getChildren(): Promise<TaskItem[]> {
const tasks = await this.miseService.getTasks();
return tasks.map((task) => new TaskItem(task));
async getChildren(element?: TreeNode): Promise<TreeNode[]> {
if (!element) {
// Root level - return source groups
const tasks = await this.miseService.getTasks();
const groupedTasks = this.groupTasksBySource(tasks);

return Object.entries(groupedTasks).map(
([source, tasks]) => new SourceGroupItem(source, tasks),
);
}

if (element instanceof SourceGroupItem) {
// Source group level - return tasks
return element.tasks.map((task) => new TaskItem(task));
}

return [];
}

private groupTasksBySource(tasks: MiseTask[]): Record<string, MiseTask[]> {
return tasks.reduce(
(groups, task) => {
const source = task.source || "Unknown";
if (!groups[source]) {
groups[source] = [];
}
groups[source].push(task);
return groups;
},
{} as Record<string, MiseTask[]>,
);
}

async runTask(taskItem: TaskItem) {
try {
await this.miseService.runTask(taskItem.task.name);
vscode.window.showInformationMessage(
`Task '${taskItem.task.name}' started`,
);
} catch (error) {
vscode.window.showErrorMessage(
`Failed to run task '${taskItem.task.name}': ${error}`,
);
}
}
}

type TreeNode = SourceGroupItem | TaskItem;

class SourceGroupItem extends vscode.TreeItem {
constructor(
public readonly source: string,
public readonly tasks: MiseTask[],
) {
super(source, vscode.TreeItemCollapsibleState.Expanded);
this.tooltip = `Source: ${source}\nTasks: ${tasks.length}`;
this.iconPath = new vscode.ThemeIcon("folder");
}
}

class TaskItem extends vscode.TreeItem {
constructor(task: MiseTask) {
constructor(public readonly task: MiseTask) {
super(task.name, vscode.TreeItemCollapsibleState.None);
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",
arguments: [this],
};
}
}

// Register the command in your extension's activate function:
export function registerMiseCommands(
context: vscode.ExtensionContext,
taskProvider: MiseTasksProvider,
) {
context.subscriptions.push(
vscode.commands.registerCommand("mise.runTask", (taskItem: TaskItem) => {
taskProvider.runTask(taskItem);
}),
);
}

0 comments on commit 1f7ea3a

Please sign in to comment.