ktlint是一个自带格式化的静态代码分析工具,可用于规范化kotlin代码风格,还可以自动格式化代码,大大节省手动格式化的时间。简单来说,ktlint是一个包含了linter和formatter的静态代码分析工具。
特性(Features)
- 无须配置。也就是说无须讨论决定使用什么代码规范,也无须去管理特定文件。ktlint会从 kotlinlang.org 和 Android Kotlin Style Guide 中获取官方代码规范。当然 ktlint 不需要配置规则文件不意味着不允许扩展,ktlint 支持自定义.editorconfig 和规则集(ruleset)。
- 内置格式化工具。因此你无须手动去修复不规范的代码。
- 自定义化输出。在检查代码格式时,可以指定任何类型的输出,例如
plain
(+plain?group_by_file
,默认输出格式),json
,checkstyle
等类型,同时也很容易创建自己的输出格式。 - 已包含所有依赖的单个jar文件,即不需要引入其他的依赖,只需要下载单个jar文件即可。
标准规则(Standard rules)
- 缩进用4个space(除非在.editorConfig中设置了不同的
indent_size
值(使用方法参考EditorConfig)); - 无分号(除非用于在同一行上分隔多个语句);
- 不使用通配符和无用的
import
; - 无连续空行;
}
之前没有空行;- 无尾部空白;
- 不使用
Unit
返回值(即用fun fn {}
代替fun fn: Unit {}
); - 没有空的(
{}
) 类主体; - 范围(
..
)运算符周围没有空格; +
,-
,*
,/
,%
,&&
,||
等二进制运算符之前无换行符;- 在链式调用中,
.
,?.
和?:
应该放在下一行 - 在赋值(
=
)操作符处分行时,后续内容紧跟在赋值符号后面; - 当类/函数签名不适合单行时,每个参数必须位于单独一行;
- 统一字符串模板(
$v
而不是${v}
,${p.v}
而不是${p.v.toString()}
); - 统一修饰剂的顺序;
- 统一关键字,逗号,冒号,大括号,中缀运算符,注释等后面的间距;
- 在每个文件末尾换行(默认情况下不使用,但推荐使用)(在.editorConfig中设置
insert_final_newline=true
便可使用) 。
EditorConfig
ktlint可以识别以下.editorconfig中的属性(需要在[*.{kt,kts}]
下面指定):
(下面显示的值为默认值,不需要显示指定)
[*.{kt,kts}]
# possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
indent_size=4
# possible values: number (e.g. 2), "unset"
continuation_indent_size=4
# true (recommended) / false
insert_final_newline=unset
# possible values: number (e.g. 120) (package name, imports & comments are ignored), "off"
# it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide)
max_line_length=off
安装
如果不需要使用ktlint的命令行接口,可以直接跳到集成,可任选以下一种进行安装。
- 使用 curl (所有平台通用方法,但速度较慢,不推荐)
curl -sSLO https://github.com/shyiko/ktlint/releases/download/0.29.0/ktlint &&chmod a+x ktlint && sudo mv ktlint /usr/local/bin/
-
直接从发布页面下载ktlint,下载成功将ktlint文件放到git gui(即MINGW64)安装目录的
/usr/local/bin
目录下,如D:\Program Files\Git\usr\local\bin
,之后执行chmod a+x ktlint
,确保ktlint为可执行文件。 -
在MacOS或Linux上,也可以使用 brew 进行安装:
brew install shyiko/ktlint/ktlint
-
使用 wget 代替 curl,只需用
wget -qO-
代替curl -sL
集成
Gradle集成
// 在module下的build.gradle中添加以下代码
repositories {
jcenter()
}
configurations {
ktlint
}
dependencies {
ktlint "com.github.shyiko:ktlint:0.29.0"
// additional 3rd party ruleset(s) can be specified here
// just add them to the classpath (e.g. ktlint 'groupId:artifactId:version') and
// ktlint will pick them up
}
task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
classpath = configurations.ktlint
main = "com.github.shyiko.ktlint.Main"
args "-a", "src/**/*.kt" // -a 表示代码规范使用 Android Kotlin Style Guide,不需要可以去掉,即改为 args "src/**/*.kt"
// to generate report in checkstyle format prepend following args:
// "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
// see https://github.com/shyiko/ktlint#usage for more
}
check.dependsOn ktlint
task ktlintFormat(type: JavaExec, group: "formatting") {
description = "Fix Kotlin code style deviations."
classpath = configurations.ktlint
main = "com.github.shyiko.ktlint.Main"
args "-F", "src/**/*.kt"
}
通过gradle ktlint
检查代码规范,通过gradle ktlintFormat
执行代码格式化。
以上为不使用插件的方式,也可以使用 jlleitschuh/ktlint-gradle 或 jeremymailen/kotlinter-gradle 插件执行 ktlint 的功能。不过在 Android Studio 的内部命令行窗口里,我们一般用 gradlew ktlint
执行ktlint命令。通过run gradle task 的方式来执行 ktlint 最终都会显示 BUILD FAILED
,但其实任务已经执行完成了,只是在显示完所有检测结果后显示以下错误而已,无须介意:
其他平台的集成方式请查阅 ktlint。
用法(Usage)
# 递归检查当前目录下所有kotlin文件的代码规范(隐藏的文件夹则会被跳过),--color作用为用不同颜色区分输出的结果
$ ktlint --color
# 结果如下:
src/main/kotlin/Main.kt:10:10: Unused import
# Andorid常用命令:
$ ktlint -a --color
# 检查指定文件 (用 ! 指定不检查指定文件)
$ ktlint "src/**/*.kt" "!src/**/*Test.kt"
# 自动格式化代码(会打印无法自动修复的错误)
$ ktlint -F
# or
$ ktlint -F "src/**/*.kt"
# 按照分组打印不规范代码,group_by_file表示按照文件分组,即同个文件的问题会打印在一起
$ ktlint --reporter=plain?group_by_file
# 依照指定输出格式打印不规范代码,下面命令表示在控制台按照plain输出,同时以checkstyle格式输出到ktlint-report-in-checkstyle-format.xml
$ ktlint --reporter=plain --reporter=checkstyle,output=ktlint-report-in-checkstyle-format.xml
# 安装git hook方便在commit前可以自动检查代码,在对应项目目录下执行以下命令
$ ktlint --install-git-pre-commit-hook
# 如果想要改为在push前检查则使用
$ ktlint --install-git-pre-push-hook
在window上需要使用 java -jar ktlint ....
,
可以使用 ktlint --help
查看更多用法
创建规则集(Creating a ruleset)
这里的规则集指的是包含了一个聚集了一个或多个规则的规则集的jar文件。
ktlint通过ServiceLoader
去发现classpath上所有可达的"RuleSet",
创建规则步骤:
- 添加依赖
com.github.shyiko.ktlint:ktlint-core:0.29.0
- 创建子类继承
Rule
,并重写visit
方法,实现自定义规则 - 创建子类继承
RuleSetProvider
,重写get
方法,返回自定义的 Rule 类 - 在
src/main
目录下创建resources/META-INF/services/com.github.shyiko.ktlint.core.RuleSetProvider
,并在该文件里输入自已的RuleSetProvider
全名,如com.fgj.expamle.CustomRuleSetProvider
- 将上述文件打包成 jar 文件,在运行 ktlint 命令时借助
-R
参数输入该jar文件的路径,如ktlint "**/ForTest.kt" -R pojectName/build/libs/customRule.jar --relative
; 若是使用 gradle 执行 ktlint,则可以将自定义的规则文件放到新的 module 里,然后在主 module 下的 build.gralde 中引入自定义规则的 module ,如
dependencies {
ktlint project(":custom-ktlint-rules")
}
See also Writing your first ktlint rule by Niklas Baudy.
class NoInternalImportRule : Rule("no-internal-import") {
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected:
Boolean) -> Unit
) {
if (node.elementType == KtStubElementTypes.IMPORT_DIRECTIVE) {
val importDirective = node.psi as KtImportDirective
val path = importDirective.importPath?.pathStr
if (path != null && path.contains("internal")) {
emit(node.startOffset, "Importing from an internal package",
false)
}
}
}
}
class CustomRuleSetProvider : RuleSetProvider {
override fun get()
= RuleSet("custom-ktlint-rules", NoInternalImportRule())
}
visit
方法中的 ASTNode
参数是 Abstract Syntax Tree
的缩写。任何编程语言的代码都可以呈现为 ASTNode 的集合,且它们都是树的结构。
禁用规则(Disable rule)
ktlint 虽然可以大大节省我们检查代码规范和格式化的时间,但有时某些代码我们并不希望被 ktlint 的规则检测到,或者说我们并不希望被修改为 ktlint 的代码风格,此时我们便可以对这部分代码屏蔽掉对应规则,例如,在 kotlin 中,是极不提倡使用通配符的,所以 ktlint 也是认为使用通配符 import
是不规范的,但我们的IDE却不这么认为,总会自动帮我们把同一个包的多个 import
合并为 import package.*
,此时我们并可以屏蔽这部分代码,具体如下:
import package.* // ktlint-disable no-wildcard-imports
或者使用一下格式屏蔽代码块
/* ktlint-disable no-wildcard-imports */
import package.a.*
import package.b.*
/* ktlint-enable no-wildcard-imports */
若是想禁用所有规则,则可以:
import package.* // ktlint-disable
不过这样依然需要为每个文件都添加这些注释,也是个耗时费力的差。遗憾的是,ktlint 目前并不支持全局禁用。
自定义输出格式(Creating a reporter)
自定义输出格式需要以下几个步骤:
- 实现
Reporter
类,在类中定义自己的格式 - 创建自定义的
ReporterProvider
- 通过
META-INF/services/com.github.shyiko.ktlint.core.ReporterProvider
进行注册 - 将它们打包为 jar 包
- 通过
ktlint --reporter=name,artifact=groupId:artifactId:version
或ktlint --reporter=name,artifact=/path/to/custom-ktlint-reporter.jar
加载自定义(或第三方)的输出格式jar文件
具体使用可参考ktlint-reporter-plain 或 mcassiano/ktlint-html-reporter