Skip to content

Commit

Permalink
Provide API to filter unwanted contributions
Browse files Browse the repository at this point in the history
Fixes eclipse-theia#9069
Signed-off-by: Tobias Ortmayr [email protected]
Contributed on behalf of STMicroelectronics
  • Loading branch information
tortmayr committed Mar 22, 2021
1 parent 5dc9612 commit 2327261
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 4 deletions.
15 changes: 11 additions & 4 deletions examples/api-samples/src/browser/api-samples-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ContributionFilter, NameBasedContributionFilter } from '@theia/core/lib/common';
import { ContainerModule } from 'inversify';
import '../../src/browser/style/branding.css';
import { bindSampleFileWatching } from './file-watching/sample-file-watching-contribution';
import { bindDynamicLabelProvider } from './label/sample-dynamic-label-provider-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';
import { bindSampleFileWatching } from './file-watching/sample-file-watching-contribution';
import { bindSampleOutputChannelWithSeverity } from './output/sample-output-channel-with-severity';
import { bindSampleUnclosableView } from './view/sample-unclosable-view-contribution';
import { bindVSXCommand } from './vsx/sample-vsx-command-contribution';

import '../../src/browser/style/branding.css';
class TestContributionFilter extends NameBasedContributionFilter {
patterns = ['TerminalActiveContext', 'TerminalSearchVisibleContext', 'TerminalQuickOpenContribution', 'TerminalFrontendContribution'];
patterMatching = 'equals';
}

export default new ContainerModule(bind => {
bindDynamicLabelProvider(bind);
Expand All @@ -31,4 +36,6 @@ export default new ContainerModule(bind => {
bindSampleMenu(bind);
bindSampleFileWatching(bind);
bindVSXCommand(bind);
bind(ContributionFilter).to(TestContributionFilter);
});

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,62 @@
/********************************************************************************
* 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) {
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 genericFilters = this.registry.get(GENERIC_CONTRIBUTION_FILTER_KEY) || [];
const filters = this.registry.get(type) || [];
filters.push(...genericFilters);
return filters;
}

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,35 @@
/********************************************************************************
* 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 { Filter, IdBasedFilter, NameBasedFilter } from './filter';
import { interfaces } from 'inversify';
export type ContributionType = interfaces.ServiceIdentifier<unknown>;

export const ContributionFilter = Symbol('ContributionFilter');
export interface ContributionFilter extends Filter<Object> {
/**
* contribution types for which this filter is applicable
*/
contributions?: ContributionType[];
}

export abstract class IdBasedContributionFilter extends IdBasedFilter<Object> implements ContributionFilter {
contributions?: ContributionType[];
}

export abstract class NameBasedContributionFilter extends NameBasedFilter<Object> implements ContributionFilter {
contributions?: ContributionType[];
}
100 changes: 100 additions & 0 deletions packages/core/src/common/contribution-filter/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/********************************************************************************
* 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 } from 'inversify';

export const Filter = Symbol('Filter');

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;
}

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;
});
}

export type PatternMatchingType = 'equals' | 'includes' | 'regex';

@injectable()
export abstract class StringBasedFilter<T extends Object> implements Filter<T> {

abstract patterns: string[];
patterMatchingType: PatternMatchingType = 'equals';
ignoreCase = false;

abstract toFilterString(toFilter: T): string | undefined;

protected doTest(filterStr: string): boolean {
const patterns = this.ignoreCase ? this.patterns.map(pattern => pattern.toLowerCase()) : this.patterns;
if (this.patterMatchingType === 'includes') {
return patterns.find(pattern => filterStr.includes(pattern)) !== undefined;
} else if (this.patterMatchingType === 'equals') {
return patterns.find(pattern => filterStr === pattern) !== undefined;
} else {
// eslint-disable-next-line no-null/no-null
return patterns.find(pattern => filterStr.match(new RegExp(pattern)) !== null) !== undefined;
}
}

test(contribution: T): boolean {
let filterStr = this.toFilterString(contribution);
if (!filterStr) {
return false;
}
if (this.ignoreCase) {
filterStr = filterStr.toLowerCase();
}
return this.doTest(filterStr);
}
}

@injectable()
export abstract class NameBasedFilter<T extends Object> extends StringBasedFilter<T> {
toFilterString(toTest: T): string {
return toTest.constructor.name;
}
}

@injectable()
export abstract class IdBasedFilter<T extends Object> extends StringBasedFilter<T> {
toFilterString(toTest: T): string | undefined {
if (Identifable.is(toTest)) {
return toTest.id;
}
return undefined;
}
}

export interface Identifable {
id: string;
}

export namespace Identifable {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function is(object: any): object is Identifable {
return 'id' in object && typeof object['id'] === 'string';
}
}
19 changes: 19 additions & 0 deletions packages/core/src/common/contribution-filter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/********************************************************************************
* 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';
8 changes: 8 additions & 0 deletions packages/core/src/common/contribution-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
********************************************************************************/

import { interfaces } from 'inversify';
import { ContributionFilterRegistry } from './contribution-filter';

export const ContributionProvider = Symbol('ContributionProvider');

Expand All @@ -29,6 +30,7 @@ export interface ContributionProvider<T extends object> {
class ContainerBasedContributionProvider<T extends object> implements ContributionProvider<T> {

protected services: T[] | undefined;
protected filterRegistry: ContributionFilterRegistry | undefined;

constructor(
protected readonly serviceIdentifier: interfaces.ServiceIdentifier<T>,
Expand All @@ -48,11 +50,17 @@ class ContainerBasedContributionProvider<T extends object> implements Contributi
console.error(error);
}
}
if (!this.filterRegistry && currentContainer.isBound(ContributionFilterRegistry)) {
this.filterRegistry = currentContainer.get(ContributionFilterRegistry);
}
// eslint-disable-next-line no-null/no-null
currentContainer = recursive === true ? currentContainer.parent : null;
}
this.services = currentServices;
}
if (this.filterRegistry) {
return this.filterRegistry.applyFilters(this.services, this.serviceIdentifier);
}
return this.services;
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export * from './selection';
export * from './strings';
export * from './application-error';
export * from './lsp-types';
export * from './contribution-filter';

import { environment } from '@theia/application-package/lib/environment';
export { environment };
3 changes: 3 additions & 0 deletions packages/core/src/node/backend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { EnvVariablesServerImpl } from './env-variables';
import { ConnectionContainerModule } from './messaging/connection-container-module';
import { QuickPickService, quickPickServicePath } from '../common/quick-pick-service';
import { WsRequestValidator, WsRequestValidatorContribution } from './ws-request-validators';
import { ContributionFilterRegistry } from '../common/contribution-filter';

decorate(injectable(), ApplicationPackage);

Expand Down Expand Up @@ -85,4 +86,6 @@ export const backendApplicationModule = new ContainerModule(bind => {

bind(WsRequestValidator).toSelf().inSingletonScope();
bindContributionProvider(bind, WsRequestValidatorContribution);

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

0 comments on commit 2327261

Please sign in to comment.