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 * characters are stripped. The first sentence (up to the first period followed by whitespace) is the summary shown on package and index pages.

Markdown, JEP 467 (6.0)

/// on every line of the run

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

@param name text

Describes a parameter. Use @param <T> text for a type parameter.

@return text

Describes the return value.

@throws Type text, @exception Type text

Describes an exception the method can throw.

@since version

When the element was added.

@author name

Author of the element. In the runs above the author line was rendered both without and with the -author option, even though the option’s own help text calls it "currently not used".

@version text

Version information.

@deprecated text

Marks the element as deprecated and names a replacement. Deprecated elements also appear on deprecated-list.html.

@see reference

Adds a See Also entry, naming a class or a member.

@apiNote text

Guidance for callers of the API.

@implSpec text

A contract that overriding methods must preserve.

@implNote text

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

{@link reference}

A hyperlink to a class or member.

{@code text}

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.

{@literal text}

Verbatim text without code styling, with the same brace rules as {@code}.

{@value #FIELD}, {@value Class#FIELD}

Inlines the value of a compile-time constant field. A bare {@value} on a constant’s own comment inlines that constant.

{@inheritDoc}

Inherits the parent class’s or interface’s documentation.

{@snippet ...}

Embeds a code sample, covered below.

{@docRoot}

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 &#125;.

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

// @highlight substring="text" or regex="...", with type="bold", "italic" or "highlighted"

Wraps matches in bold (the default), italic or mark.

// @replace substring="text" replacement="text"

Replaces matching text in the output.

// @link substring="text" target="Class#member"

Turns matches into links to the element.

// @start region="name", // @end region="name"

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

-d, --destdir=dir

Destination directory.

-sourcepath=pathlist

Directories to search for source files, separated by the platform path separator.

-classpath, -cp

Where to find class files. It must be the first argument.

-public, -protected, -package, -private

Visibility to show. -protected is the default. -private also lists the private members, which the first run left out.

-doctitle, -windowtitle, -header, -footer

Titles and text added to the overview and to every page.

-overview=file

An HTML file used as the overview page.

-exclude=pkglist

Packages to leave out, separated by colons.

-noscripts, -nomainforscripts

Skip Groovy scripts, or keep scripts but omit the implicit main method.

-showInternal

Include members annotated @groovy.transform.Internal.

-noindex, -nodeprecatedlist, -nohelp

Skip the matching auxiliary page. The links to it also disappear from the navigation bars.

-notimestamp, -noversionstamp

Leave the generation time and the Groovy version out of the hidden comment in each page, which keeps generated files stable between runs.

-charset, -fileEncoding

Character set for viewing the pages, and for writing them.

-javaVersion=version

Language level for parsed .java files, for example JAVA_17.

-stylesheetfile, -addStylesheet, -theme, -syntaxHighlighter, -preLanguage

Appearance options, covered below.

-verbose, -quiet, --debug, --version, -h

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

destdir (required)

Output directory.

sourcepath, packagenames, excludePackageNames

What to document. packagenames accepts wildcards such as com.foo.**.

windowtitle, doctitle, header, footer, overview, charset, fileEncoding

Titles, page text and encodings.

author, processScripts, includeMainForScripts

Defaults are true; the last two mirror -noscripts and -nomainforscripts.

private, public, protected, package, access

Visibility, either as flags or as one access value.

javaVersion, extensions

Java language level, and the colon-separated source extensions (default .java:.groovy:.gv:.gvy:.gsh).

showInternal, noIndex, noDeprecatedList, noHelp, noTimeStamp, noVersionStamp

The matching command-line switches.

stylesheetFile, theme, syntaxHighlighter, preLanguage

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

-theme=auto (default)

The stylesheet contains a prefers-color-scheme media query, so each reader gets the light or dark palette that matches their system. Prism themes switch with it.

-theme=light, -theme=dark

Locks the palette whatever the reader’s preference is. Useful for a branded site.

-addStylesheet=path

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 (--fg, --bg, --link, --bg-panel, --border, --caption-bg and related tokens) follows the theme automatically. Several files can be given, comma-separated.

-stylesheetfile=path

Replaces the default stylesheet; the file is copied over stylesheet.css. If it has no dark mode of its own the pages are light only whatever the theme says.

-syntaxHighlighter=prism

Turns on client-side Prism highlighting for {@snippet} blocks and Markdown fences. Prism.js is bundled, with grammars for Groovy, Java, XML, JSON, YAML, TOML, SQL, CSV, Markdown, JavaScript and regular expressions. The default is none, which leaves code as plain preformatted text.

-preLanguage=lang

Old comments often wrap samples in a bare <pre>. With this option, a post-render step turns each unattributed <pre>body</pre> into <pre class="language-lang"><code>body</code></pre> so Prism colours it. A <pre> that already has an attribute is left alone, and a language- <pre> that has no inner <code> gets one.

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.