Add rule for single space after period on comments (#4624)
This commit is contained in:
parent
33fa42becb
commit
3745704c03
|
@ -48,6 +48,7 @@ opt_in_rules:
|
||||||
- overridden_super_call
|
- overridden_super_call
|
||||||
- override_in_extension
|
- override_in_extension
|
||||||
- pattern_matching_keywords
|
- pattern_matching_keywords
|
||||||
|
- period_spacing
|
||||||
- prefer_self_type_over_type_of_self
|
- prefer_self_type_over_type_of_self
|
||||||
- private_action
|
- private_action
|
||||||
- private_outlet
|
- private_outlet
|
||||||
|
|
|
@ -49,6 +49,11 @@
|
||||||
`single_test_class` and `empty_xctest_method` rules.
|
`single_test_class` and `empty_xctest_method` rules.
|
||||||
[Martin Redington](https://github.com/mildm8nnered)
|
[Martin Redington](https://github.com/mildm8nnered)
|
||||||
[#4200](https://github.com/realm/SwiftLint/issues/4200)
|
[#4200](https://github.com/realm/SwiftLint/issues/4200)
|
||||||
|
|
||||||
|
* Add `period_spacing` opt-in rule that checks periods are not followed
|
||||||
|
by 2 or more spaces in comments.
|
||||||
|
[Julioacarrettoni](https://github.com/Julioacarrettoni)
|
||||||
|
[#4624](https://github.com/realm/SwiftLint/pull/4624)
|
||||||
|
|
||||||
* Show warnings in the console for Analyzer rules that are listed in the
|
* Show warnings in the console for Analyzer rules that are listed in the
|
||||||
`opt_in_rules` configuration section.
|
`opt_in_rules` configuration section.
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import IDEUtils
|
||||||
|
|
||||||
|
extension SyntaxClassification {
|
||||||
|
// True if it is any kind of comment.
|
||||||
|
var isComment: Bool {
|
||||||
|
switch self {
|
||||||
|
case .lineComment, .docLineComment, .blockComment, .docBlockComment:
|
||||||
|
return true
|
||||||
|
case .none, .keyword, .identifier, .typeIdentifier, .operatorIdentifier, .dollarIdentifier, .integerLiteral,
|
||||||
|
.floatingLiteral, .stringLiteral, .stringInterpolationAnchor, .poundDirectiveKeyword, .buildConfigId,
|
||||||
|
.attribute, .objectLiteral, .editorPlaceholder:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -137,6 +137,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
OverriddenSuperCallRule.self,
|
OverriddenSuperCallRule.self,
|
||||||
OverrideInExtensionRule.self,
|
OverrideInExtensionRule.self,
|
||||||
PatternMatchingKeywordsRule.self,
|
PatternMatchingKeywordsRule.self,
|
||||||
|
PeriodSpacingRule.self,
|
||||||
PreferNimbleRule.self,
|
PreferNimbleRule.self,
|
||||||
PreferSelfInStaticReferencesRule.self,
|
PreferSelfInStaticReferencesRule.self,
|
||||||
PreferSelfTypeOverTypeOfSelfRule.self,
|
PreferSelfTypeOverTypeOfSelfRule.self,
|
||||||
|
|
|
@ -122,15 +122,9 @@ struct CommentSpacingRule: SourceKitFreeRule, ConfigurationProviderRule, Substit
|
||||||
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
||||||
// Find all comment tokens in the file and regex search them for violations
|
// Find all comment tokens in the file and regex search them for violations
|
||||||
file.syntaxClassifications
|
file.syntaxClassifications
|
||||||
.compactMap { (classifiedRange: SyntaxClassifiedRange) -> [NSRange]? in
|
.filter(\.kind.isComment)
|
||||||
switch classifiedRange.kind {
|
.map { $0.range.toSourceKittenByteRange() }
|
||||||
case .blockComment, .docBlockComment, .lineComment, .docLineComment:
|
.compactMap { (range: ByteRange) -> [NSRange]? in
|
||||||
break
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let range = classifiedRange.range.toSourceKittenByteRange()
|
|
||||||
return file.stringView
|
return file.stringView
|
||||||
.substringWithByteRange(range)
|
.substringWithByteRange(range)
|
||||||
.map(StringView.init)
|
.map(StringView.init)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import Foundation
|
||||||
|
import IDEUtils
|
||||||
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
struct PeriodSpacingRule: SourceKitFreeRule, ConfigurationProviderRule, OptInRule, SubstitutionCorrectableRule {
|
||||||
|
var configuration = SeverityConfiguration(.warning)
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
|
||||||
|
static let description = RuleDescription(
|
||||||
|
identifier: "period_spacing",
|
||||||
|
name: "Period Spacing",
|
||||||
|
description: "Periods should not be followed by more than one space.",
|
||||||
|
kind: .style,
|
||||||
|
nonTriggeringExamples: [
|
||||||
|
Example("let pi = 3.2"),
|
||||||
|
Example("let pi = Double.pi"),
|
||||||
|
Example("let pi = Double. pi"),
|
||||||
|
Example("let pi = Double. pi"),
|
||||||
|
Example("// A. Single."),
|
||||||
|
Example("/// - code: Identifier of the error. Integer."),
|
||||||
|
Example("""
|
||||||
|
// value: Multiline.
|
||||||
|
// Comment.
|
||||||
|
"""),
|
||||||
|
Example("""
|
||||||
|
/**
|
||||||
|
Sentence ended in period.
|
||||||
|
|
||||||
|
- Sentence 2 new line characters after.
|
||||||
|
**/
|
||||||
|
""")
|
||||||
|
],
|
||||||
|
triggeringExamples: [
|
||||||
|
Example("/* Only god knows why. ↓ This symbol does nothing. */", testWrappingInComment: false),
|
||||||
|
Example("// Only god knows why. ↓ This symbol does nothing.", testWrappingInComment: false),
|
||||||
|
Example("// Single. Double. ↓ End.", testWrappingInComment: false),
|
||||||
|
Example("// Single. Double. ↓ Triple. ↓ End.", testWrappingInComment: false),
|
||||||
|
Example("// Triple. ↓ Quad. ↓ End.", testWrappingInComment: false),
|
||||||
|
Example("/// - code: Identifier of the error. ↓ Integer.", testWrappingInComment: false)
|
||||||
|
],
|
||||||
|
corrections: [
|
||||||
|
Example("/* Why. ↓ Symbol does nothing. */"): Example("/* Why. Symbol does nothing. */"),
|
||||||
|
Example("// Why. ↓ Symbol does nothing."): Example("// Why. Symbol does nothing."),
|
||||||
|
Example("// Single. Double. ↓ End."): Example("// Single. Double. End."),
|
||||||
|
Example("// Single. Double. ↓ Triple. ↓ End."): Example("// Single. Double. Triple. End."),
|
||||||
|
Example("// Triple. ↓ Quad. ↓ End."): Example("// Triple. Quad. End."),
|
||||||
|
Example("/// - code: Identifier. ↓ Integer."): Example("/// - code: Identifier. Integer.")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
||||||
|
// Find all comment tokens in the file and regex search them for violations
|
||||||
|
file.syntaxClassifications
|
||||||
|
.filter(\.kind.isComment)
|
||||||
|
.map { $0.range.toSourceKittenByteRange() }
|
||||||
|
.compactMap { (range: ByteRange) -> [NSRange]? in
|
||||||
|
return file.stringView
|
||||||
|
.substringWithByteRange(range)
|
||||||
|
.map(StringView.init)
|
||||||
|
.map { commentBody in
|
||||||
|
// Look for a period followed by two or more whitespaces but not new line or carriage returns
|
||||||
|
return regex(#"\.[^\S\r\n]{2,}"#)
|
||||||
|
.matches(in: commentBody)
|
||||||
|
.compactMap { result in
|
||||||
|
// Set the location to start from the second whitespace till the last one.
|
||||||
|
return file.stringView.byteRangeToNSRange(
|
||||||
|
ByteRange(
|
||||||
|
// Safe to mix NSRange offsets with byte offsets here because the
|
||||||
|
// regex can't contain multi-byte characters
|
||||||
|
location: ByteCount(range.lowerBound.value + result.range.lowerBound + 2),
|
||||||
|
length: ByteCount(result.range.length.advanced(by: -2))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flatMap { $0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func validate(file: SwiftLintFile) -> [StyleViolation] {
|
||||||
|
return violationRanges(in: file).map { range in
|
||||||
|
StyleViolation(
|
||||||
|
ruleDescription: Self.description,
|
||||||
|
severity: configuration.severity,
|
||||||
|
location: Location(file: file, characterOffset: range.location)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
|
||||||
|
return (violationRange, "")
|
||||||
|
}
|
||||||
|
}
|
|
@ -811,6 +811,12 @@ class PatternMatchingKeywordsRuleGeneratedTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PeriodSpacingRuleGeneratedTests: XCTestCase {
|
||||||
|
func testWithDefaultConfiguration() {
|
||||||
|
verifyRule(PeriodSpacingRule.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PreferNimbleRuleGeneratedTests: XCTestCase {
|
class PreferNimbleRuleGeneratedTests: XCTestCase {
|
||||||
func testWithDefaultConfiguration() {
|
func testWithDefaultConfiguration() {
|
||||||
verifyRule(PreferNimbleRule.description)
|
verifyRule(PreferNimbleRule.description)
|
||||||
|
|
Loading…
Reference in New Issue