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 11:24:30 UTC
(groovy) 01/02: groovydoc support -prelanguage
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 8d9731096d4c814541ffa0858f078c9384b33480
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Apr 21 21:20:14 2026 +1000
groovydoc support -prelanguage
---
.../groovy/org.apache.groovy-documented.gradle | 1 +
.../groovy/gradle/GroovydocAntExtension.groovy | 2 +
.../apache/groovy/gradle/GroovydocAntPlugin.groovy | 4 ++
.../java/org/codehaus/groovy/ant/Groovydoc.java | 56 ++++++++++++++++++++++
.../groovy/test/JavadocAssertionTestBuilder.groovy | 7 ++-
.../test/JavadocAssertionTestBuilderTest.groovy | 15 ++++++
6 files changed, 83 insertions(+), 2 deletions(-)
diff --git a/build-logic/src/main/groovy/org.apache.groovy-documented.gradle b/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
index 99a141be35..2bde47b3de 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
@@ -23,6 +23,7 @@ pluginManager.apply(GroovydocAntPlugin)
groovydocAnt {
syntaxHighlighter = 'prism'
+ preLanguage = 'groovy'
}
configurations {
diff --git a/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntExtension.groovy b/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntExtension.groovy
index 9253718984..b93401703e 100644
--- a/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntExtension.groovy
+++ b/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntExtension.groovy
@@ -43,6 +43,7 @@ class GroovydocAntExtension {
final Property<Boolean> noHelp
final Property<String> syntaxHighlighter
final Property<String> theme
+ final Property<String> preLanguage
final ConfigurableFileCollection additionalStylesheets
@Inject
@@ -55,6 +56,7 @@ class GroovydocAntExtension {
noHelp = objects.property(Boolean).convention(false)
syntaxHighlighter = objects.property(String).convention('none')
theme = objects.property(String).convention('auto')
+ preLanguage = objects.property(String).convention('')
additionalStylesheets = objects.fileCollection()
}
}
diff --git a/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntPlugin.groovy b/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntPlugin.groovy
index 357456bb8b..dab78236fa 100644
--- a/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntPlugin.groovy
+++ b/build-logic/src/main/groovy/org/apache/groovy/gradle/GroovydocAntPlugin.groovy
@@ -77,6 +77,7 @@ class GroovydocAntPlugin implements Plugin<Project> {
gdoc.inputs.property('antNoHelp', extension.noHelp)
gdoc.inputs.property('antSyntaxHighlighter', extension.syntaxHighlighter)
gdoc.inputs.property('antTheme', extension.theme)
+ gdoc.inputs.property('antPreLanguage', extension.preLanguage)
gdoc.inputs.files(extension.additionalStylesheets)
.withPropertyName('antAdditionalStylesheets')
.optional(true)
@@ -143,6 +144,9 @@ class GroovydocAntPlugin implements Plugin<Project> {
String jv = extension.javaVersion.getOrNull()
if (jv) antArgs.put('javaVersion', jv)
+ String preLang = extension.preLanguage.getOrNull()
+ if (preLang) antArgs.put('preLanguage', preLang)
+
def overviewText = gdoc.overviewText
if (overviewText != null) {
File overviewTmp = new File(
diff --git a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
index 050d0b5a82..66ba584226 100644
--- a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
+++ b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
@@ -74,6 +74,7 @@ public class Groovydoc extends Task {
private String syntaxHighlighter;
private String theme;
private String javaVersion;
+ private String preLanguage;
private final List<DirSet> packageSets;
private final List<String> sourceFilesToDoc;
private final List<LinkArgument> links = new ArrayList<>();
@@ -171,6 +172,21 @@ public class Groovydoc extends Task {
this.javaVersion = javaVersion;
}
+ /**
+ * Default language id for {@code <pre>} blocks in rendered doc comments
+ * that carry no {@code class=} attribute. When set (e.g. {@code "groovy"}),
+ * a post-pass rewrites such {@code <pre>} openings to
+ * {@code <pre class="language-xxx">}, which enables Prism syntax
+ * highlighting for legacy doc-comment code blocks without touching
+ * source files. {@code <pre>} blocks that already have any {@code class}
+ * attribute (including {@code class="groovyTestCase"}) are left alone.
+ *
+ * @param preLanguage the language id, or empty/null to disable
+ */
+ public void setPreLanguage(String preLanguage) {
+ this.preLanguage = preLanguage;
+ }
+
/**
* If set to true, members annotated with {@code groovy.transform.@Internal}
* (per GEP-17) are still included in the generated documentation. Defaults
@@ -644,6 +660,46 @@ public class Groovydoc extends Task {
f.getAbsolutePath(), e.getMessage());
}
}
+ if (preLanguage != null && !preLanguage.isEmpty()) {
+ applyPreLanguageToHtml(destDir);
+ }
+ }
+
+ /**
+ * Walk the generated HTML under {@code destDir} and rewrite each
+ * classless {@code <pre>} opening tag to {@code <pre class="language-xxx">}.
+ * {@code <pre>} tags that already carry a {@code class} attribute — e.g.
+ * {@code <pre class="groovyTestCase">}, or blocks emitted for inline
+ * {@code {@snippet}} tags — are untouched.
+ */
+ /**
+ * Match {@code <pre>} or {@code <pre\s*>} (no attributes at all). Any
+ * existing attribute — class, id, style, data-... — blocks the rewrite so
+ * we only touch the legacy bare form. Package-private for test access.
+ */
+ static String rewritePreTags(String html, String preLanguage) {
+ return html.replaceAll("<pre\\s*>", "<pre class=\"language-" + preLanguage + "\">");
+ }
+
+ private void applyPreLanguageToHtml(File dir) {
+ File[] entries = dir.listFiles();
+ if (entries == null) return;
+ for (File f : entries) {
+ if (f.isDirectory()) {
+ applyPreLanguageToHtml(f);
+ } else if (f.getName().endsWith(".html")) {
+ try {
+ String html = ResourceGroovyMethods.getText(f);
+ String rewritten = rewritePreTags(html, preLanguage);
+ if (!rewritten.equals(html)) {
+ ResourceGroovyMethods.setText(f, rewritten);
+ }
+ } catch (IOException e) {
+ LOGGER.log(WARNING, "Unable to apply preLanguage to ''{0}'': {1}",
+ f.getAbsolutePath(), e.getMessage());
+ }
+ }
+ }
}
private void checkScopeProperties(Properties properties) {
diff --git a/subprojects/groovy-test/src/main/groovy/groovy/test/JavadocAssertionTestBuilder.groovy b/subprojects/groovy-test/src/main/groovy/groovy/test/JavadocAssertionTestBuilder.groovy
index e0df783171..3e9bbfbac8 100644
--- a/subprojects/groovy-test/src/main/groovy/groovy/test/JavadocAssertionTestBuilder.groovy
+++ b/subprojects/groovy-test/src/main/groovy/groovy/test/JavadocAssertionTestBuilder.groovy
@@ -51,12 +51,15 @@ class JavadocAssertionTestBuilder {
private static final Pattern javadocPattern =
Pattern.compile( /(?ims)\/\*\*.*?\*\/|(?-s:(?:^[ \t]*\/\/\/[^\n]*\n?)+)/ )
// Two authoring forms for the assertion inside a doc comment:
- // - HTML wrapper: <pre class="groovyTestCase">...</pre> (or <code>, <samp>, ...)
+ // - HTML wrapper: <pre class="groovyTestCase">...</pre> (or <code>, <samp>, ...).
+ // Other classes may be present alongside groovyTestCase — e.g.
+ // <pre class="language-groovy groovyTestCase"> combines Prism highlighting
+ // with test extraction.
// - Fenced Markdown: ```groovy groovyTestCase ... ``` — only recognised within `///` runs
// (Javadoc `/** */` is not Markdown, so there's no ambiguity).
private static final Pattern assertionPattern =
Pattern.compile(
- /(?ims)<([a-z]+)\s+class\s*=\s*['"]groovyTestCase['"]\s*>.*?<\s*\/\s*\1>/
+ /(?ims)<([a-z]+)\s+class\s*=\s*['"][^'"]*\bgroovyTestCase\b[^'"]*['"]\s*>.*?<\s*\/\s*\1>/
+ /|(?m:^[ \t]*\/\/\/[ \t]*```groovy[ \t]+groovyTestCase\b[^\n]*\n(?s:.*?)^[ \t]*\/\/\/[ \t]*```)/
)
diff --git a/subprojects/groovy-test/src/test/groovy/groovy/test/JavadocAssertionTestBuilderTest.groovy b/subprojects/groovy-test/src/test/groovy/groovy/test/JavadocAssertionTestBuilderTest.groovy
index 86426a56d0..dec514b8de 100644
--- a/subprojects/groovy-test/src/test/groovy/groovy/test/JavadocAssertionTestBuilderTest.groovy
+++ b/subprojects/groovy-test/src/test/groovy/groovy/test/JavadocAssertionTestBuilderTest.groovy
@@ -49,6 +49,21 @@ class JavadocAssertionTestBuilderTest {
}
}
+ @Test
+ void testCombinedClassesAreRecognised() {
+ Class test = builder.buildTest("SomeClass.java",
+ '/** <pre class="language-groovy groovyTestCase"> assert 1 + 1 == 2 </pre> */ public class SomeClass { }')
+ assert test != null
+ test.newInstance().testAssertionFromSomeClassLine1()
+ }
+
+ @Test
+ void testSimilarClassNameIsNotMatched() {
+ Class test = builder.buildTest("SomeClass.java",
+ '/** <pre class="notAGroovyTestCaseAtAll"> assert false </pre> */ public class SomeClass { }')
+ assert test == null
+ }
+
@Test
void testLineNumbering() {
Class test = builder.buildTest("SomeClass.java", '''