Align IParameterHintsSupport with the ext host api

This commit is contained in:
Alex Dima 2016-05-19 09:20:58 +02:00
parent 387e290787
commit 3e399132f5
11 changed files with 151 additions and 155 deletions

View file

@ -554,17 +554,10 @@ export class RunOnceScheduler {
this.runner = runner;
}
/**
* Set timeout. This change will only impact new schedule calls.
*/
public setTimeout(timeout: number): void {
this.timeout = timeout;
}
/**
* Cancel previous runner (if any) & schedule a new runner.
*/
public schedule(): void {
public schedule(delay = this.timeout): void {
this.cancel();
this.timeoutToken = platform.setTimeout(this.timeoutHandler, this.timeout);
}

View file

@ -424,31 +424,91 @@ export interface IQuickFixSupport {
getQuickFixes(resource: URI, range: editorCommon.IRange): TPromise<IQuickFix[]>;
}
export interface IParameter {
label:string;
documentation?:string;
signatureLabelOffset?:number;
signatureLabelEnd?:number;
}
/**
* Represents a parameter of a callable-signature. A parameter can
* have a label and a doc-comment.
*/
export interface ParameterInformation {
export interface ISignature {
label:string;
documentation?:string;
parameters:IParameter[];
}
/**
* The label of this signature. Will be shown in
* the UI.
*/
label: string;
export interface IParameterHints {
currentSignature:number;
currentParameter:number;
signatures:ISignature[];
/**
* The human-readable doc-comment of this signature. Will be shown
* in the UI but can be omitted.
*/
documentation: string;
}
/**
* Interface used to get parameter hints.
* Represents the signature of something callable. A signature
* can have a label, like a function-name, a doc-comment, and
* a set of parameters.
*/
export interface SignatureInformation {
/**
* The label of this signature. Will be shown in
* the UI.
*/
label: string;
/**
* The human-readable doc-comment of this signature. Will be shown
* in the UI but can be omitted.
*/
documentation: string;
/**
* The parameters of this signature.
*/
parameters: ParameterInformation[];
}
/**
* Signature help represents the signature of something
* callable. There can be multiple signatures but only one
* active and only one active parameter.
*/
export interface SignatureHelp {
/**
* One or more signatures.
*/
signatures: SignatureInformation[];
/**
* The active signature.
*/
activeSignature: number;
/**
* The active parameter of the active signature.
*/
activeParameter: number;
}
/**
* The signature help provider interface defines the contract between extensions and
* the [parameter hints](https://code.visualstudio.com/docs/editor/editingevolved#_parameter-hints)-feature.
*/
export interface IParameterHintsSupport {
getParameterHintsTriggerCharacters(): string[];
getParameterHints(resource: URI, position: editorCommon.IPosition, triggerCharacter?: string): TPromise<IParameterHints>;
parameterHintsTriggerCharacters: string[];
/**
* Provide help for the signature at the given position and document.
*
* @param document The document in which the command was invoked.
* @param position The position at which the command was invoked.
* @param token A cancellation token.
* @return Signature help or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideSignatureHelp(model: editorCommon.IModel, position: editorCommon.IEditorPosition, token: CancellationToken): SignatureHelp | Thenable<SignatureHelp>;
}

View file

@ -65,7 +65,7 @@ class ParameterHintsController implements IEditorContribution {
}
public trigger(): void {
this.model.trigger(undefined, 0);
this.model.trigger(0);
}
}

View file

@ -4,17 +4,16 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {ThrottledDelayer} from 'vs/base/common/async';
import {RunOnceScheduler} from 'vs/base/common/async';
import {onUnexpectedError} from 'vs/base/common/errors';
import Event, {Emitter} from 'vs/base/common/event';
import {IDisposable, dispose, Disposable} from 'vs/base/common/lifecycle';
import {TPromise} from 'vs/base/common/winjs.base';
import {EventType, ICommonCodeEditor, ICursorSelectionChangedEvent} from 'vs/editor/common/editorCommon';
import {ParameterHintsRegistry, IParameterHints} from 'vs/editor/common/modes';
import {getParameterHints} from '../common/parameterHints';
import {ParameterHintsRegistry, SignatureHelp} from 'vs/editor/common/modes';
import {provideSignatureHelp} from '../common/parameterHints';
export interface IHintEvent {
hints: IParameterHints;
hints: SignatureHelp;
}
export class ParameterHintsModel extends Disposable {
@ -31,7 +30,7 @@ export class ParameterHintsModel extends Disposable {
private triggerCharactersListeners: IDisposable[];
private active: boolean;
private throttledDelayer: ThrottledDelayer<boolean>;
private throttledDelayer: RunOnceScheduler;
constructor(editor:ICommonCodeEditor) {
super();
@ -39,7 +38,7 @@ export class ParameterHintsModel extends Disposable {
this.editor = editor;
this.triggerCharactersListeners = [];
this.throttledDelayer = new ThrottledDelayer<boolean>(ParameterHintsModel.DELAY);
this.throttledDelayer = new RunOnceScheduler(() => this.doTrigger(), ParameterHintsModel.DELAY);
this.active = false;
@ -60,18 +59,18 @@ export class ParameterHintsModel extends Disposable {
}
}
public trigger(triggerCharacter?: string, delay: number = ParameterHintsModel.DELAY): TPromise<boolean> {
public trigger(delay = ParameterHintsModel.DELAY): void {
if (!ParameterHintsRegistry.has(this.editor.getModel())) {
return;
}
this.cancel(true);
return this.throttledDelayer.trigger(() => this.doTrigger(triggerCharacter), delay);
return this.throttledDelayer.schedule(delay);
}
private doTrigger(triggerCharacter: string): TPromise<boolean> {
return getParameterHints(this.editor.getModel(), this.editor.getPosition(), triggerCharacter)
.then<IParameterHints>(null, onUnexpectedError)
private doTrigger(): void {
provideSignatureHelp(this.editor.getModel(), this.editor.getPosition())
.then<SignatureHelp>(null, onUnexpectedError)
.then(result => {
if (!result || result.signatures.length === 0) {
this.cancel();
@ -88,7 +87,7 @@ export class ParameterHintsModel extends Disposable {
}
public isTriggered():boolean {
return this.active || this.throttledDelayer.isTriggered();
return this.active || this.throttledDelayer.isScheduled();
}
private onModelChanged(): void {
@ -107,9 +106,9 @@ export class ParameterHintsModel extends Disposable {
return;
}
this.triggerCharactersListeners = support.getParameterHintsTriggerCharacters().map((ch) => {
this.triggerCharactersListeners = support.parameterHintsTriggerCharacters.map((ch) => {
let listener = this.editor.addTypingListener(ch, () => {
this.trigger(ch);
this.trigger();
});
return { dispose: listener };

View file

@ -12,7 +12,7 @@ import {TPromise} from 'vs/base/common/winjs.base';
import {Builder, $} from 'vs/base/browser/builder';
import aria = require('vs/base/browser/ui/aria/aria');
import {EventType, ICursorSelectionChangedEvent, IConfigurationChangedEvent} from 'vs/editor/common/editorCommon';
import {IParameterHints, ISignature} from 'vs/editor/common/modes';
import {SignatureHelp, SignatureInformation} from 'vs/editor/common/modes';
import {ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition} from 'vs/editor/browser/editorBrowser';
import {IHintEvent, ParameterHintsModel} from './parameterHintsModel';
@ -35,7 +35,7 @@ export class ParameterHintsWidget implements IContentWidget {
private currentSignature: number;
private isDisposed: boolean;
private isVisible: boolean;
private parameterHints: IParameterHints;
private parameterHints: SignatureHelp;
private announcedLabel: string;
private toDispose: IDisposable[];
@ -61,7 +61,7 @@ export class ParameterHintsWidget implements IContentWidget {
this.show();
this.parameterHints = e.hints;
this.render(e.hints);
this.currentSignature = e.hints.currentSignature;
this.currentSignature = e.hints.activeSignature;
this.select(this.currentSignature);
}));
@ -156,7 +156,7 @@ export class ParameterHintsWidget implements IContentWidget {
return null;
}
private render(hints:IParameterHints): void {
private render(hints:SignatureHelp): void {
if (hints.signatures.length > 1) {
this.$el.addClass('multiple');
} else {
@ -169,9 +169,9 @@ export class ParameterHintsWidget implements IContentWidget {
for (var i = 0, len = hints.signatures.length; i < len; i++) {
var signature = hints.signatures[i];
var $signature = this.renderSignature(this.$signatures, signature, hints.currentParameter);
var $signature = this.renderSignature(this.$signatures, signature, hints.activeParameter);
this.renderDocumentation($signature, signature, hints.currentParameter);
this.renderDocumentation($signature, signature, hints.activeParameter);
var signatureHeight = $signature.getClientArea().height;
@ -184,7 +184,7 @@ export class ParameterHintsWidget implements IContentWidget {
}
}
private renderSignature($el:Builder, signature:ISignature, currentParameter:number):Builder {
private renderSignature($el:Builder, signature:SignatureInformation, currentParameter:number):Builder {
var $signature = $('.signature').appendTo($el),
hasParameters = signature.parameters.length > 0;
@ -197,11 +197,22 @@ export class ParameterHintsWidget implements IContentWidget {
var $parameters = $('span.parameters'),
offset = 0;
let idx = 0;
for (var i = 0, len = signature.parameters.length; i < len; i++) {
var parameter = signature.parameters[i];
(i === 0 ? $signature : $parameters).append($('span').text(signature.label.substring(offset, parameter.signatureLabelOffset)));
$parameters.append($('span.parameter').addClass(i === currentParameter ? 'active' : '').text(signature.label.substring(parameter.signatureLabelOffset, parameter.signatureLabelEnd)));
offset = parameter.signatureLabelEnd;
idx = signature.label.indexOf(parameter.label, idx);
let signatureLabelOffset = 0;
let signatureLabelEnd = 0;
if (idx >= 0) {
signatureLabelOffset = idx;
idx += parameter.label.length;
signatureLabelEnd = idx;
}
(i === 0 ? $signature : $parameters).append($('span').text(signature.label.substring(offset, signatureLabelOffset)));
$parameters.append($('span.parameter').addClass(i === currentParameter ? 'active' : '').text(signature.label.substring(signatureLabelOffset, signatureLabelEnd)));
offset = signatureLabelEnd;
}
$signature.append($parameters);
@ -211,7 +222,7 @@ export class ParameterHintsWidget implements IContentWidget {
return $signature;
}
private renderDocumentation($el:Builder, signature:ISignature, activeParameterIdx:number): void {
private renderDocumentation($el:Builder, signature:SignatureInformation, activeParameterIdx:number): void {
if(signature.documentation) {
$el.append($('.documentation').text(signature.documentation));
@ -243,8 +254,8 @@ export class ParameterHintsWidget implements IContentWidget {
}
this.$overloads.text(overloads);
if (this.parameterHints && this.parameterHints.signatures[position].parameters[this.parameterHints.currentParameter]) {
const labelToAnnounce = this.parameterHints.signatures[position].parameters[this.parameterHints.currentParameter].label;
if (this.parameterHints && this.parameterHints.signatures[position].parameters[this.parameterHints.activeParameter]) {
const labelToAnnounce = this.parameterHints.signatures[position].parameters[this.parameterHints.activeParameter].label;
// Select method gets called on every user type while parameter hints are visible.
// We do not want to spam the user with same announcements, so we only announce if the current parameter changed.
if (this.announcedLabel !== labelToAnnounce) {

View file

@ -5,26 +5,21 @@
'use strict';
import {illegalArgument} from 'vs/base/common/errors';
import {TPromise} from 'vs/base/common/winjs.base';
import {IModel, IPosition} from 'vs/editor/common/editorCommon';
import {IModel, IEditorPosition} from 'vs/editor/common/editorCommon';
import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions';
import {IParameterHints, ParameterHintsRegistry} from 'vs/editor/common/modes';
import {SignatureHelp, ParameterHintsRegistry} from 'vs/editor/common/modes';
import {CancellationToken} from 'vs/base/common/cancellation';
import {toThenable} from 'vs/base/common/async';
export function getParameterHints(model:IModel, position:IPosition, triggerCharacter: string): TPromise<IParameterHints> {
export function provideSignatureHelp(model:IModel, position:IEditorPosition, cancellationToken = CancellationToken.None): Thenable<SignatureHelp> {
let support = ParameterHintsRegistry.ordered(model)[0];
if (!support) {
return TPromise.as(undefined);
}
return support.getParameterHints(model.getAssociatedResource(), position, triggerCharacter);
return toThenable(support.provideSignatureHelp(model, position, cancellationToken));
}
CommonEditorRegistry.registerDefaultLanguageCommand('_executeSignatureHelpProvider', function(model, position, args) {
let {triggerCharacter} = args;
if (triggerCharacter && typeof triggerCharacter !== 'string') {
throw illegalArgument('triggerCharacter');
}
return getParameterHints(model, position, triggerCharacter);
});
CommonEditorRegistry.registerDefaultLanguageCommand('_executeSignatureHelpProvider', provideSignatureHelp);

View file

@ -244,26 +244,25 @@ class SuggestAdapter extends Adapter implements modes.ISuggestSupport {
class ParameterHintsAdapter extends Adapter implements modes.IParameterHintsSupport {
getParameterHintsTriggerCharacters(): string[] {
return ['(', ','];
}
public parameterHintsTriggerCharacters = ['(', ','];
getParameterHints(resource: URI, position: editor.IPosition, triggerCharacter?: string): TPromise<modes.IParameterHints> {
provideSignatureHelp(model: editor.IModel, position: editor.IEditorPosition, token: CancellationToken): TPromise<modes.SignatureHelp> {
let resource = model.getAssociatedResource();
return this._worker(resource).then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
if (!info) {
return;
}
let ret = <modes.IParameterHints>{
currentSignature: info.selectedItemIndex,
currentParameter: info.argumentIndex,
let ret:modes.SignatureHelp = {
activeSignature: info.selectedItemIndex,
activeParameter: info.argumentIndex,
signatures: []
};
info.items.forEach(item => {
let signature = <modes.ISignature>{
let signature:modes.SignatureInformation = {
label: '',
documentation: null,
parameters: []
@ -272,11 +271,9 @@ class ParameterHintsAdapter extends Adapter implements modes.IParameterHintsSupp
signature.label += ts.displayPartsToString(item.prefixDisplayParts);
item.parameters.forEach((p, i, a) => {
let label = ts.displayPartsToString(p.displayParts);
let parameter = <modes.IParameter>{
let parameter:modes.ParameterInformation = {
label: label,
documentation: ts.displayPartsToString(p.documentation),
signatureLabelOffset: signature.label.length,
signatureLabelEnd: signature.label.length + label.length
documentation: ts.displayPartsToString(p.documentation)
};
signature.label += label;
signature.parameters.push(parameter);

View file

@ -306,7 +306,7 @@ class ExtHostApiCommands {
position: position && typeConverters.fromPosition(position),
triggerCharacter
};
return this._commands.executeCommand<modes.IParameterHints>('_executeSignatureHelpProvider', args).then(value => {
return this._commands.executeCommand<modes.SignatureHelp>('_executeSignatureHelpProvider', args).then(value => {
if (value) {
return typeConverters.SignatureHelp.to(value);
}

View file

@ -586,7 +586,7 @@ class SuggestAdapter implements modes.ISuggestSupport {
}
}
class ParameterHintsAdapter implements modes.IParameterHintsSupport {
class ParameterHintsAdapter {
private _documents: ExtHostModelService;
private _provider: vscode.SignatureHelpProvider;
@ -596,7 +596,7 @@ class ParameterHintsAdapter implements modes.IParameterHintsSupport {
this._provider = provider;
}
getParameterHints(resource: URI, position: IPosition, triggerCharacter?: string): TPromise<modes.IParameterHints> {
provideSignatureHelp(resource: URI, position: IPosition): TPromise<modes.SignatureHelp> {
const doc = this._documents.getDocumentData(resource).document;
const pos = TypeConverters.toPosition(position);
@ -607,10 +607,6 @@ class ParameterHintsAdapter implements modes.IParameterHintsSupport {
}
});
}
getParameterHintsTriggerCharacters(): string[] {
throw new Error('illegal state');
}
}
type Adapter = OutlineAdapter | CodeLensAdapter | DeclarationAdapter | ExtraInfoAdapter
@ -841,8 +837,8 @@ export class ExtHostLanguageFeatures {
return this._createDisposable(handle);
}
$getParameterHints(handle: number, resource: URI, position: IPosition, triggerCharacter?: string): TPromise<modes.IParameterHints> {
return this._withAdapter(handle, ParameterHintsAdapter, adapter => adapter.getParameterHints(resource, position, triggerCharacter));
$provideSignatureHelp(handle: number, resource: URI, position: IPosition): TPromise<modes.SignatureHelp> {
return this._withAdapter(handle, ParameterHintsAdapter, adapter => adapter.provideSignatureHelp(resource, position));
}
}
@ -1032,12 +1028,13 @@ export class MainThreadLanguageFeatures {
$registerParameterHintsSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any> {
this._registrations[handle] = modes.ParameterHintsRegistry.register(selector, <modes.IParameterHintsSupport>{
getParameterHints: (resource: URI, position: IPosition, triggerCharacter?: string): TPromise<modes.IParameterHints> => {
return this._proxy.$getParameterHints(handle, resource, position, triggerCharacter);
},
getParameterHintsTriggerCharacters(): string[] {
return triggerCharacter;
parameterHintsTriggerCharacters: triggerCharacter,
provideSignatureHelp: (model:IModel, position:IEditorPosition, cancellationToken:CancellationToken): Thenable<modes.SignatureHelp> => {
return wireCancellationToken(cancellationToken, this._proxy.$provideSignatureHelp(handle, model.getAssociatedResource(), position));
}
});
return undefined;
}

View file

@ -426,68 +426,12 @@ export const Suggest = {
export namespace SignatureHelp {
export function from(signatureHelp: types.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;
} else {
parameterItem.signatureLabelOffset = 0;
parameterItem.signatureLabelEnd = 0;
}
}
result.signatures.push(signatureItem);
}
return result;
export function from(signatureHelp: types.SignatureHelp): modes.SignatureHelp {
return signatureHelp;
}
export function to(hints: modes.IParameterHints): types.SignatureHelp {
const result = new types.SignatureHelp();
result.activeSignature = hints.currentSignature;
result.activeParameter = hints.currentParameter;
for (let signature of hints.signatures) {
const signatureItem = new types.SignatureInformation(signature.label, signature.documentation);
result.signatures.push(signatureItem);
for (let parameter of signature.parameters) {
const parameterItem = new types.ParameterInformation(parameter.label, parameter.documentation);
signatureItem.parameters.push(parameterItem);
}
}
return result;
export function to(hints: modes.SignatureHelp): types.SignatureHelp {
return hints;
}
}

View file

@ -31,7 +31,7 @@ import {findReferences} from 'vs/editor/contrib/referenceSearch/common/reference
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';
import {provideSignatureHelp} from 'vs/editor/contrib/parameterHints/common/parameterHints';
import {suggest} from 'vs/editor/contrib/suggest/common/suggest';
import {formatDocument, formatRange, formatAfterKeystroke} from 'vs/editor/contrib/format/common/format';
@ -769,7 +769,7 @@ suite('ExtHostLanguageFeatures', function() {
threadService.sync().then(() => {
getParameterHints(model, { lineNumber: 1, column: 1 }, '(').then(value => {
provideSignatureHelp(model, new EditorPosition(1, 1)).then(value => {
done(new Error('error expeted'));
}, err => {
assert.equal(err.message, 'evil');