mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
javadoc: show short annotation names in signatures and full names with explanations underneath (IDEA-167076, IDEA-166361)
This commit is contained in:
@@ -66,37 +66,40 @@ class AnnotationDocGenerator {
|
||||
return Flow.class.getName().equals(annoQName);
|
||||
}
|
||||
|
||||
void generateAnnotation(StringBuilder buffer, boolean generateLink, boolean useShortNames) {
|
||||
boolean inferred = AnnotationUtil.isInferredAnnotation(myAnnotation);
|
||||
boolean external = AnnotationUtil.isExternalAnnotation(myAnnotation);
|
||||
boolean isExternal() {
|
||||
return AnnotationUtil.isExternalAnnotation(myAnnotation);
|
||||
}
|
||||
|
||||
boolean isInferred() {
|
||||
return AnnotationUtil.isInferredAnnotation(myAnnotation);
|
||||
}
|
||||
|
||||
void generateAnnotation(StringBuilder buffer, AnnotationFormat format) {
|
||||
String qualifiedName = myAnnotation.getQualifiedName();
|
||||
if (myTargetClass != null &&
|
||||
qualifiedName != null && JavaDocUtil.findReferenceTarget(myOwner.getManager(), qualifiedName, myOwner) != null) {
|
||||
if (inferred) buffer.append("<i>");
|
||||
PsiClassType type = JavaPsiFacade.getElementFactory(myOwner.getProject()).createType(myTargetClass, PsiSubstitutor.EMPTY);
|
||||
buffer.append("@");
|
||||
if (inferred && !generateLink) {
|
||||
buffer.append(type.getPresentableText());
|
||||
}
|
||||
else {
|
||||
JavaDocInfoGenerator.generateType(buffer, type, myOwner, generateLink, useShortNames && !external);
|
||||
}
|
||||
generateAnnotationAttributes(buffer, generateLink);
|
||||
if (inferred) buffer.append("</i>");
|
||||
}
|
||||
else if (external || myResolveNotPossible) {
|
||||
if (inferred) buffer.append("<i>");
|
||||
String annoText = inferred ? "@" + myNameReference.getReferenceName() + myAnnotation.getParameterList().getText()
|
||||
: myAnnotation.getText();
|
||||
buffer.append(XmlStringUtil.escapeString(annoText));
|
||||
if (inferred) buffer.append("</i>");
|
||||
}
|
||||
else {
|
||||
buffer.append("<font color=red>");
|
||||
buffer.append(XmlStringUtil.escapeString(myAnnotation.getText()));
|
||||
buffer.append("</font>");
|
||||
PsiClassType type =
|
||||
myTargetClass != null && qualifiedName != null && JavaDocUtil.findReferenceTarget(myOwner.getManager(), qualifiedName, myOwner) != null
|
||||
? JavaPsiFacade.getElementFactory(myOwner.getProject()).createType(myTargetClass, PsiSubstitutor.EMPTY)
|
||||
: null;
|
||||
|
||||
boolean red = type == null && !myResolveNotPossible && !isInferred() && !isExternal();
|
||||
|
||||
if (isInferred()) buffer.append("<i>");
|
||||
if (red) buffer.append("<font color=red>");
|
||||
|
||||
boolean generateLink = format != AnnotationFormat.ToolTip;
|
||||
boolean forceShortNames = format != AnnotationFormat.JavaDocComplete;
|
||||
|
||||
buffer.append("@");
|
||||
String name = forceShortNames ? myNameReference.getReferenceName() : myNameReference.getText();
|
||||
if (type != null && generateLink) {
|
||||
JavaDocInfoGenerator.generateLink(buffer, myTargetClass, name, false);
|
||||
} else {
|
||||
buffer.append(name);
|
||||
}
|
||||
if (red) buffer.append("</font>");
|
||||
|
||||
generateAnnotationAttributes(buffer, generateLink);
|
||||
if (isInferred()) buffer.append("</i>");
|
||||
}
|
||||
|
||||
private void generateAnnotationAttributes(StringBuilder buffer, boolean generateLink) {
|
||||
@@ -189,3 +192,7 @@ class AnnotationDocGenerator {
|
||||
return infos;
|
||||
}
|
||||
}
|
||||
|
||||
enum AnnotationFormat {
|
||||
ToolTip, JavaDocShort, JavaDocComplete
|
||||
}
|
||||
|
||||
@@ -400,13 +400,13 @@ public class JavaDocInfoGenerator {
|
||||
public static String generateSignature(PsiElement element) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (element instanceof PsiClass) {
|
||||
if (generateClassSignature(buf, (PsiClass)element, false)) return null;
|
||||
if (generateClassSignature(buf, (PsiClass)element, SignaturePlace.ToolTip)) return null;
|
||||
}
|
||||
else if (element instanceof PsiField) {
|
||||
generateFieldSignature(buf, (PsiField)element, false);
|
||||
generateFieldSignature(buf, (PsiField)element, SignaturePlace.ToolTip);
|
||||
}
|
||||
else if (element instanceof PsiMethod) {
|
||||
generateMethodSignature(buf, (PsiMethod)element, false, true);
|
||||
generateMethodSignature(buf, (PsiMethod)element, SignaturePlace.ToolTip);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
@@ -477,9 +477,11 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
|
||||
buffer.append("<PRE>");
|
||||
if (generateClassSignature(buffer, aClass, true)) return;
|
||||
if (generateClassSignature(buffer, aClass, SignaturePlace.Javadoc)) return;
|
||||
buffer.append("</PRE>");
|
||||
|
||||
new NonCodeAnnotationGenerator(aClass, buffer).explainAnnotations();
|
||||
|
||||
PsiDocComment comment = getDocComment(aClass);
|
||||
if (comment != null) {
|
||||
generateCommonSection(buffer, comment);
|
||||
@@ -489,8 +491,9 @@ public class JavaDocInfoGenerator {
|
||||
if (generatePrologueAndEpilogue) generateEpilogue(buffer);
|
||||
}
|
||||
|
||||
private static boolean generateClassSignature(StringBuilder buffer, PsiClass aClass, boolean generateLink) {
|
||||
generateAnnotations(buffer, aClass, generateLink, true, false);
|
||||
private static boolean generateClassSignature(StringBuilder buffer, PsiClass aClass, SignaturePlace place) {
|
||||
boolean generateLink = place == SignaturePlace.Javadoc;
|
||||
generateAnnotations(buffer, aClass, place, true);
|
||||
String modifiers = PsiFormatUtil.formatModifiers(aClass, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
|
||||
if (!modifiers.isEmpty()) {
|
||||
buffer.append(modifiers);
|
||||
@@ -628,9 +631,11 @@ public class JavaDocInfoGenerator {
|
||||
generateLinkToParentIfNeeded(buffer, field);
|
||||
|
||||
buffer.append("<PRE>");
|
||||
generateFieldSignature(buffer, field, true);
|
||||
generateFieldSignature(buffer, field, SignaturePlace.Javadoc);
|
||||
buffer.append("</PRE>");
|
||||
|
||||
new NonCodeAnnotationGenerator(field, buffer).explainAnnotations();
|
||||
|
||||
ColorUtil.appendColorPreview(field, buffer);
|
||||
|
||||
PsiDocComment comment = getDocComment(field);
|
||||
@@ -641,8 +646,10 @@ public class JavaDocInfoGenerator {
|
||||
if (generatePrologueAndEpilogue) generateEpilogue(buffer);
|
||||
}
|
||||
|
||||
private static void generateFieldSignature(StringBuilder buffer, PsiField field, boolean generateLink) {
|
||||
generateAnnotations(buffer, field, generateLink, true, false);
|
||||
private static void generateFieldSignature(StringBuilder buffer, PsiField field, SignaturePlace place) {
|
||||
boolean generateLink = place == SignaturePlace.Javadoc;
|
||||
generateAnnotations(buffer, field, place, true);
|
||||
|
||||
String modifiers = PsiFormatUtil.formatModifiers(field, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
|
||||
if (!modifiers.isEmpty()) {
|
||||
buffer.append(modifiers);
|
||||
@@ -876,13 +883,13 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateAnnotations(@NotNull StringBuilder buffer,
|
||||
@NotNull PsiModifierListOwner owner,
|
||||
boolean generateLink,
|
||||
boolean splitAnnotations,
|
||||
boolean useShortNames) {
|
||||
private static void generateAnnotations(StringBuilder buffer,
|
||||
PsiModifierListOwner owner,
|
||||
SignaturePlace place,
|
||||
boolean splitAnnotations) {
|
||||
AnnotationFormat format = place == SignaturePlace.Javadoc ? AnnotationFormat.JavaDocShort : AnnotationFormat.ToolTip;
|
||||
for (AnnotationDocGenerator anno : AnnotationDocGenerator.getAnnotationsToShow(owner)) {
|
||||
anno.generateAnnotation(buffer, generateLink, useShortNames);
|
||||
anno.generateAnnotation(buffer, format);
|
||||
|
||||
buffer.append(" ");
|
||||
if (splitAnnotations) buffer.append("\n");
|
||||
@@ -908,7 +915,7 @@ public class JavaDocInfoGenerator {
|
||||
buffer.append(modifiers);
|
||||
buffer.append(" ");
|
||||
}
|
||||
generateAnnotations(buffer, parameter, true, true, false);
|
||||
generateAnnotations(buffer, parameter, SignaturePlace.Javadoc, true);
|
||||
generateType(buffer, parameter.getType(), parameter);
|
||||
buffer.append(" ");
|
||||
buffer.append("<b>");
|
||||
@@ -917,6 +924,8 @@ public class JavaDocInfoGenerator {
|
||||
buffer.append("</b>");
|
||||
buffer.append("</PRE>");
|
||||
|
||||
new NonCodeAnnotationGenerator(parameter, buffer).explainAnnotations();
|
||||
|
||||
final PsiElement method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class, PsiLambdaExpression.class);
|
||||
|
||||
if (method instanceof PsiMethod) {
|
||||
@@ -943,9 +952,11 @@ public class JavaDocInfoGenerator {
|
||||
generateLinkToParentIfNeeded(buffer, method);
|
||||
|
||||
buffer.append("<PRE>");
|
||||
generateMethodSignature(buffer, method, true, false);
|
||||
generateMethodSignature(buffer, method, SignaturePlace.Javadoc);
|
||||
buffer.append("</PRE>");
|
||||
|
||||
new NonCodeAnnotationGenerator(method, buffer).explainAnnotations();
|
||||
|
||||
PsiDocComment comment = getMethodDocComment(method);
|
||||
|
||||
generateMethodDescription(buffer, method, comment);
|
||||
@@ -983,8 +994,10 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateMethodSignature(StringBuilder buffer, PsiMethod method, boolean generateLink, boolean useShortNames) {
|
||||
generateAnnotations(buffer, method, generateLink, true, useShortNames);
|
||||
private static void generateMethodSignature(StringBuilder buffer, PsiMethod method, SignaturePlace place) {
|
||||
boolean useShortNames = place == SignaturePlace.ToolTip;
|
||||
boolean generateLink = place == SignaturePlace.Javadoc;
|
||||
generateAnnotations(buffer, method, place, true);
|
||||
String modifiers = PsiFormatUtil.formatModifiers(method, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
|
||||
int indent = 0;
|
||||
if (!modifiers.isEmpty()) {
|
||||
@@ -1017,7 +1030,7 @@ public class JavaDocInfoGenerator {
|
||||
PsiParameter[] parms = method.getParameterList().getParameters();
|
||||
for (int i = 0; i < parms.length; i++) {
|
||||
PsiParameter parm = parms[i];
|
||||
generateAnnotations(buffer, parm, generateLink, false, useShortNames);
|
||||
generateAnnotations(buffer, parm, place, false);
|
||||
generateType(buffer, parm.getType(), method, generateLink, useShortNames);
|
||||
buffer.append(" ");
|
||||
if (parm.getName() != null) {
|
||||
@@ -1744,7 +1757,7 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateLink(StringBuilder buffer, PsiElement element, String label, boolean plainLink) {
|
||||
static void generateLink(StringBuilder buffer, PsiElement element, String label, boolean plainLink) {
|
||||
String refText = JavaDocUtil.getReferenceText(element.getProject(), element);
|
||||
if (refText != null) {
|
||||
DocumentationManagerUtil.createHyperlink(buffer, element, refText, label, plainLink);
|
||||
@@ -2180,4 +2193,8 @@ public class JavaDocInfoGenerator {
|
||||
myBuffer.append(StringUtil.escapeXml(expression.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
private enum SignaturePlace {
|
||||
Javadoc, ToolTip
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.javadoc;
|
||||
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class NonCodeAnnotationGenerator {
|
||||
private final List<PsiModifierListOwner> myAllOwners = new ArrayList<>();
|
||||
private final StringBuilder myOutput;
|
||||
|
||||
NonCodeAnnotationGenerator(PsiModifierListOwner owner, StringBuilder output) {
|
||||
myOutput = output;
|
||||
myAllOwners.add(owner);
|
||||
if (owner instanceof PsiMethod) {
|
||||
Collections.addAll(myAllOwners, ((PsiMethod)owner).getParameterList().getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
void explainAnnotations() {
|
||||
MultiMap<PsiModifierListOwner, AnnotationDocGenerator> generators = getNonCodeAnnotations();
|
||||
if (generators.isEmpty()) return;
|
||||
|
||||
boolean hasExternal = generators.values().stream().anyMatch(AnnotationDocGenerator::isExternal);
|
||||
boolean hasInferred = generators.values().stream().anyMatch(AnnotationDocGenerator::isInferred);
|
||||
boolean hasBothKinds = hasExternal && hasInferred;
|
||||
|
||||
myOutput.append("\n");
|
||||
myOutput.append(
|
||||
hasBothKinds ? "External and <i>inferred</i>" : hasExternal ? "External" : "<i>Inferred</i>").append(" annotations available:<br>\n");
|
||||
myOutput.append("<ul>\n");
|
||||
|
||||
generators.keySet().forEach(owner -> {
|
||||
myOutput.append("<li>");
|
||||
if (generators.size() > 1) {
|
||||
myOutput.append(getKind(owner)).append(" <code>").append(((PsiNamedElement)owner).getName()).append("</code>: ");
|
||||
}
|
||||
List<AnnotationDocGenerator> annotations = ContainerUtil.newArrayList(generators.get(owner));
|
||||
for (int i = 0; i < annotations.size(); i++) {
|
||||
if (i > 0) myOutput.append(" ");
|
||||
annotations.get(i).generateAnnotation(myOutput, AnnotationFormat.JavaDocComplete);
|
||||
}
|
||||
|
||||
myOutput.append("</li>\n");
|
||||
});
|
||||
myOutput.append("</ul>\n");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MultiMap<PsiModifierListOwner, AnnotationDocGenerator> getNonCodeAnnotations() {
|
||||
MultiMap<PsiModifierListOwner, AnnotationDocGenerator> generators = MultiMap.createLinked();
|
||||
for (PsiModifierListOwner owner : myAllOwners) {
|
||||
List<AnnotationDocGenerator> nonCode =
|
||||
ContainerUtil.filter(AnnotationDocGenerator.getAnnotationsToShow(owner), a -> a.isExternal() || a.isInferred());
|
||||
if (!nonCode.isEmpty()) {
|
||||
generators.putValues(owner, nonCode);
|
||||
}
|
||||
}
|
||||
return generators;
|
||||
}
|
||||
|
||||
private static String getKind(PsiModifierListOwner owner) {
|
||||
if (owner instanceof PsiParameter) return "Parameter";
|
||||
if (owner instanceof PsiMethod) {
|
||||
return ((PsiMethod)owner).isConstructor() ? "Constructor" : "Method";
|
||||
}
|
||||
return owner.getClass().getName(); // unexpected
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -1,5 +1,10 @@
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://java.util.List"><code>java.util.List</code></a></b></small><PRE><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>org.jetbrains.annotations.Contract</code></a>(pure = true)</i>
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://java.util.List"><code>java.util.List</code></a></b></small><PRE><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>Contract</code></a>(pure = true)</i>
|
||||
public abstract boolean <b>contains</b>(<a href="psi_element://java.lang.Object"><code>Object</code></a> o)</PRE>
|
||||
<i>Inferred</i> annotations available:<br>
|
||||
<ul>
|
||||
<li><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>org.jetbrains.annotations.Contract</code></a>(pure = true)</i></li>
|
||||
</ul>
|
||||
|
||||
|
||||
Returns <tt>true</tt> if this list contains the specified element.
|
||||
More formally, returns <tt>true</tt> if and only if this list contains
|
||||
|
||||
+7
-2
@@ -1,5 +1,10 @@
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://My"><code>My</code></a></b></small><PRE><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>org.jetbrains.annotations.Contract</code></a>(pure = true)</i>
|
||||
boolean <b>contains</b>(<a href="psi_element://java.lang.Object"><code>Object</code></a> o)</PRE><DD><DL><DT><b>Description copied from interface:</b> <a href="psi_element://java.util.Collection"><code>java.util.Collection</code></a><br>
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://My"><code>My</code></a></b></small><PRE><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>Contract</code></a>(pure = true)</i>
|
||||
boolean <b>contains</b>(<a href="psi_element://java.lang.Object"><code>Object</code></a> o)</PRE>
|
||||
<i>Inferred</i> annotations available:<br>
|
||||
<ul>
|
||||
<li><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>org.jetbrains.annotations.Contract</code></a>(pure = true)</i></li>
|
||||
</ul>
|
||||
<DD><DL><DT><b>Description copied from interface:</b> <a href="psi_element://java.util.Collection"><code>java.util.Collection</code></a><br>
|
||||
Returns <tt>true</tt> if this collection contains the specified
|
||||
element. More formally, returns <tt>true</tt> if and only if this
|
||||
collection contains at least one element <tt>e</tt> such that
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://En"><code>En</code></a></b></small><PRE><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>org.jetbrains.annotations.Contract</code></a>(pure = true)</i>
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://En"><code>En</code></a></b></small><PRE><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>Contract</code></a>(pure = true)</i>
|
||||
static int <b>valueOf</b>(int i)</PRE>
|
||||
<i>Inferred</i> annotations available:<br>
|
||||
<ul>
|
||||
<li><i>@<a href="psi_element://org.jetbrains.annotations.Contract"><code>org.jetbrains.annotations.Contract</code></a>(pure = true)</i></li>
|
||||
</ul>
|
||||
|
||||
myjavadoc
|
||||
<DD><DL><DT><b>Parameters:</b><DD><code>i</code> - </DD></DL></DD><DD><DL><DT><b>Returns:</b><DD></DD></DL></DD></body></html>
|
||||
+2
-2
@@ -156,8 +156,8 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC
|
||||
setUpExternalUpAnnotations();
|
||||
openDecompiledClass("java.lang.Boolean");
|
||||
checkHasGutter("<html>External and <i>inferred</i> annotations available. Full signature:<p>\n" +
|
||||
"@org.jetbrains.annotations.Contract("null->false") \n" +
|
||||
"private static boolean <b>toBoolean</b>(@org.jetbrains.annotations.Nullable String var0)</html>");
|
||||
"@Contract("null->false") \n" +
|
||||
"private static boolean <b>toBoolean</b>(@Nullable String var0)</html>");
|
||||
}
|
||||
|
||||
private void checkHasGutter(String expectedText) {
|
||||
|
||||
Reference in New Issue
Block a user