提交 f4e3dec1 编写于 作者: D Dirk Baeumer

Fixes #29007: Show task spinner only if a build task is executed.

上级 2f1508ee
......@@ -7,13 +7,12 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { IEventEmitter } from 'vs/base/common/eventEmitter';
import { TerminateResponse } from 'vs/base/common/processes';
import { LinkedMap } from 'vs/base/common/map';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Task, TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskSummary, TaskEvent, TaskType } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskSummary, TaskEvent, TaskType, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
export { ITaskSummary, Task, TaskEvent, TaskType };
export { ITaskSummary, Task, TaskEvent, TaskType, TaskTerminateResponse };
export const ITaskService = createDecorator<ITaskService>('taskService');
......@@ -40,8 +39,8 @@ export interface ITaskService extends IEventEmitter {
isActive(): TPromise<boolean>;
getActiveTasks(): TPromise<Task[]>;
restart(task: string | Task): void;
terminate(task: string | Task): TPromise<TerminateResponse>;
terminateAll(): TPromise<TerminateResponse>;
terminate(task: string | Task): TPromise<TaskTerminateResponse>;
terminateAll(): TPromise<TaskTerminateResponse[]>;
tasks(): TPromise<Task[]>;
getTasksForGroup(group: string): TPromise<Task[]>;
getRecentlyUsedTasks(): LinkedMap<string, string>;
......
......@@ -81,6 +81,7 @@ export interface ITaskExecuteResult {
export namespace TaskSystemEvents {
export let Active: string = 'active';
export let Inactive: string = 'inactive';
export let Terminated: string = 'terminated';
}
export enum TaskType {
......@@ -92,18 +93,23 @@ export interface TaskEvent {
taskId?: string;
taskName?: string;
type?: TaskType;
group?: string;
}
export interface ITaskResolver {
resolve(identifier: string): Task;
}
export interface TaskTerminateResponse extends TerminateResponse {
task: Task | undefined;
}
export interface ITaskSystem extends IEventEmitter {
run(task: Task, resolver: ITaskResolver): ITaskExecuteResult;
isActive(): TPromise<boolean>;
isActiveSync(): boolean;
getActiveTasks(): Task[];
canAutoTerminate(): boolean;
terminate(id: string): TPromise<TerminateResponse>;
terminateAll(): TPromise<TerminateResponse>;
terminate(id: string): TPromise<TaskTerminateResponse>;
terminateAll(): TPromise<TaskTerminateResponse[]>;
}
\ No newline at end of file
......@@ -25,7 +25,7 @@ import { EventEmitter } from 'vs/base/common/eventEmitter';
import * as Builder from 'vs/base/browser/builder';
import * as Types from 'vs/base/common/types';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { TerminateResponse, TerminateResponseCode } from 'vs/base/common/processes';
import { TerminateResponseCode } from 'vs/base/common/processes';
import * as strings from 'vs/base/common/strings';
import { ValidationStatus, ValidationState } from 'vs/base/common/parsers';
import * as UUID from 'vs/base/common/uuid';
......@@ -73,9 +73,9 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, TaskServiceEvents, ITaskProvider } from 'vs/workbench/parts/tasks/common/taskService';
import { ITaskService, TaskServiceEvents, ITaskProvider, TaskEvent } from 'vs/workbench/parts/tasks/common/taskService';
import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
import * as TaskConfig from 'vs/workbench/parts/tasks/common/taskConfiguration';
......@@ -378,7 +378,10 @@ class StatusBarItem extends Themable implements IStatusbarItem {
updateLabel(this.markerService.getStatistics());
});
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Active, () => {
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Active, (event: TaskEvent) => {
if (event.group !== TaskGroup.Build) {
return;
}
this.activeCount++;
if (this.activeCount === 1) {
let index = 1;
......@@ -395,7 +398,10 @@ class StatusBarItem extends Themable implements IStatusbarItem {
}
}));
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Inactive, (data: TaskServiceEventData) => {
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Inactive, (event: TaskEvent) => {
if (event.group !== TaskGroup.Build) {
return;
}
// Since the exiting of the sub process is communicated async we can't order inactive and terminate events.
// So try to treat them accordingly.
if (this.activeCount > 0) {
......@@ -410,7 +416,10 @@ class StatusBarItem extends Themable implements IStatusbarItem {
}
}));
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Terminated, () => {
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Terminated, (event: TaskEvent) => {
if (event.group !== TaskGroup.Build) {
return;
}
if (this.activeCount !== 0) {
$(progress).hide();
if (this.intervalToken) {
......@@ -456,11 +465,11 @@ class NullTaskSystem extends EventEmitter implements ITaskSystem {
public canAutoTerminate(): boolean {
return true;
}
public terminate(task: string | Task): TPromise<TerminateResponse> {
return TPromise.as<TerminateResponse>({ success: true });
public terminate(task: string | Task): TPromise<TaskTerminateResponse> {
return TPromise.as<TaskTerminateResponse>({ success: true, task: undefined });
}
public terminateAll(): TPromise<TerminateResponse> {
return TPromise.as<TerminateResponse>({ success: true });
public terminateAll(): TPromise<TaskTerminateResponse[]> {
return TPromise.as<TaskTerminateResponse[]>([]);
}
}
......@@ -961,27 +970,19 @@ class TaskService extends EventEmitter implements ITaskService {
});
}
public terminate(task: string | Task): TPromise<TerminateResponse> {
public terminate(task: string | Task): TPromise<TaskTerminateResponse> {
if (!this._taskSystem) {
return TPromise.as({ success: true });
}
const id: string = Types.isString(task) ? task : task._id;
return this._taskSystem.terminate(id).then((response) => {
if (response.success) {
this.emit(TaskServiceEvents.Terminated, {});
}
return response;
});
return this._taskSystem.terminate(id);
}
public terminateAll(): TPromise<TerminateResponse> {
public terminateAll(): TPromise<TaskTerminateResponse[]> {
if (!this._taskSystem) {
return TPromise.as({ success: true });
return TPromise.as<TaskTerminateResponse[]>([]);
}
return this._taskSystem.terminateAll().then((response) => {
this.emit(TaskServiceEvents.Terminated, {});
return response;
});
return this._taskSystem.terminateAll();
}
private getTaskSystem(): ITaskSystem {
......@@ -1006,6 +1007,7 @@ class TaskService extends EventEmitter implements ITaskService {
}
this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Active, (event) => this.emit(TaskServiceEvents.Active, event)));
this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Inactive, (event) => this.emit(TaskServiceEvents.Inactive, event)));
this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Terminated, (event) => this.emit(TaskServiceEvents.Terminated, event)));
return this._taskSystem;
}
......@@ -1292,35 +1294,53 @@ class TaskService extends EventEmitter implements ITaskService {
}
public beforeShutdown(): boolean | TPromise<boolean> {
if (!this._taskSystem) {
return false;
}
this.saveRecentlyUsedTasks();
if (this._taskSystem && this._taskSystem.isActiveSync()) {
if (this._taskSystem.canAutoTerminate() || this.messageService.confirm({
message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'),
primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"),
type: 'question'
})) {
return this._taskSystem.terminateAll().then((response) => {
if (response.success) {
this.emit(TaskServiceEvents.Terminated, {});
this._taskSystem = null;
this.disposeTaskSystemListeners();
return false; // no veto
} else if (response.code && response.code === TerminateResponseCode.ProcessNotFound) {
return !this.messageService.confirm({
message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'),
primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"),
type: 'info'
});
if (!this._taskSystem.isActiveSync()) {
return false;
}
// The terminal service kills all terminal on shutdown. So there
// is nothing we can do to prevent this here.
if (this._taskSystem instanceof TerminalTaskSystem) {
return false;
}
if (this._taskSystem.canAutoTerminate() || this.messageService.confirm({
message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'),
primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"),
type: 'question'
})) {
return this._taskSystem.terminateAll().then((responses) => {
let success = true;
let code: number = undefined;
for (let response of responses) {
success = success && response.success;
// We only have a code in the old output runner which only has one task
// So we can use the first code.
if (code === void 0 && response.code !== void 0) {
code = response.code;
}
return true; // veto
}, (err) => {
return true; // veto
});
} else {
}
if (success) {
this.emit(TaskServiceEvents.Terminated, {});
this._taskSystem = null;
this.disposeTaskSystemListeners();
return false; // no veto
} else if (code && code === TerminateResponseCode.ProcessNotFound) {
return !this.messageService.confirm({
message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'),
primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"),
type: 'info'
});
}
return true; // veto
}
}, (err) => {
return true; // veto
});
} else {
return true; // veto
}
return false; // Nothing to do here
}
private getConfigureAction(code: TaskErrors): Action {
......@@ -1450,7 +1470,9 @@ class TaskService extends EventEmitter implements ITaskService {
} else {
this.isActive().then((active) => {
if (active) {
this.terminateAll().then((response) => {
this.terminateAll().then((responses) => {
// the output runner has only one task
let response = responses[0];
if (response.success) {
return;
}
......
......@@ -19,7 +19,6 @@ import { LinkedMap, Touch } from 'vs/base/common/map';
import Severity from 'vs/base/common/severity';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TerminateResponse } from 'vs/base/common/processes';
import * as TPath from 'vs/base/common/paths';
// import URI from 'vs/base/common/uri';
......@@ -37,7 +36,7 @@ import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEv
import { Task, RevealKind, CommandOptions, ShellConfiguration, CommandType, PanelKind } from 'vs/workbench/parts/tasks/common/tasks';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType
TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse
} from 'vs/workbench/parts/tasks/common/taskSystem';
class TerminalDecoder {
......@@ -176,28 +175,50 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task);
}
public terminate(id: string): TPromise<TerminateResponse> {
let terminalData = this.activeTasks[id];
if (!terminalData) {
return TPromise.as<TerminateResponse>({ success: false });
public terminate(id: string): TPromise<TaskTerminateResponse> {
let activeTerminal = this.activeTasks[id];
if (!activeTerminal) {
return TPromise.as<TaskTerminateResponse>({ success: false, task: undefined });
};
return new TPromise<TerminateResponse>((resolve, reject) => {
let terminal = terminalData.terminal;
return new TPromise<TaskTerminateResponse>((resolve, reject) => {
let terminal = activeTerminal.terminal;
const onExit = terminal.onExit(() => {
onExit.dispose();
resolve({ success: true });
let task = activeTerminal.task;
try {
onExit.dispose();
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group };
this.emit(TaskSystemEvents.Terminated, event);
} catch (error) {
// Do nothing.
}
resolve({ success: true, task: task });
});
terminal.dispose();
});
}
public terminateAll(): TPromise<TerminateResponse> {
public terminateAll(): TPromise<TaskTerminateResponse[]> {
let promises: TPromise<TaskTerminateResponse>[] = [];
Object.keys(this.activeTasks).forEach((key) => {
let data = this.activeTasks[key];
data.terminal.dispose();
let terminalData = this.activeTasks[key];
let terminal = terminalData.terminal;
promises.push(new TPromise<TaskTerminateResponse>((resolve, reject) => {
const onExit = terminal.onExit(() => {
let task = terminalData.task;
try {
onExit.dispose();
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group };
this.emit(TaskSystemEvents.Terminated, event);
} catch (error) {
// Do nothing.
}
resolve({ success: true, task: terminalData.task });
});
}));
terminal.dispose();
});
this.activeTasks = Object.create(null);
return TPromise.as<TerminateResponse>({ success: true });
return TPromise.join<TaskTerminateResponse>(promises);
}
private executeTask(startedTasks: IStringDictionary<TPromise<ITaskSummary>>, task: Task, resolver: ITaskResolver, trigger: string): TPromise<ITaskSummary> {
......@@ -246,7 +267,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
const problemMatchers = this.resolveMatchers(task.problemMatchers);
let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService);
let toUnbind: IDisposable[] = [];
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching };
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching, group: task.group };
let eventCounter: number = 0;
toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingBeginDetected, () => {
eventCounter++;
......@@ -308,7 +329,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
} else {
promise = new TPromise<ITaskSummary>((resolve, reject) => {
[terminal, executedCommand] = this.createTerminal(task);
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun };
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group };
this.emit(TaskSystemEvents.Active, event);
let decoder = new TerminalDecoder();
let problemMatchers = this.resolveMatchers(task.problemMatchers);
......
......@@ -14,7 +14,7 @@ import Severity from 'vs/base/common/severity';
import * as Strings from 'vs/base/common/strings';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import { TerminateResponse, SuccessData, ErrorData } from 'vs/base/common/processes';
import { SuccessData, ErrorData } from 'vs/base/common/processes';
import { LineProcess, LineData } from 'vs/base/node/processes';
import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output';
......@@ -27,7 +27,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TelemetryEvent, Triggers,
TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse
} from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, CommandOptions, RevealKind, CommandConfiguration, CommandType } from 'vs/workbench/parts/tasks/common/tasks';
......@@ -103,18 +104,24 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
return true;
}
public terminate(_id: string): TPromise<TerminateResponse> {
public terminate(_id: string): TPromise<TaskTerminateResponse> {
if (!this.activeTask || this.activeTask._id !== _id) {
return TPromise.as<TerminateResponse>({ success: false });
return TPromise.as<TaskTerminateResponse>({ success: false, task: undefined });
}
return this.terminateAll();
return this.terminateAll()[0];
}
public terminateAll(): TPromise<TerminateResponse> {
public terminateAll(): TPromise<TaskTerminateResponse[]> {
if (this.childProcess) {
return this.childProcess.terminate();
let task = this.activeTask;
return this.childProcess.terminate().then((response) => {
let result: TaskTerminateResponse = Objects.assign({ task: task }, response);
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group };
this.emit(TaskSystemEvents.Terminated, event);
return [result];
});
}
return TPromise.as({ success: true });
return TPromise.as<TaskTerminateResponse[]>([{ success: true, task: undefined }]);
}
private executeTask(task: Task, trigger: string = Triggers.command): ITaskExecuteResult {
......@@ -179,7 +186,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
if (task.isBackground) {
let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService);
let toUnbind: IDisposable[] = [];
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching };
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching, group: task.group };
let eventCounter: number = 0;
toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingBeginDetected, () => {
eventCounter++;
......@@ -238,7 +245,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
: { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise };
return result;
} else {
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun };
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group };
this.emit(TaskSystemEvents.Active, event);
let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService);
this.activeTask = task;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册