ktlintを使いこなす 【kotlin】【lint】

# ktlintとは
[ktlint](https://github.com/shyiko/ktlint)はkotlinの静的コード解析ツールです。
同じようなものに、[detekt](https://github.com/arturbosch/detekt)というものもあります。
今回は、ktlintについて書いていきます。

ktlintはkotlinのコードにlintをかけるだけでなく、lintで検出した部分にフォーマットをかけることもできます。

# 使い方
ktlintの導入方法です。

## gradleの場合

```groovy
configurations {
  ktlint
}

dependencies {
  ktlint "com.github.shyiko:ktlint:$KTLINT_VERSION"
}

check.dependsOn ktlint
```

### lintのtaskを追加

```groovy
task ktlint(type: JavaExec, group: "kotlin verification") {
  description = 'Check Kotlin code style.'
  args 'src/**/*.kt'
  main = 'com.github.shyiko.ktlint.Main'
  classpath = configurations.ktlint + sourceSets.main.output
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/93589/1a3ddf0a-9b57-3504-5889-965a69c00dd2.png)

`./gradlew ktlint`でlintを走らせることができます。


### formatのtaskを追加

```groovy
task ktFormat(type: JavaExec, group: "kotlin Formatting") {
  description = 'Fix Kotlin code style deviations.'
  args '-F', 'src/**/*.kt'
  main = 'com.github.shyiko.ktlint.Main'
  classpath = configurations.ktlint + sourceSets.main.output
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/93589/55f0a775-99a2-586b-22d9-c5807f45bb15.png)


`./gradlew ktFormat`を使ってlintで検出された箇所にフォーマットをかけることができます。


## CLIの場合

brewを使ってインストールできます。

```bash
brew install shyiko/ktlint/ktlint
ktlint "src/**/*.kt"
```

# Output形式
ktlintでは様々なoutput形式を用意しています。
--reporterオプションを使ってOutputの形式を指定することができます。

```groovy
task ktlint(type: JavaExec, dependsOn: classes) {
  description = 'Check Kotlin code style.'
  args 'src/**/*.kt', '--reporter=plain'
  main = 'com.github.shyiko.ktlint.Main'
  classpath = configurations.ktlint + sourceSets.main.output
}
```

### --reporter=plain
デフォルトのアウトプット形式です。

```
takusemba/ktlint-sample/app/…/Hoge.kt:44:1: Unexpected blank line(s) before "}"

takusemba/ktlint-sample/app/…/Fuga.kt:108:1: Needless blank line(s)

takusemba/ktlint-sample/app/…/Foo.kt:25:7: Line break before assignment is not allowed

takusemba/ktlint-sample/app/…/Bar.kt:47:1: Unexpected blank line(s) before “}"
```


### --reporter=checkstyle

xml形式でアウトプットすることもできます。

```xml
<?xml version="1.0" encoding="utf-8"?>
<checkstyle version=“8.0">
	<file name="takusemba/ktlint-sample/app/…/Hoge.kt">
		<error 
			line="25"  
			column="7" 
			severity="error" 
			message="Line break before assignment is not allowed" 
			source="no-line-break-before-assignment" />
	</file>
	…
</checkstyle>
```


### --reporter=json
json形式でアウトプットすることもできます。

```json
[
	{
		"file": "ktrule/src/main/java/com/takusemba/Hoge.kt",
		"errors": [
			{
				"line": 3,
				"column": 1,
				"message": "Line break before assignment is not allowed",
				"rule": "no-line-break-before-assignment"
			},
			{
				"line": 5,
				"column": 1,
				"message": "Line break before assignment is not allowed",
				"rule": "no-line-break-before-assignment"
			}
		]
	}
]
```

### --reporter=your-custom-output

カスタムのアウトプット形式で出力することもできます。
まずは、Repoterインターフェースを実装します。

```kotlin
class CustomReporter(private val out: PrintStream) : Reporter {
  private val errors = ArrayList<LintError>()

  override fun onLintError(
      file: String,
      err: LintError,
      corrected: Boolean) {
    errors.add(err)
  }
  override fun afterAll() {
    out.println("hello custom reporter.")
    out.println("found ${errors.size} errors.")
  }
}
```

次に、ReporterProviderインターフェースを実装したものに、先程の`CustomReporter`を指定します。

```kotlin
class CustomReporterProvider : ReporterProvider {
  
  override val id: String = "custom-ktlint-reporter"
  
  override fun get(
      out: PrintStream, 
      opt: Map<String, String>
  ): Reporter = CustomReporter(out)
  
}
```

最後に`META-INF/services/com.github.shyiko.ktlint.core.RuleSetProvider`にReporterProviderを登録します。

```META-INF/services/com.github.shyiko.ktlint.core.RuleSetProvider
com.takusemba.CustomRuleSetProvider
```

最後に、jarを生成して、`-R`オプションで指定します。

```groovy

task sourcesJar(type: Jar, dependsOn: classes) {
  classifier = 'sources'
  from sourceSets.main.allSource
}

task ktlint(type: JavaExec, group: "kotlin verification") {
  description = 'Check Kotlin code style.'
  args 'src/**/*.kt' , '-R', 'path/to/hoge.jar'
  main = 'com.github.shyiko.ktlint.Main'
  classpath = configurations.ktlint + sourceSets.main.output
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/93589/3683a8d8-aeb8-6415-290a-a08a01944326.png)

# ルールの除外
Ktlintであるコードに対して特定のルールまたは、全てのルールを適応外にすることができます。


## 特定のルール(no-wildcard-imports)を除外
```kotlin
import package.* // ktlint-disable no-wildcard-imports
```
## 全てのルールを除外
```kotlin
import package.* // ktlint-disable
```

では、対象とするコード全てに対して特定のルールを除外するにはどうすればよいのでしょうか。
ktlintには、特定のルールを除外する方法はありません。


しかし、作者いわく、このアプローチは意図的らしいです。
理由はこちらを御覧ください。
https://github.com/shyiko/ktlint/issues/21

しかし、ワークアラウンドは用意されています、

ktlintは最初に`.editorconfig`のスタイル設定を読み込みに行くため、そこに独自のスタイルを明記しておけば、そのスタイルに従ってlintがかけられます。

##### .editorconfig
```
[*.{kt,kts}]
indent_size=2
continuation_indent_size=4
insert_final_newline=unset
max_line_length=off
```


# 最後に
今回は基本的はktlintの使い方を紹介しました。
Ktlintを使った独自ルールの追加方法については、こちらの記事を書きましたので、見てみてください。
https://qiita.com/takusemba/items/77e882cada7daf6f35ec