mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
40828dff03
* master: (41 commits) Fix formatting in CHANGELOG.md release 0.13.0 Update CHANGELOG.md Fix check for trailing whitespace to return early Fix checks for some inline comments Replace check for comments to use SyntaxKind Add configuration for trailing_whitespace to ignore comments Unwanted space removed - Lint issues fixed Updated HTML Reporter PR feedback Add check on autocorrect for disabled range Use `utf8.count` instead of `utf16.count` to byte range Re-write `ExplicitInitRule` to `ASTRule` added ExplicitInitRule Updated CHANGELOG HTML Reporter added HTML Reporter added Adds information about SwiftLint plugin for AppCode into README.md added reasons why a new rule should be opt in ... # Conflicts: # Source/SwiftLintFramework/Extensions/File+SwiftLint.swift # Source/SwiftLintFramework/Extensions/Structure+SwiftLint.swift # Source/SwiftLintFramework/Rules/ColonRule.swift # Source/SwiftLintFramework/Rules/CommaRule.swift # Source/SwiftLintFramework/Rules/LegacyCGGeometryFunctionsRule.swift # Source/SwiftLintFramework/Rules/LegacyConstantRule.swift # Source/SwiftLintFramework/Rules/LegacyConstructorRule.swift # Source/SwiftLintFramework/Rules/LegacyNSGeometryFunctionsRule.swift # Source/SwiftLintFramework/Rules/LineLengthRule.swift # Source/SwiftLintFramework/Rules/OperatorFunctionWhitespaceRule.swift # Source/SwiftLintFramework/Rules/ReturnArrowWhitespaceRule.swift # Source/SwiftLintFramework/Rules/RuleConfigurations/StatementPositionConfiguration.swift # Source/SwiftLintFramework/Rules/StatementPositionRule.swift # Source/SwiftLintFramework/Rules/TrailingWhitespaceRule.swift # Tests/SwiftLintFramework/RuleConfigurationTests.swift
141 lines
6.0 KiB
Swift
141 lines
6.0 KiB
Swift
//
|
|
// Comma.swift
|
|
// SwiftLint
|
|
//
|
|
// Created by Alex Culeva on 10/22/15.
|
|
// Copyright © 2015 Realm. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import SourceKittenFramework
|
|
|
|
public struct CommaRule: CorrectableRule, ConfigurationProviderRule {
|
|
|
|
public var configuration = SeverityConfiguration(.Warning)
|
|
|
|
public init() {}
|
|
|
|
public static let description = RuleDescription(
|
|
identifier: "comma",
|
|
name: "Comma Spacing",
|
|
description: "There should be no space before and one after any comma.",
|
|
nonTriggeringExamples: [
|
|
"func abc(a: String, b: String) { }",
|
|
"abc(a: \"string\", b: \"string\"",
|
|
"enum a { case a, b, c }",
|
|
"func abc(\n a: String, // comment\n bcd: String // comment\n) {\n}\n",
|
|
"func abc(\n a: String,\n bcd: String\n) {\n}\n",
|
|
],
|
|
triggeringExamples: [
|
|
"func abc(a: String↓ ,b: String) { }",
|
|
"abc(a: \"string\"↓,b: \"string\"",
|
|
"enum a { case a↓ ,b }",
|
|
"let result = plus(\n first: 3↓ , // #683\n second: 4\n)\n",
|
|
],
|
|
corrections: [
|
|
"func abc(a: String,b: String) {}\n": "func abc(a: String, b: String) {}\n",
|
|
"abc(a: \"string\",b: \"string\"\n": "abc(a: \"string\", b: \"string\"\n",
|
|
"abc(a: \"string\" , b: \"string\"\n": "abc(a: \"string\", b: \"string\"\n",
|
|
"enum a { case a ,b }\n": "enum a { case a, b }\n",
|
|
"let a = [1,1]\nlet b = 1\nf(1, b)\n": "let a = [1, 1]\nlet b = 1\nf(1, b)\n",
|
|
]
|
|
)
|
|
|
|
public func validateFile(_ file: File) -> [StyleViolation] {
|
|
return violationRangesInFile(file).map {
|
|
StyleViolation(ruleDescription: type(of: self).description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: $0.location))
|
|
}
|
|
}
|
|
|
|
public func correctFile(_ file: File) -> [Correction] {
|
|
let violations = violationRangesInFile(file)
|
|
let matches = file.ruleEnabledViolatingRanges(violations, forRule: self)
|
|
if matches.isEmpty { return [] }
|
|
|
|
var contents = file.contents as NSString
|
|
let description = type(of: self).description
|
|
var corrections = [Correction]()
|
|
for range in matches.reversed() {
|
|
contents = contents.replacingCharacters(in: range, with: ", ") as NSString
|
|
let location = Location(file: file, characterOffset: range.location)
|
|
corrections.append(Correction(ruleDescription: description, location: location))
|
|
}
|
|
file.write(contents as String)
|
|
return corrections
|
|
}
|
|
|
|
// captures spaces and comma only
|
|
// http://userguide.icu-project.org/strings/regexp
|
|
fileprivate static let pattern =
|
|
"\\S" + // not whitespace
|
|
"(" + // start first capure
|
|
"\\s+" + // followed by whitespace
|
|
"," + // to the left of a comma
|
|
"[\\t\\p{Z}]*" + // followed by any amount of tab or space.
|
|
"|" + // or
|
|
"," + // immediately followed by a comma
|
|
"(?:[\\t\\p{Z}]{0}|" + // followed by 0
|
|
"[\\t\\p{Z}]{2,})" + // or 2+ tab or space characters.
|
|
")" + // end capture
|
|
"(\\S)" // second capture is not whitespace.
|
|
|
|
// swiftlint:disable:next force_try
|
|
fileprivate static let regularExpression = try! NSRegularExpression(pattern: pattern)
|
|
fileprivate static let excludingSyntaxKindsForFirstCapture = SyntaxKind.commentAndStringKinds()
|
|
.map { $0.rawValue }
|
|
fileprivate static let excludingSyntaxKindsForSecondCapture = SyntaxKind.commentKinds()
|
|
.map { $0.rawValue }
|
|
|
|
fileprivate func violationRangesInFile(_ file: File) -> [NSRange] {
|
|
let contents = file.contents
|
|
let range = NSRange(location: 0, length: contents.utf16.count)
|
|
let syntaxMap = file.syntaxMap
|
|
return CommaRule.regularExpression
|
|
.matches(in: contents, options: [], range: range)
|
|
.flatMap { match -> NSRange? in
|
|
if match.numberOfRanges != 3 { return nil }
|
|
|
|
// check first captured range
|
|
let firstRange = match.rangeAt(1)
|
|
guard let matchByteFirstRange = contents
|
|
.NSRangeToByteRange(start: firstRange.location, length: firstRange.length)
|
|
else { return nil }
|
|
|
|
// first captured range won't match tokens if it is not comment neither string
|
|
let tokensInFirstRange = syntaxMap.tokensIn(matchByteFirstRange)
|
|
.filter { CommaRule.excludingSyntaxKindsForFirstCapture.contains($0.type) }
|
|
|
|
// If not empty, first captured range is comment or string
|
|
if !tokensInFirstRange.isEmpty {
|
|
return nil
|
|
}
|
|
|
|
// If the first range does not start with comma, it already violates this rule
|
|
// no matter what is contained in the second range.
|
|
if !(contents as NSString).substring(with: firstRange).hasPrefix(",") {
|
|
return firstRange
|
|
}
|
|
|
|
// check second captured range
|
|
let secondRange = match.rangeAt(2)
|
|
guard let matchByteSecondRange = contents
|
|
.NSRangeToByteRange(start: secondRange.location, length: secondRange.length)
|
|
else { return nil }
|
|
|
|
// second captured range won't match tokens if it is not comment
|
|
let tokensInSecondRange = syntaxMap.tokensIn(matchByteSecondRange)
|
|
.filter { CommaRule.excludingSyntaxKindsForSecondCapture.contains($0.type) }
|
|
|
|
// If not empty, second captured range is comment
|
|
if !tokensInSecondRange.isEmpty {
|
|
return nil
|
|
}
|
|
|
|
// return first captured range
|
|
return firstRange
|
|
}
|
|
}
|
|
}
|