diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd28bef5f..7cbe08a08f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ `NSRect` constants and constructors to existing rules. [David Rönnqvist](https://github.com/d-ronnqvist) +* Added Vertical Whitespace Rule. + [J. Cheyo Jimenez](https://github.com/masters3d) + [#548](https://github.com/realm/SwiftLint/issues/548) + * Removed ConditionalBindingCascadeRule. [J. Cheyo Jimenez](https://github.com/masters3d) [#701](https://github.com/realm/SwiftLint/issues/701) diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index ab5de8cefc..482192c1be 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -74,5 +74,6 @@ public let masterRuleList = RuleList(rules: TypeBodyLengthRule.self, TypeNameRule.self, ValidDocsRule.self, - VariableNameRule.self + VariableNameRule.self, + VerticalWhitespaceRule.self ) diff --git a/Source/SwiftLintFramework/Rules/ClosingBraceRule.swift b/Source/SwiftLintFramework/Rules/ClosingBraceRule.swift index ad9a860b4d..19de4cf9b3 100644 --- a/Source/SwiftLintFramework/Rules/ClosingBraceRule.swift +++ b/Source/SwiftLintFramework/Rules/ClosingBraceRule.swift @@ -44,7 +44,6 @@ public struct ClosingBraceRule: CorrectableRule, ConfigurationProviderRule { ] ) - public func validateFile(file: File) -> [StyleViolation] { return file.violatingClosingBraceRanges().map { StyleViolation(ruleDescription: self.dynamicType.description, diff --git a/Source/SwiftLintFramework/Rules/VerticalWhitespaceRule.swift b/Source/SwiftLintFramework/Rules/VerticalWhitespaceRule.swift new file mode 100644 index 0000000000..f967acfa66 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/VerticalWhitespaceRule.swift @@ -0,0 +1,168 @@ +// +// VerticalWhitespaceRule.swift +// SwiftLint +// +// Created by J. Cheyo Jimenez on 2015-05-16. +// Copyright (c) 2016 Realm. All rights reserved. +// + +import Foundation +import SourceKittenFramework + +private let descriptionReason = "Limit vertical whitespace to a single empty line." + +public struct VerticalWhitespaceRule: CorrectableRule, + ConfigurationProviderRule { + + public var configuration = SeverityConfiguration(.Warning) + + public init() {} + + public static let description = RuleDescription( + identifier: "vertical_whitespace", + name: "Vertical Whitespace", + description: descriptionReason, + nonTriggeringExamples: [ + "let abc = 0\n", + "let abc = 0\n\n", + "/* bcs \n\n\n\n*/", + "// bca \n\n", + ], + triggeringExamples: [ + "let aaaa = 0\n\n\n", + "struct AAAA {}\n\n\n\n", + "class BBBB {}\n\n\n", + ], + corrections: [ + "let b = 0\n\n\nclass AAA {}\n": "let b = 0\n\nclass AAA {}\n", + "let c = 0\n\n\nlet num = 1\n": "let c = 0\n\nlet num = 1\n", + "// bca \n\n\n": "// bca \n\n", + ] // End of line autocorrections are handeled by Trailing Newline Rule. + ) + + public func validateFile(file: File) -> [StyleViolation] { + + let linesSections = validate(file) + if linesSections.isEmpty { return [] } + + var violations = [StyleViolation]() + for (eachLastLine, eachSectionCount) in linesSections { + + // Skips violation for areas where the rule is disabled + let region = file.regions().filter { + $0.contains(Location(file: file.path, line: eachLastLine.index, character: 0)) + }.first + if region?.isRuleDisabled(self) == true { + continue + } + + let violation = StyleViolation(ruleDescription: self.dynamicType.description, + severity: configuration.severity, + location: Location(file: file.path, + line: eachLastLine.index ), + reason: descriptionReason + + " Currently \(eachSectionCount + 1)." ) + violations.append(violation) + } + + return violations + } + + func validate(file: File) -> [(lastLine: Line, linesToRemove: Int)] { + + let filteredLines = file.lines.filter { + $0.content.stringByTrimmingCharactersInSet(.whitespaceCharacterSet()).isEmpty + } + + if filteredLines.isEmpty { return [] } + + var blankLinesSections = [[Line]]() + var lineSection = [Line]() + + var previousIndex = 0 + for index in 0.. [Correction] { + let linesSections = validate(file) + if linesSections.isEmpty { return [] } + + var indexOfLinesToDelete = [Int]() + + for eachLine in linesSections { + let start = eachLine.lastLine.index - eachLine.linesToRemove + indexOfLinesToDelete.appendContentsOf(start..