From c62e45aee1a817a87a6d0c44e97fc354a7c4a330 Mon Sep 17 00:00:00 2001 From: Nate Stedman Date: Wed, 14 Oct 2020 12:10:26 -0400 Subject: [PATCH 1/4] Add lineBreakBeforeEachSwitchCaseOrDefaultBody configuration option --- .../SwiftFormatConfiguration/Configuration.swift | 13 +++++++++++++ .../SwiftFormatPrettyPrint/TokenStreamCreator.swift | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftFormatConfiguration/Configuration.swift b/Sources/SwiftFormatConfiguration/Configuration.swift index 6429586d8..e96650f66 100644 --- a/Sources/SwiftFormatConfiguration/Configuration.swift +++ b/Sources/SwiftFormatConfiguration/Configuration.swift @@ -29,6 +29,7 @@ public struct Configuration: Codable, Equatable { case lineBreakBeforeControlFlowKeywords case lineBreakBeforeEachArgument case lineBreakBeforeEachGenericRequirement + case lineBreakBeforeSwitchCaseOrDefaultBodies case prioritizeKeepingFunctionOutputTogether case indentConditionalCompilationBlocks case lineBreakAroundMultilineExpressionChainComponents @@ -97,6 +98,15 @@ public struct Configuration: Codable, Equatable { /// horizontally first, with line breaks only being fired when the line length would be exceeded. public var lineBreakBeforeEachGenericRequirement = false + /// Determines the line-breaking behavior for the bodies of `case` and `default` items within + /// a `switch` statement. + /// + /// If true, a line break will be added after the colon following `case` or `default`, forcing the + /// body to be on a separate line from the `case` or `default`. If false (the default), these bodies + /// will be laid out on the same line as the `case` or `default`, with line breaks only being added + /// when the line length would be exceeded. + public var lineBreakBeforeSwitchCaseOrDefaultBodies = false + /// Determines if function-like declaration outputs should be prioritized to be together with the /// function signature right (closing) parenthesis. /// @@ -187,6 +197,8 @@ public struct Configuration: Codable, Equatable { = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeEachArgument) ?? false self.lineBreakBeforeEachGenericRequirement = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeEachGenericRequirement) ?? false + self.lineBreakBeforeSwitchCaseOrDefaultBodies + = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeSwitchCaseOrDefaultBodies) ?? false self.prioritizeKeepingFunctionOutputTogether = try container.decodeIfPresent(Bool.self, forKey: .prioritizeKeepingFunctionOutputTogether) ?? false self.indentConditionalCompilationBlocks @@ -221,6 +233,7 @@ public struct Configuration: Codable, Equatable { try container.encode(lineBreakBeforeControlFlowKeywords, forKey: .lineBreakBeforeControlFlowKeywords) try container.encode(lineBreakBeforeEachArgument, forKey: .lineBreakBeforeEachArgument) try container.encode(lineBreakBeforeEachGenericRequirement, forKey: .lineBreakBeforeEachGenericRequirement) + try container.encode(lineBreakBeforeSwitchCaseOrDefaultBodies, forKey: .lineBreakBeforeSwitchCaseOrDefaultBodies) try container.encode(prioritizeKeepingFunctionOutputTogether, forKey: .prioritizeKeepingFunctionOutputTogether) try container.encode(indentConditionalCompilationBlocks, forKey: .indentConditionalCompilationBlocks) try container.encode( diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index c346e6d39..b2dc0b888 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -659,7 +659,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { before(node.firstToken, tokens: openBreak) after(node.unknownAttr?.lastToken, tokens: .space) - after(node.label.lastToken, tokens: .break(.reset, size: 0), .break(.open), .open) + after( + node.label.lastToken, + tokens: .break(.reset, size: 0), + .break(.open, newlines: config.lineBreakBeforeSwitchCaseOrDefaultBodies ? .hard : .elective), + .open + ) // If switch/case labels were configured to be indented, insert an extra `close` break after the // case body to match the `open` break above From 6484579a8ef120e469134e4fe39933fbc3e63eaf Mon Sep 17 00:00:00 2001 From: Nate Stedman Date: Wed, 14 Oct 2020 14:35:31 -0400 Subject: [PATCH 2/4] Add lineBreakBeforeFuncBodies configuration option --- .../SwiftFormatConfiguration/Configuration.swift | 13 +++++++++++++ .../SwiftFormatPrettyPrint/TokenStreamCreator.swift | 10 ++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftFormatConfiguration/Configuration.swift b/Sources/SwiftFormatConfiguration/Configuration.swift index e96650f66..8406f1002 100644 --- a/Sources/SwiftFormatConfiguration/Configuration.swift +++ b/Sources/SwiftFormatConfiguration/Configuration.swift @@ -28,6 +28,7 @@ public struct Configuration: Codable, Equatable { case respectsExistingLineBreaks case lineBreakBeforeControlFlowKeywords case lineBreakBeforeEachArgument + case lineBreakBeforeFuncBodies case lineBreakBeforeEachGenericRequirement case lineBreakBeforeSwitchCaseOrDefaultBodies case prioritizeKeepingFunctionOutputTogether @@ -98,6 +99,15 @@ public struct Configuration: Codable, Equatable { /// horizontally first, with line breaks only being fired when the line length would be exceeded. public var lineBreakBeforeEachGenericRequirement = false + /// Determins the line-breaking behavior for the bodies of functions declared with `func`, as + /// well as "`func`-like" declarations: initializers, deinitializers, and subscripts. + /// + /// If true, a line break will be added after the opening brace of function bodies, forcing the + /// body to be on a separate line from the function. If false (the default), these bodies will be + /// laid out on the same line as the declaration, with line breaks only being added when the line + /// length would be exceeded. + public var lineBreakBeforeFuncBodies = false + /// Determines the line-breaking behavior for the bodies of `case` and `default` items within /// a `switch` statement. /// @@ -197,6 +207,8 @@ public struct Configuration: Codable, Equatable { = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeEachArgument) ?? false self.lineBreakBeforeEachGenericRequirement = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeEachGenericRequirement) ?? false + self.lineBreakBeforeFuncBodies + = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeFuncBodies) ?? false self.lineBreakBeforeSwitchCaseOrDefaultBodies = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeSwitchCaseOrDefaultBodies) ?? false self.prioritizeKeepingFunctionOutputTogether @@ -233,6 +245,7 @@ public struct Configuration: Codable, Equatable { try container.encode(lineBreakBeforeControlFlowKeywords, forKey: .lineBreakBeforeControlFlowKeywords) try container.encode(lineBreakBeforeEachArgument, forKey: .lineBreakBeforeEachArgument) try container.encode(lineBreakBeforeEachGenericRequirement, forKey: .lineBreakBeforeEachGenericRequirement) + try container.encode(lineBreakBeforeFuncBodies, forKey: .lineBreakBeforeFuncBodies) try container.encode(lineBreakBeforeSwitchCaseOrDefaultBodies, forKey: .lineBreakBeforeSwitchCaseOrDefaultBodies) try container.encode(prioritizeKeepingFunctionOutputTogether, forKey: .prioritizeKeepingFunctionOutputTogether) try container.encode(indentConditionalCompilationBlocks, forKey: .indentConditionalCompilationBlocks) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index b2dc0b888..17e42d0ce 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -411,12 +411,18 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { attributes: AttributeListSyntax?, genericWhereClause: GenericWhereClauseSyntax?, body: Node?, - bodyContentsKeyPath: KeyPath? + bodyContentsKeyPath: KeyPath ) where BodyContents.Element: SyntaxProtocol { before(node.firstToken, tokens: .open) arrangeAttributeList(attributes) - arrangeBracesAndContents(of: body, contentsKeyPath: bodyContentsKeyPath) + arrangeBracesAndContents( + of: body, + contentsKeyPath: bodyContentsKeyPath, + openBraceNewlineBehavior: config.lineBreakBeforeFuncBodies + && body.map { !areBracesCompletelyEmpty($0, contentsKeyPath: bodyContentsKeyPath) } ?? false + ? .hard : .elective + ) if let genericWhereClause = genericWhereClause { before(genericWhereClause.firstToken, tokens: .break(.same), .open) From 2feea581f98ce153cf67ae67fe7f75048bc9ee75 Mon Sep 17 00:00:00 2001 From: Nate Stedman Date: Wed, 14 Oct 2020 14:59:02 -0400 Subject: [PATCH 3/4] Add lineBreakBeforeControlFlowBodies configuration option --- .../Configuration.swift | 12 ++++++ .../TokenStreamCreator.swift | 42 +++++++++++++++---- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftFormatConfiguration/Configuration.swift b/Sources/SwiftFormatConfiguration/Configuration.swift index 8406f1002..1319cce02 100644 --- a/Sources/SwiftFormatConfiguration/Configuration.swift +++ b/Sources/SwiftFormatConfiguration/Configuration.swift @@ -26,6 +26,7 @@ public struct Configuration: Codable, Equatable { case tabWidth case indentation case respectsExistingLineBreaks + case lineBreakBeforeControlFlowBodies case lineBreakBeforeControlFlowKeywords case lineBreakBeforeEachArgument case lineBreakBeforeFuncBodies @@ -75,6 +76,14 @@ public struct Configuration: Codable, Equatable { /// MARK: Rule-specific configuration + /// Determines the line-breaking behavior for bodies of control flow keywords, like `if` and + /// `for`. + /// + /// If true, a line break will be added after the opening brace of these bodies, forcing them + /// onto their own lines. If false (the default), these bodies will be laid out on the same line + /// as the keyword, with line breaks only being added when the line length would be exceeded. + public var lineBreakBeforeControlFlowBodies = false + /// Determines the line-breaking behavior for control flow keywords that follow a closing brace, /// like `else` and `catch`. /// @@ -201,6 +210,8 @@ public struct Configuration: Codable, Equatable { = try container.decodeIfPresent(Indent.self, forKey: .indentation) ?? .spaces(2) self.respectsExistingLineBreaks = try container.decodeIfPresent(Bool.self, forKey: .respectsExistingLineBreaks) ?? true + self.lineBreakBeforeControlFlowBodies + = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeControlFlowBodies) ?? false self.lineBreakBeforeControlFlowKeywords = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeControlFlowKeywords) ?? false self.lineBreakBeforeEachArgument @@ -242,6 +253,7 @@ public struct Configuration: Codable, Equatable { try container.encode(tabWidth, forKey: .tabWidth) try container.encode(indentation, forKey: .indentation) try container.encode(respectsExistingLineBreaks, forKey: .respectsExistingLineBreaks) + try container.encode(lineBreakBeforeControlFlowBodies, forKey: .lineBreakBeforeControlFlowBodies) try container.encode(lineBreakBeforeControlFlowKeywords, forKey: .lineBreakBeforeControlFlowKeywords) try container.encode(lineBreakBeforeEachArgument, forKey: .lineBreakBeforeEachArgument) try container.encode(lineBreakBeforeEachGenericRequirement, forKey: .lineBreakBeforeEachGenericRequirement) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 17e42d0ce..13a6a5b86 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -476,7 +476,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { after(condition.lastToken, tokens: .break(.close(mustBreak: false), size: 0)) } - arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.body, + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) if let elseKeyword = node.elseKeyword { // Add a token before the else keyword. Breaking before `else` is explicitly allowed when @@ -498,7 +502,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { } } - arrangeBracesAndContents(of: node.elseBody?.as(CodeBlockSyntax.self), contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.elseBody?.as(CodeBlockSyntax.self), + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) return .visitChildren } @@ -537,7 +545,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { after(typeAnnotation.lastToken, tokens: .break(.close(mustBreak: false), size: 0)) } - arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.body, + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) return .visitChildren } @@ -559,14 +571,22 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { after(condition.lastToken, tokens: .break(.close(mustBreak: false), size: 0)) } - arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.body, + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) return .visitChildren } override func visit(_ node: RepeatWhileStmtSyntax) -> SyntaxVisitorContinueKind { after(node.labelColon, tokens: .space) - arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.body, + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) if config.lineBreakBeforeControlFlowKeywords { before(node.whileKeyword, tokens: .break(.same), .open) @@ -586,7 +606,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { override func visit(_ node: DoStmtSyntax) -> SyntaxVisitorContinueKind { after(node.labelColon, tokens: .space) - arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.body, + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) return .visitChildren } @@ -609,7 +633,11 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { } } - arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements) + arrangeBracesAndContents( + of: node.body, + contentsKeyPath: \.statements, + openBraceNewlineBehavior: config.lineBreakBeforeControlFlowBodies ? .hard : .elective + ) return .visitChildren } From 95e315eb07852667db3378c61f31ba361a89ba5e Mon Sep 17 00:00:00 2001 From: Nate Stedman Date: Wed, 14 Oct 2020 16:40:42 -0400 Subject: [PATCH 4/4] Add lineBreakBeforeTypeBodies configuration option --- Sources/SwiftFormatConfiguration/Configuration.swift | 12 ++++++++++++ .../SwiftFormatPrettyPrint/TokenStreamCreator.swift | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftFormatConfiguration/Configuration.swift b/Sources/SwiftFormatConfiguration/Configuration.swift index 1319cce02..2a908a9a2 100644 --- a/Sources/SwiftFormatConfiguration/Configuration.swift +++ b/Sources/SwiftFormatConfiguration/Configuration.swift @@ -32,6 +32,7 @@ public struct Configuration: Codable, Equatable { case lineBreakBeforeFuncBodies case lineBreakBeforeEachGenericRequirement case lineBreakBeforeSwitchCaseOrDefaultBodies + case lineBreakBeforeTypeBodies case prioritizeKeepingFunctionOutputTogether case indentConditionalCompilationBlocks case lineBreakAroundMultilineExpressionChainComponents @@ -126,6 +127,14 @@ public struct Configuration: Codable, Equatable { /// when the line length would be exceeded. public var lineBreakBeforeSwitchCaseOrDefaultBodies = false + /// Determines the line-breaking behavior for the bodies of types: `class`, `enum`, `extension`, + /// `protocol`, and `struct`. + /// + /// If true, a line break will be added after the opening brace for all non-empty types. If false + /// (the default), these bodies will be laid out on the same line as the type declaration, with + /// line breaks only being added when the line length would be exceeded. + public var lineBreakBeforeTypeBodies = false + /// Determines if function-like declaration outputs should be prioritized to be together with the /// function signature right (closing) parenthesis. /// @@ -222,6 +231,8 @@ public struct Configuration: Codable, Equatable { = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeFuncBodies) ?? false self.lineBreakBeforeSwitchCaseOrDefaultBodies = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeSwitchCaseOrDefaultBodies) ?? false + self.lineBreakBeforeTypeBodies + = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeTypeBodies) ?? false self.prioritizeKeepingFunctionOutputTogether = try container.decodeIfPresent(Bool.self, forKey: .prioritizeKeepingFunctionOutputTogether) ?? false self.indentConditionalCompilationBlocks @@ -259,6 +270,7 @@ public struct Configuration: Codable, Equatable { try container.encode(lineBreakBeforeEachGenericRequirement, forKey: .lineBreakBeforeEachGenericRequirement) try container.encode(lineBreakBeforeFuncBodies, forKey: .lineBreakBeforeFuncBodies) try container.encode(lineBreakBeforeSwitchCaseOrDefaultBodies, forKey: .lineBreakBeforeSwitchCaseOrDefaultBodies) + try container.encode(lineBreakBeforeTypeBodies, forKey: .lineBreakBeforeTypeBodies) try container.encode(prioritizeKeepingFunctionOutputTogether, forKey: .prioritizeKeepingFunctionOutputTogether) try container.encode(indentConditionalCompilationBlocks, forKey: .indentConditionalCompilationBlocks) try container.encode( diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 13a6a5b86..caa673d7a 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -246,7 +246,13 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { before(firstTokenAfterAttributes, tokens: .open) after(typeKeyword, tokens: .break) - arrangeBracesAndContents(of: members, contentsKeyPath: \.members) + arrangeBracesAndContents( + of: members, + contentsKeyPath: \.members, + openBraceNewlineBehavior: !areBracesCompletelyEmpty(members, contentsKeyPath: \.members) + && config.lineBreakBeforeTypeBodies + ? .hard : .elective + ) if let genericWhereClause = genericWhereClause { before(genericWhereClause.firstToken, tokens: .break(.same), .open)