forked from eclipse-theia/theia
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide API to filter unwanted contributions
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 eclipse-theia#9069 Contributed on behalf of STMicroelectronics Signed-off-by: Tobias Ortmayr <[email protected]> Co-Authored-By: Paul Maréchal <[email protected]>
- Loading branch information
1 parent
6f7f401
commit fd50b21
Showing
13 changed files
with
373 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
examples/api-samples/src/browser/contribution-filter/sample-filtered-command-contribution.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/******************************************************************************** | ||
* 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 } 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' | ||
}; | ||
export const FILTERED2: Command = { | ||
id: 'example_command.filtered2', | ||
category: EXAMPLE_CATEGORY, | ||
label: 'This command should be filtered out (2)' | ||
}; | ||
} | ||
|
||
/** | ||
* 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: () => { } }); | ||
} | ||
} | ||
|
||
/** | ||
* Second sample command contribution that should be excluded. | ||
*/ | ||
@injectable() | ||
export class SampleFilteredCommandContribution2 implements CommandContribution { | ||
registerCommands(commands: CommandRegistry): void { | ||
commands.registerCommand(SampleFilteredCommand.FILTERED2, { execute: () => { } }); | ||
} | ||
} | ||
|
||
@injectable() | ||
export class SampleFilteredCommandContributionFilter implements ContributionFilter { | ||
|
||
contributions = [CommandContribution]; | ||
|
||
test(contribution: Object): boolean { | ||
return contribution.constructor.name === 'SampleFilteredCommandContribution'; | ||
} | ||
} | ||
|
||
@injectable() | ||
export class SampleGenericContributionFilter implements ContributionFilter { | ||
|
||
contributions = ['*']; | ||
|
||
test(contribution: Object): boolean { | ||
return contribution.constructor.name.toLowerCase() === 'samplefilteredcommandcontribution2'; | ||
} | ||
} | ||
|
||
export const bindSampleFilteredCommandContribution = (bind: interfaces.Bind) => { | ||
bind(CommandContribution).to(SampleFilteredCommandContribution); | ||
bind(CommandContribution).to(SampleFilteredCommandContribution2); | ||
bind(ContributionFilter).to(SampleFilteredCommandContributionFilter); | ||
bind(ContributionFilter).to(SampleGenericContributionFilter); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"'); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
packages/core/src/common/contribution-filter/contribution-filter-registry.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'; | ||
|
||
@injectable() | ||
export class ContributionFilterRegistry { | ||
|
||
protected registry = new Map<ContributionType, ContributionFilter[]>(); | ||
protected genericFilters: ContributionFilter[] = []; | ||
|
||
constructor( | ||
@multiInject(ContributionFilter) @optional() contributionFilters: ContributionFilter[] = [] | ||
) { | ||
for (const filter of contributionFilters) { | ||
if (filter.contributions === undefined || filter.contributions.length === 0 || filter.contributions.includes('*')) { | ||
this.genericFilters.push(filter); | ||
} else { | ||
for (const type of filter.contributions) { | ||
this.addFilter(type, filter); | ||
} | ||
} | ||
} | ||
} | ||
|
||
get(type: ContributionType): ContributionFilter[] { | ||
return [ | ||
...this.registry.get(type) || [], | ||
...this.genericFilters | ||
]; | ||
} | ||
|
||
/** | ||
* 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[] { | ||
return applyFilters<T>(toFilter, this.get(type)); | ||
} | ||
|
||
protected addFilter(type: ContributionType, filter: ContributionFilter): void { | ||
this.getOrCreate(type).push(filter); | ||
} | ||
|
||
protected getOrCreate(type: ContributionType): ContributionFilter[] { | ||
let value = this.registry.get(type); | ||
if (value === undefined) { | ||
this.registry.set(type, value = []); | ||
} | ||
return value; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
packages/core/src/common/contribution-filter/contribution-filter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/******************************************************************************** | ||
* 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'; | ||
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[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/******************************************************************************** | ||
* 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 => filters.every( | ||
// By default we want to *keep* objects when false === filter.test(object) | ||
// because filter.test(object) returns true to exclude items. | ||
filter => negate === filter.test(object) | ||
) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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-utils'; |
60 changes: 60 additions & 0 deletions
60
packages/core/src/common/contribution-filter/string-utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/******************************************************************************** | ||
* 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 | ||
********************************************************************************/ | ||
|
||
/** | ||
* Utility function to test wether any of the given string patterns is included in the | ||
* tested string. | ||
* @param testStr String that should be tested | ||
* @param ignoreCase Flag, to indicate wether the test should be case-sensitive or not | ||
* @param patterns The set of patterns that should be tested for inclusion | ||
* @returns `true` if any of the given patterns is included in the test string, `false` otherwise | ||
*/ | ||
export function includes(testStr: string, ignoreCase: boolean, ...patterns: string[]): boolean { | ||
if (ignoreCase) { | ||
testStr = testStr.toLowerCase(); | ||
patterns = patterns.map(pattern => pattern.toLowerCase()); | ||
} | ||
return patterns.some(pattern => testStr.includes(pattern)); | ||
} | ||
|
||
/** | ||
* Utility function to test wether any of the given string patterns is equal to the | ||
* tested string. | ||
* @param testStr String that should be tested | ||
* @param ignoreCase Flag, to indicate wether the test should be case-sensitive or not | ||
* @param patterns The set of patterns that should be tested for equality | ||
* @returns `true` if any of the given patterns is equal to the test string, `false` otherwise | ||
*/ | ||
export function equals(testStr: string, ignoreCase: boolean, ...patterns: string[]): boolean { | ||
if (ignoreCase) { | ||
testStr = testStr.toLowerCase(); | ||
patterns = patterns.map(pattern => pattern.toLowerCase()); | ||
} | ||
return patterns.some(pattern => testStr === pattern); | ||
|
||
} | ||
|
||
/** | ||
* Utility function to test wether a string matches any of the given regular expression patterns. | ||
* @param testStr String that should be tested | ||
* @param ignoreCase Flag, to indicate wether the test should be case-sensitive or not | ||
* @param patterns The set of regular expressions that should be matched | ||
* @returns `true` if the test string matches any of the given regular expressions, `false` otherwise | ||
*/ | ||
export function matches(testStr: string, ignoreCase: boolean, ...patterns: (RegExp | string)[]): boolean { | ||
const flags = ignoreCase ? 'i' : undefined; | ||
return patterns.some(pattern => new RegExp(pattern, flags).test(testStr)); | ||
} |
Oops, something went wrong.