diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/ktSignature/KotlinSignatureInJavaMarkerProvider.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/ktSignature/KotlinSignatureInJavaMarkerProvider.java index d558bc60498..350c3160923 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/ktSignature/KotlinSignatureInJavaMarkerProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/ktSignature/KotlinSignatureInJavaMarkerProvider.java @@ -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 TOOLTIP_PROVIDER = new Function() { - @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 NAVIGATION_HANDLER = new GutterIconNavigationHandler() { @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 elements, @NotNull Collection 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 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 { - public MyLineMarkerInfo(PsiModifierListOwner element) { - super(element, element.getTextOffset(), JetIcons.SMALL_LOGO, Pass.UPDATE_ALL, TOOLTIP_PROVIDER, NAVIGATION_HANDLER); + public MyLineMarkerInfo(PsiModifierListOwner element, @Nullable List 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 { + private final @Nullable List errors; + + private TooltipProvider(@Nullable List 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")); + } + } }