提交 974ed10c 编写于 作者: J Johannes Rieken

Merge pull request #1165 from Microsoft/joh/commandsAsApi

Joh/commands as api
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import {commands} from 'vscode';
import {join} from 'path';
suite("commands namespace tests", () => {
test('getCommands', function(done) {
let p1 = commands.getCommands().then(commands => {
let hasOneWithUnderscore = false;
for (let command of commands) {
if (command[0] === '_') {
hasOneWithUnderscore = true;
break;
}
}
assert.ok(hasOneWithUnderscore);
}, done);
let p2 = commands.getCommands(true).then(commands => {
let hasOneWithUnderscore = false;
for (let command of commands) {
if (command[0] === '_') {
hasOneWithUnderscore = true;
break;
}
}
assert.ok(!hasOneWithUnderscore);
}, done);
Promise.all([p1, p2]).then(() => {
done();
}, done);
});
});
\ No newline at end of file
......@@ -118,6 +118,35 @@ export function areFunctions(...objects: any[]): boolean {
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++) {
validateConstraint(args[i], constraints[i])
}
}
export function validateConstraint(arg: any, constraint: TypeConstraint): void {
if (typeof constraint === 'string') {
if (typeof arg !== constraint) {
throw new Error(`argument does not match constraint: typeof ${constraint}`);
}
} else if (typeof constraint === 'function') {
if (arg instanceof constraint) {
return;
}
if (arg && arg.constructor === constraint) {
return;
}
if (constraint.length === 1 && constraint.call(undefined, arg) === true) {
return;
}
throw new Error(`argument 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
* any additional argument supplied.
......
......@@ -171,6 +171,27 @@ suite('Types', () => {
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', () => {
var zeroConstructor = function() { /**/ };
......
......@@ -357,7 +357,15 @@ KeybindingsRegistry.registerCommandDesc({
handler: showReferencesCommand,
weight: CommonEditorRegistry.commandWeight(50),
context: [],
primary: undefined
primary: undefined,
description: {
description: 'Show references at a position in a file',
args: [
{ name: 'uri', description: 'The text document in which to show references', constraint: URI },
{ name: 'position', description: 'The position at which to show', constraint: Position.isIPosition },
{ name: 'locations', description: 'An array of locations.', constraint: Array },
]
}
});
CommonEditorRegistry.registerEditorCommand('closeReferenceSearch', CommonEditorRegistry.commandWeight(50), { primary: KeyCode.Escape }, false, CONTEXT_REFERENCE_SEARCH_VISIBLE, (accessor, editor, args) => {
var outerEditor = peekView.getOuterEditor(accessor, args);
......
......@@ -215,7 +215,7 @@ export class KeybindingService extends AbstractKeybindingService implements IKey
private _getAllCommandsAsComment(): string {
var boundCommands = this._resolver.getDefaultBoundCommands();
var unboundCommands = Object.keys(KeybindingsRegistry.getCommands()).filter((commandId) => !boundCommands[commandId]);
var unboundCommands = Object.keys(KeybindingsRegistry.getCommands()).filter(commandId => commandId[0] !== '_' && !boundCommands[commandId]);
var pretty = unboundCommands.join('\n// - ');
return '// ' + nls.localize('unboundCommands', "Here are other available commands: ") + '\n// - ' + pretty;
......
......@@ -5,6 +5,7 @@
'use strict';
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 {Keybinding} from 'vs/base/common/keyCodes';
......@@ -53,6 +54,13 @@ export interface IKeybindingItem {
export interface ICommandHandler {
(accessor: ServicesAccessor, args: any): void;
description?: string | ICommandHandlerDescription;
}
export interface ICommandHandlerDescription {
description: string;
args: { name: string; description?: string; constraint?: TypeConstraint; }[];
returns?: string;
}
export interface ICommandsMap {
......
......@@ -5,7 +5,8 @@
'use strict';
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 {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes';
import Platform = require('vs/base/common/platform');
......@@ -18,6 +19,7 @@ export interface ICommandRule extends IKeybindings {
export interface ICommandDescriptor extends ICommandRule {
handler: ICommandHandler;
description?: string | ICommandHandlerDescription;
}
export interface IKeybindingsRegistry {
......@@ -88,7 +90,28 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
// console.warn('Duplicate handler for command: ' + desc.id);
// }
// 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.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 {
......
/*---------------------------------------------------------------------------------------------
* 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',
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);
});
});
......@@ -2648,11 +2648,13 @@ declare namespace vscode {
export function executeCommand<T>(command: string, ...rest: any[]): Thenable<T>;
/**
* Retrieve the list of all available commands.
* Retrieve the list of all available commands. Commands starting an underscore are
* treated as internal commands.
*
* @param filterInternal Set `true` to not see internal commands (starting with an underscore)
* @return Thenable that resolves to a list of command ids.
*/
export function getCommands(): Thenable<string[]>;
export function getCommands(filterInternal?: boolean): Thenable<string[]>;
}
/**
......
......@@ -22,7 +22,7 @@ import {PluginHostTelemetryService} from 'vs/workbench/api/common/pluginHostTele
import {PluginHostEditors} from 'vs/workbench/api/common/pluginHostEditors';
import {ExtHostLanguages} from 'vs/workbench/api/common/extHostLanguages';
import {ExtHostLanguageFeatures} from 'vs/workbench/api/common/extHostLanguageFeatures';
import {ExtHostLanguageFeatureCommands} from 'vs/workbench/api/common/extHostLanguageFeatureCommands';
import {ExtHostApiCommands} from 'vs/workbench/api/common/extHostApiCommands';
import * as extHostTypes from 'vs/workbench/api/common/pluginHostTypes';
import 'vs/workbench/api/common/pluginHostTypes.marshalling';
import {wrapAsWinJSPromise} from 'vs/base/common/async';
......@@ -105,7 +105,6 @@ export class PluginHostAPIImplementation {
this._proxy = threadService.getRemotable(MainProcessVSCodeAPIHelper);
this.version = contextService.getConfiguration().env.version;
this.commands = this._threadService.getRemotable(PluginHostCommands);
this.Uri = URI;
this.Location = extHostTypes.Location;
this.Diagnostic = <any> extHostTypes.Diagnostic;
......@@ -138,6 +137,22 @@ export class PluginHostAPIImplementation {
this._proxy.onUnexpectedPluginHostError(errors.transformErrorForSerialization(err));
});
const pluginHostCommands = this._threadService.getRemotable(PluginHostCommands);
this.commands = {
registerCommand<T>(id: string, command: <T>(...args: any[]) => T | Thenable<T>, thisArgs?: any): vscode.Disposable {
return pluginHostCommands.registerCommand(id, command, thisArgs);
},
registerTextEditorCommand(commandId: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) => void, thisArg?: any): vscode.Disposable {
return pluginHostCommands.registerTextEditorCommand(commandId, callback, thisArg);
},
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
return pluginHostCommands.executeCommand(id, args);
},
getCommands(filterInternal: boolean = false):Thenable<string[]> {
return pluginHostCommands.getCommands(filterInternal);
}
};
const pluginHostEditors = this._threadService.getRemotable(PluginHostEditors);
const pluginHostMessageService = new PluginHostMessageService(this._threadService, this.commands);
const pluginHostQuickOpen = new PluginHostQuickOpen(this._threadService);
......@@ -252,7 +267,7 @@ export class PluginHostAPIImplementation {
const languages = new ExtHostLanguages(this._threadService);
const pluginHostDiagnostics = new PluginHostDiagnostics(this._threadService);
const languageFeatures = threadService.getRemotable(ExtHostLanguageFeatures);
const languageFeatureCommand = new ExtHostLanguageFeatureCommands(threadService.getRemotable(PluginHostCommands));
const languageFeatureCommand = new ExtHostApiCommands(threadService.getRemotable(PluginHostCommands));
this.languages = {
createDiagnosticCollection(name?: string): vscode.DiagnosticCollection {
......
......@@ -20,6 +20,7 @@ import * as typeConverters from 'vs/workbench/api/common/pluginHostTypeConverter
import * as types from 'vs/workbench/api/common/pluginHostTypes';
import {IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import {ICommandHandlerDescription} from 'vs/platform/keybinding/common/keybindingService';
import {CancellationTokenSource} from 'vs/base/common/cancellation';
import {PluginHostModelService} from 'vs/workbench/api/common/pluginHostDocuments';
import {IMarkerService, IMarker} from 'vs/platform/markers/common/markers';
......@@ -36,7 +37,7 @@ import {RenameRegistry} from 'vs/editor/contrib/rename/common/rename';
import {FormatRegistry, FormatOnTypeRegistry} from 'vs/editor/contrib/format/common/format';
import {ICodeLensData} from 'vs/editor/contrib/codelens/common/codelens';
export class ExtHostLanguageFeatureCommands {
export class ExtHostApiCommands {
private _commands: PluginHostCommands;
private _disposables: IDisposable[] = [];
......@@ -44,26 +45,125 @@ export class ExtHostLanguageFeatureCommands {
constructor(commands: PluginHostCommands) {
this._commands = commands;
this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider);
this._register('vscode.executeDefinitionProvider', this._executeDefinitionProvider);
this._register('vscode.executeHoverProvider', this._executeHoverProvider);
this._register('vscode.executeDocumentHighlights', this._executeDocumentHighlights);
this._register('vscode.executeReferenceProvider', this._executeReferenceProvider);
this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider);
this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider);
this._register('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider);
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider);
this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider);
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider);
this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider);
this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider);
this._register('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider);
this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider, {
description: 'Execute all workspace symbol provider.',
args: [{ name: 'query', constraint: String }],
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
});
this._register('vscode.executeDefinitionProvider', this._executeDefinitionProvider, {
description: 'Execute all definition provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeHoverProvider', this._executeHoverProvider, {
description: 'Execute all definition provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Hover-instances.'
});
this._register('vscode.executeDocumentHighlights', this._executeDocumentHighlights, {
description: 'Execute document highlight provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
],
returns: 'A promise that resolves to an array of DocumentHighlight-instances.'
});
this._register('vscode.executeReferenceProvider', this._executeReferenceProvider, {
description: 'Execute reference provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider, {
description: 'Execute rename provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'newName', description: 'The new symbol name', constraint: String }
],
returns: 'A promise that resolves to a WorkspaceEdit.'
});
this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, {
description: 'Execute signature help provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
],
returns: 'A promise that resolves to SignatureHelp.'
});
this._register('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider, {
description: 'Execute document symbol provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
});
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
description: 'Execute completion item provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
],
returns: 'A promise that resolves to an array of CompletionItem-instances.'
});
this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, {
description: 'Execute code action provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'range', description: 'Range in a text document', constraint: types.Range }
],
returns: 'A promise that resolves to an array of CompletionItem-instances.'
});
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
description: 'Execute completion item provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of Commands.'
});
this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, {
description: 'Execute document format provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'options', description: 'Formatting options' }
],
returns: 'A promise that resolves to an array of TextEdits.'
});
this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider, {
description: 'Execute range format provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'range', description: 'Range in a text document', constraint: types.Range },
{ name: 'options', description: 'Formatting options' }
],
returns: 'A promise that resolves to an array of TextEdits.'
});
this._register('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider, {
description: 'Execute document format provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'ch', description: 'Character that got typed', constraint: String },
{ name: 'options', description: 'Formatting options' }
],
returns: 'A promise that resolves to an array of TextEdits.'
});
}
// --- command impl
private _register(id: string, handler: (...args: any[]) => any): void {
this._disposables.push(this._commands.registerCommand(id, handler, this));
private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
let disposable = this._commands.registerCommand(id, handler, this, description);
this._disposables.push(disposable);
}
/**
......@@ -208,7 +308,7 @@ export class ExtHostLanguageFeatureCommands {
});
}
private _executeCodeLensProvider(resource: URI): Thenable<vscode.CodeLens[]>{
private _executeCodeLensProvider(resource: URI): Thenable<vscode.CodeLens[]> {
const args = { resource };
return this._commands.executeCommand<ICodeLensData[]>('_executeCodeLensProvider', args).then(value => {
if (Array.isArray(value)) {
......@@ -245,7 +345,7 @@ export class ExtHostLanguageFeatureCommands {
});
}
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch:string, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
const args = {
resource,
position: typeConverters.fromPosition(position),
......
......@@ -5,11 +5,13 @@
'use strict';
import {Remotable, IThreadService} from 'vs/platform/thread/common/thread';
import {TypeConstraint, validateConstraint} from 'vs/base/common/types';
import {IEventService} from 'vs/platform/event/common/event';
import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService';
import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils';
import {IKeybindingService, ICommandHandlerDescription} from 'vs/platform/keybinding/common/keybindingService';
import {TPromise} from 'vs/base/common/winjs.base';
import {PluginHostEditors} from 'vs/workbench/api/common/pluginHostEditors';
import {IMessageService, Severity} from 'vs/platform/message/common/message';
......@@ -17,10 +19,16 @@ import {canSerialize} from 'vs/base/common/marshalling';
import {toErrorMessage} from 'vs/base/common/errors';
import * as vscode from 'vscode';
interface CommandHandler {
callback: Function;
thisArg: any;
description: ICommandHandlerDescription;
}
@Remotable.PluginHostContext('PluginHostCommands')
export class PluginHostCommands {
private _commands: { [n: string]: Function } = Object.create(null);
private _commands: { [n: string]: CommandHandler } = Object.create(null);
private _proxy: MainThreadCommands;
private _pluginHostEditors: PluginHostEditors;
......@@ -29,7 +37,7 @@ export class PluginHostCommands {
this._proxy = threadService.getRemotable(MainThreadCommands);
}
registerCommand(id: string, command: <T>(...args: any[]) => T | Thenable<T>, thisArgs?: any): vscode.Disposable {
registerCommand(id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, description?: ICommandHandlerDescription): vscode.Disposable {
if (!id.trim().length) {
throw new Error('invalid id');
......@@ -39,8 +47,8 @@ export class PluginHostCommands {
throw new Error('command with id already exists');
}
this._commands[id] = thisArgs ? command.bind(thisArgs) : command;
this._proxy._registerCommand(id);
this._commands[id] = { callback, thisArg, description };
this._proxy.$registerCommand(id);
return {
dispose: () => {
......@@ -49,12 +57,12 @@ export class PluginHostCommands {
}
}
registerTextEditorCommand(commandId: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) => void, thisArg?: any): vscode.Disposable {
registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) => void, thisArg?: any): vscode.Disposable {
let actualCallback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) => void = thisArg ? callback.bind(thisArg) : callback;
return this.registerCommand(commandId, () => {
return this.registerCommand(id, () => {
let activeTextEditor = this._pluginHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + commandId + ' because there is no active text editor.');
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
return;
}
......@@ -62,10 +70,10 @@ export class PluginHostCommands {
actualCallback(activeTextEditor, edit);
}).then((result) => {
if (!result) {
console.warn('Edits from command ' + commandId + ' were not applied.')
console.warn('Edits from command ' + id + ' were not applied.')
}
}, (err) => {
console.warn('An error occured while running command ' + commandId, err);
console.warn('An error occured while running command ' + id, err);
});
})
}
......@@ -75,7 +83,7 @@ export class PluginHostCommands {
if (this._commands[id]) {
// we stay inside the extension host and support
// to pass any kind of parameters around
return this._executeContributedCommand(id, ...args);
return this.$executeContributedCommand(id, ...args);
} else {
// // check that we can get all parameters over to
......@@ -86,32 +94,54 @@ export class PluginHostCommands {
// }
// }
return this._proxy._executeCommand(id, args);
return this._proxy.$executeCommand(id, args);
}
}
_executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
let command = this._commands[id];
if (!command) {
return Promise.reject<T>(id);
}
try {
let result = command.apply(undefined, args);
let {callback, thisArg, description} = command;
if (description) {
for (let i = 0; i < description.args.length; i++) {
validateConstraint(args[i], description.args[i].constraint);
}
}
let result = callback.apply(thisArg, args);
return Promise.resolve(result);
} catch (err) {
try {
console.log(toErrorMessage(err));
console.log(err);
} catch (err) {
//
}
// try {
// console.log(toErrorMessage(err));
// console.log(err);
// } catch (err) {
// //
// }
return Promise.reject<T>(`Running the contributed command:'${id}' failed.`);
}
}
getCommands(): Thenable<string[]> {
return this._proxy._getCommands();
getCommands(filterUnderscoreCommands: boolean = false): Thenable<string[]> {
return this._proxy.$getCommands().then(result => {
if (filterUnderscoreCommands) {
result = result.filter(command => command[0] !== '_');
}
return result;
});
}
$getContributedCommandHandlerDescriptions(): TPromise<{ [id: string]: string | ICommandHandlerDescription }> {
const result: { [id: string]: string | ICommandHandlerDescription } = Object.create(null);
for (let id in this._commands) {
let {description} = this._commands[id];
if (description) {
result[id] = description;
}
}
return TPromise.as(result);
}
}
......@@ -128,12 +158,12 @@ export class MainThreadCommands {
this._proxy = this._threadService.getRemotable(PluginHostCommands);
}
_registerCommand(id: string): TPromise<any> {
$registerCommand(id: string): TPromise<any> {
KeybindingsRegistry.registerCommandDesc({
id,
handler: (serviceAccessor, ...args: any[]) => {
return this._proxy._executeContributedCommand(id, ...args); //TODO@Joh - we cannot serialize the args
return this._proxy.$executeContributedCommand(id, ...args); //TODO@Joh - we cannot serialize the args
},
weight: undefined,
context: undefined,
......@@ -147,11 +177,62 @@ export class MainThreadCommands {
return undefined;
}
_executeCommand<T>(id: string, args: any[]): Thenable<T> {
$executeCommand<T>(id: string, args: any[]): Thenable<T> {
return this._keybindingService.executeCommand(id, args);
}
_getCommands(): Thenable<string[]> {
$getCommands(): Thenable<string[]> {
return TPromise.as(Object.keys(KeybindingsRegistry.getCommands()));
}
$getCommandHandlerDescriptions(): TPromise<{ [id: string]: string | ICommandHandlerDescription }> {
return this._proxy.$getContributedCommandHandlerDescriptions().then(result => {
const commands = KeybindingsRegistry.getCommands();
for (let id in commands) {
let {description} = commands[id];
if (description) {
result[id] = description;
}
}
return result;
});
}
}
// --- command doc
KeybindingsRegistry.registerCommandDesc({
id: '_generateCommandsDocumentation',
handler: function(accessor) {
return accessor.get(IThreadService).getRemotable(MainThreadCommands).$getCommandHandlerDescriptions().then(result => {
const all: string[] = [];
for (let id in result) {
all.push('`' + id + '` - ' + _generateMarkdown(result[id]))
}
console.log(all.join('\n'));
});
},
context: undefined,
weight: KeybindingsRegistry.WEIGHT.builtinExtension(0),
primary: undefined
});
function _generateMarkdown(description: string | ICommandHandlerDescription): string {
if (typeof description === 'string') {
return description;
} else {
let parts = [description.description];
parts.push('\n\n');
if (description.args) {
for (let arg of description.args) {
parts.push(`* _${arg.name}_ ${arg.description || ''}\n`);
}
}
if (description.returns) {
parts.push(`* _(returns)_ ${description.returns}`);
}
parts.push('\n\n');
return parts.join('');
}
}
\ No newline at end of file
......@@ -24,7 +24,7 @@ import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingServic
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
import {IModelService} from 'vs/editor/common/services/modelService';
import {ExtHostLanguageFeatures, MainThreadLanguageFeatures} from 'vs/workbench/api/common/extHostLanguageFeatures';
import {ExtHostLanguageFeatureCommands} from 'vs/workbench/api/common/extHostLanguageFeatureCommands';
import {ExtHostApiCommands} from 'vs/workbench/api/common/extHostApiCommands';
import {PluginHostCommands, MainThreadCommands} from 'vs/workbench/api/common/pluginHostCommands';
import {PluginHostModelService} from 'vs/workbench/api/common/pluginHostDocuments';
import {SyncDescriptor0} from 'vs/platform/instantiation/common/descriptors';
......@@ -99,7 +99,7 @@ suite('ExtHostLanguageFeatureCommands', function() {
threadService.getRemotable(MainThreadCommands);
commands = threadService.getRemotable(PluginHostCommands);
new ExtHostLanguageFeatureCommands(commands);
new ExtHostApiCommands(commands);
mainThread = threadService.getRemotable(MainThreadLanguageFeatures);
extHost = threadService.getRemotable(ExtHostLanguageFeatures);
});
......@@ -119,22 +119,22 @@ suite('ExtHostLanguageFeatureCommands', function() {
// --- workspace symbols
// test('WorkspaceSymbols, invalid arguments', function(done) {
// let promises = [
// commands.executeCommand('vscode.executeWorkspaceSymbolProvider'),
// commands.executeCommand('vscode.executeWorkspaceSymbolProvider', null),
// commands.executeCommand('vscode.executeWorkspaceSymbolProvider', undefined),
// commands.executeCommand('vscode.executeWorkspaceSymbolProvider', true)
// ];
// threadService.sync().then(() => {
// TPromise.join(<any[]>promises).then(undefined, (err: any[]) => {
// assert.equal(err.length, 4);
// done();
// return [];
// });
// });
// });
test('WorkspaceSymbols, invalid arguments', function(done) {
let promises = [
commands.executeCommand('vscode.executeWorkspaceSymbolProvider'),
commands.executeCommand('vscode.executeWorkspaceSymbolProvider', null),
commands.executeCommand('vscode.executeWorkspaceSymbolProvider', undefined),
commands.executeCommand('vscode.executeWorkspaceSymbolProvider', true)
];
// threadService.sync().then(() => {
TPromise.join(<any[]>promises).then(undefined, (err: any[]) => {
assert.equal(err.length, 4);
done();
return [];
});
// });
});
test('WorkspaceSymbols, ⇔ back and forth', function(done) {
......@@ -172,22 +172,22 @@ suite('ExtHostLanguageFeatureCommands', function() {
// --- definition
// test('Definition, invalid arguments', function(done) {
// let promises = [
// commands.executeCommand('vscode.executeDefinitionProvider'),
// commands.executeCommand('vscode.executeDefinitionProvider', null),
// commands.executeCommand('vscode.executeDefinitionProvider', undefined),
// commands.executeCommand('vscode.executeDefinitionProvider', true, false)
// ];
// threadService.sync().then(() => {
// TPromise.join(<any[]>promises).then(undefined, (err: any[]) => {
// assert.equal(err.length, 4);
// done();
// return [];
// });
// });
// });
test('Definition, invalid arguments', function(done) {
let promises = [
commands.executeCommand('vscode.executeDefinitionProvider'),
commands.executeCommand('vscode.executeDefinitionProvider', null),
commands.executeCommand('vscode.executeDefinitionProvider', undefined),
commands.executeCommand('vscode.executeDefinitionProvider', true, false)
];
// threadService.sync().then(() => {
TPromise.join(<any[]>promises).then(undefined, (err: any[]) => {
assert.equal(err.length, 4);
done();
return [];
});
// });
});
test('Definition, ⇔ back and forth', function(done) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册