SwiftFormat 使用指南
25 Dec 2017
目录
什么是 SwiftFormat?
SwiftFormat 是用于重新格式化 Swift 代码的命令行工具。
它会在保持代码意义的前提下,将代码进行格式化。
为什么要用 SwiftFormat?
很多项目都有固定的代码风格,统一的代码规范有助于项目的迭代和维护,而有的程序员却无视这些规则。同时,手动强制的去修改代码的风格又容易出错,而且没有人愿意去做这些无聊的工作。
如果有自动化的工具能完成这些工作,那几乎是最完美的方案了。在代码 review 时就不需要每次都强调无数遍繁琐的代码格式问题了。
使用指南
有好几种方式来安装这个工具。
- 命令行中手动执行,或者作为其他命令的一部分。
- 作为 Source Editor Extension,可以通过 Xcode 中的 Editor > SwiftFormat 菜单调用。
- 在 Xcode 项目的构建阶段,每次按 Cmd-R 或 Cmd-B 时执行。
- 可以作为 Git 的提交之前的 hook,每次提交代码之前都可以执行。
命令行工具
安装:
安装 swiftformat
最简单的方法是通过 Homebrew。如果你已经安装了 Homebrew,只需输入命令:
> brew update
> brew install swiftformat
就是这么简单。如果你想把 SwiftFormat 集成到你的项目中,也可以使用 CocoaPods —— 请参阅下面的 Xcode 构建阶段说明以获取详细信息。
或者,自己构建命令行应用程序:
-
打开
SwiftFormat.xcodeproj
并构建SwiftFormat
的 scheme。 -
将
swiftformat
二进制文件拖到/usr/local/bin/
目录下(这是一个隐藏文件夹,可以使用 Finder 的Go > Go to Folder ...
菜单打开)。 -
在文本编辑器中打开
〜/.bash_profile
(隐藏文件,可以在终端中使用open 〜/.bash_profile
来打开)。 -
在文件的最后追加:
alias swiftformat="/usr/local/bin/swiftformat --indent 4"
(--indent 4
可以忽略,或者换成别的)。运行swiftformat --help
查看可用选项)。 -
保存
.bash_profile
文件并运行命令source〜/.bash_profile
以使更改生效。
使用:
现在,只需要在命令行中(进入需要格式化的目录)输入:
swiftformat .
警告: swiftformat .
将覆盖所在当前目录中所有的 Swift 文件以及其中的所有子文件夹。如果从主目录运行它,它可能会重新格式化硬盘上的每个 Swift 文件。
要安全地使用它,请执行以下操作:
-
选择想要应用更改的文件或目录。
-
确保你已经在 git(或者其他任何版本控制工具)中提交过之前的修改。
-
(可选)在终端中,输入
swiftformat --inferoptions "/path/to/your/code/"
。这将会列出一组格式化选项来与现有项目风格相匹配,不过可以忽略而使用默认设置,或者干脆自己定制。
路径可以指向一个 Swift 文件或一个文件目录。既可以是绝对路径,也可以是相对路径。路径周围的 ""
是可选的,但如果路径中包含空格,则需要使用引号或用 \
进行转义。
- 在终端中,输入
swiftformat "/path/to/your/code/"
,将会应用与上面同样的格式化操作。可以使用空格将多个路径的作为参数。
在步骤 3 中使用了 --inferoptions
选项并生成了建议的选项的话,则应该将其添加到命令中(放在路径的前面或后面都是可以的)。
-
按回车开始格式化。格式化完成后,使用版本控制工具检查更改,并确认没有引入不需要的更改。如果有,则需要恢复更改,调整选项,然后再试一次。
-
(可选)提交修改。
也可以使用 unix pipes 将 swiftformat 作为命令链的一部分。例如,以下:
cat /path/to/file.swift | swiftformat --output /path/to/file.swift
去掉 --output /path/to/file.swift
将会将其输出到 stdout
。
Xcode Source Editor Extension
安装:
在 SwiftFormat 项目下,有一个 EditorExtension
目录,其中包含了最新版本的 SwiftFormat for Xcode
。将其拖到 Applications
目录下,然后双击启动,然后按照屏幕上的说明进行操作。
注意: 该扩展需要 Xcode 8 和 MacOS 10.12 Sierra 以上版本。
使用:
在 Xcode 中,可以在 Editor 菜单下找到一个 SwiftFormat 选项。您可以使用它来格式化当前选择或整个文件。
其他
至于 Xcode build phase
、Git pre-commit hook
和 On CI using Danger
,本文就不介绍了,具体查看这里吧。
工作原理
SwiftFormat 首先将源文件转换为标识符,然后循环将一组规则应用于这些标识符进行格式调整。标识符最后被转换回文本。
SwiftFormat 的配置分为规则和选项。SwiftFormat 中的规则是用来修改代码的函数。选项则是控制规则的设置。
具体的选项和规则还没有应用过,所以暂时没有做整理。
选项
选项请参阅这里。
规则
规则请参阅这里。
FAQ
FAQ 请参阅这里。
缓存
SwiftFormat 使用缓存文件来避免重新格式化没有修改的文件。对于大型项目,可以显著减少处理时间。
默认情况下,缓存保存在 〜/Library/Caches/com.charcoaldesign.swiftformat
中。可以使用命令行选项 --cache ignore
来忽略缓存的版本,并重新对所有文件应用格式化。或者,您可以使用 --cache clear
来删除缓存(也可以手动删除缓存文件)。
缓存在所有项目之间共享。缓存文件很小,因为只存储了每个文件的路径和大小,而不是文件内容。如果由于缓存增长过大而开始出现变慢的情况,则可能需要考虑为每个项目使用单独的缓存文件。可以通过设置 --cache
参数来自定义缓存文件位置。例如,可以将缓存文件存储在项目目录中。同一项目的不同用户可以共享缓存文件,因为缓存文件中的路径是相对于目标文件的。
文件头
SwiftFormat 通过模板去除或替换每个文件中的头注释。“头注释”被定义为从文件的第一个非空行开始的注释块,后面至少有一个空行。这可以由单个注释组成,也可能由多个注释组成:
// 这是头注释
// 这是普通注释
func foo(bar: Int) -> Void { ... }
标题模板是使用 --header
命令行选项传入的字符串。参数为 ignore
(默认)则不会修改头注释。使用 strip
或空字符串 ""
将会移除头注释。如果要提供一个自定义的头文件模板,格式如下:
单行模板:--header "Copyright (c) 2017 Foobar Industries"
对于多行注释,用 \n
标记换行符:--header "First line\nSecond line"
还可以选择在模板中包含 Swift 注释标记:--header "/*--- Header comment ---*/"
。
如果不包含注释标记,模板中的每一行都会以 //
加一个空格开头。
头标题的最后,通常是包含当前年份的版权声明,使用以下命令:
--header "Copyright (c) {year} Foobar Industries"
每当应用 SwiftFormat 时,{year} 标记将被自动替换为当前年份(注意:年份由运行脚本的机器的语言环境和时区确定)。
已知问题
-
当使用
--self remove
选项时,redundantSelf
规则将会移除闭包中的self
,这将会改变代码的逻辑,或者编译失败。目前,只能使用--disable redundantSelf
来避免副作用。使用--self insert
选项则不会有类似问题。 -
--self insert
选项只能识别本地声明的成员变量,而无法识别从父类继承的或扩展的成员变量,所以在这些情况下无法实现self
插入。 -
trailingClosures
规则有时会生成有危害的模糊代码,所以这个规则默认是禁用的。建议手动应用此规则并检查修改后的内容,而不要将其包含在自动格式化中。 -
极少的情况下,SwiftFormat 可能会错误地解析泛型类型后面的
<>=
中的=
符号。例如,下面代码将会处理失败:
let foo: Dictionary<String, String>=["Hello": "World"]
要解决这个问题,可以在 =
字符周围手动添加空格以消除模糊性,或者在命令行选项中添加 --disable spaceAroundOperators
。
-
如果一个文件以头注释开始并且注释后跟着一个空行,
stripHeaders
规则会将其删除。为避免这种情况,请确保第一个注释后面紧接着一行代码。 -
SwiftFormat目前重新格式化多行注释块,而不考虑原始缩进。也就是说:
/* some documentation
func codeExample() {
print("Hello World")
}
*/
会变为
/* some documentation
func codeExample() {
print("Hello World")
}
*/
要解决该问题,可以使用 comments
命令行标志来禁用自动缩进注释功能。
或者,如果想要启用注释缩进功能,则可以将多行注释重写为一行单行注释:
// some documentation
//
// func codeExample() {
// print("Hello World")
// }
//
//
或者每一行用 *
(或任何其他非空白字符)开始:
/* some documentation
*
* func codeExample() {
* print("Hello World")
* }
*
*/
- 格式化的文件缓存基于文件长度,则会有可能出现修改前后字符数相同的情况,这回导致 SwiftFormat 无法鉴别出文件的修改从而使得格式化失败。
要解决该问题,可以使用 --cache ignore
强制忽略缓存文件,或者在文件中额外追加一个空格(SwiftFormat 会在格式化时删除它)。