From b312542cd0bcb8f179c741c23641f5da3aa1d918 Mon Sep 17 00:00:00 2001 From: Marius Barbulescu Date: Tue, 9 Jun 2026 04:50:14 +0200 Subject: [PATCH 1/2] javadoc code tag language is not converted to markdown --- .../lang/JavadocToMarkdownDocComment.java | 33 ++++++- .../lang/JavadocToMarkdownDocCommentTest.java | 97 +++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java b/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java index ccb42e684c..eadcdaf896 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java @@ -237,6 +237,10 @@ private void convertLink(Javadoc.Link link) { private void convertStartElement(Javadoc.StartElement element) { String name = element.getName().toLowerCase(); if (inPre && !"pre".equals(name)) { + if ("code".equals(name)) { + currentLine.append(extractCodeLanguage(element)); + return; + } renderHtmlStartElement(element); return; } @@ -293,6 +297,31 @@ private void convertStartElement(Javadoc.StartElement element) { } } + private String extractCodeLanguage(Javadoc.StartElement element) { + return element.getAttributes().stream() + .filter(attr -> attr instanceof Javadoc.Attribute) + .map(attr -> (Javadoc.Attribute) attr) + .filter(a -> "class".equalsIgnoreCase(a.getName())) + .filter(a -> a.getValue() != null) + .map(a -> renderInline(a.getValue()).trim()) + .map(JavadocToMarkdownConverter::stripAttributeQuotes) + .findFirst() + .orElse(""); + } + + private static String stripAttributeQuotes(String value) { + if (value.length() < 2) { + return value; + } + + char first = value.charAt(0); + char last = value.charAt(value.length() - 1); + if ((first == '\'' && last == '\'') || (first == '"' && last == '"')) { + return value.substring(1, value.length() - 1); + } + return value; + } + private void renderHtmlStartElement(Javadoc.StartElement element) { currentLine.append('<').append(element.getName()); for (Javadoc attr : element.getAttributes()) { @@ -314,7 +343,9 @@ private void renderHtmlStartElement(Javadoc.StartElement element) { private void convertEndElement(Javadoc.EndElement element) { String name = element.getName().toLowerCase(); if (inPre && !"pre".equals(name)) { - currentLine.append("'); + if (!"code".equals(name)) { + currentLine.append("'); + } return; } switch (name) { diff --git a/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java b/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java index 601aa856c7..c150aad88d 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java @@ -496,6 +496,103 @@ public void m() {} ); } + @Test + public void codeTagConverted() { + rewriteRun( + spec -> spec.recipe(new JavadocToMarkdownDocComment()), + java( + """ + class Test { + /** + *
 public class TolkienCharacter {
+                   *   String name;
+                   *   double height;
+                   * }
+                   * 
+ */ + public void withErrorMessageForFields() { + } + } + """, + """ + class Test { + /// ``` public class TolkienCharacter { + /// String name; + /// double height; + /// } + /// ``` + public void withErrorMessageForFields() { + } + } + """) + ); + } + + @Test + public void codeTagWithEmptyLanguageConverted() { + rewriteRun( + spec -> spec.recipe(new JavadocToMarkdownDocComment()), + java( + """ + class Test { + /** + *
 public class TolkienCharacter {
+                   *   String name;
+                   *   double height;
+                   * }
+                   * 
+ */ + public void withErrorMessageForFields() { + } + } + """, + """ + class Test { + /// ``` public class TolkienCharacter { + /// String name; + /// double height; + /// } + /// ``` + public void withErrorMessageForFields() { + } + } + """) + ); + } + + @Test + public void codeTagLanguageNotConverted() { + rewriteRun( + spec -> spec.recipe(new JavadocToMarkdownDocComment()), + java( + """ + class Test { + /** + *
 public class TolkienCharacter {
+                   *   String name;
+                   *   double height;
+                   * }
+                   * 
+ */ + public void withErrorMessageForFields() { + } + } + """, + """ + class Test { + /// ```java public class TolkienCharacter { + /// String name; + /// double height; + /// } + /// ``` + public void withErrorMessageForFields() { + } + } + """) + ); + } + + @Nested class Jep467FlagshipExamples { From c5f4b464009079173e533dae0695a32ce193d1fd Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 9 Jun 2026 16:45:39 +0200 Subject: [PATCH 2/2] Place code fence on its own line so the first code line is not consumed as the info string Co-authored-by: Tim te Beek --- .../lang/JavadocToMarkdownDocComment.java | 12 ++++++ .../lang/JavadocToMarkdownDocCommentTest.java | 37 +++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java b/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java index eadcdaf896..aec73d48cb 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocComment.java @@ -238,7 +238,12 @@ private void convertStartElement(Javadoc.StartElement element) { String name = element.getName().toLowerCase(); if (inPre && !"pre".equals(name)) { if ("code".equals(name)) { + // Place the fence (and optional language) on its own line so the + // code content starts fresh; otherwise the first code line would be + // consumed as the fenced code block's info string. currentLine.append(extractCodeLanguage(element)); + lines.add(currentLine.toString()); + currentLine = new StringBuilder(); return; } renderHtmlStartElement(element); @@ -351,6 +356,13 @@ private void convertEndElement(Javadoc.EndElement element) { switch (name) { case "pre": inPre = false; + // Flush any trailing inline code (e.g. `...);`) so the + // closing fence sits on its own line, as Markdown requires. Only flush + // real content; a whitespace-only line means the fence is already alone. + if (!currentLine.toString().trim().isEmpty()) { + lines.add(currentLine.toString()); + currentLine = new StringBuilder(); + } currentLine.append("```"); break; case "code": diff --git a/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java b/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java index c150aad88d..ed9216f8e2 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/JavadocToMarkdownDocCommentTest.java @@ -516,7 +516,8 @@ public void withErrorMessageForFields() { """, """ class Test { - /// ``` public class TolkienCharacter { + /// ``` + /// public class TolkienCharacter { /// String name; /// double height; /// } @@ -548,7 +549,8 @@ public void withErrorMessageForFields() { """, """ class Test { - /// ``` public class TolkienCharacter { + /// ``` + /// public class TolkienCharacter { /// String name; /// double height; /// } @@ -580,7 +582,8 @@ public void withErrorMessageForFields() { """, """ class Test { - /// ```java public class TolkienCharacter { + /// ```java + /// public class TolkienCharacter { /// String name; /// double height; /// } @@ -592,6 +595,34 @@ public void withErrorMessageForFields() { ); } + @Test + public void codeTagClosedInline() { + rewriteRun( + spec -> spec.recipe(new JavadocToMarkdownDocComment()), + java( + """ + class Test { + /** + *
 // assertion will pass
+                   * assertThat("abc").contains("ab");
+ */ + public void withErrorMessageForFields() { + } + } + """, + """ + class Test { + /// ```java + /// // assertion will pass + /// assertThat("abc").contains("ab"); + /// ``` + public void withErrorMessageForFields() { + } + } + """) + ); + } + @Nested class Jep467FlagshipExamples {