Skip to content

Latest commit

 

History

History
707 lines (547 loc) · 30.7 KB

README.md

File metadata and controls

707 lines (547 loc) · 30.7 KB

Spotless: Keep your code spotless with Gradle

Gradle plugin Maven central Javadoc

Changelog Travis CI Live chat License Apache

Spotless is a general-purpose formatting plugin used by a thousand projects on GitHub. 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:

cmd> gradlew build
...
:spotlessJavaCheck FAILED
> The following files had format violations:
  src\main\java\com\diffplug\gradle\spotless\FormatExtension.java
    @@ -109,7 +109,7 @@
    ...
    -\t\t····if·(targets.length·==·0)·{
    +\t\tif·(targets.length·==·0)·{
    ...
  Run 'gradlew spotlessApply' to fix these violations.

cmd> gradlew spotlessApply
:spotlessApply
BUILD SUCCESSFUL

cmd> gradlew build
BUILD SUCCESSFUL

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

spotless {
  format 'misc', {
    target '**/*.gradle', '**/*.md', '**/.gitignore'

    trimTrailingWhitespace()
    indentWithTabs() // or spaces. Takes an integer argument if you don't like 4
    endWithNewline()
  }
  format 'cpp', {
    target '**/*.hpp', '**/*.cpp'

    replace      'Not enough space after if', 'if(', 'if ('
    replaceRegex 'Too much space after if', 'if +\\(', 'if ('

    // Everything before the first #include or #pragma will
    // be replaced with whatever is in `spotless.license.cpp`
    licenseHeaderFile 'spotless.license.cpp', '#'
  }
}

Spotless can check and apply formatting to any plain-text file, using simple rules (javadoc) like those above. It also supports more powerful formatters:

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

Spotless requires Gradle to be running on JRE 8+.See issue #7 for details.

Applying to Java source

By default, all Java source sets will be formatted. To change this, set the target parameter as described in the Custom rules section.

apply plugin: 'java'
...

spotless {
  java {
    licenseHeader '/* Licensed under Apache-2.0 */'	// License header
    licenseHeaderFile 'spotless.license.java'		// License header file
    // Obviously, you can't specify both licenseHeader and licenseHeaderFile at the same time

    importOrder 'java', 'javax', 'org', 'com', 'com.diffplug', ''	// A sequence of package names
    importOrderFile 'spotless.importorder'				// An import ordering file, exported from Eclipse
    // As before, you can't specify both importOrder and importOrderFile at the same time
    // You probably want an empty string at the end - all of the imports you didn't specify
    // explicitly will go there.

    removeUnusedImports() // removes any unused imports

    eclipse().configFile 'spotless.eclipseformat.xml'	// XML file dumped out by the Eclipse formatter
    // If you have Eclipse preference or property files, you can use them too.
    // eclipse('4.7.1') to specify a specific version of eclipse,
    // available versions are: https://github.com/diffplug/spotless/tree/master/lib-extra/src/main/resources/com/diffplug/spotless/extra/config/eclipse_jdt_formatter
  }
}

See ECLIPSE_SCREENSHOTS for screenshots that demonstrate how to get and install the eclipseFormatFile and importOrderFile mentioned above.

Applying to Android Java source

Be sure to add target '**/*.java' otherwise spotless will not detect Java code inside Android modules.

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

Applying to Java source with google-java-format

spotless {
  java {
    googleJavaFormat()
    // optional: you can specify a specific version and/or switch to AOSP style
    googleJavaFormat('1.1').aosp()
    // you can then layer other format steps, such as
    licenseHeaderFile 'spotless.license.java'
  }
}

Applying to Groovy source

Configuration for Groovy is similar to Java. Most java steps, like licenseHeader and importOrder, support Groovy as well as Java.

The groovy formatter's default behavior is to format all .groovy and .java files found in the Groovy source directories. 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.

Due to cyclic ambiguities of groovy formatter results, e.g. for nested closures, the use of paddedCell() and/or Custom rules is recommended to bandaid over this third-party formatter problem.

apply plugin: 'groovy'
...

spotless {
  java {
    licenseHeaderFile 'spotless.license.java'
    googleJavaFormat() // use a specific formatter for Java files
  }
  groovy {
    licenseHeaderFile 'spotless.license.java'
    excludeJava() // excludes all Java sources within the Groovy source dirs from formatting
    paddedCell() // Avoid cyclic ambiguities
    // the Groovy Eclipse formatter extends the Java Eclipse formatter,
    // so it formats Java files by default (unless `excludeJava` is used).
    greclipse().configFile('greclipse.properties')
  }
  groovyGradle {
    // same as groovy, but for .gradle (defaults to '*.gradle')
    target '*.gradle', 'additionalScripts/*.gradle'
    greclipse().configFile('greclipse.properties')
  }
}

Groovy-Eclipse formatter

The formatter is based on the Eclipse Java formatter as used by eclipseFormatFile. It uses the same configuration parameters plus a few additional ones. These 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 org.codehaus.groovy.eclipse.ui.prefs from the 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)
    // available versions: https://github.com/diffplug/spotless/tree/master/lib-extra/src/main/resources/com/diffplug/spotless/extra/config/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.

Applying FreshMark to markdown files

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 'README.md', 'CONTRIBUTING.md'	// defaults to '**/*.md'
    propertiesFile('gradle.properties')		// loads all the properties in the given file
    properties {
      it.put('key', 'value')				// specify other properties manually
    }
  }
}

Applying scalafmt to Scala files

spotless {
  scala {
    scalafmt()
    // optional: you can specify a specific version or config file
    scalafmt('0.5.1').configFile('scalafmt.conf')
  }
}

Applying ktlint to Kotlin files

spotless {
  kotlin {
    // optionally takes a version
    ktlint()
    // Optional user arguments can be set as such:
    ktlint().userData(['indent_size': '2', 'continuation_indent_size' : '2'])

    // also supports license headers
    licenseHeader '/* Licensed under Apache-2.0 */'	// License header
    licenseHeaderFile 'path-to-license-file'		// License header file
  }
  kotlinGradle {
    // same as kotlin, but for .gradle.kts files (defaults to '*.gradle.kts')
    target '*.gradle.kts', 'additionalScripts/*.gradle.kts'

    ktlint()

    // Optional user arguments can be set as such:
    ktlint().userData(['indent_size': '2', 'continuation_indent_size' : '2'])

    // doesn't support licenseHeader, because scripts don't have a package statement
    // to clearly mark where the license should go
  }
}

Applying DBeaver to SQL scripts

spotless {
  sql {
    // default value for target files
    target '**/*.sql'
    // configFile is optional, arguments available here: https://github.com/diffplug/spotless/blob/master/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/DBeaverSQLFormatterConfiguration.java
    dbeaver().configFile('dbeaver.props')
  }
}

Default configuration file:

# 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

Applying to C/C++ sources

spotless {
  cpp {
    target '**/*.CPP' // Change file filter. By default files with 'c', 'h', 'C', 'cpp', 'cxx', 'cc', 'c++', 'h', 'hpp', 'hh', 'hxx' and 'inc' extension are supported
    eclipse().configFile 'spotless.eclipseformat.xml'	// XML file dumped out by the Eclipse formatter
    // If you have Eclipse preference or property files, you can use them too.
    // eclipse('4.7.1') to specify a specific version of Eclipse,
    // available versions are: https://github.com/diffplug/spotless/tree/master/lib-extra/src/main/resources/com/diffplug/spotless/extra/config/eclipse_cdt_formatter
    licenseHeader '// Licensed under Apache'	// License header
    licenseHeaderFile './license.txt'	// License header file
  }
}

Eclipse CDT formatter

Use the Eclipse to define the Code Style preferences (see Eclipse documentation). Within the preferences Edit... dialog, you can export your configuration as XML file, which can be used as a configFile. If no configFile is provided, the CDT default configuration is used.

Applying to Typescript source

To use tsfmt, you first have to specify the files that you want it to apply to. Then you specify tsfmt(), and optionally how you want to apply it.

By default, all typescript source sets will be formatted. To change this, set the target parameter as described in the Custom rules section.

spotless {
  typescript {
    // using existing config files
    tsfmt().tslintFile('/path/to/repo/tslint.json')
    // tsfmt('7.2.2') to specify specific version of tsfmt
    // tsfmt(['typescript-formatter': '7.2.2', 'typescript': '3.3.3', 'tslint': '5.12.1') to specify all of the npm dependencies that you want
  }
}

Supported config file types are tsconfigFile, tslintFile, vscodeFile and tsfmtFile. They are corresponding to the respective tsfmt-parameters.

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

spotless {
  typescript {
    // custom file-set
    target 'src/main/resources/**/*.ts'
    // provide config inline
    tsfmt().config(['indentSize': 1, 'convertTabsToSpaces': true])
  }
}

See tsfmt's default config settings for what is available.

... and it is also possible to apply prettier() instead of tsfmt() as formatter. For details see the section about prettier.

Prerequisite: tsfmt requires a working NodeJS version

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

spotless {
  typescript {
    tsfmt().npmExecutable('/usr/bin/npm').config(...)
  }
}

Spotless uses npm to install necessary packages locally. It runs tsfmt using J2V8 internally after that.

Applying Prettier to javascript | flow | typeScript | css | scss | less | jsx | graphQL | yaml | etc.

Prettier is a formatter that can format multiple file types.

To use prettier, you first have to specify the files that you want it to apply to. Then you specify prettier, and how you want to apply it.

spotless {
  format 'styling', {
    target '**/*.css', '**/*.scss'

    // at least provide the parser to use
    prettier().config(['parser': 'postcss'])
    // prettier('1.16.4') to specify specific version of prettier
    // prettier(['my-prettier-fork': '1.16.4']) to specify exactly which npm packages to use

    // or provide a typical filename
    prettier().config(['filepath': 'style.scss'])
  }
}

Supported config options are documented on prettier.io.

It is also possible to specify the config via file:

spotless {
  format 'styling', {
    target '**/*.css', '**/*.scss'

    prettier().configFile('/path-to/.prettierrc.yml')

    // or provide both (config options take precedence over configFile options)
    prettier().config(['parser': 'postcss']).configFile('path-to/.prettierrc.yml')
  }
}

Supported config file variants are documented on prettier.io. Please note:

  • 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

spotless {
  format 'javascript', {
    target 'src/main/resources/**/*.js'
    prettier().config(['filepath': 'file.js'])
  }
}

Prettier can also be applied from within the typescript config block:

spotless {
  typescript {
    // no parser or filepath needed
    // -> will default to 'typescript' parser when used in the typescript block
    prettier()
  }
}

Prerequisite: prettier requires a working NodeJS version

Prettier, like tsfmt, 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.

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

Spotless uses npm to install necessary packages locally. It runs prettier using J2V8 internally after that.

Applying Eclipse WTP to css | html | etc.

The Eclipse WTP formatter can be applied as follows:

spotless {
  format 'xml', {
    target fileTree('.') {
      include '**/*.xml', '**/*.xsd'
      exclude '**/build/**'
    }
    // Use for example eclipseWtp('xml', '4.7.3a') to specify a specific version of Eclipse,
    // available versions are: https://github.com/diffplug/spotless/tree/master/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_wtp_formatters
    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.

License header options

If the string contents of a licenseHeader step or the file contents of a licenseHeaderFile step contains a $YEAR token, then in the end-result generated license headers which use this license header as a template, $YEAR will be replaced with the current year.

For example:

/* Licensed under Apache-2.0 $YEAR. */

will produce

/* Licensed under Apache-2.0 2017. */

if Spotless is launched in 2017

The licenseHeader and licenseHeaderFile steps will generate license headers with automatic years from the base license header according to the following rules:

  • A generated license header will be updated with the current year when
    • the generated license header is missing
    • the generated license header is not formatted correctly
  • A generated license header will not be updated when
    • a single year is already present, e.g. /* Licensed under Apache-2.0 1990. */
    • a year range is already present, e.g. /* Licensed under Apache-2.0 1990-2003. */
    • the $YEAR token is otherwise missing

The separator for the year range defaults to the hyphen character, e.g 1990-2003, but can be customized with the yearSeparator property.

For instance, the following configuration treats 1990, 2003 as a valid year range.

spotless {
  java {
    licenseHeader('Licensed under Apache-2.0 $YEAR').yearSeparator(', ')
  }
}

Custom rules

Spotless is a generic system for specifying a sequence of steps which are applied to a set of files.

spotless {
  // this will create two tasks: spotlessMiscCheck and spotlessMiscApply
  format 'misc', {
    // target determines which files this format will apply to
    // - if you pass a string or a list of strings, they will be treated
    //       as 'include' parameters to a fileTree in the root directory
    // - if you pass a FileCollection, it will pass through untouched
    //       e.g. project.files('build.gradle', 'settings.gradle')
    // - if you pass anything else, it will be sent to project.files(yourArg)
    target '**/*.gradle', '**/*.md', '**/.gitignore'

    targetExclude 'src/main/codegen/**', 'src/test/codegen/**'
    // the files to be formatted = (target - targetExclude)
    // NOTE: if target or targetExclude is called multiple times, only the
    // last call is effective

    // spotless has built-in rules for the most basic formatting tasks
    trimTrailingWhitespace()
    indentWithTabs() // or spaces. Takes an integer argument if you don't like 4
    endWithNewline()

    // you can also call out to your own function
    custom 'superFormatter', {
      // when writing a custom step, it will be helpful to know
      // how the formatting process works, which is as follows:

      // 1) Load each target file, and convert it to unix-style line endings ('\n')
      // 2) Pass its content through a series of steps, feeding the output of each step to the next
      // 3) Put the correct line endings back on, then either check or apply

      // each step receives a string as input, and should output
      // a formatted string as output.  Each step can trust that its
      // input will have unix newlines, and it must promise to output
      // only unix newlines.  Other than that, anything is fair game!
    }
  }
}

If you use custom or customLazy, you might want to take a look at this javadoc for a big performance win.

See JavaExtension.java if you'd like to see how a language-specific set of custom rules is implemented. We'd love PR's which add support for other languages.

Line endings and encodings (invisible stuff)

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.

spotless {
  java {
    ...
    encoding 'Cp1252' // java will have Cp1252
  }
  encoding 'US-ASCII'   // but all other formats will be interpreted as US-ASCII
}

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.

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 custom rules section for details. You can fix (2) and turn these exceptions into warnings like this:

spotless {
  java {
    googleJavaFormat()
    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
  }
}

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.

Can I apply Spotless to specific files?

You can target specific files by setting the spotlessFiles project property to a comma-separated list of file patterns:

cmd> gradlew spotlessApply -PspotlessFiles=my/file/pattern.java,more/generic/.*-pattern.java

The patterns are matched using String#matches(String) against the absolute file path.

Example configurations (from real-world projects)

Spotless is hosted on jcenter and at plugins.gradle.org. Go here if you're not sure how to import the plugin.