From dac1622d18b4659d21af6caf56acad1112b4e9ad Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Sun, 18 Jan 2026 13:48:32 +0530 Subject: [PATCH 1/2] {@code} inline tag is not rendered correctly in Markdown hover This PR fixes an issue where the {@code} inline tag is not rendered correctly in the Markdown hover. It also addresses incorrect handling of the {@literal} tag, which is included as part of the same issue. Fix: https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3672 --- .../javadoc/JavadocContentAccess2.java | 21 ++++++++ .../internal/handlers/HoverHandlerTest.java | 54 +++++++++++++++++++ 2 files changed, 75 insertions(+) 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 3b48e8eee0..6060c2c569 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 @@ -168,6 +168,8 @@ private static void collectTagElements(String content, IJavaElement element, Tag if (e instanceof TagElement t) { if ("@link".equals(t.getTagName()) || "@linkplain".equals(t.getTagName())) { collectLinkedTag(element, t, buf); + } else if ("@code".equals(t.getTagName()) || "@literal".equals(t.getTagName())) { + collectCodeAndLiteralTag(element, t, buf); } else { collectTagElements(content, element, t, buf); } @@ -195,6 +197,25 @@ private static void collectTagElements(String content, IJavaElement element, Tag } } + private static void collectCodeAndLiteralTag(IJavaElement element, TagElement t, StringBuilder buf) { + if (t.fragments().size() > 0) { + try { + if (t.fragments().size() == 1) { + String code = ((TextElement) t.fragments().get(0)).getText(); + if ("@code".equals(t.getTagName())) { + buf.append("`" + code + "`"); + } else if ("@literal".equals(t.getTagName())) { + // escape chars applied for <>*^&\`[] + code = code.replaceAll("([<>*^&\\\\`\\[\\]])", "\\\\$1"); + buf.append(code); + } + } + } catch (Exception e) { + JavaManipulationPlugin.log(e); + } + } + } + private static void collectLinkedTag(IJavaElement element, TagElement t, StringBuilder buf) { @SuppressWarnings("unchecked") List children = t.fragments(); 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 b8c07c5e71..6b52db60a0 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 @@ -1050,4 +1050,58 @@ public class Meh {} actual = ResourceUtils.dos2Unix(actual); assertEquals(expectedJavadoc, actual, "Unexpected hover "); } + + @Test + public void testHoverMarkdownWithCodeTag_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" + + "/// {@code List}\n" + + "public class Markdown{}\n"); + //@formatter:on + ICompilationUnit cu = pack1.createCompilationUnit("Test.java", buf.toString(), false, null); + Hover hover = getHover(cu, 2, 14); + assertNotNull(hover); + assertEquals(2, hover.getContents().getLeft().size()); + StringBuilder expectedJavadoc = new StringBuilder(); + //@formatter:off + expectedJavadoc.append("` List` "); + //@formatter:on + String actual = hover.getContents().getLeft().get(1).getLeft(); + actual = ResourceUtils.dos2Unix(actual); + assertEquals(expectedJavadoc.toString(), actual, "Unexpected hover "); + } + + @Test + public void testHoverMarkdownWithCodeTag_02() 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" + + "/// {@literal List <>*^&`[]}\n" + + "public class Markdown{}\n"); + //@formatter:on + ICompilationUnit cu = pack1.createCompilationUnit("Test.java", buf.toString(), false, null); + Hover hover = getHover(cu, 2, 14); + assertNotNull(hover); + assertEquals(2, hover.getContents().getLeft().size()); + StringBuilder expectedJavadoc = new StringBuilder(); + //@formatter:off + expectedJavadoc.append(" List\\ \\<\\>\\*\\^\\&\\`\\[\\] "); + //@formatter:on + String actual = hover.getContents().getLeft().get(1).getLeft(); + actual = ResourceUtils.dos2Unix(actual); + assertEquals(expectedJavadoc.toString(), actual, "Unexpected hover "); + } } From 2440bbbd79fbc9e25d95a5039e54ed1cc0c30eb4 Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Tue, 20 Jan 2026 13:11:27 +0530 Subject: [PATCH 2/2] incorporated code-review comments --- .../javadoc/JavadocContentAccess2.java | 36 ++++++++--- .../internal/handlers/HoverHandlerTest.java | 64 ++++++++++++++++++- 2 files changed, 88 insertions(+), 12 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 6060c2c569..08454bffe3 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 @@ -166,9 +166,9 @@ private static void collectTagElements(String content, IJavaElement element, Tag while (!queue.isEmpty()) { ASTNode e = queue.pop(); if (e instanceof TagElement t) { - if ("@link".equals(t.getTagName()) || "@linkplain".equals(t.getTagName())) { + if (TagElement.TAG_LINK.equals(t.getTagName()) || TagElement.TAG_LINKPLAIN.equals(t.getTagName())) { collectLinkedTag(element, t, buf); - } else if ("@code".equals(t.getTagName()) || "@literal".equals(t.getTagName())) { + } else if (TagElement.TAG_CODE.equals(t.getTagName()) || TagElement.TAG_LITERAL.equals(t.getTagName())) { collectCodeAndLiteralTag(element, t, buf); } else { collectTagElements(content, element, t, buf); @@ -200,14 +200,30 @@ private static void collectTagElements(String content, IJavaElement element, Tag private static void collectCodeAndLiteralTag(IJavaElement element, TagElement t, StringBuilder buf) { if (t.fragments().size() > 0) { try { - if (t.fragments().size() == 1) { - String code = ((TextElement) t.fragments().get(0)).getText(); - if ("@code".equals(t.getTagName())) { - buf.append("`" + code + "`"); - } else if ("@literal".equals(t.getTagName())) { - // escape chars applied for <>*^&\`[] - code = code.replaceAll("([<>*^&\\\\`\\[\\]])", "\\\\$1"); - buf.append(code); + if (t.fragments().size() > 0) { + if (TagElement.TAG_CODE.equals(t.getTagName())) { + if (t.fragments().size() == 1) { + buf.append("`" + ((TextElement) t.fragments().get(0)).getText().strip() + "`"); + } else { + String code; + for (int i = 0; i < t.fragments().size(); i++) { + code = ((TextElement) t.fragments().get(i)).getText().strip(); + if (i == 0) { + buf.append("`" + code); + } else if (i == (t.fragments().size() - 1)) { + buf.append(code + "`"); + } else { + buf.append(code); + } + } + } + } else if (TagElement.TAG_LITERAL.equals(t.getTagName())) { + String code; + for (int i = 0; i < t.fragments().size(); i++) { + code = ((TextElement) t.fragments().get(i)).getText().strip(); + code = code.replaceAll("([<>*^&\\\\`\\[\\]])", "\\\\$1"); + buf.append(code); + } } } } catch (Exception e) { 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 6b52db60a0..97f6a34df6 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 @@ -1071,7 +1071,7 @@ public void testHoverMarkdownWithCodeTag_01() throws Exception { assertEquals(2, hover.getContents().getLeft().size()); StringBuilder expectedJavadoc = new StringBuilder(); //@formatter:off - expectedJavadoc.append("` List` "); + expectedJavadoc.append("`List` "); //@formatter:on String actual = hover.getContents().getLeft().get(1).getLeft(); actual = ResourceUtils.dos2Unix(actual); @@ -1098,7 +1098,67 @@ public void testHoverMarkdownWithCodeTag_02() throws Exception { assertEquals(2, hover.getContents().getLeft().size()); StringBuilder expectedJavadoc = new StringBuilder(); //@formatter:off - expectedJavadoc.append(" List\\ \\<\\>\\*\\^\\&\\`\\[\\] "); + expectedJavadoc.append("List\\ \\<\\>\\*\\^\\&\\`\\[\\] "); + //@formatter:on + String actual = hover.getContents().getLeft().get(1).getLeft(); + actual = ResourceUtils.dos2Unix(actual); + assertEquals(expectedJavadoc.toString(), actual, "Unexpected hover "); + } + + @Test + public void testHoverMarkdownWithCodeTag_03() 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" + + "/// Here's some code {@code \n" + + "/// List list = List.of(\"Hello World!\");\n" + + "/// } \n" + + "/// that does something.\n" + + "public class Markdown{}\n"); + //@formatter:on + ICompilationUnit cu = pack1.createCompilationUnit("Test.java", buf.toString(), false, null); + Hover hover = getHover(cu, 5, 14); + assertNotNull(hover); + assertEquals(2, hover.getContents().getLeft().size()); + //@formatter:off + StringBuilder expectedJavadoc = new StringBuilder(); + expectedJavadoc.append("Here's some code `List list = List.of(\"Hello World!\");` that does something."); + //@formatter:on + String actual = hover.getContents().getLeft().get(1).getLeft(); + actual = ResourceUtils.dos2Unix(actual); + assertEquals(expectedJavadoc.toString(), actual, "Unexpected hover "); + } + + @Test + public void testHoverMarkdownWithCodeTag_04() 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" + + "/// Here's some code {@literal \n" + + "/// List list = List.of(\"Hello World!\");\n" + + "/// } \n" + + "/// that does something.\n" + + "public class Markdown{}\n"); + //@formatter:on + ICompilationUnit cu = pack1.createCompilationUnit("Test.java", buf.toString(), false, null); + Hover hover = getHover(cu, 5, 14); + assertNotNull(hover); + assertEquals(2, hover.getContents().getLeft().size()); + //@formatter:off + StringBuilder expectedJavadoc = new StringBuilder(); + expectedJavadoc.append("Here's some code List\\ list = List.of(\"Hello World!\");} that does something."); //@formatter:on String actual = hover.getContents().getLeft().get(1).getLeft(); actual = ResourceUtils.dos2Unix(actual);