/*--------------------------------------------------------------------------------------------- * 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 * as defaultPlatform from 'vs/base/common/platform'; import {IHTMLContentElement} from 'vs/base/common/htmlContent'; export interface ISimplifiedPlatform { isMacintosh: boolean; isWindows: boolean; } /** * Virtual Key Codes, the value does not hold any inherent meaning. * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx * But these are "more general", as they should work across browsers & OS`s. */ export enum KeyCode { /** * Placed first to cover the 0 value of the enum. */ Unknown, Backspace, Tab, Enter, Shift, Ctrl, Alt, PauseBreak, CapsLock, Escape, Space, PageUp, PageDown, End, Home, LeftArrow, UpArrow, RightArrow, DownArrow, Insert, Delete, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, Meta, ContextMenu, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, NumLock, ScrollLock, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the ';:' key */ US_SEMICOLON, /** * For any country/region, the '+' key * For the US standard keyboard, the '=+' key */ US_EQUAL, /** * For any country/region, the ',' key * For the US standard keyboard, the ',<' key */ US_COMMA, /** * For any country/region, the '-' key * For the US standard keyboard, the '-_' key */ US_MINUS, /** * For any country/region, the '.' key * For the US standard keyboard, the '.>' key */ US_DOT, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the '/?' key */ US_SLASH, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the '`~' key */ US_BACKTICK, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the '[{' key */ US_OPEN_SQUARE_BRACKET, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the '\|' key */ US_BACKSLASH, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the ']}' key */ US_CLOSE_SQUARE_BRACKET, /** * Used for miscellaneous characters; it can vary by keyboard. * For the US standard keyboard, the ''"' key */ US_QUOTE, /** * Used for miscellaneous characters; it can vary by keyboard. */ OEM_8, /** * Either the angle bracket key or the backslash key on the RT 102-key keyboard. */ OEM_102, NUMPAD_0, // VK_NUMPAD0, 0x60, Numeric keypad 0 key NUMPAD_1, // VK_NUMPAD1, 0x61, Numeric keypad 1 key NUMPAD_2, // VK_NUMPAD2, 0x62, Numeric keypad 2 key NUMPAD_3, // VK_NUMPAD3, 0x63, Numeric keypad 3 key NUMPAD_4, // VK_NUMPAD4, 0x64, Numeric keypad 4 key NUMPAD_5, // VK_NUMPAD5, 0x65, Numeric keypad 5 key NUMPAD_6, // VK_NUMPAD6, 0x66, Numeric keypad 6 key NUMPAD_7, // VK_NUMPAD7, 0x67, Numeric keypad 7 key NUMPAD_8, // VK_NUMPAD8, 0x68, Numeric keypad 8 key NUMPAD_9, // VK_NUMPAD9, 0x69, Numeric keypad 9 key NUMPAD_MULTIPLY, // VK_MULTIPLY, 0x6A, Multiply key NUMPAD_ADD, // VK_ADD, 0x6B, Add key NUMPAD_SEPARATOR, // VK_SEPARATOR, 0x6C, Separator key NUMPAD_SUBTRACT, // VK_SUBTRACT, 0x6D, Subtract key NUMPAD_DECIMAL, // VK_DECIMAL, 0x6E, Decimal key NUMPAD_DIVIDE, // VK_DIVIDE, 0x6F, /** * Placed last to cover the length of the enum. */ MAX_VALUE } interface IReverseMap { [str:string]:KeyCode; } class Mapping { _fromKeyCode: string[]; _toKeyCode: IReverseMap; constructor(fromKeyCode: string[], toKeyCode: IReverseMap) { this._fromKeyCode = fromKeyCode; this._toKeyCode = toKeyCode; } fromKeyCode(keyCode:KeyCode): string { return this._fromKeyCode[keyCode]; } toKeyCode(str:string): KeyCode { if (this._toKeyCode.hasOwnProperty(str)) { return this._toKeyCode[str]; } return KeyCode.Unknown; } } function createMapping(fill1:(map:string[])=>void, fill2:(reverseMap:IReverseMap)=>void): Mapping { let MAP: string[] = []; fill1(MAP); let REVERSE_MAP: IReverseMap = {}; for (let i = 0, len = MAP.length; i < len; i++) { if (!MAP[i]) { continue; } REVERSE_MAP[MAP[i]] = i; } fill2(REVERSE_MAP); let FINAL_REVERSE_MAP: IReverseMap = {}; for (let entry in REVERSE_MAP) { if (REVERSE_MAP.hasOwnProperty(entry)) { FINAL_REVERSE_MAP[entry] = REVERSE_MAP[entry]; FINAL_REVERSE_MAP[entry.toLowerCase()] = REVERSE_MAP[entry]; } } return new Mapping(MAP, FINAL_REVERSE_MAP); } let STRING = createMapping((TO_STRING_MAP) => { TO_STRING_MAP[KeyCode.Unknown] = 'unknown'; TO_STRING_MAP[KeyCode.Backspace] = 'Backspace'; TO_STRING_MAP[KeyCode.Tab] = 'Tab'; TO_STRING_MAP[KeyCode.Enter] = 'Enter'; TO_STRING_MAP[KeyCode.Shift] = 'Shift'; TO_STRING_MAP[KeyCode.Ctrl] = 'Ctrl'; TO_STRING_MAP[KeyCode.Alt] = 'Alt'; TO_STRING_MAP[KeyCode.PauseBreak] = 'PauseBreak'; TO_STRING_MAP[KeyCode.CapsLock] = 'CapsLock'; TO_STRING_MAP[KeyCode.Escape] = 'Escape'; TO_STRING_MAP[KeyCode.Space] = 'Space'; TO_STRING_MAP[KeyCode.PageUp] = 'PageUp'; TO_STRING_MAP[KeyCode.PageDown] = 'PageDown'; TO_STRING_MAP[KeyCode.End] = 'End'; TO_STRING_MAP[KeyCode.Home] = 'Home'; TO_STRING_MAP[KeyCode.LeftArrow] = 'LeftArrow'; TO_STRING_MAP[KeyCode.UpArrow] = 'UpArrow'; TO_STRING_MAP[KeyCode.RightArrow] = 'RightArrow'; TO_STRING_MAP[KeyCode.DownArrow] = 'DownArrow'; TO_STRING_MAP[KeyCode.Insert] = 'Insert'; TO_STRING_MAP[KeyCode.Delete] = 'Delete'; TO_STRING_MAP[KeyCode.KEY_0] = '0'; TO_STRING_MAP[KeyCode.KEY_1] = '1'; TO_STRING_MAP[KeyCode.KEY_2] = '2'; TO_STRING_MAP[KeyCode.KEY_3] = '3'; TO_STRING_MAP[KeyCode.KEY_4] = '4'; TO_STRING_MAP[KeyCode.KEY_5] = '5'; TO_STRING_MAP[KeyCode.KEY_6] = '6'; TO_STRING_MAP[KeyCode.KEY_7] = '7'; TO_STRING_MAP[KeyCode.KEY_8] = '8'; TO_STRING_MAP[KeyCode.KEY_9] = '9'; TO_STRING_MAP[KeyCode.KEY_A] = 'A'; TO_STRING_MAP[KeyCode.KEY_B] = 'B'; TO_STRING_MAP[KeyCode.KEY_C] = 'C'; TO_STRING_MAP[KeyCode.KEY_D] = 'D'; TO_STRING_MAP[KeyCode.KEY_E] = 'E'; TO_STRING_MAP[KeyCode.KEY_F] = 'F'; TO_STRING_MAP[KeyCode.KEY_G] = 'G'; TO_STRING_MAP[KeyCode.KEY_H] = 'H'; TO_STRING_MAP[KeyCode.KEY_I] = 'I'; TO_STRING_MAP[KeyCode.KEY_J] = 'J'; TO_STRING_MAP[KeyCode.KEY_K] = 'K'; TO_STRING_MAP[KeyCode.KEY_L] = 'L'; TO_STRING_MAP[KeyCode.KEY_M] = 'M'; TO_STRING_MAP[KeyCode.KEY_N] = 'N'; TO_STRING_MAP[KeyCode.KEY_O] = 'O'; TO_STRING_MAP[KeyCode.KEY_P] = 'P'; TO_STRING_MAP[KeyCode.KEY_Q] = 'Q'; TO_STRING_MAP[KeyCode.KEY_R] = 'R'; TO_STRING_MAP[KeyCode.KEY_S] = 'S'; TO_STRING_MAP[KeyCode.KEY_T] = 'T'; TO_STRING_MAP[KeyCode.KEY_U] = 'U'; TO_STRING_MAP[KeyCode.KEY_V] = 'V'; TO_STRING_MAP[KeyCode.KEY_W] = 'W'; TO_STRING_MAP[KeyCode.KEY_X] = 'X'; TO_STRING_MAP[KeyCode.KEY_Y] = 'Y'; TO_STRING_MAP[KeyCode.KEY_Z] = 'Z'; TO_STRING_MAP[KeyCode.ContextMenu] = 'ContextMenu'; TO_STRING_MAP[KeyCode.F1] = 'F1'; TO_STRING_MAP[KeyCode.F2] = 'F2'; TO_STRING_MAP[KeyCode.F3] = 'F3'; TO_STRING_MAP[KeyCode.F4] = 'F4'; TO_STRING_MAP[KeyCode.F5] = 'F5'; TO_STRING_MAP[KeyCode.F6] = 'F6'; TO_STRING_MAP[KeyCode.F7] = 'F7'; TO_STRING_MAP[KeyCode.F8] = 'F8'; TO_STRING_MAP[KeyCode.F9] = 'F9'; TO_STRING_MAP[KeyCode.F10] = 'F10'; TO_STRING_MAP[KeyCode.F11] = 'F11'; TO_STRING_MAP[KeyCode.F12] = 'F12'; TO_STRING_MAP[KeyCode.F13] = 'F13'; TO_STRING_MAP[KeyCode.F14] = 'F14'; TO_STRING_MAP[KeyCode.F15] = 'F15'; TO_STRING_MAP[KeyCode.F16] = 'F16'; TO_STRING_MAP[KeyCode.F17] = 'F17'; TO_STRING_MAP[KeyCode.F18] = 'F18'; TO_STRING_MAP[KeyCode.F19] = 'F19'; TO_STRING_MAP[KeyCode.NumLock] = 'NumLock'; TO_STRING_MAP[KeyCode.ScrollLock] = 'ScrollLock'; TO_STRING_MAP[KeyCode.US_SEMICOLON] = ';'; TO_STRING_MAP[KeyCode.US_EQUAL] = '='; TO_STRING_MAP[KeyCode.US_COMMA] = ','; TO_STRING_MAP[KeyCode.US_MINUS] = '-'; TO_STRING_MAP[KeyCode.US_DOT] = '.'; TO_STRING_MAP[KeyCode.US_SLASH] = '/'; TO_STRING_MAP[KeyCode.US_BACKTICK] = '`'; TO_STRING_MAP[KeyCode.US_OPEN_SQUARE_BRACKET] = '['; TO_STRING_MAP[KeyCode.US_BACKSLASH] = '\\'; TO_STRING_MAP[KeyCode.US_CLOSE_SQUARE_BRACKET] = ']'; TO_STRING_MAP[KeyCode.US_QUOTE] = '\''; TO_STRING_MAP[KeyCode.OEM_8] = 'OEM_8'; TO_STRING_MAP[KeyCode.OEM_102] = 'OEM_102'; TO_STRING_MAP[KeyCode.NUMPAD_0] = 'NumPad0'; TO_STRING_MAP[KeyCode.NUMPAD_1] = 'NumPad1'; TO_STRING_MAP[KeyCode.NUMPAD_2] = 'NumPad2'; TO_STRING_MAP[KeyCode.NUMPAD_3] = 'NumPad3'; TO_STRING_MAP[KeyCode.NUMPAD_4] = 'NumPad4'; TO_STRING_MAP[KeyCode.NUMPAD_5] = 'NumPad5'; TO_STRING_MAP[KeyCode.NUMPAD_6] = 'NumPad6'; TO_STRING_MAP[KeyCode.NUMPAD_7] = 'NumPad7'; TO_STRING_MAP[KeyCode.NUMPAD_8] = 'NumPad8'; TO_STRING_MAP[KeyCode.NUMPAD_9] = 'NumPad9'; TO_STRING_MAP[KeyCode.NUMPAD_MULTIPLY] = 'NumPad_Multiply'; TO_STRING_MAP[KeyCode.NUMPAD_ADD] = 'NumPad_Add'; TO_STRING_MAP[KeyCode.NUMPAD_SEPARATOR] = 'NumPad_Separator'; TO_STRING_MAP[KeyCode.NUMPAD_SUBTRACT] = 'NumPad_Subtract'; TO_STRING_MAP[KeyCode.NUMPAD_DECIMAL] = 'NumPad_Decimal'; TO_STRING_MAP[KeyCode.NUMPAD_DIVIDE] = 'NumPad_Divide'; // for (let i = 0; i < KeyCode.MAX_VALUE; i++) { // if (!TO_STRING_MAP[i]) { // console.warn('Missing string representation for ' + KeyCode[i]); // } // } }, (FROM_STRING_MAP) => { FROM_STRING_MAP['\r'] = KeyCode.Enter; }); let USER_SETTINGS = createMapping((TO_USER_SETTINGS_MAP) => { for (let i = 0, len = STRING._fromKeyCode.length; i < len; i++) { TO_USER_SETTINGS_MAP[i] = STRING._fromKeyCode[i]; } TO_USER_SETTINGS_MAP[KeyCode.LeftArrow] = 'Left'; TO_USER_SETTINGS_MAP[KeyCode.UpArrow] = 'Up'; TO_USER_SETTINGS_MAP[KeyCode.RightArrow] = 'Right'; TO_USER_SETTINGS_MAP[KeyCode.DownArrow] = 'Down'; }, (FROM_USER_SETTINGS_MAP) => { FROM_USER_SETTINGS_MAP['OEM_1'] = KeyCode.US_SEMICOLON; FROM_USER_SETTINGS_MAP['OEM_PLUS'] = KeyCode.US_EQUAL; FROM_USER_SETTINGS_MAP['OEM_COMMA'] = KeyCode.US_COMMA; FROM_USER_SETTINGS_MAP['OEM_MINUS'] = KeyCode.US_MINUS; FROM_USER_SETTINGS_MAP['OEM_PERIOD'] = KeyCode.US_DOT; FROM_USER_SETTINGS_MAP['OEM_2'] = KeyCode.US_SLASH; FROM_USER_SETTINGS_MAP['OEM_3'] = KeyCode.US_BACKTICK; FROM_USER_SETTINGS_MAP['OEM_4'] = KeyCode.US_OPEN_SQUARE_BRACKET; FROM_USER_SETTINGS_MAP['OEM_5'] = KeyCode.US_BACKSLASH; FROM_USER_SETTINGS_MAP['OEM_6'] = KeyCode.US_CLOSE_SQUARE_BRACKET; FROM_USER_SETTINGS_MAP['OEM_7'] = KeyCode.US_QUOTE; FROM_USER_SETTINGS_MAP['OEM_8'] = KeyCode.OEM_8; FROM_USER_SETTINGS_MAP['OEM_102'] = KeyCode.OEM_102; }); export namespace KeyCode { export function toString(key:KeyCode): string { return STRING.fromKeyCode(key); } export function fromString(key:string): KeyCode { return STRING.toKeyCode(key); } } // Binary encoding strategy: // 15: 1 bit for ctrlCmd // 14: 1 bit for shift // 13: 1 bit for alt // 12: 1 bit for winCtrl // 0: 12 bits for keyCode (up to a maximum keyCode of 4096. Given we have 83 at this point thats good enough) const BIN_CTRLCMD_MASK = 1 << 15; const BIN_SHIFT_MASK = 1 << 14; const BIN_ALT_MASK = 1 << 13; const BIN_WINCTRL_MASK = 1 << 12; const BIN_KEYCODE_MASK = 0x00000fff; export class BinaryKeybindings { public static extractFirstPart(keybinding:number): number { return keybinding & 0x0000ffff; } public static extractChordPart(keybinding:number): number { return (keybinding >> 16) & 0x0000ffff; } public static hasChord(keybinding:number): boolean { return (this.extractChordPart(keybinding) !== 0); } public static hasCtrlCmd(keybinding:number): boolean { return (keybinding & BIN_CTRLCMD_MASK ? true : false); } public static hasShift(keybinding:number): boolean { return (keybinding & BIN_SHIFT_MASK ? true : false); } public static hasAlt(keybinding:number): boolean { return (keybinding & BIN_ALT_MASK ? true : false); } public static hasWinCtrl(keybinding:number): boolean { return (keybinding & BIN_WINCTRL_MASK ? true : false); } public static extractKeyCode(keybinding:number): KeyCode { return (keybinding & BIN_KEYCODE_MASK); } } export class KeyMod { public static CtrlCmd = BIN_CTRLCMD_MASK; public static Shift = BIN_SHIFT_MASK; public static Alt = BIN_ALT_MASK; public static WinCtrl = BIN_WINCTRL_MASK; public static chord(firstPart:number, secondPart:number): number { return firstPart | ((secondPart & 0x0000ffff) << 16); } } /** * A set of usual keybindings that can be reused in code */ export class CommonKeybindings { public static ENTER: number = KeyCode.Enter; public static SHIFT_ENTER: number = KeyMod.Shift | KeyCode.Enter; public static CTRLCMD_ENTER: number = KeyMod.CtrlCmd | KeyCode.Enter; public static WINCTRL_ENTER: number = KeyMod.WinCtrl | KeyCode.Enter; public static TAB: number = KeyCode.Tab; public static SHIFT_TAB: number = KeyMod.Shift | KeyCode.Tab; public static ESCAPE: number = KeyCode.Escape; public static SPACE: number = KeyCode.Space; public static DELETE: number = KeyCode.Delete; public static SHIFT_DELETE: number = KeyMod.Shift | KeyCode.Delete; public static CTRLCMD_BACKSPACE: number = KeyMod.CtrlCmd | KeyCode.Backspace; public static UP_ARROW: number = KeyCode.UpArrow; public static SHIFT_UP_ARROW: number = KeyMod.Shift | KeyCode.UpArrow; public static CTRLCMD_UP_ARROW: number = KeyMod.CtrlCmd | KeyCode.UpArrow; public static DOWN_ARROW: number = KeyCode.DownArrow; public static SHIFT_DOWN_ARROW: number = KeyMod.Shift | KeyCode.DownArrow; public static CTRLCMD_DOWN_ARROW: number = KeyMod.CtrlCmd | KeyCode.DownArrow; public static LEFT_ARROW: number = KeyCode.LeftArrow; public static RIGHT_ARROW: number = KeyCode.RightArrow; public static PAGE_UP: number = KeyCode.PageUp; public static SHIFT_PAGE_UP: number = KeyMod.Shift | KeyCode.PageUp; public static PAGE_DOWN: number = KeyCode.PageDown; public static SHIFT_PAGE_DOWN: number = KeyMod.Shift | KeyCode.PageDown; public static F2: number = KeyCode.F2; public static CTRLCMD_S: number = KeyMod.CtrlCmd | KeyCode.KEY_S; public static CTRLCMD_C: number = KeyMod.CtrlCmd | KeyCode.KEY_C; public static CTRLCMD_V: number = KeyMod.CtrlCmd | KeyCode.KEY_V; } export class Keybinding { /** * Format the binding to a format appropiate for rendering in the UI */ private static _toUSLabel(value:number, Platform:ISimplifiedPlatform): string { return _asString(value, (Platform.isMacintosh ? MacUIKeyLabelProvider.INSTANCE : ClassicUIKeyLabelProvider.INSTANCE), Platform); } /** * Format the binding to a format appropiate for rendering in the UI */ private static _toUSHTMLLabel(value:number, Platform:ISimplifiedPlatform): IHTMLContentElement[] { return _asHTML(value, (Platform.isMacintosh ? MacUIKeyLabelProvider.INSTANCE : ClassicUIKeyLabelProvider.INSTANCE), Platform); } /** * Format the binding to a format appropiate for rendering in the UI */ private static _toCustomLabel(value:number, labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform): string { return _asString(value, labelProvider, Platform); } /** * Format the binding to a format appropiate for rendering in the UI */ private static _toCustomHTMLLabel(value:number, labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform): IHTMLContentElement[] { return _asHTML(value, labelProvider, Platform); } /** * This prints the binding in a format suitable for electron's accelerators. * See https://github.com/atom/electron/blob/master/docs/api/accelerator.md */ private static _toElectronAccelerator(value:number, Platform:ISimplifiedPlatform): string { if (BinaryKeybindings.hasChord(value)) { // Electron cannot handle chords return null; } return _asString(value, ElectronAcceleratorLabelProvider.INSTANCE, Platform); } private static _cachedKeybindingRegex: string = null; public static getUserSettingsKeybindingRegex(): string { if (!this._cachedKeybindingRegex) { let numpadKey = "numpad(0|1|2|3|4|5|6|7|8|9|_multiply|_add|_subtract|_decimal|_divide|_separator)"; let oemKey = "`|\\-|=|\\[|\\]|\\\\\\\\|;|'|,|\\.|\\/|oem_8|oem_102"; let specialKey = "left|up|right|down|pageup|pagedown|end|home|tab|enter|escape|space|backspace|delete|pausebreak|capslock|insert|contextmenu|numlock|scrolllock"; let casualKey = "[a-z]|[0-9]|f(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19)"; let key = '((' + [numpadKey, oemKey, specialKey, casualKey].join(')|(') + '))'; let mod = '((ctrl|shift|alt|cmd|win|meta)\\+)*'; let keybinding = '(' + mod + key + ')'; this._cachedKeybindingRegex = '"\\s*(' + keybinding + '(\\s+' + keybinding +')?' + ')\\s*"'; } return this._cachedKeybindingRegex; } /** * Format the binding to a format appropiate for the user settings file. */ public static toUserSettingsLabel(value:number, Platform:ISimplifiedPlatform = defaultPlatform): string { let result = _asString(value, UserSettingsKeyLabelProvider.INSTANCE, Platform); result = result.toLowerCase(); if (Platform.isMacintosh) { result = result.replace(/meta/g, 'cmd'); } else if (Platform.isWindows) { result = result.replace(/meta/g, 'win'); } return result; } public static fromUserSettingsLabel(input: string, Platform: ISimplifiedPlatform = defaultPlatform): number { if (!input) { return null; } input = input.toLowerCase().trim(); let ctrlCmd = false, shift = false, alt = false, winCtrl = false, key:string = ''; while (/^(ctrl|shift|alt|meta|win|cmd)(\+|\-)/.test(input)) { if (/^ctrl(\+|\-)/.test(input)) { if (Platform.isMacintosh) { winCtrl = true; } else { ctrlCmd = true; } input = input.substr('ctrl-'.length); } if (/^shift(\+|\-)/.test(input)) { shift = true; input = input.substr('shift-'.length); } if (/^alt(\+|\-)/.test(input)) { alt = true; input = input.substr('alt-'.length); } if (/^meta(\+|\-)/.test(input)) { if (Platform.isMacintosh) { ctrlCmd = true; } else { winCtrl = true; } input = input.substr('meta-'.length); } if (/^win(\+|\-)/.test(input)) { if (Platform.isMacintosh) { ctrlCmd = true; } else { winCtrl = true; } input = input.substr('win-'.length); } if (/^cmd(\+|\-)/.test(input)) { if (Platform.isMacintosh) { ctrlCmd = true; } else { winCtrl = true; } input = input.substr('cmd-'.length); } } let chord: number = 0; let firstSpaceIdx = input.indexOf(' '); if (firstSpaceIdx > 0) { key = input.substring(0, firstSpaceIdx); chord = Keybinding.fromUserSettingsLabel(input.substring(firstSpaceIdx), Platform); } else { key = input; } let keyCode = USER_SETTINGS.toKeyCode(key); let result = 0; if (ctrlCmd) { result |= KeyMod.CtrlCmd; } if (shift) { result |= KeyMod.Shift; } if (alt) { result |= KeyMod.Alt; } if (winCtrl) { result |= KeyMod.WinCtrl; } result |= keyCode; return KeyMod.chord(result, chord); } public value:number; constructor(keybinding:number) { this.value = keybinding; } public hasCtrlCmd(): boolean { return BinaryKeybindings.hasCtrlCmd(this.value); } public hasShift(): boolean { return BinaryKeybindings.hasShift(this.value); } public hasAlt(): boolean { return BinaryKeybindings.hasAlt(this.value); } public hasWinCtrl(): boolean { return BinaryKeybindings.hasWinCtrl(this.value); } public extractKeyCode(): KeyCode { return BinaryKeybindings.extractKeyCode(this.value); } /** * Format the binding to a format appropiate for rendering in the UI */ public _toUSLabel(Platform:ISimplifiedPlatform = defaultPlatform): string { return Keybinding._toUSLabel(this.value, Platform); } /** * Format the binding to a format appropiate for rendering in the UI */ public _toUSHTMLLabel(Platform:ISimplifiedPlatform = defaultPlatform): IHTMLContentElement[] { return Keybinding._toUSHTMLLabel(this.value, Platform); } /** * Format the binding to a format appropiate for rendering in the UI */ public toCustomLabel(labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform = defaultPlatform): string { return Keybinding._toCustomLabel(this.value, labelProvider, Platform); } /** * Format the binding to a format appropiate for rendering in the UI */ public toCustomHTMLLabel(labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform = defaultPlatform): IHTMLContentElement[] { return Keybinding._toCustomHTMLLabel(this.value, labelProvider, Platform); } /** * This prints the binding in a format suitable for electron's accelerators. * See https://github.com/atom/electron/blob/master/docs/api/accelerator.md */ public _toElectronAccelerator(Platform:ISimplifiedPlatform = defaultPlatform): string { return Keybinding._toElectronAccelerator(this.value, Platform); } /** * Format the binding to a format appropiate for the user settings file. */ public toUserSettingsLabel(Platform:ISimplifiedPlatform = defaultPlatform): string { return Keybinding.toUserSettingsLabel(this.value, Platform); } } export interface IKeyBindingLabelProvider { ctrlKeyLabel:string; shiftKeyLabel:string; altKeyLabel:string; cmdKeyLabel:string; windowsKeyLabel:string; modifierSeparator:string; getLabelForKey(keyCode:KeyCode): string; } /** * Print for Electron */ export class ElectronAcceleratorLabelProvider implements IKeyBindingLabelProvider { public static INSTANCE = new ElectronAcceleratorLabelProvider(); public ctrlKeyLabel = 'Ctrl'; public shiftKeyLabel = 'Shift'; public altKeyLabel = 'Alt'; public cmdKeyLabel = 'Cmd'; public windowsKeyLabel = 'Super'; public modifierSeparator = '+'; public getLabelForKey(keyCode:KeyCode): string { switch (keyCode) { case KeyCode.UpArrow: return 'Up'; case KeyCode.DownArrow: return 'Down'; case KeyCode.LeftArrow: return 'Left'; case KeyCode.RightArrow: return 'Right'; } return KeyCode.toString(keyCode); } } /** * Print for Mac UI */ export class MacUIKeyLabelProvider implements IKeyBindingLabelProvider { public static INSTANCE = new MacUIKeyLabelProvider(); private static leftArrowUnicodeLabel = String.fromCharCode(8592); private static upArrowUnicodeLabel = String.fromCharCode(8593); private static rightArrowUnicodeLabel = String.fromCharCode(8594); private static downArrowUnicodeLabel = String.fromCharCode(8595); public ctrlKeyLabel = '\u2303'; public shiftKeyLabel = '\u21E7'; public altKeyLabel = '\u2325'; public cmdKeyLabel = '\u2318'; public windowsKeyLabel = nls.localize('windowsKey', "Windows"); public modifierSeparator = ''; public getLabelForKey(keyCode:KeyCode): string { switch (keyCode) { case KeyCode.LeftArrow: return MacUIKeyLabelProvider.leftArrowUnicodeLabel; case KeyCode.UpArrow: return MacUIKeyLabelProvider.upArrowUnicodeLabel; case KeyCode.RightArrow: return MacUIKeyLabelProvider.rightArrowUnicodeLabel; case KeyCode.DownArrow: return MacUIKeyLabelProvider.downArrowUnicodeLabel; } return KeyCode.toString(keyCode); } } /** * Print for Windows, Linux UI */ export class ClassicUIKeyLabelProvider implements IKeyBindingLabelProvider { public static INSTANCE = new ClassicUIKeyLabelProvider(); public ctrlKeyLabel = nls.localize('ctrlKey', "Ctrl"); public shiftKeyLabel = nls.localize('shiftKey', "Shift"); public altKeyLabel = nls.localize('altKey', "Alt"); public cmdKeyLabel = nls.localize('cmdKey', "Command"); public windowsKeyLabel = nls.localize('windowsKey', "Windows"); public modifierSeparator = '+'; public getLabelForKey(keyCode:KeyCode): string { return KeyCode.toString(keyCode); } } /** * Print for the user settings file. */ class UserSettingsKeyLabelProvider implements IKeyBindingLabelProvider { public static INSTANCE = new UserSettingsKeyLabelProvider(); public ctrlKeyLabel = 'Ctrl'; public shiftKeyLabel = 'Shift'; public altKeyLabel = 'Alt'; public cmdKeyLabel = 'Meta'; public windowsKeyLabel = 'Meta'; public modifierSeparator = '+'; public getLabelForKey(keyCode:KeyCode): string { return USER_SETTINGS.fromKeyCode(keyCode); } } function _asString(keybinding:number, labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform): string { let result:string[] = [], ctrlCmd = BinaryKeybindings.hasCtrlCmd(keybinding), shift = BinaryKeybindings.hasShift(keybinding), alt = BinaryKeybindings.hasAlt(keybinding), winCtrl = BinaryKeybindings.hasWinCtrl(keybinding), keyCode = BinaryKeybindings.extractKeyCode(keybinding); let keyLabel = labelProvider.getLabelForKey(keyCode); if (!keyLabel) { // cannot trigger this key code under this kb layout return ''; } // translate modifier keys: Ctrl-Shift-Alt-Meta if ((ctrlCmd && !Platform.isMacintosh) || (winCtrl && Platform.isMacintosh)) { result.push(labelProvider.ctrlKeyLabel); } if (shift) { result.push(labelProvider.shiftKeyLabel); } if (alt) { result.push(labelProvider.altKeyLabel); } if (ctrlCmd && Platform.isMacintosh) { result.push(labelProvider.cmdKeyLabel); } if (winCtrl && !Platform.isMacintosh) { result.push(labelProvider.windowsKeyLabel); } // the actual key result.push(keyLabel); var actualResult = result.join(labelProvider.modifierSeparator); if (BinaryKeybindings.hasChord(keybinding)) { return actualResult + ' ' + _asString(BinaryKeybindings.extractChordPart(keybinding), labelProvider, Platform); } return actualResult; } function _pushKey(result:IHTMLContentElement[], str:string): void { if (result.length > 0) { result.push({ tagName: 'span', text: '+' }); } result.push({ tagName: 'span', className: 'monaco-kbkey', text: str }); } function _asHTML(keybinding:number, labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform, isChord:boolean = false): IHTMLContentElement[] { let result:IHTMLContentElement[] = [], ctrlCmd = BinaryKeybindings.hasCtrlCmd(keybinding), shift = BinaryKeybindings.hasShift(keybinding), alt = BinaryKeybindings.hasAlt(keybinding), winCtrl = BinaryKeybindings.hasWinCtrl(keybinding), keyCode = BinaryKeybindings.extractKeyCode(keybinding); let keyLabel = labelProvider.getLabelForKey(keyCode); if (!keyLabel) { // cannot trigger this key code under this kb layout return []; } // translate modifier keys: Ctrl-Shift-Alt-Meta if ((ctrlCmd && !Platform.isMacintosh) || (winCtrl && Platform.isMacintosh)) { _pushKey(result, labelProvider.ctrlKeyLabel); } if (shift) { _pushKey(result, labelProvider.shiftKeyLabel); } if (alt) { _pushKey(result, labelProvider.altKeyLabel); } if (ctrlCmd && Platform.isMacintosh) { _pushKey(result, labelProvider.cmdKeyLabel); } if (winCtrl && !Platform.isMacintosh) { _pushKey(result, labelProvider.windowsKeyLabel); } // the actual key _pushKey(result, keyLabel); let chordTo: IHTMLContentElement[] = null; if (BinaryKeybindings.hasChord(keybinding)) { chordTo = _asHTML(BinaryKeybindings.extractChordPart(keybinding), labelProvider, Platform, true); result.push({ tagName: 'span', text: ' ' }); result = result.concat(chordTo); } if (isChord) { return result; } return [{ tagName: 'span', className: 'monaco-kb', children: result }]; }