From 4781224d86ef9f534d6fe65e427f638c51401565 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 30 Sep 2014 16:53:10 -0700 Subject: [PATCH] Format typeparameters information --- src/compiler/checker.ts | 41 ++++++---- src/compiler/types.ts | 8 +- src/services/services.ts | 76 +++++++++++++------ .../fourslash/completionListOfGnericSymbol.ts | 5 +- .../genericTypeWithMultipleBases1MultiFile.ts | 4 +- .../quickInfoForGenericConstraints1.ts | 4 +- tests/cases/fourslash/quickInfoGenerics.ts | 48 ++++++++++++ .../quickInfoOnGenericClass.ts | 2 +- .../quickInfoOnGenericWithConstraints1.ts | 9 +++ ...oOnMergedInterfacesWithIncrementalEdits.ts | 12 +-- .../cases/fourslash_old/quickInfoGenerics.ts | 31 -------- .../quickInfoOnGenericWithConstraints1.ts | 9 --- 12 files changed, 156 insertions(+), 93 deletions(-) create mode 100644 tests/cases/fourslash/quickInfoGenerics.ts rename tests/cases/{fourslash_old => fourslash}/quickInfoOnGenericClass.ts (61%) create mode 100644 tests/cases/fourslash/quickInfoOnGenericWithConstraints1.ts rename tests/cases/{fourslash_old => fourslash}/quickInfoOnMergedInterfacesWithIncrementalEdits.ts (60%) delete mode 100644 tests/cases/fourslash_old/quickInfoGenerics.ts delete mode 100644 tests/cases/fourslash_old/quickInfoOnGenericWithConstraints1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6e4d6be7f6d..8ffaa01033d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -102,6 +102,7 @@ module ts { isValidPropertyAccess: isValidPropertyAccess, getSignatureFromDeclaration: getSignatureFromDeclaration, writeSignature: writeSignature, + writeTypeParameter: writeTypeParameter, isImplementationOfOverload: isImplementationOfOverload }; @@ -964,12 +965,19 @@ module ts { // Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope // Meaning needs to be specified if the enclosing declaration is given - function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags): void { + function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void { function writeSymbolName(symbol: Symbol): void { if (symbol.declarations && symbol.declarations.length > 0) { var declaration = symbol.declarations[0]; if (declaration.name) { writer.writeSymbol(identifierToString(declaration.name), symbol); + if (flags & SymbolFormatFlags.WriteTypeParametersOfClassOrInterface) { + var rootSymbol = getRootSymbol(symbol); + if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) { + writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaration); + } + } + return; } } @@ -1268,26 +1276,33 @@ module ts { } } - function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) { - if (signature.typeParameters) { + function writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) { + writeSymbol(tp.symbol, writer); + var constraint = getConstraintOfTypeParameter(tp); + if (constraint) { + writeSpace(writer); + writeKeyword(writer, SyntaxKind.ExtendsKeyword); + writeSpace(writer); + writeType(constraint, writer, enclosingDeclaration, flags, typeStack); + } + } + + function writeTypeParameters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) { + if (typeParameters) { writePunctuation(writer, SyntaxKind.LessThanToken); - for (var i = 0; i < signature.typeParameters.length; i++) { + for (var i = 0; i < typeParameters.length; i++) { if (i > 0) { writePunctuation(writer, SyntaxKind.CommaToken); writeSpace(writer); } - var tp = signature.typeParameters[i]; - writeSymbol(tp.symbol, writer); - var constraint = getConstraintOfTypeParameter(tp); - if (constraint) { - writeSpace(writer); - writeKeyword(writer, SyntaxKind.ExtendsKeyword); - writeSpace(writer); - writeType(constraint, writer, enclosingDeclaration, flags, typeStack); - } + writeTypeParameter(typeParameters[i], writer, enclosingDeclaration, flags, typeStack); } writePunctuation(writer, SyntaxKind.GreaterThanToken); } + } + + function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) { + writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack); writePunctuation(writer, SyntaxKind.OpenParenToken); for (var i = 0; i < signature.parameters.length; i++) { if (i > 0) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 78147c0d7e2..4bbedf12e64 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -645,7 +645,7 @@ module ts { typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; writeType(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; - writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; + writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void; getFullyQualifiedName(symbol: Symbol): string; getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; getRootSymbol(symbol: Symbol): Symbol; @@ -653,6 +653,7 @@ module ts { getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature; getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature; writeSignature(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; + writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; isImplementationOfOverload(node: FunctionDeclaration): boolean; // Returns the constant value of this enum member, or 'undefined' if the enum member has a @@ -684,6 +685,11 @@ module ts { WriteArrowStyleSignature= 0x00000008, // Write arrow style signature } + export enum SymbolFormatFlags { + None = 0x00000000, + WriteTypeParametersOfClassOrInterface = 0x00000001, // Write c instead of just writing symbol name c of generic class + } + export enum SymbolAccessibility { Accessible, NotAccessible, diff --git a/src/services/services.ts b/src/services/services.ts index 537bd795003..f5c3998c738 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1405,29 +1405,30 @@ module ts { } } + function mapToDisplayParts(writeDisplayParts: (writer: DisplayPartsSymbolWriter) => void): SymbolDisplayPart[] { + var displayPartWriter = getDisplayPartWriter(); + writeDisplayParts(displayPartWriter); + var result = displayPartWriter.displayParts(); + releaseDisplayPartWriter(displayPartWriter); + return result; + } + export function typeToDisplayParts(typechecker: TypeChecker, type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] { - var displayPartWriter = getDisplayPartWriter(); - typechecker.writeType(type, displayPartWriter, enclosingDeclaration, flags); - var result = displayPartWriter.displayParts(); - releaseDisplayPartWriter(displayPartWriter); - return result; + return mapToDisplayParts(writer => { + typechecker.writeType(type, writer, enclosingDeclaration, flags); + }); } - export function symbolToDisplayParts(typeChecker: TypeChecker, symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[] { - var displayPartWriter = getDisplayPartWriter(); - typeChecker.writeSymbol(symbol, displayPartWriter, enclosingDeclaration, meaning); - var result = displayPartWriter.displayParts(); - releaseDisplayPartWriter(displayPartWriter); - - return result; + export function symbolToDisplayParts(typeChecker: TypeChecker, symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): SymbolDisplayPart[] { + return mapToDisplayParts(writer => { + typeChecker.writeSymbol(symbol, writer, enclosingDeclaration, meaning, flags); + }); } - function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] { - var displayPartWriter = getDisplayPartWriter(); - typechecker.writeSignature(signature, displayPartWriter, enclosingDeclaration, flags); - var result = displayPartWriter.displayParts(); - releaseDisplayPartWriter(displayPartWriter); - return result; + function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[]{ + return mapToDisplayParts(writer => { + typechecker.writeSignature(signature, writer, enclosingDeclaration, flags); + }); } export function getDefaultCompilerOptions(): CompilerOptions { @@ -2792,13 +2793,13 @@ module ts { if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) { displayParts.push(keywordPart(SyntaxKind.ClassKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile)); + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)); } if (symbolFlags & SymbolFlags.Interface) { addNewLineIfDisplayPartsExist(); displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile)); + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)); } if (symbolFlags & SymbolFlags.Enum) { addNewLineIfDisplayPartsExist(); @@ -2819,6 +2820,25 @@ module ts { displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); displayParts.push(spacePart()); displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, enclosingDeclaration)); + displayParts.push(spacePart()); + displayParts.push(keywordPart(SyntaxKind.InKeyword)); + displayParts.push(spacePart()); + if (symbol.parent) { + // Class/Interface type parameter + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)) + } + else { + // Method/function type parameter + var signatureDeclaration = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter).parent; + var signature = typeResolver.getSignatureFromDeclaration(signatureDeclaration); + if (signatureDeclaration.kind === SyntaxKind.ConstructSignature) { + displayParts.push(keywordPart(SyntaxKind.NewKeyword)); + displayParts.push(spacePart()); + } else if (signatureDeclaration.kind !== SyntaxKind.CallSignature) { + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)) + } + displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation)); + } } if (symbolFlags & SymbolFlags.EnumMember) { addPrefixForAnyFunctionOrVar(symbol, "enum member"); @@ -2848,7 +2868,16 @@ module ts { symbolFlags & SymbolFlags.Variable) { displayParts.push(punctuationPart(SyntaxKind.ColonToken)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, typeToDisplayParts(typeResolver, type, enclosingDeclaration, TypeFormatFlags.NoTruncation)); + // If the type is type parameter, format it specially + if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter) { + var typeParameterParts = mapToDisplayParts(writer => { + typeResolver.writeTypeParameter(type, writer, enclosingDeclaration, TypeFormatFlags.NoTruncation); + }); + displayParts.push.apply(displayParts, typeParameterParts); + } + else { + displayParts.push.apply(displayParts, typeToDisplayParts(typeResolver, type, enclosingDeclaration, TypeFormatFlags.NoTruncation)); + } } else if (symbolFlags & SymbolFlags.Function || symbolFlags & SymbolFlags.Method || @@ -2880,9 +2909,8 @@ module ts { displayParts.push(textPart(symbolKind)); displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); displayParts.push(spacePart()); - //if (symbol.declarations && symbol.declarations.length) { - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile)); - //} + // Write type parameters of class/Interface if it is property/method of the generic class/interface + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)); } } diff --git a/tests/cases/fourslash/completionListOfGnericSymbol.ts b/tests/cases/fourslash/completionListOfGnericSymbol.ts index 480ad76694c..95e72db58c9 100644 --- a/tests/cases/fourslash/completionListOfGnericSymbol.ts +++ b/tests/cases/fourslash/completionListOfGnericSymbol.ts @@ -6,7 +6,6 @@ ////a./**/ goTo.marker(); -// TODO. show as Array or Array.length instead -verify.memberListContains('length', "(property) Array.length: number", /*docComments*/ undefined, /*kind*/ "property"); -verify.memberListContains('toString', "(method) Array.toString(): string", /*docComments*/ undefined, /*kind*/ "method"); +verify.memberListContains('length', "(property) Array.length: number", /*docComments*/ undefined, /*kind*/ "property"); +verify.memberListContains('toString', "(method) Array.toString(): string", /*docComments*/ undefined, /*kind*/ "method"); diff --git a/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts b/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts index 735d5458daf..77390ea2dd9 100644 --- a/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts +++ b/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts @@ -20,5 +20,5 @@ goTo.marker(); verify.completionListContains('watch', '(property) iBaseScope.watch: () => void'); verify.completionListContains('moveUp', '(property) iMover.moveUp: () => void'); -//verify.completionListContains('family', '(property) iScope.family: number'); -// TODO \ No newline at end of file +debugger; +verify.completionListContains('family', '(property) iScope.family: number'); diff --git a/tests/cases/fourslash/quickInfoForGenericConstraints1.ts b/tests/cases/fourslash/quickInfoForGenericConstraints1.ts index 93449cd7dc2..86571f2be7c 100644 --- a/tests/cases/fourslash/quickInfoForGenericConstraints1.ts +++ b/tests/cases/fourslash/quickInfoForGenericConstraints1.ts @@ -4,6 +4,4 @@ ////function foo4(test: any): any { return null; } goTo.marker(); -// TODO: formatting type parameter with extends info -//verify.quickInfoIs('parameter) test: T extends Date'); -verify.quickInfoIs('(parameter) test: T'); \ No newline at end of file +verify.quickInfoIs('(parameter) test: T extends Date'); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoGenerics.ts b/tests/cases/fourslash/quickInfoGenerics.ts new file mode 100644 index 00000000000..590019bfaca --- /dev/null +++ b/tests/cases/fourslash/quickInfoGenerics.ts @@ -0,0 +1,48 @@ +/// + +////class Con/*1*/tainer { +//// x: T; +////} +////interface IList { +//// getItem(i: number): /*3*/T; +////} +////class List> implements IList { +//// private __it/*6*/em: /*5*/T[]; +//// public get/*7*/Item(i: number) { +//// return this.__item[i]; +//// } +//// public /*8*/method>(s: S, p: /*10*/T[]) { +//// return s; +//// } +////} +////function foo4(test: T): T; +////function foo4(test: S): S; +////function foo4(test: any): any; +////function foo4(test: any): any { return null; } + +goTo.marker("1"); +verify.quickInfoIs("class Container", undefined); +goTo.marker("2"); +verify.quickInfoIs("(type parameter) T in IList", undefined); +goTo.marker("3"); +verify.quickInfoIs("(type parameter) T in IList", undefined); +goTo.marker("4"); +verify.quickInfoIs("(type parameter) T in List>", undefined); +goTo.marker("5"); +verify.quickInfoIs("(type parameter) T in List>", undefined); +goTo.marker("6"); +verify.quickInfoIs("(property) List>.__item: T[]", undefined); +goTo.marker("7"); +verify.quickInfoIs("(method) List>.getItem(i: number): T", undefined); +goTo.marker("8"); +verify.quickInfoIs("(method) List>.method>(s: S, p: T[]): S", undefined); +goTo.marker("9"); +verify.quickInfoIs("(type parameter) S in List>.method>(s: S, p: T[]): S", undefined); +goTo.marker("10"); +verify.quickInfoIs("(type parameter) T in List>", undefined); +goTo.marker("11"); +verify.quickInfoIs("(type parameter) T in foo4(test: T): T", undefined); +goTo.marker("12"); +verify.quickInfoIs("(type parameter) S in foo4(test: S): S", undefined); +goTo.marker("13"); +verify.quickInfoIs("(type parameter) T in foo4(test: any): any", undefined); \ No newline at end of file diff --git a/tests/cases/fourslash_old/quickInfoOnGenericClass.ts b/tests/cases/fourslash/quickInfoOnGenericClass.ts similarity index 61% rename from tests/cases/fourslash_old/quickInfoOnGenericClass.ts rename to tests/cases/fourslash/quickInfoOnGenericClass.ts index c3eab77354d..d05a509973c 100644 --- a/tests/cases/fourslash_old/quickInfoOnGenericClass.ts +++ b/tests/cases/fourslash/quickInfoOnGenericClass.ts @@ -5,4 +5,4 @@ ////} goTo.marker(); -verify.quickInfoIs('Container', null, 'Container'); \ No newline at end of file +verify.quickInfoIs('class Container', null); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoOnGenericWithConstraints1.ts b/tests/cases/fourslash/quickInfoOnGenericWithConstraints1.ts new file mode 100644 index 00000000000..2f4fe31fdcf --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnGenericWithConstraints1.ts @@ -0,0 +1,9 @@ +/// + +////interface Fo/*1*/o {} + +goTo.marker('1'); +verify.quickInfoIs('interface Foo', null); + +goTo.marker('2'); +verify.quickInfoIs('(type parameter) TT in Foo', null); diff --git a/tests/cases/fourslash_old/quickInfoOnMergedInterfacesWithIncrementalEdits.ts b/tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts similarity index 60% rename from tests/cases/fourslash_old/quickInfoOnMergedInterfacesWithIncrementalEdits.ts rename to tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts index 684604e6c41..f5ab3706750 100644 --- a/tests/cases/fourslash_old/quickInfoOnMergedInterfacesWithIncrementalEdits.ts +++ b/tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts @@ -9,20 +9,20 @@ //// } //// var b: B; //// var r3 = b.foo; // number -//// var r/*2*/4 = b.ba/*1*/r; // string +//// var r/*2*/4 = b.b/*1*/ar; // string ////} diagnostics.setEditValidation(IncrementalEditValidation.None); goTo.marker('1'); -verify.quickInfoIs("string", undefined, "MM.B.bar", "property"); +verify.quickInfoIs("(property) B.bar: string", undefined); edit.deleteAtCaret(1); edit.insert('z'); -verify.quickInfoIs("any"); +verify.not.quickInfoExists(); verify.numberOfErrorsInCurrentFile(1); edit.backspace(1); -edit.insert('r'); -verify.quickInfoIs("string", undefined, "MM.B.bar", "property"); +edit.insert('a'); +verify.quickInfoIs("(property) B.bar: string", undefined); goTo.marker('2'); -verify.quickInfoIs("string", undefined, "r4", "var"); +verify.quickInfoIs("(var) r4: string", undefined); verify.numberOfErrorsInCurrentFile(0); diff --git a/tests/cases/fourslash_old/quickInfoGenerics.ts b/tests/cases/fourslash_old/quickInfoGenerics.ts deleted file mode 100644 index 6fe75424149..00000000000 --- a/tests/cases/fourslash_old/quickInfoGenerics.ts +++ /dev/null @@ -1,31 +0,0 @@ -/// - -////class Con/*1*/tainer { -//// x: T; -////} -////interface IList { -//// getItem(i: number): /*3*/T; -////} -////class List> implements IList { -//// private __it/*6*/em: /*5*/T[]; -//// public get/*7*/Item(i: number) { -//// return this.__item[i]; -//// } -////} - -goTo.marker("1"); -verify.quickInfoIs(undefined, undefined, "Container", "class"); -goTo.marker("2"); -verify.quickInfoIs(undefined, undefined, "T in IList", "type parameter"); -goTo.marker("3"); -verify.quickInfoIs(undefined, undefined, "T in IList", "type parameter"); -goTo.marker("4"); -verify.quickInfoIs(undefined, undefined, "T in List>", "type parameter"); -goTo.marker("5"); -verify.quickInfoIs(undefined, undefined, "T in List>", "type parameter"); -goTo.marker("6"); -verify.quickInfoIs(undefined, undefined, "List>.__item", "property"); -goTo.marker("7"); -verify.quickInfoIs(undefined, undefined, "List>.getItem", "method"); - - diff --git a/tests/cases/fourslash_old/quickInfoOnGenericWithConstraints1.ts b/tests/cases/fourslash_old/quickInfoOnGenericWithConstraints1.ts deleted file mode 100644 index 83deedf186b..00000000000 --- a/tests/cases/fourslash_old/quickInfoOnGenericWithConstraints1.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// - -////interface Fo/*1*/o {} - -goTo.marker('1'); -verify.quickInfoIs('Foo', null, 'Foo') - -goTo.marker('2'); -verify.quickInfoIs('TT extends Date', null, 'TT in Foo')