KT-3498 Show overrided/implemented line marker
#KT-3498 Fixed
This commit is contained in:
@@ -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(){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user