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 06:13:33 UTC
(groovy) 22/34: GROOVY-11938: {@snippet} per JEP 413 (syntax highlighting assets)
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 6e9db0a6808b579743a52d07531d608440d7b8db
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Apr 21 00:08:50 2026 +1000
GROOVY-11938: {@snippet} per JEP 413 (syntax highlighting assets)
---
.../java/org/codehaus/groovy/ant/Groovydoc.java | 13 +++++++
.../codehaus/groovy/tools/groovydoc/Main.groovy | 5 +++
.../groovy/tools/groovydoc/GroovyDocWriter.java | 9 ++++-
.../gstringTemplates/GroovyDocTemplateInfo.java | 21 +++++++++++
.../groovy/tools/groovydoc/Main.properties | 2 ++
.../gstringTemplates/classLevel/classDocName.html | 14 ++++++++
.../packageLevel/package-summary.html | 14 ++++++++
.../packageLevel/package-tree.html | 14 ++++++++
.../topLevel/overview-summary.html | 14 ++++++++
.../gstringTemplates/topLevel/overview-tree.html | 14 ++++++++
.../groovy/tools/groovydoc/GroovyDocToolTest.java | 42 ++++++++++++++++++++++
11 files changed, 161 insertions(+), 1 deletion(-)
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 a05f2dcc9d..58d5b84841 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
@@ -71,6 +71,7 @@ public class Groovydoc extends Task {
private Boolean noIndex;
private Boolean noDeprecatedList;
private Boolean noHelp;
+ private String syntaxHighlighter;
private String javaVersion;
private final List<DirSet> packageSets;
private final List<String> sourceFilesToDoc;
@@ -105,6 +106,7 @@ public class Groovydoc extends Task {
noIndex = false;
noDeprecatedList = false;
noHelp = false;
+ syntaxHighlighter = "none";
}
/**
@@ -202,6 +204,16 @@ public class Groovydoc extends Task {
this.noHelp = noHelp;
}
+ /**
+ * GROOVY-11938 stage 4: selects a client-side syntax highlighter for
+ * {@code {@snippet}} and fenced Markdown code blocks. Valid values are
+ * {@code "prism"} (bundled) or {@code "none"} (default). Any other value
+ * is treated as {@code "none"}.
+ */
+ public void setSyntaxHighlighter(String syntaxHighlighter) {
+ this.syntaxHighlighter = syntaxHighlighter;
+ }
+
/**
* If set to false, Scripts will not be processed.
* Defaults to true.
@@ -536,6 +548,7 @@ public class Groovydoc extends Task {
properties.setProperty("noIndex", noIndex.toString());
properties.setProperty("noDeprecatedList", noDeprecatedList.toString());
properties.setProperty("noHelp", noHelp.toString());
+ properties.setProperty("syntaxHighlighter", syntaxHighlighter != null ? syntaxHighlighter : "none");
// GROOVY-11941: expose additional stylesheet basenames to templates.
StringBuilder extras = new StringBuilder();
for (int k = 0; k < addStylesheetFiles.size(); k++) {
diff --git a/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy b/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
index 54b2be7b61..7070ad1937 100644
--- a/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
+++ b/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
@@ -49,6 +49,7 @@ class Main {
private static Boolean noIndex
private static Boolean noDeprecatedList
private static Boolean noHelp
+ private static String syntaxHighlighter
private static Boolean privateScope
private static Boolean packageScope
private static Boolean publicScope
@@ -85,6 +86,7 @@ class Main {
cli.noindex(messages['cli.option.noindex.description'])
cli.nodeprecatedlist(messages['cli.option.nodeprecatedlist.description'])
cli.nohelp(messages['cli.option.nohelp.description'])
+ cli.syntaxHighlighter(args: 1, argName: 'name', messages['cli.option.syntaxHighlighter.description'])
cli.overview(args:1, argName: 'file', messages['cli.option.overview.description'])
cli.public(messages['cli.option.public.description'])
cli.protected(messages['cli.option.protected.description'])
@@ -154,6 +156,7 @@ class Main {
noIndex = Boolean.valueOf(options.noindex) ?: false
noDeprecatedList = Boolean.valueOf(options.nodeprecatedlist) ?: false
noHelp = Boolean.valueOf(options.nohelp) ?: false
+ syntaxHighlighter = options.syntaxHighlighter ?: 'none'
showInternal = Boolean.valueOf(options.showInternal) ?: false
packageScope = Boolean.valueOf(options.package) ?: false
privateScope = Boolean.valueOf(options.private) ?: false
@@ -243,6 +246,8 @@ class Main {
properties.put("noIndex", noIndex.toString())
properties.put("noDeprecatedList", noDeprecatedList.toString())
properties.put("noHelp", noHelp.toString())
+ // GROOVY-11938 stage 4: client-side syntax highlighter ("prism"|"none").
+ properties.put("syntaxHighlighter", syntaxHighlighter)
properties.put("overviewFile", overviewFile?.absolutePath ?: "")
String phaseOverride = SystemUtil.getSystemPropertySafe("groovydoc.phase.override")
if (phaseOverride) properties.put("phaseOverride", phaseOverride)
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java
index f3fb75b518..cebd9c60cf 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java
@@ -183,7 +183,14 @@ public class GroovyDocWriter {
}
private static boolean hasBinaryExtension(String template) {
- return template.endsWith(".gif") || template.endsWith(".ico");
+ if (template.endsWith(".gif") || template.endsWith(".ico")) return true;
+ // GROOVY-11938 stage 4: Prism.js bundle. The minified JS/CSS files
+ // contain `$` characters which the GString template engine would
+ // otherwise try to interpret as expressions, so copy verbatim.
+ int lastSlash = template.lastIndexOf('/');
+ String name = lastSlash >= 0 ? template.substring(lastSlash + 1) : template;
+ if (name.startsWith("prism") && (name.endsWith(".js") || name.endsWith(".css"))) return true;
+ return false;
}
}
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/gstringTemplates/GroovyDocTemplateInfo.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/gstringTemplates/GroovyDocTemplateInfo.java
index 66bd46cdaf..a706ab997e 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/gstringTemplates/GroovyDocTemplateInfo.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/gstringTemplates/GroovyDocTemplateInfo.java
@@ -33,6 +33,27 @@ public class GroovyDocTemplateInfo {
TEMPLATE_BASEDIR + "topLevel/stylesheet.css", // copy default one, may override later
TEMPLATE_BASEDIR + "topLevel/inherit.gif",
DOCGEN_BASEDIR + "groovy.ico",
+ // GROOVY-11938 stage 4: Prism.js for client-side syntax highlighting of
+ // {@snippet} bodies and fenced Markdown code blocks. These files are
+ // always copied so an opt-in flag ({@code -syntaxHighlighter=prism})
+ // can enable highlighting at any time without re-running with a
+ // different template set.
+ TEMPLATE_BASEDIR + "topLevel/prism.js",
+ TEMPLATE_BASEDIR + "topLevel/prism.min.css",
+ TEMPLATE_BASEDIR + "topLevel/prism-dark.min.css",
+ TEMPLATE_BASEDIR + "topLevel/prism-csv.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-groovy.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-java.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-javadoclike.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-javascript.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-json.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-markdown.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-properties.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-regex.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-sql.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-toml.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-xml-doc.min.js",
+ TEMPLATE_BASEDIR + "topLevel/prism-yaml.min.js",
};
public static final String[] DEFAULT_PACKAGE_TEMPLATES = new String[]{ // package level templates
TEMPLATE_BASEDIR + "packageLevel/package-frame.html",
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/Main.properties b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/Main.properties
index b650bed86e..69dce4b90a 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/Main.properties
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/Main.properties
@@ -66,6 +66,8 @@ cli.option.nodeprecatedlist.description=Don't generate the deprecated-list page
cli.option.nohelp.description=Don't generate the help page
+cli.option.syntaxHighlighter.description=Client-side syntax highlighter for snippets and Markdown fenced code blocks: "prism" (bundled) or "none" (default).
+
cli.option.public.description=Show only public classes and members
cli.option.protected.description=Show protected/public classes and members (default)
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
index d609933b20..954f2dc2c4 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
@@ -96,6 +96,20 @@
<% if (props.additionalStylesheets) props.additionalStylesheets.tokenize(',').each { __ss -> %>
<link rel="stylesheet" type="text/css" href="${classDoc.relativeRootPath}${__ss.trim()}">
<% } %>
+<% if (props.syntaxHighlighter == 'prism') { def __p = "${classDoc.relativeRootPath}" %>
+ <link rel="stylesheet" type="text/css" href="${__p}prism.min.css">
+ <script src="${__p}prism.js"></script>
+ <script src="${__p}prism-groovy.min.js"></script>
+ <script src="${__p}prism-java.min.js"></script>
+ <script src="${__p}prism-json.min.js"></script>
+ <script src="${__p}prism-yaml.min.js"></script>
+ <script src="${__p}prism-toml.min.js"></script>
+ <script src="${__p}prism-sql.min.js"></script>
+ <script src="${__p}prism-csv.min.js"></script>
+ <script src="${__p}prism-markdown.min.js"></script>
+ <script src="${__p}prism-xml-doc.min.js"></script>
+ <script src="${__p}prism-javadoclike.min.js"></script>
+<% } %>
<body class="center">
<script type="text/javascript"><!--
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-summary.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-summary.html
index 06d1bc0691..79996ecf60 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-summary.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-summary.html
@@ -54,6 +54,20 @@ function windowTitle()
<noscript>
</noscript>
+<% if (props.syntaxHighlighter == 'prism') { def __p = '${packageDoc.relativeRootPath}' %>
+<link rel="stylesheet" type="text/css" href="${__p}prism.min.css">
+<script src="${__p}prism.js"></script>
+<script src="${__p}prism-groovy.min.js"></script>
+<script src="${__p}prism-java.min.js"></script>
+<script src="${__p}prism-json.min.js"></script>
+<script src="${__p}prism-yaml.min.js"></script>
+<script src="${__p}prism-toml.min.js"></script>
+<script src="${__p}prism-sql.min.js"></script>
+<script src="${__p}prism-csv.min.js"></script>
+<script src="${__p}prism-markdown.min.js"></script>
+<script src="${__p}prism-xml-doc.min.js"></script>
+<script src="${__p}prism-javadoclike.min.js"></script>
+<% } %>
</head>
<body class="center" onload="windowTitle();">
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-tree.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-tree.html
index 4dee916e02..cc82eb7e45 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-tree.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/packageLevel/package-tree.html
@@ -31,6 +31,20 @@
<% } %>
<link href="${packageDoc.relativeRootPath}groovy.ico" type="image/x-icon" rel="shortcut icon">
<link href="${packageDoc.relativeRootPath}groovy.ico" type="image/x-icon" rel="icon">
+<% if (props.syntaxHighlighter == 'prism') { def __p = '${packageDoc.relativeRootPath}' %>
+<link rel="stylesheet" type="text/css" href="${__p}prism.min.css">
+<script src="${__p}prism.js"></script>
+<script src="${__p}prism-groovy.min.js"></script>
+<script src="${__p}prism-java.min.js"></script>
+<script src="${__p}prism-json.min.js"></script>
+<script src="${__p}prism-yaml.min.js"></script>
+<script src="${__p}prism-toml.min.js"></script>
+<script src="${__p}prism-sql.min.js"></script>
+<script src="${__p}prism-csv.min.js"></script>
+<script src="${__p}prism-markdown.min.js"></script>
+<script src="${__p}prism-xml-doc.min.js"></script>
+<script src="${__p}prism-javadoclike.min.js"></script>
+<% } %>
</head>
<body>
<!-- ========= START OF TOP NAVBAR ======= -->
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-summary.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-summary.html
index 66e7d44eee..253cd12feb 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-summary.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-summary.html
@@ -31,6 +31,20 @@
<% } %>
<link href="groovy.ico" type="image/x-icon" rel="shortcut icon">
<link href="groovy.ico" type="image/x-icon" rel="icon">
+<% if (props.syntaxHighlighter == 'prism') { def __p = '' %>
+<link rel="stylesheet" type="text/css" href="${__p}prism.min.css">
+<script src="${__p}prism.js"></script>
+<script src="${__p}prism-groovy.min.js"></script>
+<script src="${__p}prism-java.min.js"></script>
+<script src="${__p}prism-json.min.js"></script>
+<script src="${__p}prism-yaml.min.js"></script>
+<script src="${__p}prism-toml.min.js"></script>
+<script src="${__p}prism-sql.min.js"></script>
+<script src="${__p}prism-csv.min.js"></script>
+<script src="${__p}prism-markdown.min.js"></script>
+<script src="${__p}prism-xml-doc.min.js"></script>
+<script src="${__p}prism-javadoclike.min.js"></script>
+<% } %>
</head>
<body class="center">
<script type="text/javascript"><!--
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-tree.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-tree.html
index 07acbdef56..212f8ba893 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-tree.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/topLevel/overview-tree.html
@@ -31,6 +31,20 @@
<% } %>
<link href="groovy.ico" type="image/x-icon" rel="shortcut icon">
<link href="groovy.ico" type="image/x-icon" rel="icon">
+<% if (props.syntaxHighlighter == 'prism') { def __p = '' %>
+<link rel="stylesheet" type="text/css" href="${__p}prism.min.css">
+<script src="${__p}prism.js"></script>
+<script src="${__p}prism-groovy.min.js"></script>
+<script src="${__p}prism-java.min.js"></script>
+<script src="${__p}prism-json.min.js"></script>
+<script src="${__p}prism-yaml.min.js"></script>
+<script src="${__p}prism-toml.min.js"></script>
+<script src="${__p}prism-sql.min.js"></script>
+<script src="${__p}prism-csv.min.js"></script>
+<script src="${__p}prism-markdown.min.js"></script>
+<script src="${__p}prism-xml-doc.min.js"></script>
+<script src="${__p}prism-javadoclike.min.js"></script>
+<% } %>
</head>
<body>
<!-- ========= START OF TOP NAVBAR ======= -->
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
index 5c9c0b27ff..90b0e71761 100644
--- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
@@ -495,6 +495,48 @@ public class GroovyDocToolTest extends GroovyTestCase {
page.contains("the doubled value"));
}
+ // GROOVY-11938 stage 4: opt-in client-side syntax highlighting via Prism.
+ // When -syntaxHighlighter=prism is passed, the head of each content page
+ // should include prism.min.css and the bundled language scripts; when
+ // it's unset (default), none of the prism refs should appear.
+ public void testSyntaxHighlighterPrismInjectsHead() throws Exception {
+ String base = "org/codehaus/groovy/tools/groovydoc/testfiles";
+ Properties props = new Properties();
+ props.put("syntaxHighlighter", "prism");
+ GroovyDocTool tool = makeHtmltool(new ArrayList<>(), null, props);
+ tool.add(List.of(base + "/DocumentedClass.groovy"));
+
+ MockOutputTool output = new MockOutputTool();
+ tool.renderToOutput(output, MOCK_DIR);
+
+ String classPage = output.getText(MOCK_DIR + "/" + base + "/DocumentedClass.html");
+ assertNotNull(classPage);
+ assertTrue("Expected prism.min.css <link> in class page head:\n" + classPage,
+ classPage.contains("prism.min.css"));
+ assertTrue("Expected prism.js <script> in class page head:\n" + classPage,
+ classPage.contains("prism.js"));
+ assertTrue("Expected prism-groovy language component in class page head:\n" + classPage,
+ classPage.contains("prism-groovy.min.js"));
+
+ String overview = output.getText(MOCK_DIR + "/overview-summary.html");
+ assertNotNull(overview);
+ assertTrue("Expected prism.min.css in overview:\n" + overview,
+ overview.contains("prism.min.css"));
+ }
+
+ public void testSyntaxHighlighterOffByDefault() throws Exception {
+ String base = "org/codehaus/groovy/tools/groovydoc/testfiles";
+ htmlTool.add(List.of(base + "/DocumentedClass.groovy"));
+ MockOutputTool output = new MockOutputTool();
+ htmlTool.renderToOutput(output, MOCK_DIR);
+ String classPage = output.getText(MOCK_DIR + "/" + base + "/DocumentedClass.html");
+ assertNotNull(classPage);
+ assertFalse("Prism should NOT be referenced when default (syntaxHighlighter=none):\n" + classPage,
+ classPage.contains("prism.js"));
+ assertFalse("Prism CSS should NOT be referenced when default:\n" + classPage,
+ classPage.contains("prism.min.css"));
+ }
+
// GROOVY-11542 stage 3a: inline tags inside a Markdown code fence or
// code span stay literal (TagRenderer must not expand them).
public void testMarkdownCodeFencePreservesInlineTagSyntax() throws Exception {