Skip to content

Commit

Permalink
Make vscode/theia APIs contributed instead of built-in
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Mäder <tmader@redhat.com>
  • Loading branch information
tsmaeder committed Jul 16, 2020
1 parent a1ea230 commit 10deabf
Show file tree
Hide file tree
Showing 26 changed files with 626 additions and 442 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ import { ContainerModule } from 'inversify';
import { CommandContribution } from '@theia/core';
import { PluginVscodeCommandsContribution } from './plugin-vscode-commands-contribution';
import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment';
import { VSCodeMainPluginAPIProvider } from './vscode-main-plugin-api-provider';
import { MainPluginApiProvider } from '@theia/plugin-ext/lib/common/plugin-ext-api-contribution';

export default new ContainerModule(bind => {
bind(VSCodeMainPluginAPIProvider).toSelf().inSingletonScope();
bind(MainPluginApiProvider).toService(VSCodeMainPluginAPIProvider);
bind(PluginVSCodeEnvironment).toSelf().inSingletonScope();
bind(PluginVscodeCommandsContribution).toSelf().inSingletonScope();
bind(CommandContribution).toDynamicValue(context => context.container.get(PluginVscodeCommandsContribution));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/********************************************************************************
* 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 { interfaces, injectable } from 'inversify';
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { MainPluginApiProvider } from '@theia/plugin-ext/lib/common';
import { TheiaAPIInitParameters } from '@theia/plugin-ext/lib/plugin/plugin-context';
import { TheiaMainPluginAPIProvider } from '@theia/plugin-ext/lib/main/browser/theia-main-plugin-api-provider';

@injectable()
export class VSCodeMainPluginAPIProvider implements MainPluginApiProvider {
readonly id: string = 'vscode';

initialize(rpc: RPCProtocol, container: interfaces.Container): void {
// main API will be set up by theia
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
async computeInitParameters?(rpc: RPCProtocol, container: interfaces.Container): Promise<TheiaAPIInitParameters> {
const theiaMainPluginAPIProvider: TheiaMainPluginAPIProvider = container.get(TheiaMainPluginAPIProvider);
return theiaMainPluginAPIProvider.computeInitParameters!(rpc, container);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ import { PluginVsCodeDirectoryHandler } from './plugin-vscode-directory-handler'
import { VsCodePluginScanner } from './scanner-vscode';
import { PluginVsCodeCliContribution } from './plugin-vscode-cli-contribution';
import { CliContribution } from '@theia/core/lib/node';
import { PluginHostEnvironmentVariable } from '@theia/plugin-ext/lib/common';
import { PluginHostEnvironmentVariable, ExtPluginApiProvider } from '@theia/plugin-ext/lib/common';
import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment';
import { PluginVSCodeDeployerParticipant } from './plugin-vscode-deployer-participant';
import { VSCodePluginApiProvider } from './vscode-plugin-api-provider';

export default new ContainerModule(bind => {
bind(PluginVSCodeEnvironment).toSelf().inSingletonScope();

bind(VSCodePluginApiProvider).toSelf().inSingletonScope();
bind(Symbol.for(ExtPluginApiProvider)).toService(VSCodePluginApiProvider);

bind(PluginVSCodeDeployerParticipant).toSelf().inSingletonScope();
bind(PluginDeployerParticipant).toService(PluginVSCodeDeployerParticipant);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { injectable } from 'inversify';
import { Argv, Arguments } from 'yargs';
import { CliContribution } from '@theia/core/lib/node/cli';
import { PluginHostEnvironmentVariable } from '@theia/plugin-ext/lib/common';
import { VSCODE_DEFAULT_API_VERSION } from './plugin-vscode-init';
import { VSCODE_DEFAULT_API_VERSION } from './vscode-api-node-provider';
/**
* CLI Contribution allowing to override the VS Code API version which is returned by `vscode.version` API call.
*/
Expand Down
4 changes: 1 addition & 3 deletions packages/plugin-ext-vscode/src/node/scanner-vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ export class VsCodePluginScanner extends TheiaPluginScanner implements PluginSca
getLifecycle(plugin: PluginPackage): PluginLifecycle {
return {
startMethod: 'activate',
stopMethod: 'deactivate',

backendInitPath: __dirname + '/plugin-vscode-init.js'
stopMethod: 'deactivate'
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2018-2019 Red Hat, Inc.
* 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
Expand All @@ -13,98 +13,104 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

/* eslint-disable @typescript-eslint/no-explicit-any */

import * as theia from '@theia/plugin';
import { BackendInitializationFn, PluginAPIFactory, Plugin, emptyPlugin } from '@theia/plugin-ext';
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
import { Plugin, emptyPlugin, PluginManager, PluginAPIFactory } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
import { ExtPluginApiBackendInitializationFn } from '@theia/plugin-ext/lib/common/plugin-ext-api-contribution';
import { createAPIFactory } from '@theia/plugin-ext/lib/plugin/plugin-context';
import { KeyValueStorageProxy } from '@theia/plugin-ext/lib/plugin/plugin-storage';

export const VSCODE_DEFAULT_API_VERSION = '1.44.0';

/** Set up en as a default locale for VS Code extensions using vscode-nls */
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify({ locale: 'en', availableLanguages: {} });
process.env['VSCODE_PID'] = process.env['THEIA_PARENT_PID'];
/* eslint-disable @typescript-eslint/no-explicit-any */

const pluginsApiImpl = new Map<string, typeof theia>();
const plugins = new Array<Plugin>();
let defaultApi: typeof theia;
let isLoadOverride = false;
let pluginApiFactory: PluginAPIFactory;

export enum ExtensionKind {
UI = 1,
Workspace = 2
}

export const doInitialization: BackendInitializationFn = (apiFactory: PluginAPIFactory, plugin: Plugin) => {
const vscode = Object.assign(apiFactory(plugin), { ExtensionKind });

// use Theia plugin api instead vscode extensions
(<any>vscode).extensions = {
get all(): any[] {
return vscode.plugins.all.map(p => asExtension(p));
},
getExtension(pluginId: string): any | undefined {
return asExtension(vscode.plugins.getPlugin(pluginId));
},
get onDidChange(): theia.Event<void> {
return vscode.plugins.onDidChange;
}
};

// override the version for vscode to be a VSCode version
(<any>vscode).version = process.env['VSCODE_API_VERSION'] || VSCODE_DEFAULT_API_VERSION;
let apiFactory: PluginAPIFactory;
let plugins: PluginManager;

pluginsApiImpl.set(plugin.model.id, vscode);
plugins.push(plugin);
pluginApiFactory = apiFactory;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const provideApi: ExtPluginApiBackendInitializationFn = (rpc: RPCProtocol, pluginManager: PluginManager, storageProxy: KeyValueStorageProxy, initParams?: any) => {
apiFactory = createAPIFactory(pluginManager, rpc, storageProxy, initParams);
plugins = pluginManager;

if (!isLoadOverride) {
overrideInternalLoad();
isLoadOverride = true;
}

};

function overrideInternalLoad(): void {
const module = require('module');
const vscodeModuleName = 'vscode';
// save original load method
const internalLoad = module._load;

// if we try to resolve theia module, return the filename entry to use cache.
// if we try to resolve che module, return the filename entry to use cache.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
module._load = function (request: string, parent: any, isMain: {}): any {
if (request !== vscodeModuleName) {
if (request !== 'vscode') {
return internalLoad.apply(this, arguments);
}

const plugin = findPlugin(parent.filename);
if (plugin) {
const apiImpl = pluginsApiImpl.get(plugin.model.id);
let apiImpl = pluginsApiImpl.get(plugin.model.id);
if (!apiImpl) {
apiImpl = doInitialization(plugin);
pluginsApiImpl.set(plugin.model.id, apiImpl);
}
return apiImpl;
}

if (!defaultApi) {
console.warn(`Could not identify plugin for 'Theia' require call from ${parent.filename}`);
defaultApi = pluginApiFactory(emptyPlugin);
console.warn(`Could not identify plugin for 'Che' require call from ${parent.filename}`);
defaultApi = doInitialization(emptyPlugin);
}

return defaultApi;
};
}

function findPlugin(filePath: string): Plugin | undefined {
return plugins.find(plugin => filePath.startsWith(plugin.pluginFolder));
return plugins.getAllPlugins().find(plugin => filePath.startsWith(plugin.pluginFolder));
}

function asExtension(plugin: any | undefined): any | undefined {
export default provideApi;

export const VSCODE_DEFAULT_API_VERSION = '1.44.0';

/** Set up en as a default locale for VS Code extensions using vscode-nls */
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify({ locale: 'en', availableLanguages: {} });
process.env['VSCODE_PID'] = process.env['THEIA_PARENT_PID'];

export const doInitialization = (plugin: Plugin): typeof theia => {
const api: typeof theia = apiFactory(plugin);

// use Theia plugin api instead vscode extensions
(<any>api).extensions = {
get all(): any[] {
return api.plugins.all.map(p => asExtension(p));
},
getExtension(pluginId: string): any | undefined {
return asExtension(api.plugins.getPlugin(pluginId));
},
get onDidChange(): theia.Event<void> {
return api.plugins.onDidChange;
}
};

// override the version for vscode to be a VSCode version
(<any>api).version = process.env['VSCODE_API_VERSION'] || VSCODE_DEFAULT_API_VERSION;
return api;

};

function asExtension(plugin: any | undefined): typeof theia | undefined {
if (!plugin) {
return plugin;
}
if (plugin.pluginPath) {
plugin.extensionPath = plugin.pluginPath;
}
// stub as a local VS Code extension (not running on a remote workspace)
plugin.extensionKind = ExtensionKind.UI;
return plugin;
}
29 changes: 29 additions & 0 deletions packages/plugin-ext-vscode/src/node/vscode-plugin-api-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/********************************************************************************
* 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 { injectable } from 'inversify';
import * as path from 'path';
import { ExtPluginApiProvider, ExtPluginApi } from '@theia/plugin-ext/lib/common/plugin-ext-api-contribution';

@injectable()
export class VSCodePluginApiProvider implements ExtPluginApiProvider {

provideApi(): ExtPluginApi {
return {
id: 'vscode',
backendInitPath: path.join('@theia/plugin-ext-vscode/lib/node/vscode-api-node-provider.js')
};
}
}
10 changes: 2 additions & 8 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import { createProxyIdentifier, ProxyIdentifier, RPCProtocol } from './rpc-protocol';
import * as theia from '@theia/plugin';
import { PluginLifecycle, PluginModel, PluginMetadata, PluginPackage, IconUrl, PluginJsonValidationContribution } from './plugin-protocol';
import { PluginLifecycle, PluginModel, PluginMetadata, PluginPackage, IconUrl } from './plugin-protocol';
import { QueryParameters } from './env';
import { TextEditorCursorStyle } from './editor-options';
import {
Expand Down Expand Up @@ -177,13 +177,7 @@ export const emptyPlugin: Plugin = {
};

export interface PluginManagerInitializeParams {
preferences: PreferenceData
globalState: KeysToKeysToAnyValue
workspaceState: KeysToKeysToAnyValue
env: EnvInit
extApi?: ExtPluginApi[]
webview: WebviewInitData
jsonValidation: PluginJsonValidationContribution[]
extApi?: { pluginApi: ExtPluginApi, initParameters?: any }[]
}

export interface PluginManagerStartParams {
Expand Down
11 changes: 9 additions & 2 deletions packages/plugin-ext/src/common/plugin-ext-api-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { RPCProtocol } from './rpc-protocol';
import { PluginManager, Plugin } from './plugin-api-rpc';
import { interfaces } from 'inversify';
import { KeyValueStorageProxy } from '../plugin/plugin-storage';

export const ExtPluginApiProvider = 'extPluginApi';
/**
Expand All @@ -33,6 +34,7 @@ export interface ExtPluginApiProvider {
* This interface describes scripts for both plugin runtimes: frontend(WebWorker) and backend(NodeJs)
*/
export interface ExtPluginApi {
readonly id: string;

/**
* Path to the script which should be loaded to provide api, module should export `provideApi` function with
Expand All @@ -47,11 +49,13 @@ export interface ExtPluginApi {
}

export interface ExtPluginApiFrontendInitializationFn {
(rpc: RPCProtocol, plugins: Map<string, Plugin>): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(rpc: RPCProtocol, pluginManager: PluginManager, storageProxy: KeyValueStorageProxy, plugins: Map<string, Plugin>, initParams?: any): void;
}

export interface ExtPluginApiBackendInitializationFn {
(rpc: RPCProtocol, pluginManager: PluginManager): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(rpc: RPCProtocol, pluginManager: PluginManager, storageProxy: KeyValueStorageProxy, initParams?: any): void;
}

/**
Expand All @@ -78,5 +82,8 @@ export const MainPluginApiProvider = Symbol('mainPluginApi');
* [initialize](#initialize) will be called once per plugin runtime
*/
export interface MainPluginApiProvider {
readonly id: string;
initialize(rpc: RPCProtocol, container: interfaces.Container): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
computeInitParameters?(rpc: RPCProtocol, container: interfaces.Container): Promise<any>;
}
18 changes: 1 addition & 17 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
import { RPCProtocol } from './rpc-protocol';
import { Disposable } from '@theia/core/lib/common/disposable';
import { LogPart, KeysToAnyValues, KeysToKeysToAnyValue } from './types';
import { CharacterPair, CommentRule, PluginAPIFactory, Plugin } from './plugin-api-rpc';
import { CharacterPair, CommentRule, Plugin } from './plugin-api-rpc';
import { ExtPluginApi } from './plugin-ext-api-contribution';
import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-schema';
import { RecursivePartial } from '@theia/core/lib/common/types';
Expand Down Expand Up @@ -652,23 +652,7 @@ export interface PluginLifecycle {
* Frontend module name, frontend plugin should expose this name.
*/
frontendModuleName?: string;
/**
* Path to the script which should do some initialization before frontend plugin is loaded.
*/
frontendInitPath?: string;
/**
* Path to the script which should do some initialization before backend plugin is loaded.
*/
backendInitPath?: string;
}

/**
* The export function of initialization module of backend plugin.
*/
export interface BackendInitializationFn {
(apiFactory: PluginAPIFactory, plugin: Plugin): void;
}

export interface BackendLoadingFn {
(rpc: RPCProtocol, plugin: Plugin): void;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/plugin-ext/src/common/rpc-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export interface RPCProtocol extends Disposable {
*/
set<T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R;

/**
* Get a locally created instance
*/
get<T, R extends T>(identifier: ProxyIdentifier<T>): R;

}

export class ProxyIdentifier<T> {
Expand Down Expand Up @@ -134,6 +139,10 @@ export class RPCProtocolImpl implements RPCProtocol {
return instance;
}

get<T, R extends T>(identifier: ProxyIdentifier<T>): R {
return this.locals.get(identifier.id);
}

private createProxy<T>(proxyId: string): T {
const handler = {
get: (target: any, name: string) => {
Expand Down
Loading

0 comments on commit 10deabf

Please sign in to comment.