Skip to content

Commit 5f0b53c

Browse files
dylhunnpkozlowski-opensource
authored andcommitted
feat(language-service): Allow auto-imports to suggest multiple possible imports. (angular#47787)
This change allows the language service to suggest imports for *all* directives returned from the compiler, and for *all* import specifiers for each directive. The first change has immediate effect, since there could already be multiple directives with the same selector. The second change is future-proofing; the compiler currently only returns a single best specifier, but will return more in the future. PR Close angular#47787
1 parent d436a19 commit 5f0b53c

File tree

1 file changed

+36
-35
lines changed

1 file changed

+36
-35
lines changed

packages/language-service/src/codefixes/fix_missing_import.ts

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export const missingImportMeta: CodeActionMeta = {
3939
function getCodeActions(
4040
{templateInfo, start, compiler, formatOptions, preferences, errorCode, tsLs}:
4141
CodeActionContext) {
42+
let codeActions: ts.CodeFixAction[] = [];
43+
4244
const checker = compiler.getTemplateTypeChecker();
4345
const tsChecker = compiler.programDriver.getProgram().getTypeChecker();
4446

@@ -64,46 +66,45 @@ function getCodeActions(
6466
}
6567
const importOn = owningNgModule ?? templateInfo.component;
6668

67-
// Find all possible importable directives with a matching selector, and take one of them.
68-
// In the future, we could handle multiple matches as additional quick fixes.
69+
// Find all possible importable directives with a matching selector.
6970
const allPossibleDirectives = checker.getPotentialTemplateDirectives(templateInfo.component);
7071
const matchingDirectives =
7172
getDirectiveMatchesForElementTag(missingElement, allPossibleDirectives);
72-
if (matchingDirectives.size === 0) {
73-
return [];
74-
}
75-
const bestMatch: PotentialDirective = matchingDirectives.values().next().value;
76-
const bestMatchSymbol = bestMatch.tsSymbol.valueDeclaration;
77-
78-
// Get possible trait imports corresponding to the recommended directive. Only use the
79-
// compiler's best import; in the future, we could suggest multiple imports if they exist.
80-
const potentialImports = checker.getPotentialImportsFor(bestMatch, importOn);
81-
if (potentialImports.length === 0) {
82-
return [];
83-
}
84-
const potentialImport = potentialImports[0];
85-
86-
// Update the imports on the TypeScript file and Angular decorator.
87-
let [fileImportChanges, importName] = updateImportsForTypescriptFile(
88-
tsChecker, importOn.getSourceFile(), potentialImport, bestMatchSymbol.getSourceFile());
89-
let traitImportChanges = updateImportsForAngularTrait(checker, importOn, importName);
90-
91-
// All quick fixes should always update the trait import; however, the TypeScript import might
92-
// already be present.
93-
if (traitImportChanges.length === 0) {
94-
return [];
73+
const matches = matchingDirectives.values();
74+
75+
// Generate suggestions for each possible match.
76+
for (let currMatch of matches) {
77+
const currMatchSymbol = currMatch.tsSymbol.valueDeclaration;
78+
79+
// Get possible trait imports corresponding to the recommended directive.
80+
const potentialImports = checker.getPotentialImportsFor(currMatch, importOn);
81+
82+
// For each possible import specifier, create a suggestion.
83+
for (let potentialImport of potentialImports) {
84+
// Update the imports on the TypeScript file and Angular decorator.
85+
let [fileImportChanges, importName] = updateImportsForTypescriptFile(
86+
tsChecker, importOn.getSourceFile(), potentialImport, currMatchSymbol.getSourceFile());
87+
let traitImportChanges = updateImportsForAngularTrait(checker, importOn, importName);
88+
89+
// All quick fixes should always update the trait import; however, the TypeScript import might
90+
// already be present.
91+
if (traitImportChanges.length === 0) {
92+
continue;
93+
}
94+
95+
// Create a code action for this import.
96+
codeActions.push({
97+
fixName: FixIdForCodeFixesAll.FIX_MISSING_IMPORT,
98+
description: `Import ${importName} from '${potentialImport.moduleSpecifier}' on ${
99+
importOn.name!.text}`,
100+
changes: [{
101+
fileName: importOn.getSourceFile().fileName,
102+
textChanges: [...fileImportChanges, ...traitImportChanges],
103+
}]
104+
});
105+
}
95106
}
96107

97-
// Create the code action to insert the new imports.
98-
const codeActions: ts.CodeFixAction[] = [{
99-
fixName: FixIdForCodeFixesAll.FIX_MISSING_IMPORT,
100-
description:
101-
`Import ${importName} from '${potentialImport.moduleSpecifier}' on ${importOn.name!.text}`,
102-
changes: [{
103-
fileName: importOn.getSourceFile().fileName,
104-
textChanges: [...fileImportChanges, ...traitImportChanges],
105-
}],
106-
}];
107108
return codeActions;
108109
}
109110

0 commit comments

Comments
 (0)