[mod-command] EntryPointsManagerBase as OptionControllerProvider

The corresponding intention actions converted to ModCommand now
Part of IDEA-337707 Create a uniform API to read/update various persistent settings

GitOrigin-RevId: 3a508e2a97fd2f1967c78aef2e82b661d1d8325a
This commit is contained in:
Tagir Valeev
2023-11-14 11:01:07 +00:00
committed by intellij-monorepo-bot
parent 96cc075b6f
commit 34479d8af2
7 changed files with 76 additions and 131 deletions
@@ -331,5 +331,6 @@
<projectService serviceInterface="com.intellij.java.analysis.OuterModelsModificationTrackerManager"
serviceImplementation="com.intellij.lang.OuterModelsModificationTrackerManagerImpl"/>
<optionController implementation="com.intellij.codeInspection.ex.EntryPointsManagerBase$Provider"/>
</extensions>
</idea-plugin>
@@ -5,27 +5,29 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.MetaAnnotationUtil;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInsight.options.JavaClassValidator;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.codeInspection.options.OptionContainer;
import com.intellij.codeInspection.options.OptionController;
import com.intellij.codeInspection.options.OptionControllerProvider;
import com.intellij.codeInspection.reference.*;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.configurationStore.XmlSerializer;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.modcommand.Presentation;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.JDOMExternalizableStringList;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Tag;
@@ -40,7 +42,8 @@ import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@State(name = "EntryPointsManager")
public abstract class EntryPointsManagerBase extends EntryPointsManager implements PersistentStateComponent<Element> {
public abstract class EntryPointsManagerBase extends EntryPointsManager implements PersistentStateComponent<Element>,
OptionContainer {
@ApiStatus.Internal
public static final ExtensionPointName<EntryPoint> DEAD_CODE_EP_NAME = new ExtensionPointName<>("com.intellij.deadCode");
@@ -622,21 +625,31 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
}
}
public class AddImplicitlyWriteAnnotation implements IntentionAction, LocalQuickFix {
@NotNull
private final String myQualifiedName;
@Override
public @NotNull OptionController getOptionController() {
return OptionContainer.super.getOptionController()
.withRootPane(() -> OptPane.pane(
OptPane.stringList("myWriteAnnotations", JavaBundle.message("separator.mark.field.as.implicitly.written.if.annotated.by"),
new JavaClassValidator().annotationsOnly()),
OptPane.stringList("ADDITIONAL_ANNOTATIONS", JavaBundle.message("separator.mark.as.entry.point.if.annotated.by"),
new JavaClassValidator().annotationsOnly()))
);
}
public AddImplicitlyWriteAnnotation(@NotNull String qualifiedName) {myQualifiedName = qualifiedName;}
private static class AddAnnotation implements ModCommandAction {
private final @NotNull String myQualifiedName;
private final @NotNull String myBindId;
private final @NotNull @IntentionName String myMessage;
@Override
@NotNull
public String getText() {
return QuickFixBundle.message("fix.add.write.annotation.text", myQualifiedName);
private AddAnnotation(@NotNull String qualifiedName, @NotNull String bindId, @NotNull @IntentionName String message) {
myQualifiedName = qualifiedName;
myBindId = bindId;
myMessage = message;
}
@Override
public @NotNull String getName() {
return getText();
public @NotNull Presentation getPresentation(@NotNull ActionContext context) {
return Presentation.of(myMessage);
}
@Override
@@ -646,59 +659,49 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
performAction(descriptor.getStartElement().getContainingFile());
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
return new IntentionPreviewInfo.Html(QuickFixBundle.message("fix.add.write.annotation.description", myQualifiedName));
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
return new IntentionPreviewInfo.Html(QuickFixBundle.message("fix.add.write.annotation.description", myQualifiedName));
}
@Override
public boolean isAvailable(@NotNull Project project1, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
performAction(file);
}
private void performAction(@NotNull PsiFile file) {
Project project = file.getProject();
VirtualFile vFile = file.getVirtualFile();
doAddAnnotation(project);
UndoManager.getInstance(project).undoableActionPerformed(new BasicUndoableAction(vFile) {
@Override
public void undo() {
if (myWriteAnnotations.removeAll(List.of(myQualifiedName))) {
ProjectInspectionProfileManager.getInstance(project).fireProfileChanged();
}
}
@Override
public void redo() {
doAddAnnotation(project);
}
public @NotNull ModCommand perform(@NotNull ActionContext context) {
return ModCommand.updateOption(context.file(), myBindId, oldValue -> {
@SuppressWarnings("unchecked") List<String> list = (List<String>)oldValue;
list.add(myQualifiedName);
list.sort(null);
return list;
});
}
}
private void doAddAnnotation(@NotNull Project project) {
if (!myWriteAnnotations.contains(myQualifiedName)) {
myWriteAnnotations.add(myQualifiedName);
ProjectInspectionProfileManager.getInstance(project).fireProfileChanged();
}
/**
* @param qualifiedName annotation qualified name
* @return an action that adds the specified annotation qualified name to the list of implicitly written fields annotations
*/
public static @NotNull ModCommandAction createAddImplicitWriteAnnotation(@NotNull String qualifiedName) {
return new AddAnnotation(qualifiedName, "EntryPointsManager.myWriteAnnotations",
QuickFixBundle.message("fix.add.write.annotation.text", qualifiedName));
}
/**
* @param qualifiedName annotation qualified name
* @return an action that adds the specified annotation qualified name to the list of entry point annotations
*/
public static @NotNull ModCommandAction createAddEntryPointAnnotation(@NotNull String qualifiedName) {
return new AddAnnotation(qualifiedName, "EntryPointsManager.ADDITIONAL_ANNOTATIONS",
QuickFixBundle.message("fix.unused.symbol.injection.text", qualifiedName));
}
/**
* Provides bindId = "EntryPointsManager.myWriteAnnotations" and "EntryPointsManager.ADDITIONAL_ANNOTATIONS"
* lists to control entry points
*/
public static final class Provider implements OptionControllerProvider {
@Override
public @NotNull OptionController forContext(@NotNull PsiElement context) {
Project project = context.getProject();
return getInstance(project).getOptionController()
.onValueSet((bindId, value) -> ProjectInspectionProfileManager.getInstance(project).fireProfileChanged());
}
@Override
public boolean startInWriteAction() {
return false;
public @NotNull String name() {
return "EntryPointsManager";
}
}
}
@@ -21,7 +21,6 @@ import com.intellij.codeInspection.dataFlow.fix.DeleteSwitchLabelFix;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
import com.intellij.diagnostic.CoreAttachmentFactory;
import com.intellij.ide.scratch.ScratchUtil;
import com.intellij.java.JavaBundle;
@@ -746,18 +745,13 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
@Override
public IntentionAction createAddToDependencyInjectionAnnotationsFix(@NotNull Project project,
@NotNull String qualifiedName) {
final EntryPointsManagerBase entryPointsManager = EntryPointsManagerBase.getInstance(project);
return SpecialAnnotationsUtil.createAddToSpecialAnnotationsListIntentionAction(
QuickFixBundle.message("fix.unused.symbol.injection.text", qualifiedName),
QuickFixBundle.message("fix.unused.symbol.injection.family"),
JavaBundle.message("separator.mark.as.entry.point.if.annotated.by"), entryPointsManager.ADDITIONAL_ANNOTATIONS, qualifiedName);
return EntryPointsManagerBase.createAddEntryPointAnnotation(qualifiedName).asIntention();
}
@NotNull
@Override
public IntentionAction createAddToImplicitlyWrittenFieldsFix(@NotNull Project project, @NotNull final String qualifiedName) {
EntryPointsManagerBase entryPointsManagerBase = EntryPointsManagerBase.getInstance(project);
return entryPointsManagerBase.new AddImplicitlyWriteAnnotation(qualifiedName);
return EntryPointsManagerBase.createAddImplicitWriteAnnotation(qualifiedName).asIntention();
}
@NotNull
@@ -2,8 +2,6 @@
package com.intellij.codeInspection.util;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInspection.ui.InspectionOptionsPanel;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.ClassFilter;
@@ -14,15 +12,11 @@ import com.intellij.openapi.actionSystem.ActionToolbarPosition;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
@@ -30,18 +24,14 @@ import com.intellij.ui.SortedListModel;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBList;
import com.intellij.util.IconUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UI;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.awt.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
@@ -163,47 +153,4 @@ public final class SpecialAnnotationsUtil {
.resizeY(true)
.createPanel();
}
public static IntentionAction createAddToSpecialAnnotationsListIntentionAction(final @IntentionName String text,
final @IntentionFamilyName String family,
final @Nls String listTitle,
final List<String> targetList,
final String qualifiedName) {
return new IntentionAction() {
@Override
@NotNull
public String getText() {
return text;
}
@Override
@NotNull
public String getFamilyName() {
return family;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
targetList.add(qualifiedName);
Collections.sort(targetList);
ProjectInspectionProfileManager.getInstance(project).fireProfileChanged();
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
List<@NlsSafe String> prefixes = StreamEx.of(targetList).append(qualifiedName).sorted().toList();
return IntentionPreviewInfo.addListOption(prefixes, qualifiedName, listTitle);
}
};
}
}
@@ -51,7 +51,7 @@ public class FieldMayBeFinalInspection extends BaseInspection implements Cleanup
PsiField field = (PsiField)infos[0];
fixes.add(MakeFieldFinalFix.buildFixUnconditional(field));
SpecialAnnotationsUtilBase.processUnknownAnnotations(field, annoName -> {
fixes.add(EntryPointsManagerBase.getInstance(field.getProject()).new AddImplicitlyWriteAnnotation(annoName));
fixes.add(LocalQuickFix.from(EntryPointsManagerBase.createAddImplicitWriteAnnotation(annoName)));
return true;
});
return fixes.toArray(LocalQuickFix.EMPTY_ARRAY);
@@ -6,4 +6,4 @@ annotation class XXX
@XXX
class <caret>UnusedClass
// FUS_QUICKFIX_NAME: com.intellij.codeInspection.util.SpecialAnnotationsUtil$4
// FUS_QUICKFIX_NAME: com.intellij.codeInspection.ex.EntryPointsManagerBase$AddAnnotation
@@ -6,4 +6,4 @@ annotation class XXX
@XXX
class <caret>UnusedClass
// FUS_QUICKFIX_NAME: com.intellij.codeInspection.util.SpecialAnnotationsUtil$4
// FUS_QUICKFIX_NAME: com.intellij.codeInspection.ex.EntryPointsManagerBase$AddAnnotation