diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index acb90c4767b..9ec87743577 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -56,6 +56,7 @@ module ts { isImplementationOfOverload, getAliasedSymbol: resolveImport, getEmitResolver, + getExportsOfExternalModule, }; var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined"); @@ -2754,6 +2755,19 @@ module ts { return result; } + function getExportsOfExternalModule(node: ImportDeclaration): Symbol[]{ + if (!node.moduleSpecifier) { + return emptyArray; + } + + var module = resolveExternalModuleName(node, node.moduleSpecifier); + if (!module || !module.exports) { + return emptyArray; + } + + return mapToArray(module.exports) + } + function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { var links = getNodeLinks(declaration); if (!links.resolvedSignature) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5e5993fe4a2..a81da0eaf74 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1100,6 +1100,7 @@ module ts { getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; getAliasedSymbol(symbol: Symbol): Symbol; + getExportsOfExternalModule(node: ImportDeclaration): Symbol[]; // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; diff --git a/src/services/services.ts b/src/services/services.ts index 2fc154f610f..3d3a01ad79a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2403,6 +2403,19 @@ module ts { getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession); } } + else if (getAncestor(previousToken, SyntaxKind.ImportClause)) { + // cursor is in import clause + // try to show exported member for imported module + isMemberCompletion = true; + isNewIdentifierLocation = true; + if (canShowCompletionInImportsClause(previousToken)) { + var importDeclaration = getAncestor(previousToken, SyntaxKind.ImportDeclaration); + Debug.assert(importDeclaration !== undefined); + var exports = typeInfoResolver.getExportsOfExternalModule(importDeclaration); + var filteredExports = filterModuleExports(exports, importDeclaration); + getCompletionEntriesFromSymbols(filteredExports, activeCompletionSession); + } + } else { // Get scope members isMemberCompletion = false; @@ -2453,6 +2466,16 @@ module ts { return result; } + function canShowCompletionInImportsClause(node: Node): boolean { + // import {| + // import {a,| + if (node.kind === SyntaxKind.OpenBraceToken || node.kind === SyntaxKind.CommaToken) { + return node.parent.kind === SyntaxKind.NamedImports; + } + + return false; + } + function isNewIdentifierDefinitionLocation(previousToken: Node): boolean { if (previousToken) { var containingNodeKind = previousToken.parent.kind; @@ -2531,6 +2554,8 @@ module ts { return false; } + + function getContainingObjectLiteralApplicableForCompletion(previousToken: Node): ObjectLiteralExpression { // The locations in an object literal expression that are applicable for completion are property name definition locations. @@ -2664,6 +2689,34 @@ module ts { return false; } + function filterModuleExports(exports: Symbol[], importDeclaration: ImportDeclaration): Symbol[]{ + var exisingImports: Map = {}; + + if (!importDeclaration.importClause) { + return exports; + } + + if (importDeclaration.importClause.name) { + exisingImports[importDeclaration.importClause.name.text] = true; + } + + if (importDeclaration.importClause.namedBindings && + importDeclaration.importClause.namedBindings.kind === SyntaxKind.NamedImports) { + + forEach((importDeclaration.importClause.namedBindings).elements, el => { + var name = el.propertyName || el.name; + exisingImports[name.text] = true; + }); + } + + if (isEmpty(exisingImports)) { + return exports; + } + else { + return filter(exports, e => !lookUp(exisingImports, e.name)); + } + } + function filterContextualMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] { if (!existingMembers || existingMembers.length === 0) { return contextualMemberSymbols;