@@ -97,6 +97,8 @@ extension SourceRange {
97
97
98
98
/// Represents the kind of ignore directive encountered in the source.
99
99
enum IgnoreDirective : CustomStringConvertible {
100
+ typealias RegexExpression = Regex < ( Substring , ruleNames: Substring ? ) >
101
+
100
102
/// A node-level directive that disables rules for the following node and its children.
101
103
case node
102
104
/// A file-level directive that disables rules for the entire file.
@@ -111,10 +113,14 @@ enum IgnoreDirective: CustomStringConvertible {
111
113
}
112
114
}
113
115
114
- /// Regex pattern to match an ignore comment. This pattern supports 0 or more comma delimited rule
115
- /// names. The rule name(s), when present, are in capture group #3.
116
- fileprivate var pattern : String {
117
- return #"^\s*\/\/\s*"# + description + #"((:\s+(([A-z0-9]+[,\s]*)+))?$|\s+$)"#
116
+ /// Regex pattern to match an ignore directive comment.
117
+ /// - Captures rule names when `:` is present.
118
+ ///
119
+ /// Note: We are using a string-based regex instead of a regex literal (`#/regex/#`)
120
+ /// because Windows did not have full support for regex literals until Swift 5.10.
121
+ fileprivate func makeRegex( ) -> RegexExpression {
122
+ let pattern = #"^\s*\/\/\s*"# + description + #"(?:\s*:\s*(?<ruleNames>.+))?$"#
123
+ return try ! Regex ( pattern)
118
124
}
119
125
}
120
126
@@ -140,10 +146,10 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
140
146
private let sourceLocationConverter : SourceLocationConverter
141
147
142
148
/// Cached regex object for ignoring rules at the node.
143
- private let ignoreRegex : NSRegularExpression
149
+ private let ignoreRegex : IgnoreDirective . RegexExpression
144
150
145
151
/// Cached regex object for ignoring rules at the file.
146
- private let ignoreFileRegex : NSRegularExpression
152
+ private let ignoreFileRegex : IgnoreDirective . RegexExpression
147
153
148
154
/// Stores the source ranges in which all rules are ignored.
149
155
var allRulesIgnoredRanges : [ SourceRange ] = [ ]
@@ -152,8 +158,8 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
152
158
var ruleMap : [ String : [ SourceRange ] ] = [ : ]
153
159
154
160
init ( sourceLocationConverter: SourceLocationConverter ) {
155
- ignoreRegex = try ! NSRegularExpression ( pattern : IgnoreDirective . node. pattern , options : [ ] )
156
- ignoreFileRegex = try ! NSRegularExpression ( pattern : IgnoreDirective . file. pattern , options : [ ] )
161
+ ignoreRegex = IgnoreDirective . node. makeRegex ( )
162
+ ignoreFileRegex = IgnoreDirective . file. makeRegex ( )
157
163
158
164
self . sourceLocationConverter = sourceLocationConverter
159
165
super. init ( viewMode: . sourceAccurate)
@@ -202,7 +208,7 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
202
208
private func appendRuleStatus(
203
209
from token: TokenSyntax ,
204
210
of sourceRange: SourceRange ,
205
- using regex: NSRegularExpression
211
+ using regex: IgnoreDirective . RegexExpression
206
212
) -> SyntaxVisitorContinueKind {
207
213
let isFirstInFile = token. previousToken ( viewMode: . sourceAccurate) == nil
208
214
let comments = loneLineComments ( in: token. leadingTrivia, isFirstToken: isFirstInFile)
@@ -227,18 +233,15 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
227
233
/// match, its contents (e.g. list of rule names) are returned.
228
234
private func ruleStatusDirectiveMatch(
229
235
in text: String ,
230
- using regex: NSRegularExpression
236
+ using regex: IgnoreDirective . RegexExpression
231
237
) -> RuleStatusDirectiveMatch ? {
232
- let textRange = NSRange ( text. startIndex..< text. endIndex, in: text)
233
- guard let match = regex. firstMatch ( in: text, options: [ ] , range: textRange) else {
238
+ guard let match = text. firstMatch ( of: regex) else {
234
239
return nil
235
240
}
236
- guard match. numberOfRanges == 5 else { return . all }
237
- let matchRange = match. range ( at: 3 )
238
- guard matchRange. location != NSNotFound, let ruleNamesRange = Range ( matchRange, in: text) else {
241
+ guard let matchedRuleNames = match. output. ruleNames else {
239
242
return . all
240
243
}
241
- let rules = text [ ruleNamesRange ] . split ( separator: " , " )
244
+ let rules = matchedRuleNames . split ( separator: " , " )
242
245
. map { $0. trimmingCharacters ( in: . whitespaces) }
243
246
. filter { $0. count > 0 }
244
247
return . subset( ruleNames: rules)
0 commit comments