UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

796 lines 34 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2017 TypeFox 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-only WITH Classpath-exception-2.0 // ***************************************************************************** var KeybindingRegistry_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.KeybindingRegistry = exports.KeybindingContexts = exports.KeybindingContext = exports.KeybindingContribution = exports.Keybinding = exports.KeybindingScope = void 0; const tslib_1 = require("tslib"); const inversify_1 = require("inversify"); const os_1 = require("../common/os"); const event_1 = require("../common/event"); const command_1 = require("../common/command"); const disposable_1 = require("../common/disposable"); const keys_1 = require("./keyboard/keys"); const keyboard_layout_service_1 = require("./keyboard/keyboard-layout-service"); const contribution_provider_1 = require("../common/contribution-provider"); const logger_1 = require("../common/logger"); const status_bar_1 = require("./status-bar/status-bar"); const context_key_service_1 = require("./context-key-service"); const core_preferences_1 = require("../common/core-preferences"); const common = require("../common/keybinding"); const nls_1 = require("../common/nls"); const ternary_search_tree_1 = require("../common/ternary-search-tree"); var KeybindingScope; (function (KeybindingScope) { KeybindingScope[KeybindingScope["DEFAULT"] = 0] = "DEFAULT"; KeybindingScope[KeybindingScope["USER"] = 1] = "USER"; KeybindingScope[KeybindingScope["WORKSPACE"] = 2] = "WORKSPACE"; KeybindingScope[KeybindingScope["END"] = 3] = "END"; })(KeybindingScope || (exports.KeybindingScope = KeybindingScope = {})); (function (KeybindingScope) { KeybindingScope.length = KeybindingScope.END - KeybindingScope.DEFAULT; })(KeybindingScope || (exports.KeybindingScope = KeybindingScope = {})); exports.Keybinding = common.Keybinding; exports.KeybindingContribution = Symbol('KeybindingContribution'); exports.KeybindingContext = Symbol('KeybindingContext'); var KeybindingContexts; (function (KeybindingContexts) { KeybindingContexts.NOOP_CONTEXT = { id: 'noop.keybinding.context', isEnabled: () => true }; KeybindingContexts.DEFAULT_CONTEXT = { id: 'default.keybinding.context', isEnabled: () => false }; })(KeybindingContexts || (exports.KeybindingContexts = KeybindingContexts = {})); let KeybindingRegistry = class KeybindingRegistry { constructor() { this.keySequence = []; this.contexts = {}; this.keymaps = [...Array(KeybindingScope.length)].map(() => []); this.keybindingsChanged = new event_1.Emitter(); this.toResetKeymap = new Map(); } static { KeybindingRegistry_1 = this; } static { this.PASSTHROUGH_PSEUDO_COMMAND = 'passthrough'; } async onStart() { await this.keyboardLayoutService.initialize(); this.keyboardLayoutService.onKeyboardLayoutChanged(newLayout => { this.clearResolvedKeybindings(); this.invalidateKeybindingTree(); this.keybindingsChanged.fire(undefined); }); this.registerContext(KeybindingContexts.NOOP_CONTEXT); this.registerContext(KeybindingContexts.DEFAULT_CONTEXT); this.registerContext(...this.contextProvider.getContributions()); for (const contribution of this.contributions.getContributions()) { contribution.registerKeybindings(this); } } /** * 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() { return this.keybindingsChanged.event; } /** * 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. */ registerContext(...contexts) { for (const context of contexts) { const { id } = context; if (this.contexts[id]) { this.logger.error(`A keybinding context with ID ${id} is already registered.`); } else { this.contexts[id] = context; } } } /** * Register a default keybinding to the registry. * * Keybindings registered later have higher priority during evaluation. * * @param binding the keybinding to be registered */ registerKeybinding(binding) { return this.doRegisterKeybinding(binding); } /** * Register multiple default keybindings to the registry * * @param bindings An array of keybinding to be registered */ registerKeybindings(...bindings) { return this.doRegisterKeybindings(bindings, KeybindingScope.DEFAULT); } unregisterKeybinding(arg) { const keymap = this.keymaps[KeybindingScope.DEFAULT]; const filter = command_1.Command.is(arg) ? ({ command }) => command === arg.id : ({ keybinding }) => exports.Keybinding.is(arg) ? keybinding === arg.keybinding : keybinding === arg; let removed = false; for (const binding of keymap.filter(filter)) { const idx = keymap.indexOf(binding); if (idx !== -1) { keymap.splice(idx, 1); removed = true; } } if (removed) { this.invalidateKeybindingTree(); } } doRegisterKeybindings(bindings, scope = KeybindingScope.DEFAULT) { const toDispose = new disposable_1.DisposableCollection(); for (const binding of bindings) { toDispose.push(this.doRegisterKeybinding(binding, scope)); } return toDispose; } doRegisterKeybinding(binding, scope = KeybindingScope.DEFAULT) { try { this.resolveKeybinding(binding); const scoped = Object.assign(binding, { scope }); this.insertBindingIntoScope(scoped, scope); this.invalidateKeybindingTree(); return disposable_1.Disposable.create(() => { const index = this.keymaps[scope].indexOf(scoped); if (index !== -1) { this.keymaps[scope].splice(index, 1); this.invalidateKeybindingTree(); } }); } catch (error) { this.logger.warn(`Could not register keybinding:\n ${common.Keybinding.stringify(binding)}\n${error}`); return disposable_1.Disposable.NULL; } } /** * 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) */ insertBindingIntoScope(item, scope) { const scopedKeymap = this.keymaps[scope]; const getNumberOfKeystrokes = (binding) => (binding.keybinding.trim().match(/\s/g)?.length ?? 0) + 1; const numberOfKeystrokesInBinding = getNumberOfKeystrokes(item); const indexOfFirstItemWithEqualStrokes = scopedKeymap.findIndex(existingBinding => getNumberOfKeystrokes(existingBinding) === numberOfKeystrokesInBinding); if (indexOfFirstItemWithEqualStrokes > -1) { scopedKeymap.splice(indexOfFirstItemWithEqualStrokes, 0, item); } else { scopedKeymap.push(item); } } /** * Ensure that the `resolved` property of the given binding is set by calling the KeyboardLayoutService. */ resolveKeybinding(binding) { if (!binding.resolved) { const sequence = keys_1.KeySequence.parse(binding.keybinding); binding.resolved = sequence.map(code => this.keyboardLayoutService.resolveKeyCode(code)); } return binding.resolved; } /** * 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. */ clearResolvedKeybindings() { for (let i = KeybindingScope.DEFAULT; i < KeybindingScope.END; i++) { const bindings = this.keymaps[i]; for (let j = 0; j < bindings.length; j++) { const binding = bindings[j]; binding.resolved = undefined; } } } /** * Invalidates the keybinding tree, causing it to be rebuilt on next access. */ invalidateKeybindingTree() { this.keybindingTree = undefined; } /** * Gets or builds the keybinding tree for efficient lookup. * The tree maps resolved key sequences to arrays of bindings sorted by scope (highest first). */ getKeybindingTree() { if (!this.keybindingTree) { this.keybindingTree = this.buildKeybindingTree(); } return this.keybindingTree; } /** * Builds a ternary search tree from all registered keybindings for efficient lookup. * Bindings are indexed by their resolved key sequence. * Bindings are stored in the order they should be checked: highest scope first, * and within the same scope, in the order they appear in keymaps (later-registered first). */ buildKeybindingTree() { const tree = ternary_search_tree_1.TernarySearchTree.forKeySequences(); // Process scopes from highest to lowest priority (WORKSPACE > USER > DEFAULT) // Within each scope, maintain the keymaps order (later-registered bindings are at the front) for (let scope = KeybindingScope.END - 1; scope >= KeybindingScope.DEFAULT; scope--) { for (const binding of this.keymaps[scope]) { try { const resolved = this.resolveKeybinding(binding); const existing = tree.get(resolved); if (existing) { // Append to maintain correct order: higher scopes first, then keymaps order within scope existing.push(binding); } else { tree.set(resolved, [binding]); } } catch (error) { // Skip bindings that can't be resolved this.logger.warn(`Could not resolve keybinding for tree: ${common.Keybinding.stringify(binding)}`); } } } return tree; } /** * 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, scope = KeybindingScope.USER) { const bindingKeySequence = this.resolveKeybinding(binding); const collisions = this.getKeySequenceCollisions(this.getUsableBindings(this.keymaps[scope]), bindingKeySequence) .filter(b => b.context === binding.context && !b.when && !binding.when); if (collisions.full.length > 0) { return true; } if (collisions.partial.length > 0) { return true; } if (collisions.shadow.length > 0) { return true; } return false; } /** * 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, separator = ' ', asciiOnly = false) { const bindingKeySequence = this.resolveKeybinding(keybinding); return this.acceleratorForSequence(bindingKeySequence, separator, asciiOnly); } /** * 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, separator = ' ', asciiOnly = false) { return keySequence.map(keyCode => this.acceleratorForKeyCode(keyCode, separator, asciiOnly)); } /** * 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 * @param asciiOnly if `true`, no special characters will be substituted into the string returned. Ensures correct keyboard shortcuts in Electron menus. */ acceleratorForKeyCode(keyCode, separator = ' ', asciiOnly = false) { return this.componentsForKeyCode(keyCode, asciiOnly).join(separator); } componentsForKeyCode(keyCode, asciiOnly = false) { const keyCodeResult = []; const useSymbols = os_1.isOSX && !asciiOnly; if (keyCode.meta && os_1.isOSX) { keyCodeResult.push(useSymbols ? '⌘' : 'Cmd'); } if (keyCode.ctrl) { keyCodeResult.push(useSymbols ? '⌃' : 'Ctrl'); } if (keyCode.alt) { keyCodeResult.push(useSymbols ? '⌥' : 'Alt'); } if (keyCode.shift) { keyCodeResult.push(useSymbols ? '⇧' : 'Shift'); } if (keyCode.key) { keyCodeResult.push(this.acceleratorForKey(keyCode.key, asciiOnly)); } return keyCodeResult; } /** * @param asciiOnly if `true`, no special characters will be substituted into the string returned. Ensures correct keyboard shortcuts in Electron menus. * * Return a user visible representation of a single key. */ acceleratorForKey(key, asciiOnly = false) { if (os_1.isOSX && !asciiOnly) { if (key === keys_1.Key.ARROW_LEFT) { return '←'; } if (key === keys_1.Key.ARROW_RIGHT) { return '→'; } if (key === keys_1.Key.ARROW_UP) { return '↑'; } if (key === keys_1.Key.ARROW_DOWN) { return '↓'; } } const keyString = this.keyboardLayoutService.getKeyboardCharacter(key); if (key.keyCode >= keys_1.Key.KEY_A.keyCode && key.keyCode <= keys_1.Key.KEY_Z.keyCode || key.keyCode >= keys_1.Key.F1.keyCode && key.keyCode <= keys_1.Key.F24.keyCode) { return keyString.toUpperCase(); } else if (keyString.length > 1) { return keyString.charAt(0).toUpperCase() + keyString.slice(1); } else { return keyString; } } /** * Finds collisions for a key sequence inside a list of bindings (error-free) * * @param bindings the reference bindings * @param candidate the sequence to match */ getKeySequenceCollisions(bindings, candidate) { const result = new KeybindingRegistry_1.KeybindingsResult(); for (const binding of bindings) { try { const bindingKeySequence = this.resolveKeybinding(binding); const compareResult = keys_1.KeySequence.compare(candidate, bindingKeySequence); switch (compareResult) { case keys_1.KeySequence.CompareResult.FULL: { result.full.push(binding); break; } case keys_1.KeySequence.CompareResult.PARTIAL: { result.partial.push(binding); break; } case keys_1.KeySequence.CompareResult.SHADOW: { result.shadow.push(binding); break; } } } catch (error) { this.logger.warn(error); } } return result; } /** * 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) { const result = []; const disabledBindings = new Set(); for (let scope = KeybindingScope.END - 1; scope >= KeybindingScope.DEFAULT; scope--) { this.keymaps[scope].forEach(binding => { if (binding.command?.startsWith('-')) { disabledBindings.add(JSON.stringify({ command: binding.command.substring(1), binding: binding.keybinding, context: binding.context, when: binding.when })); } else { const command = this.commandRegistry.getCommand(binding.command); if (command && command.id === commandId && !disabledBindings.has(JSON.stringify({ command: binding.command, binding: binding.keybinding, context: binding.context, when: binding.when }))) { result.push({ ...binding, scope }); } } }); } return result; } isActive(binding) { /* Pseudo commands like "passthrough" are always active (and not found in the command registry). */ if (this.isPseudoCommand(binding.command)) { return true; } const command = this.commandRegistry.getCommand(binding.command); return !!command && !!this.commandRegistry.getActiveHandler(command.id); } /** * Tries to execute a keybinding. * * @param binding to execute * @param event keyboard event. */ executeKeyBinding(binding, event) { if (this.isPseudoCommand(binding.command)) { /* Don't do anything, let the event propagate. */ } else { const command = this.commandRegistry.getCommand(binding.command); if (command) { if (this.commandRegistry.isEnabled(binding.command, binding.args)) { this.commandRegistry.executeCommand(binding.command, binding.args) .catch(e => console.error('Failed to execute command:', e)); } /* Note that if a keybinding is in context but the command is not active we still stop the processing here. */ event.preventDefault(); event.stopPropagation(); } } } /** * Only execute if it has no context (global context) or if we're in that context. */ isEnabled(binding, event) { return this.isEnabledInScope(binding, event.target); } isEnabledInScope(binding, target) { const context = binding.context && this.contexts[binding.context]; if (binding.command && (!this.isPseudoCommand(binding.command) && !this.commandRegistry.isEnabled(binding.command, binding.args))) { return false; } if (context && !context.isEnabled(binding)) { return false; } if (binding.when && !this.whenContextService.match(binding.when, target)) { return false; } return true; } dispatchCommand(id, target) { const keybindings = this.getKeybindingsForCommand(id); if (keybindings.length) { for (const keyCode of this.resolveKeybinding(keybindings[0])) { this.dispatchKeyDown(keyCode, target); } } } dispatchKeyDown(input, target = document.activeElement || window) { const eventInit = this.asKeyboardEventInit(input); const emulatedKeyboardEvent = new KeyboardEvent('keydown', eventInit); target.dispatchEvent(emulatedKeyboardEvent); } asKeyboardEventInit(input) { if (typeof input === 'string') { return this.asKeyboardEventInit(keys_1.KeyCode.createKeyCode(input)); } if (input instanceof keys_1.KeyCode) { return { metaKey: input.meta, shiftKey: input.shift, altKey: input.alt, ctrlKey: input.ctrl, code: input.key && input.key.code, key: (input && input.character) || (input.key && input.key.code), keyCode: input.key && input.key.keyCode }; } return input; } registerEventListeners(win) { /* vvv HOTFIX begin vvv * * This is a hotfix against issues eclipse/theia#6459 and gitpod-io/gitpod#875 . * It should be reverted after Theia was updated to the newer Monaco. */ let inComposition = false; const compositionStart = () => { inComposition = true; }; win.document.addEventListener('compositionstart', compositionStart); const compositionEnd = () => { inComposition = false; }; win.document.addEventListener('compositionend', compositionEnd); const keydown = (event) => { if (inComposition !== true) { this.run(event); } }; win.document.addEventListener('keydown', keydown, true); return disposable_1.Disposable.create(() => { win.document.removeEventListener('compositionstart', compositionStart); win.document.removeEventListener('compositionend', compositionEnd); win.document.removeEventListener('keydown', keydown); }); } /** * Run the command matching to the given keyboard event. */ run(event) { if (event.defaultPrevented) { return; } const eventDispatch = this.corePreferences['keyboard.dispatch']; const keyCode = keys_1.KeyCode.createKeyCode(event, eventDispatch); /* Keycode is only a modifier, next keycode will be modifier + key. Ignore this one. */ if (keyCode.isModifierOnly()) { return; } this.keyboardLayoutService.validateKeyCode(keyCode); this.keySequence.push(keyCode); const match = this.matchKeybinding(this.keySequence, event); if (match && match.kind === 'partial') { /* Accumulate the keysequence */ event.preventDefault(); event.stopPropagation(); this.statusBar.setElement('keybinding-status', { text: nls_1.nls.localize('theia/core/keybindingStatus', '{0} was pressed, waiting for more keys', `(${this.acceleratorForSequence(this.keySequence, '+')})`), alignment: status_bar_1.StatusBarAlignment.LEFT, priority: 2 }); } else { if (match && match.kind === 'full') { this.executeKeyBinding(match.binding, event); } this.keySequence = []; this.statusBar.removeElement('keybinding-status'); } } /** * Match first binding in the current context. * Keybindings ordered by a scope and by a registration order within the scope. * * Uses a ternary search tree for efficient O(log n) lookup instead of O(n) iteration. * The tree is lazily built and invalidated when keybindings change. * * When multiple bindings match, bindings that use context keys local to the focused * element are given priority over bindings that only use global context keys. */ matchKeybinding(keySequence, event) { let disabled; const isEnabled = (binding) => { const { command, context, when, keybinding } = binding; if (!this.isUsable(binding)) { disabled = disabled || new Set(); disabled.add(JSON.stringify({ command: command.substring(1), context, when, keybinding })); return false; } if (event && !this.isEnabled(binding, event)) { return false; } return !disabled?.has(JSON.stringify({ command, context, when, keybinding })); }; const tree = this.getKeybindingTree(); // Check for exact (FULL) matches first const fullMatches = tree.get(keySequence); if (fullMatches) { // Collect all enabled bindings const enabledBindings = fullMatches.filter(isEnabled); if (enabledBindings.length > 0) { // If we have multiple enabled bindings, prioritize those using local context keys const selectedBinding = this.selectBindingByLocalContext(enabledBindings, event); return { kind: 'full', binding: selectedBinding }; } } // Check for PARTIAL matches (bindings that start with keySequence but are longer) const partialIterator = tree.findSuperstr(keySequence); if (partialIterator) { // Collect all partial match bindings and sort by scope const partialBindings = []; let result = partialIterator.next(); while (!result.done) { partialBindings.push(...result.value); result = partialIterator.next(); } // Sort by scope descending (highest scope first) partialBindings.sort((a, b) => b.scope - a.scope); // Collect all enabled bindings const enabledBindings = partialBindings.filter(isEnabled); if (enabledBindings.length > 0) { // If we have multiple enabled bindings, prioritize those using local context keys const selectedBinding = this.selectBindingByLocalContext(enabledBindings, event); return { kind: 'partial', binding: selectedBinding }; } } return undefined; } /** * Selects the most appropriate binding from a list of enabled bindings. * Prioritizes bindings whose `when` clause uses context keys that are locally * defined at the focused element, as these are more specific to the user's * current focus context. * * @param enabledBindings Array of enabled bindings to choose from (must not be empty) * @param event Optional keyboard event to get the focused element from * @returns The selected binding */ selectBindingByLocalContext(enabledBindings, event) { if (enabledBindings.length === 1 || !event) { return enabledBindings[0]; } const target = event.target; if (!(target instanceof HTMLElement)) { return enabledBindings[0]; } // Get context keys that are locally defined at the focused element const localKeys = this.whenContextService.getLocalContextKeys(target); if (localKeys.size === 0) { return enabledBindings[0]; } // Find bindings whose 'when' clause uses any local context key // These are considered more specific to the focused element for (const binding of enabledBindings) { if (binding.when) { const usedKeys = this.whenContextService.parseKeys(binding.when); if (usedKeys) { for (const key of usedKeys) { if (localKeys.has(key)) { return binding; } } } } } // No binding uses local context keys, fall back to first enabled binding return enabledBindings[0]; } /** * Returns true if the binding is usable * @param binding Binding to be checked */ isUsable(binding) { return binding.command.charAt(0) !== '-'; } /** * Return a new filtered array containing only the usable bindings among the input bindings * @param bindings Bindings to filter */ getUsableBindings(bindings) { return bindings.filter(binding => this.isUsable(binding)); } /** * 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) { return commandId === KeybindingRegistry_1.PASSTHROUGH_PSEUDO_COMMAND; } /** * 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, bindings) { this.resetKeybindingsForScope(scope); this.toResetKeymap.set(scope, this.doRegisterKeybindings(bindings, scope)); this.invalidateKeybindingTree(); this.keybindingsChanged.fire(undefined); } /** * Reset keybindings for a specific scope * @param scope scope to reset the keybindings for */ resetKeybindingsForScope(scope) { const toReset = this.toResetKeymap.get(scope); if (toReset) { toReset.dispose(); } } /** * Reset keybindings for all scopes(only leaves the default keybindings mapped) */ resetKeybindings() { for (let i = KeybindingScope.DEFAULT + 1; i < KeybindingScope.END; i++) { this.keymaps[i] = []; } this.invalidateKeybindingTree(); } /** * 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) { return this.keymaps[scope]; } }; exports.KeybindingRegistry = KeybindingRegistry; tslib_1.__decorate([ (0, inversify_1.inject)(core_preferences_1.CorePreferences), tslib_1.__metadata("design:type", Object) ], KeybindingRegistry.prototype, "corePreferences", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(keyboard_layout_service_1.KeyboardLayoutService), tslib_1.__metadata("design:type", keyboard_layout_service_1.KeyboardLayoutService) ], KeybindingRegistry.prototype, "keyboardLayoutService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(contribution_provider_1.ContributionProvider), (0, inversify_1.named)(exports.KeybindingContext), tslib_1.__metadata("design:type", Object) ], KeybindingRegistry.prototype, "contextProvider", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(command_1.CommandRegistry), tslib_1.__metadata("design:type", command_1.CommandRegistry) ], KeybindingRegistry.prototype, "commandRegistry", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(contribution_provider_1.ContributionProvider), (0, inversify_1.named)(exports.KeybindingContribution), tslib_1.__metadata("design:type", Object) ], KeybindingRegistry.prototype, "contributions", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(status_bar_1.StatusBar), tslib_1.__metadata("design:type", Object) ], KeybindingRegistry.prototype, "statusBar", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(logger_1.ILogger), tslib_1.__metadata("design:type", Object) ], KeybindingRegistry.prototype, "logger", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(context_key_service_1.ContextKeyService), tslib_1.__metadata("design:type", Object) ], KeybindingRegistry.prototype, "whenContextService", void 0); exports.KeybindingRegistry = KeybindingRegistry = KeybindingRegistry_1 = tslib_1.__decorate([ (0, inversify_1.injectable)() ], KeybindingRegistry); (function (KeybindingRegistry) { class KeybindingsResult { constructor() { this.full = []; this.partial = []; this.shadow = []; } /** * Merge two results together inside `this` * * @param other the other KeybindingsResult to merge with * @return this */ merge(other) { this.full.push(...other.full); this.partial.push(...other.partial); this.shadow.push(...other.shadow); return this; } /** * Returns a new filtered KeybindingsResult * * @param fn callback filter on the results * @return filtered new result */ filter(fn) { const result = new KeybindingsResult(); result.full = this.full.filter(fn); result.partial = this.partial.filter(fn); result.shadow = this.shadow.filter(fn); return result; } } KeybindingRegistry.KeybindingsResult = KeybindingsResult; })(KeybindingRegistry || (exports.KeybindingRegistry = KeybindingRegistry = {})); //# sourceMappingURL=keybinding.js.map