Path: blob/main/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CharCode } from '../../../../base/common/charCode.js';6import { KeyCode, KeyCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, IMMUTABLE_KEY_CODE_TO_CODE, ScanCode, ScanCodeUtils } from '../../../../base/common/keyCodes.js';7import { ResolvedKeybinding, KeyCodeChord, SingleModifierChord, ScanCodeChord, Keybinding, Chord } from '../../../../base/common/keybindings.js';8import { OperatingSystem } from '../../../../base/common/platform.js';9import { IKeyboardEvent } from '../../../../platform/keybinding/common/keybinding.js';10import { IKeyboardMapper } from '../../../../platform/keyboardLayout/common/keyboardMapper.js';11import { BaseResolvedKeybinding } from '../../../../platform/keybinding/common/baseResolvedKeybinding.js';12import { IMacLinuxKeyboardMapping, IMacLinuxKeyMapping } from '../../../../platform/keyboardLayout/common/keyboardLayout.js';1314/**15* A map from character to key codes.16* e.g. Contains entries such as:17* - '/' => { keyCode: KeyCode.US_SLASH, shiftKey: false }18* - '?' => { keyCode: KeyCode.US_SLASH, shiftKey: true }19*/20const CHAR_CODE_TO_KEY_CODE: ({ keyCode: KeyCode; shiftKey: boolean } | null)[] = [];2122export class NativeResolvedKeybinding extends BaseResolvedKeybinding<ScanCodeChord> {2324private readonly _mapper: MacLinuxKeyboardMapper;2526constructor(mapper: MacLinuxKeyboardMapper, os: OperatingSystem, chords: ScanCodeChord[]) {27super(os, chords);28this._mapper = mapper;29}3031protected _getLabel(chord: ScanCodeChord): string | null {32return this._mapper.getUILabelForScanCodeChord(chord);33}3435protected _getAriaLabel(chord: ScanCodeChord): string | null {36return this._mapper.getAriaLabelForScanCodeChord(chord);37}3839protected _getElectronAccelerator(chord: ScanCodeChord): string | null {40return this._mapper.getElectronAcceleratorLabelForScanCodeChord(chord);41}4243protected _getUserSettingsLabel(chord: ScanCodeChord): string | null {44return this._mapper.getUserSettingsLabelForScanCodeChord(chord);45}4647protected _isWYSIWYG(binding: ScanCodeChord | null): boolean {48if (!binding) {49return true;50}51if (IMMUTABLE_CODE_TO_KEY_CODE[binding.scanCode] !== KeyCode.DependsOnKbLayout) {52return true;53}54const a = this._mapper.getAriaLabelForScanCodeChord(binding);55const b = this._mapper.getUserSettingsLabelForScanCodeChord(binding);5657if (!a && !b) {58return true;59}60if (!a || !b) {61return false;62}63return (a.toLowerCase() === b.toLowerCase());64}6566protected _getChordDispatch(chord: ScanCodeChord): string | null {67return this._mapper.getDispatchStrForScanCodeChord(chord);68}6970protected _getSingleModifierChordDispatch(chord: ScanCodeChord): SingleModifierChord | null {71if ((chord.scanCode === ScanCode.ControlLeft || chord.scanCode === ScanCode.ControlRight) && !chord.shiftKey && !chord.altKey && !chord.metaKey) {72return 'ctrl';73}74if ((chord.scanCode === ScanCode.AltLeft || chord.scanCode === ScanCode.AltRight) && !chord.ctrlKey && !chord.shiftKey && !chord.metaKey) {75return 'alt';76}77if ((chord.scanCode === ScanCode.ShiftLeft || chord.scanCode === ScanCode.ShiftRight) && !chord.ctrlKey && !chord.altKey && !chord.metaKey) {78return 'shift';79}80if ((chord.scanCode === ScanCode.MetaLeft || chord.scanCode === ScanCode.MetaRight) && !chord.ctrlKey && !chord.shiftKey && !chord.altKey) {81return 'meta';82}83return null;84}85}8687interface IScanCodeMapping {88scanCode: ScanCode;89value: number;90withShift: number;91withAltGr: number;92withShiftAltGr: number;93}9495class ScanCodeCombo {96public readonly ctrlKey: boolean;97public readonly shiftKey: boolean;98public readonly altKey: boolean;99public readonly scanCode: ScanCode;100101constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, scanCode: ScanCode) {102this.ctrlKey = ctrlKey;103this.shiftKey = shiftKey;104this.altKey = altKey;105this.scanCode = scanCode;106}107108public toString(): string {109return `${this.ctrlKey ? 'Ctrl+' : ''}${this.shiftKey ? 'Shift+' : ''}${this.altKey ? 'Alt+' : ''}${ScanCodeUtils.toString(this.scanCode)}`;110}111112public equals(other: ScanCodeCombo): boolean {113return (114this.ctrlKey === other.ctrlKey115&& this.shiftKey === other.shiftKey116&& this.altKey === other.altKey117&& this.scanCode === other.scanCode118);119}120121private getProducedCharCode(mapping: IMacLinuxKeyMapping): string {122if (!mapping) {123return '';124}125if (this.ctrlKey && this.shiftKey && this.altKey) {126return mapping.withShiftAltGr;127}128if (this.ctrlKey && this.altKey) {129return mapping.withAltGr;130}131if (this.shiftKey) {132return mapping.withShift;133}134return mapping.value;135}136137public getProducedChar(mapping: IMacLinuxKeyMapping): string {138const charCode = MacLinuxKeyboardMapper.getCharCode(this.getProducedCharCode(mapping));139if (charCode === 0) {140return ' --- ';141}142if (charCode >= CharCode.U_Combining_Grave_Accent && charCode <= CharCode.U_Combining_Latin_Small_Letter_X) {143// combining144return 'U+' + charCode.toString(16);145}146return ' ' + String.fromCharCode(charCode) + ' ';147}148}149150class KeyCodeCombo {151public readonly ctrlKey: boolean;152public readonly shiftKey: boolean;153public readonly altKey: boolean;154public readonly keyCode: KeyCode;155156constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, keyCode: KeyCode) {157this.ctrlKey = ctrlKey;158this.shiftKey = shiftKey;159this.altKey = altKey;160this.keyCode = keyCode;161}162163public toString(): string {164return `${this.ctrlKey ? 'Ctrl+' : ''}${this.shiftKey ? 'Shift+' : ''}${this.altKey ? 'Alt+' : ''}${KeyCodeUtils.toString(this.keyCode)}`;165}166}167168class ScanCodeKeyCodeMapper {169170/**171* ScanCode combination => KeyCode combination.172* Only covers relevant modifiers ctrl, shift, alt (since meta does not influence the mappings).173*/174private readonly _scanCodeToKeyCode: number[][] = [];175/**176* inverse of `_scanCodeToKeyCode`.177* KeyCode combination => ScanCode combination.178* Only covers relevant modifiers ctrl, shift, alt (since meta does not influence the mappings).179*/180private readonly _keyCodeToScanCode: number[][] = [];181182constructor() {183this._scanCodeToKeyCode = [];184this._keyCodeToScanCode = [];185}186187public registrationComplete(): void {188// IntlHash and IntlBackslash are rare keys, so ensure they don't end up being the preferred...189this._moveToEnd(ScanCode.IntlHash);190this._moveToEnd(ScanCode.IntlBackslash);191}192193private _moveToEnd(scanCode: ScanCode): void {194for (let mod = 0; mod < 8; mod++) {195const encodedKeyCodeCombos = this._scanCodeToKeyCode[(scanCode << 3) + mod];196if (!encodedKeyCodeCombos) {197continue;198}199for (let i = 0, len = encodedKeyCodeCombos.length; i < len; i++) {200const encodedScanCodeCombos = this._keyCodeToScanCode[encodedKeyCodeCombos[i]];201if (encodedScanCodeCombos.length === 1) {202continue;203}204for (let j = 0, len = encodedScanCodeCombos.length; j < len; j++) {205const entry = encodedScanCodeCombos[j];206const entryScanCode = (entry >>> 3);207if (entryScanCode === scanCode) {208// Move this entry to the end209for (let k = j + 1; k < len; k++) {210encodedScanCodeCombos[k - 1] = encodedScanCodeCombos[k];211}212encodedScanCodeCombos[len - 1] = entry;213}214}215}216}217}218219public registerIfUnknown(scanCodeCombo: ScanCodeCombo, keyCodeCombo: KeyCodeCombo): void {220if (keyCodeCombo.keyCode === KeyCode.Unknown) {221return;222}223const scanCodeComboEncoded = this._encodeScanCodeCombo(scanCodeCombo);224const keyCodeComboEncoded = this._encodeKeyCodeCombo(keyCodeCombo);225226const keyCodeIsDigit = (keyCodeCombo.keyCode >= KeyCode.Digit0 && keyCodeCombo.keyCode <= KeyCode.Digit9);227const keyCodeIsLetter = (keyCodeCombo.keyCode >= KeyCode.KeyA && keyCodeCombo.keyCode <= KeyCode.KeyZ);228229const existingKeyCodeCombos = this._scanCodeToKeyCode[scanCodeComboEncoded];230231// Allow a scan code to map to multiple key codes if it is a digit or a letter key code232if (keyCodeIsDigit || keyCodeIsLetter) {233// Only check that we don't insert the same entry twice234if (existingKeyCodeCombos) {235for (let i = 0, len = existingKeyCodeCombos.length; i < len; i++) {236if (existingKeyCodeCombos[i] === keyCodeComboEncoded) {237// avoid duplicates238return;239}240}241}242} else {243// Don't allow multiples244if (existingKeyCodeCombos && existingKeyCodeCombos.length !== 0) {245return;246}247}248249this._scanCodeToKeyCode[scanCodeComboEncoded] = this._scanCodeToKeyCode[scanCodeComboEncoded] || [];250this._scanCodeToKeyCode[scanCodeComboEncoded].unshift(keyCodeComboEncoded);251252this._keyCodeToScanCode[keyCodeComboEncoded] = this._keyCodeToScanCode[keyCodeComboEncoded] || [];253this._keyCodeToScanCode[keyCodeComboEncoded].unshift(scanCodeComboEncoded);254}255256public lookupKeyCodeCombo(keyCodeCombo: KeyCodeCombo): ScanCodeCombo[] {257const keyCodeComboEncoded = this._encodeKeyCodeCombo(keyCodeCombo);258const scanCodeCombosEncoded = this._keyCodeToScanCode[keyCodeComboEncoded];259if (!scanCodeCombosEncoded || scanCodeCombosEncoded.length === 0) {260return [];261}262263const result: ScanCodeCombo[] = [];264for (let i = 0, len = scanCodeCombosEncoded.length; i < len; i++) {265const scanCodeComboEncoded = scanCodeCombosEncoded[i];266267const ctrlKey = (scanCodeComboEncoded & 0b001) ? true : false;268const shiftKey = (scanCodeComboEncoded & 0b010) ? true : false;269const altKey = (scanCodeComboEncoded & 0b100) ? true : false;270const scanCode: ScanCode = (scanCodeComboEncoded >>> 3);271272result[i] = new ScanCodeCombo(ctrlKey, shiftKey, altKey, scanCode);273}274return result;275}276277public lookupScanCodeCombo(scanCodeCombo: ScanCodeCombo): KeyCodeCombo[] {278const scanCodeComboEncoded = this._encodeScanCodeCombo(scanCodeCombo);279const keyCodeCombosEncoded = this._scanCodeToKeyCode[scanCodeComboEncoded];280if (!keyCodeCombosEncoded || keyCodeCombosEncoded.length === 0) {281return [];282}283284const result: KeyCodeCombo[] = [];285for (let i = 0, len = keyCodeCombosEncoded.length; i < len; i++) {286const keyCodeComboEncoded = keyCodeCombosEncoded[i];287288const ctrlKey = (keyCodeComboEncoded & 0b001) ? true : false;289const shiftKey = (keyCodeComboEncoded & 0b010) ? true : false;290const altKey = (keyCodeComboEncoded & 0b100) ? true : false;291const keyCode: KeyCode = (keyCodeComboEncoded >>> 3);292293result[i] = new KeyCodeCombo(ctrlKey, shiftKey, altKey, keyCode);294}295return result;296}297298public guessStableKeyCode(scanCode: ScanCode): KeyCode {299if (scanCode >= ScanCode.Digit1 && scanCode <= ScanCode.Digit0) {300// digits are ok301switch (scanCode) {302case ScanCode.Digit1: return KeyCode.Digit1;303case ScanCode.Digit2: return KeyCode.Digit2;304case ScanCode.Digit3: return KeyCode.Digit3;305case ScanCode.Digit4: return KeyCode.Digit4;306case ScanCode.Digit5: return KeyCode.Digit5;307case ScanCode.Digit6: return KeyCode.Digit6;308case ScanCode.Digit7: return KeyCode.Digit7;309case ScanCode.Digit8: return KeyCode.Digit8;310case ScanCode.Digit9: return KeyCode.Digit9;311case ScanCode.Digit0: return KeyCode.Digit0;312}313}314315// Lookup the scanCode with and without shift and see if the keyCode is stable316const keyCodeCombos1 = this.lookupScanCodeCombo(new ScanCodeCombo(false, false, false, scanCode));317const keyCodeCombos2 = this.lookupScanCodeCombo(new ScanCodeCombo(false, true, false, scanCode));318if (keyCodeCombos1.length === 1 && keyCodeCombos2.length === 1) {319const shiftKey1 = keyCodeCombos1[0].shiftKey;320const keyCode1 = keyCodeCombos1[0].keyCode;321const shiftKey2 = keyCodeCombos2[0].shiftKey;322const keyCode2 = keyCodeCombos2[0].keyCode;323if (keyCode1 === keyCode2 && shiftKey1 !== shiftKey2) {324// This looks like a stable mapping325return keyCode1;326}327}328329return KeyCode.DependsOnKbLayout;330}331332private _encodeScanCodeCombo(scanCodeCombo: ScanCodeCombo): number {333return this._encode(scanCodeCombo.ctrlKey, scanCodeCombo.shiftKey, scanCodeCombo.altKey, scanCodeCombo.scanCode);334}335336private _encodeKeyCodeCombo(keyCodeCombo: KeyCodeCombo): number {337return this._encode(keyCodeCombo.ctrlKey, keyCodeCombo.shiftKey, keyCodeCombo.altKey, keyCodeCombo.keyCode);338}339340private _encode(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, principal: number): number {341return (342((ctrlKey ? 1 : 0) << 0)343| ((shiftKey ? 1 : 0) << 1)344| ((altKey ? 1 : 0) << 2)345| principal << 3346) >>> 0;347}348}349350export class MacLinuxKeyboardMapper implements IKeyboardMapper {351352/**353* used only for debug purposes.354*/355private readonly _codeInfo: IMacLinuxKeyMapping[];356/**357* Maps ScanCode combos <-> KeyCode combos.358*/359private readonly _scanCodeKeyCodeMapper: ScanCodeKeyCodeMapper;360/**361* UI label for a ScanCode.362*/363private readonly _scanCodeToLabel: Array<string | null> = [];364/**365* Dispatching string for a ScanCode.366*/367private readonly _scanCodeToDispatch: Array<string | null> = [];368369constructor(370private readonly _isUSStandard: boolean,371rawMappings: IMacLinuxKeyboardMapping,372private readonly _mapAltGrToCtrlAlt: boolean,373private readonly _OS: OperatingSystem,374) {375this._codeInfo = [];376this._scanCodeKeyCodeMapper = new ScanCodeKeyCodeMapper();377this._scanCodeToLabel = [];378this._scanCodeToDispatch = [];379380const _registerIfUnknown = (381hwCtrlKey: 0 | 1, hwShiftKey: 0 | 1, hwAltKey: 0 | 1, scanCode: ScanCode,382kbCtrlKey: 0 | 1, kbShiftKey: 0 | 1, kbAltKey: 0 | 1, keyCode: KeyCode,383): void => {384this._scanCodeKeyCodeMapper.registerIfUnknown(385new ScanCodeCombo(hwCtrlKey ? true : false, hwShiftKey ? true : false, hwAltKey ? true : false, scanCode),386new KeyCodeCombo(kbCtrlKey ? true : false, kbShiftKey ? true : false, kbAltKey ? true : false, keyCode)387);388};389390const _registerAllCombos = (_ctrlKey: 0 | 1, _shiftKey: 0 | 1, _altKey: 0 | 1, scanCode: ScanCode, keyCode: KeyCode): void => {391for (let ctrlKey = _ctrlKey; ctrlKey <= 1; ctrlKey++) {392for (let shiftKey = _shiftKey; shiftKey <= 1; shiftKey++) {393for (let altKey = _altKey; altKey <= 1; altKey++) {394_registerIfUnknown(395ctrlKey, shiftKey, altKey, scanCode,396ctrlKey, shiftKey, altKey, keyCode397);398}399}400}401};402403// Initialize `_scanCodeToLabel`404for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {405this._scanCodeToLabel[scanCode] = null;406}407408// Initialize `_scanCodeToDispatch`409for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {410this._scanCodeToDispatch[scanCode] = null;411}412413// Handle immutable mappings414for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {415const keyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];416if (keyCode !== KeyCode.DependsOnKbLayout) {417_registerAllCombos(0, 0, 0, scanCode, keyCode);418this._scanCodeToLabel[scanCode] = KeyCodeUtils.toString(keyCode);419420if (keyCode === KeyCode.Unknown || keyCode === KeyCode.Ctrl || keyCode === KeyCode.Meta || keyCode === KeyCode.Alt || keyCode === KeyCode.Shift) {421this._scanCodeToDispatch[scanCode] = null; // cannot dispatch on this ScanCode422} else {423this._scanCodeToDispatch[scanCode] = `[${ScanCodeUtils.toString(scanCode)}]`;424}425}426}427428// Try to identify keyboard layouts where characters A-Z are missing429// and forcibly map them to their corresponding scan codes if that is the case430const missingLatinLettersOverride: { [scanCode: string]: IMacLinuxKeyMapping } = {};431432{433const producesLatinLetter: boolean[] = [];434for (const strScanCode in rawMappings) {435if (rawMappings.hasOwnProperty(strScanCode)) {436const scanCode = ScanCodeUtils.toEnum(strScanCode);437if (scanCode === ScanCode.None) {438continue;439}440if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== KeyCode.DependsOnKbLayout) {441continue;442}443444const rawMapping = rawMappings[strScanCode];445const value = MacLinuxKeyboardMapper.getCharCode(rawMapping.value);446447if (value >= CharCode.a && value <= CharCode.z) {448const upperCaseValue = CharCode.A + (value - CharCode.a);449producesLatinLetter[upperCaseValue] = true;450}451}452}453454const _registerLetterIfMissing = (charCode: CharCode, scanCode: ScanCode, value: string, withShift: string): void => {455if (!producesLatinLetter[charCode]) {456missingLatinLettersOverride[ScanCodeUtils.toString(scanCode)] = {457value: value,458withShift: withShift,459withAltGr: '',460withShiftAltGr: ''461};462}463};464465// Ensure letters are mapped466_registerLetterIfMissing(CharCode.A, ScanCode.KeyA, 'a', 'A');467_registerLetterIfMissing(CharCode.B, ScanCode.KeyB, 'b', 'B');468_registerLetterIfMissing(CharCode.C, ScanCode.KeyC, 'c', 'C');469_registerLetterIfMissing(CharCode.D, ScanCode.KeyD, 'd', 'D');470_registerLetterIfMissing(CharCode.E, ScanCode.KeyE, 'e', 'E');471_registerLetterIfMissing(CharCode.F, ScanCode.KeyF, 'f', 'F');472_registerLetterIfMissing(CharCode.G, ScanCode.KeyG, 'g', 'G');473_registerLetterIfMissing(CharCode.H, ScanCode.KeyH, 'h', 'H');474_registerLetterIfMissing(CharCode.I, ScanCode.KeyI, 'i', 'I');475_registerLetterIfMissing(CharCode.J, ScanCode.KeyJ, 'j', 'J');476_registerLetterIfMissing(CharCode.K, ScanCode.KeyK, 'k', 'K');477_registerLetterIfMissing(CharCode.L, ScanCode.KeyL, 'l', 'L');478_registerLetterIfMissing(CharCode.M, ScanCode.KeyM, 'm', 'M');479_registerLetterIfMissing(CharCode.N, ScanCode.KeyN, 'n', 'N');480_registerLetterIfMissing(CharCode.O, ScanCode.KeyO, 'o', 'O');481_registerLetterIfMissing(CharCode.P, ScanCode.KeyP, 'p', 'P');482_registerLetterIfMissing(CharCode.Q, ScanCode.KeyQ, 'q', 'Q');483_registerLetterIfMissing(CharCode.R, ScanCode.KeyR, 'r', 'R');484_registerLetterIfMissing(CharCode.S, ScanCode.KeyS, 's', 'S');485_registerLetterIfMissing(CharCode.T, ScanCode.KeyT, 't', 'T');486_registerLetterIfMissing(CharCode.U, ScanCode.KeyU, 'u', 'U');487_registerLetterIfMissing(CharCode.V, ScanCode.KeyV, 'v', 'V');488_registerLetterIfMissing(CharCode.W, ScanCode.KeyW, 'w', 'W');489_registerLetterIfMissing(CharCode.X, ScanCode.KeyX, 'x', 'X');490_registerLetterIfMissing(CharCode.Y, ScanCode.KeyY, 'y', 'Y');491_registerLetterIfMissing(CharCode.Z, ScanCode.KeyZ, 'z', 'Z');492}493494const mappings: IScanCodeMapping[] = [];495let mappingsLen = 0;496for (const strScanCode in rawMappings) {497if (rawMappings.hasOwnProperty(strScanCode)) {498const scanCode = ScanCodeUtils.toEnum(strScanCode);499if (scanCode === ScanCode.None) {500continue;501}502if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== KeyCode.DependsOnKbLayout) {503continue;504}505506this._codeInfo[scanCode] = rawMappings[strScanCode];507508const rawMapping = missingLatinLettersOverride[strScanCode] || rawMappings[strScanCode];509const value = MacLinuxKeyboardMapper.getCharCode(rawMapping.value);510const withShift = MacLinuxKeyboardMapper.getCharCode(rawMapping.withShift);511const withAltGr = MacLinuxKeyboardMapper.getCharCode(rawMapping.withAltGr);512const withShiftAltGr = MacLinuxKeyboardMapper.getCharCode(rawMapping.withShiftAltGr);513514const mapping: IScanCodeMapping = {515scanCode: scanCode,516value: value,517withShift: withShift,518withAltGr: withAltGr,519withShiftAltGr: withShiftAltGr,520};521mappings[mappingsLen++] = mapping;522523this._scanCodeToDispatch[scanCode] = `[${ScanCodeUtils.toString(scanCode)}]`;524525if (value >= CharCode.a && value <= CharCode.z) {526const upperCaseValue = CharCode.A + (value - CharCode.a);527this._scanCodeToLabel[scanCode] = String.fromCharCode(upperCaseValue);528} else if (value >= CharCode.A && value <= CharCode.Z) {529this._scanCodeToLabel[scanCode] = String.fromCharCode(value);530} else if (value) {531this._scanCodeToLabel[scanCode] = String.fromCharCode(value);532} else {533this._scanCodeToLabel[scanCode] = null;534}535}536}537538// Handle all `withShiftAltGr` entries539for (let i = mappings.length - 1; i >= 0; i--) {540const mapping = mappings[i];541const scanCode = mapping.scanCode;542const withShiftAltGr = mapping.withShiftAltGr;543if (withShiftAltGr === mapping.withAltGr || withShiftAltGr === mapping.withShift || withShiftAltGr === mapping.value) {544// handled below545continue;546}547const kb = MacLinuxKeyboardMapper._charCodeToKb(withShiftAltGr);548if (!kb) {549continue;550}551const kbShiftKey = kb.shiftKey;552const keyCode = kb.keyCode;553554if (kbShiftKey) {555// Ctrl+Shift+Alt+ScanCode => Shift+KeyCode556_registerIfUnknown(1, 1, 1, scanCode, 0, 1, 0, keyCode); // Ctrl+Alt+ScanCode => Shift+KeyCode557} else {558// Ctrl+Shift+Alt+ScanCode => KeyCode559_registerIfUnknown(1, 1, 1, scanCode, 0, 0, 0, keyCode); // Ctrl+Alt+ScanCode => KeyCode560}561}562// Handle all `withAltGr` entries563for (let i = mappings.length - 1; i >= 0; i--) {564const mapping = mappings[i];565const scanCode = mapping.scanCode;566const withAltGr = mapping.withAltGr;567if (withAltGr === mapping.withShift || withAltGr === mapping.value) {568// handled below569continue;570}571const kb = MacLinuxKeyboardMapper._charCodeToKb(withAltGr);572if (!kb) {573continue;574}575const kbShiftKey = kb.shiftKey;576const keyCode = kb.keyCode;577578if (kbShiftKey) {579// Ctrl+Alt+ScanCode => Shift+KeyCode580_registerIfUnknown(1, 0, 1, scanCode, 0, 1, 0, keyCode); // Ctrl+Alt+ScanCode => Shift+KeyCode581} else {582// Ctrl+Alt+ScanCode => KeyCode583_registerIfUnknown(1, 0, 1, scanCode, 0, 0, 0, keyCode); // Ctrl+Alt+ScanCode => KeyCode584}585}586// Handle all `withShift` entries587for (let i = mappings.length - 1; i >= 0; i--) {588const mapping = mappings[i];589const scanCode = mapping.scanCode;590const withShift = mapping.withShift;591if (withShift === mapping.value) {592// handled below593continue;594}595const kb = MacLinuxKeyboardMapper._charCodeToKb(withShift);596if (!kb) {597continue;598}599const kbShiftKey = kb.shiftKey;600const keyCode = kb.keyCode;601602if (kbShiftKey) {603// Shift+ScanCode => Shift+KeyCode604_registerIfUnknown(0, 1, 0, scanCode, 0, 1, 0, keyCode); // Shift+ScanCode => Shift+KeyCode605_registerIfUnknown(0, 1, 1, scanCode, 0, 1, 1, keyCode); // Shift+Alt+ScanCode => Shift+Alt+KeyCode606_registerIfUnknown(1, 1, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+Shift+KeyCode607_registerIfUnknown(1, 1, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode608} else {609// Shift+ScanCode => KeyCode610_registerIfUnknown(0, 1, 0, scanCode, 0, 0, 0, keyCode); // Shift+ScanCode => KeyCode611_registerIfUnknown(0, 1, 0, scanCode, 0, 1, 0, keyCode); // Shift+ScanCode => Shift+KeyCode612_registerIfUnknown(0, 1, 1, scanCode, 0, 0, 1, keyCode); // Shift+Alt+ScanCode => Alt+KeyCode613_registerIfUnknown(0, 1, 1, scanCode, 0, 1, 1, keyCode); // Shift+Alt+ScanCode => Shift+Alt+KeyCode614_registerIfUnknown(1, 1, 0, scanCode, 1, 0, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+KeyCode615_registerIfUnknown(1, 1, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+Shift+KeyCode616_registerIfUnknown(1, 1, 1, scanCode, 1, 0, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Alt+KeyCode617_registerIfUnknown(1, 1, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode618}619}620// Handle all `value` entries621for (let i = mappings.length - 1; i >= 0; i--) {622const mapping = mappings[i];623const scanCode = mapping.scanCode;624const kb = MacLinuxKeyboardMapper._charCodeToKb(mapping.value);625if (!kb) {626continue;627}628const kbShiftKey = kb.shiftKey;629const keyCode = kb.keyCode;630631if (kbShiftKey) {632// ScanCode => Shift+KeyCode633_registerIfUnknown(0, 0, 0, scanCode, 0, 1, 0, keyCode); // ScanCode => Shift+KeyCode634_registerIfUnknown(0, 0, 1, scanCode, 0, 1, 1, keyCode); // Alt+ScanCode => Shift+Alt+KeyCode635_registerIfUnknown(1, 0, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+ScanCode => Ctrl+Shift+KeyCode636_registerIfUnknown(1, 0, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode637} else {638// ScanCode => KeyCode639_registerIfUnknown(0, 0, 0, scanCode, 0, 0, 0, keyCode); // ScanCode => KeyCode640_registerIfUnknown(0, 0, 1, scanCode, 0, 0, 1, keyCode); // Alt+ScanCode => Alt+KeyCode641_registerIfUnknown(0, 1, 0, scanCode, 0, 1, 0, keyCode); // Shift+ScanCode => Shift+KeyCode642_registerIfUnknown(0, 1, 1, scanCode, 0, 1, 1, keyCode); // Shift+Alt+ScanCode => Shift+Alt+KeyCode643_registerIfUnknown(1, 0, 0, scanCode, 1, 0, 0, keyCode); // Ctrl+ScanCode => Ctrl+KeyCode644_registerIfUnknown(1, 0, 1, scanCode, 1, 0, 1, keyCode); // Ctrl+Alt+ScanCode => Ctrl+Alt+KeyCode645_registerIfUnknown(1, 1, 0, scanCode, 1, 1, 0, keyCode); // Ctrl+Shift+ScanCode => Ctrl+Shift+KeyCode646_registerIfUnknown(1, 1, 1, scanCode, 1, 1, 1, keyCode); // Ctrl+Shift+Alt+ScanCode => Ctrl+Shift+Alt+KeyCode647}648}649// Handle all left-over available digits650_registerAllCombos(0, 0, 0, ScanCode.Digit1, KeyCode.Digit1);651_registerAllCombos(0, 0, 0, ScanCode.Digit2, KeyCode.Digit2);652_registerAllCombos(0, 0, 0, ScanCode.Digit3, KeyCode.Digit3);653_registerAllCombos(0, 0, 0, ScanCode.Digit4, KeyCode.Digit4);654_registerAllCombos(0, 0, 0, ScanCode.Digit5, KeyCode.Digit5);655_registerAllCombos(0, 0, 0, ScanCode.Digit6, KeyCode.Digit6);656_registerAllCombos(0, 0, 0, ScanCode.Digit7, KeyCode.Digit7);657_registerAllCombos(0, 0, 0, ScanCode.Digit8, KeyCode.Digit8);658_registerAllCombos(0, 0, 0, ScanCode.Digit9, KeyCode.Digit9);659_registerAllCombos(0, 0, 0, ScanCode.Digit0, KeyCode.Digit0);660661this._scanCodeKeyCodeMapper.registrationComplete();662}663664public dumpDebugInfo(): string {665const result: string[] = [];666667const immutableSamples = [668ScanCode.ArrowUp,669ScanCode.Numpad0670];671672let cnt = 0;673result.push(`isUSStandard: ${this._isUSStandard}`);674result.push(`----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------`);675for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {676if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== KeyCode.DependsOnKbLayout) {677if (immutableSamples.indexOf(scanCode) === -1) {678continue;679}680}681682if (cnt % 4 === 0) {683result.push(`| HW Code combination | Key | KeyCode combination | Pri | UI label | User settings | Electron accelerator | Dispatching string | WYSIWYG |`);684result.push(`----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------`);685}686cnt++;687688const mapping = this._codeInfo[scanCode];689690for (let mod = 0; mod < 8; mod++) {691const hwCtrlKey = (mod & 0b001) ? true : false;692const hwShiftKey = (mod & 0b010) ? true : false;693const hwAltKey = (mod & 0b100) ? true : false;694const scanCodeCombo = new ScanCodeCombo(hwCtrlKey, hwShiftKey, hwAltKey, scanCode);695const resolvedKb = this.resolveKeyboardEvent({696_standardKeyboardEventBrand: true,697ctrlKey: scanCodeCombo.ctrlKey,698shiftKey: scanCodeCombo.shiftKey,699altKey: scanCodeCombo.altKey,700metaKey: false,701altGraphKey: false,702keyCode: KeyCode.DependsOnKbLayout,703code: ScanCodeUtils.toString(scanCode)704});705706const outScanCodeCombo = scanCodeCombo.toString();707const outKey = scanCodeCombo.getProducedChar(mapping);708const ariaLabel = resolvedKb.getAriaLabel();709const outUILabel = (ariaLabel ? ariaLabel.replace(/Control\+/, 'Ctrl+') : null);710const outUserSettings = resolvedKb.getUserSettingsLabel();711const outElectronAccelerator = resolvedKb.getElectronAccelerator();712const outDispatchStr = resolvedKb.getDispatchChords()[0];713714const isWYSIWYG = (resolvedKb ? resolvedKb.isWYSIWYG() : false);715const outWYSIWYG = (isWYSIWYG ? ' ' : ' NO ');716717const kbCombos = this._scanCodeKeyCodeMapper.lookupScanCodeCombo(scanCodeCombo);718if (kbCombos.length === 0) {719result.push(`| ${this._leftPad(outScanCodeCombo, 30)} | ${outKey} | ${this._leftPad('', 25)} | ${this._leftPad('', 3)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 30)} | ${this._leftPad(outElectronAccelerator, 25)} | ${this._leftPad(outDispatchStr, 30)} | ${outWYSIWYG} |`);720} else {721for (let i = 0, len = kbCombos.length; i < len; i++) {722const kbCombo = kbCombos[i];723// find out the priority of this scan code for this key code724let colPriority: string;725726const scanCodeCombos = this._scanCodeKeyCodeMapper.lookupKeyCodeCombo(kbCombo);727if (scanCodeCombos.length === 1) {728// no need for priority, this key code combo maps to precisely this scan code combo729colPriority = '';730} else {731let priority = -1;732for (let j = 0; j < scanCodeCombos.length; j++) {733if (scanCodeCombos[j].equals(scanCodeCombo)) {734priority = j + 1;735break;736}737}738colPriority = String(priority);739}740741const outKeybinding = kbCombo.toString();742if (i === 0) {743result.push(`| ${this._leftPad(outScanCodeCombo, 30)} | ${outKey} | ${this._leftPad(outKeybinding, 25)} | ${this._leftPad(colPriority, 3)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 30)} | ${this._leftPad(outElectronAccelerator, 25)} | ${this._leftPad(outDispatchStr, 30)} | ${outWYSIWYG} |`);744} else {745// secondary keybindings746result.push(`| ${this._leftPad('', 30)} | | ${this._leftPad(outKeybinding, 25)} | ${this._leftPad(colPriority, 3)} | ${this._leftPad('', 25)} | ${this._leftPad('', 30)} | ${this._leftPad('', 25)} | ${this._leftPad('', 30)} | |`);747}748}749}750751}752result.push(`----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------`);753}754755return result.join('\n');756}757758private _leftPad(str: string | null, cnt: number): string {759if (str === null) {760str = 'null';761}762while (str.length < cnt) {763str = ' ' + str;764}765return str;766}767768public keyCodeChordToScanCodeChord(chord: KeyCodeChord): ScanCodeChord[] {769// Avoid double Enter bindings (both ScanCode.NumpadEnter and ScanCode.Enter point to KeyCode.Enter)770if (chord.keyCode === KeyCode.Enter) {771return [new ScanCodeChord(chord.ctrlKey, chord.shiftKey, chord.altKey, chord.metaKey, ScanCode.Enter)];772}773774const scanCodeCombos = this._scanCodeKeyCodeMapper.lookupKeyCodeCombo(775new KeyCodeCombo(chord.ctrlKey, chord.shiftKey, chord.altKey, chord.keyCode)776);777778const result: ScanCodeChord[] = [];779for (let i = 0, len = scanCodeCombos.length; i < len; i++) {780const scanCodeCombo = scanCodeCombos[i];781result[i] = new ScanCodeChord(scanCodeCombo.ctrlKey, scanCodeCombo.shiftKey, scanCodeCombo.altKey, chord.metaKey, scanCodeCombo.scanCode);782}783return result;784}785786public getUILabelForScanCodeChord(chord: ScanCodeChord | null): string | null {787if (!chord) {788return null;789}790if (chord.isDuplicateModifierCase()) {791return '';792}793if (this._OS === OperatingSystem.Macintosh) {794switch (chord.scanCode) {795case ScanCode.ArrowLeft:796return '←';797case ScanCode.ArrowUp:798return '↑';799case ScanCode.ArrowRight:800return '→';801case ScanCode.ArrowDown:802return '↓';803}804}805return this._scanCodeToLabel[chord.scanCode];806}807808public getAriaLabelForScanCodeChord(chord: ScanCodeChord | null): string | null {809if (!chord) {810return null;811}812if (chord.isDuplicateModifierCase()) {813return '';814}815return this._scanCodeToLabel[chord.scanCode];816}817818public getDispatchStrForScanCodeChord(chord: ScanCodeChord): string | null {819const codeDispatch = this._scanCodeToDispatch[chord.scanCode];820if (!codeDispatch) {821return null;822}823let result = '';824825if (chord.ctrlKey) {826result += 'ctrl+';827}828if (chord.shiftKey) {829result += 'shift+';830}831if (chord.altKey) {832result += 'alt+';833}834if (chord.metaKey) {835result += 'meta+';836}837result += codeDispatch;838839return result;840}841842public getUserSettingsLabelForScanCodeChord(chord: ScanCodeChord | null): string | null {843if (!chord) {844return null;845}846if (chord.isDuplicateModifierCase()) {847return '';848}849850const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[chord.scanCode];851if (immutableKeyCode !== KeyCode.DependsOnKbLayout) {852return KeyCodeUtils.toUserSettingsUS(immutableKeyCode).toLowerCase();853}854855// Check if this scanCode always maps to the same keyCode and back856const constantKeyCode: KeyCode = this._scanCodeKeyCodeMapper.guessStableKeyCode(chord.scanCode);857if (constantKeyCode !== KeyCode.DependsOnKbLayout) {858// Verify that this is a good key code that can be mapped back to the same scan code859const reverseChords = this.keyCodeChordToScanCodeChord(new KeyCodeChord(chord.ctrlKey, chord.shiftKey, chord.altKey, chord.metaKey, constantKeyCode));860for (let i = 0, len = reverseChords.length; i < len; i++) {861const reverseChord = reverseChords[i];862if (reverseChord.scanCode === chord.scanCode) {863return KeyCodeUtils.toUserSettingsUS(constantKeyCode).toLowerCase();864}865}866}867868return this._scanCodeToDispatch[chord.scanCode];869}870871public getElectronAcceleratorLabelForScanCodeChord(chord: ScanCodeChord | null): string | null {872if (!chord) {873return null;874}875876const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[chord.scanCode];877if (immutableKeyCode !== KeyCode.DependsOnKbLayout) {878return KeyCodeUtils.toElectronAccelerator(immutableKeyCode);879}880881// Check if this scanCode always maps to the same keyCode and back882const constantKeyCode: KeyCode = this._scanCodeKeyCodeMapper.guessStableKeyCode(chord.scanCode);883884if (this._OS === OperatingSystem.Linux && !this._isUSStandard) {885// [Electron Accelerators] On Linux, Electron does not handle correctly OEM keys.886// when using a different keyboard layout than US Standard.887// See https://github.com/microsoft/vscode/issues/23706888// See https://github.com/microsoft/vscode/pull/134890#issuecomment-941671791889const isOEMKey = (890constantKeyCode === KeyCode.Semicolon891|| constantKeyCode === KeyCode.Equal892|| constantKeyCode === KeyCode.Comma893|| constantKeyCode === KeyCode.Minus894|| constantKeyCode === KeyCode.Period895|| constantKeyCode === KeyCode.Slash896|| constantKeyCode === KeyCode.Backquote897|| constantKeyCode === KeyCode.BracketLeft898|| constantKeyCode === KeyCode.Backslash899|| constantKeyCode === KeyCode.BracketRight900);901902if (isOEMKey) {903return null;904}905}906907if (constantKeyCode !== KeyCode.DependsOnKbLayout) {908return KeyCodeUtils.toElectronAccelerator(constantKeyCode);909}910911return null;912}913914private _toResolvedKeybinding(chordParts: ScanCodeChord[][]): NativeResolvedKeybinding[] {915if (chordParts.length === 0) {916return [];917}918const result: NativeResolvedKeybinding[] = [];919this._generateResolvedKeybindings(chordParts, 0, [], result);920return result;921}922923private _generateResolvedKeybindings(chordParts: ScanCodeChord[][], currentIndex: number, previousParts: ScanCodeChord[], result: NativeResolvedKeybinding[]) {924const chordPart = chordParts[currentIndex];925const isFinalIndex = currentIndex === chordParts.length - 1;926for (let i = 0, len = chordPart.length; i < len; i++) {927const chords = [...previousParts, chordPart[i]];928if (isFinalIndex) {929result.push(new NativeResolvedKeybinding(this, this._OS, chords));930} else {931this._generateResolvedKeybindings(chordParts, currentIndex + 1, chords, result);932}933}934}935936public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): NativeResolvedKeybinding {937let code = ScanCodeUtils.toEnum(keyboardEvent.code);938939// Treat NumpadEnter as Enter940if (code === ScanCode.NumpadEnter) {941code = ScanCode.Enter;942}943944const keyCode = keyboardEvent.keyCode;945946if (947(keyCode === KeyCode.LeftArrow)948|| (keyCode === KeyCode.UpArrow)949|| (keyCode === KeyCode.RightArrow)950|| (keyCode === KeyCode.DownArrow)951|| (keyCode === KeyCode.Delete)952|| (keyCode === KeyCode.Insert)953|| (keyCode === KeyCode.Home)954|| (keyCode === KeyCode.End)955|| (keyCode === KeyCode.PageDown)956|| (keyCode === KeyCode.PageUp)957|| (keyCode === KeyCode.Backspace)958) {959// "Dispatch" on keyCode for these key codes to workaround issues with remote desktoping software960// where the scan codes appear to be incorrect (see https://github.com/microsoft/vscode/issues/24107)961const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[keyCode];962if (immutableScanCode !== ScanCode.DependsOnKbLayout) {963code = immutableScanCode;964}965966} else {967968if (969(code === ScanCode.Numpad1)970|| (code === ScanCode.Numpad2)971|| (code === ScanCode.Numpad3)972|| (code === ScanCode.Numpad4)973|| (code === ScanCode.Numpad5)974|| (code === ScanCode.Numpad6)975|| (code === ScanCode.Numpad7)976|| (code === ScanCode.Numpad8)977|| (code === ScanCode.Numpad9)978|| (code === ScanCode.Numpad0)979|| (code === ScanCode.NumpadDecimal)980) {981// "Dispatch" on keyCode for all numpad keys in order for NumLock to work correctly982if (keyCode >= 0) {983const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[keyCode];984if (immutableScanCode !== ScanCode.DependsOnKbLayout) {985code = immutableScanCode;986}987}988}989}990991const ctrlKey = keyboardEvent.ctrlKey || (this._mapAltGrToCtrlAlt && keyboardEvent.altGraphKey);992const altKey = keyboardEvent.altKey || (this._mapAltGrToCtrlAlt && keyboardEvent.altGraphKey);993const chord = new ScanCodeChord(ctrlKey, keyboardEvent.shiftKey, altKey, keyboardEvent.metaKey, code);994return new NativeResolvedKeybinding(this, this._OS, [chord]);995}996997private _resolveChord(chord: Chord | null): ScanCodeChord[] {998if (!chord) {999return [];1000}1001if (chord instanceof ScanCodeChord) {1002return [chord];1003}1004return this.keyCodeChordToScanCodeChord(chord);1005}10061007public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {1008const chords: ScanCodeChord[][] = keybinding.chords.map(chord => this._resolveChord(chord));1009return this._toResolvedKeybinding(chords);1010}10111012private static _redirectCharCode(charCode: number): number {1013switch (charCode) {1014// allow-any-unicode-next-line1015// CJK: 。 「 」 【 】 ; ,1016// map: . [ ] [ ] ; ,1017case CharCode.U_IDEOGRAPHIC_FULL_STOP: return CharCode.Period;1018case CharCode.U_LEFT_CORNER_BRACKET: return CharCode.OpenSquareBracket;1019case CharCode.U_RIGHT_CORNER_BRACKET: return CharCode.CloseSquareBracket;1020case CharCode.U_LEFT_BLACK_LENTICULAR_BRACKET: return CharCode.OpenSquareBracket;1021case CharCode.U_RIGHT_BLACK_LENTICULAR_BRACKET: return CharCode.CloseSquareBracket;1022case CharCode.U_FULLWIDTH_SEMICOLON: return CharCode.Semicolon;1023case CharCode.U_FULLWIDTH_COMMA: return CharCode.Comma;1024}1025return charCode;1026}10271028private static _charCodeToKb(charCode: number): { keyCode: KeyCode; shiftKey: boolean } | null {1029charCode = this._redirectCharCode(charCode);1030if (charCode < CHAR_CODE_TO_KEY_CODE.length) {1031return CHAR_CODE_TO_KEY_CODE[charCode];1032}1033return null;1034}10351036/**1037* Attempt to map a combining character to a regular one that renders the same way.1038*1039* https://www.compart.com/en/unicode/bidiclass/NSM1040*/1041public static getCharCode(char: string): number {1042if (char.length === 0) {1043return 0;1044}1045const charCode = char.charCodeAt(0);1046switch (charCode) {1047case CharCode.U_Combining_Grave_Accent: return CharCode.U_GRAVE_ACCENT;1048case CharCode.U_Combining_Acute_Accent: return CharCode.U_ACUTE_ACCENT;1049case CharCode.U_Combining_Circumflex_Accent: return CharCode.U_CIRCUMFLEX;1050case CharCode.U_Combining_Tilde: return CharCode.U_SMALL_TILDE;1051case CharCode.U_Combining_Macron: return CharCode.U_MACRON;1052case CharCode.U_Combining_Overline: return CharCode.U_OVERLINE;1053case CharCode.U_Combining_Breve: return CharCode.U_BREVE;1054case CharCode.U_Combining_Dot_Above: return CharCode.U_DOT_ABOVE;1055case CharCode.U_Combining_Diaeresis: return CharCode.U_DIAERESIS;1056case CharCode.U_Combining_Ring_Above: return CharCode.U_RING_ABOVE;1057case CharCode.U_Combining_Double_Acute_Accent: return CharCode.U_DOUBLE_ACUTE_ACCENT;1058}1059return charCode;1060}1061}10621063(function () {1064function define(charCode: number, keyCode: KeyCode, shiftKey: boolean): void {1065for (let i = CHAR_CODE_TO_KEY_CODE.length; i < charCode; i++) {1066CHAR_CODE_TO_KEY_CODE[i] = null;1067}1068CHAR_CODE_TO_KEY_CODE[charCode] = { keyCode: keyCode, shiftKey: shiftKey };1069}10701071for (let chCode = CharCode.A; chCode <= CharCode.Z; chCode++) {1072define(chCode, KeyCode.KeyA + (chCode - CharCode.A), true);1073}10741075for (let chCode = CharCode.a; chCode <= CharCode.z; chCode++) {1076define(chCode, KeyCode.KeyA + (chCode - CharCode.a), false);1077}10781079define(CharCode.Semicolon, KeyCode.Semicolon, false);1080define(CharCode.Colon, KeyCode.Semicolon, true);10811082define(CharCode.Equals, KeyCode.Equal, false);1083define(CharCode.Plus, KeyCode.Equal, true);10841085define(CharCode.Comma, KeyCode.Comma, false);1086define(CharCode.LessThan, KeyCode.Comma, true);10871088define(CharCode.Dash, KeyCode.Minus, false);1089define(CharCode.Underline, KeyCode.Minus, true);10901091define(CharCode.Period, KeyCode.Period, false);1092define(CharCode.GreaterThan, KeyCode.Period, true);10931094define(CharCode.Slash, KeyCode.Slash, false);1095define(CharCode.QuestionMark, KeyCode.Slash, true);10961097define(CharCode.BackTick, KeyCode.Backquote, false);1098define(CharCode.Tilde, KeyCode.Backquote, true);10991100define(CharCode.OpenSquareBracket, KeyCode.BracketLeft, false);1101define(CharCode.OpenCurlyBrace, KeyCode.BracketLeft, true);11021103define(CharCode.Backslash, KeyCode.Backslash, false);1104define(CharCode.Pipe, KeyCode.Backslash, true);11051106define(CharCode.CloseSquareBracket, KeyCode.BracketRight, false);1107define(CharCode.CloseCurlyBrace, KeyCode.BracketRight, true);11081109define(CharCode.SingleQuote, KeyCode.Quote, false);1110define(CharCode.DoubleQuote, KeyCode.Quote, true);1111})();111211131114