Skip to content

Commit

Permalink
[vscode] support workspaceContains activation events
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 11, 2019
1 parent d4adca2 commit 1fc845d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Breaking changes:
- [plugin] removed member `processOptions` from `AbstractHostedInstanceManager` as it is not initialized or used
- [plugin] added basic support of activation events [#5622](https://github.com/theia-ide/theia/pull/5622)
- `HostedPluginSupport` is refactored to support multiple `PluginManagerExt` properly
- [plugin] added support of `workspaceContains` activation events [#5649](https://github.com/theia-ide/theia/pull/5649)

## v0.8.0

Expand Down
72 changes: 69 additions & 3 deletions packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// some code copied and modified from https://github.com/microsoft/vscode/blob/da5fb7d5b865aa522abc7e82c10b746834b98639/src/vs/workbench/api/node/extHostExtensionService.ts

// tslint:disable:no-any

Expand All @@ -22,7 +27,7 @@ import { HostedPluginServer, PluginMetadata, getPluginId } from '../../common/pl
import { HostedPluginWatcher } from './hosted-plugin-watcher';
import { setUpPluginApi } from '../../main/browser/main-context';
import { RPCProtocol, RPCProtocolImpl } from '../../api/rpc-protocol';
import { ILogger, ContributionProvider, CommandRegistry, WillExecuteCommandEvent } from '@theia/core';
import { ILogger, ContributionProvider, CommandRegistry, WillExecuteCommandEvent, CancellationTokenSource } from '@theia/core';
import { PreferenceServiceImpl, PreferenceProviderProvider } from '@theia/core/lib/browser';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { PluginContributionHandler } from '../../main/browser/plugin-contribution-handler';
Expand All @@ -40,6 +45,8 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { DebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
import { WaitUntilEvent } from '@theia/core/lib/common/event';
import { FileSearchService } from '@theia/file-search/lib/common/file-search-service';
import { isCancelled } from '@theia/core';

export type PluginHost = 'frontend' | string;
export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker';
Expand Down Expand Up @@ -94,6 +101,9 @@ export class HostedPluginSupport {
@inject(DebugConfigurationManager)
protected readonly debugConfigurationManager: DebugConfigurationManager;

@inject(FileSearchService)
protected readonly fileSearchService: FileSearchService;

private theiaReadyPromise: Promise<any>;

protected readonly managers: PluginManagerExt[] = [];
Expand Down Expand Up @@ -178,11 +188,11 @@ export class HostedPluginSupport {
return rpc;
}

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

manager.$init({
await manager.$init({
plugins: data.plugins,
preferences: getPreferences(this.preferenceProviderProvider, data.roots),
globalState: data.globalStates,
Expand All @@ -194,6 +204,10 @@ export class HostedPluginSupport {
hostLogPath: data.logPath,
hostStoragePath: data.storagePath || ''
});

for (const plugin of data.plugins) {
this.activateByWorkspaceContains(manager, plugin);
}
}

private createServerRpc(pluginID: string, hostID: string): RPCProtocol {
Expand Down Expand Up @@ -271,6 +285,58 @@ export class HostedPluginSupport {
await Promise.all(promises);
}

protected async activateByWorkspaceContains(manager: PluginManagerExt, plugin: PluginMetadata): Promise<void> {
if (!plugin.source.activationEvents) {
return;
}
const paths: string[] = [];
const includePatterns: string[] = [];
// should be aligned with https://github.com/microsoft/vscode/blob/da5fb7d5b865aa522abc7e82c10b746834b98639/src/vs/workbench/api/node/extHostExtensionService.ts#L460-L469
for (const activationEvent of plugin.source.activationEvents) {
if (/^workspaceContains:/.test(activationEvent)) {
const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
includePatterns.push(fileNameOrGlob);
} else {
paths.push(fileNameOrGlob);
}
}
}
const activatePlugin = () => manager.$activateByEvent(`onPlugin:${plugin.model.id}`);
const promises: Promise<boolean>[] = [];
if (paths.length) {
promises.push(this.workspaceService.containsSome(paths));
}
if (includePatterns.length) {
const tokenSource = new CancellationTokenSource();
const searchTimeout = setTimeout(() => {
tokenSource.cancel();
// activate eagerly if took to long to search
activatePlugin();
}, 7000);
promises.push((async () => {
try {
const result = await this.fileSearchService.find('', {
rootUris: this.workspaceService.tryGetRoots().map(r => r.uri),
includePatterns,
limit: 1
}, tokenSource.token);
return result.length > 0;
} catch (e) {
if (!isCancelled(e)) {
console.error(e);
}
return false;
} finally {
clearTimeout(searchTimeout);
}
})());
}
if (promises.length && await Promise.all(promises).then(exists => exists.some(v => v))) {
await activatePlugin();
}
}

}

interface PluginsInitializationData {
Expand Down
7 changes: 5 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
'*',
'onLanguage',
'onCommand',
'onDebug', 'onDebugInitialConfigurations', 'onDebugResolve', 'onDebugAdapterProtocolTracker'
'onDebug', 'onDebugInitialConfigurations', 'onDebugResolve', 'onDebugAdapterProtocolTracker',
'workspaceContains'
]);

private readonly registry = new Map<string, Plugin>();
Expand Down Expand Up @@ -156,6 +157,8 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
this.registry.set(plugin.model.id, plugin);
if (plugin.pluginPath && Array.isArray(plugin.rawModel.activationEvents)) {
const activation = () => this.loadPlugin(plugin, configStorage);
// an internal activation event is a subject to change
this.setActivation(`onPlugin:${plugin.model.id}`, activation);
const unsupportedActivationEvents = plugin.rawModel.activationEvents.filter(e => !PluginManagerExtImpl.SUPPORTED_ACTIVATION_EVENTS.has(e.split(':')[0]));
if (unsupportedActivationEvents.length) {
console.warn(`Unsupported activation events: ${unsupportedActivationEvents.join(', ')}, please open an issue: https://github.com/theia-ide/theia/issues/new`);
Expand All @@ -164,7 +167,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
} else {
for (let activationEvent of plugin.rawModel.activationEvents) {
if (activationEvent === 'onUri') {
activationEvent = `onUri:${plugin.model.id}`;
activationEvent = `onUri:theia://${plugin.model.publisher.toLowerCase()}.${plugin.model.name.toLowerCase()}`;
}
this.setActivation(activationEvent, activation);
}
Expand Down

0 comments on commit 1fc845d

Please sign in to comment.