Awesome
<img align="left" src="_images/spotless_logo.png"> Spotless: Keep your code spotless
Spotless can format <antlr | c | c# | c++ | css | flow | graphql | groovy | html | java | javascript | json | jsx | kotlin | less | license headers | markdown | objective-c | protobuf | python | scala | scss | shell | sql | typeScript | vue | yaml | anything> using <gradle | maven | sbt | anything>.
You probably want one of the links below:
❇️ Spotless for Gradle (with integrations for VS Code and IntelliJ)
user@machine repo % ./gradlew build
:spotlessJavaCheck FAILED
The following files had format violations:
src\main\java\com\diffplug\gradle\spotless\FormatExtension.java
-\t\t····if·(targets.length·==·0)·{
+\t\tif·(targets.length·==·0)·{
Run './gradlew spotlessApply' to fix these violations.
user@machine repo % ./gradlew spotlessApply
:spotlessApply
BUILD SUCCESSFUL
user@machine repo % ./gradlew build
BUILD SUCCESSFUL
❇️ Spotless for Maven
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
❇️ Spotless for SBT (external for now)
Other build systems
How it works (for potential contributors)
Ideally, a code formatter can do more than just find formatting errors - it should fix them as well. Such a formatter is just a Function<String, String>
, which returns a formatted version of its potentially unformatted input.
It's easy to build such a function, but there are some gotchas and lots of integration work (newlines, character encodings, idempotency, git ratcheting, and build-system integration). Spotless tackles those for you so you can focus on just a simple Function<String, String>
which can compose with any of the other formatters and build tools in Spotless' arsenal.
Current feature matrix
<!---freshmark matrix function lib(className) { return '| [`' + className + '`](lib/src/main/java/com/diffplug/spotless/' + className.replaceAll('\\.', '/') + '.java) | ' } function extra(className) { return '| [`' + className + '`](lib-extra/src/main/java/com/diffplug/spotless/extra/' + className.replaceAll('\\.', '/') + '.java) | ' } // | GRADLE | MAVEN | SBT | (new) | output = [ '| Feature / FormatterStep | [gradle](plugin-gradle/README.md) | [maven](plugin-maven/README.md) | [sbt](https://github.com/moznion/sbt-spotless) | [(Your build tool here)](CONTRIBUTING.md#how-to-add-a-new-plugin-for-a-build-system) |', '| --------------------------------------------- | ------------- | ------------ | ------------ | --------|', '| Automatic [idempotency safeguard](PADDEDCELL.md) | {{yes}} | {{yes}} | {{yes}} | {{no}} |', '| Misconfigured [encoding safeguard](https://github.com/diffplug/spotless/blob/08340a11566cdf56ecf50dbd4d557ed84a70a502/testlib/src/test/java/com/diffplug/spotless/EncodingErrorMsgTest.java#L34-L38) | {{yes}} | {{yes}} | {{yes}} | {{no}} |', '| Toggle with [`spotless:off` and `spotless:on`](plugin-gradle/#spotlessoff-and-spotlesson) | {{yes}} | {{yes}} | {{no}} | {{no}} |', '| [Ratchet from](https://github.com/diffplug/spotless/tree/main/plugin-gradle#ratchet) `origin/main` or other git ref | {{yes}} | {{yes}} | {{no}} | {{no}} |', '| Define [line endings using git](https://github.com/diffplug/spotless/tree/main/plugin-gradle#line-endings-and-encodings-invisible-stuff) | {{yes}} | {{yes}} | {{yes}} | {{no}} |', '| Fast incremental format and up-to-date check | {{yes}} | {{yes}} | {{no}} | {{no}} |', '| Fast format on fresh checkout using buildcache | {{yes}} | {{no}} | {{no}} | {{no}} |', lib('generic.EndWithNewlineStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('generic.IndentStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('generic.Jsr223Step') +'{{no}} | {{yes}} | {{no}} | {{no}} |', lib('generic.LicenseHeaderStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('generic.NativeCmdStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('generic.ReplaceRegexStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('generic.ReplaceStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('generic.TrimTrailingWhitespaceStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('antlr4.Antlr4FormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('biome.BiomeStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('cpp.ClangFormatStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', extra('cpp.EclipseFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('go.GofmtFormatStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', lib('gherkin.GherkinUtilsStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', extra('groovy.GrEclipseFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('java.GoogleJavaFormatStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('java.ImportOrderStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', extra('java.EclipseJdtFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('java.FormatAnnotationsStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('java.CleanthatJavaStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.gson.GsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JacksonJsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JsonSimpleStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JsonPatchStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('kotlin.KtLintStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('kotlin.KtfmtStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('kotlin.DiktatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('markdown.FreshMarkStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', lib('markdown.FlexmarkStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('npm.EslintFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('npm.PrettierFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('npm.TsFmtFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('pom.SortPomStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('protobuf.BufStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', lib('python.BlackStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', lib('rdf.RdfFormatterStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |', lib('scala.ScalaFmtStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('shell.ShfmtStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('sql.DBeaverSQLFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('yaml.JacksonYamlStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', '| [(Your FormatterStep here)](CONTRIBUTING.md#how-to-add-a-new-formatterstep) | {{no}} | {{no}} | {{no}} | {{no}} |', ].join('\n'); -->Feature / FormatterStep | gradle | maven | sbt | (Your build tool here) |
---|---|---|---|---|
Automatic idempotency safeguard | :+1: | :+1: | :+1: | :white_large_square: |
Misconfigured encoding safeguard | :+1: | :+1: | :+1: | :white_large_square: |
Toggle with spotless:off and spotless:on | :+1: | :+1: | :white_large_square: | :white_large_square: |
Ratchet from origin/main or other git ref | :+1: | :+1: | :white_large_square: | :white_large_square: |
Define line endings using git | :+1: | :+1: | :+1: | :white_large_square: |
Fast incremental format and up-to-date check | :+1: | :+1: | :white_large_square: | :white_large_square: |
Fast format on fresh checkout using buildcache | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
generic.EndWithNewlineStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
generic.IndentStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
generic.Jsr223Step | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
generic.LicenseHeaderStep | :+1: | :+1: | :+1: | :white_large_square: |
generic.NativeCmdStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
generic.ReplaceRegexStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
generic.ReplaceStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
generic.TrimTrailingWhitespaceStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
antlr4.Antlr4FormatterStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
biome.BiomeStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
cpp.ClangFormatStep | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
cpp.EclipseFormatterStep | :+1: | :+1: | :+1: | :white_large_square: |
go.GofmtFormatStep | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
gherkin.GherkinUtilsStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
groovy.GrEclipseFormatterStep | :+1: | :+1: | :+1: | :white_large_square: |
java.GoogleJavaFormatStep | :+1: | :+1: | :+1: | :white_large_square: |
java.ImportOrderStep | :+1: | :+1: | :+1: | :white_large_square: |
java.PalantirJavaFormatStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
java.RemoveUnusedImportsStep | :+1: | :+1: | :+1: | :white_large_square: |
java.EclipseJdtFormatterStep | :+1: | :+1: | :+1: | :white_large_square: |
java.FormatAnnotationsStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
java.CleanthatJavaStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
json.gson.GsonStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
json.JacksonJsonStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
json.JsonSimpleStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
json.JsonPatchStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
kotlin.KtLintStep | :+1: | :+1: | :+1: | :white_large_square: |
kotlin.KtfmtStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
kotlin.DiktatStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
markdown.FreshMarkStep | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
markdown.FlexmarkStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
npm.EslintFormatterStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
npm.PrettierFormatterStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
npm.TsFmtFormatterStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
pom.SortPomStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
protobuf.BufStep | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
python.BlackStep | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
rdf.RdfFormatterStep | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
scala.ScalaFmtStep | :+1: | :+1: | :+1: | :white_large_square: |
shell.ShfmtStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
sql.DBeaverSQLFormatterStep | :+1: | :+1: | :+1: | :white_large_square: |
wtp.EclipseWtpFormatterStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
yaml.JacksonYamlStep | :+1: | :+1: | :white_large_square: | :white_large_square: |
(Your FormatterStep here) | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: |
Why are there empty squares?
Many projects get harder to work on as they get bigger. Spotless is easier to work on than ever, and one of the reasons why is that we don't require contributors to "fill the matrix". If you want to add Bazel support, we'd happily accept the PR even if it only supports the one formatter you use. And if you want to add FooFormatter support, we'll happily accept the PR even if it only supports the one build system you use.
Once someone has filled in one square of the formatter/build system matrix, it's easy for interested parties to fill in any empty squares, since you'll now have a working example for every piece needed.
Acknowledgements
- Thanks to Konstantin Lutovich for implementing and maintaining the Maven plugin, as well as fixing remote-build cache support for Gradle.
- Thanks to Frank Vennemeyer for Groovy support via greclipse, C++ support via CDT, XML support via WTP and a huge body of work with other eclipse-based formatters.
- Thanks to Jonathan Bluett-Duncan for
- Thanks to Daz DeBoer for the reworking the guts of our Gradle plugin to support buildcache, InputChanges, and lazy configuration.
- Thanks to Richard Willis for creating the VS Code extension for Spotless Gradle.
- Thanks to Ryan Gurney for creating the IntelliJ plugin for Spotless Gradle.
- Thanks to Markus Heberling for adding generic native formatters, jsr-223 formatters, and maven pom sorting.
- Thanks to Matthias Balke for adding support for Antlr.
- Thanks to Matthias Andreas Benkard for adding support for google-java-format 1.8+ (#563)
- Thanks to Thomas Broyer for adding support for google-java-format's skip-reflowing-long-strings option.
- Thanks to Ranadeep Polavarapu for adding support for ktfmt (#569)
- Thanks to Simon Gamma for adding support for npm-based formatters, twice including
prettier
andtsfmt
. - Thanks to Hakanai for adding wildcards last support to the import sorter.
- Thanks to Kevin Brooks for updating all eclipse-based formatters to 4.13 and fixing Groovy for multiproject.
- Thanks to Dylan Baroody for fixing sql formatting support for JDBI bind list params.
- Thanks to figroc for adding custom mavenCoordinate support to google-java-format.
- Thanks to Thomas Glaeser for finding and fixing a file-permissions-clobbering bug.
- Thanks to Joan Goyeau for fixing scalafmt integration.
- Thanks to Nick Sutcliffe for fixing scalafmt post-2.0.
- Thanks to Baptiste Mesta for
- porting the DBeaver formatter to Spotless, and thanks to DBeaver and its authors for their excellent SQL formatter.
- making license headers date-aware #179
- Thanks to vmdominguez and Luis Fors for adding the ability to limit formatting to specific files in Gradle (#322) and Maven (#392), respectively.
- Thanks to bender316 for fixing classloading on Java 9 (#426).
- Thanks to Stefan Oehme for tons of help on the internal mechanics of Gradle.
- Thanks to eyalkaspi for adding configurable date ranges to the date-aware license headers.
- Thanks to Andrew Parmet for adding ktfmt support for kotlin gradle.
- Thanks to Oliver Horn for adding AOSP support for Spotless' google-java-format integration.
- Formatting by Eclipse
- Special thanks to Mateusz Matela for huge improvements to the eclipse code formatter!
- Thanks to Zac Sweers for fixing the highly requested ktlint 0.34+ support (#469), multiple build updates and fixing a Gradle deprecation warning (#434 and others).
- Thanks to Stephen Panaro for adding support for ktlint FilenameRule (#974).
- Thanks to Nelson Osacky for android doc improvements, versions bump, and a build improvement.
- Thanks to Stanley Shyiko for his help integrating ktlint.
- Thanks to Jonathan Leitschuh for adding ktlint support for Gradle Kotlin DSL files.
- Originally forked from gradle-format-plugin by Youri Bonnaffé.
- Thanks to Ismaël Mejía for bumping eclipse-jdt deps to 4.11. PR #60.
- Thanks to Gábor Bernát for improvements to logging and multi-project support.
- Thanks to Oliver Szymanski for porting tsfmt and prettier to maven.
- Thanks to Andrew Oberstar for improvements to formatting java source in non-java source sets. PR #60.
- Thanks to Sameer Balasubrahmanyam for adding support for IntelliJ-style year placeholders.
- Thanks to Jamie Tanna for adding a simple JSON formatter.
- Thanks to Adib Saikali and Paul Merlin for tracking down the tricky cause of #506.
- Import ordering from EclipseCodeFormatter.
- Built by gradle.
- Tested by junit.
- Maintained by DiffPlug.