Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ask user to terminate or restart a task if it is active #6668

Merged
merged 1 commit into from
Dec 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions packages/task/src/browser/task-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { TaskDefinitionRegistry } from './task-definition-registry';
import { ProvidedTaskConfigurations } from './provided-task-configurations';
import { TaskConfigurationManager } from './task-configuration-manager';
import { TaskSchemaUpdater } from './task-schema-updater';
import { TaskSourceResolver } from './task-source-resolver';
import { Disposable, DisposableCollection, ResourceProvider } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri';
import { FileChange, FileChangeType } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';
Expand Down Expand Up @@ -86,6 +87,9 @@ export class TaskConfigurations implements Disposable {
@inject(TaskSchemaUpdater)
protected readonly taskSchemaUpdater: TaskSchemaUpdater;

@inject(TaskSourceResolver)
protected readonly taskSourceResolver: TaskSourceResolver;

constructor() {
this.toDispose.push(Disposable.create(() => {
this.tasksMap.clear();
Expand Down Expand Up @@ -292,7 +296,7 @@ export class TaskConfigurations implements Disposable {
return;
}

const sourceFolderUri: string | undefined = this.getSourceFolderUriFromTask(task);
const sourceFolderUri: string | undefined = this.taskSourceResolver.resolve(task);
if (!sourceFolderUri) {
console.error('Global task cannot be customized');
return;
Expand Down Expand Up @@ -414,7 +418,7 @@ export class TaskConfigurations implements Disposable {
*/
// tslint:disable-next-line:no-any
async updateTaskConfig(task: TaskConfiguration, update: { [name: string]: any }): Promise<void> {
const sourceFolderUri: string | undefined = this.getSourceFolderUriFromTask(task);
const sourceFolderUri: string | undefined = this.taskSourceResolver.resolve(task);
if (!sourceFolderUri) {
console.error('Global task cannot be customized');
return;
Expand Down Expand Up @@ -464,15 +468,4 @@ export class TaskConfigurations implements Disposable {
type: task.taskType || task.type
});
}

private getSourceFolderUriFromTask(task: TaskConfiguration): string | undefined {
const isDetectedTask = this.isDetectedTask(task);
let sourceFolderUri: string | undefined;
if (isDetectedTask) {
sourceFolderUri = task._scope;
} else {
sourceFolderUri = task._source;
}
return sourceFolderUri;
}
}
2 changes: 2 additions & 0 deletions packages/task/src/browser/task-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { bindTaskPreferences } from './task-preferences';
import '../../src/browser/style/index.css';
import './tasks-monaco-contribution';
import { TaskNameResolver } from './task-name-resolver';
import { TaskSourceResolver } from './task-source-resolver';
import { TaskTemplateSelector } from './task-templates';

export default new ContainerModule(bind => {
Expand Down Expand Up @@ -74,6 +75,7 @@ export default new ContainerModule(bind => {
bindContributionProvider(bind, TaskContribution);
bind(TaskSchemaUpdater).toSelf().inSingletonScope();
bind(TaskNameResolver).toSelf().inSingletonScope();
bind(TaskSourceResolver).toSelf().inSingletonScope();
bind(TaskTemplateSelector).toSelf().inSingletonScope();

bindProcessTaskModule(bind);
Expand Down
86 changes: 71 additions & 15 deletions packages/task/src/browser/task-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ILogger, CommandService } from '@theia/core/lib/common';
import { MessageService } from '@theia/core/lib/common/message-service';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { QuickPickItem, QuickPickService } from '@theia/core/lib/common/quick-pick-service';
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
import URI from '@theia/core/lib/common/uri';
import { EditorManager } from '@theia/editor/lib/browser';
import { ProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
Expand Down Expand Up @@ -48,6 +49,7 @@ import { TaskConfigurationClient, TaskConfigurations } from './task-configuratio
import { TaskProviderRegistry, TaskResolverRegistry } from './task-contribution';
import { TaskDefinitionRegistry } from './task-definition-registry';
import { TaskNameResolver } from './task-name-resolver';
import { TaskSourceResolver } from './task-source-resolver';
import { ProblemMatcherRegistry } from './task-problem-matcher-registry';
import { TaskSchemaUpdater } from './task-schema-updater';
import { TaskConfigurationManager } from './task-configuration-manager';
Expand Down Expand Up @@ -131,6 +133,9 @@ export class TaskService implements TaskConfigurationClient {
@inject(TaskNameResolver)
protected readonly taskNameResolver: TaskNameResolver;

@inject(TaskSourceResolver)
protected readonly taskSourceResolver: TaskSourceResolver;

@inject(TaskSchemaUpdater)
protected readonly taskSchemaUpdater: TaskSchemaUpdater;

Expand All @@ -140,6 +145,8 @@ export class TaskService implements TaskConfigurationClient {
@inject(CommandService)
protected readonly commands: CommandService;

@inject(LabelProvider)
protected readonly labelProvider: LabelProvider;
/**
* @deprecated To be removed in 0.5.0
*/
Expand All @@ -161,12 +168,9 @@ export class TaskService implements TaskConfigurationClient {
return;
}
this.runningTasks.set(event.taskId, { exitCode: new Deferred<number | undefined>(), terminateSignal: new Deferred<string | undefined>() });
const task = event.config;
let taskIdentifier = event.taskId.toString();
if (task) {
taskIdentifier = this.taskNameResolver.resolve(task);
}
this.messageService.info(`Task ${taskIdentifier} has been started`);
const taskConfig = event.config;
const taskIdentifier = taskConfig ? this.getTaskIdentifier(taskConfig) : event.taskId.toString();
this.messageService.info(`Task '${taskIdentifier}' has been started.`);
});

this.taskWatcher.onOutputProcessed((event: TaskOutputProcessedEvent) => {
Expand Down Expand Up @@ -210,27 +214,29 @@ export class TaskService implements TaskConfigurationClient {
this.runningTasks.get(event.taskId)!.terminateSignal.resolve(event.signal);
setTimeout(() => this.runningTasks.delete(event.taskId), 60 * 1000);

const taskConfiguration = event.config;
let taskIdentifier = event.taskId.toString();
if (taskConfiguration) {
taskIdentifier = this.taskNameResolver.resolve(taskConfiguration);
}

const taskConfig = event.config;
const taskIdentifier = taskConfig ? this.getTaskIdentifier(taskConfig) : event.taskId.toString();
if (event.code !== undefined) {
const message = `Task ${taskIdentifier} has exited with code ${event.code}.`;
const message = `Task '${taskIdentifier}' has exited with code ${event.code}.`;
if (event.code === 0) {
this.messageService.info(message);
} else {
this.messageService.error(message);
}
} else if (event.signal !== undefined) {
this.messageService.info(`Task ${taskIdentifier} was terminated by signal ${event.signal}.`);
this.messageService.info(`Task '${taskIdentifier}' was terminated by signal ${event.signal}.`);
} else {
console.error('Invalid TaskExitedEvent received, neither code nor signal is set.');
}
});
}

private getTaskIdentifier(taskConfig: TaskConfiguration): string {
const taskName = this.taskNameResolver.resolve(taskConfig);
const sourceStrUri = this.taskSourceResolver.resolve(taskConfig);
return `${taskName} (${this.labelProvider.getName(new URI(sourceStrUri))})`;
}

/** Returns an array of the task configurations configured in tasks.json and provided by the extensions. */
async getTasks(): Promise<TaskConfiguration[]> {
const configuredTasks = await this.getConfiguredTasks();
Expand Down Expand Up @@ -409,6 +415,52 @@ export class TaskService implements TaskConfigurationClient {
}

async runTask(task: TaskConfiguration, option?: RunTaskOption): Promise<TaskInfo | undefined> {
const runningTasksInfo: TaskInfo[] = await this.getRunningTasks();

// check if the task is active
const matchedRunningTaskInfo = runningTasksInfo.find(taskInfo => {
const taskConfig = taskInfo.config;
return this.taskDefinitionRegistry.compareTasks(taskConfig, task);
});
if (matchedRunningTaskInfo) { // the task is active
const taskName = this.taskNameResolver.resolve(task);
const terminalId = matchedRunningTaskInfo.terminalId;
if (terminalId) {
const terminal = this.terminalService.getById(this.getTerminalWidgetId(terminalId));
if (terminal) {
this.shell.activateWidget(terminal.id); // make the terminal visible and assign focus
}
}
const selectedAction = await this.messageService.info(`The task '${taskName}' is already active`, 'Terminate Task', 'Restart Task');
if (selectedAction === 'Terminate Task') {
await this.terminateTask(matchedRunningTaskInfo);
} else if (selectedAction === 'Restart Task') {
return this.restartTask(matchedRunningTaskInfo, option);
}
} else { // run task as the task is not active
return this.doRunTask(task, option);
}
}

/**
* Terminates a task that is actively running.
* @param activeTaskInfo the TaskInfo of the task that is actively running
*/
protected async terminateTask(activeTaskInfo: TaskInfo): Promise<void> {
const taskId = activeTaskInfo.taskId;
return this.kill(taskId);
}

/**
* Terminates a task that is actively running, and restarts it.
* @param activeTaskInfo the TaskInfo of the task that is actively running
*/
protected async restartTask(activeTaskInfo: TaskInfo, option?: RunTaskOption): Promise<TaskInfo | undefined> {
await this.terminateTask(activeTaskInfo);
return this.doRunTask(activeTaskInfo.config, option);
}

protected async doRunTask(task: TaskConfiguration, option?: RunTaskOption): Promise<TaskInfo | undefined> {
if (option && option.customization) {
const taskDefinition = this.taskDefinitionRegistry.getDefinition(task);
if (taskDefinition) { // use the customization object to override the task config
Expand Down Expand Up @@ -648,7 +700,7 @@ export class TaskService implements TaskConfigurationClient {
TERMINAL_WIDGET_FACTORY_ID,
<TerminalWidgetFactoryOptions>{
created: new Date().toString(),
akosyakov marked this conversation as resolved.
Show resolved Hide resolved
id: 'terminal-' + processId,
id: this.getTerminalWidgetId(processId),
title: taskInfo
? `Task: ${taskInfo.config.label}`
: `Task: #${taskId}`,
Expand All @@ -660,6 +712,10 @@ export class TaskService implements TaskConfigurationClient {
widget.start(processId);
}

private getTerminalWidgetId(terminalId: number): string {
return `${TERMINAL_WIDGET_FACTORY_ID}-${terminalId}`;
}

async configure(task: TaskConfiguration): Promise<void> {
await this.taskConfigurations.configure(task);
}
Expand Down
43 changes: 43 additions & 0 deletions packages/task/src/browser/task-source-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/********************************************************************************
* Copyright (C) 2019 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { inject, injectable } from 'inversify';
import { TaskConfiguration, ContributedTaskConfiguration } from '../common';
import { TaskDefinitionRegistry } from './task-definition-registry';

@injectable()
export class TaskSourceResolver {
@inject(TaskDefinitionRegistry)
protected taskDefinitionRegistry: TaskDefinitionRegistry;

/**
* Returns task source to display.
*/
resolve(task: TaskConfiguration): string | undefined {
const isDetectedTask = this.isDetectedTask(task);
let sourceFolderUri: string | undefined;
if (isDetectedTask) {
sourceFolderUri = task._scope;
} else {
sourceFolderUri = task._source;
}
return sourceFolderUri;
}

private isDetectedTask(task: TaskConfiguration): task is ContributedTaskConfiguration {
return !!this.taskDefinitionRegistry.getDefinition(task);
}
}