- 原文地址:Swift Code Formatters
- 原文做者:Mattt
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:iWeslie
- 校对者:swants, fireairforce
我刚离开了一家时髦的咖啡馆。里面有不少 iOS 开发者,他们互相窃窃私语,讨论他们是多么得等不及苹果公司为 Swift 发布的官方风格指南和格式化程序。javascript
在过去的几天里,社区一直在讨论 Tony Allevato 和 Dave Abrahams 采用官方版的 Swift 格式化工具。html
数十名社区成员已经对 提案草案 进行了权衡。与全部样式问题同样,每一个人都有不一样的意见。但幸运的是,来自社区的话语一般具备表明性和洞察力,其中清晰表达了各式各样的观点、用例以及关注点。前端
在撰写本文时,彷佛不少但不是全部受访者都对官方格式化表示赞同。那些同意给代码格式化约束的人也但愿有一个工具能够自动诊断和修复不符合这些约束的代码。可是,其余一些人对这格式化的适用性和可配置性表示担心。java
在本周的 NSHipster 上,咱们将讨论一下关于当前可用的 Swift 格式化程序,包括做为提案的一部分发布的 swift-format
工具,同时看看它们是如何进行处理的。而后咱们再权衡其中的利弊。android
首先,让咱们从下面的一个问题开始:ios
咱们将代码格式化定义为对代码所作的任何更改,以便在不改变其行为的状况下更容易理解代码的内容。虽然这个定义延伸到等价形式的差别(例如 [Int]
和 Array <Int>
),咱们暂且将这里的讨论内容限制为空格和标点符号。git
与许多其余编程语言同样,Swift 在接受换行符,制表符和空格时都很是自由。大多数空格都是可有可无的,从编译器的角度来看对代码没有任何影响。程序员
当咱们使用空格来使代码更易于理解而不改变其行为时,此时空格就做为一个 辅助符号。固然,主要符号是代码自己。github
另外一种辅助表示法是语法高亮,这在咱们之前的 NSHipster 文章 中讨论过.编程
虽然你能够经过分号在一行代码中写几乎任何东西,可是在其余条件相同的状况下,使用空格与换行来对齐代码难道不是一种更易于理解而且直观的方式吗?
不幸的是,因编译器接受空格的性质而产生的歧义每每会引发程序员之间对代码的混淆和分歧:“我到底应不该该在大括号以前添加换行符?如何分解超出编辑器宽度的语句?”
一个公司一般会有本身的一套代码规范,但它们一般不够明确,执行力不强,并且可能已通过时。代码格式化程序的做用是自动执行一组约定,以便程序员能够抛开差别来把重心放到解决实际问题上。
Swift 社区从一开始就考虑过代码格式问题,代码书写规范从 Swift 诞生之初就已经存在,各类开源工具也能够经过规范来自动化格式化代码。
你能够看看如下四个工具来了解目前 Swift 代码格式化程序的状态:
项目 | 仓库连接 |
---|---|
SwiftFormat | github.com/nicklockwoo… |
SwiftLint | github.com/realm/Swift… |
Prettier with Swift Plugin | github.com/prettier/pr… |
swift-format(已提议) | github.com/google/swif… |
为简洁起见,本文仅讨论一些可用的 Swift 格式化工具。若是你有兴趣能够了解如下更多内容:Swimat,SwiftRewriter 和 swiftfmt。
为了方便比较,咱们设计了如下代码来评估每一个工具,工具都使用它们的默认配置:
struct ShippingAddress : Codable {
var recipient: String
var streetAddress : String
var locality :String
var region :String;var postalCode:String
var country:String
init(recipient: String, streetAddress: String,
locality: String,region: String,postalCode: String,country:String)
{
self.recipient = recipient
self.streetAddress = streetAddress
self.locality = locality
self.region = region;self.postalCode=postalCode
guard country.count == 2, country == country.uppercased() else { fatalError("invalid country code") }
self.country=country}}
let applePark = ShippingAddress(recipient:"Apple, Inc.", streetAddress:"1 Apple Park Way", locality:"Cupertino", region:"CA", postalCode:"95014", country:"US")
复制代码
尽管代码格式化包含各类可能的语法和语义转换,但咱们将专一于换行和缩进的问题,咱们认为这是任何代码格式化程序的基础要求。
诚然,本文中的性能基准并非很是严格。可是它们应该能够做为参考。咱们把秒做为测量时间的单位,采用 2017 款配备 2.9 GHz Intel Core i7 处理器和 16 GB 2133 MHz LPDDR3 内存的 MacBook Pro。
首先是 SwiftFormat,它的简介是一个颇有用的工具。
SwiftFormat 能够经过 Homebrew,Mint 以及 CocoaPods 来安装。
你能够经过如下命令来安装它:
$ brew install swiftformat
复制代码
此外,SwiftFormat 还提供了一个 Xcode 拓展,你能够经过在 Xcode 里使用它进行格式化。或者若是你是 VSCode 用户,你可使用 此插件。
swiftformat
命令会把在指定文件和目录路径中找到的全部 Swift 文件进行格式化。
$ swiftformat Example.swift
复制代码
SwiftFormat 有不少规则,你能够经过命令行选项或使用配置文件单独配置。
使用默认配置运行 swiftformat
后,你会获得如下输出:
// swiftformat version 0.39.5
struct ShippingAddress: Codable {
var recipient: String
var streetAddress: String
var locality: String
var region: String; var postalCode: String
var country: String
init(recipient: String, streetAddress: String,
locality: String, region: String, postalCode: String, country: String) {
self.recipient = recipient
self.streetAddress = streetAddress
self.locality = locality
self.region = region; self.postalCode = postalCode
guard country.count == 2, country == country.uppercased() else { fatalError("invalid country code") }
self.country = country
}
}
let applePark = ShippingAddress(recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA", postalCode: "95014", country: "US")
复制代码
正如你所见,格式化明显改进了原始版本。每一行都根据其范围缩进,标点符号间的声明都有一致的间距。属性声明中的分号和初始化参数中的换行都被保留。可是,大括号没有像预期的那样被移动到单独一行,Nick 在 0.39.5 修复了它。
SwiftFormat 在本文中测试工具中始终是最快的,它能在几毫秒内完成处理。
$ time swiftformat Example.swift
0.03 real 0.01 user 0.01 sys
复制代码
接下来是 SwiftLint,它是 Swift 开源社区的支柱。其中包含了超过 100 个规则,SwiftLint 能够对你的代码执行各类各样的检查,从将 AnyObject
优于 class
(仅用于类的协议)到所谓的“尤达条件式”,其中规定变量应放在比较运算符的左侧(即 if n == 42
而非 if 42 == n
)。
顾名思义,SwiftLint 主要不是代码格式化程序,它确实是一种识别滥用惯例和误用 API 的诊断工具。正是因为具有自动校订功能,它经常被用于格式化代码。
你能够经过如下命令使用 Homebrew 来安装它:
$ brew install swiftlint
复制代码
或者你也能够经过 CocoaPods,Mint 或者 独立 pkg 安装包 来安装。
要用 SwiftLint 来进行代码格式化,请运行 autocorrect
子命令,--format
参数为须要执行的文件或目录。
$ swiftlint autocorrect --format --path Example.swift
复制代码
运行上述命令,你将获得如下输出:
// swiftlint version 0.31.0
struct ShippingAddress: Codable {
var recipient: String
var streetAddress: String
var locality: String
var region: String;var postalCode: String
var country: String
init(recipient: String, streetAddress: String,
locality: String, region: String, postalCode: String, country: String) {
self.recipient = recipient
self.streetAddress = streetAddress
self.locality = locality
self.region = region;self.postalCode=postalCode
guard country.count == 2, country == country.uppercased() else { fatalError("invalid country code") }
self.country=country}}
let applePark = ShippingAddress(recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA", postalCode: "95014", country: "US")
复制代码
SwiftLint 能够清除最糟糕的缩进和行内间距问题,同时保留其余无关的空格。值得注意的是,格式化不是 SwiftLint 的主要做用,通常状况下,它只是附带提供可操做的代码诊断。从 “首先,没有任何反作用” 的角度来看,你没必要在这里抱怨它处理的结果。
SwiftLint 检查的全部内容很是高效,在咱们的示例中它只需几分之一秒便可完成。
$ time swiftlint autocorrect --quiet --format --path Example.swift
0.11 real 0.05 user 0.02 sys
复制代码
若是你没有使用过 JavaScript(如 上周文章 中所述),这多是你据说过的第一篇 Prettier 相关的文章。反之,若是你沉浸在 ES6,React 和 WebPack 的世界中,你几乎确定会特别依赖它。
Prettier 在代码格式化程序中是独一无二的,由于它体现了代码的美学,用换行来分隔代码,就好像写诗同样。
多亏了一个在开发中的 插件架构,你能够在其余语言上使用它,其中 包括了 Swift。
Swift 的 Prettier 插件很是重要,可是当它遇到一个没有规则的语法标记时会崩溃(好比
EnumDecl
😩)。然而,正如你将在下面看到的那样,到目前为止它的功能仍是很强大以致于咱们不能忽视它,因此把它放在本文格式化工具 PK 中是值得的。
要使用 Prettier 及其 Swift 插件,你将涉及到 Node。虽然有不少方法能够安装它,但咱们仍是最喜欢 Yarn 😻。
$ brew install yarn
$ yarn global add prettier prettier/plugin-swift
复制代码
如今环境变量 $PATH
里已经能够访问 prettier
命令行工具,你能够传入文件或路径来运行它。
$ prettier Example.swift
复制代码
如下是在咱们的前面的例子中使用 Prettier 运行最新版本的 Swift 插件获得的输出:
// prettier version 1.16.4
// prettier/plugin-swift version 0.0.0 (bdf8726)
struct ShippingAddress: Codable {
var recipient: String
var streetAddress: String
var locality: String
var region: String
var postalCode: String
var country: String
init(
recipient: String,
streetAddress: String,
locality: String,
region: String,
postalCode: String,
country: String
) {
self.recipient = recipient
self.streetAddress = streetAddress
self.locality = locality
self.region = region;
self.postalCode = postalCode
guard country.count == 2, country == country.uppercased() else {
fatalError("invalid country code")
}
self.country = country
}
}
let applePark = ShippingAddress(
recipient: "Apple, Inc.",
streetAddress: "1 Apple Park Way",
locality: "Cupertino",
region: "CA",
postalCode: "95014",
country: "US"
)
复制代码
Prettier 将本身称为 “一个自觉得是的代码格式化程序”。实际上,它的配置方式很少,只有两个选项:“常规代码” 或 “更漂亮的代码”。
如今,你可能会面对不少垂直的对齐,但你不得不认可这段代码看起来确实很棒。全部的语句都有均匀间隔、缩进以及对齐,很难相信这是自动实现的。
固然,咱们以前的警告仍然适用:这仍然在开发中,并不适合生产环境使用,并且它还有一些性能问题。
说到底,Prettier 比本文讨论的其余工具都慢一到两个数量级。
$ time prettier Example.swift
1.14 real 0.56 user 0.38 sys
复制代码
目前还不清楚这究竟是语言障碍仍是优化不良的结果,Prettier 慢得足以带来很大的问题。
目前,咱们建议仅将 Prettier 用于一次性格式化任务,例如编写文章和书籍的代码。
在了解可用于 Swift 格式化程序的现状后,咱们如今有了一个合理的标准来评估 Tony Allevato 和 Dave Abrahams 提出的 swift-format
工具。
swift-format
的代码目前托管在 Google fork 的 Swift 的 format
分支 上。你能够经过运行如下命令来下载而且从源代码编译它:
$ git clone https://github.com/google/swift.git swift-format
$ cd swift-format
$ git submodule update --init
$ swift
复制代码
为了让你方便使用,咱们提供了一个自制的公式,该公式来自 咱们本身 Google 仓库上的 fork,你能够经过下面的命令来安装:
$ brew install nshipster/formulae/swift-format
复制代码
运行 swift-format
命令,传入要格式化的 Swift 文件或目录。
$ swift-format Example.swift
复制代码
swift-format
命令还采用了 --configuration
选项,该参数须要传入一个 JSON 文件。目前,定制 swift-format
行为的最简单方法就是将默认配置转储到文件里。
$ swift-format -m dump-configuration .swift-format.json
复制代码
建立以下的 JSON 文件,在运行上述命令时传入:
{
"blankLineBetweenMembers": {
"ignoreSingleLineProperties": true
},
"indentation": {
"spaces": 2
},
"lineLength": 100,
"maximumBlankLines": 1,
"respectsExistingLineBreaks": true,
"tabWidth": 8,
"version": 1
}
复制代码
在配置完以后,你应该这样使用:
$ swift-format Example.swift --configuration .swift-format.json
复制代码
使用其默认配置,这里是 swift-format
格式化后的输出:
// swift-format version 0.0.1
struct ShippingAddress: Codable {
var recipient: String
var streetAddress: String
var locality: String
var region :String;
var postalCode: String
var country: String
init(
recipient: String, streetAddress: String,
locality: String, region: String, postalCode: String, country: String
)
{
self.recipient = recipient
self.streetAddress = streetAddress
self.locality = locality
self.region = region
self.postalCode = postalCode
guard country.count == 2, country == country.uppercased() else {
fatalError("invalid country code")
}
self.country = country
}
}
let applePark = ShippingAddress(
recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA",
postalCode: "95014", country: "US")
复制代码
随着 0.0.1
版本的发布,这好像颇有但愿!咱们能够在没有原始分号的状况下实现,也不用太关心 region
属性的冒号位置,但总的来讲,这是无可非议的,它正是你想要的官方代码格式化工具。
在性能方面,swift-format
目前处于中间位置,不快也不慢。
$ time swift-format Example.swift
0.51 real 0.20 user 0.27 sys
复制代码
根据咱们有限的初步调查,swift-format
彷佛提供了一套合理的格式约定。但愿将来它能建立出更加生动的例子来帮助咱们理解其中的格式化规则。
不管如何,看到提案如何发展以及围绕这些问题展开讨论都将是一件颇有趣的事。
NSMutableHipster
若是你有其余问题,欢迎给咱们提 Issues 和 pull requests。
这篇文章使用 Swift 5.0.。你能够在 状态页面 上查找全部文章的状态信息。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。