[javadoc] Add completion for fragment references

#IDEA-376337

GitOrigin-RevId: 887853966d3bea20f27f3d9f70d1c045176dc538
This commit is contained in:
Louis Vignier
2025-09-17 19:25:18 +00:00
committed by intellij-monorepo-bot
parent 01693af6d4
commit f421f79436
5 changed files with 75 additions and 8 deletions
@@ -7,6 +7,8 @@ import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.editorActions.wordSelection.DocTagSelectioner;
import com.intellij.codeInsight.javadoc.JavaDocUtil;
import com.intellij.codeInsight.javadoc.SnippetMarkup;
import com.intellij.codeInsight.javadoc.index.JavaDocFragmentAnchorCacheKt;
import com.intellij.codeInsight.javadoc.index.JavaDocFragmentData;
import com.intellij.codeInsight.lookup.*;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.template.impl.ConstantNode;
@@ -15,6 +17,7 @@ import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.SuppressionUtilCore;
import com.intellij.codeInspection.javaDoc.JavadocDeclarationInspection;
import com.intellij.codeInspection.javaDoc.MissingJavadocInspection;
import com.intellij.icons.AllIcons;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
@@ -35,6 +38,8 @@ import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.filters.TrueFilter;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.impl.source.javadoc.PsiDocFragmentName;
import com.intellij.psi.impl.source.javadoc.PsiDocFragmentRef;
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
import com.intellij.psi.javadoc.*;
@@ -131,6 +136,16 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
JavaCompletionContributor.addAllClasses(parameters, result, new JavaCompletionSession(result));
}
else if (position.getParent() instanceof PsiDocFragmentName docFragmentName) {
final PsiElement parent = docFragmentName.getParent();
final PsiClass classRef = parent instanceof PsiDocFragmentRef ? ((PsiDocFragmentRef)docFragmentName.getParent()).getScope() : null;
final String fqn = classRef == null ? null : classRef.getQualifiedName();
if (fqn != null) {
for (JavaDocFragmentData anchor : JavaDocFragmentAnchorCacheKt.getAnchors(position.getProject(), fqn)) {
result.addElement(LookupElementBuilder.create(anchor.getName()).withIcon(AllIcons.Nodes.Related));
}
}
}
if (tag != null && "author".equals(tag.getName())) {
result.addElement(LookupElementBuilder.create(SystemProperties.getUserName()));
@@ -268,6 +283,12 @@ public final class JavaDocCompletionContributor extends CompletionContributor im
@Override
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (position.getParent() instanceof PsiDocFragmentName) {
super.fillCompletionVariants(parameters, result);
return;
}
if (PsiDocToken.isDocToken(position, JavaDocTokenType.DOC_TAG_VALUE_TOKEN)) {
PsiElement parent = position.getParent();
if (parent instanceof PsiDocTagValue && !(parent instanceof PsiDocParamRef) && !(parent instanceof PsiDocMethodOrFieldRef)) {
@@ -2,11 +2,13 @@
package com.intellij.psi.impl.source.javadoc;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.impl.source.Constants;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.javadoc.PsiDocTagValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PsiDocFragmentRef extends CompositePsiElement implements PsiDocTagValue, Constants {
public PsiDocFragmentRef() {
@@ -22,4 +24,8 @@ public class PsiDocFragmentRef extends CompositePsiElement implements PsiDocTagV
visitor.visitElement(this);
}
}
public @Nullable PsiClass getScope() {
return PsiDocMethodOrFieldRef.getScope(this);
}
}
@@ -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 com.intellij.psi.impl.source.javadoc;
import com.intellij.lang.ASTNode;
@@ -151,9 +151,17 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
return ArrayUtilRt.toStringArray(types);
}
private @Nullable PsiClass getScope(){
if (getFirstChildNode().getElementType() == JavaDocElementType.DOC_REFERENCE_HOLDER) {
final PsiElement firstChildPsi = SourceTreeToPsiMap.treeElementToPsi(getFirstChildNode().getFirstChildNode());
private @Nullable PsiClass getScope() {
return getScope(this);
}
/**
* Returns the PsiClass targeted by the given reference element (e.g. {@code MyClass#…} or {@code MyClass##…}).
*/
public static @Nullable PsiClass getScope(CompositePsiElement ref) {
final TreeElement firstChildNode = ref.getFirstChildNode();
if (firstChildNode != null && firstChildNode.getElementType() == DOC_REFERENCE_HOLDER) {
final PsiElement firstChildPsi = SourceTreeToPsiMap.treeElementToPsi(firstChildNode.getFirstChildNode());
if (firstChildPsi instanceof PsiJavaCodeReferenceElement) {
PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)firstChildPsi;
final PsiElement referencedElement = referenceElement.resolve();
@@ -164,15 +172,16 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
final PsiKeyword keyword = (PsiKeyword)firstChildPsi;
if (keyword.getTokenType().equals(THIS_KEYWORD)) {
return JavaResolveUtil.getContextClass(this);
} else if (keyword.getTokenType().equals(SUPER_KEYWORD)) {
final PsiClass contextClass = JavaResolveUtil.getContextClass(this);
return JavaResolveUtil.getContextClass(ref);
}
else if (keyword.getTokenType().equals(SUPER_KEYWORD)) {
final PsiClass contextClass = JavaResolveUtil.getContextClass(ref);
if (contextClass != null) return contextClass.getSuperClass();
return null;
}
}
}
return JavaResolveUtil.getContextClass(this);
return JavaResolveUtil.getContextClass(ref);
}
public static PsiMethod @NotNull [] findMethods(@Nullable MethodSignature methodSignature,
@@ -0,0 +1,25 @@
/**
* @see ##<caret>
*/
class C {
private int myField;
/**
* <p id=my-id1></p>
*/
public void foo1() {}
/**
* <p id='my-id2'></p>
*/
public void foo2() {}
/**
* <h1 id="my-id3"></h1>
*/
public void foo3() {}
/// <div id="my-id4"></div>
public void foo4() {}
}
@@ -138,6 +138,12 @@ public class JavadocCompletionTest extends LightFixtureCompletionTestCase {
assertTrue(getLookupElementStrings().containsAll(Arrays.asList("foo", "myName")));
}
@NeedsIndex.Full
public void testSee7() {
configureByFile("See7.java");
assertTrue(getLookupElementStrings().containsAll(Arrays.asList("my-id1", "my-id2", "my-id3", "my-id4")));
}
@NeedsIndex.ForStandardLibrary
public void testIDEADEV10620() {
configureByFile("IDEADEV10620.java");