diff --git a/Source/SwiftLintFramework/Command.swift b/Source/SwiftLintFramework/Command.swift new file mode 100644 index 000000000..41581c7ae --- /dev/null +++ b/Source/SwiftLintFramework/Command.swift @@ -0,0 +1,45 @@ +// +// Command.swift +// SwiftLint +// +// Created by JP Simard on 8/29/15. +// Copyright © 2015 Realm. All rights reserved. +// + +import Foundation + +public enum CommandAction: String { + case Enable = "enable" + case Disable = "disable" +} + +public struct Command { + let action: CommandAction + let ruleIdentifier: String + let line: Int + let character: Int + + public init(action: CommandAction, ruleIdentifier: String, line: Int, character: Int) { + self.action = action + self.ruleIdentifier = ruleIdentifier + self.line = line + self.character = character + } + + public init?(string: NSString, range: NSRange) { + let scanner = NSScanner(string: string.substringWithRange(range)) + scanner.scanString("swiftlint:", intoString: nil) + var actionNSString: NSString? = nil + scanner.scanUpToString(" ", intoString: &actionNSString) + guard let actionString = actionNSString as String?, + action = CommandAction(rawValue: actionString), + lineAndCharacter = string.lineAndCharacterForByteOffset(NSMaxRange(range)) else { + return nil + } + self.action = action + let ruleStart = scanner.string.startIndex.advancedBy(scanner.scanLocation + 1) + ruleIdentifier = scanner.string.substringFromIndex(ruleStart) + line = lineAndCharacter.line + character = lineAndCharacter.character + } +} diff --git a/Source/SwiftLintFramework/File+SwiftLint.swift b/Source/SwiftLintFramework/File+SwiftLint.swift index da1552b54..d2bd01bc5 100644 --- a/Source/SwiftLintFramework/File+SwiftLint.swift +++ b/Source/SwiftLintFramework/File+SwiftLint.swift @@ -9,47 +9,40 @@ import SourceKittenFramework import SwiftXPC -public typealias Line = (index: Int, content: String) - -public typealias Region = (startLine: Int, endLine: Int, disabledRules: [String]) - -public enum CommandAction: String { - case Enable = "enable" - case Disable = "disable" -} - -public typealias Command = (CommandAction, String, Int) +internal typealias Line = (index: Int, content: String) extension File { public func regions() -> [Region] { let nsStringContents = contents as NSString - let commands = matchPattern("swiftlint:(enable|disable)\\ .+", - withSyntaxKinds: [.Comment]).flatMap { range -> Command? in - let scanner = NSScanner(string: nsStringContents.substringWithRange(range)) - scanner.scanString("swiftlint:", intoString: nil) - var actionString: NSString? = nil - scanner.scanUpToString(" ", intoString: &actionString) - let start = range.location - if let actionString = actionString as String?, - action = CommandAction(rawValue: actionString), - lineRange = nsStringContents.lineRangeWithByteRange(start: start, length: 0) { - let ruleLocation = scanner.scanLocation + 1 - let ruleStart = scanner.string.startIndex.advancedBy(ruleLocation) - let rule = scanner.string.substringFromIndex(ruleStart) - return (action, rule, lineRange.start) - } - return nil - } - let totalNumberOfLines = contents.lines().count - var regions: [Region] = [(1, commands.first?.2 ?? totalNumberOfLines, [])] + let commands = matchPattern("swiftlint:(enable|disable)\\ [^\\s]+", + withSyntaxKinds: [.Comment]).flatMap { Command(string: nsStringContents, range: $0) } + let lines = contents.lines() + let totalNumberOfLines = lines.count + let numberOfCharactersInLastLine = lines.last?.content.characters.count + let firstRegion = Region(start: + Location(file: path, line: 1, character: 0), + end: Location(file: path, + line: commands.first?.line ?? totalNumberOfLines, + character: commands.first?.character ?? numberOfCharactersInLastLine), + disabledRuleIdentifiers: []) + var regions = [firstRegion] var disabledRules = Set() let commandPairs = zip(commands, Array(commands.dropFirst().map({Optional($0)})) + [nil]) for (command, nextCommand) in commandPairs { - switch command.0 { - case .Disable: disabledRules.insert(command.1) - case .Enable: disabledRules.remove(command.1) + switch command.action { + case .Disable: disabledRules.insert(command.ruleIdentifier) + case .Enable: disabledRules.remove(command.ruleIdentifier) } - regions.append((command.2, nextCommand?.2 ?? totalNumberOfLines, Array(disabledRules))) + regions.append( + Region( + start: Location(file: path, + line: command.line, + character: command.character), + end: Location(file: path, + line: nextCommand?.line ?? totalNumberOfLines, + character: nextCommand?.character ?? numberOfCharactersInLastLine), + disabledRuleIdentifiers: disabledRules) + ) } return regions } diff --git a/Source/SwiftLintFramework/Linter.swift b/Source/SwiftLintFramework/Linter.swift index f09f88c42..138aa143f 100644 --- a/Source/SwiftLintFramework/Linter.swift +++ b/Source/SwiftLintFramework/Linter.swift @@ -19,15 +19,12 @@ public struct Linter { let regions = file.regions() return rules.flatMap { rule in return rule.validateFile(self.file).filter { styleViolation in - guard let line = styleViolation.location.line else { - return true - } guard let violationRegion = regions.filter({ - $0.startLine <= line && $0.endLine >= line + $0.start <= styleViolation.location && $0.end >= styleViolation.location }).first else { return true } - return !violationRegion.disabledRules.contains(rule.identifier) + return !violationRegion.disabledRuleIdentifiers.contains(rule.identifier) } } } diff --git a/Source/SwiftLintFramework/Location.swift b/Source/SwiftLintFramework/Location.swift index 5c773d1ab..a6f6d1587 100644 --- a/Source/SwiftLintFramework/Location.swift +++ b/Source/SwiftLintFramework/Location.swift @@ -8,7 +8,7 @@ import SourceKittenFramework -public struct Location: CustomStringConvertible, Equatable { +public struct Location: CustomStringConvertible, Comparable { public let file: String? public let line: Int? public let character: Int? @@ -38,18 +38,20 @@ public struct Location: CustomStringConvertible, Equatable { } } -// MARK: Equatable +// MARK: Comparable -/** -Returns true if `lhs` Location is equal to `rhs` Location. - -:param: lhs Location to compare to `rhs`. -:param: rhs Location to compare to `lhs`. - -:returns: True if `lhs` Location is equal to `rhs` Location. -*/ public func == (lhs: Location, rhs: Location) -> Bool { return lhs.file == rhs.file && lhs.line == rhs.line && lhs.character == rhs.character } + +public func < (lhs: Location, rhs: Location) -> Bool { + if lhs.file != rhs.file { + return lhs.file < rhs.file + } + if lhs.line != rhs.line { + return lhs.line < rhs.line + } + return lhs.character < rhs.character +} diff --git a/Source/SwiftLintFramework/Region.swift b/Source/SwiftLintFramework/Region.swift new file mode 100644 index 000000000..18fb264ee --- /dev/null +++ b/Source/SwiftLintFramework/Region.swift @@ -0,0 +1,22 @@ +// +// Region.swift +// SwiftLint +// +// Created by JP Simard on 8/29/15. +// Copyright © 2015 Realm. All rights reserved. +// + +import Foundation +import SourceKittenFramework + +public struct Region { + let start: Location + let end: Location + let disabledRuleIdentifiers: Set + + public init(start: Location, end: Location, disabledRuleIdentifiers: Set) { + self.start = start + self.end = end + self.disabledRuleIdentifiers = disabledRuleIdentifiers + } +} diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index 15dd91249..d75474a46 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ E5A167C91B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */; }; E809EDA11B8A71DF00399043 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E809EDA01B8A71DF00399043 /* Configuration.swift */; }; E809EDA31B8A73FB00399043 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E809EDA21B8A73FB00399043 /* ConfigurationTests.swift */; }; + E80E018D1B92C0F60078EB70 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80E018C1B92C0F60078EB70 /* Command.swift */; settings = {ASSET_TAGS = (); }; }; + E80E018F1B92C1350078EB70 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80E018E1B92C1350078EB70 /* Region.swift */; settings = {ASSET_TAGS = (); }; }; E812249A1B04F85B001783D2 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81224991B04F85B001783D2 /* TestHelpers.swift */; }; E812249C1B04FADC001783D2 /* Linter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E812249B1B04FADC001783D2 /* Linter.swift */; }; E832F10B1B17E2F5003F265F /* NSFileManager+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */; }; @@ -151,6 +153,8 @@ E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorFunctionWhitespaceRule.swift; sourceTree = ""; }; E809EDA01B8A71DF00399043 /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; E809EDA21B8A73FB00399043 /* ConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = ""; }; + E80E018C1B92C0F60078EB70 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; + E80E018E1B92C1350078EB70 /* Region.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Region.swift; sourceTree = ""; }; E81224991B04F85B001783D2 /* TestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = ""; }; E812249B1B04FADC001783D2 /* Linter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Linter.swift; sourceTree = ""; }; E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFileManager+SwiftLint.swift"; sourceTree = ""; }; @@ -350,12 +354,14 @@ isa = PBXGroup; children = ( E88DEA8B1B0999A000A66CB0 /* ASTRule.swift */, + E80E018C1B92C0F60078EB70 /* Command.swift */, E809EDA01B8A71DF00399043 /* Configuration.swift */, 24E17F701B1481FF008195BE /* File+Cache.swift */, E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */, E812249B1B04FADC001783D2 /* Linter.swift */, E88DEA6E1B09843F00A66CB0 /* Location.swift */, E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */, + E80E018E1B92C1350078EB70 /* Region.swift */, E88DEA761B098D0C00A66CB0 /* Rule.swift */, 83D71E261B131EB5000395DE /* RuleExample.swift */, E88DEA781B098D4400A66CB0 /* RuleParameter.swift */, @@ -594,6 +600,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E80E018D1B92C0F60078EB70 /* Command.swift in Sources */, 83D71E281B131ECE000395DE /* RuleExample.swift in Sources */, E812249C1B04FADC001783D2 /* Linter.swift in Sources */, E88DEA861B0991BF00A66CB0 /* TrailingWhitespaceRule.swift in Sources */, @@ -604,6 +611,7 @@ E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */, 24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */, E88DEA6D1B09842200A66CB0 /* StyleViolationType.swift in Sources */, + E80E018F1B92C1350078EB70 /* Region.swift in Sources */, E88DEA941B099C0900A66CB0 /* VariableNameRule.swift in Sources */, E88DEA8A1B0992B300A66CB0 /* FileLengthRule.swift in Sources */, E88DEA751B09852000A66CB0 /* File+SwiftLint.swift in Sources */, @@ -704,7 +712,7 @@ "$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib", ); FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Source/SwiftLintFramework/Info.plist"; + INFOPLIST_FILE = Source/SwiftLintFramework/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -736,7 +744,7 @@ "$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib", ); FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Source/SwiftLintFramework/Info.plist"; + INFOPLIST_FILE = Source/SwiftLintFramework/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -806,7 +814,7 @@ "$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib", ); FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Source/SwiftLintFramework/Info.plist"; + INFOPLIST_FILE = Source/SwiftLintFramework/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -862,7 +870,7 @@ "$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib", ); FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Source/SwiftLintFramework/Info.plist"; + INFOPLIST_FILE = Source/SwiftLintFramework/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -899,7 +907,7 @@ baseConfigurationReference = D0D1213419E878CC005E4BAA /* Mac-Application.xcconfig */; buildSettings = { FRAMEWORK_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = "Source/swiftlint/Info.plist"; + INFOPLIST_FILE = Source/swiftlint/Info.plist; LD_RUNPATH_SEARCH_PATHS = "@executable_path/. @executable_path/../Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks /Library/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -911,7 +919,7 @@ baseConfigurationReference = D0D1213419E878CC005E4BAA /* Mac-Application.xcconfig */; buildSettings = { FRAMEWORK_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = "Source/swiftlint/Info.plist"; + INFOPLIST_FILE = Source/swiftlint/Info.plist; LD_RUNPATH_SEARCH_PATHS = "@executable_path/. @executable_path/../Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks /Library/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -923,7 +931,7 @@ baseConfigurationReference = D0D1213419E878CC005E4BAA /* Mac-Application.xcconfig */; buildSettings = { FRAMEWORK_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = "Source/swiftlint/Info.plist"; + INFOPLIST_FILE = Source/swiftlint/Info.plist; LD_RUNPATH_SEARCH_PATHS = "@executable_path/. @executable_path/../Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks /Library/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -935,7 +943,7 @@ baseConfigurationReference = D0D1213419E878CC005E4BAA /* Mac-Application.xcconfig */; buildSettings = { FRAMEWORK_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = "Source/swiftlint/Info.plist"; + INFOPLIST_FILE = Source/swiftlint/Info.plist; LD_RUNPATH_SEARCH_PATHS = "@executable_path/. @executable_path/../Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks @executable_path/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks /Library/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks/SourceKittenFramework.framework/Versions/Current/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)";