Skip to content

Commit 2825f93

Browse files
authored
Merge pull request swiftlang#213 from dylansturg/try_grouping
Group the try keyword with a relevant portion of its expression.
2 parents d9ac67e + e0893d9 commit 2825f93

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -880,8 +880,13 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
880880

881881
if let calledMemberAccessExpr = node.calledExpression.as(MemberAccessExprSyntax.self) {
882882
if let base = calledMemberAccessExpr.base, base.is(IdentifierExprSyntax.self) {
883-
before(base.firstToken, tokens: .open)
884-
after(calledMemberAccessExpr.name.lastToken, tokens: .close)
883+
// When this function call is wrapped by a try-expr, the group applied when visiting the
884+
// try-expr is sufficient. Adding another gruop here in that case can result in
885+
// unnecessarily breaking after the try keyword.
886+
if !(base.firstToken?.previousToken?.parent?.is(TryExprSyntax.self) ?? false) {
887+
before(base.firstToken, tokens: .open)
888+
after(calledMemberAccessExpr.name.lastToken, tokens: .close)
889+
}
885890
}
886891
}
887892

@@ -1416,9 +1421,41 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
14161421

14171422
override func visit(_ node: TryExprSyntax) -> SyntaxVisitorContinueKind {
14181423
before(node.expression.firstToken, tokens: .break)
1424+
1425+
// Check for an anchor token inside of the expression to group with the try keyword.
1426+
if let anchorToken = findTryExprConnectingToken(inExpr: node.expression) {
1427+
before(node.tryKeyword, tokens: .open)
1428+
after(anchorToken, tokens: .close)
1429+
}
1430+
14191431
return .visitChildren
14201432
}
14211433

1434+
/// Searches the AST from `expr` to find a token that should be grouped with an enclosing
1435+
/// try-expr. Returns that token, or nil when no such token is found.
1436+
///
1437+
/// - Parameter expr: An expression that is wrapped by a try-expr.
1438+
/// - Returns: A token that should be grouped with the try-expr, or nil.
1439+
func findTryExprConnectingToken(inExpr expr: ExprSyntax) -> TokenSyntax? {
1440+
if let callingExpr = expr.asProtocol(CallingExprSyntaxProtocol.self) {
1441+
return findTryExprConnectingToken(inExpr: callingExpr.calledExpression)
1442+
}
1443+
if let memberAccessExpr = expr.as(MemberAccessExprSyntax.self), let base = memberAccessExpr.base
1444+
{
1445+
// When there's a simple base (i.e. identifier), group the entire `try <base>.<name>`
1446+
// sequence. This check has to happen here so that the `MemberAccessExprSyntax.name` is
1447+
// available.
1448+
if base.is(IdentifierExprSyntax.self) {
1449+
return memberAccessExpr.name.lastToken
1450+
}
1451+
return findTryExprConnectingToken(inExpr: base)
1452+
}
1453+
if expr.is(IdentifierExprSyntax.self) {
1454+
return expr.lastToken
1455+
}
1456+
return nil
1457+
}
1458+
14221459
override func visit(_ node: TypeExprSyntax) -> SyntaxVisitorContinueKind {
14231460
return .visitChildren
14241461
}

Tests/SwiftFormatPrettyPrintTests/TryCatchTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,46 @@ final class TryCatchTests: PrettyPrintTestCase {
231231

232232
assertPrettyPrintEqual(input: input, expected: expected, linelength: 25)
233233
}
234+
235+
func testTryKeywordBreaking() {
236+
let input =
237+
"""
238+
let aVeryLongArgumentName = try foo.bar()
239+
let aVeryLongArgumentName = try? foo.bar()
240+
let abc = try foo.baz().quxxe(a, b, c).bar()
241+
let abc = try foo
242+
.baz().quxxe(a, b, c).bar()
243+
let abc = try [1, 2, 3, 4, 5, 6, 7].baz().quxxe(a, b, c).bar()
244+
let abc = try [1, 2, 3, 4, 5, 6, 7]
245+
.baz().quxxe(a, b, c).bar()
246+
let abc = try foo.baz().quxxe(a, b, c).bar[0]
247+
let abc = try foo
248+
.baz().quxxe(a, b, c).bar[0]
249+
"""
250+
251+
let expected =
252+
"""
253+
let aVeryLongArgumentName =
254+
try foo.bar()
255+
let aVeryLongArgumentName =
256+
try? foo.bar()
257+
let abc = try foo.baz().quxxe(a, b, c)
258+
.bar()
259+
let abc =
260+
try foo
261+
.baz().quxxe(a, b, c).bar()
262+
let abc = try [1, 2, 3, 4, 5, 6, 7]
263+
.baz().quxxe(a, b, c).bar()
264+
let abc = try [1, 2, 3, 4, 5, 6, 7]
265+
.baz().quxxe(a, b, c).bar()
266+
let abc = try foo.baz().quxxe(a, b, c)
267+
.bar[0]
268+
let abc =
269+
try foo
270+
.baz().quxxe(a, b, c).bar[0]
271+
272+
"""
273+
274+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40)
275+
}
234276
}

0 commit comments

Comments
 (0)