diff --git a/Sources/Rules/BlankLinesAroundMark.swift b/Sources/Rules/BlankLinesAroundMark.swift index 56df48b2..3dceb36f 100644 --- a/Sources/Rules/BlankLinesAroundMark.swift +++ b/Sources/Rules/BlankLinesAroundMark.swift @@ -13,7 +13,7 @@ public extension FormatRule { static let blankLinesAroundMark = FormatRule( help: "Insert blank line before and after `MARK:` comments.", options: ["line-after-marks"], - sharedOptions: ["linebreaks"] + sharedOptions: ["linebreaks", "type-blank-lines"] ) { formatter in formatter.forEachToken { i, token in guard case let .commentBody(comment) = token, comment.hasPrefix("MARK:"), @@ -29,8 +29,18 @@ public extension FormatRule { if formatter.options.insertBlankLines, let lastIndex = formatter.index(of: .linebreak, before: startIndex), let lastToken = formatter.last(.nonSpaceOrComment, before: lastIndex), - !lastToken.isLinebreak, lastToken != .startOfScope("{") + !lastToken.isLinebreak { + if lastToken == .startOfScope("{"), + formatter.options.enabledRules.contains(FormatRule.blankLinesAtStartOfScope.name) + { + // If blankLinesAtStartOfScope is enabled, only insert a blank line if it + // would not be removed by that rule (i.e. in a type body with insert or preserve option) + guard let braceIndex = formatter.index(of: .nonSpaceOrComment, before: lastIndex), + formatter.isStartOfTypeBody(at: braceIndex), + formatter.options.typeBlankLines != .remove + else { return } + } formatter.insertLinebreak(at: lastIndex) } } diff --git a/Tests/Rules/BlankLinesAroundMarkTests.swift b/Tests/Rules/BlankLinesAroundMarkTests.swift index 88ca2ddc..de5199ea 100644 --- a/Tests/Rules/BlankLinesAroundMarkTests.swift +++ b/Tests/Rules/BlankLinesAroundMarkTests.swift @@ -63,7 +63,7 @@ final class BlankLinesAroundMarkTests: XCTestCase { testFormatting(for: input, output, rule: .blankLinesAroundMark) } - func testNoInsertBlankLineBeforeMarkAtStartOfScope() { + func testInsertBlankLineBeforeMarkAtStartOfScope() { let input = """ do { // MARK: foo @@ -71,10 +71,21 @@ final class BlankLinesAroundMarkTests: XCTestCase { let foo = "foo" } """ - testFormatting(for: input, rule: .blankLinesAroundMark) + let output = """ + do { + + // MARK: foo + + let foo = "foo" + } + """ + // When only blankLinesAroundMark runs (without blankLinesAtStartOfScope), a blank line is + // inserted before the MARK. The second output (input) represents what happens when all rules + // run together, where blankLinesAtStartOfScope removes the blank line at the start of scope. + testFormatting(for: input, [output, input], rules: [.blankLinesAroundMark]) } - func testNoInsertBlankLineBeforeMarkAtStartOfScopeWithTrailingComment() { + func testInsertBlankLineBeforeMarkAtStartOfScopeWithTrailingComment() { let input = """ struct Foo { // some comment here // MARK: bar @@ -82,7 +93,69 @@ final class BlankLinesAroundMarkTests: XCTestCase { let string: String } """ - testFormatting(for: input, rule: .blankLinesAroundMark) + let output = """ + struct Foo { // some comment here + + // MARK: bar + + let string: String + } + """ + // When only blankLinesAroundMark runs (without blankLinesAtStartOfScope), a blank line is + // inserted before the MARK. The second output (input) represents what happens when all rules + // run together, where blankLinesAtStartOfScope removes the blank line at the start of scope. + testFormatting(for: input, [output, input], rules: [.blankLinesAroundMark]) + } + + func testNoInsertBlankLineBeforeMarkAtStartOfScopeWhenBlankLinesAtStartOfScopeEnabled() { + let input = """ + do { + // MARK: foo + + let foo = "foo" + } + """ + testFormatting(for: input, rules: [.blankLinesAroundMark, .blankLinesAtStartOfScope]) + } + + func testNoInsertBlankLineBeforeMarkAtStartOfTypeBodyWithRemoveOption() { + let input = """ + struct Foo { + // MARK: bar + + let string: String + } + """ + testFormatting(for: input, rules: [.blankLinesAroundMark, .blankLinesAtStartOfScope]) + } + + func testInsertBlankLineBeforeMarkAtStartOfTypeBodyWithInsertOption() { + let input = """ + struct Foo { + // MARK: bar + + let string: String + } + """ + let output = """ + struct Foo { + + // MARK: bar + + let string: String + } + """ + let outputAllRules = """ + struct Foo { + + // MARK: bar + + let string: String + + } + """ + let options = FormatOptions(typeBlankLines: .insert) + testFormatting(for: input, [output, outputAllRules], rules: [.blankLinesAroundMark, .blankLinesAtStartOfScope], options: options) } func testNoInsertBlankLineAfterMarkAtEndOfScope() { diff --git a/Tests/Rules/OrganizeDeclarationsTests.swift b/Tests/Rules/OrganizeDeclarationsTests.swift index 00132914..fec92b06 100644 --- a/Tests/Rules/OrganizeDeclarationsTests.swift +++ b/Tests/Rules/OrganizeDeclarationsTests.swift @@ -2227,7 +2227,7 @@ final class OrganizeDeclarationsTests: XCTestCase { """ testFormatting(for: input, output, rule: .organizeDeclarations, - exclude: [.blankLinesAtStartOfScope]) + exclude: [.blankLinesAtStartOfScope, .blankLinesAroundMark]) } func testOrganizeClassDeclarationsIntoCategoriesWithNoBlankLineAfterMark() {