Merge pull request #8 from JohnSundell/static-generic-calls
Correctly handle static methods called on a generic type
This commit is contained in:
commit
b892feae14
@ -11,7 +11,7 @@ extension Equatable {
|
|||||||
return candidates.contains(self)
|
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)
|
return candidates.contains(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,12 +214,14 @@ private extension SwiftGrammar {
|
|||||||
struct TypeRule: SyntaxRule {
|
struct TypeRule: SyntaxRule {
|
||||||
var tokenType: TokenType { return .type }
|
var tokenType: TokenType { return .type }
|
||||||
|
|
||||||
|
private let declarationKeywords: Set<String> = [
|
||||||
|
"class", "struct", "enum", "func",
|
||||||
|
"protocol", "typealias", "import"
|
||||||
|
]
|
||||||
|
|
||||||
func matches(_ segment: Segment) -> Bool {
|
func matches(_ segment: Segment) -> Bool {
|
||||||
// Types should not be highlighted when declared
|
// Types should not be highlighted when declared
|
||||||
if let previousToken = segment.tokens.previous {
|
if let previousToken = segment.tokens.previous {
|
||||||
let declarationKeywords = ["class", "struct", "enum",
|
|
||||||
"protocol", "typealias", "import"]
|
|
||||||
|
|
||||||
guard !previousToken.isAny(of: declarationKeywords) else {
|
guard !previousToken.isAny(of: declarationKeywords) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -234,9 +236,21 @@ private extension SwiftGrammar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In a generic declaration, only highlight constraints
|
// In a generic declaration, only highlight constraints
|
||||||
if !segment.tokens.onSameLine.contains(anyOf: "var", "let") {
|
if segment.tokens.previous.isAny(of: "<", ",") {
|
||||||
if !segment.tokens.containsBalancedOccurrences(of: "<", and: ">") {
|
// Since the declaration might be on another line, we have to walk
|
||||||
return !segment.tokens.previous.isAny(of: "<", ",")
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,16 +24,18 @@ public struct Segment {
|
|||||||
public extension Segment {
|
public extension Segment {
|
||||||
/// A collection of tokens included in a code segment
|
/// A collection of tokens included in a code segment
|
||||||
struct Tokens {
|
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
|
/// 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
|
/// 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)
|
/// 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
|
/// The current token which is currently being evaluated
|
||||||
var current: String
|
public var current: String
|
||||||
/// Any upcoming token that will follow the current one
|
/// Any upcoming token that will follow the current one
|
||||||
var next: String?
|
public var next: String?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ private extension Tokenizer {
|
|||||||
private let delimiters: CharacterSet
|
private let delimiters: CharacterSet
|
||||||
private var index: String.Index?
|
private var index: String.Index?
|
||||||
private var tokenCounts = [String : Int]()
|
private var tokenCounts = [String : Int]()
|
||||||
|
private var allTokens = [String]()
|
||||||
private var lineTokens = [String]()
|
private var lineTokens = [String]()
|
||||||
private var segments: (current: Segment?, previous: Segment?)
|
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 {
|
private func makeSegment(with component: Component, at index: String.Index) -> Segment {
|
||||||
let tokens = Segment.Tokens(
|
let tokens = Segment.Tokens(
|
||||||
|
all: allTokens,
|
||||||
counts: tokenCounts,
|
counts: tokenCounts,
|
||||||
onSameLine: lineTokens,
|
onSameLine: lineTokens,
|
||||||
previous: segments.current?.tokens.current,
|
previous: segments.current?.tokens.current,
|
||||||
@ -155,6 +157,8 @@ private extension Tokenizer {
|
|||||||
count += 1
|
count += 1
|
||||||
tokenCounts[segment.tokens.current] = count
|
tokenCounts[segment.tokens.current] = count
|
||||||
|
|
||||||
|
allTokens.append(segment.tokens.current)
|
||||||
|
|
||||||
if segment.isLastOnLine {
|
if segment.isLastOnLine {
|
||||||
lineTokens = []
|
lineTokens = []
|
||||||
} else {
|
} else {
|
||||||
|
@ -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() {
|
func testAllTestsRunOnLinux() {
|
||||||
XCTAssertTrue(TestCaseVerifier.verifyLinuxTests((type(of: self)).allTests))
|
XCTAssertTrue(TestCaseVerifier.verifyLinuxTests((type(of: self)).allTests))
|
||||||
}
|
}
|
||||||
@ -87,7 +100,8 @@ extension FunctionCallTests {
|
|||||||
("testImplicitInitializerCall", testImplicitInitializerCall),
|
("testImplicitInitializerCall", testImplicitInitializerCall),
|
||||||
("testExplicitInitializerCall", testExplicitInitializerCall),
|
("testExplicitInitializerCall", testExplicitInitializerCall),
|
||||||
("testAccessingPropertyAfterFunctionCallWithoutArguments", testAccessingPropertyAfterFunctionCallWithoutArguments),
|
("testAccessingPropertyAfterFunctionCallWithoutArguments", testAccessingPropertyAfterFunctionCallWithoutArguments),
|
||||||
("testAccessingPropertyAfterFunctionCallWithArguments", testAccessingPropertyAfterFunctionCallWithArguments)
|
("testAccessingPropertyAfterFunctionCallWithArguments", testAccessingPropertyAfterFunctionCallWithArguments),
|
||||||
|
("testCallingStaticMethodOnGenericType", testCallingStaticMethodOnGenericType)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user