diff --git a/build/gulpfile.common.js b/build/gulpfile.common.js index b96e4b238f2..69bd955b979 100644 --- a/build/gulpfile.common.js +++ b/build/gulpfile.common.js @@ -7,7 +7,7 @@ var path = require('path'); var gulp = require('gulp'); var sourcemaps = require('gulp-sourcemaps'); var filter = require('gulp-filter'); -var minifyCSS = require('gulp-minify-css'); +var minifyCSS = require('gulp-cssnano'); var uglify = require('gulp-uglify'); var es = require('event-stream'); var concat = require('gulp-concat'); diff --git a/package.json b/package.json index e64eb2a0139..2b83553bb64 100644 --- a/package.json +++ b/package.json @@ -47,10 +47,10 @@ "gulp-azure-storage": "^0.3.0", "gulp-bom": "^1.0.0", "gulp-concat": "^2.6.0", + "gulp-cssnano": "^2.1.0", "gulp-filter": "^3.0.0", "gulp-insert": "^0.5.0", "gulp-json-editor": "^2.2.1", - "gulp-minify-css": "^1.2.1", "gulp-mocha": "^2.1.3", "gulp-remote-src": "^0.4.0", "gulp-rename": "^1.2.0", diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index a15c16108c8..bf2d8bb800b 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -123,6 +123,9 @@ let KEY_CODE_MAP: {[keyCode:number]:KeyCode} = {}; KEY_CODE_MAP[220] = KeyCode.US_BACKSLASH; KEY_CODE_MAP[221] = KeyCode.US_CLOSE_SQUARE_BRACKET; KEY_CODE_MAP[222] = KeyCode.US_QUOTE; + KEY_CODE_MAP[223] = KeyCode.OEM_8; + + KEY_CODE_MAP[226] = KeyCode.OEM_102; if (Browser.isIE11orEarlier) { KEY_CODE_MAP[91] = KeyCode.Meta; diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index 5c1ddf28241..f3c75f9aa8a 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -5,10 +5,15 @@ "use strict"; -import nls = require('vs/nls'); -import Platform = require('vs/base/common/platform'); +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 @@ -106,49 +111,68 @@ export enum KeyCode { 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 @@ -174,8 +198,58 @@ export enum KeyCode { MAX_VALUE } -let TO_STRING_MAP: string[] = []; -(function() { +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'; @@ -274,6 +348,8 @@ let TO_STRING_MAP: string[] = []; 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'; @@ -298,29 +374,41 @@ let TO_STRING_MAP: string[] = []; // console.warn('Missing string representation for ' + KeyCode[i]); // } // } -})(); +}, (FROM_STRING_MAP) => { + FROM_STRING_MAP['\r'] = KeyCode.Enter; +}); -let FROM_STRING_MAP: {[str:string]:KeyCode;} = {}; -FROM_STRING_MAP['\r'] = KeyCode.Enter; -(function() { - for (let i = 0, len = TO_STRING_MAP.length; i < len; i++) { - if (!TO_STRING_MAP[i]) { - continue; - } - FROM_STRING_MAP[TO_STRING_MAP[i]] = i; - FROM_STRING_MAP[TO_STRING_MAP[i].toLowerCase()] = i; + +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 TO_STRING_MAP[key]; + return STRING.fromKeyCode(key); } export function fromString(key:string): KeyCode { - if (FROM_STRING_MAP.hasOwnProperty(key)) { - return FROM_STRING_MAP[key]; - } - return KeyCode.Unknown; + return STRING.toKeyCode(key); } } @@ -432,49 +520,65 @@ export class Keybinding { /** * Format the binding to a format appropiate for rendering in the UI */ - private static _toUSLabel(value:number): string { - return _asString(value, (Platform.isMacintosh ? MacUIKeyLabelProvider.INSTANCE : ClassicUIKeyLabelProvider.INSTANCE)); + 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): IHTMLContentElement[] { - return _asHTML(value, (Platform.isMacintosh ? MacUIKeyLabelProvider.INSTANCE : ClassicUIKeyLabelProvider.INSTANCE)); + 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): string { - return _asString(value, labelProvider); + 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): IHTMLContentElement[] { - return _asHTML(value, labelProvider); + 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): string { + private static _toElectronAccelerator(value:number, Platform:ISimplifiedPlatform): string { if (BinaryKeybindings.hasChord(value)) { // Electron cannot handle chords return null; } - return _asString(value, ElectronAcceleratorLabelProvider.INSTANCE); + 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): string { - let result = _asString(value, UserSettingsKeyLabelProvider.INSTANCE); - result = result.toLowerCase().replace(/arrow/g, ''); + 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'); @@ -485,6 +589,90 @@ export class Keybinding { 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) { @@ -514,44 +702,44 @@ export class Keybinding { /** * Format the binding to a format appropiate for rendering in the UI */ - public _toUSLabel(): string { - return Keybinding._toUSLabel(this.value); + 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(): IHTMLContentElement[] { - return Keybinding._toUSHTMLLabel(this.value); + 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): string { - return Keybinding._toCustomLabel(this.value, labelProvider); + 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): IHTMLContentElement[] { - return Keybinding._toCustomHTMLLabel(this.value, labelProvider); + 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(): string { - return Keybinding._toElectronAccelerator(this.value); + 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(): string { - return Keybinding.toUserSettingsLabel(this.value); + public toUserSettingsLabel(Platform:ISimplifiedPlatform = defaultPlatform): string { + return Keybinding.toUserSettingsLabel(this.value, Platform); } } @@ -662,11 +850,11 @@ class UserSettingsKeyLabelProvider implements IKeyBindingLabelProvider { public modifierSeparator = '+'; public getLabelForKey(keyCode:KeyCode): string { - return KeyCode.toString(keyCode); + return USER_SETTINGS.fromKeyCode(keyCode); } } -function _asString(keybinding:number, labelProvider:IKeyBindingLabelProvider): string { +function _asString(keybinding:number, labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform): string { let result:string[] = [], ctrlCmd = BinaryKeybindings.hasCtrlCmd(keybinding), shift = BinaryKeybindings.hasShift(keybinding), @@ -707,7 +895,7 @@ function _asString(keybinding:number, labelProvider:IKeyBindingLabelProvider): s var actualResult = result.join(labelProvider.modifierSeparator); if (BinaryKeybindings.hasChord(keybinding)) { - return actualResult + ' ' + _asString(BinaryKeybindings.extractChordPart(keybinding), labelProvider); + return actualResult + ' ' + _asString(BinaryKeybindings.extractChordPart(keybinding), labelProvider, Platform); } return actualResult; @@ -727,7 +915,7 @@ function _pushKey(result:IHTMLContentElement[], str:string): void { }); } -function _asHTML(keybinding:number, labelProvider:IKeyBindingLabelProvider, isChord:boolean = false): IHTMLContentElement[] { +function _asHTML(keybinding:number, labelProvider:IKeyBindingLabelProvider, Platform:ISimplifiedPlatform, isChord:boolean = false): IHTMLContentElement[] { let result:IHTMLContentElement[] = [], ctrlCmd = BinaryKeybindings.hasCtrlCmd(keybinding), shift = BinaryKeybindings.hasShift(keybinding), @@ -768,7 +956,7 @@ function _asHTML(keybinding:number, labelProvider:IKeyBindingLabelProvider, isCh let chordTo: IHTMLContentElement[] = null; if (BinaryKeybindings.hasChord(keybinding)) { - chordTo = _asHTML(BinaryKeybindings.extractChordPart(keybinding), labelProvider, true); + chordTo = _asHTML(BinaryKeybindings.extractChordPart(keybinding), labelProvider, Platform, true); result.push({ tagName: 'span', text: ' ' diff --git a/src/vs/base/test/common/keyCodes.test.ts b/src/vs/base/test/common/keyCodes.test.ts index 4cfd2dbcaf3..202df661e3e 100644 --- a/src/vs/base/test/common/keyCodes.test.ts +++ b/src/vs/base/test/common/keyCodes.test.ts @@ -5,7 +5,8 @@ 'use strict'; import * as assert from 'assert'; -import {KeyCode, KeyMod, BinaryKeybindings} from 'vs/base/common/keyCodes'; +import {KeyCode, KeyMod, BinaryKeybindings, Keybinding} from 'vs/base/common/keyCodes'; +import * as Strings from 'vs/base/common/strings'; interface ITestKeybinding { ctrlCmd?: boolean; @@ -65,4 +66,44 @@ suite('keyCodes', () => { assert.equal(encodedFirstPart, KeyMod.CtrlCmd | KeyCode.KEY_Y, 'first part'); assert.equal(encodedSecondPart, encodedSecondPart, 'chord part'); }); + + test('getUserSettingsKeybindingRegex', () => { + let regex = new RegExp(Keybinding.getUserSettingsKeybindingRegex()); + + function testIsGood(userSettingsLabel:string, message:string = userSettingsLabel): void { + let userSettings = '"' + userSettingsLabel.replace(/\\/g, '\\\\') + '"'; + let isGood = regex.test(userSettings); + assert.ok(isGood, message); + } + + // check that all key codes are covered by the regex + let ignore: boolean[] = []; + ignore[KeyCode.Shift] = true; + ignore[KeyCode.Ctrl] = true; + ignore[KeyCode.Alt] = true; + ignore[KeyCode.Meta] = true; + for (let keyCode = KeyCode.Unknown + 1; keyCode < KeyCode.MAX_VALUE; keyCode++) { + if (ignore[keyCode]) { + continue; + } + let userSettings = Keybinding.toUserSettingsLabel(keyCode); + testIsGood(userSettings, keyCode + ' - ' + KeyCode[keyCode] + ' - ' + userSettings); + } + + // one modifier + testIsGood('ctrl+a'); + testIsGood('shift+a'); + testIsGood('alt+a'); + testIsGood('cmd+a'); + testIsGood('meta+a'); + testIsGood('win+a'); + + // more modifiers + testIsGood('ctrl+shift+a'); + testIsGood('shift+alt+a'); + testIsGood('ctrl+shift+alt+a'); + + // chords + testIsGood('ctrl+a ctrl+a'); + }) }); diff --git a/src/vs/editor/browser/standalone/simpleServices.ts b/src/vs/editor/browser/standalone/simpleServices.ts index 93c7202721f..122a1d13d4b 100644 --- a/src/vs/editor/browser/standalone/simpleServices.ts +++ b/src/vs/editor/browser/standalone/simpleServices.ts @@ -21,7 +21,7 @@ import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {IKeybindingContextKey, IKeybindingItem, ICommandHandler, ICommandsMap} from 'vs/platform/keybinding/common/keybindingService'; import {AbstractPluginService} from 'vs/platform/plugins/common/abstractPluginService'; -import {IOSupport} from 'vs/platform/keybinding/common/commonKeybindingResolver'; +import {IOSupport} from 'vs/platform/keybinding/common/keybindingResolver'; import {PluginsRegistry, PluginsMessageCollector} from 'vs/platform/plugins/common/pluginsRegistry'; export class SimpleEditor implements IEditor { diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 39592189e0b..f33ff2648f0 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -119,9 +119,7 @@ export default class LanguageFeatureRegistry { return; } - if (this._updateScores(model)) { - this._sortByScore(); - } + this._updateScores(model); let supportIndex: number = -1; let supportEntry: Entry; @@ -171,10 +169,8 @@ export default class LanguageFeatureRegistry { for (let entry of this._entries) { entry._score = score(entry.selector, model.getAssociatedResource(), model.getModeId()); } - return true; - } - private _sortByScore(): void { + // needs sorting this._entries.sort(LanguageFeatureRegistry._compareByScoreAndTime); } diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index 860bf0f7661..ca27338b283 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -16,7 +16,6 @@ import Actions = require('vs/base/common/actions'); import ActionBar = require('vs/base/browser/ui/actionbar/actionbar'); import Lifecycle = require('vs/base/common/lifecycle'); import SortedList = require('vs/base/common/sortedList'); -import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; import {IContextViewService, IContextMenuService} from 'vs/platform/contextview/browser/contextView'; import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; import {INullService} from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/editor/contrib/defineKeybinding/browser/defineKeybinding.ts b/src/vs/editor/contrib/defineKeybinding/browser/defineKeybinding.ts index 97e2d3fb076..dc80982c258 100644 --- a/src/vs/editor/contrib/defineKeybinding/browser/defineKeybinding.ts +++ b/src/vs/editor/contrib/defineKeybinding/browser/defineKeybinding.ts @@ -21,7 +21,7 @@ import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/edito import {TPromise} from 'vs/base/common/winjs.base'; import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; import {RunOnceScheduler} from 'vs/base/common/async'; -import {IOSupport} from 'vs/platform/keybinding/common/commonKeybindingResolver'; +import {IOSupport} from 'vs/platform/keybinding/common/keybindingResolver'; import {IHTMLContentElement} from 'vs/base/common/htmlContent'; import {renderHtml} from 'vs/base/browser/htmlContentRenderer'; import {Range} from 'vs/editor/common/core/range'; @@ -137,26 +137,10 @@ export class DefineKeybindingController implements EditorCommon.IEditorContribut this._updateDecorations.schedule(); } - private static _cachedKeybindingRegex: string = null; - private static _getKeybindingRegex(): string { - if (!this._cachedKeybindingRegex) { - let numpadKey = "numpad(0|1|2|3|4|5|6|7|8|9|_multiply|_add|_subtract|_decimal|_divide)"; - let punctKey = "`|\\-|=|\\[|\\]|\\\\\\\\|;|'|,|\\.|\\/"; - let specialKey = "left|up|right|down|pageup|pagedown|end|home|tab|enter|escape|space|backspace|delete|pausebreak|capslock|insert"; - let casualKey = "[a-z]|[0-9]|f(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15)"; - let key = '((' + [numpadKey, punctKey, 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; - } - private _dec:string[] = []; private _updateDecorationsNow(): void { let model = this._editor.getModel(); - let regex = DefineKeybindingController._getKeybindingRegex(); + let regex = Keybinding.getUserSettingsKeybindingRegex(); var m = model.findMatches(regex, false, true, false, false); diff --git a/src/vs/editor/contrib/quickOpen/browser/quickCommand.ts b/src/vs/editor/contrib/quickOpen/browser/quickCommand.ts index 4165e8c5fb9..dd851d989e1 100644 --- a/src/vs/editor/contrib/quickOpen/browser/quickCommand.ts +++ b/src/vs/editor/contrib/quickOpen/browser/quickCommand.ts @@ -17,7 +17,6 @@ import Filters = require('vs/base/common/filters'); import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions'; import {EditorAction, Behaviour} from 'vs/editor/common/editorAction'; import EditorQuickOpen = require('./editorQuickOpen'); -import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; export class EditorActionCommandEntry extends QuickOpenModel.QuickOpenEntryGroup { diff --git a/src/vs/editor/standalone-languages/swift.ts b/src/vs/editor/standalone-languages/swift.ts index 86cddd1dca6..56d95f44d28 100644 --- a/src/vs/editor/standalone-languages/swift.ts +++ b/src/vs/editor/standalone-languages/swift.ts @@ -6,7 +6,7 @@ import {ILanguage} from './types'; -export var language = { +export var language: ILanguage = { displayName: 'Swift', name: 'swift', defaultToken: '', @@ -130,4 +130,4 @@ export var language = { [/([.])(@identifier)/, { cases: { '$2': ['delimeter', 'type.identifier'], '@default': '' } }], ] } -}; \ No newline at end of file +}; diff --git a/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts b/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts index f27fd306e82..c541ed2e697 100644 --- a/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts +++ b/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts @@ -6,21 +6,21 @@ import 'vs/css!./keybindings'; +import * as nls from 'vs/nls'; import Severity from 'vs/base/common/severity'; import {TPromise} from 'vs/base/common/winjs.base'; -import nls = require('vs/nls'); -import lifecycle = require('vs/base/common/lifecycle'); -import DOM = require('vs/base/browser/dom'); -import Keyboard = require('vs/base/browser/keyboardEvent'); +import {IDisposable} from 'vs/base/common/lifecycle'; +import * as DOM from 'vs/base/browser/dom'; +import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; -import {IKeybindingService, IKeybindingScopeLocation, ICommandHandler, IKeybindingItem, IKeybindings, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; +import {IKeybindingService, IKeybindingScopeLocation, ICommandHandler, IKeybindingItem, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IMessageService} from 'vs/platform/message/common/message'; -import {IResolveResult, CommonKeybindingResolver} from 'vs/platform/keybinding/common/commonKeybindingResolver'; +import {KeybindingResolver} from 'vs/platform/keybinding/common/keybindingResolver'; import {Keybinding, KeyCode} from 'vs/base/common/keyCodes'; import {IHTMLContentElement} from 'vs/base/common/htmlContent'; -var KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; +let KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; export class KeybindingContext { private _parent: KeybindingContext; @@ -45,8 +45,8 @@ export class KeybindingContext { } public getValue(): any { - var r = this._parent ? this._parent.getValue() : Object.create(null); - for (var key in this._value) { + let r = this._parent ? this._parent.getValue() : Object.create(null); + for (let key in this._value) { r[key] = this._value[key]; } return r; @@ -138,10 +138,10 @@ export class KeybindingService extends AbstractKeybindingService implements IKey }; protected _domNode: HTMLElement; - private _toDispose: lifecycle.IDisposable; + private _toDispose: IDisposable; private _resolver: KeybindingResolver; private _currentChord: number; - private _currentChordStatusMessage: lifecycle.IDisposable; + private _currentChordStatusMessage: IDisposable; constructor(domNode: HTMLElement) { this._lastContextId = -1; @@ -150,7 +150,7 @@ export class KeybindingService extends AbstractKeybindingService implements IKey this._contexts = Object.create(null); this._contexts[String(this._myContextId)] = new KeybindingContext(this._myContextId, null); this._toDispose = DOM.addDisposableListener(this._domNode, DOM.EventType.KEY_DOWN, (e:KeyboardEvent) => { - var keyEvent = new Keyboard.StandardKeyboardEvent(e); + let keyEvent = new StandardKeyboardEvent(e); this._dispatch(keyEvent); }); @@ -201,9 +201,10 @@ export class KeybindingService extends AbstractKeybindingService implements IKey } private _getAllCommandsAsComment(): string { - var boundCommands = this._resolver.getDefaultBoundCommands(); - var unboundCommands = Object.keys(KeybindingsRegistry.getCommands()).filter(commandId => commandId[0] !== '_' && !boundCommands[commandId]); - var pretty = unboundCommands.join('\n// - '); + let boundCommands = this._resolver.getDefaultBoundCommands(); + let unboundCommands = Object.keys(KeybindingsRegistry.getCommands()).filter(commandId => commandId[0] !== '_' && !boundCommands[commandId]); + unboundCommands.sort(); + let pretty = unboundCommands.join('\n// - '); return '// ' + nls.localize('unboundCommands', "Here are other available commands: ") + '\n// - ' + pretty; } @@ -213,17 +214,17 @@ export class KeybindingService extends AbstractKeybindingService implements IKey } private _dispatch(e: DOM.IKeyboardEvent): void { - var isModifierKey = (e.keyCode === KeyCode.Ctrl || e.keyCode === KeyCode.Shift || e.keyCode === KeyCode.Alt || e.keyCode === KeyCode.Meta); + let isModifierKey = (e.keyCode === KeyCode.Ctrl || e.keyCode === KeyCode.Shift || e.keyCode === KeyCode.Alt || e.keyCode === KeyCode.Meta); if (isModifierKey) { return; } - var contextId = this._findContextAttr(e.target); - var context = this.getContext(contextId); - var contextValue = context.getValue(); + let contextId = this._findContextAttr(e.target); + let context = this.getContext(contextId); + let contextValue = context.getValue(); // console.log(JSON.stringify(contextValue, null, '\t')); - var resolveResult = this._resolver.resolveKeyboardEvent(contextValue, this._currentChord, e); + let resolveResult = this._resolver.resolve(contextValue, this._currentChord, e.asKeybinding()); if (resolveResult && resolveResult.enterChord) { e.preventDefault(); @@ -253,7 +254,7 @@ export class KeybindingService extends AbstractKeybindingService implements IKey if (!/^\^/.test(resolveResult.commandId)) { e.preventDefault(); } - var commandId = resolveResult.commandId.replace(/^\^/, ''); + let commandId = resolveResult.commandId.replace(/^\^/, ''); this._invokeHandler(commandId, { context: contextValue }).done(undefined, err => { this._messageService.show(Severity.Warning, err); }); @@ -289,7 +290,7 @@ export class KeybindingService extends AbstractKeybindingService implements IKey } public createChildContext(parentContextId: number = this._myContextId): number { - var id = (++this._lastContextId); + let id = (++this._lastContextId); this._contexts[String(id)] = new KeybindingContext(id, this.getContext(parentContextId)); return id; } @@ -300,9 +301,9 @@ export class KeybindingService extends AbstractKeybindingService implements IKey public executeCommand(commandId: string, args:any = {}): TPromise { if (!args.context) { - var contextId = this._findContextAttr(document.activeElement); - var context = this.getContext(contextId); - var contextValue = context.getValue(); + let contextId = this._findContextAttr(document.activeElement); + let context = this.getContext(contextId); + let contextValue = context.getValue(); args.context = contextValue; } @@ -368,9 +369,3 @@ class ScopedKeybindingService extends AbstractKeybindingService { return this._parent.executeCommand(commandId, args); } } - -export class KeybindingResolver extends CommonKeybindingResolver { - public resolveKeyboardEvent(context: any, currentChord: number, key: DOM.IKeyboardEvent): IResolveResult { - return this.resolve(context, currentChord, key.asKeybinding()); - } -} \ No newline at end of file diff --git a/src/vs/platform/keybinding/common/commonKeybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts similarity index 74% rename from src/vs/platform/keybinding/common/commonKeybindingResolver.ts rename to src/vs/platform/keybinding/common/keybindingResolver.ts index d077fbcdd21..eff9a891d5a 100644 --- a/src/vs/platform/keybinding/common/commonKeybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; -import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; -import Platform = require('vs/base/common/platform'); -import {IKeybindingService, IKeybindingScopeLocation, ICommandHandler, IKeybindingItem, IKeybindings, KbExpr, IUserFriendlyKeybinding, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; -import {KeyMod, KeyCode, BinaryKeybindings, Keybinding} from 'vs/base/common/keyCodes'; +import * as defaultPlatform from 'vs/base/common/platform'; +import {IKeybindingItem, KbExpr, IUserFriendlyKeybinding} from 'vs/platform/keybinding/common/keybindingService'; +import {KeyMod, KeyCode, BinaryKeybindings, Keybinding, ISimplifiedPlatform} from 'vs/base/common/keyCodes'; export interface IResolveResult { enterChord: number; @@ -33,7 +31,7 @@ interface ICommandEntry { commandId: string; } -export class CommonKeybindingResolver { +export class KeybindingResolver { private _defaultKeybindings: IKeybindingItem[]; private _defaultBoundCommands: IBoundCommands; private _map: ICommandMap; @@ -107,10 +105,10 @@ export class CommonKeybindingResolver { return; } - var conflicts = this._map[keypress]; + let conflicts = this._map[keypress]; - for (var i = conflicts.length - 1; i >= 0; i--) { - var conflict = conflicts[i]; + for (let i = conflicts.length - 1; i >= 0; i--) { + let conflict = conflicts[i]; if (conflict.commandId === item.command) { continue; @@ -121,7 +119,7 @@ export class CommonKeybindingResolver { continue; } - if (CommonKeybindingResolver.contextIsEntirelyIncluded(true, conflict.context, item.context)) { + if (KeybindingResolver.contextIsEntirelyIncluded(true, conflict.context, item.context)) { // `item` completely overwrites `conflict` if (this._shouldWarnOnConflict && isDefault) { console.warn('Conflict detected, command `' + conflict.commandId + '` cannot be triggered by ' + Keybinding.toUserSettingsLabel(keypress)); @@ -182,11 +180,17 @@ export class CommonKeybindingResolver { } public getDefaultKeybindings(): string { - var out = new OutputBuilder(); + let out = new OutputBuilder(); out.writeLine('['); - this._defaultKeybindings.forEach(k => { + + let lastIndex = this._defaultKeybindings.length - 1; + this._defaultKeybindings.forEach((k, index) => { IOSupport.writeKeybindingItem(out, k); - out.writeLine(','); + if (index !== lastIndex) { + out.writeLine(','); + } else { + out.writeLine(); + } }); out.writeLine(']'); return out.toString(); @@ -262,7 +266,7 @@ export class CommonKeybindingResolver { for (let i = matches.length - 1; i >= 0; i--) { let k = matches[i]; - if (!CommonKeybindingResolver.contextMatchesRules(context, k.context)) { + if (!KeybindingResolver.contextMatchesRules(context, k.context)) { continue; } @@ -339,8 +343,8 @@ export class IOSupport { } public static readKeybindingItem(input: IUserFriendlyKeybinding, index:number): IKeybindingItem { - var key = IOSupport.readKeybinding(input.key); - var context = IOSupport.readKeybindingContexts(input.when); + let key = IOSupport.readKeybinding(input.key); + let context = IOSupport.readKeybindingContexts(input.when); return { keybinding: key, command: input.command, @@ -350,98 +354,12 @@ export class IOSupport { }; } - private static writeKeybinding(input: number): string { - return Keybinding.toUserSettingsLabel(input); + public static writeKeybinding(input: number, Platform: ISimplifiedPlatform = defaultPlatform): string { + return Keybinding.toUserSettingsLabel(input, Platform); } - public static readKeybinding(input: string): number { - if (!input) { - return null; - } - input = input.toLowerCase().trim(); - - var 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); - } - } - - if (/^(up|down|left|right)/.test(input)) { - input = input.replace(/^(up|down|left|right)/,(captured) => { - return captured + 'arrow'; - }); - } - - var chord: number = 0; - - var firstSpaceIdx = input.indexOf(' '); - if (firstSpaceIdx > 0) { - key = input.substring(0, firstSpaceIdx); - chord = IOSupport.readKeybinding(input.substring(firstSpaceIdx)); - } else { - key = input; - } - - let keyCode = KeyCode.fromString(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 static readKeybinding(input: string, Platform: ISimplifiedPlatform = defaultPlatform): number { + return Keybinding.fromUserSettingsLabel(input, Platform); } public static readKeybindingContexts(input: string): KbExpr { diff --git a/src/vs/platform/keybinding/common/keybindingService.ts b/src/vs/platform/keybinding/common/keybindingService.ts index 0c5b6d12dd6..c2b06425bc6 100644 --- a/src/vs/platform/keybinding/common/keybindingService.ts +++ b/src/vs/platform/keybinding/common/keybindingService.ts @@ -6,7 +6,7 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {TypeConstraint} from 'vs/base/common/types'; -import {createDecorator, IInstantiationService, ServiceIdentifier, ServicesAccessor} from 'vs/platform/instantiation/common/instantiation'; +import {createDecorator, ServiceIdentifier, ServicesAccessor} from 'vs/platform/instantiation/common/instantiation'; import {Keybinding} from 'vs/base/common/keyCodes'; import {IHTMLContentElement} from 'vs/base/common/htmlContent'; @@ -168,7 +168,7 @@ export class KbAndExpression implements KbExpr { } public evaluate(context:any): boolean { - for (var i = 0, len = this.expr.length; i < len; i++) { + for (let i = 0, len = this.expr.length; i < len; i++) { if (!this.expr[i].evaluate(context)) { return false; } @@ -221,7 +221,7 @@ export class KbAndExpression implements KbExpr { } -export var KbExpr = { +export let KbExpr = { has: (key:string) => new KbDefinedExpression(key), equals: (key:string, value:any) => new KbEqualsExpression(key, value), notEquals: (key:string, value:any) => new KbNotEqualsExpression(key, value), @@ -268,7 +268,7 @@ export var KbExpr = { return false; } - var m = /^'([^']*)'$/.exec(serializedValue); + let m = /^'([^']*)'$/.exec(serializedValue); if (m) { return m[1].trim(); } @@ -305,7 +305,7 @@ export interface IKeybindingContextKey { reset(): void; } -export var IKeybindingService = createDecorator('keybindingService'); +export let IKeybindingService = createDecorator('keybindingService'); export interface IKeybindingScopeLocation { setAttribute(attr:string, value:string): void; diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 4c171331e10..756f6ef43c8 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -7,7 +7,6 @@ import {Registry} from 'vs/platform/platform'; import {TypeConstraint, validateConstraints} from 'vs/base/common/types'; import {ICommandHandler, ICommandHandlerDescription, ICommandsMap, IKeybindingItem, IKeybindings, KbExpr} from 'vs/platform/keybinding/common/keybindingService'; -import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; import {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes'; import Platform = require('vs/base/common/platform'); @@ -28,9 +27,6 @@ export interface IKeybindingsRegistry { getCommands(): ICommandsMap; getDefaultKeybindings(): IKeybindingItem[]; - KEYBINDING_CONTEXT_OPERATOR_EQUAL: string; - KEYBINDING_CONTEXT_OPERATOR_NOT_EQUAL: string; - WEIGHT: { editorCore(importance?: number): number; editorContrib(importance?: number): number; @@ -45,9 +41,6 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { private _keybindings: IKeybindingItem[]; private _commands: ICommandsMap; - public KEYBINDING_CONTEXT_OPERATOR_EQUAL = 'equal'; - public KEYBINDING_CONTEXT_OPERATOR_NOT_EQUAL = 'not_equal'; - public WEIGHT = { editorCore: (importance: number = 0): number => { return 0 + importance; @@ -71,8 +64,29 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { this._commands = Object.create(null); } + /** + * Take current platform into account and reduce to primary & secondary. + */ + private static bindToCurrentPlatform(kb:IKeybindings): { primary?: number; secondary?: number[]; } { + if (Platform.isWindows) { + if (kb && kb.win) { + return kb.win; + } + } else if (Platform.isMacintosh) { + if (kb && kb.mac) { + return kb.mac; + } + } else { + if (kb && kb.linux) { + return kb.linux; + } + } + + return kb; + } + public registerCommandRule(rule:ICommandRule): void { - var actualKb = KeybindingsUtils.bindToCurrentPlatform(rule); + let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule); if (actualKb && actualKb.primary) { this.registerDefaultKeybinding(actualKb.primary, rule.id, rule.weight, 0, rule.context); @@ -91,14 +105,13 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { // } // this._commands[desc.id] = desc.handler; - let {handler} = desc; + let handler = desc.handler; let description = desc.description || handler.description; // add argument validation if rich command metadata is provided if (typeof description === 'object') { - const metadata = description; - const constraints: TypeConstraint[] = []; - for (let arg of metadata.args) { + let constraints: TypeConstraint[] = []; + for (let arg of description.args) { constraints.push(arg.constraint); } handler = function(accesor, args) { @@ -139,10 +152,10 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { return this._keybindings; } } -export var KeybindingsRegistry:IKeybindingsRegistry = new KeybindingsRegistryImpl(); +export let KeybindingsRegistry:IKeybindingsRegistry = new KeybindingsRegistryImpl(); // Define extension point ids -export var Extensions = { +export let Extensions = { EditorModes: 'platform.keybindingsRegistry' }; Registry.add(Extensions.EditorModes, KeybindingsRegistry); \ No newline at end of file diff --git a/src/vs/platform/keybinding/common/keybindingsUtils.ts b/src/vs/platform/keybinding/common/keybindingsUtils.ts deleted file mode 100644 index 1371eb36a44..00000000000 --- a/src/vs/platform/keybinding/common/keybindingsUtils.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 nls = require('vs/nls'); -import Platform = require('vs/base/common/platform'); - -import {IKeybindings} from 'vs/platform/keybinding/common/keybindingService'; -import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; - -export class KeybindingsUtils { - /** - * Take current platform into account and reduce to primary & secondary. - */ - public static bindToCurrentPlatform(kb:IKeybindings): { primary?: number; secondary?: number[]; } { - if (Platform.isWindows) { - if (kb && kb.win) { - return kb.win; - } - } else if (Platform.isMacintosh) { - if (kb && kb.mac) { - return kb.mac; - } - } else { - if (kb && kb.linux) { - return kb.linux; - } - } - - return kb; - } -} diff --git a/src/vs/platform/keybinding/test/common/keybindingIO.test.ts b/src/vs/platform/keybinding/test/common/keybindingIO.test.ts new file mode 100644 index 00000000000..30b2b28cd51 --- /dev/null +++ b/src/vs/platform/keybinding/test/common/keybindingIO.test.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * 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 {IOSupport} from 'vs/platform/keybinding/common/keybindingResolver'; +import {KeyMod, KeyCode, ISimplifiedPlatform} from 'vs/base/common/keyCodes'; + +suite('Keybinding IO', () => { + + test('serialize/deserialize', function() { + const WINDOWS = { isMacintosh: false, isWindows: true }; + const MACINTOSH = { isMacintosh: true, isWindows: false }; + const LINUX = { isMacintosh: false, isWindows: false }; + + function testOneSerialization(keybinding:number, expected:string, msg:string, Platform:ISimplifiedPlatform): void { + let actualSerialized = IOSupport.writeKeybinding(keybinding, Platform); + assert.equal(actualSerialized, expected, expected + ' - ' + msg); + } + function testSerialization(keybinding:number, expectedWin:string, expectedMac:string, expectedLinux:string): void { + testOneSerialization(keybinding, expectedWin, 'win', WINDOWS); + testOneSerialization(keybinding, expectedMac, 'mac', MACINTOSH); + testOneSerialization(keybinding, expectedLinux, 'linux', LINUX); + } + + function testOneDeserialization(keybinding:string, expected:number, msg:string, Platform:ISimplifiedPlatform): void { + let actualDeserialized = IOSupport.readKeybinding(keybinding, Platform); + assert.equal(actualDeserialized, expected, keybinding + ' - ' + msg); + } + function testDeserialization(inWin:string, inMac:string, inLinux:string, expected:number): void { + testOneDeserialization(inWin, expected, 'win', WINDOWS); + testOneDeserialization(inMac, expected, 'mac', MACINTOSH); + testOneDeserialization(inLinux, expected, 'linux', LINUX); + } + + function testRoundtrip(keybinding:number, expectedWin:string, expectedMac:string, expectedLinux:string): void { + testSerialization(keybinding, expectedWin, expectedMac, expectedLinux); + testDeserialization(expectedWin, expectedMac, expectedLinux, keybinding); + } + + testRoundtrip(KeyCode.KEY_0, '0', '0', '0'); + testRoundtrip(KeyCode.KEY_A, 'a', 'a', 'a'); + testRoundtrip(KeyCode.UpArrow, 'up', 'up', 'up'); + testRoundtrip(KeyCode.RightArrow, 'right', 'right', 'right'); + testRoundtrip(KeyCode.DownArrow, 'down', 'down', 'down'); + testRoundtrip(KeyCode.LeftArrow, 'left', 'left', 'left'); + + // one modifier + testRoundtrip(KeyMod.Alt | KeyCode.KEY_A, 'alt+a', 'alt+a', 'alt+a'); + testRoundtrip(KeyMod.CtrlCmd | KeyCode.KEY_A, 'ctrl+a', 'cmd+a', 'ctrl+a'); + testRoundtrip(KeyMod.Shift | KeyCode.KEY_A, 'shift+a', 'shift+a', 'shift+a'); + testRoundtrip(KeyMod.WinCtrl | KeyCode.KEY_A, 'win+a', 'ctrl+a', 'meta+a'); + + // two modifiers + testRoundtrip(KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_A, 'ctrl+alt+a', 'alt+cmd+a', 'ctrl+alt+a'); + testRoundtrip(KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A, 'ctrl+shift+a', 'shift+cmd+a', 'ctrl+shift+a'); + testRoundtrip(KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_A, 'ctrl+win+a', 'ctrl+cmd+a', 'ctrl+meta+a'); + testRoundtrip(KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A, 'shift+alt+a', 'shift+alt+a', 'shift+alt+a'); + testRoundtrip(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.KEY_A, 'shift+win+a', 'ctrl+shift+a', 'shift+meta+a'); + testRoundtrip(KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'alt+win+a', 'ctrl+alt+a', 'alt+meta+a'); + + // three modifiers + testRoundtrip(KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A, 'ctrl+shift+alt+a', 'shift+alt+cmd+a', 'ctrl+shift+alt+a'); + testRoundtrip(KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.KEY_A, 'ctrl+shift+win+a', 'ctrl+shift+cmd+a', 'ctrl+shift+meta+a'); + testRoundtrip(KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'shift+alt+win+a', 'ctrl+shift+alt+a', 'shift+alt+meta+a'); + + // all modifiers + testRoundtrip(KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'ctrl+shift+alt+win+a', 'ctrl+shift+alt+cmd+a', 'ctrl+shift+alt+meta+a'); + + // chords + testRoundtrip(KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_A, KeyMod.CtrlCmd | KeyCode.KEY_A), 'ctrl+a ctrl+a', 'cmd+a cmd+a', 'ctrl+a ctrl+a'); + testRoundtrip(KeyMod.chord(KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.CtrlCmd | KeyCode.UpArrow), 'ctrl+up ctrl+up', 'cmd+up cmd+up', 'ctrl+up ctrl+up'); + + // OEM keys + testRoundtrip(KeyCode.US_SEMICOLON, ';', ';', ';'); + testRoundtrip(KeyCode.US_EQUAL, '=', '=', '='); + testRoundtrip(KeyCode.US_COMMA, ',', ',', ','); + testRoundtrip(KeyCode.US_MINUS, '-', '-', '-'); + testRoundtrip(KeyCode.US_DOT, '.', '.', '.'); + testRoundtrip(KeyCode.US_SLASH, '/', '/', '/'); + testRoundtrip(KeyCode.US_BACKTICK, '`', '`', '`'); + testRoundtrip(KeyCode.US_OPEN_SQUARE_BRACKET, '[', '[', '['); + testRoundtrip(KeyCode.US_BACKSLASH, '\\', '\\', '\\'); + testRoundtrip(KeyCode.US_CLOSE_SQUARE_BRACKET, ']', ']', ']'); + testRoundtrip(KeyCode.US_QUOTE, '\'', '\'', '\''); + testRoundtrip(KeyCode.OEM_8, 'oem_8', 'oem_8', 'oem_8'); + testRoundtrip(KeyCode.OEM_102, 'oem_102', 'oem_102', 'oem_102'); + + // OEM aliases + testDeserialization('OEM_1', 'OEM_1', 'OEM_1', KeyCode.US_SEMICOLON); + testDeserialization('OEM_PLUS', 'OEM_PLUS', 'OEM_PLUS', KeyCode.US_EQUAL); + testDeserialization('OEM_COMMA', 'OEM_COMMA', 'OEM_COMMA', KeyCode.US_COMMA); + testDeserialization('OEM_MINUS', 'OEM_MINUS', 'OEM_MINUS', KeyCode.US_MINUS); + testDeserialization('OEM_PERIOD', 'OEM_PERIOD', 'OEM_PERIOD', KeyCode.US_DOT); + testDeserialization('OEM_2', 'OEM_2', 'OEM_2', KeyCode.US_SLASH); + testDeserialization('OEM_3', 'OEM_3', 'OEM_3', KeyCode.US_BACKTICK); + testDeserialization('OEM_4', 'OEM_4', 'OEM_4', KeyCode.US_OPEN_SQUARE_BRACKET); + testDeserialization('OEM_5', 'OEM_5', 'OEM_5', KeyCode.US_BACKSLASH); + testDeserialization('OEM_6', 'OEM_6', 'OEM_6', KeyCode.US_CLOSE_SQUARE_BRACKET); + testDeserialization('OEM_7', 'OEM_7', 'OEM_7', KeyCode.US_QUOTE); + testDeserialization('OEM_8', 'OEM_8', 'OEM_8', KeyCode.OEM_8); + testDeserialization('OEM_102', 'OEM_102', 'OEM_102', KeyCode.OEM_102); + + // accepts '-' as separator + testDeserialization('ctrl-shift-alt-win-a', 'ctrl-shift-alt-cmd-a', 'ctrl-shift-alt-meta-a', KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A); + + // various input mistakes + testDeserialization(' ctrl-shift-alt-win-A ', ' shift-alt-cmd-Ctrl-A ', ' ctrl-shift-alt-META-A ', KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A); + }); + +}); \ No newline at end of file diff --git a/src/vs/platform/keybinding/test/common/keybindingService.test.ts b/src/vs/platform/keybinding/test/common/keybindingService.test.ts index aa28933b733..3eb08ee6fe7 100644 --- a/src/vs/platform/keybinding/test/common/keybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingService.test.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import assert = require('assert'); -import {CommonKeybindingResolver, IOSupport} from 'vs/platform/keybinding/common/commonKeybindingResolver'; -import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; -import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; -import Platform = require('vs/base/common/platform'); +import * as assert from 'assert'; +import {KeybindingResolver, IOSupport} from 'vs/platform/keybinding/common/keybindingResolver'; +import * as Platform from 'vs/base/common/platform'; import {KbExpr, KbAndExpression, IKeybindingItem} from 'vs/platform/keybinding/common/keybindingService'; import {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes'; @@ -25,10 +23,10 @@ suite('Keybinding Service', () => { weight2: 0 }; - assert.equal(CommonKeybindingResolver.contextMatchesRules({ bar: 'baz' }, contextRules), true); - assert.equal(CommonKeybindingResolver.contextMatchesRules({ bar: 'bz' }, contextRules), false); + assert.equal(KeybindingResolver.contextMatchesRules({ bar: 'baz' }, contextRules), true); + assert.equal(KeybindingResolver.contextMatchesRules({ bar: 'bz' }, contextRules), false); - var resolver = new CommonKeybindingResolver([keybindingItem], []); + var resolver = new KeybindingResolver([keybindingItem], []); assert.equal(resolver.resolve({ bar: 'baz' }, 0, keybinding).commandId, 'yes'); assert.equal(resolver.resolve({ bar: 'bz' }, 0, keybinding), null); }); @@ -47,10 +45,10 @@ suite('Keybinding Service', () => { test('contextIsEntirelyIncluded', function () { var assertIsIncluded = (a: KbExpr[], b: KbExpr[]) => { - assert.equal(CommonKeybindingResolver.contextIsEntirelyIncluded(false, new KbAndExpression(a), new KbAndExpression(b)), true); + assert.equal(KeybindingResolver.contextIsEntirelyIncluded(false, new KbAndExpression(a), new KbAndExpression(b)), true); }; var assertIsNotIncluded = (a: KbExpr[], b: KbExpr[]) => { - assert.equal(CommonKeybindingResolver.contextIsEntirelyIncluded(false, new KbAndExpression(a), new KbAndExpression(b)), false); + assert.equal(KeybindingResolver.contextIsEntirelyIncluded(false, new KbAndExpression(a), new KbAndExpression(b)), false); }; var key1IsTrue = KbExpr.equals('key1', true); var key1IsNotFalse = KbExpr.notEquals('key1', false); @@ -203,7 +201,7 @@ suite('Keybinding Service', () => { } ]; - var resolver = new CommonKeybindingResolver(items, [], false); + var resolver = new KeybindingResolver(items, [], false); @@ -278,7 +276,7 @@ suite('Keybinding Service', () => { }; function testExpression(expr:string, expected:boolean): void { let rules = IOSupport.readKeybindingContexts(expr); - assert.equal(CommonKeybindingResolver.contextMatchesRules(context, rules), expected, expr); + assert.equal(KeybindingResolver.contextMatchesRules(context, rules), expected, expr); } function testBatch(expr:string, value:any): void { testExpression(expr, !!value); diff --git a/src/vs/platform/keybinding/test/common/keybindingsRegistry.test.ts b/src/vs/platform/keybinding/test/common/keybindingsRegistry.test.ts index f519b16d99d..a092384f506 100644 --- a/src/vs/platform/keybinding/test/common/keybindingsRegistry.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingsRegistry.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import assert = require('assert'); +import * as assert from 'assert'; import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; suite('Keybinding Registry', () => { diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 4a4da378adf..4d79e73017e 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -15,7 +15,7 @@ import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {PluginsRegistry, IMessageCollector} from 'vs/platform/plugins/common/pluginsRegistry'; import {IPluginService} from 'vs/platform/plugins/common/plugins'; -import {IOSupport} from 'vs/platform/keybinding/common/commonKeybindingResolver'; +import {IOSupport} from 'vs/platform/keybinding/common/keybindingResolver'; import {KeybindingService} from 'vs/platform/keybinding/browser/keybindingServiceImpl'; import {IKeybindingItem, IUserFriendlyKeybinding} from 'vs/platform/keybinding/common/keybindingService'; import {ICommandRule, KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; diff --git a/src/vs/workbench/services/keybinding/electron-browser/nativeKeymap.ts b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymap.ts index 208dbddd709..cf35f7ea885 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/nativeKeymap.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymap.ts @@ -179,8 +179,8 @@ const NATIVE_KEY_CODE_TO_KEY_CODE: {[nativeKeyCode:string]:KeyCode;} = { VKEY_OEM_5: KeyCode.US_BACKSLASH, VKEY_OEM_6: KeyCode.US_CLOSE_SQUARE_BRACKET, VKEY_OEM_7: KeyCode.US_QUOTE, - VKEY_OEM_8: KeyCode.Unknown, // MISSING - VKEY_OEM_102: KeyCode.Unknown, // MISSING + VKEY_OEM_8: KeyCode.OEM_8, + VKEY_OEM_102: KeyCode.OEM_102, VKEY_PROCESSKEY: KeyCode.Unknown, // MISSING VKEY_PACKET: KeyCode.Unknown, // MISSING VKEY_DBE_SBCSCHAR: KeyCode.Unknown, // MISSING @@ -348,6 +348,8 @@ export function getNativeLabelProvider(): IKeyBindingLabelProvider { VKEY_OEM_5: true, VKEY_OEM_6: true, VKEY_OEM_7: true, + VKEY_OEM_8: true, + VKEY_OEM_102: true, }; let remaps:string[] = [];