Language service fixes by crisbeto · Pull Request #58719 · angular/angular · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/language-service/src/codefixes/code_fixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {CodeActionMeta, FixIdForCodeFixesAll} from './utils';
export const fixInvalidBananaInBoxMeta: CodeActionMeta = {
errorCodes: [ngErrorCode(ErrorCode.INVALID_BANANA_IN_BOX)],
getCodeActions({start, fileName, templateInfo}) {
const boundEvent = getTheBoundEventAtPosition(templateInfo, start);
const boundEvent =
templateInfo === null ? null : getTheBoundEventAtPosition(templateInfo, start);
if (boundEvent === null) {
return [];
}
Expand Down
16 changes: 5 additions & 11 deletions packages/language-service/src/codefixes/fix_missing_import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,13 @@ export const missingImportMeta: CodeActionMeta = {
},
};

function getCodeActions({
templateInfo,
start,
compiler,
formatOptions,
preferences,
errorCode,
tsLs,
}: CodeActionContext) {
function getCodeActions({templateInfo, start, compiler}: CodeActionContext) {
if (templateInfo === null) {
return [];
}

let codeActions: ts.CodeFixAction[] = [];
const checker = compiler.getTemplateTypeChecker();
const tsChecker = compiler.programDriver.getProgram().getTypeChecker();

const target = getTargetAtPosition(templateInfo.template, start);
if (target === null) {
return [];
Expand Down
9 changes: 3 additions & 6 deletions packages/language-service/src/codefixes/fix_missing_member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@

import tss from 'typescript';

import {
getTargetAtPosition,
getTcbNodesOfTemplateAtPosition,
TargetNodeKind,
} from '../template_target';
import {getTcbNodesOfTemplateAtPosition} from '../template_target';
import {getTemplateInfoAtPosition} from '../utils';

import {CodeActionMeta, convertFileTextChangeInTcb, FixIdForCodeFixesAll} from './utils';
Expand All @@ -37,7 +33,8 @@ export const missingMemberMeta: CodeActionMeta = {
errorCode,
tsLs,
}) {
const tcbNodesInfo = getTcbNodesOfTemplateAtPosition(templateInfo, start, compiler);
const tcbNodesInfo =
templateInfo === null ? null : getTcbNodesOfTemplateAtPosition(templateInfo, start, compiler);
if (tcbNodesInfo === null) {
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,78 @@ import {findFirstMatchingNode} from '../utils/ts_utils';
*/
export const fixUnusedStandaloneImportsMeta: CodeActionMeta = {
errorCodes: [ngErrorCode(ErrorCode.UNUSED_STANDALONE_IMPORTS)],
getCodeActions: () => [],
getCodeActions: ({start, fileName, compiler}) => {
const file = compiler.programDriver.getProgram().getSourceFile(fileName) || null;

if (file === null) {
return [];
}

const node = findFirstMatchingNode(file, {
filter: (n): n is tss.Identifier =>
tss.isIdentifier(n) && start >= n.getStart() && start <= n.getEnd(),
});
const parent = node?.parent || null;

if (node === null || parent === null) {
return [];
}

if (isFullyUnusedArray(node, parent)) {
return [
{
fixName: FixIdForCodeFixesAll.FIX_UNUSED_STANDALONE_IMPORTS,
fixId: FixIdForCodeFixesAll.FIX_UNUSED_STANDALONE_IMPORTS,
fixAllDescription: `Remove all unused imports`,
description: `Remove all unused imports`,
changes: [
{
fileName,
textChanges: [
{
span: {
start: parent.initializer.getStart(),
length: parent.initializer.getWidth(),
},
newText: '[]',
},
],
},
],
},
];
} else if (tss.isArrayLiteralExpression(parent)) {
const newArray = tss.factory.updateArrayLiteralExpression(
parent,
parent.elements.filter((el) => el !== node),
);

return [
{
fixName: FixIdForCodeFixesAll.FIX_UNUSED_STANDALONE_IMPORTS,
fixId: FixIdForCodeFixesAll.FIX_UNUSED_STANDALONE_IMPORTS,
fixAllDescription: `Remove all unused imports`,
description: `Remove unused import ${node.text}`,
changes: [
{
fileName,
textChanges: [
{
span: {
start: parent.getStart(),
length: parent.getWidth(),
},
newText: tss.createPrinter().printNode(tss.EmitHint.Unspecified, newArray, file),
},
],
},
],
},
];
}

return [];
},
fixIds: [FixIdForCodeFixesAll.FIX_UNUSED_STANDALONE_IMPORTS],
getAllCodeActions: ({diagnostics}) => {
const arrayUpdates = new Map<tss.ArrayLiteralExpression, Set<tss.Expression>>();
Expand All @@ -42,11 +113,7 @@ export const fixUnusedStandaloneImportsMeta: CodeActionMeta = {
// If the diagnostic is reported on the name of the `imports` array initializer, it means
// that all imports are unused so we can clear the entire array. Otherwise if it's reported
// on a single element, we only have to remove that element.
if (
tss.isPropertyAssignment(parent) &&
parent.name === node &&
tss.isArrayLiteralExpression(parent.initializer)
) {
if (isFullyUnusedArray(node, parent)) {
arraysToClear.add(parent.initializer);
} else if (tss.isArrayLiteralExpression(parent)) {
if (!arrayUpdates.has(parent)) {
Expand Down Expand Up @@ -93,3 +160,15 @@ export const fixUnusedStandaloneImportsMeta: CodeActionMeta = {
return {changes};
},
};

/** Checks whether a diagnostic was reported on a node where all imports are unused. */
function isFullyUnusedArray(
node: tss.Node,
parent: tss.Node,
): parent is tss.PropertyAssignment & {initializer: tss.ArrayLiteralExpression} {
return (
tss.isPropertyAssignment(parent) &&
parent.name === node &&
tss.isArrayLiteralExpression(parent.initializer)
);
}
2 changes: 1 addition & 1 deletion packages/language-service/src/codefixes/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {TemplateInfo} from '../utils';
* context will be provided to the `CodeActionMeta` which could handle the `errorCode`.
*/
export interface CodeActionContext {
templateInfo: TemplateInfo;
templateInfo: TemplateInfo | null;
fileName: string;
compiler: NgCompiler;
start: number;
Expand Down
6 changes: 1 addition & 5 deletions packages/language-service/src/language_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,17 +404,13 @@ export class LanguageService {
return [];
}

const templateInfo = getTemplateInfoAtPosition(fileName, start, compiler);
if (templateInfo === undefined) {
return [];
}
const diags = this.getSemanticDiagnostics(fileName);
if (diags.length === 0) {
return [];
}
return this.codeFixes.getCodeFixesAtPosition(
fileName,
templateInfo,
getTemplateInfoAtPosition(fileName, start, compiler) ?? null,
compiler,
start,
end,
Expand Down
81 changes: 75 additions & 6 deletions packages/language-service/test/code_fixes_spec.ts