diff --git a/CHANGELOG.md b/CHANGELOG.md index 414faf0fab..0ee3d3fb69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ with examples. [Chris Eidhof](https://github.com/chriseidhof) +* Cache parsing to reduce execution time by more than 50%. + [Nikolaj Schumacher](https://github.com/nschum) + ##### Bug Fixes None. diff --git a/Source/SwiftLintFramework/File+Cache.swift b/Source/SwiftLintFramework/File+Cache.swift new file mode 100644 index 0000000000..b82e7f980b --- /dev/null +++ b/Source/SwiftLintFramework/File+Cache.swift @@ -0,0 +1,48 @@ +import SourceKittenFramework + +private var structureCache = Cache({file in Structure(file: file)}) +private var syntaxMapCache = Cache({file in SyntaxMap(file: file)}) + +private struct Cache { + + private var values = [String: T]() + private var factory: File -> T + + private init(_ factory: File -> T) { + self.factory = factory + } + + private mutating func get(file: File) -> T { + if let path = file.path { + if let value = values[path] { + return value + } else { + let value = factory(file) + values[path] = value + return value + } + } else { + return factory(file) + } + } + + private mutating func clear() { + values.removeAll(keepCapacity: false) + } +} + +public extension File { + + public var structure: Structure { + return structureCache.get(self) + } + + public var syntaxMap: SyntaxMap { + return syntaxMapCache.get(self) + } + + public static func clearCaches() { + structureCache.clear() + syntaxMapCache.clear() + } +} diff --git a/Source/SwiftLintFramework/File+SwiftLint.swift b/Source/SwiftLintFramework/File+SwiftLint.swift index b5b32b728e..bc86e606d9 100644 --- a/Source/SwiftLintFramework/File+SwiftLint.swift +++ b/Source/SwiftLintFramework/File+SwiftLint.swift @@ -16,7 +16,7 @@ extension File { [NSRange] { return flatMap(NSRegularExpression(pattern: pattern, options: nil, error: nil)) { regex in let range = NSRange(location: 0, length: count(self.contents.utf16)) - let syntax = SyntaxMap(file: self) + let syntax = self.syntaxMap let matches = regex.matchesInString(self.contents, options: nil, range: range) return map(matches as? [NSTextCheckingResult]) { matches in return compact(matches.map { match in diff --git a/Source/SwiftLintFramework/Rules/FunctionBodyLengthRule.swift b/Source/SwiftLintFramework/Rules/FunctionBodyLengthRule.swift index 971710b384..46e0991c32 100644 --- a/Source/SwiftLintFramework/Rules/FunctionBodyLengthRule.swift +++ b/Source/SwiftLintFramework/Rules/FunctionBodyLengthRule.swift @@ -23,7 +23,7 @@ public struct FunctionBodyLengthRule: ASTRule, ParameterizedRule { ] public func validateFile(file: File) -> [StyleViolation] { - return validateFile(file, dictionary: Structure(file: file).dictionary) + return validateFile(file, dictionary: file.structure.dictionary) } public func validateFile(file: File, dictionary: XPCDictionary) -> [StyleViolation] { diff --git a/Source/SwiftLintFramework/Rules/NestingRule.swift b/Source/SwiftLintFramework/Rules/NestingRule.swift index 69f8be65eb..ea792e1355 100644 --- a/Source/SwiftLintFramework/Rules/NestingRule.swift +++ b/Source/SwiftLintFramework/Rules/NestingRule.swift @@ -15,7 +15,7 @@ public struct NestingRule: ASTRule { public let identifier = "nesting" public func validateFile(file: File) -> [StyleViolation] { - return validateFile(file, dictionary: Structure(file: file).dictionary) + return validateFile(file, dictionary: file.structure.dictionary) } public func validateFile(file: File, dictionary: XPCDictionary) -> [StyleViolation] { diff --git a/Source/SwiftLintFramework/Rules/TypeBodyLengthRule.swift b/Source/SwiftLintFramework/Rules/TypeBodyLengthRule.swift index 35babe46e3..bf2f0fef2e 100644 --- a/Source/SwiftLintFramework/Rules/TypeBodyLengthRule.swift +++ b/Source/SwiftLintFramework/Rules/TypeBodyLengthRule.swift @@ -23,7 +23,7 @@ public struct TypeBodyLengthRule: ASTRule, ParameterizedRule { ] public func validateFile(file: File) -> [StyleViolation] { - return validateFile(file, dictionary: Structure(file: file).dictionary) + return validateFile(file, dictionary: file.structure.dictionary) } public func validateFile(file: File, dictionary: XPCDictionary) -> [StyleViolation] { diff --git a/Source/SwiftLintFramework/Rules/TypeNameRule.swift b/Source/SwiftLintFramework/Rules/TypeNameRule.swift index 90edf56ac2..5104521019 100644 --- a/Source/SwiftLintFramework/Rules/TypeNameRule.swift +++ b/Source/SwiftLintFramework/Rules/TypeNameRule.swift @@ -15,7 +15,7 @@ public struct TypeNameRule: ASTRule { public let identifier = "type_name" public func validateFile(file: File) -> [StyleViolation] { - return validateFile(file, dictionary: Structure(file: file).dictionary) + return validateFile(file, dictionary: file.structure.dictionary) } public func validateFile(file: File, dictionary: XPCDictionary) -> [StyleViolation] { diff --git a/Source/SwiftLintFramework/Rules/VariableNameRule.swift b/Source/SwiftLintFramework/Rules/VariableNameRule.swift index d69d37a078..9e5a9a4d11 100644 --- a/Source/SwiftLintFramework/Rules/VariableNameRule.swift +++ b/Source/SwiftLintFramework/Rules/VariableNameRule.swift @@ -15,7 +15,7 @@ public struct VariableNameRule: ASTRule { public let identifier = "variable_name" public func validateFile(file: File) -> [StyleViolation] { - return validateFile(file, dictionary: Structure(file: file).dictionary) + return validateFile(file, dictionary: file.structure.dictionary) } public func validateFile(file: File, dictionary: XPCDictionary) -> [StyleViolation] { diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index bc05925039..3471adecec 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E17F701B1481FF008195BE /* File+Cache.swift */; }; 83894F221B0C928A006214E1 /* RulesCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894F211B0C928A006214E1 /* RulesCommand.swift */; }; 83D71E281B131ECE000395DE /* RuleExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E261B131EB5000395DE /* RuleExample.swift */; }; 83D71E2B1B131EED000395DE /* AnsiCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E231B131C11000395DE /* AnsiCode.swift */; }; @@ -102,6 +103,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 24E17F701B1481FF008195BE /* File+Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "File+Cache.swift"; sourceTree = ""; }; 5499CA961A2394B700783309 /* Components.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = ""; }; 5499CA971A2394B700783309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = ""; }; @@ -328,6 +330,7 @@ children = ( E88DEA8B1B0999A000A66CB0 /* ASTRule.swift */, E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */, + 24E17F701B1481FF008195BE /* File+Cache.swift */, E812249B1B04FADC001783D2 /* Linter.swift */, E88DEA6E1B09843F00A66CB0 /* Location.swift */, E88DEA761B098D0C00A66CB0 /* Rule.swift */, @@ -565,6 +568,7 @@ E88DEA841B0990F500A66CB0 /* ColonRule.swift in Sources */, E88DEA791B098D4400A66CB0 /* RuleParameter.swift in Sources */, E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */, + 24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */, E88DEA6D1B09842200A66CB0 /* StyleViolationType.swift in Sources */, E88DEA941B099C0900A66CB0 /* VariableNameRule.swift in Sources */, E88DEA8A1B0992B300A66CB0 /* FileLengthRule.swift in Sources */,