[codeInsight] Select inspection profile for code cleanup on save

#IJPL-60378 Fixed

GitOrigin-RevId: aa2d004c7a62183c6a3763c0b493a13332522e24
This commit is contained in:
Louis Vignier
2024-09-13 16:01:01 +02:00
committed by intellij-monorepo-bot
parent ddf360b575
commit fd0d3ebfc7
6 changed files with 176 additions and 3 deletions

View File

@@ -520,6 +520,8 @@ actions.on.save.page.popup.title.arrangement.settings=Open Arrangement Settings
actions.on.save.page.checkbox.run.code.cleanup=Run code cleanup
actions.on.save.page.code.cleanup.comment=Applies fixes from the code cleanup inspections
actions.on.save.page.link.configure.inspections=Configure inspections\u2026
actions.on.save.page.code.cleanup.project.profile=Project Profile
actions.on.save.page.code.cleanup.profile=Profile: {0}
non.ascii.chars.inspection.non.ascii.top.label=Report Non-ASCII Characters In:
non.ascii.chars.inspection.option.files.containing.bom.checkbox=Report files starting with a BOM

View File

@@ -2531,6 +2531,7 @@ f:com.intellij.codeInsight.actions.CodeCleanupCodeProcessor
- <init>(com.intellij.codeInsight.actions.AbstractLayoutCodeProcessor,com.intellij.openapi.editor.SelectionModel):V
- <init>(com.intellij.openapi.project.Project,com.intellij.psi.PsiFile[],java.lang.Runnable,Z):V
- s:getProgressText():java.lang.String
- setProfile(com.intellij.codeInspection.ex.InspectionProfileImpl):V
com.intellij.codeInsight.actions.DirectoryFormattingOptions
- com.intellij.codeInsight.actions.ReformatFilesOptions
- a:isIncludeSubdirectories():Z
@@ -2784,7 +2785,16 @@ f:com.intellij.codeInsight.actions.onSave.CodeCleanupOnSaveActionInfo
- <init>(com.intellij.ide.actionsOnSave.ActionOnSaveContext):V
- getActionLinks():java.util.List
- getComment():com.intellij.ide.actionsOnSave.ActionOnSaveComment
- getDropDownLinks():java.util.List
- s:isCodeCleanupOnSaveEnabled(com.intellij.openapi.project.Project):Z
f:com.intellij.codeInsight.actions.onSave.CodeCleanupOnSaveOptions
- com.intellij.openapi.components.PersistentStateComponent
- PROFILE:java.lang.String
- <init>(com.intellij.openapi.project.Project):V
- getInspectionProfile():com.intellij.codeInspection.ex.InspectionProfileImpl
- s:getInstance(com.intellij.openapi.project.Project):com.intellij.codeInsight.actions.onSave.CodeCleanupOnSaveOptions
- getState():com.intellij.codeInsight.actions.onSave.CodeCleanupOnSaveOptions
- loadState(com.intellij.codeInsight.actions.onSave.CodeCleanupOnSaveOptions):V
a:com.intellij.codeInsight.actions.onSave.FormatOnSaveActionInfoBase
- com.intellij.ide.actionsOnSave.ActionOnSaveInfo
- pa:addApplicableFileTypes(java.util.Collection):V

View File

@@ -4,6 +4,7 @@ package com.intellij.codeInsight.actions;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ex.GlobalInspectionContextBase;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
@@ -19,6 +20,7 @@ import java.util.concurrent.FutureTask;
public final class CodeCleanupCodeProcessor extends AbstractLayoutCodeProcessor {
private SelectionModel mySelectionModel = null;
private InspectionProfileImpl myProfile = null;
public CodeCleanupCodeProcessor(@NotNull AbstractLayoutCodeProcessor previousProcessor) {
super(previousProcessor, CodeInsightBundle.message("command.cleanup.code"), getProgressText());
@@ -42,11 +44,19 @@ public final class CodeCleanupCodeProcessor extends AbstractLayoutCodeProcessor
return new FutureTask<>(() -> {
if (!file.isValid()) return false;
Collection<TextRange> ranges = getRanges(file, processChangedTextOnly);
GlobalInspectionContextBase.cleanupElements(myProject, null, descriptor -> isInRanges(ranges, descriptor), file);
if (myProfile == null) {
GlobalInspectionContextBase.cleanupElements(myProject, null, descriptor -> isInRanges(ranges, descriptor), file);
} else {
GlobalInspectionContextBase.cleanupElements(myProject, null, descriptor -> isInRanges(ranges, descriptor), myProfile, file);
}
return true;
});
}
public void setProfile(InspectionProfileImpl profile) {
myProfile = profile;
}
private Collection<TextRange> getRanges(@NotNull PsiFile file, boolean processChangedTextOnly) {
if (mySelectionModel != null) {
return getSelectedRanges(mySelectionModel);

View File

@@ -1,20 +1,33 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.actions.onSave;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.actionsOnSave.ActionOnSaveComment;
import com.intellij.ide.actionsOnSave.ActionOnSaveContext;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.ui.header.InspectionToolsConfigurable;
import com.intellij.ui.components.ActionLink;
import com.intellij.ui.components.DropDownLink;
import com.intellij.ui.dsl.listCellRenderer.BuilderKt;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
public final class CodeCleanupOnSaveActionInfo extends ActionOnSaveInfoBase {
private static final String CODE_CLEANUP_ON_SAVE_PROPERTY = "code.cleanup.on.save";
private static final boolean CODE_CLEANUP_ON_SAVE_DEFAULT = false;
private String myProfile;
public static boolean isCodeCleanupOnSaveEnabled(@NotNull Project project) {
return PropertiesComponent.getInstance(project).getBoolean(CODE_CLEANUP_ON_SAVE_PROPERTY, CODE_CLEANUP_ON_SAVE_DEFAULT);
@@ -25,6 +38,7 @@ public final class CodeCleanupOnSaveActionInfo extends ActionOnSaveInfoBase {
CodeInsightBundle.message("actions.on.save.page.checkbox.run.code.cleanup"),
CODE_CLEANUP_ON_SAVE_PROPERTY,
CODE_CLEANUP_ON_SAVE_DEFAULT);
myProfile = CodeCleanupOnSaveOptions.getInstance(getProject()).PROFILE;
}
@Override
@@ -37,4 +51,82 @@ public final class CodeCleanupOnSaveActionInfo extends ActionOnSaveInfoBase {
return List.of(createGoToPageInSettingsLink(CodeInsightBundle.message("actions.on.save.page.link.configure.inspections"),
InspectionToolsConfigurable.ID));
}
}
private record ProfileOption(@NlsSafe @Nullable String profileName) {}
@Override
public @NotNull List<? extends DropDownLink<?>> getDropDownLinks() {
List<ProfileOption> profiles = new ArrayList<>();
// Project Profile
profiles.add(new ProfileOption(null));
// Stored in project
List<ProfileOption> projectProfiles = ContainerUtil.map(
InspectionProfileManager.getInstance(getProject()).getProfiles(),
p -> new ProfileOption(p.getDisplayName())
);
profiles.addAll(projectProfiles);
// Stored in IDE
List<ProfileOption> ideProfiles = ContainerUtil.map(
InspectionProfileManager.getInstance().getProfiles(),
p -> new ProfileOption(p.getDisplayName())
);
profiles.addAll(ideProfiles);
// Separators
final var separators = new HashMap<ProfileOption, String>();
if (!projectProfiles.isEmpty()) {
separators.put(projectProfiles.get(0), IdeBundle.message("separator.scheme.stored.in", IdeBundle.message("scheme.project")));
}
if (!ideProfiles.isEmpty()) {
separators.put(ideProfiles.get(0), IdeBundle.message("separator.scheme.stored.in", IdeBundle.message("scheme.project")));
}
var selectedOption = getSelectedProfile(profiles);
DropDownLink<ProfileOption> inspectionProfileLink = new DropDownLink<>(
selectedOption,
profiles,
option -> {
myProfile = option.profileName;
},
true) {
@Override
protected @Nls @NotNull String itemToString(ProfileOption item) {
if (item.profileName == null) return CodeInsightBundle.message("actions.on.save.page.code.cleanup.project.profile");
return CodeInsightBundle.message("actions.on.save.page.code.cleanup.profile", item.profileName);
}
@Override
public @NotNull ListCellRenderer<? super ProfileOption> createRenderer() {
return BuilderKt.groupedTextListCellRenderer(p -> {
if (p.profileName == null) return CodeInsightBundle.message("actions.on.save.page.code.cleanup.project.profile");
return p.profileName();
}, p -> separators.getOrDefault(p, null));
}
};
return List.of(inspectionProfileLink);
}
private ProfileOption getSelectedProfile(List<ProfileOption> profiles) {
if (myProfile == null) return profiles.get(0);
final var selected = ContainerUtil.find(profiles, p -> p != null && Objects.equals(p.profileName, myProfile));
if (selected == null) return profiles.get(0); // If the profile has been deleted, default to project profile
return selected;
}
@Override
protected boolean isModified() {
return super.isModified() || !Objects.equals(myProfile, CodeCleanupOnSaveOptions.getInstance(getProject()).PROFILE);
}
@Override
protected void apply() {
super.apply();
CodeCleanupOnSaveOptions.getInstance(getProject()).PROFILE = myProfile;
}
}

View File

@@ -0,0 +1,58 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.actions.onSave;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@Service(Service.Level.PROJECT)
@State(name = "CodeCleanupOnSaveOptions", storages = @Storage(StoragePathMacros.WORKSPACE_FILE))
public final class CodeCleanupOnSaveOptions implements PersistentStateComponent<CodeCleanupOnSaveOptions> {
public static @NotNull CodeCleanupOnSaveOptions getInstance(@NotNull Project project) { return project.getService(CodeCleanupOnSaveOptions.class); }
private Project myProject;
private static Logger LOG = Logger.getInstance(CodeCleanupOnSaveOptions.class);
public @Nullable String PROFILE = null;
public CodeCleanupOnSaveOptions(Project project) {
myProject = project;
}
public InspectionProfileImpl getInspectionProfile() {
final var projectProfileManager = InspectionProfileManager.getInstance(myProject);
if (PROFILE == null) return projectProfileManager.getCurrentProfile();
final var projectProfile = ContainerUtil.find(
projectProfileManager.getProfiles(),
p -> Objects.equals(p.getName(), PROFILE)
);
if (projectProfile != null) return projectProfile;
final var appProfile = ContainerUtil.find(
InspectionProfileManager.getInstance().getProfiles(),
p -> Objects.equals(p.getName(), PROFILE)
);
if (appProfile != null) return projectProfile;
LOG.warn("Can't find profile " + PROFILE + ". Using project profile instead.");
return projectProfileManager.getCurrentProfile();
}
@Override
public CodeCleanupOnSaveOptions getState() {
return this;
}
@Override
public void loadState(@NotNull CodeCleanupOnSaveOptions state) {
XmlSerializerUtil.copyBean(state, this);
}
}

View File

@@ -83,6 +83,7 @@ internal class FormatOnSaveAction : ActionOnSave() {
if (CodeCleanupOnSaveActionInfo.isCodeCleanupOnSaveEnabled(project) && !isDumb(project)) {
processor = CodeCleanupCodeProcessor(processor)
processor.setProfile(CodeCleanupOnSaveOptions.getInstance(project).getInspectionProfile())
}
// This guarantees that per-file undo chain won't break and there won't be the "Following files affected by this action have been already changed" modal error dialog.