mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[javadoc] Fix label for links
Links that come from {@link} or @see without label should be rendered with a monospace font (!plainLink).
#IDEA-372168 Fixed
GitOrigin-RevId: 8040e6b0ea850bc9f043a8a72bb4a5bef8a2739e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cb3d9aeb39
commit
d2c974c282
@@ -47,10 +47,12 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiSnippetDocTagImpl;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaDocElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.impl.source.tree.TreeElement;
|
||||
import com.intellij.psi.javadoc.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -2303,10 +2305,11 @@ public class JavaDocInfoGenerator {
|
||||
|
||||
private void generateLinkValue(PsiInlineDocTag tag, StringBuilder buffer, boolean plainLink) {
|
||||
PsiElement[] tagElements = tag.getDataElements();
|
||||
String text = createLinkText(tagElements);
|
||||
if (!text.isEmpty()) {
|
||||
generateLink(buffer, text, tagElements[0], plainLink);
|
||||
}
|
||||
PsiElement ref = getRefElement(tagElements);
|
||||
String label = getLinkLabel(tagElements, ref);
|
||||
StringBuilder b = new StringBuilder();
|
||||
collectElementText(b, ref != null ? ref : tag);
|
||||
generateLink(buffer, b.toString(), label, tag, plainLink, !hasLinkLabel(tagElements, ref));
|
||||
}
|
||||
|
||||
private void generateMarkdownLinkValue(PsiMarkdownReferenceLink referenceLink, StringBuilder buffer) {
|
||||
@@ -2322,7 +2325,7 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
|
||||
private void generateValueValue(PsiInlineDocTag tag, StringBuilder buffer, PsiElement element) {
|
||||
String text = createLinkText(tag.getDataElements());
|
||||
String text = getRefText(tag.getDataElements());
|
||||
PsiField valueField = null;
|
||||
if (text.isEmpty()) {
|
||||
if (myElement instanceof PsiField) valueField = (PsiField)myElement;
|
||||
@@ -2364,18 +2367,53 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
protected String createLinkText(PsiElement[] tagElements) {
|
||||
int predictOffset = tagElements.length > 0 ? tagElements[0].getTextOffset() + tagElements[0].getText().length() : 0;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (PsiElement tagElement : tagElements) {
|
||||
if (tagElement.getTextOffset() > predictOffset) buffer.append(' ');
|
||||
predictOffset = tagElement.getTextOffset() + tagElement.getText().length();
|
||||
protected String getLinkLabel(PsiElement[] tagElements, @Nullable PsiElement ref) {
|
||||
if (!hasLinkLabel(tagElements, ref)) return null;
|
||||
|
||||
collectElementText(buffer, tagElement);
|
||||
}
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
Arrays.stream(tagElements)
|
||||
.skip(ref == null ? 0 : ContainerUtil.indexOf(tagElements, e -> e == ref) + 1)
|
||||
.forEach(element -> collectElementText(buffer, element));
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
private String getRefText(PsiElement[] tagElements) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
PsiElement ref = getRefElement(tagElements);
|
||||
if (ref != null) collectElementText(buffer, ref);
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
private static @Nullable PsiElement getRefElement(PsiElement[] tagElements) {
|
||||
for (PsiElement element : tagElements) {
|
||||
if (element instanceof PsiWhiteSpace) { continue; }
|
||||
|
||||
if (element instanceof PsiDocMethodOrFieldRef) {
|
||||
return element;
|
||||
}
|
||||
// JavaDoc references
|
||||
if (element instanceof TreeElement treeElement && treeElement.getTokenType() == JavaDocElementType.DOC_REFERENCE_HOLDER) {
|
||||
return element;
|
||||
}
|
||||
// JavaDoc module references
|
||||
if (element instanceof PsiDocTagValue docTagValue) {
|
||||
PsiElement firstChild = docTagValue.getFirstChild();
|
||||
if (firstChild instanceof PsiJavaModuleReferenceElement || firstChild instanceof PsiJavaModuleReference) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the link has a custom label.
|
||||
*/
|
||||
private static boolean hasLinkLabel(PsiElement[] tagElements, PsiElement ref) {
|
||||
return !(ContainerUtil.and(tagElements, element -> element == ref || element.getText().isBlank()));
|
||||
}
|
||||
|
||||
protected void collectElementText(StringBuilder buffer, PsiElement element) {
|
||||
element.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
@@ -2426,15 +2464,17 @@ public class JavaDocInfoGenerator {
|
||||
PsiDocTag tag = tags[i];
|
||||
PsiElement[] elements = tag.getDataElements();
|
||||
if (elements.length > 0) {
|
||||
String text = createLinkText(elements);
|
||||
if (StringUtil.startsWithChar(text, '<')) {
|
||||
buffer.append(text);
|
||||
PsiElement ref = getRefElement(elements);
|
||||
String linkLabel = getLinkLabel(elements, ref);
|
||||
if (StringUtil.startsWithChar(linkLabel, '<')) {
|
||||
buffer.append(linkLabel);
|
||||
}
|
||||
else if (StringUtil.startsWithChar(text, '"')) {
|
||||
appendPlainText(buffer, text);
|
||||
else if (StringUtil.startsWithChar(linkLabel, '"')) {
|
||||
appendPlainText(buffer, linkLabel);
|
||||
}
|
||||
else {
|
||||
generateLink(buffer, text, comment, false);
|
||||
boolean plain = hasLinkLabel(elements, ref);
|
||||
generateLink(buffer, ref != null ? ref.getText() : tag.getText(), plain ? linkLabel : null, tag, plain);
|
||||
}
|
||||
}
|
||||
if (i < tags.length - 1) {
|
||||
@@ -2706,13 +2746,6 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateLink(StringBuilder buffer, String linkText, @NotNull PsiElement context, boolean plainLink) {
|
||||
int index = JavaDocUtil.extractReference(linkText);
|
||||
String refText = linkText.substring(0, index).trim();
|
||||
String label = StringUtil.nullize(linkText.substring(index).trim());
|
||||
generateLink(buffer, refText, label, context, plainLink);
|
||||
}
|
||||
|
||||
void generateLink(StringBuilder buffer, PsiElement element, String label, boolean plainLink) {
|
||||
String refText = JavaDocUtil.getReferenceText(element.getProject(), element);
|
||||
if (refText != null) {
|
||||
@@ -2721,13 +2754,22 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refText The text of the target element.
|
||||
* @param label An optional user-defined label. When not null, it will always be used as the link text.
|
||||
* @return Length of the generated label.
|
||||
*/
|
||||
int generateLink(StringBuilder buffer, String refText, String label, @NotNull PsiElement context, boolean plainLink) {
|
||||
if (label == null) {
|
||||
PsiManager manager = context.getManager();
|
||||
label = JavaDocUtil.getLabelText(manager.getProject(), manager, refText, context);
|
||||
}
|
||||
return generateLink(buffer, refText, label, context, plainLink, !plainLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refText The text of the target element.
|
||||
* @param label An optional user-defined label. When not null, it will always be used as the link text.
|
||||
* @param shouldHighlightLabel True if syntax highlighting should be applied to the link (package, class, …).
|
||||
* @return Length of the generated label.
|
||||
*/
|
||||
int generateLink(StringBuilder buffer, String refText, String label, @NotNull PsiElement context, boolean plainLink, boolean shouldHighlightLabel) {
|
||||
// Resolve link target
|
||||
LOG.assertTrue(refText != null, "refText appears to be null.");
|
||||
PsiElement target = null;
|
||||
try {
|
||||
@@ -2736,8 +2778,16 @@ public class JavaDocInfoGenerator {
|
||||
catch (IndexNotReadyException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
appendMaybeUnresolvedLink(buffer, target, label, context.getProject(), plainLink);
|
||||
return StringUtil.stripHtml(label, true).length();
|
||||
|
||||
// Resolve link text
|
||||
String linkLabel = label;
|
||||
if (label == null) {
|
||||
PsiManager manager = context.getManager();
|
||||
linkLabel = JavaDocUtil.getLabelText(manager.getProject(), manager, refText, context);
|
||||
}
|
||||
|
||||
appendMaybeUnresolvedLink(buffer, target, linkLabel, context.getProject(), plainLink, shouldHighlightLabel);
|
||||
return StringUtil.stripHtml(linkLabel, true).length();
|
||||
}
|
||||
|
||||
public void appendMaybeUnresolvedLink(
|
||||
@@ -2746,6 +2796,20 @@ public class JavaDocInfoGenerator {
|
||||
String label,
|
||||
@NotNull Project project,
|
||||
boolean plainLink
|
||||
) {
|
||||
appendMaybeUnresolvedLink(buffer, target, label, project, plainLink, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param canHighlightLink false if the link should not receive syntax highlighting (e.g., it has a custom label)
|
||||
*/
|
||||
public void appendMaybeUnresolvedLink(
|
||||
StringBuilder buffer,
|
||||
@Nullable PsiElement target,
|
||||
String label,
|
||||
@NotNull Project project,
|
||||
boolean plainLink,
|
||||
boolean canHighlightLink
|
||||
) {
|
||||
if (target == null && DumbService.isDumb(project)) {
|
||||
buffer.append(label);
|
||||
@@ -2754,9 +2818,8 @@ public class JavaDocInfoGenerator {
|
||||
buffer.append(getSpanForUnresolvedItem()).append(label).append("</span>");
|
||||
}
|
||||
else {
|
||||
String highlightedLabel = myIsSignatureGenerationInProgress && doHighlightSignatures() || doSemanticHighlightingOfLinks()
|
||||
? tryHighlightLinkLabel(target, label)
|
||||
: label;
|
||||
boolean doHighlight = canHighlightLink && ((myIsSignatureGenerationInProgress && doHighlightSignatures() || doSemanticHighlightingOfLinks()));
|
||||
String highlightedLabel = doHighlight ? tryHighlightLinkLabel(target, label) : label;
|
||||
generateLink(buffer, target, highlightedLabel, plainLink);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
searching strings, for extracting substrings, and for creating a
|
||||
copy of a string with all characters translated to uppercase or to
|
||||
lowercase. Case mapping is based on the Unicode Standard version
|
||||
specified by the <a href="psi_element://java.lang.Character"><code><span style="color:#0000ff;">Character</span></code></a> class.
|
||||
specified by the <a href="psi_element://java.lang.Character"><code>Character</code></a> class.
|
||||
<p>
|
||||
The Java language provides special support for the string
|
||||
concatenation operator ( + ), and for conversion of
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">Test</span></pre></div><div class='content'>
|
||||
Link to class <a href="psi_element://java.lang.String"><code><span style="color:#0000ff;">String</span></code></a>. <br>
|
||||
Link to class <a href="psi_element://java.lang.String"><code><span style="color:#0000ff;">myLink</span></code></a>. <br>
|
||||
Link to class <a href="psi_element://java.lang.String"><code>myLink</code></a>. <br>
|
||||
</div><table class='sections'><p></table>
|
||||
@@ -2,5 +2,5 @@
|
||||
<code><span style="">Hello world</span></code></p><p>No tags are interpreted inside them
|
||||
<code><span style="">{@link java.lang.String niceLink}</span></code></p><p>No markdown markup is interpreted inside them
|
||||
<code><span style="">_Hello_ <code></span></code></p><p>Code span inside a link
|
||||
<a href="psi_element://java.lang.String"><span style="color:#0000ff;">my text with <code>a code span</code>!</span></a></p><p>This is a broken inline code span
|
||||
<a href="psi_element://java.lang.String">my text with <code>a code span</code>!</a></p><p>This is a broken inline code span
|
||||
`Start of broken code span</p>end of broken code span`</div><table class='sections'><p></table>
|
||||
@@ -6,10 +6,10 @@
|
||||
used in <code><span style="">equals</span></code> comparisons on the object is modified.
|
||||
This integer need not remain consistent from one execution of an
|
||||
application to another execution of the same application.</li><li>If two objects are equal according to the
|
||||
<a href="psi_element://java.lang.Object#equals(java.lang.Object)"><span style="color:#0000ff;">equals</span></a> method, then calling the
|
||||
<a href="psi_element://java.lang.Object#equals(java.lang.Object)">equals</a> method, then calling the
|
||||
<code><span style="">hashCode</span></code> method on each of the two objects must produce the
|
||||
same integer result.</li><li>It is <em>not</em> required that if two objects are unequal
|
||||
according to the <a href="psi_element://java.lang.Object#equals(java.lang.Object)"><span style="color:#0000ff;">equals</span></a> method, then
|
||||
according to the <a href="psi_element://java.lang.Object#equals(java.lang.Object)">equals</a> method, then
|
||||
calling the <code><span style="">hashCode</span></code> method on each of the two objects
|
||||
must produce distinct integer results. However, the programmer
|
||||
should be aware that producing distinct integer results for
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">MarkdownCodeBlock</span></pre></div><div class='content'><p><a href="psi_element://java.util.HashMap"><code><span style="color:#0000ff;">java.util.HashMap</span></code></a></p><a href="psi_element://java.util.Collections#EMPTY_LIST"><span style="color:#660e7a;">The default empty collection</span></a></div><table class='sections'><p></table>
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">MarkdownCodeBlock</span></pre></div><div class='content'><p><a href="psi_element://java.util.HashMap"><code><span style="color:#0000ff;">java.util.HashMap</span></code></a></p><a href="psi_element://java.util.Collections#EMPTY_LIST">The default empty collection</a></div><table class='sections'><p></table>
|
||||
@@ -0,0 +1 @@
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">Test</span></pre></div><table class='sections'><p><tr><td valign='top' class='section'><p>See Also:</td><td valign='top'><p>"plain",<br><a href="java.lang.Math#E">plain</a>,<br><a href="psi_element://java.lang.Math#E"><code><span style="color:#0000ff;">Math</span><span style="">.</span><span style="color:#660e7a;">E</span></code></a>,<br><a href="psi_element://java.base/"><code><span style="">java.base/</span></code></a>,<br><a href="psi_element://java.lang.Math#E"><code><span style="color:#0000ff;">Math</span><span style="">.</span><span style="color:#660e7a;">E</span></code></a>,<br><a href="psi_element://java.lang.Math#E">plain</a>,<br><a href="psi_element://java.lang.Math#E">myLabel.Math#E</a></td></table>
|
||||
@@ -0,0 +1,14 @@
|
||||
import java.lang.String;
|
||||
|
||||
/**
|
||||
* @see "plain"
|
||||
* @see <a href="java.lang.Math#E">plain</a>
|
||||
* @see java.lang.Math#E
|
||||
* @see java.base/
|
||||
* @see java.base/java.lang.Math#E
|
||||
* @see java.lang.Math#E plain
|
||||
* @see java.lang.Math#E myLabel.Math#E
|
||||
*/
|
||||
class Test {
|
||||
public String field = null;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">Test</span></pre></div><table class='sections'><p><tr><td valign='top' class='section'><p>See Also:</td><td valign='top'><p>"plain",<br><a href="java.lang.Math#E">plain</a>,<br><a href="psi_element://java.lang.Math#E"><code><span style="color:#0000ff;">Math</span><span style="">.</span><span style="color:#660e7a;">E</span></code></a>,<br><a href="psi_element://java.base/"><code><span style="">java.base/</span></code></a>,<br><a href="psi_element://java.lang.Math#E"><code><span style="color:#0000ff;">Math</span><span style="">.</span><span style="color:#660e7a;">E</span></code></a>,<br><a href="psi_element://java.lang.Math#E">plain</a>,<br><a href="psi_element://java.lang.Math#E">myLabel.Math#E</a></td></table>
|
||||
@@ -0,0 +1,12 @@
|
||||
import java.lang.String;
|
||||
|
||||
/// @see "plain"
|
||||
/// @see <a href="java.lang.Math#E">plain</a>
|
||||
/// @see java.lang.Math#E
|
||||
/// @see java.base/
|
||||
/// @see java.base/java.lang.Math#E
|
||||
/// @see java.lang.Math#E plain
|
||||
/// @see java.lang.Math#E myLabel.Math#E
|
||||
class Test {
|
||||
public String field = null;
|
||||
}
|
||||
+2
@@ -209,6 +209,8 @@ public class JavaDocInfoGeneratorTest extends JavaCodeInsightTestCase {
|
||||
public void testEscapeHtmlCodesInCodeBlock() { doTestClass(); }
|
||||
public void testPreTagLeakBeforeCode() { doTestClass(); }
|
||||
public void testPreTagStrictBeforeCode(){ doTestClass(); }
|
||||
public void testSeeTagWithLabel() { doTestClass(); }
|
||||
public void testSeeTagWithLabelMarkdown() { doTestClass(); }
|
||||
|
||||
public void testRepeatableAnnotations() {
|
||||
useJava8();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.groovy;
|
||||
|
||||
import com.intellij.codeInsight.navigation.CtrlMouseHandler;
|
||||
@@ -47,7 +47,7 @@ public class GroovyDocumentationTest extends LightJavaCodeInsightFixtureTestCase
|
||||
""",
|
||||
"""
|
||||
<div class="bottom"><icon src="AllIcons.Nodes.Class"> <a href="psi_element://Gr"><code><span style="color:#000000;">Gr</span></code></a></div><div class='definition'><pre><span style="color:#000043;font-weight:bold;">void</span> <span style="color:#000000;">foo</span><span style="">(</span><span style="">)</span></pre></div><div class='content'>
|
||||
Use <a href="psi_element://Gr#bar()"><code><span style="color:#0000ff;">bar</span><span style="">()</span></code></a> from class <a href="psi_element://Gr"><code><span style="color:#0000ff;">Gr</span></code></a> instead
|
||||
Use <a href="psi_element://Gr#bar()"><code>#bar()</code></a> from class <a href="psi_element://Gr"><code>Gr</code></a> instead
|
||||
</div><table class='sections'></table>""");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user