SwiftLint/Source/SwiftLintFramework/Rules/Lint/CommentSpacingRule.swift

173 lines
6.0 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
import IDEUtils
import SourceKittenFramework
struct CommentSpacingRule: SourceKitFreeRule, ConfigurationProviderRule, SubstitutionCorrectableRule {
var configuration = SeverityConfiguration(.warning)
init() {}
static let description = RuleDescription(
identifier: "comment_spacing",
name: "Comment Spacing",
description: "Prefer at least one space after slashes for comments.",
kind: .lint,
nonTriggeringExamples: [
Example("""
// This is a comment
"""),
Example("""
/// Triple slash comment
"""),
Example("""
// Multiline double-slash
// comment
"""),
Example("""
/// Multiline triple-slash
/// comment
"""),
Example("""
/// Multiline triple-slash
/// - This is indented
"""),
Example("""
// - MARK: Mark comment
"""),
Example("""
//: Swift Playground prose section
"""),
Example("""
///////////////////////////////////////////////
// Comment with some lines of slashes boxing it
///////////////////////////////////////////////
"""),
Example("""
//:#localized(key: "SwiftPlaygroundLocalizedProse")
"""),
Example("""
/* Asterisk comment */
"""),
Example("""
/*
Multiline asterisk comment
*/
"""),
Example("""
/*:
Multiline Swift Playground prose section
*/
"""),
Example("""
/*#-editable-code Swift Platground editable area*/default/*#-end-editable-code*/
""")
],
triggeringExamples: [
Example("""
//↓Something
"""),
Example("""
//↓MARK
"""),
Example("""
//↓👨👨👦👦Something
"""),
Example("""
func a() {
//↓This needs refactoring
print("Something")
}
//↓We should improve above function
"""),
Example("""
///↓This is a comment
"""),
Example("""
/// Multiline triple-slash
///↓This line is incorrect, though
"""),
Example("""
//↓- MARK: Mark comment
"""),
Example("""
//:↓Swift Playground prose section
""")
],
corrections: [
Example("//↓Something"): Example("// Something"),
Example("//↓- MARK: Mark comment"): Example("// - MARK: Mark comment"),
Example("""
/// Multiline triple-slash
///↓This line is incorrect, though
"""): Example("""
/// Multiline triple-slash
/// This line is incorrect, though
"""),
Example("""
func a() {
//↓This needs refactoring
print("Something")
}
//↓We should improve above function
"""): Example("""
func a() {
// This needs refactoring
print("Something")
}
// We should improve above function
""")
]
)
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
// Find all comment tokens in the file and regex search them for violations
file.syntaxClassifications
.compactMap { (classifiedRange: SyntaxClassifiedRange) -> [NSRange]? in
switch classifiedRange.kind {
case .blockComment, .docBlockComment, .lineComment, .docLineComment:
break
default:
return nil
}
let range = classifiedRange.range.toSourceKittenByteRange()
return file.stringView
.substringWithByteRange(range)
.map(StringView.init)
.map { commentBody in
// Look for 2+ slash characters followed immediately by
// a non-colon, non-whitespace character or by a colon
// followed by a non-whitespace character other than #
regex(#"^(?:\/){2,}+(?:[^\s:]|:[^\s#])"#).matches(in: commentBody, options: .anchored)
.compactMap { result in
// Set the location to be directly before the first non-slash,
// non-whitespace character which was matched
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.upperBound - 1),
length: 0
)
)
}
}
}
.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, " ")
}
}