Skip to content

Commit

Permalink
Merge pull request #747 from masters3d/aug6th2016
Browse files Browse the repository at this point in the history
[new rule] VerticalWhitespace rule
  • Loading branch information
jpsim committed Aug 22, 2016
2 parents ff553a2 + 06d2a39 commit 98d0375
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,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)
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,6 @@ public let masterRuleList = RuleList(rules:
TypeBodyLengthRule.self,
TypeNameRule.self,
ValidDocsRule.self,
VariableNameRule.self
VariableNameRule.self,
VerticalWhitespaceRule.self
)
1 change: 0 additions & 1 deletion Source/SwiftLintFramework/Rules/ClosingBraceRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public struct ClosingBraceRule: CorrectableRule, ConfigurationProviderRule {
]
)


public func validateFile(file: File) -> [StyleViolation] {
return file.violatingClosingBraceRanges().map {
StyleViolation(ruleDescription: self.dynamicType.description,
Expand Down
168 changes: 168 additions & 0 deletions Source/SwiftLintFramework/Rules/VerticalWhitespaceRule.swift
Original file line number Diff line number Diff line change
@@ -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..<filteredLines.count {
if filteredLines[previousIndex].index + 1 == filteredLines[index].index {
lineSection.append(filteredLines[index])
} else if !lineSection.isEmpty {
blankLinesSections.append(lineSection)
lineSection.removeAll()
}
previousIndex = index
}
if !lineSection.isEmpty {
blankLinesSections.append(lineSection)
}

// matching all accurrences of /* */
let matchMultilineComments = "/\\*(.|[\\r\\n])*?\\*/"
let comments = file.matchPattern(matchMultilineComments)

var result = [(lastLine: Line, linesToRemove: Int)]()
for eachSection in blankLinesSections {
guard let lastLine = eachSection.last else { continue }

// filtering out violations within a multiple comment block
let isSectionInComment = !comments.filter {
(eachRange, _ ) in eachRange.intersectsRange(lastLine.range)
}.isEmpty

if isSectionInComment {
continue // skipping the lines found in multiline comment
} else {
result.append((lastLine, eachSection.count))
}
}

return result

}

public func correctFile(file: File) -> [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..<eachLine.lastLine.index)
}

var correctedLines = [String]()
var corrections = [Correction]()
let fileRegions = file.regions()

forLoopCounter: for currentLine in file.lines {

// Doesnt correct lines where rule is disabled
let region = fileRegions.filter {
$0.contains(Location(file: file.path, line: currentLine.index, character: 0))
}.first
if region?.isRuleDisabled(self) == true {
correctedLines.append(currentLine.content)
continue forLoopCounter
}

// by not incling lines in correctedLines, it removes them
if Set(indexOfLinesToDelete).contains(currentLine.index) {
let description = self.dynamicType.description
let location = Location(file: file.path, line: currentLine.index)

//reports every line that is being deleted
corrections.append(Correction(ruleDescription: description, location: location))
continue forLoopCounter
}

// all lines that pass get added to final output file
correctedLines.append(currentLine.content)
}
// converts lines back to file, add trailing line
if !corrections.isEmpty {
file.write(correctedLines.joinWithSeparator("\n") + "\n")
return corrections
}
return []

}

}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
006ECFC41C44E99E00EF6364 /* LegacyConstantRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */; };
02FD8AEF1BFC18D60014BFFB /* ExtendedNSStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FD8AEE1BFC18D60014BFFB /* ExtendedNSStringTests.swift */; };
1EC163521D5992D900DD2928 /* VerticalWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EC163511D5992D900DD2928 /* VerticalWhitespaceRule.swift */; };
094385041D5D4F7C009168CF /* PrivateOutletRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094385021D5D4F78009168CF /* PrivateOutletRule.swift */; };
1F11B3CF1C252F23002E8FA8 /* ClosingBraceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F11B3CE1C252F23002E8FA8 /* ClosingBraceRule.swift */; };
24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E17F701B1481FF008195BE /* File+Cache.swift */; };
Expand Down Expand Up @@ -175,6 +176,7 @@
/* Begin PBXFileReference section */
006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyConstantRule.swift; sourceTree = "<group>"; };
02FD8AEE1BFC18D60014BFFB /* ExtendedNSStringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtendedNSStringTests.swift; sourceTree = "<group>"; };
1EC163511D5992D900DD2928 /* VerticalWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticalWhitespaceRule.swift; sourceTree = "<group>"; };
094385021D5D4F78009168CF /* PrivateOutletRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateOutletRule.swift; sourceTree = "<group>"; };
1F11B3CE1C252F23002E8FA8 /* ClosingBraceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosingBraceRule.swift; sourceTree = "<group>"; };
24E17F701B1481FF008195BE /* File+Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "File+Cache.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -618,6 +620,7 @@
E88DEA911B099B1F00A66CB0 /* TypeNameRule.swift */,
E81CDE701C00FEAA00B430F6 /* ValidDocsRule.swift */,
E88DEA931B099C0900A66CB0 /* VariableNameRule.swift */,
1EC163511D5992D900DD2928 /* VerticalWhitespaceRule.swift */,
);
path = Rules;
sourceTree = "<group>";
Expand Down Expand Up @@ -909,6 +912,7 @@
006ECFC41C44E99E00EF6364 /* LegacyConstantRule.swift in Sources */,
E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */,
E88198591BEA95F100333A11 /* LeadingWhitespaceRule.swift in Sources */,
1EC163521D5992D900DD2928 /* VerticalWhitespaceRule.swift in Sources */,
57ED827B1CF656E3002B3513 /* JUnitReporter.swift in Sources */,
24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */,
E80E018F1B92C1350078EB70 /* Region.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Tests/SwiftLintFramework/FunctionBodyLengthRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class FunctionBodyLengthRuleTests: XCTestCase {
"whitespace: currently spans 41 lines")])

let longerFunctionBodyWithEmptyLines = funcWithBody(
"// swiftlint:disable vertical_whitespace\n" +
Repeat(count: 100, repeatedValue: "\n").joinWithSeparator("")
)
XCTAssertEqual(violations(longerFunctionBodyWithEmptyLines), [])
Expand Down
4 changes: 4 additions & 0 deletions Tests/SwiftLintFramework/RulesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ class RulesTests: XCTestCase {
verifyRule(NestingRule.description)
}

func testVerticalWhitespace() {
verifyRule(VerticalWhitespaceRule.description)
}

func testOpeningBrace() {
verifyRule(OpeningBraceRule.description)
}
Expand Down

0 comments on commit 98d0375

Please sign in to comment.