mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
introduce functional variable (IDEA-141244)
This commit is contained in:
@@ -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();
|
||||
|
||||
+29
-23
@@ -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
|
||||
|
||||
+19
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+24
-24
@@ -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;
|
||||
|
||||
+320
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-18
@@ -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();
|
||||
}
|
||||
|
||||
+14
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -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());
|
||||
}
|
||||
}
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -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();
|
||||
}
|
||||
}
|
||||
+14
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
void foo() {
|
||||
if (true) System.out.println(<selection>"Hello, world"</selection>);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
void foo() {
|
||||
System.out.println(<selection>"Hello, world"</selection>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class Test {
|
||||
String myName;
|
||||
void foo() {
|
||||
if (true) <selection>System.out.println("Hello, world " + myName);</selection>
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class Test {
|
||||
void foo(String name) {
|
||||
System.out.println("Hello, ");
|
||||
<selection>System.out.println(name);</selection>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
if (true) <selection>System.out.println("Hello, world " + s);</selection>
|
||||
}
|
||||
}
|
||||
+10
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -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
|
||||
|
||||
+42
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user