Skip to content

Commit c87615a

Browse files
Switch signature help over to using display parts. This allows for classified sig help on the editor side.
1 parent db0a223 commit c87615a

File tree

4 files changed

+77
-28
lines changed

4 files changed

+77
-28
lines changed

src/harness/fourslash.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,14 @@ module FourSlash {
820820
this.taoInvalidReason = 'verifyCurrentSignatureHelpIs NYI';
821821

822822
var help = this.getActiveSignatureHelpItem();
823-
assert.equal(help.prefix + help.parameters.map(p => p.display).join(help.separator) + help.suffix, expected);
823+
assert.equal(
824+
this.partsToString(help.prefixDisplayParts) +
825+
help.parameters.map(p => this.partsToString(p.displayParts)).join(this.partsToString(help.separatorDisplayParts)) +
826+
this.partsToString(help.suffixDisplayParts), expected);
827+
}
828+
829+
private partsToString(parts: ts.SymbolDisplayPart[]): string {
830+
return parts.map(p => p.text).join("");
824831
}
825832

826833
public verifyCurrentParameterIsVariable(isVariable: boolean) {
@@ -844,7 +851,7 @@ module FourSlash {
844851

845852
var activeSignature = this.getActiveSignatureHelpItem();
846853
var activeParameter = this.getActiveParameter();
847-
assert.equal(activeParameter.display, parameter);
854+
assert.equal(this.partsToString(activeParameter.displayParts), parameter);
848855
}
849856

850857
public verifyCurrentParameterHelpDocComment(docComment: string) {

src/services/services.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -856,8 +856,8 @@ module ts {
856856

857857
export class SignatureHelpParameter {
858858
constructor(public name: string,
859-
public documentation: string,
860-
public display: string,
859+
public documentation: SymbolDisplayPart[],
860+
public displayParts: SymbolDisplayPart[],
861861
public isOptional: boolean) {
862862
}
863863
}
@@ -871,11 +871,11 @@ module ts {
871871
*/
872872
export class SignatureHelpItem {
873873
constructor(public isVariadic: boolean,
874-
public prefix: string,
875-
public suffix: string,
876-
public separator: string,
874+
public prefixDisplayParts: SymbolDisplayPart[],
875+
public suffixDisplayParts: SymbolDisplayPart[],
876+
public separatorDisplayParts: SymbolDisplayPart[],
877877
public parameters: SignatureHelpParameter[],
878-
public documentation: string) {
878+
public documentation: SymbolDisplayPart[]) {
879879
}
880880
}
881881

@@ -1630,6 +1630,11 @@ module ts {
16301630
});
16311631
}
16321632

1633+
export function getSymbolDocumentationDisplayParts(symbol: Symbol): SymbolDisplayPart[] {
1634+
var documentation = symbol.getDocumentationComment();
1635+
return documentation === "" ? [] : [new SymbolDisplayPart(documentation, SymbolDisplayPartKind.text, /*symbol:*/ null)];
1636+
}
1637+
16331638
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry): LanguageService {
16341639
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
16351640
var formattingRulesProvider: TypeScript.Services.Formatting.RulesProvider;
@@ -2376,8 +2381,7 @@ module ts {
23762381
return undefined;
23772382
}
23782383

2379-
var documentation = symbol.getDocumentationComment();
2380-
var documentationParts = documentation === "" ? [] : [new SymbolDisplayPart(documentation, SymbolDisplayPartKind.text, /*symbol:*/ null)];
2384+
var documentationParts = getSymbolDocumentationDisplayParts(symbol);
23812385

23822386
// Having all this logic here is pretty unclean. Consider moving to the roslyn model
23832387
// where all symbol display logic is encapsulated into visitors and options.
@@ -3731,9 +3735,9 @@ module ts {
37313735

37323736
// Reset writer back to undefined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput
37333737
writer = undefined;
3734-
return emitOutput;
3735-
}
3736-
3738+
return emitOutput;
3739+
}
3740+
37373741
// Signature help
37383742
/**
37393743
* This is a semantic operation.

src/services/signatureHelp.ts

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -252,29 +252,66 @@ module ts.SignatureHelp {
252252
var items = map(candidates, candidateSignature => {
253253
var parameters = candidateSignature.parameters;
254254
var parameterHelpItems = parameters.length === 0 ? emptyArray : map(parameters, p => {
255-
var display = p.name;
255+
var displayParts: SymbolDisplayPart[] = [];
256+
256257
if (candidateSignature.hasRestParameter && parameters[parameters.length - 1] === p) {
257-
display = "..." + display;
258+
displayParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.DotDotDotToken), SymbolDisplayPartKind.punctuation, undefined));
258259
}
260+
261+
displayParts.push(new SymbolDisplayPart(p.name, SymbolDisplayPartKind.parameterName, p));
262+
259263
var isOptional = !!(p.valueDeclaration.flags & NodeFlags.QuestionMark);
260264
if (isOptional) {
261-
display += "?";
265+
displayParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.QuestionToken), SymbolDisplayPartKind.punctuation, undefined));
262266
}
263-
display += ": " + typeInfoResolver.typeToString(typeInfoResolver.getTypeOfSymbol(p), argumentListOrTypeArgumentList);
264-
return new SignatureHelpParameter(p.name, "", display, isOptional);
267+
268+
displayParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.ColonToken), SymbolDisplayPartKind.punctuation, undefined));
269+
displayParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
270+
271+
var typeParts = typeInfoResolver.typeToDisplayParts(typeInfoResolver.getTypeOfSymbol(p), argumentListOrTypeArgumentList);
272+
displayParts.push.apply(displayParts, typeParts);
273+
274+
return new SignatureHelpParameter(p.name, getSymbolDocumentationDisplayParts(p), displayParts, isOptional);
265275
});
276+
266277
var callTargetNode = (<CallExpression>argumentListOrTypeArgumentList.parent).func;
267278
var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTargetNode);
268-
var signatureName = callTargetSymbol ? typeInfoResolver.symbolToString(callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : "";
269-
var prefix = signatureName;
279+
280+
var prefixParts = callTargetSymbol ? typeInfoResolver.symbolToDisplayParts(callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : [];
281+
282+
var separatorParts = [
283+
new SymbolDisplayPart(tokenToString(SyntaxKind.CommaToken), SymbolDisplayPartKind.punctuation, undefined),
284+
new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)
285+
];
286+
270287
// TODO(jfreeman): Constraints?
271288
if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) {
272-
prefix += "<" + map(candidateSignature.typeParameters, tp => tp.symbol.name).join(", ") + ">";
289+
prefixParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.LessThanToken), SymbolDisplayPartKind.punctuation, undefined));
290+
291+
for (var i = 0, n = candidateSignature.typeParameters.length; i < n; i++) {
292+
if (i) {
293+
prefixParts.push.apply(prefixParts, separatorParts);
294+
}
295+
296+
var tp = candidateSignature.typeParameters[i].symbol;
297+
prefixParts.push(new SymbolDisplayPart(tp.name, SymbolDisplayPartKind.typeParameterName, tp));
298+
}
299+
300+
prefixParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.GreaterThanToken), SymbolDisplayPartKind.punctuation, undefined));
273301
}
274-
prefix += "(";
275-
var suffix = "): " + typeInfoResolver.typeToString(candidateSignature.getReturnType(), argumentListOrTypeArgumentList);
276-
return new SignatureHelpItem(candidateSignature.hasRestParameter, prefix, suffix, ", ", parameterHelpItems, "");
302+
303+
prefixParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.OpenParenToken), SymbolDisplayPartKind.punctuation, undefined));
304+
305+
var suffixParts = [new SymbolDisplayPart(tokenToString(SyntaxKind.CloseParenToken), SymbolDisplayPartKind.punctuation, undefined)];
306+
suffixParts.push(new SymbolDisplayPart(tokenToString(SyntaxKind.ColonToken), SymbolDisplayPartKind.punctuation, undefined));
307+
suffixParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
308+
309+
var typeParts = typeInfoResolver.typeToDisplayParts(candidateSignature.getReturnType(), argumentListOrTypeArgumentList);
310+
suffixParts.push.apply(suffixParts, typeParts);
311+
312+
return new SignatureHelpItem(candidateSignature.hasRestParameter, prefixParts, suffixParts, separatorParts, parameterHelpItems, null);
277313
});
314+
278315
var selectedItemIndex = candidates.indexOf(bestSignature);
279316
if (selectedItemIndex < 0) {
280317
selectedItemIndex = 0;
@@ -339,11 +376,11 @@ module ts.SignatureHelp {
339376
// by 2 to exclude commas. Use bit shifting in order to take the floor of the division.
340377
var argumentIndex = indexOfNodeContainingPosition < 0 ? argumentCount - 1 : indexOfNodeContainingPosition >> 1;
341378
return new SignatureHelpState(argumentIndex, argumentCount);
342-
}
343-
344-
function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node {
379+
}
380+
381+
function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node {
345382
var children = parent.getChildren(sourceFile);
346383
var indexOfOpenerToken = children.indexOf(openerToken);
347-
return children[indexOfOpenerToken + 1];
384+
return children[indexOfOpenerToken + 1];
348385
}
349386
}

tests/cases/fourslash/signatureHelpSuperConstructorOverload.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//// }
1818
////}
1919

20+
debugger;
2021
goTo.marker('superOverload1');
2122
verify.signatureHelpCountIs(2);
2223
verify.currentSignatureHelpIs("SuperOverloadlBase(): SuperOverloadlBase");

0 commit comments

Comments
 (0)