diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java index 5c8630ecd17d..504267ef9fa7 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,47 +20,30 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; -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.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupAdapter; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.ui.popup.LightweightWindowEvent; -import com.intellij.openapi.util.Iconable; -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.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.util.PsiFormatUtil; -import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.IntroduceTargetChooser; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.util.CommonRefactoringUtil; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.components.JBList; import com.intellij.util.Function; +import com.intellij.util.PairFunction; import com.intellij.util.Processor; import gnu.trove.TObjectIntHashMap; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.GroovyIcons; -import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; @@ -70,14 +53,8 @@ import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext; import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceDialog; import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase; import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceRefactoringError; +import org.jetbrains.plugins.groovy.refactoring.ui.MethodOrClosureScopeChooser; -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.ArrayList; import java.util.Collections; import java.util.List; @@ -87,11 +64,9 @@ import static org.jetbrains.plugins.groovy.refactoring.HelpID.GROOVY_INTRODUCE_P /** * @author Maxim.Medvedev */ -public class GrIntroduceParameterHandler implements RefactoringActionHandler { +public class GrIntroduceParameterHandler implements RefactoringActionHandler, MethodOrClosureScopeChooser.JBPopupOwner { private static final Logger LOG = Logger.getInstance(GrIntroduceParameterHandler.class); - @NonNls public static final String USE_SUPER_METHOD_OF = "Change base method"; - @NonNls public static final String CHANGE_USAGES_OF = "Change usages"; private JBPopup myEnclosingMethodsPopup; public void invoke(final @NotNull Project project, final Editor editor, final PsiFile file, final @Nullable DataContext dataContext) { @@ -158,7 +133,9 @@ public class GrIntroduceParameterHandler implements RefactoringActionHandler { } } - public void findScope(final GrExpression expression, final GrVariable variable, final Editor editor, final Project project) { + private void findScope(@Nullable final GrExpression expression, @Nullable final GrVariable variable, @NotNull final Editor editor, @NotNull final Project project) { + LOG.assertTrue(expression != null || variable != null); + PsiElement place = expression == null ? variable : expression; final List scopes = new ArrayList(); @@ -174,131 +151,41 @@ public class GrIntroduceParameterHandler implements RefactoringActionHandler { } else if (scopes.size() == 1) { final GrParametersOwner owner = scopes.get(0); + final PsiElement toSearchFor; if (owner instanceof GrMethod) { - PsiMethod newMethod = SuperMethodWarningUtil.checkSuperMethod((PsiMethod)owner, RefactoringBundle.message("to.refactor")); - if (newMethod == null) return; - getContext(project, editor, expression, variable, owner, newMethod); - return; + toSearchFor = SuperMethodWarningUtil.checkSuperMethod((PsiMethod)owner, RefactoringBundle.message("to.refactor")); + if (toSearchFor == null) return; //if it is null, refactoring was canceled } else { - getContext(project, editor, expression, variable, owner, findVariableToUse(owner)); - return; + toSearchFor = MethodOrClosureScopeChooser.findVariableToUse(owner); } + getContext(project, editor, expression, variable, owner, toSearchFor); } else { - final JPanel panel = new JPanel(new BorderLayout()); - final JCheckBox superMethod = new JCheckBox(USE_SUPER_METHOD_OF, true); - superMethod.setMnemonic('U'); - panel.add(superMethod, BorderLayout.SOUTH); - final JBList list = new JBList(scopes.toArray()); - list.setVisibleRowCount(5); - list.setCellRenderer(new DefaultListCellRenderer() { + myEnclosingMethodsPopup = MethodOrClosureScopeChooser.create(scopes, editor, this, new PairFunction() { @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - - final String text; - if (value instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)value; - text = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, - PsiFormatUtilBase.SHOW_CONTAINING_CLASS | - PsiFormatUtilBase.SHOW_NAME | - PsiFormatUtilBase.SHOW_PARAMETERS, - PsiFormatUtilBase.SHOW_TYPE); - final int flags = Iconable.ICON_FLAG_VISIBILITY; - final Icon icon = method.getIcon(flags); - if (icon != null) setIcon(icon); - } - else { - LOG.assertTrue(value instanceof GrClosableBlock); - setIcon(GroovyIcons.GROOVY_ICON_16x16); - text = "{...}"; - } - setText(text); - return this; + public Object fun(GrParametersOwner owner, PsiElement element) { + getContext(project, editor, expression, variable, owner, element); + return null; } }); - list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - list.setSelectedIndex(0); - final List highlighters = new ArrayList(); - final TextAttributes attributes = - EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); - list.addListSelectionListener(new ListSelectionListener() { - public void valueChanged(final ListSelectionEvent e) { - final GrParametersOwner selectedMethod = (GrParametersOwner)list.getSelectedValue(); - if (selectedMethod == null) return; - dropHighlighters(highlighters); - updateView(selectedMethod, editor, attributes, highlighters, superMethod); - } - }); - updateView(scopes.get(0), editor, attributes, highlighters, superMethod); - final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(list); - scrollPane.setBorder(null); - panel.add(scrollPane, BorderLayout.CENTER); - - final List> keyboardActions = Collections.singletonList(Pair.create(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final GrParametersOwner ToSearchIn = (GrParametersOwner)list.getSelectedValue(); - if (myEnclosingMethodsPopup != null && myEnclosingMethodsPopup.isVisible()) { - myEnclosingMethodsPopup.cancel(); - } - - - final PsiElement toSearchFor; - if (ToSearchIn instanceof GrMethod) { - toSearchFor = superMethod.isEnabled() && superMethod.isSelected() ? ((GrMethod)ToSearchIn).findDeepestSuperMethod() : ((GrMethod)ToSearchIn); - } - else { - toSearchFor = superMethod.isEnabled() && superMethod.isSelected() ? ToSearchIn.getParent(): null; - } - Runnable runnable = new Runnable() { - public void run() { - getContext(project, editor, expression, variable, ToSearchIn, toSearchFor); - } - }; - IdeFocusManager.findInstance().doWhenFocusSettlesDown(runnable); - } - }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0))); - myEnclosingMethodsPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, list) - .setTitle("Introduce parameter to") - .setMovable(false) - .setResizable(false) - .setRequestFocus(true) - .setKeyboardActions(keyboardActions).addListener(new JBPopupAdapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - dropHighlighters(highlighters); - } - }).createPopup(); myEnclosingMethodsPopup.showInBestPositionFor(editor); } } - @Nullable - private static GrVariable findVariableToUse(GrParametersOwner owner) { - final PsiElement parent = owner.getParent(); - if (parent instanceof GrVariable) return (GrVariable)parent; - if (parent instanceof GrAssignmentExpression && - ((GrAssignmentExpression)parent).getRValue() == owner && - ((GrAssignmentExpression)parent).getOperationToken() == GroovyTokenTypes.mASSIGN) { - final GrExpression lValue = ((GrAssignmentExpression)parent).getLValue(); - if (lValue instanceof GrReferenceExpression) { - final PsiElement resolved = ((GrReferenceExpression)lValue).resolve(); - if (resolved instanceof GrVariable) { - return (GrVariable)resolved; - } - } - } - return null; + @Override + public JBPopup get() { + return myEnclosingMethodsPopup; } - protected void getContext(Project project, - Editor editor, - GrExpression expression, + protected void getContext(@NotNull Project project, + @NotNull Editor editor, + @Nullable GrExpression expression, @Nullable GrVariable variable, - GrParametersOwner toReplaceIn, + @NotNull GrParametersOwner toReplaceIn, @Nullable PsiElement toSearchFor) { + LOG.assertTrue(expression != null || variable != null); + GrIntroduceContext context; if (variable == null) { final PsiElement[] occurrences = findOccurrences(expression, toReplaceIn); @@ -329,8 +216,12 @@ public class GrIntroduceParameterHandler implements RefactoringActionHandler { dialog.show(); } - private static PsiElement[] findOccurrences(GrExpression expression, PsiElement scope) { - final PsiElement[] occurrences = GroovyRefactoringUtil.getExpressionOccurrences(PsiUtil.skipParentheses(expression, false), scope, true); + @NotNull + private static PsiElement[] findOccurrences(@NotNull GrExpression expression, PsiElement scope) { + final PsiElement expr = PsiUtil.skipParentheses(expression, false); + if (expr == null) return PsiElement.EMPTY_ARRAY; + + final PsiElement[] occurrences = GroovyRefactoringUtil.getExpressionOccurrences(expr, scope, true); if (occurrences == null || occurrences.length == 0) { throw new GrIntroduceRefactoringError(GroovyRefactoringBundle.message("no.occurences.found")); } @@ -341,31 +232,4 @@ public class GrIntroduceParameterHandler implements RefactoringActionHandler { public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { // Does nothing } - - private static void updateView(GrParametersOwner selectedMethod, - Editor editor, - TextAttributes attributes, - List highlighters, - JCheckBox superMethod) { - final MarkupModel markupModel = editor.getMarkupModel(); - final TextRange textRange = selectedMethod.getTextRange(); - final RangeHighlighter rangeHighlighter = - markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.SELECTION - 1, attributes, HighlighterTargetArea.EXACT_RANGE); - highlighters.add(rangeHighlighter); - if (selectedMethod instanceof GrMethod) { - superMethod.setText(USE_SUPER_METHOD_OF); - superMethod.setEnabled(((GrMethod)selectedMethod).findDeepestSuperMethod() != null); - } - else { - superMethod.setText(CHANGE_USAGES_OF); - superMethod.setEnabled(findVariableToUse(selectedMethod) != null); - } - } - - private static void dropHighlighters(List highlighters) { - for (RangeHighlighter highlighter : highlighters) { - highlighter.dispose(); - } - highlighters.clear(); - } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/ui/MethodOrClosureScopeChooser.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/ui/MethodOrClosureScopeChooser.java new file mode 100644 index 000000000000..b82622e1d8db --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/ui/MethodOrClosureScopeChooser.java @@ -0,0 +1,221 @@ +/* + * Copyright 2000-2012 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 org.jetbrains.plugins.groovy.refactoring.ui; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.colors.EditorColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.markup.*; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupAdapter; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.psi.util.PsiFormatUtilBase; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.components.JBList; +import com.intellij.util.PairFunction; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.GroovyIcons; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; + +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.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class MethodOrClosureScopeChooser { + private static final Logger LOG = Logger.getInstance(MethodOrClosureScopeChooser.class); + + @NonNls private static final String USE_SUPER_METHOD_OF = "Change base method"; + @NonNls private static final String CHANGE_USAGES_OF = "Change usages"; + + public interface JBPopupOwner { + JBPopup get(); + } + + /** + * @param callback is invoked if any scope was chosen. The first arg is this scope and the second arg is a psielement to search for (super method of chosen method or + * variable if the scope is a closure) + */ + public static JBPopup create(List scopes, + final Editor editor, + final JBPopupOwner popupRef, + final PairFunction callback) { + final JPanel panel = new JPanel(new BorderLayout()); + final JCheckBox superMethod = new JCheckBox(USE_SUPER_METHOD_OF, true); + superMethod.setMnemonic('U'); + panel.add(superMethod, BorderLayout.SOUTH); + final JBList list = new JBList(scopes.toArray()); + list.setVisibleRowCount(5); + list.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + + final String text; + if (value instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)value; + text = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, + PsiFormatUtilBase.SHOW_CONTAINING_CLASS | + PsiFormatUtilBase.SHOW_NAME | + PsiFormatUtilBase.SHOW_PARAMETERS, + PsiFormatUtilBase.SHOW_TYPE); + final int flags = Iconable.ICON_FLAG_VISIBILITY; + final Icon icon = method.getIcon(flags); + if (icon != null) setIcon(icon); + } + else { + LOG.assertTrue(value instanceof GrClosableBlock); + setIcon(GroovyIcons.GROOVY_ICON_16x16); + text = "{...}"; + } + setText(text); + return this; + } + }); + list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.setSelectedIndex(0); + final List highlighters = new ArrayList(); + final TextAttributes attributes = + EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); + list.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(final ListSelectionEvent e) { + final GrParametersOwner selectedMethod = (GrParametersOwner)list.getSelectedValue(); + if (selectedMethod == null) return; + dropHighlighters(highlighters); + updateView(selectedMethod, editor, attributes, highlighters, superMethod); + } + }); + updateView(scopes.get(0), editor, attributes, highlighters, superMethod); + final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(list); + scrollPane.setBorder(null); + panel.add(scrollPane, BorderLayout.CENTER); + + final List> keyboardActions = Collections.singletonList( + Pair.create(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + final GrParametersOwner ToSearchIn = (GrParametersOwner)list.getSelectedValue(); + final JBPopup popup = popupRef.get(); + if (popup != null && popup.isVisible()) { + popup.cancel(); + } + + + final PsiElement toSearchFor; + if (ToSearchIn instanceof GrMethod) { + final GrMethod method = (GrMethod)ToSearchIn; + toSearchFor = superMethod.isEnabled() && superMethod.isSelected() ? method.findDeepestSuperMethod() : method; + } + else { + toSearchFor = superMethod.isEnabled() && superMethod.isSelected() ? ToSearchIn.getParent() : null; + } + IdeFocusManager.findInstance().doWhenFocusSettlesDown(new Runnable() { + public void run() { + callback.fun(ToSearchIn, toSearchFor); + } + }); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0))); + + + return JBPopupFactory.getInstance().createComponentPopupBuilder(panel, list) + .setTitle("Introduce parameter to") + .setMovable(false) + .setResizable(false) + .setRequestFocus(true) + .setKeyboardActions(keyboardActions).addListener(new JBPopupAdapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + dropHighlighters(highlighters); + } + }).createPopup(); + } + + + public static void updateView(GrParametersOwner selectedMethod, + Editor editor, + TextAttributes attributes, + List highlighters, + JCheckBox superMethod) { + final MarkupModel markupModel = editor.getMarkupModel(); + final TextRange textRange = selectedMethod.getTextRange(); + final RangeHighlighter rangeHighlighter = + markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.SELECTION - 1, attributes, + HighlighterTargetArea.EXACT_RANGE); + highlighters.add(rangeHighlighter); + if (selectedMethod instanceof GrMethod) { + superMethod.setText(USE_SUPER_METHOD_OF); + superMethod.setEnabled(((GrMethod)selectedMethod).findDeepestSuperMethod() != null); + } + else { + superMethod.setText(CHANGE_USAGES_OF); + superMethod.setEnabled(findVariableToUse(selectedMethod) != null); + } + } + + @Nullable + public static GrVariable findVariableToUse(@NotNull GrParametersOwner owner) { + final PsiElement parent = owner.getParent(); + if (parent instanceof GrVariable) return (GrVariable)parent; + if (parent instanceof GrAssignmentExpression && + ((GrAssignmentExpression)parent).getRValue() == owner && + ((GrAssignmentExpression)parent).getOperationToken() == GroovyTokenTypes.mASSIGN) { + final GrExpression lValue = ((GrAssignmentExpression)parent).getLValue(); + if (lValue instanceof GrReferenceExpression) { + final PsiElement resolved = ((GrReferenceExpression)lValue).resolve(); + if (resolved instanceof GrVariable) { + return (GrVariable)resolved; + } + } + } + return null; + } + + private static void dropHighlighters(List highlighters) { + for (RangeHighlighter highlighter : highlighters) { + highlighter.dispose(); + } + highlighters.clear(); + } +}