diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java index b647e7d1b37f..f8c395ce17fd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java @@ -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; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java index 12b1788dcea7..72e8eca35fa6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java @@ -53,27 +53,27 @@ public class MarkerType { public static final MarkerType OVERRIDING_METHOD = new MarkerType(new NullableFunction() { @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() { @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java index 94ddb7ae863e..55d82a987001 100644 --- a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java @@ -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 diff --git a/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java b/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java index 04c48bc71e40..966a7a3422ea 100644 --- a/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java +++ b/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java @@ -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; diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index aefa768bd2b4..54bd0241e64e 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -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(); diff --git a/java/java-tests/testData/codeInsight/gotosuper/Lambda.after.java b/java/java-tests/testData/codeInsight/gotosuper/Lambda.after.java new file mode 100644 index 000000000000..2fe10c280c86 --- /dev/null +++ b/java/java-tests/testData/codeInsight/gotosuper/Lambda.after.java @@ -0,0 +1,9 @@ +interface I { + void run(); +} + +class Foo { + { + I i = () -> {}; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/gotosuper/Lambda.java b/java/java-tests/testData/codeInsight/gotosuper/Lambda.java new file mode 100644 index 000000000000..26c024317a1d --- /dev/null +++ b/java/java-tests/testData/codeInsight/gotosuper/Lambda.java @@ -0,0 +1,9 @@ +interface I { + void run(); +} + +class Foo { + { + I i = () -> {}; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/gotosuper/LambdaMarker.java b/java/java-tests/testData/codeInsight/gotosuper/LambdaMarker.java new file mode 100644 index 000000000000..26c024317a1d --- /dev/null +++ b/java/java-tests/testData/codeInsight/gotosuper/LambdaMarker.java @@ -0,0 +1,9 @@ +interface I { + void run(); +} + +class Foo { + { + I i = () -> {}; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/navigation/JavaGotoSuperTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/navigation/JavaGotoSuperTest.java new file mode 100644 index 000000000000..313720456ae5 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/navigation/JavaGotoSuperTest.java @@ -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 markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject()); + for (LineMarkerInfo info : markers) { + if (info.endOffset >= offset && info.startOffset <= offset) { + assertEquals("Overrides method in 'I'", 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"); + } +}