IDEA-379405 javadoc: fix parser improperly creating a broken reference.

Also fixes some jdoc parsing and rendering issues.

Also fixed some translation issues from the first shot related to said issue.

#IDEA-379405 Fixed

GitOrigin-RevId: 1daea4e1b9fd809a8f98ac7bfed1e5b09eb7e89a
This commit is contained in:
Mathias
2026-01-05 14:38:56 +00:00
committed by intellij-monorepo-bot
parent 27c0a9ab9c
commit 52d1d2ea6c
10 changed files with 89 additions and 57 deletions
@@ -5,6 +5,7 @@ import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.javadoc.PsiMarkdownReferenceLink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,21 +33,28 @@ public class PsiMarkdownReferenceLinkImpl extends CompositePsiElement implements
@Override
public @Nullable PsiElement getLabel() {
if (getChildren().length >= 1) {
return getChildren()[1];
}
// malformed/incomplete link
return null;
// returns `null` malformed/incomplete link
return getChildAt(1);
}
@Override
public boolean isShortLink() {
return getChildren().length < 4;
return countChildren(null) < 5;
}
@Override
public @Nullable PsiElement getLinkElement() {
if (isShortLink()) return getLabel();
return getChildren()[4];
int childrenCount = countChildren(null);
return getChildAt(childrenCount - 2);
}
/// Utility function, get PsiElement at specific index (if it exists)
private @Nullable PsiElement getChildAt(int index) {
TreeElement child = getFirstChildNode();
for (int i = 0; i < index; i++) {
child = child.getTreeNext();
if (child == null) return null;
}
return child == null ? null : child.getPsi();
}
}
@@ -320,7 +320,6 @@ class JavaDocParser(
}
private fun parseMarkdownReference() {
val refStart = builder.mark()
val moduleMarker = parseModuleRef(builder.mark())
var referenceParsed = false
var referenceEnded = false
@@ -328,12 +327,12 @@ class JavaDocParser(
if (getTokenType() === JavaDocSyntaxTokenType.DOC_RBRACKET) {
if (moduleMarker == null) {
refStart.drop()
return
} else {
referenceEnded = true
}
referenceEnded = true
}
moduleMarker?.done(JavaDocSyntaxElementType.DOC_TAG_VALUE_ELEMENT)
val refStart = builder.mark()
if (!referenceEnded && getTokenType() !== JavaDocSyntaxTokenType.DOC_SHARP && getTokenType() !== JavaDocSyntaxTokenType.DOC_DOUBLE_SHARP) {
builder.remapCurrentToken(JavaDocSyntaxElementType.DOC_REFERENCE_HOLDER)
@@ -395,8 +394,7 @@ class JavaDocParser(
}
}
if (referenceParsed || moduleMarker != null) {
moduleMarker?.done(JavaDocSyntaxElementType.DOC_TAG_VALUE_ELEMENT)
if (referenceParsed) {
when (fragmentReference) {
true -> refStart.done(JavaDocSyntaxElementType.DOC_FRAGMENT_REF)
false -> refStart.done(JavaDocSyntaxElementType.DOC_METHOD_OR_FIELD_REF)
@@ -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><p><a href="psi_element://java.util.Collections#EMPTY_LIST">The default empty collection</a></p><p><a href="psi_element://java.util.Collection#add(java.lang.Object)">Test for context resolution</a></p></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.base/"><code><span style="">java.base/</span></code></a></p><p><a href="psi_element://java.lang.String"><code><span style="color:#0000ff;">String</span></code></a></p><p><a href="psi_element://java.lang.String#toString()"><code><span style="color:#0000ff;">String</span><span style="">.</span><span style="color:#0000ff;">toString</span><span style="">()</span></code></a></p><p><a href="psi_element://java.util.HashMap"><code><span style="color:#0000ff;">java.util.HashMap</span></code></a></p><p><a href="psi_element://java.util.Collections#EMPTY_LIST">The default empty collection</a></p><p><a href="psi_element://java.util.Collection#add(java.lang.Object)">Test for context resolution</a></p></div><table class='sections'><p></table>
@@ -1,8 +1,12 @@
/// [java.base/]
///
/// [java.base/java.lang.String]
///
/// [java.base/java.lang.String#toString()]
///
/// [java.util.HashMap]
///
/// [The default empty collection][java.util.Collections#EMPTY_LIST]
///
///
/// [Test for context resolution][java.util.Collection#add(Object)]
class MarkdownCodeBlock {}
@@ -29,29 +29,29 @@ PsiJavaFile:LinkTag9Markdown.java
PsiDocToken:DOC_COMMENT_DATA('text for the link')
PsiDocToken:DOC_RBRACKET(']')
PsiDocToken:DOC_LBRACKET('[')
PsiElement(DOC_TAG_VALUE_ELEMENT)
PsiJavaModuleReference
PsiDocToken:DOC_COMMENT_DATA('java.base')
PsiDocToken:DOC_TAG_VALUE_SLASH('/')
PsiElement(DOC_FRAGMENT_REF)
PsiElement(DOC_TAG_VALUE_ELEMENT)
PsiJavaModuleReference
PsiDocToken:DOC_COMMENT_DATA('java.base')
PsiDocToken:DOC_TAG_VALUE_SLASH('/')
PsiElement(DOC_REFERENCE_HOLDER)
PsiJavaCodeReferenceElement:java.lang.String
PsiJavaCodeReferenceElement:java.lang
PsiJavaCodeReferenceElement:java
PsiIdentifier:java('java')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiIdentifier:lang('lang')
PsiElement(DOC_REFERENCE_HOLDER)
PsiJavaCodeReferenceElement:java.lang.String
PsiJavaCodeReferenceElement:java.lang
PsiJavaCodeReferenceElement:java
PsiIdentifier:java('java')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiIdentifier:String('String')
PsiIdentifier:lang('lang')
PsiReferenceParameterList
<empty list>
PsiDocToken:DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN('##')
PsiElement(DOC_FRAGMENT_NAME)
PsiDocToken:DOC_COMMENT_DATA('fragment-name')
PsiJavaToken:DOT('.')
PsiIdentifier:String('String')
PsiReferenceParameterList
<empty list>
PsiDocToken:DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN('##')
PsiElement(DOC_FRAGMENT_NAME)
PsiDocToken:DOC_COMMENT_DATA('fragment-name')
PsiDocToken:DOC_RBRACKET(']')
PsiWhiteSpace('\n')
PsiModifierList:public
@@ -29,19 +29,15 @@ java.FILE
DOC_COMMENT_DATA
DOC_RBRACKET
DOC_LBRACKET
DOC_TAG_VALUE_ELEMENT
MODULE_REFERENCE
DOC_COMMENT_DATA
DOC_TAG_VALUE_SLASH
DOC_FRAGMENT_REF
DOC_TAG_VALUE_ELEMENT
MODULE_REFERENCE
DOC_COMMENT_DATA
DOC_TAG_VALUE_SLASH
DOC_REFERENCE_HOLDER
DOC_REFERENCE_HOLDER
JAVA_CODE_REFERENCE
JAVA_CODE_REFERENCE
JAVA_CODE_REFERENCE
JAVA_CODE_REFERENCE
IDENTIFIER
REFERENCE_PARAMETER_LIST
<empty list>
DOT
IDENTIFIER
REFERENCE_PARAMETER_LIST
<empty list>
@@ -49,9 +45,13 @@ java.FILE
IDENTIFIER
REFERENCE_PARAMETER_LIST
<empty list>
DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN
DOC_FRAGMENT_NAME
DOC_COMMENT_DATA
DOT
IDENTIFIER
REFERENCE_PARAMETER_LIST
<empty list>
DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN
DOC_FRAGMENT_NAME
DOC_COMMENT_DATA
DOC_RBRACKET
WHITE_SPACE
MODIFIER_LIST
@@ -1,2 +1,2 @@
/// [link to module][java.base/]
/// [link to module][java.base/String#toString()]
class C {}
@@ -11,11 +11,22 @@ PsiJavaFile:ReferenceLinkMarkdown13.java
PsiDocToken:DOC_COMMENT_DATA('link to module')
PsiDocToken:DOC_RBRACKET(']')
PsiDocToken:DOC_LBRACKET('[')
PsiElement(DOC_TAG_VALUE_ELEMENT)
PsiJavaModuleReference
PsiDocToken:DOC_COMMENT_DATA('java.base')
PsiDocToken:DOC_TAG_VALUE_SLASH('/')
PsiElement(DOC_METHOD_OR_FIELD_REF)
PsiElement(DOC_REFERENCE_HOLDER)
PsiJavaCodeReferenceElement:String
PsiIdentifier:String('String')
PsiReferenceParameterList
<empty list>
PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#')
PsiDocToken:DOC_TAG_VALUE_TOKEN('toString')
PsiDocToken:DOC_LPAREN('(')
PsiElement(DOC_TAG_VALUE_ELEMENT)
PsiJavaModuleReference
PsiDocToken:DOC_COMMENT_DATA('java.base')
PsiDocToken:DOC_TAG_VALUE_SLASH('/')
<empty list>
PsiDocToken:DOC_RPAREN(')')
PsiDocToken:DOC_RBRACKET(']')
PsiWhiteSpace('\n')
PsiModifierList:
@@ -11,11 +11,22 @@ java.FILE
DOC_COMMENT_DATA
DOC_RBRACKET
DOC_LBRACKET
DOC_TAG_VALUE_ELEMENT
MODULE_REFERENCE
DOC_COMMENT_DATA
DOC_TAG_VALUE_SLASH
DOC_METHOD_OR_FIELD_REF
DOC_REFERENCE_HOLDER
JAVA_CODE_REFERENCE
IDENTIFIER
REFERENCE_PARAMETER_LIST
<empty list>
DOC_TAG_VALUE_SHARP_TOKEN
DOC_TAG_VALUE_TOKEN
DOC_LPAREN
DOC_TAG_VALUE_ELEMENT
MODULE_REFERENCE
DOC_COMMENT_DATA
DOC_TAG_VALUE_SLASH
<empty list>
DOC_RPAREN
DOC_RBRACKET
WHITE_SPACE
MODIFIER_LIST
@@ -1174,7 +1174,7 @@ quickfix.family.avoid.mutation.using.stream.api=Avoid mutation using Stream API
quickfix.family.change.javadoc.to=Change to \u2026
quickfix.family.find.cause=Find cause
quickfix.family.remove.javadoc.tag=Remove tag
quickfix.family.remove.javadoc.reference=Remove tag
quickfix.family.remove.javadoc.reference=Remove reference
quickfix.family.remove.redundant.parameter=Remove redundant parameter
quickfix.family.remove.redundant.parameter.types=Remove redundant parameter types
quickfix.family.replace.cast.type=Replace cast type
@@ -1195,7 +1195,7 @@ quickfix.text.0.may.not.work.before.jdk.11.0.2={0} (may not work before JDK 11.0
quickfix.text.avoid.mutation.using.stream.api.0.operation=Avoid mutation using Stream API ''{0}()'' operation
quickfix.text.remove.javadoc.0=Remove ''@{0}'' tag
quickfix.text.remove.javadoc.0.1=Remove @{0} {1}
quickfix.text.remove.javadoc.reference=Remove ''@{0}'' reference
quickfix.text.remove.javadoc.reference=Remove ''{0}'' reference
quickfix.text.remove.not.null.annotation=Remove non-null annotation
quickfix.text.replace.0.stream.with.1.2=Replace {0}.stream() with {1}.{2}()
quickfix.text.replace.collect.0.with.1.2=Replace ''collect({0}())'' with ''{1}''{2}