Skip to content

Commit f2c4335

Browse files
committed
Fixes microsoft#130396: Allow extensions to contribute keybindings using scan codes
1 parent 94503dd commit f2c4335

File tree

6 files changed

+156
-178
lines changed

6 files changed

+156
-178
lines changed

src/vs/editor/standalone/browser/simpleServices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
341341

342342
if (keybinding) {
343343
this._dynamicKeybindings.push({
344-
keybinding: keybinding,
344+
keybinding: keybinding.parts,
345345
command: commandId,
346346
when: when,
347347
weight1: 1000,
@@ -397,7 +397,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
397397
// This might be a removal keybinding item in user settings => accept it
398398
result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault, null, false);
399399
} else {
400-
const resolvedKeybindings = this.resolveKeybinding(keybinding);
400+
const resolvedKeybindings = USLayoutResolvedKeybinding.resolveUserBinding(keybinding, OS);
401401
for (const resolvedKeybinding of resolvedKeybindings) {
402402
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault, null, false);
403403
}

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

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66
import { createKeybinding, Keybinding, KeyCode, SimpleKeybinding } from 'vs/base/common/keyCodes';
77
import { OperatingSystem, OS } from 'vs/base/common/platform';
8+
import { ScanCodeBinding } from 'vs/base/common/scanCode';
89
import { CommandsRegistry, ICommandHandler, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
910
import { ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
1011
import { Registry } from 'vs/platform/registry/common/platform';
1112

1213
export interface IKeybindingItem {
13-
keybinding: Keybinding;
14+
keybinding: (SimpleKeybinding | ScanCodeBinding)[];
1415
command: string;
1516
commandArgs?: any;
1617
when: ContextKeyExpression | null | undefined;
@@ -44,11 +45,8 @@ export interface IKeybindingRule extends IKeybindings {
4445
when?: ContextKeyExpression | null | undefined;
4546
}
4647

47-
export interface IKeybindingRule2 {
48-
primary: Keybinding | null;
49-
win?: { primary: Keybinding | null; } | null;
50-
linux?: { primary: Keybinding | null; } | null;
51-
mac?: { primary: Keybinding | null; } | null;
48+
export interface IExtensionKeybindingRule {
49+
keybinding: (SimpleKeybinding | ScanCodeBinding)[];
5250
id: string;
5351
args?: any;
5452
weight: number;
@@ -72,7 +70,7 @@ export interface ICommandAndKeybindingRule extends IKeybindingRule {
7270

7371
export interface IKeybindingsRegistry {
7472
registerKeybindingRule(rule: IKeybindingRule): void;
75-
setExtensionKeybindings(rules: IKeybindingRule2[]): void;
73+
setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void;
7674
registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void;
7775
getDefaultKeybindings(): IKeybindingItem[];
7876
}
@@ -110,27 +108,6 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
110108
return kb;
111109
}
112110

113-
/**
114-
* Take current platform into account and reduce to primary & secondary.
115-
*/
116-
private static bindToCurrentPlatform2(kb: IKeybindingRule2): { primary?: Keybinding | null; } {
117-
if (OS === OperatingSystem.Windows) {
118-
if (kb && kb.win) {
119-
return kb.win;
120-
}
121-
} else if (OS === OperatingSystem.Macintosh) {
122-
if (kb && kb.mac) {
123-
return kb.mac;
124-
}
125-
} else {
126-
if (kb && kb.linux) {
127-
return kb.linux;
128-
}
129-
}
130-
131-
return kb;
132-
}
133-
134111
public registerKeybindingRule(rule: IKeybindingRule): void {
135112
const actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule);
136113

@@ -152,15 +129,12 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
152129
}
153130
}
154131

155-
public setExtensionKeybindings(rules: IKeybindingRule2[]): void {
132+
public setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void {
156133
let result: IKeybindingItem[] = [], keybindingsLen = 0;
157-
for (let i = 0, len = rules.length; i < len; i++) {
158-
const rule = rules[i];
159-
let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform2(rule);
160-
161-
if (actualKb && actualKb.primary) {
134+
for (const rule of rules) {
135+
if (rule.keybinding.length > 0) {
162136
result[keybindingsLen++] = {
163-
keybinding: actualKb.primary,
137+
keybinding: rule.keybinding,
164138
command: rule.id,
165139
commandArgs: rule.args,
166140
when: rule.when,
@@ -220,7 +194,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
220194
this._assertNoCtrlAlt(keybinding.parts[0], commandId);
221195
}
222196
this._coreKeybindings.push({
223-
keybinding: keybinding,
197+
keybinding: keybinding.parts,
224198
command: commandId,
225199
commandArgs: commandArgs,
226200
when: when,

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

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Keybinding, KeybindingModifier, KeyCode, KeyCodeUtils, SimpleKeybinding } from 'vs/base/common/keyCodes';
6+
import { ChordKeybinding, Keybinding, KeybindingModifier, KeyCode, KeyCodeUtils, SimpleKeybinding } from 'vs/base/common/keyCodes';
77
import { OperatingSystem } from 'vs/base/common/platform';
8+
import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode';
89
import { BaseResolvedKeybinding } from 'vs/platform/keybinding/common/baseResolvedKeybinding';
10+
import { removeElementsAfterNulls } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
911

1012
/**
1113
* Do not instantiate. Use KeybindingService to get a ResolvedKeybinding seeded with information about the current kb layout.
@@ -104,4 +106,89 @@ export class USLayoutResolvedKeybinding extends BaseResolvedKeybinding<SimpleKey
104106
}
105107
return null;
106108
}
109+
110+
/**
111+
* *NOTE*: Check return value for `KeyCode.Unknown`.
112+
*/
113+
private static _scanCodeToKeyCode(scanCode: ScanCode): KeyCode {
114+
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
115+
if (immutableKeyCode !== KeyCode.DependsOnKbLayout) {
116+
return immutableKeyCode;
117+
}
118+
119+
switch (scanCode) {
120+
case ScanCode.KeyA: return KeyCode.KEY_A;
121+
case ScanCode.KeyB: return KeyCode.KEY_B;
122+
case ScanCode.KeyC: return KeyCode.KEY_C;
123+
case ScanCode.KeyD: return KeyCode.KEY_D;
124+
case ScanCode.KeyE: return KeyCode.KEY_E;
125+
case ScanCode.KeyF: return KeyCode.KEY_F;
126+
case ScanCode.KeyG: return KeyCode.KEY_G;
127+
case ScanCode.KeyH: return KeyCode.KEY_H;
128+
case ScanCode.KeyI: return KeyCode.KEY_I;
129+
case ScanCode.KeyJ: return KeyCode.KEY_J;
130+
case ScanCode.KeyK: return KeyCode.KEY_K;
131+
case ScanCode.KeyL: return KeyCode.KEY_L;
132+
case ScanCode.KeyM: return KeyCode.KEY_M;
133+
case ScanCode.KeyN: return KeyCode.KEY_N;
134+
case ScanCode.KeyO: return KeyCode.KEY_O;
135+
case ScanCode.KeyP: return KeyCode.KEY_P;
136+
case ScanCode.KeyQ: return KeyCode.KEY_Q;
137+
case ScanCode.KeyR: return KeyCode.KEY_R;
138+
case ScanCode.KeyS: return KeyCode.KEY_S;
139+
case ScanCode.KeyT: return KeyCode.KEY_T;
140+
case ScanCode.KeyU: return KeyCode.KEY_U;
141+
case ScanCode.KeyV: return KeyCode.KEY_V;
142+
case ScanCode.KeyW: return KeyCode.KEY_W;
143+
case ScanCode.KeyX: return KeyCode.KEY_X;
144+
case ScanCode.KeyY: return KeyCode.KEY_Y;
145+
case ScanCode.KeyZ: return KeyCode.KEY_Z;
146+
case ScanCode.Digit1: return KeyCode.KEY_1;
147+
case ScanCode.Digit2: return KeyCode.KEY_2;
148+
case ScanCode.Digit3: return KeyCode.KEY_3;
149+
case ScanCode.Digit4: return KeyCode.KEY_4;
150+
case ScanCode.Digit5: return KeyCode.KEY_5;
151+
case ScanCode.Digit6: return KeyCode.KEY_6;
152+
case ScanCode.Digit7: return KeyCode.KEY_7;
153+
case ScanCode.Digit8: return KeyCode.KEY_8;
154+
case ScanCode.Digit9: return KeyCode.KEY_9;
155+
case ScanCode.Digit0: return KeyCode.KEY_0;
156+
case ScanCode.Minus: return KeyCode.US_MINUS;
157+
case ScanCode.Equal: return KeyCode.US_EQUAL;
158+
case ScanCode.BracketLeft: return KeyCode.US_OPEN_SQUARE_BRACKET;
159+
case ScanCode.BracketRight: return KeyCode.US_CLOSE_SQUARE_BRACKET;
160+
case ScanCode.Backslash: return KeyCode.US_BACKSLASH;
161+
case ScanCode.IntlHash: return KeyCode.Unknown; // missing
162+
case ScanCode.Semicolon: return KeyCode.US_SEMICOLON;
163+
case ScanCode.Quote: return KeyCode.US_QUOTE;
164+
case ScanCode.Backquote: return KeyCode.US_BACKTICK;
165+
case ScanCode.Comma: return KeyCode.US_COMMA;
166+
case ScanCode.Period: return KeyCode.US_DOT;
167+
case ScanCode.Slash: return KeyCode.US_SLASH;
168+
case ScanCode.IntlBackslash: return KeyCode.OEM_102;
169+
}
170+
return KeyCode.Unknown;
171+
}
172+
173+
private static _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding | null): SimpleKeybinding | null {
174+
if (!binding) {
175+
return null;
176+
}
177+
if (binding instanceof SimpleKeybinding) {
178+
return binding;
179+
}
180+
const keyCode = this._scanCodeToKeyCode(binding.scanCode);
181+
if (keyCode === KeyCode.Unknown) {
182+
return null;
183+
}
184+
return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode);
185+
}
186+
187+
public static resolveUserBinding(input: (SimpleKeybinding | ScanCodeBinding)[], os: OperatingSystem): USLayoutResolvedKeybinding[] {
188+
const parts: SimpleKeybinding[] = removeElementsAfterNulls(input.map(keybinding => this._resolveSimpleUserBinding(keybinding)));
189+
if (parts.length > 0) {
190+
return [new USLayoutResolvedKeybinding(new ChordKeybinding(parts), os)];
191+
}
192+
return [];
193+
}
107194
}

src/vs/workbench/contrib/extensions/browser/extensionEditor.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
4444
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
4545
import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer';
4646
import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update';
47-
import { KeybindingParser } from 'vs/base/common/keybindingParser';
4847
import { IStorageService } from 'vs/platform/storage/common/storage';
4948
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
5049
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
@@ -1563,12 +1562,7 @@ export class ExtensionEditor extends EditorPane {
15631562
case 'darwin': key = rawKeyBinding.mac; break;
15641563
}
15651564

1566-
const keyBinding = KeybindingParser.parseKeybinding(key || rawKeyBinding.key, OS);
1567-
if (keyBinding) {
1568-
return this.keybindingService.resolveKeybinding(keyBinding)[0];
1569-
1570-
}
1571-
return null;
1565+
return this.keybindingService.resolveUserBinding(key || rawKeyBinding.key)[0];
15721566
}
15731567

15741568
private loadContents<T>(loadingTask: () => CacheResult<T>, container: HTMLElement): Promise<T> {

0 commit comments

Comments
 (0)