提交 2f5061ff 编写于 作者: D Dirk Baeumer

Implements #45664: Add API to query and execute tasks

上级 d1b4302b
......@@ -781,32 +781,65 @@ declare module 'vscode' {
//#region Tasks
/**
* A task item represents a task in the system. It can be used to
* present task information in the user interface or to execute the
* underlying task.
* An object representing an executed Task. It can be used
* to terminate a task.
*/
export interface TaskItem {
export interface TaskExecution {
}
/**
* An event signaling the start of a task execution.
*/
interface TaskStartEvent {
/**
* The task item representing the task that got started.
*/
execution: TaskExecution;
}
/**
* An event signaling the end of an executed task.
*/
interface TaskEndEvent {
/**
* The task item representing the task that finished.
*/
execution: TaskExecution;
}
export namespace workspace {
/**
* A unique ID representing the underlying task.
* Fetches all task available in the systems. This includes tasks
* from `tasks.json` files as well as tasks from task providers
* contributed through extensions.
*/
readonly id: string;
export function fetchTasks(): Thenable<Task[]>;
/**
* A human readable label of the task.
* Executes a task that is managed by VS Code. The returned
* task execution can be used to terminate the task.
*
* @param task the task to execute
*/
readonly label: string;
export function executeTask(task: Task): Thenable<TaskExecution>;
/**
* The task definition.
* Fires when a task starts.
*/
export const onDidStartTask: Event<TaskStartEvent>;
/**
* Terminates a task that was previously started using `executeTask`
*
* @param task the task to terminate
*/
readonly definition: TaskDefinition;
export function terminateTask(task: TaskExecution): void;
/**
* The workspace folder the task belongs to. Is undefined
* to tasks that aren't scoped to a workspace folder.
* Fires when a task ends.
*/
readonly workspaceFolder: WorkspaceFolder | undefined;
export const onDidEndTask: Event<TaskEndEvent>;
}
//#endregion
......
......@@ -4,16 +4,327 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as crypto from 'crypto';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as Objects from 'vs/base/common/objects';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Types from 'vs/base/common/types';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ContributedTask, ExtensionTaskSourceTransfer } from 'vs/workbench/parts/tasks/common/tasks';
import {
ContributedTask, ExtensionTaskSourceTransfer, TaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource
} from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import URI from 'vs/base/common/uri';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO
} from 'vs/workbench/api/shared/tasks';
export { TaskDTO, TaskHandleDTO, TaskExecutionDTO };
namespace TaskExecutionDTO {
export function from(value: TaskExecution): TaskExecutionDTO {
return {
id: value.id,
};
}
export function to(value: TaskExecutionDTO, workspace: IWorkspaceContextService): TaskExecution {
return {
id: value.id,
};
}
}
namespace TaskDefinitionDTO {
export function from(value: TaskIdentifier): TaskDefinitionDTO {
let result = Objects.assign(Object.create(null), value);
delete result._key;
return result;
}
export function to(value: TaskDefinitionDTO): TaskIdentifier {
const hash = crypto.createHash('md5');
hash.update(JSON.stringify(value));
let result = Objects.assign(Object.create(null), value);
result._key = hash.digest('hex');
return result;
}
}
namespace TaskPresentationOptionsDTO {
export function from(value: PresentationOptions): TaskPresentationOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
export function to(value: TaskPresentationOptionsDTO): PresentationOptions {
if (value === void 0 || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
}
namespace ProcessExecutionOptionsDTO {
export function from(value: CommandOptions): ProcessExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return {
cwd: value.cwd,
env: value.env
};
}
export function to(value: ProcessExecutionOptionsDTO): CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
return {
cwd: value.cwd,
env: value.env
};
}
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
export function from(value: CommandConfiguration): ProcessExecutionDTO {
let process: string = Types.isString(value.name) ? value.name : value.name.value;
let args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : [];
let result: ProcessExecutionDTO = {
process: process,
args: args
};
if (value.options) {
result.options = ProcessExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ProcessExecutionDTO): CommandConfiguration {
let result: CommandConfiguration = {
runtime: RuntimeType.Process,
name: value.process,
args: value.args,
presentation: undefined
};
if (value.options) {
result.options = ProcessExecutionOptionsDTO.to(value.options);
}
return result;
}
}
namespace ShellExecutionOptionsDTO {
export function from(value: CommandOptions): ShellExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: ShellExecutionOptionsDTO = {
cwd: value.cwd,
env: value.env
};
if (value.shell) {
result.executable = value.shell.executable;
result.shellArgs = value.shell.args;
result.shellQuoting = value.shell.quoting;
}
return result;
}
export function to(value: ShellExecutionOptionsDTO): CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
let result: CommandOptions = {
cwd: value.cwd,
env: value.env
};
if (value.executable) {
result.shell = {
executable: value.executable
};
if (value.shellArgs) {
result.shell.args = value.shellArgs;
}
if (value.shellQuoting) {
result.shell.quoting = value.shellQuoting;
}
}
return result;
}
}
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: CommandConfiguration): ShellExecutionDTO {
let result: ShellExecutionDTO = {};
if (value.name && Types.isString(value.name) && (value.args === void 0 || value.args === null || value.args.length === 0)) {
result.commandLine = value.name;
} else {
result.command = value.name;
result.args = value.args;
}
if (value.options) {
result.options = ShellExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ShellExecutionDTO): CommandConfiguration {
let result: CommandConfiguration = {
runtime: RuntimeType.Shell,
name: value.commandLine ? value.commandLine : value.command,
args: value.args,
presentation: undefined
};
if (value.options) {
result.options = ShellExecutionOptionsDTO.to(value.options);
}
return result;
}
}
namespace TaskSourceDTO {
export function from(value: TaskSource): TaskSourceDTO {
let result: TaskSourceDTO = {
label: value.label
};
if (value.kind === TaskSourceKind.Extension) {
result.extensionId = value.extension;
if (value.workspaceFolder) {
result.scope = value.workspaceFolder.uri;
} else {
result.scope = value.scope;
}
} else if (value.kind === TaskSourceKind.Workspace) {
result.extensionId = '$core';
result.scope = value.config.workspaceFolder.uri;
}
return result;
}
export function to(value: TaskSourceDTO, workspace: IWorkspaceContextService): ExtensionTaskSource {
let scope: TaskScope;
let workspaceFolder: IWorkspaceFolder;
if (value.scope === void 0) {
if (workspace.getWorkspace().folders.length === 0) {
scope = TaskScope.Global;
workspaceFolder = undefined;
} else {
scope = TaskScope.Folder;
workspaceFolder = workspace.getWorkspace().folders[0];
}
} else if (typeof value.scope === 'number') {
scope = value.scope;
} else {
scope = TaskScope.Folder;
workspaceFolder = workspace.getWorkspaceFolder(URI.revive(value.scope));
}
let result: ExtensionTaskSource = {
kind: TaskSourceKind.Extension,
label: value.label,
extension: value.extensionId,
scope,
workspaceFolder
};
return result;
}
}
namespace TaskHandleDTO {
export function is(value: any): value is TaskHandleDTO {
let candidate: TaskHandleDTO = value;
return candidate && Types.isString(candidate.id) && !!candidate.workspaceFolder;
}
}
namespace TaskDTO {
export function from(task: Task): TaskDTO {
if (task === void 0 || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) {
return undefined;
}
let result: TaskDTO = {
_id: task._id,
name: task.name,
definition: TaskDefinitionDTO.from(Task.getTaskDefinition(task)),
source: TaskSourceDTO.from(task._source),
execution: undefined,
presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
isBackground: task.isBackground,
problemMatchers: [],
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false
};
if (task.group) {
result.group = task.group;
}
if (task.command) {
if (task.command.runtime === RuntimeType.Process) {
result.execution = ProcessExecutionDTO.from(task.command);
} else if (task.command.runtime === RuntimeType.Shell) {
result.execution = ShellExecutionDTO.from(task.command);
}
}
if (task.problemMatchers) {
for (let matcher of task.problemMatchers) {
if (Types.isString(matcher)) {
result.problemMatchers.push(matcher);
}
}
}
if (!result.execution) {
return undefined;
}
return result;
}
export function to(task: TaskDTO, workspace: IWorkspaceContextService): Task {
if (typeof task.name !== 'string') {
return undefined;
}
let command: CommandConfiguration;
if (ShellExecutionDTO.is(task.execution)) {
command = ShellExecutionDTO.to(task.execution);
} else if (ProcessExecutionDTO.is(task.execution)) {
command = ProcessExecutionDTO.to(task.execution);
}
if (!command) {
return undefined;
}
command.presentation = TaskPresentationOptionsDTO.to(task.presentationOptions);
let source = TaskSourceDTO.to(task.source, workspace);
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let definition = TaskDefinitionDTO.to(task.definition);
let id = `${task.source.extensionId}.${definition._key}`;
let result: ContributedTask = {
_id: id, // uuidMap.getUUID(identifier),
_source: source,
_label: label,
type: definition.type,
defines: definition,
name: task.name,
identifier: label,
group: task.group,
command: command,
isBackground: !!task.isBackground,
problemMatchers: task.problemMatchers.slice(),
hasDefinedMatchers: task.hasDefinedMatchers
};
return result;
}
}
@extHostNamedCustomer(MainContext.MainThreadTask)
export class MainThreadTask implements MainThreadTaskShape {
......@@ -28,6 +339,14 @@ export class MainThreadTask implements MainThreadTaskShape {
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask);
this._activeHandles = Object.create(null);
this._taskService.onDidStateChange((event: TaskEvent) => {
let task = event.__task;
if (event.kind === TaskEventKind.Start) {
this._proxy.$taskStarted(TaskExecutionDTO.from(Task.getTaskExecution(task)));
} else if (event.kind === TaskEventKind.End) {
this._proxy.$taskEnded(TaskExecutionDTO.from(Task.getTaskExecution(task)));
}
});
}
public dispose(): void {
......@@ -63,4 +382,60 @@ export class MainThreadTask implements MainThreadTaskShape {
delete this._activeHandles[handle];
return TPromise.wrap<void>(undefined);
}
public $executeTaskProvider(): TPromise<TaskDTO[]> {
return this._taskService.tasks().then((tasks) => {
let result: TaskDTO[] = [];
for (let task of tasks) {
let item = TaskDTO.from(task);
if (item) {
result.push(item);
}
}
return result;
});
}
public $executeTask(value: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO> {
return new TPromise<TaskExecutionDTO>((resolve, reject) => {
if (TaskHandleDTO.is(value)) {
let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => {
this._taskService.run(task);
let result: TaskExecutionDTO = {
id: value.id
};
resolve(result);
}, (error) => {
reject(new Error('Task not found'));
});
} else {
let task = TaskDTO.to(value, this._workspaceContextServer);
this._taskService.run(task);
let result: TaskExecutionDTO = {
id: task._id
};
resolve(result);
}
});
}
public $terminateTask(value: TaskExecutionDTO): TPromise<void> {
let execution: TaskExecution = TaskExecutionDTO.to(value, this._workspaceContextServer);
return new TPromise<void>((resolve, reject) => {
this._taskService.getActiveTasks().then((tasks) => {
for (let task of tasks) {
if (execution.id === task._id) {
this._taskService.terminate(task).then((value) => {
resolve(undefined);
}, (error) => {
reject(undefined);
});
return;
}
}
reject(new Error('Task to terminate not found'));
});
});
}
}
......@@ -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);
}),
......
......@@ -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 {
......
......@@ -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();
}
}
......
......@@ -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) {
......
......@@ -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;
}
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { UriComponents } from 'vs/base/common/uri';
export interface TaskDefinitionDTO {
type: string;
[name: string]: any;
}
export interface TaskExecutionDTO {
id: string;
}
export interface TaskPresentationOptionsDTO {
reveal?: number;
echo?: boolean;
focus?: boolean;
panel?: number;
}
export interface ExecutionOptionsDTO {
cwd?: string;
env?: { [key: string]: string };
}
export interface ProcessExecutionOptionsDTO extends ExecutionOptionsDTO {
}
export interface ProcessExecutionDTO {
process: string;
args: string[];
options?: ProcessExecutionOptionsDTO;
}
export interface ShellQuotingOptionsDTO {
escape?: string | {
escapeChar: string;
charsToEscape: string;
};
strong?: string;
weak?: string;
}
export interface ShellExecutionOptionsDTO extends ExecutionOptionsDTO {
executable?: string;
shellArgs?: string[];
shellQuoting?: ShellQuotingOptionsDTO;
}
export interface ShellQuotedStringDTO {
value: string;
quoting: number;
}
export interface ShellExecutionDTO {
commandLine?: string;
command?: string | ShellQuotedStringDTO;
args?: (string | ShellQuotedStringDTO)[];
options?: ShellExecutionOptionsDTO;
}
export interface TaskSourceDTO {
label: string;
extensionId?: string;
scope?: number | UriComponents;
}
export interface TaskHandleDTO {
id: string;
workspaceFolder: UriComponents;
}
export interface TaskDTO {
_id: string;
name: string;
execution: ProcessExecutionDTO | ShellExecutionDTO;
definition: TaskDefinitionDTO;
isBackground: boolean;
source: TaskSourceDTO;
group?: string;
presentationOptions: TaskPresentationOptionsDTO;
problemMatchers: string[];
hasDefinedMatchers: boolean;
}
\ No newline at end of file
......@@ -47,9 +47,9 @@ export interface ITaskService {
terminateAll(): TPromise<TaskTerminateResponse[]>;
tasks(): TPromise<Task[]>;
/**
* @param identifier The task's name, label or defined identifier.
* @param alias The task's name, label or defined identifier.
*/
getTask(workspaceFolder: IWorkspaceFolder | string, identifier: string): TPromise<Task>;
getTask(workspaceFolder: IWorkspaceFolder | string, alias: string, compareId?: boolean): TPromise<Task>;
getTasksForGroup(group: string): TPromise<Task[]>;
getRecentlyUsedTasks(): LinkedMap<string, string>;
createSorter(): TaskSorter;
......
......@@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
import * as Types from 'vs/base/common/types';
import { IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import * as Objects from 'vs/base/common/objects';
import { generateUuid } from 'vs/base/common/uuid';
import { UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ProblemMatcher } from 'vs/workbench/parts/tasks/common/problemMatcher';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { generateUuid } from '../../../../base/common/uuid';
export enum ShellQuoting {
......@@ -598,8 +598,8 @@ export namespace Task {
}
}
export function matches(task: Task, alias: string): boolean {
return alias === task._label || alias === task.identifier;
export function matches(task: Task, alias: string, compareId: boolean = false): boolean {
return alias === task._label || alias === task.identifier || (compareId && alias === task._id);
}
export function getQualifiedLabel(task: Task): string {
......@@ -610,13 +610,53 @@ export namespace Task {
return task._label;
}
}
export function getTaskItem(task: Task): TaskItem {
let folder: IWorkspaceFolder = Task.getWorkspaceFolder(task);
let definition: TaskIdentifier;
if (ContributedTask.is(task)) {
definition = task.defines;
} else if (CustomTask.is(task) && task.command !== void 0) {
definition = CustomTask.getDefinition(task);
} else {
return undefined;
}
let result: TaskItem = {
id: task._id,
label: task._label,
definition: definition,
workspaceFolder: folder
};
return result;
}
export function getTaskDefinition(task: Task): TaskIdentifier {
if (ContributedTask.is(task)) {
return task.defines;
} else if (CustomTask.is(task) && task.command !== void 0) {
return CustomTask.getDefinition(task);
} else {
return undefined;
}
}
export function getTaskExecution(task: Task): TaskExecution {
let result: TaskExecution = {
id: task._id
};
return result;
}
}
export interface TaskItemTransfer {
export interface TaskItem {
id: string;
label: string;
definition: TaskIdentifier;
workspaceFolderUri: URI;
workspaceFolder: IWorkspaceFolder;
}
export interface TaskExecution {
id: string;
}
export enum ExecutionEngine {
......@@ -679,10 +719,12 @@ export class TaskSorter {
}
export enum TaskEventKind {
Start = 'start',
Active = 'active',
Inactive = 'inactive',
Terminated = 'terminated',
Changed = 'changed',
Terminated = 'terminated',
End = 'end'
}
......@@ -701,7 +743,7 @@ export interface TaskEvent {
}
export namespace TaskEvent {
export function create(kind: TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated, task: Task);
export function create(kind: TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.Start | TaskEventKind.End, task: Task);
export function create(kind: TaskEventKind.Changed);
export function create(kind: TaskEventKind, task?: Task): TaskEvent {
if (task) {
......
......@@ -74,7 +74,7 @@ import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, T
import {
Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent,
TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind,
TaskIdentifier, TaskSorter, TaskItemTransfer
TaskIdentifier, TaskSorter, TaskItem
} from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, ITaskProvider, RunOptions, CustomizationProperties } from 'vs/workbench/parts/tasks/common/taskService';
import { getTemplates as getTaskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
......@@ -582,23 +582,12 @@ class TaskService implements ITaskService {
CommandsRegistry.registerCommand('_executeTaskProvider', (accessor, args) => {
return this.tasks().then((tasks) => {
let result: TaskItemTransfer[] = [];
let result: TaskItem[] = [];
for (let task of tasks) {
let folder: IWorkspaceFolder = Task.getWorkspaceFolder(task);
let folderUri = folder ? folder.uri : undefined;
let definition: TaskIdentifier;
if (ContributedTask.is(task)) {
definition = task.defines;
} else if (CustomTask.is(task) && task.command !== void 0) {
definition = CustomTask.getDefinition(task);
}
let handle: TaskItemTransfer = {
id: task._id,
label: task._label,
definition: definition,
workspaceFolderUri: folderUri
};
result.push(handle);
let item = Task.getTaskItem(task);
if (item) {
result.push(item);
}
}
return result;
});
......@@ -685,7 +674,7 @@ class TaskService implements ITaskService {
return this._providers.delete(handle);
}
public getTask(folder: IWorkspaceFolder | string, alias: string): TPromise<Task> {
public getTask(folder: IWorkspaceFolder | string, alias: string, compareId: boolean = false): TPromise<Task> {
let name = Types.isString(folder) ? folder : folder.name;
if (this.ignoredWorkspaceFolders.some(ignored => ignored.name === name)) {
return TPromise.wrapError(new Error(nls.localize('TaskServer.folderIgnored', 'The folder {0} is ignored since it uses task version 0.1.0', name)));
......@@ -696,7 +685,7 @@ class TaskService implements ITaskService {
return undefined;
}
for (let task of values) {
if (Task.matches(task, alias)) {
if (Task.matches(task, alias, compareId)) {
return task;
}
}
......
......@@ -288,6 +288,7 @@ export class TerminalTaskSystem implements ITaskSystem {
watchingProblemMatcher.aboutToStart();
let delayer: Async.Delayer<any> = undefined;
[terminal, executedCommand] = this.createTerminal(task);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task));
const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers);
const onData = terminal.onLineData((line) => {
watchingProblemMatcher.processLine(line);
......@@ -328,12 +329,14 @@ export class TerminalTaskSystem implements ITaskSystem {
this.terminalService.setActiveInstance(terminal);
this.terminalService.showPanel(false);
}
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task));
resolve({ exitCode });
});
});
} else {
promise = new TPromise<ITaskSummary>((resolve, reject) => {
[terminal, executedCommand] = this.createTerminal(task);
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task));
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
let problemMatchers = this.resolveMatchers(task, task.problemMatchers);
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService);
......@@ -359,6 +362,7 @@ export class TerminalTaskSystem implements ITaskSystem {
startStopProblemMatcher.dispose();
registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle));
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task));
// See https://github.com/Microsoft/vscode/issues/31965
if (exitCode === 0 && startStopProblemMatcher.numberOfMatches > 0) {
exitCode = 1;
......
......@@ -35,6 +35,7 @@ import { NullLogService } from 'vs/platform/log/common/log';
import { ITextModel } from 'vs/editor/common/model';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { generateUuid } from 'vs/base/common/uuid';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
const defaultSelector = { scheme: 'far' };
const model: ITextModel = EditorModel.createFromString(
......@@ -51,6 +52,7 @@ let rpcProtocol: TestRPCProtocol;
let extHost: ExtHostLanguageFeatures;
let mainThread: MainThreadLanguageFeatures;
let commands: ExtHostCommands;
let task: ExtHostTask;
let workspace: ExtHostWorkspace;
let disposables: vscode.Disposable[] = [];
let originalErrorHandler: (e: any) => any;
......@@ -119,9 +121,10 @@ suite('ExtHostLanguageFeatureCommands', function () {
commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService());
workspace = new ExtHostWorkspace(rpcProtocol, { id: generateUuid(), name: 'Test', folders: [] }, new NullLogService());
task = new ExtHostTask(rpcProtocol, workspace);
rpcProtocol.set(ExtHostContext.ExtHostCommands, commands);
rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol));
ExtHostApiCommands.register(commands, workspace);
ExtHostApiCommands.register(commands, task);
const diagnostics = new ExtHostDiagnostics(rpcProtocol);
rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册