提交 2f26afe5 编写于 作者: J Johannes Rieken

Introduce ICommandMetadata with the option for TypeConstraints

上级 ec720bfa
...@@ -118,6 +118,33 @@ export function areFunctions(...objects: any[]): boolean { ...@@ -118,6 +118,33 @@ export function areFunctions(...objects: any[]): boolean {
return objects && objects.length > 0 && objects.every((object) => isFunction(object)); return objects && objects.length > 0 && objects.every((object) => isFunction(object));
} }
export type TypeConstraint = string | Function;
export function validateConstraints(args: any[], constraints: TypeConstraint[]): void {
const len = Math.min(args.length, constraints.length);
for (let i = 0; i < len; i++) {
const arg = args[i];
const constraint = constraints[i];
if (typeof constraint === 'string') {
if (typeof arg !== constraint) {
throw new Error(`argument #${i} does not match constraint: typeof ${constraint}`);
}
} else if (typeof constraint === 'function') {
if (arg instanceof constraint) {
continue;
}
if (arg && arg.constructor === constraint) {
continue;
}
if (constraint.length === 1 && constraint.call(undefined, arg) === true) {
continue;
}
throw new Error(`argument #${i} does not match one of these constraints: arg instanceof constraint, arg.constructor === constraint, nor constraint(arg) === true`);
}
}
}
/** /**
* Creates a new object of the provided class and will call the constructor with * Creates a new object of the provided class and will call the constructor with
* any additional argument supplied. * any additional argument supplied.
......
...@@ -171,6 +171,27 @@ suite('Types', () => { ...@@ -171,6 +171,27 @@ suite('Types', () => {
assert(types.isUndefinedOrNull(null)); assert(types.isUndefinedOrNull(null));
}); });
test('validateConstraints', () => {
types.validateConstraints([1, 'test', true], [Number, String, Boolean])
types.validateConstraints([1, 'test', true], ['number', 'string', 'boolean'])
types.validateConstraints([console.log], [Function])
types.validateConstraints([undefined], [types.isUndefined])
types.validateConstraints([1], [types.isNumber])
function foo() {}
types.validateConstraints([new foo()], [foo]);
function isFoo(f) {}
assert.throws(() => types.validateConstraints([new foo()], [isFoo]));
function isFoo2(f) { return true}
types.validateConstraints([new foo()], [isFoo2]);
assert.throws(() => types.validateConstraints([1, true], [types.isNumber, types.isString]));
assert.throws(() => types.validateConstraints(['2'], [types.isNumber]));
assert.throws(() => types.validateConstraints([1, 'test', true], [Number, String, Number]));
});
test('create', () => { test('create', () => {
var zeroConstructor = function() { /**/ }; var zeroConstructor = function() { /**/ };
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
'use strict'; 'use strict';
import {TPromise} from 'vs/base/common/winjs.base'; 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, IInstantiationService, ServiceIdentifier, ServicesAccessor} from 'vs/platform/instantiation/common/instantiation';
import {Keybinding} from 'vs/base/common/keyCodes'; import {Keybinding} from 'vs/base/common/keyCodes';
...@@ -53,6 +54,15 @@ export interface IKeybindingItem { ...@@ -53,6 +54,15 @@ export interface IKeybindingItem {
export interface ICommandHandler { export interface ICommandHandler {
(accessor: ServicesAccessor, args: any): void; (accessor: ServicesAccessor, args: any): void;
description?: string | ICommandHandlerDescription;
}
export interface ICommandHandlerDescription {
description: string;
signature: {
args: { name: string; description?: string; constraint?: TypeConstraint;}[];
returns?: string;
}
} }
export interface ICommandsMap { export interface ICommandsMap {
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
'use strict'; 'use strict';
import {Registry} from 'vs/platform/platform'; import {Registry} from 'vs/platform/platform';
import {ICommandHandler, ICommandsMap, IKeybindingItem, IKeybindings, IKeybindingContextRule} from 'vs/platform/keybinding/common/keybindingService'; import {TypeConstraint, validateConstraints} from 'vs/base/common/types';
import {ICommandHandler, ICommandHandlerDescription, ICommandsMap, IKeybindingItem, IKeybindings, IKeybindingContextRule} from 'vs/platform/keybinding/common/keybindingService';
import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils';
import {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes'; import {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes';
import Platform = require('vs/base/common/platform'); import Platform = require('vs/base/common/platform');
...@@ -18,6 +19,7 @@ export interface ICommandRule extends IKeybindings { ...@@ -18,6 +19,7 @@ export interface ICommandRule extends IKeybindings {
export interface ICommandDescriptor extends ICommandRule { export interface ICommandDescriptor extends ICommandRule {
handler: ICommandHandler; handler: ICommandHandler;
description?: string | ICommandHandlerDescription;
} }
export interface IKeybindingsRegistry { export interface IKeybindingsRegistry {
...@@ -88,7 +90,28 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { ...@@ -88,7 +90,28 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
// console.warn('Duplicate handler for command: ' + desc.id); // console.warn('Duplicate handler for command: ' + desc.id);
// } // }
// this._commands[desc.id] = desc.handler; // this._commands[desc.id] = desc.handler;
this._commands[desc.id] = desc.handler;
let {handler} = desc;
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.signature.args) {
constraints.push(arg.constraint);
}
handler = function(accesor, args) {
validateConstraints(args, constraints);
return desc.handler(accesor, args);
};
}
// make sure description is there
handler.description = description;
// register handler
this._commands[desc.id] = handler;
} }
public getCommands(): ICommandsMap { public getCommands(): ICommandsMap {
......
/*---------------------------------------------------------------------------------------------
* 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 assert = require('assert');
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
suite('Keybinding Registry', () => {
test('command with description', function() {
KeybindingsRegistry.registerCommandDesc({
id: 'test',
context: undefined,
primary: undefined,
weight: 0,
handler: function(accessor, args) {
assert.ok(typeof args === 'string')
}
});
KeybindingsRegistry.registerCommandDesc({
id: 'test2',
description: 'test',
context: undefined,
primary: undefined,
weight: 0,
handler: function(accessor, args) {
assert.ok(typeof args === 'string')
}
});
KeybindingsRegistry.registerCommandDesc({
id: 'test3',
context: undefined,
primary: undefined,
weight: 0,
description: {
description: 'a command',
signature: {
args: [{ name: 'value', constraint: Number }],
}
},
handler: function(accessor, args) {
return true;
}
});
KeybindingsRegistry.getCommands()['test'].apply(undefined, [undefined, 'string']);
KeybindingsRegistry.getCommands()['test2'].apply(undefined, [undefined, 'string']);
assert.throws(() => KeybindingsRegistry.getCommands()['test3'].apply(undefined, [undefined, 'string']));
assert.equal(KeybindingsRegistry.getCommands()['test3'].apply(undefined, [undefined, 1]), true);
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册