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..aec73d48cb 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,15 @@ 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)) { + // 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); return; } @@ -293,6 +302,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,12 +348,21 @@ private void renderHtmlStartElement(Javadoc.StartElement element) { private void convertEndElement(Javadoc.EndElement element) { String name = element.getName().toLowerCase(); if (inPre && !"pre".equals(name)) { - currentLine.append("").append(element.getName()).append('>'); + if (!"code".equals(name)) { + currentLine.append("").append(element.getName()).append('>'); + } return; } 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 601aa856c7..ed9216f8e2 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,134 @@ 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() {
+ }
+ }
+ """)
+ );
+ }
+
+ @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 {