131 lines
4.6 KiB
Swift
131 lines
4.6 KiB
Swift
//
|
|
// OpeningBraceRule.swift
|
|
// SwiftLint
|
|
//
|
|
// Created by Alex Culeva on 10/21/15.
|
|
// Copyright © 2015 Realm. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import SourceKittenFramework
|
|
|
|
private let whitespaceAndNewlineCharacterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet()
|
|
|
|
extension File {
|
|
private func violatingOpeningBraceRanges() -> [NSRange] {
|
|
return matchPattern(
|
|
"((?:[^( ]|[\\s(][\\s]+)\\{)",
|
|
excludingSyntaxKinds: SyntaxKind.commentAndStringKinds()
|
|
)
|
|
}
|
|
}
|
|
|
|
public struct OpeningBraceRule: CorrectableRule, ConfigProviderRule {
|
|
|
|
public var config = SeverityConfig(.Warning)
|
|
|
|
public init() {}
|
|
|
|
public static let description = RuleDescription(
|
|
identifier: "opening_brace",
|
|
name: "Opening Brace Spacing",
|
|
description: "Opening braces should be preceded by a single space and on the same line " +
|
|
"as the declaration.",
|
|
nonTriggeringExamples: [
|
|
Trigger("func abc() {\n}"),
|
|
Trigger("[].map() { $0 }"),
|
|
Trigger("[].map({ })")
|
|
],
|
|
triggeringExamples: [
|
|
Trigger("func abc(↓){\n}"),
|
|
Trigger("func abc()↓\n\t{ }"),
|
|
Trigger("[].map(↓){ $0 }"),
|
|
Trigger("[].map↓( { } )")
|
|
],
|
|
corrections: [
|
|
"struct Rule{}\n": "struct Rule {}\n",
|
|
"struct Rule\n{\n}\n": "struct Rule {\n}\n",
|
|
"struct Rule\n\n\t{\n}\n": "struct Rule {\n}\n",
|
|
"struct Parent {\n\tstruct Child\n\t{\n\t\tlet foo: Int\n\t}\n}\n":
|
|
"struct Parent {\n\tstruct Child {\n\t\tlet foo: Int\n\t}\n}\n",
|
|
"[].map(){ $0 }\n": "[].map() { $0 }\n",
|
|
"[].map( { })\n": "[].map({ })\n",
|
|
]
|
|
)
|
|
|
|
public func validateFile(file: File) -> [StyleViolation] {
|
|
return file.violatingOpeningBraceRanges().map {
|
|
StyleViolation(ruleDescription: self.dynamicType.description,
|
|
severity: config.severity,
|
|
location: Location(file: file, characterOffset: $0.location))
|
|
}
|
|
}
|
|
|
|
public func correctFile(file: File) -> [Correction] {
|
|
let violatingRanges = file.ruleEnabledViolatingRanges(
|
|
file.violatingOpeningBraceRanges(),
|
|
forRule: self
|
|
)
|
|
return writeToFile(file, violatingRanges: violatingRanges)
|
|
}
|
|
|
|
private func writeToFile(file: File, violatingRanges: [NSRange]) -> [Correction] {
|
|
var correctedContents = file.contents
|
|
var adjustedLocations = [Int]()
|
|
|
|
for violatingRange in violatingRanges.reverse() {
|
|
let (contents, adjustedRange) =
|
|
correctContents(correctedContents, violatingRange: violatingRange)
|
|
|
|
correctedContents = contents
|
|
if let adjustedRange = adjustedRange {
|
|
adjustedLocations.insert(adjustedRange.location, atIndex: 0)
|
|
}
|
|
}
|
|
|
|
file.write(correctedContents)
|
|
|
|
return adjustedLocations.map {
|
|
Correction(ruleDescription: self.dynamicType.description,
|
|
location: Location(file: file, characterOffset: $0))
|
|
}
|
|
}
|
|
|
|
private func correctContents(contents: String, violatingRange: NSRange)
|
|
-> (correctedContents: String, adjustedRange: NSRange?) {
|
|
guard let indexRange = contents.nsrangeToIndexRange(violatingRange) else {
|
|
return (contents, nil)
|
|
}
|
|
let capturedString = contents[indexRange]
|
|
var adjustedRange = violatingRange
|
|
var correctString = " {"
|
|
|
|
// "struct Command{" has violating string = "d{", so ignore first "d"
|
|
if capturedString.characters.count == 2 &&
|
|
capturedString.rangeOfCharacterFromSet(whitespaceAndNewlineCharacterSet) == nil {
|
|
adjustedRange = NSRange(
|
|
location: violatingRange.location + 1,
|
|
length: violatingRange.length - 1
|
|
)
|
|
}
|
|
|
|
// "[].map( { } )" has violating string = "( {",
|
|
// so ignore first "(" and use "{" as correction string instead
|
|
if capturedString.hasPrefix("(") {
|
|
adjustedRange = NSRange(
|
|
location: violatingRange.location + 1,
|
|
length: violatingRange.length - 1
|
|
)
|
|
correctString = "{"
|
|
}
|
|
|
|
if let indexRange = contents.nsrangeToIndexRange(adjustedRange) {
|
|
let correctedContents = contents
|
|
.stringByReplacingCharactersInRange(indexRange, withString: correctString)
|
|
return (correctedContents, adjustedRange)
|
|
} else {
|
|
return (contents, nil)
|
|
}
|
|
}
|
|
}
|