Improve default keybinding generation; Formatting changes around keybindings code

This commit is contained in:
Alex Dima
2016-01-06 15:53:11 +01:00
parent 772c4ef93a
commit e28cd4d8a4
12 changed files with 94 additions and 120 deletions

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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<any> {
if (!args.context) {
var contextId = this._findContextAttr(<HTMLElement>document.activeElement);
var context = this.getContext(contextId);
var contextValue = context.getValue();
let contextId = this._findContextAttr(<HTMLElement>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());
}
}

View File

@@ -4,10 +4,8 @@
*--------------------------------------------------------------------------------------------*/
'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 * as Platform from 'vs/base/common/platform';
import {IKeybindingItem, KbExpr, IUserFriendlyKeybinding} from 'vs/platform/keybinding/common/keybindingService';
import {KeyMod, KeyCode, BinaryKeybindings, Keybinding} from 'vs/base/common/keyCodes';
export interface IResolveResult {
@@ -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,
@@ -360,7 +364,7 @@ export class IOSupport {
}
input = input.toLowerCase().trim();
var ctrlCmd = false,
let ctrlCmd = false,
shift = false,
alt = false,
winCtrl = false,
@@ -415,9 +419,9 @@ export class IOSupport {
});
}
var chord: number = 0;
let chord: number = 0;
var firstSpaceIdx = input.indexOf(' ');
let firstSpaceIdx = input.indexOf(' ');
if (firstSpaceIdx > 0) {
key = input.substring(0, firstSpaceIdx);
chord = IOSupport.readKeybinding(input.substring(firstSpaceIdx));

View File

@@ -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<T> {
reset(): void;
}
export var IKeybindingService = createDecorator<IKeybindingService>('keybindingService');
export let IKeybindingService = createDecorator<IKeybindingService>('keybindingService');
export interface IKeybindingScopeLocation {
setAttribute(attr:string, value:string): void;

View File

@@ -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 = <ICommandHandlerDescription>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);

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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', () => {

View File

@@ -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';