Skip to content

Commit 5e636e6

Browse files
authored
Merge pull request #77 from kkebo/merge-upstream-main
2 parents 54fe419 + 1df96b9 commit 5e636e6

21 files changed

+507
-107
lines changed

Documentation/Configuration.md

+7
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ top-level keys and values:
6161
(the default), requirements will be laid out horizontally first, with line breaks
6262
only being fired when the line length would be exceeded.
6363

64+
* `lineBreakBetweenDeclarationAttributes` _(boolean)_: Determines the
65+
line-breaking behavior for adjacent attributes on declarations.
66+
If true, a line break will be added between each attribute, forcing the
67+
attribute list to be laid out vertically. If false (the default),
68+
attributes will be laid out horizontally first, with line breaks only
69+
being fired when the line length would be exceeded.
70+
6471
* `prioritizeKeepingFunctionOutputTogether` _(boolean)_: Determines if
6572
function-like declaration outputs should be prioritized to be together with the
6673
function signature right (closing) parenthesis. If false (the default), function

Documentation/RuleDocumentation.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
Use the rules below in the `rules` block of your `.swift-format`
66
configuration file, as described in
7-
[Configuration](Configuration.md). All of these rules can be
7+
[Configuration](Documentation/Configuration.md). All of these rules can be
88
applied in the linter, but only some of them can format your source code
99
automatically.
1010

Sources/SwiftFormat/API/Configuration+Default.swift

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extension Configuration {
3131
self.lineBreakBeforeControlFlowKeywords = false
3232
self.lineBreakBeforeEachArgument = false
3333
self.lineBreakBeforeEachGenericRequirement = false
34+
self.lineBreakBetweenDeclarationAttributes = false
3435
self.prioritizeKeepingFunctionOutputTogether = false
3536
self.indentConditionalCompilationBlocks = true
3637
self.lineBreakAroundMultilineExpressionChainComponents = false

Sources/SwiftFormat/API/Configuration.swift

+8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public struct Configuration: Codable, Equatable {
3535
case lineBreakBeforeControlFlowKeywords
3636
case lineBreakBeforeEachArgument
3737
case lineBreakBeforeEachGenericRequirement
38+
case lineBreakBetweenDeclarationAttributes
3839
case prioritizeKeepingFunctionOutputTogether
3940
case indentConditionalCompilationBlocks
4041
case lineBreakAroundMultilineExpressionChainComponents
@@ -115,6 +116,9 @@ public struct Configuration: Codable, Equatable {
115116
/// horizontally first, with line breaks only being fired when the line length would be exceeded.
116117
public var lineBreakBeforeEachGenericRequirement: Bool
117118

119+
/// If true, a line break will be added between adjacent attributes.
120+
public var lineBreakBetweenDeclarationAttributes: Bool
121+
118122
/// Determines if function-like declaration outputs should be prioritized to be together with the
119123
/// function signature right (closing) parenthesis.
120124
///
@@ -250,6 +254,9 @@ public struct Configuration: Codable, Equatable {
250254
self.lineBreakBeforeEachGenericRequirement =
251255
try container.decodeIfPresent(Bool.self, forKey: .lineBreakBeforeEachGenericRequirement)
252256
?? defaults.lineBreakBeforeEachGenericRequirement
257+
self.lineBreakBetweenDeclarationAttributes =
258+
try container.decodeIfPresent(Bool.self, forKey: .lineBreakBetweenDeclarationAttributes)
259+
?? defaults.lineBreakBetweenDeclarationAttributes
253260
self.prioritizeKeepingFunctionOutputTogether =
254261
try container.decodeIfPresent(Bool.self, forKey: .prioritizeKeepingFunctionOutputTogether)
255262
?? defaults.prioritizeKeepingFunctionOutputTogether
@@ -304,6 +311,7 @@ public struct Configuration: Codable, Equatable {
304311
try container.encode(lineBreakBeforeEachGenericRequirement, forKey: .lineBreakBeforeEachGenericRequirement)
305312
try container.encode(prioritizeKeepingFunctionOutputTogether, forKey: .prioritizeKeepingFunctionOutputTogether)
306313
try container.encode(indentConditionalCompilationBlocks, forKey: .indentConditionalCompilationBlocks)
314+
try container.encode(lineBreakBetweenDeclarationAttributes, forKey: .lineBreakBetweenDeclarationAttributes)
307315
try container.encode(
308316
lineBreakAroundMultilineExpressionChainComponents,
309317
forKey: .lineBreakAroundMultilineExpressionChainComponents)

Sources/SwiftFormat/Core/Pipelines+Generated.swift

+6-10
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ class LintPipeline: SyntaxVisitor {
3737
}
3838

3939
override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
40+
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
4041
visitIfEnabled(TypeNamesShouldBeCapitalized.visit, for: node)
4142
return .visitChildren
4243
}
4344
override func visitPost(_ node: ActorDeclSyntax) {
45+
onVisitPost(rule: AllPublicDeclarationsHaveDocumentation.self, for: node)
4446
onVisitPost(rule: TypeNamesShouldBeCapitalized.self, for: node)
4547
}
4648

@@ -76,7 +78,6 @@ class LintPipeline: SyntaxVisitor {
7678
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
7779
visitIfEnabled(AlwaysUseLowerCamelCase.visit, for: node)
7880
visitIfEnabled(BeginDocumentationCommentWithOneLineSummary.visit, for: node)
79-
visitIfEnabled(DontRepeatTypeInStaticProperties.visit, for: node)
8081
visitIfEnabled(NoLeadingUnderscores.visit, for: node)
8182
visitIfEnabled(TypeNamesShouldBeCapitalized.visit, for: node)
8283
visitIfEnabled(UseTripleSlashForDocumentationComments.visit, for: node)
@@ -86,7 +87,6 @@ class LintPipeline: SyntaxVisitor {
8687
onVisitPost(rule: AllPublicDeclarationsHaveDocumentation.self, for: node)
8788
onVisitPost(rule: AlwaysUseLowerCamelCase.self, for: node)
8889
onVisitPost(rule: BeginDocumentationCommentWithOneLineSummary.self, for: node)
89-
onVisitPost(rule: DontRepeatTypeInStaticProperties.self, for: node)
9090
onVisitPost(rule: NoLeadingUnderscores.self, for: node)
9191
onVisitPost(rule: TypeNamesShouldBeCapitalized.self, for: node)
9292
onVisitPost(rule: UseTripleSlashForDocumentationComments.self, for: node)
@@ -181,8 +181,8 @@ class LintPipeline: SyntaxVisitor {
181181
}
182182

183183
override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
184+
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
184185
visitIfEnabled(BeginDocumentationCommentWithOneLineSummary.visit, for: node)
185-
visitIfEnabled(DontRepeatTypeInStaticProperties.visit, for: node)
186186
visitIfEnabled(FullyIndirectEnum.visit, for: node)
187187
visitIfEnabled(NoLeadingUnderscores.visit, for: node)
188188
visitIfEnabled(OneCasePerLine.visit, for: node)
@@ -191,8 +191,8 @@ class LintPipeline: SyntaxVisitor {
191191
return .visitChildren
192192
}
193193
override func visitPost(_ node: EnumDeclSyntax) {
194+
onVisitPost(rule: AllPublicDeclarationsHaveDocumentation.self, for: node)
194195
onVisitPost(rule: BeginDocumentationCommentWithOneLineSummary.self, for: node)
195-
onVisitPost(rule: DontRepeatTypeInStaticProperties.self, for: node)
196196
onVisitPost(rule: FullyIndirectEnum.self, for: node)
197197
onVisitPost(rule: NoLeadingUnderscores.self, for: node)
198198
onVisitPost(rule: OneCasePerLine.self, for: node)
@@ -202,14 +202,12 @@ class LintPipeline: SyntaxVisitor {
202202

203203
override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
204204
visitIfEnabled(AvoidRetroactiveConformances.visit, for: node)
205-
visitIfEnabled(DontRepeatTypeInStaticProperties.visit, for: node)
206205
visitIfEnabled(NoAccessLevelOnExtensionDeclaration.visit, for: node)
207206
visitIfEnabled(UseTripleSlashForDocumentationComments.visit, for: node)
208207
return .visitChildren
209208
}
210209
override func visitPost(_ node: ExtensionDeclSyntax) {
211210
onVisitPost(rule: AvoidRetroactiveConformances.self, for: node)
212-
onVisitPost(rule: DontRepeatTypeInStaticProperties.self, for: node)
213211
onVisitPost(rule: NoAccessLevelOnExtensionDeclaration.self, for: node)
214212
onVisitPost(rule: UseTripleSlashForDocumentationComments.self, for: node)
215213
}
@@ -423,7 +421,6 @@ class LintPipeline: SyntaxVisitor {
423421
override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
424422
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
425423
visitIfEnabled(BeginDocumentationCommentWithOneLineSummary.visit, for: node)
426-
visitIfEnabled(DontRepeatTypeInStaticProperties.visit, for: node)
427424
visitIfEnabled(NoLeadingUnderscores.visit, for: node)
428425
visitIfEnabled(TypeNamesShouldBeCapitalized.visit, for: node)
429426
visitIfEnabled(UseTripleSlashForDocumentationComments.visit, for: node)
@@ -432,7 +429,6 @@ class LintPipeline: SyntaxVisitor {
432429
override func visitPost(_ node: ProtocolDeclSyntax) {
433430
onVisitPost(rule: AllPublicDeclarationsHaveDocumentation.self, for: node)
434431
onVisitPost(rule: BeginDocumentationCommentWithOneLineSummary.self, for: node)
435-
onVisitPost(rule: DontRepeatTypeInStaticProperties.self, for: node)
436432
onVisitPost(rule: NoLeadingUnderscores.self, for: node)
437433
onVisitPost(rule: TypeNamesShouldBeCapitalized.self, for: node)
438434
onVisitPost(rule: UseTripleSlashForDocumentationComments.self, for: node)
@@ -469,7 +465,6 @@ class LintPipeline: SyntaxVisitor {
469465
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
470466
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
471467
visitIfEnabled(BeginDocumentationCommentWithOneLineSummary.visit, for: node)
472-
visitIfEnabled(DontRepeatTypeInStaticProperties.visit, for: node)
473468
visitIfEnabled(NoLeadingUnderscores.visit, for: node)
474469
visitIfEnabled(TypeNamesShouldBeCapitalized.visit, for: node)
475470
visitIfEnabled(UseSynthesizedInitializer.visit, for: node)
@@ -479,7 +474,6 @@ class LintPipeline: SyntaxVisitor {
479474
override func visitPost(_ node: StructDeclSyntax) {
480475
onVisitPost(rule: AllPublicDeclarationsHaveDocumentation.self, for: node)
481476
onVisitPost(rule: BeginDocumentationCommentWithOneLineSummary.self, for: node)
482-
onVisitPost(rule: DontRepeatTypeInStaticProperties.self, for: node)
483477
onVisitPost(rule: NoLeadingUnderscores.self, for: node)
484478
onVisitPost(rule: TypeNamesShouldBeCapitalized.self, for: node)
485479
onVisitPost(rule: UseSynthesizedInitializer.self, for: node)
@@ -568,6 +562,7 @@ class LintPipeline: SyntaxVisitor {
568562
visitIfEnabled(AllPublicDeclarationsHaveDocumentation.visit, for: node)
569563
visitIfEnabled(AlwaysUseLowerCamelCase.visit, for: node)
570564
visitIfEnabled(BeginDocumentationCommentWithOneLineSummary.visit, for: node)
565+
visitIfEnabled(DontRepeatTypeInStaticProperties.visit, for: node)
571566
visitIfEnabled(NeverUseImplicitlyUnwrappedOptionals.visit, for: node)
572567
visitIfEnabled(UseTripleSlashForDocumentationComments.visit, for: node)
573568
return .visitChildren
@@ -576,6 +571,7 @@ class LintPipeline: SyntaxVisitor {
576571
onVisitPost(rule: AllPublicDeclarationsHaveDocumentation.self, for: node)
577572
onVisitPost(rule: AlwaysUseLowerCamelCase.self, for: node)
578573
onVisitPost(rule: BeginDocumentationCommentWithOneLineSummary.self, for: node)
574+
onVisitPost(rule: DontRepeatTypeInStaticProperties.self, for: node)
579575
onVisitPost(rule: NeverUseImplicitlyUnwrappedOptionals.self, for: node)
580576
onVisitPost(rule: UseTripleSlashForDocumentationComments.self, for: node)
581577
}

Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift

+29-13
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
271271
// `arrange*` functions here.
272272
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)
273273

274-
arrangeAttributeList(node.attributes)
274+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBeforeEachArgument)
275275

276276
let hasArguments = !node.signature.parameterClause.parameters.isEmpty
277277

@@ -326,7 +326,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
326326
) {
327327
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)
328328

329-
arrangeAttributeList(attributes)
329+
arrangeAttributeList(attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
330330

331331
// Prioritize keeping "<modifiers> <keyword> <name>:" together (corresponding group close is
332332
// below at `lastTokenBeforeBrace`).
@@ -458,7 +458,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
458458
after(node.returnClause.lastToken(viewMode: .sourceAccurate), tokens: .close)
459459
}
460460

461-
arrangeAttributeList(node.attributes)
461+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
462462

463463
if let genericWhereClause = node.genericWhereClause {
464464
before(genericWhereClause.firstToken(viewMode: .sourceAccurate), tokens: .break(.same), .open)
@@ -513,7 +513,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
513513
) where BodyContents.Element: SyntaxProtocol {
514514
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)
515515

516-
arrangeAttributeList(attributes)
516+
arrangeAttributeList(attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
517517
arrangeBracesAndContents(of: body, contentsKeyPath: bodyContentsKeyPath)
518518

519519
if let genericWhereClause = genericWhereClause {
@@ -549,7 +549,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
549549
}
550550

551551
override func visit(_ node: AccessorDeclSyntax) -> SyntaxVisitorContinueKind {
552-
arrangeAttributeList(node.attributes)
552+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
553553
arrangeBracesAndContents(of: node.body, contentsKeyPath: \.statements)
554554
return .visitChildren
555555
}
@@ -1327,7 +1327,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
13271327
}
13281328

13291329
override func visit(_ node: MacroExpansionDeclSyntax) -> SyntaxVisitorContinueKind {
1330-
arrangeAttributeList(node.attributes)
1330+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
13311331

13321332
before(
13331333
node.trailingClosure?.leftBrace,
@@ -1546,7 +1546,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
15461546
override func visit(_ node: EnumCaseDeclSyntax) -> SyntaxVisitorContinueKind {
15471547
before(node.firstToken(viewMode: .sourceAccurate), tokens: .open)
15481548

1549-
arrangeAttributeList(node.attributes)
1549+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
15501550

15511551
after(node.caseKeyword, tokens: .break)
15521552
after(node.lastToken(viewMode: .sourceAccurate), tokens: .close)
@@ -2179,7 +2179,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
21792179
}
21802180

21812181
override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
2182-
arrangeAttributeList(node.attributes)
2182+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
21832183

21842184
if node.bindings.count == 1 {
21852185
// If there is only a single binding, don't allow a break between the `let/var` keyword and
@@ -2285,7 +2285,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
22852285
}
22862286

22872287
override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind {
2288-
arrangeAttributeList(node.attributes)
2288+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
22892289

22902290
after(node.typealiasKeyword, tokens: .break)
22912291

@@ -2499,7 +2499,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
24992499
}
25002500

25012501
override func visit(_ node: AssociatedTypeDeclSyntax) -> SyntaxVisitorContinueKind {
2502-
arrangeAttributeList(node.attributes)
2502+
arrangeAttributeList(node.attributes, separateByLineBreaks: config.lineBreakBetweenDeclarationAttributes)
25032503

25042504
after(node.associatedtypeKeyword, tokens: .break)
25052505

@@ -2890,14 +2890,30 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
28902890
/// Applies formatting tokens around and between the attributes in an attribute list.
28912891
private func arrangeAttributeList(
28922892
_ attributes: AttributeListSyntax?,
2893-
suppressFinalBreak: Bool = false
2893+
suppressFinalBreak: Bool = false,
2894+
separateByLineBreaks: Bool = false
28942895
) {
28952896
if let attributes = attributes {
2897+
let behavior: NewlineBehavior = separateByLineBreaks ? .hard : .elective
28962898
before(attributes.firstToken(viewMode: .sourceAccurate), tokens: .open)
2897-
insertTokens(.break(.same), betweenElementsOf: attributes)
2899+
for element in attributes.dropLast() {
2900+
if let ifConfig = element.as(IfConfigDeclSyntax.self) {
2901+
for clause in ifConfig.clauses {
2902+
if let nestedAttributes = AttributeListSyntax(clause.elements) {
2903+
arrangeAttributeList(
2904+
nestedAttributes,
2905+
suppressFinalBreak: true,
2906+
separateByLineBreaks: separateByLineBreaks
2907+
)
2908+
}
2909+
}
2910+
} else {
2911+
after(element.lastToken(viewMode: .sourceAccurate), tokens: .break(.same, newlines: behavior))
2912+
}
2913+
}
28982914
var afterAttributeTokens = [Token.close]
28992915
if !suppressFinalBreak {
2900-
afterAttributeTokens.append(.break(.same))
2916+
afterAttributeTokens.append(.break(.same, newlines: behavior))
29012917
}
29022918
after(attributes.lastToken(viewMode: .sourceAccurate), tokens: afterAttributeTokens)
29032919
}

Sources/SwiftFormat/Rules/AllPublicDeclarationsHaveDocumentation.swift

+12-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public final class AllPublicDeclarationsHaveDocumentation: SyntaxLintRule {
4646

4747
public override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
4848
diagnoseMissingDocComment(DeclSyntax(node), name: node.name.text, modifiers: node.modifiers)
49-
return .skipChildren
49+
return .visitChildren
5050
}
5151

5252
public override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
@@ -57,7 +57,17 @@ public final class AllPublicDeclarationsHaveDocumentation: SyntaxLintRule {
5757

5858
public override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
5959
diagnoseMissingDocComment(DeclSyntax(node), name: node.name.text, modifiers: node.modifiers)
60-
return .skipChildren
60+
return .visitChildren
61+
}
62+
63+
public override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
64+
diagnoseMissingDocComment(DeclSyntax(node), name: node.name.text, modifiers: node.modifiers)
65+
return .visitChildren
66+
}
67+
68+
public override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
69+
diagnoseMissingDocComment(DeclSyntax(node), name: node.name.text, modifiers: node.modifiers)
70+
return .visitChildren
6171
}
6272

6373
public override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {

Sources/SwiftFormat/Rules/AlwaysUseLowerCamelCase.swift

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import SwiftSyntax
1717
///
1818
/// This rule does not apply to test code, defined as code which:
1919
/// * Contains the line `import XCTest`
20+
/// * The function is marked with `@Test` attribute
2021
///
2122
/// Lint: If an identifier contains underscores or begins with a capital letter, a lint error is
2223
/// raised.

Sources/SwiftFormat/Rules/BeginDocumentationCommentWithOneLineSummary.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public final class BeginDocumentationCommentWithOneLineSummary: SyntaxLintRule {
4040

4141
public override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
4242
diagnoseDocComments(in: DeclSyntax(node))
43-
return .skipChildren
43+
return .visitChildren
4444
}
4545

4646
public override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
@@ -60,7 +60,7 @@ public final class BeginDocumentationCommentWithOneLineSummary: SyntaxLintRule {
6060

6161
public override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
6262
diagnoseDocComments(in: DeclSyntax(node))
63-
return .skipChildren
63+
return .visitChildren
6464
}
6565

6666
public override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
@@ -70,7 +70,7 @@ public final class BeginDocumentationCommentWithOneLineSummary: SyntaxLintRule {
7070

7171
public override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
7272
diagnoseDocComments(in: DeclSyntax(node))
73-
return .skipChildren
73+
return .visitChildren
7474
}
7575

7676
public override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {

0 commit comments

Comments
 (0)