From 7de8c6bcaa164ea478b450da84e135dfdf3860bb Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 3 Jan 2018 14:56:59 -0800 Subject: [PATCH] For `export default foo`, use 'foo' for the completion identifier name, not the module name (#20987) --- src/services/completions.ts | 20 +++++--------- ...sImport_default_exportDefaultIdentifier.ts | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 2b24309d1c1..15a99942c85 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -444,7 +444,9 @@ namespace ts.Completions { function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string { return origin && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default - ? codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) + // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase. + ? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined) + || codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) : symbol.name; } @@ -1143,8 +1145,6 @@ namespace ts.Completions { codefix.forEachExternalModuleToImportFrom(typeChecker, sourceFile, allSourceFiles, moduleSymbol => { for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) { - let { name } = symbol; - // Don't add a completion for a re-export, only for the original. // If `symbol.parent !== moduleSymbol`, this comes from an `export * from "foo"` re-export. Those don't create new symbols. // If `some(...)`, this comes from an `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check). @@ -1152,19 +1152,13 @@ namespace ts.Completions { continue; } - const isDefaultExport = name === InternalSymbolName.Default; + const isDefaultExport = symbol.name === InternalSymbolName.Default; if (isDefaultExport) { - const localSymbol = getLocalSymbolForExportDefault(symbol); - if (localSymbol) { - symbol = localSymbol; - name = localSymbol.name; - } - else { - name = codefix.moduleSymbolToValidIdentifier(moduleSymbol, target); - } + symbol = getLocalSymbolForExportDefault(symbol) || symbol; } - if (stringContainsCharactersInOrder(name.toLowerCase(), tokenTextLowerCase)) { + const origin: SymbolOriginInfo = { moduleSymbol, isDefaultExport }; + if (stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) { symbols.push(symbol); symbolToOriginInfoMap[getSymbolId(symbol)] = { moduleSymbol, isDefaultExport }; } diff --git a/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts new file mode 100644 index 00000000000..042da2fee39 --- /dev/null +++ b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts @@ -0,0 +1,26 @@ +/// + +// Tests that we use the name "foo". + +// @Filename: /a.ts +////const foo = 0; +////export default foo; + +// @Filename: /b.ts +////f/**/; + +goTo.marker(""); +verify.completionListContains({ name: "foo", source: "/a" }, "export default foo", "", "alias", /*spanIndex*/ undefined, /*hasAction*/ true, { + includeExternalModuleExports: true, + sourceDisplay: "./a", +}); + +verify.applyCodeActionFromCompletion("", { + name: "foo", + source: "/a", + description: `Import 'foo' from module "./a"`, + // TODO: GH#18445 + newFileContent: `import foo from "./a";\r +\r +f;`, +});