Skip to content

Commit

Permalink
add fetchTasks() and executeTask() to tasks API
Browse files Browse the repository at this point in the history
- This pull request adds the support of executing a vscode.Task, and fetching tasks by type and schema version to the plugins API.
- resolved #5342

Signed-off-by: Liang Huang <liang.huang@ericsson.com>
  • Loading branch information
Liang Huang authored and elaihau committed Sep 5, 2019
1 parent e55e339 commit 8054da5
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 15 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## v0.11.0

- [task] added `tasks.fetchTasks()` and `tasks.executeTask()` to plugins API [#6058](https://github.com/theia-ide/theia/pull/6058)

Breaking changes:

- [core][plugin] support alternative commands in context menus [6069](https://github.com/theia-ide/theia/pull/6069)
Expand Down Expand Up @@ -72,8 +74,8 @@ Breaking changes:
- [task] fixed the problem where a detected task can be customized more than once [#5777](https://github.com/theia-ide/theia/pull/5777)
- [task] notified clients of TaskDefinitionRegistry on change [#5915](https://github.com/theia-ide/theia/pull/5915)
- [task] updated `isVisible` and `isEnabled` handling for `Run Selected Text` [#6018](https://github.com/theia-ide/theia/pull/6018)
- [tasks] added support for removing all data from tasks.json [#6033](https://github.com/theia-ide/theia/pull/6033)
- [tasks] updated compare task to use task definitions [#5975](https://github.com/theia-ide/theia/pull/5975)
- [task] added support for removing all data from tasks.json [#6033](https://github.com/theia-ide/theia/pull/6033)
- [task] updated compare task to use task definitions [#5975](https://github.com/theia-ide/theia/pull/5975)
- [terminal] added a preference `terminal.integrated.scrollback` to control the terminal scrollback [#5783](https://github.com/theia-ide/theia/pull/5783)
- [vscode] added support for `command` variable substitution [#5835](https://github.com/theia-ide/theia/pull/5835)
- [vscode] added support for `config` variable substitution [#5835](https://github.com/theia-ide/theia/pull/5835)
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,8 @@ export interface TasksExt {

export interface TasksMain {
$registerTaskProvider(handle: number, type: string): void;
$fetchTasks(taskVersion: string | undefined, taskType: string | undefined): Promise<TaskDto[]>;
$executeTask(taskDto: TaskDto): Promise<TaskExecutionDto | undefined>;
$taskExecutions(): Promise<TaskExecutionDto[]>;
$unregister(handle: number): void;
$terminateTask(id: number): void;
Expand Down
78 changes: 69 additions & 9 deletions packages/plugin-ext/src/main/browser/tasks-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import {
TasksMain,
MAIN_RPC_CONTEXT,
TasksExt
TaskExecutionDto,
TasksExt,
TaskDto
} from '../../common/plugin-api-rpc';
import { RPCProtocol } from '../../common/rpc-protocol';
import { DisposableCollection } from '@theia/core';
Expand All @@ -27,6 +29,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service
import { TaskInfo, TaskExitedEvent, TaskConfiguration } from '@theia/task/lib/common/task-protocol';
import { TaskWatcher } from '@theia/task/lib/common/task-watcher';
import { TaskService } from '@theia/task/lib/browser/task-service';
import { TaskDefinitionRegistry } from '@theia/task/lib/browser';

export class TasksMainImpl implements TasksMain {
private workspaceRootUri: string | undefined = undefined;
Expand All @@ -38,6 +41,7 @@ export class TasksMainImpl implements TasksMain {
private readonly taskWatcher: TaskWatcher;
private readonly taskService: TaskService;
private readonly workspaceService: WorkspaceService;
private readonly taskDefinitionRegistry: TaskDefinitionRegistry;

constructor(rpc: RPCProtocol, container: interfaces.Container, ) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TASKS_EXT);
Expand All @@ -46,6 +50,7 @@ export class TasksMainImpl implements TasksMain {
this.workspaceService = container.get(WorkspaceService);
this.taskWatcher = container.get(TaskWatcher);
this.taskService = container.get(TaskService);
this.taskDefinitionRegistry = container.get(TaskDefinitionRegistry);

this.workspaceService.roots.then(roots => {
const root = roots[0];
Expand Down Expand Up @@ -95,6 +100,60 @@ export class TasksMainImpl implements TasksMain {
this.disposables.set(handle, disposable);
}

async $fetchTasks(taskVersion: string | undefined, taskType: string | undefined): Promise<TaskDto[]> {
if (taskVersion && !taskVersion.startsWith('2.')) { // Theia does not support 1.x or earlier task versions
return [];
}

let found: TaskConfiguration[] = [];
const tasks = [...(await this.taskService.getConfiguredTasks()), ...(await this.taskService.getProvidedTasks())];
if (taskType) {
found = tasks.filter(t => {
if (!!this.taskDefinitionRegistry.getDefinition(t)) {
return t._source === taskType;
}
return t.type === taskType;
});
} else {
found = tasks;
}

const filtered: TaskConfiguration[] = [];
found.forEach((taskConfig, index) => {
const rest = found.slice(index + 1);
const isDuplicate = rest.some(restTask => this.taskDefinitionRegistry.compareTasks(taskConfig, restTask));
if (!isDuplicate) {
filtered.push(taskConfig);
}
});
return filtered.map(taskConfig => {
const dto: TaskDto = {
type: taskConfig.type,
label: taskConfig.label
};
const { _scope, _source, ...properties } = taskConfig;
dto.scope = _scope;
dto.source = _source;
for (const key in properties) {
if (properties.hasOwnProperty(key)) {
dto[key] = properties[key];
}
}
return dto;
});
}

async $executeTask(taskDto: TaskDto): Promise<TaskExecutionDto | undefined> {
const taskConfig = this.toTaskConfiguration(taskDto);
const taskInfo = await this.taskService.runTask(taskConfig);
if (taskInfo) {
return {
id: taskInfo.taskId,
task: taskInfo.config
};
}
}

$unregister(handle: number): void {
const disposable = this.disposables.get(handle);
if (disposable) {
Expand Down Expand Up @@ -123,10 +182,7 @@ export class TasksMainImpl implements TasksMain {
provideTasks: () =>
this.proxy.$provideTasks(handle).then(v =>
v!.map(taskDto =>
Object.assign(taskDto, {
_source: taskDto.source || 'plugin',
_scope: taskDto.scope
})
this.toTaskConfiguration(taskDto)
)
)
};
Expand All @@ -136,11 +192,15 @@ export class TasksMainImpl implements TasksMain {
return {
resolveTask: taskConfig =>
this.proxy.$resolveTask(handle, taskConfig).then(v =>
Object.assign(v!, {
_source: v!.source || 'plugin',
_scope: v!.scope
})
this.toTaskConfiguration(v!)
)
};
}

protected toTaskConfiguration(taskDto: TaskDto): TaskConfiguration {
return Object.assign(taskDto, {
_source: taskDto.source || 'plugin',
_scope: taskDto.scope
});
}
}
8 changes: 8 additions & 0 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,14 @@ export function createAPIFactory(
return tasksExt.registerTaskProvider(type, provider);
},

fetchTasks(filter?: theia.TaskFilter): Thenable<theia.Task[]> {
return tasksExt.fetchTasks(filter);
},

executeTask(task: theia.Task): Thenable<theia.TaskExecution> {
return tasksExt.executeTask(task);
},

get taskExecutions(): ReadonlyArray<theia.TaskExecution> {
return tasksExt.taskExecutions;
},
Expand Down
20 changes: 20 additions & 0 deletions packages/plugin-ext/src/plugin/tasks/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,26 @@ export class TasksExtImpl implements TasksExt {
return this.createDisposable(callId);
}

async fetchTasks(filter?: theia.TaskFilter): Promise<theia.Task[]> {
const taskVersion = filter ? filter.version : undefined;
const taskType = filter ? filter.type : undefined;
const taskDtos = await this.proxy.$fetchTasks(taskVersion, taskType);
return taskDtos.map(dto => converter.toTask(dto));
}

async executeTask(task: theia.Task): Promise<theia.TaskExecution> {
const taskDto = converter.fromTask(task);
if (taskDto) {
const executionDto = await this.proxy.$executeTask(taskDto);
if (executionDto) {
const taskExecution = this.getTaskExecution(executionDto);
return taskExecution;
}
throw new Error('Run task config does not return after being started');
}
throw new Error('Task was not successfully transformed into a task config');
}

$provideTasks(handle: number, token?: theia.CancellationToken): Promise<TaskDto[] | undefined> {
const adapter = this.adaptersMap.get(handle);
if (adapter) {
Expand Down
30 changes: 30 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8253,6 +8253,19 @@ declare module '@theia/plugin' {
exitCode: number;
}

export interface TaskFilter {
/**
* The task version as used in the tasks.json file.
* The string support the package.json semver notation.
*/
version?: string;

/**
* The type of tasks to return.
*/
type?: string;
}

export namespace tasks {

/**
Expand All @@ -8264,6 +8277,23 @@ declare module '@theia/plugin' {
*/
export function registerTaskProvider(type: string, provider: TaskProvider): Disposable;

/**
* Fetches all tasks available in the systems. This includes tasks
* from `tasks.json` files as well as tasks from task providers
* contributed through extensions and plugins.
*
* @param filter a filter to filter the return tasks.
*/
export function fetchTasks(filter?: TaskFilter): PromiseLike<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
*/
export function executeTask(task: Task): PromiseLike<TaskExecution>;

/**
* The currently active task executions or an empty array.
*/
Expand Down
10 changes: 6 additions & 4 deletions packages/task/src/browser/task-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ export class TaskService implements TaskConfigurationClient {
/**
* Run the last executed task.
*/
async runLastTask(): Promise<void> {
async runLastTask(): Promise<TaskInfo | undefined> {
if (!this.lastTask) {
return;
}
Expand All @@ -303,7 +303,7 @@ export class TaskService implements TaskConfigurationClient {
* Runs a task, by the source and label of the task configuration.
* It looks for configured and detected tasks.
*/
async run(source: string, taskLabel: string): Promise<void> {
async run(source: string, taskLabel: string): Promise<TaskInfo | undefined> {
let task = await this.getProvidedTask(source, taskLabel);
const customizationObject: TaskCustomization = { type: '' };
if (!task) { // if a detected task cannot be found, search from tasks.json
Expand Down Expand Up @@ -345,12 +345,12 @@ export class TaskService implements TaskConfigurationClient {
resolvedMatchers.push(resolvedMatcher);
}
}
this.runTask(task, {
return this.runTask(task, {
customization: { ...customizationObject, ...{ problemMatcher: resolvedMatchers } }
});
}

async runTask(task: TaskConfiguration, option?: RunTaskOption): Promise<void> {
async runTask(task: TaskConfiguration, option?: RunTaskOption): Promise<TaskInfo | undefined> {
const source = task._source;
const taskLabel = task.label;
if (option && option.customization) {
Expand Down Expand Up @@ -400,6 +400,8 @@ export class TaskService implements TaskConfigurationClient {
if (typeof taskInfo.terminalId === 'number') {
this.attach(taskInfo.terminalId, taskInfo.taskId);
}

return taskInfo;
}

async runTaskByLabel(taskLabel: string): Promise<boolean> {
Expand Down

0 comments on commit 8054da5

Please sign in to comment.