@@ -11,5 +11,195 @@ import SwiftSyntax
1111///
1212/// - SeeAlso: https://google.github.io/swift#horizontal-whitespace
1313public final class OneSpaceAfterKeywords : SyntaxFormatRule {
14+ public override func visit( _ token: TokenSyntax ) -> Syntax {
15+ guard let nextToken = token. nextToken else { return token }
16+
17+ // Keywords own their trailing spaces, so ensure it only has 1, if there's
18+ // another token on the same line.
19+ if token. tokenKind. isTypeKeyword,
20+ ( nextToken. tokenKind. isTypeKeyword || isIdentifier ( nextToken. tokenKind) ) ,
21+ !nextToken. leadingTrivia. containsNewlines {
22+ let numSpaces = token. trailingTrivia. numberOfSpaces
23+ if numSpaces > 1 {
24+ diagnose ( . removeSpacesAfterKeyword( numSpaces - 1 , token. description) , on: token)
25+ return token. withOneTrailingSpace ( )
26+ }
27+ }
28+ return token
29+ }
30+ }
31+
32+ /// Indicates if the token kind is an identifier
33+ func isIdentifier( _ tokKind: TokenKind ) -> Bool {
34+ if case . identifier( _) = tokKind {
35+ return true
36+ }
37+ return false
38+ }
39+ extension Diagnostic . Message {
40+ static func removeSpacesAfterKeyword( _ count: Int , _ keyWord: String ) -> Diagnostic . Message {
41+ let ending = count == 1 ? " " : " s "
42+ return Diagnostic . Message ( . warning, " remove \( count) space \( ending) after ' \( keyWord) ' " )
43+ }
44+ }
1445
46+ extension TokenKind {
47+
48+ // Indicates if the token kind is a keyword.
49+ var isTypeKeyword : Bool {
50+ switch self {
51+ case . associatedtypeKeyword:
52+ return true
53+ case . classKeyword:
54+ return true
55+ case . enumKeyword:
56+ return true
57+ case . extensionKeyword:
58+ return true
59+ case . funcKeyword:
60+ return true
61+ case . importKeyword:
62+ return true
63+ case . initKeyword:
64+ return true
65+ case . inoutKeyword:
66+ return true
67+ case . letKeyword:
68+ return true
69+ case . operatorKeyword:
70+ return true
71+ case . precedencegroupKeyword:
72+ return true
73+ case . protocolKeyword:
74+ return true
75+ case . structKeyword:
76+ return true
77+ case . subscriptKeyword:
78+ return true
79+ case . typealiasKeyword:
80+ return true
81+ case . varKeyword:
82+ return true
83+ case . fileprivateKeyword:
84+ return true
85+ case . internalKeyword:
86+ return true
87+ case . privateKeyword:
88+ return true
89+ case . publicKeyword:
90+ return true
91+ case . staticKeyword:
92+ return true
93+ case . deferKeyword:
94+ return true
95+ case . ifKeyword:
96+ return true
97+ case . guardKeyword:
98+ return true
99+ case . doKeyword:
100+ return true
101+ case . repeatKeyword:
102+ return true
103+ case . elseKeyword:
104+ return true
105+ case . forKeyword:
106+ return true
107+ case . inKeyword:
108+ return true
109+ case . whileKeyword:
110+ return true
111+ case . returnKeyword:
112+ return true
113+ case . breakKeyword:
114+ return true
115+ case . continueKeyword:
116+ return true
117+ case . fallthroughKeyword:
118+ return true
119+ case . switchKeyword:
120+ return true
121+ case . caseKeyword:
122+ return true
123+ case . defaultKeyword:
124+ return true
125+ case . whereKeyword:
126+ return true
127+ case . catchKeyword:
128+ return true
129+ case . asKeyword:
130+ return true
131+ case . anyKeyword:
132+ return true
133+ case . falseKeyword:
134+ return true
135+ case . isKeyword:
136+ return true
137+ case . nilKeyword:
138+ return true
139+ case . rethrowsKeyword:
140+ return true
141+ case . superKeyword:
142+ return true
143+ case . selfKeyword:
144+ return true
145+ case . capitalSelfKeyword:
146+ return true
147+ case . throwKeyword:
148+ return true
149+ case . trueKeyword:
150+ return true
151+ case . tryKeyword:
152+ return true
153+ case . throwsKeyword:
154+ return true
155+ case . __file__Keyword:
156+ return true
157+ case . __line__Keyword:
158+ return true
159+ case . __column__Keyword:
160+ return true
161+ case . __function__Keyword:
162+ return true
163+ case . __dso_handle__Keyword:
164+ return true
165+ case . wildcardKeyword:
166+ return true
167+ case . poundAvailableKeyword:
168+ return true
169+ case . poundEndifKeyword:
170+ return true
171+ case . poundElseKeyword:
172+ return true
173+ case . poundElseifKeyword:
174+ return true
175+ case . poundIfKeyword:
176+ return true
177+ case . poundSourceLocationKeyword:
178+ return true
179+ case . poundFileKeyword:
180+ return true
181+ case . poundLineKeyword:
182+ return true
183+ case . poundColumnKeyword:
184+ return true
185+ case . poundDsohandleKeyword:
186+ return true
187+ case . poundFunctionKeyword:
188+ return true
189+ case . poundSelectorKeyword:
190+ return true
191+ case . poundKeyPathKeyword:
192+ return true
193+ case . poundColorLiteralKeyword:
194+ return true
195+ case . poundFileLiteralKeyword:
196+ return true
197+ case . poundImageLiteralKeyword:
198+ return true
199+ case . contextualKeyword( _) :
200+ return true
201+ default :
202+ return false
203+ }
204+ }
15205}
0 commit comments