jfmt is a standalone Java source code formatter โ fast, consistent, and ready for CI.
jfmt is an opinionated command-line Java formatter with a sane default configuration. It is built on top of the Eclipse JDT formatter engine โ but unlike the Eclipse IDE, jfmt runs completely standalone as a native binary or JAR.
Itโs designed for developers who want:
-
predictable, idempotent formatting;
-
a fast, zero-configuration CLI tool;
-
an easy way to enforce consistent formatting locally and in CI.
In short:
Run it locally before you commit. Let CI check it again.
jfmt also normalizes all source files to UTF-8 encoding and Unix line endings (\n) to ensure consistent formatting across operating systems and editors.
Thatโs all you need.
-
Install or build jfmt
You can run jfmt as a native binary or as a portable JAR (see Compiling).
-
Format your code locally
# Format and rewrite all Java files jfmt write src/ # Or just check which files need formatting jfmt list --all . # Show a diff of what would change jfmt diff --all .
-
Workflow Summary
Action
Who
Purpose
jfmt writeDeveloper
Format files locally before committing
jfmt diffCI pipeline
Verify formatting consistency
jfmt printTooling
Print the correctly formatted output for the given file
jfmt listDeveloper or CI
List unformatted files
Spotless and Checkstyle are Maven or Gradle plugins that integrate deeply into build lifecycles.
jfmt, on the other hand, is a self-contained command-line tool designed to be lightweight and fast โ closer to gofmt or rustfmt.
Tool |
Integration |
Configuration |
Typical Use |
Spotless |
Maven/Gradle plugin |
Customizable |
In build lifecycle |
Checkstyle |
Maven/Gradle plugin |
Highly customizable |
Style rule enforcement |
jfmt |
CLI tool / native binary |
No config needed |
Pre-commit & CI enforcement |
jfmt complements these tools: you can use it alongside Checkstyle or in projects where build tooling is minimal.
-
๐ซโโ๏ธ No config file by default
-
ship a sane default config, similar to palantir-java-format.
-
-
๐ช idempotent operation
-
do not do other/additional changes upon subsequent reformat operations
-
-
๐ reading and writing
-
allows output on stdout or overwriting the input files
-
-
๐ฏ no AST loss
-
do not swallow comments, intentional empty lines and line breaks, etc.
-
-
๐ FAST
-
The project aims to create a GraalVM-based binary for most architectures, so it can operate almost as fast as
gofmtor similar tools.
-
-
๐ง consistent import ordering
-
automatically groups and sorts imports:
-
all static imports first, followed by
-
all other imports.
-
-
Usage: jfmt [-hlV] [COMMAND]
A command-line tool to format Java source code using JDT.
-h, --help Show this help message and exit.
-l, --list Just report the name of the files which are not indented correctly.
-V, --version Print version information and exit.
Commands:
list Just list files which are not formatted correctly without printing a diff or writing changes.
write, fix Write the formatted source code back to the file(s).
If not given, the non-indented file(s) will be printed to stdout.
Only the first file is printed, unless -a is also given.
print Print the correctly formatted output for the given file(s).
Stops on the first file unless -a (--all) is given.
In this case, the file name is printed before each output.
diff Output in diff format. Normal diff is used unless -u is also given.Usage: jfmt list [-ahV] [--no-color] [--config=<config>]
[--config-file=<configFile>] [--import-order=<importOrder>]
[--import-order-file=<importOrderFile>] <filesOrDirectories>...
Just list files which are not formatted correctly without printing a diff or
writing changes.
<filesOrDirectories>...
Files or directory to scan and to format.
-a, --all Report all errors, not just the first one.
--config=<config> Named config. Default: builtin.
Available configs: builtin, equalsverifier
--config-file=<configFile>
Path to a config file. If unset (default), the named
config (--config) will be used.
-h, --help Show this help message and exit.
--import-order=<importOrder>
Named import order. Default: defaultorder. Available:
defaultorder, equalsverifier, google, eclipse,
intellij, enterprise
--import-order-file=<importOrderFile>
Path to an import-order properties file. If set,
overrides --import-order.
--no-color, --no-colour
Force no colored output, even if the terminal
supports it.
-V, --version Print version information and exit.Usage: jfmt write [-ahV] [--no-color] [--config=<config>]
[--config-file=<configFile>] [--import-order=<importOrder>]
[--import-order-file=<importOrderFile>]
<filesOrDirectories>...
Write the formatted source code back to the file(s).
If not given, the non-indented file(s) will be printed to stdout.
Only the first file is printed, unless -e is also given.
<filesOrDirectories>...
Files or directory to scan and to format.
-a, --all Report all errors, not just the first one.
--config=<config> Named config. Default: builtin.
Available configs: builtin, equalsverifier
--config-file=<configFile>
Path to a config file. If unset (default), the named
config (--config) will be used.
-h, --help Show this help message and exit.
--import-order=<importOrder>
Named import order. Default: defaultorder. Available:
defaultorder, equalsverifier, google, eclipse,
intellij, enterprise
--import-order-file=<importOrderFile>
Path to an import-order properties file. If set,
overrides --import-order.
--no-color, --no-colour
Force no colored output, even if the terminal
supports it.
-V, --version Print version information and exit.Usage: jfmt diff [-ahuV] [--no-color] [--config=<config>]
[--config-file=<configFile>] [--context=<context>]
[--import-order=<importOrder>]
[--import-order-file=<importOrderFile>] <filesOrDirectories>...
Output in diff format. Normal diff is used unless -u is also given.
<filesOrDirectories>...
Files or directory to scan and to format.
-a, --all Report all errors, not just the first one.
--config=<config> Named config. Default: builtin.
Available configs: builtin, equalsverifier
--config-file=<configFile>
Path to a config file. If unset (default), the
named config (--config) will be used.
--context=<context> Number of context lines when in unified diff mode
(-u). Defaults to 3.
-h, --help Show this help message and exit.
--import-order=<importOrder>
Named import order. Default: defaultorder.
Available: defaultorder, equalsverifier, google,
eclipse, intellij, enterprise
--import-order-file=<importOrderFile>
Path to an import-order properties file. If set,
overrides --import-order.
--no-color, --no-colour
Force no colored output, even if the terminal
supports it.
-u, --unified Output diff in unified format.
Deactivated by default.
-V, --version Print version information and exit.-
๐ป Uses PicoCLI for command line parsing
-
๐ Uses Eclipse JDT for formatting
-
โ๏ธ Uses GraalVM for creating native, statically linked stand-alone binaries
-
๐ Automatically converts non-UTF-8 inputs to UTF-8 and replaces mixed or Windows line endings (
\r\n) with Unix (\n) endings before formatting. -
๐งฉ Sorts import statements according to a default two-group scheme
(0=#,1=in Eclipse JDT syntax) โ static imports first, then all others.
Features:
-
โ basic commands implemented
-
โ basic tests implemented
-
โ CI/CD implemented
-
[X] Formatting of import statements (#34)
-
โ Refinement of default formatting rules
Distribution:
-
โ native binaries for Linux, Windows, MacOS
-
โ package for Homebrew
-
โ package for Scoop
-
โ package for apt/dpkg
-
โ package for rpm/yum/dnf
-
โ package for Chocolatey
-
โ package for winget
-
โ package for SDKMAN
-
Use Maven 4 or the Maven wrapper
-
JDK 25 required
You can use mvn verify to create a tested and verified runnable application archive using jreleaser.
You can find and execute the archive in the path cli/target/jreleaser/assemble/jfmt/java-archive.
More info about these kinds of archives can be found in my blog post: Creating an App Distribution with Apache Maven and JReleaser.
Please note that Java 25 or better needs to be installed on your system and must be in the PATH environment variable.
Why use native binaries? Because they are fast, small, and do not require a JRE to be installed on the system.
Here is a simple comparison of this project running with both options:
โฏ time ./cli/target/jfmt-0.1.0-SNAPSHOT list --all .
Processing file: ./cli/src/main/java/io/github/bmarwell/jfmt/nio/PathUtils.java
Processing file: ./cli/src/test/resources/diff/SomeRecord.java
Not formatted correctly: ./cli/src/test/resources/diff/SomeRecord.java
Processing file: ./integration-tests/jreleaser-builtin/src/test/resources/correctly_formatted/CorrectlyFormatted.java
Processing file: ./integration-tests/jreleaser-builtin/src/test/resources/incorrectly_formatted/SomeRecord.java
Not formatted correctly: ./integration-tests/jreleaser-builtin/src/test/resources/incorrectly_formatted/SomeRecord.java
Processing file: ./integration-tests/jreleaser-builtin/src/test/java/io/github/bmarwell/jfmt/its/jreleaser/builtin/WriteCommandIT.java
Processing file: ./cli/src/test/java/io/github/bmarwell/jfmt/JFmtTest.java
Processing file: ./integration-tests/jreleaser-builtin/src/test/resources/write/t001/SomeRecord.java
Not formatted correctly: ./integration-tests/jreleaser-builtin/src/test/resources/write/t001/SomeRecord.java
[...]
jfmt list --all . 0,03s user 0,02s system 95% cpu 0,057 totalโฏ time ./cli/target/jreleaser/assemble/jfmt/java-archive/work/jfmt-0.1.0-SNAPSHOT/bin/jfmt list --all .
Processing file: ./cli/src/main/java/io/github/bmarwell/jfmt/nio/PathUtils.java
Processing file: ./cli/src/test/resources/diff/SomeRecord.java
Not formatted correctly: ./cli/src/test/resources/diff/SomeRecord.java
Processing file: ./integration-tests/jreleaser-builtin/src/test/resources/correctly_formatted/CorrectlyFormatted.java
Processing file: ./integration-tests/jreleaser-builtin/src/test/resources/incorrectly_formatted/SomeRecord.java
Not formatted correctly: ./integration-tests/jreleaser-builtin/src/test/resources/incorrectly_formatted/SomeRecord.java
[...]
jfmt list --all . 2,29s user 0,13s system 281% cpu 0,860 total
----As you can see, the native binary is about three times faster than the JAR distribution.
-
Why are there no decent code formatters for Java? โ by Jan Ouwens.
-
Class DefaultCodeFormatterConstants โ the reference for the JDT code formatter config.
-
Spotless Maven Plugin JDT setup โ the reference.
- Why not call it
javafmt? -
The JAVAยฎ brand name is a registered trademark of Oracle, and except in limited circumstances, may not be used without a written license from Oracle. The โJavaโ name may be used in product, service, and event names without permission only in specific, limited ways.
โฆ also, I wanted to keep the book open for the OpenJDK community to adopt an official tool with this name.
- How opinionated is it?
-
While there is a default rule, you can use other named rules or even foreign config files created by Eclipse IDE as well. But I encourage everyone to use the default configuration โ and letโs discuss how well it is defined in the GitHub discussions and the issue tracker.
For information about the release process, including early access builds and official releases, see RELEASING.adoc.
See CONTRIBUTORS.adoc for a list of all contributors.