From 9d3ca6a5519050e50f13aedd45af40b8470afe20 Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Fri, 30 Jan 2026 13:45:52 +0530 Subject: [PATCH 1/2] JDT.LS hover shows class name only instead of full method signature On mouse hover, Markdown links can display the full constructor or method signature as the anchor text, which provides more context to users Fix: https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3690 --- .../javadoc/JavadocContentAccess2.java | 36 ++++++++- .../internal/handlers/HoverHandlerTest.java | 75 +++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess2.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess2.java index 1d5607e6e8..9d9790c60a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess2.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess2.java @@ -201,16 +201,44 @@ private static void collectLinkedTag(IJavaElement element, TagElement t, StringB if (t.fragments().size() > 0) { try { String[] res; - String linkTitle; + String linkTitle = null; if (t.fragments().size() == 2) { linkTitle = ((TextElement) t.fragments().get(0)).getText(); res = collectLinkElement((ASTNode) children.get(1)); } else { + res = collectLinkElement((ASTNode) children.get(0)); - if (res[0].isEmpty() && children.get(0).toString().startsWith("#") && res.length > 1) {// member implicitly refers to the current class - linkTitle = res[1]; + if (t.fragments().get(0) instanceof MethodRef methodRef) { + if (methodRef.getName().isSimpleName() && !methodRef.getName().getIdentifier().isEmpty() && methodRef.getQualifier() != null && methodRef.getQualifier().isSimpleName() + && !methodRef.getQualifier().getFullyQualifiedName().isEmpty()) { + linkTitle = methodRef.getQualifier().getFullyQualifiedName() + "." + methodRef.getName().getIdentifier(); + @SuppressWarnings("unchecked") + List params = methodRef.parameters(); + for (int i = 0; i < params.size(); i++) { + if (i == 0) { + linkTitle += "("; + } + linkTitle += params.get(i).toString(); + if (i < params.size() - 1) { + linkTitle += ", "; + } + if (i == params.size() - 1) { + linkTitle += ")"; + } + } + if (params.size() == 0) { + linkTitle += "()"; + } + + } else if (methodRef.getQualifier() == null) { + linkTitle = methodRef.getName().getIdentifier() + "()"; + } } else { - linkTitle = res[0]; + if (res[0].isEmpty() && children.get(0).toString().startsWith("#") && res.length > 1) {// member implicitly refers to the current class + linkTitle = res[1]; + } else { + linkTitle = res[0] + "." + res[1]; + } } } buf.append("[" + linkTitle + "]"); diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java index fbd6a3bd22..773eb0766e 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java @@ -1124,4 +1124,79 @@ public void testHoverInlineLinkTagInMarkdown_02() throws Exception { actual = ResourceUtils.dos2Unix(actual); assertEquals(expectedJavadoc.toString(), actual.stripTrailing(), "Unexpected hover "); } + + enum HoverSignatureCase { + FIELD_REF("Ball.xxx", 3, 9), SELF_METHOD("newMethodBeingLinkedToo()", 5, 9), METHOD_REF("Ball.abc()", 7, 9), CTOR_REF("Ball.Ball", 9, 9), CTOR_X("Ball.Ball(int x)", 11, 9), CTOR_Y("Ball.Ball(String y)", 13, + 9), CTOR_MANY("Ball.Ball(String y, int i, Double x)", 15, 9); + + final String link; + final int line; + final int column; + + HoverSignatureCase(String link, int line, int column) { + this.link = link; + this.line = line; + this.column = column; + } + } + + @Test + public void testJavadocHoverShowFullSignature3690_01() throws Exception { + String name = "java25"; + importProjects("eclipse/" + name); + IProject project = getProject(name); + IJavaProject javaProject = JavaCore.create(project); + IPackageFragmentRoot packageFragmentRoot = javaProject.getPackageFragmentRoot(project.getFolder("src/main/java")); + IPackageFragment pack1 = packageFragmentRoot.createPackageFragment("test", false, null); + StringBuilder buf = new StringBuilder(); + //@formatter:off + buf.append("package test;\n" + + "public class Apple {\n" + + " /// {@link Ball#xxx}\n" + + " void foo(){}\n" + + " /// {@link #newMethodBeingLinkedToo()}\n" + + " void foo1(){}\n" + + " /// {@link Ball#abc()}\n" + + " void voo(){}\n" + + " /// {@link Ball#Ball}\n" + + " void voo1(){}\n" + + " /// {@link Ball#Ball(int x)}\n" + + " void voo2(){}\n" + + " /// {@link Ball#Ball(String y)}\n" + + " void voo3(){}\n" + + " /// {@link Ball#Ball(String y, int i, Double x)}\n" + + " void voo4(){}\n" + + " void newMethodBeingLinkedToo(){}" + + "}\n" + + "class Ball {\n" + + " private String xxx;\n" + + " Ball(int x){}\n" + + " Ball(String y){}\n" + + " void abc(){}\n" + + "}" + ); + //@formatter:on + ICompilationUnit cu = pack1.createCompilationUnit("Markdown.java", buf.toString(), false, null); + for (HoverSignatureCase hovercase : HoverSignatureCase.values()) { + enumLinkTest(cu, hovercase.line, hovercase.column, hovercase.link); + } + } + + void enumLinkTest(ICompilationUnit cu, int line, int column, String link) { + Hover hover = getHover(cu, line, column); + assertNotNull(hover); + assertEquals(3, hover.getContents().getLeft().size()); + + String actual = hover.getContents().getLeft().get(1).getLeft(); + int hashIndex = actual.lastIndexOf('#'); + int endIndex = actual.lastIndexOf(')'); + String lineNbr = actual.substring(hashIndex + 1, endIndex); + String uri = JDTUtils.toURI(cu) + "#" + lineNbr; + //@formatter:off + String expectedJavadoc = "[" + link +"](" + uri +")"; + //@formatter:on + + actual = ResourceUtils.dos2Unix(actual); + assertEquals(expectedJavadoc.toString(), actual.stripTrailing(), "Unexpected hover "); + } } From e464065f153e97f75ce371af3d29a4779a44e8a6 Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Fri, 30 Jan 2026 17:05:34 +0530 Subject: [PATCH 2/2] incorporated code-review comments --- .../internal/handlers/HoverHandlerTest.java | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java index 773eb0766e..de7a90305b 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java @@ -1126,8 +1126,13 @@ public void testHoverInlineLinkTagInMarkdown_02() throws Exception { } enum HoverSignatureCase { - FIELD_REF("Ball.xxx", 3, 9), SELF_METHOD("newMethodBeingLinkedToo()", 5, 9), METHOD_REF("Ball.abc()", 7, 9), CTOR_REF("Ball.Ball", 9, 9), CTOR_X("Ball.Ball(int x)", 11, 9), CTOR_Y("Ball.Ball(String y)", 13, - 9), CTOR_MANY("Ball.Ball(String y, int i, Double x)", 15, 9); + FIELD_REF("Ball.xxx", 3, 9), + SELF_METHOD("newMethodBeingLinkedToo()", 6, 9), + METHOD_REF("Ball.abc()", 9, 9), + CTOR_REF("Ball.Ball", 12, 9), + CTOR_X("Ball.Ball(int x)", 15, 9), + CTOR_Y("Ball.Ball(String y)", 18,9), + CTOR_MANY("Ball.Ball(String y, int i, Double x)", 21, 9); final String link; final int line; @@ -1150,31 +1155,39 @@ public void testJavadocHoverShowFullSignature3690_01() throws Exception { IPackageFragment pack1 = packageFragmentRoot.createPackageFragment("test", false, null); StringBuilder buf = new StringBuilder(); //@formatter:off - buf.append("package test;\n" - + "public class Apple {\n" - + " /// {@link Ball#xxx}\n" - + " void foo(){}\n" - + " /// {@link #newMethodBeingLinkedToo()}\n" - + " void foo1(){}\n" - + " /// {@link Ball#abc()}\n" - + " void voo(){}\n" - + " /// {@link Ball#Ball}\n" - + " void voo1(){}\n" - + " /// {@link Ball#Ball(int x)}\n" - + " void voo2(){}\n" - + " /// {@link Ball#Ball(String y)}\n" - + " void voo3(){}\n" - + " /// {@link Ball#Ball(String y, int i, Double x)}\n" - + " void voo4(){}\n" - + " void newMethodBeingLinkedToo(){}" - + "}\n" - + "class Ball {\n" - + " private String xxx;\n" - + " Ball(int x){}\n" - + " Ball(String y){}\n" - + " void abc(){}\n" - + "}" - ); + buf.append(""" + package test; + public class Apple { + /// {@link Ball#xxx} + void foo(){} + + /// {@link #newMethodBeingLinkedToo()} + void foo1(){} + + /// {@link Ball#abc()} + void voo(){} + + /// {@link Ball#Ball} + void voo1(){} + + /// {@link Ball#Ball(int x)} + void voo2(){} + + /// {@link Ball#Ball(String y)} + void voo3(){} + + /// {@link Ball#Ball(String y, int i, Double x)} + void voo4(){} + + void newMethodBeingLinkedToo(){} + } + class Ball { + private String xxx; + Ball(int x){} + Ball(String y){} + void abc(){} + } + """); //@formatter:on ICompilationUnit cu = pack1.createCompilationUnit("Markdown.java", buf.toString(), false, null); for (HoverSignatureCase hovercase : HoverSignatureCase.values()) {