From c957650096dc0152a2e29c2d2ce41472612d4425 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 1 Apr 2020 14:17:27 +0700 Subject: [PATCH] Type annotations in quick-doc (IDEA-230655) GitOrigin-RevId: e108b4f4344981ee58ab8fe460cba3f0c5f4e834 --- .../javadoc/AnnotationDocGenerator.java | 67 +++++++++++-------- .../javadoc/JavaDocInfoGenerator.java | 29 ++++++-- .../codeInsight/javadocIG/typeAnnotation.html | 1 + .../codeInsight/javadocIG/typeAnnotation.java | 13 ++++ .../javadocIG/typeAnnotationArray.html | 1 + .../javadocIG/typeAnnotationArray.java | 13 ++++ .../javadocIG/typeAnnotationClass.html | 3 + .../javadocIG/typeAnnotationClass.java | 12 ++++ .../javadoc/JavaDocInfoGeneratorTest.java | 3 + 9 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.java diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/AnnotationDocGenerator.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/AnnotationDocGenerator.java index f8f4bc7577b0..ba3f04bdd07c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/AnnotationDocGenerator.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/AnnotationDocGenerator.java @@ -1,18 +1,19 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight.javadoc; +import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; import com.intellij.xml.util.XmlStringUtil; +import one.util.streamex.StreamEx; import org.intellij.lang.annotations.Flow; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -21,14 +22,16 @@ public class AnnotationDocGenerator { private static final Logger LOG = Logger.getInstance(AnnotationDocGenerator.class); @NotNull private final PsiAnnotation myAnnotation; @NotNull private final PsiJavaCodeReferenceElement myNameReference; - @NotNull private final PsiModifierListOwner myOwner; + @NotNull private final PsiElement myContext; @Nullable private final PsiClass myTargetClass; private final boolean myResolveNotPossible; - private AnnotationDocGenerator(@NotNull PsiAnnotation annotation, @NotNull PsiJavaCodeReferenceElement nameReference, @NotNull PsiModifierListOwner owner) { - myAnnotation = annotation; - myNameReference = nameReference; - myOwner = owner; + private AnnotationDocGenerator(@NotNull PsiAnnotation annotation, + @NotNull PsiJavaCodeReferenceElement nameReference, + @NotNull PsiElement context) { + myAnnotation = annotation; + myNameReference = nameReference; + myContext = context; boolean indexNotReady = false; PsiElement target = null; @@ -65,16 +68,12 @@ public class AnnotationDocGenerator { return AnnotationUtil.isInferredAnnotation(myAnnotation); } - public boolean isInferredFromSource() { - return isInferred() && !(PsiUtil.preferCompiledElement(myOwner) instanceof PsiCompiledElement); - } - void generateAnnotation(StringBuilder buffer, AnnotationFormat format) { String qualifiedName = myAnnotation.getQualifiedName(); - PsiClassType type = - myTargetClass != null && qualifiedName != null && JavaDocUtil.findReferenceTarget(myOwner.getManager(), qualifiedName, myOwner) != null - ? JavaPsiFacade.getElementFactory(myOwner.getProject()).createType(myTargetClass, PsiSubstitutor.EMPTY) - : null; + PsiClassType type = myTargetClass != null && qualifiedName != null && + JavaDocUtil.findReferenceTarget(myContext.getManager(), qualifiedName, myContext) != null + ? JavaPsiFacade.getElementFactory(myContext.getProject()).createType(myTargetClass, PsiSubstitutor.EMPTY) + : null; boolean red = type == null && !myResolveNotPossible && !isInferred() && !isExternal(); @@ -169,25 +168,37 @@ public class AnnotationDocGenerator { buffer.append(XmlStringUtil.escapeString(memberValue.getText())); } + + public static List getAnnotationsToShow(@NotNull PsiAnnotationOwner owner, @NotNull PsiElement context) { + if (owner instanceof PsiModifierList) { + return getAnnotationsToShow(((PsiModifierListOwner)((PsiModifierList)owner).getParent())); + } + Set shownAnnotations = new HashSet<>(); + return ContainerUtil.mapNotNull(owner.getAnnotations(), + annotation -> forAnnotation(context, shownAnnotations, annotation)); + } public static List getAnnotationsToShow(@NotNull PsiModifierListOwner owner) { - List infos = new ArrayList<>(); - Set shownAnnotations = new HashSet<>(); + return StreamEx.of(AnnotationUtil.getAllAnnotations(owner, false, null)) + .filter(owner instanceof PsiClass || owner instanceof PsiJavaModule ? anno -> true + : anno -> !AnnotationTargetUtil.isTypeAnnotation(anno)) + .map(annotation -> forAnnotation(owner, shownAnnotations, annotation)) + .nonNull() + .toList(); + } - for (PsiAnnotation annotation : AnnotationUtil.getAllAnnotations(owner, false, null)) { - PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); - if (nameReferenceElement == null) continue; + private static @Nullable AnnotationDocGenerator forAnnotation(@NotNull PsiElement context, + @NotNull Set shownAnnotations, + @NotNull PsiAnnotation annotation) { + PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); + if (nameReferenceElement == null) return null; - AnnotationDocGenerator anno = new AnnotationDocGenerator(annotation, nameReferenceElement, owner); + AnnotationDocGenerator anno = new AnnotationDocGenerator(annotation, nameReferenceElement, context); + if (anno.isNonDocumentedAnnotation()) return null; - if (anno.isNonDocumentedAnnotation()) continue; - - if (!(shownAnnotations.add(annotation.getQualifiedName()) || JavaDocInfoGenerator.isRepeatableAnnotationType(annotation))) continue; - - infos.add(anno); - } - return infos; + if (!(shownAnnotations.add(annotation.getQualifiedName()) || JavaDocInfoGenerator.isRepeatableAnnotationType(annotation))) return null; + return anno; } } diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 40328a190ea9..bada1dcb9f87 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -881,6 +881,17 @@ public class JavaDocInfoGenerator { return modifiers.length(); } + private static void generateTypeAnnotations(StringBuilder buffer, PsiAnnotationOwner owner, PsiElement context, boolean leadingSpace) { + List generators = AnnotationDocGenerator.getAnnotationsToShow(owner, context); + if (leadingSpace && !generators.isEmpty()) { + buffer.append(NBSP); + } + for (AnnotationDocGenerator anno : generators) { + anno.generateAnnotation(buffer, AnnotationFormat.JavaDocShort); + buffer.append(NBSP); + } + } + private static void generateAnnotations(StringBuilder buffer, PsiModifierListOwner owner, SignaturePlace place, @@ -1820,14 +1831,10 @@ public class JavaDocInfoGenerator { * @return Length of the generated label. */ public static int generateType(StringBuilder buffer, PsiType type, PsiElement context, boolean generateLink, boolean useShortNames) { - if (type instanceof PsiPrimitiveType) { - String text = StringUtil.escapeXmlEntities(type.getCanonicalText()); - buffer.append(text); - return text.length(); - } - if (type instanceof PsiArrayType) { int rest = generateType(buffer, ((PsiArrayType)type).getComponentType(), context, generateLink, useShortNames); + + generateTypeAnnotations(buffer, type, context, true); if (type instanceof PsiEllipsisType) { buffer.append("..."); return rest + 3; @@ -1838,6 +1845,14 @@ public class JavaDocInfoGenerator { } } + generateTypeAnnotations(buffer, type, context, false); + + if (type instanceof PsiPrimitiveType) { + String text = StringUtil.escapeXmlEntities(type.getCanonicalText()); + buffer.append(text); + return text.length(); + } + if (type instanceof PsiCapturedWildcardType) { type = ((PsiCapturedWildcardType)type).getWildcard(); } @@ -1982,6 +1997,8 @@ public class JavaDocInfoGenerator { for (int i = 0; i < parameters.length; i++) { PsiTypeParameter p = parameters[i]; + + generateTypeAnnotations(buffer, p, p, false); buffer.append(p.getName()); diff --git a/java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.html b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.html new file mode 100644 index 000000000000..4208ae2af04e --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.html @@ -0,0 +1 @@ +
Test
public <@Foo(1) T extends @Foo(2) CharSequence & @Foo(3) Cloneable> @Foo(4) List<@Foo(5) ? super @Foo(6) T> bar()
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.java b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.java new file mode 100644 index 000000000000..1836ce4f80ce --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotation.java @@ -0,0 +1,13 @@ +import java.lang.annotation.*; +import java.util.List; + +class Test { + public <@Foo(1) T extends @Foo(2) CharSequence & @Foo(3) Cloneable> + @Foo(4) List<@Foo(5) ? super @Foo(6) T> bar() {return null;} + + @Documented + @Target(ElementType.TYPE_USE) + @interface Foo { + int value(); + } +} diff --git a/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.html b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.html new file mode 100644 index 000000000000..ec4ef5b18ede --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.html @@ -0,0 +1 @@ +
Test
public @Foo(1) String @Foo(2) [] foo()
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.java b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.java new file mode 100644 index 000000000000..99de30375272 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationArray.java @@ -0,0 +1,13 @@ +import java.lang.annotation.*; + +class Test { + public @Foo(1) String @Foo(2) @Bar [] foo(){} +} +@Documented +@Target(ElementType.TYPE_USE) +@interface Foo { + int value(); +} + +@Target(ElementType.TYPE_USE) +@interface Bar {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.html b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.html new file mode 100644 index 000000000000..ba33671386d8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.html @@ -0,0 +1,3 @@ +
@Foo(1) 
+class Test
+extends @Foo(2) List<@Foo(3) String>
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.java b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.java new file mode 100644 index 000000000000..6a71b6d8bd81 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/typeAnnotationClass.java @@ -0,0 +1,12 @@ +import java.lang.annotation.*; +import java.util.List; + +@Foo(1) +class Test extends @Foo(2) List<@Foo(3) String> { +} + +@Documented +@Target(ElementType.TYPE_USE) +@interface Foo { + int value(); +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java index d1d913164c63..afd3320a1000 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaDocInfoGeneratorTest.java @@ -104,6 +104,9 @@ public class JavaDocInfoGeneratorTest extends JavaCodeInsightTestCase { public void testPackageHtml() { doTestPackageInfo(); } public void testSyntheticEnumValues() { doTestAtCaret(); } public void testVariableDoc() { doTestAtCaret(); } + public void testTypeAnnotation() { useJava8(); doTestAtCaret(); } + public void testTypeAnnotationArray() { useJava8(); doTestAtCaret(); } + public void testTypeAnnotationClass() { useJava8(); doTestClass(); } public void testAnonymousAndSuperJavadoc() { PsiClass psiClass = PsiTreeUtil.findChildOfType(getTestClass(), PsiAnonymousClass.class);