feat: add global tasks folder

This commit is contained in:
2026-07-15 00:32:25 +05:00
parent dc37cd8553
commit beeb9dd7f9
17 changed files with 473 additions and 156 deletions
+68 -10
View File
@@ -1,7 +1,14 @@
import * as vscode from "vscode";
import { listLocatedTasks } from "@/commands/listLocatedTasks";
import { resolveTaskLocations } from "@/commands/resolveTaskLocations";
import { Task, TaskStatus } from "@/model/task";
import { LocatedTask, TaskLocation } from "@/model/taskLocation";
import { IConfigProvider, ITaskRepository } from "@/ports";
import { TASK_STATUS_LABELS, TASK_STATUS_ORDER } from "@/ui/taskLabels";
import {
TASK_SCOPE_LABELS,
TASK_STATUS_LABELS,
TASK_STATUS_ORDER,
} from "@/ui/taskLabels";
import { TaskTreeItem } from "./taskTreeItem";
const STATUS_ICONS: Record<TaskStatus, vscode.ThemeIcon> = {
@@ -11,19 +18,42 @@ const STATUS_ICONS: Record<TaskStatus, vscode.ThemeIcon> = {
cancelled: new vscode.ThemeIcon("close"),
};
class ScopeTreeItem extends vscode.TreeItem {
readonly location: TaskLocation;
readonly locatedTasks: LocatedTask[];
constructor(location: TaskLocation, locatedTasks: LocatedTask[]) {
super(
TASK_SCOPE_LABELS[location.scope],
vscode.TreeItemCollapsibleState.Expanded,
);
this.location = location;
this.locatedTasks = locatedTasks;
this.id = `scope-${location.scope}`;
this.contextValue = "taskScope";
this.description = `(${locatedTasks.length})`;
this.iconPath =
location.scope === "project"
? new vscode.ThemeIcon("root-folder")
: new vscode.ThemeIcon("globe");
}
}
class TaskGroupTreeItem extends vscode.TreeItem {
readonly location: TaskLocation;
readonly tasks: Task[];
constructor(status: TaskStatus, tasks: Task[]) {
constructor(status: TaskStatus, location: TaskLocation, tasks: Task[]) {
super(
TASK_STATUS_LABELS[status],
vscode.TreeItemCollapsibleState.Collapsed,
);
this.iconPath = STATUS_ICONS[status];
this.location = location;
this.tasks = tasks;
this.description = `(${tasks.length})`;
this.id = `group-${status}`;
this.id = `group-${location.scope}-${status}`;
this.contextValue = "taskGroup";
this.iconPath = STATUS_ICONS[status];
}
}
@@ -53,23 +83,51 @@ export class TaskTreeProvider implements vscode.TreeDataProvider<vscode.TreeItem
if (!element) {
return this.getRootChildren();
}
if (element instanceof ScopeTreeItem) {
return this.getStatusGroups(element);
}
if (element instanceof TaskGroupTreeItem) {
return element.tasks.map((task) => new TaskTreeItem(task));
return element.tasks.map(
(task) => new TaskTreeItem(task, element.location),
);
}
return [];
}
private async getRootChildren(): Promise<vscode.TreeItem[]> {
const projectPath = this.#configStore.getProjectTaskPath();
if (!projectPath) {
return [new vscode.TreeItem("Open a workspace folder to start")];
const locations = resolveTaskLocations(this.#configStore);
if (locations.length === 0) {
return [
new vscode.TreeItem(
"Configure project or global task path in settings",
),
];
}
const tasks = await this.#taskStore.list(projectPath);
const result = await listLocatedTasks(this.#taskStore, locations);
if (result.isErr()) {
return [new vscode.TreeItem(result.error.message)];
}
const byScope = new Map<string, LocatedTask[]>();
for (const location of locations) {
byScope.set(location.scope, []);
}
for (const located of result.value) {
byScope.get(located.location.scope)?.push(located);
}
return locations.map((location) => {
const tasks = byScope.get(location.scope) ?? [];
return new ScopeTreeItem(location, tasks);
});
}
private getStatusGroups(scope: ScopeTreeItem): vscode.TreeItem[] {
const tasks = scope.locatedTasks.map((item) => item.task);
return TASK_STATUS_ORDER.map((status) => {
const groupTasks = tasks.filter((t) => t.status === status);
return new TaskGroupTreeItem(status, groupTasks);
return new TaskGroupTreeItem(status, scope.location, groupTasks);
});
}
}