Skip to content

Commit 693716c

Browse files
committed
feat: use --nested flag
1 parent 2d34ff0 commit 693716c

File tree

8 files changed

+85
-180
lines changed

8 files changed

+85
-180
lines changed

.vscode/tasks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"tasks": [
66
{
77
"type": "npm",
8-
"script": "bundle",
8+
"script": "compile",
99
"problemMatcher": "$tsc-watch",
1010
"isBackground": true,
1111
"presentation": {

src/elements/activityBar.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as vscode from 'vscode';
22
import { TaskTreeDataProvider } from '../providers/taskTreeDataProvider.js';
3-
import { Taskfile } from '../models/taskfile.js';
3+
import { Namespace } from '../models/models.js';
44

55
export class ActivityBar {
66
private _provider: TaskTreeDataProvider;
@@ -16,12 +16,7 @@ export class ActivityBar {
1616
});
1717
}
1818

19-
public setTreeNesting(enabled: boolean) {
20-
this._provider.setTreeNesting(enabled);
21-
this._provider.refresh();
22-
}
23-
24-
public refresh(taskfiles?: Taskfile[]) {
25-
this._provider.refresh(taskfiles);
19+
public refresh(taskfiles?: Namespace[], nesting?: boolean): void {
20+
this._provider.refresh(taskfiles, nesting);
2621
}
2722
}

src/elements/quickPickItem.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import * as vscode from 'vscode';
2-
import { Taskfile, Task } from '../models/taskfile.js';
2+
import { Namespace, Task } from '../models/models.js';
33

44
export class QuickPickTaskItem implements vscode.QuickPickItem {
5-
constructor(taskfile: Taskfile, task: Task) {
6-
this.taskfile = taskfile;
5+
constructor(namespace: Namespace, task: Task) {
6+
this.namespace = namespace;
77
this.task = task;
88
this.label = task.name;
99
this.description = task.desc;
1010
this.kind = vscode.QuickPickItemKind.Default;
1111
}
12-
taskfile: Taskfile;
12+
namespace: Namespace;
1313
task: Task;
1414
label: string;
1515
description: string;
1616
kind: vscode.QuickPickItemKind;
1717
}
1818

1919
export class QuickPickTaskSeparator implements vscode.QuickPickItem {
20-
constructor(taskfile: Taskfile) {
21-
this.label = taskfile.location;
20+
constructor(namespace: Namespace) {
21+
this.label = namespace.location;
2222
this.kind = vscode.QuickPickItemKind.Separator;
2323
}
2424
label: string;

src/elements/treeItem.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as vscode from 'vscode';
2-
import { Task } from '../models/taskfile.js';
2+
import { Namespace, Task } from '../models/models.js';
33

44
export type TreeItem = WorkspaceTreeItem | NamespaceTreeItem | TaskTreeItem;
55

66
export class WorkspaceTreeItem extends vscode.TreeItem {
77
constructor(
88
readonly label: string,
99
readonly workspace: string,
10-
readonly tasks: Task[],
10+
readonly namespace: Namespace,
1111
readonly collapsibleState: vscode.TreeItemCollapsibleState,
1212
readonly command?: vscode.Command
1313
) {
@@ -22,8 +22,7 @@ export class NamespaceTreeItem extends vscode.TreeItem {
2222
constructor(
2323
readonly label: string,
2424
readonly workspace: string,
25-
readonly namespaceMap: any,
26-
readonly tasks: Task[],
25+
readonly namespace: Namespace,
2726
readonly collapsibleState: vscode.TreeItemCollapsibleState,
2827
readonly command?: vscode.Command
2928
) {

src/models/taskfile.ts renamed to src/models/models.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
export type TaskMapping = {
2-
[key: string]: TaskMapping | null;
3-
};
4-
5-
export interface Taskfile {
1+
export interface Namespace {
62
tasks: Task[];
3+
namespaces: Map<string, Namespace>;
74
location: string; // The location of the actual Taskfile
85
// The vscode workspace directory where the command was executed to find this taskfile
96
// This is where tasks in this taskfile will be executed from.
Lines changed: 52 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
11
import * as path from 'path';
22
import * as vscode from 'vscode';
33
import { TreeItem, TaskTreeItem, NamespaceTreeItem, WorkspaceTreeItem } from '../elements/treeItem.js';
4-
import { Taskfile, Task, TaskMapping } from '../models/taskfile.js';
4+
import { Namespace, Task } from '../models/models.js';
55

66
const namespaceSeparator = ':';
77

88
export class TaskTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
99
private _onDidChangeTreeData: vscode.EventEmitter<TaskTreeItem | undefined> = new vscode.EventEmitter<TaskTreeItem | undefined>();
1010
readonly onDidChangeTreeData: vscode.Event<TaskTreeItem | undefined> = this._onDidChangeTreeData.event;
11-
private _taskfiles?: Taskfile[];
12-
private _treeViewMap: TaskMapping = {};
11+
private _namespaces?: Namespace[];
12+
private _nesting: boolean = false;
1313

14-
constructor(
15-
private nestingEnabled: boolean = false
16-
) { }
17-
18-
setTreeNesting(enabled: boolean) {
19-
this.nestingEnabled = enabled;
14+
refresh(namespaces?: Namespace[], nesting?: boolean): void {
15+
if (namespaces) {
16+
this._namespaces = namespaces;
17+
}
18+
this._nesting = nesting ?? this._nesting;
19+
this._onDidChangeTreeData.fire(undefined);
2020
}
2121

2222
getTreeItem(element: TreeItem): vscode.TreeItem {
2323
return element;
2424
}
2525

2626
getChildren(parent?: TreeItem): Thenable<TreeItem[]> {
27-
var treeItems: TreeItem[] = [];
28-
2927
// If there are no workspace folders, return an empty array
3028
if (vscode.workspace.workspaceFolders === undefined || vscode.workspace.workspaceFolders.length === 0) {
3129
return Promise.resolve([]);
@@ -38,51 +36,56 @@ export class TaskTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
3836
return Promise.resolve(workspaces);
3937
}
4038

41-
// If there are no taskfiles, return an empty array
42-
if (!this._taskfiles || this._taskfiles.length === 0) {
39+
// If there are no namespaces, return an empty array
40+
if (!this._namespaces || this._namespaces.length === 0) {
4341
return Promise.resolve([]);
4442
}
4543

46-
var tasks: Task[] | undefined;
47-
var parentNamespace = "";
48-
var namespaceMap = this._treeViewMap;
49-
var workspace = "";
50-
5144
// If there is no parent and exactly one workspace folder or if the parent is a workspace
5245
if (!parent && vscode.workspace.workspaceFolders.length === 1) {
53-
tasks = this._taskfiles[0].tasks;
54-
workspace = this._taskfiles[0].workspace ?? "";
46+
return Promise.resolve(this.createTreeItems(
47+
this._namespaces[0].workspace ?? "",
48+
this._namespaces[0].namespaces,
49+
this._namespaces[0].tasks
50+
));
5551
}
5652

57-
// If there is a parent and it is a workspace
58-
if (parent instanceof WorkspaceTreeItem) {
59-
tasks = parent.tasks;
60-
workspace = parent.workspace;
53+
// If there is a parent and it is a workspace or namespace
54+
if (parent instanceof WorkspaceTreeItem || parent instanceof NamespaceTreeItem) {
55+
return Promise.resolve(this.createTreeItems(
56+
parent.workspace,
57+
parent.namespace.namespaces,
58+
parent.namespace.tasks
59+
));
6160
}
6261

63-
// If there is a parent and it is a namespace
64-
if (parent instanceof NamespaceTreeItem) {
65-
tasks = parent.tasks;
66-
parentNamespace = parent.label;
67-
namespaceMap = parent.namespaceMap;
68-
workspace = parent.workspace;
69-
}
62+
return Promise.resolve([]);
63+
}
7064

65+
createTreeItems(
66+
workspace: string,
67+
namespaces: Map<string, Namespace>,
68+
tasks: Task[]
69+
): TreeItem[] {
70+
var treeItems: TreeItem[] = [];
7171

72-
if (tasks === undefined) {
73-
return Promise.resolve([]);
72+
// Add each namespace to the tree
73+
if (namespaces) {
74+
for (const [key, namespace] of Object.entries(namespaces)){
75+
treeItems = treeItems.concat(new NamespaceTreeItem(
76+
key,
77+
workspace,
78+
namespace,
79+
vscode.TreeItemCollapsibleState.Collapsed
80+
));
81+
}
7482
}
7583

76-
let namespaceTreeItems = new Map<string, NamespaceTreeItem>();
77-
let taskTreeItems: TaskTreeItem[] = [];
78-
tasks.forEach(task => {
79-
let taskName = task.name.split(":").pop() ?? task.name;
80-
let namespacePath = trimParentNamespace(task.name, parentNamespace);
81-
let namespaceName = getNamespaceName(namespacePath);
82-
83-
if (taskName in namespaceMap) {
84-
let item = new TaskTreeItem(
85-
task.name.split(namespaceSeparator).pop() ?? task.name,
84+
// Add each task to the tree
85+
if (tasks) {
86+
for (const task of tasks) {
87+
treeItems = treeItems.concat(new TaskTreeItem(
88+
this._nesting ? task.name.split(namespaceSeparator).pop() ?? task.name : task.name,
8689
workspace,
8790
task,
8891
vscode.TreeItemCollapsibleState.None,
@@ -91,119 +94,25 @@ export class TaskTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
9194
title: 'Go to Definition',
9295
arguments: [task, true]
9396
}
94-
);
95-
taskTreeItems = taskTreeItems.concat(item);
96-
}
97-
98-
if (namespaceName in namespaceMap && namespaceMap[namespaceName] !== null) {
99-
let namespaceTreeItem = namespaceTreeItems.get(namespaceName);
100-
101-
if (namespaceTreeItem === undefined) {
102-
namespaceTreeItem = new NamespaceTreeItem(
103-
namespaceName,
104-
workspace,
105-
namespaceMap[namespaceName],
106-
[],
107-
vscode.TreeItemCollapsibleState.Collapsed
108-
);
109-
}
110-
namespaceTreeItem.tasks.push(task);
111-
namespaceTreeItems.set(namespaceName, namespaceTreeItem);
97+
));
11298
}
99+
}
113100

114-
});
115-
116-
// Add the namespace and tasks to the tree
117-
namespaceTreeItems.forEach(namespace => {
118-
treeItems = treeItems.concat(namespace);
119-
});
120-
treeItems = treeItems.concat(taskTreeItems);
121-
122-
return Promise.resolve(treeItems);
101+
return treeItems;
123102
}
124103

125104
getWorkspaces(): WorkspaceTreeItem[] {
126105
let workspaceTreeItems: WorkspaceTreeItem[] = [];
127-
this._taskfiles?.forEach(taskfile => {
128-
let dir = path.dirname(taskfile.location);
106+
this._namespaces?.forEach(namespace => {
107+
let dir = path.dirname(namespace.location);
129108
let workspaceTreeItem = new WorkspaceTreeItem(
130109
path.basename(dir),
131110
dir,
132-
taskfile.tasks,
111+
namespace,
133112
vscode.TreeItemCollapsibleState.Expanded
134113
);
135114
workspaceTreeItems = workspaceTreeItems.concat(workspaceTreeItem);
136115
});
137116
return workspaceTreeItems;
138117
}
139-
140-
refresh(taskfiles?: Taskfile[]): void {
141-
if (taskfiles) {
142-
this._taskfiles = taskfiles;
143-
this._treeViewMap = {};
144-
145-
// loop over all of the tasks in all of the task files and map their names into a set
146-
const taskNames = Array.from(new Set(
147-
taskfiles.flatMap(taskfile =>
148-
taskfile.tasks.flatMap(task => task.name)
149-
)
150-
// and sort desc so we know that the namespace reduction sets child objects correctly.
151-
)).sort((a, b) => (a > b ? -1 : 1));
152-
153-
taskNames.reduce((acc: any, key: string) => {
154-
const parts = key.split(':');
155-
let currentLevel = acc;
156-
157-
parts.forEach((part, index) => {
158-
if (part === "") {
159-
return;
160-
};
161-
162-
if (!(part in currentLevel)) {
163-
currentLevel[part] = {};
164-
if (index === parts.length - 1) {
165-
currentLevel[part] = null;
166-
}
167-
}
168-
169-
currentLevel = currentLevel[part] as TaskMapping;
170-
});
171-
172-
return acc;
173-
}, this._treeViewMap);
174-
}
175-
this._onDidChangeTreeData.fire(undefined);
176-
}
177-
}
178-
179-
function getFullNamespacePath(task: Task): string {
180-
// If the task has no namespace, return undefined
181-
if (!task.name.includes(namespaceSeparator)) {
182-
return "";
183-
}
184-
// Return the task's namespace by removing the last element
185-
return task.name.substring(0, task.name.lastIndexOf(namespaceSeparator));
186-
}
187-
188-
function trimParentNamespace(namespace: string, parentNamespace: string): string {
189-
if (parentNamespace === "") {
190-
return namespace;
191-
}
192-
193-
const index = namespace.indexOf(parentNamespace + namespaceSeparator);
194-
195-
if (index === -1) {
196-
return namespace;
197-
}
198-
199-
return namespace.substring(index + parentNamespace.length + 1);
200-
}
201-
202-
function getNamespaceName(namespacePath: string): string {
203-
// If the namespace has no separator, return the namespace
204-
if (!namespacePath.includes(namespaceSeparator)) {
205-
return namespacePath;
206-
}
207-
// Return the first element of the namespace
208-
return namespacePath.substring(0, namespacePath.indexOf(namespaceSeparator));
209118
}

src/services/taskfile.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Octokit } from 'octokit';
55
import * as path from 'path';
66
import * as semver from 'semver';
77
import * as vscode from 'vscode';
8-
import { Taskfile, Task } from '../models/taskfile.js';
8+
import { Namespace, Task } from '../models/models.js';
99
import { OutputTo, TerminalClose, TerminalPer, TreeSort, settings } from '../utils/settings.js';
1010
import { log } from '../utils/log.js';
1111
import stripAnsi from 'strip-ansi';
@@ -177,14 +177,17 @@ class TaskfileService {
177177
}
178178
}
179179

180-
public async read(dir: string): Promise<Taskfile | undefined> {
180+
public async read(dir: string, nesting: boolean): Promise<Namespace | undefined> {
181181
log.info(`Searching for taskfile in: "${dir}"`);
182182
return await new Promise((resolve, reject) => {
183183
let additionalFlags = "";
184184
// Sorting
185185
if (settings.tree.sort !== TreeSort.default) {
186186
additionalFlags = ` --sort ${settings.tree.sort}`;
187187
}
188+
if (nesting) {
189+
additionalFlags = ` --nested`;
190+
}
188191
let command = this.command(`--list-all --json${additionalFlags}`);
189192
cp.exec(command, { cwd: dir }, (err: cp.ExecException | null, stdout: string, stderr: string) => {
190193
if (err) {
@@ -209,7 +212,7 @@ class TaskfileService {
209212
}
210213
return resolve(undefined);
211214
}
212-
var taskfile: Taskfile = JSON.parse(stdout);
215+
var taskfile: Namespace = JSON.parse(stdout);
213216
if (path.dirname(taskfile.location) !== dir) {
214217
log.info(`Ignoring taskfile: "${taskfile.location}" (outside of workspace)`);
215218
return reject();

0 commit comments

Comments
 (0)