introduce functional variable (IDEA-141244)

This commit is contained in:
Anna Kozlova
2017-06-05 11:27:43 +03:00
parent e1ae657059
commit 5d9e95ad5d
24 changed files with 683 additions and 69 deletions
@@ -33,6 +33,7 @@ import com.intellij.refactoring.extractclass.ExtractClassHandler;
import com.intellij.refactoring.introduceField.IntroduceConstantHandler;
import com.intellij.refactoring.introduceField.IntroduceFieldHandler;
import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler;
import com.intellij.refactoring.introduceVariable.IntroduceFunctionalVariableHandler;
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
import com.intellij.refactoring.memberPullUp.JavaPullUpHandler;
import com.intellij.refactoring.memberPushDown.JavaPushDownHandler;
@@ -92,6 +93,11 @@ public class JavaRefactoringSupportProvider extends RefactoringSupportProvider {
return new IntroduceFunctionalParameterHandler();
}
@Override
public RefactoringActionHandler getIntroduceFunctionalVariableHandler() {
return new IntroduceFunctionalVariableHandler();
}
@Override
public RefactoringActionHandler getPullUpHandler() {
return new JavaPullUpHandler();
@@ -124,6 +124,7 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
if (canBeChainedConstructor) {
myCbChainedConstructor = new NonFocusableCheckBox(RefactoringBundle.message("extract.chained.constructor.checkbox"));
}
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
init();
}
@@ -308,28 +309,7 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
//optionsPanel.add(new JLabel("Options: "));
if (myStaticFlag || myCanBeStatic) {
myMakeStatic.setEnabled(!myStaticFlag);
myMakeStatic.setSelected(myStaticFlag);
if (myVariableData.hasInstanceFields()) {
myMakeStatic.setText(RefactoringBundle.message("declare.static.pass.fields.checkbox"));
}
myMakeStatic.addItemListener(e -> {
if (myVariableData.hasInstanceFields()) {
myVariableData.setPassFields(myMakeStatic.isSelected());
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
updateVarargsEnabled();
createParametersPanel();
}
updateSignature();
});
optionsPanel.add(myMakeStatic);
} else {
myMakeStatic.setSelected(false);
myMakeStatic.setEnabled(false);
}
final Border emptyBorder = IdeBorderFactory.createEmptyBorder(5, 0, 5, 4);
myMakeStatic.setBorder(emptyBorder);
createStaticOptions(optionsPanel, RefactoringBundle.message("declare.static.pass.fields.checkbox"));
myFoldParameters.setSelected(myVariableData.isFoldingSelectedByDefault());
myFoldParameters.setVisible(myVariableData.isFoldable());
@@ -344,6 +324,7 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
updateSignature();
});
optionsPanel.add(myFoldParameters);
final Border emptyBorder = IdeBorderFactory.createEmptyBorder(5, 0, 5, 4);
myFoldParameters.setBorder(emptyBorder);
boolean canBeVarargs = false;
@@ -394,6 +375,30 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
return optionsPanel;
}
protected void createStaticOptions(JPanel optionsPanel, String passFieldsAsParamsLabel) {
if (myStaticFlag || myCanBeStatic) {
myMakeStatic.setEnabled(!myStaticFlag);
myMakeStatic.setSelected(myStaticFlag);
if (myVariableData.hasInstanceFields()) {
myMakeStatic.setText(passFieldsAsParamsLabel);
}
myMakeStatic.addItemListener(e -> {
if (myVariableData.hasInstanceFields()) {
myVariableData.setPassFields(myMakeStatic.isSelected());
myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]);
updateVarargsEnabled();
createParametersPanel();
}
updateSignature();
});
optionsPanel.add(myMakeStatic);
} else {
myMakeStatic.setSelected(false);
myMakeStatic.setEnabled(false);
}
myMakeStatic.setBorder(IdeBorderFactory.createEmptyBorder(5, 0, 5, 4));
}
private ComboBoxVisibilityPanel<String> createVisibilityPanel() {
final JavaComboBoxVisibilityPanel panel = new JavaComboBoxVisibilityPanel();
final PsiMethod containingMethod = getContainingMethod();
@@ -432,7 +437,8 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
@Override
@NotNull
public String getVisibility() {
return myTargetClass.isInterface() ? PsiModifier.PUBLIC : ObjectUtils.notNull(myVisibilityPanel.getVisibility(), PsiModifier.PUBLIC);
return myTargetClass.isInterface() || myVisibilityPanel == null
? PsiModifier.PUBLIC : ObjectUtils.notNull(myVisibilityPanel.getVisibility(), PsiModifier.PUBLIC);
}
@Override
@@ -549,8 +549,7 @@ public class ExtractMethodProcessor implements MatchProvider {
}
protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) {
final List<VariableData> variables = myInputVariables.getInputVariables();
myVariableDatum = variables.toArray(new VariableData[variables.size()]);
setDataFromInputVariables();
myNullness = initNullness();
myArtificialOutputVariable = PsiType.VOID.equals(myReturnType) ? getArtificialOutputVariable() : null;
final PsiType returnType = myArtificialOutputVariable != null ? myArtificialOutputVariable.getType() : myReturnType;
@@ -608,6 +607,11 @@ public class ExtractMethodProcessor implements MatchProvider {
};
}
public void setDataFromInputVariables() {
final List<VariableData> variables = myInputVariables.getInputVariables();
myVariableDatum = variables.toArray(new VariableData[variables.size()]);
}
public PsiExpression[] findOccurrences() {
if (myExpression != null) {
return new PsiExpression[] {myExpression};
@@ -1350,7 +1354,7 @@ public class ExtractMethodProcessor implements MatchProvider {
}
list.add(parm);
}
else {
else if (defineVariablesForUnselectedParameters()){
@NonNls StringBuilder buffer = new StringBuilder();
if (isFinal) {
buffer.append("final ");
@@ -1382,6 +1386,10 @@ public class ExtractMethodProcessor implements MatchProvider {
return (PsiMethod)myStyleManager.reformat(newMethod);
}
protected boolean defineVariablesForUnselectedParameters() {
return true;
}
private void copyParamAnnotations(PsiParameter parm) {
final PsiVariable variable = PsiResolveHelper.SERVICE.getInstance(myProject).resolveReferencedVariable(parm.getName(), myElements[0]);
if (variable instanceof PsiParameter) {
@@ -1735,6 +1743,10 @@ public class ExtractMethodProcessor implements MatchProvider {
return myExtractedMethod;
}
public void setMethodName(String methodName) {
myMethodName = methodName;
}
public Boolean hasDuplicates() {
List<Match> duplicates = getDuplicates();
if (duplicates != null && !duplicates.isEmpty()) {
@@ -1831,4 +1843,8 @@ public class ExtractMethodProcessor implements MatchProvider {
public PsiVariable[] getOutputVariables() {
return myOutputVariables;
}
public void setMethodVisibility(String methodVisibility) {
myMethodVisibility = methodVisibility;
}
}
@@ -548,20 +548,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase {
return false;
}
final PsiElement[] elementsCopy;
if (!elements[0].isPhysical()) {
elementsCopy = elements;
}
else {
final PsiFile copy = PsiFileFactory.getInstance(project)
.createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false);
final TextRange range = new TextRange(elements[0].getTextRange().getStartOffset(),
elements[elements.length - 1].getTextRange().getEndOffset());
final PsiExpression exprInRange = CodeInsightUtil.findExpressionInRange(copy, range.getStartOffset(), range.getEndOffset());
elementsCopy = exprInRange != null
? new PsiElement[]{exprInRange}
: CodeInsightUtil.findStatementsInRange(copy, range.getStartOffset(), range.getEndOffset());
}
final PsiElement[] elementsCopy = getElementsInCopy(project, file, elements);
final PsiMethod containingMethodCopy = Util.getContainingMethod(elementsCopy[0]);
LOG.assertTrue(containingMethodCopy != null);
final List<PsiMethod> enclosingMethodsInCopy = getEnclosingMethods(containingMethodCopy);
@@ -613,6 +600,28 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase {
return false;
}
public static PsiElement[] getElementsInCopy(Project project, PsiFile file, PsiElement[] elements) {
final PsiElement[] elementsCopy;
if (!elements[0].isPhysical()) {
elementsCopy = elements;
}
else {
final PsiFile copy = PsiFileFactory.getInstance(project)
.createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false);
final TextRange range = new TextRange(elements[0].getTextRange().getStartOffset(),
elements[elements.length - 1].getTextRange().getEndOffset());
final PsiExpression exprInRange = CodeInsightUtil.findExpressionInRange(copy, range.getStartOffset(), range.getEndOffset());
elementsCopy = exprInRange != null
? new PsiElement[]{exprInRange}
: CodeInsightUtil.findStatementsInRange(copy, range.getStartOffset(), range.getEndOffset());
}
if (elementsCopy.length == 1 && elementsCopy[0].getUserData(ElementToWorkOn.PARENT) == null) {
elementsCopy[0].putUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL, true);
}
return elementsCopy;
}
private void functionalInterfaceSelected(final PsiType selectedType,
final List<PsiMethod> enclosingMethods,
final Project project,
@@ -628,7 +637,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase {
PsiMethod methodToSearchFor, Editor editor,
final Project project,
final PsiType selectedType,
final MyExtractMethodProcessor processor,
final ExtractMethodProcessor processor,
final PsiElement[] elements) {
final PsiElement commonParent = findCommonParent(elements);
if (commonParent == null) {
@@ -657,11 +666,6 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase {
LOG.assertTrue(method != null);
final String interfaceMethodName = method.getName();
processor.setMethodName(interfaceMethodName);
if (copyElements.length == 1 && copyElements[0].getUserData(ElementToWorkOn.PARENT) == null) {
copyElements[0].putUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL, true);
}
processor.doExtract();
final PsiMethod extractedMethod = processor.getExtractedMethod();
@@ -730,10 +734,6 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase {
return false;
}
public void setMethodName(String methodName) {
myMethodName = methodName;
}
@Override
public Boolean hasDuplicates() {
return false;
@@ -0,0 +1,320 @@
/*
* 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.codeInsight.FunctionalInterfaceSuggester;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.ide.util.PsiClassListCellRenderer;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pass;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.actions.IntroduceFunctionalVariableAction;
import com.intellij.refactoring.extractMethod.*;
import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler;
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
public class IntroduceFunctionalVariableHandler extends IntroduceVariableHandler {
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) {
ExtractMethodHandler.selectAndPass(project, editor, file, new Pass<PsiElement[]>() {
@Override
public void pass(PsiElement[] elements) {
PsiElement anchorStatement = RefactoringUtil.getParentStatement(elements[0], false);
PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement);
if (tempContainer == null) return;
PsiElement[] elementsInCopy = IntroduceParameterHandler.getElementsInCopy(project, file, elements);
MyExtractMethodProcessor processor =
new MyExtractMethodProcessor(project, editor, elementsInCopy, null, IntroduceFunctionalVariableAction.REFACTORING_NAME, null,
HelpID.INTRODUCE_VARIABLE);
try {
processor.prepare();
}
catch (PrepareFailedException e) {
showErrorMessage(project, editor);
}
if (!processor.showDialog()) return;
final PsiMethod emptyMethod = JavaPsiFacade.getElementFactory(project)
.createMethodFromText(processor.generateEmptyMethod("name").getText(), elements[0]);
final Collection<? extends PsiType> types = FunctionalInterfaceSuggester.suggestFunctionalInterfaces(emptyMethod);
if (types.isEmpty()) {
showErrorMessage(project, editor, "No applicable functional interfaces found");
return;
}
if (types.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) {
functionalInterfaceSelected(ContainerUtil.getFirstItem(types), project, editor, processor, elements, anchorStatement);
}
else {
final Map<PsiClass, PsiType> classes = new LinkedHashMap<>();
for (PsiType type : types) {
classes.put(PsiUtil.resolveClassInType(type), type);
}
final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]);
final String methodSignature =
PsiFormatUtil.formatMethod(emptyMethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_PARAMETERS, PsiFormatUtilBase.SHOW_TYPE);
final PsiType returnType = emptyMethod.getReturnType();
assert returnType != null;
final String title = "Choose Applicable Functional Interface: " + methodSignature + " -> " + returnType.getPresentableText();
NavigationUtil.getPsiElementPopup(psiClasses, new PsiClassListCellRenderer(), title,
new PsiElementProcessor<PsiClass>() {
@Override
public boolean execute(@NotNull PsiClass psiClass) {
functionalInterfaceSelected(classes.get(psiClass), project, editor, processor, elements,
anchorStatement);
return true;
}
}).showInBestPositionFor(editor);
}
}
});
}
private static void functionalInterfaceSelected(PsiType type,
Project project,
Editor editor,
MyExtractMethodProcessor processor,
PsiElement[] elements,
PsiElement anchorStatement) {
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, elements[0])) return;
PsiMethodCallExpression functionalExpression = createReplacement(project, type, processor, elements);
PsiExpression qualifier = functionalExpression.getMethodExpression().getQualifierExpression();
assert qualifier != null;
SuggestedNameInfo uniqueNames = getSuggestedName(type, qualifier, anchorStatement);
WriteCommandAction.runWriteCommandAction(project, () -> {
PsiDeclarationStatement declaration =
replaceSelectionWithFunctionalCall(type, elements, anchorStatement, functionalExpression, qualifier, uniqueNames.names[0]);
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
PsiLocalVariable localVariable = (PsiLocalVariable)declaration.getDeclaredElements()[0];
PsiIdentifier nameIdentifier = localVariable.getNameIdentifier();
final int textOffset = ObjectUtils.notNull(nameIdentifier, localVariable).getTextOffset();
editor.getCaretModel().moveToOffset(textOffset);
new VariableInplaceRenamer(localVariable, editor) {
@Override
protected boolean shouldSelectAll() {
return true;
}
@Override
protected void moveOffsetAfter(boolean success) {
super.moveOffsetAfter(success);
if (success) {
final PsiNamedElement renamedVariable = getVariable();
if (renamedVariable != null) {
editor.getCaretModel().moveToOffset(renamedVariable.getTextRange().getEndOffset());
}
}
}
}.performInplaceRename();
});
}
private static PsiDeclarationStatement replaceSelectionWithFunctionalCall(PsiType type,
PsiElement[] elements,
PsiElement anchorStatement,
PsiMethodCallExpression functionalExpression,
PsiExpression qualifier,
String variableName) {
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(functionalExpression.getProject());
PsiElement tempContainer = anchorStatement.getParent();
boolean singleExpression = elements.length == 1 && elements[0] instanceof PsiExpression;
PsiDeclarationStatement declaration = elementFactory
.createVariableDeclarationStatement(variableName, type, qualifier, anchorStatement);
String callExpressionText = variableName + "." +
functionalExpression.getMethodExpression().getReferenceName() +
functionalExpression.getArgumentList().getText();
if (singleExpression) {
elements[0].replace(elementFactory.createExpressionFromText(callExpressionText, declaration));
}
if (RefactoringUtil.isLoopOrIf(tempContainer)) {
declaration = (PsiDeclarationStatement)RefactoringUtil.putStatementInLoopBody(declaration, tempContainer, anchorStatement, !singleExpression);
tempContainer = declaration.getParent();
}
else {
declaration = (PsiDeclarationStatement)tempContainer.addBefore(declaration, anchorStatement);
if (!singleExpression) {
tempContainer.deleteChildRange(elements[0], elements[elements.length - 1]);
}
}
if (!singleExpression) {
tempContainer.addAfter(elementFactory.createStatementFromText(callExpressionText + ";", declaration), declaration);
}
return (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
}
private static PsiMethodCallExpression createReplacement(Project project,
PsiType selectedType,
ExtractMethodProcessor processor,
PsiElement[] elements) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(selectedType);
final PsiClass wrapperClass = resolveResult.getElement();
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(wrapperClass);
assert method != null : "not functional class";
final String interfaceMethodName = method.getName();
processor.setMethodName(interfaceMethodName);
processor.doExtract();
final PsiMethod extractedMethod = processor.getExtractedMethod();
final PsiParameter[] parameters = extractedMethod.getParameterList().getParameters();
final PsiParameter[] interfaceParameters = method.getParameterList().getParameters();
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
for (int i = 0; i < interfaceParameters.length; i++) {
final PsiTypeElement typeAfterInterface = factory.createTypeElement(substitutor.substitute(interfaceParameters[i].getType()));
final PsiTypeElement typeElement = parameters[i].getTypeElement();
if (typeElement != null) {
typeElement.replace(typeAfterInterface);
}
}
final PsiMethodCallExpression methodCall = processor.getMethodCall();
PsiExpression psiExpression = factory
.createExpressionFromText("new " + selectedType.getCanonicalText() + "() {" + extractedMethod.getText() + "}." + methodCall.getText(),
elements[0]);
return (PsiMethodCallExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(psiExpression);
}
@Override
protected void showErrorMessage(Project project, Editor editor, String message) {
CommonRefactoringUtil
.showErrorHint(project, editor, message, IntroduceFunctionalVariableAction.REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE);
}
private void showErrorMessage(@NotNull Project project, Editor editor) {
final String message = RefactoringBundle
.getCannotRefactorMessage(
RefactoringBundle.message("is.not.supported.in.the.current.context", IntroduceFunctionalVariableAction.REFACTORING_NAME));
showErrorMessage(project, editor, message);
}
protected void setupProcessorWithoutDialog(ExtractMethodProcessor processor, InputVariables inputVariables) {
processor.setDataFromInputVariables();
processor.setMethodVisibility(PsiModifier.PUBLIC);
}
private class MyExtractMethodProcessor extends ExtractMethodProcessor {
public MyExtractMethodProcessor(Project project,
Editor editor,
PsiElement[] elements,
PsiType forcedReturnType,
String refactoringName, String initialMethodName, String helpId) {
super(project, editor, elements, forcedReturnType, refactoringName, initialMethodName, helpId);
}
@Override
public boolean isStatic() {
return false;
}
@Override
protected boolean isFoldingApplicable() {
return false;
}
@Override
protected AbstractExtractDialog createExtractMethodDialog(boolean direct) {
setDataFromInputVariables();
return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, null, getTypeParameterList(),
getThrownExceptions(), isStatic(), isCanBeStatic(), false,
IntroduceFunctionalVariableAction.REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE, null, myElements) {
@Override
protected JComponent createNorthPanel() {
if (!myInputVariables.hasInstanceFields()) {
return null;
}
JPanel optionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5));
createStaticOptions(optionsPanel, "Pass fields as params");
return optionsPanel;
}
@Override
public JComponent getPreferredFocusedComponent() {
return myParamTable;
}
@Override
protected String getSignature() {
String parametersList =
Arrays.stream(getChosenParameters())
.filter(data -> data.passAsParameter)
.map(data -> data.type.getPresentableText())
.reduce((result, item) -> result + ", " + item)
.orElse("");
String returnTypeString = myReturnType == null || PsiType.VOID.equals(myReturnType)
? "{}" : myReturnType.getPresentableText();
return "(" + parametersList + ") -> " + returnTypeString;
}
@NotNull
@Override
public String getVisibility() {
return PsiModifier.PUBLIC;
}
};
}
@Override
public boolean showDialog() {
if (!myInputVariables.hasInstanceFields() && myInputVariables.getInputVariables().isEmpty() ||
ApplicationManager.getApplication().isUnitTestMode()) {
setupProcessorWithoutDialog(this, myInputVariables);
return true;
}
return super.showDialog();
}
@Override
protected boolean defineVariablesForUnselectedParameters() {
return false;
}
}
}
@@ -613,18 +613,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
final PsiElement anchorStatement = RefactoringUtil.getParentStatement(physicalElement != null ? physicalElement : expr, false);
if (anchorStatement == null) {
return parentStatementNotFound(project, editor);
}
if (checkAnchorBeforeThisOrSuper(project, editor, anchorStatement, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE)) return false;
final PsiElement tempContainer = anchorStatement.getParent();
if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return false;
}
PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement);
if (tempContainer == null) return false;
final PsiFile file = anchorStatement.getContainingFile();
LOG.assertTrue(file != null, "expr.getContainingFile() == null");
@@ -766,6 +756,24 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
return wasSucceed[0];
}
protected PsiElement checkAnchorStatement(Project project, Editor editor, PsiElement anchorStatement) {
if (anchorStatement == null) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return null;
}
if (checkAnchorBeforeThisOrSuper(project, editor, anchorStatement, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE)) return null;
final PsiElement tempContainer = anchorStatement.getParent();
if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return null;
}
return tempContainer;
}
protected JavaReplaceChoice getOccurrencesChoice() {
return null;
}
@@ -1110,12 +1118,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
: factory.createExpressionFromText(text, parent);
}
private boolean parentStatementNotFound(final Project project, Editor editor) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return false;
}
protected boolean invokeImpl(Project project, PsiLocalVariable localVariable, Editor editor) {
throw new UnsupportedOperationException();
}
@@ -0,0 +1,14 @@
import java.util.function.Supplier;
class Test {
void foo() {
if (true) {
Supplier<String> stringSupplier = new Supplier<String>() {
public String get() {
return "Hello, world";
}
};
System.out.println(stringSupplier.get());
}
}
}
@@ -0,0 +1,12 @@
import java.util.function.Supplier;
class Test {
void foo() {
Supplier<String> stringSupplier = new Supplier<String>() {
public String get() {
return "Hello, world";
}
};
System.out.println(stringSupplier.get());
}
}
@@ -0,0 +1,15 @@
import java.util.function.Consumer;
class Test {
String myName;
void foo() {
if (true) {
Consumer<String> stringConsumer = new Consumer<String>() {
public void accept(String myName) {
System.out.println("Hello, world " + myName);
}
};
stringConsumer.accept(myName);
}
}
}
@@ -0,0 +1,11 @@
class Test {
void foo(String name) {
System.out.println("Hello, ");
Runnable runnable = new Runnable() {
public void run() {
System.out.println(name);
}
};
runnable.run();
}
}
@@ -0,0 +1,14 @@
import java.util.function.Consumer;
class Test {
void foo(String s) {
if (true) {
Consumer<String> stringConsumer = new Consumer<String>() {
public void accept(String s) {
System.out.println("Hello, world " + s);
}
};
stringConsumer.accept(s);
}
}
}
@@ -0,0 +1,17 @@
import java.util.function.Consumer;
class Test {
void foo(String s) {
if (true) {
Consumer<String> stringConsumer = new Consumer<String>() {
public void accept(String s) {
System.out.println("Hello, world " + s);
System.out.println();
}
};
stringConsumer.accept(s);
System.out.println();
}
}
}
@@ -0,0 +1,5 @@
class Test {
void foo() {
if (true) System.out.println(<selection>"Hello, world"</selection>);
}
}
@@ -0,0 +1,5 @@
class Test {
void foo() {
System.out.println(<selection>"Hello, world"</selection>);
}
}
@@ -0,0 +1,6 @@
class Test {
String myName;
void foo() {
if (true) <selection>System.out.println("Hello, world " + myName);</selection>
}
}
@@ -0,0 +1,6 @@
class Test {
void foo(String name) {
System.out.println("Hello, ");
<selection>System.out.println(name);</selection>
}
}
@@ -0,0 +1,5 @@
class Test {
void foo(String s) {
if (true) <selection>System.out.println("Hello, world " + s);</selection>
}
}
@@ -0,0 +1,10 @@
class Test {
void foo(String s) {
if (true) {
<selection>System.out.println("Hello, world " + s);
System.out.println();
</selection>
System.out.println();
}
}
}
@@ -0,0 +1,86 @@
/*
* 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.java.refactoring;
import com.intellij.JavaTestUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.refactoring.extractMethod.ExtractMethodProcessor;
import com.intellij.refactoring.extractMethod.InputVariables;
import com.intellij.refactoring.introduceVariable.IntroduceFunctionalVariableHandler;
import com.intellij.testFramework.MapDataContext;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.annotations.NotNull;
@TestDataPath("$CONTENT_ROOT/testData")
public class IntroduceFunctionalVariableTest extends LightRefactoringTestCase {
@NotNull
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath();
}
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_1_8;
}
public void testExpressionNoVarsSelected() throws Exception {
doTest();
}
public void testExpressionInLoopNoVars() throws Exception {
doTest();
}
public void testStatementInLoop() throws Exception {
doTest();
}
public void testStatements() throws Exception {
doTest();
}
public void testPassFieldsAsParameters() throws Exception {
doTest();
}
public void testSkipUsedLocals() throws Exception {
doTest(0);
}
private void doTest(int... disableParams) {
boolean enabled = true;
try {
configureByFile("/refactoring/introduceFunctionalVariable/before" + getTestName(false) + ".java");
enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
getEditor().getSettings().setVariableInplaceRenameEnabled(false);
new IntroduceFunctionalVariableHandler() {
@Override
protected void setupProcessorWithoutDialog(ExtractMethodProcessor processor, InputVariables inputVariables) {
inputVariables.setPassFields(true);
super.setupProcessorWithoutDialog(processor, inputVariables);
for (int i : disableParams) {
processor.doNotPassParameter(i);
}
}
}.invoke(getProject(), getEditor(), getFile(), new MapDataContext());
checkResultByFile("/refactoring/introduceFunctionalVariable/after" + getTestName(false) + ".java");
} finally {
getEditor().getSettings().setVariableInplaceRenameEnabled(enabled);
}
}
}
@@ -101,6 +101,15 @@ public abstract class RefactoringSupportProvider {
return null;
}
/**
* @return handler for introducing functional locals in this language
* @see ContextAwareActionHandler
* @see RefactoringActionHandler
*/
public RefactoringActionHandler getIntroduceFunctionalVariableHandler() {
return null;
}
/**
* @return handler for pulling up members in this language
* @see com.intellij.refactoring.RefactoringActionHandler
@@ -0,0 +1,42 @@
/*
* 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.actions;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import org.jetbrains.annotations.NotNull;
public class IntroduceFunctionalVariableAction extends BasePlatformRefactoringAction {
public static final String REFACTORING_NAME = RefactoringBundle.message("introduce.functional.variable.title");
@Override
protected boolean isAvailableInEditorOnly() {
return true;
}
@Override
protected boolean isEnabledOnElements(@NotNull PsiElement[] elements) {
return false;
}
@Override
protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) {
return provider.getIntroduceFunctionalVariableHandler();
}
}
@@ -674,7 +674,9 @@ action.IntroduceConstant.description=Replace selected expression with a constant
action.IntroduceParameter.text=_Parameter...
action.IntroduceParameter.description=Turn the selected expression into method parameter
action.IntroduceFunctionalParameter.text=Functiona_l Parameter...
action.IntroduceFunctionalParameter.description=Replace selected statements with a call to new functional method parameter
action.IntroduceFunctionalParameter.description=Replace selected statements with a call to a new functional method parameter
action.IntroduceFunctionalVariable.text=Functional Variable...
action.IntroduceFunctionalVariable.description=Replace selected statements with a call to a new functional variable
action.ExtractInterface.text=_Interface...
action.ExtractInterface.description=Extract interface from the selected class
action.ExtractModule.text=_Module...
@@ -805,6 +805,7 @@ enter.new.project.name=Enter new project name:
rename.project=Rename Project
renames.project=Renames project
introduce.functional.parameter.title=Extract Functional Parameter
introduce.functional.variable.title=Extract Functional Variable
introduce.parameter.convert.lambda=&Convert to functional expression
expand.method.reference.warning=Method is used in method reference. Proceeding would result in conversion to lambda expression
+4
View File
@@ -79,6 +79,10 @@
<keyboard-shortcut first-keystroke="control shift alt P" keymap="$default"/>
</action>
<action id="IntroduceFunctionalVariable" class="com.intellij.refactoring.actions.IntroduceFunctionalVariableAction">
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceFunctionalParameter"/>
</action>
<action id="RenameFile" class="com.intellij.refactoring.actions.RenameFileAction">
<add-to-group group-id="RefactoringMenu" anchor="after" relative-to-action="RenameElement"/>
<add-to-group group-id="EditorTabPopupMenuEx" anchor="after" relative-to-action="AddAllToFavorites"/>