blob: 43317bd5ede2cc7e1d63ed2302ec5adb2f6bae53 [file] [log] [blame]
/*
* Copyright 2000-2014 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 icons.JetgroovyIcons;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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<? extends GrParametersOwner> scopes,
final Editor editor,
final JBPopupOwner popupRef,
final PairFunction<GrParametersOwner, PsiElement, Object> 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(JetgroovyIcons.Groovy.Groovy_16x16);
text = "{...}";
}
setText(text);
return this;
}
});
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() {
@Override
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<Pair<ActionListener, KeyStroke>> keyboardActions = Collections.singletonList(
Pair.<ActionListener, KeyStroke>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() {
@Override
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<RangeHighlighter> 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).getOperationTokenType() == 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<RangeHighlighter> highlighters) {
for (RangeHighlighter highlighter : highlighters) {
highlighter.dispose();
}
highlighters.clear();
}
}