mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-11 06:02:53 -05:00
Merge pull request #746 from Microsoft/classifiedSigHelp
Switch signature help over to using display parts.
This commit is contained in:
@@ -925,38 +925,17 @@ module ts {
|
||||
var displayPartWriters: DisplayPartsSymbolWriter[] = [];
|
||||
var stringWriters: StringSymbolWriter[] = [];
|
||||
|
||||
function displayPartKind(symbol: Symbol): SymbolDisplayPartKind {
|
||||
var flags = symbol.flags;
|
||||
|
||||
if (flags & SymbolFlags.Variable) {
|
||||
return symbol.declarations && symbol.declarations.length > 0 && symbol.declarations[0].kind === SyntaxKind.Parameter
|
||||
? SymbolDisplayPartKind.parameterName
|
||||
: SymbolDisplayPartKind.localName;
|
||||
}
|
||||
else if (flags & SymbolFlags.Property) { return SymbolDisplayPartKind.propertyName; }
|
||||
else if (flags & SymbolFlags.EnumMember) { return SymbolDisplayPartKind.enumMemberName; }
|
||||
else if (flags & SymbolFlags.Function) { return SymbolDisplayPartKind.functionName; }
|
||||
else if (flags & SymbolFlags.Class) { return SymbolDisplayPartKind.className; }
|
||||
else if (flags & SymbolFlags.Interface) { return SymbolDisplayPartKind.interfaceName; }
|
||||
else if (flags & SymbolFlags.Enum) { return SymbolDisplayPartKind.enumName; }
|
||||
else if (flags & SymbolFlags.Module) { return SymbolDisplayPartKind.moduleName; }
|
||||
else if (flags & SymbolFlags.Method) { return SymbolDisplayPartKind.methodName; }
|
||||
else if (flags & SymbolFlags.TypeParameter) { return SymbolDisplayPartKind.typeParameterName; }
|
||||
|
||||
return SymbolDisplayPartKind.text;
|
||||
}
|
||||
|
||||
function getDisplayPartWriter(): DisplayPartsSymbolWriter {
|
||||
if (displayPartWriters.length == 0) {
|
||||
var displayParts: SymbolDisplayPart[] = [];
|
||||
return {
|
||||
displayParts: () => displayParts,
|
||||
writeKind: (text, kind) => displayParts.push(new SymbolDisplayPart(text, kind, undefined)),
|
||||
writeSymbol: (text, symbol) => displayParts.push(new SymbolDisplayPart(text, displayPartKind(symbol), symbol)),
|
||||
writeSymbol: (text, symbol) => displayParts.push(symbolPart(text, symbol)),
|
||||
|
||||
// Completely ignore indentation for display part writers. And map newlines to
|
||||
// a single space.
|
||||
writeLine: () => displayParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)),
|
||||
writeLine: () => displayParts.push(spacePart()),
|
||||
increaseIndent: () => { },
|
||||
decreaseIndent: () => { },
|
||||
clear: () => displayParts = [],
|
||||
@@ -7693,4 +7672,49 @@ module ts {
|
||||
|
||||
return checker;
|
||||
}
|
||||
|
||||
export function spacePart() {
|
||||
return new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined);
|
||||
}
|
||||
|
||||
export function keywordPart(kind: SyntaxKind) {
|
||||
return new SymbolDisplayPart(tokenToString(kind), SymbolDisplayPartKind.keyword, undefined);
|
||||
}
|
||||
|
||||
export function punctuationPart(kind: SyntaxKind) {
|
||||
return new SymbolDisplayPart(tokenToString(kind), SymbolDisplayPartKind.punctuation, undefined);
|
||||
}
|
||||
|
||||
export function operatorPart(kind: SyntaxKind) {
|
||||
return new SymbolDisplayPart(tokenToString(kind), SymbolDisplayPartKind.operator, undefined);
|
||||
}
|
||||
|
||||
export function textPart(text: string) {
|
||||
return new SymbolDisplayPart(text, SymbolDisplayPartKind.text, undefined);
|
||||
}
|
||||
|
||||
export function symbolPart(text: string, symbol: Symbol) {
|
||||
return new SymbolDisplayPart(text, displayPartKind(symbol), symbol)
|
||||
}
|
||||
|
||||
function displayPartKind(symbol: Symbol): SymbolDisplayPartKind {
|
||||
var flags = symbol.flags;
|
||||
|
||||
if (flags & SymbolFlags.Variable) {
|
||||
return symbol.declarations && symbol.declarations.length > 0 && symbol.declarations[0].kind === SyntaxKind.Parameter
|
||||
? SymbolDisplayPartKind.parameterName
|
||||
: SymbolDisplayPartKind.localName;
|
||||
}
|
||||
else if (flags & SymbolFlags.Property) { return SymbolDisplayPartKind.propertyName; }
|
||||
else if (flags & SymbolFlags.EnumMember) { return SymbolDisplayPartKind.enumMemberName; }
|
||||
else if (flags & SymbolFlags.Function) { return SymbolDisplayPartKind.functionName; }
|
||||
else if (flags & SymbolFlags.Class) { return SymbolDisplayPartKind.className; }
|
||||
else if (flags & SymbolFlags.Interface) { return SymbolDisplayPartKind.interfaceName; }
|
||||
else if (flags & SymbolFlags.Enum) { return SymbolDisplayPartKind.enumName; }
|
||||
else if (flags & SymbolFlags.Module) { return SymbolDisplayPartKind.moduleName; }
|
||||
else if (flags & SymbolFlags.Method) { return SymbolDisplayPartKind.methodName; }
|
||||
else if (flags & SymbolFlags.TypeParameter) { return SymbolDisplayPartKind.typeParameterName; }
|
||||
|
||||
return SymbolDisplayPartKind.text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1199,6 +1199,10 @@ module ts {
|
||||
kind: SymbolDisplayPartKind[this.kind]
|
||||
};
|
||||
}
|
||||
|
||||
public static toString(parts: SymbolDisplayPart[]) {
|
||||
return parts.map(p => p.text).join("");
|
||||
}
|
||||
}
|
||||
|
||||
export enum SymbolDisplayPartKind {
|
||||
|
||||
@@ -820,7 +820,10 @@ module FourSlash {
|
||||
this.taoInvalidReason = 'verifyCurrentSignatureHelpIs NYI';
|
||||
|
||||
var help = this.getActiveSignatureHelpItem();
|
||||
assert.equal(help.prefix + help.parameters.map(p => p.display).join(help.separator) + help.suffix, expected);
|
||||
assert.equal(
|
||||
ts.SymbolDisplayPart.toString(help.prefixDisplayParts) +
|
||||
help.parameters.map(p => ts.SymbolDisplayPart.toString(p.displayParts)).join(ts.SymbolDisplayPart.toString(help.separatorDisplayParts)) +
|
||||
ts.SymbolDisplayPart.toString(help.suffixDisplayParts), expected);
|
||||
}
|
||||
|
||||
public verifyCurrentParameterIsVariable(isVariable: boolean) {
|
||||
@@ -844,7 +847,7 @@ module FourSlash {
|
||||
|
||||
var activeSignature = this.getActiveSignatureHelpItem();
|
||||
var activeParameter = this.getActiveParameter();
|
||||
assert.equal(activeParameter.display, parameter);
|
||||
assert.equal(ts.SymbolDisplayPart.toString(activeParameter.displayParts), parameter);
|
||||
}
|
||||
|
||||
public verifyCurrentParameterHelpDocComment(docComment: string) {
|
||||
|
||||
@@ -722,7 +722,7 @@ module ts {
|
||||
currentParameterIsTypeParameter: boolean; // current parameter is a type argument or a normal argument
|
||||
currentParameter: number; // Index of active parameter in "parameters" or "typeParamters" array
|
||||
}
|
||||
|
||||
|
||||
export interface ClassifiedSpan {
|
||||
textSpan: TypeScript.TextSpan;
|
||||
classificationType: string; // ClassificationTypeNames
|
||||
@@ -833,8 +833,8 @@ module ts {
|
||||
|
||||
export interface SignatureHelpParameter {
|
||||
name: string;
|
||||
documentation: string;
|
||||
display: string;
|
||||
documentation: SymbolDisplayPart[];
|
||||
displayParts: SymbolDisplayPart[];
|
||||
isOptional: boolean;
|
||||
}
|
||||
|
||||
@@ -847,11 +847,11 @@ module ts {
|
||||
*/
|
||||
export interface SignatureHelpItem {
|
||||
isVariadic: boolean;
|
||||
prefix: string;
|
||||
suffix: string;
|
||||
separator: string;
|
||||
prefixDisplayParts: SymbolDisplayPart[];
|
||||
suffixDisplayParts: SymbolDisplayPart[];
|
||||
separatorDisplayParts: SymbolDisplayPart[];
|
||||
parameters: SignatureHelpParameter[];
|
||||
documentation: string;
|
||||
documentation: SymbolDisplayPart[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1622,6 +1622,11 @@ module ts {
|
||||
});
|
||||
}
|
||||
|
||||
export function getSymbolDocumentationDisplayParts(symbol: Symbol): SymbolDisplayPart[] {
|
||||
var documentation = symbol.getDocumentationComment();
|
||||
return documentation === "" ? [] : [new SymbolDisplayPart(documentation, SymbolDisplayPartKind.text, /*symbol:*/ null)];
|
||||
}
|
||||
|
||||
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry): LanguageService {
|
||||
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
|
||||
var formattingRulesProvider: TypeScript.Services.Formatting.RulesProvider;
|
||||
@@ -2372,42 +2377,41 @@ module ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var documentation = symbol.getDocumentationComment();
|
||||
var documentationParts = documentation === "" ? [] : [new SymbolDisplayPart(documentation, SymbolDisplayPartKind.text, /*symbol:*/ null)];
|
||||
var documentationParts = getSymbolDocumentationDisplayParts(symbol);
|
||||
|
||||
// Having all this logic here is pretty unclean. Consider moving to the roslyn model
|
||||
// where all symbol display logic is encapsulated into visitors and options.
|
||||
var totalParts: SymbolDisplayPart[] = [];
|
||||
|
||||
if (symbol.flags & SymbolFlags.Class) {
|
||||
totalParts.push(new SymbolDisplayPart("class", SymbolDisplayPartKind.keyword, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(keywordPart(SyntaxKind.ClassKeyword));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile));
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.Interface) {
|
||||
totalParts.push(new SymbolDisplayPart("interface", SymbolDisplayPartKind.keyword, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(keywordPart(SyntaxKind.InterfaceKeyword));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile));
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.Enum) {
|
||||
totalParts.push(new SymbolDisplayPart("enum", SymbolDisplayPartKind.keyword, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(keywordPart(SyntaxKind.EnumKeyword));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile));
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.Module) {
|
||||
totalParts.push(new SymbolDisplayPart("module", SymbolDisplayPartKind.keyword, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(keywordPart(SyntaxKind.ModuleKeyword));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile));
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.TypeParameter) {
|
||||
totalParts.push(new SymbolDisplayPart("(", SymbolDisplayPartKind.punctuation, undefined));
|
||||
totalParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
totalParts.push(new SymbolDisplayPart("type parameter", SymbolDisplayPartKind.text, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(")", SymbolDisplayPartKind.punctuation, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol));
|
||||
}
|
||||
else {
|
||||
totalParts.push(new SymbolDisplayPart("(", SymbolDisplayPartKind.punctuation, undefined));
|
||||
totalParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
var text: string;
|
||||
|
||||
if (symbol.flags & SymbolFlags.Property) { text = "property" }
|
||||
@@ -2421,8 +2425,8 @@ module ts {
|
||||
}
|
||||
|
||||
totalParts.push(new SymbolDisplayPart(text, SymbolDisplayPartKind.text, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(")", SymbolDisplayPartKind.punctuation, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
totalParts.push(spacePart());
|
||||
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node)));
|
||||
|
||||
@@ -2432,8 +2436,8 @@ module ts {
|
||||
symbol.flags & SymbolFlags.Variable) {
|
||||
|
||||
if (type) {
|
||||
totalParts.push(new SymbolDisplayPart(":", SymbolDisplayPartKind.punctuation, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(punctuationPart(SyntaxKind.ColonToken));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type, getContainerNode(node)));
|
||||
}
|
||||
}
|
||||
@@ -2448,9 +2452,9 @@ module ts {
|
||||
if (declaration.kind === SyntaxKind.EnumMember) {
|
||||
var constantValue = typeInfoResolver.getEnumMemberValue(<EnumMember>declaration);
|
||||
if (constantValue !== undefined) {
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(new SymbolDisplayPart("=", SymbolDisplayPartKind.operator, undefined));
|
||||
totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
totalParts.push(spacePart());
|
||||
totalParts.push(new SymbolDisplayPart(constantValue.toString(), SymbolDisplayPartKind.numericLiteral, undefined));
|
||||
}
|
||||
}
|
||||
@@ -3782,7 +3786,7 @@ module ts {
|
||||
|
||||
var formalSignatures: FormalSignatureItemInfo[] = [];
|
||||
forEach(signatureHelpItems.items, signature => {
|
||||
var signatureInfoString = signature.prefix;
|
||||
var signatureInfoString = ts.SymbolDisplayPart.toString(signature.prefixDisplayParts);
|
||||
|
||||
var parameters: FormalParameterInfo[] = [];
|
||||
if (signature.parameters) {
|
||||
@@ -3791,32 +3795,31 @@ module ts {
|
||||
|
||||
// add the parameter to the string
|
||||
if (i) {
|
||||
signatureInfoString += signature.separator;
|
||||
signatureInfoString += ts.SymbolDisplayPart.toString(signature.separatorDisplayParts);
|
||||
}
|
||||
|
||||
var start = signatureInfoString.length;
|
||||
signatureInfoString += parameter.display;
|
||||
signatureInfoString += ts.SymbolDisplayPart.toString(parameter.displayParts);
|
||||
var end = signatureInfoString.length - 1;
|
||||
|
||||
// add the parameter to the list
|
||||
parameters.push({
|
||||
name: parameter.name,
|
||||
isVariable: i == n - 1 && signature.isVariadic,
|
||||
docComment: parameter.documentation,
|
||||
docComment: ts.SymbolDisplayPart.toString(parameter.documentation),
|
||||
minChar: start,
|
||||
limChar: end
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
signatureInfoString += signature.suffix;
|
||||
signatureInfoString += ts.SymbolDisplayPart.toString(signature.suffixDisplayParts);
|
||||
|
||||
formalSignatures.push({
|
||||
signatureInfo: signatureInfoString,
|
||||
docComment: signature.documentation,
|
||||
docComment: ts.SymbolDisplayPart.toString(signature.documentation),
|
||||
parameters: parameters,
|
||||
typeParameters: [],
|
||||
docComments: signature.documentation
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -249,39 +249,78 @@ module ts.SignatureHelp {
|
||||
}
|
||||
|
||||
function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListOrTypeArgumentList: Node): SignatureHelpItems {
|
||||
var items = map(candidates, candidateSignature => {
|
||||
var items: SignatureHelpItem[] = map(candidates, candidateSignature => {
|
||||
var parameters = candidateSignature.parameters;
|
||||
var parameterHelpItems = parameters.length === 0 ? emptyArray : map(parameters, p => {
|
||||
var display = p.name;
|
||||
var parameterHelpItems: SignatureHelpParameter[] = parameters.length === 0 ? emptyArray : map(parameters, p => {
|
||||
var displayParts: SymbolDisplayPart[] = [];
|
||||
|
||||
if (candidateSignature.hasRestParameter && parameters[parameters.length - 1] === p) {
|
||||
display = "..." + display;
|
||||
displayParts.push(punctuationPart(SyntaxKind.DotDotDotToken));
|
||||
}
|
||||
|
||||
displayParts.push(symbolPart(p.name, p));
|
||||
|
||||
var isOptional = !!(p.valueDeclaration.flags & NodeFlags.QuestionMark);
|
||||
if (isOptional) {
|
||||
display += "?";
|
||||
displayParts.push(punctuationPart(SyntaxKind.QuestionToken));
|
||||
}
|
||||
display += ": " + typeInfoResolver.typeToString(typeInfoResolver.getTypeOfSymbol(p), argumentListOrTypeArgumentList);
|
||||
return { name: p.name, documentation: "", display: display, isOptiona: isOptional };
|
||||
|
||||
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
|
||||
displayParts.push(spacePart());
|
||||
|
||||
var typeParts = typeInfoResolver.typeToDisplayParts(typeInfoResolver.getTypeOfSymbol(p), argumentListOrTypeArgumentList);
|
||||
displayParts.push.apply(displayParts, typeParts);
|
||||
|
||||
return {
|
||||
name: p.name,
|
||||
documentation: getSymbolDocumentationDisplayParts(p),
|
||||
displayParts: displayParts,
|
||||
isOptional: isOptional
|
||||
};
|
||||
});
|
||||
|
||||
var callTargetNode = (<CallExpression>argumentListOrTypeArgumentList.parent).func;
|
||||
var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTargetNode);
|
||||
var signatureName = callTargetSymbol ? typeInfoResolver.symbolToString(callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : "";
|
||||
var prefix = signatureName;
|
||||
|
||||
var prefixParts = callTargetSymbol ? typeInfoResolver.symbolToDisplayParts(callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : [];
|
||||
|
||||
var separatorParts = [punctuationPart(SyntaxKind.CommaToken), spacePart()];
|
||||
|
||||
// TODO(jfreeman): Constraints?
|
||||
if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) {
|
||||
prefix += "<" + map(candidateSignature.typeParameters, tp => tp.symbol.name).join(", ") + ">";
|
||||
prefixParts.push(punctuationPart(SyntaxKind.LessThanToken));
|
||||
|
||||
for (var i = 0, n = candidateSignature.typeParameters.length; i < n; i++) {
|
||||
if (i) {
|
||||
prefixParts.push.apply(prefixParts, separatorParts);
|
||||
}
|
||||
|
||||
var tp = candidateSignature.typeParameters[i].symbol;
|
||||
prefixParts.push(symbolPart(tp.name, tp));
|
||||
}
|
||||
|
||||
prefixParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
|
||||
}
|
||||
prefix += "(";
|
||||
var suffix = "): " + typeInfoResolver.typeToString(candidateSignature.getReturnType(), argumentListOrTypeArgumentList);
|
||||
|
||||
prefixParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
|
||||
var suffixParts = [punctuationPart(SyntaxKind.CloseParenToken)];
|
||||
suffixParts.push(punctuationPart(SyntaxKind.ColonToken));
|
||||
suffixParts.push(spacePart());
|
||||
|
||||
var typeParts = typeInfoResolver.typeToDisplayParts(candidateSignature.getReturnType(), argumentListOrTypeArgumentList);
|
||||
suffixParts.push.apply(suffixParts, typeParts);
|
||||
|
||||
return {
|
||||
isVariadic: candidateSignature.hasRestParameter,
|
||||
prefix: prefix,
|
||||
suffix: suffix,
|
||||
separator: ", ",
|
||||
prefixDisplayParts: prefixParts,
|
||||
suffixDisplayParts: suffixParts,
|
||||
separatorDisplayParts: separatorParts,
|
||||
parameters: parameterHelpItems,
|
||||
documentation: ""
|
||||
documentation: <SymbolDisplayPart[]>null
|
||||
};
|
||||
});
|
||||
|
||||
var selectedItemIndex = candidates.indexOf(bestSignature);
|
||||
if (selectedItemIndex < 0) {
|
||||
selectedItemIndex = 0;
|
||||
|
||||
Reference in New Issue
Block a user