Repository: microsoft/TypeScript
File: src/services/codefixes/convertToTypeOnlyImport.ts (TypeScript)

Code before:
    Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error.code,
    Diagnostics._0_is_a_type_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled.code,
];
const fixId = "convertToTypeOnlyImport";

registerCodeFix({
    errorCodes,
    getCodeActions: function getCodeActionsToConvertToTypeOnlyImport(context) {
        const declaration = getDeclaration(context.sourceFile, context.span.start);
        if (declaration) {
            const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration));
            const importDeclarationChanges = declaration.kind === SyntaxKind.ImportSpecifier && canConvertImportDeclarationForSpecifier(declaration, context.sourceFile, context.program)
                ? textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration.parent.parent.parent))
                : undefined;
            const mainAction = createCodeFixAction(
                fixId,
                changes,
                declaration.kind === SyntaxKind.ImportSpecifier
                    ? [Diagnostics.Use_type_0, declaration.propertyName?.text ?? declaration.name.text]
                    : Diagnostics.Use_import_type,
                fixId,
                Diagnostics.Fix_all_with_type_only_imports);

            if (some(importDeclarationChanges)) {
                return [
                    createCodeFixActionWithoutFixAll(fixId, importDeclarationChanges, Diagnostics.Use_import_type),
                    mainAction
                ];
            }
            return [mainAction];
        }
        return undefined;
    },
    fixIds: [fixId],
    getAllCodeActions: function getAllCodeActionsToConvertToTypeOnlyImport(context) {
        const fixedImportDeclarations: Set<ImportDeclaration> = new Set();
        return codeFixAll(context, errorCodes, (changes, diag) => {
            const errorDeclaration = getDeclaration(diag.file, diag.start);
            if (errorDeclaration?.kind === SyntaxKind.ImportDeclaration && !fixedImportDeclarations.has(errorDeclaration)) {
                doChange(changes, diag.file, errorDeclaration);
                fixedImportDeclarations.add(errorDeclaration);
            }
            else if (errorDeclaration?.kind === SyntaxKind.ImportSpecifier
                && !fixedImportDeclarations.has(errorDeclaration.parent.parent.parent)
                && canConvertImportDeclarationForSpecifier(errorDeclaration, diag.file, context.program)
            ) {
                doChange(changes, diag.file, errorDeclaration.parent.parent.parent);
            }
        });
    }
});

Review comment (suggestion):
I’m not sure if the two fix options should have different `fixName`s

Code after:
    Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error.code,
    Diagnostics._0_is_a_type_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled.code,
];
const fixId = "convertToTypeOnlyImport";

registerCodeFix({
    errorCodes,
    getCodeActions: function getCodeActionsToConvertToTypeOnlyImport(context) {
        const declaration = getDeclaration(context.sourceFile, context.span.start);
        if (declaration) {
            const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration));
            const importDeclarationChanges = declaration.kind === SyntaxKind.ImportSpecifier && canConvertImportDeclarationForSpecifier(declaration, context.sourceFile, context.program)
                ? textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration.parent.parent.parent))
                : undefined;
            const mainAction = createCodeFixAction(
                fixId,
                changes,
                declaration.kind === SyntaxKind.ImportSpecifier
                    ? [Diagnostics.Use_type_0, declaration.propertyName?.text ?? declaration.name.text]
                    : Diagnostics.Use_import_type,
                fixId,
                Diagnostics.Fix_all_with_type_only_imports);

            if (some(importDeclarationChanges)) {
                return [
                    createCodeFixActionWithoutFixAll(fixId, importDeclarationChanges, Diagnostics.Use_import_type),
                    mainAction
                ];
            }
            return [mainAction];
        }
        return undefined;
    },
    fixIds: [fixId],
    getAllCodeActions: function getAllCodeActionsToConvertToTypeOnlyImport(context) {
        const fixedImportDeclarations: Set<ImportDeclaration> = new Set();
        return codeFixAll(context, errorCodes, (changes, diag) => {
            const errorDeclaration = getDeclaration(diag.file, diag.start);
            if (errorDeclaration?.kind === SyntaxKind.ImportDeclaration && !fixedImportDeclarations.has(errorDeclaration)) {
                doChange(changes, diag.file, errorDeclaration);
                fixedImportDeclarations.add(errorDeclaration);
            }
            else if (errorDeclaration?.kind === SyntaxKind.ImportSpecifier
                && !fixedImportDeclarations.has(errorDeclaration.parent.parent.parent)
                && canConvertImportDeclarationForSpecifier(errorDeclaration, diag.file, context.program)
            ) {
                doChange(changes, diag.file, errorDeclaration.parent.parent.parent);
                fixedImportDeclarations.add(errorDeclaration.parent.parent.parent);
            }
            else if (errorDeclaration?.kind === SyntaxKind.ImportSpecifier) {
                doChange(changes, diag.file, errorDeclaration);