You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/04/21 22:40:42 UTC

(groovy) 05/05: groovydoc: correct triple slash comment processing for Java

This is an automated email from the ASF dual-hosted git repository.

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 4216746b027de3fafb06fbeb5954ab102591c135
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Apr 22 07:31:05 2026 +1000

    groovydoc: correct triple slash comment processing for Java
---
 .../groovydoc/antlr4/GroovydocJavaVisitor.java     | 71 ++++++++++++++++++----
 1 file changed, 60 insertions(+), 11 deletions(-)

diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
index 9578bdaee4..34b4ef9884 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
@@ -34,6 +34,7 @@ import com.github.javaparser.ast.body.MethodDeclaration;
 import com.github.javaparser.ast.body.Parameter;
 import com.github.javaparser.ast.body.RecordDeclaration;
 import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.comments.JavadocComment;
 import com.github.javaparser.ast.expr.AnnotationExpr;
 import com.github.javaparser.ast.expr.Name;
 import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations;
@@ -129,8 +130,7 @@ public class GroovydocJavaVisitor
         enumConstantDoc.setPublic(true);
         currentClassDoc.addEnumConstant(enumConstantDoc);
         processAnnotations(enumConstantDoc, n);
-        n.getJavadocComment().ifPresent(javadocComment ->
-                enumConstantDoc.setRawCommentText(javadocComment.getContent()));
+        applyJavadocComment(n.getJavadocComment(), enumConstantDoc);
         super.visit(n, arg);
     }
 
@@ -156,10 +156,13 @@ public class GroovydocJavaVisitor
         fieldDoc.setPublic(true);
         processAnnotations(fieldDoc, n);
         currentClassDoc.add(fieldDoc);
-        n.getJavadocComment().ifPresent(javadocComment ->
-                fieldDoc.setRawCommentText(javadocComment.getContent()));
+        applyJavadocComment(n.getJavadocComment(), fieldDoc);
         n.getDefaultValue().ifPresent(defValue -> {
-            fieldDoc.setRawCommentText(fieldDoc.getRawCommentText() + "\n* @default " + defValue);
+            // For Markdown-form comments (no `*` line prefix), the synthesised
+            // @default tag goes on a bare line; traditional /** */ form keeps
+            // the `* ` prefix for visual parity with existing continuation lines.
+            String prefix = fieldDoc.isMarkdown() ? "\n@default " : "\n* @default ";
+            fieldDoc.setRawCommentText(fieldDoc.getRawCommentText() + prefix + defValue);
             fieldDoc.setConstantValueExpression(defValue.toString());
         });
         super.visit(n, arg);
@@ -246,8 +249,7 @@ public class GroovydocJavaVisitor
         processAnnotations(currentClassDoc, n);
         currentClassDoc.setFullPathName(withSlashes(packagePath + FS + name));
         classDocs.put(currentClassDoc.getFullPathName(), currentClassDoc);
-        n.getJavadocComment().ifPresent(javadocComment ->
-                currentClassDoc.setRawCommentText(javadocComment.getContent()));
+        applyJavadocComment(n.getJavadocComment(), currentClassDoc);
         return parent;
     }
 
@@ -392,8 +394,7 @@ public class GroovydocJavaVisitor
     }
 
     private void setConstructorOrMethodCommon(CallableDeclaration<? extends CallableDeclaration<?>> n, SimpleGroovyExecutableMemberDoc methOrCons) {
-        n.getJavadocComment().ifPresent(javadocComment ->
-                methOrCons.setRawCommentText(javadocComment.getContent()));
+        applyJavadocComment(n.getJavadocComment(), methOrCons);
         NodeList<Modifier> mods = n.getModifiers();
         if (currentClassDoc.isInterface()) {
             mods.add(Modifier.publicModifier());
@@ -433,8 +434,7 @@ public class GroovydocJavaVisitor
         field.setType(makeType(f.getVariable(0).getType()));
         setModifiers(f.getModifiers(), field);
         processAnnotations(field, f);
-        f.getJavadocComment().ifPresent(javadocComment ->
-                field.setRawCommentText(javadocComment.getContent()));
+        applyJavadocComment(f.getJavadocComment(), field);
         currentClassDoc.add(field);
         super.visit(f, arg);
     }
@@ -443,4 +443,53 @@ public class GroovydocJavaVisitor
         return classDocs;
     }
 
+    /**
+     * Apply the content of a JavaParser-recognised Javadoc comment to the
+     * given target. Handles both traditional {@code /** ... *}{@code /} form and
+     * JEP 467 Markdown form (runs of {@code ///} lines). For Markdown, the
+     * {@code ///} line prefixes are stripped and the target is flagged via
+     * {@link SimpleGroovyDoc#setMarkdown(boolean)} so downstream rendering
+     * routes through CommonMark.
+     */
+    private static void applyJavadocComment(Optional<JavadocComment> optComment, SimpleGroovyDoc target) {
+        if (!optComment.isPresent()) return;
+        String content = optComment.get().getContent();
+        String markdownBody = tryExtractMarkdownBody(content);
+        if (markdownBody != null) {
+            target.setRawCommentText(markdownBody);
+            target.setMarkdown(true);
+        } else {
+            target.setRawCommentText(content);
+        }
+    }
+
+    /**
+     * If {@code content} is a JEP 467 Markdown Javadoc body (every non-blank
+     * line starts with {@code ///}), return the body with those line prefixes
+     * stripped (one optional trailing space after {@code ///} is also
+     * consumed). Returns {@code null} if the content isn't Markdown form.
+     */
+    private static String tryExtractMarkdownBody(String content) {
+        if (content == null || content.isEmpty()) return null;
+        String[] lines = content.split("\n", -1);
+        boolean anyMarkdownLine = false;
+        for (String line : lines) {
+            String trimmed = line.trim();
+            if (trimmed.isEmpty()) continue;
+            if (!trimmed.startsWith("///")) return null;
+            anyMarkdownLine = true;
+        }
+        if (!anyMarkdownLine) return null;
+        StringBuilder body = new StringBuilder();
+        for (int k = 0; k < lines.length; k++) {
+            if (k > 0) body.append('\n');
+            String trimmed = lines[k].trim();
+            if (trimmed.isEmpty()) continue;
+            String rest = trimmed.substring(3);
+            if (rest.startsWith(" ")) rest = rest.substring(1);
+            body.append(rest);
+        }
+        return body.toString();
+    }
+
 }