diff --git a/plugins/grazie/src/test/kotlin/com/intellij/grazie/ide/language/JavaSupportTest.kt b/plugins/grazie/src/test/kotlin/com/intellij/grazie/ide/language/JavaSupportTest.kt
index 5bdfeee42034..ab17c674ba84 100644
--- a/plugins/grazie/src/test/kotlin/com/intellij/grazie/ide/language/JavaSupportTest.kt
+++ b/plugins/grazie/src/test/kotlin/com/intellij/grazie/ide/language/JavaSupportTest.kt
@@ -13,6 +13,8 @@ import com.intellij.spellchecker.settings.SpellCheckerSettings
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
import com.intellij.tools.ide.metrics.benchmark.Benchmark
+import com.intellij.ui.ChooserInterceptor
+import com.intellij.ui.UiInterceptors
import java.util.function.Consumer
@@ -123,7 +125,7 @@ class JavaSupportTest : GrazieTestBase() {
)
}
- fun `test meaningful suggestions in RenameTo action`() {
+ fun `test meaningful single suggestion in RenameTo action`() {
myFixture.configureByText("a.java", """
class A {
void foo() {
@@ -132,12 +134,32 @@ class JavaSupportTest : GrazieTestBase() {
}
""")
myFixture.checkHighlighting()
+ val intention = myFixture.findSingleIntention("Typo: Rename to 'targetDir'")
+ myFixture.launchAction(intention)
+ myFixture.checkResult("""
+ class A {
+ void foo() {
+ int targetDir = 1;
+ }
+ }
+ """)
+ }
+
+ fun `test multiple suggestions in RenameTo action`() {
+ myFixture.configureByText("a.java", """
+ class A {
+ void foo() {
+ int barek = 1;
+ }
+ }
+ """)
+ myFixture.checkHighlighting()
val intention = myFixture.findSingleIntention("Typo: Rename to…")
myFixture.launchAction(intention)
myFixture.checkResult("""
class A {
void foo() {
- int targetDir = 1;
+ int bark = 1;
}
}
""")
diff --git a/plugins/grazie/src/test/kotlin/com/intellij/grazie/spellchecker/inspection/quickfixes/PlainTextSpellCheckerFixesTest.java b/plugins/grazie/src/test/kotlin/com/intellij/grazie/spellchecker/inspection/quickfixes/PlainTextSpellCheckerFixesTest.java
index 3b728c846f7d..cda448d878ea 100644
--- a/plugins/grazie/src/test/kotlin/com/intellij/grazie/spellchecker/inspection/quickfixes/PlainTextSpellCheckerFixesTest.java
+++ b/plugins/grazie/src/test/kotlin/com/intellij/grazie/spellchecker/inspection/quickfixes/PlainTextSpellCheckerFixesTest.java
@@ -19,6 +19,8 @@ import com.intellij.spellchecker.quickfixes.ChangeTo;
import com.intellij.spellchecker.quickfixes.RenameTo;
import com.intellij.spellchecker.quickfixes.SaveTo;
+import java.util.List;
+
public class PlainTextSpellCheckerFixesTest extends AbstractSpellCheckerFixesTest {
@Override
protected String getExtension() {
@@ -54,7 +56,7 @@ public class PlainTextSpellCheckerFixesTest extends AbstractSpellCheckerFixesTes
}
public void testEmptyRenameTo() {
- doNoQuickFixTest(RenameTo.getFixName());
+ doNoQuickFixTest(RenameTo.getFixName(List.of()));
}
public void testEmptySaveTo() {
@@ -66,10 +68,10 @@ public class PlainTextSpellCheckerFixesTest extends AbstractSpellCheckerFixesTes
}
public void testNoTypoRenameTo() {
- doNoQuickFixTest(RenameTo.getFixName());
+ doNoQuickFixTest(RenameTo.getFixName(List.of()));
}
public void testSimpleWordRenameTo() {
- doNoQuickFixTest(RenameTo.getFixName());
+ doNoQuickFixTest(RenameTo.getFixName(List.of()));
}
}
diff --git a/plugins/properties/tests/testSrc/com/intellij/lang/properties/rename/PropertyRenameToTest.kt b/plugins/properties/tests/testSrc/com/intellij/lang/properties/rename/PropertyRenameToTest.kt
index a75201ecebfc..e46f1ba1613c 100644
--- a/plugins/properties/tests/testSrc/com/intellij/lang/properties/rename/PropertyRenameToTest.kt
+++ b/plugins/properties/tests/testSrc/com/intellij/lang/properties/rename/PropertyRenameToTest.kt
@@ -23,6 +23,6 @@ class PropertyRenameToTest : BasePlatformTestCase() {
myFixture.configureByText("a.properties", "helloworld=value")
myFixture.enableInspections(GrazieSpellCheckingInspection())
myFixture.checkHighlighting()
- myFixture.getAvailableIntention("Typo: Rename to…") ?: error("RenameTo intention is not available")
+ myFixture.getAvailableIntention("Typo: Rename to 'hello-world'") ?: error("RenameTo intention is not available")
}
}
diff --git a/spellchecker/resources/messages/SpellCheckerBundle.properties b/spellchecker/resources/messages/SpellCheckerBundle.properties
index ce5b32cc540e..9229f9e07f8d 100644
--- a/spellchecker/resources/messages/SpellCheckerBundle.properties
+++ b/spellchecker/resources/messages/SpellCheckerBundle.properties
@@ -1,6 +1,7 @@
change.to.title=Fix typo
change.to.tooltip=Replace with ''{0}''
rename.to=Typo: Rename to\u2026
+rename.to.0=Typo: Rename to ''{0}''
proofread=Proofreading
spelling=Spelling
spellchecking.inspection.name=Spelling
diff --git a/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java b/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java
index 007a20a4e0a0..5106e6928df2 100644
--- a/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java
+++ b/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java
@@ -3,8 +3,10 @@ package com.intellij.spellchecker.quickfixes;
import com.intellij.codeInsight.intention.EventTrackingIntentionAction;
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils;
-import com.intellij.modcommand.ModPsiUpdater;
-import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
+import com.intellij.codeInspection.IntentionAndQuickFixAction;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformCoreDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Iconable;
@@ -12,7 +14,9 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.refactoring.rename.RenameUtil;
+import com.intellij.refactoring.RefactoringActionHandler;
+import com.intellij.refactoring.RefactoringActionHandlerFactory;
+import com.intellij.refactoring.rename.*;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.statistics.SpellcheckerActionStatistics;
import com.intellij.spellchecker.statistics.SpellcheckerRateTracker;
@@ -23,16 +27,16 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
-public class RenameTo extends PsiUpdateModCommandQuickFix implements Iconable, EventTrackingIntentionAction {
+public class RenameTo extends IntentionAndQuickFixAction implements Iconable, EventTrackingIntentionAction {
private final String typo;
private final TextRange range;
private final SmartPsiElementPointer pointer;
private final SpellcheckerRateTracker tracker;
- private final List suggestions = new ArrayList<>();
+ private final SequencedSet suggestions = new LinkedHashSet<>();
+ private SmartPsiElementPointer namedPointer;
public RenameTo(String typo, TextRange range, PsiElement psi, SpellcheckerRateTracker tracker) {
this.typo = typo;
@@ -42,16 +46,50 @@ public class RenameTo extends PsiUpdateModCommandQuickFix implements Iconable, E
}
@Override
- public @NotNull String getFamilyName() {
- return getFixName();
+ public boolean isAvailable(@NotNull Project project, @Nullable Editor editor, PsiFile psiFile) {
+ PsiElement element = Objects.requireNonNull(pointer.getElement());
+ var presentationName = getPresentationName(element);
+ if (presentationName == null) return false;
+ generateSuggestions(presentationName.getSecond(), element);
+ this.namedPointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(presentationName.getFirst());
+ if (suggestions.isEmpty()) return false;
+ return true;
}
@Override
- protected void applyFix(@NotNull Project project, @NotNull PsiElement psiElement, @NotNull ModPsiUpdater updater) {
- var name = getPresentationName(psiElement);
- if (name == null) return;
- generateSuggestions(name.second, psiElement);
- updater.rename(name.first, psiElement, suggestions);
+ public boolean startInWriteAction() {
+ return false;
+ }
+
+ @Override
+ public @NotNull String getName() {
+ return getFixName(suggestions);
+ }
+
+ @Override
+ public @NotNull String getFamilyName() {
+ return getFixName(suggestions);
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, PsiFile psiFile, @Nullable Editor editor) {
+ PsiElement element = namedPointer.getElement() == null ? null : namedPointer.getElement();
+ if (element == null) return;
+
+ if (suggestions.size() == 1) {
+ runRenamer(element, suggestions.getFirst());
+ }
+ else {
+ var context = DataManager.getInstance().getDataContext(editor.getContentComponent());
+ DataContext contextWithSuggestions = dataId -> {
+ if (PsiElementRenameHandler.NAME_SUGGESTIONS.is(dataId)) return new ArrayList<>(suggestions);
+ if (PlatformCoreDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) return new PsiElement[]{element};
+ return context.getData(dataId);
+ };
+ RefactoringActionHandler handler = getRenameHandler(contextWithSuggestions);
+ handler.invoke(project, editor, psiFile, contextWithSuggestions);
+ }
+
if (!IntentionPreviewUtils.isIntentionPreviewActive()) {
SpellcheckerActionStatistics.renameToPerformed(tracker, suggestions.size());
}
@@ -64,8 +102,10 @@ public class RenameTo extends PsiUpdateModCommandQuickFix implements Iconable, E
}
}
- public static @Nls String getFixName() {
- return SpellCheckerBundle.message("rename.to");
+ public static @Nls String getFixName(SequencedCollection suggestions) {
+ return suggestions.size() == 1 ?
+ SpellCheckerBundle.message("rename.to.0", suggestions.getFirst()) :
+ SpellCheckerBundle.message("rename.to");
}
@Override
@@ -83,6 +123,12 @@ public class RenameTo extends PsiUpdateModCommandQuickFix implements Iconable, E
return new Pair<>(namedElement, name);
}
+ private static RefactoringActionHandler getRenameHandler(DataContext dataContext) {
+ RenameHandler handler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext);
+ if (handler == null) return RefactoringActionHandlerFactory.getInstance().createRenameHandler();
+ return handler;
+ }
+
private void generateSuggestions(String name, PsiElement element) {
if (suggestions.isEmpty()) {
TextRange range = this.range.shiftLeft(element.getText().indexOf(name));
@@ -93,4 +139,8 @@ public class RenameTo extends PsiUpdateModCommandQuickFix implements Iconable, E
.forEach(suggestions::add);
}
}
+
+ private void runRenamer(PsiElement element, String suggestion) {
+ new RenameProcessor(pointer.getProject(), element, suggestion, true, true).run();
+ }
}
\ No newline at end of file