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

Fixes 38519: Clean up event emitter in task land

上级 e5c5a35e
......@@ -38,7 +38,7 @@ import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
import { ITaskService, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
import { ITaskService, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
......@@ -52,6 +52,7 @@ import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EX
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { TaskEvent, TaskEventKind } from 'vs/workbench/parts/tasks/common/tasks';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
......@@ -954,6 +955,17 @@ export class DebugService implements debug.IDebugService {
return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
}
function once(kind: TaskEventKind, event: Event<TaskEvent>): Event<TaskEvent> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
if (e.kind === kind) {
result.dispose();
return listener.call(thisArgs, e);
}
}, null, disposables);
return result;
};
}
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
let taskStarted = false;
const promise = this.taskService.getActiveTasks().then(tasks => {
......@@ -961,11 +973,16 @@ export class DebugService implements debug.IDebugService {
// task is already running - nothing to do.
return TPromise.as(null);
}
this.toDisposeOnSessionEnd.get(sessionId).push(this.taskService.addOneTimeListener(TaskServiceEvents.Active, () => taskStarted = true));
this.toDisposeOnSessionEnd.get(sessionId).push(
once(TaskEventKind.Active, this.taskService.onDidStateChange)(() => {
taskStarted = true;
})
);
const taskPromise = this.taskService.run(task);
if (task.isBackground) {
return new TPromise((c, e) => this.toDisposeOnSessionEnd.get(sessionId).push(this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null))));
return new TPromise((c, e) => this.toDisposeOnSessionEnd.get(sessionId).push(
once(TaskEventKind.Inactive, this.taskService.onDidStateChange)(() => c(null)))
);
}
return taskPromise;
......
......@@ -6,7 +6,7 @@
import { IStringDictionary, INumberDictionary } from 'vs/base/common/collections';
import URI from 'vs/base/common/uri';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IModelService } from 'vs/editor/common/services/modelService';
......@@ -14,16 +14,26 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ILineMatcher, createLineMatcher, ProblemMatcher, ProblemMatch, ApplyToKind, WatchingPattern, getResource } from 'vs/platform/markers/common/problemMatcher';
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
export namespace ProblemCollectorEvents {
export let WatchingBeginDetected: string = 'watchingBeginDetected';
export let WatchingEndDetected: string = 'watchingEndDetected';
export enum ProblemCollectorEventKind {
BackgroundProcessingBegins = 'backgroundProcessingBegins',
BackgroundProcessingEnds = 'backgroundProcessingEnds'
}
export interface ProblemCollectorEvent {
kind: ProblemCollectorEventKind;
}
namespace ProblemCollectorEvent {
export function create(kind: ProblemCollectorEventKind) {
return Object.freeze({ kind });
}
}
export interface IProblemMatcher {
processLine(line: string): void;
}
export class AbstractProblemCollector extends EventEmitter implements IDisposable {
export class AbstractProblemCollector implements IDisposable {
private matchers: INumberDictionary<ILineMatcher[]>;
private activeMatcher: ILineMatcher;
......@@ -42,8 +52,9 @@ export class AbstractProblemCollector extends EventEmitter implements IDisposabl
// [owner] -> [resource] -> number;
private deliveredMarkers: Map<string, Map<string, number>>;
protected _onDidStateChange: Emitter<ProblemCollectorEvent>;
constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService) {
super();
this.matchers = Object.create(null);
this.bufferLength = 1;
problemMatchers.map(elem => createLineMatcher(elem)).forEach((matcher) => {
......@@ -82,6 +93,12 @@ export class AbstractProblemCollector extends EventEmitter implements IDisposabl
delete this.openModels[model.uri.toString()];
}, this, this.modelListeners);
this.modelService.getModels().forEach(model => this.openModels[model.uri.toString()] = true);
this._onDidStateChange = new Emitter();
}
public get onDidStateChange(): Event<ProblemCollectorEvent> {
return this._onDidStateChange.event;
}
public dispose() {
......@@ -368,7 +385,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
public aboutToStart(): void {
this.problemMatchers.forEach(matcher => {
if (matcher.watching && matcher.watching.activeOnStart) {
this.emit(ProblemCollectorEvents.WatchingBeginDetected, {});
this._onDidStateChange.fire(ProblemCollectorEvent.create(ProblemCollectorEventKind.BackgroundProcessingBegins));
this.recordResourcesToClean(matcher.owner);
}
});
......@@ -408,7 +425,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
let matches = beginMatcher.pattern.regexp.exec(line);
if (matches) {
result = true;
this.emit(ProblemCollectorEvents.WatchingBeginDetected, {});
this._onDidStateChange.fire(ProblemCollectorEvent.create(ProblemCollectorEventKind.BackgroundProcessingBegins));
this.cleanMarkerCaches();
this.resetCurrentResource();
let owner = beginMatcher.problemMatcher.owner;
......@@ -430,7 +447,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
let endMatcher = this.watchingEndsPatterns[i];
let matches = endMatcher.pattern.regexp.exec(line);
if (matches) {
this.emit(ProblemCollectorEvents.WatchingEndDetected, {});
this._onDidStateChange.fire(ProblemCollectorEvent.create(ProblemCollectorEventKind.BackgroundProcessingEnds));
result = true;
let owner = endMatcher.problemMatcher.owner;
this.resetCurrentResource();
......
......@@ -6,26 +6,18 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { IEventEmitter } from 'vs/base/common/eventEmitter';
import Event from 'vs/base/common/event';
import { LinkedMap } from 'vs/base/common/map';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Task, ContributedTask, CustomTask, TaskSet, TaskSorter } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskSummary, TaskEvent, TaskType, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, ContributedTask, CustomTask, TaskSet, TaskSorter, TaskEvent } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskSummary, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
export { ITaskSummary, Task, TaskEvent, TaskType, TaskTerminateResponse };
export { ITaskSummary, Task, TaskTerminateResponse };
export const ITaskService = createDecorator<ITaskService>('taskService');
export namespace TaskServiceEvents {
export let Active: string = 'active';
export let Inactive: string = 'inactive';
export let ConfigChanged: string = 'configChanged';
export let Terminated: string = 'terminated';
export let Changed: string = 'changed';
}
export interface ITaskProvider {
provideTasks(): TPromise<TaskSet>;
}
......@@ -40,8 +32,9 @@ export interface CustomizationProperties {
isBackground?: boolean;
}
export interface ITaskService extends IEventEmitter {
export interface ITaskService {
_serviceBrand: any;
onDidStateChange: Event<TaskEvent>;
configureAction(): Action;
build(): TPromise<ITaskSummary>;
runTest(): TPromise<ITaskSummary>;
......
......@@ -7,11 +7,11 @@
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { TerminateResponse } from 'vs/base/common/processes';
import { IEventEmitter } from 'vs/base/common/eventEmitter';
import Event from 'vs/base/common/event';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Task } from './tasks';
import { Task, TaskEvent } from './tasks';
export enum TaskErrors {
NotConfigured,
......@@ -93,26 +93,6 @@ export interface ITaskExecuteResult {
};
}
export namespace TaskSystemEvents {
export let Active: string = 'active';
export let Inactive: string = 'inactive';
export let Terminated: string = 'terminated';
export let Changed: string = 'changed';
}
export enum TaskType {
SingleRun,
Watching
}
export interface TaskEvent {
taskId?: string;
taskName?: string;
type?: TaskType;
group?: string;
__task?: Task;
}
export interface ITaskResolver {
resolve(workspaceFolder: IWorkspaceFolder, identifier: string): Task;
}
......@@ -121,7 +101,8 @@ export interface TaskTerminateResponse extends TerminateResponse {
task: Task | undefined;
}
export interface ITaskSystem extends IEventEmitter {
export interface ITaskSystem {
onDidStateChange: Event<TaskEvent>;
run(task: Task, resolver: ITaskResolver): ITaskExecuteResult;
isActive(): TPromise<boolean>;
isActiveSync(): boolean;
......
......@@ -571,4 +571,45 @@ export class TaskSorter {
return 0;
}
}
}
export enum TaskEventKind {
Active = 'active',
Inactive = 'inactive',
Terminated = 'terminated',
Changed = 'changed',
}
export enum TaskRunType {
SingleRun = 'singleRun',
Background = 'background'
}
export interface TaskEvent {
kind: TaskEventKind;
taskId?: string;
taskName?: string;
runType?: TaskRunType;
group?: string;
__task?: Task;
}
export namespace TaskEvent {
export function create(kind: TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated, task: Task);
export function create(kind: TaskEventKind.Changed);
export function create(kind: TaskEventKind, task?: Task): TaskEvent {
if (task) {
return Object.freeze({
kind: kind,
taskId: task._id,
taskName: task.name,
runType: task.isBackground ? TaskRunType.Background : TaskRunType.SingleRun,
group: task.group,
__task: task,
});
} else {
return Object.freeze({ kind: TaskEventKind.Changed });
}
}
}
\ No newline at end of file
......@@ -17,7 +17,7 @@ import { IStringDictionary } from 'vs/base/common/collections';
import { Action } from 'vs/base/common/actions';
import * as Dom from 'vs/base/browser/dom';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import Event, { Emitter } from 'vs/base/common/event';
import * as Builder from 'vs/base/browser/builder';
import * as Types from 'vs/base/common/types';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
......@@ -69,9 +69,9 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier, TaskSorter } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, TaskServiceEvents, ITaskProvider, TaskEvent, RunOptions, CustomizationProperties } from 'vs/workbench/parts/tasks/common/taskService';
import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent, TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier, TaskSorter } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, ITaskProvider, RunOptions, CustomizationProperties } from 'vs/workbench/parts/tasks/common/taskService';
import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
import * as TaskConfig from '../node/taskConfiguration';
......@@ -229,37 +229,33 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem {
updateLabel(this.markerService.getStatistics());
});
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Active, (event: TaskEvent) => {
callOnDispose.push(this.taskService.onDidStateChange((event) => {
if (this.ignoreEvent(event)) {
return;
}
this.activeCount++;
if (this.activeCount === 1) {
$(building).show();
}
}));
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Inactive, (event: TaskEvent) => {
if (this.ignoreEvent(event)) {
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) {
this.activeCount--;
if (this.activeCount === 0) {
$(building).hide();
}
}
}));
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Terminated, (event: TaskEvent) => {
if (this.ignoreEvent(event)) {
return;
}
if (this.activeCount !== 0) {
$(building).hide();
this.activeCount = 0;
switch (event.kind) {
case TaskEventKind.Active:
this.activeCount++;
if (this.activeCount === 1) {
$(building).show();
}
break;
case TaskEventKind.Inactive:
// 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) {
this.activeCount--;
if (this.activeCount === 0) {
$(building).hide();
}
}
break;
case TaskEventKind.Terminated:
if (this.activeCount !== 0) {
$(building).hide();
this.activeCount = 0;
}
break;
}
}));
......@@ -331,8 +327,10 @@ class TaskStatusBarItem extends Themable implements IStatusbarItem {
});
};
callOnDispose.push(this.taskService.addListener(TaskServiceEvents.Changed, (event: TaskEvent) => {
updateStatus();
callOnDispose.push(this.taskService.onDidStateChange((event) => {
if (event.kind === TaskEventKind.Changed) {
updateStatus();
}
}));
container.appendChild(element);
......@@ -442,7 +440,7 @@ interface TaskQuickPickEntry extends IPickOpenEntry {
task: Task;
}
class TaskService extends EventEmitter implements ITaskService {
class TaskService implements ITaskService {
// private static autoDetectTelemetryName: string = 'taskServer.autoDetect';
private static RecentlyUsedTasks_Key = 'workbench.tasks.recentlyUsedTasks';
......@@ -481,10 +479,11 @@ class TaskService extends EventEmitter implements ITaskService {
private _workspaceTasksPromise: TPromise<Map<string, WorkspaceFolderTaskResult>>;
private _taskSystem: ITaskSystem;
private _taskSystemListeners: IDisposable[];
private _taskSystemListener: IDisposable;
private _recentlyUsedTasks: LinkedMap<string, string>;
private _outputChannel: IOutputChannel;
private _onDidStateChange: Emitter<TaskEvent>;
constructor(
@IConfigurationService configurationService: IConfigurationService,
......@@ -503,8 +502,6 @@ class TaskService extends EventEmitter implements ITaskService {
@IOpenerService private openerService: IOpenerService,
@IWindowService private _windowServive: IWindowService
) {
super();
this.configurationService = configurationService;
this.markerService = markerService;
this.outputService = outputService;
......@@ -522,7 +519,7 @@ class TaskService extends EventEmitter implements ITaskService {
this._configHasErrors = false;
this._workspaceTasksPromise = undefined;
this._taskSystem = undefined;
this._taskSystemListeners = [];
this._taskSystemListener = undefined;
this._outputChannel = this.outputService.getChannel(TaskService.OutputChannelId);
this._providers = new Map<number, ITaskProvider>();
this.configurationService.onDidChangeConfiguration(() => {
......@@ -558,9 +555,14 @@ class TaskService extends EventEmitter implements ITaskService {
this.updateWorkspaceTasks();
});
lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown()));
this._onDidStateChange = new Emitter();
this.registerCommands();
}
public get onDidStateChange(): Event<TaskEvent> {
return this._onDidStateChange.event;
}
private registerCommands(): void {
CommandsRegistry.registerCommand('workbench.action.tasks.runTask', (accessor, arg) => {
this.runTaskCommand(accessor, arg);
......@@ -683,7 +685,9 @@ class TaskService extends EventEmitter implements ITaskService {
}
private disposeTaskSystemListeners(): void {
this._taskSystemListeners = dispose(this._taskSystemListeners);
if (this._taskSystemListener) {
this._taskSystemListener.dispose();
}
}
public registerTaskProvider(handle: number, provider: ITaskProvider): void {
......@@ -1209,7 +1213,6 @@ class TaskService extends EventEmitter implements ITaskService {
}
this._taskSystem.terminate(task).then((response) => {
if (response.success) {
this.emit(TaskServiceEvents.Terminated, {});
this.run(task);
} else {
this.messageService.show(Severity.Warning, nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.name));
......@@ -1250,10 +1253,9 @@ class TaskService extends EventEmitter implements ITaskService {
system.hasErrors(this._configHasErrors);
this._taskSystem = system;
}
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)));
this._taskSystemListeners.push(this._taskSystem.addListener(TaskSystemEvents.Changed, () => this.emit(TaskServiceEvents.Changed)));
this._taskSystemListener = this._taskSystem.onDidStateChange((event) => {
this._onDidStateChange.fire(event);
});
return this._taskSystem;
}
......@@ -1681,7 +1683,6 @@ class TaskService extends EventEmitter implements ITaskService {
}
}
if (success) {
this.emit(TaskServiceEvents.Terminated, {});
this._taskSystem = null;
this.disposeTaskSystemListeners();
return false; // no veto
......
......@@ -16,7 +16,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IStringDictionary } from 'vs/base/common/collections';
import { LinkedMap, Touch } from 'vs/base/common/map';
import Severity from 'vs/base/common/severity';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as TPath from 'vs/base/common/paths';
......@@ -30,11 +30,14 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors';
import { Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind } from 'vs/workbench/parts/tasks/common/tasks';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/parts/tasks/common/problemCollectors';
import {
Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind,
TaskEvent, TaskEventKind
} from 'vs/workbench/parts/tasks/common/tasks';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse
TelemetryEvent, Triggers, TaskTerminateResponse
} from 'vs/workbench/parts/tasks/common/taskSystem';
interface TerminalData {
......@@ -48,7 +51,7 @@ interface ActiveTerminalData {
promise: TPromise<ITaskSummary>;
}
export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
export class TerminalTaskSystem implements ITaskSystem {
public static TelemetryEventName: string = 'taskService';
......@@ -58,19 +61,26 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
private idleTaskTerminals: LinkedMap<string, string>;
private sameTaskTerminals: IStringDictionary<string>;
private _onDidStateChange: Emitter<TaskEvent>;
constructor(private terminalService: ITerminalService, private outputService: IOutputService,
private markerService: IMarkerService, private modelService: IModelService,
private configurationResolverService: IConfigurationResolverService,
private telemetryService: ITelemetryService,
private contextService: IWorkspaceContextService,
outputChannelId: string) {
super();
this.outputChannel = this.outputService.getChannel(outputChannelId);
this.activeTasks = Object.create(null);
this.terminals = Object.create(null);
this.idleTaskTerminals = new LinkedMap<string, string>();
this.sameTaskTerminals = Object.create(null);
this._onDidStateChange = new Emitter();
}
public get onDidStateChange(): Event<TaskEvent> {
return this._onDidStateChange.event;
}
public log(value: string): void {
......@@ -152,8 +162,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
let task = activeTerminal.task;
try {
onExit.dispose();
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group, __task: task };
this.emit(TaskSystemEvents.Terminated, event);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Terminated, task));
} catch (error) {
// Do nothing.
}
......@@ -173,8 +182,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
let task = terminalData.task;
try {
onExit.dispose();
let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group, __task: task };
this.emit(TaskSystemEvents.Terminated, event);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Terminated, task));
} catch (error) {
// Do nothing.
}
......@@ -237,15 +245,15 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
const problemMatchers = this.resolveMatchers(task, 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, group: task.group, __task: task };
let eventCounter: number = 0;
toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingBeginDetected, () => {
eventCounter++;
this.emit(TaskSystemEvents.Active, event);
}));
toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingEndDetected, () => {
eventCounter--;
this.emit(TaskSystemEvents.Inactive, event);
toUnbind.push(watchingProblemMatcher.onDidStateChange((event) => {
if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) {
eventCounter++;
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
} else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) {
eventCounter--;
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
}
}));
watchingProblemMatcher.aboutToStart();
let delayer: Async.Delayer<any> = undefined;
......@@ -266,7 +274,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
onExit.dispose();
let key = Task.getMapKey(task);
delete this.activeTasks[key];
this.emit(TaskSystemEvents.Changed);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed));
switch (task.command.presentation.panel) {
case PanelKind.Dedicated:
this.sameTaskTerminals[key] = terminal.id.toString();
......@@ -281,7 +289,8 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
toUnbind = dispose(toUnbind);
toUnbind = null;
for (let i = 0; i < eventCounter; i++) {
this.emit(TaskSystemEvents.Inactive, event);
let event = TaskEvent.create(TaskEventKind.Inactive, task);
this._onDidStateChange.fire(event);
}
eventCounter = 0;
let reveal = task.command.presentation.reveal;
......@@ -295,8 +304,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, group: task.group, __task: task };
this.emit(TaskSystemEvents.Active, event);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
let problemMatchers = this.resolveMatchers(task, task.problemMatchers);
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService);
const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers);
......@@ -308,7 +316,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
onExit.dispose();
let key = Task.getMapKey(task);
delete this.activeTasks[key];
this.emit(TaskSystemEvents.Changed);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed));
switch (task.command.presentation.panel) {
case PanelKind.Dedicated:
this.sameTaskTerminals[key] = terminal.id.toString();
......@@ -320,7 +328,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
startStopProblemMatcher.done();
startStopProblemMatcher.dispose();
registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle));
this.emit(TaskSystemEvents.Inactive, event);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
// See https://github.com/Microsoft/vscode/issues/31965
if (exitCode === 0 && startStopProblemMatcher.numberOfMatches > 0) {
exitCode = 1;
......@@ -337,7 +345,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
this.terminalService.showPanel(task.command.presentation.focus);
}
this.activeTasks[Task.getMapKey(task)] = { terminal, task, promise };
this.emit(TaskSystemEvents.Changed);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed));
return promise.then((summary) => {
try {
let telemetryEvent: TelemetryEvent = {
......
......@@ -12,7 +12,7 @@ import { TPromise, Promise } from 'vs/base/common/winjs.base';
import * as Async from 'vs/base/common/async';
import Severity from 'vs/base/common/severity';
import * as Strings from 'vs/base/common/strings';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import Event, { Emitter } from 'vs/base/common/event';
import { SuccessData, ErrorData } from 'vs/base/common/processes';
import { LineProcess, LineData } from 'vs/base/node/processes';
......@@ -25,16 +25,19 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ProblemMatcher, ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/parts/tasks/common/problemCollectors';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TelemetryEvent, Triggers,
TaskSystemEvents, TaskEvent, TaskType, TaskTerminateResponse
TaskTerminateResponse
} from 'vs/workbench/parts/tasks/common/taskSystem';
import { Task, CustomTask, CommandOptions, RevealKind, CommandConfiguration, RuntimeType } from 'vs/workbench/parts/tasks/common/tasks';
import {
Task, CustomTask, CommandOptions, RevealKind, CommandConfiguration, RuntimeType,
TaskEvent, TaskEventKind
} from 'vs/workbench/parts/tasks/common/tasks';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
export class ProcessTaskSystem implements ITaskSystem {
public static TelemetryEventName: string = 'taskService';
......@@ -51,9 +54,10 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
private activeTask: CustomTask;
private activeTaskPromise: TPromise<ITaskSummary>;
private _onDidStateChange: Emitter<TaskEvent>;
constructor(markerService: IMarkerService, modelService: IModelService, telemetryService: ITelemetryService,
outputService: IOutputService, configurationResolverService: IConfigurationResolverService, outputChannelId: string) {
super();
this.markerService = markerService;
this.modelService = modelService;
this.outputService = outputService;
......@@ -65,6 +69,11 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
this.activeTaskPromise = null;
this.outputChannel = this.outputService.getChannel(outputChannelId);
this.errorsShown = true;
this._onDidStateChange = new Emitter();
}
public get onDidStateChange(): Event<TaskEvent> {
return this._onDidStateChange.event;
}
public isActive(): TPromise<boolean> {
......@@ -121,8 +130,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
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);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Terminated, task));
return [result];
});
}
......@@ -216,19 +224,20 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
if (task.isBackground) {
let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task, task.problemMatchers), this.markerService, this.modelService);
let toUnbind: IDisposable[] = [];
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++;
this.emit(TaskSystemEvents.Active, event);
}));
toUnbind.push(watchingProblemMatcher.addListener(ProblemCollectorEvents.WatchingEndDetected, () => {
eventCounter--;
this.emit(TaskSystemEvents.Inactive, event);
toUnbind.push(watchingProblemMatcher.onDidStateChange((event) => {
if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) {
eventCounter++;
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
} else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) {
eventCounter--;
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
}
}));
watchingProblemMatcher.aboutToStart();
let delayer: Async.Delayer<any> = null;
this.activeTask = task;
const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task);
this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => {
this.childProcessEnded();
watchingProblemMatcher.done();
......@@ -236,7 +245,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
toUnbind = dispose(toUnbind);
toUnbind = null;
for (let i = 0; i < eventCounter; i++) {
this.emit(TaskSystemEvents.Inactive, event);
this._onDidStateChange.fire(inactiveEvent);
}
eventCounter = 0;
if (!this.checkTerminated(task, success)) {
......@@ -253,7 +262,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
toUnbind = dispose(toUnbind);
toUnbind = null;
for (let i = 0; i < eventCounter; i++) {
this.emit(TaskSystemEvents.Inactive, event);
this._onDidStateChange.fire(inactiveEvent);
}
eventCounter = 0;
return this.handleError(task, error);
......@@ -276,16 +285,16 @@ 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, group: task.group };
this.emit(TaskSystemEvents.Active, event);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task, task.problemMatchers), this.markerService, this.modelService);
this.activeTask = task;
const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task);
this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => {
this.childProcessEnded();
startStopProblemMatcher.done();
startStopProblemMatcher.dispose();
this.checkTerminated(task, success);
this.emit(TaskSystemEvents.Inactive, event);
this._onDidStateChange.fire(inactiveEvent);
if (success.cmdCode && success.cmdCode === 1 && startStopProblemMatcher.numberOfMatches === 0 && reveal !== RevealKind.Never) {
this.showOutput();
}
......@@ -294,7 +303,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem {
}, (error: ErrorData) => {
this.childProcessEnded();
startStopProblemMatcher.dispose();
this.emit(TaskSystemEvents.Inactive, event);
this._onDidStateChange.fire(inactiveEvent);
return this.handleError(task, error);
}, (progress) => {
let line = Strings.removeAnsiEscapeCodes(progress.line);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册