diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 4628269b0c6..551ac9aef18 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2798,7 +2798,12 @@ namespace FourSlash { const details = this.getCompletionEntryDetails(options.name, options.source, options.preferences); if (!details) { - return this.raiseError(`No completions were found for the given name, source, and preferences.`); + const completions = this.getCompletionListAtCaret(options.preferences)?.entries; + const matchingName = completions?.filter(e => e.name === options.name); + const detailMessage = matchingName?.length + ? `\n Found ${matchingName.length} with name '${options.name}' from source(s) ${matchingName.map(e => `'${e.source}'`).join(", ")}.` + : ""; + return this.raiseError(`No completions were found for the given name, source, and preferences.` + detailMessage); } const codeActions = details.codeActions; if (codeActions?.length !== 1) { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 7497b8222a4..e47c7f79b53 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -210,7 +210,9 @@ namespace ts.codefix { preferences: UserPreferences, ): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } { const compilerOptions = program.getCompilerOptions(); - const exportInfos = getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true); + const exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name)) + ? [getSymbolExportInfoForSymbol(exportedSymbol, moduleSymbol, sourceFile, program, host)] + : getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true); const useRequire = shouldUseRequire(sourceFile, program); const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && !isSourceFileJS(sourceFile) && isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position)); const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, preferTypeOnlyImport, useRequire, exportInfos, host, preferences)).moduleSpecifier; @@ -228,6 +230,27 @@ namespace ts.codefix { return { description, changes, commands }; } + function getSymbolExportInfoForSymbol(symbol: Symbol, moduleSymbol: Symbol, importingFile: SourceFile, program: Program, host: LanguageServiceHost): SymbolExportInfo { + const compilerOptions = program.getCompilerOptions(); + const mainProgramInfo = getInfoWithChecker(program.getTypeChecker()); + if (mainProgramInfo) { + return mainProgramInfo; + } + const autoImportProvider = host.getPackageJsonAutoImportProvider?.()?.getTypeChecker(); + return Debug.checkDefined(autoImportProvider && getInfoWithChecker(autoImportProvider), `Could not find symbol in specified module for code actions`); + + function getInfoWithChecker(checker: TypeChecker): SymbolExportInfo | undefined { + const defaultInfo = getDefaultLikeExportInfo(importingFile, moduleSymbol, checker, compilerOptions); + if (defaultInfo && skipAlias(defaultInfo.symbol, checker) === symbol) { + return { moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(symbol, checker) }; + } + const named = checker.tryGetMemberInModuleExportsAndProperties(symbol.name, moduleSymbol); + if (named && skipAlias(named, checker) === symbol) { + return { moduleSymbol, importKind: ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(symbol, checker) }; + } + } + } + function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, useAutoImportProvider: boolean): readonly SymbolExportInfo[] { const result: SymbolExportInfo[] = []; const compilerOptions = program.getCompilerOptions(); diff --git a/tests/cases/fourslash/completionsImport_ambient.ts b/tests/cases/fourslash/completionsImport_ambient.ts new file mode 100644 index 00000000000..f28028300b1 --- /dev/null +++ b/tests/cases/fourslash/completionsImport_ambient.ts @@ -0,0 +1,57 @@ +/// + +// @module: commonjs + +// @Filename: a.d.ts +//// declare namespace foo { class Bar {} } +//// declare module 'path1' { +//// import Bar = foo.Bar; +//// export default Bar; +//// } +//// declare module 'path2longer' { +//// import Bar = foo.Bar; +//// export {Bar}; +//// } +//// + +// @Filename: b.ts +//// Ba/**/ + +verify.completions({ + marker: "", + exact: [ + completion.globalThisEntry, + ...completion.globalsVars, + { + name: "foo", + sortText: completion.SortText.GlobalsOrKeywords + }, + completion.undefinedVarEntry, + { + name: "Bar", + source: "path1", + hasAction: true, + sortText: completion.SortText.AutoImportSuggestions + }, + { + name: "Bar", + source: "path2longer", + hasAction: true, + sortText: completion.SortText.AutoImportSuggestions + }, + ...completion.statementKeywordsWithTypes + ], + preferences: { + includeCompletionsForModuleExports: true + } +}); + +verify.applyCodeActionFromCompletion("", { + name: "Bar", + source: "path2longer", + description: `Import 'Bar' from module "path2longer"`, + newFileContent: `import { Bar } from "path2longer";\n\nBa`, + preferences: { + includeCompletionsForModuleExports: true + } +});