Add special class for getting methods from traits

This commit is contained in:
Nikolay Krasko
2014-08-25 22:25:50 +04:00
parent 35c67734d3
commit 8ada9b68b0
4 changed files with 179 additions and 12 deletions
@@ -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<PsiParameterList> by Delegates.blockingLazy {
@@ -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()!!)
}
}
@@ -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<PsiMethod> getOwnMethods() {
return ContainerUtil.map(getDelegate().getMethods(), new Function<PsiMethod, PsiMethod>() {
return KotlinPackage.map(getDelegate().getMethods(), new Function1<PsiMethod, PsiMethod>() {
@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));
}
}
@@ -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<PsiMethod> processor = new PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(5);
OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter<PsiMethod>(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<PsiMethod> filter = ContainerUtil.filter(javaOverridings, new Condition<PsiMethod>() {
@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<PsiMethod> 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 = "&nbsp;&nbsp;&nbsp;&nbsp;{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<PsiMethod> collectProcessor =
new PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(2, new THashSet<PsiMethod>());
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter<PsiMethod>(collectProcessor));
}
}, "Searching for overriding methods", true, method.getProject(), (JComponent)e.getComponent())) {
return;
}
PsiMethod[] javaOverridings = collectProcessor.toArray(PsiMethod.EMPTY_ARRAY);
List<PsiMethod> filter = ContainerUtil.filter(javaOverridings, new Condition<PsiMethod>() {
@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<PsiMethod>() {
@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<JetElement> {
private List<NavigatablePsiElement> testNavigableElements;
@TestOnly
@NotNull
public Collection<NavigatablePsiElement> getNavigationElements() {
Collection<NavigatablePsiElement> navigationResult = testNavigableElements;
public List<NavigatablePsiElement> getNavigationElements() {
List<NavigatablePsiElement> navigationResult = testNavigableElements;
testNavigableElements = null;
return navigationResult;
}