Groovydoc
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Apache Groovy 6.0.0 (released 2026-09-22), run on JDK 26 and checked on 2026-10-14.
Groovydoc is to Groovy what Javadoc is to Java: it reads documentation comments from source files and writes linked
HTML API pages. It accepts .groovy and .java sources together, so a mixed code base gets one set of pages, and
Groovy adds a few conveniences of its own: Markdown comments, {@snippet} code samples, a dark theme, bundled
syntax highlighting, and pages for scripts and traits. The Java side of the story, the standard tags and the
javadoc tool, is on Javadoc; this page concentrates on what is
different or new in Groovy. Comments in the source are introduced on
Lexical Structure,
Comments, Keywords & Identifiers.
Every command below was run from a scratch directory with a small greenhouse code base in src/greenhouse: a
Sensor class, a Zone class documented with Markdown, a Reading class using snippets, a Probe subclass, a trait,
a script and one Java class. The real output is quoted next to each command. The Ant task was run through
AntBuilder, not from a build file; GMavenPlus was not run here and is described from its documentation.
Doc comment forms
Groovydoc recognises three kinds of comment, and they can be mixed in one file.
| Form | Opening marker | Notes |
|---|---|---|
Traditional Javadoc |
|
Leading |
Markdown, JEP 467 (6.0) |
|
The body is CommonMark. Headings are shifted down two levels, and the inline tags and block tags still work. |
Runtime Groovydoc |
|
Renders like a traditional comment, and can also be kept in the class file (see below). |
A traditional comment on a class, a property, a constant and two methods looks like this:
package greenhouse
/**
* A sensor in the greenhouse. Reports temperature readings in degrees Celsius.
*
* <p>Readings are validated against {@link #MAX_CELSIUS}.
*
* @author Greenhouse team
* @version 1.2
* @since 1.0
* @see Zone
*/
class Sensor {
/** Highest plausible reading. */
static final int MAX_CELSIUS = 60
/** Unique id of the sensor, for example {@code bench-1}. */
String id
/**
* Checks a reading.
*
* @param celsius the reading in degrees Celsius
* @return {@code true} when the reading is plausible
* @throws IllegalArgumentException when {@code celsius} is NaN
* @apiNote Call this before storing a reading.
* @implSpec Overriders must not throw for infinite values.
*/
boolean isPlausible(double celsius) {
if (Double.isNaN(celsius)) throw new IllegalArgumentException('NaN')
celsius <= MAX_CELSIUS
}
/**
* Old way to read.
*
* @deprecated use {@link #isPlausible(double)} instead
*/
@Deprecated
boolean check(double c) { isPlausible(c) }
/** Internal helper, hidden unless -showInternal is given. */
@groovy.transform.Internal
void reset() { }
private void secret() { }
}
Running groovydoc on it (the command line is explained below) produced, as text extracted from the generated
Sensor.html:
class Sensor
extends java.lang.Object
A sensor in the greenhouse. Reports temperature readings in degrees Celsius.
Readings are validated against MAX_CELSIUS. Authors: Greenhouse team Version: 1.2 Since: 1.0 See Also: Zone
Properties Summary
static int MAX_CELSIUS Highest plausible reading.
String id Unique id of the sensor, for example bench-1.
Methods Summary
boolean check(double c) Old way to read.
boolean isPlausible(double celsius) Checks a reading.
Method Detail
@java.lang.Deprecated boolean check(double c)
Old way to read. deprecated: use isPlausible(double) instead
boolean isPlausible(double celsius)
Checks a reading. throws: IllegalArgumentException when celsius is NaN
Parameters: celsius - the reading in degrees Celsius
Returns: true when the reading is plausible
API Note: Call this before storing a reading.
Implementation Requirements: Overriders must not throw for infinite values.
Three things in that output are worth knowing. A Groovy property such as id, and also a static final constant
declared without an access modifier, is listed under Properties, because that is what it is in Groovy. The private
method secret and the method annotated @Internal are not listed by default. And @apiNote and @implSpec
are rendered as API Note and Implementation Requirements.
Block tags
Block tags come at the end of a comment, each starting a line with @. The tags documented for 6.0 are:
| Tag | Purpose |
|---|---|
|
Describes a parameter. Use |
|
Describes the return value. |
|
Describes an exception the method can throw. |
|
When the element was added. |
|
Author of the element. In the runs above the author line was rendered both without and with the |
|
Version information. |
|
Marks the element as deprecated and names a replacement. Deprecated elements also appear on |
|
Adds a See Also entry, naming a class or a member. |
|
Guidance for callers of the API. |
|
A contract that overriding methods must preserve. |
|
An implementation detail that is not part of the contract. |
Several @param, @throws and @see entries are collated under one heading in the output, as the Sensor example
shows.
Inline tags
Inline tags sit inside the text of a comment and are expanded in place.
| Tag | Purpose |
|---|---|
|
A hyperlink to a class or member. |
|
Monospaced text with HTML characters escaped. The body is brace balanced with a linear scan, so nested braces such as closures and map literals are kept, and braces inside string literals, text blocks and comments are ignored while the matching close is found. |
|
Verbatim text without code styling, with the same brace rules as |
|
Inlines the value of a compile-time constant field. A bare |
|
Inherits the parent class’s or interface’s documentation. |
|
Embeds a code sample, covered below. |
|
The path to the root of the generated documentation, for relative links that must work at any depth. |
A class that exercises the less common tags:
package v
/** Limits. */
class Limits {
/** The cap is {@value}. */
public static final int CAP = 60
/** Name. */
public static final String NAME = 'greenhouse'
/** Cap again: {@value #CAP}, other class: {@value Limits#NAME}. */
void m() { }
}
public static final int CAP
The cap is 60.
void m()
Cap again: 60, other class: "greenhouse".
In this 6.0.0 run {@value} was resolved only for a field declared with an explicit public (as above). The same
tag pointed at static final int MAX_CELSIUS = 60 in Sensor, which Groovy turns into a property because it has no
access modifier, was left in the page as the literal text {@value #MAX_CELSIUS}. If you want the value
substituted, declare the constant public static final.
In the same run {@docRoot} became a relative path (../. on a class page), {@literal <b>not bold</b>} kept
the tags as text, and {@code each { it }} and {@code [a: { 1 }]} kept their nested braces. One problem was
seen with {@inheritDoc}: on Probe.isPlausible, which overrides Sensor.isPlausible and whose comment is written
on one line, the inherited text was copied from the parent’s raw comment and showed leading * characters
in the page. If you rely on inheritance, check the generated page, or restate the text.
Markdown comments with ///
Since 6.0, a run of lines that each start with /// is a JEP 467 Markdown comment. The body is rendered with
CommonMark, so headings, bullets, emphasis, links and fenced code blocks work, inline tags such as {@link} still
resolve, and a {@link} inside a fenced block is left literal because the fence is code. Block tags such as @param
and @return end the comment as usual. A # heading becomes an h3.
package greenhouse
/// # Zone
///
/// A named area of the greenhouse that groups {@link Sensor} instances.
///
/// * the *name* is unique
/// * a zone may be empty
///
/// ```groovy
/// def zone = new Zone(name: 'bench')
/// assert zone.sensors.isEmpty()
/// ```
///
/// @since 1.0
class Zone {
/// Name of the zone.
String name
/// Sensors in this zone.
List<Sensor> sensors = []
/// Adds a sensor.
///
/// @param s the sensor to add
/// @return this zone, for chaining
Zone add(Sensor s) { sensors << s; this }
}
The generated Zone.html contained a real h3, a link to Sensor.html, a bulleted list with emphasis, and the fenced
block as <pre><code class="language-groovy">:
Zone
A named area of the greenhouse that groups Sensor instances.
- the name is unique
- a zone may be empty
def zone = new Zone(name: 'bench')
assert zone.sensors.isEmpty()
Since: 1.0
...
Zone add(Sensor s)
Adds a sensor. Parameters: s - the sensor to add Returns: this zone, for chaining
Markdown and traditional comments can sit in the same file. A Markdown comment is the natural choice when a
description needs a list or a code sample, because you write the sample as a fence instead of <pre>{@code ...}</pre>.
Code samples with the snippet tag
{@snippet} (modelled on JEP 413) embeds a code sample that is rendered as a preformatted block and, when syntax
highlighting is on, coloured for its declared language. There are three ways to use it.
Inline body. The sample is written in the comment, after a colon. The optional lang attribute becomes the
highlighter’s language class:
/**
* {@snippet lang="groovy" :
* def r = new Reading(celsius: 18.4)
* r.format() // @highlight substring="format"
* def m = [a: { 1 }] // closures and braces are balanced
* }
*/
class Reading { /* ... */ }
The body ends at the matching closing brace. Groovydoc finds it with a linear scan that counts structural braces and
skips quoted strings, text blocks, block comments and line comments, so closures, GStrings and a stray } inside a
string do not end the tag early. Because a // comment runs to the end of the line, put the closing } of the tag on
its own line, not after a trailing //. If a sample still confuses the scan, move it to a file (next form), or write a
problem brace as }.
External file. Put the sample in a snippet-files directory inside the package, and refer to it by name. The file is
found through the source path, is copied verbatim to the output, and its language is inferred from the extension
(.groovy, .gvy, .gy, .gsh, .java, .xml, .html, .json, .yaml, .yml, .toml, .sql, .csv, .md,
.properties, .js) unless lang is given. A region attribute selects the part of the file between
// @start region="name" and // @end region="name" markers, and a leading block comment that mentions "Licensed" or
"Copyright" is stripped unless you add keepHeader=true.
/*
* Licensed to nobody. Header is stripped.
*/
// @start region="demo"
def r = new greenhouse.Reading(celsius: 18.44)
assert r.format() == '18.4 C'
// @end region="demo"
/**
* Formats readings.
*
* {@snippet file="FormatExample.groovy" region="demo"}
*/
Both forms rendered in Reading.html as <pre><code class="language-groovy"> blocks, with the licence header and
the region markers gone:
def r = new Reading(celsius: 18.4)
r.format() <- "format" is wrapped in a bold element by @highlight
def m = [a: { 1 }] // closures and braces are balanced
def r = new greenhouse.Reading(celsius: 18.44)
assert r.format() == '18.4 C'
Markup comments. Inside an inline body, a comment can change how code is shown without changing the code:
| Directive | Effect |
|---|---|
|
Wraps matches in bold (the default), italic or mark. |
|
Replaces matching text in the output. |
|
Turns matches into links to the element. |
|
Defines a region for the external form, or a scope for the other directives. |
A directive on its own line applies to the next code line; at the end of a code line it applies to that line. The directive comment lines are removed from the output. The full rules are in JEP 413.
Runtime Groovydoc
A comment opened with /**@ renders in the HTML exactly like a normal doc comment. In addition, when the compiler
option runtimeGroovydoc is enabled, the parser keeps the raw comment text as a groovy.lang.Groovydoc annotation on
the class, method or field, so a running program can read its own documentation. The option goes into the
CompilerConfiguration optimisation options. This script compiled a class from source with the option on and read the
comments back:
import org.codehaus.groovy.control.CompilerConfiguration
def cfg = new CompilerConfiguration()
cfg.optimizationOptions[CompilerConfiguration.RUNTIME_GROOVYDOC] = true
def shell = new GroovyShell(cfg)
def cls = shell.evaluate('''
/**@
* A tank that holds water.
* @since 2.0
*/
class Tank {
/**@ Fill level in litres. */
int litres
/**@ Fills the tank. */
void fill() { }
}
Tank
''')
println cls.groovydoc.content
println cls.getMethod('fill').groovydoc.content
println cls.getDeclaredField('litres').groovydoc.content
/**@
* A tank that holds water.
* @since 2.0
*/
/**@ Fills the tank. */
/**@ Fill level in litres. */
The text is the raw source of the comment, including the markers and the per-line asterisks. Parse it yourself if you need the tags. The feature is useful for tools that show help from the code’s own comments, such as a DSL that prints the documentation of its commands.
The groovydoc command
The command is groovydoc [options] [packagenames] [sourcefiles]. It is installed next to groovy in the bin
directory of the distribution. The run that produced the pages above was:
groovydoc -d out -sourcepath src -doctitle "Greenhouse API" -windowtitle "Greenhouse" \
-notimestamp -noversionstamp greenhouse
It printed nothing and wrote index.html, overview-summary.html, overview-tree.html, index-all.html,
deprecated-list.html, help-doc.html, a greenhouse directory with one page per class plus package-summary.html,
package-tree.html and package-frame.html, the copied doc-files and snippet-files directories, the stylesheet and
the bundled Prism files. The output was about 340 KB for the five classes. The most used options of 6.0.0 are:
| Option | Description |
|---|---|
|
Destination directory. |
|
Directories to search for source files, separated by the platform path separator. |
|
Where to find class files. It must be the first argument. |
|
Visibility to show. |
|
Titles and text added to the overview and to every page. |
|
An HTML file used as the overview page. |
|
Packages to leave out, separated by colons. |
|
Skip Groovy scripts, or keep scripts but omit the implicit |
|
Include members annotated |
|
Skip the matching auxiliary page. The links to it also disappear from the navigation bars. |
|
Leave the generation time and the Groovy version out of the hidden comment in each page, which keeps generated files stable between runs. |
|
Character set for viewing the pages, and for writing them. |
|
Language level for parsed |
|
Appearance options, covered below. |
|
Logging and information. |
Packages named on the command line are scanned in the source path; individual files can be named too. Scripts get a
page like any class: a script tool.groovy with a function banner(String) was listed as Script tool that
extends groovy.lang.Script, with the function documented, a synthetic main and a run method (both marked
@Generated). With -noscripts it was absent from the output (the run listed Legacy, Probe, Reading,
Sensor, Waterable and Zone), and -public -noindex -nohelp -nodeprecatedlist removed the three auxiliary pages.
A package comment is written in a package-info.groovy file holding a doc comment before the package statement
(/** Greenhouse monitoring classes. */); the text appeared on package-summary.html and on the overview page. Mixed
Java and Groovy sources are handled in one pass, and the Java class Legacy, with {@code Map<String, List<Integer>>}
in its comment, appeared in the output beside the Groovy ones, labelled [Java] where the Groovy classes are labelled
[Groovy].
Class hierarchy pages
Groovydoc writes overview-tree.html for the whole run and a package-tree.html per package. Classes hang below
java.lang.Object, and interfaces and traits form a separate tree; a type that Groovydoc cannot see, such as a
JDK class, is shown as plain text (or linked to an external site, see the next section) so the chain up to the
documented classes stays complete. In the run with Probe extends Sensor implements Comparable<Probe>, Runnable,
Waterable appeared in the tree as trait greenhouse.Waterable and its page was titled Trait Waterable, with the
@groovy.transform.Trait annotation shown. The Probe page listed All Implemented Interfaces and Traits and a
group of inherited properties and methods taken from Sensor.
Linking to external APIs
References to types outside the documented sources are linked to an external site when a link rule matches. In the
Ant task this is the nested <link> element with a comma-separated packages prefix list and an href. The optional
module attribute adds a JPMS module name as a path segment for modular layouts: with module="java.base" and an
href of the Java 17 API, java.lang.Object resolves to
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html.
On the command line, the documentation text mentions a -link option, but 6.0.0’s --help does not list it. Running
groovydoc -link "java.,https://x.example/" ... was accepted with exit code 0, and the generated pages did not contain
the given host. JDK types were already linked to the Java 17 API (the tree page linked java.lang.Object there)
without any option, and groovy.lang types were linked to docs.groovy-lang.org. For other libraries, use the Ant
task.
The Ant task
The task is org.codehaus.groovy.ant.Groovydoc, defined with taskdef against a class path that contains the Groovy
jars (groovy, groovy-ant, groovy-groovydoc and the modules you document against). It takes the same settings as
the command line, spelled as attributes:
| Attribute | Meaning |
|---|---|
|
Output directory. |
|
What to document. |
|
Titles, page text and encodings. |
|
Defaults are |
|
Visibility, either as flags or as one |
|
Java language level, and the colon-separated source extensions (default |
|
The matching command-line switches. |
|
Appearance. |
The nested elements are <link packages="..." href="..." module="..."> and <addStylesheet file="..."/>. The
documentation also lists a legacy use attribute, accepted for Javadoc parity, that has no effect. Running the task
from Groovy through AntBuilder is equivalent to an Ant build file, and it is what was run here:
import groovy.ant.AntBuilder
def ant = new AntBuilder()
ant.taskdef(name: 'groovydoc', classname: 'org.codehaus.groovy.ant.Groovydoc')
ant.groovydoc(destdir: 'out-ant', sourcepath: 'src', packagenames: 'greenhouse',
windowtitle: 'Greenhouse via Ant', doctitle: 'Greenhouse API', private: 'false',
noTimestamp: 'true', noVersionStamp: 'true') {
link(packages: 'java.,javax.', href: 'https://docs.oracle.com/en/java/javase/17/docs/api/')
link(packages: 'groovy.', href: 'https://docs.groovy-lang.org/latest/html/api/')
}
println new File('out-ant/index.html').exists()
true
The generated pages carried the window title Greenhouse via Ant, and the script page tool.html linked its
superclass to https://docs.groovy-lang.org/latest/html/api/groovy/lang/Script.html. In an Ant build file the same
call is the <groovydoc destdir="..." sourcepath="..." ...> element with nested <link> elements, after the
<taskdef> shown in the documentation. A build file was not run here.
GMavenPlus
GMavenPlus is a third-party Maven plugin that compiles Groovy and has goals that drive groovydoc, so
mvn can produce the same pages as the command line. This page does not reproduce its configuration: the supported
goals and options change with the plugin’s releases, so read the plugin’s own documentation for the goal names, how
to pass link rules and which Groovy version it uses. It was not run here. For Gradle, the Groovydoc task of the
Groovy plugin plays the same role; Gradle itself is on the ecosystem page.
Themes, stylesheets and highlighting
The generated stylesheet follows the reader’s operating-system colour scheme by default:
| Option | Effect |
|---|---|
|
The stylesheet contains a |
|
Locks the palette whatever the reader’s preference is. Useful for a branded site. |
|
Adds your own stylesheet on top of the default, which still supplies the baseline. This is the safer way to tweak
the look. An added stylesheet that uses the default’s CSS custom properties ( |
|
Replaces the default stylesheet; the file is copied over |
|
Turns on client-side Prism highlighting for |
|
Old comments often wrap samples in a bare |
With -syntaxHighlighter prism -preLanguage groovy -theme dark, the pages loaded prism.js, prism-groovy.min.js and
the other bundled grammars and prism-dark.min.css through relative <script> and <link> tags, and copied those
files to the output root. The pages need no network access, so they work from a file system or an intranet. Without
the option no Prism file is referenced.
doc-files and snippet-files
A package may contain a doc-files directory. Its content is copied verbatim into the output under the same package
path, which is the place for diagrams, images and extra HTML referenced from the comments. In the run,
src/greenhouse/doc-files/layout.svg was copied to out/greenhouse/doc-files/layout.svg, and the comment’s
<img src="doc-files/layout.svg" ...> resolved from the class page. The sibling snippet-files directory serves the
file= form of {@snippet}; both are found through the source path and keep any nested subdirectories.
Custom templates
The page set is produced from templates, and they can be replaced by extending org.codehaus.groovy.ant.Groovydoc in a
small Java class and overriding getClassTemplates, getPackageTemplates or getDocTemplates, then registering the
subclass with taskdef under the name groovydoc. The default list is in the class
org.codehaus.groovy.tools.groovydoc.gstringTemplates.GroovyDocTemplateInfo. The documentation itself warns that
template customisation is provided as is and the template API can change between releases, so prefer stylesheets
and -addStylesheet where they are enough. This was not run here.
Where the book is outdated
The first edition of the reference book (Groovy 1.0, 2007) predates everything that is specific to this page: Markdown
comments and {@snippet} arrived in 6.0, and the dark theme, bundled Prism highlighting and the -theme,
-preLanguage and -addStylesheet options are recent additions. Use the book for the idea of generating API pages
from comments, and this page and the official documentation for the options of the current tool. See
What’s New & Migrating from Older
Versions for the full list.