Implements #45664: Add API to query and execute tasks

This commit is contained in:
Dirk Baeumer
2018-03-20 22:19:00 +01:00
parent d1b4302bcf
commit 2f5061ff2d
13 changed files with 941 additions and 78 deletions

View File

@@ -132,7 +132,7 @@ export function createApiFactory(
const extHostLanguages = new ExtHostLanguages(rpcProtocol);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands, extHostWorkspace);
ExtHostApiCommands.register(extHostCommands, extHostTask);
return function (extension: IExtensionDescription): typeof vscode {
@@ -517,6 +517,21 @@ export function createApiFactory(
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
},
fetchTasks: proposedApiFunction(extension, (): Thenable<vscode.Task[]> => {
return extHostTask.executeTaskProvider();
}),
executeTask: proposedApiFunction(extension, (task: vscode.Task): Thenable<vscode.TaskExecution> => {
return extHostTask.executeTask(extension, task);
}),
onDidStartTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidStartTask(listeners, thisArgs, disposables);
},
terminateTask: proposedApiFunction(extension, (task: vscode.TaskExecution): void => {
extHostTask.terminateTask(task);
}),
onDidEndTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidEndTask(listeners, thisArgs, disposables);
},
registerFileSystemProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostFileSystem.registerFileSystemProvider(scheme, provider);
}),

View File

@@ -48,6 +48,7 @@ import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/
import { ISingleEditOperation } from 'vs/editor/common/model';
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
import { LogLevel } from 'vs/platform/log/common/log';
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO } from 'vs/workbench/api/shared/tasks';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -388,6 +389,9 @@ export interface MainThreadFileSystemShape extends IDisposable {
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): TPromise<void>;
$executeTaskProvider(): TPromise<TaskDTO[]>;
$executeTask(task: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO>;
$terminateTask(task: TaskExecutionDTO): TPromise<void>;
$unregisterTaskProvider(handle: number): TPromise<void>;
}
@@ -727,6 +731,8 @@ export interface ExtHostSCMShape {
export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
$taskStarted(execution: TaskExecutionDTO): void;
$taskEnded(execution: TaskExecutionDTO): void;
}
export interface IBreakpointDto {

View File

@@ -6,7 +6,6 @@
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
@@ -18,22 +17,21 @@ import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures';
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
import { ExtHostWorkspace } from './extHostWorkspace';
import { ExtHostTask } from './extHostTask';
export class ExtHostApiCommands {
static register(commands: ExtHostCommands, workspace: ExtHostWorkspace) {
static register(commands: ExtHostCommands, workspace: ExtHostTask) {
return new ExtHostApiCommands(commands, workspace).registerCommands();
}
private _commands: ExtHostCommands;
private _workspace: ExtHostWorkspace;
private _tasks: ExtHostTask;
private _disposables: IDisposable[] = [];
private constructor(commands: ExtHostCommands, workspace: ExtHostWorkspace) {
private constructor(commands: ExtHostCommands, task: ExtHostTask) {
this._commands = commands;
this._workspace = workspace;
this._tasks = task;
}
registerCommands() {
@@ -476,29 +474,8 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.DocumentLink.to));
}
private _executeTaskProvider(): Thenable<vscode.TaskItem[]> {
return this._commands.executeCommand<TaskSystem.TaskItemTransfer[]>('_executeTaskProvider').then<vscode.TaskItem[]>((values) => {
let workspace = this._workspace;
return values.map(handle => {
let definition: vscode.TaskDefinition = Objects.assign(Object.create(null), handle.definition);
delete definition._key;
let uri = URI.revive(handle.workspaceFolderUri);
return new class {
get id(): string {
return handle.id;
}
get label(): string {
return handle.label;
}
get definition(): vscode.TaskDefinition {
return definition;
}
get workspaceFolder(): vscode.WorkspaceFolder {
return uri ? workspace.resolveWorkspaceFolder(uri) : undefined;
}
};
});
});
private _executeTaskProvider(): Thenable<vscode.Task[]> {
return this._tasks.executeTaskProvider();
}
}

View File

@@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import URI, { UriComponents } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import { asWinJsPromise } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
@@ -18,7 +19,12 @@ import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from
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
} from '../shared/tasks';
export { TaskExecutionDTO };
/*
namespace ProblemPattern {
@@ -450,6 +456,240 @@ namespace Tasks {
}
}
namespace TaskDefinitionDTO {
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: TaskDefinitionDTO): vscode.TaskDefinition {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace TaskPresentationOptionsDTO {
export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace ProcessExecutionOptionsDTO {
export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
export function from(value: vscode.ProcessExecution): ProcessExecutionDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: ProcessExecutionDTO = {
process: value.process,
args: value.args
};
if (value.options) {
result.options = ProcessExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ProcessExecutionDTO): types.ProcessExecution {
if (value === void 0 || value === null) {
return undefined;
}
return new types.ProcessExecution(value.process, value.args, value.options);
}
}
namespace ShellExecutionOptionsDTO {
export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
export function from(value: vscode.ShellExecution): ShellExecutionDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: ShellExecutionDTO = {
};
if (value.commandLine !== void 0) {
result.commandLine = value.commandLine;
} else {
result.command = value.command;
result.args = value.args;
}
if (value.options) {
result.options = ShellExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ShellExecutionDTO): types.ShellExecution {
if (value === void 0 || value === null) {
return undefined;
}
if (value.commandLine) {
return new types.ShellExecution(value.commandLine, value.options);
} else {
return new types.ShellExecution(value.command, value.args ? value.args : [], value.options);
}
}
}
namespace TaskHandleDTO {
export function from(value: types.Task): TaskHandleDTO {
let folder: UriComponents;
if (value.scope !== void 0 && typeof value.scope !== 'number') {
folder = value.scope.uri;
}
return {
id: value._id,
workspaceFolder: folder
};
}
}
namespace TaskDTO {
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO {
if (value === void 0 || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
}
let definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition);
let scope: number | UriComponents;
if (value.scope) {
if (typeof value.scope === 'number') {
scope = value.scope;
} else {
scope = value.scope.uri.toJSON();
}
}
if (!execution || !definition || !scope) {
return undefined;
}
let result: TaskDTO = {
_id: (value as types.Task)._id,
definition,
name: value.name,
source: {
extensionId: extension.id,
label: value.source,
scope: scope
},
execution,
isBackground: value.isBackground,
group: (value.group as types.TaskGroup).id,
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
problemMatchers: value.problemMatchers,
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers
};
return result;
}
export function to(value: TaskDTO, workspace: ExtHostWorkspace): types.Task {
if (value === void 0 || value === null) {
return undefined;
}
let execution: types.ShellExecution | types.ProcessExecution;
if (ProcessExecutionDTO.is(value.execution)) {
execution = ProcessExecutionDTO.to(value.execution);
} else if (ShellExecutionDTO.is(value.execution)) {
execution = ShellExecutionDTO.to(value.execution);
}
let definition: vscode.TaskDefinition = TaskDefinitionDTO.to(value.definition);
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder;
if (value.source) {
if (value.source.scope !== void 0) {
if (typeof value.source.scope === 'number') {
scope = value.source.scope;
} else {
scope = workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
}
} else {
scope = types.TaskScope.Workspace;
}
}
if (!execution || !definition || !scope) {
return undefined;
}
let result = new types.Task(definition, scope, value.name, value.source.label, execution, value.problemMatchers);
if (value.isBackground !== void 0) {
result.isBackground = value.isBackground;
}
if (value.group !== void 0) {
result.group = types.TaskGroup.from(value.group);
}
if (value.presentationOptions) {
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions);
}
if (value._id) {
result._id = value._id;
}
return result;
}
}
class TaskExecutionImpl implements vscode.TaskExecution {
constructor(readonly _id: string) {
}
}
namespace TaskExecutionDTO {
export function to(value: TaskExecutionDTO): vscode.TaskExecution {
return new TaskExecutionImpl(value.id);
}
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
return {
id: (value as TaskExecutionImpl)._id
};
}
}
interface HandlerData {
provider: vscode.TaskProvider;
extension: IExtensionDescription;
@@ -462,6 +702,9 @@ export class ExtHostTask implements ExtHostTaskShape {
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
constructor(mainContext: IMainContext, extHostWorkspace: ExtHostWorkspace) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTask);
this._extHostWorkspace = extHostWorkspace;
@@ -482,6 +725,56 @@ export class ExtHostTask implements ExtHostTaskShape {
});
}
public executeTaskProvider(): Thenable<vscode.Task[]> {
return this._proxy.$executeTaskProvider().then((values) => {
let result: vscode.Task[] = [];
for (let value of values) {
let task = TaskDTO.to(value, this._extHostWorkspace);
if (task) {
result.push(task);
}
}
return result;
});
}
public executeTask(extension: IExtensionDescription, task: vscode.Task): Thenable<vscode.TaskExecution> {
let tTask = (task as types.Task);
// We have a preserved ID. So the task didn't change.
if (tTask._id !== void 0) {
return this._proxy.$executeTask(TaskHandleDTO.from(tTask)).then(value => TaskExecutionDTO.to(value));
} else {
return this._proxy.$executeTask(TaskDTO.from(task, extension)).then(value => TaskExecutionDTO.to(value));
}
}
public $taskStarted(execution: TaskExecutionDTO): void {
this._onDidExecuteTask.fire({
execution: TaskExecutionDTO.to(execution)
});
}
get onDidStartTask(): Event<vscode.TaskStartEvent> {
return this._onDidExecuteTask.event;
}
public terminateTask(execution: vscode.TaskExecution): TPromise<void> {
if (!(execution instanceof TaskExecutionImpl)) {
throw new Error('No valid task execution provided');
}
return this._proxy.$terminateTask(TaskExecutionDTO.from(execution));
}
public $taskEnded(execution: TaskExecutionDTO): void {
this._onDidTerminateTask.fire({
execution: TaskExecutionDTO.to(execution)
});
}
get onDidEndTask(): Event<vscode.TaskEndEvent> {
return this._onDidTerminateTask.event;
}
public $provideTasks(handle: number): TPromise<TaskSystem.TaskSet> {
let handler = this._handlers.get(handle);
if (!handler) {

View File

@@ -1257,6 +1257,21 @@ export class TaskGroup implements vscode.TaskGroup {
public static Test: TaskGroup = new TaskGroup('test', 'Test');
public static from(value: string) {
switch (value) {
case 'clean':
return TaskGroup.Clean;
case 'build':
return TaskGroup.Build;
case 'rebuild':
return TaskGroup.Rebuild;
case 'test':
return TaskGroup.Test;
default:
return undefined;
}
}
constructor(id: string, _label: string) {
if (typeof id !== 'string') {
throw illegalArgument('name');
@@ -1411,6 +1426,8 @@ export enum TaskScope {
export class Task implements vscode.Task {
private __id: string;
private _definition: vscode.TaskDefinition;
private _definitionKey: string;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder;
@@ -1459,6 +1476,18 @@ export class Task implements vscode.Task {
this._isBackground = false;
}
get _id(): string {
return this.__id;
}
set _id(value: string) {
this.__id = value;
}
private clear(): void {
this.__id = undefined;
}
get definition(): vscode.TaskDefinition {
return this._definition;
}
@@ -1467,6 +1496,7 @@ export class Task implements vscode.Task {
if (value === void 0 || value === null) {
throw illegalArgument('Kind can\'t be undefined or null');
}
this.clear();
this._definitionKey = undefined;
this._definition = value;
}
@@ -1485,6 +1515,7 @@ export class Task implements vscode.Task {
}
set target(value: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder) {
this.clear();
this._scope = value;
}
@@ -1496,6 +1527,7 @@ export class Task implements vscode.Task {
if (typeof value !== 'string') {
throw illegalArgument('name');
}
this.clear();
this._name = value;
}
@@ -1507,6 +1539,7 @@ export class Task implements vscode.Task {
if (value === null) {
value = undefined;
}
this.clear();
this._execution = value;
}
@@ -1520,6 +1553,7 @@ export class Task implements vscode.Task {
this._hasDefinedMatchers = false;
return;
}
this.clear();
this._problemMatchers = value;
this._hasDefinedMatchers = true;
}
@@ -1536,6 +1570,7 @@ export class Task implements vscode.Task {
if (value !== true && value !== false) {
value = false;
}
this.clear();
this._isBackground = value;
}
@@ -1547,6 +1582,7 @@ export class Task implements vscode.Task {
if (typeof value !== 'string' || value.length === 0) {
throw illegalArgument('source must be a string of length > 0');
}
this.clear();
this._source = value;
}
@@ -1559,6 +1595,7 @@ export class Task implements vscode.Task {
this._group = undefined;
return;
}
this.clear();
this._group = value;
}
@@ -1570,6 +1607,7 @@ export class Task implements vscode.Task {
if (value === null) {
value = undefined;
}
this.clear();
this._presentationOptions = value;
}
}