diff --git a/Sources/SwiftFormatConfiguration/Configuration.swift b/Sources/SwiftFormatConfiguration/Configuration.swift
index 6429586d8..2a908a9a2 100644
--- a/Sources/SwiftFormatConfiguration/Configuration.swift
+++ b/Sources/SwiftFormatConfiguration/Configuration.swift
@@ -26,9 +26,13 @@ public struct Configuration: Codable, Equatable {
     case tabWidth
     case indentation
     case respectsExistingLineBreaks
+    case lineBreakBeforeControlFlowBodies
     case lineBreakBeforeControlFlowKeywords
     case lineBreakBeforeEachArgument
+    case lineBreakBeforeFuncBodies
     case lineBreakBeforeEachGenericRequirement
+    case lineBreakBeforeSwitchCaseOrDefaultBodies
+    case lineBreakBeforeTypeBodies
     case prioritizeKeepingFunctionOutputTogether
     case indentConditionalCompilationBlocks
     case lineBreakAroundMultilineExpressionChainComponents
@@ -73,6 +77,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`.
   ///
@@ -97,6 +109,32 @@ 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.
+  ///
+  /// 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 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.
   ///
@@ -181,12 +219,20 @@ 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
       = 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.lineBreakBeforeTypeBodies
+      = try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeTypeBodies) ?? false
     self.prioritizeKeepingFunctionOutputTogether
       = try container.decodeIfPresent(Bool.self, forKey: .prioritizeKeepingFunctionOutputTogether) ?? false
     self.indentConditionalCompilationBlocks
@@ -218,9 +264,13 @@ 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)
+    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 c346e6d39..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)
@@ -411,12 +417,18 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
     attributes: AttributeListSyntax?,
     genericWhereClause: GenericWhereClauseSyntax?,
     body: Node?,
-    bodyContentsKeyPath: KeyPath<Node, BodyContents>?
+    bodyContentsKeyPath: KeyPath<Node, BodyContents>
   ) 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)
@@ -470,7 +482,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
@@ -492,7 +508,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
   }
@@ -531,7 +551,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
   }
@@ -553,14 +577,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)
@@ -580,7 +612,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
   }
 
@@ -603,7 +639,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
   }
@@ -659,7 +699,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