Merge pull request #1007 from jaherhi/line_length_url

Add check to ignore urls for line_length rule
This commit is contained in:
JP Simard
2017-01-06 10:41:17 -08:00
committed by GitHub
8 changed files with 295 additions and 9 deletions
+5
View File
@@ -98,6 +98,11 @@
[Aaron McTavish](https://github.com/aamctustwo)
[#769](https://github.com/realm/SwiftLint/issues/769)
* Add check to ignore urls in `line_length` rule when `ignores_urls`
configuration is enabled.
[Javier Hernández](https://github.com/jaherhi)
[#384](https://github.com/realm/SwiftLint/issues/384)
##### Bug Fixes
* Ignore close parentheses on `vertical_parameter_alignment` rule.
@@ -6,10 +6,11 @@
// Copyright © 2015 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
public struct LineLengthRule: ConfigurationProviderRule, SourceKitFreeRule {
public var configuration = SeverityLevelsConfiguration(warning: 120, error: 200)
public var configuration = LineLengthConfiguration(warning: 120, error: 200, ignoresURLs: false)
public init() {}
@@ -40,6 +41,9 @@ public struct LineLengthRule: ConfigurationProviderRule, SourceKitFreeRule {
}
var strippedString = line.content
if configuration.ignoresURLs {
strippedString = strippedString.strippingURLs
}
strippedString = stripLiterals(fromSourceString: strippedString,
withDelimiter: "#colorLiteral")
strippedString = stripLiterals(fromSourceString: strippedString,
@@ -51,7 +55,7 @@ public struct LineLengthRule: ConfigurationProviderRule, SourceKitFreeRule {
return StyleViolation(ruleDescription: type(of: self).description,
severity: param.severity,
location: Location(file: file.path, line: line.index),
reason: "Line should be \(configuration.warning) characters or less: " +
reason: "Line should be \(configuration.length.warning) characters or less: " +
"currently \(length) characters")
}
return nil
@@ -88,3 +92,24 @@ public struct LineLengthRule: ConfigurationProviderRule, SourceKitFreeRule {
}
}
fileprivate extension String {
var strippingURLs: String {
let range = NSRange(location: 0, length: bridge().length)
// Workaround for Linux until NSDataDetector is available
#if os(Linux)
// Regex pattern from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
let pattern = "(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)" +
"(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*" +
"\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))"
let urlRegex = regex(pattern)
return urlRegex.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "")
#else
let types = NSTextCheckingResult.CheckingType.link.rawValue
guard let urlDetector = try? NSDataDetector(types: types) else {
return self
}
return urlDetector.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "")
#endif
}
}
@@ -0,0 +1,48 @@
//
// LineLengthConfiguration.swift
// SwiftLint
//
// Created by Javier Hernández on 21/12/16.
// Copyright © 2016 Realm. All rights reserved.
//
import Foundation
public struct LineLengthConfiguration: RuleConfiguration, Equatable {
public var consoleDescription: String {
return length.consoleDescription + ", ignores urls: \(ignoresURLs)"
}
var length: SeverityLevelsConfiguration
var ignoresURLs: Bool
var params: [RuleParameter<Int>] {
return length.params
}
public init(warning: Int, error: Int?, ignoresURLs: Bool) {
length = SeverityLevelsConfiguration(warning: warning, error: error)
self.ignoresURLs = ignoresURLs
}
public mutating func applyConfiguration(_ configuration: Any) throws {
if let configurationArray = [Int].array(of: configuration), !configurationArray.isEmpty {
let warning = configurationArray[0]
let error = (configurationArray.count > 1) ? configurationArray[1] : nil
length = SeverityLevelsConfiguration(warning: warning, error: error)
} else if let configDict = configuration as? [String: Any], !configDict.isEmpty
&& Set(configDict.keys).isSubset(of: ["warning", "error", "ignores_urls"]) {
let warning = configDict["warning"] as? Int ?? length.warning
let error = configDict["error"] as? Int
length = SeverityLevelsConfiguration(warning: warning, error: error)
ignoresURLs = configDict["ignores_urls"] as? Bool ?? ignoresURLs
} else {
throw ConfigurationError.unknownConfiguration
}
}
}
public func == (lhs: LineLengthConfiguration, rhs: LineLengthConfiguration) -> Bool {
return lhs.length == rhs.length && lhs.ignoresURLs == rhs.ignoresURLs
}
+12
View File
@@ -24,6 +24,7 @@
2E336D1B1DF08BFB00CCFE77 /* EmojiReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E336D191DF08AF200CCFE77 /* EmojiReporter.swift */; };
2E5761AA1C573B83003271AF /* FunctionParameterCountRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */; };
37B3FA8B1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B3FA8A1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift */; };
3B034B6E1E0BE549005D49A9 /* LineLengthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */; };
3B0B14541C505D6300BE82F7 /* SeverityConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B0B14531C505D6300BE82F7 /* SeverityConfiguration.swift */; };
3B1150CA1C31FC3F00D83B1E /* Yaml+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B1150C91C31FC3F00D83B1E /* Yaml+SwiftLint.swift */; };
3B12C9C11C3209CB000B423F /* test.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3B12C9BF1C3209AC000B423F /* test.yml */; };
@@ -33,6 +34,8 @@
3B1DF0121C5148140011BCED /* CustomRules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B1DF0111C5148140011BCED /* CustomRules.swift */; };
3B30C4A11C3785B300E04027 /* YamlParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B30C4A01C3785B300E04027 /* YamlParserTests.swift */; };
3B5B9FE11C444DA20009AD27 /* Array+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5B9FE01C444DA20009AD27 /* Array+SwiftLint.swift */; };
3B63D46D1E1F05160057BE35 /* LineLengthConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B63D46C1E1F05160057BE35 /* LineLengthConfigurationTests.swift */; };
3B63D46F1E1F09DF0057BE35 /* LineLengthRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B63D46E1E1F09DF0057BE35 /* LineLengthRuleTests.swift */; };
3B828E531C546468000D180E /* RuleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B828E521C546468000D180E /* RuleConfiguration.swift */; };
3BA79C9B1C4767910057E705 /* NSRange+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA79C9A1C4767910057E705 /* NSRange+SwiftLint.swift */; };
3BB47D831C514E8100AE6A10 /* RegexConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB47D821C514E8100AE6A10 /* RegexConfiguration.swift */; };
@@ -264,6 +267,7 @@
2E336D191DF08AF200CCFE77 /* EmojiReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiReporter.swift; sourceTree = "<group>"; };
2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FunctionParameterCountRule.swift; sourceTree = "<group>"; };
37B3FA8A1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+SwiftLint.swift"; sourceTree = "<group>"; };
3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineLengthConfiguration.swift; sourceTree = "<group>"; };
3B0B14531C505D6300BE82F7 /* SeverityConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeverityConfiguration.swift; sourceTree = "<group>"; };
3B1150C91C31FC3F00D83B1E /* Yaml+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Yaml+SwiftLint.swift"; sourceTree = "<group>"; };
3B12C9BF1C3209AC000B423F /* test.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = test.yml; sourceTree = "<group>"; };
@@ -273,6 +277,8 @@
3B1DF0111C5148140011BCED /* CustomRules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomRules.swift; sourceTree = "<group>"; };
3B30C4A01C3785B300E04027 /* YamlParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YamlParserTests.swift; sourceTree = "<group>"; };
3B5B9FE01C444DA20009AD27 /* Array+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+SwiftLint.swift"; sourceTree = "<group>"; };
3B63D46C1E1F05160057BE35 /* LineLengthConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineLengthConfigurationTests.swift; sourceTree = "<group>"; };
3B63D46E1E1F09DF0057BE35 /* LineLengthRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineLengthRuleTests.swift; sourceTree = "<group>"; };
3B828E521C546468000D180E /* RuleConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleConfiguration.swift; sourceTree = "<group>"; };
3BA79C9A1C4767910057E705 /* NSRange+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRange+SwiftLint.swift"; sourceTree = "<group>"; };
3BB47D821C514E8100AE6A10 /* RegexConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexConfiguration.swift; sourceTree = "<group>"; };
@@ -512,6 +518,7 @@
D4C4A34A1DEA4FD700E0E04C /* AttributesConfiguration.swift */,
D43B04671E07228D004016AF /* ColonConfiguration.swift */,
D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */,
3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */,
3BCC04D01C4F56D3006073C3 /* NameConfiguration.swift */,
D4DA1DFD1E1A10DB0037413D /* NumberSeparatorConfiguration.swift */,
78F032471D7D614300BE709A /* OverridenSuperCallConfiguration.swift */,
@@ -691,6 +698,8 @@
E809EDA21B8A73FB00399043 /* ConfigurationTests.swift */,
D4348EE91C46122C007707FB /* FunctionBodyLengthRuleTests.swift */,
D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */,
3B63D46C1E1F05160057BE35 /* LineLengthConfigurationTests.swift */,
3B63D46E1E1F09DF0057BE35 /* LineLengthRuleTests.swift */,
C9802F2E1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift */,
E832F10C1B17E725003F265F /* IntegrationTests.swift */,
E86396C61BADAFE6002C9E88 /* ReporterTests.swift */,
@@ -1120,6 +1129,7 @@
D4C4A34E1DEA877200E0E04C /* FileHeaderRule.swift in Sources */,
009E092A1DFEE4DD00B588A7 /* ProhibitedSuperConfiguration.swift in Sources */,
BFF028AE1CBCF8A500B38A9D /* TrailingWhitespaceConfiguration.swift in Sources */,
3B034B6E1E0BE549005D49A9 /* LineLengthConfiguration.swift in Sources */,
D4C4A34C1DEA4FF000E0E04C /* AttributesConfiguration.swift in Sources */,
83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */,
3B12C9C51C322032000B423F /* MasterRuleList.swift in Sources */,
@@ -1251,6 +1261,7 @@
006204DE1E1E4E0A00FFFBE1 /* VerticalWhitespaceRuleTests.swift in Sources */,
02FD8AEF1BFC18D60014BFFB /* ExtendedNSStringTests.swift in Sources */,
D4348EEA1C46122C007707FB /* FunctionBodyLengthRuleTests.swift in Sources */,
3B63D46D1E1F05160057BE35 /* LineLengthConfigurationTests.swift in Sources */,
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */,
3BB47D871C51DE6E00AE6A10 /* CustomRulesTests.swift in Sources */,
E812249A1B04F85B001783D2 /* TestHelpers.swift in Sources */,
@@ -1262,6 +1273,7 @@
E88198631BEA9A5400333A11 /* RulesTests.swift in Sources */,
D46202211E16002A0027AAD1 /* Swift2RulesTests.swift in Sources */,
C9802F2F1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift in Sources */,
3B63D46F1E1F09DF0057BE35 /* LineLengthRuleTests.swift in Sources */,
3BCC04D41C502BAB006073C3 /* RuleConfigurationTests.swift in Sources */,
E809EDA31B8A73FB00399043 /* ConfigurationTests.swift in Sources */,
);
+2
View File
@@ -18,6 +18,8 @@ XCTMain([
testCase(FileHeaderRuleTests.allTests),
testCase(FunctionBodyLengthRuleTests.allTests),
testCase(IntegrationTests.allTests),
testCase(LineLengthConfigurationTests.allTests),
testCase(LineLengthRuleTests.allTests),
testCase(ReporterTests.allTests),
testCase(RuleConfigurationsTests.allTests),
testCase(RuleTests.allTests),
@@ -0,0 +1,154 @@
//
// LineLengthConfigurationTests.swift
// SwiftLint
//
// Created by Javier Hernández on 05/01/17.
// Copyright © 2017 Realm. All rights reserved.
//
import SourceKittenFramework
@testable import SwiftLintFramework
import XCTest
class LineLengthConfigurationTests: XCTestCase {
func testLineLengthConfigurationInitializerSetsLength() {
let warning = 100
let error = 150
let length1 = SeverityLevelsConfiguration(warning: warning, error: error)
let configuration1 = LineLengthConfiguration(warning: warning, error: error, ignoresURLs: true)
XCTAssertEqual(configuration1.length, length1)
let length2 = SeverityLevelsConfiguration(warning: warning, error: nil)
let configuration2 = LineLengthConfiguration(warning: warning, error: nil, ignoresURLs: true)
XCTAssertEqual(configuration2.length, length2)
}
func testLineLengthConfigurationInitialiserSetsIgnoresUrls() {
let configuration1 = LineLengthConfiguration(warning: 100, error: 150, ignoresURLs: true)
XCTAssertTrue(configuration1.ignoresURLs)
let configuration2 = LineLengthConfiguration(warning: 100, error: 150, ignoresURLs: false)
XCTAssertFalse(configuration2.ignoresURLs)
}
func testLineLengthConfigurationParams() {
let warning = 13
let error = 10
let configuration = LineLengthConfiguration(warning: warning, error: error, ignoresURLs: true)
let params = [RuleParameter(severity: .error, value: error), RuleParameter(severity: .warning, value: warning)]
XCTAssertEqual(configuration.params, params)
}
func testLineLengthConfigurationPartialParams() {
let warning = 13
let configuration = LineLengthConfiguration(warning: warning, error: nil, ignoresURLs: true)
XCTAssertEqual(configuration.params, [RuleParameter(severity: .warning, value: 13)])
}
func testLineLengthConfigurationThrowsOnBadConfig() {
let config = "unknown"
var configuration = LineLengthConfiguration(warning: 100, error: 150, ignoresURLs: true)
checkError(ConfigurationError.unknownConfiguration) {
try configuration.applyConfiguration(config)
}
}
func testLineLengthConfigurationApplyConfigurationWithArray() {
var configuration = LineLengthConfiguration(warning: 0, error: 0, ignoresURLs: false)
let warning1 = 100
let error1 = 100
let length1 = SeverityLevelsConfiguration(warning: warning1, error: error1)
let config1 = [warning1, error1]
let warning2 = 150
let length2 = SeverityLevelsConfiguration(warning: warning2, error: nil)
let config2 = [warning2]
do {
try configuration.applyConfiguration(config1)
XCTAssertEqual(configuration.length, length1)
try configuration.applyConfiguration(config2)
XCTAssertEqual(configuration.length, length2)
} catch {
XCTFail()
}
}
func testLineLengthConfigurationApplyConfigurationWithDictionary() {
var configuration = LineLengthConfiguration(warning: 0, error: 0, ignoresURLs: false)
let warning1 = 100
let error1 = 100
let length1 = SeverityLevelsConfiguration(warning: warning1, error: error1)
let config1: [String: Any] = ["warning": warning1, "error": error1, "ignores_urls": true]
let warning2 = 200
let error2 = 200
let length2 = SeverityLevelsConfiguration(warning: warning2, error: error2)
let config2: [String: Int] = ["warning": warning2, "error": error2]
let length3 = SeverityLevelsConfiguration(warning: warning2, error: nil)
let config3: [String: Bool] = ["ignores_urls": false]
do {
try configuration.applyConfiguration(config1)
XCTAssertEqual(configuration.length, length1)
XCTAssertTrue(configuration.ignoresURLs)
try configuration.applyConfiguration(config2)
XCTAssertEqual(configuration.length, length2)
XCTAssertTrue(configuration.ignoresURLs)
try configuration.applyConfiguration(config3)
XCTAssertEqual(configuration.length, length3)
XCTAssertFalse(configuration.ignoresURLs)
} catch {
XCTFail()
}
}
func testLineLengthConfigurationCompares() {
let configuration1 = LineLengthConfiguration(warning: 100, error: 100, ignoresURLs: true)
let configuration2 = LineLengthConfiguration(warning: 100, error: 100, ignoresURLs: false)
XCTAssertFalse(configuration1 == configuration2)
let configuration3 = LineLengthConfiguration(warning: 100, error: 200, ignoresURLs: true)
XCTAssertFalse(configuration1 == configuration3)
let configuration4 = LineLengthConfiguration(warning: 200, error: 100, ignoresURLs: true)
XCTAssertFalse(configuration1 == configuration4)
let configuration5 = LineLengthConfiguration(warning: 100, error: 100, ignoresURLs: true)
XCTAssertTrue(configuration1 == configuration5)
let configuration6 = LineLengthConfiguration(warning: 100, error: 100, ignoresURLs: false)
XCTAssertTrue(configuration2 == configuration6)
}
}
extension LineLengthConfigurationTests {
static var allTests: [(String, (LineLengthConfigurationTests) -> () throws -> Void)] {
return [
("testLineLengthConfigurationInitializerSetsLength",
testLineLengthConfigurationInitializerSetsLength),
("testLineLengthConfigurationInitialiserSetsIgnoresUrls",
testLineLengthConfigurationInitialiserSetsIgnoresUrls),
("testLineLengthConfigurationPartialParams",
testLineLengthConfigurationPartialParams),
("testLineLengthConfigurationParams",
testLineLengthConfigurationParams),
("testLineLengthConfigurationThrowsOnBadConfig",
testLineLengthConfigurationThrowsOnBadConfig),
("testLineLengthConfigurationApplyConfigurationWithArray",
testLineLengthConfigurationApplyConfigurationWithArray),
("testLineLengthConfigurationApplyConfigurationWithDictionary",
testLineLengthConfigurationApplyConfigurationWithDictionary),
("testLineLengthConfigurationCompares",
testLineLengthConfigurationCompares)
]
}
}
@@ -0,0 +1,47 @@
//
// LineLengthRuleTests.swift
// SwiftLint
//
// Created by Javier Hernández on 06/01/17.
// Copyright © 2017 Realm. All rights reserved.
//
import SourceKittenFramework
@testable import SwiftLintFramework
import XCTest
class LineLengthRuleTests: XCTestCase {
func testLineLength() {
verifyRule(LineLengthRule.description, commentDoesntViolate: false, stringDoesntViolate: false)
}
func testLineLengthWithIgnoreURLsEnabled() {
let url = "https://github.com/realm/SwiftLint"
let triggeringLines = [String(repeating: "/", count: 121) + "\(url)\n"]
let nonTriggeringLines = ["\(url) " + String(repeating: "/", count: 118) + " \(url)\n",
"\(url)/" + String(repeating: "a", count: 120)]
let baseDescription = LineLengthRule.description
let nonTriggeringExamples = baseDescription.nonTriggeringExamples + nonTriggeringLines
let triggeringExamples = baseDescription.triggeringExamples + triggeringLines
let description = RuleDescription(identifier: baseDescription.identifier,
name: baseDescription.name,
description: baseDescription.description,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: triggeringExamples,
corrections: baseDescription.corrections)
verifyRule(description, ruleConfiguration: ["ignores_urls": true],
commentDoesntViolate: false, stringDoesntViolate: false)
}
}
extension LineLengthRuleTests {
static var allTests: [(String, (LineLengthRuleTests) -> () throws -> Void)] {
return [
("testLineLength", testLineLength),
("testLineLengthWithIgnoreURLsEnabled", testLineLengthWithIgnoreURLsEnabled)
]
}
}
@@ -10,7 +10,6 @@ import SwiftLintFramework
import XCTest
// swiftlint:disable file_length
// swiftlint:disable:next type_body_length
class RulesTests: XCTestCase {
func testClassDelegateProtocol() {
@@ -134,11 +133,6 @@ class RulesTests: XCTestCase {
verifyRule(LegacyConstructorRule.description)
}
func testLineLength() {
verifyRule(LineLengthRule.description, commentDoesntViolate: false,
stringDoesntViolate: false)
}
func testMark() {
verifyRule(MarkRule.description, commentDoesntViolate: false)
}
@@ -371,7 +365,6 @@ extension RulesTests {
("testLegacyNSGeometryFunctions", testLegacyNSGeometryFunctions),
("testLegacyConstant", testLegacyConstant),
("testLegacyConstructor", testLegacyConstructor),
("testLineLength", testLineLength),
("testMark", testMark),
("testNesting", testNesting),
("testNimbleOperator", testNimbleOperator),