@theia/core 1.77.0-next.49 → 1.77.0-next.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/breadcrumbs/breadcrumbs-renderer.js +1 -1
- package/lib/browser/breadcrumbs/breadcrumbs-renderer.js.map +1 -1
- package/lib/browser/catalog.json +81 -0
- package/lib/browser/color-application-contribution.d.ts +1 -1
- package/lib/browser/color-application-contribution.d.ts.map +1 -1
- package/lib/browser/color-registry.d.ts +1 -1
- package/lib/browser/color-registry.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.d.ts +6 -1
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +20 -3
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/common-frontend-contribution.spec.js +2 -1
- package/lib/browser/common-frontend-contribution.spec.js.map +1 -1
- package/lib/browser/components/card.d.ts +1 -1
- package/lib/browser/components/card.d.ts.map +1 -1
- package/lib/browser/components/card.js +3 -2
- package/lib/browser/components/card.js.map +1 -1
- package/lib/browser/connection-status-service.spec.js +2 -1
- package/lib/browser/connection-status-service.spec.js.map +1 -1
- package/lib/browser/context-key-service.d.ts +11 -0
- package/lib/browser/context-key-service.d.ts.map +1 -1
- package/lib/browser/context-key-service.js +7 -0
- package/lib/browser/context-key-service.js.map +1 -1
- package/lib/browser/decorations-service.spec.js +2 -1
- package/lib/browser/decorations-service.spec.js.map +1 -1
- package/lib/browser/dialogs/react-dialog.spec.js +2 -1
- package/lib/browser/dialogs/react-dialog.spec.js.map +1 -1
- package/lib/browser/diff-uris.js +1 -1
- package/lib/browser/diff-uris.js.map +1 -1
- package/lib/browser/endpoint.js +2 -1
- package/lib/browser/endpoint.js.map +1 -1
- package/lib/browser/endpoint.spec.js +2 -1
- package/lib/browser/endpoint.spec.js.map +1 -1
- package/lib/browser/keybinding.d.ts +95 -17
- package/lib/browser/keybinding.d.ts.map +1 -1
- package/lib/browser/keybinding.js +406 -76
- package/lib/browser/keybinding.js.map +1 -1
- package/lib/browser/keybinding.spec.js +789 -33
- package/lib/browser/keybinding.spec.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js +1 -1
- package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts +3 -4
- package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.js +3 -3
- package/lib/browser/keyboard/browser-keyboard-layout-provider.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js +42 -3
- package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js.map +1 -1
- package/lib/browser/keyboard/keyboard-layout-service.d.ts +67 -15
- package/lib/browser/keyboard/keyboard-layout-service.d.ts.map +1 -1
- package/lib/browser/keyboard/keyboard-layout-service.js +309 -98
- package/lib/browser/keyboard/keyboard-layout-service.js.map +1 -1
- package/lib/browser/keyboard/keyboard-layout-service.spec.js +541 -15
- package/lib/browser/keyboard/keyboard-layout-service.spec.js.map +1 -1
- package/lib/browser/keyboard/keys.spec.js +187 -3
- package/lib/browser/keyboard/keys.spec.js.map +1 -1
- package/lib/browser/label-provider.d.ts.map +1 -1
- package/lib/browser/label-provider.js +5 -3
- package/lib/browser/label-provider.js.map +1 -1
- package/lib/browser/label-provider.spec.js +2 -1
- package/lib/browser/label-provider.spec.js.map +1 -1
- package/lib/browser/markdown-rendering/markdown-link-handler.js +2 -1
- package/lib/browser/markdown-rendering/markdown-link-handler.js.map +1 -1
- package/lib/browser/markdown-rendering/markdown-renderer.d.ts +1 -1
- package/lib/browser/markdown-rendering/markdown-renderer.d.ts.map +1 -1
- package/lib/browser/markdown-rendering/markdown-renderer.js +4 -4
- package/lib/browser/markdown-rendering/markdown-renderer.js.map +1 -1
- package/lib/browser/markdown-rendering/markdown.js +2 -1
- package/lib/browser/markdown-rendering/markdown.js.map +1 -1
- package/lib/browser/markdown-rendering/markdown.spec.js +2 -1
- package/lib/browser/markdown-rendering/markdown.spec.js.map +1 -1
- package/lib/browser/menu/action-menu-node.d.ts +5 -3
- package/lib/browser/menu/action-menu-node.d.ts.map +1 -1
- package/lib/browser/menu/action-menu-node.js +2 -11
- package/lib/browser/menu/action-menu-node.js.map +1 -1
- package/lib/browser/menu/action-menu-node.spec.d.ts +2 -0
- package/lib/browser/menu/action-menu-node.spec.d.ts.map +1 -0
- package/lib/browser/menu/action-menu-node.spec.js +54 -0
- package/lib/browser/menu/action-menu-node.spec.js.map +1 -0
- package/lib/browser/menu/browser-menu-plugin.js +1 -1
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/menu/menu.spec.js +2 -1
- package/lib/browser/menu/menu.spec.js.map +1 -1
- package/lib/browser/opener-service.spec.js +3 -2
- package/lib/browser/opener-service.spec.js.map +1 -1
- package/lib/browser/perspective-service.spec.js +2 -1
- package/lib/browser/perspective-service.spec.js.map +1 -1
- package/lib/browser/preferences/preference-proxy.spec.js +2 -1
- package/lib/browser/preferences/preference-proxy.spec.js.map +1 -1
- package/lib/browser/preferences/preference-schema-provider.spec.js +2 -1
- package/lib/browser/preferences/preference-schema-provider.spec.js.map +1 -1
- package/lib/browser/preferences/preference-service.spec.js +2 -1
- package/lib/browser/preferences/preference-service.spec.js.map +1 -1
- package/lib/browser/preferences/preference-validation-service.spec.js +78 -77
- package/lib/browser/preferences/preference-validation-service.spec.js.map +1 -1
- package/lib/browser/quick-input/quick-command-service.d.ts +10 -1
- package/lib/browser/quick-input/quick-command-service.d.ts.map +1 -1
- package/lib/browser/quick-input/quick-command-service.js +31 -6
- package/lib/browser/quick-input/quick-command-service.js.map +1 -1
- package/lib/browser/quick-input/quick-command-service.spec.d.ts +2 -0
- package/lib/browser/quick-input/quick-command-service.spec.d.ts.map +1 -0
- package/lib/browser/quick-input/quick-command-service.spec.js +130 -0
- package/lib/browser/quick-input/quick-command-service.spec.js.map +1 -0
- package/lib/browser/quick-input/quick-help-service.js +1 -1
- package/lib/browser/quick-input/quick-help-service.js.map +1 -1
- package/lib/browser/quick-input/quick-input-service.spec.js +2 -1
- package/lib/browser/quick-input/quick-input-service.spec.js.map +1 -1
- package/lib/browser/quick-input/quick-pick-service-impl.d.ts +6 -6
- package/lib/browser/quick-input/quick-pick-service-impl.d.ts.map +1 -1
- package/lib/browser/quick-input/quick-view-service.spec.js +2 -1
- package/lib/browser/quick-input/quick-view-service.spec.js.map +1 -1
- package/lib/browser/secondary-window-handler.d.ts +4 -4
- package/lib/browser/secondary-window-handler.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js +1 -1
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/browser/shell/application-shell.spec.js +2 -1
- package/lib/browser/shell/application-shell.spec.js.map +1 -1
- package/lib/browser/shell/shell-layout-restorer.spec.js +2 -1
- package/lib/browser/shell/shell-layout-restorer.spec.js.map +1 -1
- package/lib/browser/shell/side-panel-toolbar.d.ts +1 -1
- package/lib/browser/shell/side-panel-toolbar.d.ts.map +1 -1
- package/lib/browser/shell/sidebar-menu-widget.js +1 -1
- package/lib/browser/shell/sidebar-menu-widget.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js +2 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -1
- package/lib/browser/shell/tab-bars.js +2 -1
- package/lib/browser/shell/tab-bars.js.map +1 -1
- package/lib/browser/status-bar/status-bar.js +1 -1
- package/lib/browser/status-bar/status-bar.js.map +1 -1
- package/lib/browser/storage-service.spec.js +2 -1
- package/lib/browser/storage-service.spec.js.map +1 -1
- package/lib/browser/test/jsdom.d.ts.map +1 -1
- package/lib/browser/test/jsdom.js +11 -0
- package/lib/browser/test/jsdom.js.map +1 -1
- package/lib/browser/tooltip-service.js +1 -1
- package/lib/browser/tooltip-service.js.map +1 -1
- package/lib/browser/tree/tree-consistency.spec.js +1 -1
- package/lib/browser/tree/tree-consistency.spec.js.map +1 -1
- package/lib/browser/tree/tree-container.spec.js +6 -5
- package/lib/browser/tree/tree-container.spec.js.map +1 -1
- package/lib/browser/tree/tree-iterator.js +1 -1
- package/lib/browser/tree/tree-iterator.js.map +1 -1
- package/lib/browser/tree/tree-model.js +2 -2
- package/lib/browser/tree/tree-model.js.map +1 -1
- package/lib/browser/tree/tree-view-welcome-widget.js +1 -1
- package/lib/browser/tree/tree-view-welcome-widget.js.map +1 -1
- package/lib/browser/tree/tree-widget.d.ts +2 -2
- package/lib/browser/tree/tree-widget.d.ts.map +1 -1
- package/lib/browser/tree/tree-widget.js +1 -1
- package/lib/browser/tree/tree-widget.js.map +1 -1
- package/lib/browser/tree/tree.spec.js +2 -1
- package/lib/browser/tree/tree.spec.js.map +1 -1
- package/lib/browser/user-working-directory-provider.js +1 -1
- package/lib/browser/user-working-directory-provider.js.map +1 -1
- package/lib/browser/widgets/react-renderer.js +1 -1
- package/lib/browser/widgets/react-renderer.js.map +1 -1
- package/lib/browser/widgets/react-widget.js +1 -1
- package/lib/browser/widgets/react-widget.js.map +1 -1
- package/lib/browser/widgets/select-component.js +4 -3
- package/lib/browser/widgets/select-component.js.map +1 -1
- package/lib/browser/widgets/select-component.spec.js +3 -2
- package/lib/browser/widgets/select-component.spec.js.map +1 -1
- package/lib/browser/widgets/widget.js +1 -1
- package/lib/browser/widgets/widget.js.map +1 -1
- package/lib/common/buffer.js +3 -2
- package/lib/common/buffer.js.map +1 -1
- package/lib/common/cancellation.js.map +1 -1
- package/lib/common/command.spec.js +2 -1
- package/lib/common/command.spec.js.map +1 -1
- package/lib/common/disposable.spec.js +3 -2
- package/lib/common/disposable.spec.js.map +1 -1
- package/lib/common/encoding-service.js +2 -2
- package/lib/common/encoding-service.js.map +1 -1
- package/lib/common/event.d.ts +1 -1
- package/lib/common/event.d.ts.map +1 -1
- package/lib/common/event.js +0 -1
- package/lib/common/event.js.map +1 -1
- package/lib/common/file-uri.js +2 -1
- package/lib/common/file-uri.js.map +1 -1
- package/lib/common/fuzzy-search.js +1 -1
- package/lib/common/fuzzy-search.js.map +1 -1
- package/lib/common/glob.d.ts.map +1 -1
- package/lib/common/glob.js +9 -15
- package/lib/common/glob.js.map +1 -1
- package/lib/common/index.js +1 -1
- package/lib/common/index.js.map +1 -1
- package/lib/common/keyboard/keyboard-layout-provider.d.ts +7 -0
- package/lib/common/keyboard/keyboard-layout-provider.d.ts.map +1 -1
- package/lib/common/keyboard/keyboard-layout-provider.js.map +1 -1
- package/lib/common/keys.d.ts +86 -3
- package/lib/common/keys.d.ts.map +1 -1
- package/lib/common/keys.js +263 -24
- package/lib/common/keys.js.map +1 -1
- package/lib/common/message-rpc/channel.spec.js +3 -2
- package/lib/common/message-rpc/channel.spec.js.map +1 -1
- package/lib/common/messaging/proxy-factory.js +2 -2
- package/lib/common/messaging/proxy-factory.js.map +1 -1
- package/lib/common/messaging/proxy-factory.spec.js +2 -1
- package/lib/common/messaging/proxy-factory.spec.js.map +1 -1
- package/lib/common/messaging/web-socket-channel.d.ts +1 -1
- package/lib/common/messaging/web-socket-channel.d.ts.map +1 -1
- package/lib/common/path.spec.js +2 -1
- package/lib/common/path.spec.js.map +1 -1
- package/lib/common/performance/stopwatch.spec.js +2 -1
- package/lib/common/performance/stopwatch.spec.js.map +1 -1
- package/lib/common/preferences/preference-schema-service.d.ts +2 -2
- package/lib/common/preferences/preference-schema-service.d.ts.map +1 -1
- package/lib/common/preferences/preference-service.js +1 -1
- package/lib/common/preferences/preference-service.js.map +1 -1
- package/lib/common/promise-util.spec.js +2 -1
- package/lib/common/promise-util.spec.js.map +1 -1
- package/lib/common/quick-pick-service.d.ts +5 -0
- package/lib/common/quick-pick-service.d.ts.map +1 -1
- package/lib/common/quick-pick-service.js +2 -1
- package/lib/common/quick-pick-service.js.map +1 -1
- package/lib/common/reference.spec.js +2 -1
- package/lib/common/reference.spec.js.map +1 -1
- package/lib/common/resource.js +1 -1
- package/lib/common/resource.js.map +1 -1
- package/lib/common/selection-service.spec.js +2 -1
- package/lib/common/selection-service.spec.js.map +1 -1
- package/lib/common/selection.js +2 -1
- package/lib/common/selection.js.map +1 -1
- package/lib/common/stream.d.ts.map +1 -1
- package/lib/common/stream.js +2 -1
- package/lib/common/stream.js.map +1 -1
- package/lib/common/strings.d.ts.map +1 -1
- package/lib/common/strings.js +1 -2
- package/lib/common/strings.js.map +1 -1
- package/lib/common/ternary-search-tree.js +2 -2
- package/lib/common/ternary-search-tree.js.map +1 -1
- package/lib/common/ternary-search-tree.spec.js +12 -0
- package/lib/common/ternary-search-tree.spec.js.map +1 -1
- package/lib/common/test/mock-resource-provider.js +1 -1
- package/lib/common/test/mock-resource-provider.js.map +1 -1
- package/lib/common/types.spec.js +2 -1
- package/lib/common/types.spec.js.map +1 -1
- package/lib/common/uri-command-handler.js +4 -3
- package/lib/common/uri-command-handler.js.map +1 -1
- package/lib/common/uri-command-handler.spec.js +3 -2
- package/lib/common/uri-command-handler.spec.js.map +1 -1
- package/lib/common/uri.spec.js +3 -2
- package/lib/common/uri.spec.js.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts +2 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.js +8 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.js.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts +2 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts.map +1 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js +28 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js.map +1 -0
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +3 -2
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-menu-contribution.js +3 -0
- package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
- package/lib/electron-browser/window/window-zoom-action-bar.js +2 -1
- package/lib/electron-browser/window/window-zoom-action-bar.js.map +1 -1
- package/lib/electron-common/electron-api.d.ts +5 -0
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-common/electron-api.js +21 -0
- package/lib/electron-common/electron-api.js.map +1 -1
- package/lib/electron-common/electron-api.spec.d.ts +2 -0
- package/lib/electron-common/electron-api.spec.d.ts.map +1 -0
- package/lib/electron-common/electron-api.spec.js +40 -0
- package/lib/electron-common/electron-api.spec.js.map +1 -0
- package/lib/electron-main/electron-api-main.d.ts +0 -1
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +3 -14
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/lib/electron-main/electron-main-application.js +2 -2
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-node/hosting/electron-backend-hosting-module.spec.js +2 -1
- package/lib/electron-node/hosting/electron-backend-hosting-module.spec.js.map +1 -1
- package/lib/electron-node/keyboard/electron-keyboard-layout-provider.js +1 -1
- package/lib/electron-node/keyboard/electron-keyboard-layout-provider.js.map +1 -1
- package/lib/electron-node/token/electron-token-validator.js +2 -2
- package/lib/electron-node/token/electron-token-validator.js.map +1 -1
- package/lib/node/backend-application.d.ts +1 -1
- package/lib/node/backend-application.d.ts.map +1 -1
- package/lib/node/backend-application.js +7 -7
- package/lib/node/backend-application.js.map +1 -1
- package/lib/node/backend-application.spec.js +1 -1
- package/lib/node/backend-application.spec.js.map +1 -1
- package/lib/node/bundled-resource-provider.js +4 -4
- package/lib/node/bundled-resource-provider.js.map +1 -1
- package/lib/node/bundled-resource-provider.spec.js +5 -4
- package/lib/node/bundled-resource-provider.spec.js.map +1 -1
- package/lib/node/cli.d.ts +1 -1
- package/lib/node/cli.d.ts.map +1 -1
- package/lib/node/cli.js +2 -2
- package/lib/node/cli.js.map +1 -1
- package/lib/node/cli.spec.js +3 -2
- package/lib/node/cli.spec.js.map +1 -1
- package/lib/node/console-logger-server.js +1 -1
- package/lib/node/console-logger-server.js.map +1 -1
- package/lib/node/env-variables/env-variables-server.js +1 -1
- package/lib/node/env-variables/env-variables-server.js.map +1 -1
- package/lib/node/environment-utils.spec.js +2 -1
- package/lib/node/environment-utils.spec.js.map +1 -1
- package/lib/node/file-uri.spec.js +4 -3
- package/lib/node/file-uri.spec.js.map +1 -1
- package/lib/node/filesystem-locking.js +1 -1
- package/lib/node/filesystem-locking.js.map +1 -1
- package/lib/node/hosting/browser-connection-token.js +2 -2
- package/lib/node/hosting/browser-connection-token.js.map +1 -1
- package/lib/node/i18n/localization-contribution.js +1 -1
- package/lib/node/i18n/localization-contribution.js.map +1 -1
- package/lib/node/key-store-server.js +1 -1
- package/lib/node/key-store-server.js.map +1 -1
- package/lib/node/logger-cli-contribution.js +2 -2
- package/lib/node/logger-cli-contribution.js.map +1 -1
- package/lib/node/logger-cli-contribution.spec.js +17 -16
- package/lib/node/logger-cli-contribution.spec.js.map +1 -1
- package/lib/node/messaging/ipc-connection-provider.js +2 -2
- package/lib/node/messaging/ipc-connection-provider.js.map +1 -1
- package/lib/node/process-utils.js +1 -1
- package/lib/node/process-utils.js.map +1 -1
- package/lib/node/process-utils.spec.js +2 -1
- package/lib/node/process-utils.spec.js.map +1 -1
- package/lib/node/request/backend-request-facade.spec.js +2 -1
- package/lib/node/request/backend-request-facade.spec.js.map +1 -1
- package/lib/node/setting-service.spec.js +2 -1
- package/lib/node/setting-service.spec.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/common-frontend-contribution.ts +17 -1
- package/src/browser/components/card.tsx +1 -1
- package/src/browser/context-key-service.ts +15 -0
- package/src/browser/keybinding.spec.ts +878 -34
- package/src/browser/keybinding.ts +470 -85
- package/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +1 -1
- package/src/browser/keyboard/browser-keyboard-layout-provider.spec.ts +52 -1
- package/src/browser/keyboard/browser-keyboard-layout-provider.ts +5 -5
- package/src/browser/keyboard/keyboard-layout-service.spec.ts +640 -15
- package/src/browser/keyboard/keyboard-layout-service.ts +358 -106
- package/src/browser/keyboard/keys.spec.ts +206 -1
- package/src/browser/label-provider.ts +3 -1
- package/src/browser/markdown-rendering/markdown-renderer.ts +1 -1
- package/src/browser/menu/action-menu-node.spec.ts +60 -0
- package/src/browser/menu/action-menu-node.ts +8 -14
- package/src/browser/menu/browser-menu-plugin.ts +1 -1
- package/src/browser/preferences/preference-validation-service.spec.ts +1 -1
- package/src/browser/quick-input/quick-command-service.spec.ts +159 -0
- package/src/browser/quick-input/quick-command-service.ts +36 -9
- package/src/browser/quick-input/quick-help-service.ts +1 -1
- package/src/browser/test/jsdom.ts +11 -0
- package/src/browser/tree/tree-container.spec.ts +1 -1
- package/src/browser/tree/tree-iterator.ts +1 -1
- package/src/browser/tree/tree-model.ts +2 -2
- package/src/browser/tree/tree-widget.tsx +2 -2
- package/src/common/cancellation.ts +1 -1
- package/src/common/disposable.spec.ts +1 -1
- package/src/common/event.ts +1 -2
- package/src/common/glob.ts +13 -30
- package/src/common/json-schema.ts +1 -1
- package/src/common/keyboard/keyboard-layout-provider.ts +8 -0
- package/src/common/keys.ts +312 -27
- package/src/common/message-rpc/channel.spec.ts +1 -1
- package/src/common/messaging/proxy-factory.ts +2 -2
- package/src/common/quick-pick-service.ts +5 -0
- package/src/common/stream.ts +4 -3
- package/src/common/strings.ts +2 -5
- package/src/common/ternary-search-tree.spec.ts +14 -0
- package/src/common/ternary-search-tree.ts +3 -3
- package/src/common/uri-command-handler.ts +2 -2
- package/src/electron-browser/keyboard/electron-keyboard-module.spec.ts +29 -0
- package/src/electron-browser/keyboard/electron-keyboard-module.ts +9 -2
- package/src/electron-browser/menu/electron-main-menu-factory.ts +3 -3
- package/src/electron-browser/menu/electron-menu-contribution.ts +4 -0
- package/src/electron-common/electron-api.spec.ts +42 -0
- package/src/electron-common/electron-api.ts +23 -0
- package/src/electron-main/electron-api-main.ts +2 -13
- package/src/node/backend-application.ts +1 -1
- package/src/node/cli.ts +1 -1
- package/src/node/logger-cli-contribution.spec.ts +1 -1
|
@@ -15,11 +15,14 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { injectable, inject, named } from 'inversify';
|
|
18
|
-
import { isOSX } from '../common/os';
|
|
18
|
+
import { isOSX, isWindows } from '../common/os';
|
|
19
19
|
import { Emitter, Event } from '../common/event';
|
|
20
20
|
import { CommandRegistry, Command } from '../common/command';
|
|
21
21
|
import { Disposable, DisposableCollection } from '../common/disposable';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
KeyCode, KeySequence, Key, NormalizedKeyboardInput, normalizeKeyboardInput,
|
|
24
|
+
layoutModifiersIncludeAltGraph, layoutModifiersIncludeShift
|
|
25
|
+
} from './keyboard/keys';
|
|
23
26
|
import { KeyboardLayoutService } from './keyboard/keyboard-layout-service';
|
|
24
27
|
import { ContributionProvider } from '../common/contribution-provider';
|
|
25
28
|
import { ILogger } from '../common/logger';
|
|
@@ -46,6 +49,9 @@ export namespace KeybindingScope {
|
|
|
46
49
|
export type Keybinding = common.Keybinding;
|
|
47
50
|
export const Keybinding = common.Keybinding;
|
|
48
51
|
|
|
52
|
+
/** Selects whether an accelerator describes the authored character or its physical realization. */
|
|
53
|
+
export type AcceleratorForm = 'logical' | 'physical';
|
|
54
|
+
|
|
49
55
|
export interface ResolvedKeybinding extends common.Keybinding {
|
|
50
56
|
/**
|
|
51
57
|
* The KeyboardLayoutService may transform the `keybinding` depending on the
|
|
@@ -54,6 +60,8 @@ export interface ResolvedKeybinding extends common.Keybinding {
|
|
|
54
60
|
* has not been called yet to resolve the keybinding.
|
|
55
61
|
*/
|
|
56
62
|
resolved?: KeyCode[];
|
|
63
|
+
inactive?: boolean;
|
|
64
|
+
inactiveReason?: string;
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
export interface ScopedKeybinding extends common.Keybinding {
|
|
@@ -99,7 +107,9 @@ export namespace KeybindingContexts {
|
|
|
99
107
|
export class KeybindingRegistry {
|
|
100
108
|
|
|
101
109
|
static readonly PASSTHROUGH_PSEUDO_COMMAND = 'passthrough';
|
|
102
|
-
protected
|
|
110
|
+
protected keySequenceCandidates: KeySequence[] = [[]];
|
|
111
|
+
/** Whether keyboard shortcut troubleshooting logs are enabled. */
|
|
112
|
+
protected keyboardShortcutsTroubleshooting = false;
|
|
103
113
|
|
|
104
114
|
protected readonly contexts: { [id: string]: KeybindingContext } = {};
|
|
105
115
|
protected readonly keymaps: ScopedKeybinding[][] = [...Array(KeybindingScope.length)].map(() => []);
|
|
@@ -108,7 +118,9 @@ export class KeybindingRegistry {
|
|
|
108
118
|
* Ternary search tree for efficient keybinding lookup.
|
|
109
119
|
* Maps resolved key sequences to arrays of bindings sorted by scope (highest first).
|
|
110
120
|
*/
|
|
111
|
-
protected keybindingTree: TernarySearchTree<KeySequence,
|
|
121
|
+
protected keybindingTree: TernarySearchTree<KeySequence, KeybindingRegistry.RuntimeKeybinding[]> | undefined;
|
|
122
|
+
protected readonly handledKeyboardEvents = new WeakSet<KeyboardEvent>();
|
|
123
|
+
protected readonly warnedAmbiguousSequences = new Set<string>();
|
|
112
124
|
|
|
113
125
|
@inject(CorePreferences)
|
|
114
126
|
protected readonly corePreferences: CorePreferences;
|
|
@@ -136,10 +148,11 @@ export class KeybindingRegistry {
|
|
|
136
148
|
|
|
137
149
|
async onStart(): Promise<void> {
|
|
138
150
|
await this.keyboardLayoutService.initialize();
|
|
139
|
-
this.keyboardLayoutService.onKeyboardLayoutChanged(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
this.keyboardLayoutService.onKeyboardLayoutChanged(() => this.invalidateResolvedKeybindings());
|
|
152
|
+
this.corePreferences.onPreferenceChanged(change => {
|
|
153
|
+
if (change.preferenceName === 'keyboard.dispatch') {
|
|
154
|
+
this.invalidateResolvedKeybindings();
|
|
155
|
+
}
|
|
143
156
|
});
|
|
144
157
|
this.registerContext(KeybindingContexts.NOOP_CONTEXT);
|
|
145
158
|
this.registerContext(KeybindingContexts.DEFAULT_CONTEXT);
|
|
@@ -283,14 +296,63 @@ export class KeybindingRegistry {
|
|
|
283
296
|
resolveKeybinding(binding: ResolvedKeybinding): KeyCode[] {
|
|
284
297
|
if (!binding.resolved) {
|
|
285
298
|
const sequence = KeySequence.parse(binding.keybinding);
|
|
286
|
-
|
|
299
|
+
const resolved = this.corePreferences['keyboard.dispatch'] === 'keyCode'
|
|
300
|
+
? sequence
|
|
301
|
+
: sequence.map(code => this.keyboardLayoutService.resolveKeyCode(code));
|
|
302
|
+
binding.inactiveReason = this.getInactiveReason(resolved, this.corePreferences['keyboard.dispatch']);
|
|
303
|
+
binding.inactive = binding.inactiveReason !== undefined;
|
|
304
|
+
binding.resolved = resolved;
|
|
287
305
|
}
|
|
288
306
|
return binding.resolved;
|
|
289
307
|
}
|
|
290
308
|
|
|
309
|
+
protected getInactiveReason(sequence: KeyCode[], dispatch: 'code' | 'keyCode'): string | undefined {
|
|
310
|
+
if (dispatch === 'keyCode' && sequence.some(code => code.character !== undefined && !code.key)) {
|
|
311
|
+
return nls.localize('theia/core/keybinding/characterRequiresCodeDispatch',
|
|
312
|
+
'Character keybindings require the "keyboard.dispatch" preference to be set to "code".');
|
|
313
|
+
}
|
|
314
|
+
if (sequence.some(code => code.shift && layoutModifiersIncludeAltGraph(code.layoutModifiers)
|
|
315
|
+
&& !layoutModifiersIncludeShift(code.layoutModifiers))) {
|
|
316
|
+
return nls.localize('theia/core/keybinding/shiftedAltGraphCharacterUnavailable',
|
|
317
|
+
'The logical character and Shift command modifier cannot be produced together on the current keyboard layout.');
|
|
318
|
+
}
|
|
319
|
+
// On macOS, Option selects the AltGraph layer. On Windows, AltGr is reported as Ctrl+Alt, so both Ctrl and Alt are
|
|
320
|
+
// consumed by the layout layer. Linux keeps Alt and AltGraph distinct.
|
|
321
|
+
const altGraphCodes = isOSX || isWindows ? sequence.filter(code => layoutModifiersIncludeAltGraph(code.layoutModifiers)) : [];
|
|
322
|
+
const consumedModifier = altGraphCodes.some(code => code.alt) ? (isOSX ? 'Option' : 'Alt')
|
|
323
|
+
: isWindows && altGraphCodes.some(code => code.ctrl) ? 'Ctrl' : undefined;
|
|
324
|
+
if (consumedModifier) {
|
|
325
|
+
return nls.localize('theia/core/keybinding/commandModifierAltGraphCharacterUnavailable',
|
|
326
|
+
'The logical character and the {0} command modifier cannot be produced together on the current keyboard layout.',
|
|
327
|
+
consumedModifier);
|
|
328
|
+
}
|
|
329
|
+
if (sequence.some(code => !code.supportedByLayout)) {
|
|
330
|
+
return nls.localize('theia/core/keybinding/unavailableCharacter', 'The key is not available on the current keyboard layout.');
|
|
331
|
+
}
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Return the localized explanation when a binding is inactive on the current layout. */
|
|
336
|
+
getKeybindingInactiveReason(binding: ResolvedKeybinding): string | undefined {
|
|
337
|
+
this.resolveKeybinding(binding);
|
|
338
|
+
return binding.inactiveReason;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Return whether a binding is inactive on the current keyboard layout. */
|
|
342
|
+
isKeybindingInactive(binding: ResolvedKeybinding): boolean {
|
|
343
|
+
this.resolveKeybinding(binding);
|
|
344
|
+
return !!binding.inactive;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Clear cached resolutions, invalidate the lookup tree, and notify listeners. */
|
|
348
|
+
protected invalidateResolvedKeybindings(): void {
|
|
349
|
+
this.clearResolvedKeybindings();
|
|
350
|
+
this.invalidateKeybindingTree();
|
|
351
|
+
this.keybindingsChanged.fire(undefined);
|
|
352
|
+
}
|
|
353
|
+
|
|
291
354
|
/**
|
|
292
|
-
* Clear all `resolved` properties of registered keybindings so the
|
|
293
|
-
* again to resolve them. This is necessary when the user's keyboard layout has changed.
|
|
355
|
+
* Clear all `resolved` properties of registered keybindings so the keyboard layout service resolves them again.
|
|
294
356
|
*/
|
|
295
357
|
protected clearResolvedKeybindings(): void {
|
|
296
358
|
for (let i = KeybindingScope.DEFAULT; i < KeybindingScope.END; i++) {
|
|
@@ -298,6 +360,8 @@ export class KeybindingRegistry {
|
|
|
298
360
|
for (let j = 0; j < bindings.length; j++) {
|
|
299
361
|
const binding = bindings[j] as ResolvedKeybinding;
|
|
300
362
|
binding.resolved = undefined;
|
|
363
|
+
binding.inactive = undefined;
|
|
364
|
+
binding.inactiveReason = undefined;
|
|
301
365
|
}
|
|
302
366
|
}
|
|
303
367
|
}
|
|
@@ -307,13 +371,14 @@ export class KeybindingRegistry {
|
|
|
307
371
|
*/
|
|
308
372
|
protected invalidateKeybindingTree(): void {
|
|
309
373
|
this.keybindingTree = undefined;
|
|
374
|
+
this.warnedAmbiguousSequences.clear();
|
|
310
375
|
}
|
|
311
376
|
|
|
312
377
|
/**
|
|
313
378
|
* Gets or builds the keybinding tree for efficient lookup.
|
|
314
379
|
* The tree maps resolved key sequences to arrays of bindings sorted by scope (highest first).
|
|
315
380
|
*/
|
|
316
|
-
protected getKeybindingTree(): TernarySearchTree<KeySequence,
|
|
381
|
+
protected getKeybindingTree(): TernarySearchTree<KeySequence, KeybindingRegistry.RuntimeKeybinding[]> {
|
|
317
382
|
if (!this.keybindingTree) {
|
|
318
383
|
this.keybindingTree = this.buildKeybindingTree();
|
|
319
384
|
}
|
|
@@ -326,8 +391,8 @@ export class KeybindingRegistry {
|
|
|
326
391
|
* Bindings are stored in the order they should be checked: highest scope first,
|
|
327
392
|
* and within the same scope, in the order they appear in keymaps (later-registered first).
|
|
328
393
|
*/
|
|
329
|
-
protected buildKeybindingTree(): TernarySearchTree<KeySequence,
|
|
330
|
-
const tree = TernarySearchTree.forKeySequences<
|
|
394
|
+
protected buildKeybindingTree(): TernarySearchTree<KeySequence, KeybindingRegistry.RuntimeKeybinding[]> {
|
|
395
|
+
const tree = TernarySearchTree.forKeySequences<KeybindingRegistry.RuntimeKeybinding[]>();
|
|
331
396
|
|
|
332
397
|
// Process scopes from highest to lowest priority (WORKSPACE > USER > DEFAULT)
|
|
333
398
|
// Within each scope, maintain the keymaps order (later-registered bindings are at the front)
|
|
@@ -335,12 +400,15 @@ export class KeybindingRegistry {
|
|
|
335
400
|
for (const binding of this.keymaps[scope]) {
|
|
336
401
|
try {
|
|
337
402
|
const resolved = this.resolveKeybinding(binding);
|
|
403
|
+
if (this.isKeybindingInactive(binding)) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const runtime: KeybindingRegistry.RuntimeKeybinding = { binding, sequence: resolved };
|
|
338
407
|
const existing = tree.get(resolved);
|
|
339
408
|
if (existing) {
|
|
340
|
-
|
|
341
|
-
existing.push(binding);
|
|
409
|
+
existing.push(runtime);
|
|
342
410
|
} else {
|
|
343
|
-
tree.set(resolved, [
|
|
411
|
+
tree.set(resolved, [runtime]);
|
|
344
412
|
}
|
|
345
413
|
} catch (error) {
|
|
346
414
|
// Skip bindings that can't be resolved
|
|
@@ -359,19 +427,60 @@ export class KeybindingRegistry {
|
|
|
359
427
|
* @returns true if there is a colliding keybinding
|
|
360
428
|
*/
|
|
361
429
|
containsKeybindingInScope(binding: common.Keybinding, scope = KeybindingScope.USER): boolean {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
.
|
|
365
|
-
|
|
366
|
-
|
|
430
|
+
const diagnostics = this.getKeybindingCollisionDiagnostics(binding, scope);
|
|
431
|
+
return [diagnostics.authored, diagnostics.layoutDerived].some(result =>
|
|
432
|
+
result.full.length > 0 || result.partial.length > 0 || result.shadow.length > 0);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** Return authored, layout-resolved, and shadowing collision diagnostics for a binding. */
|
|
436
|
+
getKeybindingCollisionDiagnostics(binding: common.Keybinding, scope = KeybindingScope.USER): KeybindingRegistry.CollisionDiagnostics {
|
|
437
|
+
const diagnostics: KeybindingRegistry.CollisionDiagnostics = {
|
|
438
|
+
authored: new KeybindingRegistry.KeybindingsResult(),
|
|
439
|
+
layoutDerived: new KeybindingRegistry.KeybindingsResult(),
|
|
440
|
+
shadowedBy: []
|
|
441
|
+
};
|
|
442
|
+
const sequence = this.resolveKeybinding(binding);
|
|
443
|
+
if (this.isKeybindingInactive(binding)) {
|
|
444
|
+
return diagnostics;
|
|
367
445
|
}
|
|
368
|
-
|
|
369
|
-
|
|
446
|
+
const candidates = this.getUsableBindings(this.keymaps[scope]).filter(candidate => candidate !== binding
|
|
447
|
+
&& candidate.context === binding.context && !candidate.when && !binding.when);
|
|
448
|
+
for (const candidate of candidates) {
|
|
449
|
+
const resolved = this.resolveKeybinding(candidate);
|
|
450
|
+
const target = resolved.some(code => code.layoutModifiers !== 'none')
|
|
451
|
+
? diagnostics.layoutDerived
|
|
452
|
+
: diagnostics.authored;
|
|
453
|
+
target.merge(this.getKeySequenceCollisions([candidate], sequence));
|
|
370
454
|
}
|
|
371
|
-
|
|
372
|
-
|
|
455
|
+
diagnostics.shadowedBy.push(...this.getShadowingKeybindings(binding, scope));
|
|
456
|
+
return diagnostics;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Return bindings whose command-modifier interpretation takes the keystroke instead of the supplied binding. */
|
|
460
|
+
getShadowingKeybindings(binding: common.Keybinding, scope?: KeybindingScope): ScopedKeybinding[] {
|
|
461
|
+
const sequence = this.resolveKeybinding(binding);
|
|
462
|
+
if (!sequence.some(code => code.layoutModifiers !== 'none')) {
|
|
463
|
+
return [];
|
|
464
|
+
}
|
|
465
|
+
const commandSequence = sequence.map(code => code.layoutModifiers !== 'none' ? new KeyCode({
|
|
466
|
+
key: code.key,
|
|
467
|
+
ctrl: code.ctrl,
|
|
468
|
+
shift: code.shift || layoutModifiersIncludeShift(code.layoutModifiers),
|
|
469
|
+
alt: code.alt || layoutModifiersIncludeAltGraph(code.layoutModifiers),
|
|
470
|
+
meta: code.meta,
|
|
471
|
+
character: code.key?.easyString,
|
|
472
|
+
interpretation: 'commandModifiers'
|
|
473
|
+
}) : code);
|
|
474
|
+
const result: ScopedKeybinding[] = [];
|
|
475
|
+
const matchesContext = (candidate: ScopedKeybinding) => candidate !== binding
|
|
476
|
+
&& candidate.context === binding.context && candidate.when === binding.when;
|
|
477
|
+
const firstScope = scope ?? KeybindingScope.END - 1;
|
|
478
|
+
const lastScope = scope ?? KeybindingScope.DEFAULT;
|
|
479
|
+
for (let currentScope = firstScope; currentScope >= lastScope; currentScope--) {
|
|
480
|
+
const collisions = this.getKeySequenceCollisions(this.getUsableBindings(this.keymaps[currentScope]), commandSequence);
|
|
481
|
+
result.push(...collisions.full.filter(matchesContext));
|
|
373
482
|
}
|
|
374
|
-
return
|
|
483
|
+
return result;
|
|
375
484
|
}
|
|
376
485
|
|
|
377
486
|
/**
|
|
@@ -379,10 +488,12 @@ export class KeybindingRegistry {
|
|
|
379
488
|
* @returns an array of strings representing all elements of the {@link KeySequence} defined by the {@link common.Keybinding}
|
|
380
489
|
* @param keybinding the keybinding
|
|
381
490
|
* @param separator the separator to be used to stringify {@link KeyCode}s that are part of the {@link KeySequence}
|
|
491
|
+
* @param form whether to render logical characters or their physical realization
|
|
382
492
|
*/
|
|
383
|
-
acceleratorFor(keybinding: common.Keybinding, separator: string = ' ', asciiOnly = false
|
|
493
|
+
acceleratorFor(keybinding: common.Keybinding, separator: string = ' ', asciiOnly = false,
|
|
494
|
+
form: AcceleratorForm = 'logical'): string[] {
|
|
384
495
|
const bindingKeySequence = this.resolveKeybinding(keybinding);
|
|
385
|
-
return this.acceleratorForSequence(bindingKeySequence, separator, asciiOnly);
|
|
496
|
+
return this.acceleratorForSequence(bindingKeySequence, separator, asciiOnly, form);
|
|
386
497
|
}
|
|
387
498
|
|
|
388
499
|
/**
|
|
@@ -390,9 +501,11 @@ export class KeybindingRegistry {
|
|
|
390
501
|
* @returns an array of strings representing all elements of the {@link KeySequence}
|
|
391
502
|
* @param keySequence the keysequence
|
|
392
503
|
* @param separator the separator to be used to stringify {@link KeyCode}s that are part of the {@link KeySequence}
|
|
504
|
+
* @param form whether to render logical characters or their physical realization
|
|
393
505
|
*/
|
|
394
|
-
acceleratorForSequence(keySequence: KeySequence, separator: string = ' ', asciiOnly = false
|
|
395
|
-
|
|
506
|
+
acceleratorForSequence(keySequence: KeySequence, separator: string = ' ', asciiOnly = false,
|
|
507
|
+
form: AcceleratorForm = 'logical'): string[] {
|
|
508
|
+
return keySequence.map(keyCode => this.acceleratorForKeyCode(keyCode, separator, asciiOnly, form));
|
|
396
509
|
}
|
|
397
510
|
|
|
398
511
|
/**
|
|
@@ -403,13 +516,33 @@ export class KeybindingRegistry {
|
|
|
403
516
|
* Ignored when rendering macOS symbols (i.e. on macOS with `asciiOnly=false`), as the convention is
|
|
404
517
|
* to juxtapose modifier symbols without a separator (e.g. `⌃⌘V`).
|
|
405
518
|
* @param asciiOnly if `true`, no special characters will be substituted into the string returned. Ensures correct keyboard shortcuts in Electron menus.
|
|
519
|
+
* @param form whether to render the logical character or its physical realization
|
|
406
520
|
*/
|
|
407
|
-
acceleratorForKeyCode(keyCode: KeyCode, separator: string = ' ', asciiOnly = false
|
|
521
|
+
acceleratorForKeyCode(keyCode: KeyCode, separator: string = ' ', asciiOnly = false,
|
|
522
|
+
form: AcceleratorForm = 'logical'): string {
|
|
408
523
|
const useSymbols = isOSX && !asciiOnly;
|
|
409
|
-
return this.componentsForKeyCode(keyCode, asciiOnly).join(useSymbols ? '' : separator);
|
|
524
|
+
return this.componentsForKeyCode(keyCode, asciiOnly, form).join(useSymbols ? '' : separator);
|
|
410
525
|
}
|
|
411
526
|
|
|
412
|
-
componentsForKeyCode(keyCode: KeyCode, asciiOnly = false): string[] {
|
|
527
|
+
componentsForKeyCode(keyCode: KeyCode, asciiOnly = false, form: AcceleratorForm = 'logical'): string[] {
|
|
528
|
+
if (form === 'physical') {
|
|
529
|
+
const physical = new KeyCode({
|
|
530
|
+
key: keyCode.key,
|
|
531
|
+
meta: keyCode.meta,
|
|
532
|
+
ctrl: keyCode.ctrl,
|
|
533
|
+
alt: keyCode.alt,
|
|
534
|
+
shift: keyCode.shift || layoutModifiersIncludeShift(keyCode.layoutModifiers)
|
|
535
|
+
});
|
|
536
|
+
const result = this.componentsForKeyCode(physical, asciiOnly);
|
|
537
|
+
if (layoutModifiersIncludeAltGraph(keyCode.layoutModifiers)) {
|
|
538
|
+
const layoutModifier = isOSX ? (asciiOnly ? 'Option' : '⌥') : 'AltGr';
|
|
539
|
+
const keyIndex = result.length - (physical.key ? 1 : 0);
|
|
540
|
+
if (!(isOSX && keyCode.alt)) {
|
|
541
|
+
result.splice(keyIndex, 0, layoutModifier);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return result;
|
|
545
|
+
}
|
|
413
546
|
const keyCodeResult = [];
|
|
414
547
|
const useSymbols = isOSX && !asciiOnly;
|
|
415
548
|
if (keyCode.meta && isOSX) {
|
|
@@ -421,15 +554,29 @@ export class KeybindingRegistry {
|
|
|
421
554
|
if (keyCode.alt) {
|
|
422
555
|
keyCodeResult.push(useSymbols ? '⌥' : 'Alt');
|
|
423
556
|
}
|
|
424
|
-
if (keyCode
|
|
557
|
+
if (this.displaysShift(keyCode)) {
|
|
425
558
|
keyCodeResult.push(useSymbols ? '⇧' : 'Shift');
|
|
426
559
|
}
|
|
427
|
-
if (keyCode.
|
|
560
|
+
if (keyCode.character) {
|
|
561
|
+
keyCodeResult.push(this.acceleratorForCharacter(keyCode.character));
|
|
562
|
+
} else if (keyCode.key) {
|
|
428
563
|
keyCodeResult.push(this.acceleratorForKey(keyCode.key, asciiOnly));
|
|
429
564
|
}
|
|
430
565
|
return keyCodeResult;
|
|
431
566
|
}
|
|
432
567
|
|
|
568
|
+
/**
|
|
569
|
+
* Whether the logical label of a key code shows Shift: when Shift is a command modifier or produces an uppercase letter.
|
|
570
|
+
* Shift that only selects a layout layer for other characters (e.g. `/` on German layouts) is not shown.
|
|
571
|
+
*/
|
|
572
|
+
displaysShift(keyCode: KeyCode): boolean {
|
|
573
|
+
return keyCode.shift || layoutModifiersIncludeShift(keyCode.layoutModifiers) && !!keyCode.character && /^[A-Z]$/.test(keyCode.character);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
protected acceleratorForCharacter(character: string): string {
|
|
577
|
+
return /^[a-z]$/i.test(character) ? character.toUpperCase() : character;
|
|
578
|
+
}
|
|
579
|
+
|
|
433
580
|
/**
|
|
434
581
|
* @param asciiOnly if `true`, no special characters will be substituted into the string returned. Ensures correct keyboard shortcuts in Electron menus.
|
|
435
582
|
*
|
|
@@ -472,6 +619,9 @@ export class KeybindingRegistry {
|
|
|
472
619
|
for (const binding of bindings) {
|
|
473
620
|
try {
|
|
474
621
|
const bindingKeySequence = this.resolveKeybinding(binding);
|
|
622
|
+
if (this.isKeybindingInactive(binding)) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
475
625
|
const compareResult = KeySequence.compare(candidate, bindingKeySequence);
|
|
476
626
|
switch (compareResult) {
|
|
477
627
|
case KeySequence.CompareResult.FULL: {
|
|
@@ -520,6 +670,21 @@ export class KeybindingRegistry {
|
|
|
520
670
|
return result;
|
|
521
671
|
}
|
|
522
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Get the accelerator of the first keybinding of a command that is enabled in the given context.
|
|
675
|
+
* Physical accelerators (for native menus) are omitted for inactive keybindings.
|
|
676
|
+
*/
|
|
677
|
+
acceleratorForCommand(commandId: string, context: HTMLElement | undefined, form: AcceleratorForm = 'logical'): string[] {
|
|
678
|
+
const binding = this.getKeybindingsForCommand(commandId).find(candidate => this.isEnabledInScope(candidate, context));
|
|
679
|
+
if (!binding) {
|
|
680
|
+
return [];
|
|
681
|
+
}
|
|
682
|
+
if (form === 'physical') {
|
|
683
|
+
return this.isKeybindingInactive(binding) ? [] : this.acceleratorFor(binding, '+', true, 'physical');
|
|
684
|
+
}
|
|
685
|
+
return this.acceleratorFor(binding, '+');
|
|
686
|
+
}
|
|
687
|
+
|
|
523
688
|
protected isActive(binding: common.Keybinding): boolean {
|
|
524
689
|
/* Pseudo commands like "passthrough" are always active (and not found
|
|
525
690
|
in the command registry). */
|
|
@@ -577,20 +742,65 @@ export class KeybindingRegistry {
|
|
|
577
742
|
return true;
|
|
578
743
|
}
|
|
579
744
|
|
|
745
|
+
/**
|
|
746
|
+
* Executes the command by dispatching its first keybinding: the resolved key codes are matched directly,
|
|
747
|
+
* and a DOM keydown marked as already handled is dispatched to the target for other listeners to observe.
|
|
748
|
+
*/
|
|
580
749
|
dispatchCommand(id: string, target?: EventTarget): void {
|
|
581
750
|
const keybindings = this.getKeybindingsForCommand(id);
|
|
582
|
-
if (keybindings.length) {
|
|
751
|
+
if (keybindings.length && !this.isKeybindingInactive(keybindings[0])) {
|
|
583
752
|
for (const keyCode of this.resolveKeybinding(keybindings[0])) {
|
|
584
|
-
|
|
753
|
+
const input: NormalizedKeyboardInput = {
|
|
754
|
+
key: keyCode.character ?? keyCode.key?.code,
|
|
755
|
+
code: keyCode.key?.code,
|
|
756
|
+
keyCode: keyCode.key?.keyCode,
|
|
757
|
+
ctrlKey: keyCode.ctrl,
|
|
758
|
+
shiftKey: keyCode.shift || layoutModifiersIncludeShift(keyCode.layoutModifiers),
|
|
759
|
+
altKey: keyCode.alt,
|
|
760
|
+
metaKey: keyCode.meta,
|
|
761
|
+
altGraph: layoutModifiersIncludeAltGraph(keyCode.layoutModifiers)
|
|
762
|
+
};
|
|
763
|
+
this.dispatchNormalizedKeyDown(input, target, [keyCode]);
|
|
764
|
+
this.dispatchKeyDown({ ...input, modifierAltGraph: input.altGraph }, target, true);
|
|
585
765
|
}
|
|
586
766
|
}
|
|
587
767
|
}
|
|
588
768
|
|
|
589
|
-
|
|
769
|
+
/** Dispatch a DOM keydown, optionally marking it as already handled by direct normalized dispatch. */
|
|
770
|
+
dispatchKeyDown(input: KeyboardEventInit | KeyCode | string, target: EventTarget = document.activeElement || window, alreadyHandled = false): void {
|
|
590
771
|
const eventInit = this.asKeyboardEventInit(input);
|
|
591
|
-
const emulatedKeyboardEvent = new KeyboardEvent('keydown', eventInit);
|
|
772
|
+
const emulatedKeyboardEvent = new KeyboardEvent('keydown', { ...eventInit, cancelable: true });
|
|
773
|
+
if (alreadyHandled) {
|
|
774
|
+
this.handledKeyboardEvents.add(emulatedKeyboardEvent);
|
|
775
|
+
}
|
|
592
776
|
target.dispatchEvent(emulatedKeyboardEvent);
|
|
593
777
|
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Runs normalized keyboard data directly through keybinding matching without dispatching a DOM event.
|
|
781
|
+
*/
|
|
782
|
+
dispatchNormalizedKeyDown(input: NormalizedKeyboardInput, target?: EventTarget, keyCodes?: KeyCode[]): void {
|
|
783
|
+
const normalizedInput = normalizeKeyboardInput(input);
|
|
784
|
+
const eventTarget = target ?? (typeof document === 'undefined' ? new EventTarget() : document.activeElement || window);
|
|
785
|
+
const event = new KeyboardEvent('keydown', this.asKeyboardEventInit(normalizedInput));
|
|
786
|
+
Object.defineProperty(event, 'target', { value: eventTarget });
|
|
787
|
+
this.runNormalizedKeyboardInput(normalizedInput, event, keyCodes);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
isKeyboardShortcutsTroubleshooting(): boolean {
|
|
791
|
+
return this.keyboardShortcutsTroubleshooting;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Mirrors VS Code's `workbench.action.toggleKeybindingsLog` command.
|
|
796
|
+
* It logs how each key press was interpreted so keyboard-layout problems can be diagnosed on other machines.
|
|
797
|
+
*/
|
|
798
|
+
toggleKeyboardShortcutsTroubleshooting(): boolean {
|
|
799
|
+
this.keyboardShortcutsTroubleshooting = !this.keyboardShortcutsTroubleshooting;
|
|
800
|
+
this.logger.info(`Keyboard shortcuts troubleshooting is now ${this.keyboardShortcutsTroubleshooting ? 'active' : 'inactive'}.`);
|
|
801
|
+
return this.keyboardShortcutsTroubleshooting;
|
|
802
|
+
}
|
|
803
|
+
|
|
594
804
|
protected asKeyboardEventInit(input: KeyboardEventInit | KeyCode | string): KeyboardEventInit & Partial<{ keyCode: number }> {
|
|
595
805
|
if (typeof input === 'string') {
|
|
596
806
|
return this.asKeyboardEventInit(KeyCode.createKeyCode(input));
|
|
@@ -625,17 +835,7 @@ export class KeybindingRegistry {
|
|
|
625
835
|
};
|
|
626
836
|
win.document.addEventListener('compositionend', compositionEnd);
|
|
627
837
|
|
|
628
|
-
const keydown = (event: KeyboardEvent) =>
|
|
629
|
-
// Skip IME-related keydowns. `inComposition` covers the textarea input path;
|
|
630
|
-
// `isComposing` and `keyCode === 229` cover the native EditContext path where
|
|
631
|
-
// composition events don't reach the DOM. The keyCode check catches keys that
|
|
632
|
-
// finalize a composition (e.g. arrow keys) where isComposing is already false.
|
|
633
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
634
|
-
if (inComposition || event.isComposing || event.keyCode === 229) {
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
this.run(event);
|
|
638
|
-
};
|
|
838
|
+
const keydown = (event: KeyboardEvent) => this.run(event, inComposition);
|
|
639
839
|
win.document.addEventListener('keydown', keydown, true);
|
|
640
840
|
|
|
641
841
|
return Disposable.create(() => {
|
|
@@ -647,42 +847,210 @@ export class KeybindingRegistry {
|
|
|
647
847
|
/**
|
|
648
848
|
* Run the command matching to the given keyboard event.
|
|
649
849
|
*/
|
|
650
|
-
run(event: KeyboardEvent): void {
|
|
651
|
-
|
|
850
|
+
run(event: KeyboardEvent, inComposition = false): void {
|
|
851
|
+
const input = normalizeKeyboardInput(event);
|
|
852
|
+
// The textarea input path reports composition through separate DOM events.
|
|
853
|
+
if (inComposition) {
|
|
854
|
+
this.logKeyboardTroubleshooting(input, undefined, undefined, 'composition');
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
this.runNormalizedKeyboardInput(input, event);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** Return all runtime interpretations of normalized keyboard input. */
|
|
861
|
+
getKeyCodeInterpretations(input: NormalizedKeyboardInput): KeyCode[] {
|
|
862
|
+
return this.keyboardLayoutService.getKeyCodeInterpretations(normalizeKeyboardInput(input), this.corePreferences['keyboard.dispatch']);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/** Return the authored representation that should be persisted for normalized keyboard input. */
|
|
866
|
+
authoredKeyCodeForKeyboardInput(input: NormalizedKeyboardInput): KeyCode | undefined {
|
|
867
|
+
const interpretations = this.getKeyCodeInterpretations(input);
|
|
868
|
+
// macOS Option is recorded as the `alt` command modifier rather than as the character it produces.
|
|
869
|
+
const optionAsCommandModifier = isOSX && !!input.altKey;
|
|
870
|
+
const keyCode = interpretations.find(candidate => candidate.interpretation === 'layoutModifiers' && (!optionAsCommandModifier || candidate.alt))
|
|
871
|
+
?? interpretations[0];
|
|
872
|
+
if (!keyCode || keyCode.isModifierOnly()) {
|
|
873
|
+
return keyCode;
|
|
874
|
+
}
|
|
875
|
+
const fallbackLetter = keyCode.key && this.keyboardLayoutService.getLatinLetterFallback(keyCode.key);
|
|
876
|
+
if (fallbackLetter && keyCode.character && !layoutModifiersIncludeAltGraph(keyCode.layoutModifiers)
|
|
877
|
+
&& keyCode.character.toLocaleLowerCase() !== fallbackLetter) {
|
|
878
|
+
const shift = keyCode.shift || layoutModifiersIncludeShift(keyCode.layoutModifiers);
|
|
879
|
+
return new KeyCode({
|
|
880
|
+
key: keyCode.key,
|
|
881
|
+
ctrl: keyCode.ctrl,
|
|
882
|
+
alt: keyCode.alt,
|
|
883
|
+
meta: keyCode.meta,
|
|
884
|
+
shift,
|
|
885
|
+
character: shift ? fallbackLetter.toLocaleUpperCase() : fallbackLetter
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
if (keyCode.interpretation !== 'layoutModifiers' || !keyCode.character) {
|
|
889
|
+
return keyCode;
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
const parsedCharacter = KeyCode.parse(keyCode.character);
|
|
893
|
+
const logical = new KeyCode({
|
|
894
|
+
key: parsedCharacter.key,
|
|
895
|
+
ctrl: keyCode.ctrl,
|
|
896
|
+
shift: keyCode.shift,
|
|
897
|
+
alt: keyCode.alt,
|
|
898
|
+
meta: keyCode.meta,
|
|
899
|
+
character: keyCode.character,
|
|
900
|
+
authoredToken: parsedCharacter.authoredToken
|
|
901
|
+
});
|
|
902
|
+
const persisted = KeyCode.parse(logical.toString());
|
|
903
|
+
if (this.keyboardLayoutService.resolveKeyCode(persisted).dispatchString() === keyCode.dispatchString()) {
|
|
904
|
+
return logical;
|
|
905
|
+
}
|
|
906
|
+
return new KeyCode({
|
|
907
|
+
key: logical.key,
|
|
908
|
+
ctrl: logical.ctrl,
|
|
909
|
+
shift: logical.shift || layoutModifiersIncludeShift(keyCode.layoutModifiers),
|
|
910
|
+
alt: logical.alt,
|
|
911
|
+
meta: logical.meta,
|
|
912
|
+
character: logical.character
|
|
913
|
+
});
|
|
914
|
+
} catch {
|
|
915
|
+
return new KeyCode({
|
|
916
|
+
key: keyCode.key,
|
|
917
|
+
ctrl: keyCode.ctrl,
|
|
918
|
+
shift: keyCode.shift || layoutModifiersIncludeShift(keyCode.layoutModifiers),
|
|
919
|
+
alt: keyCode.alt,
|
|
920
|
+
meta: keyCode.meta,
|
|
921
|
+
character: keyCode.key?.easyString
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** Return the keybinding stroke the keybinding recorder persists for normalized keyboard input, or `undefined` for modifier-only input. */
|
|
927
|
+
authoredKeybindingStringForKeyboardInput(input: NormalizedKeyboardInput): string | undefined {
|
|
928
|
+
const keyCode = this.authoredKeyCodeForKeyboardInput(input);
|
|
929
|
+
return keyCode && !keyCode.isModifierOnly() ? keyCode.toAuthoredKeybindingString() : undefined;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
protected runNormalizedKeyboardInput(input: NormalizedKeyboardInput, event: KeyboardEvent, keyCodesOverride?: KeyCode[]): void {
|
|
933
|
+
// Skip IME-related keydowns. `isComposing` and `keyCode === 229` cover the native EditContext path where
|
|
934
|
+
// composition events do not reach the DOM. The keyCode check catches keys that finalize a composition,
|
|
935
|
+
// such as arrow keys, when isComposing is already false.
|
|
936
|
+
if (input.isComposing || input.keyCode === 229) {
|
|
937
|
+
this.logKeyboardTroubleshooting(input, undefined, undefined, 'composition');
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
if (this.handledKeyboardEvents.has(event) || event.defaultPrevented) {
|
|
941
|
+
this.logKeyboardTroubleshooting(input, undefined, undefined, this.handledKeyboardEvents.has(event) ? 'alreadyHandled' : 'defaultPrevented');
|
|
652
942
|
return;
|
|
653
943
|
}
|
|
654
944
|
|
|
655
|
-
const
|
|
656
|
-
const keyCode =
|
|
945
|
+
const keyCodes = keyCodesOverride ?? this.getKeyCodeInterpretations(input);
|
|
946
|
+
const keyCode = keyCodes[0];
|
|
657
947
|
/* Keycode is only a modifier, next keycode will be modifier + key.
|
|
658
948
|
Ignore this one. */
|
|
659
949
|
if (keyCode.isModifierOnly()) {
|
|
950
|
+
this.logKeyboardTroubleshooting(input, keyCode, undefined, 'modifierOnly');
|
|
660
951
|
return;
|
|
661
952
|
}
|
|
662
953
|
|
|
663
|
-
this.keyboardLayoutService.validateKeyCode(keyCode);
|
|
664
|
-
this.
|
|
665
|
-
|
|
954
|
+
this.keyboardLayoutService.validateKeyCode(keyCode, input);
|
|
955
|
+
const candidates = this.keySequenceCandidates.flatMap(sequence => keyCodes.flatMap(keyCodeCandidate => {
|
|
956
|
+
const candidateSequence = [...sequence, keyCodeCandidate];
|
|
957
|
+
const candidateMatch = this.matchKeybinding(candidateSequence, event);
|
|
958
|
+
return candidateMatch ? [{ sequence: candidateSequence, keyCode: keyCodeCandidate, match: candidateMatch }] : [];
|
|
959
|
+
}));
|
|
960
|
+
const prioritized = candidates.map(candidate => ({ candidate, priority: this.matchPriority(candidate) }))
|
|
961
|
+
.sort((left, right) => right.priority - left.priority);
|
|
962
|
+
const selectedEntry = prioritized[0];
|
|
963
|
+
const selected = selectedEntry?.candidate;
|
|
964
|
+
const equallyPreferred = prioritized.filter(entry => entry.priority === selectedEntry?.priority).map(entry => entry.candidate);
|
|
965
|
+
const ambiguous = selected?.match.kind === 'full'
|
|
966
|
+
&& equallyPreferred.some(candidate => candidate.match.kind === 'full'
|
|
967
|
+
&& candidate.match.runtime.binding !== selected.match.runtime.binding);
|
|
968
|
+
const match = ambiguous ? undefined : selected?.match;
|
|
969
|
+
if (ambiguous) {
|
|
970
|
+
const sequence = selected.sequence.map(code => code.dispatchString()).join(' ');
|
|
971
|
+
if (!this.warnedAmbiguousSequences.has(sequence)) {
|
|
972
|
+
this.warnedAmbiguousSequences.add(sequence);
|
|
973
|
+
this.logger.warn(`Ambiguous keybinding sequence ignored: ${sequence}`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
this.logKeyboardTroubleshooting(input, selected?.keyCode ?? keyCode, match, ambiguous ? 'ambiguous' : undefined);
|
|
666
977
|
|
|
667
|
-
if (match
|
|
668
|
-
|
|
978
|
+
if (match?.kind === 'partial') {
|
|
979
|
+
this.keySequenceCandidates = candidates.filter(candidate => candidate.match.kind === 'partial').map(candidate => candidate.sequence);
|
|
669
980
|
event.preventDefault();
|
|
670
981
|
event.stopPropagation();
|
|
671
982
|
|
|
672
983
|
this.statusBar.setElement('keybinding-status', {
|
|
673
|
-
text: nls.localize('theia/core/keybindingStatus', '{0} was pressed, waiting for more keys', `(${this.acceleratorForSequence(
|
|
984
|
+
text: nls.localize('theia/core/keybindingStatus', '{0} was pressed, waiting for more keys', `(${this.acceleratorForSequence(selected.sequence, '+')})`),
|
|
674
985
|
alignment: StatusBarAlignment.LEFT,
|
|
675
986
|
priority: 2
|
|
676
987
|
});
|
|
677
988
|
} else {
|
|
678
|
-
if (match
|
|
679
|
-
this.executeKeyBinding(match.binding, event);
|
|
989
|
+
if (match?.kind === 'full') {
|
|
990
|
+
this.executeKeyBinding(match.runtime.binding, event);
|
|
680
991
|
}
|
|
681
|
-
this.
|
|
992
|
+
this.keySequenceCandidates = [[]];
|
|
682
993
|
this.statusBar.removeElement('keybinding-status');
|
|
683
994
|
}
|
|
684
995
|
}
|
|
685
996
|
|
|
997
|
+
/** Higher values win; explicit command modifiers take precedence over layout-modifier interpretations. */
|
|
998
|
+
protected matchPriority(candidate: KeybindingRegistry.MatchedCandidate): number {
|
|
999
|
+
const usesLayoutModifiers = candidate.keyCode.interpretation === 'layoutModifiers';
|
|
1000
|
+
return (usesLayoutModifiers ? 0 : 2) + (candidate.match.kind === 'full' ? 1 : 0);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
protected logKeyboardTroubleshooting(
|
|
1004
|
+
input: NormalizedKeyboardInput,
|
|
1005
|
+
keyCode?: KeyCode,
|
|
1006
|
+
match?: KeybindingRegistry.Match,
|
|
1007
|
+
skipped?: string
|
|
1008
|
+
): void {
|
|
1009
|
+
if (!this.keyboardShortcutsTroubleshooting) {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
this.logger.info('Keyboard shortcut troubleshooting', {
|
|
1013
|
+
input: {
|
|
1014
|
+
key: input.key,
|
|
1015
|
+
code: input.code,
|
|
1016
|
+
keyCode: input.keyCode,
|
|
1017
|
+
ctrlKey: input.ctrlKey,
|
|
1018
|
+
shiftKey: input.shiftKey,
|
|
1019
|
+
altKey: input.altKey,
|
|
1020
|
+
metaKey: input.metaKey,
|
|
1021
|
+
altGraph: input.altGraph,
|
|
1022
|
+
controlModifier: input.getModifierState?.('Control'),
|
|
1023
|
+
altModifier: input.getModifierState?.('Alt'),
|
|
1024
|
+
repeat: input.repeat,
|
|
1025
|
+
isComposing: input.isComposing,
|
|
1026
|
+
location: input.location,
|
|
1027
|
+
timeStamp: input.timeStamp
|
|
1028
|
+
},
|
|
1029
|
+
normalized: keyCode && {
|
|
1030
|
+
key: keyCode.key?.code,
|
|
1031
|
+
character: keyCode.character,
|
|
1032
|
+
ctrl: keyCode.ctrl,
|
|
1033
|
+
shift: keyCode.shift,
|
|
1034
|
+
alt: keyCode.alt,
|
|
1035
|
+
meta: keyCode.meta,
|
|
1036
|
+
layoutModifiers: keyCode.layoutModifiers,
|
|
1037
|
+
interpretation: keyCode.interpretation,
|
|
1038
|
+
dispatch: keyCode.dispatchString()
|
|
1039
|
+
},
|
|
1040
|
+
layout: {
|
|
1041
|
+
info: this.keyboardLayoutService.layoutInfo,
|
|
1042
|
+
source: this.keyboardLayoutService.layoutSource
|
|
1043
|
+
},
|
|
1044
|
+
match: match && {
|
|
1045
|
+
kind: match.kind,
|
|
1046
|
+
command: match.runtime.binding.command,
|
|
1047
|
+
keybinding: match.runtime.binding.keybinding,
|
|
1048
|
+
interpretations: match.runtime.sequence.map(code => code.interpretation)
|
|
1049
|
+
},
|
|
1050
|
+
skipped
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
|
|
686
1054
|
/**
|
|
687
1055
|
* Match first binding in the current context.
|
|
688
1056
|
* Keybindings ordered by a scope and by a registration order within the scope.
|
|
@@ -690,10 +1058,14 @@ export class KeybindingRegistry {
|
|
|
690
1058
|
* Uses a ternary search tree for efficient O(log n) lookup instead of O(n) iteration.
|
|
691
1059
|
* The tree is lazily built and invalidated when keybindings change.
|
|
692
1060
|
*
|
|
1061
|
+
* `keySequence` must be a resolved sequence from {@link resolveKeybinding} or a runtime interpretation from
|
|
1062
|
+
* {@link getKeyCodeInterpretations}. Lookup uses {@link KeyCode.dispatchString} identity, so raw
|
|
1063
|
+
* {@link KeySequence.parse} output only matches when parsing and resolution coincide, such as `keyboard.dispatch: 'keyCode'`.
|
|
1064
|
+
*
|
|
693
1065
|
* When multiple bindings match, bindings that use context keys local to the focused
|
|
694
1066
|
* element are given priority over bindings that only use global context keys.
|
|
695
1067
|
*/
|
|
696
|
-
matchKeybinding(keySequence: KeySequence, event?: KeyboardEvent): KeybindingRegistry.Match {
|
|
1068
|
+
matchKeybinding(keySequence: KeySequence, event?: KeyboardEvent): KeybindingRegistry.Match | undefined {
|
|
697
1069
|
let disabled: Set<string> | undefined;
|
|
698
1070
|
const isEnabled = (binding: ScopedKeybinding) => {
|
|
699
1071
|
const { command, context, when, keybinding } = binding;
|
|
@@ -713,36 +1085,32 @@ export class KeybindingRegistry {
|
|
|
713
1085
|
// Check for exact (FULL) matches first
|
|
714
1086
|
const fullMatches = tree.get(keySequence);
|
|
715
1087
|
if (fullMatches) {
|
|
716
|
-
|
|
717
|
-
const enabledBindings = fullMatches.filter(isEnabled);
|
|
1088
|
+
const enabledForms = fullMatches.filter(runtimeForm => isEnabled(runtimeForm.binding));
|
|
718
1089
|
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
const
|
|
722
|
-
return { kind: 'full',
|
|
1090
|
+
if (enabledForms.length > 0) {
|
|
1091
|
+
const selectedBinding = this.selectBindingByLocalContext(enabledForms.map(candidate => candidate.binding), event);
|
|
1092
|
+
const runtimeForm = enabledForms.find(candidate => candidate.binding === selectedBinding)!;
|
|
1093
|
+
return { kind: 'full', runtime: runtimeForm };
|
|
723
1094
|
}
|
|
724
1095
|
}
|
|
725
1096
|
|
|
726
1097
|
// Check for PARTIAL matches (bindings that start with keySequence but are longer)
|
|
727
1098
|
const partialIterator = tree.findSuperstr(keySequence);
|
|
728
1099
|
if (partialIterator) {
|
|
729
|
-
|
|
730
|
-
const partialBindings: ScopedKeybinding[] = [];
|
|
1100
|
+
const partialForms: KeybindingRegistry.RuntimeKeybinding[] = [];
|
|
731
1101
|
let result = partialIterator.next();
|
|
732
1102
|
while (!result.done) {
|
|
733
|
-
|
|
1103
|
+
partialForms.push(...result.value);
|
|
734
1104
|
result = partialIterator.next();
|
|
735
1105
|
}
|
|
736
|
-
|
|
737
|
-
partialBindings.sort((a, b) => b.scope - a.scope);
|
|
1106
|
+
partialForms.sort((a, b) => b.binding.scope - a.binding.scope);
|
|
738
1107
|
|
|
739
|
-
|
|
740
|
-
const enabledBindings = partialBindings.filter(isEnabled);
|
|
1108
|
+
const enabledForms = partialForms.filter(runtimeForm => isEnabled(runtimeForm.binding));
|
|
741
1109
|
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
return { kind: 'partial',
|
|
1110
|
+
if (enabledForms.length > 0) {
|
|
1111
|
+
const selectedBinding = this.selectBindingByLocalContext(enabledForms.map(candidate => candidate.binding), event);
|
|
1112
|
+
const runtimeForm = enabledForms.find(candidate => candidate.binding === selectedBinding)!;
|
|
1113
|
+
return { kind: 'partial', runtime: runtimeForm };
|
|
746
1114
|
}
|
|
747
1115
|
}
|
|
748
1116
|
|
|
@@ -867,10 +1235,27 @@ export class KeybindingRegistry {
|
|
|
867
1235
|
}
|
|
868
1236
|
|
|
869
1237
|
export namespace KeybindingRegistry {
|
|
870
|
-
|
|
1238
|
+
/** A binding paired with the key sequence resolved for runtime matching. */
|
|
1239
|
+
export interface RuntimeKeybinding {
|
|
1240
|
+
binding: ScopedKeybinding;
|
|
1241
|
+
sequence: KeySequence;
|
|
1242
|
+
}
|
|
1243
|
+
export interface Match {
|
|
871
1244
|
kind: 'full' | 'partial'
|
|
872
|
-
|
|
873
|
-
}
|
|
1245
|
+
runtime: RuntimeKeybinding
|
|
1246
|
+
}
|
|
1247
|
+
/** A runtime interpretation and its match while selecting a keyboard input candidate. */
|
|
1248
|
+
export interface MatchedCandidate {
|
|
1249
|
+
sequence: KeySequence;
|
|
1250
|
+
keyCode: KeyCode;
|
|
1251
|
+
match: Match;
|
|
1252
|
+
}
|
|
1253
|
+
/** Collisions grouped by authored and layout-resolved identity, plus bindings that shadow the supplied binding. */
|
|
1254
|
+
export interface CollisionDiagnostics {
|
|
1255
|
+
authored: KeybindingsResult;
|
|
1256
|
+
layoutDerived: KeybindingsResult;
|
|
1257
|
+
shadowedBy: ScopedKeybinding[];
|
|
1258
|
+
}
|
|
874
1259
|
export class KeybindingsResult {
|
|
875
1260
|
full: ScopedKeybinding[] = [];
|
|
876
1261
|
partial: ScopedKeybinding[] = [];
|