diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7ebdba0dcb6..fb4ded66e65 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -103,6 +103,7 @@ module ts { getSignatureFromDeclaration: getSignatureFromDeclaration, writeSignature: writeSignature, writeTypeParameter: writeTypeParameter, + writeTypeParametersOfSymbol: writeTypeParametersOfSymbol, isImplementationOfOverload: isImplementationOfOverload }; @@ -966,18 +967,26 @@ 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, flags?: SymbolFormatFlags): void { + var previouslyWrittenSymbol: Symbol; function writeSymbolName(symbol: Symbol): void { + if (previouslyWrittenSymbol) { + // Write type arguments of instantiated class/interface here + if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) { + if (symbol.flags & SymbolFlags.Instantiated) { + writeTypeArguments(getTypeParametersOfClassOrInterface(previouslyWrittenSymbol), + (symbol).mapper, writer, enclosingDeclaration); + } + else { + writeTypeParametersOfSymbol(previouslyWrittenSymbol, writer, enclosingDeclaration); + } + } + writePunctuation(writer, SyntaxKind.DotToken); + } + previouslyWrittenSymbol = symbol; 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; } } @@ -985,16 +994,14 @@ module ts { writer.writeSymbol(symbol.name, symbol); } - // Let the writer know we just wrote out a symbol. The declarationemitter writer uses - // this to determine if an import it has previously seen (and not writter out) needs + // Let the writer know we just wrote out a symbol. The declaration emitter writer uses + // this to determine if an import it has previously seen (and not writer out) needs // to be written to the file once the walk of the tree is complete. // // NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree - // up front (for example, during checking) could determien if we need to emit the imports + // up front (for example, during checking) could determine if we need to emit the imports // and we could then access that data during declaration emit. writer.trackSymbol(symbol, enclosingDeclaration, meaning); - - var needsDot = false; function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void { if (symbol) { var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning); @@ -1010,17 +1017,12 @@ module ts { if (accessibleSymbolChain) { for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) { - if (needsDot) { - writePunctuation(writer, SyntaxKind.DotToken); - } - writeSymbolName(accessibleSymbolChain[i]); - needsDot = true; } } else { // If we didn't find accessible symbol chain for this symbol, break if this is external module - if (!needsDot && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) { + if (!previouslyWrittenSymbol && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) { return; } @@ -1029,12 +1031,7 @@ module ts { return; } - if (needsDot) { - writePunctuation(writer, SyntaxKind.DotToken); - } - writeSymbolName(symbol); - needsDot = true; } } } @@ -1303,8 +1300,35 @@ module ts { } } + function writeTypeArguments(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) { + if (typeParameters && typeParameters.length) { + writePunctuation(writer, SyntaxKind.LessThanToken); + for (var i = 0; i < typeParameters.length; i++) { + if (i > 0) { + writePunctuation(writer, SyntaxKind.CommaToken); + writeSpace(writer); + } + writeType(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.WriteArrowStyleSignature); + } + writePunctuation(writer, SyntaxKind.GreaterThanToken); + } + } + + function writeTypeParametersOfSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags) { + var rootSymbol = getRootSymbol(symbol); + if (rootSymbol.flags & SymbolFlags.Class || rootSymbol.flags & SymbolFlags.Interface) { + writeTypeParameters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaraiton, flags); + } + } + function writeSignature(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) { - writeTypeParameters(signature.typeParameters, writer, enclosingDeclaration, flags, typeStack); + if (signature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature)) { + // Instantiated signature, write type arguments instead + writeTypeArguments(signature.target.typeParameters, signature.mapper, writer, enclosingDeclaration); + } + else { + 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 216d5dd2fe9..72825713427 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -654,6 +654,7 @@ module ts { getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature; writeSignature(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; writeTypeParameter(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; + writeTypeParametersOfSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: 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,11 +685,15 @@ module ts { NoTruncation = 0x00000004, // Don't truncate typeToString result WriteArrowStyleSignature= 0x00000008, // Write arrow style signature WriteOwnNameForAnyLike = 0x00000010, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc) + WriteTypeArgumentsOfSignature = 0x00000020, // Write the type arguments instead of type parameters of the signature } export enum SymbolFormatFlags { - None = 0x00000000, - WriteTypeParametersOfClassOrInterface = 0x00000001, // Write c instead of just writing symbol name c of generic class + None = 0x00000000, + WriteTypeParametersOrArguments = 0x00000001, // Write symbols's type argument if it is instantiated symbol + // eg. class C { p: T } <-- Show p as C.p here + // var a: C; + // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p } export enum SymbolAccessibility { diff --git a/src/services/services.ts b/src/services/services.ts index f948f2ec714..0316f736387 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2726,7 +2726,7 @@ module ts { var useConstructSignatures = callExpression.kind === SyntaxKind.NewExpression || callExpression.func.kind === SyntaxKind.SuperKeyword; var allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); - if (contains(allSignatures, signature)) { + if (contains(allSignatures, signature.target || signature)) { // Write it as method/function/constructor as: (constructor) a(....) if (symbolKind === ScriptElementKind.memberVariableElement || symbolFlags & SymbolFlags.Variable || symbolFlags & SymbolFlags.Class) { if (useConstructSignatures) { @@ -2793,13 +2793,15 @@ module ts { if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) { displayParts.push(keywordPart(SyntaxKind.ClassKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)); + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); + writeTypeParametersOfSymbol(symbol, sourceFile); } if (symbolFlags & SymbolFlags.Interface) { addNewLineIfDisplayPartsExist(); displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)); + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); + writeTypeParametersOfSymbol(symbol, sourceFile); } if (symbolFlags & SymbolFlags.Enum) { addNewLineIfDisplayPartsExist(); @@ -2825,7 +2827,8 @@ module ts { displayParts.push(spacePart()); if (symbol.parent) { // Class/Interface type parameter - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOfClassOrInterface)) + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol.parent, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)) + writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration); } else { // Method/function type parameter @@ -2835,9 +2838,9 @@ module ts { 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, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)) } - displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation)); + displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteTypeArgumentsOfSignature)); } } if (symbolFlags & SymbolFlags.EnumMember) { @@ -2910,12 +2913,12 @@ module ts { displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); displayParts.push(spacePart()); // 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)); + displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); } } function addSignatureDisplayParts(signature: Signature, allSignatures: Signature[]) { - displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, TypeFormatFlags.NoTruncation)); + displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteTypeArgumentsOfSignature)); if (allSignatures.length > 1) { displayParts.push(spacePart()); displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); @@ -2931,6 +2934,13 @@ module ts { } documentation = signature.getDocumentationComment(); } + + function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node) { + var typeParameterParts = mapToDisplayParts(writer => { + typeResolver.writeTypeParametersOfSymbol(symbol, writer, enclosingDeclaration, TypeFormatFlags.NoTruncation); + }); + displayParts.push.apply(displayParts, typeParameterParts); + } } function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo { diff --git a/tests/cases/fourslash/completionListOfGnericSymbol.ts b/tests/cases/fourslash/completionListOfGnericSymbol.ts index 95e72db58c9..936eeeff714 100644 --- a/tests/cases/fourslash/completionListOfGnericSymbol.ts +++ b/tests/cases/fourslash/completionListOfGnericSymbol.ts @@ -6,6 +6,6 @@ ////a./**/ goTo.marker(); -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 77390ea2dd9..00c3c505a77 100644 --- a/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts +++ b/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts @@ -21,4 +21,4 @@ goTo.marker(); verify.completionListContains('watch', '(property) iBaseScope.watch: () => void'); verify.completionListContains('moveUp', '(property) iMover.moveUp: () => void'); debugger; -verify.completionListContains('family', '(property) iScope.family: number'); +verify.completionListContains('family', '(property) iScope.family: number'); diff --git a/tests/cases/fourslash/quickInfoForGenericPrototypeMember.ts b/tests/cases/fourslash/quickInfoForGenericPrototypeMember.ts new file mode 100644 index 00000000000..895a897452d --- /dev/null +++ b/tests/cases/fourslash/quickInfoForGenericPrototypeMember.ts @@ -0,0 +1,13 @@ +/// + +////class C { +//// foo(x: T) { } +////} +////var x = new /*1*/C(); +////var y = C.proto/*2*/type; + +goTo.marker('1'); +verify.quickInfoIs('(constructor) C(): C'); + +goTo.marker('2'); +verify.quickInfoIs('(property) C.prototype: C'); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoGenerics.ts b/tests/cases/fourslash/quickInfoGenerics.ts index 590019bfaca..7b34ad173e9 100644 --- a/tests/cases/fourslash/quickInfoGenerics.ts +++ b/tests/cases/fourslash/quickInfoGenerics.ts @@ -6,7 +6,7 @@ ////interface IList { //// getItem(i: number): /*3*/T; ////} -////class List> implements IList { +////class List2> implements IList { //// private __it/*6*/em: /*5*/T[]; //// public get/*7*/Item(i: number) { //// return this.__item[i]; @@ -19,6 +19,11 @@ ////function foo4(test: S): S; ////function foo4(test: any): any; ////function foo4(test: any): any { return null; } +////var x: List2>; +////var y = x./*14*/getItem(10); +////var x2: IList>; +////var x3: IList; +////var y2 = x./*15*/method(x2, [x3, x3]); goTo.marker("1"); verify.quickInfoIs("class Container", undefined); @@ -27,22 +32,26 @@ 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); +verify.quickInfoIs("(type parameter) T in List2>", undefined); goTo.marker("5"); -verify.quickInfoIs("(type parameter) T in List>", undefined); +verify.quickInfoIs("(type parameter) T in List2>", undefined); goTo.marker("6"); -verify.quickInfoIs("(property) List>.__item: T[]", undefined); +verify.quickInfoIs("(property) List2>.__item: T[]", undefined); goTo.marker("7"); -verify.quickInfoIs("(method) List>.getItem(i: number): T", undefined); +verify.quickInfoIs("(method) List2>.getItem(i: number): T", undefined); goTo.marker("8"); -verify.quickInfoIs("(method) List>.method>(s: S, p: T[]): S", undefined); +verify.quickInfoIs("(method) List2>.method>(s: S, p: T[]): S", undefined); goTo.marker("9"); -verify.quickInfoIs("(type parameter) S in List>.method>(s: S, p: T[]): S", undefined); +verify.quickInfoIs("(type parameter) S in List2>.method>(s: S, p: T[]): S", undefined); goTo.marker("10"); -verify.quickInfoIs("(type parameter) T in List>", undefined); +verify.quickInfoIs("(type parameter) T in List2>", 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 +verify.quickInfoIs("(type parameter) T in foo4(test: any): any", undefined); +goTo.marker("14"); +verify.quickInfoIs("(method) List2>.getItem(i: number): IList", undefined); +goTo.marker("15"); +verify.quickInfoIs("(method) List2>.method>>(s: IList>, p: IList[]): IList>", undefined); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts b/tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts index 16096cfcc18..5f6b3483084 100644 --- a/tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts +++ b/tests/cases/fourslash/quickInfoOnMergedInterfacesWithIncrementalEdits.ts @@ -15,14 +15,14 @@ diagnostics.setEditValidation(IncrementalEditValidation.None); goTo.marker('1'); -verify.quickInfoIs("(property) B.bar: string", undefined); +verify.quickInfoIs("(property) B.bar: string", undefined); edit.deleteAtCaret(1); edit.insert('z'); verify.quickInfoIs("any"); verify.numberOfErrorsInCurrentFile(1); edit.backspace(1); edit.insert('a'); -verify.quickInfoIs("(property) B.bar: string", undefined); +verify.quickInfoIs("(property) B.bar: string", undefined); goTo.marker('2'); verify.quickInfoIs("(var) r4: string", undefined); verify.numberOfErrorsInCurrentFile(0); diff --git a/tests/cases/fourslash_old/quickInfoForGenericPrototypeMember.ts b/tests/cases/fourslash_old/quickInfoForGenericPrototypeMember.ts deleted file mode 100644 index e1233f10bf6..00000000000 --- a/tests/cases/fourslash_old/quickInfoForGenericPrototypeMember.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// - -////class C { -//// foo(x: T) { } -////} -////var x = new C/*1*/(); -////var y = C.proto/*2*/type; - -goTo.marker('1'); -verify.quickInfoIs('(): C'); - -goTo.marker('2'); -verify.quickInfoIs('C'); \ No newline at end of file