Files
SwiftLint/Source/SwiftLintFramework/Extensions/File+Cache.swift
T
Norio Nomura 40828dff03 Merge branch 'master' into swift3.0
* master: (41 commits)
  Fix formatting in CHANGELOG.md
  release 0.13.0
  Update CHANGELOG.md
  Fix check for trailing whitespace to return early
  Fix checks for some inline comments
  Replace check for comments to use SyntaxKind
  Add configuration for trailing_whitespace to ignore comments
  Unwanted space removed
  - Lint issues fixed
  Updated HTML Reporter
  PR feedback
  Add check on autocorrect for disabled range
  Use `utf8.count` instead of `utf16.count` to byte range
  Re-write `ExplicitInitRule` to `ASTRule`
  added ExplicitInitRule
  Updated CHANGELOG
  HTML Reporter added
  HTML Reporter added
  Adds information about SwiftLint plugin for AppCode into README.md
  added reasons why a new rule should be opt in
  ...

# Conflicts:
#	Source/SwiftLintFramework/Extensions/File+SwiftLint.swift
#	Source/SwiftLintFramework/Extensions/Structure+SwiftLint.swift
#	Source/SwiftLintFramework/Rules/ColonRule.swift
#	Source/SwiftLintFramework/Rules/CommaRule.swift
#	Source/SwiftLintFramework/Rules/LegacyCGGeometryFunctionsRule.swift
#	Source/SwiftLintFramework/Rules/LegacyConstantRule.swift
#	Source/SwiftLintFramework/Rules/LegacyConstructorRule.swift
#	Source/SwiftLintFramework/Rules/LegacyNSGeometryFunctionsRule.swift
#	Source/SwiftLintFramework/Rules/LineLengthRule.swift
#	Source/SwiftLintFramework/Rules/OperatorFunctionWhitespaceRule.swift
#	Source/SwiftLintFramework/Rules/ReturnArrowWhitespaceRule.swift
#	Source/SwiftLintFramework/Rules/RuleConfigurations/StatementPositionConfiguration.swift
#	Source/SwiftLintFramework/Rules/StatementPositionRule.swift
#	Source/SwiftLintFramework/Rules/TrailingWhitespaceRule.swift
#	Tests/SwiftLintFramework/RuleConfigurationTests.swift
2016-11-04 21:40:56 +09:00

199 lines
5.9 KiB
Swift

//
// File+Cache.swift
// SwiftLint
//
// Created by Nikolaj Schumacher on 2015-05-26.
// Copyright (c) 2015 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
private var responseCache = Cache({file -> [String: SourceKitRepresentable]? in
do {
return try Request.editorOpen(file: file).failableSend()
} catch let error as Request.Error {
queuedPrintError(error.description)
return nil
} catch {
return nil
}
})
private var structureCache = Cache({file -> Structure? in
if let structure = responseCache.get(file).map(Structure.init) {
queueForRebuild.append(structure)
return structure
}
return nil
})
private var syntaxMapCache = Cache({ file in responseCache.get(file).map(SyntaxMap.init) })
private var syntaxKindsByLinesCache = Cache({ file in file.syntaxKindsByLine() })
private var syntaxTokensByLinesCache = Cache({ file in file.syntaxTokensByLine() })
private typealias AssertHandler = () -> ()
private var assertHandlers = [String: AssertHandler?]()
private var _allDeclarationsByType = [String: [String]]()
private var queueForRebuild = [Structure]()
private struct Cache<T> {
fileprivate var values = [String: T]()
fileprivate var factory: (File) -> T
fileprivate init(_ factory: @escaping (File) -> T) {
self.factory = factory
}
fileprivate mutating func get(_ file: File) -> T {
let key = file.cacheKey
if let value = values[key] {
return value
}
let value = factory(file)
values[key] = value
return value
}
fileprivate mutating func invalidate(_ file: File) {
if let key = file.path {
values.removeValue(forKey: key)
}
}
fileprivate mutating func clear() {
values.removeAll(keepingCapacity: false)
}
}
extension File {
fileprivate var cacheKey: String {
return path ?? contents
}
internal var sourcekitdFailed: Bool {
get {
return responseCache.get(self) == nil
}
set {
if newValue {
responseCache.values[cacheKey] = Optional<[String: SourceKitRepresentable]>.none
} else {
responseCache.values.removeValue(forKey: cacheKey)
}
}
}
internal var assertHandler: (() -> ())? {
get {
return assertHandlers[cacheKey] ?? nil
}
set {
assertHandlers[cacheKey] = newValue
}
}
internal var structure: Structure {
guard let structure = structureCache.get(self) else {
if let handler = assertHandler {
handler()
return Structure(sourceKitResponse: [:])
}
fatalError("Never call this for file that sourcekitd fails.")
}
return structure
}
internal var syntaxMap: SyntaxMap {
guard let syntaxMap = syntaxMapCache.get(self) else {
if let handler = assertHandler {
handler()
return SyntaxMap(data: [])
}
fatalError("Never call this for file that sourcekitd fails.")
}
return syntaxMap
}
internal var syntaxTokensByLines: [[SyntaxToken]] {
guard let syntaxTokensByLines = syntaxTokensByLinesCache.get(self) else {
if let handler = assertHandler {
handler()
return []
}
fatalError("Never call this for file that sourcekitd fails.")
}
return syntaxTokensByLines
}
internal var syntaxKindsByLines: [[SyntaxKind]] {
guard let syntaxKindsByLines = syntaxKindsByLinesCache.get(self) else {
if let handler = assertHandler {
handler()
return []
}
fatalError("Never call this for file that sourcekitd fails.")
}
return syntaxKindsByLines
}
public func invalidateCache() {
responseCache.invalidate(self)
assertHandlers.removeValue(forKey: cacheKey)
structureCache.invalidate(self)
syntaxMapCache.invalidate(self)
syntaxTokensByLinesCache.invalidate(self)
syntaxKindsByLinesCache.invalidate(self)
}
internal static func clearCaches() {
queueForRebuild = []
_allDeclarationsByType = [:]
responseCache.clear()
assertHandlers = [:]
structureCache.clear()
syntaxMapCache.clear()
syntaxTokensByLinesCache.clear()
syntaxKindsByLinesCache.clear()
}
internal static var allDeclarationsByType: [String: [String]] {
if !queueForRebuild.isEmpty {
rebuildAllDeclarationsByType()
}
return _allDeclarationsByType
}
}
private func dictFromKeyValuePairs<Key: Hashable, Value>(_ pairs: [(Key, Value)]) -> [Key: Value] {
var dict = [Key: Value]()
for pair in pairs {
dict[pair.0] = pair.1
}
return dict
}
private func substructureForDict(_ dict: [String: SourceKitRepresentable]) ->
[[String: SourceKitRepresentable]]? {
return (dict["key.substructure"] as? [SourceKitRepresentable])?.flatMap {
$0 as? [String: SourceKitRepresentable]
}
}
private func rebuildAllDeclarationsByType() {
let allDeclarationsByType = queueForRebuild.flatMap { structure -> (String, [String])? in
guard let firstSubstructureDict = substructureForDict(structure.dictionary)?.first,
let name = firstSubstructureDict["key.name"] as? String,
let kind = (firstSubstructureDict["key.kind"] as? String)
.flatMap(SwiftDeclarationKind.init),
kind == .protocol,
let substructure = substructureForDict(firstSubstructureDict) else {
return nil
}
return (name, substructure.flatMap({ $0["key.name"] as? String }))
}
allDeclarationsByType.forEach { _allDeclarationsByType[$0.0] = $0.1 }
queueForRebuild = []
}