mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
134 lines
5.3 KiB
Swift
134 lines
5.3 KiB
Swift
//
|
|
// BlankLinesBetweenScopes.swift
|
|
// SwiftFormat
|
|
//
|
|
// Created by Nick Lockwood on 9/7/16.
|
|
// Copyright © 2024 Nick Lockwood. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public extension FormatRule {
|
|
/// Adds a blank line immediately after a closing brace, unless followed by another closing brace
|
|
static let blankLinesBetweenScopes = FormatRule(
|
|
help: """
|
|
Insert blank line before class, struct, enum, extension, protocol or function
|
|
declarations.
|
|
""",
|
|
sharedOptions: ["linebreaks"]
|
|
) { formatter in
|
|
var spaceableScopeStack = [true]
|
|
var isSpaceableScopeType = false
|
|
formatter.forEachToken(onlyWhereEnabled: false) { i, token in
|
|
outer: switch token {
|
|
case .keyword("class"),
|
|
.keyword("actor"),
|
|
.keyword("struct"),
|
|
.keyword("extension"),
|
|
.keyword("enum"):
|
|
isSpaceableScopeType =
|
|
(formatter.last(.nonSpaceOrCommentOrLinebreak, before: i) != .keyword("import"))
|
|
case .keyword("func"), .keyword("var"):
|
|
isSpaceableScopeType = false
|
|
case .startOfScope("{"):
|
|
spaceableScopeStack.append(isSpaceableScopeType)
|
|
isSpaceableScopeType = false
|
|
case .endOfScope("}"):
|
|
spaceableScopeStack.removeLast()
|
|
guard spaceableScopeStack.last == true,
|
|
let openingBraceIndex = formatter.index(of: .startOfScope("{"), before: i),
|
|
formatter.lastIndex(of: .linebreak, in: openingBraceIndex + 1 ..< i) != nil
|
|
else {
|
|
// Inline braces
|
|
break
|
|
}
|
|
var i = i
|
|
if let nextTokenIndex = formatter.index(of: .nonSpace, after: i, if: {
|
|
$0 == .startOfScope("(")
|
|
}), let closingParenIndex = formatter.index(of:
|
|
.endOfScope(")"), after: nextTokenIndex)
|
|
{
|
|
i = closingParenIndex
|
|
}
|
|
guard let nextTokenIndex = formatter.index(of: .nonSpaceOrLinebreak, after: i),
|
|
formatter.isEnabled, formatter.options.insertBlankLines,
|
|
let firstLinebreakIndex = formatter.index(of: .linebreak, in: i + 1 ..< nextTokenIndex),
|
|
formatter.index(of: .linebreak, in: firstLinebreakIndex + 1 ..< nextTokenIndex) == nil
|
|
else {
|
|
break
|
|
}
|
|
if var nextNonCommentIndex =
|
|
formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i)
|
|
{
|
|
while formatter.tokens[nextNonCommentIndex] == .startOfScope("#if"),
|
|
let nextIndex = formatter.index(
|
|
of: .nonSpaceOrCommentOrLinebreak,
|
|
after: formatter.endOfLine(at: nextNonCommentIndex)
|
|
)
|
|
{
|
|
nextNonCommentIndex = nextIndex
|
|
}
|
|
switch formatter.tokens[nextNonCommentIndex] {
|
|
case .error, .endOfScope,
|
|
.operator(".", _), .delimiter(","), .delimiter(":"),
|
|
.keyword("else"), .keyword("catch"), .keyword("#else"), .keyword("#elseif"):
|
|
break outer
|
|
case .keyword("while"):
|
|
if let previousBraceIndex = formatter.index(of: .startOfScope("{"), before: i),
|
|
formatter.last(.nonSpaceOrCommentOrLinebreak, before: previousBraceIndex)
|
|
== .keyword("repeat")
|
|
{
|
|
break outer
|
|
}
|
|
default:
|
|
if formatter.isLabel(at: nextNonCommentIndex), let colonIndex
|
|
= formatter.index(of: .delimiter(":"), after: nextNonCommentIndex),
|
|
formatter.next(.nonSpaceOrCommentOrLinebreak, after: colonIndex)
|
|
== .startOfScope("{")
|
|
{
|
|
break outer
|
|
}
|
|
}
|
|
}
|
|
switch formatter.tokens[nextTokenIndex] {
|
|
case .startOfScope("//"):
|
|
if case let .commentBody(body)? = formatter.next(.nonSpace, after: nextTokenIndex),
|
|
body.trimmingCharacters(in: .whitespaces).lowercased().hasPrefix("sourcery:")
|
|
{
|
|
break
|
|
}
|
|
formatter.insertLinebreak(at: firstLinebreakIndex)
|
|
default:
|
|
formatter.insertLinebreak(at: firstLinebreakIndex)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
} examples: {
|
|
"""
|
|
```diff
|
|
func foo() {
|
|
// foo
|
|
}
|
|
func bar() {
|
|
// bar
|
|
}
|
|
var baz: Bool
|
|
var quux: Int
|
|
|
|
func foo() {
|
|
// foo
|
|
}
|
|
+
|
|
func bar() {
|
|
// bar
|
|
}
|
|
+
|
|
var baz: Bool
|
|
var quux: Int
|
|
```
|
|
"""
|
|
}
|
|
}
|