Skip to content

Commit 71dd257

Browse files
committed
feat: nested tree view for namespaced tasks
1 parent 2df781d commit 71dd257

File tree

2 files changed

+63
-8
lines changed

2 files changed

+63
-8
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@
4242
]
4343
},
4444
"colors": [
45+
{
46+
"id": "vscodetask.namespace",
47+
"description": "Color for up-to-date tasks in the activity bar.",
48+
"defaults": {
49+
"dark": "#5FBBB0",
50+
"light": "#5FBBB0",
51+
"highContrast": "#5FBBB0",
52+
"highContrastLight": "#5FBBB0"
53+
}
54+
},
4555
{
4656
"id": "vscodetask.upToDate",
4757
"description": "Color for up-to-date tasks in the activity bar.",

src/providers/tasks.ts

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as vscode from 'vscode';
22
import * as models from '../models';
33

4+
const namespaceSeparator = ':';
5+
46
export class Tasks implements vscode.TreeDataProvider<TaskTreeItem> {
57
private _onDidChangeTreeData: vscode.EventEmitter<TaskTreeItem | undefined> = new vscode.EventEmitter<TaskTreeItem | undefined>();
68
readonly onDidChangeTreeData: vscode.Event<TaskTreeItem | undefined> = this._onDidChangeTreeData.event;
@@ -10,12 +12,34 @@ export class Tasks implements vscode.TreeDataProvider<TaskTreeItem> {
1012
return element;
1113
}
1214

13-
getChildren(element?: TaskTreeItem): Thenable<TaskTreeItem[]> {
15+
getChildren(parent?: TaskTreeItem): Thenable<TaskTreeItem[]> {
1416
var taskTreeItems: TaskTreeItem[] = [];
17+
var namespaceTreeItems: TaskTreeItem[] = [];
18+
19+
// Loop over each task
1520
this._taskfile?.tasks.forEach(task => {
16-
taskTreeItems.push(new TaskTreeItem(task, vscode.TreeItemCollapsibleState.None));
21+
22+
// Split the task's name into a namespace and label
23+
var namespace = task.name.substring(0, task.name.lastIndexOf(namespaceSeparator));
24+
var taskLabel = task.name.substring(task.name.lastIndexOf(namespaceSeparator) + 1, task.name.length);
25+
26+
// If the task's namespace is the same as the parent's namespace
27+
// add the task to the tree
28+
if ((parent && parent.namespace === namespace) || (!parent && namespace === "")) {
29+
taskTreeItems.push(new TaskTreeItem(taskLabel, namespace, task, vscode.TreeItemCollapsibleState.None));
30+
return;
31+
}
32+
33+
// If the namespace is a direct child of the parent namespace
34+
// and the namespace doesn't already exist in the tree
35+
// add the namespace to the tree
36+
if (!namespaceExistsInTree(namespaceTreeItems, namespace) && namespaceIsDirectChild(namespace, parent?.namespace)) {
37+
let namespaceLabel = trimNamespacePrefix(namespace, parent?.namespace);
38+
namespaceTreeItems.push(new TaskTreeItem(namespaceLabel, namespace, undefined, vscode.TreeItemCollapsibleState.Collapsed));
39+
return;
40+
}
1741
});
18-
return Promise.resolve(taskTreeItems);
42+
return Promise.resolve(namespaceTreeItems.concat(taskTreeItems));
1943
}
2044

2145
refresh(taskfile?: models.Taskfile): void {
@@ -24,15 +48,36 @@ export class Tasks implements vscode.TreeDataProvider<TaskTreeItem> {
2448
}
2549
}
2650

51+
function namespaceExistsInTree(tree: TaskTreeItem[], namespace: string) {
52+
return tree.find(item => item.namespace === namespace) !== undefined;
53+
}
54+
55+
function namespaceIsDirectChild(namespace: string, parentNamespace: string = "") {
56+
return namespace.startsWith(parentNamespace) && !trimNamespacePrefix(namespace, parentNamespace).includes(namespaceSeparator);
57+
}
58+
59+
function trimNamespacePrefix(str: string, prefix?: string) {
60+
if (!prefix) {
61+
return str;
62+
}
63+
if (str.startsWith(prefix)) {
64+
return str.substring(prefix.length + 1);
65+
}
66+
return str;
67+
}
68+
2769
class TaskTreeItem extends vscode.TreeItem {
2870
constructor(
29-
private task: models.Task,
71+
readonly label: string,
72+
readonly namespace: string,
73+
readonly task: models.Task | undefined,
3074
public readonly collapsibleState: vscode.TreeItemCollapsibleState
3175
) {
32-
super(task.name, collapsibleState);
33-
this.tooltip = `${this.task.desc}`;
34-
this.description = this.task.desc;
35-
if (this.task.up_to_date) {
76+
super(label, collapsibleState);
77+
this.description = this.task?.desc;
78+
if (!this.task) {
79+
this.iconPath = new vscode.ThemeIcon('symbol-namespace', new vscode.ThemeColor('vscodetask.namespace'));
80+
} else if (this.task.up_to_date) {
3681
this.iconPath = new vscode.ThemeIcon('debug-breakpoint-log-unverified', new vscode.ThemeColor('vscodetask.upToDate'));
3782
} else {
3883
this.iconPath = new vscode.ThemeIcon('debug-breakpoint-data-unverified', new vscode.ThemeColor('vscodetask.outOfDate'));

0 commit comments

Comments
 (0)