mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-26 03:29:00 +01:00
Start to hook up the terminal and execution
This commit is contained in:
@@ -116,7 +116,7 @@ export function createApiFactory(
|
||||
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
|
||||
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
|
||||
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService, extHostConfiguration));
|
||||
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration));
|
||||
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
|
||||
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
|
||||
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
|
||||
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
|
||||
|
||||
@@ -527,6 +527,7 @@ export interface MainThreadSearchShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadTaskShape extends IDisposable {
|
||||
$createTaskId(task: TaskDTO): Promise<string>;
|
||||
$registerTaskProvider(handle: number): Promise<void>;
|
||||
$unregisterTaskProvider(handle: number): Promise<void>;
|
||||
$fetchTasks(filter?: TaskFilterDTO): Promise<TaskDTO[]>;
|
||||
@@ -938,7 +939,7 @@ export interface ExtHostSCMShape {
|
||||
|
||||
export interface ExtHostTaskShape {
|
||||
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO>;
|
||||
$onDidStartTask(execution: TaskExecutionDTO): void;
|
||||
$onDidStartTask(execution: TaskExecutionDTO, terminalId: number): void;
|
||||
$onDidStartTaskProcess(value: TaskProcessStartedDTO): void;
|
||||
$onDidEndTaskProcess(value: TaskProcessEndedDTO): void;
|
||||
$OnDidEndTask(execution: TaskExecutionDTO): void;
|
||||
|
||||
@@ -28,8 +28,9 @@ import {
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
|
||||
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/extHostTerminalService';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
namespace TaskDefinitionDTO {
|
||||
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO {
|
||||
@@ -331,15 +332,55 @@ interface HandlerData {
|
||||
extension: IExtensionDescription;
|
||||
}
|
||||
|
||||
class ExtensionCallbackExecutionData {
|
||||
private _cancellationSource?: CancellationTokenSource;
|
||||
constructor(private readonly callbackData: vscode.ExtensionCallbackExecution, private readonly terminalService: ExtHostTerminalService) {
|
||||
}
|
||||
|
||||
public terminateCallback(): void {
|
||||
if (this._cancellationSource) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this._cancellationSource.cancel();
|
||||
this._cancellationSource.dispose();
|
||||
this._cancellationSource = undefined;
|
||||
}
|
||||
|
||||
public startCallback(terminalId: number): Thenable<void> {
|
||||
if (this._cancellationSource) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let foundTerminal: ExtHostTerminal | undefined;
|
||||
for (let terminal of this.terminalService.terminals) {
|
||||
if (terminal._id === terminalId) {
|
||||
foundTerminal = terminal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundTerminal) {
|
||||
throw new Error('Should have a terminal by this point.');
|
||||
}
|
||||
|
||||
this._cancellationSource = new CancellationTokenSource();
|
||||
return this.callbackData.callback(undefined, this._cancellationSource.token);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTask implements ExtHostTaskShape {
|
||||
|
||||
private _proxy: MainThreadTaskShape;
|
||||
private _workspaceService: ExtHostWorkspace;
|
||||
private _editorService: ExtHostDocumentsAndEditors;
|
||||
private _configurationService: ExtHostConfiguration;
|
||||
private _terminalService: ExtHostTerminalService;
|
||||
private _handleCounter: number;
|
||||
private _handlers: Map<number, HandlerData>;
|
||||
private _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
private _providedExtensionCallbacks: Map<string, ExtensionCallbackExecutionData>;
|
||||
private _activeExtensionCallbacks: Map<string, ExtensionCallbackExecutionData>;
|
||||
|
||||
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
|
||||
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
|
||||
@@ -347,14 +388,22 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
private readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
|
||||
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
|
||||
|
||||
constructor(mainContext: IMainContext, workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfiguration) {
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
workspaceService: ExtHostWorkspace,
|
||||
editorService: ExtHostDocumentsAndEditors,
|
||||
configurationService: ExtHostConfiguration,
|
||||
extHostTerminalService: ExtHostTerminalService) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadTask);
|
||||
this._workspaceService = workspaceService;
|
||||
this._editorService = editorService;
|
||||
this._configurationService = configurationService;
|
||||
this._terminalService = extHostTerminalService;
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._taskExecutions = new Map<string, TaskExecutionImpl>();
|
||||
this._providedExtensionCallbacks = new Map<string, ExtensionCallbackExecutionData>();
|
||||
this._activeExtensionCallbacks = new Map<string, ExtensionCallbackExecutionData>();
|
||||
}
|
||||
|
||||
public get extHostWorkspace(): ExtHostWorkspace {
|
||||
@@ -422,7 +471,21 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
return this._onDidExecuteTask.event;
|
||||
}
|
||||
|
||||
public $onDidStartTask(execution: TaskExecutionDTO): void {
|
||||
public $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): void {
|
||||
// Once a terminal is spun up for the extension callback task execution
|
||||
// this event will be fired.
|
||||
// At that point, we need to actually start the callback, but
|
||||
// only if it hasn't already begun.
|
||||
const extensionCallback: ExtensionCallbackExecutionData | undefined = this._providedExtensionCallbacks.get(execution.id);
|
||||
if (extensionCallback) {
|
||||
// TODO: Verify whether this can ever happen???
|
||||
if (this._activeExtensionCallbacks.get(execution.id) === undefined) {
|
||||
this._activeExtensionCallbacks.set(execution.id, extensionCallback);
|
||||
}
|
||||
|
||||
extensionCallback.startCallback(terminalId);
|
||||
}
|
||||
|
||||
this._onDidExecuteTask.fire({
|
||||
execution: this.getTaskExecution(execution)
|
||||
});
|
||||
@@ -435,6 +498,7 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
public $OnDidEndTask(execution: TaskExecutionDTO): void {
|
||||
const _execution = this.getTaskExecution(execution);
|
||||
this._taskExecutions.delete(execution.id);
|
||||
this.terminateExtensionCallbackExecution(execution);
|
||||
this._onDidTerminateTask.fire({
|
||||
execution: _execution
|
||||
});
|
||||
@@ -473,21 +537,55 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
return asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
|
||||
let sanitized: vscode.Task[] = [];
|
||||
|
||||
// For extension callback tasks, we need to store the execution objects locally
|
||||
// since we obviously cannot send callback functions through the proxy.
|
||||
// So, clear out any existing ones.
|
||||
this._providedExtensionCallbacks.clear();
|
||||
|
||||
// Set up a list of task ID promises that we can wait on
|
||||
// before returning the provided tasks. The ensures that
|
||||
// our task IDs are calculated for any extension callback tasks.
|
||||
// Knowing this ID ahead of time is needed because when a task
|
||||
// start event is fired this is when the extension callback is called.
|
||||
// The task start event is also the first time we see the ID from the main
|
||||
// thread, which is too late for us because we need to save an map
|
||||
// from an ID to an extension callback function. (Kind of a cart before the horse problem).
|
||||
let taskIdPromises: Promise<void>[] = [];
|
||||
let fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
|
||||
const taskDTOs: TaskDTO[] = [];
|
||||
for (let task of value) {
|
||||
if (task.definition && validTypes[task.definition.type] === true) {
|
||||
sanitized.push(task);
|
||||
} else {
|
||||
sanitized.push(task);
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
const taskDTO: TaskDTO = TaskDTO.from(task, handler.extension);
|
||||
taskDTOs.push(taskDTO);
|
||||
|
||||
if (CallbackExecutionDTO.is(taskDTO.execution)) {
|
||||
taskIdPromises.push(new Promise((resolve) => {
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
this._proxy.$createTaskId(taskDTO).then((taskId) => {
|
||||
this._providedExtensionCallbacks.set(taskId, new ExtensionCallbackExecutionData(<vscode.ExtensionCallbackExecution>task.execution, this._terminalService));
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tasks: TaskDTO.fromMany(sanitized, handler.extension),
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fetchPromise.then((result) => {
|
||||
Promise.all(taskIdPromises).then(() => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
|
||||
@@ -544,4 +642,12 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
this._taskExecutions.set(execution.id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private terminateExtensionCallbackExecution(execution: TaskExecutionDTO): void {
|
||||
const extensionCallback: ExtensionCallbackExecutionData | undefined = this._activeExtensionCallbacks.get(execution.id);
|
||||
if (extensionCallback) {
|
||||
extensionCallback.terminateCallback();
|
||||
this._activeExtensionCallbacks.delete(execution.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user