2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.jetbrains.plugins.groovy.refactoring.ui;
18 import com.intellij.openapi.application.ModalityState;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.editor.colors.EditorColors;
22 import com.intellij.openapi.editor.colors.EditorColorsManager;
23 import com.intellij.openapi.editor.markup.*;
24 import com.intellij.openapi.ui.popup.JBPopup;
25 import com.intellij.openapi.ui.popup.JBPopupAdapter;
26 import com.intellij.openapi.ui.popup.JBPopupFactory;
27 import com.intellij.openapi.ui.popup.LightweightWindowEvent;
28 import com.intellij.openapi.util.Iconable;
29 import com.intellij.openapi.util.Pair;
30 import com.intellij.openapi.util.TextRange;
31 import com.intellij.openapi.wm.IdeFocusManager;
32 import com.intellij.psi.PsiElement;
33 import com.intellij.psi.PsiMethod;
34 import com.intellij.psi.PsiSubstitutor;
35 import com.intellij.psi.util.PsiFormatUtil;
36 import com.intellij.psi.util.PsiFormatUtilBase;
37 import com.intellij.ui.ScrollPaneFactory;
38 import com.intellij.ui.components.JBList;
39 import com.intellij.util.PairFunction;
40 import icons.JetgroovyIcons;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
50 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
51 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
54 import javax.swing.event.ListSelectionEvent;
55 import javax.swing.event.ListSelectionListener;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.awt.event.KeyEvent;
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.List;
65 * @author Max Medvedev
67 public class MethodOrClosureScopeChooser {
68 private static final Logger LOG = Logger.getInstance(MethodOrClosureScopeChooser.class);
70 @NonNls private static final String USE_SUPER_METHOD_OF = "Change base method";
71 @NonNls private static final String CHANGE_USAGES_OF = "Change usages";
73 public interface JBPopupOwner {
78 * @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
79 * variable if the scope is a closure)
81 public static JBPopup create(List<? extends GrParametersOwner> scopes,
83 final JBPopupOwner popupRef,
84 final PairFunction<GrParametersOwner, PsiElement, Object> callback) {
85 final JPanel panel = new JPanel(new BorderLayout());
86 final JCheckBox superMethod = new JCheckBox(USE_SUPER_METHOD_OF, true);
87 superMethod.setMnemonic('U');
88 panel.add(superMethod, BorderLayout.SOUTH);
89 final JBList list = new JBList(scopes.toArray());
90 list.setVisibleRowCount(5);
91 list.setCellRenderer(new DefaultListCellRenderer() {
93 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
94 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
97 if (value instanceof PsiMethod) {
98 final PsiMethod method = (PsiMethod)value;
99 text = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY,
100 PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
101 PsiFormatUtilBase.SHOW_NAME |
102 PsiFormatUtilBase.SHOW_PARAMETERS,
103 PsiFormatUtilBase.SHOW_TYPE);
104 final int flags = Iconable.ICON_FLAG_VISIBILITY;
105 final Icon icon = method.getIcon(flags);
106 if (icon != null) setIcon(icon);
109 LOG.assertTrue(value instanceof GrClosableBlock);
110 setIcon(JetgroovyIcons.Groovy.Groovy_16x16);
117 list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
118 list.setSelectedIndex(0);
119 final List<RangeHighlighter> highlighters = new ArrayList<>();
120 final TextAttributes attributes =
121 EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
122 list.addListSelectionListener(new ListSelectionListener() {
124 public void valueChanged(final ListSelectionEvent e) {
125 final GrParametersOwner selectedMethod = (GrParametersOwner)list.getSelectedValue();
126 if (selectedMethod == null) return;
127 dropHighlighters(highlighters);
128 updateView(selectedMethod, editor, attributes, highlighters, superMethod);
131 updateView(scopes.get(0), editor, attributes, highlighters, superMethod);
132 final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(list);
133 scrollPane.setBorder(null);
134 panel.add(scrollPane, BorderLayout.CENTER);
136 final List<Pair<ActionListener, KeyStroke>> keyboardActions = Collections.singletonList(
137 Pair.<ActionListener, KeyStroke>create(new ActionListener() {
139 public void actionPerformed(ActionEvent e) {
140 final GrParametersOwner ToSearchIn = (GrParametersOwner)list.getSelectedValue();
141 final JBPopup popup = popupRef.get();
142 if (popup != null && popup.isVisible()) {
147 final PsiElement toSearchFor;
148 if (ToSearchIn instanceof GrMethod) {
149 final GrMethod method = (GrMethod)ToSearchIn;
150 toSearchFor = superMethod.isEnabled() && superMethod.isSelected() ? method.findDeepestSuperMethod() : method;
153 toSearchFor = superMethod.isEnabled() && superMethod.isSelected() ? ToSearchIn.getParent() : null;
155 IdeFocusManager.findInstance().doWhenFocusSettlesDown(() -> callback.fun(ToSearchIn, toSearchFor), ModalityState.current());
157 }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)));
160 return JBPopupFactory.getInstance().createComponentPopupBuilder(panel, list)
161 .setTitle("Introduce parameter to")
164 .setRequestFocus(true)
165 .setKeyboardActions(keyboardActions).addListener(new JBPopupAdapter() {
167 public void onClosed(LightweightWindowEvent event) {
168 dropHighlighters(highlighters);
174 public static void updateView(GrParametersOwner selectedMethod,
176 TextAttributes attributes,
177 List<RangeHighlighter> highlighters,
178 JCheckBox superMethod) {
179 final MarkupModel markupModel = editor.getMarkupModel();
180 final TextRange textRange = selectedMethod.getTextRange();
181 final RangeHighlighter rangeHighlighter =
182 markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.SELECTION - 1, attributes,
183 HighlighterTargetArea.EXACT_RANGE);
184 highlighters.add(rangeHighlighter);
185 if (selectedMethod instanceof GrMethod) {
186 superMethod.setText(USE_SUPER_METHOD_OF);
187 superMethod.setEnabled(((GrMethod)selectedMethod).findDeepestSuperMethod() != null);
190 superMethod.setText(CHANGE_USAGES_OF);
191 superMethod.setEnabled(findVariableToUse(selectedMethod) != null);
196 public static GrVariable findVariableToUse(@NotNull GrParametersOwner owner) {
197 final PsiElement parent = owner.getParent();
198 if (parent instanceof GrVariable) return (GrVariable)parent;
199 if (parent instanceof GrAssignmentExpression &&
200 ((GrAssignmentExpression)parent).getRValue() == owner &&
201 ((GrAssignmentExpression)parent).getOperationTokenType() == GroovyTokenTypes.mASSIGN) {
202 final GrExpression lValue = ((GrAssignmentExpression)parent).getLValue();
203 if (lValue instanceof GrReferenceExpression) {
204 final PsiElement resolved = ((GrReferenceExpression)lValue).resolve();
205 if (resolved instanceof GrVariable) {
206 return (GrVariable)resolved;
213 private static void dropHighlighters(List<RangeHighlighter> highlighters) {
214 for (RangeHighlighter highlighter : highlighters) {
215 highlighter.dispose();
217 highlighters.clear();