Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the 'keymaps' documentation. #6369

Merged
merged 1 commit into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/core/src/browser/keybinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ export interface ScopedKeybinding extends Keybinding {
}

export const KeybindingContribution = Symbol('KeybindingContribution');
/**
* Representation of a keybinding contribution.
*/
export interface KeybindingContribution {
/**
* Registers keybindings.
* @param keybindings the keybinding registry.
*/
registerKeybindings(keybindings: KeybindingRegistry): void;
}

Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/browser/shell/tab-bar-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,15 @@ export namespace TabBarToolbar {
* Clients should implement this interface if they want to contribute to the tab-bar toolbar.
*/
export const TabBarToolbarContribution = Symbol('TabBarToolbarContribution');
/**
* Representation of a tabbar toolbar contribution.
*/
export interface TabBarToolbarContribution {

/**
* Registers toolbar items.
* @param registry the tabbar toolbar registry.
*/
registerToolbarItems(registry: TabBarToolbarRegistry): void;

}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/common/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,15 @@ export type MenuPath = string[];
export const MAIN_MENU_BAR: MenuPath = ['menubar'];

export const MenuContribution = Symbol('MenuContribution');

/**
* Representation of a menu contribution.
*/
export interface MenuContribution {
/**
* Registers menus.
* @param menus the menu model registry.
*/
registerMenus(menus: MenuModelRegistry): void;
}

Expand Down
58 changes: 38 additions & 20 deletions packages/keymaps/src/browser/keybindings-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,14 @@ export class KeybindingWidget extends ReactWidget {
@inject(KeymapsService)
protected readonly keymapsService: KeymapsService;

protected items: KeybindingItem[] = [];

static readonly ID = 'keybindings.view.widget';
static readonly LABEL = 'Keyboard Shortcuts';

/**
* The list of all available keybindings.
*/
protected items: KeybindingItem[] = [];

/**
* The current user search query.
*/
Expand All @@ -94,6 +97,9 @@ export class KeybindingWidget extends ReactWidget {
* The regular expression used to extract values between fuzzy results.
*/
protected readonly regexp = /<match>(.*?)<\/match>/g;
/**
* The regular expression used to extract values between the keybinding separator.
*/
protected readonly keybindingSeparator = /<match>\+<\/match>/g;

/**
Expand All @@ -108,8 +114,14 @@ export class KeybindingWidget extends ReactWidget {
protected readonly onDidUpdateEmitter = new Emitter<void>();
readonly onDidUpdate: Event<void> = this.onDidUpdateEmitter.event;

/**
* Search keybindings.
*/
protected readonly searchKeybindings: () => void = debounce(() => this.doSearchKeybindings(), 50);

/**
* Initialize the widget.
*/
@postConstruct()
protected init(): void {
this.id = KeybindingWidget.ID;
Expand Down Expand Up @@ -368,15 +380,15 @@ export class KeybindingWidget extends ReactWidget {

/**
* Render the actions container with action icons.
* @param item {KeybindingItem} the keybinding item for the row.
* @param item the keybinding item for the row.
*/
protected renderActions(item: KeybindingItem): React.ReactNode {
return <span className='kb-actions-icons'>{this.renderEdit(item)}{this.renderReset(item)}</span>;
}

/**
* Render the edit action used to update a keybinding.
* @param item {KeybindingItem} the keybinding item for the row.
* @param item the keybinding item for the row.
*/
protected renderEdit(item: KeybindingItem): React.ReactNode {
return <a title='Edit Keybinding' href='#' onClick={a => this.editKeybinding(item)}><i className='fa fa-pencil kb-action-item'></i></a>;
Expand All @@ -385,7 +397,7 @@ export class KeybindingWidget extends ReactWidget {
/**
* Render the reset action to reset the custom keybinding.
* Only visible if a keybinding has a `user` scope.
* @param item {KeybindingItem} the keybinding item for the row.
* @param item the keybinding item for the row.
*/
protected renderReset(item: KeybindingItem): React.ReactNode {
return (item.source && this.getRawValue(item.source) === KeybindingScope[1].toLocaleLowerCase())
Expand All @@ -394,7 +406,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Render the keybinding.
* @param keybinding {string} the keybinding value.
* @param keybinding the keybinding value.
*/
protected renderKeybinding(keybinding: string): React.ReactNode {
const regex = new RegExp(this.keybindingSeparator);
Expand Down Expand Up @@ -472,8 +484,14 @@ export class KeybindingWidget extends ReactWidget {

/**
* Compare two strings.
* @param a {string | undefined} the first string.
* @param b {string | undefined} the second string.
* - Strings are first normalized before comparison (`toLowerCase`).
* @param a the optional first string.
* @param b the optional second string.
vince-fugnitto marked this conversation as resolved.
Show resolved Hide resolved
*
* @returns an integer indicating whether `a` comes before, after or is equivalent to `b`.
* - returns `-1` if `a` occurs before `b`.
* - returns `1` if `a` occurs after `b`.
* - returns `0` if they are equivalent.
*/
protected compareItem(a: string | undefined, b: string | undefined): number {
if (a && b) {
Expand All @@ -498,7 +516,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Prompt users to update the keybinding for the given command.
* @param item {KeybindingItem} the keybinding item.
* @param item the keybinding item.
*/
protected editKeybinding(item: KeybindingItem): void {
const command = this.getRawValue(item.command);
Expand All @@ -519,7 +537,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Prompt users for confirmation before resetting.
* @param command {string} the command label.
* @param command the command label.
*
* @returns a Promise which resolves to `true` if a user accepts resetting.
*/
Expand All @@ -533,7 +551,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Reset the keybinding to its default value.
* @param item {KeybindingItem} the keybinding item.
* @param item the keybinding item.
*/
protected async resetKeybinding(item: KeybindingItem): Promise<void> {
const rawCommandId = this.getRawValue(item.id);
Expand All @@ -546,9 +564,9 @@ export class KeybindingWidget extends ReactWidget {

/**
* Validate the provided keybinding value against its previous value.
* @param command {string} the command label.
* @param oldKeybinding {string} the old keybinding value.
* @param keybinding {string} the new keybinding value.
* @param command the command label.
* @param oldKeybinding the old keybinding value.
* @param keybinding the new keybinding value.
*
* @returns the end user message to display.
*/
Expand All @@ -573,7 +591,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Build the cell data with highlights if applicable.
* @param raw {string} the raw cell value.
* @param raw the raw cell value.
*
* @returns the list of cell data.
*/
Expand Down Expand Up @@ -612,7 +630,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Render the fuzzy representation of a matched result.
* @param property {string} one of the `KeybindingItem` properties.
* @param property one of the `KeybindingItem` properties.
*/
protected renderMatchedData(property: string): React.ReactNode {
if (this.query !== '') {
Expand All @@ -629,7 +647,7 @@ export class KeybindingWidget extends ReactWidget {

/**
* Render the raw value of a item without fuzzy highlighting.
* @param property {string} one of the `KeybindingItem` properties.
* @param property one of the `KeybindingItem` properties.
*/
protected getRawValue(property: string): string {
return property.replace(new RegExp(this.regexp), '$1');
Expand Down Expand Up @@ -675,8 +693,8 @@ class EditKeybindingDialog extends SingleTextInputDialog {

/**
* Add `Reset` action used to reset a custom keybinding, and close the dialog.
* @param element {HTMLElement} the HTML element in question.
* @param additionalEventTypes {K[]} additional event types.
* @param element the HTML element in question.
* @param additionalEventTypes additional event types.
*/
protected addResetAction<K extends keyof HTMLElementEventMap>(element: HTMLElement, ...additionalEventTypes: K[]): void {
this.addKeyListener(element, Key.ENTER, () => {
Expand Down Expand Up @@ -712,7 +730,7 @@ class EditKeybindingDialog extends SingleTextInputDialog {

/**
* Extract the raw value from a string (without fuzzy matching).
* @param a {string} given string value for extraction.
* @param a given string value for extraction.
*
* @returns the raw value of a string without any fuzzy matching.
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/keymaps/src/browser/keymaps-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ export class KeymapsFrontendContribution extends AbstractViewContribution<Keybin
});
}

/**
* Determine if the current widget is the keybindings widget.
*/
protected withWidget<T>(widget: Widget | undefined = this.tryGetWidget(), fn: (widget: KeybindingWidget) => T): T | false {
if (widget instanceof KeybindingWidget && widget.id === KeybindingWidget.ID) {
return fn(widget);
Expand Down
5 changes: 5 additions & 0 deletions packages/keymaps/src/browser/keymaps-parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ describe('keymaps-parser', () => {
]`);
});

/**
* Assert that the content equals the expected content.
* @param expectation the expected string.
* @param content the content to verify.
*/
function assertParsing(expectation: string, content: string): void {
const errors: string[] = [];
const keybindings = parser.parse(content, errors);
Expand Down
9 changes: 9 additions & 0 deletions packages/keymaps/src/browser/keymaps-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ export class KeymapsParser {
}).compile(keymapsSchema);
}

/**
* Parse the keybindings for potential errors.
* @param content the content.
* @param errors the optional list of parsing errors.
*/
parse(content: string, errors?: string[]): Keybinding[] {
const strippedContent = parser.stripComments(content);
const parsingErrors: parser.ParseError[] | undefined = errors ? [] : undefined;
Expand All @@ -76,6 +81,10 @@ export class KeymapsParser {
return [];
}

/**
* Print the parsed error code.
* @param code the error code if available.
*/
// https://github.com/Microsoft/node-jsonc-parser/issues/13
// tslint:disable-next-line:typedef
protected printParseErrorCode(code: number | undefined) {
Expand Down
38 changes: 38 additions & 0 deletions packages/keymaps/src/browser/keymaps-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,21 @@ import { KeymapsParser } from './keymaps-parser';
import * as jsoncparser from 'jsonc-parser';
import { Emitter } from '@theia/core/lib/common/';

/**
* Representation of a JSON keybinding.
*/
export interface KeybindingJson {
/**
* The keybinding command.
*/
command: string,
/**
* The actual keybinding.
*/
keybinding: string,
/**
* The keybinding context.
*/
context: string,
}

Expand All @@ -49,6 +61,9 @@ export class KeymapsService {

protected resource: Resource;

/**
* Initialize the keybinding service.
*/
@postConstruct()
protected async init(): Promise<void> {
this.resource = await this.resourceProvider(new URI().withScheme(UserStorageUri.SCHEME).withPath('keymaps.json'));
Expand All @@ -59,12 +74,18 @@ export class KeymapsService {
this.keyBindingRegistry.onKeybindingsChanged(() => this.changeKeymapEmitter.fire(undefined));
}

/**
* Reconcile all the keybindings, registering them to the registry.
*/
protected async reconcile(): Promise<void> {
const keybindings = await this.parseKeybindings();
this.keyBindingRegistry.setKeymap(KeybindingScope.USER, keybindings);
this.changeKeymapEmitter.fire(undefined);
}

/**
* Parsed the read keybindings.
*/
protected async parseKeybindings(): Promise<Keybinding[]> {
try {
const content = await this.resource.readContents();
Expand All @@ -74,6 +95,10 @@ export class KeymapsService {
}
}

/**
* Open the keybindings widget.
* @param ref the optional reference for opening the widget.
*/
open(ref?: Widget): void {
const options: WidgetOpenerOptions = {
widgetOptions: ref ? { area: 'main', mode: 'split-right', ref } : { area: 'main' },
Expand All @@ -82,6 +107,10 @@ export class KeymapsService {
open(this.opener, this.resource.uri, options);
}

/**
* Set the keybinding in the JSON.
* @param keybindingJson the JSON keybindings.
*/
async setKeybinding(keybindingJson: KeybindingJson): Promise<void> {
if (!this.resource.saveContents) {
return;
Expand All @@ -102,6 +131,10 @@ export class KeymapsService {
await this.resource.saveContents(JSON.stringify(keybindings, undefined, 4));
}

/**
* Remove the given keybinding with the given command id from the JSON.
* @param commandId the keybinding command id.
*/
async removeKeybinding(commandId: string): Promise<void> {
if (!this.resource.saveContents) {
return;
Expand All @@ -112,6 +145,11 @@ export class KeymapsService {
await this.resource.saveContents(JSON.stringify(filtered, undefined, 4));
}

/**
* Get the list of keybindings from the JSON.
*
* @returns the list of keybindings in JSON.
*/
async getKeybindings(): Promise<KeybindingJson[]> {
if (!this.resource.saveContents) {
return [];
Expand Down