From 30c6947ae3ad737bf89ec52e5de2a2e0080fb4e2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 13 Jul 2015 17:55:48 -0700 Subject: [PATCH] Addressed CR feedback. --- src/compiler/parser.ts | 8 ++----- src/services/services.ts | 45 ++++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 986967edb45..8c220604e89 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4345,10 +4345,6 @@ namespace ts { } } - function nextTokenIsStringLiteralOnSameLine() { - return !scanner.hasPrecedingLineBreak() && token === SyntaxKind.StringLiteral; - } - function nextTokenIsIdentifierOrStringLiteralOnSameLine() { nextToken(); return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token === SyntaxKind.StringLiteral); @@ -5141,9 +5137,9 @@ namespace ts { node.exportClause = parseNamedImportsOrExports(SyntaxKind.NamedExports); // It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios, - // the 'from' keyword be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`) + // the 'from' keyword can be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`) // If we don't have a 'from' keyword, see if we have a string literal such that ASI won't take effect. - if (token === SyntaxKind.FromKeyword || lookAhead(nextTokenIsStringLiteralOnSameLine)) { + if (token === SyntaxKind.FromKeyword || (token === SyntaxKind.StringLiteral && !scanner.hasPrecedingLineBreak())) { parseExpected(SyntaxKind.FromKeyword) node.moduleSpecifier = parseModuleSpecifier(); } diff --git a/src/services/services.ts b/src/services/services.ts index dff8e46e2b5..0229a356b97 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3255,11 +3255,6 @@ namespace ts { * @returns true if 'symbols' was successfully populated; false otherwise. */ function tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports: NamedImportsOrExports): boolean { - // cursor is in import clause - // try to show exported member for imported module - isMemberCompletion = true; - isNewIdentifierLocation = false; - let declarationKind = namedImportsOrExports.kind === SyntaxKind.NamedImports ? SyntaxKind.ImportDeclaration : SyntaxKind.ExportDeclaration; @@ -3270,13 +3265,16 @@ namespace ts { return false; } + isMemberCompletion = true; + isNewIdentifierLocation = false; + let exports: Symbol[]; let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importOrExportDeclaration.moduleSpecifier); if (moduleSpecifierSymbol) { exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); } - symbols = exports ? filterModuleExports(exports, namedImportsOrExports) : emptyArray; + symbols = exports ? filterNamedImportOrExportCompletionItems(exports, namedImportsOrExports.elements) : emptyArray; return true; } @@ -3474,26 +3472,41 @@ namespace ts { return false; } - function filterModuleExports(exports: Symbol[], namedImportsOrExports: NamedImportsOrExports): Symbol[] { - let exisingImports: Map = {}; + /** + * Filters out completion suggestions for named imports or exports. + * + * @param exportsOfModule The list of symbols which a module exposes. + * @param namedImportsOrExports The list of existing import/export specifiers in the import/export clause. + * + * @returns Symbols to be suggested at an import/export clause, barring those whose named imports/exports + * do not occur at the current position and have not otherwise been typed. + */ + function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ImportOrExportSpecifier[]): Symbol[] { + let exisingImportsOrExports: Map = {}; - for (let element of namedImportsOrExports.elements) { + for (let element of namedImportsOrExports) { // If this is the current item we are editing right now, do not filter it out if (element.getStart() <= position && position <= element.getEnd()) { continue; } let name = element.propertyName || element.name; - exisingImports[name.text] = true; + exisingImportsOrExports[name.text] = true; } - if (isEmpty(exisingImports)) { - return exports; + if (isEmpty(exisingImportsOrExports)) { + return exportsOfModule; } - return filter(exports, e => !lookUp(exisingImports, e.name)); + return filter(exportsOfModule, e => !lookUp(exisingImportsOrExports, e.name)); } + /** + * Filters out completion suggestions for named imports or exports. + * + * @returns Symbols to be suggested in an object binding pattern or object literal expression, barring those whose declarations + * do not occur at the current position and have not otherwise been typed. + */ function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] { if (!existingMembers || existingMembers.length === 0) { return contextualMemberSymbols; @@ -3538,6 +3551,12 @@ namespace ts { return filteredMembers; } + /** + * Filters out completion suggestions from 'symbols' according to existing JSX attributes. + * + * @returns Symbols to be suggested in a JSX element, barring those whose attributes + * do not occur at the current position and have not otherwise been typed. + */ function filterJsxAttributes(attributes: NodeArray, symbols: Symbol[]): Symbol[] { let seenNames: Map = {}; for (let attr of attributes) {