mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-381363 IDEA-375688 javadoc: handle bare method/class references
#Fixed IDEA-381363 IDEA-375688 GitOrigin-RevId: eb2cb1928026665b7626c77b9d730ddc70b769ea
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8c0d077abe
commit
673d615897
+10
@@ -43,6 +43,7 @@ import com.intellij.psi.PsiReferenceExpression;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
|
||||
import com.intellij.psi.javadoc.PsiDocFragmentName;
|
||||
import com.intellij.psi.javadoc.PsiDocReferenceHolder;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -137,6 +138,15 @@ final class JavaNamesHighlightVisitor extends JavaElementVisitor implements High
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDocReferenceHolder(PsiDocReferenceHolder refHolder) {
|
||||
PsiElement resolved = refHolder.getReference().resolve();
|
||||
if (resolved instanceof PsiMethod psiMethod) {
|
||||
myHolder.add(HighlightNamesUtil.highlightMethodName(psiMethod, refHolder, false, myHolder.getColorsScheme()));
|
||||
}
|
||||
|
||||
super.visitDocReferenceHolder(refHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIdentifier(@NotNull PsiIdentifier identifier) {
|
||||
|
||||
+2
@@ -61,6 +61,7 @@ public abstract class AbstractBasicJavadocParsingTest extends AbstractBasicJavaP
|
||||
public void testLinkTag8() { doTest(true); }
|
||||
public void testLinkTag9() { doTest(true); }
|
||||
public void testLinkTag10() { doTest(true); }
|
||||
public void testLinkTag11() { doTest(true); }
|
||||
|
||||
public void testParamTag0() { doTest(true); }
|
||||
public void testParamTag1() { doTest(true); }
|
||||
@@ -238,6 +239,7 @@ public abstract class AbstractBasicJavadocParsingTest extends AbstractBasicJavaP
|
||||
public void testReferenceLinkMarkdown12() { doTest(true); }
|
||||
public void testReferenceLinkMarkdown13() { doTest(true); }
|
||||
public void testReferenceLinkMarkdown14() { doTest(true); }
|
||||
public void testReferenceLinkMarkdown15() { doTest(true); }
|
||||
|
||||
public void testNestedTag0Markdown() { doTest(true); }
|
||||
public void testNestedTag1Markdown() { doTest(true); }
|
||||
|
||||
+2
-1
@@ -103,7 +103,8 @@ public final class JavaSafeDeleteDelegateImpl implements JavaSafeDeleteDelegate
|
||||
usages.add(new SafeDeleteReferenceJavaDeleteUsageInfo(element, parameter, true) {
|
||||
@Override
|
||||
public void deleteElement() throws IncorrectOperationException {
|
||||
final PsiDocMethodOrFieldRef.MyReference javadocMethodReference = (PsiDocMethodOrFieldRef.MyReference)element.getReference();
|
||||
final PsiDocMethodOrFieldRef.MethodOrFieldReference
|
||||
javadocMethodReference = (PsiDocMethodOrFieldRef.MethodOrFieldReference)element.getReference();
|
||||
if (javadocMethodReference != null) {
|
||||
javadocMethodReference.bindToText(newText);
|
||||
}
|
||||
|
||||
+36
-3
@@ -51,6 +51,7 @@ import com.intellij.psi.PsiParameter;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.PsiResolveHelper;
|
||||
import com.intellij.psi.PsiTypeElement;
|
||||
import com.intellij.psi.ResolveResult;
|
||||
import com.intellij.psi.SmartPointerManager;
|
||||
import com.intellij.psi.SmartPsiElementPointer;
|
||||
@@ -60,6 +61,7 @@ import com.intellij.psi.javadoc.JavadocManager;
|
||||
import com.intellij.psi.javadoc.JavadocTagInfo;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.javadoc.PsiDocFragmentRef;
|
||||
import com.intellij.psi.javadoc.PsiDocReferenceHolder;
|
||||
import com.intellij.psi.javadoc.PsiDocTag;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
@@ -155,9 +157,28 @@ public final class JavaDocReferenceInspection extends LocalInspectionTool {
|
||||
|
||||
JavadocManager javadocManager = JavadocManager.getInstance(holder.getProject());
|
||||
comment.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
|
||||
@Override
|
||||
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) {
|
||||
visitRefElement(reference, context, isOnTheFly, holder);
|
||||
public void visitTypeElement(@NotNull PsiTypeElement type) {
|
||||
PsiJavaCodeReferenceElement ref = type.getInnermostComponentReferenceElement();
|
||||
if (ref == null) return;
|
||||
|
||||
visitRefElement(ref, context, isOnTheFly, holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDocReferenceHolder(PsiDocReferenceHolder refHolder) {
|
||||
PsiReference ref = refHolder.getReference();
|
||||
if (ref == null) return;
|
||||
PsiElement resolved = ref.resolve();
|
||||
|
||||
if (resolved == null) {
|
||||
PsiElement element = refHolder.getFirstChild();
|
||||
if (element instanceof PsiJavaCodeReferenceElement) {
|
||||
// Since we don't know whether a method or a class is the intended element, treat it as a potentially missing class reference
|
||||
visitRefElement((PsiJavaCodeReferenceElement)element, context, isOnTheFly, holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,7 +260,7 @@ public final class JavaDocReferenceInspection extends LocalInspectionTool {
|
||||
element, message, fix, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, isOnTheFly));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void visitMarkdownReference(PsiMarkdownReferenceLink referenceLink, PsiElement context, ProblemsHolder holder, boolean isOnTheFly) {
|
||||
PsiElement linkElement = referenceLink.getLinkElement();
|
||||
if (linkElement == null) return;
|
||||
@@ -247,6 +268,12 @@ public final class JavaDocReferenceInspection extends LocalInspectionTool {
|
||||
if (reference == null) return;
|
||||
PsiElement element = reference.resolve();
|
||||
|
||||
if (element == null && linkElement instanceof PsiDocReferenceHolder) {
|
||||
linkElement = linkElement.getFirstChild();
|
||||
reference = linkElement.getReference();
|
||||
element = reference.resolve();
|
||||
}
|
||||
|
||||
String linkText = linkElement.getText();
|
||||
String message = element == null && reference instanceof PsiPolyVariantReference ?
|
||||
getResolveErrorMessage(((PsiPolyVariantReference)reference).multiResolve(false), context, linkText) :
|
||||
@@ -265,6 +292,12 @@ public final class JavaDocReferenceInspection extends LocalInspectionTool {
|
||||
linkElement, reference.getRangeInElement(), message, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, isOnTheFly, fixes.toArray(LocalQuickFix.EMPTY_ARRAY)));
|
||||
}
|
||||
|
||||
private boolean visitDocRefHolder(PsiDocReferenceHolder refHolder, PsiElement context, ProblemsHolder holder, boolean isOnTheFly) {
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void visitRefInDocTag(PsiDocTag tag, JavadocManager manager, PsiElement context, ProblemsHolder holder, boolean isOnTheFly) {
|
||||
PsiDocTagValue value = tag.getValueElement();
|
||||
if (value == null) return;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.javadoc.PsiDocReferenceHolder;
|
||||
import com.intellij.psi.javadoc.PsiDocTag;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
@@ -122,6 +123,10 @@ public abstract class JavaElementVisitor extends PsiElementVisitor {
|
||||
visitComment(comment);
|
||||
}
|
||||
|
||||
public void visitDocReferenceHolder(PsiDocReferenceHolder refHolder) {
|
||||
visitElement(refHolder);
|
||||
}
|
||||
|
||||
public void visitDocTag(@NotNull PsiDocTag tag) {
|
||||
visitElement(tag);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.javadoc;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
/// Represent a reference from the Javadoc.
|
||||
/// Usually a reference to a class through its children ([com.intellij.psi.PsiJavaReference]), or a bare reference to a method/field through itself.
|
||||
///
|
||||
/// @see PsiDocMethodOrFieldRef PsiDocMethodOrFieldRef for other methods/field references
|
||||
public interface PsiDocReferenceHolder extends PsiElement {
|
||||
}
|
||||
@@ -138,24 +138,25 @@ public final class JavaDocUtil {
|
||||
|
||||
int poundIndex = refTextCorrected.indexOf('#');
|
||||
if (poundIndex < 0) {
|
||||
return findClassOrPackage(manager, context, useNavigationElement, facade, refTextCorrected);
|
||||
PsiElement maybeClass = findClassOrPackage(manager, context, useNavigationElement, facade, refTextCorrected);
|
||||
if (maybeClass != null) return maybeClass;
|
||||
}
|
||||
else {
|
||||
int fragmentIndex = refTextCorrected.indexOf("##");
|
||||
String classRef = refTextCorrected.substring(0, poundIndex).trim();
|
||||
PsiClass aClass = classRef.isEmpty()
|
||||
? PsiTreeUtil.getParentOfType(context, PsiClass.class, false)
|
||||
: findClassFromRef(manager, facade, classRef, context);
|
||||
while (aClass != null){
|
||||
PsiElement reference = fragmentIndex >= 0
|
||||
? findFragmentOwner(aClass, useNavigationElement, refTextCorrected, fragmentIndex, manager)
|
||||
: findReference(aClass, context, useNavigationElement, refTextCorrected, poundIndex);
|
||||
if (reference != null) return reference;
|
||||
|
||||
aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
|
||||
}
|
||||
return null;
|
||||
int fragmentIndex = refTextCorrected.indexOf("##");
|
||||
String classRef = refTextCorrected.substring(0, Math.max(poundIndex, 0)).trim();
|
||||
PsiClass aClass = classRef.isEmpty()
|
||||
? PsiTreeUtil.getParentOfType(context, PsiClass.class, false)
|
||||
: findClassFromRef(manager, facade, classRef, context);
|
||||
while (aClass != null) {
|
||||
PsiElement reference = fragmentIndex >= 0
|
||||
? findFragmentOwner(aClass, useNavigationElement, refTextCorrected, fragmentIndex, manager)
|
||||
: findReference(aClass, context, useNavigationElement, refTextCorrected, poundIndex);
|
||||
if (reference != null) return reference;
|
||||
|
||||
aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private static @Nullable PsiElement findClassOrPackage(@NotNull PsiManager manager,
|
||||
|
||||
+170
-138
@@ -18,6 +18,7 @@ import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement;
|
||||
import com.intellij.psi.PsiJavaReference;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
import com.intellij.psi.PsiReference;
|
||||
@@ -27,7 +28,6 @@ import com.intellij.psi.PsiTypeParameter;
|
||||
import com.intellij.psi.PsiTypes;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.filters.ElementFilter;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
|
||||
import com.intellij.psi.impl.source.Constants;
|
||||
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
|
||||
@@ -41,6 +41,7 @@ import com.intellij.psi.impl.source.tree.SharedImplUtil;
|
||||
import com.intellij.psi.impl.source.tree.TreeElement;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.javadoc.PsiDocReferenceHolder;
|
||||
import com.intellij.psi.javadoc.PsiDocTag;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.scope.DelegatingScopeProcessor;
|
||||
@@ -65,6 +66,9 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/// PsiElement that is a _guarantee_ to reference either a **method** or a **field**.
|
||||
///
|
||||
/// @see PsiDocReferenceHolder PsiDocReferenceHolder for other ways to reference methods and fields
|
||||
public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDocTagValue, Constants {
|
||||
private static final List<String> SIGNATURE_TO_REPLACE = Arrays.asList("\\[", "\\]");
|
||||
private static final List<String> SIGNATURE_REPLACEMENT = Arrays.asList("[", "]");
|
||||
@@ -84,75 +88,8 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiReference getReference() {
|
||||
final PsiClass scope = getScope();
|
||||
final PsiElement element = getNameElement();
|
||||
if (scope == null || element == null) return new MyReference(PsiElement.EMPTY_ARRAY);
|
||||
|
||||
PsiReference psiReference = getReferenceInScope(scope, element);
|
||||
if (psiReference != null) return psiReference;
|
||||
|
||||
PsiClass classScope;
|
||||
PsiClass containingClass = scope.getContainingClass();
|
||||
while (containingClass != null) {
|
||||
classScope = containingClass;
|
||||
psiReference = getReferenceInScope(classScope, element);
|
||||
if (psiReference != null) return psiReference;
|
||||
containingClass = classScope.getContainingClass();
|
||||
}
|
||||
return new MyReference(PsiElement.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
private @Nullable PsiReference getReferenceInScope(PsiClass scope, PsiElement element) {
|
||||
final String name = element.getText();
|
||||
final String[] signature = getSignature();
|
||||
|
||||
if (signature == null) {
|
||||
PsiField var = scope.findFieldByName(name, true);
|
||||
if (var != null) {
|
||||
return new MyReference(new PsiElement[]{var});
|
||||
}
|
||||
}
|
||||
|
||||
final MethodSignature methodSignature;
|
||||
if (signature != null) {
|
||||
final List<PsiType> types = new ArrayList<>(signature.length);
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(element.getProject());
|
||||
for (String s : signature) {
|
||||
try {
|
||||
types.add(elementFactory.createTypeFromText(s, element));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
types.add(PsiTypes.nullType());
|
||||
}
|
||||
}
|
||||
methodSignature = MethodSignatureUtil.createMethodSignature(name, types.toArray(PsiType.createArray(types.size())),
|
||||
PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY,
|
||||
name.equals(scope.getName()));
|
||||
}
|
||||
else {
|
||||
methodSignature = null;
|
||||
}
|
||||
|
||||
PsiMethod[] methods = findMethods(methodSignature, scope, name, getAllMethods(scope, this));
|
||||
|
||||
if (methods.length == 0) return null;
|
||||
|
||||
return new MyReference(methods) {
|
||||
|
||||
@Override
|
||||
public void processVariants(@NotNull PsiScopeProcessor processor) {
|
||||
super.processVariants(new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
|
||||
if (element instanceof PsiMethod && name.equals(((PsiMethod)element).getName())) {
|
||||
return super.execute(element, state);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
public @Nullable PsiReference getReference() {
|
||||
return getReference(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,59 +99,39 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
}
|
||||
|
||||
public @Nullable PsiElement getNameElement() {
|
||||
final ASTNode name = findChildByType(DOC_TAG_VALUE_TOKEN);
|
||||
return name != null ? SourceTreeToPsiMap.treeToPsiNotNull(name) : null;
|
||||
return getNameElement(this);
|
||||
}
|
||||
|
||||
public String @Nullable [] getSignature() {
|
||||
PsiElement element = getNameElement();
|
||||
if (element == null) return null;
|
||||
|
||||
element = element.getNextSibling();
|
||||
while (element != null && !(element instanceof PsiDocTagValue)) {
|
||||
element = element.getNextSibling();
|
||||
}
|
||||
if (element == null) return null;
|
||||
|
||||
List<String> types = new ArrayList<>();
|
||||
for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (child.getNode().getElementType() == DOC_TYPE_HOLDER) {
|
||||
// JEP-467: Markdown comments have escaped brackets for array types
|
||||
types.add(Strings.replace(child.getText(), SIGNATURE_TO_REPLACE, SIGNATURE_REPLACEMENT));
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtilRt.toStringArray(types);
|
||||
}
|
||||
|
||||
private @Nullable PsiClass getScope() {
|
||||
return getScope(this);
|
||||
return getSignature(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();
|
||||
if (referencedElement instanceof PsiClass) return (PsiClass)referencedElement;
|
||||
return null;
|
||||
}
|
||||
else if (firstChildPsi instanceof PsiKeyword) {
|
||||
final PsiKeyword keyword = (PsiKeyword)firstChildPsi;
|
||||
|
||||
if (keyword.getTokenType().equals(THIS_KEYWORD)) {
|
||||
return JavaResolveUtil.getContextClass(ref);
|
||||
}
|
||||
else if (keyword.getTokenType().equals(SUPER_KEYWORD)) {
|
||||
final PsiClass contextClass = JavaResolveUtil.getContextClass(ref);
|
||||
if (contextClass != null) return contextClass.getSuperClass();
|
||||
public static @Nullable PsiClass getScope(PsiElement ref) {
|
||||
if (ref instanceof TreeElement) {
|
||||
final TreeElement firstChildNode = ((TreeElement)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();
|
||||
if (referencedElement instanceof PsiClass) return (PsiClass)referencedElement;
|
||||
return null;
|
||||
}
|
||||
else if (firstChildPsi instanceof PsiKeyword) {
|
||||
final PsiKeyword keyword = (PsiKeyword)firstChildPsi;
|
||||
|
||||
if (keyword.getTokenType().equals(THIS_KEYWORD)) {
|
||||
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(ref);
|
||||
@@ -273,10 +190,124 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
return result.toArray(PsiMethod.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
public class MyReference implements PsiJavaReference {
|
||||
/// Get the dog tag value name from the psi tree
|
||||
private static @Nullable PsiElement getNameElement(PsiElement element) {
|
||||
ASTNode name = element.getNode().findChildByType(DOC_TAG_VALUE_TOKEN);
|
||||
if (name != null) {
|
||||
return SourceTreeToPsiMap.treeToPsiNotNull(name);
|
||||
}
|
||||
name = element.getNode().findChildByType(JAVA_CODE_REFERENCE);
|
||||
if (name != null) {
|
||||
return SourceTreeToPsiMap.treeToPsiNotNull(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String @Nullable [] getSignature(PsiElement element) {
|
||||
PsiElement nameElement = getNameElement(element);
|
||||
if (nameElement == null) return null;
|
||||
|
||||
nameElement = nameElement.getNextSibling();
|
||||
while (nameElement != null && !(nameElement instanceof PsiDocTagValue)) {
|
||||
nameElement = nameElement.getNextSibling();
|
||||
}
|
||||
if (nameElement == null) return null;
|
||||
|
||||
List<String> types = new ArrayList<>();
|
||||
for (PsiElement child = nameElement.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (child.getNode().getElementType() == DOC_TYPE_HOLDER) {
|
||||
// JEP-467: Markdown comments have escaped brackets for array types
|
||||
types.add(Strings.replace(child.getText(), SIGNATURE_TO_REPLACE, SIGNATURE_REPLACEMENT));
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtilRt.toStringArray(types);
|
||||
}
|
||||
|
||||
/// Tries to get the reference in an ever larger scope
|
||||
///
|
||||
/// @return The reference to a method or a ref
|
||||
public static @NotNull PsiDocMethodOrFieldRef.MethodOrFieldReference getReference(PsiElement element) {
|
||||
final PsiClass scope = getScope(element);
|
||||
final PsiElement nameElement = getNameElement(element);
|
||||
if (scope == null || nameElement == null) return new MethodOrFieldReference(element, PsiElement.EMPTY_ARRAY);
|
||||
|
||||
MethodOrFieldReference psiReference = getReferenceInScope(element, scope, nameElement);
|
||||
if (psiReference != null) return psiReference;
|
||||
|
||||
PsiClass classScope;
|
||||
PsiClass containingClass = scope.getContainingClass();
|
||||
while (containingClass != null) {
|
||||
classScope = containingClass;
|
||||
psiReference = getReferenceInScope(element, classScope, nameElement);
|
||||
if (psiReference != null) return psiReference;
|
||||
containingClass = classScope.getContainingClass();
|
||||
}
|
||||
return new MethodOrFieldReference(element, PsiElement.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
/// @return The reference if found in the given scope
|
||||
private static @Nullable PsiDocMethodOrFieldRef.MethodOrFieldReference getReferenceInScope(PsiElement referringElement,
|
||||
PsiClass scope,
|
||||
PsiElement element) {
|
||||
final String name = element.getText();
|
||||
final String[] signature = getSignature(referringElement);
|
||||
|
||||
if (signature == null) {
|
||||
PsiField var = scope.findFieldByName(name, true);
|
||||
if (var != null) {
|
||||
return new MethodOrFieldReference(referringElement, new PsiElement[]{var});
|
||||
}
|
||||
}
|
||||
|
||||
final MethodSignature methodSignature;
|
||||
if (signature != null) {
|
||||
final List<PsiType> types = new ArrayList<>(signature.length);
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(element.getProject());
|
||||
for (String s : signature) {
|
||||
try {
|
||||
types.add(elementFactory.createTypeFromText(s, element));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
types.add(PsiTypes.nullType());
|
||||
}
|
||||
}
|
||||
methodSignature = MethodSignatureUtil.createMethodSignature(name, types.toArray(PsiType.createArray(types.size())),
|
||||
PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY,
|
||||
name.equals(scope.getName()));
|
||||
}
|
||||
else {
|
||||
methodSignature = null;
|
||||
}
|
||||
|
||||
PsiMethod[] methods = findMethods(methodSignature, scope, name, getAllMethods(scope, referringElement));
|
||||
|
||||
if (methods.length == 0) return null;
|
||||
|
||||
return new MethodOrFieldReference(referringElement, methods) {
|
||||
|
||||
@Override
|
||||
public void processVariants(@NotNull PsiScopeProcessor processor) {
|
||||
super.processVariants(new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
|
||||
if (element instanceof PsiMethod && name.equals(((PsiMethod)element).getName())) {
|
||||
return super.execute(element, state);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Reference to a Java element made from the Javadoc
|
||||
public static class MethodOrFieldReference implements PsiJavaReference {
|
||||
private final PsiElement myReferringElement;
|
||||
private final PsiElement[] myReferredElements;
|
||||
|
||||
public MyReference(PsiElement[] referredElements) {
|
||||
public MethodOrFieldReference(PsiElement element, PsiElement[] referredElements) {
|
||||
myReferringElement = element;
|
||||
myReferredElements = referredElements;
|
||||
}
|
||||
|
||||
@@ -288,7 +319,7 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
|
||||
@Override
|
||||
public void processVariants(@NotNull PsiScopeProcessor processor) {
|
||||
PsiClass scope = getScope();
|
||||
PsiClass scope = getScope(myReferringElement);
|
||||
while (scope != null) {
|
||||
if (!scope.processDeclarations(new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
@@ -298,7 +329,7 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, ResolveState.initial(), null, PsiDocMethodOrFieldRef.this)) {
|
||||
}, ResolveState.initial(), null, myReferringElement)) {
|
||||
return;
|
||||
}
|
||||
scope = scope.getContainingClass();
|
||||
@@ -330,26 +361,27 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
|
||||
@Override
|
||||
public @NotNull String getCanonicalText() {
|
||||
final PsiElement nameElement = getNameElement();
|
||||
final PsiElement nameElement = getNameElement(myReferringElement);
|
||||
assert nameElement != null;
|
||||
return nameElement.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
|
||||
final PsiElement nameElement = getNameElement();
|
||||
final PsiElement nameElement = getNameElement(myReferringElement);
|
||||
assert nameElement != null;
|
||||
final ASTNode treeElement = SourceTreeToPsiMap.psiToTreeNotNull(nameElement);
|
||||
final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(treeElement);
|
||||
final LeafElement newToken = Factory.createSingleLeafElement(DOC_TAG_VALUE_TOKEN, newElementName, charTableByTree, getManager());
|
||||
final LeafElement newToken =
|
||||
Factory.createSingleLeafElement(DOC_TAG_VALUE_TOKEN, newElementName, charTableByTree, myReferringElement.getManager());
|
||||
((CompositeElement)treeElement.getTreeParent()).replaceChildInternal(SourceTreeToPsiMap.psiToTreeNotNull(nameElement), newToken);
|
||||
return SourceTreeToPsiMap.treeToPsiNotNull(newToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
|
||||
if (isReferenceTo(element)) return PsiDocMethodOrFieldRef.this;
|
||||
final PsiElement nameElement = getNameElement();
|
||||
if (isReferenceTo(element)) return myReferringElement;
|
||||
final PsiElement nameElement = getNameElement(myReferringElement);
|
||||
assert nameElement != null;
|
||||
final String name = nameElement.getText();
|
||||
final String newName;
|
||||
@@ -360,7 +392,7 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
final PsiClass containingClass;
|
||||
if (element instanceof PsiMethod) {
|
||||
method = (PsiMethod)element;
|
||||
hasSignature = getSignature() != null;
|
||||
hasSignature = getSignature(myReferringElement) != null;
|
||||
containingClass = method.getContainingClass();
|
||||
newName = method.getName();
|
||||
} else if (element instanceof PsiField) {
|
||||
@@ -373,25 +405,25 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
throw new IncorrectOperationException();
|
||||
}
|
||||
|
||||
final PsiElement child = getFirstChild();
|
||||
final PsiElement child = myReferringElement.getFirstChild();
|
||||
if (containingClass != null && child != null && child.getNode().getElementType() == JavaDocElementType.DOC_REFERENCE_HOLDER) {
|
||||
PsiElement ref = child.getFirstChild();
|
||||
if (ref instanceof PsiJavaCodeReferenceElement) {
|
||||
((PsiJavaCodeReferenceElement)ref).bindToElement(containingClass);
|
||||
}
|
||||
}
|
||||
else if (containingClass != null && PsiTreeUtil.getParentOfType(PsiDocMethodOrFieldRef.this, PsiClass.class) != containingClass) {
|
||||
else if (containingClass != null && PsiTreeUtil.getParentOfType(myReferringElement, PsiClass.class) != containingClass) {
|
||||
String qName = containingClass.getQualifiedName();
|
||||
if (qName == null) qName = containingClass.getName(); // local class has no qualified name, but has a short name
|
||||
if (qName == null) return PsiDocMethodOrFieldRef.this; // ref can't be fixed
|
||||
if (qName == null) return myReferringElement; // ref can't be fixed
|
||||
PsiDocComment fromText = JavaPsiFacade.getElementFactory(containingClass.getProject())
|
||||
.createDocCommentFromText("/**{@link " + qName + "#" + newName + "}*/");
|
||||
PsiDocMethodOrFieldRef methodOrFieldRefFromText = PsiTreeUtil.findChildOfType(fromText, PsiDocMethodOrFieldRef.class);
|
||||
addAfter(Objects.requireNonNull(methodOrFieldRefFromText).getFirstChild(), null);
|
||||
myReferringElement.addAfter(Objects.requireNonNull(methodOrFieldRefFromText).getFirstChild(), null);
|
||||
}
|
||||
|
||||
if (hasSignature || !name.equals(newName)) {
|
||||
String text = getText();
|
||||
String text = myReferringElement.getText();
|
||||
|
||||
@NonNls StringBuffer newText = new StringBuffer();
|
||||
newText.append("/** @see ");
|
||||
@@ -416,21 +448,21 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
return bindToText(newText);
|
||||
}
|
||||
|
||||
return PsiDocMethodOrFieldRef.this;
|
||||
return myReferringElement;
|
||||
}
|
||||
|
||||
public PsiElement bindToText(StringBuffer newText) {
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(getProject());
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myReferringElement.getProject());
|
||||
PsiComment comment = elementFactory.createCommentFromText(newText.toString(), null);
|
||||
PsiElement tag = PsiTreeUtil.getChildOfType(comment, PsiDocTag.class);
|
||||
PsiElement ref = PsiTreeUtil.getChildOfType(tag, PsiDocMethodOrFieldRef.class);
|
||||
assert ref != null : newText;
|
||||
return replace(ref);
|
||||
return myReferringElement.replace(ref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReferenceTo(@NotNull PsiElement element) {
|
||||
PsiManagerEx manager = getManager();
|
||||
PsiManager manager = myReferringElement.getManager();
|
||||
for (PsiElement myReferredElement : myReferredElements) {
|
||||
if (manager.areElementsEquivalent(element, myReferredElement)) return true;
|
||||
}
|
||||
@@ -439,20 +471,20 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
|
||||
|
||||
@Override
|
||||
public @NotNull TextRange getRangeInElement() {
|
||||
final ASTNode sharp = findChildByType(DOC_TAG_VALUE_SHARP_TOKEN);
|
||||
if (sharp == null) return new TextRange(0, getTextLength());
|
||||
final ASTNode sharp = myReferringElement.getNode().findChildByType(DOC_TAG_VALUE_SHARP_TOKEN);
|
||||
if (sharp == null) return new TextRange(0, myReferringElement.getTextLength());
|
||||
final PsiElement nextSibling = SourceTreeToPsiMap.treeToPsiNotNull(sharp).getNextSibling();
|
||||
if (nextSibling != null) {
|
||||
final int startOffset = nextSibling.getTextRange().getStartOffset() - getTextRange().getStartOffset();
|
||||
int endOffset = nextSibling.getTextRange().getEndOffset() - getTextRange().getStartOffset();
|
||||
final int startOffset = nextSibling.getTextRange().getStartOffset() - myReferringElement.getTextRange().getStartOffset();
|
||||
int endOffset = nextSibling.getTextRange().getEndOffset() - myReferringElement.getTextRange().getStartOffset();
|
||||
return new TextRange(startOffset, endOffset);
|
||||
}
|
||||
return new TextRange(getTextLength(), getTextLength());
|
||||
return new TextRange(myReferringElement.getTextLength(), myReferringElement.getTextLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElement getElement() {
|
||||
return PsiDocMethodOrFieldRef.this;
|
||||
return myReferringElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2026 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.psi.JavaElementVisitor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.PsiDelegateReference;
|
||||
import com.intellij.psi.impl.source.tree.JavaDocElementType;
|
||||
import com.intellij.psi.impl.source.tree.LazyParseablePsiElement;
|
||||
import com.intellij.psi.javadoc.PsiDocReferenceHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
final public class PsiDocReferenceHolderImpl extends LazyParseablePsiElement implements PsiDocReferenceHolder {
|
||||
public PsiDocReferenceHolderImpl(CharSequence text) {
|
||||
super(JavaDocElementType.DOC_REFERENCE_HOLDER, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||
if (visitor instanceof JavaElementVisitor) {
|
||||
((JavaElementVisitor)visitor).visitDocReferenceHolder(this);
|
||||
}
|
||||
super.accept(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiReference getReference() {
|
||||
return new PsiDelegateReference(PsiDocMethodOrFieldRef.getReference(this)) {
|
||||
@Override
|
||||
public @Nullable PsiElement resolve() {
|
||||
PsiReference childRef = PsiDocReferenceHolderImpl.this.getFirstChild().getReference();
|
||||
if (childRef != null && childRef.resolve() != null) {
|
||||
return null;
|
||||
}
|
||||
return super.resolve();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.intellij.psi.impl.source.javadoc.PsiDocFragmentNameImpl;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocFragmentRefImpl;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocReferenceHolderImpl;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocTagImpl;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiInlineDocTagImpl;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiMarkdownCodeBlockImpl;
|
||||
@@ -139,6 +140,11 @@ public interface JavaDocElementType {
|
||||
false,
|
||||
LanguageLevel.JDK_1_3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTNode createNode(CharSequence text) {
|
||||
return new PsiDocReferenceHolderImpl(text);
|
||||
}
|
||||
}
|
||||
|
||||
final class DocTypeHolderElementType extends JavaDocLazyElementType {
|
||||
|
||||
@@ -369,18 +369,31 @@ class JavaDocParser(
|
||||
}
|
||||
moduleMarker?.done(JavaDocSyntaxElementType.DOC_TAG_VALUE_ELEMENT)
|
||||
val refStart = builder.mark()
|
||||
|
||||
var isMethodFieldOrRef = false
|
||||
if (!referenceEnded && getTokenType() !== JavaDocSyntaxTokenType.DOC_SHARP && getTokenType() !== JavaDocSyntaxTokenType.DOC_DOUBLE_SHARP) {
|
||||
builder.remapCurrentToken(JavaDocSyntaxElementType.DOC_REFERENCE_HOLDER)
|
||||
builder.advanceLexer()
|
||||
// Javadoc methods references may not have the # token if it is alone, rely on the existence of () to assign the proper type.
|
||||
// In practice, () is not mandatory, and fields have the same issue but cannot be separated from class names on parsing
|
||||
if (builder.lookAhead(1) == JavaDocSyntaxTokenType.DOC_LPAREN) {
|
||||
isMethodFieldOrRef = true
|
||||
}
|
||||
else {
|
||||
builder.remapCurrentToken(JavaDocSyntaxElementType.DOC_REFERENCE_HOLDER)
|
||||
builder.advanceLexer()
|
||||
}
|
||||
}
|
||||
|
||||
if (!referenceEnded && getTokenType() === JavaDocSyntaxTokenType.DOC_SHARP) {
|
||||
// Existing integration require this token for auto completion
|
||||
builder.remapCurrentToken(JavaDocSyntaxTokenType.DOC_TAG_VALUE_SHARP_TOKEN)
|
||||
if (!referenceEnded && !isMethodFieldOrRef) {
|
||||
isMethodFieldOrRef = getTokenType() === JavaDocSyntaxTokenType.DOC_SHARP
|
||||
if (isMethodFieldOrRef) {
|
||||
// Existing integration require this token for auto completion
|
||||
builder.remapCurrentToken(JavaDocSyntaxTokenType.DOC_TAG_VALUE_SHARP_TOKEN)
|
||||
|
||||
builder.advanceLexer()
|
||||
}
|
||||
}
|
||||
|
||||
if (!referenceEnded && isMethodFieldOrRef) {
|
||||
// method/variable name
|
||||
builder.advanceLexer()
|
||||
builder.remapCurrentToken(JavaDocSyntaxTokenType.DOC_TAG_VALUE_TOKEN)
|
||||
|
||||
// A method only has parenthesis, comment data which may be the type, the optional argument name and commas
|
||||
@@ -403,7 +416,7 @@ class JavaDocParser(
|
||||
else if (type !== JavaDocSyntaxTokenType.DOC_COMMA) {
|
||||
break
|
||||
} else {
|
||||
dataSinceComma = false;
|
||||
dataSinceComma = false
|
||||
}
|
||||
builder.advanceLexer()
|
||||
}
|
||||
@@ -627,6 +640,10 @@ class JavaDocParser(
|
||||
attribute.done(JavaDocSyntaxElementType.DOC_SNIPPET_ATTRIBUTE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the reference inside a tag (like `@link` and `@see`)
|
||||
* @param allowBareFieldReference Whether bare references are **always** considered method/field refs
|
||||
*/
|
||||
private fun parseSeeTagValue(allowBareFieldReference: Boolean) {
|
||||
val moduleMarker = parseModuleRef(builder.mark())
|
||||
|
||||
@@ -648,7 +665,9 @@ class JavaDocParser(
|
||||
else if (getTokenType() === JavaDocSyntaxTokenType.DOC_TAG_VALUE_DOUBLE_SHARP_TOKEN) {
|
||||
parseFragmentRef(refStart)
|
||||
}
|
||||
else if (allowBareFieldReference) {
|
||||
// Javadoc methods references may not have the # token if it is alone, rely on the existence of () to assign the proper type.
|
||||
// In practice, () is not mandatory, and fields have the same issue but cannot be separated from class names on parsing
|
||||
else if (allowBareFieldReference || getTokenType() == JavaDocSyntaxTokenType.DOC_TAG_VALUE_LPAREN) {
|
||||
refStart.rollbackTo()
|
||||
builder.remapCurrentToken(JavaDocSyntaxTokenType.DOC_TAG_VALUE_TOKEN)
|
||||
parseMethodRef(builder.mark())
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/// [java.lang.String]
|
||||
/// [foo]
|
||||
/// {@link foo}
|
||||
/// [<error descr="Cannot resolve symbol 'bar'">bar</error>]
|
||||
/// {@link <error descr="Cannot resolve symbol 'bar'">bar</error>}
|
||||
class Test {
|
||||
public void foo(Object bar) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +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;">Main</span></pre></div><div class='content'><p><a href="psi_element://java.lang.Object#equals(java.lang.Object)"><code><span style="color:#0000ff;">equals</span></code></a>
|
||||
<a href="psi_element://java.lang.Object#equals(java.lang.Object)"><code><span style="color:#0000ff;">equals</span><span style="">(Object)</span></code></a>
|
||||
<a href="psi_element://java.lang.Object#equals(java.lang.Object)"><code><span style="color:#0000ff;">equals</span></code></a>
|
||||
<a href="psi_element://java.lang.Object#equals(java.lang.Object)"><code><span style="color:#0000ff;">equals</span><span style="">(Object)</span></code></a></p></div><table class='sections'><p></table>
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
/// [equals]
|
||||
/// [equals(Object)]
|
||||
/// {@link equals}
|
||||
/// {@link equals(Object)}
|
||||
class Ma<caret>in {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* {@link equals}
|
||||
* {@link equals(Object)}
|
||||
*/
|
||||
public class C {}
|
||||
@@ -0,0 +1,56 @@
|
||||
PsiJavaFile:LinkTag11.java
|
||||
PsiImportList
|
||||
<empty list>
|
||||
PsiClass:C
|
||||
PsiDocComment
|
||||
PsiDocToken:DOC_COMMENT_START('/**')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('*')
|
||||
PsiDocToken:DOC_COMMENT_DATA(' ')
|
||||
PsiInlineDocTag:@link
|
||||
PsiDocToken:DOC_INLINE_TAG_START('{')
|
||||
PsiDocToken:DOC_TAG_NAME('@link')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiJavaCodeReferenceElement:equals
|
||||
PsiIdentifier:equals('equals')
|
||||
PsiReferenceParameterList
|
||||
<empty list>
|
||||
PsiDocToken:DOC_INLINE_TAG_END('}')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('*')
|
||||
PsiDocToken:DOC_COMMENT_DATA(' ')
|
||||
PsiInlineDocTag:@link
|
||||
PsiDocToken:DOC_INLINE_TAG_START('{')
|
||||
PsiDocToken:DOC_TAG_NAME('@link')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_METHOD_OR_FIELD_REF)
|
||||
PsiDocToken:DOC_TAG_VALUE_TOKEN('equals')
|
||||
PsiDocToken:DOC_TAG_VALUE_LPAREN('(')
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_TYPE_HOLDER)
|
||||
PsiTypeElement:Object
|
||||
PsiJavaCodeReferenceElement:Object
|
||||
PsiIdentifier:Object('Object')
|
||||
PsiReferenceParameterList
|
||||
<empty list>
|
||||
PsiDocToken:DOC_TAG_VALUE_RPAREN(')')
|
||||
PsiDocToken:DOC_INLINE_TAG_END('}')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiDocToken:DOC_COMMENT_END('*/')
|
||||
PsiWhiteSpace('\n')
|
||||
PsiModifierList:public
|
||||
PsiKeyword:public('public')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiKeyword:class('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiIdentifier:C('C')
|
||||
PsiTypeParameterList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiJavaToken:LBRACE('{')
|
||||
PsiJavaToken:RBRACE('}')
|
||||
@@ -0,0 +1,56 @@
|
||||
java.FILE
|
||||
IMPORT_LIST
|
||||
<empty list>
|
||||
CLASS
|
||||
DOC_COMMENT
|
||||
DOC_COMMENT_START
|
||||
WHITE_SPACE
|
||||
DOC_COMMENT_LEADING_ASTERISKS
|
||||
DOC_COMMENT_DATA
|
||||
DOC_INLINE_TAG
|
||||
DOC_INLINE_TAG_START
|
||||
DOC_TAG_NAME
|
||||
WHITE_SPACE
|
||||
DOC_REFERENCE_HOLDER
|
||||
JAVA_CODE_REFERENCE
|
||||
IDENTIFIER
|
||||
REFERENCE_PARAMETER_LIST
|
||||
<empty list>
|
||||
DOC_INLINE_TAG_END
|
||||
WHITE_SPACE
|
||||
DOC_COMMENT_LEADING_ASTERISKS
|
||||
DOC_COMMENT_DATA
|
||||
DOC_INLINE_TAG
|
||||
DOC_INLINE_TAG_START
|
||||
DOC_TAG_NAME
|
||||
WHITE_SPACE
|
||||
DOC_METHOD_OR_FIELD_REF
|
||||
DOC_TAG_VALUE_TOKEN
|
||||
DOC_TAG_VALUE_LPAREN
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TYPE_HOLDER
|
||||
TYPE
|
||||
JAVA_CODE_REFERENCE
|
||||
IDENTIFIER
|
||||
REFERENCE_PARAMETER_LIST
|
||||
<empty list>
|
||||
DOC_TAG_VALUE_RPAREN
|
||||
DOC_INLINE_TAG_END
|
||||
WHITE_SPACE
|
||||
DOC_COMMENT_END
|
||||
WHITE_SPACE
|
||||
MODIFIER_LIST
|
||||
PUBLIC_KEYWORD
|
||||
WHITE_SPACE
|
||||
CLASS_KEYWORD
|
||||
WHITE_SPACE
|
||||
IDENTIFIER
|
||||
TYPE_PARAMETER_LIST
|
||||
<empty list>
|
||||
EXTENDS_LIST
|
||||
<empty list>
|
||||
IMPLEMENTS_LIST
|
||||
<empty list>
|
||||
WHITE_SPACE
|
||||
LBRACE
|
||||
RBRACE
|
||||
@@ -0,0 +1,3 @@
|
||||
/// [equals] method Link without the hash char and params
|
||||
/// [equals(Object)] method Link without the hash char
|
||||
class C {}
|
||||
@@ -0,0 +1,49 @@
|
||||
PsiJavaFile:ReferenceLinkMarkdown15.java
|
||||
PsiImportList
|
||||
<empty list>
|
||||
PsiClass:C
|
||||
PsiDocComment
|
||||
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiReferenceLink:
|
||||
PsiDocToken:DOC_LBRACKET('[')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiJavaCodeReferenceElement:equals
|
||||
PsiIdentifier:equals('equals')
|
||||
PsiReferenceParameterList
|
||||
<empty list>
|
||||
PsiDocToken:DOC_RBRACKET(']')
|
||||
PsiDocToken:DOC_COMMENT_DATA(' method Link without the hash char and params')
|
||||
PsiWhiteSpace('\n')
|
||||
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
|
||||
PsiDocToken:DOC_COMMENT_DATA(' ')
|
||||
PsiReferenceLink:
|
||||
PsiDocToken:DOC_LBRACKET('[')
|
||||
PsiElement(DOC_METHOD_OR_FIELD_REF)
|
||||
PsiDocToken:DOC_TAG_VALUE_TOKEN('equals')
|
||||
PsiDocToken:DOC_LPAREN('(')
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_TYPE_HOLDER)
|
||||
PsiTypeElement:Object
|
||||
PsiJavaCodeReferenceElement:Object
|
||||
PsiIdentifier:Object('Object')
|
||||
PsiReferenceParameterList
|
||||
<empty list>
|
||||
PsiDocToken:DOC_RPAREN(')')
|
||||
PsiDocToken:DOC_RBRACKET(']')
|
||||
PsiDocToken:DOC_COMMENT_DATA(' method Link without the hash char')
|
||||
PsiWhiteSpace('\n')
|
||||
PsiModifierList:
|
||||
<empty list>
|
||||
PsiKeyword:class('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiIdentifier:C('C')
|
||||
PsiTypeParameterList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiReferenceList
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiJavaToken:LBRACE('{')
|
||||
PsiJavaToken:RBRACE('}')
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
java.FILE
|
||||
IMPORT_LIST
|
||||
<empty list>
|
||||
CLASS
|
||||
DOC_MARKDOWN_COMMENT
|
||||
DOC_COMMENT_LEADING_ASTERISKS
|
||||
WHITE_SPACE
|
||||
DOC_REFERENCE_LINK
|
||||
DOC_LBRACKET
|
||||
DOC_REFERENCE_HOLDER
|
||||
JAVA_CODE_REFERENCE
|
||||
IDENTIFIER
|
||||
REFERENCE_PARAMETER_LIST
|
||||
<empty list>
|
||||
DOC_RBRACKET
|
||||
DOC_COMMENT_DATA
|
||||
WHITE_SPACE
|
||||
DOC_COMMENT_LEADING_ASTERISKS
|
||||
DOC_COMMENT_DATA
|
||||
DOC_REFERENCE_LINK
|
||||
DOC_LBRACKET
|
||||
DOC_METHOD_OR_FIELD_REF
|
||||
DOC_TAG_VALUE_TOKEN
|
||||
DOC_LPAREN
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TYPE_HOLDER
|
||||
TYPE
|
||||
JAVA_CODE_REFERENCE
|
||||
IDENTIFIER
|
||||
REFERENCE_PARAMETER_LIST
|
||||
<empty list>
|
||||
DOC_RPAREN
|
||||
DOC_RBRACKET
|
||||
DOC_COMMENT_DATA
|
||||
WHITE_SPACE
|
||||
MODIFIER_LIST
|
||||
<empty list>
|
||||
CLASS_KEYWORD
|
||||
WHITE_SPACE
|
||||
IDENTIFIER
|
||||
TYPE_PARAMETER_LIST
|
||||
<empty list>
|
||||
EXTENDS_LIST
|
||||
<empty list>
|
||||
IMPLEMENTS_LIST
|
||||
<empty list>
|
||||
WHITE_SPACE
|
||||
LBRACE
|
||||
RBRACE
|
||||
@@ -10,21 +10,18 @@ PsiJavaFile:SeeTag9.java
|
||||
PsiDocTag:@see
|
||||
PsiDocToken:DOC_TAG_NAME('@see')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiJavaCodeReferenceElement:equals
|
||||
PsiIdentifier:equals('equals')
|
||||
PsiReferenceParameterList
|
||||
<empty list>
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_METHOD_OR_FIELD_REF)
|
||||
PsiDocToken:DOC_TAG_VALUE_TOKEN('equals')
|
||||
PsiDocToken:DOC_TAG_VALUE_LPAREN('(')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiKeyword:long('long')
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiDocToken:DOC_TAG_VALUE_COMMA(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiKeyword:long('long')
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_TYPE_HOLDER)
|
||||
PsiTypeElement:long
|
||||
PsiKeyword:long('long')
|
||||
PsiDocToken:DOC_TAG_VALUE_COMMA(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_TYPE_HOLDER)
|
||||
PsiTypeElement:long
|
||||
PsiKeyword:long('long')
|
||||
PsiDocToken:DOC_TAG_VALUE_RPAREN(')')
|
||||
PsiWhiteSpace('\n ')
|
||||
PsiDocToken:DOC_COMMENT_END('*/')
|
||||
|
||||
@@ -10,21 +10,18 @@ PsiJavaFile:SeeTag9Markdown.java
|
||||
PsiDocTag:@see
|
||||
PsiDocToken:DOC_TAG_NAME('@see')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiJavaCodeReferenceElement:equals
|
||||
PsiIdentifier:equals('equals')
|
||||
PsiReferenceParameterList
|
||||
<empty list>
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_METHOD_OR_FIELD_REF)
|
||||
PsiDocToken:DOC_TAG_VALUE_TOKEN('equals')
|
||||
PsiDocToken:DOC_TAG_VALUE_LPAREN('(')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiKeyword:long('long')
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiDocToken:DOC_TAG_VALUE_COMMA(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_REFERENCE_HOLDER)
|
||||
PsiKeyword:long('long')
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_TAG_VALUE_ELEMENT)
|
||||
PsiElement(DOC_TYPE_HOLDER)
|
||||
PsiTypeElement:long
|
||||
PsiKeyword:long('long')
|
||||
PsiDocToken:DOC_TAG_VALUE_COMMA(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(DOC_TYPE_HOLDER)
|
||||
PsiTypeElement:long
|
||||
PsiKeyword:long('long')
|
||||
PsiDocToken:DOC_TAG_VALUE_RPAREN(')')
|
||||
PsiWhiteSpace('\n')
|
||||
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('///')
|
||||
|
||||
@@ -10,21 +10,18 @@ java.FILE
|
||||
DOC_TAG
|
||||
DOC_TAG_NAME
|
||||
WHITE_SPACE
|
||||
DOC_REFERENCE_HOLDER
|
||||
JAVA_CODE_REFERENCE
|
||||
IDENTIFIER
|
||||
REFERENCE_PARAMETER_LIST
|
||||
<empty list>
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_METHOD_OR_FIELD_REF
|
||||
DOC_TAG_VALUE_TOKEN
|
||||
DOC_TAG_VALUE_LPAREN
|
||||
DOC_REFERENCE_HOLDER
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TAG_VALUE_COMMA
|
||||
WHITE_SPACE
|
||||
DOC_REFERENCE_HOLDER
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TYPE_HOLDER
|
||||
TYPE
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_COMMA
|
||||
WHITE_SPACE
|
||||
DOC_TYPE_HOLDER
|
||||
TYPE
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_RPAREN
|
||||
WHITE_SPACE
|
||||
DOC_COMMENT_LEADING_ASTERISKS
|
||||
|
||||
@@ -10,21 +10,18 @@ java.FILE
|
||||
DOC_TAG
|
||||
DOC_TAG_NAME
|
||||
WHITE_SPACE
|
||||
DOC_REFERENCE_HOLDER
|
||||
JAVA_CODE_REFERENCE
|
||||
IDENTIFIER
|
||||
REFERENCE_PARAMETER_LIST
|
||||
<empty list>
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_METHOD_OR_FIELD_REF
|
||||
DOC_TAG_VALUE_TOKEN
|
||||
DOC_TAG_VALUE_LPAREN
|
||||
DOC_REFERENCE_HOLDER
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TAG_VALUE_COMMA
|
||||
WHITE_SPACE
|
||||
DOC_REFERENCE_HOLDER
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TAG_VALUE_ELEMENT
|
||||
DOC_TYPE_HOLDER
|
||||
TYPE
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_COMMA
|
||||
WHITE_SPACE
|
||||
DOC_TYPE_HOLDER
|
||||
TYPE
|
||||
LONG_KEYWORD
|
||||
DOC_TAG_VALUE_RPAREN
|
||||
WHITE_SPACE
|
||||
DOC_COMMENT_END
|
||||
|
||||
+1
@@ -80,6 +80,7 @@ public class JavadocDeclarationHighlightingTest extends LightDaemonAnalyzerTestC
|
||||
public void testLink0() { doTest(); }
|
||||
public void testLink1() { doTest(); }
|
||||
public void testLink2() { doTest(); }
|
||||
public void testLink3() { doTest(); }
|
||||
public void testLinkFromInnerClassToSelfMethod() { doTest(); }
|
||||
public void testValueBadReference() { doTest(); }
|
||||
public void testValueGoodReference() { doTest(); }
|
||||
|
||||
+1
@@ -253,6 +253,7 @@ public class JavaDocInfoGeneratorTest extends JavaCodeInsightTestCase {
|
||||
public void testPackageInfoMarkdown() { doTestPackageInfo(); }
|
||||
public void testListInTags() { doTestMethod(); }
|
||||
public void testParagraphInTagsMarkdown() { doTestMethod(); }
|
||||
public void testBareMethodReferences() { doTestClass(); }
|
||||
public void testPreTagInJavadocTag() { doTestClass();}
|
||||
public void testLinkTagMalformed() { doTestClass(); }
|
||||
public void testCursedCodeBlock() { doTestClass(); }
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInsight.navigation;
|
||||
|
||||
import com.intellij.refactoring.suggested.LightJavaCodeInsightFixtureTestCaseWithUtils;
|
||||
|
||||
public final class JavadocReferenceNavigationTest extends LightJavaCodeInsightFixtureTestCaseWithUtils {
|
||||
private static final String BASIC_FUNCTION = "void function();";
|
||||
private static final String BASIC_FUNCTION_ARGS = "void functionArgs(int beep, int boop);";
|
||||
|
||||
public void testTags() {
|
||||
check("/// @see function<caret>", BASIC_FUNCTION);
|
||||
check("/// @see #functionArgs<caret>(int, int)", BASIC_FUNCTION_ARGS);
|
||||
|
||||
check("/// {@link function<caret>()}", BASIC_FUNCTION);
|
||||
check("/// {@link functionArgs<caret>(int, int)}", BASIC_FUNCTION_ARGS);
|
||||
}
|
||||
|
||||
public void testMarkdownReferences() {
|
||||
check("/// [function<caret>()]", BASIC_FUNCTION);
|
||||
check("/// [functionArgs<caret>(int,int)]", BASIC_FUNCTION_ARGS);
|
||||
check("/// [function<caret>()]", BASIC_FUNCTION);
|
||||
check("/// [functionArgs<caret>(int,int)]", BASIC_FUNCTION_ARGS);
|
||||
|
||||
// Just ignore how the module is not taken into account here
|
||||
check("/// [java.base/SeeTags#function<caret>()]", BASIC_FUNCTION);
|
||||
}
|
||||
|
||||
/// Verify if the navigation brings you to the expected line
|
||||
///
|
||||
/// The caller is expected to bring a comment containing a `<caret>` tag
|
||||
private void check(String comment, String expectedLine) {
|
||||
myFixture.configureByText("SeeTags.java", """
|
||||
%s
|
||||
interface SeeTags {
|
||||
%s
|
||||
%s
|
||||
}""".formatted(comment, BASIC_FUNCTION, BASIC_FUNCTION_ARGS));
|
||||
navigateAndCheckLine(expectedLine);
|
||||
}
|
||||
|
||||
private void navigateAndCheckLine(String expectedLine) {
|
||||
myFixture.performEditorAction("GotoDeclaration");
|
||||
final var selectionModel = myFixture.getEditor().getSelectionModel();
|
||||
selectionModel.selectLineAtCaret();
|
||||
assertEquals(expectedLine, selectionModel.getSelectedText().trim());
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -19,9 +19,9 @@ import com.intellij.psi.PsiElementFinder;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiJavaReference;
|
||||
import com.intellij.psi.PsiPackage;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.PsiPolyVariantReference;import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||
import com.intellij.testFramework.IndexingTestUtil;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;import com.intellij.testFramework.IndexingTestUtil;
|
||||
import com.intellij.testFramework.JavaResolveTestCase;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -68,6 +68,9 @@ public class ResolveClassInModulesWithDependenciesTest extends JavaResolveTestCa
|
||||
configureDependency();
|
||||
|
||||
PsiReference ref = configure();
|
||||
if (ref instanceof PsiMultiReference polyRef) {
|
||||
ref = polyRef.getReferences()[0];
|
||||
}
|
||||
PsiElement target = ((PsiJavaReference)ref).advancedResolve(true).getElement();
|
||||
assertNotNull(target);
|
||||
assertNotSame(unrelated, ModuleUtilCore.findModuleForPsiElement(target));
|
||||
|
||||
Reference in New Issue
Block a user