Skip to content

Commit a2a2796

Browse files
authored
Merge pull request swiftlang#224 from google/fix-brace
Allow breaks to ignore discretionary newlines.
2 parents 2757517 + 4d891cd commit a2a2796

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

Sources/SwiftFormatPrettyPrint/PrettyPrint.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public class PrettyPrinter {
193193

194194
// Create a line break if needed. Calculate the indentation required and adjust spaceRemaining
195195
// accordingly.
196-
case .break(let kind, let size):
196+
case .break(let kind, let size, _):
197197
lastBreakKind = kind
198198
var mustBreak = forceBreakStack.last ?? false
199199
var isContinuation = false
@@ -346,7 +346,7 @@ public class PrettyPrinter {
346346

347347
// Break lengths are equal to its size plus the token or group following it. Calculate the
348348
// length of any prior break tokens.
349-
case .break(_, let size):
349+
case .break(_, let size, _):
350350
if let index = delimIndexStack.last, case .break = tokens[index] {
351351
lengths[index] += total
352352
delimIndexStack.removeLast()
@@ -436,9 +436,11 @@ public class PrettyPrinter {
436436
printDebugIndent()
437437
print("[SYNTAX \"\(syntax)\" Length: \(length)]")
438438

439-
case .break(let kind, let size):
439+
case .break(let kind, let size, let ignoresDiscretionary):
440440
printDebugIndent()
441-
print("[BREAK Kind: \(kind) Size: \(size) Length: \(length)]")
441+
print(
442+
"[BREAK Kind: \(kind) Size: \(size) Length: \(length) "
443+
+ "Ignores Discretionary NL: \(ignoresDiscretionary)]")
442444

443445
case .open(let breakstyle):
444446
printDebugIndent()

Sources/SwiftFormatPrettyPrint/Token.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ enum Token {
108108
case syntax(String)
109109
case open(GroupBreakStyle)
110110
case close
111-
case `break`(BreakKind, size: Int)
111+
case `break`(BreakKind, size: Int, ignoresDiscretionary: Bool)
112112
case space(size: Int)
113113
case newlines(Int, discretionary: Bool)
114114
case comment(Comment, wasEndOfLine: Bool)
@@ -126,9 +126,9 @@ enum Token {
126126

127127
static let space = Token.space(size: 1)
128128

129-
static let `break` = Token.break(.continue, size: 1)
130-
static func `break`(_ kind: BreakKind) -> Token {
131-
return .break(kind, size: 1)
129+
static let `break` = Token.break(.continue, size: 1, ignoresDiscretionary: false)
130+
static func `break`(_ kind: BreakKind, size: Int = 1) -> Token {
131+
return .break(kind, size: size, ignoresDiscretionary: false)
132132
}
133133

134134
static func verbatim(text: String) -> Token {

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,7 @@ private final class TokenStreamCreator: SyntaxVisitor {
14601460
guard let node = node, let contentsKeyPath = contentsKeyPath else { return }
14611461

14621462
if shouldResetBeforeLeftBrace {
1463-
before(node.leftBrace, tokens: .break(.reset, size: 1))
1463+
before(node.leftBrace, tokens: .break(.reset, size: 1, ignoresDiscretionary: true))
14641464
}
14651465
if !areBracesCompletelyEmpty(node, contentsKeyPath: contentsKeyPath) {
14661466
after(node.leftBrace, tokens: .break(.open, size: 1), .open)
@@ -1626,17 +1626,25 @@ private final class TokenStreamCreator: SyntaxVisitor {
16261626
/// Returns a value indicating whether or not discretionary newlines are permitted before the
16271627
/// given syntax token.
16281628
///
1629-
/// Discretionary newlines are allowed before any token that is preceded by a break or an existing
1630-
/// newline (ignoring open/close group tokens, which do not contribute to this). In other words,
1631-
/// this means that users may insert their own breaks in places where the pretty printer allows
1632-
/// them, even if those breaks wouldn't cause wrapping based on the column limit, but they may not
1633-
/// place them in places where the pretty printer would not break (for example, at a space token
1634-
/// that is intended to keep two tokens glued together).
1629+
/// Discretionary newlines are allowed before any token (ignoring open/close group tokens, which
1630+
/// do not contribute to this) that is preceded by an existing newline or that is preceded by a
1631+
/// break whose `ignoresDiscretionary` property is false. In other words, this means that users
1632+
/// may insert their own breaks in places where the pretty printer allows them, even if those
1633+
/// breaks wouldn't cause wrapping based on the column limit, but they may not place them in
1634+
/// places where the pretty printer would not break (for example, at a space token that is
1635+
/// intended to keep two tokens glued together).
1636+
///
1637+
/// Furthermore, breaks with `ignoresDiscretionary` equal to `true` are in effect "last resort"
1638+
/// breaks; a user's newline will be discarded unless the algorithm *must* break there. For
1639+
/// example, an open curly brace on a non-continuation line should always be kept on the same line
1640+
/// as the tokens before it unless the tokens before it are exactly the length of the line and a
1641+
/// break must be inserted there to prevent the brace from going over the limit.
16351642
private func isDiscretionaryNewlineAllowed(before token: TokenSyntax) -> Bool {
16361643
func isBreakMoreRecentThanNonbreakingContent(_ tokens: [Token]) -> Bool? {
16371644
for token in tokens.reversed() as ReversedCollection {
16381645
switch token {
1639-
case .break, .newlines: return true
1646+
case .newlines: return true
1647+
case .break(_, _, let ignoresDiscretionary): return !ignoresDiscretionary
16401648
case .comment, .space, .syntax, .verbatim: return false
16411649
default: break
16421650
}

Tests/SwiftFormatPrettyPrintTests/FunctionDeclTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,4 +552,33 @@ public class FunctionDeclTests: PrettyPrintTestCase {
552552
"""
553553
assertPrettyPrintEqual(input: input, expected: expected, linelength: 35)
554554
}
555+
556+
public func testRemovesLineBreakBeforeOpenBraceUnlessAbsolutelyNecessary() {
557+
let input =
558+
"""
559+
func foo(bar: Int)
560+
{
561+
baz()
562+
}
563+
564+
func foo(longer: Int)
565+
{
566+
baz()
567+
}
568+
"""
569+
570+
let expected =
571+
"""
572+
func foo(bar: Int) {
573+
baz()
574+
}
575+
576+
func foo(longer: Int)
577+
{
578+
baz()
579+
}
580+
581+
"""
582+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 21)
583+
}
555584
}

0 commit comments

Comments
 (0)