Handle export declarations with module specifiers as well.

This commit is contained in:
Daniel Rosenwasser 2015-07-10 15:38:06 -07:00
parent 123e02974e
commit a309b1dd67

View File

@ -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 = <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 = <ImportDeclaration>importClause.parent;
Debug.assert(importDeclaration !== undefined && importDeclaration.kind === SyntaxKind.ImportDeclaration);
let declarationKind = namedImportsOrExports.kind === SyntaxKind.NamedImports ?
SyntaxKind.ImportDeclaration :
SyntaxKind.ExportDeclaration;
let importOrExportDeclaration = <ImportDeclaration | ExportDeclaration>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 <NamedImportsOrExports>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<boolean> = {};
if (!importDeclaration.importClause) {
return exports;
}
if (importDeclaration.importClause.namedBindings &&
importDeclaration.importClause.namedBindings.kind === SyntaxKind.NamedImports) {
forEach((<NamedImports>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));
}