提交 78e64e72 编写于 作者: J Johannes Rieken

allow to provide a precondition when registering commands

上级 88a2fba0
......@@ -10,6 +10,7 @@ import { ICommandService, ICommand, ICommandEvent, CommandsRegistry } from 'vs/p
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import Event, { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
export class CommandService extends Disposable implements ICommandService {
......@@ -22,7 +23,8 @@ export class CommandService extends Disposable implements ICommandService {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IExtensionService private _extensionService: IExtensionService
@IExtensionService private _extensionService: IExtensionService,
@IContextKeyService private _contextKeyService: IContextKeyService
) {
super();
this._extensionService.onReady().then(value => this._extensionHostIsReady = value);
......@@ -46,6 +48,11 @@ export class CommandService extends Disposable implements ICommandService {
return TPromise.wrapError(new Error(`command '${id}' not found`));
}
if (command.precondition && !this._contextKeyService.contextMatchesRules(command.precondition)) {
// not enabled
return TPromise.wrapError(new Error('NOT_ENABLED'));
}
try {
this._onWillExecuteCommand.fire({ commandId: id });
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler].concat(args));
......@@ -55,7 +62,7 @@ export class CommandService extends Disposable implements ICommandService {
}
}
protected _getCommand(id: string): ICommand {
private _getCommand(id: string): ICommand {
return CommandsRegistry.getCommand(id);
}
}
......@@ -9,6 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { TypeConstraint, validateConstraints } from 'vs/base/common/types';
import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import Event from 'vs/base/common/event';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
export const ICommandService = createDecorator<ICommandService>('commandService');
......@@ -33,6 +34,7 @@ export interface ICommandHandler {
export interface ICommand {
handler: ICommandHandler;
precondition?: ContextKeyExpr;
description?: ICommandHandlerDescription;
}
......@@ -52,6 +54,7 @@ export interface ICommandRegistry {
function isCommand(thing: any): thing is ICommand {
return typeof thing === 'object'
&& typeof (<ICommand>thing).handler === 'function'
&& (!(<ICommand>thing).precondition || typeof (<ICommand>thing).precondition === 'object')
&& (!(<ICommand>thing).description || typeof (<ICommand>thing).description === 'object');
}
......@@ -71,24 +74,20 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR
command = { handler: commandOrDesc };
} else {
const { handler, description } = commandOrDesc;
if (description) {
if (commandOrDesc.description) {
// add argument validation if rich command metadata is provided
const constraints: TypeConstraint[] = [];
for (let arg of description.args) {
for (let arg of commandOrDesc.description.args) {
constraints.push(arg.constraint);
}
command = {
description,
handler(accessor, ...args: any[]) {
validateConstraints(args, constraints);
return handler(accessor, ...args);
}
const actualHandler = commandOrDesc.handler;
commandOrDesc.handler = function (accessor, ...args: any[]) {
validateConstraints(args, constraints);
return actualHandler(accessor, ...args);
};
} else {
// add as simple handler
command = { handler };
}
command = commandOrDesc;
}
// find a place to store the command
......
......@@ -12,6 +12,9 @@ import { CommandService } from 'vs/platform/commands/common/commandService';
import { IExtensionService, ExtensionPointContribution, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { SimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
class SimpleExtensionService implements IExtensionService {
_serviceBrand: any;
......@@ -62,7 +65,7 @@ suite('CommandService', function () {
lastEvent = activationEvent;
return super.activateByEvent(activationEvent);
}
});
}, new ContextKeyService(new SimpleConfigurationService()));
return service.executeCommand('foo').then(() => {
assert.ok(lastEvent, 'onCommand:foo');
......@@ -80,7 +83,7 @@ suite('CommandService', function () {
activateByEvent(activationEvent: string): TPromise<void> {
return TPromise.wrapError<void>(new Error('bad_activate'));
}
});
}, new ContextKeyService(new SimpleConfigurationService()));
return service.executeCommand('foo').then(() => assert.ok(false), err => {
assert.equal(err.message, 'bad_activate');
......@@ -97,7 +100,7 @@ suite('CommandService', function () {
onReady() {
return new TPromise<boolean>(_resolve => { resolve = _resolve; });
}
});
}, new ContextKeyService(new SimpleConfigurationService()));
return service.executeCommand('bar').then(() => {
reg.dispose();
......@@ -105,4 +108,29 @@ suite('CommandService', function () {
});
});
});
\ No newline at end of file
test('honor command-precondition', function () {
let contextKeyService = new ContextKeyService(new SimpleConfigurationService());
let commandService = new CommandService(
new InstantiationService(),
new SimpleExtensionService(),
contextKeyService
);
let counter = 0;
let reg = CommandsRegistry.registerCommand('bar', {
handler: () => { counter += 1; },
precondition: ContextKeyExpr.has('foocontext')
});
return commandService.executeCommand('bar').then(() => {
assert.throws(() => { });
}, () => {
contextKeyService.setContext('foocontext', true);
return commandService.executeCommand('bar');
}).then(() => {
assert.equal(counter, 1);
reg.dispose();
});
});
});
......@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
suite('Command Tests', function () {
......@@ -76,4 +76,20 @@ suite('Command Tests', function () {
assert.equal(CommandsRegistry.getCommands()['test3'].handler.apply(undefined, [undefined, 1]), true);
});
test('CommandsRegistry with precondition', function () {
let r1 = CommandsRegistry.registerCommand('foo', () => { });
const precondition = new RawContextKey<boolean>('ddd', false);
let r2 = CommandsRegistry.registerCommand('bar', {
handler: () => { },
precondition
});
assert.ok(CommandsRegistry.getCommand('bar').precondition === precondition);
assert.equal(CommandsRegistry.getCommand('foo').precondition, undefined);
r1.dispose();
r2.dispose();
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册