Skip to content

Commit

Permalink
Provide API to filter unwanted contributions
Browse files Browse the repository at this point in the history
Adds the ContributionFilterRegistry which can be used to filter
contributions of Theia extensions before they are bound.

This mechanism can be used by application developers to specifically
filter individual contributions from Theia extensions they have no
control over, i.e. core or 3rd party extensions.

Resolves #9069

Contributed on behalf of STMicroelectronics

Signed-off-by: Tobias Ortmayr <tortmayr@eclipsesource.com>
  • Loading branch information
tortmayr committed Apr 8, 2021
1 parent fd91f21 commit 13c96d0
Show file tree
Hide file tree
Showing 13 changed files with 394 additions and 7 deletions.
14 changes: 7 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

## v1.13.0

- [core] add API to filter contributions at runtime [#9317](https://github.com/eclipse-theia/theia/pull/9317) Contributed on behalf of STMicroelectronics

<a name="breaking_changes_1.13.0">[Breaking Changes:](#breaking_changes_1.13.0)</a>

## v1.12.0 - 3/25/2020

[1.12.0 Milestone](https://github.com/eclipse-theia/theia/milestone/17)
Expand Down Expand Up @@ -626,13 +632,7 @@ Breaking changes:
- [cli] updated the `download:plugins` script to include colors for better visibility [#7648](https://github.com/eclipse-theia/theia/pull/7648)
- [core] added functionality to prevent pasting into the active editor when closing a tab with a middle mouse click [#7565](https://github.com/eclipse-theia/theia/pull/7565)
- [core] added support to allow providing a custom node for ReactWidget [#7422](https://github.com/eclipse-theia/theia/pull/7422)
- [core] aligned max listeners check with VS Code expectations [#7508](https://github.com/eclipse-theia/theia/pull/7508)
- [core] fixed 'recently used commands' to be only added when triggered by the quick-command palette [#7552](https://github.com/eclipse-theia/theia/pull/7552)
- [core] fixed 'recently used commands': [#7562](https://github.com/eclipse-theia/theia/pull/7562)
- added sorting of commands based on their human-readable label
- fixed issue where recently used commands where not correctly separated by a border and groupName
- [core] fixed issue where menu args where unnecessarily wrapped into another array [#7622](https://github.com/eclipse-theia/theia/pull/7622)
- [core] fixed leaking `DisposableCollection.onDispose` event [#7508](https://github.com/eclipse-theia/theia/pull/7508)
- [core] aligned max listeners check with VS Code expectations [#7508](https://github.com/eclipse-theia/theia/pull/9317508)
- [core] fixed release of leaking editor from quick-open and goto references [#7508](https://github.com/eclipse-theia/theia/pull/7508)
- [core] improved display for the 'Remove Folder from Workspace` dialog [#7449](https://github.com/eclipse-theia/theia/pull/7449)
- [core] upgraded `nsfw` to 1.2.9 [#7535](https://github.com/eclipse-theia/theia/pull/7535)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { ContainerModule } from '@theia/core/shared/inversify';
import { bindDynamicLabelProvider } from './label/sample-dynamic-label-provider-command-contribution';
import { bindSampleFilteredCommandContribution } from './contribution-filter/sample-filtered-command-contribution';
import { bindSampleUnclosableView } from './view/sample-unclosable-view-contribution';
import { bindSampleOutputChannelWithSeverity } from './output/sample-output-channel-with-severity';
import { bindSampleMenu } from './menu/sample-menu-contribution';
Expand All @@ -31,4 +32,5 @@ export default new ContainerModule(bind => {
bindSampleMenu(bind);
bindSampleFileWatching(bind);
bindVSXCommand(bind);
bindSampleFilteredCommandContribution(bind);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/********************************************************************************
* Copyright (C) 2021 STMicroelectronics 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 { Command, CommandContribution, CommandRegistry, ContributionFilter, equals, NameBasedContributionFilter } from '@theia/core/lib/common';
import { injectable, interfaces } from '@theia/core/shared/inversify';
export namespace SampleFilteredCommand {
const EXAMPLE_CATEGORY = 'Examples';
export const FILTERED: Command = {
id: 'example_command.filtered',
category: EXAMPLE_CATEGORY,
label: 'This command should be filtered out'
};
}

/**
* This sample command is used to test the runtime filtering of already bound contributions.
*/
@injectable()
export class SampleFilteredCommandContribution implements CommandContribution {

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(SampleFilteredCommand.FILTERED, { execute: () => { } });
}
}

@injectable()
export class SampleFilteredCommandContributionFilter extends NameBasedContributionFilter {

contributions = [CommandContribution];
doTest(toTest: string): boolean {
return equals(toTest, false, 'SampleFilteredCommandContribution');
}
}

export const bindSampleFilteredCommandContribution = (bind: interfaces.Bind) => {
bind(CommandContribution).to(SampleFilteredCommandContribution);
bind(ContributionFilter).to(SampleFilteredCommandContributionFilter);
};
36 changes: 36 additions & 0 deletions examples/api-tests/src/contribution-filter.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/********************************************************************************
* Copyright (C) 2021 STMicroelectronics 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
********************************************************************************/

// @ts-check
describe('Contribution filter', function () {
this.timeout(5000);
const { assert } = chai;

const { CommandRegistry, CommandContribution } = require('@theia/core/lib/common/command');
const { SampleFilteredCommandContribution, SampleFilteredCommand } = require('@theia/api-samples/lib/browser/contribution-filter/sample-filtered-command-contribution');

const container = window.theia.container;
const commands = container.get(CommandRegistry);

it('filtered command in container but not in registry', async function () {
const allCommands = container.getAll(CommandContribution);
assert.isDefined(allCommands.find(contribution => contribution instanceof SampleFilteredCommandContribution),
'SampleFilteredCommandContribution is not bound in container');
const filteredCommand = commands.getCommand(SampleFilteredCommand.FILTERED.id);
assert.isUndefined(filteredCommand, 'SampleFilteredCommandContribution should be filtered out but is present in "CommandRegistry"');
});

});
4 changes: 4 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import { LanguageService } from './language-service';
import { EncodingRegistry } from './encoding-registry';
import { EncodingService } from '../common/encoding-service';
import { AuthenticationService, AuthenticationServiceImpl } from '../browser/authentication-service';
import { ContributionFilterRegistry } from '../common/contribution-filter';

export { bindResourceProvider, bindMessageService, bindPreferenceService };

Expand Down Expand Up @@ -342,4 +343,7 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
bind(ContextMenuContext).toSelf().inSingletonScope();

bind(AuthenticationService).to(AuthenticationServiceImpl).inSingletonScope();

bind(ContributionFilterRegistry).toSelf().inSingletonScope();

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/********************************************************************************
* Copyright (C) 2021 STMicroelectronics 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, multiInject, optional } from 'inversify';
import { ContributionFilter, ContributionType } from './contribution-filter';
import { applyFilters } from './filter';

const GENERIC_CONTRIBUTION_FILTER_KEY = '*';
@injectable()
export class ContributionFilterRegistry {
registry: Map<ContributionType, ContributionFilter[]>;
constructor(@multiInject(ContributionFilter) @optional() contributionFilters: ContributionFilter[] = []) {
this.registry = new Map();
contributionFilters.forEach(filter => {
if (!filter.contributions || filter.contributions.length === 0) {
this.addFilter(GENERIC_CONTRIBUTION_FILTER_KEY, filter);
} else {
filter.contributions.forEach(type => {
this.addFilter(type, filter);
});
}
});
}

private addFilter(type: ContributionType, filter: ContributionFilter): void {
this.getOrCreate(type).push(filter);
}

private getOrCreate(type: ContributionType): ContributionFilter[] {
let value = this.registry.get(type);
if (!value) {
value = [];
this.registry.set(type, value);
}
return value;
}

get(type: ContributionType): ContributionFilter[] {
const filters = [...(this.registry.get(type) || [])];
if (type !== GENERIC_CONTRIBUTION_FILTER_KEY) {
filters.push(...(this.registry.get(GENERIC_CONTRIBUTION_FILTER_KEY) || []));
}
return filters;
}

/**
* Applies the filters for the given contribution type. Generic filters will be applied on any given type.
* @param toFilter the elements to filter
* @param type the contribution type for which potentially filters were registered
* @returns the filtered elements
*/
applyFilters<T extends Object>(toFilter: T[], type: ContributionType): T[] {
const filters = this.get(type);
return applyFilters<T>(toFilter, filters);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/********************************************************************************
* Copyright (C) 2021 STMicroelectronics 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 } from 'inversify';
import { Filter } from './filter';
import { NameBasedFilter } from './string-based-filter';
export type ContributionType = interfaces.ServiceIdentifier<unknown>;

export const ContributionFilter = Symbol('ContributionFilter');

/**
* Specialized `Filter` that is used by the `ContainerBasedContributionProvider` to
* filter unwanted contributions that are already bound in the DI container.
*/
export interface ContributionFilter extends Filter<Object> {
/**
* Contribution types for which this filter is applicable. If `undefined` or empty this filter
* will be applied to all contribution types.
*/
contributions?: ContributionType[];
}

/**
* Specialized `ContributionFilter` that can be used to filter contributions based on their constructor name.
*/
export abstract class NameBasedContributionFilter extends NameBasedFilter<Object> implements ContributionFilter {
}
48 changes: 48 additions & 0 deletions packages/core/src/common/contribution-filter/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/********************************************************************************
* Copyright (C) 2021 STMicroelectronics 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
********************************************************************************/

export const Filter = Symbol('Filter');
/**
* A `Filter` can be used to test whether a given object should be filtered
* from a set of objects. The `test` function can be applied to an object
* of matching type and returns `true` if the object should be filtered out.
*/
export interface Filter<T extends Object> {
/**
* Evaluates this filter on the given argument.
* @param toTest Object that should be tested
* @returns `true` if the object should be filtered out, `false` otherwise
*/
test(toTest: T): Boolean;
}

/**
* Applies a set of filters to a set of given objects and returns the set of filtered objects.
* @param toFilter Set of objects which should be filtered
* @param filters Set of filters that should be applied
* @param negate Negation flag. If set to true the result of all `Filter.test` methods is negated
* @returns The set of filtered arguments
*/
export function applyFilters<T extends Object>(toFilter: T[], filters: Filter<T>[], negate: boolean = false): T[] {
if (filters.length === 0) {
return toFilter;
}
return toFilter.filter(object => {
const result = filters.every(filter => !filter.test(object));
return negate ? !result : result;
});
}

20 changes: 20 additions & 0 deletions packages/core/src/common/contribution-filter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/********************************************************************************
* Copyright (C) 2021 STMicroelectronics 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
********************************************************************************/

export * from './contribution-filter';
export * from './contribution-filter-registry';
export * from './filter';
export * from './string-based-filter';
Loading

0 comments on commit 13c96d0

Please sign in to comment.