diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index fecff11b1b4..8c220604e89 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5135,7 +5135,12 @@ namespace ts { } else { node.exportClause = parseNamedImportsOrExports(SyntaxKind.NamedExports); - if (parseOptional(SyntaxKind.FromKeyword)) { + + // It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios, + // 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 || (token === SyntaxKind.StringLiteral && !scanner.hasPrecedingLineBreak())) { + parseExpected(SyntaxKind.FromKeyword) node.moduleSpecifier = parseModuleSpecifier(); } } diff --git a/src/services/services.ts b/src/services/services.ts index 743eff49864..df0e1fe0972 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3028,17 +3028,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)) { @@ -3048,7 +3048,7 @@ namespace ts { attrsType = typeChecker.getJsxElementAttributesType(jsxContainer); if (attrsType) { - symbols = filterJsxAttributes((jsxContainer).attributes, typeChecker.getPropertiesOfType(attrsType)); + symbols = filterJsxAttributes(typeChecker.getPropertiesOfType(attrsType), (jsxContainer).attributes); isMemberCompletion = true; isNewIdentifierLocation = false; return true; @@ -3117,24 +3117,12 @@ namespace ts { function isCompletionListBlocker(contextToken: Node): boolean { let start = new Date().getTime(); let result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) || - isIdentifierDefinitionLocation(contextToken) || + isSolelyIdentifierDefinitionLocation(contextToken) || isDotOfNumericLiteral(contextToken); log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start)); 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; @@ -3266,38 +3254,42 @@ 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 { - // cursor is in import clause - // try to show exported member for imported module - if (shouldShowCompletionsInImportsClause(contextToken)) { - isMemberCompletion = true; - isNewIdentifierLocation = false; + function tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports: NamedImportsOrExports): boolean { + let declarationKind = namedImportsOrExports.kind === SyntaxKind.NamedImports ? + SyntaxKind.ImportDeclaration : + SyntaxKind.ExportDeclaration; + let importOrExportDeclaration = getAncestor(namedImportsOrExports, declarationKind); + let moduleSpecifier = importOrExportDeclaration.moduleSpecifier; - let importDeclaration = importClause.parent; - Debug.assert(importDeclaration !== undefined && importDeclaration.kind === SyntaxKind.ImportDeclaration); - - 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; + + isMemberCompletion = true; + isNewIdentifierLocation = false; + + let exports: Symbol[]; + let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importOrExportDeclaration.moduleSpecifier); + if (moduleSpecifierSymbol) { + exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); } + symbols = exports ? filterNamedImportOrExportCompletionItems(exports, namedImportsOrExports.elements) : emptyArray; + return true; } @@ -3321,6 +3313,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; @@ -3368,7 +3380,10 @@ namespace ts { return false; } - function isIdentifierDefinitionLocation(contextToken: Node): boolean { + /** + * @returns true if we are certain that the currently edited location must define a new location; false otherwise. + */ + function isSolelyIdentifierDefinitionLocation(contextToken: Node): boolean { let containingNodeKind = contextToken.parent.kind; switch (contextToken.kind) { case SyntaxKind.CommaToken: @@ -3425,6 +3440,11 @@ namespace ts { case SyntaxKind.ProtectedKeyword: return containingNodeKind === SyntaxKind.Parameter; + case SyntaxKind.AsKeyword: + containingNodeKind === SyntaxKind.ImportSpecifier || + containingNodeKind === SyntaxKind.ExportSpecifier || + containingNodeKind === SyntaxKind.NamespaceImport; + case SyntaxKind.ClassKeyword: case SyntaxKind.EnumKeyword: case SyntaxKind.InterfaceKeyword: @@ -3466,33 +3486,41 @@ namespace ts { return false; } - function filterModuleExports(exports: Symbol[], importDeclaration: ImportDeclaration): 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 = {}; - if (!importDeclaration.importClause) { - return exports; + 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; + exisingImportsOrExports[name.text] = true; } - if (importDeclaration.importClause.namedBindings && - importDeclaration.importClause.namedBindings.kind === SyntaxKind.NamedImports) { - - forEach((importDeclaration.importClause.namedBindings).elements, el => { - // If this is the current item we are editing right now, do not filter it out - if (el.getStart() <= position && position <= el.getEnd()) { - return; - } - - let name = el.propertyName || el.name; - exisingImports[name.text] = true; - }); + if (isEmpty(exisingImportsOrExports)) { + return exportsOfModule; } - if (isEmpty(exisingImports)) { - return exports; - } - 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; @@ -3527,17 +3555,16 @@ namespace ts { existingMemberNames[existingName] = true; } - let filteredMembers: Symbol[] = []; - forEach(contextualMemberSymbols, s => { - if (!existingMemberNames[s.name]) { - filteredMembers.push(s); - } - }); - - return filteredMembers; + return filter(contextualMemberSymbols, m => !lookUp(existingMemberNames, m.name)); } - function filterJsxAttributes(attributes: NodeArray, symbols: Symbol[]): Symbol[] { + /** + * 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(symbols: Symbol[], attributes: NodeArray): Symbol[] { let seenNames: Map = {}; for (let attr of attributes) { // If this is the current item we are editing right now, do not filter it out @@ -3549,13 +3576,8 @@ namespace ts { seenNames[(attr).name.text] = true; } } - let result: Symbol[] = []; - for (let sym of symbols) { - if (!seenNames[sym.name]) { - result.push(sym); - } - } - return result; + + return filter(symbols, a => !lookUp(seenNames, a.name)); } } diff --git a/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.js b/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.js new file mode 100644 index 00000000000..a6700fb5e59 --- /dev/null +++ b/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.js @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/exportDeclarationWithModuleSpecifierNameOnNextLine1.ts] //// + +//// [t1.ts] + +export var x = "x"; + +//// [t2.ts] +export { x } from + "./t1"; + +//// [t3.ts] +export { } from + "./t1"; + +//// [t4.ts] +export { x as a } from + "./t1"; + +//// [t5.ts] +export { x as a, } from + "./t1"; + +//// [t1.js] +exports.x = "x"; +//// [t2.js] +var t1_1 = require("./t1"); +exports.x = t1_1.x; +//// [t3.js] +//// [t4.js] +var t1_1 = require("./t1"); +exports.a = t1_1.x; +//// [t5.js] +var t1_1 = require("./t1"); +exports.a = t1_1.x; diff --git a/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.symbols b/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.symbols new file mode 100644 index 00000000000..331766f7712 --- /dev/null +++ b/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.symbols @@ -0,0 +1,28 @@ +=== tests/cases/compiler/t1.ts === + +export var x = "x"; +>x : Symbol(x, Decl(t1.ts, 1, 10)) + +=== tests/cases/compiler/t2.ts === +export { x } from +>x : Symbol(x, Decl(t2.ts, 0, 8)) + + "./t1"; + +=== tests/cases/compiler/t3.ts === +export { } from +No type information for this code. "./t1"; +No type information for this code. +No type information for this code.=== tests/cases/compiler/t4.ts === +export { x as a } from +>x : Symbol(a, Decl(t4.ts, 0, 8)) +>a : Symbol(a, Decl(t4.ts, 0, 8)) + + "./t1"; + +=== tests/cases/compiler/t5.ts === +export { x as a, } from +>x : Symbol(a, Decl(t5.ts, 0, 8)) +>a : Symbol(a, Decl(t5.ts, 0, 8)) + + "./t1"; diff --git a/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.types b/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.types new file mode 100644 index 00000000000..1ad841fe947 --- /dev/null +++ b/tests/baselines/reference/exportDeclarationWithModuleSpecifierNameOnNextLine1.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/t1.ts === + +export var x = "x"; +>x : string +>"x" : string + +=== tests/cases/compiler/t2.ts === +export { x } from +>x : string + + "./t1"; + +=== tests/cases/compiler/t3.ts === +export { } from +No type information for this code. "./t1"; +No type information for this code. +No type information for this code.=== tests/cases/compiler/t4.ts === +export { x as a } from +>x : string +>a : string + + "./t1"; + +=== tests/cases/compiler/t5.ts === +export { x as a, } from +>x : string +>a : string + + "./t1"; diff --git a/tests/baselines/reference/unclosedExportClause01.errors.txt b/tests/baselines/reference/unclosedExportClause01.errors.txt new file mode 100644 index 00000000000..6590bc5cfed --- /dev/null +++ b/tests/baselines/reference/unclosedExportClause01.errors.txt @@ -0,0 +1,44 @@ +tests/cases/compiler/t2.ts(1,13): error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. +tests/cases/compiler/t2.ts(1,18): error TS1005: ',' expected. +tests/cases/compiler/t3.ts(1,10): error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. +tests/cases/compiler/t3.ts(1,15): error TS1005: ',' expected. +tests/cases/compiler/t4.ts(1,17): error TS1005: ',' expected. +tests/cases/compiler/t4.ts(1,17): error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. +tests/cases/compiler/t4.ts(1,22): error TS1005: ',' expected. +tests/cases/compiler/t5.ts(1,18): error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. +tests/cases/compiler/t5.ts(1,23): error TS1005: ',' expected. + + +==== tests/cases/compiler/t1.ts (0 errors) ==== + + export var x = "x"; + +==== tests/cases/compiler/t2.ts (2 errors) ==== + export { x, from "./t1" + ~~~~ +!!! error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. + ~~~~~~ +!!! error TS1005: ',' expected. + +==== tests/cases/compiler/t3.ts (2 errors) ==== + export { from "./t1" + ~~~~ +!!! error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. + ~~~~~~ +!!! error TS1005: ',' expected. + +==== tests/cases/compiler/t4.ts (3 errors) ==== + export { x as a from "./t1" + ~~~~ +!!! error TS1005: ',' expected. + ~~~~ +!!! error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. + ~~~~~~ +!!! error TS1005: ',' expected. + +==== tests/cases/compiler/t5.ts (2 errors) ==== + export { x as a, from "./t1" + ~~~~ +!!! error TS2305: Module '"tests/cases/compiler/t1"' has no exported member 'from'. + ~~~~~~ +!!! error TS1005: ',' expected. \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause01.js b/tests/baselines/reference/unclosedExportClause01.js new file mode 100644 index 00000000000..a683f8d987d --- /dev/null +++ b/tests/baselines/reference/unclosedExportClause01.js @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/unclosedExportClause01.ts] //// + +//// [t1.ts] + +export var x = "x"; + +//// [t2.ts] +export { x, from "./t1" + +//// [t3.ts] +export { from "./t1" + +//// [t4.ts] +export { x as a from "./t1" + +//// [t5.ts] +export { x as a, from "./t1" + +//// [t1.js] +exports.x = "x"; +//// [t2.js] +var t1_1 = require("./t1"); +exports.x = t1_1.x; +//// [t3.js] +//// [t4.js] +var t1_1 = require("./t1"); +exports.a = t1_1.x; +//// [t5.js] +var t1_1 = require("./t1"); +exports.a = t1_1.x; diff --git a/tests/baselines/reference/unclosedExportClause02.errors.txt b/tests/baselines/reference/unclosedExportClause02.errors.txt new file mode 100644 index 00000000000..a4809e2c0c3 --- /dev/null +++ b/tests/baselines/reference/unclosedExportClause02.errors.txt @@ -0,0 +1,57 @@ +tests/cases/compiler/t2.ts(1,10): error TS2304: Cannot find name 'x'. +tests/cases/compiler/t2.ts(1,13): error TS2304: Cannot find name 'from'. +tests/cases/compiler/t2.ts(2,5): error TS1005: ',' expected. +tests/cases/compiler/t3.ts(1,10): error TS2304: Cannot find name 'from'. +tests/cases/compiler/t3.ts(2,5): error TS1005: ',' expected. +tests/cases/compiler/t4.ts(1,10): error TS2304: Cannot find name 'x'. +tests/cases/compiler/t4.ts(1,17): error TS1005: ',' expected. +tests/cases/compiler/t4.ts(1,17): error TS2304: Cannot find name 'from'. +tests/cases/compiler/t4.ts(2,5): error TS1005: ',' expected. +tests/cases/compiler/t5.ts(1,10): error TS2304: Cannot find name 'x'. +tests/cases/compiler/t5.ts(1,18): error TS2304: Cannot find name 'from'. +tests/cases/compiler/t5.ts(2,5): error TS1005: ',' expected. + + +==== tests/cases/compiler/t1.ts (0 errors) ==== + + export var x = "x"; + +==== tests/cases/compiler/t2.ts (3 errors) ==== + export { x, from + ~ +!!! error TS2304: Cannot find name 'x'. + ~~~~ +!!! error TS2304: Cannot find name 'from'. + "./t1"; + ~~~~~~ +!!! error TS1005: ',' expected. + +==== tests/cases/compiler/t3.ts (2 errors) ==== + export { from + ~~~~ +!!! error TS2304: Cannot find name 'from'. + "./t1"; + ~~~~~~ +!!! error TS1005: ',' expected. + +==== tests/cases/compiler/t4.ts (4 errors) ==== + export { x as a from + ~ +!!! error TS2304: Cannot find name 'x'. + ~~~~ +!!! error TS1005: ',' expected. + ~~~~ +!!! error TS2304: Cannot find name 'from'. + "./t1"; + ~~~~~~ +!!! error TS1005: ',' expected. + +==== tests/cases/compiler/t5.ts (3 errors) ==== + export { x as a, from + ~ +!!! error TS2304: Cannot find name 'x'. + ~~~~ +!!! error TS2304: Cannot find name 'from'. + "./t1"; + ~~~~~~ +!!! error TS1005: ',' expected. \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause02.js b/tests/baselines/reference/unclosedExportClause02.js new file mode 100644 index 00000000000..964cc29899c --- /dev/null +++ b/tests/baselines/reference/unclosedExportClause02.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/unclosedExportClause02.ts] //// + +//// [t1.ts] + +export var x = "x"; + +//// [t2.ts] +export { x, from + "./t1"; + +//// [t3.ts] +export { from + "./t1"; + +//// [t4.ts] +export { x as a from + "./t1"; + +//// [t5.ts] +export { x as a, from + "./t1"; + +//// [t1.js] +exports.x = "x"; +//// [t2.js] +"./t1"; +//// [t3.js] +"./t1"; +//// [t4.js] +"./t1"; +//// [t5.js] +"./t1"; diff --git a/tests/cases/compiler/exportDeclarationWithModuleSpecifierNameOnNextLine1.ts b/tests/cases/compiler/exportDeclarationWithModuleSpecifierNameOnNextLine1.ts new file mode 100644 index 00000000000..ea3f55db16b --- /dev/null +++ b/tests/cases/compiler/exportDeclarationWithModuleSpecifierNameOnNextLine1.ts @@ -0,0 +1,20 @@ +// @module: commonjs + +// @filename: t1.ts +export var x = "x"; + +// @filename: t2.ts +export { x } from + "./t1"; + +// @filename: t3.ts +export { } from + "./t1"; + +// @filename: t4.ts +export { x as a } from + "./t1"; + +// @filename: t5.ts +export { x as a, } from + "./t1"; \ No newline at end of file diff --git a/tests/cases/compiler/unclosedExportClause01.ts b/tests/cases/compiler/unclosedExportClause01.ts new file mode 100644 index 00000000000..3bcbc713d61 --- /dev/null +++ b/tests/cases/compiler/unclosedExportClause01.ts @@ -0,0 +1,16 @@ +// @module: commonjs + +// @filename: t1.ts +export var x = "x"; + +// @filename: t2.ts +export { x, from "./t1" + +// @filename: t3.ts +export { from "./t1" + +// @filename: t4.ts +export { x as a from "./t1" + +// @filename: t5.ts +export { x as a, from "./t1" \ No newline at end of file diff --git a/tests/cases/compiler/unclosedExportClause02.ts b/tests/cases/compiler/unclosedExportClause02.ts new file mode 100644 index 00000000000..45dcb65a4cb --- /dev/null +++ b/tests/cases/compiler/unclosedExportClause02.ts @@ -0,0 +1,20 @@ +// @module: commonjs + +// @filename: t1.ts +export var x = "x"; + +// @filename: t2.ts +export { x, from + "./t1"; + +// @filename: t3.ts +export { from + "./t1"; + +// @filename: t4.ts +export { x as a from + "./t1"; + +// @filename: t5.ts +export { x as a, from + "./t1"; \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInExportClause01.ts b/tests/cases/fourslash/completionListInExportClause01.ts new file mode 100644 index 00000000000..12726444bd2 --- /dev/null +++ b/tests/cases/fourslash/completionListInExportClause01.ts @@ -0,0 +1,40 @@ +/// + +// @Filename: m1.ts +////export var foo: number = 1; +////export function bar() { return 10; } +////export function baz() { return 10; } + +// @Filename: m2.ts +////export {/*1*/, /*2*/ from "m1" +////export {/*3*/} from "m1" +////export {foo,/*4*/ from "m1" +////export {bar as /*5*/, /*6*/ from "m1" +////export {foo, bar, baz as b,/*7*/} from "m1" + +function verifyCompletionAtMarker(marker: string, showBuilder: boolean, ...completions: string[]) { + goTo.marker(marker); + if (completions.length) { + for (let completion of completions) { + verify.completionListContains(completion); + } + } + else { + verify.completionListIsEmpty(); + } + + if (showBuilder) { + verify.completionListAllowsNewIdentifier(); + } + else { + verify.not.completionListAllowsNewIdentifier(); + } +} + +verifyCompletionAtMarker("1", /*showBuilder*/ false, "foo", "bar", "baz"); +verifyCompletionAtMarker("2", /*showBuilder*/ false, "foo", "bar", "baz"); +verifyCompletionAtMarker("3", /*showBuilder*/ false, "foo", "bar", "baz"); +verifyCompletionAtMarker("4", /*showBuilder*/ false, "bar", "baz"); +verifyCompletionAtMarker("5", /*showBuilder*/ true); +verifyCompletionAtMarker("6", /*showBuilder*/ false, "foo", "baz"); +verifyCompletionAtMarker("7", /*showBuilder*/ false); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInExportClause02.ts b/tests/cases/fourslash/completionListInExportClause02.ts new file mode 100644 index 00000000000..4b40976f521 --- /dev/null +++ b/tests/cases/fourslash/completionListInExportClause02.ts @@ -0,0 +1,14 @@ +/// + +////declare module "M1" { +//// export var V; +////} +////var W; +////declare module "M2" { +//// export { /**/ } from "M1" +////} + +goTo.marker(); +verify.completionListContains("V"); +verify.not.completionListContains("W"); +verify.not.completionListAllowsNewIdentifier(); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInExportClause03.ts b/tests/cases/fourslash/completionListInExportClause03.ts new file mode 100644 index 00000000000..426c85d02ea --- /dev/null +++ b/tests/cases/fourslash/completionListInExportClause03.ts @@ -0,0 +1,16 @@ +/// + +////declare module "M1" { +//// export var abc: number; +//// export var def: string; +////} +//// +////declare module "M2" { +//// export { abc/**/ } from "M1"; +////} + +// Ensure we don't filter out the current item. +goTo.marker(); +verify.completionListContains("abc"); +verify.completionListContains("def"); +verify.not.completionListAllowsNewIdentifier(); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInImportClause01.ts b/tests/cases/fourslash/completionListInImportClause01.ts index 1191216baf1..2b679ffe550 100644 --- a/tests/cases/fourslash/completionListInImportClause01.ts +++ b/tests/cases/fourslash/completionListInImportClause01.ts @@ -11,6 +11,7 @@ ////import {foo,/*4*/ from "m1" ////import {bar as /*5*/, /*6*/ from "m1" ////import {foo, bar, baz as b,/*7*/} from "m1" + function verifyCompletionAtMarker(marker: string, showBuilder: boolean, ...completions: string[]) { goTo.marker(marker); if (completions.length) { diff --git a/tests/cases/fourslash/completionListInNamespaceImportName01.ts b/tests/cases/fourslash/completionListInNamespaceImportName01.ts new file mode 100644 index 00000000000..ac1c0e13e07 --- /dev/null +++ b/tests/cases/fourslash/completionListInNamespaceImportName01.ts @@ -0,0 +1,11 @@ +/// + +// @Filename: m1.ts +////export var foo: number = 1; + +// @Filename: m2.ts +////import * as /**/ from "m1" + +goTo.marker(); +verify.completionListIsEmpty(); +verify.completionListAllowsNewIdentifier(); \ No newline at end of file