Why use Spotless?
When you write code, it’s always good to follow some common code style. But doing this manually, even using modern IDEs like Android Studio is painful — sooner or later you’ll forget about this.
This is especially important when you work in a team. If I (or you) press ⌘+⌥+L
(autoformatting) pretty often, you can’t be sure that all your teammates do the same.
Of course, even with big problems in the code style, the code will compile successfully and would work absolutely equal with code with good code style. But it’s always better when your code is formatted following the common code style, diffs would be more readable and your colleagues will scold you less 🙂
How to solve the problem?
Spotless will help us! It allows us to format (and check rules) code in multiple languages, but we’re interested in Kotlin. Spotless uses ktlint to work with it.
ktlint’s standard rules are listed in its’ README, but let me duplicate some of them here:
- 4 spaces for indentation
- No semicolons (unless used to separate multiple statements on the same line)
- No unused
import
s - No consecutive blank lines
- No blank lines before
}
- etc
As you can see, it’s pretty useful. Let’s set it up!
Spotless & ktlint integration
So, let’s assume that we have an Android project with Gradle, Kotlin, Java, and XML.
Spotless is provided as Gradle-plugin (not only Gradle, but we need just it).
First of all, add the following dependency to your project-level build.gradle
:
classpath "com.diffplug.spotless:spotless-plugin-gradle:3.27.0"
It’ll look like this:
buildscript { repositories { google() } dependencies { classpath "com.android.tools.build:gradle:4.0.0-alpha07" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.diffplug.spotless:spotless-plugin-gradle:3.27.0" } } allprojects { repositories { google() } }
Then create spotless.gradle
file, and add the following code to it:
apply plugin: "com.diffplug.gradle.spotless" spotless { java { target '**/*.java' googleJavaFormat().aosp() removeUnusedImports() trimTrailingWhitespace() indentWithSpaces() endWithNewline() } kotlin { target '**/*.kt' ktlint() trimTrailingWhitespace() indentWithSpaces() endWithNewline() } format 'misc', { target '**/*.gradle', '**/*.md', '**/.gitignore' indentWithSpaces() trimTrailingWhitespace() endWithNewline() } format 'xml', { target '**/*.xml' indentWithSpaces() trimTrailingWhitespace() endWithNewline() } }
The main configuration block is spotless
, it contains rules for different file formats.
Let’s take a look at Java rules:
target
specifies the mask of files for which the rules apply. For example, target '**/*.java'
indicates that rules should be applied to all files with the .java
extension in all directories.
Next, we list the rules for these files:
googleJavaFormat().aosp() removeUnusedImports() trimTrailingWhitespace() indentWithSpaces() endWithNewline()
Rules names are self-describing, so I don’t think there is a need to describe any of them.
Next, we set the rules for the other languages — Kotlin, XML and other files, like Markdown
, .gitignore
, or .gradle
.
Now we need to include spotless.gradle
in our build — for example, in app/build.gradle
:
apply from: "$project.rootDir/spotless.gradle"
Now app/build.gradle
looks something like this:
apply plugin: "com.android.application" apply plugin: "kotlin-android" apply plugin: "kotlin-kapt" apply from: "$project.rootDir/spotless.gradle" // ....
Now you have just to synchronize your project with Gradle.
Troubleshooting
Gradle probably will raise this error during the synchronization:
Cannot add task ‘clean’ as a task with that name already exists
If so, you have to remove the following block from your project-level build.gradle
:
task clean(type: Delete) { delete rootProject.buildDir }
Setting up Android Studio
We have to change some settings in Android Studio to make it format code the right way.
First of all, install ktlint. On macOS the easiest way to do it is to use Homebrew:
$ brew install ktlint
Next, navigate to your project’s directory in the Terminal and execute this command:
$ ktlint --android applyToIDEAProject
The last thing to do is to disable wildcard imports in Android Studio. Go to the Preferences -> Editor -> Code Style -> Java
and check the Use single class import
checkbox, then increase Class count to use import with ‘*’
and Names count to use static import with ‘*’
values to something bigger, like 99.
For Kotlin we need just to check the Use single name import
radio buttons:
And it’s done!
Using Spotless
Using Spotless is even easier than setting it up. We need just these two commands:
$ ./gradlew spotlessCheck $ ./gradlew spotlessApply
The first one is checking the code style and fails with an error if there are some problems.
The second one auto-formats the code. You can fix formatting issues in the whole project calling it. But only formatting — it can’t fix issues like wildcard imports, you have to fix them manually.
Now just execute these two commands before every Git push 🙂
Bonus: Travis CI integration
Every serious project uses some CI system, and it’s important to add code style checks in it.
For Travis CI it’s enough just to add ./gradlew spotlessCheck
to the script
block, and it will check your formatting on every build (and fail if there are some issues). Now your script block should look like this:
script: — "./gradlew spotlessCheck" — "./gradlew :library:clean :library:build :library:connectedCheck -PdisablePreDex — stacktrace" — "./gradlew :app:clean :app:build :app:connectedCheck -PdisablePreDex — stacktrace"
You can find the full config here.
Source code
You can find the full integration in the real project in this repository.
Have a nice day!
Leave a Reply