Skip to content

Commit 4b7ef21

Browse files
committed
[classification] Add API TokenSyntax.tokenClassification to get the classification for a specific token
1 parent e1fa7ff commit 4b7ef21

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

Sources/SwiftSyntax/SyntaxClassification.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ extension SyntaxClassification {
5555
/// - Returns: A pair of classification and whether it is "forced", or nil if
5656
/// no classification is attached.
5757
internal static func classify(
58-
parentKind: SyntaxKind, indexInParent: UInt32, childKind: SyntaxKind
58+
parentKind: SyntaxKind, indexInParent: Int, childKind: SyntaxKind
5959
) -> (SyntaxClassification, Bool)? {
6060
// Separate checks for token nodes (most common checks) versus checks for layout nodes.
6161
if childKind == .token {

Sources/SwiftSyntax/SyntaxClassifier.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
extension TokenSyntax {
14+
/// The `SyntaxClassifiedRange` for the token text, excluding trivia.
15+
public var tokenClassification: SyntaxClassifiedRange {
16+
var contextualClassification: (SyntaxClassification, Bool)? = nil
17+
var curData = self.data
18+
repeat {
19+
guard let parent = curData.parent else { break }
20+
contextualClassification = SyntaxClassification.classify(parentKind: parent.raw.kind,
21+
indexInParent: curData.indexInParent, childKind: raw.kind)
22+
curData = parent.data
23+
} while contextualClassification == nil
24+
25+
let relativeOffset = raw.tokenLeadingTriviaLength.utf8Length
26+
let absoluteOffset = position.utf8Offset + relativeOffset
27+
let classifiedRange = raw.withUnsafeTokenText(
28+
relativeOffset: relativeOffset
29+
) { (tokText: UnsafeTokenText?) -> SyntaxClassifiedRange in
30+
return tokText!.classify(offset: absoluteOffset,
31+
contextualClassification: contextualClassification)
32+
}
33+
return classifiedRange
34+
}
35+
}
36+
1337
extension UnsafeTriviaPiece {
1438
func classify(offset: Int) -> SyntaxClassifiedRange {
1539
let range = ByteSourceRange(offset: offset, length: length)
@@ -72,7 +96,7 @@ fileprivate struct AbsoluteNode {
7296
self.position = position
7397
// Check if this node has a classification otherwise inherit it from the parent.
7498
self.classification = SyntaxClassification.classify(parentKind: parent.raw.kind,
75-
indexInParent: position.indexInParent, childKind: raw.kind) ?? parent.classification
99+
indexInParent: Int(position.indexInParent), childKind: raw.kind) ?? parent.classification
76100
}
77101

78102
var offset: Int { return Int(position.offset) }

Tests/SwiftSyntaxTest/ClassificationTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SwiftSyntax
44
public class ClassificationTests: XCTestCase {
55
public static let allTests = [
66
("testClassification", testClassification),
7+
("testTokenClassification", testTokenClassification),
78
]
89

910
public func testClassification() {
@@ -92,4 +93,25 @@ public class ClassificationTests: XCTestCase {
9293
XCTAssertEqual(classif.range, ByteSourceRange(offset: 11, length: 1))
9394
}
9495
}
96+
97+
public func testTokenClassification() {
98+
let source = "let x: Int"
99+
let tree = try! SyntaxParser.parse(source: source)
100+
do {
101+
let tokens = Array(tree.tokens)
102+
XCTAssertEqual(tokens.count, 4)
103+
guard tokens.count == 4 else {
104+
return
105+
}
106+
let classif = tokens.map { $0.tokenClassification }
107+
XCTAssertEqual(classif[0].kind, .keyword)
108+
XCTAssertEqual(classif[0].range, ByteSourceRange(offset: 0, length: 3))
109+
XCTAssertEqual(classif[1].kind, .none)
110+
XCTAssertEqual(classif[1].range, ByteSourceRange(offset: 4, length: 1))
111+
XCTAssertEqual(classif[2].kind, .none)
112+
XCTAssertEqual(classif[2].range, ByteSourceRange(offset: 5, length: 1))
113+
XCTAssertEqual(classif[3].kind, .typeIdentifier)
114+
XCTAssertEqual(classif[3].range, ByteSourceRange(offset: 7, length: 3))
115+
}
116+
}
95117
}

0 commit comments

Comments
 (0)