inplace introduce parameter: choose methods to introduce parameters in popup

This commit is contained in:
anna
2011-03-06 15:00:59 +01:00
parent 5436cbad01
commit 703612fcc7
3 changed files with 229 additions and 86 deletions
@@ -28,6 +28,7 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.TestUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.application.ApplicationManager;
@@ -552,15 +553,18 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase
public void pass(final ElementToWorkOn elementToWorkOn) {
if (elementToWorkOn == null) return;
final boolean hasRunTemplate = LookupManager.getActiveLookup(editor) == null;
if (elementToWorkOn.getExpression() == null) {
final PsiLocalVariable localVariable = elementToWorkOn.getLocalVariable();
final boolean result = invokeImpl(project, localVariable, editor);
final boolean result = invokeImpl(project, localVariable, editor) && hasRunTemplate;
if (result) {
editor.getSelectionModel().removeSelection();
}
}
else if (invokeImpl(project, elementToWorkOn.getExpression(), editor)) {
editor.getSelectionModel().removeSelection();
else {
if (invokeImpl(project, elementToWorkOn.getExpression(), editor) && hasRunTemplate) {
editor.getSelectionModel().removeSelection();
}
}
}
};
@@ -209,7 +209,7 @@ public class IntroduceParameterDialog extends RefactoringDialog {
gbConstraints.insets = new Insets(0, 16, 4, 8);
}
myPanel.createLocalVariablePanel(gbConstraints, panel, settings);
myPanel.createRemoveParamsPanel(gbConstraints, panel);
gbConstraints.insets = new Insets(4, 0, 4, 8);
gbConstraints.gridy++;
myPanel.createDelegateCb(gbConstraints, panel);
@@ -32,8 +32,16 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
@@ -41,17 +49,28 @@ import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.*;
import com.intellij.refactoring.introduceField.ElementToWorkOn;
import com.intellij.refactoring.ui.MethodCellRenderer;
import com.intellij.refactoring.ui.NameSuggestionsGenerator;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.occurences.ExpressionOccurenceManager;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBList;
import com.intellij.usageView.UsageInfo;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.*;
import java.util.List;
@@ -60,6 +79,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase implements R
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceParameter.IntroduceParameterHandler");
static final String REFACTORING_NAME = RefactoringBundle.message("introduce.parameter.title");
private Project myProject;
private JBPopup myEnclosingMethodsPopup;
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file, DataContext dataContext) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
@@ -86,7 +106,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase implements R
return invoke(editor, project, null, localVariable, true);
}
private boolean invoke(Editor editor, Project project, final PsiExpression expr,
private boolean invoke(final Editor editor, final Project project, final PsiExpression expr,
PsiLocalVariable localVar, boolean invokedOnDeclaration) {
LOG.assertTrue(!PsiDocumentManager.getInstance(project).hasUncommitedDocuments());
PsiMethod method;
@@ -131,95 +151,108 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase implements R
return false;
}
method = chooseEnclosingMethod(method);
if (method == null) return false;
final PsiMethod methodToSearchFor = SuperMethodWarningUtil.checkSuperMethod(method, RefactoringBundle.message("to.refactor"));
if (methodToSearchFor == null) return false;
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, methodToSearchFor)) return false;
PsiExpression[] occurences;
if (expr != null) {
occurences = new ExpressionOccurenceManager(expr, method, null).findExpressionOccurrences();
final List<PsiMethod> validEnclosingMethods = getEnclosingMethods(method);
if (validEnclosingMethods.isEmpty()) {
return false;
}
else { // local variable
occurences = CodeInsightUtil.findReferenceExpressions(method, localVar);
}
PsiExpression expressionToRemoveParamFrom = expr;
if (expr == null) {
expressionToRemoveParamFrom = localVar.getInitializer();
}
TIntArrayList parametersToRemove = expressionToRemoveParamFrom == null ? new TIntArrayList() : Util.findParametersToRemove(method, expressionToRemoveParamFrom, occurences);
boolean mustBeFinal = false;
if (localVar != null) {
for(PsiExpression occurrence: occurences) {
if (PsiTreeUtil.getParentOfType(occurrence, PsiClass.class, PsiMethod.class) != method) {
mustBeFinal = true;
break;
}
final Introducer introducer = new Introducer(project, expr, localVar, editor);
final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
if (validEnclosingMethods.size() == 1 || unitTestMode) {
if (validEnclosingMethods.get(0).findDeepestSuperMethod() == null || unitTestMode) {
introducer.introduceParameter(method, method);
return true;
}
}
List<UsageInfo> localVars = new ArrayList<UsageInfo>();
List<UsageInfo> classMemberRefs = new ArrayList<UsageInfo>();
List<UsageInfo> params = new ArrayList<UsageInfo>();
chooseMethodToIntroduceParameter(editor, validEnclosingMethods, introducer);
if (expr != null) {
Util.analyzeExpression(expr, localVars, classMemberRefs, params);
}
if (expr instanceof PsiReferenceExpression) {
PsiElement resolved = ((PsiReferenceExpression) expr).resolve();
if (resolved instanceof PsiLocalVariable) {
localVar = (PsiLocalVariable) resolved;
}
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
@NonNls String parameterName = "anObject";
boolean replaceAllOccurences = true;
boolean isDeleteLocalVariable = true;
PsiExpression initializer = localVar != null && expr == null ? localVar.getInitializer() : expr;
new IntroduceParameterProcessor(myProject, method, methodToSearchFor, initializer, expr, localVar, isDeleteLocalVariable, parameterName,
replaceAllOccurences, IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, mustBeFinal,
false, null,
parametersToRemove).run();
}
else {
final String propName = localVar != null ? JavaCodeStyleManager.getInstance(myProject).variableNameToPropertyName(localVar.getName(), VariableKind.LOCAL_VARIABLE) : null;
final PsiType initializerType = IntroduceParameterProcessor.getInitializerType(null, expr, localVar);
TypeSelectorManagerImpl typeSelectorManager = expr != null
? new TypeSelectorManagerImpl(project, initializerType, expr, occurences)
: new TypeSelectorManagerImpl(project, initializerType, occurences);
NameSuggestionsGenerator nameSuggestionsGenerator = createNameSuggestionGenerator(expr, propName);
boolean isInplaceAvailableOnDataContext = editor != null && editor.getSettings().isVariableInplaceRenameEnabled()
&& method == methodToSearchFor
&& method.hasModifierProperty(PsiModifier.PRIVATE);
if (!isInplaceAvailableOnDataContext) {
if (editor != null) {
RefactoringUtil.highlightAllOccurences(myProject, occurences, editor);
}
new IntroduceParameterDialog(myProject, classMemberRefs, occurences.length, localVar, expr, nameSuggestionsGenerator,
typeSelectorManager, methodToSearchFor, method, parametersToRemove, mustBeFinal).show();
if (editor != null) {
editor.getSelectionModel().removeSelection();
}
} else {
new InplaceIntroduceParameterPopup(project, editor, classMemberRefs,
typeSelectorManager,
expr, localVar, method, methodToSearchFor, occurences, parametersToRemove,
mustBeFinal).inplaceIntroduceParameter();
}
}
return true;
}
private void chooseMethodToIntroduceParameter(final Editor editor,
final List<PsiMethod> validEnclosingMethods,
final Introducer introducer) {
final JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(IdeBorderFactory.createRoundedBorder());
final JCheckBox superMethod = new JCheckBox("Choose base method", true);
superMethod.setMnemonic('b');
panel.add(superMethod, BorderLayout.SOUTH);
final JBList list = new JBList(validEnclosingMethods.toArray());
list.setCellRenderer(new MethodCellRenderer());
list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
final List<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
final TextAttributes attributes =
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(final ListSelectionEvent e) {
final PsiMethod selectedMethod = (PsiMethod)list.getSelectedValue();
if (selectedMethod == null) return;
dropHighlighters(highlighters);
updateView(selectedMethod, editor, attributes, highlighters, superMethod);
}
});
updateView(validEnclosingMethods.get(0), editor, attributes, highlighters, superMethod);
panel.add(ScrollPaneFactory.createScrollPane(list), BorderLayout.CENTER);
final List<Pair<ActionListener, KeyStroke>>
keyboardActions = Collections.singletonList(Pair.<ActionListener, KeyStroke>create(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final PsiMethod methodToSearchIn = (PsiMethod)list.getSelectedValue();
if (myEnclosingMethodsPopup != null && myEnclosingMethodsPopup.isVisible()) {
myEnclosingMethodsPopup.cancel();
}
final PsiMethod methodToSearchFor = superMethod.isEnabled() && superMethod.isSelected()
? methodToSearchIn.findDeepestSuperMethod() : methodToSearchIn;
Runnable runnable = new Runnable() {
public void run() {
introducer.introduceParameter(methodToSearchIn, methodToSearchFor);
}
};
IdeFocusManager.findInstance().doWhenFocusSettlesDown(runnable);
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)));
myEnclosingMethodsPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, list)
.setTitle("Introduce parameter to method")
.setMovable(false)
.setResizable(false)
.setRequestFocus(true)
.setKeyboardActions(keyboardActions).addListener(new JBPopupAdapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
dropHighlighters(highlighters);
}
}).createPopup();
myEnclosingMethodsPopup.showInBestPositionFor(editor);
}
private static void updateView(PsiMethod selectedMethod,
Editor editor,
TextAttributes attributes,
List<RangeHighlighter> highlighters,
JCheckBox superMethod) {
final MarkupModel markupModel = editor.getMarkupModel();
final PsiIdentifier nameIdentifier = selectedMethod.getNameIdentifier();
if (nameIdentifier != null) {
final TextRange textRange = nameIdentifier.getTextRange();
final RangeHighlighter rangeHighlighter = markupModel.addRangeHighlighter(
textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.SELECTION - 1,
attributes,
HighlighterTargetArea.EXACT_RANGE);
highlighters.add(rangeHighlighter);
}
superMethod.setEnabled(selectedMethod.findDeepestSuperMethod() != null);
}
private static void dropHighlighters(List<RangeHighlighter> highlighters) {
for (RangeHighlighter highlighter : highlighters) {
highlighter.dispose();
}
highlighters.clear();
}
protected static NameSuggestionsGenerator createNameSuggestionGenerator(final PsiExpression expr,
final String propName) {
return new NameSuggestionsGenerator() {
@@ -291,4 +324,110 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase implements R
private static boolean isLibraryInterfaceMethod(final PsiMethod method) {
return method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.getManager().isInProject(method);
}
private static class Introducer {
private final Project myProject;
private final PsiExpression myExpr;
private PsiLocalVariable myLocalVar;
private final Editor myEditor;
public Introducer(Project project,
PsiExpression expr,
PsiLocalVariable localVar,
Editor editor) {
myProject = project;
myExpr = expr;
myLocalVar = localVar;
myEditor = editor;
}
public void introduceParameter(PsiMethod method, PsiMethod methodToSearchFor) {
if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, methodToSearchFor)) return;
PsiExpression[] occurences;
if (myExpr != null) {
occurences = new ExpressionOccurenceManager(myExpr, method, null).findExpressionOccurrences();
}
else { // local variable
occurences = CodeInsightUtil.findReferenceExpressions(method, myLocalVar);
}
PsiExpression expressionToRemoveParamFrom = myExpr;
if (myExpr == null) {
expressionToRemoveParamFrom = myLocalVar.getInitializer();
}
TIntArrayList parametersToRemove = expressionToRemoveParamFrom == null ? new TIntArrayList() : Util
.findParametersToRemove(method, expressionToRemoveParamFrom, occurences);
boolean mustBeFinal = false;
if (myLocalVar != null) {
for(PsiExpression occurrence: occurences) {
if (PsiTreeUtil.getParentOfType(occurrence, PsiClass.class, PsiMethod.class) != method) {
mustBeFinal = true;
break;
}
}
}
List<UsageInfo> localVars = new ArrayList<UsageInfo>();
List<UsageInfo> classMemberRefs = new ArrayList<UsageInfo>();
List<UsageInfo> params = new ArrayList<UsageInfo>();
if (myExpr != null) {
Util.analyzeExpression(myExpr, localVars, classMemberRefs, params);
}
if (myExpr instanceof PsiReferenceExpression) {
PsiElement resolved = ((PsiReferenceExpression)myExpr).resolve();
if (resolved instanceof PsiLocalVariable) {
myLocalVar = (PsiLocalVariable) resolved;
}
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
@NonNls String parameterName = "anObject";
boolean replaceAllOccurences = true;
boolean isDeleteLocalVariable = true;
PsiExpression initializer = myLocalVar != null && myExpr == null ? myLocalVar.getInitializer() : myExpr;
new IntroduceParameterProcessor(myProject, method, methodToSearchFor, initializer, myExpr, myLocalVar, isDeleteLocalVariable, parameterName,
replaceAllOccurences, IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, mustBeFinal,
false, null,
parametersToRemove).run();
}
else {
final String propName = myLocalVar != null ? JavaCodeStyleManager
.getInstance(myProject).variableNameToPropertyName(myLocalVar.getName(), VariableKind.LOCAL_VARIABLE) : null;
final PsiType initializerType = IntroduceParameterProcessor.getInitializerType(null, myExpr, myLocalVar);
TypeSelectorManagerImpl typeSelectorManager = myExpr != null
? new TypeSelectorManagerImpl(myProject, initializerType, myExpr, occurences)
: new TypeSelectorManagerImpl(myProject, initializerType, occurences);
NameSuggestionsGenerator nameSuggestionsGenerator = createNameSuggestionGenerator(myExpr, propName);
boolean isInplaceAvailableOnDataContext = myEditor != null && myEditor.getSettings().isVariableInplaceRenameEnabled();
if (!isInplaceAvailableOnDataContext) {
if (myEditor != null) {
RefactoringUtil.highlightAllOccurences(myProject, occurences, myEditor);
}
new IntroduceParameterDialog(myProject, classMemberRefs, occurences.length, myLocalVar, myExpr, nameSuggestionsGenerator,
typeSelectorManager, methodToSearchFor, method, parametersToRemove, mustBeFinal).show();
if (myEditor != null) {
myEditor.getSelectionModel().removeSelection();
}
}
else {
new InplaceIntroduceParameterPopup(myProject, myEditor, classMemberRefs,
typeSelectorManager,
myExpr, myLocalVar, method, methodToSearchFor, occurences, parametersToRemove,
mustBeFinal).inplaceIntroduceParameter();
}
}
}
}
}