diff --git a/packages/vscode-extension/README.md b/packages/vscode-extension/README.md
index bcfe0ea..ddb71d1 100644
--- a/packages/vscode-extension/README.md
+++ b/packages/vscode-extension/README.md
@@ -25,6 +25,13 @@ alt="Screenshot of projects view" width="300px" />
Similar to the projects view, the tags view displays projects grouped by their `tags`.
+For each tag, two sections are shown:
+
+- **Tasks** – Lists tasks that exist across projects with that tag. These tasks can be executed for all projects with the tag.
+- **Projects** – Lists the projects associated with the tag.
+
+Running a task from the **Tasks** section will execute the task for all projects matching the tag.
+
diff --git a/packages/vscode-extension/images/tags-view.png b/packages/vscode-extension/images/tags-view.png
index 9fc6f94..8a8ccd9 100644
Binary files a/packages/vscode-extension/images/tags-view.png and b/packages/vscode-extension/images/tags-view.png differ
diff --git a/packages/vscode-extension/src/extension.ts b/packages/vscode-extension/src/extension.ts
index 34e09ca..4b4d48e 100644
--- a/packages/vscode-extension/src/extension.ts
+++ b/packages/vscode-extension/src/extension.ts
@@ -34,6 +34,8 @@ export function activate(context: vscode.ExtensionContext) {
appendSchemasConfig(context, workspace),
),
+ vscode.commands.registerCommand('moon.projectTag.runTagTask', (item) => tagsProvider.runTagTask(item)),
+
// Create a tree view for all moon projects
vscode.window.createTreeView('moonProjects', {
showCollapseAll: true,
diff --git a/packages/vscode-extension/src/projectsView.ts b/packages/vscode-extension/src/projectsView.ts
index 8084b1d..a19b3ca 100644
--- a/packages/vscode-extension/src/projectsView.ts
+++ b/packages/vscode-extension/src/projectsView.ts
@@ -269,6 +269,49 @@ class ProjectTagItem extends TreeItem {
}
}
+class TagTaskItem extends TreeItem {
+ tag: string;
+ taskId: string;
+
+ constructor(tag: string, taskId: string) {
+ super(taskId, TreeItemCollapsibleState.None);
+
+ this.tag = tag;
+ this.taskId = taskId;
+ this.id = `tag-${tag}-task-${taskId}`;
+ this.contextValue = 'tagTask';
+ this.iconPath = new ThemeIcon('play');
+
+ this.command = {
+ command: 'moon.projectTag.runTagTask',
+ title: 'Run Tag Task',
+ arguments: [this],
+ };
+ }
+}
+
+class TagTasksGroup extends TreeItem {
+ parent: ProjectTagItem;
+
+ constructor(parent: ProjectTagItem) {
+ super('Tasks', TreeItemCollapsibleState.Expanded);
+ this.parent = parent;
+ this.contextValue = 'tagTasksGroup';
+ this.iconPath = new ThemeIcon('list-unordered');
+ }
+}
+
+class TagProjectsGroup extends TreeItem {
+ parent: ProjectTagItem;
+
+ constructor(parent: ProjectTagItem) {
+ super('Projects', TreeItemCollapsibleState.Expanded);
+ this.parent = parent;
+ this.contextValue = 'tagProjectsGroup';
+ this.iconPath = new ThemeIcon('repo');
+ }
+}
+
export type ProjectsType = 'category' | 'tag';
export class ProjectsProvider implements vscode.TreeDataProvider {
@@ -354,9 +397,36 @@ export class ProjectsProvider implements vscode.TreeDataProvider {
}
if (element instanceof ProjectTagItem) {
- return element.projects;
+ if (element.id === `tag-${UNTAGGED}`) {
+ return element.projects;
+ }
+ return [
+ new TagTasksGroup(element),
+ new TagProjectsGroup(element)
+ ];
+ }
+
+ if (element instanceof TagTasksGroup) {
+ const projects = element.parent.projects;
+
+ const taskIds = new Set();
+
+ projects.forEach((projectItem) => {
+ projectItem.tasks.forEach((taskItem) => {
+ taskIds.add(taskItem.task.id);
+ });
+ });
+
+ const tag = element.parent.label!.replace('#', '');
+
+ return Array.from(taskIds)
+ .sort()
+ .map((taskId) => new TagTaskItem(tag, taskId));
}
+ if (element instanceof TagProjectsGroup) {
+ return element.parent.projects;
+ }
if (!this.projects) {
const version = await this.workspace.getMoonVersion();
const args = ['query', 'projects'];
@@ -450,6 +520,14 @@ export class ProjectsProvider implements vscode.TreeDataProvider {
});
}
+ async runTagTask(item: TagTaskItem) {
+ const target = `'#${item.tag}:${item.taskId}'`;
+
+ await runTask(target, this.workspace, (task) => {
+ task.group = TaskGroup.Build;
+ });
+ }
+
async checkProject(item: ProjectItem) {
await checkProject(item.project.id, this.workspace);
}