提交 c73695ab 编写于 作者: G Gabriel DeBacker

Implement new extension callback execution for a task and create a terminal for it

上级 957f536a
......@@ -43,5 +43,20 @@
},
"publisherDisplayName": "Microsoft"
}
},
{
"name": "ms-vscode.cpptools",
"version": "0.20.1",
"repo": "https://github.com/Microsoft/vscode-cpptools",
"metadata": {
"id": "36d19e17-7569-4841-a001-947eb18602b2",
"publisherId": {
"publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",
"publisherName": "ms-vscode",
"displayName": "Microsoft",
"flags": "verified"
},
"publisherDisplayName": "Microsoft"
}
}
]
......@@ -5074,6 +5074,36 @@ declare module 'vscode' {
options?: ShellExecutionOptions;
}
/**
* Arguments passed to the callback provided in [ExtensionCallbackExecution](#ExtensionCallbackExecution) class.
*/
export interface ExtensionCallbackExecutionArgs {
/**
* @param text Text to output from the task.
**/
sendText(text: string, addNewLine?: boolean): void;
/**
* An event fired when the task is to accept input data.
*/
onTextInput: Event<string>;
}
/**
* Class used to execute an extension callback as a task.
*/
export class ExtensionCallbackExecution {
/**
* @param callback The callback that will be called when the extension callback task is executed.
*/
constructor(callback: (args: ExtensionCallbackExecutionArgs, cancellationToken: CancellationToken) => Thenable<void>);
/**
* The callback used to execute the task.
*/
callback: (args: ExtensionCallbackExecutionArgs, cancellationToken: CancellationToken) => Thenable<void>;
}
/**
* The scope of a task.
*/
......@@ -5116,7 +5146,7 @@ declare module 'vscode' {
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | ExtensionCallbackExecution, problemMatchers?: string | string[]);
/**
* ~~Creates a new task.~~
......@@ -5131,7 +5161,7 @@ declare module 'vscode' {
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(taskDefinition: TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(taskDefinition: TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | ExtensionCallbackExecution, problemMatchers?: string | string[]);
/**
* The task's definition.
......@@ -5151,7 +5181,7 @@ declare module 'vscode' {
/**
* The task's execution engine
*/
execution?: ProcessExecution | ShellExecution;
execution?: ProcessExecution | ShellExecution | ExtensionCallbackExecution;
/**
* Whether the task is a background task or not.
......
......@@ -31,7 +31,8 @@ import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IEx
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
RunOptionsDTO
RunOptionsDTO,
CallbackExecutionDTO
} from 'vs/workbench/api/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
......@@ -138,7 +139,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
......@@ -206,7 +207,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
......@@ -237,6 +238,26 @@ namespace ShellExecutionDTO {
}
}
namespace CallbackExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO): value is CallbackExecutionDTO {
let candidate = value as CallbackExecutionDTO;
return candidate && candidate.extensionCallback === 'extensionCallback';
}
export function from(value: CommandConfiguration): CallbackExecutionDTO {
return {
extensionCallback: 'extensionCallback'
};
}
export function to(value: CallbackExecutionDTO): CommandConfiguration {
return {
runtime: RuntimeType.ExtensionCallback,
presentation: undefined
};
}
}
namespace TaskSourceDTO {
export function from(value: TaskSource): TaskSourceDTO {
let result: TaskSourceDTO = {
......@@ -336,6 +357,8 @@ namespace TaskDTO {
command = ShellExecutionDTO.to(task.execution);
} else if (ProcessExecutionDTO.is(task.execution)) {
command = ProcessExecutionDTO.to(task.execution);
} else if (CallbackExecutionDTO.is(task.execution)) {
command = CallbackExecutionDTO.to(task.execution);
}
if (!command) {
return undefined;
......
......@@ -765,6 +765,7 @@ export function createApiFactory(
DocumentSymbol: extHostTypes.DocumentSymbol,
EndOfLine: extHostTypes.EndOfLine,
EventEmitter: Emitter,
ExtensionCallbackExecution: extHostTypes.ExtensionCallbackExecution,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
FileType: files.FileType,
......
......@@ -19,8 +19,11 @@ import * as types from 'vs/workbench/api/node/extHostTypes';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import {
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO, ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO,
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO,
CallbackExecutionDTO,
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
} from '../shared/tasks';
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
......@@ -74,7 +77,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
......@@ -115,7 +118,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
......@@ -148,6 +151,19 @@ namespace ShellExecutionDTO {
}
}
namespace CallbackExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO): value is CallbackExecutionDTO {
let candidate = value as CallbackExecutionDTO;
return candidate && candidate.extensionCallback === 'extensionCallback';
}
export function from(value: vscode.ExtensionCallbackExecution): CallbackExecutionDTO {
return {
extensionCallback: 'extensionCallback'
};
}
}
namespace TaskHandleDTO {
export function from(value: types.Task): TaskHandleDTO {
let folder: UriComponents;
......@@ -181,12 +197,15 @@ namespace TaskDTO {
if (value === undefined || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO;
let execution: ShellExecutionDTO | ProcessExecutionDTO | CallbackExecutionDTO;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ExtensionCallbackExecution) {
execution = CallbackExecutionDTO.from(value.execution);
}
let definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition);
let scope: number | UriComponents;
if (value.scope) {
......
......@@ -1621,8 +1621,34 @@ export enum TaskScope {
Workspace = 2
}
export class ExtensionCallbackExecution implements vscode.ExtensionCallbackExecution {
private _callback: (args: vscode.ExtensionCallbackExecutionArgs, cancellationToken: vscode.CancellationToken) => Thenable<void>;
constructor(callback: (args: vscode.ExtensionCallbackExecutionArgs, cancellationToken: vscode.CancellationToken) => Thenable<void>) {
this._callback = callback;
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('extensionCallback');
// TODO: How is this ID used. Not sure how to create a valid ID from a function.
// TODO: Also not clear on how the ID is used
hash.update(generateUuid());
return hash.digest('hex');
}
public set callback(value: (args: vscode.ExtensionCallbackExecutionArgs, cancellationToken: vscode.CancellationToken) => Thenable<void>) {
this._callback = value;
}
public get callback(): (args: vscode.ExtensionCallbackExecutionArgs, cancellationToken: vscode.CancellationToken) => Thenable<void> {
return this._callback;
}
}
export class Task implements vscode.Task {
private static ExtensionCallbackType: string = 'extensionCallback';
private static ProcessType: string = 'process';
private static ShellType: string = 'shell';
private static EmptyType: string = '$empty';
......@@ -1632,7 +1658,7 @@ export class Task implements vscode.Task {
private _definition: vscode.TaskDefinition;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
private _name: string;
private _execution: ProcessExecution | ShellExecution | undefined;
private _execution: ProcessExecution | ShellExecution | ExtensionCallbackExecution | undefined;
private _problemMatchers: string[];
private _hasDefinedMatchers: boolean;
private _isBackground: boolean;
......@@ -1641,8 +1667,8 @@ export class Task implements vscode.Task {
private _presentationOptions: vscode.TaskPresentationOptions;
private _runOptions: vscode.RunOptions;
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | ExtensionCallbackExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | ExtensionCallbackExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) {
this.definition = definition;
let problemMatchers: string | string[];
......@@ -1707,6 +1733,11 @@ export class Task implements vscode.Task {
type: Task.ShellType,
id: this._execution.computeId()
};
} else if (this._execution instanceof ExtensionScriptApis) {
this._definition = {
type: Task.ExtensionCallbackType,
id: this._execution.computeId()
};
} else {
this._definition = {
type: Task.EmptyType,
......@@ -1748,11 +1779,11 @@ export class Task implements vscode.Task {
this._name = value;
}
get execution(): ProcessExecution | ShellExecution | undefined {
get execution(): ProcessExecution | ShellExecution | ExtensionCallbackExecution | undefined {
return this._execution;
}
set execution(value: ProcessExecution | ShellExecution | undefined) {
set execution(value: ProcessExecution | ShellExecution | ExtensionCallbackExecution | undefined) {
if (value === null) {
value = undefined;
}
......
......@@ -65,6 +65,10 @@ export interface ShellExecutionDTO {
options?: ShellExecutionOptionsDTO;
}
export interface CallbackExecutionDTO {
extensionCallback: 'extensionCallback';
}
export interface TaskSourceDTO {
label: string;
extensionId?: string;
......@@ -79,7 +83,7 @@ export interface TaskHandleDTO {
export interface TaskDTO {
_id: string;
name: string;
execution: ProcessExecutionDTO | ShellExecutionDTO;
execution: ProcessExecutionDTO | ShellExecutionDTO | CallbackExecutionDTO;
definition: TaskDefinitionDTO;
isBackground: boolean;
source: TaskSourceDTO;
......
......@@ -224,7 +224,8 @@ export namespace PresentationOptions {
export enum RuntimeType {
Shell = 1,
Process = 2
Process = 2,
ExtensionCallback = 3
}
export namespace RuntimeType {
......@@ -234,6 +235,8 @@ export namespace RuntimeType {
return RuntimeType.Shell;
case 'process':
return RuntimeType.Process;
case 'extensionCallback':
return RuntimeType.ExtensionCallback;
default:
return RuntimeType.Process;
}
......
......@@ -689,11 +689,16 @@ export class TerminalTaskSystem implements ITaskSystem {
});
}
private createTerminalName(task: CustomTask | ContributedTask): string {
const needsFolderQualification = this.currentTask.workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
return nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? task.getQualifiedLabel() : task.configurationProperties.name);
}
private createShellLaunchConfig(task: CustomTask | ContributedTask, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): IShellLaunchConfig | undefined {
let shellLaunchConfig: IShellLaunchConfig;
let isShellCommand = task.command.runtime === RuntimeType.Shell;
let needsFolderQualification = this.currentTask.workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? task.getQualifiedLabel() : task.configurationProperties.name);
let terminalName = this.createTerminalName(task);
let originalCommand = task.command.name;
if (isShellCommand) {
shellLaunchConfig = { name: terminalName, executable: undefined, args: undefined, waitOnExit };
......@@ -775,7 +780,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
} else {
let commandExecutable = CommandString.value(command);
let commandExecutable = task.command.runtime !== RuntimeType.ExtensionCallback ? CommandString.value(command) : undefined;
let executable = !isShellCommand
? this.resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}')
: commandExecutable;
......@@ -834,9 +839,8 @@ export class TerminalTaskSystem implements ITaskSystem {
private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance | undefined, string | undefined, TaskError | undefined] {
let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform;
let options = this.resolveOptions(resolver, task.command.options);
let waitOnExit: boolean | string = false;
let { command, args } = this.resolveCommandAndArgs(resolver, task.command);
let commandExecutable = CommandString.value(command);
const presentationOptions = task.command.presentation;
if (!presentationOptions) {
throw new Error('Task presentation options should not be undefined here.');
......@@ -851,10 +855,23 @@ export class TerminalTaskSystem implements ITaskSystem {
waitOnExit = true;
}
}
this.currentTask.shellLaunchConfig = this.isRerun ? this.lastTask.getVerifiedTask().shellLaunchConfig : this.createShellLaunchConfig(task, resolver, platform, options, command, args, waitOnExit);
if (this.currentTask.shellLaunchConfig === undefined) {
return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)];
let commandExecutable: string | undefined;
let command: CommandString | undefined;
let args: CommandString[] | undefined;
if (task.command.runtime !== RuntimeType.ExtensionCallback) {
let resolvedResult: { command: CommandString, args: CommandString[] } = this.resolveCommandAndArgs(resolver, task.command);
command = resolvedResult.command;
args = resolvedResult.args;
commandExecutable = CommandString.value(command);
this.currentTask.shellLaunchConfig = this.isRerun ? this.lastTask.getVerifiedTask().shellLaunchConfig : this.createShellLaunchConfig(task, resolver, platform, options, command, args, waitOnExit);
if (this.currentTask.shellLaunchConfig === undefined) {
return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)];
}
}
let prefersSameTerminal = presentationOptions.panel === PanelKind.Dedicated;
let allowsSharedTerminal = presentationOptions.panel === PanelKind.Shared;
......@@ -873,14 +890,21 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
if (terminalToReuse) {
terminalToReuse.terminal.reuseTerminal(this.currentTask.shellLaunchConfig);
if (task.command.runtime !== RuntimeType.ExtensionCallback) {
if (!this.currentTask.shellLaunchConfig) {
throw new Error('Task shell launch configuration should not be undefined here.');
}
terminalToReuse.terminal.reuseTerminal(this.currentTask.shellLaunchConfig);
}
if (task.command.presentation && task.command.presentation.clear) {
terminalToReuse.terminal.clear();
}
return [terminalToReuse.terminal, commandExecutable, undefined];
}
const result = this.terminalService.createTerminal(this.currentTask.shellLaunchConfig);
const result = task.command.runtime !== RuntimeType.ExtensionCallback ? this.terminalService.createTerminal(this.currentTask.shellLaunchConfig) : this.terminalService.createTerminalRenderer(this.createTerminalName(task));
const terminalKey = result.id.toString();
result.onDisposed((terminal) => {
let terminalData = this.terminals[terminalKey];
......@@ -1016,6 +1040,12 @@ export class TerminalTaskSystem implements ITaskSystem {
}
private collectCommandVariables(variables: Set<string>, command: CommandConfiguration, task: CustomTask | ContributedTask): void {
// An extension callback should have everything it needs already as it provided
// the callback.
if (command.runtime === RuntimeType.ExtensionCallback) {
return;
}
if (command.name === undefined) {
throw new Error('Command name should never be undefined here.');
}
......
......@@ -260,6 +260,8 @@ export class TerminalInstance implements ITerminalInstance {
this._logService.trace(`terminalInstance#ctor (id: ${this.id})`, this._shellLaunchConfig);
this._initDimensions();
// There may not be an executable name if a terminal is being used
// for rendering as well as input\output
if (!this.shellLaunchConfig.isRendererOnly) {
this._createProcess();
} else {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册