Skip to content

Commit 1f9186b

Browse files
authored
Merge pull request swiftlang#17 from allevato/enable-disable-format-rules
Allow format rules to be enabled/disabled.
2 parents 5652419 + 82ee04d commit 1f9186b

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

Sources/SwiftFormatCore/RuleMask.swift

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -82,25 +82,58 @@ public class RuleMask {
8282
return nil
8383
}
8484

85+
/// Returns the list of line comments in the given trivia that are on a line by themselves
86+
/// (excluding leading whitespace).
87+
///
88+
/// - Parameters:
89+
/// - trivia: The trivia collection to scan for comments.
90+
/// - isFirstToken: True if the trivia came from the first token in the file.
91+
/// - Returns: The list of lone line comments from the trivia.
92+
private func loneLineComments(in trivia: Trivia, isFirstToken: Bool) -> [String] {
93+
var currentComment: String? = nil
94+
var lineComments = [String]()
95+
96+
for piece in trivia.reversed() {
97+
switch piece {
98+
case .lineComment(let text):
99+
currentComment = text
100+
case .spaces, .tabs:
101+
break // Intentionally do nothing.
102+
case .carriageReturnLineFeeds, .carriageReturns, .newlines:
103+
if let text = currentComment {
104+
lineComments.insert(text, at: 0)
105+
currentComment = nil
106+
}
107+
default:
108+
// If anything other than spaces intervened between the line comment and a newline, then the
109+
// comment isn't on a line by itself, so reset our state.
110+
currentComment = nil
111+
}
112+
}
113+
114+
// For the first token in the file, there may not be a newline preceding the first line comment,
115+
// so check for that here.
116+
if isFirstToken, let text = currentComment {
117+
lineComments.insert(text, at: 0)
118+
}
119+
120+
return lineComments
121+
}
122+
85123
/// Generate the dictionary (ruleMap) by walking the syntax tokens.
86124
private func generateDictionary(_ node: Syntax) {
87125
var disableStart: [String: Int] = [:]
88126
var enableStart: [String: Int] = [:]
89127

90-
for token in node.tokens {
91-
guard let leadingTrivia = token.leadingTrivia else { continue }
128+
var isFirstToken = true
92129

93-
// Flags must be on lines by themselves: not at the end of an existing line.
94-
var firstPiece = true
130+
for token in node.tokens {
131+
defer { isFirstToken = false }
95132

96-
for piece in leadingTrivia {
97-
guard case .lineComment(let text) = piece else {
98-
firstPiece = false
99-
continue
100-
}
101-
guard !firstPiece else { continue }
133+
guard let leadingTrivia = token.leadingTrivia else { continue }
102134

103-
if let ruleName = getRule(regex: disableRegex, text: text) {
135+
for comment in loneLineComments(in: leadingTrivia, isFirstToken: isFirstToken) {
136+
if let ruleName = getRule(regex: disableRegex, text: comment) {
104137
guard !disableStart.keys.contains(ruleName) else { continue }
105138
guard let disableStartLine = getLine(token) else { continue }
106139

@@ -113,7 +146,7 @@ public class RuleMask {
113146

114147
disableStart[ruleName] = disableStartLine
115148
}
116-
else if let ruleName = getRule(regex: enableRegex, text: text) {
149+
else if let ruleName = getRule(regex: enableRegex, text: comment) {
117150
guard !enableStart.keys.contains(ruleName) else { continue }
118151
guard let enableStartLine = getLine(token) else { continue }
119152

@@ -126,8 +159,6 @@ public class RuleMask {
126159

127160
enableStart[ruleName] = enableStartLine
128161
}
129-
130-
firstPiece = false
131162
}
132163
}
133164

Sources/SwiftFormatCore/SyntaxFormatRule.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,11 @@ open class SyntaxFormatRule: SyntaxRewriter, Rule {
2121
public required init(context: Context) {
2222
self.context = context
2323
}
24+
25+
open override func visitAny(_ node: Syntax) -> Syntax? {
26+
// If the rule is not enabled, then return the node unmodified; otherwise, returning nil tells
27+
// SwiftSyntax to continue with the standard dispatch.
28+
guard context.isRuleEnabled(Self.ruleName, node: node) else { return node }
29+
return nil
30+
}
2431
}

Tests/SwiftFormatRulesTests/DiagnosingTestCase.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public class DiagnosingTestCase: XCTestCase {
7272

7373
self.context = makeContext(sourceFileSyntax: sourceFileSyntax)
7474

75+
// Force the rule to be enabled while we test it.
76+
context!.configuration.rules[type.ruleName] = true
77+
7578
// If we're linting, then indicate that we want to fail for unasserted diagnostics when the test
7679
// is torn down.
7780
shouldCheckForUnassertedDiagnostics = true
@@ -110,6 +113,9 @@ public class DiagnosingTestCase: XCTestCase {
110113

111114
context = makeContext(sourceFileSyntax: sourceFileSyntax)
112115

116+
// Force the rule to be enabled while we test it.
117+
context!.configuration.rules[formatType.ruleName] = true
118+
113119
shouldCheckForUnassertedDiagnostics = checkForUnassertedDiagnostics
114120
let formatter = formatType.init(context: context!)
115121
let result = formatter.visit(sourceFileSyntax)

0 commit comments

Comments
 (0)