mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
Initial cache implementation
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
Reference in New Issue
Block a user