Skip to content

Commit 0505ae0

Browse files
committed
Add events to listen on process start / end for task executions.
1 parent 82a8a52 commit 0505ae0

9 files changed

Lines changed: 225 additions & 24 deletions

File tree

src/vs/base/node/processes.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export abstract class AbstractProcess<TProgressData> {
7979

8080
private childProcess: cp.ChildProcess;
8181
protected childProcessPromise: TPromise<cp.ChildProcess>;
82+
private pidResolve: TValueCallback<number>;
8283
protected terminateRequested: boolean;
8384

8485
private static WellKnowCommands: IStringDictionary<boolean> = {
@@ -241,6 +242,10 @@ export abstract class AbstractProcess<TProgressData> {
241242
return;
242243
}
243244
this.childProcess = childProcess;
245+
if (this.pidResolve) {
246+
this.pidResolve(Types.isNumber(childProcess.pid) ? childProcess.pid : -1);
247+
this.pidResolve = undefined;
248+
}
244249
this.childProcess.on('close', closeHandler);
245250
this.handleSpawn(childProcess, cc, pp, ee, false);
246251
c(childProcess);
@@ -251,6 +256,10 @@ export abstract class AbstractProcess<TProgressData> {
251256
if (childProcess) {
252257
this.childProcess = childProcess;
253258
this.childProcessPromise = TPromise.as(childProcess);
259+
if (this.pidResolve) {
260+
this.pidResolve(Types.isNumber(childProcess.pid) ? childProcess.pid : -1);
261+
this.pidResolve = undefined;
262+
}
254263
childProcess.on('error', (error: Error) => {
255264
this.childProcess = null;
256265
ee({ terminated: this.terminateRequested, error: error });
@@ -288,7 +297,13 @@ export abstract class AbstractProcess<TProgressData> {
288297
}
289298

290299
public get pid(): TPromise<number> {
291-
return this.childProcessPromise.then(childProcess => childProcess.pid, err => -1);
300+
if (this.childProcessPromise) {
301+
return this.childProcessPromise.then(childProcess => childProcess.pid, err => -1);
302+
} else {
303+
return new TPromise<number>((resolve) => {
304+
this.pidResolve = resolve;
305+
});
306+
}
292307
}
293308

294309
public terminate(): TPromise<TerminateResponse> {

src/vs/vscode.proposed.d.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,40 @@ declare module 'vscode' {
334334

335335
//#region Tasks
336336

337+
/**
338+
* An event signaling the start of a process execution
339+
* triggered through a task
340+
*/
341+
export interface TaskProcessStartEvent {
342+
343+
/**
344+
* The task execution for which the process got started.
345+
*/
346+
execution: TaskExecution;
347+
348+
/**
349+
* The underlying process id.
350+
*/
351+
processId: number;
352+
}
353+
354+
/**
355+
* An event signaling the end of a process execution
356+
* triggered through a task
357+
*/
358+
export interface TaskProcessEndEvent {
359+
360+
/**
361+
* The task execution for which the process got started.
362+
*/
363+
execution: TaskExecution;
364+
365+
/**
366+
* The process's exit code.
367+
*/
368+
exitCode: number;
369+
}
370+
337371
/**
338372
* An object representing an executed Task. It can be used
339373
* to terminate a task.
@@ -346,6 +380,20 @@ declare module 'vscode' {
346380
*/
347381
task: Task;
348382

383+
/**
384+
* Fires when the underlying process has been started.
385+
* This event might not fire for tasks that don't
386+
* execute an underlying process.
387+
*/
388+
onDidStartProcess: Event<TaskProcessStartEvent>;
389+
390+
/**
391+
* Fires when the underlying process has ended.
392+
* This event might not fire for tasks that don't
393+
* execute an underlying process.
394+
*/
395+
onDidEndProcess: Event<TaskProcessEndEvent>;
396+
349397
/**
350398
* Terminates the task execution.
351399
*/
@@ -392,7 +440,7 @@ declare module 'vscode' {
392440
export namespace workspace {
393441

394442
/**
395-
* Fetches all task available in the systems. Thisweweb includes tasks
443+
* Fetches all tasks available in the systems. This includes tasks
396444
* from `tasks.json` files as well as tasks from task providers
397445
* contributed through extensions.
398446
*

src/vs/workbench/api/electron-browser/mainThreadTask.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
2626
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
2727
import {
2828
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
29-
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO
29+
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO
3030
} from 'vs/workbench/api/shared/tasks';
3131

3232
export { TaskDTO, TaskHandleDTO, TaskExecutionDTO, TaskFilterDTO };
@@ -46,6 +46,24 @@ namespace TaskExecutionDTO {
4646
}
4747
}
4848

49+
namespace TaskProcessStartedDTO {
50+
export function from(value: TaskExecution, processId: number): TaskProcessStartedDTO {
51+
return {
52+
id: value.id,
53+
processId
54+
};
55+
}
56+
}
57+
58+
namespace TaskProcessEndedDTO {
59+
export function from(value: TaskExecution, exitCode: number): TaskProcessEndedDTO {
60+
return {
61+
id: value.id,
62+
exitCode
63+
};
64+
}
65+
}
66+
4967
namespace TaskDefinitionDTO {
5068
export function from(value: TaskIdentifier): TaskDefinitionDTO {
5169
let result = Objects.assign(Object.create(null), value);
@@ -352,9 +370,13 @@ export class MainThreadTask implements MainThreadTaskShape {
352370
this._taskService.onDidStateChange((event: TaskEvent) => {
353371
let task = event.__task;
354372
if (event.kind === TaskEventKind.Start) {
355-
this._proxy.$taskStarted(TaskExecutionDTO.from(Task.getTaskExecution(task)));
373+
this._proxy.$onDidStartTask(TaskExecutionDTO.from(Task.getTaskExecution(task)));
374+
} else if (event.kind === TaskEventKind.ProcessStarted) {
375+
this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(Task.getTaskExecution(task), event.processId));
376+
} else if (event.kind === TaskEventKind.ProcessEnded) {
377+
this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(Task.getTaskExecution(task), event.exitCode));
356378
} else if (event.kind === TaskEventKind.End) {
357-
this._proxy.$taskEnded(TaskExecutionDTO.from(Task.getTaskExecution(task)));
379+
this._proxy.$OnDidEndTask(TaskExecutionDTO.from(Task.getTaskExecution(task)));
358380
}
359381
});
360382
}
@@ -417,7 +439,7 @@ export class MainThreadTask implements MainThreadTaskShape {
417439
task: TaskDTO.from(task)
418440
};
419441
resolve(result);
420-
}, (error) => {
442+
}, (_error) => {
421443
reject(new Error('Task not found'));
422444
});
423445
} else {

src/vs/workbench/api/node/extHost.protocol.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/
4848
import { ISingleEditOperation } from 'vs/editor/common/model';
4949
import { IPatternInfo, IRawSearchQuery, IRawFileMatch2 } from 'vs/platform/search/common/search';
5050
import { LogLevel } from 'vs/platform/log/common/log';
51-
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO } from 'vs/workbench/api/shared/tasks';
51+
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO } from 'vs/workbench/api/shared/tasks';
5252

5353
export interface IEnvironment {
5454
isExtensionDevelopmentDebug: boolean;
@@ -773,8 +773,10 @@ export interface ExtHostSCMShape {
773773

774774
export interface ExtHostTaskShape {
775775
$provideTasks(handle: number): TPromise<TaskSet>;
776-
$taskStarted(execution: TaskExecutionDTO): void;
777-
$taskEnded(execution: TaskExecutionDTO): void;
776+
$onDidStartTask(execution: TaskExecutionDTO): void;
777+
$onDidStartTaskProcess(value: TaskProcessStartedDTO): void;
778+
$onDidEndTaskProcess(value: TaskProcessEndedDTO): void;
779+
$OnDidEndTask(execution: TaskExecutionDTO): void;
778780
}
779781

780782
export interface IBreakpointDto {

src/vs/workbench/api/node/extHostTask.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
2121
import * as vscode from 'vscode';
2222
import {
2323
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO, ProcessExecutionOptionsDTO, ProcessExecutionDTO,
24-
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO
24+
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO
2525
} from '../shared/tasks';
2626

2727
export { TaskExecutionDTO };
@@ -689,21 +689,47 @@ namespace TaskFilterDTO {
689689
}
690690

691691
class TaskExecutionImpl implements vscode.TaskExecution {
692-
constructor(readonly _id: string, private readonly _task: vscode.Task, private readonly _tasks: ExtHostTask) {
692+
693+
private readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
694+
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
695+
696+
constructor(private readonly _tasks: ExtHostTask, readonly _id: string, private readonly _task: vscode.Task) {
693697
}
694698

695-
get task(): vscode.Task {
699+
public get task(): vscode.Task {
696700
return this._task;
697701
}
698702

699703
public terminate(): void {
700704
this._tasks.terminateTask(this);
701705
}
706+
707+
public get onDidStartProcess(): Event<vscode.TaskProcessStartEvent> {
708+
return this._onDidTaskProcessStarted.event;
709+
}
710+
711+
public fireDidStartProcess(value: TaskProcessStartedDTO): void {
712+
this._onDidTaskProcessStarted.fire({
713+
execution: this,
714+
processId: value.processId
715+
});
716+
}
717+
718+
public get onDidEndProcess(): Event<vscode.TaskProcessEndEvent> {
719+
return this._onDidTaskProcessEnded.event;
720+
}
721+
722+
public fireDidEndProcess(value: TaskProcessEndedDTO): void {
723+
this._onDidTaskProcessEnded.fire({
724+
execution: this,
725+
exitCode: value.exitCode
726+
});
727+
}
702728
}
703729

704730
namespace TaskExecutionDTO {
705731
export function to(value: TaskExecutionDTO, tasks: ExtHostTask): vscode.TaskExecution {
706-
return new TaskExecutionImpl(value.id, TaskDTO.to(value.task, tasks.extHostWorkspace), tasks);
732+
return new TaskExecutionImpl(tasks, value.id, TaskDTO.to(value.task, tasks.extHostWorkspace));
707733
}
708734
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
709735
return {
@@ -781,12 +807,26 @@ export class ExtHostTask implements ExtHostTaskShape {
781807
}
782808
}
783809

784-
public $taskStarted(execution: TaskExecutionDTO): void {
810+
public $onDidStartTask(execution: TaskExecutionDTO): void {
785811
this._onDidExecuteTask.fire({
786812
execution: this.getTaskExecution(execution)
787813
});
788814
}
789815

816+
public $onDidStartTaskProcess(value: TaskProcessStartedDTO): void {
817+
const execution = this.getTaskExecution(value.id);
818+
if (execution) {
819+
execution.fireDidStartProcess(value);
820+
}
821+
}
822+
823+
public $onDidEndTaskProcess(value: TaskProcessEndedDTO): void {
824+
const execution = this.getTaskExecution(value.id);
825+
if (execution) {
826+
execution.fireDidEndProcess(value);
827+
}
828+
}
829+
790830
get taskExecutions(): vscode.TaskExecution[] {
791831
let result: vscode.TaskExecution[] = [];
792832
this._taskExecutions.forEach(value => result.push(value));
@@ -804,7 +844,7 @@ export class ExtHostTask implements ExtHostTaskShape {
804844
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
805845
}
806846

807-
public $taskEnded(execution: TaskExecutionDTO): void {
847+
public $OnDidEndTask(execution: TaskExecutionDTO): void {
808848
const _execution = this.getTaskExecution(execution);
809849
this._taskExecutions.delete(execution.id);
810850
this._onDidTerminateTask.fire({
@@ -834,12 +874,16 @@ export class ExtHostTask implements ExtHostTaskShape {
834874
return this._handleCounter++;
835875
}
836876

837-
private getTaskExecution(execution: TaskExecutionDTO, task?: vscode.Task): TaskExecutionImpl {
877+
private getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): TaskExecutionImpl {
878+
if (typeof execution === 'string') {
879+
return this._taskExecutions.get(execution);
880+
}
881+
838882
let result: TaskExecutionImpl = this._taskExecutions.get(execution.id);
839883
if (result) {
840884
return result;
841885
}
842-
result = new TaskExecutionImpl(execution.id, task ? task : TaskDTO.to(execution.task, this._extHostWorkspace), this);
886+
result = new TaskExecutionImpl(this, execution.id, task ? task : TaskDTO.to(execution.task, this._extHostWorkspace));
843887
this._taskExecutions.set(execution.id, result);
844888
return result;
845889
}

src/vs/workbench/api/shared/tasks.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@ export interface TaskExecutionDTO {
8888
task: TaskDTO;
8989
}
9090

91+
export interface TaskProcessStartedDTO {
92+
id: string;
93+
processId: number;
94+
}
95+
96+
export interface TaskProcessEndedDTO {
97+
id: string;
98+
exitCode: number;
99+
}
100+
101+
91102
export interface TaskFilterDTO {
92103
version?: string;
93104
type?: string;

src/vs/workbench/parts/tasks/common/tasks.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -695,10 +695,12 @@ export class TaskSorter {
695695

696696
export enum TaskEventKind {
697697
Start = 'start',
698+
ProcessStarted = 'processStarted',
698699
Active = 'active',
699700
Inactive = 'inactive',
700701
Changed = 'changed',
701702
Terminated = 'terminated',
703+
ProcessEnded = 'processEnded',
702704
End = 'end'
703705
}
704706

@@ -714,22 +716,34 @@ export interface TaskEvent {
714716
taskName?: string;
715717
runType?: TaskRunType;
716718
group?: string;
719+
processId?: number;
720+
exitCode?: number;
717721
__task?: Task;
718722
}
719723

720724
export namespace TaskEvent {
721-
export function create(kind: TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.Start | TaskEventKind.End, task: Task);
722-
export function create(kind: TaskEventKind.Changed);
723-
export function create(kind: TaskEventKind, task?: Task): TaskEvent {
725+
export function create(kind: TaskEventKind.ProcessStarted, task: Task, processId: number): TaskEvent;
726+
export function create(kind: TaskEventKind.ProcessEnded, task: Task, exitCode: number): TaskEvent;
727+
export function create(kind: TaskEventKind.Start | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.End, task: Task): TaskEvent;
728+
export function create(kind: TaskEventKind.Changed): TaskEvent;
729+
export function create(kind: TaskEventKind, task?: Task, processIdOrExitCode?: number): TaskEvent {
724730
if (task) {
725-
return Object.freeze({
731+
let result = {
726732
kind: kind,
727733
taskId: task._id,
728734
taskName: task.name,
729735
runType: task.isBackground ? TaskRunType.Background : TaskRunType.SingleRun,
730736
group: task.group,
737+
processId: undefined,
738+
exitCode: undefined,
731739
__task: task,
732-
});
740+
};
741+
if (kind === TaskEventKind.ProcessStarted) {
742+
result.processId = processIdOrExitCode;
743+
} else if (kind === TaskEventKind.ProcessEnded) {
744+
result.exitCode = processIdOrExitCode;
745+
}
746+
return Object.freeze(result);
733747
} else {
734748
return Object.freeze({ kind: TaskEventKind.Changed });
735749
}

0 commit comments

Comments
 (0)