@@ -18,122 +18,62 @@ import SwiftSyntax
18
18
///
19
19
/// Optionally, declarations of single-line properties can be ignored.
20
20
///
21
- /// Lint: If more than the maximum number of blank lines appear, a lint error is raised.
22
- /// If there are no blank lines between members, a lint error is raised.
21
+ /// This rule does not check the maximum number of blank lines; the pretty printer clamps those
22
+ /// as needed.
23
+ ///
24
+ /// Lint: If there are no blank lines between members, a lint error is raised.
23
25
///
24
26
/// Format: Declarations with no blank lines will have a blank line inserted.
25
- /// Declarations with more than the maximum number of blank lines will be reduced to the
26
- /// maximum number of blank lines.
27
27
///
28
- /// Configuration: maximumBlankLines, blankLineBetweenMembers.ignoreSingleLineProperties
28
+ /// Configuration: blankLineBetweenMembers.ignoreSingleLineProperties
29
29
///
30
30
/// - SeeAlso: https://google.github.io/swift#vertical-whitespace
31
31
public final class BlankLineBetweenMembers : SyntaxFormatRule {
32
32
public override func visit( _ node: MemberDeclBlockSyntax ) -> Syntax {
33
- var membersList = [ MemberDeclListItemSyntax] ( )
34
- var hasValidNumOfBlankLines = true
33
+ guard let firstMember = node. members. first else { return super. visit ( node) }
34
+
35
+ let ignoreSingleLine = context. configuration. blankLineBetweenMembers. ignoreSingleLineProperties
36
+
37
+ // The first member can just be added as-is; we don't force any newline before it.
38
+ var membersList = [ visitNestedDecls ( of: firstMember) ]
35
39
36
40
// Iterates through all the declaration of the member, to ensure that the declarations have
37
- // at least on blank line and doesn't exceed the maximum number of blank lines.
38
- for member in node. members {
39
- let currentMember = checkForNestedMembers ( member)
40
- guard let memberTrivia = currentMember. leadingTrivia else { continue }
41
- let triviaWithoutTrailingSpaces = memberTrivia. withoutTrailingSpaces ( )
42
- guard let firstPiece = triviaWithoutTrailingSpaces. first else { continue }
41
+ // at least one blank line between them when necessary.
42
+ var previousMemberWasSingleLine = firstMember. isSingleLine (
43
+ includingLeadingComment: true ,
44
+ sourceLocationConverter: context. sourceLocationConverter
45
+ )
43
46
44
- if exceedsMaxBlankLines ( triviaWithoutTrailingSpaces) {
45
- let correctTrivia = removeExtraBlankLines ( triviaWithoutTrailingSpaces, currentMember)
46
- let newMember = replaceTrivia (
47
- on: currentMember,
48
- token: currentMember. firstToken!,
49
- leadingTrivia: correctTrivia
50
- ) as! MemberDeclListItemSyntax
51
-
52
- hasValidNumOfBlankLines = false
53
- membersList. append ( newMember)
54
- }
55
- // Ensures that there is at least one blank line between each member of a type.
56
- // Unless is a single-line declaration and the format is configured to
57
- // ignored them.
58
- else if case . newlines( let numNewLines) = firstPiece,
59
- !ignoreItem( item: currentMember) ,
60
- numNewLines == 1 {
61
- let numBlankLines = member. indexInParent == 0 ? 0 : 1
62
- let correctTrivia = Trivia . newlines ( numBlankLines) + memberTrivia
63
- let newMember = replaceTrivia (
64
- on: currentMember, token: currentMember. firstToken!,
47
+ for member in node. members. dropFirst ( ) {
48
+ var memberToAdd = visitNestedDecls ( of: member)
49
+
50
+ let memberIsSingleLine = memberToAdd. isSingleLine (
51
+ includingLeadingComment: true ,
52
+ sourceLocationConverter: context. sourceLocationConverter
53
+ )
54
+
55
+ if let memberTrivia = memberToAdd. leadingTrivia,
56
+ !( ignoreSingleLine && previousMemberWasSingleLine && memberIsSingleLine)
57
+ && memberTrivia. numberOfLeadingNewlines == 1 // Only one newline => no blank line
58
+ {
59
+ let correctTrivia = Trivia . newlines ( 1 ) + memberTrivia
60
+ memberToAdd = replaceTrivia (
61
+ on: memberToAdd, token: memberToAdd. firstToken!,
65
62
leadingTrivia: correctTrivia
66
63
) as! MemberDeclListItemSyntax
67
-
68
- diagnose ( . addBlankLine, on: currentMember)
69
- hasValidNumOfBlankLines = false
70
- membersList. append ( newMember)
71
- }
72
- else {
73
- membersList. append ( member)
74
- }
75
- }
76
-
77
- return hasValidNumOfBlankLines ? node :
78
- node. withMembers ( SyntaxFactory . makeMemberDeclList ( membersList) )
79
- }
80
-
81
- /// Indicates if the given trivia has more than
82
- /// the maximum number of blank lines.
83
- func exceedsMaxBlankLines( _ trivia: Trivia ) -> Bool {
84
- let maxBlankLines = context. configuration. maximumBlankLines
85
64
86
- for piece in trivia {
87
- if case . newlines( let num) = piece,
88
- num - 1 > maxBlankLines {
89
- return true
65
+ diagnose ( . addBlankLine, on: memberToAdd)
90
66
}
91
- }
92
- return false
93
- }
94
-
95
- /// Returns the given trivia without any set of consecutive blank lines
96
- /// that exceeds the maximumBlankLines.
97
- func removeExtraBlankLines( _ trivia: Trivia , _ member: MemberDeclListItemSyntax ) -> Trivia {
98
- let maxBlankLines = context. configuration. maximumBlankLines
99
- var pieces = [ TriviaPiece] ( )
100
-
101
- // Iterates through the trivia, verifying that the number of blank
102
- // lines in the file do not exceed the maximumBlankLines. If it does
103
- // a lint error is raised.
104
- for piece in trivia {
105
- if case . newlines( let num) = piece,
106
- num - 1 > maxBlankLines {
107
- pieces. append ( . newlines( maxBlankLines + 1 ) )
108
- diagnose ( . removeBlankLines( count: num - maxBlankLines) , on: member)
109
- }
110
- else {
111
- pieces. append ( piece)
112
- }
113
- }
114
- return Trivia ( pieces: pieces)
115
- }
116
-
117
- /// Indicates if a declaration has to be ignored by checking if it's
118
- /// a single line and if the format is configured to ignore single lines.
119
- func ignoreItem( item: MemberDeclListItemSyntax ) -> Bool {
120
- guard let firstToken = item. firstToken else { return false }
121
- guard let lastToken = item. lastToken else { return false }
122
67
123
- let firstTokenLocation = context. sourceLocationConverter. location (
124
- for: firstToken. positionAfterSkippingLeadingTrivia)
125
- let lastTokenLocation = context. sourceLocationConverter. location (
126
- for: lastToken. positionAfterSkippingLeadingTrivia)
127
- let isSingleLine = firstTokenLocation. line == lastTokenLocation. line
128
-
129
- let ignoreLine = context. configuration. blankLineBetweenMembers
130
- . ignoreSingleLineProperties
68
+ membersList. append ( memberToAdd)
69
+ previousMemberWasSingleLine = memberIsSingleLine
70
+ }
131
71
132
- return isSingleLine && ignoreLine
72
+ return node . withMembers ( SyntaxFactory . makeMemberDeclList ( membersList ) )
133
73
}
134
74
135
75
/// Recursively ensures all nested member types follows the BlankLineBetweenMembers rule.
136
- func checkForNestedMembers ( _ member: MemberDeclListItemSyntax ) -> MemberDeclListItemSyntax {
76
+ func visitNestedDecls ( of member: MemberDeclListItemSyntax ) -> MemberDeclListItemSyntax {
137
77
switch member. decl {
138
78
case let nestedEnum as EnumDeclSyntax :
139
79
let nestedMembers = visit ( nestedEnum. members)
@@ -157,22 +97,6 @@ public final class BlankLineBetweenMembers: SyntaxFormatRule {
157
97
}
158
98
}
159
99
160
- /// Indicates if the given trivia piece is any type of comment.
161
- func isComment( _ triviaPiece: TriviaPiece ) -> Bool {
162
- switch triviaPiece {
163
- case . lineComment( _) , . docLineComment( _) ,
164
- . blockComment( _) , . docBlockComment( _) :
165
- return true
166
- default :
167
- return false
168
- }
169
- }
170
-
171
100
extension Diagnostic . Message {
172
101
static let addBlankLine = Diagnostic . Message ( . warning, " add one blank line between declarations " )
173
-
174
- static func removeBlankLines( count: Int ) -> Diagnostic . Message {
175
- let ending = count > 1 ? " s " : " "
176
- return Diagnostic . Message ( . warning, " remove \( count) blank line \( ending) " )
177
- }
178
102
}
0 commit comments