src/vs/platform/keybinding/common/abstractKeybindingService.ts

440 LOC · 299 covered · 141 uncovered · 66 ranges · 28 concepts · 18 introducers · 20 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/base/common/ime.ts · 36 LOCcommon/ime.tssrc/vs/platform/keybinding/common/keybindingResolver.ts · 415 LOCcommon/keybindingResolve…src/vs/platform/keybinding/common/resolvedKeybindingItem.ts · 57 LOCcommon/resolvedKeybindin…abstractKeybindingService.test|title=AbstractKeybindingService can trigger command that is sharing keybinding with chord|occurrence=1 · 0 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×1 · 6 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×1 · 7 introduced LOCabstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService simple tests: single- and multi-chord keybindings are dispatched a multi-chord keybinding is dispatched correctly|occurrence=1 · 0 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×1 · 2 introduced LOCabstractKeybindingServic…keybindingResolver.ts ×1 · 4 introduced LOCkeybindingResolver.ts ×1abstractKeybindingService.ts ×7 · 46 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×2 · 5 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×2 · 6 introduced LOCabstractKeybindingServic…null command ID|occurrence=1 · 0 introduced LOCnull command ID|occurren…abstractKeybindingService.test|title=AbstractKeybindingService can have spying command|occurrence=1 · 0 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×1 · 2 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×4 · 19 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×5 · 19 introduced LOCabstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding for command with context when context matches|occurrence=1, abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding when context does not match but enforceContextCheck is false|occurrence=1 · 0 introduced LOCabstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding even when label is empty string|occurrence=1, abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding label when command has a keybinding|occurrence=1 · 0 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×2 · 9 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×1 · 7 introduced LOCabstractKeybindingServic…keybindingResolver.ts ×2 · 9 introduced LOCkeybindingResolver.ts ×2abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is null|occurrence=1, abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is undefined|occurrence=1, +1 · 0 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×2 · 2 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×7 · 22 introduced LOCabstractKeybindingServic…keybindingResolver.ts ×1 · 2 introduced LOCkeybindingResolver.ts ×1abstractKeybindingService.ts ×1 · 2 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×4 · 10 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×1 · 1 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×2 · 2 introduced LOCabstractKeybindingServic…abstractKeybindingService.ts ×22 · 179 introduced LOCabstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding even when label is empty string|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding even when label is empty string|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding for command with context when context matches|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding for command with context when context matches|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding label when command has a keybinding|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding label when command has a keybinding|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding when context does not match but enforceContextCheck is false|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding appends keybinding when context does not match but enforceContextCheck is false|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when command has no keybinding|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when command has no keybinding|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is empty string|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is empty string|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is null|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is null|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is undefined|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when commandId is undefined|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when context does not match and enforceContextCheck is true|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService appendKeybinding returns only label when context does not match and enforceContextCheck is true|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService can have spying command|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService can have spying command|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService can trigger command that is sharing keybinding with chord|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService can trigger command that is sharing keybinding with chord|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService cannot trigger chord if command is overwriting|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService cannot trigger chord if command is overwriting|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService issue #16498: chord mode is quit for invalid chords|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService issue #16498: chord mode is quit for invalid chords|occurrence=1abstractKeybindingServic…abstractKeybindingService.test|title=AbstractKeybindingService issue #16833: Keybinding service should not testDispatch on modifier keys|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService issue #16833: Keybinding service should not testDispatch on modifier keys|occurrence=1abstractKeybindingServic…null command ID a multi-chord keybinding with a null command ID keeps the keybinding (shouldPreventDefault = true)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService keybindings with empty-string/null command ID a multi-chord keybinding with a null command ID keeps the keybinding (shouldPreventDefault = true)|occurrence=1null command ID a multi-…null command ID a multi-chord keybinding with an empty-string command ID keeps the keybinding (shouldPreventDefault = true)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService keybindings with empty-string/null command ID a multi-chord keybinding with an empty-string command ID keeps the keybinding (shouldPreventDefault = true)|occurrence=1null command ID a multi-…null command ID a single-chord keybinding with a null command ID unbinds the keybinding (shouldPreventDefault = false)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService keybindings with empty-string/null command ID a single-chord keybinding with a null command ID unbinds the keybinding (shouldPreventDefault = false)|occurrence=1null command ID a single…null command ID a single-chord keybinding with an empty string command ID unbinds the keybinding (shouldPreventDefault = false)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService keybindings with empty-string/null command ID a single-chord keybinding with an empty string command ID unbinds the keybinding (shouldPreventDefault = false)|occurrence=1null command ID a single…abstractKeybindingService.test|title=AbstractKeybindingService simple tests: single- and multi-chord keybindings are dispatched a multi-chord keybinding is dispatched correctly|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService simple tests: single- and multi-chord keybindings are dispatched a multi-chord keybinding is dispatched correctly|occurrence=1abstractKeybindingServic…null command ID|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/keybinding/test/common/abstractKeybindingService.test|title=AbstractKeybindingService simple tests: single- and multi-chord keybindings are dispatched a single-chord keybinding is dispatched correctly; this test makes sure the dispatch in general works before we test empty-string/null command ID|occurrence=1null command ID|occurren…Focused file · src/vs/platform/keybinding/common/abstractKeybindingService.ts · 440 LOCcommon/abstractKeybindin…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- abstractKeybindingService.ts ×22
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../base/common/actions.js';
7 > import * as arrays from '../../../base/common/arrays.js';
8 > import { IntervalTimer, TimeoutTimer } from '../../../base/common/async.js';
9 > import { illegalState } from '../../../base/common/errors.js';
10 > import { Emitter, Event } from '../../../base/common/event.js';
11 > import { IME } from '../../../base/common/ime.js';
12 > import { KeyCode } from '../../../base/common/keyCodes.js';
13 > import { Keybinding, ResolvedChord, ResolvedKeybinding, SingleModifierChord } from '../../../base/common/keybindings.js';
14 > import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
15 > import * as nls from '../../../nls.js';
16 >
17 > import { ICommandService } from '../../commands/common/commands.js';
18 > import { IContextKeyService, IContextKeyServiceTarget } from '../../contextkey/common/contextkey.js';
19 > import { IKeybindingService, IKeyboardEvent, KeybindingsSchemaContribution } from './keybinding.js';
20 > import { ResolutionResult, KeybindingResolver, ResultKind, NoMatchingKb } from './keybindingResolver.js';
21 > import { ResolvedKeybindingItem } from './resolvedKeybindingItem.js';
22 > import { ILogService } from '../../log/common/log.js';
23 > import { INotificationService, IStatusHandle } from '../../notification/common/notification.js';
24 > import { ITelemetryService } from '../../telemetry/common/telemetry.js';
25 >
26 > interface CurrentChord {
27 > keypress: string;
28 > label: string | null;
29 > }
30 >
31 > const HIGH_FREQ_COMMANDS = /^(cursor|delete|undo|redo|tab|editor\.action\.clipboard)/;
32 >
33 > export abstract class AbstractKeybindingService extends Disposable implements IKeybindingService {
34 >
35 > public _serviceBrand: undefined;
36 >
37 > protected readonly _onDidUpdateKeybindings: Emitter<void> = this._register(new Emitter<void>());
38 > get onDidUpdateKeybindings(): Event<void> {
39 > return this._onDidUpdateKeybindings ? this._onDidUpdateKeybindings.event : Event.None; // Sinon stubbing walks properties on prototype
40 > }
41 >
42 > /** recently recorded keypresses that can trigger a keybinding;
43 > *
44 > * example: say, there's "cmd+k cmd+i" keybinding;
45 > * the user pressed "cmd+k" (before they press "cmd+i")
46 > * "cmd+k" would be stored in this array, when on pressing "cmd+i", the service
47 > * would invoke the command bound by the keybinding
48 > */
49 > private _currentChords: CurrentChord[];
50 >
51 > private _currentChordChecker: IntervalTimer;
52 > private _currentChordStatusMessage: IStatusHandle | null;
53 > private _ignoreSingleModifiers: KeybindingModifierSet;
54 > private _currentSingleModifier: SingleModifierChord | null;
55 > private _currentSingleModifierClearTimeout: TimeoutTimer;
56 > protected _currentlyDispatchingCommandId: string | null;
57 >
58 > protected _logging: boolean;
59 >
60 > public get inChordMode(): boolean {
61 > return this._currentChords.length > 0; abstractKeybindingService.ts ×5
62 > }
64 > constructor(
65 > private _contextKeyService: IContextKeyService,
66 > protected _commandService: ICommandService,
67 > protected _telemetryService: ITelemetryService,
68 > private _notificationService: INotificationService,
69 > protected _logService: ILogService,
70 > ) {
71 > super();
72 >
73 > this._currentChords = [];
74 > this._currentChordChecker = new IntervalTimer();
75 > this._currentChordStatusMessage = null;
76 > this._ignoreSingleModifiers = KeybindingModifierSet.EMPTY;
77 > this._currentSingleModifier = null;
78 > this._currentSingleModifierClearTimeout = new TimeoutTimer();
79 > this._currentlyDispatchingCommandId = null;
80 > this._logging = false;
81 > }
82 >
83 >
84 > protected abstract _getResolver(): KeybindingResolver;
85 > protected abstract _documentHasFocus(): boolean;
86 > public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
87 > public abstract resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
88 > public abstract resolveUserBinding(userBinding: string): ResolvedKeybinding[];
89 > public abstract registerSchemaContribution(contribution: KeybindingsSchemaContribution): IDisposable;
90 > public abstract _dumpDebugInfo(): string;
91 > public abstract _dumpDebugInfoJSON(): string;
92 >
93 > public getDefaultKeybindingsContent(): string {
94 return '';
95 }
97 > public toggleLogging(): boolean {
98 this._logging = !this._logging;
99 return this._logging;
100 }
102 > protected _log(str: string): void {
103 > if (this._logging) { abstractKeybindingService.ts ×2
104 this._logService.info(`[KeybindingService]: ${str}`);
105 }
108 > public getDefaultKeybindings(): readonly ResolvedKeybindingItem[] {
109 return this._getResolver().getDefaultKeybindings();
110 }
112 > public getKeybindings(): readonly ResolvedKeybindingItem[] {
113 return this._getResolver().getKeybindings();
114 }
116 > public customKeybindingsCount(): number {
117 return 0;
118 }
120 > public lookupKeybindings(commandId: string): ResolvedKeybinding[] {
121 return arrays.coalesce(
122 this._getResolver().lookupKeybindings(commandId).map(item => item.resolvedKeybinding)
123 );
124 }
126 > public lookupKeybinding(commandId: string, context?: IContextKeyService, enforceContextCheck = false): ResolvedKeybinding | undefined {
127 > const result = this._getResolver().lookupPrimaryKeybinding(commandId, context || this._contextKeyService, enforceContextCheck); abstractKeybindingService.ts ×4
128 > if (!result) {
129 > return undefined; abstractKeybindingService.ts ×1
130 > }
131 > return result.resolvedKeybinding; abstractKeybindingService.ts ×2
134 > public dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
135 return this._dispatch(e, target);
136 }
138 > // TODO@ulugbekna: update namings to align with `_doDispatch`
139 > // TODO@ulugbekna: this fn doesn't seem to take into account single-modifier keybindings, eg `shift shift`
140 > public softDispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): ResolutionResult {
141 this._log(`/ Soft dispatching keyboard event`);
142 const keybinding = this.resolveKeyboardEvent(e);
143 if (keybinding.hasMultipleChords()) {
144 console.warn('keyboard event should not be mapped to multiple chords');
145 return NoMatchingKb;
146 }
147 const [firstChord,] = keybinding.getDispatchChords();
148 if (firstChord === null) {
149 // cannot be dispatched, probably only modifier keys
150 this._log(`\\ Keyboard event cannot be dispatched`);
151 return NoMatchingKb;
152 }
153
154 const contextValue = this._contextKeyService.getContext(target);
155 const currentChords = this._currentChords.map((({ keypress }) => keypress));
156 return this._getResolver().resolve(contextValue, currentChords, firstChord);
157 }
159 > private _scheduleLeaveChordMode(): void {
160 > const chordLastInteractedTime = Date.now(); abstractKeybindingService.ts ×7
161 > this._currentChordChecker.cancelAndSet(() => {
162
163 if (!this._documentHasFocus()) {
164 // Focus has been lost => leave chord mode
165 this._leaveChordMode();
166 return;
167 }
168
169 if (Date.now() - chordLastInteractedTime > 5000) {
170 // 5 seconds elapsed => leave chord mode
171 this._leaveChordMode();
172 }
173
175 > }
177 > private _expectAnotherChord(firstChord: string, keypressLabel: string | null): void {
179 > this._currentChords.push({ keypress: firstChord, label: keypressLabel });
180 >
181 > switch (this._currentChords.length) {
182 > case 0:
183 throw illegalState('impossible');
185 > // TODO@ulugbekna: revise this message and the one below (at least, fix terminology)
186 > this._currentChordStatusMessage = this._notificationService.status(nls.localize('first.chord', "({0}) was pressed. Waiting for second key of chord...", keypressLabel));
187 > break;
188 > default: {
189 const fullKeypressLabel = this._currentChords.map(({ label }) => label).join(', ');
190 this._currentChordStatusMessage = this._notificationService.status(nls.localize('next.chord', "({0}) was pressed. Waiting for next key of chord...", fullKeypressLabel));
191 }
193 >
194 > this._scheduleLeaveChordMode();
195 >
196 > if (IME.enabled) {
197 > IME.disable();
198 > }
199 > }
201 > private _leaveChordMode(): void {
202 > if (this._currentChordStatusMessage) { abstractKeybindingService.ts ×7
203 > this._currentChordStatusMessage.close();
204 > this._currentChordStatusMessage = null;
205 > }
206 > this._currentChordChecker.cancel();
207 > this._currentChords = [];
208 > IME.enable();
209 > }
211 > public dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void {
212 this._log(`/ Dispatching keybinding triggered via menu entry accelerator - ${userSettingsLabel}`);
213 const keybindings = this.resolveUserBinding(userSettingsLabel);
214 if (keybindings.length === 0) {
215 this._log(`\\ Could not resolve - ${userSettingsLabel}`);
216 } else {
217 this._doDispatch(keybindings[0], target, /*isSingleModiferChord*/false);
218 }
219 }
221 > protected _dispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
222 > return this._doDispatch(this.resolveKeyboardEvent(e), target, /*isSingleModiferChord*/false); abstractKeybindingService.ts ×7
223 > }
225 > protected _singleModifierDispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
226 const keybinding = this.resolveKeyboardEvent(e);
227 const [singleModifier,] = keybinding.getSingleModifierDispatchChords();
228
229 if (singleModifier) {
230
231 if (this._ignoreSingleModifiers.has(singleModifier)) {
232 this._log(`+ Ignoring single modifier ${singleModifier} due to it being pressed together with other keys.`);
233 this._ignoreSingleModifiers = KeybindingModifierSet.EMPTY;
234 this._currentSingleModifierClearTimeout.cancel();
235 this._currentSingleModifier = null;
236 return false;
237 }
238
239 this._ignoreSingleModifiers = KeybindingModifierSet.EMPTY;
240
241 if (this._currentSingleModifier === null) {
242 // we have a valid `singleModifier`, store it for the next keyup, but clear it in 300ms
243 this._log(`+ Storing single modifier for possible chord ${singleModifier}.`);
244 this._currentSingleModifier = singleModifier;
245 this._currentSingleModifierClearTimeout.cancelAndSet(() => {
246 this._log(`+ Clearing single modifier due to 300ms elapsed.`);
247 this._currentSingleModifier = null;
248 }, 300);
249 return false;
250 }
251
252 if (singleModifier === this._currentSingleModifier) {
253 // bingo!
254 this._log(`/ Dispatching single modifier chord ${singleModifier} ${singleModifier}`);
255 this._currentSingleModifierClearTimeout.cancel();
256 this._currentSingleModifier = null;
257 return this._doDispatch(keybinding, target, /*isSingleModiferChord*/true);
258 }
259
260 this._log(`+ Clearing single modifier due to modifier mismatch: ${this._currentSingleModifier} ${singleModifier}`);
261 this._currentSingleModifierClearTimeout.cancel();
262 this._currentSingleModifier = null;
263 return false;
264 }
265
266 // When pressing a modifier and holding it pressed with any other modifier or key combination,
267 // the pressed modifiers should no longer be considered for single modifier dispatch.
268 const [firstChord,] = keybinding.getChords();
269 this._ignoreSingleModifiers = new KeybindingModifierSet(firstChord);
270
271 if (this._currentSingleModifier !== null) {
272 this._log(`+ Clearing single modifier due to other key up.`);
273 }
274 this._currentSingleModifierClearTimeout.cancel();
275 this._currentSingleModifier = null;
276 return false;
277 }
279 > private _doDispatch(userKeypress: ResolvedKeybinding, target: IContextKeyServiceTarget, isSingleModiferChord = false): boolean {
280 > let shouldPreventDefault = false; abstractKeybindingService.ts ×7
281 >
282 > if (userKeypress.hasMultipleChords()) { // warn - because user can press a single chord at a time
283 console.warn('Unexpected keyboard event mapped to multiple chords');
284 return false;
285 }
287 > let userPressedChord: string | null = null;
288 > let currentChords: string[] | null = null;
289 >
290 > if (isSingleModiferChord) {
291 // The keybinding is the second keypress of a single modifier chord, e.g. "shift shift".
292 // A single modifier can only occur when the same modifier is pressed in short sequence,
293 // hence we disregard `_currentChord` and use the same modifier instead.
294 const [dispatchKeyname,] = userKeypress.getSingleModifierDispatchChords();
295 userPressedChord = dispatchKeyname;
296 currentChords = dispatchKeyname ? [dispatchKeyname] : []; // TODO@ulugbekna: in the `else` case we assign an empty array - make sure `resolve` can handle an empty array well
298 > [userPressedChord,] = userKeypress.getDispatchChords();
299 > currentChords = this._currentChords.map(({ keypress }) => keypress);
300 > }
301 >
302 > if (userPressedChord === null) {
303 > this._log(`\\ Keyboard event cannot be dispatched in keydown phase.`); abstractKeybindingService.ts ×1
304 > // cannot be dispatched, probably only modifier keys
305 > return shouldPreventDefault;
306 > }
308 > const contextValue = this._contextKeyService.getContext(target);
309 > const keypressLabel = userKeypress.getLabel();
310 >
311 > const resolveResult = this._getResolver().resolve(contextValue, currentChords, userPressedChord);
312 >
313 > switch (resolveResult.kind) {
314 >
315 > case ResultKind.NoMatchingKb: {
317 > this._logService.trace('KeybindingService#dispatch', keypressLabel, `[ No matching keybinding ]`);
318 >
319 > if (this.inChordMode) {
320 > const currentChordsLabel = this._currentChords.map(({ label }) => label).join(', '); abstractKeybindingService.ts ×1
321 > this._log(`+ Leaving multi-chord mode: Nothing bound to "${currentChordsLabel}, ${keypressLabel}".`);
322 > this._notificationService.status(nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", currentChordsLabel, keypressLabel), { hideAfter: 10 * 1000 /* 10s */ });
323 > this._leaveChordMode();
324 >
325 > shouldPreventDefault = true;
326 > }
327 > return shouldPreventDefault; abstractKeybindingService.ts ×2
328 > }
330 > case ResultKind.MoreChordsNeeded: {
332 > this._logService.trace('KeybindingService#dispatch', keypressLabel, `[ Several keybindings match - more chords needed ]`);
333 >
334 > shouldPreventDefault = true;
335 > this._expectAnotherChord(userPressedChord, keypressLabel);
336 > this._log(this._currentChords.length === 1 ? `+ Entering multi-chord mode...` : `+ Continuing multi-chord mode...`);
337 > return shouldPreventDefault;
338 > }
340 > case ResultKind.KbFound: {
342 > this._logService.trace('KeybindingService#dispatch', keypressLabel, `[ Will dispatch command ${resolveResult.commandId} ]`);
343 >
344 > if (resolveResult.commandId === null || resolveResult.commandId === '') {
346 > if (this.inChordMode) {
347 > const currentChordsLabel = this._currentChords.map(({ label }) => label).join(', '); abstractKeybindingService.ts ×1
348 > this._log(`+ Leaving chord mode: Nothing bound to "${currentChordsLabel}, ${keypressLabel}".`);
349 > this._notificationService.status(nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", currentChordsLabel, keypressLabel), { hideAfter: 10 * 1000 /* 10s */ });
350 > this._leaveChordMode();
351 > shouldPreventDefault = true;
352 > }
355 > if (this.inChordMode) { abstractKeybindingService.ts ×4
356 > this._leaveChordMode(); abstractKeybindingService.ts ×1
357 > }
359 > if (!resolveResult.isBubble) {
360 > shouldPreventDefault = true; abstractKeybindingService.ts ×1
361 > }
363 > this._log(`+ Invoking command ${resolveResult.commandId}.`);
364 > this._currentlyDispatchingCommandId = resolveResult.commandId;
365 > try {
366 > if (typeof resolveResult.commandArgs === 'undefined') {
367 this._commandService.executeCommand(resolveResult.commandId).then(undefined, err => this._notificationService.warn(err));
369 > this._commandService.executeCommand(resolveResult.commandId, resolveResult.commandArgs).then(undefined, err => this._notificationService.warn(err));
370 > }
371 > } finally {
372 > this._currentlyDispatchingCommandId = null;
373 > }
374 >
375 > if (!HIGH_FREQ_COMMANDS.test(resolveResult.commandId)) {
376 > this._telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: resolveResult.commandId, from: 'keybinding', detail: userKeypress.getUserSettingsLabel() ?? undefined });
377 > }
378 > }
380 > return shouldPreventDefault;
381 > }
383 > }
385 > abstract enableKeybindingHoldMode(commandId: string): Promise<void> | undefined;
386 >
387 > mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
388 if (event.ctrlKey || event.metaKey) {
389 // ignore ctrl/cmd-combination but not shift/alt-combinatios
390 return false;
391 }
392 // weak check for certain ranges. this is properly implemented in a subclass
393 // with access to the KeyboardMapperFactory.
394 if ((event.keyCode >= KeyCode.KeyA && event.keyCode <= KeyCode.KeyZ)
395 || (event.keyCode >= KeyCode.Digit0 && event.keyCode <= KeyCode.Digit9)) {
396 return true;
397 }
398 return false;
399 }
401 > public appendKeybinding(label: string, commandId: string | undefined | null, context?: IContextKeyService, enforceContextCheck?: boolean): string {
402 > if (commandId) { abstractKeybindingService.ts ×2
403 > const keybindingLabel = this.lookupKeybinding(commandId, context, enforceContextCheck)?.getLabel(); abstractKeybindingService.ts ×4
404 > if (keybindingLabel) {
405 > return nls.localize( abstractKeybindingService.ts ×2
406 > { key: 'keybindingLabel', comment: ['UI element label', 'A keybinding label'] },
407 > "{0} ({1})",
408 > label,
409 > keybindingLabel);
410 > }
412 > return label; abstractKeybindingService.ts ×1
415 >
416 > class KeybindingModifierSet {
417 >
418 > public static EMPTY = new KeybindingModifierSet(null);
419 >
420 > private readonly _ctrlKey: boolean;
421 > private readonly _shiftKey: boolean;
422 > private readonly _altKey: boolean;
423 > private readonly _metaKey: boolean;
424 >
425 > constructor(source: ResolvedChord | null) {
426 > this._ctrlKey = source ? source.ctrlKey : false;
427 > this._shiftKey = source ? source.shiftKey : false;
428 > this._altKey = source ? source.altKey : false;
429 > this._metaKey = source ? source.metaKey : false;
430 > }
431 >
432 > has(modifier: SingleModifierChord) {
433 switch (modifier) {
434 case 'ctrl': return this._ctrlKey;
435 case 'shift': return this._shiftKey;
436 case 'alt': return this._altKey;
437 case 'meta': return this._metaKey;
438 }
439 }