Skip to content

Commit e84fc70

Browse files
authored
Adopt TerminalVirtualProcess for Custom task execution (microsoft#76852)
* Adopt TerminalVirtualProcess for Custom task execution * Update Custom task execution API to return TerminalVirtualProcess in callback This also required the addtion of a start on the virtual terminal process * Clarify start API Fixes microsoft#76492
1 parent 5a834e7 commit e84fc70

9 files changed

Lines changed: 162 additions & 22 deletions

File tree

src/vs/vscode.proposed.d.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,11 @@ declare module 'vscode' {
13941394
* Implement to handle when the terminal shuts down by an act of the user.
13951395
*/
13961396
shutdown?(): void;
1397+
1398+
/**
1399+
* Implement to handle when the terminal is ready to start firing events.
1400+
*/
1401+
start?(): void;
13971402
}
13981403

13991404
//#endregion
@@ -1494,6 +1499,23 @@ declare module 'vscode' {
14941499
callback: (terminalRenderer: TerminalRenderer, cancellationToken: CancellationToken, thisArg?: any) => Thenable<number>;
14951500
}
14961501

1502+
/**
1503+
* Class used to execute an extension callback as a task.
1504+
*/
1505+
export class CustomExecution2 {
1506+
/**
1507+
* @param process The [TerminalVirtualProcess](#TerminalVirtualProcess) to be used by the task to display output.
1508+
* @param callback The callback that will be called when the task is started by a user.
1509+
*/
1510+
constructor(callback: (thisArg?: any) => Thenable<TerminalVirtualProcess>);
1511+
1512+
/**
1513+
* The callback used to execute the task. Cancellation should be handled using the shutdown method of [TerminalVirtualProcess](#TerminalVirtualProcess).
1514+
* When the task is complete, onDidExit should be fired on the TerminalVirtualProcess with the exit code with '0' for success and a non-zero value for failure.
1515+
*/
1516+
callback: (thisArg?: any) => Thenable<TerminalVirtualProcess>;
1517+
}
1518+
14971519
/**
14981520
* A task to execute
14991521
*/
@@ -1510,12 +1532,12 @@ declare module 'vscode' {
15101532
* or '$eslint'. Problem matchers can be contributed by an extension using
15111533
* the `problemMatchers` extension point.
15121534
*/
1513-
constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
1535+
constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
15141536

15151537
/**
15161538
* The task's execution engine
15171539
*/
1518-
execution2?: ProcessExecution | ShellExecution | CustomExecution;
1540+
execution2?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2;
15191541
}
15201542

15211543
//#region Tasks

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
2929
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
3030
import {
3131
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
32-
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
32+
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
3333
RunOptionsDTO
3434
} from 'vs/workbench/api/common/shared/tasks';
3535
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
@@ -131,7 +131,7 @@ namespace ProcessExecutionOptionsDTO {
131131
}
132132

133133
namespace ProcessExecutionDTO {
134-
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ProcessExecutionDTO {
134+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO {
135135
const candidate = value as ProcessExecutionDTO;
136136
return candidate && !!candidate.process;
137137
}
@@ -199,7 +199,7 @@ namespace ShellExecutionOptionsDTO {
199199
}
200200

201201
namespace ShellExecutionDTO {
202-
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ShellExecutionDTO {
202+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO {
203203
const candidate = value as ShellExecutionDTO;
204204
return candidate && (!!candidate.commandLine || !!candidate.command);
205205
}
@@ -231,7 +231,7 @@ namespace ShellExecutionDTO {
231231
}
232232

233233
namespace CustomExecutionDTO {
234-
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is CustomExecutionDTO {
234+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecutionDTO {
235235
const candidate = value as CustomExecutionDTO;
236236
return candidate && candidate.customExecution === 'customExecution';
237237
}
@@ -250,6 +250,26 @@ namespace CustomExecutionDTO {
250250
}
251251
}
252252

253+
namespace CustomExecution2DTO {
254+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO {
255+
const candidate = value as CustomExecution2DTO;
256+
return candidate && candidate.customExecution === 'customExecution2';
257+
}
258+
259+
export function from(value: CommandConfiguration): CustomExecution2DTO {
260+
return {
261+
customExecution: 'customExecution2'
262+
};
263+
}
264+
265+
export function to(value: CustomExecution2DTO): CommandConfiguration {
266+
return {
267+
runtime: RuntimeType.CustomExecution2,
268+
presentation: undefined
269+
};
270+
}
271+
}
272+
253273
namespace TaskSourceDTO {
254274
export function from(value: TaskSource): TaskSourceDTO {
255275
const result: TaskSourceDTO = {
@@ -353,6 +373,8 @@ namespace TaskDTO {
353373
command = ProcessExecutionDTO.to(task.execution);
354374
} else if (CustomExecutionDTO.is(task.execution)) {
355375
command = CustomExecutionDTO.to(task.execution);
376+
} else if (CustomExecution2DTO.is(task.execution)) {
377+
command = CustomExecution2DTO.to(task.execution);
356378
}
357379
}
358380

src/vs/workbench/api/common/extHostTypes.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,24 @@ export class CustomExecution implements vscode.CustomExecution {
17721772
}
17731773
}
17741774

1775+
export class CustomExecution2 implements vscode.CustomExecution2 {
1776+
private _callback: () => Thenable<vscode.TerminalVirtualProcess>;
1777+
constructor(callback: () => Thenable<vscode.TerminalVirtualProcess>) {
1778+
this._callback = callback;
1779+
}
1780+
public computeId(): string {
1781+
return 'customExecution' + generateUuid();
1782+
}
1783+
1784+
public set callback(value: () => Thenable<vscode.TerminalVirtualProcess>) {
1785+
this._callback = value;
1786+
}
1787+
1788+
public get callback(): (() => Thenable<vscode.TerminalVirtualProcess>) {
1789+
return this._callback;
1790+
}
1791+
}
1792+
17751793
@es5ClassCompat
17761794
export class Task implements vscode.Task2 {
17771795

@@ -1785,7 +1803,7 @@ export class Task implements vscode.Task2 {
17851803
private _definition: vscode.TaskDefinition;
17861804
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
17871805
private _name: string;
1788-
private _execution: ProcessExecution | ShellExecution | CustomExecution | undefined;
1806+
private _execution: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined;
17891807
private _problemMatchers: string[];
17901808
private _hasDefinedMatchers: boolean;
17911809
private _isBackground: boolean;
@@ -1794,8 +1812,8 @@ export class Task implements vscode.Task2 {
17941812
private _presentationOptions: vscode.TaskPresentationOptions;
17951813
private _runOptions: vscode.RunOptions;
17961814

1797-
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
1798-
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
1815+
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
1816+
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
17991817
constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) {
18001818
this.definition = definition;
18011819
let problemMatchers: string | string[];
@@ -1907,18 +1925,18 @@ export class Task implements vscode.Task2 {
19071925
}
19081926

19091927
get execution(): ProcessExecution | ShellExecution | undefined {
1910-
return (this._execution instanceof CustomExecution) ? undefined : this._execution;
1928+
return ((this._execution instanceof CustomExecution) || (this._execution instanceof CustomExecution2)) ? undefined : this._execution;
19111929
}
19121930

19131931
set execution(value: ProcessExecution | ShellExecution | undefined) {
19141932
this.execution2 = value;
19151933
}
19161934

1917-
get execution2(): ProcessExecution | ShellExecution | CustomExecution | undefined {
1935+
get execution2(): ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined {
19181936
return this._execution;
19191937
}
19201938

1921-
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | undefined) {
1939+
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined) {
19221940
if (value === null) {
19231941
value = undefined;
19241942
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export interface CustomExecutionDTO {
7070
customExecution: 'customExecution';
7171
}
7272

73+
export interface CustomExecution2DTO {
74+
customExecution: 'customExecution2';
75+
}
76+
7377
export interface TaskSourceDTO {
7478
label: string;
7579
extensionId?: string;
@@ -84,7 +88,7 @@ export interface TaskHandleDTO {
8488
export interface TaskDTO {
8589
_id: string;
8690
name?: string;
87-
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | undefined;
91+
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
8892
definition: TaskDefinitionDTO;
8993
isBackground?: boolean;
9094
source: TaskSourceDTO;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ export function createApiFactory(
849849
ExtensionExecutionContext: extHostTypes.ExtensionExecutionContext,
850850
ExtensionKind: extHostTypes.ExtensionKind,
851851
CustomExecution: extHostTypes.CustomExecution,
852+
CustomExecution2: extHostTypes.CustomExecution2,
852853
FileChangeType: extHostTypes.FileChangeType,
853854
FileSystemError: extHostTypes.FileSystemError,
854855
FileType: files.FileType,

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

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
2323
ShellExecutionOptionsDTO, ShellExecutionDTO,
2424
CustomExecutionDTO,
25+
CustomExecution2DTO,
2526
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
2627
} from '../common/shared/tasks';
2728
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
@@ -79,7 +80,7 @@ namespace ProcessExecutionOptionsDTO {
7980
}
8081

8182
namespace ProcessExecutionDTO {
82-
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ProcessExecutionDTO {
83+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
8384
if (value) {
8485
const candidate = value as ProcessExecutionDTO;
8586
return candidate && !!candidate.process;
@@ -124,7 +125,7 @@ namespace ShellExecutionOptionsDTO {
124125
}
125126

126127
namespace ShellExecutionDTO {
127-
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ShellExecutionDTO {
128+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
128129
if (value) {
129130
const candidate = value as ShellExecutionDTO;
130131
return candidate && (!!candidate.commandLine || !!candidate.command);
@@ -162,7 +163,7 @@ namespace ShellExecutionDTO {
162163
}
163164

164165
namespace CustomExecutionDTO {
165-
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is CustomExecutionDTO {
166+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecutionDTO {
166167
if (value) {
167168
let candidate = value as CustomExecutionDTO;
168169
return candidate && candidate.customExecution === 'customExecution';
@@ -178,6 +179,23 @@ namespace CustomExecutionDTO {
178179
}
179180
}
180181

182+
namespace CustomExecution2DTO {
183+
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
184+
if (value) {
185+
let candidate = value as CustomExecution2DTO;
186+
return candidate && candidate.customExecution === 'customExecution2';
187+
} else {
188+
return false;
189+
}
190+
}
191+
192+
export function from(value: vscode.CustomExecution2): CustomExecution2DTO {
193+
return {
194+
customExecution: 'customExecution2'
195+
};
196+
}
197+
}
198+
181199
namespace TaskHandleDTO {
182200
export function from(value: types.Task): TaskHandleDTO {
183201
let folder: UriComponents | undefined;
@@ -211,14 +229,17 @@ namespace TaskDTO {
211229
if (value === undefined || value === null) {
212230
return undefined;
213231
}
214-
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined;
232+
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
215233
if (value.execution instanceof types.ProcessExecution) {
216234
execution = ProcessExecutionDTO.from(value.execution);
217235
} else if (value.execution instanceof types.ShellExecution) {
218236
execution = ShellExecutionDTO.from(value.execution);
219237
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution) {
220238
execution = CustomExecutionDTO.from(<types.CustomExecution>(<vscode.Task2>value).execution2);
239+
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
240+
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
221241
}
242+
222243
const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
223244
let scope: number | UriComponents;
224245
if (value.scope) {
@@ -468,6 +489,8 @@ export class ExtHostTask implements ExtHostTaskShape {
468489
private _taskExecutions: Map<string, TaskExecutionImpl>;
469490
private _providedCustomExecutions: Map<string, CustomExecutionData>;
470491
private _activeCustomExecutions: Map<string, CustomExecutionData>;
492+
private _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
493+
private _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
471494

472495
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
473496
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
@@ -491,6 +514,8 @@ export class ExtHostTask implements ExtHostTaskShape {
491514
this._taskExecutions = new Map<string, TaskExecutionImpl>();
492515
this._providedCustomExecutions = new Map<string, CustomExecutionData>();
493516
this._activeCustomExecutions = new Map<string, CustomExecutionData>();
517+
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
518+
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
494519
}
495520

496521
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
@@ -555,6 +580,19 @@ export class ExtHostTask implements ExtHostTaskShape {
555580
}
556581

557582
public async $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): Promise<void> {
583+
const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id);
584+
if (execution2) {
585+
if (this._activeCustomExecutions2.get(execution.id) !== undefined) {
586+
throw new Error('We should not be trying to start the same custom task executions twice.');
587+
}
588+
589+
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
590+
this._activeCustomExecutions2.set(execution.id, execution2);
591+
this._terminalService.performTerminalIdAction(terminalId, async terminal => {
592+
this._terminalService.attachVirtualProcessToTerminal(terminalId, await execution2.callback());
593+
});
594+
}
595+
558596
// Once a terminal is spun up for the custom execution task this event will be fired.
559597
// At that point, we need to actually start the callback, but
560598
// only if it hasn't already begun.
@@ -630,6 +668,7 @@ export class ExtHostTask implements ExtHostTaskShape {
630668
// since we obviously cannot send callback functions through the proxy.
631669
// So, clear out any existing ones.
632670
this._providedCustomExecutions.clear();
671+
this._providedCustomExecutions2.clear();
633672

634673
// Set up a list of task ID promises that we can wait on
635674
// before returning the provided tasks. The ensures that
@@ -657,6 +696,9 @@ export class ExtHostTask implements ExtHostTaskShape {
657696
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
658697
// is invoked, we have to be able to map it back to our data.
659698
taskIdPromises.push(this.addCustomExecution(taskDTO, <vscode.Task2>task));
699+
} else if (CustomExecution2DTO.is(taskDTO.execution)) {
700+
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task));
701+
660702
}
661703
}
662704
}
@@ -705,6 +747,10 @@ export class ExtHostTask implements ExtHostTaskShape {
705747
await this.addCustomExecution(taskDTO, <vscode.Task2>task);
706748
}
707749

750+
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
751+
await this.addCustomExecution2(taskDTO, <vscode.Task2>task);
752+
}
753+
708754
return resolvedTaskDTO;
709755
}
710756

@@ -758,6 +804,11 @@ export class ExtHostTask implements ExtHostTaskShape {
758804
this._providedCustomExecutions.set(taskId, new CustomExecutionData(<vscode.CustomExecution>(<vscode.Task2>task).execution2, this._terminalService));
759805
}
760806

807+
private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
808+
const taskId = await this._proxy.$createTaskId(taskDTO);
809+
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
810+
}
811+
761812
private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
762813
if (typeof execution === 'string') {
763814
const taskExecution = this._taskExecutions.get(execution);
@@ -787,5 +838,9 @@ export class ExtHostTask implements ExtHostTaskShape {
787838
this._proxy.$customExecutionComplete(execution.id, extensionCallback.result);
788839
extensionCallback.dispose();
789840
}
841+
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
842+
if (extensionCallback2) {
843+
this._activeCustomExecutions2.delete(execution.id);
844+
}
790845
}
791846
}

0 commit comments

Comments
 (0)