@difizen/mana-core
Version:
333 lines • 14.6 kB
TypeScript
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