Skip to content

Commit 4781224

Browse files
committed
Format typeparameters information
1 parent 010b9b6 commit 4781224

12 files changed

+152
-89
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ module ts {
102102
isValidPropertyAccess: isValidPropertyAccess,
103103
getSignatureFromDeclaration: getSignatureFromDeclaration,
104104
writeSignature: writeSignature,
105+
writeTypeParameter: writeTypeParameter,
105106
isImplementationOfOverload: isImplementationOfOverload
106107
};
107108

@@ -964,12 +965,19 @@ module ts {
964965

965966
// Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope
966967
// Meaning needs to be specified if the enclosing declaration is given
967-
function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags): void {
968+
function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void {
968969
function writeSymbolName(symbol: Symbol): void {
969970
if (symbol.declarations && symbol.declarations.length > 0) {
970971
var declaration = symbol.declarations[0];
971972
if (declaration.name) {
972973
writer.writeSymbol(identifierToString(declaration.name), symbol);
974+
if (flags & SymbolFormatFlags.WriteTypeParametersOfClassOrInterface) {
975+
var rootSymbol = getRootSymbol(symbol);
976+
if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) {
977+
writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaration);
978+
}
979+
}
980+
973981
return;
974982
}
975983
}
@@ -1268,26 +1276,33 @@ module ts {
12681276
}
12691277
}
12701278

1271-
function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1272-
if (signature.typeParameters) {
1279+
function writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1280+
writeSymbol(tp.symbol, writer);
1281+
var constraint = getConstraintOfTypeParameter(tp);
1282+
if (constraint) {
1283+
writeSpace(writer);
1284+
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
1285+
writeSpace(writer);
1286+
writeType(constraint, writer, enclosingDeclaration, flags, typeStack);
1287+
}
1288+
}
1289+
1290+
function writeTypeParameters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1291+
if (typeParameters) {
12731292
writePunctuation(writer, SyntaxKind.LessThanToken);
1274-
for (var i = 0; i < signature.typeParameters.length; i++) {
1293+
for (var i = 0; i < typeParameters.length; i++) {
12751294
if (i > 0) {
12761295
writePunctuation(writer, SyntaxKind.CommaToken);
12771296
writeSpace(writer);
12781297
}
1279-
var tp = signature.typeParameters[i];
1280-
writeSymbol(tp.symbol, writer);
1281-
var constraint = getConstraintOfTypeParameter(tp);
1282-
if (constraint) {
1283-
writeSpace(writer);
1284-
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
1285-
writeSpace(writer);
1286-
writeType(constraint, writer, enclosingDeclaration, flags, typeStack);
1287-
}
1298+
writeTypeParameter(typeParameters[i], writer, enclosingDeclaration, flags, typeStack);
12881299
}
12891300
writePunctuation(writer, SyntaxKind.GreaterThanToken);
12901301
}
1302+
}
1303+
1304+
function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
1305+
writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack);
12911306
writePunctuation(writer, SyntaxKind.OpenParenToken);
12921307
for (var i = 0; i < signature.parameters.length; i++) {
12931308
if (i > 0) {

src/compiler/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,14 +645,15 @@ module ts {
645645
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
646646
writeType(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
647647
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
648-
writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
648+
writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void;
649649
getFullyQualifiedName(symbol: Symbol): string;
650650
getAugmentedPropertiesOfApparentType(type: Type): Symbol[];
651651
getRootSymbol(symbol: Symbol): Symbol;
652652
getContextualType(node: Node): Type;
653653
getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature;
654654
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature;
655655
writeSignature(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
656+
writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
656657
isImplementationOfOverload(node: FunctionDeclaration): boolean;
657658

658659
// Returns the constant value of this enum member, or 'undefined' if the enum member has a
@@ -684,6 +685,11 @@ module ts {
684685
WriteArrowStyleSignature= 0x00000008, // Write arrow style signature
685686
}
686687

688+
export enum SymbolFormatFlags {
689+
None = 0x00000000,
690+
WriteTypeParametersOfClassOrInterface = 0x00000001, // Write c<T> instead of just writing symbol name c of generic class
691+
}
692+
687693
export enum SymbolAccessibility {
688694
Accessible,
689695
NotAccessible,

src/services/services.ts

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,29 +1405,30 @@ module ts {
14051405
}
14061406
}
14071407

1408-
export function typeToDisplayParts(typechecker: TypeChecker, type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] {
1408+
function mapToDisplayParts(writeDisplayParts: (writer: DisplayPartsSymbolWriter) => void): SymbolDisplayPart[] {
14091409
var displayPartWriter = getDisplayPartWriter();
1410-
typechecker.writeType(type, displayPartWriter, enclosingDeclaration, flags);
1410+
writeDisplayParts(displayPartWriter);
14111411
var result = displayPartWriter.displayParts();
14121412
releaseDisplayPartWriter(displayPartWriter);
14131413
return result;
14141414
}
14151415

1416-
export function symbolToDisplayParts(typeChecker: TypeChecker, symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[] {
1417-
var displayPartWriter = getDisplayPartWriter();
1418-
typeChecker.writeSymbol(symbol, displayPartWriter, enclosingDeclaration, meaning);
1419-
var result = displayPartWriter.displayParts();
1420-
releaseDisplayPartWriter(displayPartWriter);
1416+
export function typeToDisplayParts(typechecker: TypeChecker, type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] {
1417+
return mapToDisplayParts(writer => {
1418+
typechecker.writeType(type, writer, enclosingDeclaration, flags);
1419+
});
1420+
}
14211421

1422-
return result;
1422+
export function symbolToDisplayParts(typeChecker: TypeChecker, symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): SymbolDisplayPart[] {
1423+
return mapToDisplayParts(writer => {
1424+
typeChecker.writeSymbol(symbol, writer, enclosingDeclaration, meaning, flags);
1425+
});
14231426
}
14241427

1425-
function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] {
1426-
var displayPartWriter = getDisplayPartWriter();
1427-
typechecker.writeSignature(signature, displayPartWriter, enclosingDeclaration, flags);
1428-
var result = displayPartWriter.displayParts();
1429-
releaseDisplayPartWriter(displayPartWriter);
1430-
return result;
1428+
function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[]{
1429+
return mapToDisplayParts(writer => {
1430+
typechecker.writeSignature(signature, writer, enclosingDeclaration, flags);
1431+
});
14311432
}
14321433

14331434
export function getDefaultCompilerOptions(): CompilerOptions {
@@ -2792,13 +2793,13 @@ module ts {
27922793
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) {
27932794
displayParts.push(keywordPart(SyntaxKind.ClassKeyword));
27942795
displayParts.push(spacePart());
2795-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile));
2796+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
27962797
}
27972798
if (symbolFlags & SymbolFlags.Interface) {
27982799
addNewLineIfDisplayPartsExist();
27992800
displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword));
28002801
displayParts.push(spacePart());
2801-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile));
2802+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
28022803
}
28032804
if (symbolFlags & SymbolFlags.Enum) {
28042805
addNewLineIfDisplayPartsExist();
@@ -2819,6 +2820,25 @@ module ts {
28192820
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
28202821
displayParts.push(spacePart());
28212822
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, enclosingDeclaration));
2823+
displayParts.push(spacePart());
2824+
displayParts.push(keywordPart(SyntaxKind.InKeyword));
2825+
displayParts.push(spacePart());
2826+
if (symbol.parent) {
2827+
// Class/Interface type parameter
2828+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface))
2829+
}
2830+
else {
2831+
// Method/function type parameter
2832+
var signatureDeclaration = <SignatureDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter).parent;
2833+
var signature = typeResolver.getSignatureFromDeclaration(signatureDeclaration);
2834+
if (signatureDeclaration.kind === SyntaxKind.ConstructSignature) {
2835+
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
2836+
displayParts.push(spacePart());
2837+
} else if (signatureDeclaration.kind !== SyntaxKind.CallSignature) {
2838+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface))
2839+
}
2840+
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation));
2841+
}
28222842
}
28232843
if (symbolFlags & SymbolFlags.EnumMember) {
28242844
addPrefixForAnyFunctionOrVar(symbol, "enum member");
@@ -2848,7 +2868,16 @@ module ts {
28482868
symbolFlags & SymbolFlags.Variable) {
28492869
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
28502870
displayParts.push(spacePart());
2851-
displayParts.push.apply(displayParts, typeToDisplayParts(typeResolver, type, enclosingDeclaration, TypeFormatFlags.NoTruncation));
2871+
// If the type is type parameter, format it specially
2872+
if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter) {
2873+
var typeParameterParts = mapToDisplayParts(writer => {
2874+
typeResolver.writeTypeParameter(<TypeParameter>type, writer, enclosingDeclaration, TypeFormatFlags.NoTruncation);
2875+
});
2876+
displayParts.push.apply(displayParts, typeParameterParts);
2877+
}
2878+
else {
2879+
displayParts.push.apply(displayParts, typeToDisplayParts(typeResolver, type, enclosingDeclaration, TypeFormatFlags.NoTruncation));
2880+
}
28522881
}
28532882
else if (symbolFlags & SymbolFlags.Function ||
28542883
symbolFlags & SymbolFlags.Method ||
@@ -2880,9 +2909,8 @@ module ts {
28802909
displayParts.push(textPart(symbolKind));
28812910
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
28822911
displayParts.push(spacePart());
2883-
//if (symbol.declarations && symbol.declarations.length) {
2884-
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile));
2885-
//}
2912+
// Write type parameters of class/Interface if it is property/method of the generic class/interface
2913+
displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface));
28862914
}
28872915
}
28882916

tests/cases/fourslash/completionListOfGnericSymbol.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
////a./**/
77

88
goTo.marker();
9-
// TODO. show as Array<number> or Array<T>.length instead
10-
verify.memberListContains('length', "(property) Array.length: number", /*docComments*/ undefined, /*kind*/ "property");
11-
verify.memberListContains('toString', "(method) Array.toString(): string", /*docComments*/ undefined, /*kind*/ "method");
9+
verify.memberListContains('length', "(property) Array<T>.length: number", /*docComments*/ undefined, /*kind*/ "property");
10+
verify.memberListContains('toString', "(method) Array<T>.toString(): string", /*docComments*/ undefined, /*kind*/ "method");
1211

tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@
2020
goTo.marker();
2121
verify.completionListContains('watch', '(property) iBaseScope.watch: () => void');
2222
verify.completionListContains('moveUp', '(property) iMover.moveUp: () => void');
23-
//verify.completionListContains('family', '(property) iScope<number>.family: number');
24-
// TODO
23+
debugger;
24+
verify.completionListContains('family', '(property) iScope<TModel>.family: number');

tests/cases/fourslash/quickInfoForGenericConstraints1.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,4 @@
44
////function foo4<T extends Date>(test: any): any { return null; }
55

66
goTo.marker();
7-
// TODO: formatting type parameter with extends info
8-
//verify.quickInfoIs('parameter) test: T extends Date');
9-
verify.quickInfoIs('(parameter) test: T');
7+
verify.quickInfoIs('(parameter) test: T extends Date');
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////class Con/*1*/tainer<T> {
4+
//// x: T;
5+
////}
6+
////interface IList</*2*/T> {
7+
//// getItem(i: number): /*3*/T;
8+
////}
9+
////class List</*4*/T extends IList<number>> implements IList<T> {
10+
//// private __it/*6*/em: /*5*/T[];
11+
//// public get/*7*/Item(i: number) {
12+
//// return this.__item[i];
13+
//// }
14+
//// public /*8*/method</*9*/S extends IList<T>>(s: S, p: /*10*/T[]) {
15+
//// return s;
16+
//// }
17+
////}
18+
////function foo4</*11*/T extends Date>(test: T): T;
19+
////function foo4</*12*/S extends string>(test: S): S;
20+
////function foo4(test: any): any;
21+
////function foo4</*13*/T extends Date>(test: any): any { return null; }
22+
23+
goTo.marker("1");
24+
verify.quickInfoIs("class Container<T>", undefined);
25+
goTo.marker("2");
26+
verify.quickInfoIs("(type parameter) T in IList<T>", undefined);
27+
goTo.marker("3");
28+
verify.quickInfoIs("(type parameter) T in IList<T>", undefined);
29+
goTo.marker("4");
30+
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
31+
goTo.marker("5");
32+
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
33+
goTo.marker("6");
34+
verify.quickInfoIs("(property) List<T extends IList<number>>.__item: T[]", undefined);
35+
goTo.marker("7");
36+
verify.quickInfoIs("(method) List<T extends IList<number>>.getItem(i: number): T", undefined);
37+
goTo.marker("8");
38+
verify.quickInfoIs("(method) List<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
39+
goTo.marker("9");
40+
verify.quickInfoIs("(type parameter) S in List<T extends IList<number>>.method<S extends IList<T>>(s: S, p: T[]): S", undefined);
41+
goTo.marker("10");
42+
verify.quickInfoIs("(type parameter) T in List<T extends IList<number>>", undefined);
43+
goTo.marker("11");
44+
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: T): T", undefined);
45+
goTo.marker("12");
46+
verify.quickInfoIs("(type parameter) S in foo4<S extends string>(test: S): S", undefined);
47+
goTo.marker("13");
48+
verify.quickInfoIs("(type parameter) T in foo4<T extends Date>(test: any): any", undefined);

tests/cases/fourslash_old/quickInfoOnGenericClass.ts renamed to tests/cases/fourslash/quickInfoOnGenericClass.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
////}
66

77
goTo.marker();
8-
verify.quickInfoIs('Container<T>', null, 'Container<T>');
8+
verify.quickInfoIs('class Container<T>', null);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////interface Fo/*1*/o<T/*2*/T extends Date> {}
4+
5+
goTo.marker('1');
6+
verify.quickInfoIs('interface Foo<TT extends Date>', null);
7+
8+
goTo.marker('2');
9+
verify.quickInfoIs('(type parameter) TT in Foo<TT extends Date>', null);

tests/cases/fourslash_old/quickInfoOnMergedInterfacesWithIncrementalEdits.ts renamed to tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99
//// }
1010
//// var b: B<string>;
1111
//// var r3 = b.foo; // number
12-
//// var r/*2*/4 = b.ba/*1*/r; // string
12+
//// var r/*2*/4 = b.b/*1*/ar; // string
1313
////}
1414

1515
diagnostics.setEditValidation(IncrementalEditValidation.None);
1616

1717
goTo.marker('1');
18-
verify.quickInfoIs("string", undefined, "MM.B<T>.bar", "property");
18+
verify.quickInfoIs("(property) B<T>.bar: string", undefined);
1919
edit.deleteAtCaret(1);
2020
edit.insert('z');
21-
verify.quickInfoIs("any");
21+
verify.not.quickInfoExists();
2222
verify.numberOfErrorsInCurrentFile(1);
2323
edit.backspace(1);
24-
edit.insert('r');
25-
verify.quickInfoIs("string", undefined, "MM.B<T>.bar", "property");
24+
edit.insert('a');
25+
verify.quickInfoIs("(property) B<T>.bar: string", undefined);
2626
goTo.marker('2');
27-
verify.quickInfoIs("string", undefined, "r4", "var");
27+
verify.quickInfoIs("(var) r4: string", undefined);
2828
verify.numberOfErrorsInCurrentFile(0);

0 commit comments

Comments
 (0)