Type annotations in quick-doc (IDEA-230655)

GitOrigin-RevId: e108b4f4344981ee58ab8fe460cba3f0c5f4e834
This commit is contained in:
Tagir Valeev
2020-04-01 07:31:11 +00:00
committed by intellij-monorepo-bot
parent b0d9f51d6a
commit c957650096
9 changed files with 108 additions and 34 deletions
@@ -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<AnnotationDocGenerator> getAnnotationsToShow(@NotNull PsiAnnotationOwner owner, @NotNull PsiElement context) {
if (owner instanceof PsiModifierList) {
return getAnnotationsToShow(((PsiModifierListOwner)((PsiModifierList)owner).getParent()));
}
Set<String> shownAnnotations = new HashSet<>();
return ContainerUtil.mapNotNull(owner.getAnnotations(),
annotation -> forAnnotation(context, shownAnnotations, annotation));
}
public static List<AnnotationDocGenerator> getAnnotationsToShow(@NotNull PsiModifierListOwner owner) {
List<AnnotationDocGenerator> infos = new ArrayList<>();
Set<String> 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<String> 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;
}
}
@@ -881,6 +881,17 @@ public class JavaDocInfoGenerator {
return modifiers.length();
}
private static void generateTypeAnnotations(StringBuilder buffer, PsiAnnotationOwner owner, PsiElement context, boolean leadingSpace) {
List<AnnotationDocGenerator> 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());
@@ -0,0 +1 @@
<html><head><base href="placeholder"></head><body><div class='definition-only'><pre><a href="psi_element://Test"><code>Test</code></a><br>public&nbsp;&lt;@<a href="psi_element://Test.Foo"><code>Foo</code></a>(1)&nbsp;T extends @<a href="psi_element://Test.Foo"><code>Foo</code></a>(2)&nbsp;<a href="psi_element://java.lang.CharSequence"><code>CharSequence</code></a> & @<a href="psi_element://Test.Foo"><code>Foo</code></a>(3)&nbsp;<a href="psi_element://java.lang.Cloneable"><code>Cloneable</code></a>&gt;&nbsp;@<a href="psi_element://Test.Foo"><code>Foo</code></a>(4)&nbsp;<a href="psi_element://java.util.List"><code>List</code></a>&lt;@<a href="psi_element://Test.Foo"><code>Foo</code></a>(5)&nbsp;? super @<a href="psi_element://Test.Foo"><code>Foo</code></a>(6)&nbsp;T&gt;&nbsp;<b>bar</b>()</pre></div>
@@ -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> <caret>bar() {return null;}
@Documented
@Target(ElementType.TYPE_USE)
@interface Foo {
int value();
}
}
@@ -0,0 +1 @@
<html><head><base href="placeholder"></head><body><div class='definition-only'><pre><a href="psi_element://Test"><code>Test</code></a><br>public&nbsp;@<a href="psi_element://Foo"><code>Foo</code></a>(1)&nbsp;<a href="psi_element://java.lang.String"><code>String</code></a>&nbsp;@<a href="psi_element://Foo"><code>Foo</code></a>(2)&nbsp;[]&nbsp;<b>foo</b>()</pre></div>
@@ -0,0 +1,13 @@
import java.lang.annotation.*;
class Test {
public @Foo(1) String @Foo(2) @Bar [] <caret>foo(){}
}
@Documented
@Target(ElementType.TYPE_USE)
@interface Foo {
int value();
}
@Target(ElementType.TYPE_USE)
@interface Bar {}
@@ -0,0 +1,3 @@
<html><head><base href="placeholder"></head><body><div class='definition'><pre>@<a href="psi_element://Foo"><code>Foo</code></a>(1)&nbsp;
class <b>Test</b>
extends @<a href="psi_element://Foo"><code>Foo</code></a>(2)&nbsp;<a href="psi_element://java.util.List"><code>List</code></a>&lt;@<a href="psi_element://Foo"><code>Foo</code></a>(3)&nbsp;<a href="psi_element://java.lang.String"><code>String</code></a>&gt;</pre></div><table class='sections'></table>
@@ -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();
}
@@ -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);