diff --git a/CHANGELOG.md b/CHANGELOG.md index 334eb1fb93..4acba9fc89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,9 @@ * Improve performance of `FunctionParameterCountRule`. [Norio Nomura](https://github.com/norio-nomura) +* Improve performance of `ColonRule`. + [Norio Nomura](https://github.com/norio-nomura) + ##### Bug Fixes * Fix case sensitivity of keywords for `valid_docs`. diff --git a/Source/SwiftLintFramework/Extensions/File+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/File+SwiftLint.swift index 47a04e85fe..e543d32f59 100644 --- a/Source/SwiftLintFramework/Extensions/File+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/File+SwiftLint.swift @@ -73,23 +73,33 @@ extension File { }.map { $0.0 } } - public func matchPattern(pattern: String) -> [(NSRange, [SyntaxKind])] { - return matchPattern(regex(pattern)) + internal func rangesAndTokensMatching(pattern: String) -> [(NSRange, [SyntaxToken])] { + return rangesAndTokensMatching(regex(pattern)) } - public func matchPattern(regex: NSRegularExpression) -> [(NSRange, [SyntaxKind])] { + internal func rangesAndTokensMatching(regex: NSRegularExpression) -> + [(NSRange, [SyntaxToken])] { let contents = self.contents as NSString let range = NSRange(location: 0, length: contents.length) let syntax = syntaxMap - let matches = regex.matchesInString(self.contents, options: [], range: range) - return matches.map { match in + return regex.matchesInString(self.contents, options: [], range: range).map { match in let matchByteRange = contents.NSRangeToByteRange(start: match.range.location, length: match.range.length) ?? match.range - let kindsInRange = syntax.tokens.filter { token in + let tokensInRange = syntax.tokens.filter { token in let tokenByteRange = NSRange(location: token.offset, length: token.length) return NSIntersectionRange(matchByteRange, tokenByteRange).length > 0 - }.map({ $0.type }).flatMap(SyntaxKind.init) - return (match.range, kindsInRange) + }.map({ $0 }) + return (match.range, tokensInRange) + } + } + + public func matchPattern(pattern: String) -> [(NSRange, [SyntaxKind])] { + return matchPattern(regex(pattern)) + } + + public func matchPattern(regex: NSRegularExpression) -> [(NSRange, [SyntaxKind])] { + return rangesAndTokensMatching(regex).map { range, tokens in + (range, tokens.map({ $0.type }).flatMap(SyntaxKind.init)) } } diff --git a/Source/SwiftLintFramework/Rules/ColonRule.swift b/Source/SwiftLintFramework/Rules/ColonRule.swift index 1357f93bc6..3a8092fc4f 100644 --- a/Source/SwiftLintFramework/Rules/ColonRule.swift +++ b/Source/SwiftLintFramework/Rules/ColonRule.swift @@ -111,25 +111,32 @@ public struct ColonRule: CorrectableRule, ConfigProviderRule { // MARK: - Private private let pattern = - "(\\w+)" + // Capture an identifier - "(?:" + // start group - "\\s+" + // followed by whitespace - ":" + // to the left of a colon - "\\s*" + // followed by any amount of whitespace. - "|" + // or - ":" + // immediately followed by a colon + "(\\w)" + // Capture an identifier + "(?:" + // start group + "\\s+" + // followed by whitespace + ":" + // to the left of a colon + "\\s*" + // followed by any amount of whitespace. + "|" + // or + ":" + // immediately followed by a colon "(?:\\s{0}|\\s{2,})" + // followed by 0 or 2+ whitespace characters. - ")" + // end group - "(" + // Capture a type identifier - "(?:\\[|\\()*" + // which may begin with a series of nested parenthesis or brackets - "\\S+?)" // lazily to the first non-whitespace character. + ")" + // end group + "(" + // Capture a type identifier + "[\\[|\\(]*" + // which may begin with a series of nested parenthesis or brackets + "\\S)" // lazily to the first non-whitespace character. private func violationRangesInFile(file: File, withPattern pattern: String) -> [NSRange] { - return file.matchPattern(pattern).filter { range, syntaxKinds in + let nsstring = file.contents as NSString + let commentAndStringKindsSet = Set(SyntaxKind.commentAndStringKinds()) + return file.rangesAndTokensMatching(pattern).filter { range, syntaxTokens in + let syntaxKinds = syntaxTokens.map({ $0.type }).flatMap(SyntaxKind.init) if !syntaxKinds.startsWith([.Identifier, .Typeidentifier]) { return false } - return Set(syntaxKinds).intersect(Set(SyntaxKind.commentAndStringKinds())).isEmpty - }.flatMap { $0.0 } + return Set(syntaxKinds).intersect(commentAndStringKindsSet).isEmpty + }.flatMap { range, syntaxTokens in + let identifierRange = nsstring // swiftlint:disable:next force_unwrapping + .byteRangeToNSRange(start: syntaxTokens.first!.offset, length: 0) + return identifierRange.map { NSUnionRange($0, range) } + } } }