diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 927cc050e20..0a40d881433 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -736,7 +736,7 @@ module ts { return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace; } - function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): Symbol[] { + function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] { function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] { function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) { // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible @@ -745,7 +745,7 @@ module ts { } // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too - var accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning)); + var accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing); return !!accessibleParent; } @@ -767,16 +767,21 @@ module ts { // Check if symbol is any of the alias return forEachValue(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Import) { - var resolvedImportedSymbol = resolveImport(symbolFromSymbolTable); - if (isAccessible(symbolFromSymbolTable, resolveImport(symbolFromSymbolTable))) { - return [symbolFromSymbolTable]; - } + if (!useOnlyExternalAliasing || // We can use any type of alias to get the name + // Is this external alias, then use it to name + ts.forEach(symbolFromSymbolTable.declarations, declaration => + declaration.kind === SyntaxKind.ImportDeclaration && (declaration).externalModuleName)) { + var resolvedImportedSymbol = resolveImport(symbolFromSymbolTable); + if (isAccessible(symbolFromSymbolTable, resolveImport(symbolFromSymbolTable))) { + return [symbolFromSymbolTable]; + } - // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain - // but only if the symbolFromSymbolTable can be qualified - var accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined; - if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) { - return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); + // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain + // but only if the symbolFromSymbolTable can be qualified + var accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined; + if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) { + return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); + } } } }); @@ -822,7 +827,7 @@ module ts { var meaningToLook = meaning; while (symbol) { // Symbol is accessible if it by itself is accessible - var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook); + var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false); if (accessibleSymbolChain) { var hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0]); if (!hasAccessibleDeclarations) { @@ -1005,7 +1010,7 @@ module ts { writer.trackSymbol(symbol, enclosingDeclaration, meaning); function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void { if (symbol) { - var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning); + var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, !!(flags & SymbolFormatFlags.UseOnlyExternalAliasing)); if (!accessibleSymbolChain || needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2a291c828cd..68ef296e263 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -695,6 +695,9 @@ module ts { // 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 + UseOnlyExternalAliasing = 0x00000002, // Use only external alias information to get the symbol name in the given context + // eg. module m { export class c { } } import x = m.c; + // When this flag is specified m.c will be used to refer to the class instead of alias symbol x } export enum SymbolAccessibility { diff --git a/src/services/services.ts b/src/services/services.ts index 42f6160bd3e..05aeb47f7c6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2749,15 +2749,11 @@ module ts { var symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, symbolFlags); var hasAddedSymbolInfo: boolean; // Class at constructor site need to be shown as constructor apart from property,method, vars - if (symbolKind !== ScriptElementKind.unknown || symbolFlags & SymbolFlags.Signature || symbolFlags & SymbolFlags.Class) { + if (symbolKind !== ScriptElementKind.unknown || symbolFlags & SymbolFlags.Class || symbolFlags & SymbolFlags.Import) { // If it is accessor they are allowed only if location is at name of the accessor if (symbolKind === ScriptElementKind.memberGetAccessorElement || symbolKind === ScriptElementKind.memberSetAccessorElement) { symbolKind = ScriptElementKind.memberVariableElement; } - else if (symbol.name === "undefined") { - // undefined is symbol and not property - symbolKind = ScriptElementKind.variableElement; - } var type = typeResolver.getTypeOfSymbol(symbol); if (type) { @@ -2790,6 +2786,18 @@ module ts { symbolKind = ScriptElementKind.constructorImplementationElement; addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); } + else if (symbolFlags & SymbolFlags.Import) { + symbolKind = ScriptElementKind.alias; + displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); + displayParts.push(textPart(symbolKind)); + displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); + displayParts.push(spacePart()); + if (useConstructSignatures) { + displayParts.push(keywordPart(SyntaxKind.NewKeyword)); + displayParts.push(spacePart()); + } + addFullSymbolName(symbol); + } else { addPrefixForAnyFunctionOrVar(symbol, symbolKind); } @@ -2851,27 +2859,27 @@ 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.WriteTypeParametersOrArguments)); + addFullSymbolName(symbol); 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.WriteTypeParametersOrArguments)); + addFullSymbolName(symbol); writeTypeParametersOfSymbol(symbol, sourceFile); } if (symbolFlags & SymbolFlags.Enum) { addNewLineIfDisplayPartsExist(); displayParts.push(keywordPart(SyntaxKind.EnumKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile)); + addFullSymbolName(symbol); } if (symbolFlags & SymbolFlags.Module) { addNewLineIfDisplayPartsExist(); displayParts.push(keywordPart(SyntaxKind.ModuleKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile)); + addFullSymbolName(symbol); } if (symbolFlags & SymbolFlags.TypeParameter) { addNewLineIfDisplayPartsExist(); @@ -2879,13 +2887,13 @@ module ts { displayParts.push(textPart("type parameter")); displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, enclosingDeclaration)); + addFullSymbolName(symbol); 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.WriteTypeParametersOrArguments)) + addFullSymbolName(symbol.parent, enclosingDeclaration); writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration); } else { @@ -2897,7 +2905,7 @@ module ts { displayParts.push(spacePart()); } else if (signatureDeclaration.kind !== SyntaxKind.CallSignature && signatureDeclaration.name) { - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, signatureDeclaration.symbol, sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)) + addFullSymbolName(signatureDeclaration.symbol); } displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); } @@ -2917,11 +2925,28 @@ module ts { } if (symbolFlags & SymbolFlags.Import) { addNewLineIfDisplayPartsExist(); - displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); - displayParts.push(textPart("alias")); - displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); + displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); displayParts.push(spacePart()); - displayParts.push.apply(displayParts, symbolToDisplayParts(typeResolver, symbol, sourceFile)); + addFullSymbolName(symbol); + displayParts.push(spacePart()); + displayParts.push(punctuationPart(SyntaxKind.EqualsToken)); + displayParts.push(spacePart()); + ts.forEach(symbol.declarations, declaration => { + if (declaration.kind === SyntaxKind.ImportDeclaration) { + var importDeclaration = declaration; + if (importDeclaration.externalModuleName) { + displayParts.push(keywordPart(SyntaxKind.RequireKeyword)); + displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); + displayParts.push(displayPart(getTextOfNode(importDeclaration.externalModuleName), SymbolDisplayPartKind.stringLiteral)); + displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); + } + else { + var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.entityName); + addFullSymbolName(internalAliasSymbol, enclosingDeclaration); + } + return true; + } + }); } if (!hasAddedSymbolInfo) { if (symbolKind !== ScriptElementKind.unknown) { @@ -2969,6 +2994,12 @@ module ts { } } + function addFullSymbolName(symbol: Symbol, enclosingDeclaration?: Node) { + var fullSymbolDisplayParts = symbolToDisplayParts(typeResolver, symbol, enclosingDeclaration || sourceFile, /*meaning*/ undefined, + SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing); + displayParts.push.apply(displayParts, fullSymbolDisplayParts); + } + function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) { addNewLineIfDisplayPartsExist(); if (symbolKind) { @@ -2976,8 +3007,7 @@ module ts { displayParts.push(textPart(symbolKind)); 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.WriteTypeParametersOrArguments)); + addFullSymbolName(symbol); } } @@ -3023,7 +3053,7 @@ module ts { case SyntaxKind.QualifiedName: case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: - // For the identifiers/this/usper etc get the type at position + // For the identifiers/this/super etc get the type at position var type = typeInfoResolver.getTypeOfNode(node); if (type) { return { diff --git a/tests/cases/fourslash/commentsExternalModules.ts b/tests/cases/fourslash/commentsExternalModules.ts index 0eed08ca01e..c0e27149202 100644 --- a/tests/cases/fourslash/commentsExternalModules.ts +++ b/tests/cases/fourslash/commentsExternalModules.ts @@ -69,10 +69,10 @@ verify.memberListContains("i", "(var) m1.m2.i: m1.m2.c", "i"); goTo.file("commentsExternalModules_file1.ts"); goTo.marker('9'); -verify.quickInfoIs('(alias) extMod', "This is on import declaration"); +verify.quickInfoIs('import extMod = require("commentsExternalModules_file0")', "This is on import declaration"); goTo.marker('10'); -verify.completionListContains("extMod", "(alias) extMod", "This is on import declaration"); +verify.completionListContains("extMod", 'import extMod = require("commentsExternalModules_file0")', "This is on import declaration"); goTo.marker('11'); verify.memberListContains("m1", "module extMod.m1"); diff --git a/tests/cases/fourslash/commentsImportDeclaration.ts b/tests/cases/fourslash/commentsImportDeclaration.ts index 192cb2763ca..f115f3607b7 100644 --- a/tests/cases/fourslash/commentsImportDeclaration.ts +++ b/tests/cases/fourslash/commentsImportDeclaration.ts @@ -28,7 +28,7 @@ goTo.marker('2'); verify.quickInfoIs("module m1", "ModuleComment"); goTo.marker('3'); -verify.quickInfoIs("(alias) extMod", "Import declaration"); +verify.quickInfoIs('import extMod = require("commentsImportDeclaration_file0")', "Import declaration"); goTo.marker('6'); verify.memberListContains("m1", "module extMod.m1"); diff --git a/tests/cases/fourslash/completionListOnAliases.ts b/tests/cases/fourslash/completionListOnAliases.ts index 68c25d3608e..3d9026c3036 100644 --- a/tests/cases/fourslash/completionListOnAliases.ts +++ b/tests/cases/fourslash/completionListOnAliases.ts @@ -9,7 +9,7 @@ ////} goTo.marker("1"); -verify.memberListContains("x", "(alias) x", undefined); +verify.memberListContains("x", "import x = M", undefined); goTo.marker("2"); verify.memberListContains("value"); diff --git a/tests/cases/fourslash/exportEqualTypes.ts b/tests/cases/fourslash/exportEqualTypes.ts index 47a266f7f39..8cfe931d87b 100644 --- a/tests/cases/fourslash/exportEqualTypes.ts +++ b/tests/cases/fourslash/exportEqualTypes.ts @@ -15,7 +15,7 @@ ////var /*3*/r2 = t./*4*/foo; // t should have 'foo' in dropdown list and be of type 'string' goTo.marker('1'); -verify.quickInfoIs('(alias) test'); +verify.quickInfoIs("import test = require('exportEqualTypes_file0')"); goTo.marker('2'); verify.quickInfoIs('(var) r1: Date'); goTo.marker('3'); diff --git a/tests/cases/fourslash/externalModuleWithExportAssignment.ts b/tests/cases/fourslash/externalModuleWithExportAssignment.ts index 952640f2c6c..1de1e55579b 100644 --- a/tests/cases/fourslash/externalModuleWithExportAssignment.ts +++ b/tests/cases/fourslash/externalModuleWithExportAssignment.ts @@ -30,7 +30,7 @@ goTo.file("externalModuleWithExportAssignment_file1.ts"); goTo.marker('1'); -verify.quickInfoIs("(alias) a1"); +verify.quickInfoIs('import a1 = require("externalModuleWithExportAssignment_file0")'); goTo.marker('2'); verify.quickInfoIs("(var) a: {\n (): a1.connectExport;\n test1: a1.connectModule;\n test2(): a1.connectModule;\n}", undefined); diff --git a/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts b/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts index be5376d5de5..3198b6e9243 100644 --- a/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts +++ b/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts @@ -19,7 +19,7 @@ edit.insert(''); goTo.marker('1'); -verify.quickInfoIs('(alias) Foo'); +verify.quickInfoIs("import Foo = require('mergedDeclarationsWithExportAssignment1_file0')"); goTo.marker('2'); verify.completionListContains('Foo'); diff --git a/tests/cases/fourslash/quickInfoOnInternalAliases.ts b/tests/cases/fourslash/quickInfoOnInternalAliases.ts new file mode 100644 index 00000000000..b1c562df494 --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnInternalAliases.ts @@ -0,0 +1,59 @@ +/// + +/////** Module comment*/ +////export module m1 { +//// /** m2 comments*/ +//// export module m2 { +//// /** class comment;*/ +//// export class /*1*/c { +//// }; +//// } +//// export function foo() { +//// } +////} +/////**This is on import declaration*/ +////import /*2*/internalAlias = m1.m2./*3*/c; +////var /*4*/newVar = new /*5*/internalAlias(); +////var /*6*/anotherAliasVar = /*7*/internalAlias; +////import /*8*/internalFoo = m1./*9*/foo; +////var /*10*/callVar = /*11*/internalFoo(); +////var /*12*/anotherAliasFoo = /*13*/internalFoo; + +goTo.marker('1'); +verify.quickInfoIs("class m1.m2.c", "class comment;"); + +goTo.marker('2'); +verify.quickInfoIs('import internalAlias = m1.m2.c', "This is on import declaration"); + +goTo.marker('3'); +verify.quickInfoIs("class m1.m2.c", "class comment;"); + +goTo.marker('4'); +verify.quickInfoIs("(var) newVar: internalAlias", ""); + +goTo.marker('5'); +verify.quickInfoIs("(alias) new internalAlias(): internalAlias\nimport internalAlias = m1.m2.c", ""); + +goTo.marker('6'); +verify.quickInfoIs("(var) anotherAliasVar: typeof internalAlias", ""); + +goTo.marker('7'); +verify.quickInfoIs("import internalAlias = m1.m2.c", "This is on import declaration"); + +goTo.marker('8'); +verify.quickInfoIs('import internalFoo = m1.foo', ""); + +goTo.marker('9'); +verify.quickInfoIs("(function) m1.foo(): void", ""); + +goTo.marker('10'); +verify.quickInfoIs("(var) callVar: void", ""); + +goTo.marker('11'); +verify.quickInfoIs("(alias) internalFoo(): void\nimport internalFoo = m1.foo", ""); + +goTo.marker('12'); +verify.quickInfoIs("(var) anotherAliasFoo: () => void", ""); + +goTo.marker('13'); +verify.quickInfoIs("import internalFoo = m1.foo", ""); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoOnUndefined.ts b/tests/cases/fourslash/quickInfoOnUndefined.ts index 252da49d4fe..1bb516d2c18 100644 --- a/tests/cases/fourslash/quickInfoOnUndefined.ts +++ b/tests/cases/fourslash/quickInfoOnUndefined.ts @@ -3,6 +3,13 @@ ////function foo(a: string) { ////} ////foo(/*1*/undefined); +////var x = { +//// undefined: 10 +////}; +////x./*2*/undefined = 30; goTo.marker('1'); -verify.quickInfoIs('(var) undefined'); \ No newline at end of file +verify.quickInfoIs('(var) undefined'); + +goTo.marker('2'); +verify.quickInfoIs('(property) undefined: number'); \ No newline at end of file diff --git a/tests/cases/fourslash/selfReferencedExternalModule.ts b/tests/cases/fourslash/selfReferencedExternalModule.ts index ec312ccb1e5..f5e955839c7 100644 --- a/tests/cases/fourslash/selfReferencedExternalModule.ts +++ b/tests/cases/fourslash/selfReferencedExternalModule.ts @@ -5,5 +5,5 @@ ////A./**/I goTo.marker(); -verify.completionListContains("A", "(alias) A"); +verify.completionListContains("A", "import A = require('app')"); verify.completionListContains("I", "(var) I: number"); \ No newline at end of file