Skip to content

Commit

Permalink
[vscode] support any activation event
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Jul 2, 2019
1 parent 65ffea5 commit 87602e2
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 115 deletions.
3 changes: 3 additions & 0 deletions packages/plugin-ext/src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface PluginInitData {
workspaceState: KeysToKeysToAnyValue;
env: EnvInit;
extApi?: ExtPluginApi[];
activationEvents: string[]
}

export interface PreferenceData {
Expand Down Expand Up @@ -163,6 +164,8 @@ export interface PluginManagerExt {
$init(pluginInit: PluginInitData, configStorage: ConfigStorage): PromiseLike<void>;

$updateStoragePath(path: string | undefined): PromiseLike<void>;

$activateByEvent(event: string): Promise<void>;
}

export interface CommandRegistryMain {
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface PluginPackage {
description: string;
contributes?: PluginPackageContribution;
packagePath: string;
activationEvents?: string[];
}
export namespace PluginPackage {
export function toPluginUrl(pck: PluginPackage, relativePath: string): string {
Expand Down
173 changes: 74 additions & 99 deletions packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

// tslint:disable:no-any

import { injectable, inject, interfaces, named } from 'inversify';
import { injectable, inject, interfaces, named, postConstruct } from 'inversify';
import { PluginWorker } from '../../main/browser/plugin-worker';
import { HostedPluginServer, PluginMetadata, getPluginId } from '../../common/plugin-protocol';
import { HostedPluginWatcher } from './hosted-plugin-watcher';
import { MAIN_RPC_CONTEXT, ConfigStorage, PluginManagerExt } from '../../api/plugin-api';
import { setUpPluginApi } from '../../main/browser/main-context';
import { RPCProtocol, RPCProtocolImpl } from '../../api/rpc-protocol';
import { ILogger, ContributionProvider } from '@theia/core';
Expand All @@ -35,6 +34,9 @@ import { getPreferences } from '../../main/browser/preference-registry-main';
import { PluginServer } from '../../common/plugin-protocol';
import { KeysToKeysToAnyValue } from '../../common/types';
import { FileStat } from '@theia/filesystem/lib/common/filesystem';
import { PluginManagerExt, MAIN_RPC_CONTEXT } from '../../common';

export type PluginHost = 'frontend' | string;

@injectable()
export class HostedPluginSupport {
Expand Down Expand Up @@ -62,26 +64,31 @@ export class HostedPluginSupport {
@inject(PreferenceProviderProvider)
protected readonly preferenceProviderProvider: PreferenceProviderProvider;

@inject(PreferenceServiceImpl)
private readonly preferenceServiceImpl: PreferenceServiceImpl;

@inject(PluginPathsService)
private readonly pluginPathsService: PluginPathsService;

@inject(StoragePathService)
private readonly storagePathService: StoragePathService;

@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;

private theiaReadyPromise: Promise<any>;
private frontendExtManagerProxy: PluginManagerExt;
private backendExtManagerProxy: PluginManagerExt;

protected readonly managers: PluginManagerExt[] = [];

// loaded plugins per #id
private loadedPlugins: Set<string> = new Set<string>();
private readonly loadedPlugins = new Set<string>();

// per #hostKey
private rpc: Map<string, RPCProtocol> = new Map<string, RPCProtocol>();
protected readonly activationEvents = new Set<string>();

constructor(
@inject(PreferenceServiceImpl) private readonly preferenceServiceImpl: PreferenceServiceImpl,
@inject(PluginPathsService) private readonly pluginPathsService: PluginPathsService,
@inject(StoragePathService) private readonly storagePathService: StoragePathService,
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService,
) {
@postConstruct()
protected init(): void {
this.theiaReadyPromise = Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots]);
this.storagePathService.onStoragePathChanged(path => {
this.updateStoragePath(path);
});
this.storagePathService.onStoragePathChanged(path => this.updateStoragePath(path));
}

checkAndLoadPlugin(container: interfaces.Container): void {
Expand Down Expand Up @@ -112,93 +119,54 @@ export class HostedPluginSupport {
}).catch(e => console.error(e));
}

loadPlugins(initData: PluginsInitializationData, container: interfaces.Container): void {
async loadPlugins(initData: PluginsInitializationData, container: interfaces.Container): Promise<void> {
// don't load plugins twice
initData.plugins = initData.plugins.filter(value => !this.loadedPlugins.has(value.model.id));

const confStorage: ConfigStorage = {
hostLogPath: initData.logPath,
hostStoragePath: initData.storagePath || ''
};
const [frontend, backend] = this.initContributions(initData.plugins);
this.theiaReadyPromise.then(() => {
if (frontend) {
const worker = new PluginWorker();
const hostedExtManager = worker.rpc.getProxy(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT);
hostedExtManager.$init({
plugins: initData.plugins,
preferences: getPreferences(this.preferenceProviderProvider, initData.roots),
globalState: initData.globalStates,
workspaceState: initData.workspaceStates,
env: { queryParams: getQueryParameters(), language: navigator.language },
extApi: initData.pluginAPIs
}, confStorage);
setUpPluginApi(worker.rpc, container);
this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(worker.rpc, container));
this.frontendExtManagerProxy = hostedExtManager;
}

if (backend) {
// sort plugins per host
const pluginsPerHost = initData.plugins.reduce((map: any, pluginMetadata) => {
const host = pluginMetadata.host;
if (!map[host]) {
map[host] = [pluginMetadata];
} else {
map[host].push(pluginMetadata);
}
return map;
}, {});

// create one RPC per host and init.
Object.keys(pluginsPerHost).forEach(hostKey => {
const plugins: PluginMetadata[] = pluginsPerHost[hostKey];
let pluginID = hostKey;
if (plugins.length >= 1) {
pluginID = getPluginId(plugins[0].model);
}

let rpc = this.rpc.get(hostKey);
if (!rpc) {
rpc = this.createServerRpc(pluginID, hostKey);
setUpPluginApi(rpc, container);
this.rpc.set(hostKey, rpc);
}

const hostedExtManager = rpc.getProxy(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT);
hostedExtManager.$init({
plugins: plugins,
preferences: getPreferences(this.preferenceProviderProvider, initData.roots),
globalState: initData.globalStates,
workspaceState: initData.workspaceStates,
env: { queryParams: getQueryParameters(), language: navigator.language },
extApi: initData.pluginAPIs
}, confStorage);
this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc!, container));
this.backendExtManagerProxy = hostedExtManager;
});
}

// update list with loaded plugins
initData.plugins.forEach(value => this.loadedPlugins.add(value.model.id));
});
}

private initContributions(pluginsMetadata: PluginMetadata[]): [boolean, boolean] {
const result: [boolean, boolean] = [false, false];
for (const plugin of pluginsMetadata) {
if (plugin.model.entryPoint.frontend) {
result[0] = true;
} else {
result[1] = true;
}

const hostToPlugins = new Map<PluginHost, PluginMetadata[]>();
for (const plugin of initData.plugins) {
const host = plugin.model.entryPoint.frontend ? 'frontend' : plugin.host;
const plugins = hostToPlugins.get(plugin.host) || [];
plugins.push(plugin);
hostToPlugins.set(host, plugins);
if (plugin.model.contributes) {
this.contributionHandler.handleContributions(plugin.model.contributes);
}
}
await this.theiaReadyPromise;
for (const [host, plugins] of hostToPlugins) {
const pluginId = getPluginId(plugins[0].model);
const rpc = this.initRpc(host, pluginId, container);
this.initPluginHostManager(rpc, { ...initData, plugins });
}

// update list with loaded plugins
initData.plugins.forEach(value => this.loadedPlugins.add(value.model.id));
}

return result;
protected initRpc(host: PluginHost, pluginId: string, container: interfaces.Container): RPCProtocol {
const rpc = host === 'frontend' ? new PluginWorker().rpc : this.createServerRpc(pluginId, host);
setUpPluginApi(rpc, container);
this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc, container));
return rpc;
}

protected initPluginHostManager(rpc: RPCProtocol, data: PluginsInitializationData): void {
const manager = rpc.getProxy(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT);
this.managers.push(manager);

manager.$init({
plugins: data.plugins,
preferences: getPreferences(this.preferenceProviderProvider, data.roots),
globalState: data.globalStates,
workspaceState: data.workspaceStates,
env: { queryParams: getQueryParameters(), language: navigator.language },
extApi: data.pluginAPIs,
activationEvents: [...this.activationEvents]
}, {
hostLogPath: data.logPath,
hostStoragePath: data.storagePath || ''
});
}

private createServerRpc(pluginID: string, hostID: string): RPCProtocol {
Expand All @@ -214,11 +182,18 @@ export class HostedPluginSupport {
}

private updateStoragePath(path: string | undefined): void {
if (this.frontendExtManagerProxy) {
this.frontendExtManagerProxy.$updateStoragePath(path);
for (const manager of this.managers) {
manager.$updateStoragePath(path);
}
}

activateByEvent(activationEvent: string): void {
if (this.activationEvents.has(activationEvent)) {
return;
}
if (this.backendExtManagerProxy) {
this.backendExtManagerProxy.$updateStoragePath(path);
this.activationEvents.add(activationEvent);
for (const manager of this.managers) {
manager.$activateByEvent(activationEvent);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox 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';

@injectable()
export class PluginActivationEventService {

protected readonly activationEvents = new Set<string>();

get initial(): string[] {
return [...this.activationEvents];
}

push(activationEvent: string): void {

}

}
39 changes: 39 additions & 0 deletions packages/plugin-ext/src/hosted/browser/plugin-host-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

/********************************************************************************
* Copyright (C) 2019 TypeFox 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 { PluginManagerExt, MAIN_RPC_CONTEXT, PluginInitData, ConfigStorage } from '../../api/plugin-api';
import { RPCProtocol } from '../../api/rpc-protocol';

export class PluginHostManager {

protected readonly proxy: PluginManagerExt;

constructor(rpc: RPCProtocol) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT);
}

async init(data: PluginInitData, configStorage: ConfigStorage): Promise<void> {
await this.proxy.$init(data, configStorage);
}

async updateStoragePath(path: string | undefined): Promise<void> {
if (this.proxy) {
this.proxy.$updateStoragePath(path);
}
}

}
Loading

0 comments on commit 87602e2

Please sign in to comment.