Skip to content

Commit

Permalink
Add 'tasks.registerTaskProvider' for Plugin API
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com>
  • Loading branch information
RomanNikitenko committed Dec 10, 2018
1 parent 29dd2bd commit 558d4b5
Show file tree
Hide file tree
Showing 11 changed files with 1,140 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- [plug-in] added `window.showTextDocument` Plug-in API
- [plug-in] added ability to provide custom namespaces for the Plug-in API
- [plug-in] registered a type definition provider
- [plug-in] added `tasks.registerTaskProvider` Plug-in API
- [preview-editor] added the ability to open editors in preview mode
- [process] added the ability to create new node processes through forking
- [search-in-workspace] prompt users when performing `Replace All...` to limit accidental triggering
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@theia/navigator": "^0.3.17",
"@theia/plugin": "^0.3.17",
"@theia/workspace": "^0.3.17",
"@theia/task": "^0.3.17",
"decompress": "^4.2.0",
"jsonc-parser": "^2.0.2",
"lodash.clonedeep": "^4.5.0",
Expand Down
29 changes: 29 additions & 0 deletions packages/plugin-ext/src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,23 @@ export interface WorkspaceEditDto {
rejectReason?: string;
}

export interface CommandProperties {
command: string;
args?: string[];
options?: object;
}
export interface TaskDto {
type: string;
label: string;
// tslint:disable-next-line:no-any
[key: string]: any;
}

export interface ProcessTaskDto extends TaskDto, CommandProperties {
windows?: CommandProperties;
cwd?: string;
}

export interface LanguagesExt {
$provideCompletionItems(handle: number, resource: UriComponents, position: Position, context: CompletionContext): Promise<CompletionResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion): Promise<Completion>;
Expand Down Expand Up @@ -882,6 +899,7 @@ export const PLUGIN_RPC_CONTEXT = {
LANGUAGES_MAIN: createProxyIdentifier<LanguagesMain>('LanguagesMain'),
CONNECTION_MAIN: createProxyIdentifier<ConnectionMain>('ConnectionMain'),
WEBVIEWS_MAIN: createProxyIdentifier<WebviewsMain>('WebviewsMain'),
TASKS_MAIN: createProxyIdentifier<TasksMain>('TasksMain'),
};

export const MAIN_RPC_CONTEXT = {
Expand All @@ -900,4 +918,15 @@ export const MAIN_RPC_CONTEXT = {
LANGUAGES_EXT: createProxyIdentifier<LanguagesExt>('LanguagesExt'),
CONNECTION_EXT: createProxyIdentifier<ConnectionExt>('ConnectionExt'),
WEBVIEWS_EXT: createProxyIdentifier<WebviewsExt>('WebviewsExt'),
TASKS_EXT: createProxyIdentifier<TasksExt>('TasksExt'),
};

export interface TasksExt {
$provideTasks(handle: number): Promise<TaskDto[] | undefined>;
$resolveTask(handle: number, task: TaskDto): Promise<TaskDto | undefined>;
}

export interface TasksMain {
$registerTaskProvider(handle: number, type: string): void;
$unregister(handle: number): void;
}
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/main/browser/main-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { TreeViewsMainImpl } from './view/tree-views-main';
import { NotificationMainImpl } from './notification-main';
import { ConnectionMainImpl } from './connection-main';
import { WebviewsMainImpl } from './webviews-main';
import { TasksMainImpl } from './tasks-main';

export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
const commandRegistryMain = new CommandRegistryMainImpl(rpc, container);
Expand Down Expand Up @@ -84,4 +85,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container

const pluginConnection = new ConnectionMainImpl(rpc);
rpc.set(PLUGIN_RPC_CONTEXT.CONNECTION_MAIN, pluginConnection);

const tasksMain = new TasksMainImpl(rpc, container);
rpc.set(PLUGIN_RPC_CONTEXT.TASKS_MAIN, tasksMain);
}
71 changes: 71 additions & 0 deletions packages/plugin-ext/src/main/browser/tasks-main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/********************************************************************************
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import {
TasksMain,
MAIN_RPC_CONTEXT,
TasksExt
} from '../../api/plugin-api';
import { RPCProtocol } from '../../api/rpc-protocol';
import { DisposableCollection } from '@theia/core';
import { TaskProviderRegistry, TaskResolverRegistry, TaskProvider, TaskResolver } from '@theia/task/lib/browser/task-contribution';
import { interfaces } from 'inversify';

export class TasksMainImpl implements TasksMain {

private readonly proxy: TasksExt;
private readonly disposables = new Map<number, monaco.IDisposable>();
private readonly taskProviderRegistry: TaskProviderRegistry;
private readonly taskResolverRegistry: TaskResolverRegistry;

constructor(rpc: RPCProtocol, container: interfaces.Container, ) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TASKS_EXT);
this.taskProviderRegistry = container.get(TaskProviderRegistry);
this.taskResolverRegistry = container.get(TaskResolverRegistry);
}

$registerTaskProvider(handle: number, type: string): void {
const taskProvider = this.createTaskProvider(handle);
const taskResolver = this.createTaskResolver(handle);

const disposable = new DisposableCollection();
disposable.push(this.taskProviderRegistry.register(type, taskProvider));
disposable.push(this.taskResolverRegistry.register(type, taskResolver));
this.disposables.set(handle, disposable);
}

$unregister(handle: number): void {
const disposable = this.disposables.get(handle);
if (disposable) {
disposable.dispose();
this.disposables.delete(handle);
}
}

protected createTaskProvider(handle: number): TaskProvider {
return {
provideTasks: () =>
this.proxy.$provideTasks(handle).then(v => v!),
};
}

protected createTaskResolver(handle: number): TaskResolver {
return {
resolveTask: taskConfig =>
this.proxy.$resolveTask(handle, taskConfig).then(v => v!),
};
}
}
29 changes: 27 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,15 @@ import {
WorkspaceEdit,
SymbolInformation,
FileType,
FileChangeType
FileChangeType,
ShellQuoting,
ShellExecution,
ProcessExecution,
TaskScope,
TaskPanelKind,
TaskRevealKind,
TaskGroup,
Task
} from './types-impl';
import { EditorsAndDocumentsExtImpl } from './editors-and-documents';
import { TextEditorsExtImpl } from './text-editors';
Expand All @@ -95,6 +103,7 @@ import { MarkdownString } from './markdown-string';
import { TreeViewsExtImpl } from './tree/tree-views';
import { ConnectionExtImpl } from './connection-ext';
import { WebviewsExtImpl } from './webviews';
import { TasksExtImpl } from './tasks/tasks';

export function createAPIFactory(
rpc: RPCProtocol,
Expand All @@ -119,6 +128,7 @@ export function createAPIFactory(
const languagesExt = rpc.set(MAIN_RPC_CONTEXT.LANGUAGES_EXT, new LanguagesExtImpl(rpc, documents, commandRegistry));
const treeViewsExt = rpc.set(MAIN_RPC_CONTEXT.TREE_VIEWS_EXT, new TreeViewsExtImpl(rpc, commandRegistry));
const webviewExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEWS_EXT, new WebviewsExtImpl(rpc));
const tasksExt = rpc.set(MAIN_RPC_CONTEXT.TASKS_EXT, new TasksExtImpl(rpc));
rpc.set(MAIN_RPC_CONTEXT.CONNECTION_EXT, new ConnectionExtImpl(rpc));

return function (plugin: InternalPlugin): typeof theia {
Expand Down Expand Up @@ -480,6 +490,12 @@ export function createAPIFactory(
}
};

const tasks: typeof theia.tasks = {
registerTaskProvider(type: string, provider: theia.TaskProvider): theia.Disposable {
return tasksExt.registerTaskProvider(type, provider);
}
};

return <typeof theia>{
version: require('../../package.json').version,
commands,
Expand All @@ -489,6 +505,7 @@ export function createAPIFactory(
languages,
plugins,
debug,
tasks,
// Types
StatusBarAlignment: StatusBarAlignment,
Disposable: Disposable,
Expand Down Expand Up @@ -543,7 +560,15 @@ export function createAPIFactory(
WorkspaceEdit,
SymbolInformation,
FileType,
FileChangeType
FileChangeType,
ShellQuoting,
ShellExecution,
ProcessExecution,
TaskScope,
TaskRevealKind,
TaskPanelKind,
TaskGroup,
Task
};
};
}
Expand Down
67 changes: 67 additions & 0 deletions packages/plugin-ext/src/plugin/tasks/task-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/********************************************************************************
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import * as theia from '@theia/plugin';
import * as Converter from '../type-converters';
import { ObjectIdentifier } from '../../common/object-identifier';
import { createToken } from '../token-provider';
import { TaskDto } from '../../common';

export class TaskProviderAdapter {
private cacheId = 0;
private cache = new Map<number, theia.Task>();

constructor(private readonly provider: theia.TaskProvider) { }

provideTasks(): Promise<TaskDto[] | undefined> {
return Promise.resolve(this.provider.provideTasks(createToken())).then(tasks => {
if (!Array.isArray(tasks)) {
return undefined;
}
const result: TaskDto[] = [];
for (const task of tasks) {
const data = Converter.fromTask(task);
if (!data) {
continue;
}

const id = this.cacheId++;
ObjectIdentifier.mixin(data, id);
this.cache.set(id, task);
result.push(data);
}
return result;
});
}

resolveTask(task: TaskDto): Promise<TaskDto | undefined> {
if (typeof this.provider.resolveTask !== 'function') {
return Promise.resolve(undefined);
}
const id = ObjectIdentifier.of(task);
const item = this.cache.get(id);
if (!item) {
return Promise.resolve(undefined);
}

return Promise.resolve(this.provider.resolveTask(item, createToken())).then(value => {
if (value) {
return Converter.fromTask(value);
}
return undefined;
});
}
}
78 changes: 78 additions & 0 deletions packages/plugin-ext/src/plugin/tasks/tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/********************************************************************************
* Copyright (C) 2018 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import {
PLUGIN_RPC_CONTEXT,
TasksExt,
TasksMain,
TaskDto
} from '../../api/plugin-api';
import * as theia from '@theia/plugin';
import { Disposable } from '../types-impl';
import { RPCProtocol } from '../../api/rpc-protocol';
import { TaskProviderAdapter } from './task-provider';

export class TasksExtImpl implements TasksExt {
private proxy: TasksMain;

private callId = 0;
private adaptersMap = new Map<number, TaskProviderAdapter>();

constructor(rpc: RPCProtocol) {
this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.TASKS_MAIN);
}

registerTaskProvider(type: string, provider: theia.TaskProvider): theia.Disposable {
const callId = this.addNewAdapter(new TaskProviderAdapter(provider));
this.proxy.$registerTaskProvider(callId, type);
return this.createDisposable(callId);
}

$provideTasks(handle: number): Promise<TaskDto[] | undefined> {
const adapter = this.adaptersMap.get(handle);
if (adapter) {
return adapter.provideTasks();
} else {
return Promise.reject(new Error('No adapter found to provide tasks'));
}
}

$resolveTask(handle: number, task: TaskDto): Promise<TaskDto | undefined> {
const adapter = this.adaptersMap.get(handle);
if (adapter) {
return adapter.resolveTask(task);
} else {
return Promise.reject(new Error('No adapter found to resolve task'));
}
}

private addNewAdapter(adapter: TaskProviderAdapter): number {
const callId = this.nextCallId();
this.adaptersMap.set(callId, adapter);
return callId;
}

private nextCallId(): number {
return this.callId++;
}

private createDisposable(callId: number): theia.Disposable {
return new Disposable(() => {
this.adaptersMap.delete(callId);
this.proxy.$unregister(callId);
});
}
}
Loading

0 comments on commit 558d4b5

Please sign in to comment.