Skip to content

Latest commit

 

History

History
1020 lines (771 loc) · 52.8 KB

README.md

File metadata and controls

1020 lines (771 loc) · 52.8 KB

Spotless plugin for Gradle

Keep your code Spotless with Gradle

Gradle plugin Maven central Javadoc License Apache Changelog

Circle CI Live chat VS Code plugin IntelliJ plugin Add other IDE

Spotless is a general-purpose formatting plugin used by 4,000 projects on GitHub (August 2020). It is completely à la carte, but also includes powerful "batteries-included" if you opt-in.

To people who use your build, it looks like this (IDE support also available):

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 supports all of Gradle's built-in performance features (incremental build, remote and local buildcache, lazy configuration, etc), and also automatically fixes idempotence issues, infers line-endings from git, is cautious about misconfigured encoding bugs, and can use git to ratchet formatting without "format-everything" commits.

Table of Contents

Contributions are welcome, see the contributing guide for development info.

Quickstart

To use it in your buildscript, just add the Spotless dependency, and configure it like so:

spotless {
  // optional: limit format enforcement to just the files changed by this feature branch
  ratchetFrom 'origin/main'

  format 'misc', {
    // define the files to apply `misc` to
    target '*.gradle', '*.md', '.gitignore'

    // define the steps to apply to those files
    trimTrailingWhitespace()
    indentWithTabs() // or spaces. Takes an integer argument if you don't like 4
    endWithNewline()
  }
  java {
    // don't need to set target, it is inferred from java

    // apply a specific flavor of google-java-format
    googleJavaFormat('1.8').aosp().reflowLongStrings()
    // 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 '/* (C)$YEAR */'
  }
}

Spotless consists of a list of formats (in the example above, misc and java), and each format has:

All the generic steps live in FormatExtension, and there are many language-specific steps which live in its language-specific subclasses, which are described below.

Requirements

Spotless requires JRE 8+, and Gradle 6.1.1+. Some steps require JRE 11+, Unsupported major.minor version means you're using a step that needs a newer JRE.

If you're stuck on an older version of Gradle, id 'com.diffplug.gradle.spotless' version '4.5.1' supports all the way back to Gradle 2.x.

Java

com.diffplug.gradle.spotless.JavaExtension javadoc, code

spotless {
  java {
    // Use the default importOrder configuration
    importOrder()
    // optional: you can specify import groups directly
    // note: you can use an empty string for all the imports you didn't specify explicitly, and '\\#` prefix for static imports
    importOrder('java', 'javax', 'com.acme', '', '\\#com.acme', '\\#')
    // optional: instead of specifying import groups directly you can specify a config file
    // export config file: https://github.com/diffplug/spotless/blob/main/ECLIPSE_SCREENSHOTS.md#creating-spotlessimportorder
    importOrderFile('eclipse-import-order.txt') // import order file as exported from eclipse

    removeUnusedImports()

    googleJavaFormat() // has its own section below
    eclipse()          // has its own section below
    prettier()         // has its own section below
    clangFormat()      // has its own section below

    licenseHeader '/* (C) $YEAR */' // or licenseHeaderFile
  }
}

The target is usually inferred automatically from the java source sets. However, Spotless cannot automatically detect android or java-gradle-plugin sources, but you can fix this easily:

spotless {
  java {
    target 'src/*/java/**/*.java'

google-java-format

homepage. changelog.

spotless {
  java {
    googleJavaFormat()
    // optional: you can specify a specific version and/or switch to AOSP style
    //   and/or reflow long strings (requires at least 1.8)
    //   and/or use custom group artifact (you probably don't need this)
    googleJavaFormat('1.8').aosp().reflowLongStrings().groupArtifact('com.google.googlejavaformat:google-java-format')

⚠️ Note on using Google Java Format with Java 16+

Using Java 16+ with Google Java Format 1.10.0 requires additional flags to the running JDK. These Flags can be provided using the gradle.properties file (See documentation).

For example the following file under gradle.properties will run gradle with the required flags:

org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

This is a workaround to a pending issue.

palantir-java-format

homepage. changelog.

spotless {
  java {
    palantirJavaFormat()
    // optional: you can specify a specific version
    palantirJavaFormat('2.9.0')

⚠️ Note on using Palantir Java Format with Java 16+

Using Java 16+ with Palantir Java Format requires additional flags on the running JDK. These Flags can be provided using the gradle.properties file (See documentation).

For example the following file under gradle.properties will run gradle with the required flags:

org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
  --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

This is a workaround to a pending issue.

eclipse jdt

homepage. compatible versions. See here for screenshots that demonstrate how to get and install the config file mentioned below.

spotless {
  java {
    eclipse()
    // optional: you can specify a specific version and/or config file
    eclipse('4.17').configFile('eclipse-prefs.xml')

Groovy

  • com.diffplug.gradle.spotless.GroovyExtension javadoc, code
  • com.diffplug.gradle.spotless.GroovyGradleExtension javadoc, code

Configuration for Groovy is similar to Java, in that it also supports licenseHeader and importOrder.

The groovy formatter's default behavior is to format all .groovy and .java files found in the Java and Groovy source sets. If you would like to exclude the .java files, set the parameter excludeJava, or you can set the target parameter as described in the Custom rules section.

apply plugin: 'groovy'
spotless {
  groovy {
    // Use the default importOrder configuration
    importOrder()
    // optional: you can specify import groups directly
    // note: you can use an empty string for all the imports you didn't specify explicitly, and '\\#` prefix for static imports
    importOrder('java', 'javax', 'com.acme', '', '\\#com.acme', '\\#')
    // optional: instead of specifying import groups directly you can specify a config file
    // export config file: https://github.com/diffplug/spotless/blob/main/ECLIPSE_SCREENSHOTS.md#creating-spotlessimportorder
    importOrderFile('eclipse-import-order.txt') // import order file as exported from eclipse

    excludeJava() // excludes all Java sources within the Groovy source dirs from formatting
    // the Groovy Eclipse formatter extends the Java Eclipse formatter,
    // so it formats Java files by default (unless `excludeJava` is used).
    greclipse() // has its own section below

    licenseHeader '/* (C) $YEAR */' // or licenseHeaderFile
  }
  groovyGradle {
    target '*.gradle' // default target of groovyGradle
    greclipse()
  }
}

eclipse groovy

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.

spotless {
  groovy {
    // Use the default version and Groovy-Eclipse default configuration
    greclipse()
    // optional: you can specify a specific version or config file(s)
    // compatible versions: https://github.com/diffplug/spotless/tree/main/lib-extra/src/main/resources/com/diffplug/spotless/extra/groovy_eclipse_formatter
    greclipse('2.3.0').configFile('spotless.eclipseformat.xml', 'org.codehaus.groovy.eclipse.ui.prefs')

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.

Kotlin

  • com.diffplug.gradle.spotless.KotlinExtension javadoc, code
  • com.diffplug.gradle.spotless.KotlinGradleExtension javadoc, code
spotless { // if you are using build.gradle.kts, instead of 'spotless {' use:
           // configure<com.diffplug.gradle.spotless.SpotlessExtension> {
  kotlin {
    // by default the target is every '.kt' and '.kts` file in the java sourcesets
    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 '/* (C)$YEAR */' // or licenseHeaderFile
  }
  kotlinGradle {
    target '*.gradle.kts' // default target for kotlinGradle
    ktlint() // or ktfmt() or prettier()
  }
}

ktfmt

homepage. changelog.

spotless {
  kotlin {
    ktfmt('0.30').dropboxStyle() // version and dropbox style are optional

ktlint

homepage. changelog. Spotless does not (yet) respect the .editorconfig settings (ktlint docs), but you can provide them manually as editorConfigOverride.

spotless {
  kotlin {
    // version, setUseExperimental, userData and editorConfigOverride are all optional
    ktlint("0.45.2")
      .setUseExperimental(true)
      .userData(mapOf("android" to "true"))
      .editorConfigOverride(mapOf("indent_size" to 2))
  }
}

diktat

homepage. changelog. You can provide configuration path manually as configFile.

spotless {
  kotlin {
    // version and configFile are both optional
    diktat('1.0.1').configFile("full/path/to/diktat-analysis.yml")

Scala

com.diffplug.gradle.spotless.ScalaExtension javadoc, code

spotless {
  scala {
    // by default, all `.scala` and `.sc` files in the java sourcesets will be formatted

    scalafmt() // has its own section below

    licenseHeader '/* (C) $YEAR */', 'package ' // or licenseHeaderFile
    // 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
  }
}

scalafmt

homepage. changelog. config docs.

spotless {
  scala {
    // version and configFile are both optional
    scalafmt('2.6.1').configFile('scalafmt.conf')

C/C++

com.diffplug.gradle.spotless.CppExtension javadoc, code

spotless {
  cpp {
    target 'src/native/**' // you have to set the target manually

    clangFormat()  // has its own section below
    eclipseCdt()   // has its own section below

    licenseHeader '/* (C) $YEAR */' // or licenseHeaderFile
  }
}

eclipse cdt

homepage. compatible versions.

spotles {
  cpp {
    // version and configFile are both optional
    eclipseCdt('4.13.0').configFile('eclipse-cdt.xml')
  }
}

Python

com.diffplug.gradle.spotless.PythonExtension javadoc, code

spotless {
  python {
    target 'src/main/**/*.py' // have to set manually

    black()  // has its own section below

    licenseHeader '/* (C) $YEAR */', 'REGEX_TO_DEFINE_TOP_OF_FILE' // or licenseHeaderFile
  }
}

black

homepage. changelog.

black('19.10b0') // version is optional

// if black is not on your path, you must specify its location manually
black().pathToExe('C:/myuser/.pyenv/versions/3.8.0/scripts/black.exe')
// Spotless always checks the version of the black it is using
// and will fail with an error if it does not match the expected version
// (whether manually specified or default). If there is a problem, Spotless
// will suggest commands to help install the correct version.
//   TODO: handle installation & packaging automatically - https://github.com/diffplug/spotless/issues/674

FreshMark

com.diffplug.gradle.spotless.FreshMarkExtension javadoc, code

homepage. changelog. FreshMark lets you generate markdown in the comments of your markdown. This helps to keep badges and links up-to-date (see the source for this file), and can also be helpful for generating complex tables (see the source for the parent readme).

To apply freshmark to all of the .md files in your project, with all of your project's properties available for templating, use this snippet:

spotless {
  freshmark {
    target '*.md' // you have to set the target manually
    propertiesFile('gradle.properties')		// loads all the properties in the given file
    properties {
      it.put('key', 'value')				// specify other properties manually
    }
  }
}

Antlr4

com.diffplug.gradle.spotless.Antlr4Extension javadoc, code

spotless {
  antlr4 {
    target 'src/*/antlr4/**/*.g4' // default value, you can change if you want
    antlr4Formatter() // has its own section below
    licenseHeader '/* (C) $YEAR */' // or licenseHeaderFile
  }
}

antlr4formatter

homepage. available versions.

antlr4formatter('1.2.1') // version is optional

SQL

com.diffplug.gradle.spotless.SqlExtension javadoc, code

spotless {
  sql {
    target 'src/main/resources/**/*.sql' // have to set manually

    dbeaver()  // has its own section below
    prettier() // has its own section below
  }
}

dbeaver

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.

spotless {
  sql {
    dbeaver().configFile('dbeaver.props') // configFile is optional

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 indentation characters
sql.formatter.indent.size=4

Typescript

  • com.diffplug.gradle.spotless.TypescriptExtension javadoc, code
spotless {
  typescript {
    target 'src/**/*.ts' // you have to set the target manually

    tsfmt()    // has its own section below
    prettier() // has its own section below

    licenseHeader '/* (C) $YEAR */', '(import|const|declare|export|var) ' // or licenseHeaderFile
    // 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
  }
}

tsfmt

npm. changelog. 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.

spotless {
  typescript {
    tsfmt('7.2.2')
      // provide config inline: https://github.com/vvakame/typescript-formatter/blob/7764258ad42ac65071399840d1b8701868510ca7/lib/utils.ts#L11L32
      .config(['indentSize': 1, 'convertTabsToSpaces': true])
      // or according to tsfmt-parameters: https://github.com/vvakame/typescript-formatter/blob/7764258ad42ac65071399840d1b8701868510ca7/lib/index.ts#L27L34
      .tsconfigFile('tsconfig.json')
      .tslintFile('tslint.json')
      .vscodeFile('vscode.json')
      .tsfmtFile('tsfmt.json')

Prerequisite: tsfmt requires a working NodeJS version

For details, see the npm detection and .npmrc detection sections of prettier, which apply also to tsfmt.

JSON

  • com.diffplug.gradle.spotless.JsonExtension javadoc, code
spotless {
  json {
    target 'src/**/*.json' // you have to set the target manually
    simple() // has its own section below
    prettier().config(['parser': 'json']) // see Prettier section below
    eclipseWtp('json') // see Eclipse web tools platform section
    gson() // has its own section below
  }
}

simple

Uses a JSON pretty-printer that optionally allows configuring the number of spaces that are used to pretty print objects:

spotless {
  json {
    target 'src/**/*.json'
    simple()
    // optional: specify the number of spaces to use
    simple().indentWithSpaces(6)
  }
}

Gson

Uses Google Gson to also allow sorting by keys besides custom indentation - useful for i18n files.

spotless {
  json {
    target 'src/**/*.json'
    gson()
      .indentWithSpaces(6) // optional: specify the number of spaces to use
      .sortByKeys()        // optional: sort JSON by its keys
      .escapeHtml()        // optional: escape HTML in values
      .version('2.8.1')    // optional: specify version
  }
}

Notes:

  • There's no option in Gson to leave HTML as-is (i.e. escaped HTML would remain escaped, raw would remain raw). Either all HTML characters are written escaped or none. Set escapeHtml if you prefer the former.
  • sortByKeys will apply lexicographic order on the keys of the input JSON. See the javadoc of String for details.

Prettier

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.

spotless {
  format 'styling', {
    // you have to set the target manually
    target 'src/*/webapp/**/*.css', 'src/*/webapp/**/*.scss'

    prettier('2.0.4') // version is optional
    prettier(['my-prettier-fork': '1.16.4']) // can specify exactly which npm packages to use

    // by default, prettier uses the file's extension to guess a parser
    // but you can override that and specify the parser manually
    prettier().config(['parser': 'css'])

    // you can also set some style options
    // https://prettier.io/docs/en/configuration.html
    prettier().config(['tabWidth': 4])

    // you can also slurp from a file or even provide both (inline always takes precedence over file)
    prettier().config(['tabWidth': 4]).configFile('path-to/.prettierrc.yml')
  }
}

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

prettier plugins

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.

spotless {
  java {
    prettier(['prettier': '2.0.5', 'prettier-plugin-java': '0.8.0']).config(['parser': 'java', 'tabWidth': 4])
  }
  format 'php', {
    target 'src/**/*.php'
    prettier(['prettier': '2.0.5', '@prettier/plugin-php': '0.14.2']).config(['parser': 'php', 'tabWidth': 3])
  }
}

npm detection

Prettier is based on NodeJS, so 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.

spotless {
  format 'javascript', {
    prettier().npmExecutable('/usr/bin/npm').config(...)

.npmrc detection

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.)

spotless {
  typescript {
    prettier().npmrc("$projectDir/config/.npmrc").config(...)

clang-format

homepage. changelog. clang-format is a formatter for c, c++, c#, objective-c, protobuf, javascript, and java. You can use clang-format in any language-specific format, but usually you will be creating a generic format.

spotless {
  format 'csharp', {
    // you have to set the target manually
    target 'src/**/*.cs'

    clangFormat('10.0.1') // version is optional

    // can also specify a code style
    clangFormat().style('LLVM') // or Google, Chromium, Mozilla, WebKit
    // TODO: support arbitrary .clang-format

    // if clang-format is not on your path, you must specify its location manually
    clangFormat().pathToExe('/usr/local/Cellar/clang-format/10.0.1/bin/clang-format')
    // Spotless always checks the version of the clang-format it is using
    // and will fail with an error if it does not match the expected version
    // (whether manually specified or default). If there is a problem, Spotless
    // will suggest commands to help install the correct version.
    //   TODO: handle installation & packaging automatically - https://github.com/diffplug/spotless/issues/673
  }
}

Eclipse web tools platform

changelog. compatible versions.

spotless {
  format 'xml', {
    target 'src/**/*/xml' // must specify target
    eclipseWtp('xml')     // must specify a type (table below)
    eclipseWtp('xml', '4.13.0') // optional version
    // you can also specify an arbitrary number of config files
    eclipseWtp('xml').configFile('spotless.xml.prefs', 'spotless.common.properties'
  }
}

The WTP formatter accept multiple configuration files. All Eclipse configuration file formats are accepted as well as simple Java property files. Omit the configFile 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.

Generic steps

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:

spotless {
  // run a native binary
  format 'terraform', {
    target 'src/**/*.tf', 'src/**/*.tfvars' // you have to set the target manually
    nativeCmd('terraform', '/opt/homebrew/bin/terraform', ['fmt', '-']) // name, path to binary, additional arguments
  }
}

License header

If the license header (specified with licenseHeader or licenseHeaderFile) 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

See the javadoc for a complete listing of options.

Retroactively slurp years from git history

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 -PspotlessSetLicenseHeaderYearsFromGitHistory=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.

How can I enforce formatting gradually? (aka "ratchet")

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:

spotless {
  ratchetFrom 'origin/main' // only format files which have changed since origin/main

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. spotless { 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.

spotless:off and spotless:on

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:

spotless {
  java { // or kotlin, or c, or python, or whatever
    toggleOffOn()

Then whenever Spotless encounters a pair of spotless:off / spotless:on, it will exclude the code between them from formatting, regardless of all other rules. If you want, you can change the tags to be whatever you want, e.g. toggleOffOn('fmt:off', 'fmt:on'). If you decide to change the default, be sure to read this for some gotchas.

Line endings and encodings (invisible stuff)

Spotless uses UTF-8 by default, but you can use any encoding which the JVM supports. You can set it globally, and you can also set it per-format.

spotless {
  encoding 'UTF-8' // all formats will be interpreted as UTF-8
  java {
    encoding 'Cp1252' // except java, which will be Cp1252

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.

Custom steps

As described in the quickstart, Spotless is just a set of files ("target"), passed through a list of String -> String functions. The string each function gets will always have unix \n endings, and Spotless doesn't care which endings the function provides back, it will renormalize regardless. You can easily make a new step directly in your buildscript, like so:

spotless {
  format 'misc', {
    custom 'lowercase', { str -> str.toLowerCase() }

However, custom rules will disable up-to-date checking and caching, unless you read this javadoc and follow its instructions carefully.

Another option is to create proper FormatterStep in your buildSrc, and then call addStep. The contributing guide describes how to do this. If the step is generally-useful, we hope you'll open a PR to share it!

spotless {
  format 'misc', {
    addStep(MyFormatterStep.create())

Throwing errors

Ideally, your formatter will be able to silently fix any problems that it finds, that's the beauty of the String -> String model. But sometimes that's not possible. If you throw

  • AssertionError or a subclass -> Spotless reports as a problem in the file being formatted
  • anything else -> Spotless reports as a bug in the formatter itself

Multiple (or custom) language-specific blocks

If you want to have two independent java blocks, you can do something like this:

spotless { java { ... } }
spotless { format 'javaFoo', com.diffplug.gradle.spotless.JavaExtension, { ... } }
// has to be 'javaFoo' not 'java' because each format needs a unique name

That's how the real spotless { java { works anyway. As a follow-on, you can make your own subclass to FormatExtension in the buildSrc directory, and then use it in your buildscript like so:

spotless {
  format 'foo', com.acme.FooLanguageExtension, {

If you'd like to create a one-off Spotless task outside of the check/apply framework, see FormatExtension.createIndependentApplyTask.

Inception (languages within languages within...)

In very rare cases, you might want to format e.g. javascript which is written inside JSP templates, or maybe java within a markdown file, or something wacky like that. You can specify hunks within a file using either open/close tags or a regex with a single capturing group, and then specify rules within it, like so. See javadoc for more details.

import com.diffplug.gradle.spotless.JavaExtension

spotless {
  format 'templates', {
    target 'src/templates/**/*.foo.html'
    prettier().config(['parser': 'html'])
    withinBlocks 'javascript block', '<script>', '</script>', {
      prettier().config(['parser': 'javascript'])
    }
    withinBlocksRegex 'single-line @(java-expresion)', '@\\((.*?)\\)', JavaExtension, {
      googleJavaFormat()
    }

Disabling warnings and error messages

The check task is Gradle's built-in task for grouping all verification tasks - unit tests, static analysis, etc. By default, spotlessCheck is added as a dependency to check.

You might want to disable this behavior. We recommend against this, but it's easy to do if you'd like:

spotless {
  enforceCheck false
}

When a misformatted file throws an exception, it will be for one of two reasons:

  1. Spotless calculated the properly formatted version, and it is different than the current contents.
  2. One of the formatters threw an exception while attempting to calculate the properly formatted version.

You can fix (1) by excluding the file from formatting using the targetExclude method, see the quickstart section for details. You can fix (2) and turn these exceptions into warnings like this:

spotless {
  java {
    custom 'my-glitchy-step', { ... }

    ignoreErrorForStep('my-glitchy-step')   // ignore errors on all files thrown by a specific step
    ignoreErrorForPath('path/to/file.java') // ignore errors by all steps on this specific file

Dependency resolution modes

By default, Spotless resolves dependencies on a per-project basis. For very large parallel builds, this can sometimes cause problems. As an alternative, Spotless can be configured to resolve all dependencies in the root project like so:

spotless {
  ...
  predeclareDeps()
}
spotlessPredeclare {
  java { eclipse() }
  kotlin { ktfmt('0.28') }
}

Alternatively, you can also use predeclareDepsFromBuildscript() to resolve the dependencies from the buildscript repositories rather than the project repositories.

If you use this feature, you will get an error if you use a formatter in a subproject which is not declared in the spotlessPredeclare block.

How do I preview what spotlessApply will do?

  • Save your working tree with git add -A, then git commit -m "Checkpoint before spotless."
  • Run gradlew spotlessApply
  • 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.

Example configurations (from real-world projects)