136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import { listLocatedTasks } from "@/commands/listLocatedTasks";
|
|
import { resolveTaskLocations } from "@/commands/resolveTaskLocations";
|
|
import { Task } from "@/model/task";
|
|
import { LocatedTask, TaskLocation } from "@/model/taskLocation";
|
|
import {
|
|
TaskStatus,
|
|
TASK_STATUS_META,
|
|
TASK_STATUS_ORDER,
|
|
} from "@/model/taskStatus";
|
|
import { IConfigProvider, ITaskRepository } from "@/ports";
|
|
import { TASK_SCOPE_LABELS, TASK_STATUS_LABELS } from "@/ui/taskLabels";
|
|
import * as vscode from "vscode";
|
|
import { TaskTreeItem } from "./taskTreeItem";
|
|
|
|
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, location: TaskLocation, tasks: Task[]) {
|
|
super(
|
|
TASK_STATUS_LABELS[status],
|
|
vscode.TreeItemCollapsibleState.Collapsed,
|
|
);
|
|
this.location = location;
|
|
this.tasks = tasks;
|
|
this.description = `(${tasks.length})`;
|
|
this.id = `group-${location.scope}-${status}`;
|
|
this.contextValue = "taskGroup";
|
|
this.iconPath = new vscode.ThemeIcon(
|
|
TASK_STATUS_META[status].icon,
|
|
status === TaskStatus.UNKNOWN
|
|
? new vscode.ThemeColor("charts.red")
|
|
: undefined,
|
|
);
|
|
}
|
|
}
|
|
|
|
export class TaskTreeProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
|
|
#onDidChangeTreeData = new vscode.EventEmitter<
|
|
vscode.TreeItem | undefined | null | void
|
|
>();
|
|
readonly onDidChangeTreeData = this.#onDidChangeTreeData.event;
|
|
|
|
#taskStore: ITaskRepository;
|
|
#configStore: IConfigProvider;
|
|
|
|
constructor(taskStore: ITaskRepository, configStore: IConfigProvider) {
|
|
this.#taskStore = taskStore;
|
|
this.#configStore = configStore;
|
|
}
|
|
|
|
refresh(): void {
|
|
this.#onDidChangeTreeData.fire();
|
|
}
|
|
|
|
getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
|
|
return element;
|
|
}
|
|
|
|
async getChildren(element?: vscode.TreeItem): Promise<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, element.location),
|
|
);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
private async getRootChildren(): Promise<vscode.TreeItem[]> {
|
|
const locations = resolveTaskLocations(this.#configStore);
|
|
if (locations.length === 0) {
|
|
return [
|
|
new vscode.TreeItem(
|
|
"Configure project or global task path in settings",
|
|
),
|
|
];
|
|
}
|
|
|
|
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.filter((status) => {
|
|
const groupTasks = tasks.filter((t) => t.status === status);
|
|
return !(groupTasks.length === 0 && TASK_STATUS_META[status].fallback);
|
|
}).map((status) => {
|
|
const groupTasks = tasks.filter((t) => t.status === status);
|
|
return new TaskGroupTreeItem(status, scope.location, groupTasks);
|
|
});
|
|
}
|
|
}
|