SwiftRewriter
SwiftRewriter copied to clipboard
π Swift code formatter using SwiftSyntax.
π SwiftRewriter
Swift code formatter using SwiftSyntax.
Requirements: Swift 5.1 (Xcode 11.0) + SwiftSyntax 0.50100.0
See also my iOSConf SG 2019 talk for more detail:
- Slide: Making your own Code Formatter in Swift - Speaker Deck
- Video: Make your own code formatter in Swift β iOS Conf SG 2019 β Learn Talks
Overview
-
SwiftRewriter
: Collection of reusable & composableSyntaxRewriter
s -
swift-rewriter
: Simple command-line executable
How to use
$ swift build
$ swift run swift-rewriter help
Available commands:
help Display general or command-specific help
print-ast print AST from file or string
run Auto-correct code in the file or directory
# Auto-correct code in the directory
$ swift run swift-rewriter run --path /path/to/file-or-directory
Configuration
In swift-rewriter
CLI tool, rewriting rules are configured in rewriter.swift (configuration file e.g. yaml or json is not supported yet).
- rewriter.swift
Please change the configuration as you like (you can make your own rewriter and combine!), and swift build & run
.
// rewriter.swift
import SwiftRewriter
var rewriter: Rewriter {
// Comment
HeaderCopyrightTrimmer()
// Move
>>> ImportSorter()
// >>> ExtensionIniter() // not useful for everyone
// Token
>>> DecimalLiteralUnderscorer()
>>> SemicolonTrimmer()
// Newline (whitespace)
// >>> ExtraNewliner() // not useful for everyone
>>> ElseNewliner(newline: false)
>>> MethodChainNewliner()
// Indent (whitespace)
>>> Indenter(.init(
perIndent: .spaces(4),
shouldIndentSwitchCase: false,
shouldIndentIfConfig: false,
skipsCommentedLine: true
))
// Space (whitespace)
// >>> ExtraSpaceTrimmer() // may disturb manually-aligned code
>>> ColonSpacer(spaceBefore: false, spaceAfter: true)
>>> TernaryExprSpacer()
>>> BinaryOperatorSpacer(spacesAround: true)
// Ignore to not distrub user-aligned multiple assignments
// TODO: Improve multiple assignment alignment
// >>> EqualSpacer(spacesAround: true)
>>> ArrowSpacer(spaceBefore: true, spaceAfter: true)
>>> LeftBraceSpacer(spaceBefore: true)
>>> LeftParenSpacer(spaceBefore: true)
>>> TrailingSpaceTrimmer()
}
Rewriter
examples
Indenter
Better right-brace position
@@ β1,6 +1,6 @@
βlets
β .code {
β }
β .format {
-} // this!!!
+ } // this!!!
P.S. This is the primary goal of making SwiftRewriter
.
First-item-aware indent
struct Foo {
init(bool: Bool,
int: Int) {
self.bool = bool
if true {
print()
}
run { x in
print(x,
y,
z)
}
}
}
will be:
struct Foo {
init(bool: Bool,
int: Int) {
self.bool = bool
if true {
print()
}
run { x in
print(x,
y,
z)
}
}
}
HeaderCopyrightTrimmer
@@ β1,10 +1,2 @@
-//
-// example.swift
-// SwiftRewriter
-//
-// Created by Yasuhiro Inami on 2018-12-09.
-// Copyright Β© 2018 Yasuhiro Inami. All rights reserved.
-//
-
β// All your code are belong to us.
ImportSorter
import C
import B
func foo() {}
import A
import D
will be:
import A
import B
import C
import D
func foo() {}
ExtensionIniter
This rewriter moves the code to enable struct
's memberwise initializer.
struct Foo {
let int: Int
init(int: Int) {
self.int = int
}
init() {
self.int = 0
}
}
@@ β1,9 +1,12 @@
βstruct Foo {
β let int: Int
+}
+
+extension Foo {
β init(int: Int) {
β self.int = int
β }
β init() {
β self.int = 0
β }
β}
ExtraNewliner
(Work in Progress)
This rewriter adds a newline when code is too dense.
import Foundation
var computed1: Int = 1
var computed2: Int = { return 2 }
/// doc
var computed3: Int = { return 3 }
/// doc
var computedBlock: String {
return ""
}
func send() -> Observable<Void> {
return apiSession
.send(request)
.do(onError: { [weak self] error in
guard let me = self else { return }
me.doSomething()
})
.do(onError: { [weak self] error in
guard let me = self else { return }
me.doSomething()
me.doSomething()
})
}
will be:
import Foundation
var computed1: Int = 1
var computed2: Int = { return 2 }
/// doc
var computed3: Int = { return 3 }
/// doc
var computedBlock: String {
return ""
}
func send() -> Observable<Void> {
return apiSession
.send(request)
.do(onError: { [weak self] error in
guard let me = self else { return }
me.doSomething()
})
.do(onError: { [weak self] error in
guard let me = self else { return }
me.doSomething()
me.doSomething()
})
}
Roadmap / TODO
- [ ] Add configuration file support
- [ ] Automatic code folding
- [ ] Move properties above method (for "states" readability)
- [ ] Move inner types to
extension
scope (for "states" readability) - [ ] Align multiline
=
assignments - [ ] (Your idea comes here π‘)
Acknowledgement
- Improving Swift Tools with libSyntax by @harlanhaskins
- Creating Refactoring Transformations for Swift by @nkcsgexi
- try! Swift Tokyo 2018 - AST Meta-programming by @kishikawakatsumi
- SwiftSyntax - NSHipster by @mattt
- SwiftLint and SwiftSyntax benchmarking by @jpsim et al.
License
MIT