diff --git a/extensions/eclipse-che-theia-plugin-ext/src/browser/che-task-terminal-widget-manager.ts b/extensions/eclipse-che-theia-plugin-ext/src/browser/che-task-terminal-widget-manager.ts index 3d08f660d..e870d2ab5 100644 --- a/extensions/eclipse-che-theia-plugin-ext/src/browser/che-task-terminal-widget-manager.ts +++ b/extensions/eclipse-che-theia-plugin-ext/src/browser/che-task-terminal-widget-manager.ts @@ -8,7 +8,7 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import { TaskTerminalWidgetManager, TaskTerminalWidgetOpenerOptions } from '@theia/task/lib/browser/task-terminal-widget-manager'; +import { TaskTerminalWidgetManager, TaskTerminalWidgetOpenerOptions, TaskTerminalWidget } from '@theia/task/lib/browser/task-terminal-widget-manager'; import { TerminalWidget, TerminalWidgetOptions } from '@theia/terminal/lib/browser/base/terminal-widget'; import { TerminalWidgetFactoryOptions } from '@theia/terminal/lib/browser/terminal-widget-impl'; import { injectable } from 'inversify'; @@ -67,4 +67,12 @@ export class CheTaskTerminalWidgetManager extends TaskTerminalWidgetManager { return super.open(factoryOptions, openerOptions); } + + getTaskTerminals(): TerminalWidget[] { + return this.terminalService.all.filter(terminal => this.isTaskTerminal(terminal)); + } + + isTaskTerminal(terminal: TerminalWidget): boolean { + return TaskTerminalWidget.is(terminal) || RemoteTaskTerminalWidget.is(terminal); + } } diff --git a/extensions/eclipse-che-theia-plugin-ext/src/browser/style/tasks.css b/extensions/eclipse-che-theia-plugin-ext/src/browser/style/tasks.css index 24aa8a8ae..094cf1482 100644 --- a/extensions/eclipse-che-theia-plugin-ext/src/browser/style/tasks.css +++ b/extensions/eclipse-che-theia-plugin-ext/src/browser/style/tasks.css @@ -14,10 +14,10 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - .task-status-success:before { - color: #89d185; -} - -.task-status-error:before { - color: var(--theia-notificationsErrorIcon-foreground); -} +.p-TabBar.theia-app-centers .p-TabBar-tabIcon.task-status-in-progress { + background-size: 12px; + background-image: var(--theia-preloader); + background-repeat: no-repeat; + background-position: center; + background-color: transparent; + } diff --git a/extensions/eclipse-che-theia-plugin-ext/src/browser/task-config-service.ts b/extensions/eclipse-che-theia-plugin-ext/src/browser/task-config-service.ts index b30f2de77..0009ff2fe 100644 --- a/extensions/eclipse-che-theia-plugin-ext/src/browser/task-config-service.ts +++ b/extensions/eclipse-che-theia-plugin-ext/src/browser/task-config-service.ts @@ -8,6 +8,7 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ +import { Emitter, Event } from '@theia/core'; import { WidgetOpenMode } from '@theia/core/lib/browser'; import { TaskService } from '@theia/task/lib/browser'; import { TaskTerminalWidgetOpenerOptions } from '@theia/task/lib/browser/task-terminal-widget-manager'; @@ -20,6 +21,9 @@ import { CHE_TASK_TYPE, REMOTE_TASK_KIND, TASK_KIND } from './che-task-terminal- @injectable() export class TaskConfigurationsService extends TaskService { + protected readonly onDidStartTaskFailureEmitter = new Emitter(); + readonly onDidStartTaskFailure: Event = this.onDidStartTaskFailureEmitter.event; + @inject(CheTaskResolver) protected readonly cheTaskResolver: CheTaskResolver; @@ -39,6 +43,8 @@ export class TaskConfigurationsService extends TaskService { return taskInfo; } catch (error) { + this.onDidStartTaskFailureEmitter.fire({ config: resolvedTask, kind: terminal.kind, terminalId: terminal.terminalId, taskId: -1, }); + const errorMessage = `Error launching task '${taskLabel}': ${error.message}`; terminal.writeLine(`\x1b[31m ${errorMessage} \x1b[0m\n`); diff --git a/extensions/eclipse-che-theia-plugin-ext/src/browser/task-status-handler.ts b/extensions/eclipse-che-theia-plugin-ext/src/browser/task-status-handler.ts index ea23b85c9..780ccfbd0 100644 --- a/extensions/eclipse-che-theia-plugin-ext/src/browser/task-status-handler.ts +++ b/extensions/eclipse-che-theia-plugin-ext/src/browser/task-status-handler.ts @@ -10,18 +10,21 @@ import { TaskStatusOptions } from '@eclipse-che/plugin'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; -import { inject, injectable } from 'inversify'; +import { inject, injectable, postConstruct } from 'inversify'; +import { CheTaskTerminalWidgetManager } from './che-task-terminal-widget-manager'; import { TaskConfigurationsService } from './task-config-service'; const StatusIcon = { - SUCCESS: 'fa fa-check task-status-success', - ERROR: 'fa fa-times-circle task-status-error', + SUCCESS: 'fa fa-check', + ERROR: 'fa fa-times-circle', + IN_PROGRESS: 'task-status-in-progress', UNKNOWN: 'fa-question' }; enum TaskStatus { Success = 'SUCCESS', Error = 'ERROR', + InProgress = 'IN_PROGRESS', Unknown = 'UNKNOWN' } @@ -34,6 +37,34 @@ export class TaskStatusHandler { @inject(TaskConfigurationsService) protected readonly taskService: TaskConfigurationsService; + @inject(CheTaskTerminalWidgetManager) + protected readonly cheTaskTerminalWidgetManager: CheTaskTerminalWidgetManager; + + @postConstruct() + protected init(): void { + this.terminalService.onDidCreateTerminal(async (terminal: TerminalWidget) => { + if (this.cheTaskTerminalWidgetManager.isTaskTerminal(terminal)) { + this.setStatus(TaskStatus.InProgress, terminal); + + this.subscribeOnTaskTerminalEvents(terminal); + } + }); + + this.taskService.onDidStartTaskFailure(taskInfo => { + const kind = taskInfo.kind; + const terminalId = taskInfo.terminalId; + + if (kind && terminalId) { + const status = TaskStatus.Error; + const terminalIdentifier = { kind, terminalId }; + + this.setTaskStatus({ status, terminalIdentifier }); + } + }); + + this.handleOpenTerminals(); + } + async setTaskStatus(options: TaskStatusOptions): Promise { const terminalIdentifier = options.terminalIdentifier; const kind = terminalIdentifier.kind; @@ -55,4 +86,33 @@ export class TaskStatusHandler { terminal.title.iconClass = newStatusIcon; } } + + private async handleOpenTerminals(): Promise { + const taskTerminals = this.cheTaskTerminalWidgetManager.getTaskTerminals(); + for (const terminal of taskTerminals) { + try { + const processId = await terminal.processId; + if (processId) { + this.setStatus(TaskStatus.InProgress, terminal); + } + } catch (error) { + // an error is thrown if a terminal is not started, we are trying to get a process ID for started terminals + } + } + } + + private subscribeOnTaskTerminalEvents(terminal: TerminalWidget): void { + const didOpenListener = terminal.onDidOpen(async () => { + this.setStatus(TaskStatus.InProgress, terminal); + }); + + const didOpenFailureListener = terminal.onDidOpenFailure(async () => { + this.setStatus(TaskStatus.Error, terminal); + }); + + terminal.onDidDispose(() => { + didOpenListener.dispose(); + didOpenFailureListener.dispose(); + }); + } } diff --git a/extensions/eclipse-che-theia-plugin-ext/src/plugin/che-task-impl.ts b/extensions/eclipse-che-theia-plugin-ext/src/plugin/che-task-impl.ts index 72f46d063..95c58f71d 100644 --- a/extensions/eclipse-che-theia-plugin-ext/src/plugin/che-task-impl.ts +++ b/extensions/eclipse-che-theia-plugin-ext/src/plugin/che-task-impl.ts @@ -14,6 +14,7 @@ import { CheTask, CheTaskMain, PLUGIN_RPC_CONTEXT } from '../common/che-protocol export enum TaskStatus { Success = 'SUCCESS', + InProgress = 'IN_PROGRESS', Error = 'ERROR', Unknown = 'UNKNOWN' } diff --git a/extensions/eclipse-che-theia-plugin/src/che-proposed.d.ts b/extensions/eclipse-che-theia-plugin/src/che-proposed.d.ts index 9c8da42ce..6e669aaad 100644 --- a/extensions/eclipse-che-theia-plugin/src/che-proposed.d.ts +++ b/extensions/eclipse-che-theia-plugin/src/che-proposed.d.ts @@ -178,6 +178,7 @@ declare module '@eclipse-che/plugin' { export enum TaskStatus { Success = 'SUCCESS', Error = 'ERROR', + InProgress = 'IN_PROGRESS', Unknown = 'UNKNOWN' }