Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

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

View File

@@ -9,6 +9,8 @@ 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';
import { LinkedList } from 'vs/base/common/linkedList';
export const ICommandService = createDecorator<ICommandService>('commandService');
@@ -19,8 +21,7 @@ export interface ICommandEvent {
export interface ICommandService {
_serviceBrand: any;
onWillExecuteCommand: Event<ICommandEvent>;
executeCommand<T>(commandId: string, ...args: any[]): TPromise<T>;
executeCommand(commandId: string, ...args: any[]): TPromise<any>;
executeCommand<T = any>(commandId: string, ...args: any[]): TPromise<T>;
}
export interface ICommandsMap {
@@ -32,7 +33,9 @@ export interface ICommandHandler {
}
export interface ICommand {
id: string;
handler: ICommandHandler;
precondition?: ContextKeyExpr;
description?: ICommandHandlerDescription;
}
@@ -44,78 +47,56 @@ export interface ICommandHandlerDescription {
export interface ICommandRegistry {
registerCommand(id: string, command: ICommandHandler): IDisposable;
registerCommand(id: string, command: ICommand): IDisposable;
registerCommand(command: ICommand): IDisposable;
getCommand(id: string): ICommand;
getCommands(): ICommandsMap;
}
function isCommand(thing: any): thing is ICommand {
return typeof thing === 'object'
&& typeof (<ICommand>thing).handler === 'function'
&& (!(<ICommand>thing).description || typeof (<ICommand>thing).description === 'object');
}
export const CommandsRegistry: ICommandRegistry = new class implements ICommandRegistry {
private _commands = new Map<string, ICommand | ICommand[]>();
private _commands = new Map<string, LinkedList<ICommand>>();
registerCommand(id: string, commandOrDesc: ICommandHandler | ICommand): IDisposable {
registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): IDisposable {
if (!commandOrDesc) {
if (!idOrCommand) {
throw new Error(`invalid command`);
}
let command: ICommand;
if (!isCommand(commandOrDesc)) {
// simple handler
command = { handler: commandOrDesc };
} else {
const { handler, description } = commandOrDesc;
if (description) {
// add argument validation if rich command metadata is provided
const constraints: TypeConstraint[] = [];
for (let arg of description.args) {
constraints.push(arg.constraint);
}
command = {
description,
handler(accessor, ...args: any[]) {
validateConstraints(args, constraints);
return handler(accessor, ...args);
}
};
} else {
// add as simple handler
command = { handler };
if (typeof idOrCommand === 'string') {
if (!handler) {
throw new Error(`invalid command`);
}
return this.registerCommand({ id: idOrCommand, handler });
}
// add argument validation if rich command metadata is provided
if (idOrCommand.description) {
const constraints: TypeConstraint[] = [];
for (let arg of idOrCommand.description.args) {
constraints.push(arg.constraint);
}
const actualHandler = idOrCommand.handler;
idOrCommand.handler = function (accessor, ...args: any[]) {
validateConstraints(args, constraints);
return actualHandler(accessor, ...args);
};
}
// find a place to store the command
const commandOrArray = this._commands.get(id);
if (commandOrArray === void 0) {
this._commands.set(id, command);
} else if (Array.isArray(commandOrArray)) {
commandOrArray.unshift(command);
} else {
this._commands.set(id, [command, commandOrArray]);
const { id } = idOrCommand;
let commands = this._commands.get(id);
if (!commands) {
commands = new LinkedList<ICommand>();
this._commands.set(id, commands);
}
let removeFn = commands.unshift(idOrCommand);
return {
dispose: () => {
const commandOrArray = this._commands.get(id);
if (Array.isArray(commandOrArray)) {
// remove from array, remove array
// if last element removed
const idx = commandOrArray.indexOf(command);
if (idx >= 0) {
commandOrArray.splice(idx, 1);
if (commandOrArray.length === 0) {
this._commands.delete(id);
}
}
} else if (isCommand(commandOrArray)) {
// remove from map
removeFn();
if (this._commands.get(id).isEmpty()) {
this._commands.delete(id);
}
}
@@ -123,12 +104,11 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR
}
getCommand(id: string): ICommand {
const commandOrArray = this._commands.get(id);
if (Array.isArray(commandOrArray)) {
return commandOrArray[0];
} else {
return commandOrArray;
const list = this._commands.get(id);
if (!list || list.isEmpty()) {
return undefined;
}
return list.iterator().next().value;
}
getCommands(): ICommandsMap {

View File

@@ -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;
@@ -35,6 +38,10 @@ class SimpleExtensionService implements IExtensionService {
}
restartExtensionHost(): void {
}
startExtensionHost(): void {
}
stopExtensionHost(): void {
}
}
suite('CommandService', function () {
@@ -58,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');
@@ -76,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');
@@ -93,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();
@@ -101,4 +108,30 @@ suite('CommandService', function () {
});
});
});
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({
id: '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();
});
});
});

View File

@@ -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 () {
@@ -60,7 +60,8 @@ suite('Command Tests', function () {
assert.ok(typeof args === 'string');
});
CommandsRegistry.registerCommand('test3', {
CommandsRegistry.registerCommand({
id: 'test3',
handler: function (accessor, args) {
return true;
},
@@ -76,4 +77,21 @@ 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({
id: 'bar',
handler: () => { },
precondition
});
assert.ok(CommandsRegistry.getCommand('bar').precondition === precondition);
assert.equal(CommandsRegistry.getCommand('foo').precondition, undefined);
r1.dispose();
r2.dispose();
});
});