From 7d1de2c673065a63dca778aba33a7e04543056c3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 26 Nov 2015 12:34:48 +0100 Subject: [PATCH] signature help provider as support --- .../browser/parameterHintsModel.ts | 11 +- .../parameterHints/common/parameterHints.ts | 15 ++- .../api/browser/pluginHost.api.impl.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 103 ++++++++++++++- .../workbench/api/common/languageFeatures.ts | 118 ------------------ .../api/extHostLanguageFeatures.test.ts | 22 ++++ 6 files changed, 141 insertions(+), 130 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts index 9dee0382288..d5a645759eb 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts @@ -11,7 +11,7 @@ import async = require('vs/base/common/async'); import events = require('vs/base/common/eventEmitter'); import EditorCommon = require('vs/editor/common/editorCommon'); import Modes = require('vs/editor/common/modes'); -import {ParameterHintsRegistry} from '../common/parameterHints'; +import {ParameterHintsRegistry, getParameterHints} from '../common/parameterHints'; import {sequence} from 'vs/base/common/async'; function hashParameterHints(hints: Modes.IParameterHints): string { @@ -96,15 +96,8 @@ export class ParameterHintsModel extends events.EventEmitter { } public doTrigger(triggerCharacter: string): TPromise { - let model = this.editor.getModel(); - let support = ParameterHintsRegistry.ordered(model)[0]; - if (!support) { - return TPromise.as(false); - } - - return support.getParameterHints(model.getAssociatedResource(), this.editor.getPosition(), triggerCharacter).then((result: Modes.IParameterHints) => { + return getParameterHints(this.editor.getModel(), this.editor.getPosition(), triggerCharacter).then(result => { var hash = hashParameterHints(result); - if (!result || result.signatures.length === 0 || (this.hash && hash !== this.hash)) { this.cancel(); this.emit('cancel'); diff --git a/src/vs/editor/contrib/parameterHints/common/parameterHints.ts b/src/vs/editor/contrib/parameterHints/common/parameterHints.ts index 47b974a63f3..58b4bd073bc 100644 --- a/src/vs/editor/contrib/parameterHints/common/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/common/parameterHints.ts @@ -5,7 +5,20 @@ 'use strict'; -import {IParameterHintsSupport} from 'vs/editor/common/modes'; +import {IParameterHintsSupport, IParameterHints} from 'vs/editor/common/modes'; +import {IModel, IPosition} from 'vs/editor/common/editorCommon'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {onUnexpectedError} from 'vs/base/common/errors'; import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; export const ParameterHintsRegistry = new LanguageFeatureRegistry('parameterHintsSupport'); + +export function getParameterHints(model:IModel, position:IPosition, triggerCharacter: string): TPromise { + + let support = ParameterHintsRegistry.ordered(model)[0]; + if (!support) { + return TPromise.as(undefined); + } + + return support.getParameterHints(model.getAssociatedResource(), position, triggerCharacter); +} \ No newline at end of file diff --git a/src/vs/workbench/api/browser/pluginHost.api.impl.ts b/src/vs/workbench/api/browser/pluginHost.api.impl.ts index bb5de6b7174..606d5ccfb5c 100644 --- a/src/vs/workbench/api/browser/pluginHost.api.impl.ts +++ b/src/vs/workbench/api/browser/pluginHost.api.impl.ts @@ -301,7 +301,7 @@ export class PluginHostAPIImplementation { return languageFeatures.registerOnTypeFormattingEditProvider(selector, provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); }, registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, ...triggerCharacters: string[]): vscode.Disposable { - return features.signatureHelp.register(selector, { triggerCharacters, provider }); + return languageFeatures.registerSignatureHelpProvider(selector, provider, triggerCharacters); }, registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { return languageFeatures.registerCompletionItemProvider(selector, provider, triggerCharacters); diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index bcca2a482da..e638636c307 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -617,10 +617,81 @@ class SuggestAdapter implements modes.ISuggestSupport { } } +class ParameterHintsAdapter implements modes.IParameterHintsSupport { + + private _documents: PluginHostModelService; + private _provider: vscode.SignatureHelpProvider; + + constructor(documents: PluginHostModelService, provider: vscode.SignatureHelpProvider) { + this._documents = documents; + this._provider = provider; + } + + getParameterHints(resource: URI, position: IPosition, triggerCharacter?: string): TPromise { + + const doc = this._documents.getDocument(resource); + const pos = TypeConverters.toPosition(position); + + return asWinJsPromise(token => this._provider.provideSignatureHelp(doc, pos, token)).then(value => { + if (value instanceof SignatureHelp) { + return ParameterHintsAdapter._convertSignatureHelp(value); + } + }); + } + + private static _convertSignatureHelp(signatureHelp: SignatureHelp): modes.IParameterHints { + + let result: modes.IParameterHints = { + currentSignature: signatureHelp.activeSignature, + currentParameter: signatureHelp.activeParameter, + signatures: [] + } + + for (let signature of signatureHelp.signatures) { + + let signatureItem: modes.ISignature = { + label: signature.label, + documentation: signature.documentation, + parameters: [] + }; + + let idx = 0; + for (let parameter of signature.parameters) { + + let parameterItem: modes.IParameter = { + label: parameter.label, + documentation: parameter.documentation, + }; + + signatureItem.parameters.push(parameterItem); + idx = signature.label.indexOf(parameter.label, idx); + + if (idx >= 0) { + parameterItem.signatureLabelOffset = idx; + idx += parameter.label.length; + parameterItem.signatureLabelEnd = idx; + } + } + + result.signatures.push(signatureItem); + } + + return result; + } + + getParameterHintsTriggerCharacters(): string[] { + throw new Error('illegal state'); + } + + shouldTriggerParameterHints(context: modes.ILineContext, offset: number): boolean { + throw new Error('illegal state'); + } +} + type Adapter = OutlineAdapter | CodeLensAdapter | DeclarationAdapter | ExtraInfoAdapter | OccurrencesAdapter | ReferenceAdapter | QuickFixAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter - | SuggestAdapter; + | SuggestAdapter | ParameterHintsAdapter; @Remotable.PluginHostContext('ExtHostLanguageFeatures') export class ExtHostLanguageFeatures { @@ -833,6 +904,19 @@ export class ExtHostLanguageFeatures { $getSuggestionDetails(handle: number, resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise { return this._withAdapter(handle, SuggestAdapter, adapter => adapter.getSuggestionDetails(resource, position, suggestion)); } + + // --- parameter hints + + registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, triggerCharacters: string[]): vscode.Disposable { + const handle = this._nextHandle(); + this._adapter[handle] = new ParameterHintsAdapter(this._documents, provider); + this._proxy.$registerParameterHintsSupport(handle, selector, triggerCharacters); + return this._createDisposable(handle); + } + + $getParameterHints(handle: number, resource: URI, position: IPosition, triggerCharacter?: string): TPromise { + return this._withAdapter(handle, ParameterHintsAdapter, adapter => adapter.getParameterHints(resource, position, triggerCharacter)); + } } @Remotable.MainContext('MainThreadLanguageFeatures') @@ -1030,4 +1114,21 @@ export class MainThreadLanguageFeatures { }); return undefined; } + + // --- parameter hints + + $registerParameterHintsSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise { + this._registrations[handle] = ParameterHintsRegistry.register(selector, { + getParameterHints: (resource: URI, position: IPosition, triggerCharacter?: string): TPromise => { + return this._proxy.$getParameterHints(handle, resource, position, triggerCharacter); + }, + getParameterHintsTriggerCharacters(): string[] { + return triggerCharacter; + }, + shouldTriggerParameterHints(context: modes.ILineContext, offset: number): boolean { + return true; + } + }); + return undefined; + } } \ No newline at end of file diff --git a/src/vs/workbench/api/common/languageFeatures.ts b/src/vs/workbench/api/common/languageFeatures.ts index 82b14ecbf4b..fa22152bcc6 100644 --- a/src/vs/workbench/api/common/languageFeatures.ts +++ b/src/vs/workbench/api/common/languageFeatures.ts @@ -282,132 +282,14 @@ export class MainThreadFormatOnType extends AbstractMainThreadFeature { - - constructor( @IThreadService threadService: IThreadService) { - super(threadService.getRemotable(MainThreadSignatureHelp), threadService); - } - - register(selector: vscode.DocumentSelector, entry: SignatureHelpEntry): vscode.Disposable { - - let disposable = this._registry.register(selector, entry); - let registered = this._proxy._register(selector, entry.triggerCharacters); - - return new Disposable(() => { - disposable.dispose(); - registered.then(() => this._proxy._unregister()); - }); - } - - _runAsCommand(resource: URI, position: IPosition, triggerCharacter?: string): TPromise { - - let document = this._models.getDocument(resource); - let pos = TypeConverters.toPosition(position); - - let entry = this._getOrderedFor(document)[0]; - if (entry) { - - if (triggerCharacter) { - if (entry.triggerCharacters.indexOf(triggerCharacter) < 0) { - return; - } - } - - return asWinJsPromise(token => entry.provider.provideSignatureHelp(document, pos, token)).then(result => { - if (result instanceof SignatureHelp) { - return ExtHostSignatureHelp._convertSignatureHelp(result); - } - }); - } - } - - private static _convertSignatureHelp(signatureHelp: SignatureHelp): modes.IParameterHints { - - let result: modes.IParameterHints = { - currentSignature: signatureHelp.activeSignature, - currentParameter: signatureHelp.activeParameter, - signatures: [] - } - - for (let signature of signatureHelp.signatures) { - - let signatureItem: modes.ISignature = { - label: signature.label, - documentation: signature.documentation, - parameters: [] - }; - - let idx = 0; - for (let parameter of signature.parameters) { - - let parameterItem: modes.IParameter = { - label: parameter.label, - documentation: parameter.documentation, - }; - - signatureItem.parameters.push(parameterItem); - idx = signature.label.indexOf(parameter.label, idx); - - if (idx >= 0) { - parameterItem.signatureLabelOffset = idx; - idx += parameter.label.length; - parameterItem.signatureLabelEnd = idx; - } - } - - result.signatures.push(signatureItem); - } - - return result; - } -} - -@Remotable.MainContext('MainThreadSignatureHelp') -export class MainThreadSignatureHelp extends AbstractMainThreadFeature implements modes.IParameterHintsSupport { - - private _triggerCharacters: string[] = []; - - constructor( @IThreadService threadService: IThreadService) { - super('vscode.executeSignatureHelpProvider', ParameterHintsRegistry, threadService); - } - - _register(selector: vscode.DocumentSelector, triggerCharacters: string[] = []): TPromise { - this._triggerCharacters.push(...triggerCharacters); - return super._register(selector); - } - - getParameterHintsTriggerCharacters(): string[] { - return this._triggerCharacters; - } - - shouldTriggerParameterHints(context: modes.ILineContext, offset: number): boolean { - return true; - } - - getParameterHints(resource: URI, position: IPosition, triggerCharacter?: string): TPromise { - return this._executeCommand(resource, position, triggerCharacter); - } -} - -// ---- Completions - export namespace LanguageFeatures { export function createMainThreadInstances(threadService: IThreadService): void { - threadService.getRemotable(MainThreadSignatureHelp); } export function createExtensionHostInstances(threadService: IThreadService) { return { - signatureHelp: new ExtHostSignatureHelp(threadService), }; } } \ No newline at end of file diff --git a/src/vs/workbench/test/common/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/common/api/extHostLanguageFeatures.test.ts index 2de690442a2..6c5073e6325 100644 --- a/src/vs/workbench/test/common/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/common/api/extHostLanguageFeatures.test.ts @@ -35,6 +35,7 @@ import {ReferenceRegistry, findReferences} from 'vs/editor/contrib/referenceSear import {getQuickFixes} from 'vs/editor/contrib/quickFix/common/quickFix'; import {getNavigateToItems} from 'vs/workbench/parts/search/common/search'; import {rename} from 'vs/editor/contrib/rename/common/rename'; +import {getParameterHints} from 'vs/editor/contrib/parameterHints/common/parameterHints'; const defaultSelector = { scheme: 'far' }; const model: EditorCommon.IModel = new EditorModel( @@ -779,4 +780,25 @@ suite('ExtHostLanguageFeatures', function() { }); }); }); + + // --- parameter hints + + test('Parameter Hints, evil provider', function(done) { + + disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + provideSignatureHelp(): any { + throw new Error('evil'); + } + }, [])); + + threadService.sync().then(() => { + + getParameterHints(model, { lineNumber: 1, column: 1 }, '(').then(value => { + done(new Error('error expeted')); + }, err => { + assert.equal(err.message, 'evil'); + done(); + }) + }); + }) }); \ No newline at end of file