diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts index 5a4a82a90fed0..861af202005ab 100644 --- a/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts +++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts @@ -38,13 +38,13 @@ export enum ExtensionKind { export const doInitialization: BackendInitializationFn = (apiFactory: PluginAPIFactory, plugin: Plugin) => { const vscode = Object.assign(apiFactory(plugin), { ExtensionKind }); - // use Theia plugin api instead vscode extensions + // use Theia plugin api to implement vscode extensions api (vscode).extensions = { get all(): any[] { - return vscode.plugins.all.map(p => asExtension(p)); + return vscode.plugins.all; }, getExtension(pluginId: string): any | undefined { - return asExtension(vscode.plugins.getPlugin(pluginId)); + return vscode.plugins.getPlugin(pluginId); }, get onDidChange(): theia.Event { return vscode.plugins.onDidChange; @@ -95,15 +95,3 @@ function overrideInternalLoad(): void { function findPlugin(filePath: string): Plugin | undefined { return plugins.find(plugin => filePath.startsWith(plugin.pluginFolder)); } - -function asExtension(plugin: any | undefined): any | 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; -} diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 4d9da813a55eb..10b664e32c4a3 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (C) 2018 Red Hat, Inc. and others. + * Copyright (C) 2018-2022 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 @@ -720,13 +720,13 @@ export function createAPIFactory( const plugins: typeof theia.plugins = { // eslint-disable-next-line @typescript-eslint/no-explicit-any get all(): theia.Plugin[] { - return pluginManager.getAllPlugins().map(plg => new Plugin(pluginManager, plg)); + return pluginManager.getAllPlugins().map(plg => new PluginExt(pluginManager, plg)); }, // eslint-disable-next-line @typescript-eslint/no-explicit-any getPlugin(pluginId: string): theia.Plugin | undefined { const plg = pluginManager.getPluginById(pluginId.toLowerCase()); if (plg) { - return new Plugin(pluginManager, plg); + return new PluginExt(pluginManager, plg); } return undefined; }, @@ -954,14 +954,44 @@ export function createAPIFactory( }; } -class Plugin implements theia.Plugin { +export enum ExtensionKind { + UI = 1, + Workspace = 2 +} + +/** + * Represents a Theia plugin as well as a VSCode extension. + */ +export interface ExtensionPlugin extends theia.Plugin { + /** + * The uri of the directory containing the extension. Same as {@linkcode theia.Plugin.pluginUri}. + */ + readonly extensionUri: theia.Uri; + + /** + * The absolute file path of the directory containing this extension. + * Same as {@linkcode theia.Plugin.pluginPath}. + */ + readonly extensionPath: string; + + /** + * The extension kind describes if an extension runs where the UI runs + * or if an extension runs where the remote extension host runs. The extension kind + * is defined in the `package.json`-file of extensions. When no remote extension host exists, + * the value is {@linkcode ExtensionKind.UI}. + */ + extensionKind: ExtensionKind; +} + +export class Plugin implements theia.Plugin { id: string; pluginPath: string; pluginUri: URI; // eslint-disable-next-line @typescript-eslint/no-explicit-any packageJSON: any; pluginType: theia.PluginType; - constructor(private readonly pluginManager: PluginManager, plugin: InternalPlugin) { + + constructor(protected readonly pluginManager: PluginManager, plugin: InternalPlugin) { this.id = plugin.model.id; this.pluginPath = plugin.pluginFolder; this.pluginUri = URI.file(plugin.pluginFolder); @@ -981,3 +1011,29 @@ class Plugin implements theia.Plugin { return this.pluginManager.activatePlugin(this.id).then(() => this.exports); } } + +export class PluginExt extends Plugin implements ExtensionPlugin { + extensionPath: string; + extensionUri: theia.Uri; + extensionKind: ExtensionKind; + + constructor(protected readonly pluginManager: PluginManager, plugin: InternalPlugin) { + super(pluginManager, plugin); + + this.extensionPath = this.pluginPath; + this.extensionUri = this.pluginUri; + this.extensionKind = ExtensionKind.UI; // stub as a local extension (not running on a remote workspace) + } + + get isActive(): boolean { + return this.pluginManager.isActive(this.id); + } + + get exports(): T { + return this.pluginManager.getPluginExport(this.id); + } + + activate(): PromiseLike { + return this.pluginManager.activatePlugin(this.id).then(() => this.exports); + } +} diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index 5a1f2fb9dc323..c39fd8f31224e 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -39,6 +39,7 @@ import { RPCProtocol } from '../common/rpc-protocol'; import { Emitter } from '@theia/core/lib/common/event'; import { WebviewsExtImpl } from './webviews'; import { URI as Uri } from './types-impl'; +import { PluginExt } from './plugin-context'; export interface PluginHost { @@ -357,7 +358,10 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { storageUri: storagePath ? Uri.file(storagePath) : undefined, globalStoragePath: globalStoragePath, globalStorageUri: Uri.file(globalStoragePath), - environmentVariableCollection: this.terminalService.getEnvironmentVariableCollection(plugin.model.id) + environmentVariableCollection: this.terminalService.getEnvironmentVariableCollection(plugin.model.id), + extensionMode: 1, // @todo: implement proper `extensionMode`. + extension: new PluginExt(this, plugin), + logUri: Uri.file(logPath) }; this.pluginContextsMap.set(plugin.model.id, pluginContext); diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index e8fc23ec821d2..7b7ca29e91bf7 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -3190,6 +3190,26 @@ declare module '@theia/plugin' { * the parent directory is guaranteed to be existent. */ readonly logPath: string; + + /** + * The mode the extension is running in. This is specific to the current + * extension. One extension may be in `ExtensionMode.Development` while + * other extensions in the host run in `ExtensionMode.Release`. + */ + readonly extensionMode: ExtensionMode; + + /** + * The current extension instance. + */ + readonly extension: Plugin | undefined; + + /** + * The uri of a directory in which the extension can create log files. The directory might + * not exist on disk and creation is up to the extension. However, the parent directory is + * guaranteed to be existent. + * see - workspace.fs for how to read and write files and folders from an uri. + */ + readonly logUri: Uri; } /**