KT-3248 Highlight Java members with invalid annotations in IDE

#KT-3248 fixed
This commit is contained in:
Evgeny Gerashchenko
2013-01-17 19:23:13 +04:00
parent d2cddde04e
commit 6f2625c7eb
@@ -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"));
}
}
}