diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java index fdb7e2e6a682..67696314c974 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java @@ -29,21 +29,15 @@ import java.util.HashSet; public class InputValidator implements IntroduceVariableBase.Validator { private final Project myProject; - private final PsiElement myAnchorStatementIfAll; - private final PsiElement myAnchorStatement; private final ExpressionOccurrenceManager myOccurenceManager; private final IntroduceVariableBase myIntroduceVariableBase; @Override public boolean isOK(IntroduceVariableSettings settings) { String name = settings.getEnteredName(); - final PsiElement anchor; - final boolean replaceAllOccurrences = settings.isReplaceAllOccurrences(); - if (replaceAllOccurrences) { - anchor = myAnchorStatementIfAll; - } else { - anchor = myAnchorStatement; - } + PsiExpression[] occurrences = settings.getReplaceChoice().filter(myOccurenceManager); + final PsiElement anchor = IntroduceVariableBase.getAnchor(occurrences); + if (anchor == null) return true; final PsiElement scope = anchor.getParent(); if(scope == null) return true; final MultiMap conflicts = new MultiMap<>(); @@ -59,13 +53,8 @@ public class InputValidator implements IntroduceVariableBase.Validator { } }; JavaUnresolvableLocalCollisionDetector.visitLocalsCollisions(anchor, name, scope, anchor, visitor); - if (replaceAllOccurrences) { - final PsiExpression[] occurences = myOccurenceManager.getOccurrences(); - for (PsiExpression occurence : occurences) { - IntroduceVariableBase.checkInLoopCondition(occurence, conflicts); - } - } else { - IntroduceVariableBase.checkInLoopCondition(myOccurenceManager.getMainOccurence(), conflicts); + for (PsiExpression occurence : occurrences) { + IntroduceVariableBase.checkInLoopCondition(occurence, conflicts); } if (conflicts.size() > 0) { @@ -78,13 +67,9 @@ public class InputValidator implements IntroduceVariableBase.Validator { public InputValidator(final IntroduceVariableBase introduceVariableBase, Project project, - PsiElement anchorStatementIfAll, - PsiElement anchorStatement, ExpressionOccurrenceManager occurenceManager) { myIntroduceVariableBase = introduceVariableBase; myProject = project; - myAnchorStatementIfAll = anchorStatementIfAll; - myAnchorStatement = anchorStatement; myOccurenceManager = occurenceManager; } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 89a1fa8b44bc..58156c9034aa 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -49,6 +49,7 @@ import com.intellij.refactoring.*; import com.intellij.refactoring.chainCall.ChainCallExtractor; import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer; import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; +import com.intellij.refactoring.introduce.inplace.OccurrencesChooser.ReplaceChoice; import com.intellij.refactoring.introduceField.ElementToWorkOn; import com.intellij.refactoring.listeners.RefactoringEventData; import com.intellij.refactoring.listeners.RefactoringEventListener; @@ -65,12 +66,9 @@ import com.intellij.util.containers.MultiMap; import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.VariableAccessUtils; import com.siyeh.ipp.psiutils.ErrorUtil; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.*; -import java.text.MessageFormat; import java.util.*; /** @@ -78,49 +76,51 @@ import java.util.*; */ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { public static class JavaReplaceChoice implements OccurrencesChooser.BaseReplaceChoice { - public static final JavaReplaceChoice NO = new JavaReplaceChoice(OccurrencesChooser.ReplaceChoice.NO); - public static final JavaReplaceChoice NO_WRITE = new JavaReplaceChoice(OccurrencesChooser.ReplaceChoice.NO_WRITE); - public static final JavaReplaceChoice ALL = new JavaReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL); - public static final JavaReplaceChoice NO_CHAIN = new JavaReplaceChoice("Create variable inside current lambda", false, false, false); - public static final JavaReplaceChoice CHAIN = new JavaReplaceChoice("Extract as a separate operation", false, false, true); - public static final JavaReplaceChoice CHAIN_ALL = - new JavaReplaceChoice("Replace all occurrences and extract as a separate operation", true, true, true); + public static final JavaReplaceChoice NO = new JavaReplaceChoice(ReplaceChoice.NO, null, false); + public static final JavaReplaceChoice NO_WRITE = new JavaReplaceChoice(ReplaceChoice.NO_WRITE, null, false); + public static final JavaReplaceChoice ALL = new JavaReplaceChoice(ReplaceChoice.ALL, null, false); private final String myDescription; - private final boolean myAll, myMultiple, myChain; + private final boolean myChain; + private final ReplaceChoice myChoice; - JavaReplaceChoice(OccurrencesChooser.ReplaceChoice choice) { - this(choice.getDescription(), choice.isAll(), choice.isMultiple(), false); - } - - JavaReplaceChoice(String description, boolean all, boolean multiple, boolean chain) { + JavaReplaceChoice(@NotNull ReplaceChoice choice, @Nullable @Nls String description, boolean chain) { + myChoice = choice; myDescription = description; - myAll = all; - myMultiple = multiple; myChain = chain; } - public String getDescription() { - return myDescription; - } - - @Override - public boolean isMultiple() { - return myMultiple; - } - @Override public boolean isAll() { - return myAll; + return myChoice.isAll(); } public boolean isChain() { return myChain; } + + public PsiExpression[] filter(ExpressionOccurrenceManager manager) { + switch (myChoice) { + case NO: + return new PsiExpression[]{manager.getMainOccurence()}; + case NO_WRITE: + return StreamEx.of(manager.getOccurrences()).filter(expr -> !PsiUtil.isAccessedForWriting(expr)).toArray(PsiExpression.EMPTY_ARRAY); + case ALL: + return manager.getOccurrences(); + default: + throw new IllegalStateException("Unexpected value: " + myChoice); + } + } @Override public String formatDescription(int occurrencesCount) { - return MessageFormat.format(getDescription(), occurrencesCount); + return myDescription == null ? myChoice.formatDescription(occurrencesCount) : myDescription; + } + + @Override + public String toString() { + // For debug/test purposes + return formatDescription(0); } } @@ -567,8 +567,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { } @Override - protected boolean invokeImpl(final Project project, final PsiExpression expr, - final Editor editor) { + protected boolean invokeImpl(final Project project, final PsiExpression expr, final Editor editor) { if (expr != null) { final String errorMessage = getErrorMessage(expr); if (errorMessage != null) { @@ -623,10 +622,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { } } - - final PsiElement physicalElement = expr.getUserData(ElementToWorkOn.PARENT); - - final PsiElement anchorStatement = getAnchor(physicalElement != null ? physicalElement : expr); + final PsiElement anchorStatement = getAnchor(expr); PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement); if (tempContainer == null) return false; @@ -653,8 +649,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, tempContainer); final PsiExpression[] occurrences = occurrenceManager.getOccurrences(); - final PsiElement anchorStatementIfAll = occurrenceManager.getAnchorStatementForAll(); - OccurrencesInfo occurrencesInfo = new OccurrencesInfo(occurrences); @@ -662,112 +656,113 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final LinkedHashMap> occurrencesMap = occurrencesInfo.buildOccurrencesMap(expr); - final boolean inFinalContext = occurrenceManager.isInFinalContext(); - final InputValidator validator = new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurrenceManager); - final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences); - final boolean[] wasSucceed = new boolean[]{true}; - final Pass callback = new Pass() { + class IntroduceVariablePass extends Pass { + boolean wasSucceed = true; + @Override public void pass(final JavaReplaceChoice choice) { - boolean hasWriteAccess = occurrencesInfo.myHasWriteAccess; - List nonWrite = occurrencesInfo.myNonWrite; - if (choice != null) { - final boolean noWriteChoice = choice == JavaReplaceChoice.NO_WRITE; - final boolean allChoice = choice.isAll(); - final boolean replaceAll = allChoice || noWriteChoice; - typeSelectorManager.setAllOccurrences(replaceAll); + if (choice == null || !tryIntroduceInplace(project, editor, choice, occurrenceManager, originalType)) { + CommandProcessor.getInstance().executeCommand(project, () -> introduce(choice), getRefactoringName(), null); + } + } - final PsiElement chosenAnchor = chooseAnchor(replaceAll, noWriteChoice, nonWrite, anchorStatementIfAll, anchorStatement); - final IntroduceVariableSettings settings = - getSettings(project, editor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, chosenAnchor, - choice); - - final boolean cantChangeFinalModifier = (hasWriteAccess && allChoice) || inFinalContext; - - PsiExpression[] allOccurrences = Arrays.stream(occurrences) - .filter(occurrence -> allChoice || (noWriteChoice && !PsiUtil.isAccessedForWriting(occurrence)) || expr.equals(occurrence)) - .toArray(PsiExpression[]::new); - if (choice.isChain()) { - myInplaceIntroducer = new ChainCallInplaceIntroducer(project, - settings, - chosenAnchor, - editor, expr, - allOccurrences, - typeSelectorManager, - getRefactoringName()); - } - else { - myInplaceIntroducer = new JavaVariableInplaceIntroducer(project, - settings, - chosenAnchor, - editor, expr, cantChangeFinalModifier, - allOccurrences, - typeSelectorManager, - getRefactoringName()); - } - if (myInplaceIntroducer.startInplaceIntroduceTemplate()) { - return; - } + private void introduce(@Nullable JavaReplaceChoice choice) { + if (!anchorStatement.isValid()) { + return; + } + final Editor topLevelEditor; + if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) { + topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor); + } + else { + topLevelEditor = editor; } - CommandProcessor.getInstance().executeCommand( - project, - () -> { - if (!anchorStatement.isValid()) { - return; - } - final Editor topLevelEditor ; - if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) { - topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor); - } else { - topLevelEditor = editor; - } + PsiVariable variable = null; + try { + boolean hasWriteAccess = occurrencesInfo.myHasWriteAccess; + final InputValidator validator = new InputValidator(IntroduceVariableBase.this, project, occurrenceManager); - PsiVariable variable = null; - try { - final IntroduceVariableSettings settings = - getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, - anchorStatement, choice); - if (!settings.isOK()) { - wasSucceed[0] = false; - return; - } + final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences); + boolean inFinalContext = occurrenceManager.isInFinalContext(); + final IntroduceVariableSettings settings = + getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, + anchorStatement, choice); + if (!settings.isOK()) { + wasSucceed = false; + return; + } + JavaReplaceChoice finalChoice = settings.getReplaceChoice(); + PsiExpression[] selectedOccurrences = finalChoice.filter(occurrenceManager); + final PsiElement chosenAnchor = getAnchor(selectedOccurrences); - final RefactoringEventData beforeData = new RefactoringEventData(); - beforeData.addElement(expr); - project.getMessageBus() - .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData); + final RefactoringEventData beforeData = new RefactoringEventData(); + beforeData.addElement(expr); + project.getMessageBus() + .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData); - final PsiElement chosenAnchor = - chooseAnchor(settings.isReplaceAllOccurrences(), hasWriteAccess, nonWrite, anchorStatementIfAll, anchorStatement); - - variable = VariableExtractor.introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings); - } - finally { - final RefactoringEventData afterData = new RefactoringEventData(); - afterData.addElement(variable); - project.getMessageBus() - .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone(REFACTORING_ID, afterData); - } - }, getRefactoringName(), null); + variable = VariableExtractor.introduce(project, expr, topLevelEditor, chosenAnchor, selectedOccurrences, settings); + } + finally { + final RefactoringEventData afterData = new RefactoringEventData(); + afterData.addElement(variable); + project.getMessageBus() + .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone(REFACTORING_ID, afterData); + } } - }; + } + final IntroduceVariablePass callback = new IntroduceVariablePass(); if (!isInplaceAvailableOnDataContext) { callback.pass(null); } else { - JavaReplaceChoice choice = getOccurrencesChoice(); - if (choice != null) { - callback.pass(choice); - } else { - String title = occurrencesInfo.myChainMethodName != null && occurrences.length == 1 - ? "Lambda chain detected" - : OccurrencesChooser.DEFAULT_CHOOSER_TITLE; - OccurrencesChooser.simpleChooser(editor).showChooser(callback, occurrencesMap, title); - } + String title = occurrencesInfo.myChainMethodName != null && occurrences.length == 1 + ? JavaRefactoringBundle.message("replace.lambda.chain.detected") + : RefactoringBundle.message("replace.multiple.occurrences.found"); + OccurrencesChooser.simpleChooser(editor).showChooser(callback, occurrencesMap, title); } - return wasSucceed[0]; + return callback.wasSucceed; + } + + private boolean tryIntroduceInplace(@NotNull Project project, + Editor editor, + @NotNull JavaReplaceChoice choice, + @NotNull ExpressionOccurrenceManager occurrenceManager, + @NotNull PsiType originalType) { + boolean inFinalContext = occurrenceManager.isInFinalContext(); + PsiExpression expr = occurrenceManager.getMainOccurence(); + PsiExpression[] selectedOccurrences = choice.filter(occurrenceManager); + final InputValidator validator = new InputValidator(IntroduceVariableBase.this, project, occurrenceManager); + final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, selectedOccurrences); + typeSelectorManager.setAllOccurrences(true); + + boolean hasWriteAccess = ContainerUtil.exists(selectedOccurrences, occ -> PsiUtil.isAccessedForWriting(occ)); + final PsiElement chosenAnchor = getAnchor(selectedOccurrences); + final IntroduceVariableSettings settings = + getSettings(project, editor, expr, selectedOccurrences, typeSelectorManager, inFinalContext, + hasWriteAccess, validator, chosenAnchor, choice); + + if (choice.isChain()) { + myInplaceIntroducer = new ChainCallInplaceIntroducer(project, + settings, + chosenAnchor, + editor, expr, + selectedOccurrences, + typeSelectorManager, + getRefactoringName()); + } + else { + final boolean cantChangeFinalModifier = hasWriteAccess || inFinalContext; + myInplaceIntroducer = new JavaVariableInplaceIntroducer(project, + settings, + chosenAnchor, + editor, expr, cantChangeFinalModifier, + selectedOccurrences, + typeSelectorManager, + getRefactoringName()); + } + return myInplaceIntroducer.startInplaceIntroduceTemplate(); } public static boolean canBeExtractedWithoutExplicitType(PsiExpression expr) { @@ -783,6 +778,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { @Nullable private static PsiElement getAnchor(PsiElement place) { + place = getPhysicalElement(place); PsiElement anchorStatement = RefactoringUtil.getParentStatement(place, false); if (anchorStatement == null) { PsiField field = PsiTreeUtil.getParentOfType(place, PsiField.class, true, PsiStatement.class); @@ -793,6 +789,19 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { return anchorStatement; } + static @Nullable PsiElement getAnchor(PsiExpression[] places) { + if (places.length == 1) { + return getAnchor(places[0]); + } + PsiElement anchor = RefactoringUtil.getAnchorElementForMultipleExpressions(places, null); + return anchor instanceof PsiField && !(anchor instanceof PsiEnumConstant) ? ((PsiField)anchor).getInitializer() : anchor; + } + + private static @NotNull PsiElement getPhysicalElement(PsiElement place) { + PsiElement physicalElement = place.getUserData(ElementToWorkOn.PARENT); + return physicalElement != null ? physicalElement : place; + } + @Contract("_, _, null -> null") protected PsiElement checkAnchorStatement(Project project, Editor editor, PsiElement anchorStatement) { if (anchorStatement == null) { @@ -812,28 +821,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { return tempContainer; } - protected JavaReplaceChoice getOccurrencesChoice() { - return null; - } - - protected static PsiElement chooseAnchor(boolean allOccurrences, - boolean hasWriteAccess, - List nonWrite, - PsiElement anchorStatementIfAll, - PsiElement anchorStatement) { - if (allOccurrences) { - if (hasWriteAccess) { - return RefactoringUtil.getAnchorElementForMultipleExpressions(nonWrite.toArray(PsiExpression.EMPTY_ARRAY), null); - } - else { - return anchorStatementIfAll; - } - } - else { - return anchorStatement; - } - } - protected boolean isInplaceAvailableInTestMode() { return false; } @@ -1047,13 +1034,13 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final InputValidator validator, PsiElement anchor, final JavaReplaceChoice replaceChoice) { - final boolean replaceAll = replaceChoice.isMultiple(); + final boolean replaceAll = replaceChoice.isAll(); final SuggestedNameInfo suggestedName = getSuggestedName(typeSelectorManager.getDefaultType(), expr, anchor); final String variableName = suggestedName.names.length > 0 ? suggestedName.names[0] : ""; final boolean declareFinal = replaceAll && declareFinalIfAll || !anyAssignmentLHS && createFinals(anchor.getContainingFile()) || anchor instanceof PsiSwitchLabelStatementBase; final boolean declareVarType = canBeExtractedWithoutExplicitType(expr) && createVarType() && !replaceChoice.isChain(); - final boolean replaceWrite = anyAssignmentLHS && replaceChoice.isAll(); + final boolean replaceWrite = anyAssignmentLHS && replaceAll; return new IntroduceVariableSettings() { @Override public String getEnteredName() { @@ -1086,6 +1073,11 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { return selectedType != null ? selectedType : typeSelectorManager.getDefaultType(); } + @Override + public JavaReplaceChoice getReplaceChoice() { + return replaceChoice; + } + @Override public boolean isOK() { return true; @@ -1207,13 +1199,19 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { if (myOccurrences.size() > 1 && !myCantReplaceAll) { occurrencesMap.put(JavaReplaceChoice.NO, Collections.singletonList(expr)); occurrencesMap.put(JavaReplaceChoice.ALL, myOccurrences); - occurrencesMap.put( - new JavaReplaceChoice("Replace all {0} occurrences and extract as ''" + myChainMethodName + "'' operation", true, true, true), - myOccurrences); + occurrencesMap.put(new JavaReplaceChoice(ReplaceChoice.ALL, null, true) { + @Override + public String formatDescription(int occurrencesCount) { + return JavaRefactoringBundle.message("replace.all.and.extract", occurrencesCount, myChainMethodName); + } + }, myOccurrences); } else { - occurrencesMap.put(JavaReplaceChoice.NO_CHAIN, Collections.singletonList(expr)); - occurrencesMap.put(new JavaReplaceChoice("Extract as ''" + myChainMethodName + "'' operation", false, false, true), - Collections.singletonList(expr)); + JavaReplaceChoice noChain = + new JavaReplaceChoice(ReplaceChoice.NO, JavaRefactoringBundle.message("replace.inside.current.lambda"), false); + JavaReplaceChoice chain = + new JavaReplaceChoice(ReplaceChoice.NO, JavaRefactoringBundle.message("replace.as.separate.operation", myChainMethodName), true); + occurrencesMap.put(noChain, Collections.singletonList(expr)); + occurrencesMap.put(chain, Collections.singletonList(expr)); } } else { occurrencesMap.put(JavaReplaceChoice.NO, Collections.singletonList(expr)); @@ -1223,7 +1221,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { if (myOccurrences.size() > 1 && !myCantReplaceAll) { JavaReplaceChoice choice = occurrencesMap.containsKey(JavaReplaceChoice.NO_WRITE) - ? new JavaReplaceChoice("Replace read and write occurrences (will change semantics!)", true, true, false) + ? new JavaReplaceChoice(ReplaceChoice.ALL, JavaRefactoringBundle.message("replace.all.read.and.write"), + false) : JavaReplaceChoice.ALL; occurrencesMap.put(choice, myOccurrences); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableSettings.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableSettings.java index b17d7d6b2dad..7d6686a41541 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableSettings.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableSettings.java @@ -34,4 +34,11 @@ public interface IntroduceVariableSettings { PsiType getSelectedType(); boolean isOK(); + + default IntroduceVariableBase.JavaReplaceChoice getReplaceChoice() { + if (isReplaceAllOccurrences()) { + return isReplaceLValues() ? IntroduceVariableBase.JavaReplaceChoice.ALL : IntroduceVariableBase.JavaReplaceChoice.NO_WRITE; + } + return IntroduceVariableBase.JavaReplaceChoice.NO; + } } diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/InplaceIntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/InplaceIntroduceVariableTest.java index 9517fe002e2b..06f2a529308c 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/InplaceIntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/InplaceIntroduceVariableTest.java @@ -20,10 +20,13 @@ import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer; import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler; import com.intellij.testFramework.MapDataContext; +import com.intellij.ui.ChooserInterceptor; +import com.intellij.ui.UiInterceptors; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; +import java.util.regex.Pattern; public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTest { @Nullable @@ -80,19 +83,19 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe } public void testPlaceInsideLambdaBodyMultipleOccurrences1() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL, introducer -> type("expr")); + doTestReplaceChoice("Replace all 0 occurrences", introducer -> type("expr")); } public void testReplaceAllOnDummyCodeWithSameNameAsGenerated() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL, introducer -> type("expr")); + doTestReplaceChoice("Replace all 0 occurrences", introducer -> type("expr")); } public void testReplaceAllIntroduceFieldInLocalClass() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL, introducer -> type("smth")); + doTestReplaceChoice("Replace all 0 occurrences", introducer -> type("smth")); } public void testReplaceAllWithScopeInvalidation() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL, introducer -> type("newType")); + doTestReplaceChoice("Replace all 0 occurrences", introducer -> type("newType")); } public void testRanges() { @@ -134,31 +137,31 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe } public void testWritable() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL); + doTestReplaceChoice("Replace read and write occurrences (will change semantics!)"); } public void testNoWritable() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.NO_WRITE); + doTestReplaceChoice("Replace all occurrences but write"); } public void testAllInsertFinal() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL); + doTestReplaceChoice("Replace all 0 occurrences"); } public void testAllIncomplete() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL); + doTestReplaceChoice("Replace all 0 occurrences"); } public void testStreamSimple() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.CHAIN); + doTestReplaceChoice("Extract as 'map' operation"); } public void testStreamMultiple() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.CHAIN_ALL); + doTestReplaceChoice("Replace all 0 occurrences and extract as 'mapToInt' operation"); } public void testStreamMultiline() { - doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.CHAIN); + doTestReplaceChoice("Extract as 'map' operation"); } public void testBrokenFormattingWithInValidation() { @@ -216,11 +219,11 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe } } - private void doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice choice) { - doTestReplaceChoice(choice, null); + private void doTestReplaceChoice(String choiceText) { + doTestReplaceChoice(choiceText, null); } - private void doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice choice, Consumer pass) { + private void doTestReplaceChoice(String choiceText, Consumer> pass) { String name = getTestName(true); configureByFile(getBasePath() + name + getExtension()); final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled(); @@ -229,8 +232,8 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe getEditor().getSettings().setVariableInplaceRenameEnabled(true); MyIntroduceHandler handler = createIntroduceHandler(); - ((MyIntroduceVariableHandler)handler).setChoice(choice); - final AbstractInplaceIntroducer introducer = invokeRefactoring(handler); + UiInterceptors.register(new ChooserInterceptor(null, Pattern.quote(choiceText))); + final AbstractInplaceIntroducer introducer = invokeRefactoring(handler); if (pass != null) { pass.accept(introducer); } @@ -261,12 +264,6 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe } public static class MyIntroduceVariableHandler extends IntroduceVariableHandler implements MyIntroduceHandler { - private JavaReplaceChoice myChoice = null; - - public void setChoice(JavaReplaceChoice choice) { - myChoice = choice; - } - @Override public boolean invokeImpl(Project project, @NotNull PsiExpression selectedExpr, Editor editor) { return super.invokeImpl(project, selectedExpr, editor); @@ -277,11 +274,6 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe return super.invokeImpl(project, localVariable, editor); } - @Override - protected JavaReplaceChoice getOccurrencesChoice() { - return myChoice; - } - @Override protected boolean isInplaceAvailableInTestMode() { return true; diff --git a/java/openapi/resources/messages/JavaRefactoringBundle.properties b/java/openapi/resources/messages/JavaRefactoringBundle.properties index d6c67902a512..c4287ca8f238 100644 --- a/java/openapi/resources/messages/JavaRefactoringBundle.properties +++ b/java/openapi/resources/messages/JavaRefactoringBundle.properties @@ -603,4 +603,10 @@ wrap.return.value.inner.class.name=Na&me wrap.return.value.new.class.name=&Name wrap.return.value.new.class.package.name=&Package name wrap.return.value.use.existing.class=&Use existing class -wrap.return.value.wrapper.field=Wrapper &field \ No newline at end of file +wrap.return.value.wrapper.field=Wrapper &field + +replace.inside.current.lambda=Create variable inside current lambda +replace.as.separate.operation=Extract as ''{0}'' operation +replace.all.read.and.write=Replace read and write occurrences (will change semantics!) +replace.all.and.extract=Replace all {0} occurrences and extract as ''{1}'' operation +replace.lambda.chain.detected=Lambda chain detected \ No newline at end of file diff --git a/platform/lang-api/resources/messages/RefactoringBundle.properties b/platform/lang-api/resources/messages/RefactoringBundle.properties index 61556b729dc1..ebe15e69d30d 100644 --- a/platform/lang-api/resources/messages/RefactoringBundle.properties +++ b/platform/lang-api/resources/messages/RefactoringBundle.properties @@ -526,4 +526,9 @@ extract.include.file.action.title=Extract Include File... label.change.signature.in.all.calls.to.this.method.leave.the.parameter.blank=In all calls to this method leave the parameter blank checkbox.introduce.parameter.object.keep.method.as.delegate=Keep method as &delegate border.title.introduce.parameter.class=Parameter Class -border.title.introduce.parameters.to.extract=Parameters to Extract \ No newline at end of file +border.title.introduce.parameters.to.extract=Parameters to Extract + +replace.this.occurrence.only=Replace this occurrence only +replace.all.occurrences.but.write=Replace all occurrences but write +replace.all.occurrences=Replace all {0} occurrences +replace.multiple.occurrences.found=Multiple occurrences found \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/OccurrencesChooser.java b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/OccurrencesChooser.java index bff5e3438b32..bab86a2f35d2 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/OccurrencesChooser.java +++ b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/OccurrencesChooser.java @@ -11,11 +11,12 @@ import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; +import com.intellij.refactoring.RefactoringBundle; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -import java.text.MessageFormat; import java.util.List; import java.util.*; @@ -23,42 +24,39 @@ import java.util.*; // This prevents languages with polyadic expressions or sequences // from reusing it, use simpleChooser instead. public abstract class OccurrencesChooser { - public static final String DEFAULT_CHOOSER_TITLE = "Multiple occurrences found"; - public interface BaseReplaceChoice { - boolean isMultiple(); - + /** + * @return true if more than one element is selected + */ boolean isAll(); - String formatDescription(int occurrencesCount); + /** + * @param occurrencesCount number of occurrences + * @return user-readable description of given choice + */ + @Nls String formatDescription(int occurrencesCount); } public enum ReplaceChoice implements BaseReplaceChoice { - NO("Replace this occurrence only"), NO_WRITE("Replace all occurrences but write"), ALL("Replace all {0} occurrences"); - - private final String myDescription; - - ReplaceChoice(String description) { - myDescription = description; - } - - public String getDescription() { - return myDescription; - } - - @Override - public boolean isMultiple() { - return this == NO_WRITE || this == ALL; - } + NO, NO_WRITE, ALL; @Override public boolean isAll() { - return this == ALL; + return this != NO; } @Override - public String formatDescription(int occurrencesCount) { - return MessageFormat.format(getDescription(), occurrencesCount); + public @Nls String formatDescription(int occurrencesCount) { + switch (this) { + case NO: + return RefactoringBundle.message("replace.this.occurrence.only"); + case NO_WRITE: + return RefactoringBundle.message("replace.all.occurrences.but.write"); + case ALL: + return RefactoringBundle.message("replace.all.occurrences", occurrencesCount); + default: + throw new IllegalStateException("Unexpected value: " + this); + } } } @@ -93,12 +91,12 @@ public abstract class OccurrencesChooser { } public void showChooser(final Pass callback, final Map> occurrencesMap) { - showChooser(callback, occurrencesMap, DEFAULT_CHOOSER_TITLE); + showChooser(callback, occurrencesMap, RefactoringBundle.message("replace.multiple.occurrences.found")); } public void showChooser(final Pass callback, final Map> occurrencesMap, - String title) { + @Nls String title) { if (occurrencesMap.size() == 1) { callback.pass(occurrencesMap.keySet().iterator().next()); return;