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

Fixed #8509: Starting the same watching task should be ignored.

上级 974ded83
......@@ -10,7 +10,7 @@ import { TerminateResponse} from 'vs/base/common/processes';
import { IMode } from 'vs/editor/common/modes';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import { ITaskSystem, ITaskSummary, TaskDescription, TelemetryEvent, Triggers, TaskConfiguration, ITaskRunResult } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskSystem, ITaskSummary, TaskDescription, TelemetryEvent, Triggers, TaskConfiguration, ITaskExecuteResult, TaskExecuteKind } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -33,30 +33,30 @@ export class LanguageServiceTaskSystem extends EventEmitter implements ITaskSyst
this.modeService = modeService;
}
public build(): ITaskRunResult {
public build(): ITaskExecuteResult {
return this.processMode((mode) => {
return null;
}, 'build', Triggers.shortcut);
}
public rebuild(): ITaskRunResult {
public rebuild(): ITaskExecuteResult {
return this.processMode((mode) => {
return null;
}, 'rebuild', Triggers.shortcut);
}
public clean(): ITaskRunResult {
public clean(): ITaskExecuteResult {
return this.processMode((mode) => {
return null;
}, 'clean', Triggers.shortcut);
}
public runTest(): ITaskRunResult {
return { promise: TPromise.wrapError<ITaskSummary>('Not implemented yet.') };
public runTest(): ITaskExecuteResult {
return { kind: TaskExecuteKind.Started, promise: TPromise.wrapError<ITaskSummary>('Not implemented yet.') };
}
public run(taskIdentifier:string): ITaskRunResult {
return { promise: TPromise.wrapError<ITaskSummary>('Not implemented yet.') };
public run(taskIdentifier:string): ITaskExecuteResult {
return { kind: TaskExecuteKind.Started, promise: TPromise.wrapError<ITaskSummary>('Not implemented yet.') };
}
public isActive(): TPromise<boolean> {
......@@ -84,13 +84,13 @@ export class LanguageServiceTaskSystem extends EventEmitter implements ITaskSyst
return TPromise.as(result);
}
private processMode(fn: (mode: IMode) => Promise, taskName: string, trigger: string): ITaskRunResult {
private processMode(fn: (mode: IMode) => Promise, taskName: string, trigger: string): ITaskExecuteResult {
let telemetryEvent: TelemetryEvent = {
trigger: trigger,
command: 'languageService',
success: true
};
return { promise: Promise.join(this.configuration.modes.map((mode) => {
return { kind: TaskExecuteKind.Started, started: {}, promise: Promise.join(this.configuration.modes.map((mode) => {
return this.modeService.getOrCreateMode(mode);
})).then((modes: IMode[]) => {
let promises: Promise[] = [];
......@@ -104,7 +104,7 @@ export class LanguageServiceTaskSystem extends EventEmitter implements ITaskSyst
}).then((value) => {
this.telemetryService.publicLog(LanguageServiceTaskSystem.TelemetryEventName, telemetryEvent);
return value;
},(err) => {
}, (err) => {
telemetryEvent.success = false;
this.telemetryService.publicLog(LanguageServiceTaskSystem.TelemetryEventName, telemetryEvent);
return Promise.wrapError(err);
......
......@@ -188,9 +188,21 @@ export interface ITaskSummary {
exitCode?: number;
}
export interface ITaskRunResult {
restartOnFileChanges?: string;
export enum TaskExecuteKind {
Started = 1,
Active = 2
}
export interface ITaskExecuteResult {
kind: TaskExecuteKind;
promise: TPromise<ITaskSummary>;
started?: {
restartOnFileChanges?: string;
};
active?: {
same: boolean;
watching: boolean;
};
}
export namespace TaskSystemEvents {
......@@ -210,11 +222,11 @@ export interface TaskEvent {
}
export interface ITaskSystem extends IEventEmitter {
build(): ITaskRunResult;
rebuild(): ITaskRunResult;
clean(): ITaskRunResult;
runTest(): ITaskRunResult;
run(taskIdentifier: string): ITaskRunResult;
build(): ITaskExecuteResult;
rebuild(): ITaskExecuteResult;
clean(): ITaskExecuteResult;
runTest(): ITaskExecuteResult;
run(taskIdentifier: string): ITaskExecuteResult;
isActive(): TPromise<boolean>;
isActiveSync(): boolean;
canAutoTerminate(): boolean;
......
......@@ -60,7 +60,7 @@ import { ConfigVariables } from 'vs/workbench/parts/lib/node/configVariables';
import { ITextFileService, EventType } from 'vs/workbench/parts/files/common/files';
import { IOutputService, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannel } from 'vs/workbench/parts/output/common/output';
import { ITaskSystem, ITaskSummary, ITaskRunResult, TaskError, TaskErrors, TaskConfiguration, TaskDescription, TaskSystemEvents } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskConfiguration, TaskDescription, TaskSystemEvents } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskService, TaskServiceEvents } from 'vs/workbench/parts/tasks/common/taskService';
import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
......@@ -544,28 +544,33 @@ interface TaskServiceEventData {
}
class NullTaskSystem extends EventEmitter implements ITaskSystem {
public build(): ITaskRunResult {
public build(): ITaskExecuteResult {
return {
kind: TaskExecuteKind.Started,
promise: TPromise.as<ITaskSummary>({})
};
}
public rebuild(): ITaskRunResult {
public rebuild(): ITaskExecuteResult {
return {
kind: TaskExecuteKind.Started,
promise: TPromise.as<ITaskSummary>({})
};
}
public clean(): ITaskRunResult {
public clean(): ITaskExecuteResult {
return {
kind: TaskExecuteKind.Started,
promise: TPromise.as<ITaskSummary>({})
};
}
public runTest(): ITaskRunResult {
public runTest(): ITaskExecuteResult {
return {
kind: TaskExecuteKind.Started,
promise: TPromise.as<ITaskSummary>({})
};
}
public run(taskIdentifier: string): ITaskRunResult {
public run(taskIdentifier: string): ITaskExecuteResult {
return {
kind: TaskExecuteKind.Started,
promise: TPromise.as<ITaskSummary>({})
};
}
......@@ -812,43 +817,50 @@ class TaskService extends EventEmitter implements ITaskService {
return this.executeTarget(taskSystem => taskSystem.run(taskIdentifier));
}
private executeTarget(fn: (taskSystem: ITaskSystem) => ITaskRunResult): TPromise<ITaskSummary> {
private executeTarget(fn: (taskSystem: ITaskSystem) => ITaskExecuteResult): TPromise<ITaskSummary> {
return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
return this.configurationService.reloadConfiguration().then(() => { // make sure configuration is up to date
return this.taskSystemPromise.
then((taskSystem) => {
return taskSystem.isActive().then((active) => {
if (!active) {
return fn(taskSystem);
let executeResult = fn(taskSystem);
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active.same && active.watching) {
this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame', 'The task is already active and in watch mode.'));
} else {
throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is an active running task right now. Terminate it first before executing another task.'), TaskErrors.RunningTask);
}
});
}
return executeResult;
}).
then((runResult: ITaskRunResult) => {
if (runResult.restartOnFileChanges) {
let pattern = runResult.restartOnFileChanges;
this.fileChangesListener = this.eventService.addListener2(FileEventType.FILE_CHANGES, (event: FileChangesEvent) => {
let needsRestart = event.changes.some((change) => {
return (change.type === FileChangeType.ADDED || change.type === FileChangeType.DELETED) && !!match(pattern, change.resource.fsPath);
});
if (needsRestart) {
this.terminate().done(() => {
// We need to give the child process a change to stop.
setTimeout(() => {
this.executeTarget(fn);
}, 2000);
then((executeResult: ITaskExecuteResult) => {
if (executeResult.kind === TaskExecuteKind.Started) {
if (executeResult.started.restartOnFileChanges) {
let pattern = executeResult.started.restartOnFileChanges;
this.fileChangesListener = this.eventService.addListener2(FileEventType.FILE_CHANGES, (event: FileChangesEvent) => {
let needsRestart = event.changes.some((change) => {
return (change.type === FileChangeType.ADDED || change.type === FileChangeType.DELETED) && !!match(pattern, change.resource.fsPath);
});
if (needsRestart) {
this.terminate().done(() => {
// We need to give the child process a change to stop.
setTimeout(() => {
this.executeTarget(fn);
}, 2000);
});
}
});
}
return executeResult.promise.then((value) => {
if (this.clearTaskSystemPromise) {
this._taskSystemPromise = null;
this.clearTaskSystemPromise = false;
}
return value;
});
} else {
return executeResult.promise;
}
return runResult.promise.then((value) => {
if (this.clearTaskSystemPromise) {
this._taskSystemPromise = null;
this.clearTaskSystemPromise = false;
}
return value;
});
}, (err: any) => {
this.handleError(err);
});
......
......@@ -27,7 +27,7 @@ import { ProblemMatcher } 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 { ITaskSystem, ITaskSummary, ITaskRunResult, TaskError, TaskErrors, TaskRunnerConfiguration, TaskDescription, CommandOptions, ShowOutput, TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskRunnerConfiguration, TaskDescription, CommandOptions, ShowOutput, TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType } from 'vs/workbench/parts/tasks/common/taskSystem';
import * as FileConfig from './processRunnerConfiguration';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
......@@ -52,6 +52,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
private errorsShown: boolean;
private childProcess: LineProcess;
private activeTaskIdentifier: string;
private activeTaskPromise: TPromise<ITaskSummary>;
constructor(fileConfig:FileConfig.ExternalTaskRunnerConfiguration, variables:ISystemVariables, markerService:IMarkerService, modelService: IModelService, telemetryService: ITelemetryService, outputService:IOutputService, outputChannelId:string, clearOutput: boolean = true) {
super();
......@@ -66,6 +67,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
this.defaultTestTaskIdentifier = null;
this.childProcess = null;
this.activeTaskIdentifier = null;
this.activeTaskPromise = null;
this.outputChannel = this.outputService.getChannel(outputChannelId);
if (clearOutput) {
......@@ -84,29 +86,41 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
}
public build(): ITaskRunResult {
public build(): ITaskExecuteResult {
if (this.activeTaskIdentifier) {
let task = this.configuration.tasks[this.activeTaskIdentifier];
return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === this.defaultBuildTaskIdentifier, watching: task.isWatching }, promise: this.activeTaskPromise };
}
if (!this.defaultBuildTaskIdentifier) {
throw new TaskError(Severity.Info, nls.localize('TaskRunnerSystem.noBuildTask', 'No task is marked as a build task in the tasks.json. Mark a task with \'isBuildCommand\'.'), TaskErrors.NoBuildTask);
}
return this.executeTask(this.defaultBuildTaskIdentifier, Triggers.shortcut);
}
public rebuild(): ITaskRunResult {
public rebuild(): ITaskExecuteResult {
throw new Error('Task - Rebuild: not implemented yet');
}
public clean(): ITaskRunResult {
public clean(): ITaskExecuteResult {
throw new Error('Task - Clean: not implemented yet');
}
public runTest(): ITaskRunResult {
public runTest(): ITaskExecuteResult {
if (this.activeTaskIdentifier) {
let task = this.configuration.tasks[this.activeTaskIdentifier];
return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === this.defaultTestTaskIdentifier, watching: task.isWatching }, promise: this.activeTaskPromise };
}
if (!this.defaultTestTaskIdentifier) {
throw new TaskError(Severity.Info, nls.localize('TaskRunnerSystem.noTestTask', 'No test task configured.'), TaskErrors.NoTestTask);
}
return this.executeTask(this.defaultTestTaskIdentifier, Triggers.shortcut);
}
public run(taskIdentifier: string): ITaskRunResult {
public run(taskIdentifier: string): ITaskExecuteResult {
if (this.activeTaskIdentifier) {
let task = this.configuration.tasks[this.activeTaskIdentifier];
return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === taskIdentifier, watching: task.isWatching }, promise: this.activeTaskPromise };
}
return this.executeTask(taskIdentifier);
}
......@@ -148,7 +162,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
return TPromise.as(result);
}
private executeTask(taskIdentifier: string, trigger: string = Triggers.command): ITaskRunResult {
private executeTask(taskIdentifier: string, trigger: string = Triggers.command): ITaskExecuteResult {
if (this.validationStatus.isFatal()) {
throw new TaskError(Severity.Error, nls.localize('TaskRunnerSystem.fatalError', 'The provided task configuration has validation errors. See tasks output log for details.'), TaskErrors.ConfigValidationError);
}
......@@ -188,7 +202,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
}
}
private doExecuteTask(task: TaskDescription, telemetryEvent: TelemetryEvent): ITaskRunResult {
private doExecuteTask(task: TaskDescription, telemetryEvent: TelemetryEvent): ITaskExecuteResult {
let taskSummary: ITaskSummary = {};
let configuration = this.configuration;
if (!this.validationStatus.isOK() && !this.errorsShown) {
......@@ -240,7 +254,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
watchingProblemMatcher.aboutToStart();
let delayer:Async.Delayer<any> = null;
this.activeTaskIdentifier = task.id;
let promise = this.childProcess.start().then((success): ITaskSummary => {
this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => {
this.childProcessEnded();
watchingProblemMatcher.dispose();
toUnbind = dispose(toUnbind);
......@@ -281,14 +295,16 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
delayer = null;
});
});
let result: ITaskRunResult = (<any>task).tscWatch ? { restartOnFileChanges: '**/*.ts', promise } : { promise };
let result: ITaskExecuteResult = (<any>task).tscWatch
? { kind: TaskExecuteKind.Started, started: { restartOnFileChanges: '**/*.ts'} , promise: this.activeTaskPromise }
: { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise };
return result;
} else {
let event: TaskEvent = { taskId: task.id, taskName: task.name, type: TaskType.SingleRun };
this.emit(TaskSystemEvents.Active, event );
let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService);
this.activeTaskIdentifier = task.id;
let promise = this.childProcess.start().then((success): ITaskSummary => {
this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => {
this.childProcessEnded();
startStopProblemMatcher.done();
startStopProblemMatcher.dispose();
......@@ -309,13 +325,14 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
this.outputChannel.append(line + '\n');
startStopProblemMatcher.processLine(line);
});
return { promise };
return { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise };
}
}
private childProcessEnded(): void {
this.childProcess = null;
this.activeTaskIdentifier = null;
this.activeTaskPromise = null;
}
private handleError(task: TaskDescription, error: ErrorData): Promise {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册