KT-3248 Highlight Java members with invalid annotations in IDE
#KT-3248 fixed
This commit is contained in:
+102
-22
@@ -20,6 +20,7 @@ import com.intellij.codeHighlighting.Pass;
|
||||
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo;
|
||||
import com.intellij.codeInsight.daemon.LineMarkerProvider;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
@@ -27,13 +28,18 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiAnnotation;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.di.InjectorForJavaSemanticServices;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
import org.jetbrains.jet.plugin.JetIcons;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
@@ -45,20 +51,6 @@ import static org.jetbrains.jet.plugin.codeInsight.ktSignature.KotlinSignatureUt
|
||||
public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
|
||||
private static final String SHOW_MARKERS_PROPERTY = "kotlin.signature.markers.enabled";
|
||||
|
||||
private static final Function<PsiElement, String> TOOLTIP_PROVIDER = new Function<PsiElement, String>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String fun(PsiElement element) {
|
||||
PsiAnnotation annotation = findKotlinSignatureAnnotation(element);
|
||||
|
||||
if (annotation == null) return null;
|
||||
|
||||
String signature = getKotlinSignature(annotation);
|
||||
return "Alternative Kotlin signature is available for this method:\n"
|
||||
+ StringUtil.escapeXml(signature);
|
||||
}
|
||||
};
|
||||
|
||||
private static final GutterIconNavigationHandler<PsiModifierListOwner> NAVIGATION_HANDLER = new GutterIconNavigationHandler<PsiModifierListOwner>() {
|
||||
@Override
|
||||
public void navigate(MouseEvent e, PsiModifierListOwner element) {
|
||||
@@ -72,14 +64,70 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
|
||||
@Override
|
||||
@Nullable
|
||||
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
|
||||
if (isMarkersEnabled(element.getProject()) && findKotlinSignatureAnnotation(element) != null) {
|
||||
return new MyLineMarkerInfo((PsiModifierListOwner) element);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
|
||||
if (elements.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Project project = elements.get(0).getProject();
|
||||
if (!isMarkersEnabled(project)) {
|
||||
return;
|
||||
}
|
||||
|
||||
InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices(project);
|
||||
JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver();
|
||||
BindingContext bindingContext = injector.getBindingTrace().getBindingContext();
|
||||
|
||||
for (PsiElement element : elements) {
|
||||
if (!(element instanceof PsiMember)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiMember member = (PsiMember) element;
|
||||
if (member.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiClass containingClass = member.getContainingClass();
|
||||
if (containingClass == null) { // e.g., type parameter
|
||||
continue;
|
||||
}
|
||||
|
||||
String qualifiedName = containingClass.getQualifiedName();
|
||||
assert qualifiedName != null;
|
||||
|
||||
FqName classFqName = new FqName(qualifiedName);
|
||||
ClassDescriptor klass = javaDescriptorResolver.resolveClass(classFqName);
|
||||
assert klass != null : " Couldn't find class " + classFqName;
|
||||
|
||||
String memberNameAsString = member.getName();
|
||||
assert memberNameAsString != null;
|
||||
|
||||
Name name = Name.identifier(memberNameAsString);
|
||||
if (element instanceof PsiMethod) {
|
||||
klass.getDefaultType().getMemberScope().getFunctions(name);
|
||||
}
|
||||
else if (element instanceof PsiField) {
|
||||
klass.getDefaultType().getMemberScope().getProperties(name);
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiModifierListOwner annotationOwner = getAnnotationOwner(element);
|
||||
DeclarationDescriptor memberDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotationOwner);
|
||||
assert memberDescriptor != null : "Couldn't find descriptor for " + annotationOwner;
|
||||
List<String> errors = bindingContext.get(BindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, memberDescriptor);
|
||||
boolean hasSignatureAnnotation = findKotlinSignatureAnnotation(element) != null;
|
||||
|
||||
if (errors != null || hasSignatureAnnotation) {
|
||||
result.add(new MyLineMarkerInfo((PsiModifierListOwner) element, errors, hasSignatureAnnotation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isMarkersEnabled(@NotNull Project project) {
|
||||
@@ -92,8 +140,9 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
|
||||
}
|
||||
|
||||
private static class MyLineMarkerInfo extends LineMarkerInfo<PsiModifierListOwner> {
|
||||
public MyLineMarkerInfo(PsiModifierListOwner element) {
|
||||
super(element, element.getTextOffset(), JetIcons.SMALL_LOGO, Pass.UPDATE_ALL, TOOLTIP_PROVIDER, NAVIGATION_HANDLER);
|
||||
public MyLineMarkerInfo(PsiModifierListOwner element, @Nullable List<String> errors, boolean hasAnnotation) {
|
||||
super(element, element.getTextOffset(), errors != null ? AllIcons.Ide.Error : JetIcons.SMALL_LOGO, Pass.UPDATE_ALL,
|
||||
new TooltipProvider(errors), hasAnnotation ? NAVIGATION_HANDLER : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -111,4 +160,35 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class TooltipProvider implements Function<PsiElement, String> {
|
||||
private final @Nullable List<String> errors;
|
||||
|
||||
private TooltipProvider(@Nullable List<String> errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String fun(PsiElement element) {
|
||||
PsiAnnotation annotation = findKotlinSignatureAnnotation(element);
|
||||
|
||||
if (annotation == null) return errorsString();
|
||||
|
||||
String signature = getKotlinSignature(annotation);
|
||||
String text = "Alternative Kotlin signature is available for this method:\n"
|
||||
+ StringUtil.escapeXml(signature);
|
||||
if (errors == null) {
|
||||
return text;
|
||||
}
|
||||
return text + "\nIt has the following " + StringUtil.pluralize("error", errors.size()) + ":\n"
|
||||
+ errorsString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String errorsString() {
|
||||
assert errors != null;
|
||||
return StringUtil.escapeXml(StringUtil.join(errors, "\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user