Skip to content

Commit 0654d1a

Browse files
authored
Merge pull request swiftlang#20 from allevato/sr-11106
Don't break before `[` or `{` in single argument function calls.
2 parents 0d148a3 + 82e3e16 commit 0654d1a

File tree

4 files changed

+77
-9
lines changed

4 files changed

+77
-9
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -622,25 +622,23 @@ private final class TokenStreamCreator: SyntaxVisitor {
622622
}
623623

624624
func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind {
625-
var argumentIterator = node.argumentList.makeIterator()
626-
let firstArgument = argumentIterator.next()
627-
628-
if firstArgument != nil {
625+
if shouldBreakAroundArgumentList(of: node) {
629626
// If there is a trailing closure, force the right parenthesis down to the next line so it
630627
// stays with the open curly brace.
631628
let breakBeforeRightParen = node.trailingClosure != nil
632629

633630
after(node.leftParen, tokens: .break(.open, size: 0), .open(argumentListConsistency()))
634631
before(
635-
node.rightParen,
636-
tokens: .break(.close(mustBreak: breakBeforeRightParen), size: 0), .close)
632+
node.rightParen, tokens: .break(.close(mustBreak: breakBeforeRightParen), size: 0), .close)
637633
}
638634
before(node.trailingClosure?.leftBrace, tokens: .break(.reset))
639635
return .visitChildren
640636
}
641637

642638
func visit(_ node: FunctionCallArgumentSyntax) -> SyntaxVisitorContinueKind {
643-
before(node.firstToken, tokens: .open)
639+
if node.colon != nil {
640+
before(node.firstToken, tokens: .open)
641+
}
644642

645643
// If we have an open delimiter following the colon, use a space instead of a continuation
646644
// break so that we don't awkwardly shift the delimiter down and indent it further if it
@@ -649,8 +647,12 @@ private final class TokenStreamCreator: SyntaxVisitor {
649647
after(node.colon, tokens: tokenAfterColon)
650648

651649
if let trailingComma = node.trailingComma {
652-
after(trailingComma, tokens: .close, .break(.same))
653-
} else {
650+
var afterTrailingComma: [Token] = [.break(.same)]
651+
if node.colon != nil {
652+
afterTrailingComma.insert(.close, at: 0)
653+
}
654+
after(trailingComma, tokens: afterTrailingComma)
655+
} else if node.colon != nil {
654656
after(node.lastToken, tokens: .close)
655657
}
656658
return .visitChildren
@@ -1939,6 +1941,28 @@ private final class TokenStreamCreator: SyntaxVisitor {
19391941
default: return false
19401942
}
19411943
}
1944+
1945+
/// Returns true if open/close breaks should be inserted around the entire argument list of the
1946+
/// given function call expression.
1947+
private func shouldBreakAroundArgumentList(of node: FunctionCallExprSyntax) -> Bool {
1948+
let argumentCount = node.argumentList.count
1949+
1950+
// If there are no arguments, there's no reason to break.
1951+
if argumentCount == 0 { return false }
1952+
1953+
// If there is more than one argument, we must open/close break around the whole list.
1954+
if argumentCount > 1 { return true }
1955+
1956+
// At this point, we know there is only one argument in the list. If it's unlabeled and it's an
1957+
// array, dictionary, or closure literal, we shouldn't open/close break around it; it will look
1958+
// better if we keep the neighboring parentheses and brackets together and wrap inside them
1959+
// instead.
1960+
let firstArgument = node.argumentList.first!
1961+
let expression = firstArgument.expression
1962+
return firstArgument.colon != nil
1963+
|| !(expression is ArrayExprSyntax || expression is DictionaryExprSyntax
1964+
|| expression is ClosureExprSyntax)
1965+
}
19421966
}
19431967

19441968
extension Syntax {

Tests/SwiftFormatPrettyPrintTests/CommentTests.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ public class CommentTests: PrettyPrintTestCase {
109109
// Comment 5
110110
}
111111
112+
let a = myfun(123 // Cmt 7
113+
)
112114
let a = myfun(var1: 123 // Cmt 7
113115
)
114116
@@ -159,6 +161,9 @@ public class CommentTests: PrettyPrintTestCase {
159161
// Comment 5
160162
}
161163
164+
let a = myfun(
165+
123 // Cmt 7
166+
)
162167
let a = myfun(
163168
var1: 123 // Cmt 7
164169
)
@@ -342,6 +347,8 @@ public class CommentTests: PrettyPrintTestCase {
342347
343348
let reallyLongVariableName = 123 /* This comment should wrap */
344349
350+
let a = myfun(123 /* Cmt 5 */
351+
)
345352
let a = myfun(var1: 123 /* Cmt 5 */
346353
)
347354
@@ -366,6 +373,9 @@ public class CommentTests: PrettyPrintTestCase {
366373
let reallyLongVariableName
367374
= 123 /* This comment should wrap */
368375
376+
let a = myfun(
377+
123 /* Cmt 5 */
378+
)
369379
let a = myfun(
370380
var1: 123 /* Cmt 5 */
371381
)

Tests/SwiftFormatPrettyPrintTests/FunctionCallTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,37 @@ public class FunctionCallTests: PrettyPrintTestCase {
162162

163163
assertPrettyPrintEqual(input: input, expected: expected, linelength: 45)
164164
}
165+
166+
public func testSingleUnlabeledArgumentWithDelimiters() {
167+
let input =
168+
"""
169+
myFunc([1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000])
170+
myFunc(["foo": "bar", "baz": "quux", "glip": "glop"])
171+
myFunc({ foo, bar in baz(1000, 2000, 3000, 4000, 5000) })
172+
myFunc([1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000]) { foo in bar() }
173+
"""
174+
175+
let expected =
176+
"""
177+
myFunc([
178+
1000, 2000, 3000, 4000, 5000, 6000, 7000,
179+
8000
180+
])
181+
myFunc([
182+
"foo": "bar", "baz": "quux", "glip": "glop"
183+
])
184+
myFunc({ foo, bar in
185+
baz(1000, 2000, 3000, 4000, 5000)
186+
})
187+
myFunc([
188+
1000, 2000, 3000, 4000, 5000, 6000, 7000,
189+
8000
190+
]) { foo in
191+
bar()
192+
}
193+
194+
"""
195+
196+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 45)
197+
}
165198
}

Tests/SwiftFormatPrettyPrintTests/XCTestManifests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ extension FunctionCallTests {
194194
("testBasicFunctionCalls_packArguments", testBasicFunctionCalls_packArguments),
195195
("testDiscretionaryLineBreakBeforeClosingParenthesis", testDiscretionaryLineBreakBeforeClosingParenthesis),
196196
("testDiscretionaryLineBreaksAreSelfCorrecting", testDiscretionaryLineBreaksAreSelfCorrecting),
197+
("testSingleUnlabeledArgumentWithDelimiters", testSingleUnlabeledArgumentWithDelimiters),
197198
]
198199
}
199200

0 commit comments

Comments
 (0)