From cdfd92b90d21a07e5b7f6947a4ec427f6c88cb9c Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 25 Apr 2018 16:15:49 -0700 Subject: [PATCH] Don't add import completion from a re-export in "./index" (#23623) * Don't add import completion from a re-export in "./index" * Simpler heuristic --- src/services/codefixes/importFixes.ts | 11 +++++--- .../completionsImport_notFromIndex.ts | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/completionsImport_notFromIndex.ts diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index b869494747..fa466eb8cb 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -98,16 +98,21 @@ namespace ts.codefix { symbolToken: Node | undefined, preferences: UserPreferences, ): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } { - const exportInfos = getAllReExportingModules(exportedSymbol, symbolName, checker, allSourceFiles); + const exportInfos = getAllReExportingModules(exportedSymbol, moduleSymbol, symbolName, sourceFile, checker, allSourceFiles); Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol)); // We sort the best codefixes first, so taking `first` is best for completions. const moduleSpecifier = first(getNewImportInfos(program, sourceFile, exportInfos, compilerOptions, getCanonicalFileName, host, preferences)).moduleSpecifier; const ctx: ImportCodeFixContext = { host, program, checker, compilerOptions, sourceFile, formatContext, symbolName, getCanonicalFileName, symbolToken, preferences }; return { moduleSpecifier, codeAction: first(getCodeActionsForImport(exportInfos, ctx)) }; } - function getAllReExportingModules(exportedSymbol: Symbol, symbolName: string, checker: TypeChecker, allSourceFiles: ReadonlyArray): ReadonlyArray { + function getAllReExportingModules(exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, sourceFile: SourceFile, checker: TypeChecker, allSourceFiles: ReadonlyArray): ReadonlyArray { const result: SymbolExportInfo[] = []; - forEachExternalModule(checker, allSourceFiles, moduleSymbol => { + forEachExternalModule(checker, allSourceFiles, (moduleSymbol, moduleFile) => { + // Don't import from a re-export when looking "up" like to `./index` or `../index`. + if (moduleFile && moduleSymbol !== exportingModuleSymbol && startsWith(sourceFile.fileName, getDirectoryPath(moduleFile.fileName))) { + return; + } + for (const exported of checker.getExportsOfModule(moduleSymbol)) { if (exported.escapedName === InternalSymbolName.Default || exported.name === symbolName && skipAlias(exported, checker) === exportedSymbol) { const isDefaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol) === exported; diff --git a/tests/cases/fourslash/completionsImport_notFromIndex.ts b/tests/cases/fourslash/completionsImport_notFromIndex.ts new file mode 100644 index 0000000000..c42c768d23 --- /dev/null +++ b/tests/cases/fourslash/completionsImport_notFromIndex.ts @@ -0,0 +1,27 @@ +/// + +// @Filename: /src/a.ts +////export const x = 0; + +// @Filename: /src/index.ts +////export { x } from "./a"; + +// @Filename: /0.ts +////x/*0*/ + +// @Filename: /src/1.ts +////x/*1*/ + +// @Filename: /src/inner/2.ts +////x/*2*/ + +for (const [marker, sourceDisplay] of [["0", "./src"], ["1", "./a"], ["2", "../a"]]) { + goTo.marker(marker); + verify.completionListContains({ name: "x", source: "/src/a" }, "const x: 0", "", "const", /*spanIndex*/ undefined, /*hasAction*/ true, { includeCompletionsForModuleExports: true, sourceDisplay }); + verify.applyCodeActionFromCompletion(marker, { + name: "x", + source: "/src/a", + description: `Import 'x' from module "${sourceDisplay}"`, + newFileContent: `import { x } from "${sourceDisplay}";\n\nx`, + }); +}