Skip to content

Commit 114a515

Browse files
authored
Add text on Node (#41)
1 parent f07ede3 commit 114a515

File tree

4 files changed

+97
-16
lines changed

4 files changed

+97
-16
lines changed

Sources/SwiftTreeSitter/Node.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,29 @@ extension Node {
192192

193193
return Node(internalNode: n, internalTree: internalTree)
194194
}
195+
196+
/// The text of the node
197+
public var text: String? {
198+
// Tree.source holds the original source text. It can be empty if the parse
199+
// was performed without a backing string; handle that gracefully.
200+
let source = self.internalTree.source
201+
guard source != nil else { return nil }
202+
203+
// byteRange is in UTF-16LE bytes. Convert to UTF-16 code unit offsets.
204+
let lowerUnits = Int(byteRange.lowerBound / 2)
205+
let upperUnits = Int(byteRange.upperBound / 2)
206+
207+
// Clamp to valid bounds of the source’s UTF-16 view.
208+
let utf16Count = source!.utf16.count
209+
let startUnits = max(0, min(lowerUnits, utf16Count))
210+
let endUnits = max(startUnits, min(upperUnits, utf16Count))
211+
212+
// Convert UTF-16 code unit offsets to String.Index and slice.
213+
let startIndex = String.Index(utf16Offset: startUnits, in: source!)
214+
let endIndex = String.Index(utf16Offset: endUnits, in: source!)
215+
216+
return String(source![startIndex..<endIndex])
217+
}
195218
}
196219

197220
extension Node {

Sources/SwiftTreeSitter/Parser.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ extension Parser {
9999
public typealias ReadBlock = (Int, Point) -> Data?
100100
public typealias DataSnapshotProvider = @Sendable (Int, Point) -> Data?
101101

102-
public func parse(_ string: String) -> MutableTree? {
103-
guard let data = string.data(using: encoding) else { return nil }
102+
public func parse(_ source: String) -> MutableTree? {
103+
guard let data = source.data(using: encoding) else { return nil }
104104

105105
let dataLength = data.count
106106

@@ -112,7 +112,7 @@ extension Parser {
112112
return ts_parser_parse_string_encoding(internalParser, nil, ptr, UInt32(dataLength), TSInputEncodingUTF16LE)
113113
})
114114

115-
return optionalTreePtr.flatMap({ MutableTree(internalTree: $0) })
115+
return optionalTreePtr.flatMap({ MutableTree(internalTree: $0,source: source) })
116116
}
117117

118118
public func parse(tree: Tree?, encoding: TSInputEncoding = TSInputEncodingUTF16LE, readBlock: ReadBlock) -> MutableTree? {
@@ -127,7 +127,7 @@ extension Parser {
127127
return nil
128128
}
129129

130-
return MutableTree(internalTree: newTree)
130+
return MutableTree(internalTree: newTree,source: tree?.source)
131131
}
132132
}
133133

Sources/SwiftTreeSitter/Tree.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import TreeSitter
33
/// An immutable tree-sitter tree structure.
44
public final class Tree: Sendable {
55
private let internalTreePointer: SendableOpaquePointer
6+
7+
let source:String?
68

7-
init(internalTree: OpaquePointer) {
9+
init(internalTree: OpaquePointer,source:String? = nil) {
810
self.internalTreePointer = SendableOpaquePointer(internalTree)
11+
self.source = source
912
}
1013

1114
deinit {
@@ -131,8 +134,8 @@ extension Tree {
131134
public final class MutableTree {
132135
let tree: Tree
133136

134-
init(internalTree: OpaquePointer) {
135-
self.tree = Tree(internalTree: internalTree)
137+
init(internalTree: OpaquePointer,source: String?) {
138+
self.tree = Tree(internalTree: internalTree,source: source)
136139
}
137140

138141
init(tree: Tree) {

Tests/SwiftTreeSitterTests/NodeTests.swift

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,79 @@ import SwiftTreeSitter
44
import TestTreeSitterSwift
55

66
final class NodeTests: XCTestCase {
7-
#if !os(WASI)
8-
func testTreeNodeLifecycle() throws {
7+
#if !os(WASI)
8+
func testTreeNodeLifecycle() throws {
9+
let language = Language(language: tree_sitter_swift())
10+
11+
let text = """
12+
func main() {
13+
}
14+
"""
15+
16+
let parser = Parser()
17+
try parser.setLanguage(language)
18+
19+
var tree: MutableTree? = try XCTUnwrap(parser.parse(text))
20+
let root = try XCTUnwrap(tree?.rootNode)
21+
22+
tree = nil
23+
24+
XCTAssertTrue(root.childCount != 0)
25+
}
26+
#endif
27+
28+
func testTreeNodeText() throws {
929
let language = Language(language: tree_sitter_swift())
1030

1131
let text = """
12-
func main() {
13-
}
14-
"""
32+
func greet(name: String){
33+
print("hello,\(name)")
34+
}
35+
greet("world")
36+
"""
1537

1638
let parser = Parser()
1739
try parser.setLanguage(language)
1840

19-
var tree: MutableTree? = try XCTUnwrap(parser.parse(text))
41+
let tree: MutableTree? = try XCTUnwrap(parser.parse(text))
2042
let root = try XCTUnwrap(tree?.rootNode)
2143

22-
tree = nil
44+
// function_declaration
45+
let function_declaration_node = try XCTUnwrap(root.child(at: 0))
46+
47+
for i in (0..<function_declaration_node.namedChildCount) {
48+
let node = try XCTUnwrap(function_declaration_node.namedChild(at: i))
49+
let nodeType = try XCTUnwrap(node.nodeType)
50+
let nodeText = try XCTUnwrap(node.text)
51+
52+
switch nodeType {
53+
case "simple_identifier":
54+
XCTAssertEqual(nodeText, "greet")
55+
case "parameter":
56+
XCTAssertEqual(nodeText, "name: String")
57+
default:
58+
break
59+
}
60+
}
61+
62+
// call_expression
63+
let call_expression_node = try XCTUnwrap(root.child(at: 1))
64+
let call_expression_text = try XCTUnwrap(call_expression_node.text)
65+
XCTAssertEqual(call_expression_text, "greet(\"world\")")
66+
67+
for i in (0..<call_expression_node.namedChildCount) {
68+
let node = try XCTUnwrap(call_expression_node.namedChild(at: i))
69+
let nodeType = try XCTUnwrap(node.nodeType)
70+
let nodeText = try XCTUnwrap(node.text)
2371

24-
XCTAssertTrue(root.childCount != 0)
72+
switch nodeType {
73+
case "simple_identifier":
74+
XCTAssertEqual(nodeText, "greet")
75+
case "call_suffix":
76+
XCTAssertEqual(nodeText, "(\"world\")")
77+
default:
78+
break
79+
}
80+
}
2581
}
26-
#endif
2782
}

0 commit comments

Comments
 (0)