KT-3498 Show overrided/implemented line marker

#KT-3498 Fixed
This commit is contained in:
Nikolay Krasko
2013-04-24 17:05:25 +04:00
parent fe124a9828
commit 0c749005e1
3 changed files with 135 additions and 11 deletions
@@ -17,6 +17,8 @@
package org.jetbrains.jet.plugin.highlighter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
@@ -26,27 +28,32 @@ import com.intellij.codeInsight.daemon.impl.MarkerType;
import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.searches.AllOverridingMethodsSearch;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.Function;
import com.intellij.util.NullableFunction;
import com.intellij.util.Processor;
import com.intellij.util.PsiNavigateUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.Modality;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lexer.JetTokens;
@@ -58,6 +65,7 @@ import javax.swing.*;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JetLineMarkerProvider implements LineMarkerProvider {
@@ -86,6 +94,26 @@ public class JetLineMarkerProvider implements LineMarkerProvider {
}
);
private static final MarkerType OVERRIDDEN_FUNCTION = new MarkerType(
new NullableFunction<PsiElement, String>() {
@Override
public String fun(@Nullable PsiElement element) {
PsiMethod psiMethod = getPsiMethod(element);
return psiMethod != null ? MarkerType.getOverriddenMethodTooltip(psiMethod) : null;
}
},
new LineMarkerNavigator() {
@Override
public void browse(@Nullable MouseEvent e, @Nullable PsiElement element) {
PsiMethod psiMethod = getPsiMethod(element);
if (psiMethod != null) {
MarkerType.navigateToOverriddenMethod(e, psiMethod);
}
}
}
);
@Nullable
private static PsiClass getPsiClass(@Nullable PsiElement element) {
if (element == null) return null;
@@ -101,13 +129,20 @@ public class JetLineMarkerProvider implements LineMarkerProvider {
return LightClassUtil.getPsiClass((JetClass) element);
}
@Nullable
private static PsiMethod getPsiMethod(@Nullable PsiElement element) {
if (element == null) return null;
if (element instanceof PsiMethod) return (PsiMethod) element;
if (element.getParent() instanceof JetNamedFunction) return LightClassUtil.getLightClassMethod((JetNamedFunction) element.getParent());
return null;
}
@Override
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
JetFile file = (JetFile)element.getContainingFile();
if (file == null) return null;
if (!(element instanceof JetNamedFunction || element instanceof JetProperty)) return null;
if (!(element instanceof JetNamedFunction || element instanceof JetProperty)) return null;
BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(file).getBindingContext();
@@ -236,15 +271,23 @@ public class JetLineMarkerProvider implements LineMarkerProvider {
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
if (elements.isEmpty() || DumbService.getInstance(elements.get(0).getProject()).isDumb()) {
return;
return;
}
Set<JetNamedFunction> functions = Sets.newHashSet();
for (PsiElement element : elements) {
if (element instanceof JetClass) {
collectInheritingClasses((JetClass)element, result);
collectInheritingClasses((JetClass) element, result);
}
if (element instanceof JetNamedFunction) {
functions.add((JetNamedFunction) element);
}
}
collectOverridingAccessors(functions, result);
}
private static void collectInheritingClasses(JetClass element, Collection<LineMarkerInfo> result) {
@@ -267,4 +310,85 @@ public class JetLineMarkerProvider implements LineMarkerProvider {
SUBCLASSED_CLASS.getTooltip(), SUBCLASSED_CLASS.getNavigationHandler()));
}
}
private static boolean isOverridableHeuristic(JetNamedDeclaration declaration) {
PsiElement parent = declaration.getParent();
if (parent instanceof JetFile) return false;
if (parent instanceof JetClassBody && parent.getParent() instanceof JetClass) {
// Not all open or abstract declarations are actually overridable, but this is heuristic
return declaration.hasModifier(JetTokens.ABSTRACT_KEYWORD) ||
declaration.hasModifier(JetTokens.OPEN_KEYWORD) ||
((JetClass) parent.getParent()).isTrait();
}
return false;
}
private static boolean isImplemented(JetNamedDeclaration declaration) {
if (declaration.hasModifier(JetTokens.ABSTRACT_KEYWORD)) return true;
PsiElement parent = declaration.getParent();
if (parent instanceof JetClass) {
return ((JetClass) parent).isTrait();
}
return false;
}
private static void collectOverridingAccessors(Collection<JetNamedFunction> functions, Collection<LineMarkerInfo> result) {
final Map<PsiMethod, JetNamedFunction> mappingToJava = Maps.newHashMap();
for (JetNamedFunction function : functions) {
if (isOverridableHeuristic(function)) {
PsiMethod method = LightClassUtil.getLightClassMethod(function);
if (method != null) {
mappingToJava.put(method, function);
}
}
}
Set<PsiClass> classes = new THashSet<PsiClass>();
for (PsiMethod method : mappingToJava.keySet()) {
ProgressManager.checkCanceled();
PsiClass parentClass = method.getContainingClass();
if (parentClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(parentClass.getQualifiedName())) {
classes.add(parentClass);
}
}
final Set<JetNamedFunction> overridden = Sets.newHashSet();
for (PsiClass aClass : classes) {
AllOverridingMethodsSearch.search(aClass).forEach(new Processor<Pair<PsiMethod, PsiMethod>>() {
@Override
public boolean process(Pair<PsiMethod, PsiMethod> pair) {
ProgressManager.checkCanceled();
PsiMethod superMethod = pair.getFirst();
JetNamedFunction function = mappingToJava.get(superMethod);
if (function != null) {
mappingToJava.remove(superMethod);
overridden.add(function);
}
return !mappingToJava.isEmpty();
}
});
}
for (JetNamedFunction function : overridden) {
ProgressManager.checkCanceled();
PsiElement anchor = function.getNameIdentifier();
if (anchor == null) anchor = function;
LineMarkerInfo info = new LineMarkerInfo<PsiElement>(
anchor, anchor.getTextOffset(),
isImplemented(function) ? IMPLEMENTED_MARK : OVERRIDDEN_MARK,
Pass.UPDATE_OVERRIDEN_MARKERS,
OVERRIDDEN_FUNCTION.getTooltip(), OVERRIDDEN_FUNCTION.getNavigationHandler(),
GutterIconRenderer.Alignment.RIGHT);
result.add(info);
}
}
}
@@ -1,5 +1,5 @@
open class <lineMarker></lineMarker>A {
open fun a(){
open fun <lineMarker></lineMarker>a(){
}
}
@@ -1,5 +1,5 @@
trait <lineMarker></lineMarker>A {
fun a(){
fun <lineMarker></lineMarker>a(){
}
}