An opinionated AsciiDoc formatter for Java and the command line.

adocfmt applies a configurable set of transformations to AsciiDoc source files. The following transformations are available (default state shown):
| Transformation | Default | Description |
|---|---|---|
| Normalize setext headings | on | Convert underline-style headings to ATX-style (= prefix) |
| Collapse consecutive blank lines | on | Reduce runs of multiple blank lines to a single blank line |
| One sentence per line | on | Reflow paragraph text so each sentence starts on its own line |
| Normalize block delimiters | on | Shorten delimiter lines to exactly four characters (e.g. ----) |
Remove trailing header = signs |
on | Strip trailing = from headings (e.g. == Title == → == Title) |
| Remove trailing whitespace | on | Strip trailing whitespace from every line |
| Ensure heading blank lines | on | Surround each section heading with exactly one blank line |
| Title case | off | Apply title-case formatting to section headings and block titles |
| Normalize list bullets | off | Normalise unordered list bullets from - to * |
| Normalize ordered list markers | off | Replace explicit numbers (1. , 2. ) with the auto-number marker . |
| Ensure source delimiters | off | Wrap bare [source]/[listing] blocks in ---- delimiters |
Invariants guaranteed by every run:
format(format(x)) == format(x).This section describes the AsciiDoc style that adocfmt enforces. The rationale behind each rule is consistent diffs, readable source, and unambiguous structure – not aesthetic preference.
Use =-prefixed headings (ATX style) instead of underline-style (setext) headings.
The level maps directly to the number of = signs: one for the document title, two
for the first section level, and so on.
// Before
My Document Title
=================
Introduction
------------
// After
= My Document Title
== Introduction
Trailing = signs are also removed:
// Before
== Installation ==
// After
== Installation
Every section heading is surrounded by exactly one blank line on each side. This makes section boundaries visible at a glance and avoids parser ambiguity.
// Before
Some text.
== Next Section
More text.
// After
Some text.
== Next Section
More text.
Each sentence in a paragraph is placed on its own line. This produces minimal, meaningful diffs: editing one sentence touches exactly one line, and adding a sentence does not reflow the paragraph.
// Before
AsciiDoc is a lightweight markup language. It is used for writing documentation. It can be converted to HTML, PDF, and other formats.
// After
AsciiDoc is a lightweight markup language.
It is used for writing documentation.
It can be converted to HTML, PDF, and other formats.
Common abbreviations (e.g. Dr., etc., ca.) are recognised and do not trigger a split.
At most one consecutive blank line is permitted. Multiple blank lines carry no structural meaning in AsciiDoc and are collapsed to one.
// Before
First paragraph.
Second paragraph.
// After
First paragraph.
Second paragraph.
Block delimiter lines use exactly four characters, regardless of how many were written originally. This removes visual noise and prevents mismatched delimiter lengths from silently breaking block structure.
// Before
--------
$ echo hello
--------
// After
----
$ echo hello
----
[source] and [listing] attribute lines that are not followed by ---- delimiters
have them added automatically, ensuring the code is rendered as a verbatim block.
// Before
[source,java]
int x = 1;
// After
[source,java]
----
int x = 1;
----
Unordered list items use * as the bullet character.
The - form is converted for consistency.
// Before
- Item A
- Item B
// After
* Item A
* Item B
Ordered list items use the AsciiDoc auto-numbering marker . instead of explicit numbers.
This prevents numbering from going out of sync when items are reordered.
// Before
1. First
2. Second
3. Third
// After
. First
. Second
. Third
Section headings and block titles are formatted in title case. Short words (articles, conjunctions, short prepositions) are lowercased unless they appear as the first or last word.
// Before
== getting started with the cli
// After
== Getting Started with the CLI
Requires Java 17+.
Download the .deb from the latest release and run:
sudo apt install ./adocfmt_<version>_all.deb
Download the .rpm from the latest release and run:
sudo dnf install ./adocfmt-<version>.noarch.rpm
brew install drjekyll-org/tap/adocfmt
Download adocfmt.exe from the latest release. Ensure Java 17+ is on your PATH.
Download adocfmt.jar from the latest release and run:
java -jar adocfmt.jar [args]
Requires Java 17+. Add the core module to your project:
<dependency>
<groupId>org.drjekyll</groupId>
<artifactId>adocfmt</artifactId>
<version>0.2.0</version>
</dependency>
Build an AsciidocFormatterConfig with the desired transformations enabled, then create an AsciidocFormatter and call
one of its format overloads:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.drjekyll.adocfmt.AsciidocFormatter;
import org.drjekyll.adocfmt.AsciidocFormatterConfig;
import org.drjekyll.adocfmt.UnsupportedLineEndingException;
AsciidocFormatterConfig config = AsciidocFormatterConfig.builder()
.normalizeSetextHeadings(true)
.collapseConsecutiveBlankLines(true)
.oneSentencePerLine(true)
.normalizeBlockDelimiters(true)
.removeTrailingHeaderEqualsSign(true)
.removeTrailingWhitespace(true)
.ensureHeadingBlankLines(true)
.titleCase(false)
.normalizeListBullets(false)
.normalizeOrderedListMarkers(false)
.ensureSourceDelimiters(false)
.build();
AsciidocFormatter formatter = new AsciidocFormatter(config);
// Format a string
String formatted = formatter.format("== my title\n\ncontent");
// Format a file (charset is auto-detected and preserved)
Path path = Path.of("my.adoc");
byte[] formattedBytes = formatter.format(Files.readAllBytes(path));
// Format a stream
try (InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(path)) {
formatter.format(in, out);
}
Usage: adocfmt [-chVw] [-cbl[=<true|false>]] [-ehlb[=<true|false>]]
[-esd[=<true|false>]] [-nbd[=<true|false>]] [-nlb[=<true|false>]]
[-nolm[=<true|false>]] [-nsh[=<true|false>]] [-ols[=<true|false>]]
[-rthe[=<true|false>]] [-rtrw[=<true|false>]] [-tc[=<true|false>]]
[<files>...]
| Option | Default | Description |
|---|---|---|
-w, --write |
– | Write formatted content back to files |
-c, --check |
– | Check formatting without modifying files; exit 1 if any file would change |
-nsh, --normalize-setext-headings |
true |
Convert setext headings to ATX |
-cbl, --collapse-blank-lines |
true |
Collapse consecutive blank lines |
-ols, --one-sentence-per-line |
true |
One sentence per line |
-nbd, --normalize-block-delimiters |
true |
Normalise block delimiters to four characters |
-rthe, --remove-trailing-header-equals |
true |
Remove trailing = from headings |
-rtrw, --remove-trailing-whitespace |
true |
Remove trailing whitespace |
-ehlb, --ensure-heading-blank-lines |
true |
Ensure blank lines around headings |
-tc, --title-case |
false |
Apply title case to headings |
-nlb, --normalize-list-bullets |
false |
Normalise list bullets to * |
-nolm, --normalize-ordered-list-markers |
false |
Replace explicit numbers with . |
-esd, --ensure-source-delimiters |
false |
Add missing ---- delimiters around source blocks |
| Code | Meaning |
|---|---|
0 |
Success – all files processed without error |
1 |
Unformatted files detected (only when --check is active) |
2 |
Processing error (file not found, not readable, I/O error, etc.) |
# Format a single file in-place
adocfmt -w my.adoc
# Format multiple files in-place
adocfmt -w docs/getting-started.adoc docs/reference.adoc
# Format all .adoc files in a directory tree
find . -name "*.adoc" | xargs adocfmt -w
# Preview formatted output without modifying the file (single file → stdout)
adocfmt my.adoc
# Format stdin to stdout
cat my.adoc | adocfmt
# Check formatting in CI -- exits 1 if any file would change
adocfmt --check docs/*.adoc
# Check all .adoc files in a directory tree in CI
find . -name "*.adoc" | xargs adocfmt --check
# Check only the staged .adoc files (useful in a pre-commit hook)
git diff --cached --name-only --diff-filter=ACM | grep '\.adoc$' | xargs adocfmt --check
# Disable a transformation that is on by default
adocfmt -w --one-sentence-per-line=false my.adoc
# Disable multiple default transformations
adocfmt -w -ols=false -cbl=false my.adoc
# Enable optional transformations
adocfmt -w --title-case --normalize-list-bullets my.adoc
# Enable optional transformations using short aliases
adocfmt -w -tc -nlb -nolm -esd my.adoc
# Enable all optional transformations and disable one default
adocfmt -w -tc -nlb -nolm -esd -ols=false my.adoc
adocfmt can be used as a pre-commit hook.
Add the following to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/dheid/adocfmt
rev: v0.2.0
hooks:
- id: adocfmt # formats .adoc/.asciidoc files in place
- id: adocfmt-check # CI mode: fails if any file would change
Use adocfmt in a local developer workflow (rewrites files before the commit is
recorded) and adocfmt-check in CI (read-only, exits 1 if any file is not
formatted).
Requirement: Java 17 or later must be on your PATH.
The hook downloads adocfmt.jar from the GitHub release on first use and caches
it in ~/.cache/adocfmt/ (or $XDG_CACHE_HOME/adocfmt/).
Set the ADOCFMT_JAR environment variable to point at a local jar to skip the
download entirely.
adocfmt is available as a reusable composite GitHub Action. Java is set up automatically — no pre-installed runtime is required in the calling workflow.
Fails the job when any .adoc or .asciidoc file is not formatted.
Directories are walked recursively.
steps:
- uses: actions/checkout@v6
- uses: dheid/adocfmt@v0.2.0
with:
mode: check # default — can be omitted
paths: . # default — whole repo
Formats files in place and leaves the working tree changed. The action does not commit or open a PR; use a separate step for that, for example stefanzweifel/git-auto-commit-action or peter-evans/create-pull-request.
steps:
- uses: actions/checkout@v6
- uses: dheid/adocfmt@v0.2.0
with:
mode: write
paths: docs # limit to a subdirectory
# working tree now contains any formatting changes — commit or PR here
| Input | Default | Description |
|---|---|---|
mode |
check |
check or write |
paths |
. |
Space-separated files or directories; directories are walked recursively |
version |
0.2.0 |
adocfmt version to download; override to pin to a specific release |
Notes:
.adoc and .asciidoc files are selected during directory traversal. Explicitly listed file paths are always processed regardless of extension.Layout: adocfmt (core library) |
adocfmt-cli (shaded JAR) |
mvn verify--check builds.