mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
61
src/vs/platform/commands/common/commandService.ts
Normal file
61
src/vs/platform/commands/common/commandService.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICommandService, ICommand, ICommandEvent, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class CommandService extends Disposable implements ICommandService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _extensionHostIsReady: boolean = false;
|
||||
|
||||
private _onWillExecuteCommand: Emitter<ICommandEvent> = this._register(new Emitter<ICommandEvent>());
|
||||
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IExtensionService private _extensionService: IExtensionService
|
||||
) {
|
||||
super();
|
||||
this._extensionService.onReady().then(value => this._extensionHostIsReady = value);
|
||||
}
|
||||
|
||||
executeCommand<T>(id: string, ...args: any[]): TPromise<T> {
|
||||
// we always send an activation event, but
|
||||
// we don't wait for it when the extension
|
||||
// host didn't yet start
|
||||
|
||||
const activation = this._extensionService.activateByEvent(`onCommand:${id}`);
|
||||
|
||||
return this._extensionHostIsReady
|
||||
? activation.then(_ => this._tryExecuteCommand(id, args))
|
||||
: this._tryExecuteCommand(id, args);
|
||||
}
|
||||
|
||||
private _tryExecuteCommand(id: string, args: any[]): TPromise<any> {
|
||||
const command = this._getCommand(id);
|
||||
if (!command) {
|
||||
return TPromise.wrapError(new Error(`command '${id}' not found`));
|
||||
}
|
||||
|
||||
try {
|
||||
this._onWillExecuteCommand.fire({ commandId: id });
|
||||
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler].concat(args));
|
||||
return TPromise.as(result);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
}
|
||||
|
||||
protected _getCommand(id: string): ICommand {
|
||||
return CommandsRegistry.getCommand(id);
|
||||
}
|
||||
}
|
||||
149
src/vs/platform/commands/common/commands.ts
Normal file
149
src/vs/platform/commands/common/commands.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
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';
|
||||
|
||||
export const ICommandService = createDecorator<ICommandService>('commandService');
|
||||
|
||||
export interface ICommandEvent {
|
||||
commandId: string;
|
||||
}
|
||||
|
||||
export interface ICommandService {
|
||||
_serviceBrand: any;
|
||||
onWillExecuteCommand: Event<ICommandEvent>;
|
||||
executeCommand<T>(commandId: string, ...args: any[]): TPromise<T>;
|
||||
executeCommand(commandId: string, ...args: any[]): TPromise<any>;
|
||||
}
|
||||
|
||||
export interface ICommandsMap {
|
||||
[id: string]: ICommand;
|
||||
}
|
||||
|
||||
export interface ICommandHandler {
|
||||
(accessor: ServicesAccessor, ...args: any[]): void;
|
||||
}
|
||||
|
||||
export interface ICommand {
|
||||
handler: ICommandHandler;
|
||||
description?: ICommandHandlerDescription;
|
||||
}
|
||||
|
||||
export interface ICommandHandlerDescription {
|
||||
description: string;
|
||||
args: { name: string; description?: string; constraint?: TypeConstraint; }[];
|
||||
returns?: string;
|
||||
}
|
||||
|
||||
export interface ICommandRegistry {
|
||||
registerCommand(id: string, command: ICommandHandler): IDisposable;
|
||||
registerCommand(id: string, 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[]>();
|
||||
|
||||
registerCommand(id: string, commandOrDesc: ICommandHandler | ICommand): IDisposable {
|
||||
|
||||
if (!commandOrDesc) {
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
|
||||
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
|
||||
this._commands.delete(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getCommand(id: string): ICommand {
|
||||
const commandOrArray = this._commands.get(id);
|
||||
if (Array.isArray(commandOrArray)) {
|
||||
return commandOrArray[0];
|
||||
} else {
|
||||
return commandOrArray;
|
||||
}
|
||||
}
|
||||
|
||||
getCommands(): ICommandsMap {
|
||||
const result: ICommandsMap = Object.create(null);
|
||||
this._commands.forEach((value, key) => {
|
||||
result[key] = this.getCommand(key);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export const NullCommandService: ICommandService = {
|
||||
_serviceBrand: undefined,
|
||||
onWillExecuteCommand: () => ({ dispose: () => { } }),
|
||||
executeCommand() {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
};
|
||||
104
src/vs/platform/commands/test/commandService.test.ts
Normal file
104
src/vs/platform/commands/test/commandService.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
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';
|
||||
|
||||
class SimpleExtensionService implements IExtensionService {
|
||||
_serviceBrand: any;
|
||||
activateByEvent(activationEvent: string): TPromise<void> {
|
||||
return this.onReady().then(() => { });
|
||||
}
|
||||
onReady(): TPromise<boolean> {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): TPromise<ExtensionPointContribution<T>[]> {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
getExtensionsStatus() {
|
||||
return undefined;
|
||||
}
|
||||
getExtensionsActivationTimes() {
|
||||
return undefined;
|
||||
}
|
||||
getExtensions(): TPromise<IExtensionDescription[]> {
|
||||
return TPromise.wrap([]);
|
||||
}
|
||||
restartExtensionHost(): void {
|
||||
}
|
||||
}
|
||||
|
||||
suite('CommandService', function () {
|
||||
|
||||
let commandRegistration: IDisposable;
|
||||
|
||||
setup(function () {
|
||||
commandRegistration = CommandsRegistry.registerCommand('foo', function () { });
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
commandRegistration.dispose();
|
||||
});
|
||||
|
||||
test('activateOnCommand', function () {
|
||||
|
||||
let lastEvent: string;
|
||||
|
||||
let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService {
|
||||
activateByEvent(activationEvent: string): TPromise<void> {
|
||||
lastEvent = activationEvent;
|
||||
return super.activateByEvent(activationEvent);
|
||||
}
|
||||
});
|
||||
|
||||
return service.executeCommand('foo').then(() => {
|
||||
assert.ok(lastEvent, 'onCommand:foo');
|
||||
return service.executeCommand('unknownCommandId');
|
||||
}).then(() => {
|
||||
assert.ok(false);
|
||||
}, () => {
|
||||
assert.ok(lastEvent, 'onCommand:unknownCommandId');
|
||||
});
|
||||
});
|
||||
|
||||
test('fwd activation error', function () {
|
||||
|
||||
let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService {
|
||||
activateByEvent(activationEvent: string): TPromise<void> {
|
||||
return TPromise.wrapError<void>(new Error('bad_activate'));
|
||||
}
|
||||
});
|
||||
|
||||
return service.executeCommand('foo').then(() => assert.ok(false), err => {
|
||||
assert.equal(err.message, 'bad_activate');
|
||||
});
|
||||
});
|
||||
|
||||
test('!onReady, but executeCommand', function () {
|
||||
|
||||
let callCounter = 0;
|
||||
let reg = CommandsRegistry.registerCommand('bar', () => callCounter += 1);
|
||||
|
||||
let resolve: Function;
|
||||
let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService {
|
||||
onReady() {
|
||||
return new TPromise<boolean>(_resolve => { resolve = _resolve; });
|
||||
}
|
||||
});
|
||||
|
||||
return service.executeCommand('bar').then(() => {
|
||||
reg.dispose();
|
||||
assert.equal(callCounter, 1);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
79
src/vs/platform/commands/test/commands.test.ts
Normal file
79
src/vs/platform/commands/test/commands.test.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
|
||||
|
||||
suite('Command Tests', function () {
|
||||
|
||||
test('register command - no handler', function () {
|
||||
assert.throws(() => CommandsRegistry.registerCommand('foo', null));
|
||||
});
|
||||
|
||||
test('register/dispose', function () {
|
||||
const command = function () { };
|
||||
const reg = CommandsRegistry.registerCommand('foo', command);
|
||||
assert.ok(CommandsRegistry.getCommand('foo').handler === command);
|
||||
reg.dispose();
|
||||
assert.ok(CommandsRegistry.getCommand('foo') === undefined);
|
||||
});
|
||||
|
||||
test('register/register/dispose', function () {
|
||||
const command1 = function () { };
|
||||
const command2 = function () { };
|
||||
|
||||
// dispose overriding command
|
||||
let reg1 = CommandsRegistry.registerCommand('foo', command1);
|
||||
assert.ok(CommandsRegistry.getCommand('foo').handler === command1);
|
||||
|
||||
let reg2 = CommandsRegistry.registerCommand('foo', command2);
|
||||
assert.ok(CommandsRegistry.getCommand('foo').handler === command2);
|
||||
reg2.dispose();
|
||||
|
||||
assert.ok(CommandsRegistry.getCommand('foo').handler === command1);
|
||||
reg1.dispose();
|
||||
assert.ok(CommandsRegistry.getCommand('foo') === void 0);
|
||||
|
||||
// dispose override command first
|
||||
reg1 = CommandsRegistry.registerCommand('foo', command1);
|
||||
reg2 = CommandsRegistry.registerCommand('foo', command2);
|
||||
assert.ok(CommandsRegistry.getCommand('foo').handler === command2);
|
||||
|
||||
reg1.dispose();
|
||||
assert.ok(CommandsRegistry.getCommand('foo').handler === command2);
|
||||
|
||||
reg2.dispose();
|
||||
assert.ok(CommandsRegistry.getCommand('foo') === void 0);
|
||||
});
|
||||
|
||||
test('command with description', function () {
|
||||
|
||||
CommandsRegistry.registerCommand('test', function (accessor, args) {
|
||||
assert.ok(typeof args === 'string');
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('test2', function (accessor, args) {
|
||||
assert.ok(typeof args === 'string');
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('test3', {
|
||||
handler: function (accessor, args) {
|
||||
return true;
|
||||
},
|
||||
description: {
|
||||
description: 'a command',
|
||||
args: [{ name: 'value', constraint: Number }]
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.getCommands()['test'].handler.apply(undefined, [undefined, 'string']);
|
||||
CommandsRegistry.getCommands()['test2'].handler.apply(undefined, [undefined, 'string']);
|
||||
assert.throws(() => CommandsRegistry.getCommands()['test3'].handler.apply(undefined, [undefined, 'string']));
|
||||
assert.equal(CommandsRegistry.getCommands()['test3'].handler.apply(undefined, [undefined, 1]), true);
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user