IntroduceVariableBase refactoring

1. Encapsulate knowledge about selected items inside JavaReplaceChoice object (filter method)
  - Now, we don't need to pass flags like 'replaceAll' in many places, we just ask JavaReplaceChoice which elements were selected
  - Also anchor now recomputed from selected elements, so we can avoid passing it
2. Extract tryIntroduceInplace method
3. Use UiInterceptors for testing JavaReplaceChoice selection
4. Internationalization

GitOrigin-RevId: 30bd191758e17c934ef7ae074ffdfedab54b63be
This commit is contained in:
Tagir Valeev
2020-03-20 11:32:52 +00:00
committed by intellij-monorepo-bot
parent 06dcd54bb4
commit f843918254
7 changed files with 229 additions and 237 deletions
@@ -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<PsiElement, String> 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;
}
}
@@ -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<JavaReplaceChoice, List<PsiExpression>> 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<JavaReplaceChoice> callback = new Pass<JavaReplaceChoice>() {
class IntroduceVariablePass extends Pass<JavaReplaceChoice> {
boolean wasSucceed = true;
@Override
public void pass(final JavaReplaceChoice choice) {
boolean hasWriteAccess = occurrencesInfo.myHasWriteAccess;
List<PsiExpression> 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.<PsiExpression>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.<PsiExpression>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<PsiExpression> 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);
}
@@ -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;
}
}
@@ -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<AbstractInplaceIntroducer> pass) {
private void doTestReplaceChoice(String choiceText, Consumer<AbstractInplaceIntroducer<?, ?>> 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;
@@ -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
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
@@ -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
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
@@ -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<T> {
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<T> {
}
public void showChooser(final Pass<? super ReplaceChoice> callback, final Map<ReplaceChoice, List<T>> occurrencesMap) {
showChooser(callback, occurrencesMap, DEFAULT_CHOOSER_TITLE);
showChooser(callback, occurrencesMap, RefactoringBundle.message("replace.multiple.occurrences.found"));
}
public <C extends BaseReplaceChoice> void showChooser(final Pass<? super C> callback,
final Map<C, List<T>> occurrencesMap,
String title) {
@Nls String title) {
if (occurrencesMap.size() == 1) {
callback.pass(occurrencesMap.keySet().iterator().next());
return;