From a309b1dd6705fe7deb2e774af2f43a3396cd9fef Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 10 Jul 2015 15:38:06 -0700 Subject: [PATCH] Handle export declarations with module specifiers as well. --- src/services/services.ts | 99 +++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 3f656fe75ff..b311898f38d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3014,17 +3014,17 @@ namespace ts { function tryGetGlobalSymbols(): boolean { let objectLikeContainer: ObjectLiteralExpression | BindingPattern; - let importClause: ImportClause; + let namedImportsOrExports: NamedImportsOrExports; let jsxContainer: JsxOpeningLikeElement; if (objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken)) { return tryGetObjectLikeCompletionSymbols(objectLikeContainer); } - if (importClause = getAncestor(contextToken, SyntaxKind.ImportClause)) { + if (namedImportsOrExports = tryGetNamedImportsOrExportsForCompletion(contextToken)) { // cursor is in an import clause // try to show exported member for imported module - return tryGetImportClauseCompletionSymbols(importClause); + return tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports); } if (jsxContainer = tryGetContainingJsxElement(contextToken)) { @@ -3109,18 +3109,6 @@ namespace ts { return result; } - function shouldShowCompletionsInImportsClause(node: Node): boolean { - if (node) { - // 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) { let containingNodeKind = previousToken.parent.kind; @@ -3252,38 +3240,44 @@ namespace ts { } /** - * Aggregates relevant symbols for completion in import clauses; for instance, + * Aggregates relevant symbols for completion in import clauses and export clauses + * whose declarations have a module specifier; for instance, symbols will be aggregated for * - * import { $ } from "moduleName"; + * import { | } from "moduleName"; + * export { a as foo, | } from "moduleName"; + * + * but not for + * + * export { | }; * * Relevant symbols are stored in the captured 'symbols' variable. * * @returns true if 'symbols' was successfully populated; false otherwise. */ - function tryGetImportClauseCompletionSymbols(importClause: ImportClause): boolean { + function tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports: NamedImportsOrExports): boolean { // cursor is in import clause // try to show exported member for imported module - if (shouldShowCompletionsInImportsClause(contextToken)) { - isMemberCompletion = true; - isNewIdentifierLocation = false; + isMemberCompletion = true; + isNewIdentifierLocation = false; - let importDeclaration = importClause.parent; - Debug.assert(importDeclaration !== undefined && importDeclaration.kind === SyntaxKind.ImportDeclaration); + let declarationKind = namedImportsOrExports.kind === SyntaxKind.NamedImports ? + SyntaxKind.ImportDeclaration : + SyntaxKind.ExportDeclaration; + let importOrExportDeclaration = getAncestor(namedImportsOrExports, declarationKind); + let moduleSpecifier = importOrExportDeclaration.moduleSpecifier; - let exports: Symbol[]; - let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importDeclaration.moduleSpecifier); - if (moduleSpecifierSymbol) { - exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); - } - - //let exports = typeInfoResolver.getExportsOfImportDeclaration(importDeclaration); - symbols = exports ? filterModuleExports(exports, importDeclaration) : emptyArray; + if (!moduleSpecifier) { + return false; } - else { - isMemberCompletion = false; - isNewIdentifierLocation = true; + + let exports: Symbol[]; + let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importOrExportDeclaration.moduleSpecifier); + if (moduleSpecifierSymbol) { + exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); } + symbols = exports ? filterModuleExports(exports, namedImportsOrExports) : emptyArray; + return true; } @@ -3307,6 +3301,26 @@ namespace ts { return undefined; } + /** + * Returns the containing list of named imports or exports of a context token, + * on the condition that one exists and that the context implies completion should be given. + */ + function tryGetNamedImportsOrExportsForCompletion(contextToken: Node): NamedImportsOrExports { + if (contextToken) { + switch (contextToken.kind) { + case SyntaxKind.OpenBraceToken: // import { | + case SyntaxKind.CommaToken: // import { a as 0, | + switch (contextToken.parent.kind) { + case SyntaxKind.NamedImports: + case SyntaxKind.NamedExports: + return contextToken.parent; + } + } + } + + return undefined; + } + function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement { if (contextToken) { let parent = contextToken.parent; @@ -3452,25 +3466,18 @@ namespace ts { return false; } - function filterModuleExports(exports: Symbol[], importDeclaration: ImportDeclaration): Symbol[] { + function filterModuleExports(exports: Symbol[], namedImportsOrExports: NamedImportsOrExports): Symbol[] { let exisingImports: Map = {}; - if (!importDeclaration.importClause) { - return exports; - } - - if (importDeclaration.importClause.namedBindings && - importDeclaration.importClause.namedBindings.kind === SyntaxKind.NamedImports) { - - forEach((importDeclaration.importClause.namedBindings).elements, el => { - let name = el.propertyName || el.name; - exisingImports[name.text] = true; - }); + for (let element of namedImportsOrExports.elements) { + let name = element.propertyName || element.name; + exisingImports[name.text] = true; } if (isEmpty(exisingImports)) { return exports; } + return filter(exports, e => !lookUp(exisingImports, e.name)); }