Skip to content

Commit

Permalink
uncuddled else folded into statement position
Browse files Browse the repository at this point in the history
still needs autocorrect
  • Loading branch information
Michael Skiba committed Jun 9, 2016
1 parent 314f7db commit 7a56338
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 112 deletions.
1 change: 0 additions & 1 deletion Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public let masterRuleList = RuleList(rules:
ConditionalBindingCascadeRule.self,
ControlStatementRule.self,
CustomRules.self,
CuddledElseRule.self,
CyclomaticComplexityRule.self,
EmptyCountRule.self,
FileLengthRule.self,
Expand Down
94 changes: 0 additions & 94 deletions Source/SwiftLintFramework/Rules/CuddledElseRule.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// StatementPositionConfiguration.swift
// SwiftLint
//
// Created by Michael Skiba on 6/8/16.
// Copyright © 2016 Realm. All rights reserved.
//

import Foundation

public enum StatmentModeConfiguration: String {
case Standard, UncuddledElse

init(value: AnyObject) throws {
guard let string = (value as? String)?.lowercaseString else {
throw ConfigurationError.UnknownConfiguration
}
if string == StatmentModeConfiguration.Standard.rawValue.lowercaseString {
self = .Standard
} else if string == StatmentModeConfiguration.UncuddledElse.rawValue.lowercaseString {
self = .UncuddledElse
} else {
throw ConfigurationError.UnknownConfiguration
}
}

}

public struct StatmentConfiguration: RuleConfiguration, Equatable {
public var consoleDescription: String {
return "(statement mode) \(statementMode.rawValue), " +
"(severity) \(severity.consoleDescription)"
}

var statementMode: StatmentModeConfiguration
var severity: SeverityConfiguration

public init(statementMode: StatmentModeConfiguration,
severity: SeverityConfiguration) {
self.statementMode = statementMode
self.severity = severity
}

public mutating func applyConfiguration(configuration: AnyObject) throws {
guard let configurationDict = configuration as? [String: AnyObject] else {
throw ConfigurationError.UnknownConfiguration
}
if let statementModeConfiguration = configurationDict["statementMode"] {
try statementMode = StatmentModeConfiguration(value: statementModeConfiguration)
}
if let severityConfiguration = configurationDict["severity"] {
try severity.applyConfiguration(severityConfiguration)
}
}
}

public func == (lhs: StatmentConfiguration, rhs: StatmentConfiguration) -> Bool {
return lhs.statementMode == rhs.statementMode && lhs.severity == rhs.severity
}
134 changes: 122 additions & 12 deletions Source/SwiftLintFramework/Rules/StatementPositionRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import SourceKittenFramework

public struct StatementPositionRule: CorrectableRule, ConfigurationProviderRule {

public var configuration = SeverityConfiguration(.Warning)
public var configuration = StatmentConfiguration(statementMode: .Standard,
severity: SeverityConfiguration(.Warning))

public init() {}

Expand Down Expand Up @@ -41,19 +42,80 @@ public struct StatementPositionRule: CorrectableRule, ConfigurationProviderRule
]
)

public static let uncuddledDescription = RuleDescription(
identifier: "statement_position",
name: "Statment Position",
description: "Else and catch should on the next line, with equal indentation to the " +
"previous declaration.",
nonTriggeringExamples: [
" }\n else if {",
" }\n else {",
" }\n catch {",
" }\n\n catch {",
"\n\n }\n catch {",
"\"}\nelse{\"",
"struct A { let catchphrase: Int }\nlet a = A(\n catchphrase: 0\n)",
"struct A { let `catch`: Int }\nlet a = A(\n `catch`: 0\n)"
],
triggeringExamples: [
"↓ }else if {",
"↓}\n else {",
"↓ }\ncatch {",
"↓}\n\t catch {"
],
corrections: [:]
)

public func validateFile(file: File) -> [StyleViolation] {
return violationRangesInFile(file, withPattern: pattern).flatMap { range in
switch configuration.statementMode {
case .Standard:
return standardValidateFile(file)
case .UncuddledElse:
return uncuddledValidateFile(file)
}
}

public func correctFile(file: File) -> [Correction] {
switch configuration.statementMode {
case .Standard:
return standardCorrectFile(file)
case .UncuddledElse:
return uncuddledCorrectFile(file)
}

}

}

// Standard Behaviors
private extension StatementPositionRule {

// match literal '}'
// followed by 1) nothing, 2) two+ whitespace/newlines or 3) newlines or tabs
// followed by 'else' or 'catch' literals
static let standardPattern = "\\}(?:[\\s\\n\\r]{2,}|[\\n\\t\\r]+)?\\b(else|catch)\\b"

func standardValidateFile(file: File) -> [StyleViolation] {
return standardViolationRangesInFile(file,
withPattern: self.dynamicType.standardPattern).flatMap { range in
return StyleViolation(ruleDescription: self.dynamicType.description,
severity: configuration.severity,
severity: configuration.severity.severity,
location: Location(file: file, characterOffset: range.location))
}
}

public func correctFile(file: File) -> [Correction] {
let matches = violationRangesInFile(file, withPattern: pattern)
func standardViolationRangesInFile(file: File, withPattern pattern: String) -> [NSRange] {
return file.matchPattern(pattern).filter { range, syntaxKinds in
return syntaxKinds.startsWith([.Keyword])
}.flatMap { $0.0 }
}

func standardCorrectFile(file: File) -> [Correction] {
let matches = standardViolationRangesInFile(file,
withPattern: self.dynamicType.standardPattern)
guard !matches.isEmpty else { return [] }

let regularExpression = regex(pattern)
let regularExpression = regex(self.dynamicType.standardPattern)
let description = self.dynamicType.description
var corrections = [Correction]()
var contents = file.contents
Expand All @@ -68,17 +130,65 @@ public struct StatementPositionRule: CorrectableRule, ConfigurationProviderRule
file.write(contents)
return corrections
}
}

//Uncuddled Behaviors
extension StatementPositionRule {
func uncuddledValidateFile(file: File) -> [StyleViolation] {
return uncuddledViolationRangesInFile(file).flatMap { range in
return StyleViolation(ruleDescription: self.dynamicType.uncuddledDescription,
severity: configuration.severity.severity,
location: Location(file: file, characterOffset: range.location))
}
}

// MARK: - Private

// match literal '}'
// preceded by whitespace (or nothing)
// followed by 1) nothing, 2) two+ whitespace/newlines or 3) newlines or tabs
// followed by 'else' or 'catch' literals
private let pattern = "\\}(?:[\\s\\n\\r]{2,}|[\\n\\t\\r]+)?\\b(else|catch)\\b"
// followed by newline and the same amount of whitespace then 'else' or 'catch' literals
static let uncuddledPattern = "([ \t]*)\\}(\\n+)?([ \t]*)\\b(else|catch)\\b"

private func violationRangesInFile(file: File, withPattern pattern: String) -> [NSRange] {
return file.matchPattern(pattern).filter { range, syntaxKinds in
return syntaxKinds.startsWith([.Keyword])
}.flatMap { $0.0 }
static let uncuddledRegularExpression = (try? NSRegularExpression(pattern: uncuddledPattern,
options: [])) ?? NSRegularExpression()

func uncuddledViolationRangesInFile(file: File) -> [NSRange] {
let contents = file.contents
let range = NSRange(location: 0, length: contents.utf16.count)
let syntaxMap = file.syntaxMap
let matches = StatementPositionRule.uncuddledRegularExpression.matchesInString(contents,
options: [],
range: range)
let validMatches = matches.flatMap { match -> NSRange? in
if match.numberOfRanges != 5 {
return match.range
}
if match.rangeAtIndex(2).length == 0 {
return match.range
}
let range1 = match.rangeAtIndex(1)
let range2 = match.rangeAtIndex(3)
let whitespace1 = contents.substring(range1.location, length: range1.length)
let whitespace2 = contents.substring(range2.location, length: range2.length)
if whitespace1 == whitespace2 {
return nil
}
return match.range
}
let rangesWithValidTokens = validMatches.filter { range in
guard let matchRange = contents.NSRangeToByteRange(start: range.location,
length: range.length) else {
return false
}
let tokens = syntaxMap.tokensIn(matchRange).flatMap { SyntaxKind(rawValue: $0.type) }
return tokens == [.Keyword]
}

return rangesWithValidTokens
}

func uncuddledCorrectFile(file: File) -> [Correction] {
return []
}
}
7 changes: 4 additions & 3 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
6CCFCF2E1CFEF73A003239EB /* SWXMLHash.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E8C0DFCC1AD349DB007EE3D4 /* SWXMLHash.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6CCFCF2F1CFEF73E003239EB /* SwiftyTextTable.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = 3BBF2F9C1C640A0F006CD775 /* SwiftyTextTable.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6CCFCF301CFEF742003239EB /* Yaml.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E89376AC1B8A701E0025708E /* Yaml.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7250948A1D0859260039B353 /* StatementPositionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 725094881D0855760039B353 /* StatementPositionConfiguration.swift */; };
83894F221B0C928A006214E1 /* RulesCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894F211B0C928A006214E1 /* RulesCommand.swift */; };
83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E261B131EB5000395DE /* RuleDescription.swift */; };
B58AEED61C492C7B00E901FD /* ForceUnwrappingRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */; };
Expand Down Expand Up @@ -208,7 +209,7 @@
6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceKitCrashTests.swift; sourceTree = "<group>"; };
6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Structure+SwiftLint.swift"; sourceTree = "<group>"; };
6CC4259A1C77046200AEA885 /* SyntaxMap+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyntaxMap+SwiftLint.swift"; sourceTree = "<group>"; };
72B761311CD917DB00B846D5 /* CuddledElseRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuddledElseRule.swift; sourceTree = "<group>"; };
725094881D0855760039B353 /* StatementPositionConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementPositionConfiguration.swift; sourceTree = "<group>"; };
83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = "<group>"; };
83D71E261B131EB5000395DE /* RuleDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleDescription.swift; sourceTree = "<group>"; };
B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForceUnwrappingRule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -351,6 +352,7 @@
3B0B14531C505D6300BE82F7 /* SeverityConfiguration.swift */,
3BCC04CF1C4F56D3006073C3 /* SeverityLevelsConfiguration.swift */,
BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */,
725094881D0855760039B353 /* StatementPositionConfiguration.swift */,
);
path = RuleConfigurations;
sourceTree = "<group>";
Expand Down Expand Up @@ -574,7 +576,6 @@
695BE9CE1BDFD92B0071E985 /* CommaRule.swift */,
00970C741C3FC3A0007C4A83 /* ConditionalBindingCascadeRule.swift */,
65454F451B14D73800319A6C /* ControlStatementRule.swift */,
72B761311CD917DB00B846D5 /* CuddledElseRule.swift */,
3B1DF0111C5148140011BCED /* CustomRules.swift */,
2E02005E1C54BF680024D09D /* CyclomaticComplexityRule.swift */,
E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */,
Expand Down Expand Up @@ -909,6 +910,7 @@
3BCC04D21C4F56D3006073C3 /* NameConfiguration.swift in Sources */,
E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */,
E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */,
7250948A1D0859260039B353 /* StatementPositionConfiguration.swift in Sources */,
E81619531BFC162C00946723 /* QueuedPrint.swift in Sources */,
E87E4A051BFB927C00FCFE46 /* TrailingSemicolonRule.swift in Sources */,
E88198421BEA929F00333A11 /* NestingRule.swift in Sources */,
Expand All @@ -918,7 +920,6 @@
E88DEA711B09847500A66CB0 /* ViolationSeverity.swift in Sources */,
3BD9CD3D1C37175B009A5D25 /* YamlParser.swift in Sources */,
E88DEA8C1B0999A000A66CB0 /* ASTRule.swift in Sources */,
72B761331CD9193500B846D5 /* CuddledElseRule.swift in Sources */,
E88DEA6B1B0983FE00A66CB0 /* StyleViolation.swift in Sources */,
3BB47D831C514E8100AE6A10 /* RegexConfiguration.swift in Sources */,
3B1150CA1C31FC3F00D83B1E /* Yaml+SwiftLint.swift in Sources */,
Expand Down
5 changes: 3 additions & 2 deletions Tests/SwiftLintFramework/RulesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ class RulesTests: XCTestCase {
verifyRule(ControlStatementRule.description)
}

func testCuddledElse() {
verifyRule(CuddledElseRule.description)
func testUncuddledElse() {
let configuration = ["statementMode": "uncuddledelse"]
verifyRule(StatementPositionRule.uncuddledDescription, ruleConfiguration: configuration)
}

func testCyclomaticComplexity() {
Expand Down

0 comments on commit 7a56338

Please sign in to comment.