mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
committed by
intellij-monorepo-bot
parent
06dcd54bb4
commit
f843918254
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+160
-161
@@ -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);
|
||||
}
|
||||
|
||||
+7
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-27
@@ -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
|
||||
+25
-27
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user