UNPKG

@theia/core

Version:

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

457 lines • 22.7 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2017 Ericsson 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 // ***************************************************************************** var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsdom_1 = require("./test/jsdom"); let disableJSDOM = (0, jsdom_1.enableJSDOM)(); const frontend_application_config_provider_1 = require("./frontend-application-config-provider"); frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({}); const inversify_1 = require("inversify"); const contribution_provider_1 = require("../common/contribution-provider"); const keyboard_layout_provider_1 = require("../common/keyboard/keyboard-layout-provider"); const logger_1 = require("../common/logger"); const keybinding_1 = require("./keybinding"); const keys_1 = require("./keyboard/keys"); const keyboard_layout_service_1 = require("./keyboard/keyboard-layout-service"); const command_1 = require("../common/command"); const label_parser_1 = require("./label-parser"); const mock_logger_1 = require("../common/test/mock-logger"); const frontend_application_state_1 = require("./frontend-application-state"); const context_key_service_1 = require("./context-key-service"); const core_preferences_1 = require("./core-preferences"); const os = require("../common/os"); const chai = require("chai"); const sinon = require("sinon"); const event_1 = require("../common/event"); const frontend_application_bindings_1 = require("./frontend-application-bindings"); const markdown_renderer_1 = require("./markdown-rendering/markdown-renderer"); const status_bar_1 = require("./status-bar"); disableJSDOM(); const expect = chai.expect; let keybindingRegistry; let commandRegistry; let testContainer; let stub; before(async () => { disableJSDOM = (0, jsdom_1.enableJSDOM)(); testContainer = new inversify_1.Container(); const module = new inversify_1.ContainerModule((bind, unbind, isBound, rebind) => { /* Mock logger binding*/ bind(logger_1.ILogger).to(mock_logger_1.MockLogger); bind(keyboard_layout_service_1.KeyboardLayoutService).toSelf().inSingletonScope(); bind(MockKeyboardLayoutProvider).toSelf().inSingletonScope(); bind(keyboard_layout_provider_1.KeyboardLayoutProvider).toService(MockKeyboardLayoutProvider); bind(MockKeyboardLayoutChangeNotifier).toSelf().inSingletonScope(); bind(keyboard_layout_provider_1.KeyboardLayoutChangeNotifier).toService(MockKeyboardLayoutChangeNotifier); (0, contribution_provider_1.bindContributionProvider)(bind, keybinding_1.KeybindingContext); bind(command_1.CommandRegistry).toSelf().inSingletonScope(); (0, contribution_provider_1.bindContributionProvider)(bind, command_1.CommandContribution); bind(keybinding_1.KeybindingRegistry).toSelf(); (0, contribution_provider_1.bindContributionProvider)(bind, keybinding_1.KeybindingContribution); bind(TestContribution).toSelf().inSingletonScope(); [command_1.CommandContribution, keybinding_1.KeybindingContribution].forEach(serviceIdentifier => bind(serviceIdentifier).toService(TestContribution)); bind(keybinding_1.KeybindingContext).toConstantValue({ id: 'testContext', isEnabled(arg) { return true; } }); bind(status_bar_1.StatusBar).toConstantValue({}); bind(markdown_renderer_1.MarkdownRendererImpl).toSelf().inSingletonScope(); bind(markdown_renderer_1.MarkdownRenderer).toService(markdown_renderer_1.MarkdownRendererImpl); bind(markdown_renderer_1.MarkdownRendererFactory).toFactory(({ container }) => container.get(markdown_renderer_1.MarkdownRenderer)); bind(command_1.CommandService).toService(command_1.CommandRegistry); bind(label_parser_1.LabelParser).toSelf().inSingletonScope(); bind(context_key_service_1.ContextKeyService).to(context_key_service_1.ContextKeyServiceDummyImpl).inSingletonScope(); bind(frontend_application_state_1.FrontendApplicationStateService).toSelf().inSingletonScope(); bind(core_preferences_1.CorePreferences).toConstantValue({}); (0, frontend_application_bindings_1.bindPreferenceService)(bind); }); testContainer.load(module); commandRegistry = testContainer.get(command_1.CommandRegistry); commandRegistry.onStart(); }); after(() => { disableJSDOM(); }); beforeEach(async () => { stub = sinon.stub(os, 'isOSX').value(false); keybindingRegistry = testContainer.get(keybinding_1.KeybindingRegistry); await keybindingRegistry.onStart(); }); afterEach(() => { stub.restore(); }); describe('keybindings', () => { it('should register the default keybindings', () => { const keybinding = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND.id); expect(keybinding).is.not.undefined; const keybinding2 = keybindingRegistry.getKeybindingsForCommand('undefined.command'); expect(keybinding2.length).is.equal(0); }); it('should set a keymap', () => { const keybindings = [{ command: TEST_COMMAND.id, keybinding: 'ctrl+c' }]; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindings); const bindings = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND.id); if (bindings) { const keyCode = keys_1.KeyCode.parse(bindings[0].keybinding); expect(keyCode.key).to.be.equal(keys_1.Key.KEY_C); expect(keyCode.ctrl).to.be.true; } }); it('should reset to default in case of invalid keybinding', () => { const keybindings = [{ command: TEST_COMMAND.id, keybinding: 'ctrl+invalid' }]; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindings); const bindings = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND.id); if (bindings) { const keyCode = keys_1.KeyCode.parse(bindings[0].keybinding); expect(keyCode.key).to.be.equal(keys_1.Key.KEY_A); expect(keyCode.ctrl).to.be.true; } }); it('should remove all disabled keybindings from a command that has multiple keybindings', () => { const keybindings = [{ command: TEST_COMMAND2.id, keybinding: 'F3' }, { command: '-' + TEST_COMMAND2.id, context: 'testContext', keybinding: 'ctrl+f1' }, ]; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindings); const bindings = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND2.id); if (bindings) { // a USER one and a DEFAULT one expect(bindings.length).to.be.equal(2); const keyCode = keys_1.KeyCode.parse(bindings[0].keybinding); expect(keyCode.key).to.be.equal(keys_1.Key.F3); expect(keyCode.ctrl).to.be.false; const keyCode2 = keys_1.KeyCode.parse(bindings[1].keybinding); expect(keyCode2.key).to.be.equal(keys_1.Key.F2); expect(keyCode2.ctrl).to.be.true; } }); it('should register a keybinding', () => { const keybinding = { command: TEST_COMMAND2.id, keybinding: 'F5' }; expect(isKeyBindingRegistered(keybinding)).to.be.false; keybindingRegistry.registerKeybinding(keybinding); expect(isKeyBindingRegistered(keybinding)).to.be.true; }); it('should unregister all keybindings from a specific command', () => { const otherKeybinding = { command: TEST_COMMAND.id, keybinding: 'F4' }; keybindingRegistry.registerKeybinding(otherKeybinding); expect(isKeyBindingRegistered(otherKeybinding)).to.be.true; const keybinding = { command: TEST_COMMAND2.id, keybinding: 'F5' }; const keybinding2 = { command: TEST_COMMAND2.id, keybinding: 'F6' }; keybindingRegistry.registerKeybinding(keybinding); keybindingRegistry.registerKeybinding(keybinding2); expect(isKeyBindingRegistered(keybinding)).to.be.true; expect(isKeyBindingRegistered(keybinding2)).to.be.true; keybindingRegistry.unregisterKeybinding(TEST_COMMAND2); expect(isKeyBindingRegistered(keybinding)).to.be.false; expect(isKeyBindingRegistered(keybinding2)).to.be.false; const bindingsAfterUnregister = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND2.id); expect(bindingsAfterUnregister).not.to.be.undefined; expect(bindingsAfterUnregister.length).to.be.equal(0); expect(isKeyBindingRegistered(otherKeybinding)).to.be.true; }); it('should unregister a specific keybinding', () => { const otherKeybinding = { command: TEST_COMMAND2.id, keybinding: 'F4' }; keybindingRegistry.registerKeybinding(otherKeybinding); const keybinding = { command: TEST_COMMAND2.id, keybinding: 'F5' }; keybindingRegistry.registerKeybinding(keybinding); expect(isKeyBindingRegistered(otherKeybinding)).to.be.true; expect(isKeyBindingRegistered(keybinding)).to.be.true; keybindingRegistry.unregisterKeybinding(keybinding); expect(isKeyBindingRegistered(keybinding)).to.be.false; expect(isKeyBindingRegistered(otherKeybinding)).to.be.true; }); it('should unregister a specific key', () => { const otherKeybinding = { command: TEST_COMMAND.id, keybinding: 'F4' }; keybindingRegistry.registerKeybinding(otherKeybinding); const testKey = 'F5'; const keybinding = { command: TEST_COMMAND2.id, keybinding: testKey }; const keybinding2 = { command: TEST_COMMAND.id, keybinding: testKey }; keybindingRegistry.registerKeybinding(keybinding); keybindingRegistry.registerKeybinding(keybinding2); expect(isKeyBindingRegistered(otherKeybinding)).to.be.true; expect(isKeyBindingRegistered(keybinding)).to.be.true; expect(isKeyBindingRegistered(keybinding2)).to.be.true; keybindingRegistry.unregisterKeybinding(testKey); expect(isKeyBindingRegistered(otherKeybinding)).to.be.true; expect(isKeyBindingRegistered(keybinding)).to.be.false; expect(isKeyBindingRegistered(keybinding2)).to.be.false; }); it('should register a correct keybinding, then default back to the original for a wrong one after', () => { let keybindings = [{ command: TEST_COMMAND.id, keybinding: 'ctrl+c' }]; // Get default binding const keystroke = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND.id); // Set correct new binding keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindings); const bindings = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND.id); if (bindings) { const keyCode = keys_1.KeyCode.parse(bindings[0].keybinding); expect(keyCode.key).to.be.equal(keys_1.Key.KEY_C); expect(keyCode.ctrl).to.be.true; } // Set invalid binding keybindings = [{ command: TEST_COMMAND.id, keybinding: 'ControlLeft+Invalid' }]; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindings); const defaultBindings = keybindingRegistry.getKeybindingsForCommand(TEST_COMMAND.id); if (defaultBindings) { if (keystroke) { const keyCode = keys_1.KeyCode.parse(defaultBindings[0].keybinding); const keyStrokeCode = keys_1.KeyCode.parse(keystroke[0].keybinding); expect(keyCode.key).to.be.equal(keyStrokeCode.key); } } }); it('should only return the more specific keybindings when a keystroke is entered', () => { const keybindingsUser = [{ command: TEST_COMMAND.id, keybinding: 'ctrl+b' }]; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindingsUser); const keybindingsSpecific = [{ command: TEST_COMMAND.id, keybinding: 'ctrl+c' }]; const validKeyCode = keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_C, modifiers: [keys_1.KeyModifier.CtrlCmd] }); keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.WORKSPACE, keybindingsSpecific); let match = keybindingRegistry.matchKeybinding([keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_A, modifiers: [keys_1.KeyModifier.CtrlCmd] })]); expect(match && match.kind).to.be.equal('full'); match = keybindingRegistry.matchKeybinding([keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_B, modifiers: [keys_1.KeyModifier.CtrlCmd] })]); expect(match && match.kind).to.be.equal('full'); match = keybindingRegistry.matchKeybinding([keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_C, modifiers: [keys_1.KeyModifier.CtrlCmd] })]); const keyCode = match && keys_1.KeyCode.parse(match.binding.keybinding); expect(keyCode === null || keyCode === void 0 ? void 0 : keyCode.key).to.be.equal(validKeyCode.key); }); it('should return partial keybinding matches', () => { const keybindingsUser = [{ command: TEST_COMMAND.id, keybinding: 'ctrlcmd+x t' }]; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, keybindingsUser); const validKeyCodes = []; validKeyCodes.push(keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_C, modifiers: [keys_1.KeyModifier.CtrlCmd] })); validKeyCodes.push(keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_T })); const match = keybindingRegistry.matchKeybinding(keys_1.KeySequence.parse('ctrlcmd+x')); expect(match && match.kind).to.be.equal('partial'); }); it('should possible to override keybinding', () => { const overriddenKeybinding = 'ctrlcmd+b a'; const command = TEST_COMMAND_SHADOW.id; const keybindingShadowing = [ { command, keybinding: overriddenKeybinding }, { command, keybinding: 'ctrlcmd+b' } ]; keybindingRegistry.registerKeybindings(...keybindingShadowing); const bindings = keybindingRegistry.getKeybindingsForCommand(command); expect(bindings.length).to.be.equal(2); expect(bindings[0].keybinding).to.be.equal('ctrlcmd+b'); expect(bindings[1].keybinding).to.be.equal(overriddenKeybinding); }); it('overridden bindings should be returned last', () => { var _a, _b, _c; const keyCode = keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_A, modifiers: [keys_1.KeyModifier.Shift] }); const overriddenDefaultBinding = { keybinding: keyCode.toString(), command: 'test.overridden-default-command' }; const defaultBinding = { keybinding: keyCode.toString(), command: 'test.default-command' }; const userBinding = { keybinding: keyCode.toString(), command: 'test.user-command' }; const workspaceBinding = { keybinding: keyCode.toString(), command: 'test.workspace-command' }; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.DEFAULT, [overriddenDefaultBinding, defaultBinding]); keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, [userBinding]); keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.WORKSPACE, [workspaceBinding]); // now WORKSPACE bindings are overriding the other scopes let match = keybindingRegistry.matchKeybinding([keyCode]); expect(match === null || match === void 0 ? void 0 : match.kind).to.be.equal('full'); expect((_a = match === null || match === void 0 ? void 0 : match.binding) === null || _a === void 0 ? void 0 : _a.command).to.be.equal(workspaceBinding.command); keybindingRegistry.resetKeybindingsForScope(keybinding_1.KeybindingScope.WORKSPACE); // now it should find USER bindings match = keybindingRegistry.matchKeybinding([keyCode]); expect(match === null || match === void 0 ? void 0 : match.kind).to.be.equal('full'); expect((_b = match === null || match === void 0 ? void 0 : match.binding) === null || _b === void 0 ? void 0 : _b.command).to.be.equal(userBinding.command); keybindingRegistry.resetKeybindingsForScope(keybinding_1.KeybindingScope.USER); // and finally it should fallback to DEFAULT bindings. match = keybindingRegistry.matchKeybinding([keyCode]); expect(match === null || match === void 0 ? void 0 : match.kind).to.be.equal('full'); expect((_c = match === null || match === void 0 ? void 0 : match.binding) === null || _c === void 0 ? void 0 : _c.command).to.be.equal(defaultBinding.command); keybindingRegistry.resetKeybindingsForScope(keybinding_1.KeybindingScope.DEFAULT); // now the registry should be empty match = keybindingRegistry.matchKeybinding([keyCode]); expect(match).to.be.undefined; }); it('should not match disabled keybindings', () => { var _a, _b; const keyCode = keys_1.KeyCode.createKeyCode({ first: keys_1.Key.KEY_A, modifiers: [keys_1.KeyModifier.Shift] }); const defaultBinding = { keybinding: keyCode.toString(), command: 'test.workspace-command' }; const disableDefaultBinding = { keybinding: keyCode.toString(), command: '-test.workspace-command' }; keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.DEFAULT, [defaultBinding]); let match = keybindingRegistry.matchKeybinding([keyCode]); expect(match === null || match === void 0 ? void 0 : match.kind).to.be.equal('full'); expect((_a = match === null || match === void 0 ? void 0 : match.binding) === null || _a === void 0 ? void 0 : _a.command).to.be.equal(defaultBinding.command); keybindingRegistry.setKeymap(keybinding_1.KeybindingScope.USER, [disableDefaultBinding]); match = keybindingRegistry.matchKeybinding([keyCode]); expect(match).to.be.undefined; keybindingRegistry.resetKeybindingsForScope(keybinding_1.KeybindingScope.USER); match = keybindingRegistry.matchKeybinding([keyCode]); expect(match === null || match === void 0 ? void 0 : match.kind).to.be.equal('full'); expect((_b = match === null || match === void 0 ? void 0 : match.binding) === null || _b === void 0 ? void 0 : _b.command).to.be.equal(defaultBinding.command); }); }); const TEST_COMMAND = { id: 'test.command' }; const TEST_COMMAND2 = { id: 'test.command2' }; const TEST_COMMAND_SHADOW = { id: 'test.command-shadow' }; let MockKeyboardLayoutProvider = class MockKeyboardLayoutProvider { getNativeLayout() { return Promise.resolve({ info: { id: 'mock', lang: 'en' }, mapping: {} }); } }; MockKeyboardLayoutProvider = __decorate([ (0, inversify_1.injectable)() ], MockKeyboardLayoutProvider); let MockKeyboardLayoutChangeNotifier = class MockKeyboardLayoutChangeNotifier { constructor() { this.emitter = new event_1.Emitter(); } get onDidChangeNativeLayout() { return this.emitter.event; } }; MockKeyboardLayoutChangeNotifier = __decorate([ (0, inversify_1.injectable)() ], MockKeyboardLayoutChangeNotifier); let TestContribution = class TestContribution { registerCommands(commands) { commands.registerCommand(TEST_COMMAND); commands.registerCommand(TEST_COMMAND2); commands.registerCommand(TEST_COMMAND_SHADOW); } registerKeybindings(keybindings) { [{ command: TEST_COMMAND.id, context: 'testContext', keybinding: 'ctrl+a' }, { command: TEST_COMMAND2.id, context: 'testContext', keybinding: 'ctrl+f1' }, { command: TEST_COMMAND2.id, context: 'testContext', keybinding: 'ctrl+f2' }, ].forEach(binding => { keybindings.registerKeybinding(binding); }); } }; TestContribution = __decorate([ (0, inversify_1.injectable)() ], TestContribution); function isKeyBindingRegistered(keybinding) { const bindings = keybindingRegistry.getKeybindingsForCommand(keybinding.command); expect(bindings).not.to.be.undefined; let keyBindingFound = false; bindings.forEach((value) => { if (value.command === keybinding.command && value.keybinding === keybinding.keybinding) { keyBindingFound = true; } }); return keyBindingFound; } //# sourceMappingURL=keybinding.spec.js.map