Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to using new keyboard mappers #22894

Merged
merged 27 commits into from
Mar 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d102396
First cut at integrating keyboard mappers
alexdima Mar 20, 2017
0ebecd5
Merge branch 'master' into alex/keybinding
alexdima Mar 20, 2017
8a33db6
Add Inspect Key Mapppings action
alexdima Mar 20, 2017
abb9d83
Update mac_de tests
alexdima Mar 21, 2017
298b0b8
Rename KeyboardEventCode to KeyboardScanCode
alexdima Mar 21, 2017
969cbdf
Renames
alexdima Mar 21, 2017
f8faa58
Extract ScanCodeKeyCodeMapper out of MacLinuxKeyboardMapper
alexdima Mar 21, 2017
24b9ddc
Also assert/debug user settings and electron accelerator
alexdima Mar 21, 2017
13e4bfe
Prepare for a scan code mapping to N key codes
alexdima Mar 21, 2017
28d3282
Print the real results
alexdima Mar 21, 2017
2215a75
ScanCode : KeyCode is N : N
alexdima Mar 21, 2017
968da13
Improve the ScanCode -> KeyCode guessing strategy
alexdima Mar 21, 2017
3b2c730
Add a Priority column
alexdima Mar 21, 2017
4d5a6c1
Add mac_en_us test
alexdima Mar 21, 2017
bd2611a
Avoid preferring IntlBackslash if possible
alexdima Mar 21, 2017
ac1af75
Introduce, test and adopt MacLinuxFallbackKeyboardMapper
alexdima Mar 21, 2017
bb15958
native-keymap@1.2.0
alexdima Mar 22, 2017
b228c50
React to changing keyboard layout
alexdima Mar 22, 2017
801ec90
Merge remote-tracking branch 'origin/master' into alex/keybinding
alexdima Mar 22, 2017
e344ce3
KeybindingIO.readKeybinding returns a Keybinding
alexdima Mar 22, 2017
e4bc907
Improve KeybindingIO.readKeybinding
alexdima Mar 22, 2017
b059bd2
Improve Inspect KeyMap data
alexdima Mar 22, 2017
0262e47
Add support for reading scan code based keybindings from user settings
alexdima Mar 22, 2017
9003dbc
Add IKeybindingService.resolveUserBinding
alexdima Mar 23, 2017
a4c9602
Renames
alexdima Mar 23, 2017
89e15e1
Add ResolvedKeybinding.isWYSIWYG() and tests
alexdima Mar 23, 2017
7d96d3d
Make the keybindings decorators aware of scan code based keybindings
alexdima Mar 23, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"https-proxy-agent": "0.3.6",
"iconv-lite": "0.4.15",
"minimist": "1.2.0",
"native-keymap": "0.4.0",
"native-keymap": "1.2.1",
"node-pty": "0.6.2",
"semver": "4.3.6",
"v8-profiler": "jrieken/v8-profiler#vscode",
Expand Down
39 changes: 35 additions & 4 deletions src/typings/native-keymap.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,42 @@

declare module 'native-keymap' {

export interface INativeKeyMap {
key_code: string;
export interface IWindowsKeyMapping {
vkey: string;
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
}
export interface IWindowsKeyboardMapping {
[code: string]: IWindowsKeyMapping;
}
export interface ILinuxKeyMapping {
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
}
export interface ILinuxKeyboardMapping {
[code: string]: ILinuxKeyMapping;
}
export interface IMacKeyMapping {
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
valueIsDeadKey: boolean;
withShiftIsDeadKey: boolean;
withAltGrIsDeadKey: boolean;
withShiftAltGrIsDeadKey: boolean;
}
export interface IMacKeyboardMapping {
[code: string]: IMacKeyMapping;
}

export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping;

export function getKeyMap(): INativeKeyMap[];
export function getKeyMap(): IKeyboardMapping;

export interface IWindowsKeyboardLayoutInfo {
name: string;
Expand All @@ -34,5 +61,9 @@ declare module 'native-keymap' {
lang: string;
}

export function getCurrentKeyboardLayout(): IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo;
export type IKeyboardLayoutInfo = IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo;

export function getCurrentKeyboardLayout(): IKeyboardLayoutInfo;

export function onDidChangeKeyboardLayout(callback: () => void);
}
12 changes: 2 additions & 10 deletions src/vs/base/browser/keyboardEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,23 +148,15 @@ let KEY_CODE_MAP: { [keyCode: number]: KeyCode } = {};
}
})();

export function lookupKeyCode(e: KeyboardEvent): KeyCode {
return KEY_CODE_MAP[e.keyCode] || KeyCode.Unknown;
}

let extractKeyCode = function extractKeyCode(e: KeyboardEvent): KeyCode {
function extractKeyCode(e: KeyboardEvent): KeyCode {
if (e.charCode) {
// "keypress" events mostly
let char = String.fromCharCode(e.charCode).toUpperCase();
return KeyCodeUtils.fromString(char);
}
return lookupKeyCode(e);
return KEY_CODE_MAP[e.keyCode] || KeyCode.Unknown;
};

export function setExtractKeyCode(newExtractKeyCode: (e: KeyboardEvent) => KeyCode): void {
extractKeyCode = newExtractKeyCode;
}

export interface IKeyboardEvent {
readonly browserEvent: KeyboardEvent;
readonly target: HTMLElement;
Expand Down
4 changes: 4 additions & 0 deletions src/vs/base/common/keyCodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,10 @@ export abstract class ResolvedKeybinding {
* This prints the binding in a format suitable for user settings.
*/
public abstract getUserSettingsLabel(): string;
/**
* Is the user settings label reflecting the label?
*/
public abstract isWYSIWYG(): boolean;

/**
* Is the binding a chord?
Expand Down
31 changes: 31 additions & 0 deletions src/vs/code/electron-main/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import product from 'vs/platform/node/product';
import { OpenContext } from 'vs/code/common/windows';
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { isParent, isEqual, isEqualOrParent } from 'vs/platform/files/common/files';
import * as nativeKeymap from 'native-keymap';
import { IDisposable } from 'vs/base/common/lifecycle';

enum WindowError {
UNRESPONSIVE,
Expand Down Expand Up @@ -250,6 +252,12 @@ export class WindowsManager implements IWindowsMainService {
// Update our windows state before quitting and before closing windows
this.lifecycleService.onBeforeWindowClose(win => this.onBeforeWindowClose(win));
this.lifecycleService.onBeforeQuit(() => this.onBeforeQuit());

KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
WindowsManager.WINDOWS.forEach((window) => {
window.sendWhenReady('vscode:keyboardLayoutChanged');
});
});
}

// Note that onBeforeQuit() and onBeforeWindowClose() are fired in different order depending on the OS:
Expand Down Expand Up @@ -1315,3 +1323,26 @@ export class WindowsManager implements IWindowsMainService {
}
}
}

class KeyboardLayoutMonitor {

public static INSTANCE = new KeyboardLayoutMonitor();

private _emitter: Emitter<void>;
private _registered: boolean;

private constructor() {
this._emitter = new Emitter<void>();
this._registered = false;
}

public onDidChangeKeyboardLayout(callback: () => void): IDisposable {
if (!this._registered) {
this._registered = true;
nativeKeymap.onDidChangeKeyboardLayout(() => {
this._emitter.fire();
});
}
return this._emitter.event(callback);
}
}
10 changes: 7 additions & 3 deletions src/vs/editor/browser/standalone/simpleServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,8 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
return result;
}

public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] {
return [new USLayoutResolvedKeybinding(kb, OS)];
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {
return [new USLayoutResolvedKeybinding(keybinding, OS)];
}

public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
Expand All @@ -414,7 +414,11 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
keyboardEvent.metaKey,
keyboardEvent.keyCode
);
return this.resolveKeybinding(keybinding)[0];
return new USLayoutResolvedKeybinding(keybinding, OS);
}

public resolveUserBinding(userBinding: string): ResolvedKeybinding[] {
return [];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
protected abstract _getResolver(): KeybindingResolver;
public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
public abstract resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
public abstract resolveUserBinding(userBinding: string): ResolvedKeybinding[];

public getDefaultKeybindings(): string {
return '';
Expand Down
2 changes: 2 additions & 0 deletions src/vs/platform/keybinding/common/keybinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export interface IKeybindingService {

resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;

resolveUserBinding(userBinding: string): ResolvedKeybinding[];

/**
* Resolve and dispatch `keyboardEvent`, but do not invoke the command or change inner state.
*/
Expand Down
47 changes: 43 additions & 4 deletions src/vs/platform/keybinding/common/keybindingsRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,24 @@ export interface IKeybindingRule extends IKeybindings {
when: ContextKeyExpr;
}

export interface IKeybindingRule2 {
primary: Keybinding;
win?: { primary: Keybinding; };
linux?: { primary: Keybinding; };
mac?: { primary: Keybinding; };
id: string;
weight: number;
when: ContextKeyExpr;
}

export interface ICommandAndKeybindingRule extends IKeybindingRule {
handler: ICommandHandler;
description?: ICommandHandlerDescription;
}

export interface IKeybindingsRegistry {
registerKeybindingRule(rule: IKeybindingRule): void;
registerKeybindingRule2(rule: IKeybindingRule2): void;
registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void;
getDefaultKeybindings(): IKeybindingItem[];

Expand Down Expand Up @@ -108,15 +119,44 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
return kb;
}

/**
* Take current platform into account and reduce to primary & secondary.
*/
private static bindToCurrentPlatform2(kb: IKeybindingRule2): { primary?: Keybinding; } {
if (OS === OperatingSystem.Windows) {
if (kb && kb.win) {
return kb.win;
}
} else if (OS === OperatingSystem.Macintosh) {
if (kb && kb.mac) {
return kb.mac;
}
} else {
if (kb && kb.linux) {
return kb.linux;
}
}

return kb;
}

public registerKeybindingRule(rule: IKeybindingRule): void {
let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule);

if (actualKb && actualKb.primary) {
this.registerDefaultKeybinding(actualKb.primary, rule.id, rule.weight, 0, rule.when);
this.registerDefaultKeybinding(createKeybinding(actualKb.primary, OS), rule.id, rule.weight, 0, rule.when);
}

if (actualKb && Array.isArray(actualKb.secondary)) {
actualKb.secondary.forEach((k, i) => this.registerDefaultKeybinding(k, rule.id, rule.weight, -i - 1, rule.when));
actualKb.secondary.forEach((k, i) => this.registerDefaultKeybinding(createKeybinding(k, OS), rule.id, rule.weight, -i - 1, rule.when));
}
}

public registerKeybindingRule2(rule: IKeybindingRule2): void {
let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform2(rule);

if (actualKb && actualKb.primary) {
this.registerDefaultKeybinding(actualKb.primary, rule.id, rule.weight, 0, rule.when);
}
}

Expand Down Expand Up @@ -157,8 +197,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
}
}

private registerDefaultKeybinding(kb: number, commandId: string, weight1: number, weight2: number, when: ContextKeyExpr): void {
const keybinding = createKeybinding(kb, OS);
private registerDefaultKeybinding(keybinding: Keybinding, commandId: string, weight1: number, weight2: number, when: ContextKeyExpr): void {
if (OS === OperatingSystem.Windows) {
if (keybinding.type === KeybindingType.Chord) {
this._assertNoCtrlAlt(keybinding.firstPart, commandId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export class USLayoutResolvedKeybinding extends ResolvedKeybinding {
return result.toLowerCase();
}

public isWYSIWYG(): boolean {
return true;
}

public isChord(): boolean {
return (this._chordPart ? true : false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ suite('AbstractKeybindingService', () => {
return this.resolveKeybinding(keybinding)[0];
}

public resolveUserBinding(userBinding: string): ResolvedKeybinding[] {
return [];
}

public testDispatch(kb: number): boolean {
const keybinding = createSimpleKeybinding(kb, OS);
return this._dispatch({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export class MockKeybindingService implements IKeybindingService {
return this.resolveKeybinding(keybinding)[0];
}

public resolveUserBinding(userBinding: string): ResolvedKeybinding[] {
return [];
}

public lookupKeybindings(commandId: string): ResolvedKeybinding[] {
return [];
}
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/electron-browser/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { Position, IResourceInput } from 'vs/platform/editor/common/editor';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { Themable, EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';

import { remote, ipcRenderer as ipc, webFrame } from 'electron';
Expand Down Expand Up @@ -297,6 +298,11 @@ export class ElectronWindow extends Themable {
}
});

// keyboard layout changed event
ipc.on('vscode:keyboardLayoutChanged', () => {
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
});

// Configuration changes
let previousConfiguredZoomLevel: number;
this.configurationService.onDidUpdateConfiguration(e => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ import './electron-browser/toggleRenderControlCharacter';
import './electron-browser/toggleRenderWhitespace';
import './electron-browser/toggleWordWrap';
import './electron-browser/wordWrapMigration';
import './electron-browser/inspectKeybindings';
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import * as nls from 'vs/nls';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';

@editorAction
class InspectKeyMap extends EditorAction {

constructor() {
super({
id: 'workbench.action.inspectKeyMappings',
label: nls.localize('workbench.action.inspectKeyMap', "Developer: Inspect Key Mapppings"),
alias: 'Developer: Inspect Key Mapppings',
precondition: null
});
}

public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
const keybindingService = accessor.get(IKeybindingService);
const editorService = accessor.get(IWorkbenchEditorService);
const untitledEditorService = accessor.get(IUntitledEditorService);

if (keybindingService instanceof WorkbenchKeybindingService) {
const input = untitledEditorService.createOrGet(undefined, null, keybindingService.dumpDebugInfo());
editorService.openEditor(input, { pinned: true });
}
}
}