UNPKG

@difizen/mana-core

Version:

333 lines 14.6 kB
import type { Event } from '@difizen/mana-common'; import { Emitter, Disposable } from '@difizen/mana-common'; import { Syringe } from '@difizen/mana-syringe'; import type { Contribution } from '@difizen/mana-syringe'; import type { Application } from '../application'; import { ApplicationContribution } from '../application'; import { CommandRegistry, Command } from '../command'; import { DebugService } from '../common/debug'; import { KeyboardLayoutService } from '../keyboard/keyboard-layout-service'; import { KeyCode, KeySequence, Key } from '../keyboard/keys'; import { ContextKeyService } from './context-key-service'; import { KeybindingContribution } from './keybinding-proocol'; /** * A Keybinding binds a specific key sequence ({@link Keybinding#keybinding}) to trigger a command ({@link Keybinding#command}). A Keybinding optionally may * define a "when clause" ({@link Keybinding#when}) to specify in which context it becomes active. * @see KeyBindingRegistry */ export type Keybinding = { /** * Unique command identifier of the command to be triggered by this keybinding. */ command: string; /** * The key sequence for the keybinding as defined in packages/keymaps/README.md. */ keybinding: string; /** * The optional keybinding context where this binding belongs to. * If not specified, then this keybinding context belongs to the NOOP * keybinding context. * * @deprecated use `when` closure instead */ context?: string | undefined; /** * An optional clause defining the condition when the keybinding is active, e.g. based on the current focus. * See {@link https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts} for more details. */ when?: string | undefined; /** * Optional arguments that will be passed to the command when it gets triggered via this keybinding. * Needs to be specified when the triggered command expects arguments to be passed to the command handler. */ args?: any; preventDefault?: boolean; stopPropagation?: boolean; }; export declare namespace Keybinding { /** * Compares two keybindings for equality. * Can optionally ignore the keybinding and/or args property in the comparison. * @param a The first Keybinding in the comparison * @param b The second Keybinding in the comparison * @param ignoreKeybinding Ignore the 'keybinding' property in the comparison * @param ignoreArgs Ignore the 'args' property in the comparison */ function equals(a: Keybinding, b: Keybinding, ignoreKeybinding?: boolean, ignoreArgs?: boolean): boolean; /** * Returns a new object only containing properties which * are described on the `Keybinding` API. * * @param binding the binding to create an API object for. */ function apiObjectify(binding: Keybinding): Keybinding; /** * Returns with the string representation of the binding. * Any additional properties which are not described on * the `Keybinding` API will be ignored. * * @param binding the binding to stringify. */ function stringify(binding: Keybinding): string; function is(arg: Keybinding | any): arg is Keybinding; } export declare enum KeybindingScope { DEFAULT = 0, USER = 1, WORKSPACE = 2, END = 3 } export declare namespace KeybindingScope { const length: number; } export type ResolvedKeybinding = { /** * The KeyboardLayoutService may transform the `keybinding` depending on the * user's keyboard layout. This property holds the transformed keybinding that * should be used in the UI. The value is undefined if the KeyboardLayoutService * has not been called yet to resolve the keybinding. */ resolved?: KeyCode[] | undefined; } & Keybinding; export type ScopedKeybinding = { /** Current keybinding scope */ scope: KeybindingScope; } & Keybinding; export declare const KeybindingContext: Syringe.DefinedToken; export type KeybindingContext = { /** * The unique ID of the current context. */ readonly id: string; isEnabled: (arg: Keybinding) => boolean; }; export declare namespace KeybindingContexts { const NOOP_CONTEXT: KeybindingContext; const DEFAULT_CONTEXT: KeybindingContext; } export declare class KeybindingRegistry implements ApplicationContribution { preventDefault?: boolean; stopPropagation?: boolean; static readonly PASSTHROUGH_PSEUDO_COMMAND = "passthrough"; protected keySequence: KeySequence; protected readonly contexts: Record<string, KeybindingContext>; protected readonly keymaps: ScopedKeybinding[][]; protected emitter: Emitter<KeybindingRegistry.Match | undefined>; protected readonly keyboardLayoutService: KeyboardLayoutService; protected readonly contextProvider: Contribution.Provider<KeybindingContext>; protected readonly commandRegistry: CommandRegistry; protected readonly contributions: Contribution.Provider<KeybindingContribution>; protected readonly logger: DebugService; protected readonly whenContextService: ContextKeyService; constructor(keyboardLayoutService: KeyboardLayoutService, contextProvider: Contribution.Provider<KeybindingContext>, commandRegistry: CommandRegistry, contributions: Contribution.Provider<KeybindingContribution>, logger: DebugService, whenContextService: ContextKeyService); onStart(app: Application): Promise<void>; protected keybindingsChanged: Emitter<void>; /** * Event that is fired when the resolved keybindings change due to a different keyboard layout * or when a new keymap is being set */ get onKeybindingsChanged(): Event<void>; /** * Registers the keybinding context arguments into the application. Fails when an already registered * context is being registered. * * @param contexts the keybinding contexts to register into the application. */ protected registerContext(...contexts: KeybindingContext[]): void; /** * Register a default keybinding to the registry. * * Keybindings registered later have higher priority during evaluation. * * @param binding the keybinding to be registered */ registerKeybinding(binding: Keybinding): Disposable; /** * Register multiple default keybindings to the registry * * @param bindings An array of keybinding to be registered */ registerKeybindings(...bindings: Keybinding[]): Disposable; /** * Unregister all keybindings from the registry that are bound to the key of the given keybinding * * @param binding a keybinding specifying the key to be unregistered */ unregisterKeybinding(binding: Keybinding): void; /** * Unregister all keybindings with the given key from the registry * * @param key a key to be unregistered */ unregisterKeybinding(key: string): void; /** * Unregister all existing keybindings for the given command * @param command the command to unregister all keybindings for */ unregisterKeybinding(command: Command): void; protected doRegisterKeybindings(bindings: Keybinding[], scope?: KeybindingScope): Disposable; protected doRegisterKeybinding(binding: Keybinding, scope?: KeybindingScope): Disposable; /** * Ensures that keybindings are inserted in order of increasing length of binding to ensure that if a * user triggers a short keybinding (e.g. ctrl+k), the UI won't wait for a longer one (e.g. ctrl+k enter) */ protected insertBindingIntoScope(item: Keybinding & { scope: KeybindingScope; }, scope: KeybindingScope): void; /** * Ensure that the `resolved` property of the given binding is set by calling the KeyboardLayoutService. */ resolveKeybinding(binding: ResolvedKeybinding): KeyCode[]; /** * Clear all `resolved` properties of registered keybindings so the KeyboardLayoutService is called * again to resolve them. This is necessary when the user's keyboard layout has changed. */ protected clearResolvedKeybindings(): void; /** * Checks whether a colliding {@link common.Keybinding} exists in a specific scope. * @param binding the keybinding to check * @param scope the keybinding scope to check * @returns true if there is a colliding keybinding */ containsKeybindingInScope(binding: Keybinding, scope?: KeybindingScope): boolean; /** * Get a user visible representation of a {@link common.Keybinding}. * @returns an array of strings representing all elements of the {@link KeySequence} defined by the {@link common.Keybinding} * @param keybinding the keybinding * @param separator the separator to be used to stringify {@link KeyCode}s that are part of the {@link KeySequence} */ acceleratorFor(keybinding: Keybinding, separator?: string): string[]; /** * Get a user visible representation of a {@link KeySequence}. * @returns an array of strings representing all elements of the {@link KeySequence} * @param keySequence the keysequence * @param separator the separator to be used to stringify {@link KeyCode}s that are part of the {@link KeySequence} */ acceleratorForSequence(keySequence: KeySequence, separator?: string): string[]; /** * Get a user visible representation of a key code (a key with modifiers). * @returns a string representing the {@link KeyCode} * @param keyCode the keycode * @param separator the separator used to separate keys (key and modifiers) in the returning string */ acceleratorForKeyCode(keyCode: KeyCode, separator?: string): string; /** * Return a user visible representation of a single key. */ acceleratorForKey(key: Key): string; /** * Finds collisions for a key sequence inside a list of bindings (error-free) * * @param bindings the reference bindings * @param candidate the sequence to match */ protected getKeySequenceCollisions(bindings: ScopedKeybinding[], candidate: KeySequence): KeybindingRegistry.KeybindingsResult; /** * Get all keybindings associated to a commandId. * * @param commandId The ID of the command for which we are looking for keybindings. * @returns an array of {@link ScopedKeybinding} */ getKeybindingsForCommand(commandId: string): ScopedKeybinding[]; protected isActive(binding: Keybinding): boolean; /** * Tries to execute a keybinding. * * @param binding to execute * @param event keyboard event. */ protected executeKeyBinding(binding: Keybinding, event: KeyboardEvent): void; /** * Only execute if it has no context (global context) or if we're in that context. */ protected isEnabled(binding: Keybinding, event: KeyboardEvent): boolean; dispatchCommand(id: string, target?: EventTarget): void; dispatchKeyDown(input: KeyboardEventInit | KeyCode | string, target?: EventTarget): void; protected asKeyboardEventInit(input: KeyboardEventInit | KeyCode | string): KeyboardEventInit & Partial<{ keyCode: number; }>; /** * Run the command matching to the given keyboard event. */ run(event: KeyboardEvent): void; onMatchChange(callback: (match?: KeybindingRegistry.Match) => void): void; /** * Match first binding in the current context. * Keybindings ordered by a scope and by a registration order within the scope. * * FIXME: * This method should run very fast since it happens on each keystroke. We should reconsider how keybindings are stored. * It should be possible to look up full and partial keybinding for given key sequence for constant time using some kind of tree. * Such tree should not contain disabled keybindings and be invalidated whenever the registry is changed. */ matchKeybinding(keySequence: KeySequence, event?: KeyboardEvent): KeybindingRegistry.Match; /** * Returns true if the binding is usable * @param binding Binding to be checked */ protected isUsable(binding: Keybinding): boolean; /** * Return a new filtered array containing only the usable bindings among the input bindings * @param bindings Bindings to filter */ protected getUsableBindings<T extends Keybinding>(bindings: T[]): T[]; /** * Return true of string a pseudo-command id, in other words a command id * that has a special meaning and that we won't find in the command * registry. * * @param commandId commandId to test */ isPseudoCommand(commandId: string): boolean; /** * Sets a new keymap replacing all existing {@link common.Keybinding}s in the given scope. * @param scope the keybinding scope * @param bindings an array containing the new {@link common.Keybinding}s */ setKeymap(scope: KeybindingScope, bindings: Keybinding[]): void; protected readonly toResetKeymap: Map<KeybindingScope, Disposable>; /** * Reset keybindings for a specific scope * @param scope scope to reset the keybindings for */ resetKeybindingsForScope(scope: KeybindingScope): void; /** * Reset keybindings for all scopes(only leaves the default keybindings mapped) */ resetKeybindings(): void; /** * Get all {@link common.Keybinding}s for a {@link KeybindingScope}. * @returns an array of {@link common.ScopedKeybinding} * @param scope the keybinding scope to retrieve the {@link common.Keybinding}s for. */ getKeybindingsByScope(scope: KeybindingScope): ScopedKeybinding[]; } export declare namespace KeybindingRegistry { type Match = { kind: 'full' | 'partial'; binding: ScopedKeybinding; preventDefault?: boolean; stopPropagation?: boolean; } | undefined; class KeybindingsResult { full: ScopedKeybinding[]; partial: ScopedKeybinding[]; shadow: ScopedKeybinding[]; /** * Merge two results together inside `this` * * @param other the other KeybindingsResult to merge with * @return this */ merge(other: KeybindingsResult): KeybindingsResult; /** * Returns a new filtered KeybindingsResult * * @param fn callback filter on the results * @return filtered new result */ filter(fn: (binding: Keybinding) => boolean): KeybindingsResult; } } //# sourceMappingURL=keybinding.d.ts.map