lambda/method refs: provide super class navigation - ctrl-u/line markers (IDEA-120160)

This commit is contained in:
Anna Kozlova
2014-03-04 13:46:27 +01:00
parent 179bdc5ee7
commit a4977be1d5
9 changed files with 144 additions and 25 deletions
@@ -81,6 +81,13 @@ public class JavaLineMarkerProvider implements LineMarkerProvider, DumbAware {
}
}
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(element);
if (interfaceMethod != null) {
final Icon icon = AllIcons.Gutter.ImplementingMethod;
final MarkerType type = MarkerType.OVERRIDING_METHOD;
return new ArrowUpLineMarkerInfo(element, icon, type);
}
if (myDaemonSettings.SHOW_METHOD_SEPARATORS && element.getFirstChild() == null) {
PsiElement element1 = element;
boolean isMember = false;
@@ -53,27 +53,27 @@ public class MarkerType {
public static final MarkerType OVERRIDING_METHOD = new MarkerType(new NullableFunction<PsiElement, String>() {
@Override
public String fun(PsiElement element) {
PsiElement parent = element.getParent();
PsiElement parent = getParentMethod(element);
if (!(parent instanceof PsiMethod)) return null;
PsiMethod method = (PsiMethod)parent;
return calculateOverridingMethodTooltip(method);
return calculateOverridingMethodTooltip(method, method != element.getParent());
}
}, new LineMarkerNavigator(){
@Override
public void browse(MouseEvent e, PsiElement element) {
PsiElement parent = element.getParent();
PsiElement parent = getParentMethod(element);
if (!(parent instanceof PsiMethod)) return;
PsiMethod method = (PsiMethod)parent;
navigateToOverridingMethod(e, method);
navigateToOverridingMethod(e, method, method != element.getParent());
}
});
@Nullable
public static String calculateOverridingMethodTooltip(PsiMethod method) {
PsiMethod[] superMethods = method.findSuperMethods(false);
if (superMethods.length == 0) return null;
public static String calculateOverridingMethodTooltip(PsiMethod method, boolean acceptSelf) {
PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf);
if (superMethods == null) return null;
PsiMethod superMethod = superMethods[0];
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
@@ -90,9 +90,9 @@ public class MarkerType {
return GutterIconTooltipHelper.composeText(superMethods, "", DaemonBundle.message(key));
}
public static void navigateToOverridingMethod(MouseEvent e, PsiMethod method) {
PsiMethod[] superMethods = method.findSuperMethods(false);
if (superMethods.length == 0) return;
public static void navigateToOverridingMethod(MouseEvent e, PsiMethod method, boolean acceptSelf) {
PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf);
if (superMethods == null) return;
boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature(superMethods);
PsiElementListNavigator.openTargets(e, superMethods,
DaemonBundle.message("navigation.title.super.method", method.getName()),
@@ -100,6 +100,22 @@ public class MarkerType {
new MethodCellRenderer(showMethodNames));
}
@Nullable
private static PsiMethod[] composeSuperMethods(PsiMethod method, boolean acceptSelf) {
PsiMethod[] superMethods = method.findSuperMethods(false);
if (acceptSelf) {
superMethods = ArrayUtil.prepend(method, superMethods);
}
if (superMethods.length == 0) return null;
return superMethods;
}
private static PsiElement getParentMethod(PsiElement element) {
final PsiElement parent = element.getParent();
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(element);
return interfaceMethod != null ? interfaceMethod : parent;
}
public static final String SEARCHING_FOR_OVERRIDING_METHODS = "Searching for overriding methods";
public static final MarkerType OVERRIDEN_METHOD = new MarkerType(new NullableFunction<PsiElement, String>() {
@Override
@@ -30,6 +30,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.FindSuperElementsHelper;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -65,20 +66,27 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler {
@Nullable
private PsiElement[] findSuperElements(PsiFile file, int offset) {
PsiNameIdentifierOwner parent = getElement(file, offset);
if (parent == null) return null;
PsiElement element = getElement(file, offset);
if (element == null) return null;
final PsiExpression expression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class, PsiMethodReferenceExpression.class);
if (expression != null) {
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(expression);
if (interfaceMethod != null) {
return ArrayUtil.prepend(interfaceMethod, interfaceMethod.findSuperMethods(false));
}
}
final PsiNameIdentifierOwner parent = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class, PsiClass.class);
if (parent == null) {
return null;
}
return FindSuperElementsHelper.findSuperElements(parent);
}
protected PsiNameIdentifierOwner getElement(PsiFile file, int offset) {
PsiElement element = file.findElementAt(offset);
if (element == null) return null;
PsiNameIdentifierOwner parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class);
if (parent == null)
return null;
return parent;
protected PsiElement getElement(PsiFile file, int offset) {
return file.findElementAt(offset);
}
@Override
@@ -16,9 +16,8 @@
package com.intellij.spi;
import com.intellij.codeInsight.navigation.JavaGotoSuperHandler;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNameIdentifierOwner;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.spi.psi.SPIClassProviderReferenceElement;
@@ -27,11 +26,11 @@ import com.intellij.spi.psi.SPIClassProviderReferenceElement;
*/
public class SPIGotoSuperHandler extends JavaGotoSuperHandler {
@Override
protected PsiNameIdentifierOwner getElement(PsiFile file, int offset) {
protected PsiElement getElement(PsiFile file, int offset) {
final SPIClassProviderReferenceElement
providerElement = PsiTreeUtil.getParentOfType(file.findElementAt(offset), SPIClassProviderReferenceElement.class);
providerElement = PsiTreeUtil.getParentOfType(super.getElement(file, offset), SPIClassProviderReferenceElement.class);
if (providerElement != null) {
return (PsiClass)providerElement.resolve();
return providerElement.resolve();
}
return null;
@@ -60,6 +60,16 @@ public class LambdaUtil {
return getFunctionalInterfaceMethod(PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
}
public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiElement element) {
if (element instanceof PsiLambdaExpression || element instanceof PsiMethodReferenceExpression) {
final PsiType samType = element instanceof PsiLambdaExpression
? ((PsiLambdaExpression)element).getFunctionalInterfaceType()
: ((PsiMethodReferenceExpression)element).getFunctionalInterfaceType();
return getFunctionalInterfaceMethod(samType);
}
return null;
}
@Nullable
public static PsiMethod getFunctionalInterfaceMethod(PsiClassType.ClassResolveResult result) {
final PsiClass psiClass = result.getElement();
@@ -0,0 +1,9 @@
interface I {
void <caret>run();
}
class Foo {
{
I i = () -> {};
}
}
@@ -0,0 +1,9 @@
interface I {
void run();
}
class Foo {
{
I i = ()<caret> -> {};
}
}
@@ -0,0 +1,9 @@
interface I {
void run();
}
class Foo {
{
I i = ()<caret> -> {};
}
}
@@ -0,0 +1,52 @@
package com.intellij.codeInsight.navigation;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.lang.CodeInsightActions;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Document;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase {
@NotNull
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath();
}
protected String getBasePath() {
return "/codeInsight/gotosuper/";
}
public void testLambda() throws Throwable {
doTest();
}
public void testLambdaMarker() throws Exception {
configureByFile(getBasePath() + getTestName(false) + ".java");
int offset = myEditor.getCaretModel().getOffset();
doHighlighting();
Document document = getEditor().getDocument();
List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
for (LineMarkerInfo info : markers) {
if (info.endOffset >= offset && info.startOffset <= offset) {
assertEquals("<html><body>Overrides method in 'I'</body></html>", info.getLineMarkerTooltip());
return;
}
}
fail("Gutter expected");
}
private void doTest() throws Throwable {
configureByFile(getBasePath() + getTestName(false) + ".java");
final CodeInsightActionHandler handler = CodeInsightActions.GOTO_SUPER.forLanguage(JavaLanguage.INSTANCE);
handler.invoke(getProject(), getEditor(), getFile());
checkResultByFile(getBasePath() + getTestName(false) + ".after.java");
}
}