Initial cache implementation

This commit is contained in:
Marcelo Fabri
2016-12-27 02:36:33 -02:00
parent cbc360a612
commit c698d021dc
4 changed files with 113 additions and 1 deletions
@@ -0,0 +1,86 @@
//
// LinterCache.swift
// SwiftLint
//
// Created by Marcelo Fabri on 12/27/16.
// Copyright © 2016 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
public struct LinterCache {
private var cache = [String: [String: Any]]()
public init() { }
public init(contentsOf url: URL) throws {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let json = json as? [String: [String: Any]] {
cache = json
}
}
public mutating func cacheFile(_ file: String, violations: [StyleViolation], hash: Int) {
var entry = [String: Any]()
var fileViolations = entry["violations"] as? [[String: Any]] ?? []
for violation in violations {
fileViolations.append(dictionaryForViolation(violation))
}
entry["violations"] = fileViolations
entry["hash"] = hash
cache[file] = entry
}
public func violations(for file: String, hash: Int) -> [StyleViolation]? {
guard let entry = cache[file],
let cacheHash = entry["hash"] as? Int,
cacheHash == hash,
let violations = entry["violations"] as? [[String: Any]] else {
return nil
}
return violations.flatMap { StyleViolation.fromCache($0, file: file) }
}
public func save(to url: URL) throws {
let json = toJSON(cache)
try json.write(to: url, atomically: true, encoding: .utf8)
}
private func dictionaryForViolation(_ violation: StyleViolation) -> [String: Any] {
return [
"line": violation.location.line ?? NSNull() as Any,
"character": violation.location.character ?? NSNull() as Any,
"severity": violation.severity.rawValue,
"type": violation.ruleDescription.name,
"rule_id": violation.ruleDescription.identifier,
"reason": violation.reason
]
}
}
extension StyleViolation {
fileprivate static func fromCache(_ cache: [String: Any], file: String) -> StyleViolation? {
guard let severity = (cache["severity"] as? String).flatMap(ViolationSeverity.init),
let name = cache["type"] as? String,
let ruleId = cache["rule_id"] as? String,
let reason = cache["reason"] as? String else {
return nil
}
let line = cache["line"] as? Int
let character = cache["character"] as? Int
let ruleDescription = RuleDescription(identifier: ruleId, name: name, description: reason)
let location = Location(file: file, line: line, character: character)
let violation = StyleViolation(ruleDescription: ruleDescription, severity: severity,
location: location, reason: reason)
return violation
}
}
@@ -9,6 +9,10 @@
public enum ViolationSeverity: String, Comparable {
case warning
case error
public init?(identifier: String) {
self.init(rawValue: identifier)
}
}
// MARK: Comparable
+19 -1
View File
@@ -40,12 +40,23 @@ struct LintCommand: CommandProtocol {
let reporter = reporterFromString(
options.reporter.isEmpty ? configuration.reporter : options.reporter
)
let cacheUrl = URL(fileURLWithPath: "swiftlint.json")
var cache = (try? LinterCache(contentsOf: cacheUrl)) ?? LinterCache()
return configuration.visitLintableFiles(options.path, action: "Linting",
useSTDIN: options.useSTDIN, quiet: options.quiet,
useScriptInputFiles: options.useScriptInputFiles) { linter in
var currentViolations: [StyleViolation] = []
var readFromCache = false
var fileHash: Int?
autoreleasepool {
if options.benchmark {
if let file = linter.file.path,
case let hash = linter.file.contents.hash,
let cachedViolations = cache.violations(for: file, hash: hash) {
currentViolations = cachedViolations
readFromCache = true
fileHash = hash
} else if options.benchmark {
let start = Date()
let (_currentViolations, currentRuleTimes) = linter.styleViolationsAndRuleTimes
currentViolations = _currentViolations
@@ -58,6 +69,10 @@ struct LintCommand: CommandProtocol {
}
violations += currentViolations
reporter.reportViolations(currentViolations, realtimeCondition: true)
if !readFromCache, let file = linter.file.path {
let hash = fileHash ?? linter.file.contents.hash
cache.cacheFile(file, violations: currentViolations, hash: hash)
}
}.flatMap { files in
if isWarningThresholdBroken(configuration, violations: violations) {
violations.append(createThresholdViolation(configuration.warningThreshold!))
@@ -73,6 +88,9 @@ struct LintCommand: CommandProtocol {
fileBenchmark.save()
ruleBenchmark.save()
}
try? cache.save(to: cacheUrl)
if numberOfSeriousViolations > 0 {
exit(2)
} else if options.strict && !violations.isEmpty {
+4
View File
@@ -125,6 +125,7 @@
D4C4A3521DEFBBB700E0E04C /* FileHeaderConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */; };
D4DAE8BC1DE14E8F00B0AE7A /* NimbleOperatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */; };
D4FBADD01E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FBADCF1E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift */; };
D4FD58B21E12A0200019503C /* LinterCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FD58B11E12A0200019503C /* LinterCache.swift */; };
DAD3BE4A1D6ECD9500660239 /* PrivateOutletRuleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD3BE491D6ECD9500660239 /* PrivateOutletRuleConfiguration.swift */; };
E315B83C1DFA4BC500621B44 /* DynamicInlineRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E315B83B1DFA4BC500621B44 /* DynamicInlineRule.swift */; };
E57B23C11B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */; };
@@ -375,6 +376,7 @@
D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHeaderConfiguration.swift; sourceTree = "<group>"; };
D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NimbleOperatorRule.swift; sourceTree = "<group>"; };
D4FBADCF1E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorUsageWhitespaceRule.swift; sourceTree = "<group>"; };
D4FD58B11E12A0200019503C /* LinterCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinterCache.swift; sourceTree = "<group>"; };
DAD3BE491D6ECD9500660239 /* PrivateOutletRuleConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateOutletRuleConfiguration.swift; sourceTree = "<group>"; };
E315B83B1DFA4BC500621B44 /* DynamicInlineRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicInlineRule.swift; sourceTree = "<group>"; };
E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReturnArrowWhitespaceRule.swift; sourceTree = "<group>"; };
@@ -834,6 +836,7 @@
E8B67C3D1C095E6300FDED8E /* Correction.swift */,
E809EDA01B8A71DF00399043 /* Configuration.swift */,
E812249B1B04FADC001783D2 /* Linter.swift */,
D4FD58B11E12A0200019503C /* LinterCache.swift */,
E88DEA6E1B09843F00A66CB0 /* Location.swift */,
E80E018E1B92C1350078EB70 /* Region.swift */,
83D71E261B131EB5000395DE /* RuleDescription.swift */,
@@ -1186,6 +1189,7 @@
E88DEA711B09847500A66CB0 /* ViolationSeverity.swift in Sources */,
B2902A0C1D66815600BFCCF7 /* PrivateUnitTestRule.swift in Sources */,
D47A51101DB2DD4800A4CC21 /* AttributesRule.swift in Sources */,
D4FD58B21E12A0200019503C /* LinterCache.swift in Sources */,
3BD9CD3D1C37175B009A5D25 /* YamlParser.swift in Sources */,
F22314B01D4FA4D7009AD165 /* LegacyNSGeometryFunctionsRule.swift in Sources */,
E88DEA8C1B0999A000A66CB0 /* ASTRule.swift in Sources */,