tree_haver 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,302 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeHaver
4
+ module Backends
5
+ # Citrus backend using pure Ruby PEG parser
6
+ #
7
+ # This backend wraps Citrus-based parsers (like toml-rb) to provide a
8
+ # pure Ruby alternative to tree-sitter. Citrus is a PEG (Parsing Expression
9
+ # Grammar) parser generator written in Ruby.
10
+ #
11
+ # Unlike tree-sitter backends which are language-agnostic runtime parsers,
12
+ # Citrus parsers are grammar-specific and compiled into Ruby code. Each
13
+ # language needs its own Citrus grammar (e.g., toml-rb for TOML).
14
+ #
15
+ # @note This backend requires a Citrus grammar for the specific language
16
+ # @see https://github.com/mjackson/citrus Citrus parser generator
17
+ # @see https://github.com/emancu/toml-rb toml-rb (TOML Citrus grammar)
18
+ #
19
+ # @example Using with toml-rb
20
+ # require "toml-rb"
21
+ #
22
+ # parser = TreeHaver::Parser.new
23
+ # # For Citrus, "language" is actually a grammar module
24
+ # parser.language = TomlRB::Document
25
+ # tree = parser.parse(toml_source)
26
+ module Citrus
27
+ @load_attempted = false
28
+ @loaded = false
29
+
30
+ # Check if the Citrus backend is available
31
+ #
32
+ # Attempts to require citrus on first call and caches the result.
33
+ #
34
+ # @return [Boolean] true if citrus gem is available
35
+ # @example
36
+ # if TreeHaver::Backends::Citrus.available?
37
+ # puts "Citrus backend is ready"
38
+ # end
39
+ class << self
40
+ def available?
41
+ return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
42
+ @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
43
+ begin
44
+ require "citrus"
45
+
46
+ @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
47
+ rescue LoadError
48
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
49
+ end
50
+ @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
51
+ end
52
+
53
+ # Reset the load state (primarily for testing)
54
+ #
55
+ # @return [void]
56
+ # @api private
57
+ def reset!
58
+ @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
59
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
60
+ end
61
+
62
+ # Get capabilities supported by this backend
63
+ #
64
+ # @return [Hash{Symbol => Object}] capability map
65
+ # @example
66
+ # TreeHaver::Backends::Citrus.capabilities
67
+ # # => { backend: :citrus, query: false, bytes_field: true, incremental: false }
68
+ def capabilities
69
+ return {} unless available?
70
+ {
71
+ backend: :citrus,
72
+ query: false, # Citrus doesn't have a query API like tree-sitter
73
+ bytes_field: true, # Citrus::Match provides offset and length
74
+ incremental: false, # Citrus doesn't support incremental parsing
75
+ pure_ruby: true, # Citrus is pure Ruby (portable)
76
+ }
77
+ end
78
+ end
79
+
80
+ # Citrus grammar wrapper
81
+ #
82
+ # Unlike tree-sitter which loads compiled .so files, Citrus uses Ruby modules
83
+ # that define grammars. This class wraps a Citrus grammar module.
84
+ #
85
+ # @example
86
+ # # For TOML, use toml-rb's grammar
87
+ # language = TreeHaver::Backends::Citrus::Language.new(TomlRB::Document)
88
+ class Language
89
+ # The Citrus grammar module
90
+ # @return [Module] Citrus grammar module (e.g., TomlRB::Document)
91
+ attr_reader :grammar_module
92
+
93
+ # @param grammar_module [Module] A Citrus grammar module with a parse method
94
+ def initialize(grammar_module)
95
+ unless grammar_module.respond_to?(:parse)
96
+ raise TreeHaver::NotAvailable,
97
+ "Grammar module must respond to :parse. " \
98
+ "Expected a Citrus grammar module (e.g., TomlRB::Document)."
99
+ end
100
+ @grammar_module = grammar_module
101
+ end
102
+
103
+ # Not applicable for Citrus (tree-sitter-specific)
104
+ #
105
+ # Citrus grammars are Ruby modules, not shared libraries.
106
+ # This method exists for API compatibility but will raise an error.
107
+ #
108
+ # @raise [TreeHaver::NotAvailable] always raises
109
+ class << self
110
+ def from_library(path, symbol: nil, name: nil)
111
+ raise TreeHaver::NotAvailable,
112
+ "Citrus backend doesn't use shared libraries. " \
113
+ "Use Citrus::Language.new(GrammarModule) instead."
114
+ end
115
+
116
+ alias_method :from_path, :from_library
117
+ end
118
+ end
119
+
120
+ # Citrus parser wrapper
121
+ #
122
+ # Wraps Citrus grammar modules to provide a tree-sitter-like API.
123
+ class Parser
124
+ # Create a new Citrus parser instance
125
+ #
126
+ # @raise [TreeHaver::NotAvailable] if citrus gem is not available
127
+ def initialize
128
+ raise TreeHaver::NotAvailable, "citrus gem not available" unless Citrus.available?
129
+ @grammar = nil
130
+ end
131
+
132
+ # Set the grammar for this parser
133
+ #
134
+ # @param grammar [Language, Module] Citrus grammar module or Language wrapper
135
+ # @return [Language, Module] the grammar that was set
136
+ # @example
137
+ # require "toml-rb"
138
+ # parser.language = TomlRB::Document # Pass module directly
139
+ # # or
140
+ # parser.language = TreeHaver::Backends::Citrus::Language.new(TomlRB::Document)
141
+ def language=(grammar)
142
+ @grammar = if grammar.respond_to?(:grammar_module)
143
+ grammar.grammar_module
144
+ elsif grammar.respond_to?(:parse)
145
+ grammar
146
+ else
147
+ raise ArgumentError,
148
+ "Expected Citrus grammar module or Language wrapper, " \
149
+ "got #{grammar.class}"
150
+ end
151
+ grammar
152
+ end
153
+
154
+ # Parse source code
155
+ #
156
+ # @param source [String] the source code to parse
157
+ # @return [TreeHaver::Tree] wrapped tree
158
+ # @raise [TreeHaver::NotAvailable] if no grammar is set
159
+ # @raise [::Citrus::ParseError] if parsing fails
160
+ def parse(source)
161
+ raise TreeHaver::NotAvailable, "No grammar loaded" unless @grammar
162
+
163
+ begin
164
+ citrus_match = @grammar.parse(source)
165
+ inner_tree = Tree.new(citrus_match, source)
166
+ TreeHaver::Tree.new(inner_tree, source: source)
167
+ rescue ::Citrus::ParseError => e
168
+ # Re-raise with more context
169
+ raise TreeHaver::Error, "Parse error: #{e.message}"
170
+ end
171
+ end
172
+
173
+ # Parse source code (compatibility with tree-sitter API)
174
+ #
175
+ # Citrus doesn't support incremental parsing, so old_tree is ignored.
176
+ #
177
+ # @param old_tree [TreeHaver::Tree, nil] ignored (no incremental parsing support)
178
+ # @param source [String] the source code to parse
179
+ # @return [TreeHaver::Tree] wrapped tree
180
+ def parse_string(old_tree, source)
181
+ parse(source) # Citrus doesn't support incremental parsing
182
+ end
183
+ end
184
+
185
+ # Citrus tree wrapper
186
+ #
187
+ # Wraps a Citrus::Match (which represents the parse tree) to provide
188
+ # tree-sitter-compatible API.
189
+ #
190
+ # @api private
191
+ class Tree
192
+ attr_reader :root_match, :source
193
+
194
+ def initialize(root_match, source)
195
+ @root_match = root_match
196
+ @source = source
197
+ end
198
+
199
+ def root_node
200
+ Node.new(@root_match, @source)
201
+ end
202
+ end
203
+
204
+ # Citrus node wrapper
205
+ #
206
+ # Wraps Citrus::Match objects to provide tree-sitter-compatible node API.
207
+ #
208
+ # Citrus::Match provides:
209
+ # - events[0]: rule name (Symbol) - used as type
210
+ # - offset: byte position
211
+ # - length: byte length
212
+ # - string: matched text
213
+ # - matches: child matches
214
+ # - captures: named groups
215
+ #
216
+ # @api private
217
+ class Node
218
+ attr_reader :match, :source
219
+
220
+ def initialize(match, source)
221
+ @match = match
222
+ @source = source
223
+ end
224
+
225
+ # Get node type from Citrus rule name
226
+ #
227
+ # @return [String] rule name from grammar
228
+ def type
229
+ # Citrus stores the rule name in events[0]
230
+ return "unknown" unless @match.respond_to?(:events)
231
+ return "unknown" unless @match.events.is_a?(Array)
232
+ return "unknown" if @match.events.empty?
233
+
234
+ first = @match.events.first
235
+ first.is_a?(Symbol) ? first.to_s : "unknown"
236
+ end
237
+
238
+ def start_byte
239
+ @match.offset
240
+ end
241
+
242
+ def end_byte
243
+ @match.offset + @match.length
244
+ end
245
+
246
+ def start_point
247
+ calculate_point(@match.offset)
248
+ end
249
+
250
+ def end_point
251
+ calculate_point(@match.offset + @match.length)
252
+ end
253
+
254
+ def text
255
+ @match.string
256
+ end
257
+
258
+ def child_count
259
+ @match.respond_to?(:matches) ? @match.matches.size : 0
260
+ end
261
+
262
+ def child(index)
263
+ return unless @match.respond_to?(:matches)
264
+ return if index >= @match.matches.size
265
+
266
+ Node.new(@match.matches[index], @source)
267
+ end
268
+
269
+ def children
270
+ return [] unless @match.respond_to?(:matches)
271
+ @match.matches.map { |m| Node.new(m, @source) }
272
+ end
273
+
274
+ def each(&block)
275
+ return to_enum(__method__) unless block_given?
276
+ children.each(&block)
277
+ end
278
+
279
+ def has_error?
280
+ false # Citrus raises on parse error, so successful parse has no errors
281
+ end
282
+
283
+ def missing?
284
+ false # Citrus doesn't have the concept of missing nodes
285
+ end
286
+
287
+ def named?
288
+ true # Citrus matches are typically "named" in tree-sitter terminology
289
+ end
290
+
291
+ private
292
+
293
+ def calculate_point(offset)
294
+ lines_before = @source[0...offset].count("\n")
295
+ line_start = @source.rindex("\n", offset - 1) || -1
296
+ column = offset - line_start - 1
297
+ {row: lines_before, column: column}
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
@@ -14,7 +14,7 @@ module TreeHaver
14
14
  module Backends
15
15
  # FFI-based backend for calling libtree-sitter directly
16
16
  #
17
- # This backend uses Ruby FFI (JNR-FFI on JRuby) to call the native Tree-sitter
17
+ # This backend uses Ruby FFI (JNR-FFI on JRuby) to call the native tree-sitter
18
18
  # C library without requiring MRI C extensions. This makes it compatible with
19
19
  # JRuby, TruffleRuby, and other Ruby implementations that support FFI.
20
20
  #
@@ -24,16 +24,16 @@ module TreeHaver
24
24
  # - Accessing node types and children
25
25
  #
26
26
  # Not yet supported:
27
- # - Query API (Tree-sitter queries/patterns)
27
+ # - Query API (tree-sitter queries/patterns)
28
28
  #
29
29
  # @note Requires the `ffi` gem and libtree-sitter shared library to be installed
30
30
  # @see https://github.com/ffi/ffi Ruby FFI
31
- # @see https://tree-sitter.github.io/tree-sitter/ Tree-sitter
31
+ # @see https://tree-sitter.github.io/tree-sitter/ tree-sitter
32
32
  module FFI
33
33
  # Native FFI bindings to libtree-sitter
34
34
  #
35
- # This module handles loading the Tree-sitter runtime library and defining
36
- # FFI function attachments for the core Tree-sitter API.
35
+ # This module handles loading the tree-sitter runtime library and defining
36
+ # FFI function attachments for the core tree-sitter API.
37
37
  #
38
38
  # @api private
39
39
  module Native
@@ -42,8 +42,8 @@ module TreeHaver
42
42
 
43
43
  # FFI struct representation of TSNode
44
44
  #
45
- # Mirrors the C struct layout used by Tree-sitter. TSNode is passed
46
- # by value in the Tree-sitter C API.
45
+ # Mirrors the C struct layout used by tree-sitter. TSNode is passed
46
+ # by value in the tree-sitter C API.
47
47
  #
48
48
  # @api private
49
49
  class TSNode < ::FFI::Struct
@@ -79,10 +79,10 @@ module TreeHaver
79
79
  ].compact
80
80
  end
81
81
 
82
- # Load the Tree-sitter runtime library
82
+ # Load the tree-sitter runtime library
83
83
  #
84
84
  # Tries each candidate library name in order until one succeeds.
85
- # After loading, attaches FFI function definitions for the Tree-sitter API.
85
+ # After loading, attaches FFI function definitions for the tree-sitter API.
86
86
  #
87
87
  # @raise [TreeHaver::NotAvailable] if no library can be loaded
88
88
  # @return [void]
@@ -189,7 +189,7 @@ module TreeHaver
189
189
  end
190
190
  end
191
191
 
192
- # Represents a Tree-sitter language loaded via FFI
192
+ # Represents a tree-sitter language loaded via FFI
193
193
  #
194
194
  # Holds a pointer to a TSLanguage struct from a loaded shared library.
195
195
  class Language
@@ -276,7 +276,7 @@ module TreeHaver
276
276
  end
277
277
  end
278
278
 
279
- # FFI-based Tree-sitter parser
279
+ # FFI-based tree-sitter parser
280
280
  #
281
281
  # Wraps a TSParser pointer and manages its lifecycle with a finalizer.
282
282
  class Parser
@@ -323,18 +323,19 @@ module TreeHaver
323
323
  # Parse source code into a syntax tree
324
324
  #
325
325
  # @param source [String] the source code to parse (should be UTF-8)
326
- # @return [Tree] the parsed syntax tree
326
+ # @return [TreeHaver::Tree] wrapped tree
327
327
  # @raise [TreeHaver::NotAvailable] if parsing fails
328
328
  def parse(source)
329
329
  src = String(source)
330
330
  tree_ptr = Native.ts_parser_parse_string(@parser, ::FFI::Pointer::NULL, src, src.bytesize)
331
331
  raise TreeHaver::NotAvailable, "Parse returned NULL" if tree_ptr.null?
332
332
 
333
- Tree.new(tree_ptr)
333
+ inner_tree = Tree.new(tree_ptr)
334
+ TreeHaver::Tree.new(inner_tree, source: src)
334
335
  end
335
336
  end
336
337
 
337
- # FFI-based Tree-sitter tree
338
+ # FFI-based tree-sitter tree
338
339
  #
339
340
  # Wraps a TSTree pointer and manages its lifecycle with a finalizer.
340
341
  class Tree
@@ -369,10 +370,10 @@ module TreeHaver
369
370
  end
370
371
  end
371
372
 
372
- # FFI-based Tree-sitter node
373
+ # FFI-based tree-sitter node
373
374
  #
374
375
  # Wraps a TSNode by-value struct. TSNode is passed by value in the
375
- # Tree-sitter C API, so we store the struct value directly.
376
+ # tree-sitter C API, so we store the struct value directly.
376
377
  class Node
377
378
  # @api private
378
379
  # @param ts_node_value [Native::TSNode] the TSNode struct (by value)
@@ -388,6 +389,63 @@ module TreeHaver
388
389
  Native.ts_node_type(@val)
389
390
  end
390
391
 
392
+ # Get the number of children
393
+ #
394
+ # @return [Integer] child count
395
+ def child_count
396
+ Native.ts_node_child_count(@val)
397
+ end
398
+
399
+ # Get a child by index
400
+ #
401
+ # @param index [Integer] child index
402
+ # @return [Node, nil] child node or nil if index out of bounds
403
+ def child(index)
404
+ return if index >= child_count || index < 0
405
+ child_node = Native.ts_node_child(@val, index)
406
+ Node.new(child_node)
407
+ end
408
+
409
+ # Get start byte offset
410
+ #
411
+ # @return [Integer]
412
+ def start_byte
413
+ Native.ts_node_start_byte(@val)
414
+ end
415
+
416
+ # Get end byte offset
417
+ #
418
+ # @return [Integer]
419
+ def end_byte
420
+ Native.ts_node_end_byte(@val)
421
+ end
422
+
423
+ # Get start point
424
+ #
425
+ # @return [Object] with row and column
426
+ def start_point
427
+ # FFI backend would need to implement ts_node_start_point
428
+ # For now, return a simple struct
429
+ Struct.new(:row, :column).new(0, Native.ts_node_start_byte(@val))
430
+ end
431
+
432
+ # Get end point
433
+ #
434
+ # @return [Object] with row and column
435
+ def end_point
436
+ # FFI backend would need to implement ts_node_end_point
437
+ # For now, return a simple struct
438
+ Struct.new(:row, :column).new(0, Native.ts_node_end_byte(@val))
439
+ end
440
+
441
+ # Check if node has error
442
+ #
443
+ # @return [Boolean]
444
+ def has_error?
445
+ # Would need ts_node_has_error implementation
446
+ false
447
+ end
448
+
391
449
  # Iterate over child nodes
392
450
  #
393
451
  # @yieldparam child [Node] each child node
@@ -395,7 +453,7 @@ module TreeHaver
395
453
  def each
396
454
  return enum_for(:each) unless block_given?
397
455
 
398
- count = Native.ts_node_child_count(@val)
456
+ count = child_count
399
457
  i = 0
400
458
  while i < count
401
459
  child = Native.ts_node_child(@val, i)
@@ -7,7 +7,7 @@ module TreeHaver
7
7
  # This backend integrates with java-tree-sitter JARs on JRuby,
8
8
  # leveraging JRuby's native Java integration for optimal performance.
9
9
  #
10
- # java-tree-sitter provides Java bindings to Tree-sitter and supports:
10
+ # java-tree-sitter provides Java bindings to tree-sitter and supports:
11
11
  # - Parsing source code into syntax trees
12
12
  # - Incremental parsing via Parser.parse(Tree, String)
13
13
  # - The Query API for pattern matching
@@ -393,10 +393,11 @@ module TreeHaver
393
393
  # Parse source code
394
394
  #
395
395
  # @param source [String] the source code to parse
396
- # @return [Tree] the parsed syntax tree
396
+ # @return [TreeHaver::Tree] wrapped tree
397
397
  def parse(source)
398
398
  java_tree = @parser.parse(source)
399
- Tree.new(java_tree)
399
+ inner_tree = Tree.new(java_tree)
400
+ TreeHaver::Tree.new(inner_tree, source: source)
400
401
  end
401
402
 
402
403
  # Parse source code with optional incremental parsing
@@ -404,18 +405,21 @@ module TreeHaver
404
405
  # When old_tree is provided and has been edited, tree-sitter will reuse
405
406
  # unchanged nodes for better performance.
406
407
  #
407
- # @param old_tree [Tree, nil] previous tree for incremental parsing
408
+ # @param old_tree [TreeHaver::Tree, nil] previous tree for incremental parsing
408
409
  # @param source [String] the source code to parse
409
- # @return [Tree] the parsed syntax tree
410
+ # @return [TreeHaver::Tree] wrapped tree
410
411
  # @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Parser.html#parse(io.github.treesitter.jtreesitter.Tree,java.lang.String)
411
412
  def parse_string(old_tree, source)
412
413
  if old_tree
413
- java_old_tree = old_tree.is_a?(Tree) ? old_tree.impl : old_tree
414
+ # Unwrap TreeHaver::Tree to get inner tree
415
+ inner_old_tree = old_tree.respond_to?(:inner_tree) ? old_tree.inner_tree : old_tree
416
+ java_old_tree = inner_old_tree.is_a?(Tree) ? inner_old_tree.impl : inner_old_tree
414
417
  java_tree = @parser.parse(java_old_tree, source)
415
418
  else
416
419
  java_tree = @parser.parse(source)
417
420
  end
418
- Tree.new(java_tree)
421
+ inner_tree = Tree.new(java_tree)
422
+ TreeHaver::Tree.new(inner_tree, source: source)
419
423
  end
420
424
  end
421
425
 
@@ -5,7 +5,7 @@ module TreeHaver
5
5
  # MRI backend using the ruby_tree_sitter gem
6
6
  #
7
7
  # This backend wraps the ruby_tree_sitter gem, which is a native C extension
8
- # for MRI Ruby. It provides the most feature-complete Tree-sitter integration
8
+ # for MRI Ruby. It provides the most feature-complete tree-sitter integration
9
9
  # on MRI, including support for the Query API.
10
10
  #
11
11
  # @note This backend only works on MRI Ruby, not JRuby or TruffleRuby
@@ -96,34 +96,24 @@ module TreeHaver
96
96
  # Parse source code
97
97
  #
98
98
  # @param source [String] the source code to parse
99
- # @return [::TreeSitter::Tree] the parsed syntax tree
99
+ # @return [TreeHaver::Tree] wrapped tree
100
100
  def parse(source)
101
- @parser.parse(source)
101
+ tree = @parser.parse(source)
102
+ TreeHaver::Tree.new(tree, source: source)
102
103
  end
103
104
 
104
105
  # Parse source code with optional incremental parsing
105
106
  #
106
- # @param old_tree [::TreeSitter::Tree, nil] previous tree for incremental parsing
107
+ # @param old_tree [TreeHaver::Tree, nil] previous tree for incremental parsing
107
108
  # @param source [String] the source code to parse
108
- # @return [::TreeSitter::Tree] the parsed syntax tree
109
+ # @return [TreeHaver::Tree] wrapped tree
109
110
  def parse_string(old_tree, source)
110
- @parser.parse_string(old_tree, source)
111
+ # Unwrap if TreeHaver::Tree to get inner tree for incremental parsing
112
+ inner_old_tree = old_tree.respond_to?(:inner_tree) ? old_tree.inner_tree : old_tree
113
+ tree = @parser.parse_string(inner_old_tree, source)
114
+ TreeHaver::Tree.new(tree, source: source)
111
115
  end
112
116
  end
113
-
114
- # Wrapper for ruby_tree_sitter Tree
115
- #
116
- # Not used directly; TreeHaver passes through ::TreeSitter::Tree objects.
117
- class Tree
118
- # Not used directly; we pass through ruby_tree_sitter::Tree
119
- end
120
-
121
- # Wrapper for ruby_tree_sitter Node
122
- #
123
- # Not used directly; TreeHaver passes through ::TreeSitter::Node objects.
124
- class Node
125
- # Not used directly; we pass through ruby_tree_sitter::Node
126
- end
127
117
  end
128
118
  end
129
119
  end
@@ -5,7 +5,7 @@ module TreeHaver
5
5
  # Rust backend using the tree_stump gem
6
6
  #
7
7
  # This backend wraps the tree_stump gem, which provides Ruby bindings to
8
- # Tree-sitter written in Rust. It offers native performance with Rust's
8
+ # tree-sitter written in Rust. It offers native performance with Rust's
9
9
  # safety guarantees and includes precompiled binaries for common platforms.
10
10
  #
11
11
  # tree_stump supports incremental parsing and the Query API, making it
@@ -140,36 +140,24 @@ module TreeHaver
140
140
  # Parse source code
141
141
  #
142
142
  # @param source [String] the source code to parse
143
- # @return [Object] the parsed syntax tree
143
+ # @return [TreeHaver::Tree] wrapped tree
144
144
  def parse(source)
145
- @parser.parse(source)
145
+ tree = @parser.parse(source)
146
+ TreeHaver::Tree.new(tree, source: source)
146
147
  end
147
148
 
148
149
  # Parse source code with optional incremental parsing
149
150
  #
150
- # @param old_tree [Object, nil] previous tree for incremental parsing
151
+ # @param old_tree [TreeHaver::Tree, nil] previous tree for incremental parsing
151
152
  # @param source [String] the source code to parse
152
- # @return [Object] the parsed syntax tree
153
+ # @return [TreeHaver::Tree] wrapped tree
153
154
  def parse_string(old_tree, source)
154
155
  # tree_stump doesn't have parse_string, use parse instead
155
156
  # TODO: Check if tree_stump supports incremental parsing
156
- @parser.parse(source)
157
+ tree = @parser.parse(source)
158
+ TreeHaver::Tree.new(tree, source: source)
157
159
  end
158
160
  end
159
-
160
- # Wrapper for tree_stump Tree
161
- #
162
- # Not used directly; TreeHaver passes through tree_stump Tree objects.
163
- class Tree
164
- # Not used directly; we pass through tree_stump::Tree
165
- end
166
-
167
- # Wrapper for tree_stump Node
168
- #
169
- # Not used directly; TreeHaver passes through tree_stump::Node objects.
170
- class Node
171
- # Not used directly; we pass through tree_stump::Node
172
- end
173
161
  end
174
162
  end
175
163
  end
@@ -221,7 +221,7 @@ module TreeHaver
221
221
  #
222
222
  # @return [String] error message with installation hints
223
223
  def not_found_message
224
- "Tree-sitter #{@language_name} grammar not found. " \
224
+ "tree-sitter #{@language_name} grammar not found. " \
225
225
  "Searched: #{search_paths.join(", ")}. " \
226
226
  "Install tree-sitter-#{@language_name} or set #{env_var_name}."
227
227
  end