Skip to content

Commit

Permalink
Refactor to hide VS Code interfaces behind core services (#9727)
Browse files Browse the repository at this point in the history
Refactor to hide VS Code interfaces behind core services

Signed-off-by: Thomas Mäder <[email protected]>
  • Loading branch information
tsmaeder authored Aug 17, 2021
1 parent 84bca9b commit 7d637a1
Show file tree
Hide file tree
Showing 39 changed files with 1,009 additions and 1,038 deletions.
26 changes: 11 additions & 15 deletions examples/api-tests/src/file-search.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,26 @@ describe('file-search', function () {

it('should compare two quick-open-items by `label`', () => {

/** @type monaco.quickInput.IAnythingQuickPickItem */
const a = { label: 'a', resource: new Uri.default('a') };
/** @type monaco.quickInput.IAnythingQuickPickItem */
const b = { label: 'a', resource: new Uri.default('b') };
/** @type import ('@theia/file-search/lib/browser/quick-file-open').FileQuickPickItem*/
const a = { label: 'a', uri: new Uri.default('b') };
/** @type import ('@theia/file-search/lib/browser/quick-file-open').FileQuickPickItem*/
const b = { label: 'b', uri: new Uri.default('a') };

assert.equal(quickFileOpenService['compareItems'](a, b), 1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](b, a), -1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](a, a), 0, 'items should be equal');

assert.equal(quickFileOpenService['compareItems'](a, b, 'label'), 1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](b, a, 'label'), -1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](a, a, 'label'), 0, 'items should be equal');
});

it('should compare two quick-open-items by `uri`', () => {

/** @type monaco.quickInput.IAnythingQuickPickItem */
const a = { label: 'a', resource: new Uri.default('a') };
/** @type monaco.quickInput.IAnythingQuickPickItem */
const b = { label: 'a', resource: new Uri.default('b') };
/** @type import ('@theia/file-search/lib/browser/quick-file-open').FileQuickPickItem*/
const a = { label: 'a', uri: new Uri.default('a') };
/** @type import ('@theia/file-search/lib/browser/quick-file-open').FileQuickPickItem*/
const b = { label: 'a', uri: new Uri.default('b') };

assert.equal(quickFileOpenService['compareItems'](a, b, 'resource'), 1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](b, a, 'resource'), -1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](a, a, 'resource'), 0, 'items should be equal');
assert.equal(quickFileOpenService['compareItems'](a, b), 1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](b, a), -1, 'a should be before b');
assert.equal(quickFileOpenService['compareItems'](a, a), 0, 'items should be equal');
});

});
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/browser/common-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,6 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
@inject(StorageService)
protected readonly storageService: StorageService;

@inject(QuickViewService) @optional()
protected readonly quickView: QuickViewService;

@inject(QuickInputService) @optional()
protected readonly quickInputService: QuickInputService;

Expand Down Expand Up @@ -762,7 +759,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
});

commandRegistry.registerCommand(CommonCommands.OPEN_VIEW, {
execute: () => this.quickInputService?.open(this.quickView?.prefix)
execute: () => this.quickInputService?.open(QuickViewService.PREFIX)
});

commandRegistry.registerCommand(CommonCommands.SELECT_COLOR_THEME, {
Expand Down
14 changes: 9 additions & 5 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,14 @@ import { keytarServicePath, KeytarService } from '../common/keytar-protocol';
import { CredentialsService, CredentialsServiceImpl } from './credentials-service';
import { ContributionFilterRegistry, ContributionFilterRegistryImpl } from '../common/contribution-filter';
import { QuickCommandFrontendContribution } from './quick-input/quick-command-frontend-contribution';
import { QuickHelpFrontendContribution } from './quick-input/quick-help-frontend-contribution';
import { QuickHelpService } from './quick-input/quick-help-service';
import { QuickPickService, quickPickServicePath } from '../common/quick-pick-service';
import {
QuickPickServiceImpl,
QuickInputFrontendContribution
} from './quick-input';
import { QuickAccessContribution } from './quick-input/quick-access-contribution';
import { QuickAccessContribution } from './quick-input/quick-access';
import { QuickCommandService } from './quick-input/quick-command-service';

export { bindResourceProvider, bindMessageService, bindPreferenceService };

Expand Down Expand Up @@ -233,12 +234,14 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
);

bind(QuickCommandFrontendContribution).toSelf().inSingletonScope();
[CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution].forEach(serviceIdentifier =>
[CommandContribution, KeybindingContribution, MenuContribution].forEach(serviceIdentifier =>
bind(serviceIdentifier).toService(QuickCommandFrontendContribution)
);
bind(QuickCommandService).toSelf().inSingletonScope();
bind(QuickAccessContribution).toService(QuickCommandService);

bind(QuickHelpFrontendContribution).toSelf().inSingletonScope();
bind(QuickAccessContribution).toService(QuickHelpFrontendContribution);
bind(QuickHelpService).toSelf().inSingletonScope();
bind(QuickAccessContribution).toService(QuickHelpService);

bind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope().onActivation(({ container }, quickPickService: QuickPickService) => {
WebSocketConnectionProvider.createProxy(container, quickPickServicePath, quickPickService);
Expand Down Expand Up @@ -319,6 +322,7 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
return container.get(ViewContainer);
});

bind(QuickViewService).toSelf().inSingletonScope();
bind(QuickAccessContribution).toService(QuickViewService);

bind(DialogOverlayService).toSelf().inSingletonScope();
Expand Down
4 changes: 1 addition & 3 deletions packages/core/src/browser/quick-input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
********************************************************************************/
export * from './quick-command-frontend-contribution';
export * from './quick-command-service';
export * from './quick-editor-service';
export * from './quick-help-frontend-contribution';
export * from './quick-help-service';
export * from './quick-access-contribution';
export * from './quick-access';
export * from './quick-input-frontend-contribution';
export * from './quick-input-service';
export * from './quick-view-service';
Expand Down
23 changes: 0 additions & 23 deletions packages/core/src/browser/quick-input/quick-access-contribution.ts

This file was deleted.

75 changes: 75 additions & 0 deletions packages/core/src/browser/quick-input/quick-access.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/********************************************************************************
* Copyright (c) 2021 SAP SE or an SAP affiliate company 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 { CancellationToken, Disposable } from '../../common';
import { QuickPicks } from './quick-input-service';

export const QuickAccessContribution = Symbol('QuickAccessContribution');
/**
* Bind this contribution in order to register quick access providers with the
* QuickAccessRegistry at startup
*/
export interface QuickAccessContribution {
registerQuickAccessProvider(): void;
}

export interface QuickAccessProvider {
getPicks(filter: string, token: CancellationToken): QuickPicks | Promise<QuickPicks>;
reset?(): void;
}

export interface QuickAccessProviderHelp {
prefix?: string;
description: string;
needsEditor: boolean;
}

export interface QuickAccessProviderDescriptor {
/**
* return an instance of QuickAccessProvider. Implementers are free to return that same instance multiple times
*/
readonly getInstance: () => QuickAccessProvider;
/**
* The prefix for quick access picker to use the provider for.
*/
readonly prefix: string;
/**
* A placeholder to use for the input field when the provider is active.
* This will also be read out by screen readers and thus helps for
* accessibility.
*/
readonly placeholder?: string;
/**
* Help entries for this quick access provider
*/
readonly helpEntries: QuickAccessProviderHelp[];
/**
* A context key that will be set automatically when this quick access is being shown
*/
readonly contextKey?: string;
}

export const QuickAccessRegistry = Symbol('QuickAccessRegistry');

/**
* A registry for quick access providers.
*/
export interface QuickAccessRegistry {
registerQuickAccessProvider(provider: QuickAccessProviderDescriptor): Disposable;
getQuickAccessProviders(): QuickAccessProviderDescriptor[];
getQuickAccessProvider(prefix: string): QuickAccessProviderDescriptor | undefined;
clear(): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ import { injectable, inject, optional } from 'inversify';
import { CommandRegistry, CommandContribution, MenuContribution, MenuModelRegistry } from '../../common';
import { KeybindingRegistry, KeybindingContribution } from '../keybinding';
import { CommonMenus } from '../common-frontend-contribution';
import { QuickInputService } from './quick-input-service';
import { CLEAR_COMMAND_HISTORY, quickCommand, QuickCommandService } from './quick-command-service';
import { QuickAccessContribution } from './quick-access-contribution';
import { QuickInputService } from './quick-input-service';

@injectable()
export class QuickCommandFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution {
export class QuickCommandFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution {

@inject(QuickInputService) @optional()
protected readonly quickInputService: QuickInputService;
Expand Down Expand Up @@ -59,8 +58,4 @@ export class QuickCommandFrontendContribution implements CommandContribution, Ke
keybinding: 'ctrlcmd+shift+p'
});
}

registerQuickAccessProvider(): void {
this.quickCommandService?.registerQuickAccessProvider();
}
}
97 changes: 92 additions & 5 deletions packages/core/src/browser/quick-input/quick-command-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
********************************************************************************/

import { inject, injectable } from 'inversify';
import { Disposable, Command, CommandRegistry } from '../../common';
import { KeybindingRegistry } from '../keybinding';
import { Disposable, Command, CommandRegistry, CancellationToken } from '../../common';
import { ContextKeyService } from '../context-key-service';
import { CorePreferences } from '../core-preferences';
import { QuickAccessContribution } from './quick-access-contribution';
import { QuickInputService } from './quick-input-service';
import { QuickAccessContribution, QuickAccessProvider, QuickAccessRegistry } from './quick-access';
import { filterItems, QuickPickItem, QuickPicks } from './quick-input-service';
import { KeySequence } from '../keys';

export const quickCommand: Command = {
id: 'workbench.action.showCommands'
Expand All @@ -31,7 +33,8 @@ export const CLEAR_COMMAND_HISTORY: Command = {
};

@injectable()
export class QuickCommandService extends QuickInputService implements QuickAccessContribution {
export class QuickCommandService implements QuickAccessContribution, QuickAccessProvider {
static PREFIX = '>';

@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;
Expand All @@ -42,12 +45,96 @@ export class QuickCommandService extends QuickInputService implements QuickAcces
@inject(CorePreferences)
protected readonly corePreferences: CorePreferences;

@inject(QuickAccessRegistry)
protected readonly quickAccessRegistry: QuickAccessRegistry;

@inject(KeybindingRegistry)
protected readonly keybindingRegistry: KeybindingRegistry;

// The list of exempted commands not to be displayed in the recently used list.
readonly exemptedCommands: Command[] = [
CLEAR_COMMAND_HISTORY,
];

registerQuickAccessProvider(): void { }
private recentItems: QuickPickItem[] = [];
private otherItems: QuickPickItem[] = [];

registerQuickAccessProvider(): void {
this.quickAccessRegistry.registerQuickAccessProvider({
getInstance: () => this,
prefix: QuickCommandService.PREFIX,
placeholder: '',
helpEntries: [{ description: 'Quick Command', needsEditor: false }]
});
}

reset(): void {
const { recent, other } = this.getCommands();
this.recentItems = [];
this.otherItems = [];
this.recentItems.push(...recent.map(command => this.toItem(command)));
this.otherItems.push(...other.map(command => this.toItem(command)));
}

getPicks(filter: string, token: CancellationToken): QuickPicks {
const items: QuickPicks = [];
if (this.recentItems.length === 0 && this.otherItems.length === 0) {
this.reset();
}
const recentItems = filterItems(this.recentItems.slice(), filter);
const otherItems = filterItems(this.otherItems.slice(), filter);

if (recentItems.length > 0) {
items.push({ type: 'separator', label: 'recently used' }, ...recentItems);
}

if (otherItems.length > 0) {
if (recentItems.length > 0) {
items.push({ type: 'separator', label: 'other commands' });
}
items.push(...otherItems);
}
return items;
}

private toItem(command: Command): QuickPickItem {
const label = (command.category) ? `${command.category}: ` + command.label! : command.label!;
const iconClasses = this.getItemIconClasses(command);
const activeElement = window.document.activeElement as HTMLElement;

return {
label,
iconClasses,
alwaysShow: !!this.commandRegistry.getActiveHandler(command.id),
keySequence: this.getKeybinding(command),
execute: () => {
activeElement.focus({ preventScroll: true });
this.commandRegistry.executeCommand(command.id);
this.commandRegistry.addRecentCommand(command);
}
};
}

private getKeybinding(command: Command): KeySequence | undefined {
const keybindings = this.keybindingRegistry.getKeybindingsForCommand(command.id);
if (!keybindings || keybindings.length === 0) {
return undefined;
}

try {
return this.keybindingRegistry.resolveKeybinding(keybindings[0]);
} catch (error) {
return undefined;
}
}

private getItemIconClasses(command: Command): string[] | undefined {
const toggledHandler = this.commandRegistry.getToggledHandler(command.id);
if (toggledHandler) {
return ['fa fa-check'];
}
return undefined;
}

protected readonly contexts = new Map<string, string[]>();
pushCommandContext(commandId: string, when: string): Disposable {
Expand Down
33 changes: 0 additions & 33 deletions packages/core/src/browser/quick-input/quick-editor-service.ts

This file was deleted.

Loading

0 comments on commit 7d637a1

Please sign in to comment.