diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodForDeclaration.kt b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodForDeclaration.kt index 1f949f707de..aac358310b3 100644 --- a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodForDeclaration.kt +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodForDeclaration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 JetBrains s.r.o. + * Copyright 2010-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. @@ -32,8 +32,11 @@ import org.jetbrains.jet.lang.psi.JetClassOrObject import com.intellij.psi.impl.light.LightTypeParameterListBuilder import com.intellij.psi.search.SearchScope -public class KotlinLightMethodForDeclaration( - manager: PsiManager, override val delegate: PsiMethod, override val origin: JetDeclaration, containingClass: PsiClass +open public class KotlinLightMethodForDeclaration( + manager: PsiManager, + override val delegate: PsiMethod, + override val origin: JetDeclaration, + containingClass: PsiClass ): LightMethod(manager, delegate, containingClass), KotlinLightMethod { private val paramsList: CachedValue by Delegates.blockingLazy { diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodFromTrait.kt b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodFromTrait.kt new file mode 100644 index 00000000000..b7b6e2ca918 --- /dev/null +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightMethodFromTrait.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-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.jet.asJava + +import com.intellij.psi.PsiManager +import com.intellij.psi.PsiMethod +import org.jetbrains.jet.lang.psi.JetDeclaration +import com.intellij.psi.PsiClass +import org.jetbrains.jet.asJava.KotlinLightMethodFromTrait +import com.intellij.psi.PsiElement + + +public class KotlinLightMethodFromTrait(manager: PsiManager, + override val delegate: PsiMethod, + override val origin: JetDeclaration, + containingClass: PsiClass) : + KotlinLightMethodForDeclaration(manager, delegate, origin, containingClass) { + + override fun copy(): PsiElement { + return KotlinLightMethodFromTrait(getManager()!!, delegate, origin.copy() as JetDeclaration, getContainingClass()!!) + } +} \ No newline at end of file diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinWrappingLightClass.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinWrappingLightClass.java index 11966fd13a4..378f0a0ccbf 100644 --- a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinWrappingLightClass.java +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinWrappingLightClass.java @@ -24,9 +24,12 @@ import com.intellij.psi.impl.light.LightField; import com.intellij.psi.impl.light.LightMethod; import com.intellij.psi.impl.source.ClassInnerStuffCache; import com.intellij.psi.impl.source.PsiExtensibleClass; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import kotlin.Function1; +import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; @@ -125,13 +128,18 @@ public abstract class KotlinWrappingLightClass extends AbstractLightClass implem @NotNull @Override public List getOwnMethods() { - return ContainerUtil.map(getDelegate().getMethods(), new Function() { + return KotlinPackage.map(getDelegate().getMethods(), new Function1() { @Override - public PsiMethod fun(PsiMethod method) { + public PsiMethod invoke(PsiMethod method) { JetDeclaration declaration = ClsWrapperStubPsiFactory.getOriginalDeclaration(method); - return declaration != null - ? new KotlinLightMethodForDeclaration(myManager, method, declaration, KotlinWrappingLightClass.this) - : new LightMethod(myManager, method, KotlinWrappingLightClass.this); + + if (declaration != null) { + return !isMethodFromTrait(declaration) ? + new KotlinLightMethodForDeclaration(myManager, method, declaration, KotlinWrappingLightClass.this) : + new KotlinLightMethodFromTrait(myManager, method, declaration, KotlinWrappingLightClass.this); + } + + return new LightMethod(myManager, method, KotlinWrappingLightClass.this); } }); } @@ -152,4 +160,18 @@ public abstract class KotlinWrappingLightClass extends AbstractLightClass implem JetClassOrObject origin = getOrigin(); return origin == null ? null : origin.getText(); } + + private boolean isMethodFromTrait(@NotNull JetDeclaration originMethodDeclaration) { + if (!(originMethodDeclaration instanceof JetNamedFunction || + originMethodDeclaration instanceof JetPropertyAccessor || + originMethodDeclaration instanceof JetProperty)) { + return false; + } + + JetClassOrObject parentOfMethodOrigin = PsiTreeUtil.getParentOfType(originMethodDeclaration, JetClassOrObject.class); + JetClassOrObject thisClassDeclaration = getOrigin(); + + // Method was generated from declaration in some other trait + return (parentOfMethodOrigin != null && thisClassDeclaration != parentOfMethodOrigin && JetPsiUtil.isTrait(parentOfMethodOrigin)); + } } diff --git a/idea/src/org/jetbrains/jet/plugin/highlighter/JetLineMarkerProvider.java b/idea/src/org/jetbrains/jet/plugin/highlighter/JetLineMarkerProvider.java index f4d865a535c..3cf90ea743a 100644 --- a/idea/src/org/jetbrains/jet/plugin/highlighter/JetLineMarkerProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/highlighter/JetLineMarkerProvider.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.plugin.highlighter; import com.google.common.collect.*; import com.intellij.codeHighlighting.Pass; +import com.intellij.codeInsight.daemon.DaemonBundle; import com.intellij.codeInsight.daemon.GutterIconNavigationHandler; import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.codeInsight.daemon.LineMarkerProvider; @@ -25,13 +26,18 @@ import com.intellij.codeInsight.daemon.impl.GutterIconTooltipHelper; import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator; import com.intellij.codeInsight.daemon.impl.MarkerType; import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator; +import com.intellij.codeInsight.navigation.ListBackgroundUpdaterTask; import com.intellij.icons.AllIcons; import com.intellij.ide.util.DefaultPsiElementCellRenderer; +import com.intellij.ide.util.MethodCellRenderer; import com.intellij.ide.util.PsiClassListCellRenderer; +import com.intellij.ide.util.PsiElementListCellRenderer; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -41,6 +47,7 @@ import com.intellij.psi.search.PsiElementProcessorAdapter; import com.intellij.psi.search.searches.AllOverridingMethodsSearch; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.OverridingMethodsSearch; +import com.intellij.psi.util.PsiUtil; import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; @@ -48,6 +55,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import org.jetbrains.jet.asJava.KotlinLightMethodFromTrait; import org.jetbrains.jet.asJava.LightClassUtil; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; @@ -98,7 +106,7 @@ public class JetLineMarkerProvider implements LineMarkerProvider { @Override public String fun(@Nullable PsiElement element) { PsiMethod psiMethod = getPsiMethod(element); - return psiMethod != null ? MarkerType.getOverriddenMethodTooltip(psiMethod) : null; + return psiMethod != null ? getOverriddenMethodTooltip(psiMethod) : null; } }, @@ -107,7 +115,7 @@ public class JetLineMarkerProvider implements LineMarkerProvider { public void browse(@Nullable MouseEvent e, @Nullable PsiElement element) { PsiMethod psiMethod = getPsiMethod(element); if (psiMethod != null) { - MarkerType.navigateToOverriddenMethod(e, psiMethod); + navigateToOverriddenMethod(e, psiMethod); } } } @@ -485,13 +493,111 @@ public class JetLineMarkerProvider implements LineMarkerProvider { return overridden; } + public static String getOverriddenMethodTooltip(PsiMethod method) { + PsiElementProcessor.CollectElementsWithLimit processor = new PsiElementProcessor.CollectElementsWithLimit(5); + OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter(processor)); + + boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); + + if (processor.isOverflow()){ + return isAbstract ? DaemonBundle.message("method.is.implemented.too.many") : DaemonBundle.message("method.is.overridden.too.many"); + } + + PsiMethod[] javaOverridings = processor.toArray(PsiMethod.EMPTY_ARRAY); + List filter = ContainerUtil.filter(javaOverridings, new Condition() { + @Override + public boolean value(PsiMethod method) { + return !(method instanceof KotlinLightMethodFromTrait); + } + }); + PsiMethod[] overridings = filter.toArray(new PsiMethod[filter.size()]); + + if (overridings.length == 0) return null; + + Comparator comparator = new MethodCellRenderer(false).getComparator(); + Arrays.sort(overridings, comparator); + + String start = isAbstract ? DaemonBundle.message("method.is.implemented.header") : DaemonBundle.message("method.is.overriden.header"); + @NonNls String pattern = "    {1}"; + return GutterIconTooltipHelper.composeText(overridings, start, pattern); + } + + public static void navigateToOverriddenMethod(MouseEvent e, final PsiMethod method) { + if (DumbService.isDumb(method.getProject())) { + DumbService.getInstance(method.getProject()).showDumbModeNotification( + "Navigation to overriding classes is not possible during index update"); + return; + } + + final PsiElementProcessor.CollectElementsWithLimit collectProcessor = + new PsiElementProcessor.CollectElementsWithLimit(2, new THashSet()); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + @Override + public void run() { + OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter(collectProcessor)); + } + }, "Searching for overriding methods", true, method.getProject(), (JComponent)e.getComponent())) { + return; + } + + PsiMethod[] javaOverridings = collectProcessor.toArray(PsiMethod.EMPTY_ARRAY); + List filter = ContainerUtil.filter(javaOverridings, new Condition() { + @Override + public boolean value(PsiMethod method) { + return !(method instanceof KotlinLightMethodFromTrait); + } + }); + PsiMethod[] overridings = filter.toArray(new PsiMethod[filter.size()]); + + if (overridings.length == 0) return; + boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature(overridings); + MethodCellRenderer renderer = new MethodCellRenderer(showMethodNames); + Arrays.sort(overridings, renderer.getComparator()); + OverridingMethodsUpdater methodsUpdater = new OverridingMethodsUpdater(method, renderer); + PsiElementListNavigator.openTargets(e, overridings, methodsUpdater.getCaption(overridings.length), "Overriding methods of " + method.getName(), renderer, methodsUpdater); + } + + private static class OverridingMethodsUpdater extends ListBackgroundUpdaterTask { + private final PsiMethod myMethod; + private final PsiElementListCellRenderer myRenderer; + + public OverridingMethodsUpdater(PsiMethod method, PsiElementListCellRenderer renderer) { + super(method.getProject(), "Searching for overriding methods"); + myMethod = method; + myRenderer = renderer; + } + + @Override + public String getCaption(int size) { + return myMethod.hasModifierProperty(PsiModifier.ABSTRACT) ? + DaemonBundle.message("navigation.title.implementation.method", myMethod.getName(), size) : + DaemonBundle.message("navigation.title.overrider.method", myMethod.getName(), size); + } + + @Override + public void run(@NotNull final ProgressIndicator indicator) { + super.run(indicator); + OverridingMethodsSearch.search(myMethod, true).forEach( + new CommonProcessors.CollectProcessor() { + @Override + public boolean process(PsiMethod psiMethod) { + if (!updateComponent(psiMethod, myRenderer.getComparator())) { + indicator.cancel(); + } + indicator.checkCanceled(); + return super.process(psiMethod); + } + }); + } + } + public static class KotlinSuperNavigationHandler implements GutterIconNavigationHandler { private List testNavigableElements; @TestOnly @NotNull - public Collection getNavigationElements() { - Collection navigationResult = testNavigableElements; + public List getNavigationElements() { + List navigationResult = testNavigableElements; testNavigableElements = null; return navigationResult; }