Spotless is a general-purpose formatting plugin. It is completely à la carte, but also includes powerful "batteries-included" if you opt-in. Plugin requires a version of Maven higher or equal to 3.1.0.
To people who use your build, it looks like this:
user@machine repo % mvn spotless:check
[ERROR] > The following files had format violations:
[ERROR] src\main\java\com\diffplug\gradle\spotless\FormatExtension.java
[ERROR] -\t\t····if·(targets.length·==·0)·{
[ERROR] +\t\tif·(targets.length·==·0)·{
[ERROR] Run 'mvn spotless:apply' to fix these violations.
user@machine repo % mvn spotless:apply
[INFO] BUILD SUCCESS
user@machine repo % mvn spotless:check
[INFO] BUILD SUCCESS
- Quickstart
- Languages
- Java (google-java-format, eclipse jdt, prettier)
- Groovy (eclipse groovy)
- Kotlin (ktfmt, ktlint, diktat, prettier)
- Scala (scalafmt)
- C/C++ (eclipse cdt)
- Antlr4 (antlr4formatter)
- Sql (dbeaver)
- Typescript (tsfmt, prettier)
- Multiple languages
- Language independent
- Generic steps
- License header (slurp year from git)
- How can I enforce formatting gradually? (aka "ratchet")
spotless:off
andspotless:on
- Line endings and encodings (invisible stuff)
- Disabling warnings and error messages
- How do I preview what
mvn spotless:apply
will do? - Can I apply Spotless to specific files?
- Example configurations (from real-world projects)
Contributions are welcome, see the contributing guide for development info.
To use it in your pom, just add the Spotless dependency, and configure it like so:
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>${spotless.version}</version>
<configuration>
<!-- optional: limit format enforcement to just the files changed by this feature branch -->
<ratchetFrom>origin/main</ratchetFrom>
<formats>
<!-- you can define as many formats as you want, each is independent -->
<format>
<!-- define the files to apply to -->
<includes>
<include>*.md</include>
<include>.gitignore</include>
</includes>
<!-- define the steps to apply to those files -->
<trimTrailingWhitespace/>
<endWithNewline/>
<indent>
<tabs>true</tabs>
<spacesPerTab>4</spacesPerTab>
</indent>
</format>
</formats>
<!-- define a language-specific format -->
<java>
<!-- no need to specify files, inferred automatically, but you can if you want -->
<!-- apply a specific flavor of google-java-format -->
<googleJavaFormat>
<version>1.8</version>
<style>AOSP</style>
</googleJavaFormat>
<!-- make sure every file has the following copyright header.
optionally, Spotless can set copyright years by digging
through git history (see "license" section below) -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
</licenseHeader>
</java>
</configuration>
</plugin>
Spotless consists of a list of formats (in the example above, misc
and java
), and each format has:
- a
target
(the files to format), which you set withincludes
andexcludes
- a list of
FormatterStep
, which are justString -> String
functions, such asreplace
,replaceRegex
,trimTrailingWhitespace
,indent
,prettier
,eclipseWtp
, andlicenseHeader
.
Spotless requires Maven to be running on JRE 8+.
By default, spotless:check is bound to verify maven phase. This means it is not required to explicitly bind the plugin execution, and the following will suffice;
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
with this mvn verify
will run spotless:check
. If you require the check goal to be run with
any other maven phase (i.e. compile) then it can be configured as below;
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<java>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>src/main/java/**/*.java</include>
<include>src/test/java/**/*.java</include>
</includes>
<importOrder /> <!-- standard import order -->
<importOrder> <!-- or a custom ordering -->
<order>java,javax,org,com,com.diffplug,</order> <!-- or use <file>${basedir}/eclipse.importorder</file> -->
<!-- You probably want an empty string at the end - all of the
imports you didn't specify explicitly will go there. -->
</importOrder>
<removeUnusedImports /> <!-- self-explanatory -->
<googleJavaFormat /> <!-- has its own section below -->
<eclipse /> <!-- has its own section below -->
<prettier /> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
</licenseHeader>
</java>
</configuration>
<googleJavaFormat>
<version>1.7</version> <!-- optional -->
<style>GOOGLE</style> <!-- or AOSP (optional) -->
</googleJavaFormat>
homepage. compatible versions. code. See here for screenshots that demonstrate how to get and install the config file mentioned below.
<eclipse>
<version>4.13.0</version> <!-- optional -->
<file>${basedir}/eclipse-formatter.xml</file> <!-- optional -->
</eclipse>
<configuration>
<groovy>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>src/main/groovy/**/*.groovy</include>
<include>src/test/groovy/**/*.groovy</include>
<include>src/main/java/**/*.java</include>
<include>src/test/java/**/*.java</include>
</includes>
<importOrder /> <!-- standard import order -->
<importOrder> <!-- or a custom ordering -->
<order>java,javax,org,com,com.diffplug,</order> <!-- or use <file>${basedir}/eclipse.importorder</file> -->
<!-- You probably want an empty string at the end - all of the
imports you didn't specify explicitly will go there. -->
</importOrder>
<greclipse /> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
</licenseHeader>
</java>
</configuration>
homepage. changelog. compatible versions. The Groovy formatter uses some of the eclipse jdt configuration parameters in addition to groovy-specific ones. All parameters can be configured within a single file, like the Java properties file greclipse.properties in the previous example. The formatter step can also load the exported Eclipse properties and augment it with the .metadata/.plugins/org.eclipse.core.runtime/.settings/org.codehaus.groovy.eclipse.ui.prefs
from your Eclipse workspace as shown below.
<greclipse>
<version>4.13.0</version> <!-- optional -->
<file>${basedir}/greclipse.properties</file> <!-- optional -->
</greclipse>
Groovy-Eclipse formatting errors/warnings lead per default to a build failure. This behavior can be changed by adding the property/key value ignoreFormatterProblems=true
to a configuration file. In this scenario, files causing problems, will not be modified by this formatter step.
<configuration>
<kotlin>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>src/main/kotlin/**/*.kt</include>
<include>src/test/kotlin/**/*.kt</include>
</includes>
<ktfmt /> <!-- has its own section below -->
<ktlint /> <!-- has its own section below -->
<diktat /> <!-- has its own section below -->
<prettier /> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
</licenseHeader>
</kotlin>
</configuration>
<ktfmt>
<version>0.18</version> <!-- optional -->
<style>DEFAULT</style> <!-- optional, other option is DROPBOX -->
</ktfmt>
homepage. changelog. code. Spotless does not (yet) respect the .editorconfig
settings.
<ktlint>
<version>0.37.2</version> <!-- optional -->
</ktlint>
homepage. changelog. code. You can provide configuration path manually as configFile
.
<diktat>
<version>0.4.0</version> <!-- optional -->
<configFile>full/path/to/diktat-analysis.yml</configFile> <!-- optional, configuration file path -->
</diktat>
<configuration>
<scala>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>src/main/scala/**/*.scala</include>
<include>src/test/scala/**/*.scala</include>
<include>src/main/scala/**/*.sc</include>
<include>src/test/scala/**/*.sc</include>
</includes>
<scalafmt /> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
<delimiter>package </delimiter> <!--
note the 'package ' argument - this is a regex which identifies the top
of the file, be careful that all of your sources have a package declaration,
or pick a regex which works better for your code -->
</licenseHeader>
</scala>
</configuration>
homepage. changelog. config docs. code.
<scalafmt>
<version>2.0.1</version> <!-- optional -->
<file>${basedir}/scalafmt.conf</file> <!-- optional -->
</scalafmt>
<configuration>
<cpp>
<includes> <!-- You have to set the target manually -->
<include>src/native/**</inclue>
</includes>
<eclipseCdt /> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
</licenseHeader>
</cpp>
</configuration>
homepage. compatible versions. code.
<eclipseCdt>
<version>4.13.0</version> <!-- optional -->
<file>${basedir}/eclipse-cdt.xml</file> <!-- optional -->
</eclipseCdt>
<configuration>
<antlr4>
<!-- These are the defaults, you can override if you want -->
<includes>
<include>src/*/antlr4/**/*.g4</include>
</includes>
<antlr4formatter /> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
</licenseHeader>
</antlr4>
</configuration>
homepage. available versions. code.
<antlr4formatter>
<version>1.2.1</version> <!-- optional -->
</antlr4formatter>
<configuration>
<sql>
<!-- You have to set the target manually -->
<includes>
<include>src/main/resources/**/*.sql</include>
</includes>
<dbeaver /> <!-- has its own section below -->
<prettier /> <!-- has its own section below -->
</sql>
</configuration>
homepage. DBeaver is only distributed as a monolithic jar, so the formatter used here was copy-pasted into Spotless, and thus there is no version to change.
<dbeaver>
<configFile>dbeaver.props</configFile> <!-- configFile is optional -->
</dbeaver>
Default configuration file, other options available here.
# case of the keywords (UPPER, LOWER or ORIGINAL)
sql.formatter.keyword.case=UPPER
# Statement delimiter
sql.formatter.statement.delimiter=;
# Indentation style (space or tab)
sql.formatter.indent.type=space
# Number of identation characters
sql.formatter.indent.size=4
<configuration>
<typescript>
<includes> <!-- You have to set the target manually -->
<include>src/**/*.ts</include>
</includes>
<tsfmt/> <!-- has its own section below -->
<prettier/> <!-- has its own section below -->
<licenseHeader>
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
<delimiter>(import|const|declare|export|var) </delimiter> <!--
note the '(import|const|...' argument - this is a regex which identifies the top
of the file, be careful that all of your sources have a suitable top-level declaration,
or pick a regex which works better for your code -->
</licenseHeader>
</typescript>
</configuration>
npm. changelog. code. Please note: The auto-discovery of config files (up the file tree) will not work when using tsfmt within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. See tsfmt's default config settings for what is available.
<tsfmt>
<typescriptFormatterVersion>7.2.2</typescriptFormatterVersion> <!-- optional https://www.npmjs.com/package/typescript-formatter -->
<typescriptVersion>3.9.5</typescriptVersion> <!-- optional https://www.npmjs.com/package/typescript -->
<tslintVersion>6.1.2</tslintVersion> <!-- optional https://www.npmjs.com/package/tslint -->
<config> <!-- https://github.com/vvakame/typescript-formatter/blob/7764258ad42ac65071399840d1b8701868510ca7/lib/utils.ts#L11L32 -->
<indentSize>1</indentSize>
<convertTabsToSpaces>true</convertTabsToSpaces>
</config>
<!-- // or according to tsfmt-parameters: https://github.com/vvakame/typescript-formatter/blob/7764258ad42ac65071399840d1b8701868510ca7/lib/index.ts#L27L34 -->
<tsconfigFile>${basedir}/tsconfig.json</tsconfigFile>
<tslintFile>${basedir}/tslint.json</tslintFile>
<vscodeFile>${basedir}/vscode.json</vscodeFile>
<tsfmtFile>${basedir}/tsfmt.json</tsfmtFile>
</tsfmt>
Prerequisite: tsfmt requires a working NodeJS version
For details, see the npm detection and .npmrc
detection sections of prettier, which apply also to tsfmt.
homepage. changelog. official plugins. community plugins. Prettier is a formatter that can format almost every anything - JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, Less, SCSS, HTML, JSON, GraphQL, Markdown (including GFM and MDX), and YAML. It can format even more using plugins (PHP, Ruby, Swift, XML, Apex, Elm, Java (!!), Kotlin, pgSQL, .properties, solidity, svelte, toml, shellscript, ...).
You can use prettier in any language-specific format, but usually you will be creating a generic format.
<configuration>
<formats>
<format>
<includes>
<include>src/**/typescript/**/*.ts</include>
</includes>
<prettier>
<!-- Specify at most one of the following 3 configs: either 'prettierVersion' (2.0.5 is default), 'devDependencies' or 'devDependencyProperties' -->
<prettierVersion>1.19.0</prettierVersion>
<devDependencies>
<prettier>1.19.0</prettier>
</devDependencies>
<devDependencyProperties>
<property>
<name>prettier</name>
<value>2.0.5</value>
</property>
<property>
<name>@prettier/plugin-php</name> <!-- this could not be written in the simpler to write 'devDependencies' element. -->
<value>0.14.2</value>
</property>
</devDependencyProperties>
<!-- Specify config file and/or inline config, the inline always trumps file -->
<configFile>${basedir}/path/to/configfile</configFile>
<config>
<useTabs>true</useTabs>
</config>
</prettier>
</format>
</formats>
</configuration>
Limitations:
- The auto-discovery of config files (up the file tree) will not work when using prettier within spotless.
- Prettier's override syntax is not supported when using prettier within spotless.
To apply prettier to more kinds of files, just add more formats
<configuration>
<formats>
<format><includes>src/**/*.ts</includes><prettier/></format>
<format><includes>src/**/*.css</includes><prettier/></format>
Since spotless uses the actual npm prettier package behind the scenes, it is possible to use prettier with plugins or community-plugins in order to support even more file types.
<configuration>
<formats>
<!-- prettier with java-plugin -->
<format>
<includes>
<include>src/*/java/**/*.java</include>
</includes>
<prettier>
<devDependencies>
<prettier>2.0.5</prettier>
<prettier-plugin-java>0.8.0</prettier-plugin-java>
</devDependencies>
<config>
<tabWidth>4</tabWidth>
<parser>java</parser>
</config>
</prettier>
</format>
<!-- prettier with php-plugin -->
<format>
<includes>
<include>src/**/*.php</include>
</includes>
<prettier>
<!-- use the devDependencyProperties writing style when the property-names are not well-formed such as @prettier/plugin-php -->
<devDependencyProperties>
<property>
<name>prettier</name>
<value>2.0.5</value>
</property>
<property>
<name>@prettier/plugin-php</name>
<value>0.14.2</value>
</property>
</devDependencyProperties>
<config>
<tabWidth>3</tabWidth>
<parser>php</parser>
</config>
</prettier>
</format>
</formats>
</configuration>
Prettier is based on NodeJS, so to use it, a working NodeJS installation (especially npm) is required on the host running spotless. Spotless will try to auto-discover an npm installation. If that is not working for you, it is possible to directly configure the npm binary to use.
<prettier>
<npmExecutable>/usr/bin/npm</npmExecutable>
Spotless picks up npm configuration stored in a .npmrc
file either in the project directory or in your user home.
Alternatively you can supply spotless with a location of the .npmrc
file to use. (This can be combined with npmExecutable
, of course.)
<prettier>
<npmrc>/usr/local/shared/.npmrc</npmrc>
changelog. compatible versions.
<configuration>
<formats>
<format>
<includes>
<include>src/**/resources/**/*.xml</include>
<include>src/**/resources/**/*.xsd</include>
</includes>
<eclipseWtp>
<type>XML</type> <!-- specify the WTP formatter type (XML, JS, ...) -->
<files> <!-- specify the configuration for the selected type -->
<file>${basedir}/xml.prefs</file>
<file>${basedir}/additional.properties</file>
</files>
<version>4.13.0</version> <!-- optional -->
</eclipseWtp>
</format>
</formats>
</configuration>
The WTP formatter accept multiple configuration files. All Eclipse configuration file formats are accepted as well as simple Java property files. Omit the <files>
entirely to use the default Eclipse configuration. The following formatters and configurations are supported:
Type | Configuration | File location |
---|---|---|
CSS | editor preferences | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.css.core.prefs |
cleanup preferences | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.css.core.prefs | |
HTML | editor preferences | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.html.core.prefs |
cleanup preferences | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.html.core.prefs | |
embedded CSS | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.css.core.prefs | |
embedded JS | Use the export in the Eclipse editor configuration dialog | |
JS | editor preferences | Use the export in the Eclipse editor configuration dialog |
JSON | editor preferences | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.json.core.prefs |
XML | editor preferences | .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.xml.core.prefs |
Note that HTML
should be used for X-HTML
sources instead of XML
.
The Eclipse XML catalog cannot be configured for the Spotless WTP formatter, instead a
user defined catalog file can be specified using the property userCatalog
. Catalog versions
1.0 and 1.1 are supported by Spotless.
Unlike Eclipse, Spotless WTP ignores per default external URIs in schema location hints and
external entities. To allow the access of external URIs, set the property resolveExternalURI
to true.
Prettier, eclipse wtp, and license header are available in every format, and they each have their own section. As mentioned in the quickstart, there are a variety of simple generic steps which are also available in every format, here are examples of these:
<trimTrailingWhitespace /> <!-- trim trailing whitespaces -->
<endWithNewline /> <!-- files must end with a newline -->
<indent> <!-- specify whether to use tabs or spaces for indentation -->
<spaces>true</spaces> <!-- or <tabs>true</tabs> -->
<spacesPerTab>4</spacesPerTab> <!-- optional, default is 4 -->
</indent>
<replace> <!-- specify replacements using search and replace -->
<name>Say Hello to Mars</name>
<search>World</search>
<replacement>Mars</replacement>
</replace>
<replaceRegex> <!-- specify replacements using regex match and replace -->
<name>Say Hello to Mars from Regex</name>
<searchRegex>(Hello) W[a-z]{3}d</searchRegex>
<replacement>$1 Mars</replacement>
</replaceRegex>
Spotless can inject a license header into your files, including populating an accurate copyright header from today's date or from git history.
<licenseHeader> <!-- specify either content or file, but not both -->
<content>/* (C)$YEAR */</content> <!-- or <file>${basedir}/license-header</file> -->
<delimiter>#</delimiter> <!-- content until first occurrence of the delimiter regex will be interpreted as header section -->
</licenseHeader>
If the license header (specified with content
or file
) contains $YEAR
or $today.year
, then that token will be replaced with the current 4-digit year. For example, if Spotless is launched in 2020, then /* Licensed under Apache-2.0 $YEAR. */
will produce /* Licensed under Apache-2.0 2020. */
Once a file's license header has a valid year, whether it is a year (2020
) or a year range (2017-2020
), it will not be changed. If you want the date to be updated when it changes, enable the ratchetFrom
functionality, and the year will be automatically set to today's year according to the following table (assuming the current year is 2020):
- No license header ->
2020
2017
->2017-2020
2017-2019
->2017-2020
If your project has not been rigorous with copyright headers, and you'd like to use git history to repair this retroactively, you can do so with -DspotlessSetLicenseHeaderYearsFromGitHistory=true
. When run in this mode, Spotless will do an expensive search through git history for each file, and set the copyright header based on the oldest and youngest commits for that file. This is intended to be a one-off sort of thing.
If your project is not currently enforcing formatting, then it can be a noisy transition. Having a giant commit where every single file gets changed makes the history harder to read. To address this, you can use the ratchet
feature:
<configuration>
<ratchetFrom>origin/main</ratchetFrom> <!-- only format files which have changed since origin/main -->
<!-- ... define formats ... -->
</configuration>
In this mode, Spotless will apply only to files which have changed since origin/main
. You can ratchet from any point you want, even HEAD
. You can also set ratchetFrom
per-format if you prefer (e.g. <configuration><java><ratchetFrom>...
).
However, we strongly recommend that you use a non-local branch, such as a tag or origin/main
. The problem with HEAD
or any local branch is that as soon as you commit a file, that is now the canonical formatting, even if it was formatted incorrectly. By instead specifying origin/main
or a tag, your CI server will fail unless every changed file is at least as good or better than it was before the change.
This is especially helpful for injecting accurate copyright dates using the license step.
Sometimes there is a chunk of code which you have carefully handcrafted, and you would like to exclude just this one little part from getting clobbered by the autoformat. Some formatters have a way to do this, many don't, but who cares. If you setup your spotless like this:
<configuration>
<java> <!-- or scala, or c, or whatever -->
<toggleOffOn />
...
Then whenever Spotless encounters a pair of spotless:off
/ spotless:on
, it will exclude that subsection of code from formatting. If you want, you can change the tags to be whatever you want, e.g. <toggleOffOn><off>fmt:off</off><on>fmt:on</on></toggleOffOn>')
. If you change the default, read this for some gotchas.
Spotless uses UTF-8 by default, but you can use any encoding which Java supports. You can set it globally, and you can also set it per-format.
<configuration>
<java>
<encoding>Cp1252</encoding>
<!-- ... other steps ... -->
</java>
<encoding>US-ASCII</encoding>
</configuration>
Line endings can also be set globally or per-format using the lineEndings
property. Spotless supports four line ending modes: UNIX
, WINDOWS
, PLATFORM_NATIVE
, and GIT_ATTRIBUTES
. The default value is GIT_ATTRIBUTES
, and we highly recommend that you do not change this value. Git has opinions about line endings, and if Spotless and git disagree, then you're going to have a bad time.
You can easily set the line endings of different files using a .gitattributes
file. Here's an example .gitattributes
which sets all files to unix newlines: * text eol=lf
.
By default, spotless:check
is bound to the verify
phase. You might want to disable this behavior. We recommend against this, but it's easy to do if you'd like:
- set
-Dspotless.check.skip=true
at the command line - set
spotless.check.skip
totrue
in the<properties>
section of thepom.xml
- Save your working tree with
git add -A
, thengit commit -m "Checkpoint before spotless."
- Run
mvn spotless:apply
- View the changes with
git diff
- If you don't like what spotless did,
git reset --hard
- If you'd like to remove the "checkpoint" commit,
git reset --soft head~1
will make the checkpoint commit "disappear" from history, but keeps the changes in your working directory.
You can target specific files by setting the spotlessFiles
project property to a comma-separated list of file patterns:
cmd> mvn spotless:apply -DspotlessFiles=my/file/pattern.java,more/generic/.*-pattern.java
The patterns are matched using String#matches(String)
against the absolute file path.