Extract variable to chained method call

This commit is contained in:
Tagir Valeev
2017-03-06 15:32:17 +07:00
parent 504a5727d6
commit 0e44858a99
19 changed files with 347 additions and 84 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.introduceVariable.InputValidator;
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
import com.intellij.refactoring.introduceVariable.IntroduceVariableSettings;
@@ -53,7 +52,7 @@ public class IntroduceVariablePostfixTemplate extends PostfixTemplateWithExpress
public final IntroduceVariableSettings getSettings(Project project, Editor editor, final PsiExpression expr,
PsiExpression[] occurrences, TypeSelectorManagerImpl typeSelectorManager,
boolean declareFinalIfAll, boolean anyAssignmentLHS, InputValidator validator,
PsiElement anchor, OccurrencesChooser.ReplaceChoice replaceChoice) {
PsiElement anchor, JavaReplaceChoice replaceChoice) {
return new IntroduceVariableSettings() {
@Override
public String getEnteredName() {
@@ -0,0 +1,85 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.introduceVariable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.chainCall.ChainCallExtractor;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.Nullable;
/**
* @author Tagir Valeev
*/
public class ChainCallInplaceIntroducer extends JavaVariableInplaceIntroducer {
private PsiParameter myParameter;
private PsiMethodCallExpression myCall;
public ChainCallInplaceIntroducer(Project project,
IntroduceVariableSettings settings,
PsiElement chosenAnchor,
Editor editor,
PsiExpression expr,
PsiExpression[] occurrences,
TypeSelectorManagerImpl selectorManager,
String title) {
super(project, settings, chosenAnchor, editor, expr, true, occurrences, selectorManager, title);
}
@Nullable
@Override
protected PsiVariable getVariable() {
if (myParameter != null && myParameter.isValid()) {
return myParameter;
}
return super.getVariable();
}
@Nullable
@Override
protected PsiElement checkLocalScope() {
return myCall;
}
@Nullable
@Override
protected PsiVariable introduceVariable() {
PsiVariable variable = super.introduceVariable();
if (variable instanceof PsiLocalVariable) {
PsiLambdaExpression lambda = ApplicationManager.getApplication().runWriteAction(
(Computable<PsiLambdaExpression>)() -> ChainCallExtractor.extractMappingStep(myProject, (PsiLocalVariable)variable));
if (lambda != null) {
PsiParameter parameter = ArrayUtil.getFirstElement(lambda.getParameterList().getParameters());
myParameter = parameter;
myCall = PsiTreeUtil.getParentOfType(lambda, PsiMethodCallExpression.class);
myExprMarker = null;
myExpr = null;
setAdvertisementText(null);
PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myEditor.getDocument());
return parameter;
}
else if (!variable.isValid()) {
return null;
}
}
return variable;
}
}
@@ -54,8 +54,10 @@ import com.intellij.psi.impl.source.jsp.jspJava.JspCodeBlock;
import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.*;
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.introduceField.ElementToWorkOn;
@@ -70,12 +72,14 @@ import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager;
import com.intellij.refactoring.util.occurrences.NotInSuperCallOccurrenceFilter;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.*;
/**
@@ -83,6 +87,37 @@ import java.util.*;
* Date: Nov 15, 2002
*/
public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
public enum JavaReplaceChoice implements OccurrencesChooser.BaseReplaceChoice {
NO("Replace this occurrence only"), NO_WRITE("Replace all occurrences but write"), ALL("Replace all {0} occurrences"),
NO_CHAIN("Create variable inside current lambda"), CHAIN("Extract as separate step"),
CHAIN_ALL("Replace all {0} occurrences and extract as separate step");
private final String myDescription;
JavaReplaceChoice(String description) {
myDescription = description;
}
public String getDescription() {
return myDescription;
}
@Override
public boolean isMultiple() {
return this == NO_WRITE || this == ALL || this == CHAIN_ALL;
}
@Override
public boolean isAll() {
return this == ALL || this == CHAIN_ALL;
}
@Override
public String formatDescription(int occurrencesCount) {
return MessageFormat.format(getDescription(), occurrencesCount);
}
}
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceVariable.IntroduceVariableBase");
@NonNls private static final String PREFER_STATEMENTS_OPTION = "introduce.variable.prefer.statements";
@NonNls private static final String REFACTORING_ID = "refactoring.extractVariable";
@@ -610,20 +645,20 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return false;
final LinkedHashMap<OccurrencesChooser.ReplaceChoice, List<PsiExpression>> occurrencesMap = occurrencesInfo.buildOccurrencesMap(expr);
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<OccurrencesChooser.ReplaceChoice> callback = new Pass<OccurrencesChooser.ReplaceChoice>() {
final Pass<JavaReplaceChoice> callback = new Pass<JavaReplaceChoice>() {
@Override
public void pass(final OccurrencesChooser.ReplaceChoice choice) {
public void pass(final JavaReplaceChoice choice) {
boolean hasWriteAccess = occurrencesInfo.myHasWriteAccess;
List<PsiExpression> nonWrite = occurrencesInfo.myNonWrite;
if (choice != null) {
final boolean noWriteChoice = choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
final boolean allChoice = choice == OccurrencesChooser.ReplaceChoice.ALL;
final boolean noWriteChoice = choice == JavaReplaceChoice.NO_WRITE;
final boolean allChoice = choice.isAll();
final boolean replaceAll = allChoice || noWriteChoice;
typeSelectorManager.setAllOccurrences(replaceAll);
@@ -638,13 +673,24 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
.filter(occurrence -> !(expr.equals(occurrence) && expr.getParent() instanceof PsiExpressionStatement))
.filter(occurrence -> allChoice || (noWriteChoice && !PsiUtil.isAccessedForWriting(occurrence)) || expr.equals(occurrence))
.toArray(PsiExpression[]::new);
myInplaceIntroducer = new JavaVariableInplaceIntroducer(project,
settings,
chosenAnchor,
editor, expr, cantChangeFinalModifier,
allOccurrences,
typeSelectorManager,
REFACTORING_NAME);
if (choice == JavaReplaceChoice.CHAIN || choice == JavaReplaceChoice.CHAIN_ALL) {
myInplaceIntroducer = new ChainCallInplaceIntroducer(project,
settings,
chosenAnchor,
editor, expr,
allOccurrences,
typeSelectorManager,
REFACTORING_NAME);
}
else {
myInplaceIntroducer = new JavaVariableInplaceIntroducer(project,
settings,
chosenAnchor,
editor, expr, cantChangeFinalModifier,
allOccurrences,
typeSelectorManager,
REFACTORING_NAME);
}
if (myInplaceIntroducer.startInplaceIntroduceTemplate()) {
return;
}
@@ -653,6 +699,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
CommandProcessor.getInstance().executeCommand(
project,
() -> {
if (!anchorStatement.isValid()) {
return;
}
final Editor topLevelEditor ;
if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) {
topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
@@ -694,17 +743,20 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
callback.pass(null);
}
else {
OccurrencesChooser.ReplaceChoice choice = getOccurrencesChoice();
JavaReplaceChoice choice = getOccurrencesChoice();
if (choice != null) {
callback.pass(choice);
} else {
OccurrencesChooser.<PsiExpression>simpleChooser(editor).showChooser(callback, occurrencesMap);
String title = occurrencesInfo.myChainCallCandidate && occurrences.length == 1
? "Lambda chain detected"
: OccurrencesChooser.DEFAULT_CHOOSER_TITLE;
OccurrencesChooser.<PsiExpression>simpleChooser(editor).showChooser(callback, occurrencesMap, title);
}
}
return wasSucceed[0];
}
protected OccurrencesChooser.ReplaceChoice getOccurrencesChoice() {
protected JavaReplaceChoice getOccurrencesChoice() {
return null;
}
@@ -1096,13 +1148,12 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
boolean anyAssignmentLHS,
final InputValidator validator,
PsiElement anchor,
final OccurrencesChooser.ReplaceChoice replaceChoice) {
final boolean replaceAll =
replaceChoice == OccurrencesChooser.ReplaceChoice.ALL || replaceChoice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
final JavaReplaceChoice replaceChoice) {
final boolean replaceAll = replaceChoice.isMultiple();
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(project);
final boolean replaceWrite = anyAssignmentLHS && replaceChoice == OccurrencesChooser.ReplaceChoice.ALL;
final boolean replaceWrite = anyAssignmentLHS && replaceChoice.isAll();
return new IntroduceVariableSettings() {
@Override
public String getEnteredName() {
@@ -1192,11 +1243,14 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
}
static class OccurrencesInfo {
static final boolean CHAIN_ALLOWED = Registry.is("java.extract.variable.chaining.method");
List<PsiExpression> myOccurrences;
List<PsiExpression> myNonWrite;
boolean myCantReplaceAll;
boolean myCantReplaceAllButWrite;
boolean myHasWriteAccess;
boolean myChainCallCandidate;
public OccurrencesInfo(PsiExpression[] occurrences) {
myOccurrences = Arrays.asList(occurrences);
@@ -1214,18 +1268,45 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
}
}
myHasWriteAccess = myOccurrences.size() > myNonWrite.size() && myOccurrences.size() > 1;
myChainCallCandidate = CHAIN_ALLOWED && isChainCallCandidate();
}
private boolean isChainCallCandidate() {
if (myHasWriteAccess || myOccurrences.isEmpty()) return false;
// The whole lambda body selected
PsiExpression expression = myOccurrences.get(0);
if (myOccurrences.size() == 1 && expression.getParent() instanceof PsiLambdaExpression) return false;
PsiElement parent = PsiTreeUtil.findCommonParent(myOccurrences);
if (parent == null) return false;
PsiType type = expression.getType();
PsiLambdaExpression lambda = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, true, PsiStatement.class);
if (ChainCallExtractor.findExtractor(lambda, expression, type) == null) return false;
PsiParameter parameter = lambda.getParameterList().getParameters()[0];
return ReferencesSearch.search(parameter).forEach((Processor<PsiReference>)ref ->
myOccurrences.stream().anyMatch(expr -> PsiTreeUtil.isAncestor(expr, ref.getElement(), false)));
}
@NotNull
private LinkedHashMap<OccurrencesChooser.ReplaceChoice, List<PsiExpression>> buildOccurrencesMap(PsiExpression expr) {
final LinkedHashMap<OccurrencesChooser.ReplaceChoice, List<PsiExpression>> occurrencesMap = ContainerUtil.newLinkedHashMap();
occurrencesMap.put(OccurrencesChooser.ReplaceChoice.NO, Collections.singletonList(expr));
if (myHasWriteAccess && !myCantReplaceAllButWrite) {
occurrencesMap.put(OccurrencesChooser.ReplaceChoice.NO_WRITE, myNonWrite);
}
LinkedHashMap<JavaReplaceChoice, List<PsiExpression>> buildOccurrencesMap(PsiExpression expr) {
final LinkedHashMap<JavaReplaceChoice, List<PsiExpression>> occurrencesMap = ContainerUtil.newLinkedHashMap();
if (myChainCallCandidate) {
if (myOccurrences.size() > 1 && !myCantReplaceAll) {
occurrencesMap.put(JavaReplaceChoice.NO, Collections.singletonList(expr));
occurrencesMap.put(JavaReplaceChoice.ALL, myOccurrences);
occurrencesMap.put(JavaReplaceChoice.CHAIN_ALL, myOccurrences);
} else {
occurrencesMap.put(JavaReplaceChoice.NO_CHAIN, Collections.singletonList(expr));
occurrencesMap.put(JavaReplaceChoice.CHAIN, Collections.singletonList(expr));
}
} else {
occurrencesMap.put(JavaReplaceChoice.NO, Collections.singletonList(expr));
if (myHasWriteAccess && !myCantReplaceAllButWrite) {
occurrencesMap.put(JavaReplaceChoice.NO_WRITE, myNonWrite);
}
if (myOccurrences.size() > 1 && !myCantReplaceAll) {
occurrencesMap.put(OccurrencesChooser.ReplaceChoice.ALL, myOccurrences);
if (myOccurrences.size() > 1 && !myCantReplaceAll) {
occurrencesMap.put(JavaReplaceChoice.ALL, myOccurrences);
}
}
return occurrencesMap;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.refactoring.util.CommonRefactoringUtil;
@@ -51,9 +50,9 @@ public class IntroduceVariableHandler extends IntroduceVariableBase {
boolean declareFinalIfAll,
boolean anyAssignmentLHS,
final InputValidator validator,
PsiElement anchor, OccurrencesChooser.ReplaceChoice replaceChoice) {
PsiElement anchor, JavaReplaceChoice replaceChoice) {
if (replaceChoice == null && ApplicationManager.getApplication().isUnitTestMode()) {
replaceChoice = OccurrencesChooser.ReplaceChoice.NO;
replaceChoice = JavaReplaceChoice.NO;
}
if (replaceChoice != null) {
return super.getSettings(project, editor, expr, occurrences, typeSelectorManager, declareFinalIfAll, anyAssignmentLHS, validator,
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -396,13 +396,9 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer
@Override
protected PsiVariable createFieldToStartTemplateOn(String[] names, PsiType psiType) {
PsiVariable variable = IntroduceVariableBase.introduce(myProject, myExpr, myEditor, myChosenAnchor.getElement(), getOccurrences(), mySettings);
final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class);
myPointer = declarationStatement != null ? SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(declarationStatement) : null;
myEditor.putUserData(ReassignVariableUtil.DECLARATION_KEY, myPointer);
setAdvertisementText(getAdvertisementText(declarationStatement, variable.getType(), myHasTypeSuggestion));
PsiVariable variable = introduceVariable();
PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myEditor.getDocument());
if (variable == null) return null;
final PsiVariable restoredVar = getVariable();
if (restoredVar != null) {
@@ -433,6 +429,19 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer
return variable;
}
@Nullable
protected PsiVariable introduceVariable() {
PsiVariable variable = IntroduceVariableBase
.introduce(myProject, myExpr, myEditor, myChosenAnchor.getElement(), getOccurrences(), mySettings);
final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class);
myPointer = declarationStatement != null ? SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(declarationStatement) : null;
myEditor.putUserData(ReassignVariableUtil.DECLARATION_KEY, myPointer);
setAdvertisementText(getAdvertisementText(declarationStatement, variable.getType(), myHasTypeSuggestion));
PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myEditor.getDocument());
return variable;
}
@Override
protected int getCaretOffset() {
final PsiVariable variable = getVariable();
@@ -2,7 +2,7 @@ class C {
{
int[] a = new int[1];
a[1] = 42;
int x = a[1];
System.out.println(x);
int i = a[1];
System.out.println(i);
}
}
@@ -0,0 +1,11 @@
import java.util.List;
public class StreamExtract {
void test(List<String> list) {
list.stream().forEach(s -> System.out.println(
new StringBuilder().append(String.forma<caret>t("[%s]", s))
.append("oops")
.append("argh")
));
}
}
@@ -0,0 +1,11 @@
import java.util.List;
public class StreamExtract {
void test(List<String> list) {
list.stream().map(s -> String.format("[%s]", s)).forEach(format -> System.out.println(
new StringBuilder().append(format)
.append("oops")
.append("argh")
));
}
}
@@ -0,0 +1,7 @@
import java.util.List;
public class StreamExtract {
void test(List<String> list) {
list.stream().mapToInt(x -> x.length() + 10 * x.l<caret>ength()).forEach(System.out::println);
}
}
@@ -0,0 +1,7 @@
import java.util.List;
public class StreamExtract {
void test(List<String> list) {
list.stream().mapToInt(String::length).map(length -> length + 10 * length).forEach(System.out::println);
}
}
@@ -0,0 +1,7 @@
import java.util.List;
public class StreamExtract {
void test(List<String> list) {
list.stream().forEach(s -> System.out.println(s.t<caret>oLowerCase()));
}
}
@@ -0,0 +1,7 @@
import java.util.List;
public class StreamExtract {
void test(List<String> list) {
list.stream().map(String::toLowerCase).forEach(s1 -> System.out.println(s1));
}
}
@@ -1,8 +1,8 @@
class C {
{
int[] a = new int[1];
int x = a[1];
x = 42;
System.out.println(x);
int i = a[1];
i = 42;
System.out.println(i);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ public abstract class AbstractJavaInplaceIntroduceTest extends AbstractInplaceIn
@Override
protected Sdk getProjectJDK() {
return IdeaTestUtil.getMockJdk17();
return IdeaTestUtil.getMockJdk18();
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
import com.intellij.testFramework.MapDataContext;
import org.jetbrains.annotations.NotNull;
@@ -117,7 +117,7 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe
}
public void testPlaceInsideLambdaBodyMultipleOccurrences1() throws Exception {
doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL, new Pass<AbstractInplaceIntroducer>() {
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL, new Pass<AbstractInplaceIntroducer>() {
@Override
public void pass(AbstractInplaceIntroducer inplaceIntroduceFieldPopup) {
type("expr");
@@ -190,19 +190,31 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe
}
public void testWritable() throws Exception {
doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL);
}
public void testNoWritable() throws Exception {
doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.NO_WRITE);
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.NO_WRITE);
}
public void testAllInsertFinal() throws Exception {
doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL);
}
public void testAllIncomplete() throws Exception {
doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.ALL);
}
public void testStreamSimple() throws Exception {
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.CHAIN);
}
public void testStreamMultiple() throws Exception {
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.CHAIN_ALL);
}
public void testStreamMultiline() throws Exception {
doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice.CHAIN);
}
public void testBrokenFormattingWithInValidation() throws Exception {
@@ -273,11 +285,11 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe
}
}
private void doTestReplaceChoice(OccurrencesChooser.ReplaceChoice choice) {
private void doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice choice) {
doTestReplaceChoice(choice, null);
}
private void doTestReplaceChoice(OccurrencesChooser.ReplaceChoice choice, Pass<AbstractInplaceIntroducer> pass) {
private void doTestReplaceChoice(IntroduceVariableBase.JavaReplaceChoice choice, Pass<AbstractInplaceIntroducer> pass) {
String name = getTestName(true);
configureByFile(getBasePath() + name + getExtension());
final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
@@ -318,9 +330,9 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe
}
public static class MyIntroduceVariableHandler extends IntroduceVariableHandler implements MyIntroduceHandler {
private OccurrencesChooser.ReplaceChoice myChoice = null;
private JavaReplaceChoice myChoice = null;
public void setChoice(OccurrencesChooser.ReplaceChoice choice) {
public void setChoice(JavaReplaceChoice choice) {
myChoice = choice;
}
@@ -335,7 +347,7 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe
}
@Override
protected OccurrencesChooser.ReplaceChoice getOccurrencesChoice() {
protected JavaReplaceChoice getOccurrencesChoice() {
return myChoice;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.introduceVariable.InputValidator;
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
import com.intellij.refactoring.introduceVariable.IntroduceVariableSettings;
@@ -365,7 +364,7 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
boolean declareFinalIfAll,
boolean anyAssignmentLHS,
InputValidator validator,
PsiElement anchor, final OccurrencesChooser.ReplaceChoice replaceChoice) {
PsiElement anchor, final JavaReplaceChoice replaceChoice) {
final PsiType type = typeSelectorManager.getDefaultType();
assertTrue(type.getPresentableText(), type.getPresentableText().equals(expectedTypeName));
assertEquals("path", IntroduceVariableBase.getSuggestedName(type, expr).names[0]);
@@ -384,7 +383,7 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
boolean declareFinalIfAll,
boolean anyAssignmentLHS,
InputValidator validator,
PsiElement anchor, final OccurrencesChooser.ReplaceChoice replaceChoice) {
PsiElement anchor, final JavaReplaceChoice replaceChoice) {
final PsiType type = typeSelectorManager.getDefaultType();
assertTrue(type.getPresentableText(), type.getPresentableText().equals("B"));
return super.getSettings(project, editor, expr, occurrences, typeSelectorManager, declareFinalIfAll, anyAssignmentLHS,
@@ -552,7 +551,7 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
boolean declareFinalIfAll,
boolean anyAssignmentLHS,
InputValidator validator,
PsiElement anchor, final OccurrencesChooser.ReplaceChoice replaceChoice) {
PsiElement anchor, final JavaReplaceChoice replaceChoice) {
final PsiType[] types = typeSelectorManager.getTypesForAll();
assertTrue(types[0].getPresentableText(), types[0].getPresentableText().equals("B"));
assertTrue(types[1].getPresentableText(), types[1].getPresentableText().equals("A"));
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,12 +20,10 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.introduceVariable.InputValidator;
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
import com.intellij.refactoring.introduceVariable.IntroduceVariableSettings;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
import static org.junit.Assert.assertTrue;
@@ -66,7 +64,7 @@ class MockIntroduceVariableHandler extends IntroduceVariableBase {
final boolean declareFinalIfAll,
boolean anyAssignmentLHS,
InputValidator validator,
PsiElement anchor, final OccurrencesChooser.ReplaceChoice replaceChoice) {
PsiElement anchor, final JavaReplaceChoice replaceChoice) {
final PsiType type = myLookForType ? findType(typeSelectorManager.getTypesForAll(), typeSelectorManager.getDefaultType())
: typeSelectorManager.getDefaultType();
assertTrue(type.getInternalCanonicalText(), type.getInternalCanonicalText().equals(myExpectedTypeCanonicalName));
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,7 +44,17 @@ import java.util.List;
// This prevents languages with polyadic expressions or sequences
// from reusing it, use simpleChooser instead.
public abstract class OccurrencesChooser<T> {
public enum ReplaceChoice {
public static final String DEFAULT_CHOOSER_TITLE = "Multiple occurrences found";
public interface BaseReplaceChoice {
boolean isMultiple();
boolean isAll();
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;
@@ -56,6 +66,21 @@ public abstract class OccurrencesChooser<T> {
public String getDescription() {
return myDescription;
}
@Override
public boolean isMultiple() {
return this == NO_WRITE || this == ALL;
}
@Override
public boolean isAll() {
return this == ALL;
}
@Override
public String formatDescription(int occurrencesCount) {
return MessageFormat.format(getDescription(), occurrencesCount);
}
}
public static <T extends PsiElement> OccurrencesChooser<T> simpleChooser(Editor editor) {
@@ -89,15 +114,21 @@ public abstract class OccurrencesChooser<T> {
}
public void showChooser(final Pass<ReplaceChoice> callback, final Map<ReplaceChoice, List<T>> occurrencesMap) {
showChooser(callback, occurrencesMap, DEFAULT_CHOOSER_TITLE);
}
public <C extends BaseReplaceChoice> void showChooser(final Pass<C> callback,
final Map<C, List<T>> occurrencesMap,
String title) {
if (occurrencesMap.size() == 1) {
callback.pass(occurrencesMap.keySet().iterator().next());
return;
}
final DefaultListModel model = new DefaultListModel();
for (ReplaceChoice choice : occurrencesMap.keySet()) {
final DefaultListModel<C> model = new DefaultListModel<>();
for (C choice : occurrencesMap.keySet()) {
model.addElement(choice);
}
final JList list = new JBList(model);
final JList<C> list = new JBList<>(model);
list.setCellRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(final JList list,
@@ -106,13 +137,9 @@ public abstract class OccurrencesChooser<T> {
final boolean isSelected,
final boolean cellHasFocus) {
final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
final ReplaceChoice choices = (ReplaceChoice)value;
@SuppressWarnings("unchecked") final C choices = (C)value;
if (choices != null) {
String text = choices.getDescription();
if (choices == ReplaceChoice.ALL) {
text = MessageFormat.format(text, occurrencesMap.get(choices).size());
}
setText(text);
setText(choices.formatDescription(occurrencesMap.get(choices).size()));
}
return rendererComponent;
}
@@ -120,7 +147,7 @@ public abstract class OccurrencesChooser<T> {
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
final ReplaceChoice value = (ReplaceChoice)list.getSelectedValue();
final C value = list.getSelectedValue();
if (value == null) return;
dropHighlighters();
final MarkupModel markupModel = myEditor.getMarkupModel();
@@ -136,11 +163,11 @@ public abstract class OccurrencesChooser<T> {
});
JBPopupFactory.getInstance().createListPopupBuilder(list)
.setTitle("Multiple occurrences found")
.setTitle(title)
.setMovable(true)
.setResizable(false)
.setRequestFocus(true)
.setItemChoosenCallback(() -> callback.pass((ReplaceChoice)list.getSelectedValue()))
.setItemChoosenCallback(() -> callback.pass(list.getSelectedValue()))
.addListener(new JBPopupAdapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
@@ -1046,4 +1046,8 @@ jest.debugger.use.inspect.description=Use --inspect for debugging Jest tests wit
JavaScript.WebPack.With.JsonSchema=true
JavaScript.WebPack.With.JsonSchema.description=Use JSON-Schema powered support for webpack config files
ide.diagnostics.suggest.sending.all.attachments=false
ide.diagnostics.suggest.sending.all.attachments.description=Suggest the user to send all error attachments by default
ide.diagnostics.suggest.sending.all.attachments.description=Suggest the user to send all error attachments by default
java.extract.variable.chaining.method=true
java.extract.variable.chaining.method.description=Allows "Extract variable" to create separate call chain step
java.extract.variable.chaining.method.restartRequired=true