From 003ece45b2277656d4e1ebf8fc3b7ece4d6eba39 Mon Sep 17 00:00:00 2001 From: Vincent Fugnitto Date: Tue, 4 Jun 2019 09:53:48 -0400 Subject: [PATCH] Implement 'Show Running Tasks...' Fixes #5346 Fixed #1100 - Implement the command and terminal menu item `Show Running Tasks...` which displays the list of currently running tasks, and upon selection, sets the focus to the running task's terminal. - Implemented a status-bar contribution displaying the number of currently running tasks, and when clicked, triggers the `Show Running Tasks...` command. - Fixed issue regarding the creation of the task terminal id. Signed-off-by: Vincent Fugnitto --- packages/task/src/browser/quick-open-task.ts | 52 +++++++++++++++ .../src/browser/task-frontend-contribution.ts | 64 +++++++++++++++++-- .../task/src/browser/task-frontend-module.ts | 3 +- packages/task/src/browser/task-service.ts | 2 +- 4 files changed, 114 insertions(+), 7 deletions(-) diff --git a/packages/task/src/browser/quick-open-task.ts b/packages/task/src/browser/quick-open-task.ts index d209d6d8c9f75..ee6c57e5d701f 100644 --- a/packages/task/src/browser/quick-open-task.ts +++ b/packages/task/src/browser/quick-open-task.ts @@ -26,6 +26,7 @@ import URI from '@theia/core/lib/common/uri'; import { TaskActionProvider } from './task-action-provider'; import { LabelProvider } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser'; +import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; @injectable() export class QuickOpenTask implements QuickOpenModel, QuickOpenHandler { @@ -387,3 +388,54 @@ export class TaskTerminateQuickOpen implements QuickOpenModel { } } + +@injectable() +export class TaskRunningQuickOpen implements QuickOpenModel { + + @inject(QuickOpenService) + protected readonly quickOpenService: QuickOpenService; + + @inject(TaskService) + protected readonly taskService: TaskService; + + @inject(TerminalService) + protected readonly terminalService: TerminalService; + + async onType(_lookFor: string, acceptor: (items: QuickOpenItem[]) => void): Promise { + const items: QuickOpenItem[] = []; + const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); + if (runningTasks.length <= 0) { + items.push(new QuickOpenItem({ + label: 'No task is currently running', + run: (): boolean => false, + })); + } else { + runningTasks.forEach((task: TaskInfo) => { + items.push(new QuickOpenItem({ + label: task.config.label, + run: (mode: QuickOpenMode): boolean => { + if (mode !== QuickOpenMode.OPEN) { + return false; + } + if (task.terminalId) { + const terminal = this.terminalService.getById('terminal-' + task.terminalId); + if (terminal) { + this.terminalService.open(terminal); + } + } + return true; + } + })); + }); + } + acceptor(items); + } + + async open(): Promise { + this.quickOpenService.open(this, { + placeholder: 'Select the task to show its output', + fuzzyMatchLabel: true, + fuzzyMatchDescription: true, + }); + } +} diff --git a/packages/task/src/browser/task-frontend-contribution.ts b/packages/task/src/browser/task-frontend-contribution.ts index 114e04fbee4f0..76bd5b3281003 100644 --- a/packages/task/src/browser/task-frontend-contribution.ts +++ b/packages/task/src/browser/task-frontend-contribution.ts @@ -14,20 +14,20 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, named } from 'inversify'; +import { inject, injectable, named, postConstruct } from 'inversify'; import { ILogger, ContributionProvider } from '@theia/core/lib/common'; -import { QuickOpenTask, TaskTerminateQuickOpen } from './quick-open-task'; +import { QuickOpenTask, TaskTerminateQuickOpen, TaskRunningQuickOpen } from './quick-open-task'; import { CommandContribution, Command, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core/lib/common'; import { FrontendApplication, FrontendApplicationContribution, QuickOpenContribution, - QuickOpenHandlerRegistry, KeybindingRegistry, KeybindingContribution, StorageService + QuickOpenHandlerRegistry, KeybindingRegistry, KeybindingContribution, StorageService, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; import { TaskContribution, TaskResolverRegistry, TaskProviderRegistry } from './task-contribution'; import { TaskService } from './task-service'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; import { TaskSchemaUpdater } from './task-schema-updater'; -import { TaskConfiguration } from '../common'; +import { TaskConfiguration, TaskWatcher } from '../common'; export namespace TaskCommands { const TASK_CATEGORY = 'Task'; @@ -67,6 +67,12 @@ export namespace TaskCommands { label: 'Clear History' }; + export const TASK_SHOW_RUNNING: Command = { + id: 'task:show-running', + category: TASK_CATEGORY, + label: 'Show Running Tasks' + }; + export const TASK_TERMINATE: Command = { id: 'task:terminate', category: TASK_CATEGORY, @@ -108,9 +114,24 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri @inject(StorageService) protected readonly storageService: StorageService; + @inject(TaskRunningQuickOpen) + protected readonly taskRunningQuickOpen: TaskRunningQuickOpen; + @inject(TaskTerminateQuickOpen) protected readonly taskTerminateQuickOpen: TaskTerminateQuickOpen; + @inject(TaskWatcher) + protected readonly taskWatcher: TaskWatcher; + + @inject(StatusBar) + protected readonly statusBar: StatusBar; + + @postConstruct() + protected async init(): Promise { + this.taskWatcher.onTaskCreated(() => this.updateRunningTasksItem()); + this.taskWatcher.onTaskExit(() => this.updateRunningTasksItem()); + } + onStart(): void { this.contributionProvider.getContributions().forEach(contrib => { if (contrib.registerResolvers) { @@ -131,6 +152,26 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri this.storageService.setData<{ recent: TaskConfiguration[] }>(TASKS_STORAGE_KEY, { recent }); } + /** + * Contribute a status-bar item to trigger + * the `Show Running Tasks` command. + */ + protected async updateRunningTasksItem(): Promise { + const id = 'show-running-tasks'; + const items = await this.taskService.getRunningTasks(); + if (!!items.length) { + this.statusBar.setElement(id, { + text: `$(wrench) ${items.length}`, + tooltip: 'Show Running Tasks', + alignment: StatusBarAlignment.LEFT, + priority: 2, + command: TaskCommands.TASK_SHOW_RUNNING.id, + }); + } else { + this.statusBar.removeElement(id); + } + } + registerCommands(registry: CommandRegistry): void { registry.registerCommand( TaskCommands.TASK_RUN, @@ -182,6 +223,13 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri } ); + registry.registerCommand( + TaskCommands.TASK_SHOW_RUNNING, + { + execute: () => this.taskRunningQuickOpen.open() + } + ); + registry.registerCommand( TaskCommands.TASK_TERMINATE, { @@ -211,10 +259,16 @@ export class TaskFrontendContribution implements CommandContribution, MenuContri order: '3' }); + menus.registerMenuAction(TerminalMenus.TERMINAL_TASKS_INFO, { + commandId: TaskCommands.TASK_SHOW_RUNNING.id, + label: 'Show Running Tasks...', + order: '0' + }); + menus.registerMenuAction(TerminalMenus.TERMINAL_TASKS_INFO, { commandId: TaskCommands.TASK_TERMINATE.id, label: 'Terminate Task...', - order: '0' + order: '1' }); menus.registerMenuAction(TerminalMenus.TERMINAL_TASKS_CONFIG, { diff --git a/packages/task/src/browser/task-frontend-module.ts b/packages/task/src/browser/task-frontend-module.ts index eb21be1d6a96c..590b25069180f 100644 --- a/packages/task/src/browser/task-frontend-module.ts +++ b/packages/task/src/browser/task-frontend-module.ts @@ -18,7 +18,7 @@ import { ContainerModule } from 'inversify'; import { FrontendApplicationContribution, QuickOpenContribution, KeybindingContribution } from '@theia/core/lib/browser'; import { CommandContribution, MenuContribution, bindContributionProvider } from '@theia/core/lib/common'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging'; -import { QuickOpenTask, TaskTerminateQuickOpen } from './quick-open-task'; +import { QuickOpenTask, TaskTerminateQuickOpen, TaskRunningQuickOpen } from './quick-open-task'; import { TaskContribution, TaskProviderRegistry, TaskResolverRegistry } from './task-contribution'; import { TaskService } from './task-service'; import { TaskConfigurations } from './task-configurations'; @@ -43,6 +43,7 @@ export default new ContainerModule(bind => { } bind(QuickOpenTask).toSelf().inSingletonScope(); + bind(TaskRunningQuickOpen).toSelf().inSingletonScope(); bind(TaskTerminateQuickOpen).toSelf().inSingletonScope(); bind(TaskConfigurations).toSelf().inSingletonScope(); bind(ProvidedTaskConfigurations).toSelf().inSingletonScope(); diff --git a/packages/task/src/browser/task-service.ts b/packages/task/src/browser/task-service.ts index 0b9dfc4198af4..e47b3aa964fdb 100644 --- a/packages/task/src/browser/task-service.ts +++ b/packages/task/src/browser/task-service.ts @@ -313,7 +313,7 @@ export class TaskService implements TaskConfigurationClient { TERMINAL_WIDGET_FACTORY_ID, { created: new Date().toString(), - id: 'task-' + taskId, + id: 'terminal-' + terminalId, caption: `Task #${taskId}`, label: `Task #${taskId}`, destroyTermOnClose: true