提交 ebd346b3 编写于 作者: A Alex Dima

Share test utils between keyboard mappers

上级 b3f8aee7
......@@ -5,598 +5,9 @@
'use strict';
import { OperatingSystem } from 'vs/base/common/platform';
import { KeyCode, ResolvedKeybinding, KeyCodeUtils, SimpleKeybinding, Keybinding, KeybindingType } from 'vs/base/common/keyCodes';
import { KeyboardEventCode, KeyboardEventCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/workbench/services/keybinding/common/keyboardEventCode';
import { CharCode } from 'vs/base/common/charCode';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
import { UILabelProvider, AriaLabelProvider } from 'vs/platform/keybinding/common/keybindingLabels';
import { Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes';
export interface IKeyMapping {
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
valueIsDeadKey?: boolean;
withShiftIsDeadKey?: boolean;
withAltGrIsDeadKey?: boolean;
withShiftAltGrIsDeadKey?: boolean;
}
export interface IKeyboardMapping {
[code: string]: IKeyMapping;
}
const LOG = false;
function log(str: string): void {
if (LOG) {
console.info(str);
}
}
const CHAR_CODE_TO_KEY_CODE: { keyCode: KeyCode; shiftKey: boolean }[] = [];
export class HardwareKeypress {
public readonly ctrlKey: boolean;
public readonly shiftKey: boolean;
public readonly altKey: boolean;
public readonly metaKey: boolean;
public readonly code: KeyboardEventCode;
constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, code: KeyboardEventCode) {
this.ctrlKey = ctrlKey;
this.shiftKey = shiftKey;
this.altKey = altKey;
this.metaKey = metaKey;
this.code = code;
}
}
export class NativeResolvedKeybinding extends ResolvedKeybinding {
private readonly _mapper: KeyboardMapper;
private readonly _OS: OperatingSystem;
private readonly _firstPart: HardwareKeypress;
private readonly _chordPart: HardwareKeypress;
constructor(mapper: KeyboardMapper, OS: OperatingSystem, firstPart: HardwareKeypress, chordPart: HardwareKeypress) {
super();
this._mapper = mapper;
this._OS = OS;
this._firstPart = firstPart;
this._chordPart = chordPart;
}
public getLabel(): string {
let firstPart = this._firstPart ? this._mapper.getUILabelForHardwareCode(this._firstPart.code) : null;
let chordPart = this._chordPart ? this._mapper.getUILabelForHardwareCode(this._chordPart.code) : null;
return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS);
}
public getAriaLabel(): string {
let firstPart = this._firstPart ? this._mapper.getAriaLabelForHardwareCode(this._firstPart.code) : null;
let chordPart = this._chordPart ? this._mapper.getAriaLabelForHardwareCode(this._chordPart.code) : null;
return AriaLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS);
}
public getHTMLLabel(): IHTMLContentElement[] {
let firstPart = this._firstPart ? this._mapper.getUILabelForHardwareCode(this._firstPart.code) : null;
let chordPart = this._chordPart ? this._mapper.getUILabelForHardwareCode(this._chordPart.code) : null;
return UILabelProvider.toHTMLLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS);
}
public getElectronAccelerator(): string {
throw new Error('TODO!');
// const usResolvedKeybinding = new USLayoutResolvedKeybinding(this._actual, OS);
// if (OS === OperatingSystem.Windows) {
// // electron menus always do the correct rendering on Windows
// return usResolvedKeybinding.getElectronAccelerator();
// }
// let usLabel = usResolvedKeybinding.getLabel();
// let label = this.getLabel();
// if (usLabel !== label) {
// // electron menus are incorrect in rendering (linux) and in rendering and interpreting (mac)
// // for non US standard keyboard layouts
// return null;
// }
// return usResolvedKeybinding.getElectronAccelerator();
}
public getUserSettingsLabel(): string {
throw new Error('TODO!');
// return KeybindingIO.writeKeybinding(this._actual, OS);
}
public isChord(): boolean {
throw new Error('TODO!');
}
public hasCtrlModifier(): boolean {
throw new Error('TODO!');
}
public hasShiftModifier(): boolean {
throw new Error('TODO!');
}
public hasAltModifier(): boolean {
throw new Error('TODO!');
}
public hasMetaModifier(): boolean {
throw new Error('TODO!');
}
public getDispatchParts(): [string, string] {
throw new Error('TODO!');
}
export interface IKeyboardMapper {
dumpDebugInfo(): string;
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
}
interface IHardwareCodeMapping {
code: KeyboardEventCode;
value: number;
withShift: number;
withAltGr: number;
withShiftAltGr: number;
}
export class KeyboardMapper {
private readonly _OS: OperatingSystem;
private readonly _codeInfo: IHardwareCodeMapping[];
private readonly _hwToKb: number[] = [];
private readonly _hwToLabel: string[] = [];
private readonly _kbToHw: number[][] = [];
constructor(rawMappings: IKeyboardMapping, OS: OperatingSystem) {
this._OS = OS;
this._hwToKb = [];
this._kbToHw = [];
this._hwToLabel = [];
for (let code = KeyboardEventCode.None; code < KeyboardEventCode.MAX_VALUE; code++) {
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[code];
if (immutableKeyCode !== -1) {
this._registerAllCombos1(false, false, false, code, immutableKeyCode);
this._hwToLabel[code] = KeyCodeUtils.toString(immutableKeyCode);
}
}
this._codeInfo = [];
let mappings: IHardwareCodeMapping[] = [], mappingsLen = 0;
for (let strCode in rawMappings) {
if (rawMappings.hasOwnProperty(strCode)) {
const code = KeyboardEventCodeUtils.toEnum(strCode);
if (code === KeyboardEventCode.None) {
log(`Unknown code ${strCode} in mapping.`);
continue;
}
if (IMMUTABLE_CODE_TO_KEY_CODE[code] !== -1) {
continue;
}
const rawMapping = rawMappings[strCode];
const value = KeyboardMapper._getCharCode(rawMapping.value);
const withShift = KeyboardMapper._getCharCode(rawMapping.withShift);
const withAltGr = KeyboardMapper._getCharCode(rawMapping.withAltGr);
const withShiftAltGr = KeyboardMapper._getCharCode(rawMapping.withShiftAltGr);
const mapping: IHardwareCodeMapping = {
code: code,
value: value,
withShift: withShift,
withAltGr: withAltGr,
withShiftAltGr: withShiftAltGr,
};
mappings[mappingsLen++] = mapping;
this._codeInfo[code] = mapping;
if (value >= CharCode.a && value <= CharCode.z) {
this._hwToLabel[code] = String.fromCharCode(CharCode.A + (value - CharCode.a));
} else if (value) {
this._hwToLabel[code] = String.fromCharCode(value);
} else {
this._hwToLabel[code] = null;
}
}
}
// Handle all `withShiftAltGr` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
const withShiftAltGr = mapping.withShiftAltGr;
if (withShiftAltGr === mapping.withAltGr || withShiftAltGr === mapping.withShift || withShiftAltGr === mapping.value) {
// handled below
continue;
}
this._registerCharCode(mapping.code, true, true, true, withShiftAltGr);
}
// Handle all `withAltGr` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
const withAltGr = mapping.withAltGr;
if (withAltGr === mapping.withShift || withAltGr === mapping.value) {
// handled below
continue;
}
this._registerCharCode(mapping.code, true, false, true, withAltGr);
}
// Handle all `withShift` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
const withShift = mapping.withShift;
if (withShift === mapping.value) {
// handled below
continue;
}
this._registerCharCode(mapping.code, false, true, false, withShift);
}
// Handle all `value` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
this._registerCharCode(mapping.code, false, false, false, mapping.value);
}
// // Handle all left-over available digits
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit1, KeyCode.KEY_1);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit2, KeyCode.KEY_2);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit3, KeyCode.KEY_3);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit4, KeyCode.KEY_4);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit5, KeyCode.KEY_5);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit6, KeyCode.KEY_6);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit7, KeyCode.KEY_7);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit8, KeyCode.KEY_8);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit9, KeyCode.KEY_9);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit0, KeyCode.KEY_0);
for (let i = 0; i < KeyboardEventCode.MAX_VALUE; i++) {
let base = (i << 3);
for (let j = 0; j < 8; j++) {
let actual = base + j;
let entry = this._hwToKb[actual];
if (typeof entry === 'undefined') {
log(`${KeyboardEventCodeUtils.toString(i)} - ${j.toString(2)} --- is missing`);
}
}
}
}
public dumpDebugInfo(): string {
let result: string[] = [];
let cnt = 0;
result.push(`--------------------------------------------------------------------------------------------------`);
for (let code = KeyboardEventCode.None; code < KeyboardEventCode.MAX_VALUE; code++) {
if (IMMUTABLE_CODE_TO_KEY_CODE[code] !== -1) {
continue;
}
if (cnt % 4 === 0) {
result.push(`| HW Code combination | Key | KeyCode combination | UI label |`);
result.push(`--------------------------------------------------------------------------------------------------`);
}
cnt++;
const mapping = this._codeInfo[code];
const strCode = KeyboardEventCodeUtils.toString(code);
const uiLabel = this._hwToLabel[code];
for (let mod = 0; mod < 8; mod++) {
const hwCtrlKey = (mod & 0b0001) ? true : false;
const hwShiftKey = (mod & 0b0010) ? true : false;
const hwAltKey = (mod & 0b0100) ? true : false;
const strHw = `${hwCtrlKey ? 'Ctrl+' : ''}${hwShiftKey ? 'Shift+' : ''}${hwAltKey ? 'Alt+' : ''}${strCode}`;
const uiHwLabel = `${hwCtrlKey ? 'Ctrl+' : ''}${hwShiftKey ? 'Shift+' : ''}${hwAltKey ? 'Alt+' : ''}${uiLabel}`;
let key = 0;
if (mapping) {
if (hwCtrlKey && hwShiftKey && hwAltKey) {
key = mapping.withShiftAltGr;
} else if (hwCtrlKey && hwAltKey) {
key = mapping.withAltGr;
} else if (hwShiftKey) {
key = mapping.withShift;
} else {
key = mapping.value;
}
}
let strKey: string = ' --- ';
if (key !== 0) {
if (key >= CharCode.U_Combining_Grave_Accent && key <= CharCode.U_Combining_Latin_Small_Letter_X) {
// combining
strKey = 'U+' + key.toString(16);
} else {
strKey = ' ' + String.fromCharCode(key) + ' ';
}
}
const hwEncoded = this._encode(hwCtrlKey, hwShiftKey, hwAltKey, code);
const kbEncoded = this._hwToKb[hwEncoded];
const kbCtrlKey = (kbEncoded & 0b0001) ? true : false;
const kbShiftKey = (kbEncoded & 0b0010) ? true : false;
const kbAltKey = (kbEncoded & 0b0100) ? true : false;
const keyCode = (kbEncoded >>> 3);
const strKb = `${kbCtrlKey ? 'Ctrl+' : ''}${kbShiftKey ? 'Shift+' : ''}${kbAltKey ? 'Alt+' : ''}${KeyCodeUtils.toString(keyCode)}`;
result.push(`| ${this._leftPad(strHw, 30)} | ${strKey} | ${this._leftPad(strKb, 25)} | ${this._leftPad(uiHwLabel, 25)} |`);
}
result.push(`--------------------------------------------------------------------------------------------------`);
}
return result.join('\n');
}
private _leftPad(str: string, cnt: number): string {
while (str.length < cnt) {
str = ' ' + str;
}
return str;
}
private _registerIfUnknown(
hwCtrlKey: boolean, hwShiftKey: boolean, hwAltKey: boolean, code: KeyboardEventCode,
kbCtrlKey: boolean, kbShiftKey: boolean, kbAltKey: boolean, keyCode: KeyCode,
): void {
let hwEncoded = this._encode(hwCtrlKey, hwShiftKey, hwAltKey, code);
let kbEncoded = this._encode(kbCtrlKey, kbShiftKey, kbAltKey, keyCode);
let existing = this._hwToKb[hwEncoded] | 0;
let existingKeyCode = existing >>> 3;
if (existingKeyCode !== 0) {
return;
}
this._hwToKb[hwEncoded] = kbEncoded;
this._kbToHw[kbEncoded] = this._kbToHw[kbEncoded] || [];
this._kbToHw[kbEncoded].unshift(hwEncoded);
}
private _registerAllCombos1(
_ctrlKey: boolean, _shiftKey: boolean, _altKey: boolean, code: KeyboardEventCode,
keyCode: KeyCode,
): void {
for (let _ctrl = (_ctrlKey ? 1 : 0); _ctrl <= 1; _ctrl++) {
const ctrlKey = (_ctrl ? true : false);
for (let _shift = (_shiftKey ? 1 : 0); _shift <= 1; _shift++) {
const shiftKey = (_shift ? true : false);
for (let _alt = (_altKey ? 1 : 0); _alt <= 1; _alt++) {
const altKey = (_alt ? true : false);
this._registerIfUnknown(
ctrlKey, shiftKey, altKey, code,
ctrlKey, shiftKey, altKey, keyCode
);
}
}
}
}
private _registerAllCombos2(
hwCtrlKey: boolean, hwShiftKey: boolean, hwAltKey: boolean, code: KeyboardEventCode,
kbShiftKey: boolean, keyCode: KeyCode,
): void {
this._registerIfUnknown(
hwCtrlKey, hwShiftKey, hwAltKey, code,
false, kbShiftKey, false, keyCode
);
if (!kbShiftKey) {
for (let _ctrl = (hwCtrlKey ? 1 : 0); _ctrl <= 1; _ctrl++) {
const ctrlKey = (_ctrl ? true : false);
for (let _alt = (hwAltKey ? 1 : 0); _alt <= 1; _alt++) {
const altKey = (_alt ? true : false);
this._registerIfUnknown(
ctrlKey, hwShiftKey, altKey, code,
ctrlKey, kbShiftKey, altKey, keyCode
);
this._registerIfUnknown(
ctrlKey, true, altKey, code,
ctrlKey, true, altKey, keyCode
);
}
}
} else {
for (let _ctrl = (hwCtrlKey ? 1 : 0); _ctrl <= 1; _ctrl++) {
const ctrlKey = (_ctrl ? true : false);
for (let _alt = (hwAltKey ? 1 : 0); _alt <= 1; _alt++) {
const altKey = (_alt ? true : false);
this._registerIfUnknown(
ctrlKey, hwShiftKey, altKey, code,
ctrlKey, kbShiftKey, altKey, keyCode
);
}
}
}
}
private _registerCharCode(code: KeyboardEventCode, ctrlKey: boolean, shiftKey: boolean, altKey: boolean, charCode: number): void {
let _kb = KeyboardMapper._charCodeToKb(charCode);
let kb = _kb ? {
ctrlKey: false,
shiftKey: _kb.shiftKey,
altKey: false,
keyCode: _kb.keyCode
} : null;
if (!_kb) {
this._registerAllCombos1(ctrlKey, shiftKey, altKey, code, KeyCode.Unknown);
return;
}
this._registerAllCombos2(
ctrlKey, shiftKey, altKey, code,
kb.shiftKey, kb.keyCode
);
}
public simpleKeybindingToHardwareKeypress(keybinding: SimpleKeybinding): HardwareKeypress[] {
const kbEncoded = this._encode(keybinding.ctrlKey, keybinding.shiftKey, keybinding.altKey, keybinding.keyCode);
const hwEncoded = this._kbToHw[kbEncoded];
let result: HardwareKeypress[] = [];
if (hwEncoded) {
for (let i = 0, len = hwEncoded.length; i < len; i++) {
result[i] = this._decodeHw(hwEncoded[i], keybinding.metaKey);
}
}
return result;
}
public getUILabelForHardwareCode(code: KeyboardEventCode): string {
if (this._OS === OperatingSystem.Macintosh) {
switch (code) {
case KeyboardEventCode.ArrowLeft:
return '';
case KeyboardEventCode.ArrowUp:
return '';
case KeyboardEventCode.ArrowRight:
return '';
case KeyboardEventCode.ArrowDown:
return '';
}
}
return this._hwToLabel[code];
}
public getAriaLabelForHardwareCode(code: KeyboardEventCode): string {
return this._hwToLabel[code];
}
public resolveKeybinding(keybinding: Keybinding): NativeResolvedKeybinding[] {
let result: NativeResolvedKeybinding[] = [], resultLen = 0;
if (keybinding.type === KeybindingType.Chord) {
const firstParts = this.simpleKeybindingToHardwareKeypress(keybinding.firstPart);
const chordParts = this.simpleKeybindingToHardwareKeypress(keybinding.chordPart);
for (let i = 0, len = firstParts.length; i < len; i++) {
const firstPart = firstParts[i];
for (let j = 0, lenJ = chordParts.length; j < lenJ; j++) {
const chordPart = chordParts[j];
result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, chordPart);
}
}
} else {
const firstParts = this.simpleKeybindingToHardwareKeypress(keybinding);
for (let i = 0, len = firstParts.length; i < len; i++) {
const firstPart = firstParts[i];
result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, null);
}
}
return result;
}
private static _charCodeToKb(charCode: number): { keyCode: KeyCode; shiftKey: boolean } {
if (charCode < CHAR_CODE_TO_KEY_CODE.length) {
return CHAR_CODE_TO_KEY_CODE[charCode];
}
return null;
}
/**
* Attempt to map a combining character to a regular one that renders the same way.
*
* To the brave person following me: Good Luck!
* https://www.compart.com/en/unicode/bidiclass/NSM
*/
private static _getCharCode(char: string): number {
if (char.length === 0) {
return 0;
}
const charCode = char.charCodeAt(0);
switch (charCode) {
case CharCode.U_Combining_Grave_Accent: return CharCode.U_GRAVE_ACCENT;
case CharCode.U_Combining_Acute_Accent: return CharCode.U_ACUTE_ACCENT;
case CharCode.U_Combining_Circumflex_Accent: return CharCode.U_CIRCUMFLEX;
case CharCode.U_Combining_Tilde: return CharCode.U_SMALL_TILDE;
case CharCode.U_Combining_Macron: return CharCode.U_MACRON;
case CharCode.U_Combining_Overline: return CharCode.U_OVERLINE;
case CharCode.U_Combining_Breve: return CharCode.U_BREVE;
case CharCode.U_Combining_Dot_Above: return CharCode.U_DOT_ABOVE;
case CharCode.U_Combining_Diaeresis: return CharCode.U_DIAERESIS;
case CharCode.U_Combining_Ring_Above: return CharCode.U_RING_ABOVE;
case CharCode.U_Combining_Double_Acute_Accent: return CharCode.U_DOUBLE_ACUTE_ACCENT;
}
return charCode;
}
private _encode(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, principal: number): number {
return (
((ctrlKey ? 1 : 0) << 0)
| ((shiftKey ? 1 : 0) << 1)
| ((altKey ? 1 : 0) << 2)
| principal << 3
) >>> 0;
}
private _decodeHw(hwEncoded: number, metaKey: boolean): HardwareKeypress {
const ctrlKey = (hwEncoded & 0b0001) ? true : false;
const shiftKey = (hwEncoded & 0b0010) ? true : false;
const altKey = (hwEncoded & 0b0100) ? true : false;
const code = (hwEncoded >>> 3);
return new HardwareKeypress(ctrlKey, shiftKey, altKey, metaKey, code);
}
}
(function () {
function define(charCode: number, keyCode: KeyCode, shiftKey: boolean): void {
for (let i = CHAR_CODE_TO_KEY_CODE.length; i < charCode; i++) {
CHAR_CODE_TO_KEY_CODE[i] = null;
}
CHAR_CODE_TO_KEY_CODE[charCode] = { keyCode: keyCode, shiftKey: shiftKey };
}
for (let chCode = CharCode.A; chCode <= CharCode.Z; chCode++) {
define(chCode, KeyCode.KEY_A + (chCode - CharCode.A), true);
}
for (let chCode = CharCode.a; chCode <= CharCode.z; chCode++) {
define(chCode, KeyCode.KEY_A + (chCode - CharCode.a), false);
}
define(CharCode.Semicolon, KeyCode.US_SEMICOLON, false);
define(CharCode.Colon, KeyCode.US_SEMICOLON, true);
define(CharCode.Equals, KeyCode.US_EQUAL, false);
define(CharCode.Plus, KeyCode.US_EQUAL, true);
define(CharCode.Comma, KeyCode.US_COMMA, false);
define(CharCode.LessThan, KeyCode.US_COMMA, true);
define(CharCode.Dash, KeyCode.US_MINUS, false);
define(CharCode.Underline, KeyCode.US_MINUS, true);
define(CharCode.Period, KeyCode.US_DOT, false);
define(CharCode.GreaterThan, KeyCode.US_DOT, true);
define(CharCode.Slash, KeyCode.US_SLASH, false);
define(CharCode.QuestionMark, KeyCode.US_SLASH, true);
define(CharCode.BackTick, KeyCode.US_BACKTICK, false);
define(CharCode.Tilde, KeyCode.US_BACKTICK, true);
define(CharCode.OpenSquareBracket, KeyCode.US_OPEN_SQUARE_BRACKET, false);
define(CharCode.OpenCurlyBrace, KeyCode.US_OPEN_SQUARE_BRACKET, true);
define(CharCode.Backslash, KeyCode.US_BACKSLASH, false);
define(CharCode.Pipe, KeyCode.US_BACKSLASH, true);
define(CharCode.CloseSquareBracket, KeyCode.US_CLOSE_SQUARE_BRACKET, false);
define(CharCode.CloseCurlyBrace, KeyCode.US_CLOSE_SQUARE_BRACKET, true);
define(CharCode.SingleQuote, KeyCode.US_QUOTE, false);
define(CharCode.DoubleQuote, KeyCode.US_QUOTE, true);
})();
/*---------------------------------------------------------------------------------------------
* 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 { OperatingSystem } from 'vs/base/common/platform';
import { KeyCode, ResolvedKeybinding, KeyCodeUtils, SimpleKeybinding, Keybinding, KeybindingType } from 'vs/base/common/keyCodes';
import { KeyboardEventCode, KeyboardEventCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/workbench/services/keybinding/common/keyboardEventCode';
import { CharCode } from 'vs/base/common/charCode';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
import { UILabelProvider, AriaLabelProvider } from 'vs/platform/keybinding/common/keybindingLabels';
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
export interface IKeyMapping {
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
valueIsDeadKey?: boolean;
withShiftIsDeadKey?: boolean;
withAltGrIsDeadKey?: boolean;
withShiftAltGrIsDeadKey?: boolean;
}
export interface IKeyboardMapping {
[code: string]: IKeyMapping;
}
const LOG = false;
function log(str: string): void {
if (LOG) {
console.info(str);
}
}
const CHAR_CODE_TO_KEY_CODE: { keyCode: KeyCode; shiftKey: boolean }[] = [];
export class HardwareKeypress {
public readonly ctrlKey: boolean;
public readonly shiftKey: boolean;
public readonly altKey: boolean;
public readonly metaKey: boolean;
public readonly code: KeyboardEventCode;
constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, code: KeyboardEventCode) {
this.ctrlKey = ctrlKey;
this.shiftKey = shiftKey;
this.altKey = altKey;
this.metaKey = metaKey;
this.code = code;
}
}
export class NativeResolvedKeybinding extends ResolvedKeybinding {
private readonly _mapper: MacLinuxKeyboardMapper;
private readonly _OS: OperatingSystem;
private readonly _firstPart: HardwareKeypress;
private readonly _chordPart: HardwareKeypress;
constructor(mapper: MacLinuxKeyboardMapper, OS: OperatingSystem, firstPart: HardwareKeypress, chordPart: HardwareKeypress) {
super();
this._mapper = mapper;
this._OS = OS;
this._firstPart = firstPart;
this._chordPart = chordPart;
}
public getLabel(): string {
let firstPart = this._firstPart ? this._mapper.getUILabelForHardwareCode(this._firstPart.code) : null;
let chordPart = this._chordPart ? this._mapper.getUILabelForHardwareCode(this._chordPart.code) : null;
return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS);
}
public getAriaLabel(): string {
let firstPart = this._firstPart ? this._mapper.getAriaLabelForHardwareCode(this._firstPart.code) : null;
let chordPart = this._chordPart ? this._mapper.getAriaLabelForHardwareCode(this._chordPart.code) : null;
return AriaLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS);
}
public getHTMLLabel(): IHTMLContentElement[] {
let firstPart = this._firstPart ? this._mapper.getUILabelForHardwareCode(this._firstPart.code) : null;
let chordPart = this._chordPart ? this._mapper.getUILabelForHardwareCode(this._chordPart.code) : null;
return UILabelProvider.toHTMLLabel(this._firstPart, firstPart, this._chordPart, chordPart, this._OS);
}
public getElectronAccelerator(): string {
throw new Error('TODO!');
// const usResolvedKeybinding = new USLayoutResolvedKeybinding(this._actual, OS);
// if (OS === OperatingSystem.Windows) {
// // electron menus always do the correct rendering on Windows
// return usResolvedKeybinding.getElectronAccelerator();
// }
// let usLabel = usResolvedKeybinding.getLabel();
// let label = this.getLabel();
// if (usLabel !== label) {
// // electron menus are incorrect in rendering (linux) and in rendering and interpreting (mac)
// // for non US standard keyboard layouts
// return null;
// }
// return usResolvedKeybinding.getElectronAccelerator();
}
public getUserSettingsLabel(): string {
throw new Error('TODO!');
// return KeybindingIO.writeKeybinding(this._actual, OS);
}
public isChord(): boolean {
throw new Error('TODO!');
}
public hasCtrlModifier(): boolean {
throw new Error('TODO!');
}
public hasShiftModifier(): boolean {
throw new Error('TODO!');
}
public hasAltModifier(): boolean {
throw new Error('TODO!');
}
public hasMetaModifier(): boolean {
throw new Error('TODO!');
}
public getDispatchParts(): [string, string] {
throw new Error('TODO!');
}
}
interface IHardwareCodeMapping {
code: KeyboardEventCode;
value: number;
withShift: number;
withAltGr: number;
withShiftAltGr: number;
}
export class MacLinuxKeyboardMapper implements IKeyboardMapper {
private readonly _OS: OperatingSystem;
private readonly _codeInfo: IHardwareCodeMapping[];
private readonly _hwToKb: number[] = [];
private readonly _hwToLabel: string[] = [];
private readonly _kbToHw: number[][] = [];
constructor(rawMappings: IKeyboardMapping, OS: OperatingSystem) {
this._OS = OS;
this._hwToKb = [];
this._kbToHw = [];
this._hwToLabel = [];
for (let code = KeyboardEventCode.None; code < KeyboardEventCode.MAX_VALUE; code++) {
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[code];
if (immutableKeyCode !== -1) {
this._registerAllCombos1(false, false, false, code, immutableKeyCode);
this._hwToLabel[code] = KeyCodeUtils.toString(immutableKeyCode);
}
}
this._codeInfo = [];
let mappings: IHardwareCodeMapping[] = [], mappingsLen = 0;
for (let strCode in rawMappings) {
if (rawMappings.hasOwnProperty(strCode)) {
const code = KeyboardEventCodeUtils.toEnum(strCode);
if (code === KeyboardEventCode.None) {
log(`Unknown code ${strCode} in mapping.`);
continue;
}
if (IMMUTABLE_CODE_TO_KEY_CODE[code] !== -1) {
continue;
}
const rawMapping = rawMappings[strCode];
const value = MacLinuxKeyboardMapper._getCharCode(rawMapping.value);
const withShift = MacLinuxKeyboardMapper._getCharCode(rawMapping.withShift);
const withAltGr = MacLinuxKeyboardMapper._getCharCode(rawMapping.withAltGr);
const withShiftAltGr = MacLinuxKeyboardMapper._getCharCode(rawMapping.withShiftAltGr);
const mapping: IHardwareCodeMapping = {
code: code,
value: value,
withShift: withShift,
withAltGr: withAltGr,
withShiftAltGr: withShiftAltGr,
};
mappings[mappingsLen++] = mapping;
this._codeInfo[code] = mapping;
if (value >= CharCode.a && value <= CharCode.z) {
this._hwToLabel[code] = String.fromCharCode(CharCode.A + (value - CharCode.a));
} else if (value) {
this._hwToLabel[code] = String.fromCharCode(value);
} else {
this._hwToLabel[code] = null;
}
}
}
// Handle all `withShiftAltGr` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
const withShiftAltGr = mapping.withShiftAltGr;
if (withShiftAltGr === mapping.withAltGr || withShiftAltGr === mapping.withShift || withShiftAltGr === mapping.value) {
// handled below
continue;
}
this._registerCharCode(mapping.code, true, true, true, withShiftAltGr);
}
// Handle all `withAltGr` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
const withAltGr = mapping.withAltGr;
if (withAltGr === mapping.withShift || withAltGr === mapping.value) {
// handled below
continue;
}
this._registerCharCode(mapping.code, true, false, true, withAltGr);
}
// Handle all `withShift` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
const withShift = mapping.withShift;
if (withShift === mapping.value) {
// handled below
continue;
}
this._registerCharCode(mapping.code, false, true, false, withShift);
}
// Handle all `value` entries
for (let i = mappings.length - 1; i >= 0; i--) {
const mapping = mappings[i];
this._registerCharCode(mapping.code, false, false, false, mapping.value);
}
// // Handle all left-over available digits
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit1, KeyCode.KEY_1);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit2, KeyCode.KEY_2);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit3, KeyCode.KEY_3);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit4, KeyCode.KEY_4);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit5, KeyCode.KEY_5);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit6, KeyCode.KEY_6);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit7, KeyCode.KEY_7);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit8, KeyCode.KEY_8);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit9, KeyCode.KEY_9);
this._registerAllCombos1(false, false, false, KeyboardEventCode.Digit0, KeyCode.KEY_0);
for (let i = 0; i < KeyboardEventCode.MAX_VALUE; i++) {
let base = (i << 3);
for (let j = 0; j < 8; j++) {
let actual = base + j;
let entry = this._hwToKb[actual];
if (typeof entry === 'undefined') {
log(`${KeyboardEventCodeUtils.toString(i)} - ${j.toString(2)} --- is missing`);
}
}
}
}
public dumpDebugInfo(): string {
let result: string[] = [];
let cnt = 0;
result.push(`--------------------------------------------------------------------------------------------------`);
for (let code = KeyboardEventCode.None; code < KeyboardEventCode.MAX_VALUE; code++) {
if (IMMUTABLE_CODE_TO_KEY_CODE[code] !== -1) {
continue;
}
if (cnt % 4 === 0) {
result.push(`| HW Code combination | Key | KeyCode combination | UI label |`);
result.push(`--------------------------------------------------------------------------------------------------`);
}
cnt++;
const mapping = this._codeInfo[code];
const strCode = KeyboardEventCodeUtils.toString(code);
const uiLabel = this._hwToLabel[code];
for (let mod = 0; mod < 8; mod++) {
const hwCtrlKey = (mod & 0b0001) ? true : false;
const hwShiftKey = (mod & 0b0010) ? true : false;
const hwAltKey = (mod & 0b0100) ? true : false;
const strHw = `${hwCtrlKey ? 'Ctrl+' : ''}${hwShiftKey ? 'Shift+' : ''}${hwAltKey ? 'Alt+' : ''}${strCode}`;
const uiHwLabel = `${hwCtrlKey ? 'Ctrl+' : ''}${hwShiftKey ? 'Shift+' : ''}${hwAltKey ? 'Alt+' : ''}${uiLabel}`;
let key = 0;
if (mapping) {
if (hwCtrlKey && hwShiftKey && hwAltKey) {
key = mapping.withShiftAltGr;
} else if (hwCtrlKey && hwAltKey) {
key = mapping.withAltGr;
} else if (hwShiftKey) {
key = mapping.withShift;
} else {
key = mapping.value;
}
}
let strKey: string = ' --- ';
if (key !== 0) {
if (key >= CharCode.U_Combining_Grave_Accent && key <= CharCode.U_Combining_Latin_Small_Letter_X) {
// combining
strKey = 'U+' + key.toString(16);
} else {
strKey = ' ' + String.fromCharCode(key) + ' ';
}
}
const hwEncoded = this._encode(hwCtrlKey, hwShiftKey, hwAltKey, code);
const kbEncoded = this._hwToKb[hwEncoded];
const kbCtrlKey = (kbEncoded & 0b0001) ? true : false;
const kbShiftKey = (kbEncoded & 0b0010) ? true : false;
const kbAltKey = (kbEncoded & 0b0100) ? true : false;
const keyCode = (kbEncoded >>> 3);
const strKb = `${kbCtrlKey ? 'Ctrl+' : ''}${kbShiftKey ? 'Shift+' : ''}${kbAltKey ? 'Alt+' : ''}${KeyCodeUtils.toString(keyCode)}`;
result.push(`| ${this._leftPad(strHw, 30)} | ${strKey} | ${this._leftPad(strKb, 25)} | ${this._leftPad(uiHwLabel, 25)} |`);
}
result.push(`--------------------------------------------------------------------------------------------------`);
}
return result.join('\n');
}
private _leftPad(str: string, cnt: number): string {
while (str.length < cnt) {
str = ' ' + str;
}
return str;
}
private _registerIfUnknown(
hwCtrlKey: boolean, hwShiftKey: boolean, hwAltKey: boolean, code: KeyboardEventCode,
kbCtrlKey: boolean, kbShiftKey: boolean, kbAltKey: boolean, keyCode: KeyCode,
): void {
let hwEncoded = this._encode(hwCtrlKey, hwShiftKey, hwAltKey, code);
let kbEncoded = this._encode(kbCtrlKey, kbShiftKey, kbAltKey, keyCode);
let existing = this._hwToKb[hwEncoded] | 0;
let existingKeyCode = existing >>> 3;
if (existingKeyCode !== 0) {
return;
}
this._hwToKb[hwEncoded] = kbEncoded;
this._kbToHw[kbEncoded] = this._kbToHw[kbEncoded] || [];
this._kbToHw[kbEncoded].unshift(hwEncoded);
}
private _registerAllCombos1(
_ctrlKey: boolean, _shiftKey: boolean, _altKey: boolean, code: KeyboardEventCode,
keyCode: KeyCode,
): void {
for (let _ctrl = (_ctrlKey ? 1 : 0); _ctrl <= 1; _ctrl++) {
const ctrlKey = (_ctrl ? true : false);
for (let _shift = (_shiftKey ? 1 : 0); _shift <= 1; _shift++) {
const shiftKey = (_shift ? true : false);
for (let _alt = (_altKey ? 1 : 0); _alt <= 1; _alt++) {
const altKey = (_alt ? true : false);
this._registerIfUnknown(
ctrlKey, shiftKey, altKey, code,
ctrlKey, shiftKey, altKey, keyCode
);
}
}
}
}
private _registerAllCombos2(
hwCtrlKey: boolean, hwShiftKey: boolean, hwAltKey: boolean, code: KeyboardEventCode,
kbShiftKey: boolean, keyCode: KeyCode,
): void {
this._registerIfUnknown(
hwCtrlKey, hwShiftKey, hwAltKey, code,
false, kbShiftKey, false, keyCode
);
if (!kbShiftKey) {
for (let _ctrl = (hwCtrlKey ? 1 : 0); _ctrl <= 1; _ctrl++) {
const ctrlKey = (_ctrl ? true : false);
for (let _alt = (hwAltKey ? 1 : 0); _alt <= 1; _alt++) {
const altKey = (_alt ? true : false);
this._registerIfUnknown(
ctrlKey, hwShiftKey, altKey, code,
ctrlKey, kbShiftKey, altKey, keyCode
);
this._registerIfUnknown(
ctrlKey, true, altKey, code,
ctrlKey, true, altKey, keyCode
);
}
}
} else {
for (let _ctrl = (hwCtrlKey ? 1 : 0); _ctrl <= 1; _ctrl++) {
const ctrlKey = (_ctrl ? true : false);
for (let _alt = (hwAltKey ? 1 : 0); _alt <= 1; _alt++) {
const altKey = (_alt ? true : false);
this._registerIfUnknown(
ctrlKey, hwShiftKey, altKey, code,
ctrlKey, kbShiftKey, altKey, keyCode
);
}
}
}
}
private _registerCharCode(code: KeyboardEventCode, ctrlKey: boolean, shiftKey: boolean, altKey: boolean, charCode: number): void {
let _kb = MacLinuxKeyboardMapper._charCodeToKb(charCode);
let kb = _kb ? {
ctrlKey: false,
shiftKey: _kb.shiftKey,
altKey: false,
keyCode: _kb.keyCode
} : null;
if (!_kb) {
this._registerAllCombos1(ctrlKey, shiftKey, altKey, code, KeyCode.Unknown);
return;
}
this._registerAllCombos2(
ctrlKey, shiftKey, altKey, code,
kb.shiftKey, kb.keyCode
);
}
public simpleKeybindingToHardwareKeypress(keybinding: SimpleKeybinding): HardwareKeypress[] {
const kbEncoded = this._encode(keybinding.ctrlKey, keybinding.shiftKey, keybinding.altKey, keybinding.keyCode);
const hwEncoded = this._kbToHw[kbEncoded];
let result: HardwareKeypress[] = [];
if (hwEncoded) {
for (let i = 0, len = hwEncoded.length; i < len; i++) {
result[i] = this._decodeHw(hwEncoded[i], keybinding.metaKey);
}
}
return result;
}
public getUILabelForHardwareCode(code: KeyboardEventCode): string {
if (this._OS === OperatingSystem.Macintosh) {
switch (code) {
case KeyboardEventCode.ArrowLeft:
return '';
case KeyboardEventCode.ArrowUp:
return '';
case KeyboardEventCode.ArrowRight:
return '';
case KeyboardEventCode.ArrowDown:
return '';
}
}
return this._hwToLabel[code];
}
public getAriaLabelForHardwareCode(code: KeyboardEventCode): string {
return this._hwToLabel[code];
}
public resolveKeybinding(keybinding: Keybinding): NativeResolvedKeybinding[] {
let result: NativeResolvedKeybinding[] = [], resultLen = 0;
if (keybinding.type === KeybindingType.Chord) {
const firstParts = this.simpleKeybindingToHardwareKeypress(keybinding.firstPart);
const chordParts = this.simpleKeybindingToHardwareKeypress(keybinding.chordPart);
for (let i = 0, len = firstParts.length; i < len; i++) {
const firstPart = firstParts[i];
for (let j = 0, lenJ = chordParts.length; j < lenJ; j++) {
const chordPart = chordParts[j];
result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, chordPart);
}
}
} else {
const firstParts = this.simpleKeybindingToHardwareKeypress(keybinding);
for (let i = 0, len = firstParts.length; i < len; i++) {
const firstPart = firstParts[i];
result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, null);
}
}
return result;
}
private static _charCodeToKb(charCode: number): { keyCode: KeyCode; shiftKey: boolean } {
if (charCode < CHAR_CODE_TO_KEY_CODE.length) {
return CHAR_CODE_TO_KEY_CODE[charCode];
}
return null;
}
/**
* Attempt to map a combining character to a regular one that renders the same way.
*
* To the brave person following me: Good Luck!
* https://www.compart.com/en/unicode/bidiclass/NSM
*/
private static _getCharCode(char: string): number {
if (char.length === 0) {
return 0;
}
const charCode = char.charCodeAt(0);
switch (charCode) {
case CharCode.U_Combining_Grave_Accent: return CharCode.U_GRAVE_ACCENT;
case CharCode.U_Combining_Acute_Accent: return CharCode.U_ACUTE_ACCENT;
case CharCode.U_Combining_Circumflex_Accent: return CharCode.U_CIRCUMFLEX;
case CharCode.U_Combining_Tilde: return CharCode.U_SMALL_TILDE;
case CharCode.U_Combining_Macron: return CharCode.U_MACRON;
case CharCode.U_Combining_Overline: return CharCode.U_OVERLINE;
case CharCode.U_Combining_Breve: return CharCode.U_BREVE;
case CharCode.U_Combining_Dot_Above: return CharCode.U_DOT_ABOVE;
case CharCode.U_Combining_Diaeresis: return CharCode.U_DIAERESIS;
case CharCode.U_Combining_Ring_Above: return CharCode.U_RING_ABOVE;
case CharCode.U_Combining_Double_Acute_Accent: return CharCode.U_DOUBLE_ACUTE_ACCENT;
}
return charCode;
}
private _encode(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, principal: number): number {
return (
((ctrlKey ? 1 : 0) << 0)
| ((shiftKey ? 1 : 0) << 1)
| ((altKey ? 1 : 0) << 2)
| principal << 3
) >>> 0;
}
private _decodeHw(hwEncoded: number, metaKey: boolean): HardwareKeypress {
const ctrlKey = (hwEncoded & 0b0001) ? true : false;
const shiftKey = (hwEncoded & 0b0010) ? true : false;
const altKey = (hwEncoded & 0b0100) ? true : false;
const code = (hwEncoded >>> 3);
return new HardwareKeypress(ctrlKey, shiftKey, altKey, metaKey, code);
}
}
(function () {
function define(charCode: number, keyCode: KeyCode, shiftKey: boolean): void {
for (let i = CHAR_CODE_TO_KEY_CODE.length; i < charCode; i++) {
CHAR_CODE_TO_KEY_CODE[i] = null;
}
CHAR_CODE_TO_KEY_CODE[charCode] = { keyCode: keyCode, shiftKey: shiftKey };
}
for (let chCode = CharCode.A; chCode <= CharCode.Z; chCode++) {
define(chCode, KeyCode.KEY_A + (chCode - CharCode.A), true);
}
for (let chCode = CharCode.a; chCode <= CharCode.z; chCode++) {
define(chCode, KeyCode.KEY_A + (chCode - CharCode.a), false);
}
define(CharCode.Semicolon, KeyCode.US_SEMICOLON, false);
define(CharCode.Colon, KeyCode.US_SEMICOLON, true);
define(CharCode.Equals, KeyCode.US_EQUAL, false);
define(CharCode.Plus, KeyCode.US_EQUAL, true);
define(CharCode.Comma, KeyCode.US_COMMA, false);
define(CharCode.LessThan, KeyCode.US_COMMA, true);
define(CharCode.Dash, KeyCode.US_MINUS, false);
define(CharCode.Underline, KeyCode.US_MINUS, true);
define(CharCode.Period, KeyCode.US_DOT, false);
define(CharCode.GreaterThan, KeyCode.US_DOT, true);
define(CharCode.Slash, KeyCode.US_SLASH, false);
define(CharCode.QuestionMark, KeyCode.US_SLASH, true);
define(CharCode.BackTick, KeyCode.US_BACKTICK, false);
define(CharCode.Tilde, KeyCode.US_BACKTICK, true);
define(CharCode.OpenSquareBracket, KeyCode.US_OPEN_SQUARE_BRACKET, false);
define(CharCode.OpenCurlyBrace, KeyCode.US_OPEN_SQUARE_BRACKET, true);
define(CharCode.Backslash, KeyCode.US_BACKSLASH, false);
define(CharCode.Pipe, KeyCode.US_BACKSLASH, true);
define(CharCode.CloseSquareBracket, KeyCode.US_CLOSE_SQUARE_BRACKET, false);
define(CharCode.CloseCurlyBrace, KeyCode.US_CLOSE_SQUARE_BRACKET, true);
define(CharCode.SingleQuote, KeyCode.US_QUOTE, false);
define(CharCode.DoubleQuote, KeyCode.US_QUOTE, true);
})();
......@@ -11,6 +11,7 @@ import { CharCode } from 'vs/base/common/charCode';
import { UILabelProvider, AriaLabelProvider, ElectronAcceleratorLabelProvider, UserSettingsLabelProvider } from 'vs/platform/keybinding/common/keybindingLabels';
import { OperatingSystem } from 'vs/base/common/platform';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
export interface IKeyMapping {
vkey: string;
......@@ -173,7 +174,7 @@ export class WindowsNativeResolvedKeybinding extends ResolvedKeybinding {
}
}
export class WindowsKeyboardMapper {
export class WindowsKeyboardMapper implements IKeyboardMapper {
private readonly _codeInfo: IHardwareCodeMapping[];
private readonly _hwToKb: KeyCode[];
......
/*---------------------------------------------------------------------------------------------
* 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 assert from 'assert';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
import { Keybinding } from 'vs/base/common/keyCodes';
import { TPromise } from 'vs/base/common/winjs.base';
import { readFile, writeFile } from 'vs/base/node/pfs';
export interface IResolvedKeybinding {
label: string;
ariaLabel: string;
HTMLLabel: IHTMLContentElement[];
electronAccelerator: string;
userSettingsLabel: string;
isChord: boolean;
hasCtrlModifier: boolean;
hasShiftModifier: boolean;
hasAltModifier: boolean;
hasMetaModifier: boolean;
dispatchParts: [string, string];
}
export function assertResolveKeybinding(mapper: IKeyboardMapper, keybinding: Keybinding, expected: IResolvedKeybinding[]): void {
let actual: IResolvedKeybinding[] = mapper.resolveKeybinding(keybinding).map((kb => {
return {
label: kb.getLabel(),
ariaLabel: kb.getAriaLabel(),
HTMLLabel: kb.getHTMLLabel(),
electronAccelerator: kb.getElectronAccelerator(),
userSettingsLabel: kb.getUserSettingsLabel(),
isChord: kb.isChord(),
hasCtrlModifier: kb.hasCtrlModifier(),
hasShiftModifier: kb.hasShiftModifier(),
hasAltModifier: kb.hasAltModifier(),
hasMetaModifier: kb.hasMetaModifier(),
dispatchParts: kb.getDispatchParts(),
};
}));
assert.deepEqual(actual, expected);
}
function _htmlPieces(pieces: string[]): IHTMLContentElement[] {
let children: IHTMLContentElement[] = [];
for (let i = 0, len = pieces.length; i < len; i++) {
if (i !== 0) {
children.push({ tagName: 'span', text: '+' });
}
children.push({ tagName: 'span', className: 'monaco-kbkey', text: pieces[i] });
}
return children;
}
export function simpleHTMLLabel(pieces: string[]): IHTMLContentElement {
return {
tagName: 'span',
className: 'monaco-kb',
children: _htmlPieces(pieces)
};
}
export function chordHTMLLabel(firstPart: string[], chordPart: string[]): IHTMLContentElement {
return {
tagName: 'span',
className: 'monaco-kb',
children: [].concat(
_htmlPieces(firstPart),
[{ tagName: 'span', text: ' ' }],
_htmlPieces(chordPart)
)
};
}
export function readRawMapping<T>(file: string): TPromise<T> {
return readFile(require.toUrl(`vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => {
let contents = buff.toString();
let func = new Function('define', contents);
let rawMappings: T = null;
func(function (value) {
rawMappings = value;
});
return rawMappings;
});
}
export function assertMapping(mapper: IKeyboardMapper, file: string, done: (err?: any) => void): void {
const filePath = require.toUrl(`vs/workbench/services/keybinding/test/${file}`);
readFile(filePath).then((buff) => {
let expected = buff.toString();
const actual = mapper.dumpDebugInfo();
if (actual !== expected) {
writeFile(filePath, actual);
}
try {
assert.deepEqual(actual, expected);
} catch (err) {
return done(err);
}
done();
}, done);
}
......@@ -7,48 +7,24 @@
import * as assert from 'assert';
import { KeyMod, KeyCode, createKeybinding, SimpleKeybinding, Keybinding } from 'vs/base/common/keyCodes';
import { KeyboardMapper, IKeyboardMapping } from 'vs/workbench/services/keybinding/common/keyboardMapper';
import { MacLinuxKeyboardMapper, IKeyboardMapping } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper';
import { OperatingSystem } from 'vs/base/common/platform';
import { UserSettingsLabelProvider } from 'vs/platform/keybinding/common/keybindingLabels';
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { KeyboardEventCodeUtils } from 'vs/workbench/services/keybinding/common/keyboardEventCode';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
import { readFile, writeFile } from 'vs/base/node/pfs';
import { TPromise } from 'vs/base/common/winjs.base';
import { readRawMapping, assertMapping } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils';
function createKeyboardMapper(file: string, OS: OperatingSystem): TPromise<KeyboardMapper> {
return readFile(require.toUrl(`vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => {
let contents = buff.toString();
let func = new Function('define', contents);
let rawMappings: IKeyboardMapping = null;
func(function (value) {
rawMappings = value;
});
return new KeyboardMapper(rawMappings, OS);
function createKeyboardMapper(file: string, OS: OperatingSystem): TPromise<MacLinuxKeyboardMapper> {
return readRawMapping<IKeyboardMapping>(file).then((rawMappings) => {
return new MacLinuxKeyboardMapper(rawMappings, OS);
});
}
function assertMapping(mapper: KeyboardMapper, file: string, done: (err?: any) => void): void {
const filePath = require.toUrl(`vs/workbench/services/keybinding/test/${file}`);
readFile(filePath).then((buff) => {
let expected = buff.toString();
const actual = mapper.dumpDebugInfo();
if (actual !== expected) {
writeFile(filePath, actual);
}
try {
assert.deepEqual(actual, expected);
} catch (err) {
return done(err);
}
done();
}, done);
}
suite('keyboardMapper - MAC de_ch', () => {
let mapper: KeyboardMapper;
let mapper: MacLinuxKeyboardMapper;
suiteSetup((done) => {
createKeyboardMapper('mac_de_ch', OperatingSystem.Macintosh).then((_mapper) => {
......@@ -120,7 +96,7 @@ suite('keyboardMapper - MAC de_ch', () => {
suite('keyboardMapper - LINUX de_ch', () => {
let mapper: KeyboardMapper;
let mapper: MacLinuxKeyboardMapper;
suiteSetup((done) => {
createKeyboardMapper('linux_de_ch', OperatingSystem.Linux).then((_mapper) => {
......@@ -155,7 +131,7 @@ suite('keyboardMapper - LINUX de_ch', () => {
suite('keyboardMapper - LINUX de_ch', () => {
let mapper: KeyboardMapper;
let mapper: MacLinuxKeyboardMapper;
suiteSetup((done) => {
createKeyboardMapper('linux_en_us', OperatingSystem.Linux).then((_mapper) => {
......@@ -169,7 +145,7 @@ suite('keyboardMapper - LINUX de_ch', () => {
});
});
function _assertKeybindingTranslation(mapper: KeyboardMapper, OS: OperatingSystem, kb: number, _expected: string | string[]): void {
function _assertKeybindingTranslation(mapper: MacLinuxKeyboardMapper, OS: OperatingSystem, kb: number, _expected: string | string[]): void {
let expected: string[];
if (typeof _expected === 'string') {
expected = [_expected];
......
......@@ -5,108 +5,20 @@
'use strict';
import * as assert from 'assert';
import { OperatingSystem } from 'vs/base/common/platform';
import { readFile, writeFile } from 'vs/base/node/pfs';
import { TPromise } from 'vs/base/common/winjs.base';
import { WindowsKeyboardMapper, IKeyboardMapping } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper';
import { createKeybinding, KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
import { IResolvedKeybinding, assertResolveKeybinding, readRawMapping, assertMapping, simpleHTMLLabel, chordHTMLLabel } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils';
function createKeyboardMapper(file: string, OS: OperatingSystem): TPromise<WindowsKeyboardMapper> {
return readFile(require.toUrl(`vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => {
let contents = buff.toString();
let func = new Function('define', contents);
let rawMappings: IKeyboardMapping = null;
func(function (value) {
rawMappings = value;
});
function createKeyboardMapper(file: string): TPromise<WindowsKeyboardMapper> {
return readRawMapping<IKeyboardMapping>(file).then((rawMappings) => {
return new WindowsKeyboardMapper(rawMappings);
});
}
function assertMapping(mapper: WindowsKeyboardMapper, file: string, done: (err?: any) => void): void {
const filePath = require.toUrl(`vs/workbench/services/keybinding/test/${file}`);
readFile(filePath).then((buff) => {
let expected = buff.toString();
const actual = mapper.dumpDebugInfo();
if (actual !== expected) {
writeFile(filePath, actual);
}
try {
assert.deepEqual(actual, expected);
} catch (err) {
return done(err);
}
done();
}, done);
}
function _htmlPieces(pieces: string[]): IHTMLContentElement[] {
let children: IHTMLContentElement[] = [];
for (let i = 0, len = pieces.length; i < len; i++) {
if (i !== 0) {
children.push({ tagName: 'span', text: '+' });
}
children.push({ tagName: 'span', className: 'monaco-kbkey', text: pieces[i] });
}
return children;
}
function simpleHTMLLabel(pieces: string[]): IHTMLContentElement {
return {
tagName: 'span',
className: 'monaco-kb',
children: _htmlPieces(pieces)
};
}
function chordHTMLLabel(firstPart: string[], chordPart: string[]): IHTMLContentElement {
return {
tagName: 'span',
className: 'monaco-kb',
children: [].concat(
_htmlPieces(firstPart),
[{ tagName: 'span', text: ' ' }],
_htmlPieces(chordPart)
)
};
}
interface IResolvedKeybindingExpected {
label: string;
ariaLabel: string;
HTMLLabel: IHTMLContentElement[];
electronAccelerator: string;
userSettingsLabel: string;
isChord: boolean;
hasCtrlModifier: boolean;
hasShiftModifier: boolean;
hasAltModifier: boolean;
hasMetaModifier: boolean;
dispatchParts: [string, string];
}
function _assertResolvedKeybinding(mapper: WindowsKeyboardMapper, k: number, expected: IResolvedKeybindingExpected): void {
let kbs = mapper.resolveKeybinding(createKeybinding(k, OperatingSystem.Windows));
assert.equal(kbs.length, 1);
let kb = kbs[0];
let actual: IResolvedKeybindingExpected = {
label: kb.getLabel(),
ariaLabel: kb.getAriaLabel(),
HTMLLabel: kb.getHTMLLabel(),
electronAccelerator: kb.getElectronAccelerator(),
userSettingsLabel: kb.getUserSettingsLabel(),
isChord: kb.isChord(),
hasCtrlModifier: kb.hasCtrlModifier(),
hasShiftModifier: kb.hasShiftModifier(),
hasAltModifier: kb.hasAltModifier(),
hasMetaModifier: kb.hasMetaModifier(),
dispatchParts: kb.getDispatchParts(),
};
assert.deepEqual(actual, expected);
function _assertResolveKeybinding(mapper: WindowsKeyboardMapper, k: number, expected: IResolvedKeybinding): void {
assertResolveKeybinding(mapper, createKeybinding(k, OperatingSystem.Windows), [expected]);
}
suite('keyboardMapper - WINDOWS de_ch', () => {
......@@ -114,7 +26,7 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
let mapper: WindowsKeyboardMapper;
suiteSetup((done) => {
createKeyboardMapper('win_de_ch', OperatingSystem.Macintosh).then((_mapper) => {
createKeyboardMapper('win_de_ch').then((_mapper) => {
mapper = _mapper;
done();
}, done);
......@@ -124,12 +36,9 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
assertMapping(mapper, 'win_de_ch.txt', done);
});
function assertResolveKeybinding(kb: number, expected: IResolvedKeybindingExpected): void {
_assertResolvedKeybinding(mapper, kb, expected);
}
test('resolveKeybinding Ctrl+A', () => {
assertResolveKeybinding(
_assertResolveKeybinding(
mapper,
KeyMod.CtrlCmd | KeyCode.KEY_A,
{
label: 'Ctrl+A',
......@@ -148,7 +57,8 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
});
test('resolveKeybinding Ctrl+Z', () => {
assertResolveKeybinding(
_assertResolveKeybinding(
mapper,
KeyMod.CtrlCmd | KeyCode.KEY_Z,
{
label: 'Ctrl+Z',
......@@ -167,7 +77,8 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
});
test('resolveKeybinding Ctrl+]', () => {
assertResolveKeybinding(
_assertResolveKeybinding(
mapper,
KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET,
{
label: 'Ctrl+^',
......@@ -186,7 +97,8 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
});
test('resolveKeybinding Ctrl+/', () => {
assertResolveKeybinding(
_assertResolveKeybinding(
mapper,
KeyMod.CtrlCmd | KeyCode.US_SLASH,
{
label: 'Ctrl+§',
......@@ -205,7 +117,8 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
});
test('resolveKeybinding Ctrl+K Ctrl+\\', () => {
assertResolveKeybinding(
_assertResolveKeybinding(
mapper,
KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH),
{
label: 'Ctrl+K Ctrl+ä',
......@@ -229,7 +142,7 @@ suite('keyboardMapper - WINDOWS en_us', () => {
let mapper: WindowsKeyboardMapper;
suiteSetup((done) => {
createKeyboardMapper('win_en_us', OperatingSystem.Macintosh).then((_mapper) => {
createKeyboardMapper('win_en_us').then((_mapper) => {
mapper = _mapper;
done();
}, done);
......@@ -239,12 +152,9 @@ suite('keyboardMapper - WINDOWS en_us', () => {
assertMapping(mapper, 'win_en_us.txt', done);
});
function assertResolveKeybinding(kb: number, expected: IResolvedKeybindingExpected): void {
_assertResolvedKeybinding(mapper, kb, expected);
}
test('resolveKeybinding Ctrl+K Ctrl+\\', () => {
assertResolveKeybinding(
_assertResolveKeybinding(
mapper,
KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH),
{
label: 'Ctrl+K Ctrl+\\',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册