Add hoistAwait rule (#1360)

This commit is contained in:
Facundo Menzella
2023-02-28 22:11:31 +00:00
committed by Nick Lockwood
parent 749d23ec81
commit 9518366da6
3 changed files with 145 additions and 0 deletions
+5
View File
@@ -83,6 +83,7 @@
* [blankLinesBetweenImports](#blankLinesBetweenImports)
* [blockComments](#blockComments)
* [docComments](#docComments)
* [hoistAwait](#hoistAwait)
* [isEmpty](#isEmpty)
* [markTypes](#markTypes)
* [organizeDeclarations](#organizeDeclarations)
@@ -747,6 +748,10 @@ Option | Description
</details>
<br/>
## hoistAwait
Reposition `await` keyword outside of the current scope.
## hoistPatternLet
Reposition `let` or `var` bindings within pattern.
+70
View File
@@ -4171,6 +4171,76 @@ public struct _FormatRules {
}
}
/// Reposition `await` keyword outside of the current scope.
public let hoistAwait = FormatRule(
help: "Reposition `await` keyword outside of the current scope.",
disabledByDefault: true,
options: []
) { formatter in
formatter.forEach(.startOfScope("(")) { idx, _ in
func insertAwait(at insertIndex: Int) {
let awaitIndex = formatter.index(of: .keyword("await"), before: idx)
guard awaitIndex == nil || formatter.tokens[awaitIndex! ..< idx].contains(
where: { $0.isStartOfScope || $0.isLinebreak }
) else {
return
}
formatter.insert([.keyword("await")], at: insertIndex)
if formatter.token(at: insertIndex + 1)?.isSpace == false {
formatter.insertSpace(" ", at: insertIndex + 1)
}
if insertIndex > 0,
let previousToken = formatter.token(at: insertIndex - 1),
!previousToken.isLinebreak,
previousToken.isSpace == false
{
formatter.insertSpace(" ", at: insertIndex)
}
}
guard let endIndex = formatter.index(of: .endOfScope(")"), after: idx),
formatter.lastSignificantKeyword(at: idx) != "if"
else {
return
}
var awaitIndexes: [Int] = []
var index = idx
while let next = formatter.index(of: .keyword("await"), after: index), index < endIndex {
awaitIndexes.append(next)
index = next
}
guard !awaitIndexes.isEmpty else { return }
let prevIndex = formatter.index(before: idx, where: {
$0.isSpaceOrLinebreak
})
awaitIndexes.reversed().forEach { awaitIndex in
formatter.removeToken(at: awaitIndex)
if formatter.tokens[awaitIndex].isSpace == true {
formatter.removeToken(at: awaitIndex)
}
}
if let prevIndex = prevIndex {
let token = formatter.token(at: prevIndex)
if token?.isLinebreak == true {
return insertAwait(at: prevIndex + 1)
} else {
return insertAwait(at: prevIndex)
}
} else {
insertAwait(at: 0)
}
}
}
/// Move `let` and `var` inside patterns to the beginning
public let hoistPatternLet = FormatRule(
help: "Reposition `let` or `var` bindings within pattern.",
+70
View File
@@ -604,6 +604,76 @@ class SyntaxTests: RulesTests {
testFormatting(for: input, rule: FormatRules.trailingClosures)
}
// MARK: - hoistAwait
func testHoistAwait() {
let input = "greet(await name, await surname)"
let output = "await greet(name, surname)"
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitInsideIfDoesNothing() {
let input = "if !(await isSomething()) {}"
testFormatting(for: input, rule: FormatRules.hoistAwait)
}
func testHoistAwaitInsideArgument() {
let input = """
array.append(contentsOf: try await asyncFunction(param1: param1))
"""
let output = """
await array.append(contentsOf: try asyncFunction(param1: param1))
"""
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitWithReturn() {
let input = "return .enumCase(try await service.greet())"
let output = "return await .enumCase(try service.greet())"
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitWithInitAssignment() {
let input = "let variable = String(try await asyncFunction())"
let output = "let variable = await String(try asyncFunction())"
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitWithAssignment() {
let input = "let variable = (try await asyncFunction())"
let output = "let variable = await (try asyncFunction())"
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitInRedundantScopePriorToNumber() {
let input = """
let identifiersTypes = 1
(try? await asyncFunction(param1: param1))
"""
let output = """
let identifiersTypes = 1
await (try? asyncFunction(param1: param1))
"""
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitOnlyOne() {
let input = "greet(name, await surname)"
let output = "await greet(name, surname)"
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitRedundantAwait() {
let input = "await greet(await name, await surname)"
let output = "await greet(name, surname)"
testFormatting(for: input, output, rule: FormatRules.hoistAwait)
}
func testHoistAwaitDoesNothing() {
let input = "await greet(name, surname)"
testFormatting(for: input, rule: FormatRules.hoistAwait)
}
// MARK: - hoistPatternLet
// hoist = true