Merge pull request #8 from JohnSundell/static-generic-calls

Correctly handle static methods called on a generic type
This commit is contained in:
John Sundell 2018-08-27 00:12:38 +02:00 committed by GitHub
commit b892feae14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 13 deletions

View File

@ -11,7 +11,7 @@ extension Equatable {
return candidates.contains(self)
}
func isAny(of candidates: [Self]) -> Bool {
func isAny<S: Sequence>(of candidates: S) -> Bool where S.Element == Self {
return candidates.contains(self)
}
}

View File

@ -214,12 +214,14 @@ private extension SwiftGrammar {
struct TypeRule: SyntaxRule {
var tokenType: TokenType { return .type }
private let declarationKeywords: Set<String> = [
"class", "struct", "enum", "func",
"protocol", "typealias", "import"
]
func matches(_ segment: Segment) -> Bool {
// Types should not be highlighted when declared
if let previousToken = segment.tokens.previous {
let declarationKeywords = ["class", "struct", "enum",
"protocol", "typealias", "import"]
guard !previousToken.isAny(of: declarationKeywords) else {
return false
}
@ -234,9 +236,21 @@ private extension SwiftGrammar {
}
// In a generic declaration, only highlight constraints
if !segment.tokens.onSameLine.contains(anyOf: "var", "let") {
if !segment.tokens.containsBalancedOccurrences(of: "<", and: ">") {
return !segment.tokens.previous.isAny(of: "<", ",")
if segment.tokens.previous.isAny(of: "<", ",") {
// Since the declaration might be on another line, we have to walk
// backwards through all tokens until we've found enough information.
for token in segment.tokens.all.reversed() {
guard !declarationKeywords.contains(token) else {
return false
}
guard !keywords.contains(token) else {
return true
}
if token.isAny(of: ">", "=", "==", "(") {
return true
}
}
}

View File

@ -24,16 +24,18 @@ public struct Segment {
public extension Segment {
/// A collection of tokens included in a code segment
struct Tokens {
/// All tokens that have been found so far (excluding the current one)
public var all: [String]
/// The number of times a given token has been found up until this point
var counts: [String : Int]
public var counts: [String : Int]
/// The tokens that were previously found on the same line as the current one
var onSameLine: [String]
public var onSameLine: [String]
/// The token that was previously found (may be on a different line)
var previous: String?
public var previous: String?
/// The current token which is currently being evaluated
var current: String
public var current: String
/// Any upcoming token that will follow the current one
var next: String?
public var next: String?
}
}

View File

@ -43,6 +43,7 @@ private extension Tokenizer {
private let delimiters: CharacterSet
private var index: String.Index?
private var tokenCounts = [String : Int]()
private var allTokens = [String]()
private var lineTokens = [String]()
private var segments: (current: Segment?, previous: Segment?)
@ -132,6 +133,7 @@ private extension Tokenizer {
private func makeSegment(with component: Component, at index: String.Index) -> Segment {
let tokens = Segment.Tokens(
all: allTokens,
counts: tokenCounts,
onSameLine: lineTokens,
previous: segments.current?.tokens.current,
@ -155,6 +157,8 @@ private extension Tokenizer {
count += 1
tokenCounts[segment.tokens.current] = count
allTokens.append(segment.tokens.current)
if segment.isLastOnLine {
lineTokens = []
} else {

View File

@ -75,6 +75,19 @@ final class FunctionCallTests: SyntaxHighlighterTestCase {
])
}
func testCallingStaticMethodOnGenericType() {
let components = highlighter.highlight("Array<String>.call()")
XCTAssertEqual(components, [
.token("Array", .type),
.plainText("<"),
.token("String", .type),
.plainText(">."),
.token("call", .call),
.plainText("()")
])
}
func testAllTestsRunOnLinux() {
XCTAssertTrue(TestCaseVerifier.verifyLinuxTests((type(of: self)).allTests))
}
@ -87,7 +100,8 @@ extension FunctionCallTests {
("testImplicitInitializerCall", testImplicitInitializerCall),
("testExplicitInitializerCall", testExplicitInitializerCall),
("testAccessingPropertyAfterFunctionCallWithoutArguments", testAccessingPropertyAfterFunctionCallWithoutArguments),
("testAccessingPropertyAfterFunctionCallWithArguments", testAccessingPropertyAfterFunctionCallWithArguments)
("testAccessingPropertyAfterFunctionCallWithArguments", testAccessingPropertyAfterFunctionCallWithArguments),
("testCallingStaticMethodOnGenericType", testCallingStaticMethodOnGenericType)
]
}
}