From bc6aa7c3e21b6ab951ce75afc0a6e6d16fd6caef Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Wed, 13 Dec 2023 10:17:04 -0800 Subject: [PATCH] Add rules for how to wrap attributes on property declarations (#254) --- Package.swift | 4 +- README.md | 166 +++++++++++++++++- .../AirbnbSwiftFormatTool/airbnb.swiftformat | 3 + 3 files changed, 170 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index 0fdc73f..4a367ac 100644 --- a/Package.swift +++ b/Package.swift @@ -42,8 +42,8 @@ let package = Package( .binaryTarget( name: "SwiftFormat", - url: "https://github.com/calda/SwiftFormat/releases/download/0.53-beta-4/SwiftFormat.artifactbundle.zip", - checksum: "8506e9f6a434127f9eabe62c0a0349ccfd1e12e7cd7a96ba6a0c8f9d4a596099"), + url: "https://github.com/calda/SwiftFormat/releases/download/0.53-beta-8/SwiftFormat.artifactbundle.zip", + checksum: "42b2612305c5bffe57102d6c1a2e137309b5cf246638ff9ff75e3260a6795a19"), .binaryTarget( name: "SwiftLintBinary", diff --git a/README.md b/README.md index 38741d9..21f1c14 100644 --- a/README.md +++ b/README.md @@ -655,7 +655,7 @@ _You can enable the following settings in Xcode by running [this script](resourc -* (link) **Place function/type attributes on the line above the declaration**. [![SwiftFormat: wrapAttributes](https://img.shields.io/badge/SwiftFormat-wrapAttributes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#wrapAttributes) +* (link) **Place attributes for functions, types, and computed properties on the line above the declaration**. [![SwiftFormat: wrapAttributes](https://img.shields.io/badge/SwiftFormat-wrapAttributes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#wrapAttributes)
@@ -663,18 +663,182 @@ _You can enable the following settings in Xcode by running [this script](resourc // WRONG @objc class Spaceship { + @ViewBuilder var controlPanel: some View { + // ... + } + @discardableResult func fly() -> Bool { + // ... } + } // RIGHT @objc class Spaceship { + @ViewBuilder + var controlPanel: some View { + // ... + } + @discardableResult func fly() -> Bool { + // ... } + + } + ``` + +
+ +* (link) **Place simple attributes for stored properties on the same line as the rest of the declaration**. Complex attributes with named arguments, or more than one unnamed argument, should be placed on the previous line. [![SwiftFormat: wrapAttributes](https://img.shields.io/badge/SwiftFormat-wrapAttributes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#wrapAttributes) + +
+ + ```swift + // WRONG. These simple property wrappers should be written on the same line as the declaration. + struct SpaceshipDashboardView { + + @State + private var warpDriveEnabled: Bool + + @ObservedObject + private var lifeSupportService: LifeSupportService + + @Environment(\.controlPanelStyle) + private var controlPanelStyle + + } + + // RIGHT + struct SpaceshipDashboardView { + + @State private var warpDriveEnabled: Bool + + @ObservedObject private var lifeSupportService: LifeSupportService + + @Environment(\.controlPanelStyle) private var controlPanelStyle + + } + ``` + + ```swift + // WRONG. These complex attached macros should be written on the previous line. + struct SolarSystemView { + + @Query(sort: \.distance) var allPlanets: [Planet] + + @Query(sort: \.age, order: .reverse) var moonsByAge: [Moon] + + } + + // RIGHT + struct SolarSystemView { + + @Query(sort: \.distance) + var allPlanets: [Planet] + + @Query(sort: \.age, order: .reverse) + var oldestMoons: [Moon] + } + ```swift + + ``` + // WRONG. These long, complex attributes should be written on the previous line. + struct RocketFactory { + + @available(*, unavailable, message: "No longer in production") var saturn5Builder: Saturn5Builder + + @available(*, deprecated, message: "To be retired by 2030") var atlas5Builder: Atlas5Builder + + @available(*, iOS 18.0, tvOS 18.0, macOS 15.0, watchOS 11.0) var newGlennBuilder: NewGlennBuilder + + } + + // RIGHT + struct RocketFactory { + + @available(*, unavailable, message: "No longer in production") + var saturn5Builder: Saturn5Builder + + @available(*, deprecated, message: "To be retired by 2030") + var atlas5Builder: Atlas5Builder + + @available(*, iOS 18.0, tvOS 18.0, macOS 15.0, watchOS 11.0) + var newGlennBuilder: NewGlennBuilder + + } + ``` + + #### Why? + + Unlike other types of declarations, which have braces and span multiple lines, stored property declarations are often only a single line of code. Stored properties are often written sequentially without any blank lines between them. This makes the code compact without hurting readability, and allows for related properties to be grouped together in blocks: + + ```swift + struct SpaceshipDashboardView { + @State private var warpDriveEnabled: Bool + @State private var lifeSupportEnabled: Bool + @State private var artificialGravityEnabled: Bool + @State private var tractorBeamEnabled: Bool + + @Environment(\.controlPanelStyle) private var controlPanelStyle + @Environment(\.toggleButtonStyle) private var toggleButtonStyle + } + ``` + + If stored property attributes were written on the previous line (like other types of attributes), then the properties start to visually bleed together unless you add blank lines between them: + + ```swift + struct SpaceshipDashboardView { + @State + private var warpDriveEnabled: Bool + @State + private var lifeSupportEnabled: Bool + @State + private var artificialGravityEnabled: Bool + @State + private var tractorBeamEnabled: Bool + + @Environment(\.controlPanelStyle) + private var controlPanelStyle + @Environment(\.toggleButtonStyle) + private var toggleButtonStyle + } + ``` + + If you add blank lines, the list of properties becomes much longer and you lose the ability to group related properties together: + + ```swift + struct SpaceshipDashboardView { + @State + private var warpDriveEnabled: Bool + + @State + private var lifeSupportEnabled: Bool + + @State + private var artificialGravityEnabled: Bool + + @State + private var tractorBeamEnabled: Bool + + @Environment(\.controlPanelStyle) + private var controlPanelStyle + + @Environment(\.toggleButtonStyle) + private var toggleButtonStyle + } + ``` + + This doesn't apply to complex attributes with named arguments, or multiple unnamed arguments. These arguments are visually complex and typically encode a lot of information, so feel cramped and difficult to read when written on a single line: + + ```swift + // Despite being less than 100 characters long, these lines are very complex and feel unnecessarily long: + @available(*, unavailable, message: "No longer in production") var saturn5Builder: Saturn5Builder + @available(*, deprecated, message: "To be retired by 2030") var atlas5Builder: Atlas5Builder + @available(*, iOS 18.0, tvOS 18.0, macOS 15.0, watchOS 11.0) var newGlennBuilder: NewGlennBuilder ```
diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index ebf23f1..d628a3a 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -19,6 +19,9 @@ --closingparen same-line # wrapArguments --wraptypealiases before-first # wrapArguments --funcattributes prev-line # wrapAttributes +--computedvarattrs prev-line # wrapAttributes +--storedvarattrs same-line # wrapAttributes +--complexattrs prev-line # wrapAttributes --typeattributes prev-line # wrapAttributes --wrapternary before-operators # wrap --structthreshold 20 # organizeDeclarations