tree_haver 5.0.5 → 7.1.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +299 -660
- data/lib/tree_haver/backend_api.rb +78 -35
- data/lib/tree_haver/backend_context.rb +28 -0
- data/lib/tree_haver/backend_registry.rb +119 -382
- data/lib/tree_haver/backends/citrus.rb +55 -53
- data/lib/tree_haver/backends/ffi.rb +105 -101
- data/lib/tree_haver/backends/java.rb +92 -76
- data/lib/tree_haver/backends/mri.rb +44 -39
- data/lib/tree_haver/backends/parslet.rb +53 -48
- data/lib/tree_haver/backends/prism.rb +140 -43
- data/lib/tree_haver/backends/psych.rb +30 -26
- data/lib/tree_haver/backends/rust.rb +21 -17
- data/lib/tree_haver/backends/tslp.rb +274 -0
- data/lib/tree_haver/base/comment.rb +320 -0
- data/lib/tree_haver/base/language.rb +2 -2
- data/lib/tree_haver/base/node.rb +39 -31
- data/lib/tree_haver/base/parser.rb +4 -0
- data/lib/tree_haver/base/point.rb +3 -3
- data/lib/tree_haver/base/tree.rb +1 -1
- data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
- data/lib/tree_haver/contracts.rb +1025 -0
- data/lib/tree_haver/grammar_finder.rb +125 -70
- data/lib/tree_haver/kaitai_backend.rb +30 -0
- data/lib/tree_haver/language.rb +36 -37
- data/lib/tree_haver/language_pack.rb +41 -0
- data/lib/tree_haver/language_registry.rb +34 -3
- data/lib/tree_haver/library_path_utils.rb +7 -7
- data/lib/tree_haver/node.rb +40 -31
- data/lib/tree_haver/parser.rb +44 -37
- data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
- data/lib/tree_haver/path_validator.rb +39 -36
- data/lib/tree_haver/peg_backends.rb +76 -0
- data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
- data/lib/tree_haver/rspec.rb +1 -31
- data/lib/tree_haver/tree.rb +16 -7
- data/lib/tree_haver/version.rb +5 -14
- data/lib/tree_haver.rb +536 -1240
- data/sig/tree_haver.rbs +1 -229
- data.tar.gz.sig +0 -0
- metadata +154 -70
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -1393
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -359
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
- data/lib/tree_haver/base.rb +0 -12
- data/lib/tree_haver/compat.rb +0 -43
- data/lib/tree_haver/rspec/testable_node.rb +0 -217
- data/sig/tree_haver/backends.rbs +0 -352
- data/sig/tree_haver/grammar_finder.rbs +0 -29
- data/sig/tree_haver/path_validator.rbs +0 -32
|
@@ -70,12 +70,12 @@ module TreeHaver
|
|
|
70
70
|
# @see https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter Maven Central
|
|
71
71
|
module Java
|
|
72
72
|
# The Java package for java-tree-sitter
|
|
73
|
-
JAVA_PACKAGE =
|
|
73
|
+
JAVA_PACKAGE = 'io.github.treesitter.jtreesitter'
|
|
74
74
|
|
|
75
75
|
@load_attempted = false
|
|
76
76
|
@loaded = false
|
|
77
|
-
@java_classes = {}
|
|
78
|
-
@runtime_lookup = nil
|
|
77
|
+
@java_classes = {}
|
|
78
|
+
@runtime_lookup = nil # Cached SymbolLookup for libtree-sitter.so
|
|
79
79
|
|
|
80
80
|
module_function
|
|
81
81
|
|
|
@@ -104,16 +104,17 @@ module TreeHaver
|
|
|
104
104
|
# ENV["TREE_SITTER_JAVA_JARS_DIR"] = "/path/to/java-tree-sitter/jars"
|
|
105
105
|
# TreeHaver::Backends::Java.add_jars_from_env!
|
|
106
106
|
def add_jars_from_env!
|
|
107
|
-
# :
|
|
107
|
+
# simplecov:disable
|
|
108
108
|
# This method requires JRuby and cannot be tested on MRI/CRuby.
|
|
109
109
|
# JRuby-specific CI jobs would test this code.
|
|
110
|
-
require
|
|
110
|
+
require 'java'
|
|
111
111
|
|
|
112
112
|
# Add JARs to classpath
|
|
113
|
-
dir = ENV[
|
|
113
|
+
dir = ENV['TREE_SITTER_JAVA_JARS_DIR']
|
|
114
114
|
if dir && Dir.exist?(dir)
|
|
115
|
-
Dir[File.join(dir,
|
|
115
|
+
Dir[File.join(dir, '**', '*.jar')].each do |jar|
|
|
116
116
|
next if $CLASSPATH.include?(jar)
|
|
117
|
+
|
|
117
118
|
$CLASSPATH << jar
|
|
118
119
|
end
|
|
119
120
|
end
|
|
@@ -121,7 +122,7 @@ module TreeHaver
|
|
|
121
122
|
# Configure native library path for libtree-sitter
|
|
122
123
|
# java-tree-sitter uses JNI and needs to find the native library
|
|
123
124
|
configure_native_library_path!
|
|
124
|
-
# :
|
|
125
|
+
# simplecov:enable
|
|
125
126
|
rescue LoadError
|
|
126
127
|
# ignore; not JRuby or Java bridge not available
|
|
127
128
|
end
|
|
@@ -131,23 +132,23 @@ module TreeHaver
|
|
|
131
132
|
# @return [void]
|
|
132
133
|
# @api private
|
|
133
134
|
def configure_native_library_path!
|
|
134
|
-
# :
|
|
135
|
+
# simplecov:disable
|
|
135
136
|
# This method requires JRuby and cannot be tested on MRI/CRuby.
|
|
136
|
-
lib_path = ENV[
|
|
137
|
+
lib_path = ENV['TREE_SITTER_RUNTIME_LIB']
|
|
137
138
|
return unless lib_path && File.exist?(lib_path)
|
|
138
139
|
|
|
139
140
|
lib_dir = File.dirname(lib_path)
|
|
140
|
-
current_path = java.lang.System.getProperty(
|
|
141
|
+
current_path = java.lang.System.getProperty('java.library.path') || ''
|
|
141
142
|
|
|
142
143
|
unless current_path.include?(lib_dir)
|
|
143
144
|
new_path = current_path.empty? ? lib_dir : "#{lib_dir}:#{current_path}"
|
|
144
|
-
java.lang.System.setProperty(
|
|
145
|
+
java.lang.System.setProperty('java.library.path', new_path)
|
|
145
146
|
|
|
146
147
|
# Also set jna.library.path in case it uses JNA
|
|
147
|
-
java.lang.System.setProperty(
|
|
148
|
+
java.lang.System.setProperty('jna.library.path', new_path)
|
|
148
149
|
end
|
|
149
|
-
# :
|
|
150
|
-
rescue =>
|
|
150
|
+
# simplecov:enable
|
|
151
|
+
rescue StandardError => _e
|
|
151
152
|
# Ignore errors setting library path
|
|
152
153
|
end
|
|
153
154
|
|
|
@@ -165,9 +166,10 @@ module TreeHaver
|
|
|
165
166
|
# end
|
|
166
167
|
class << self
|
|
167
168
|
def available?
|
|
168
|
-
return @loaded if @load_attempted
|
|
169
|
-
|
|
170
|
-
@
|
|
169
|
+
return @loaded if @load_attempted
|
|
170
|
+
|
|
171
|
+
@load_attempted = true
|
|
172
|
+
@loaded = check_availability
|
|
171
173
|
end
|
|
172
174
|
|
|
173
175
|
# Reset the load state (primarily for testing)
|
|
@@ -175,21 +177,21 @@ module TreeHaver
|
|
|
175
177
|
# @return [void]
|
|
176
178
|
# @api private
|
|
177
179
|
def reset!
|
|
178
|
-
@load_attempted = false
|
|
179
|
-
@loaded = false
|
|
180
|
-
@load_error = nil
|
|
181
|
-
@loader = nil
|
|
182
|
-
@java_classes = {}
|
|
180
|
+
@load_attempted = false
|
|
181
|
+
@loaded = false
|
|
182
|
+
@load_error = nil
|
|
183
|
+
@loader = nil
|
|
184
|
+
@java_classes = {}
|
|
183
185
|
end
|
|
184
186
|
|
|
185
187
|
private
|
|
186
188
|
|
|
187
189
|
def check_availability
|
|
188
190
|
# 1. Check Ruby engine
|
|
189
|
-
return false unless RUBY_ENGINE ==
|
|
191
|
+
return false unless RUBY_ENGINE == 'jruby'
|
|
190
192
|
|
|
191
193
|
# 2. Check for required JARs via environment variable
|
|
192
|
-
jars_dir = ENV[
|
|
194
|
+
jars_dir = ENV['TREE_SITTER_JAVA_JARS_DIR']
|
|
193
195
|
return false unless jars_dir && Dir.exist?(jars_dir)
|
|
194
196
|
|
|
195
197
|
# 3. Check if we can load the classes
|
|
@@ -222,19 +224,21 @@ module TreeHaver
|
|
|
222
224
|
# @return [Hash{Symbol => Object}] capability map
|
|
223
225
|
# @example
|
|
224
226
|
# TreeHaver::Backends::Java.capabilities
|
|
225
|
-
# # => { backend: :java, parse: true, query: true, bytes_field: true, incremental: true }
|
|
227
|
+
# # => { backend: :java, parse: true, query: true, bytes_field: true, incremental: true, comment_support: :nodes_only }
|
|
226
228
|
def capabilities
|
|
227
|
-
# :
|
|
229
|
+
# simplecov:disable
|
|
228
230
|
# This method returns meaningful data only on JRuby when java-tree-sitter is available.
|
|
229
231
|
return {} unless available?
|
|
232
|
+
|
|
230
233
|
{
|
|
231
234
|
backend: :java,
|
|
232
235
|
parse: true,
|
|
233
236
|
query: true, # java-tree-sitter supports the Query API
|
|
234
237
|
bytes_field: true,
|
|
235
238
|
incremental: true, # java-tree-sitter supports Parser.parse(Tree, String)
|
|
239
|
+
comment_support: :nodes_only
|
|
236
240
|
}
|
|
237
|
-
# :
|
|
241
|
+
# simplecov:enable
|
|
238
242
|
end
|
|
239
243
|
|
|
240
244
|
# Java backend language wrapper (raw backend language)
|
|
@@ -250,7 +254,7 @@ module TreeHaver
|
|
|
250
254
|
# @see TreeHaver::Language The factory module users should interact with
|
|
251
255
|
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Language.html
|
|
252
256
|
#
|
|
253
|
-
# :
|
|
257
|
+
# simplecov:disable
|
|
254
258
|
# All Java backend implementation classes require JRuby and cannot be tested on MRI/CRuby.
|
|
255
259
|
# JRuby-specific CI jobs would test this code.
|
|
256
260
|
class Language
|
|
@@ -290,10 +294,10 @@ module TreeHaver
|
|
|
290
294
|
return unless other.backend == @backend
|
|
291
295
|
|
|
292
296
|
# Compare by path first, then symbol
|
|
293
|
-
cmp = (@path ||
|
|
297
|
+
cmp = (@path || '') <=> (other.path || '')
|
|
294
298
|
return cmp if cmp.nonzero?
|
|
295
299
|
|
|
296
|
-
(@symbol ||
|
|
300
|
+
(@symbol || '') <=> (other.symbol || '')
|
|
297
301
|
end
|
|
298
302
|
|
|
299
303
|
# Hash value for this language (for use in Sets/Hashes)
|
|
@@ -303,7 +307,7 @@ module TreeHaver
|
|
|
303
307
|
end
|
|
304
308
|
|
|
305
309
|
# Alias eql? to ==
|
|
306
|
-
|
|
310
|
+
alias eql? ==
|
|
307
311
|
|
|
308
312
|
# Load a language from a shared library
|
|
309
313
|
#
|
|
@@ -334,7 +338,7 @@ module TreeHaver
|
|
|
334
338
|
# )
|
|
335
339
|
class << self
|
|
336
340
|
def from_library(path, symbol: nil, name: nil)
|
|
337
|
-
raise TreeHaver::NotAvailable,
|
|
341
|
+
raise TreeHaver::NotAvailable, 'Java backend not available' unless Java.available?
|
|
338
342
|
|
|
339
343
|
# Use shared utility for consistent symbol derivation across backends
|
|
340
344
|
# If symbol not provided, derive from name or path
|
|
@@ -351,7 +355,7 @@ module TreeHaver
|
|
|
351
355
|
# We cache this lookup at the module level
|
|
352
356
|
unless Java.runtime_lookup
|
|
353
357
|
# Use libraryLookup(String, Arena) to search LD_LIBRARY_PATH
|
|
354
|
-
Java.runtime_lookup = symbol_lookup_class.libraryLookup(
|
|
358
|
+
Java.runtime_lookup = symbol_lookup_class.libraryLookup('libtree-sitter.so', arena)
|
|
355
359
|
end
|
|
356
360
|
|
|
357
361
|
# Now load the grammar library
|
|
@@ -378,30 +382,30 @@ module TreeHaver
|
|
|
378
382
|
error_msg = "Failed to load language '#{sym}' from #{path}: #{e.message}"
|
|
379
383
|
if root_cause.is_a?(::Java::JavaLang::UnsatisfiedLinkError)
|
|
380
384
|
unresolved = root_cause.message.to_s
|
|
381
|
-
if unresolved.include?(
|
|
385
|
+
if unresolved.include?('ts_language_version')
|
|
382
386
|
# This specific symbol was renamed in tree-sitter 0.24
|
|
383
387
|
error_msg += "\n\nVersion mismatch detected: The grammar was built against " \
|
|
384
|
-
|
|
388
|
+
'tree-sitter < 0.24 (uses ts_language_version), but your runtime library ' \
|
|
385
389
|
"is tree-sitter >= 0.24 (uses ts_language_abi_version).\n\n" \
|
|
386
390
|
"Solutions:\n" \
|
|
387
391
|
"1. Rebuild the grammar against your version of tree-sitter\n" \
|
|
388
392
|
"2. Install a matching version of tree-sitter (< 0.24)\n" \
|
|
389
|
-
|
|
390
|
-
elsif unresolved.include?(
|
|
393
|
+
'3. Find a pre-built grammar compatible with tree-sitter 0.24+'
|
|
394
|
+
elsif unresolved.include?('ts_language') || unresolved.include?('ts_parser')
|
|
391
395
|
error_msg += "\n\nThe grammar library has unresolved tree-sitter symbols. " \
|
|
392
|
-
|
|
393
|
-
|
|
396
|
+
'Ensure libtree-sitter.so is in LD_LIBRARY_PATH and version-compatible ' \
|
|
397
|
+
'with the grammar.'
|
|
394
398
|
end
|
|
395
399
|
end
|
|
396
400
|
raise TreeHaver::NotAvailable, error_msg
|
|
397
401
|
rescue ::Java::JavaLang::UnsatisfiedLinkError => e
|
|
398
402
|
raise TreeHaver::NotAvailable,
|
|
399
|
-
|
|
400
|
-
|
|
403
|
+
"Native library error loading #{path}: #{e.message}. " \
|
|
404
|
+
'Ensure the library is in LD_LIBRARY_PATH.'
|
|
401
405
|
rescue ::Java::JavaLang::IllegalArgumentException => e
|
|
402
406
|
raise TreeHaver::NotAvailable,
|
|
403
|
-
|
|
404
|
-
|
|
407
|
+
"Could not find library '#{path}': #{e.message}. " \
|
|
408
|
+
"Ensure it's in LD_LIBRARY_PATH or provide an absolute path."
|
|
405
409
|
end
|
|
406
410
|
end
|
|
407
411
|
|
|
@@ -420,14 +424,14 @@ module TreeHaver
|
|
|
420
424
|
# # tree-sitter-toml-0.23.2.jar from Maven Central
|
|
421
425
|
# lang = TreeHaver::Backends::Java::Language.load_by_name("toml")
|
|
422
426
|
def load_by_name(name)
|
|
423
|
-
raise TreeHaver::NotAvailable,
|
|
427
|
+
raise TreeHaver::NotAvailable, 'Java backend not available' unless Java.available?
|
|
424
428
|
|
|
425
429
|
# Try to find the grammar library in standard locations
|
|
426
430
|
# Look for library names like "tree-sitter-toml" or "libtree-sitter-toml"
|
|
427
431
|
lib_names = [
|
|
428
432
|
"tree-sitter-#{name}",
|
|
429
433
|
"libtree-sitter-#{name}",
|
|
430
|
-
"tree_sitter_#{name}"
|
|
434
|
+
"tree_sitter_#{name}"
|
|
431
435
|
]
|
|
432
436
|
|
|
433
437
|
begin
|
|
@@ -436,7 +440,7 @@ module TreeHaver
|
|
|
436
440
|
|
|
437
441
|
# Ensure runtime lookup is available
|
|
438
442
|
unless Java.runtime_lookup
|
|
439
|
-
Java.runtime_lookup = symbol_lookup_class.libraryLookup(
|
|
443
|
+
Java.runtime_lookup = symbol_lookup_class.libraryLookup('libtree-sitter.so', arena)
|
|
440
444
|
end
|
|
441
445
|
|
|
442
446
|
# Try each library name
|
|
@@ -451,9 +455,9 @@ module TreeHaver
|
|
|
451
455
|
|
|
452
456
|
unless grammar_lookup
|
|
453
457
|
raise TreeHaver::NotAvailable,
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
458
|
+
"Failed to load language '#{name}': Library not found. " \
|
|
459
|
+
"Ensure the grammar library (e.g., libtree-sitter-#{name}.so) " \
|
|
460
|
+
'is in LD_LIBRARY_PATH.'
|
|
457
461
|
end
|
|
458
462
|
|
|
459
463
|
combined_lookup = grammar_lookup.or(Java.runtime_lookup)
|
|
@@ -462,15 +466,15 @@ module TreeHaver
|
|
|
462
466
|
new(java_lang, symbol: sym)
|
|
463
467
|
rescue ::Java::JavaLang::RuntimeException => e
|
|
464
468
|
raise TreeHaver::NotAvailable,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
469
|
+
"Failed to load language '#{name}': #{e.message}. " \
|
|
470
|
+
"Ensure the grammar library (e.g., libtree-sitter-#{name}.so) " \
|
|
471
|
+
'is in LD_LIBRARY_PATH.'
|
|
468
472
|
end
|
|
469
473
|
end
|
|
470
474
|
end
|
|
471
475
|
|
|
472
476
|
class << self
|
|
473
|
-
|
|
477
|
+
alias from_path from_library
|
|
474
478
|
end
|
|
475
479
|
end
|
|
476
480
|
|
|
@@ -493,7 +497,8 @@ module TreeHaver
|
|
|
493
497
|
#
|
|
494
498
|
# @raise [TreeHaver::NotAvailable] if Java backend is not available
|
|
495
499
|
def initialize
|
|
496
|
-
raise TreeHaver::NotAvailable,
|
|
500
|
+
raise TreeHaver::NotAvailable, 'Java backend not available' unless Java.available?
|
|
501
|
+
|
|
497
502
|
@parser = Java.java_classes[:Parser].new
|
|
498
503
|
end
|
|
499
504
|
|
|
@@ -517,7 +522,8 @@ module TreeHaver
|
|
|
517
522
|
java_result = @parser.parse(source)
|
|
518
523
|
# jtreesitter 0.26.0 returns Optional<Tree>
|
|
519
524
|
java_tree = unwrap_optional(java_result)
|
|
520
|
-
raise TreeHaver::Error,
|
|
525
|
+
raise TreeHaver::Error, 'Parser returned no tree' unless java_tree
|
|
526
|
+
|
|
521
527
|
Tree.new(java_tree)
|
|
522
528
|
end
|
|
523
529
|
|
|
@@ -538,23 +544,24 @@ module TreeHaver
|
|
|
538
544
|
if old_tree
|
|
539
545
|
# Get the actual Java Tree object
|
|
540
546
|
java_old_tree = if old_tree.is_a?(Tree)
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
547
|
+
old_tree.impl
|
|
548
|
+
else
|
|
549
|
+
unwrap_optional(old_tree)
|
|
550
|
+
end
|
|
545
551
|
|
|
546
552
|
java_result = if java_old_tree
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
553
|
+
# jtreesitter 0.26.0 API: parse(String source, Tree oldTree)
|
|
554
|
+
@parser.parse(source, java_old_tree)
|
|
555
|
+
else
|
|
556
|
+
@parser.parse(source)
|
|
557
|
+
end
|
|
552
558
|
else
|
|
553
559
|
java_result = @parser.parse(source)
|
|
554
560
|
end
|
|
555
561
|
# jtreesitter 0.26.0 returns Optional<Tree>
|
|
556
562
|
java_tree = unwrap_optional(java_result)
|
|
557
|
-
raise TreeHaver::Error,
|
|
563
|
+
raise TreeHaver::Error, 'Parser returned no tree' unless java_tree
|
|
564
|
+
|
|
558
565
|
Tree.new(java_tree)
|
|
559
566
|
end
|
|
560
567
|
|
|
@@ -568,6 +575,7 @@ module TreeHaver
|
|
|
568
575
|
# @return [Object, nil] unwrapped value or nil if empty
|
|
569
576
|
def unwrap_optional(value)
|
|
570
577
|
return value unless value.respond_to?(:isPresent)
|
|
578
|
+
|
|
571
579
|
value.isPresent ? value.get : nil
|
|
572
580
|
end
|
|
573
581
|
end
|
|
@@ -618,12 +626,14 @@ module TreeHaver
|
|
|
618
626
|
result = @impl.rootNode
|
|
619
627
|
# jtreesitter 0.26.0: rootNode() may return Optional<Node> or Node directly
|
|
620
628
|
java_node = if result.respond_to?(:isPresent)
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
629
|
+
raise TreeHaver::Error, 'Tree has no root node' unless result.isPresent
|
|
630
|
+
|
|
631
|
+
result.get
|
|
632
|
+
else
|
|
633
|
+
result
|
|
634
|
+
end
|
|
635
|
+
raise TreeHaver::Error, 'Tree has no root node' unless java_node
|
|
636
|
+
|
|
627
637
|
Node.new(java_node)
|
|
628
638
|
end
|
|
629
639
|
|
|
@@ -650,7 +660,7 @@ module TreeHaver
|
|
|
650
660
|
new_end_byte,
|
|
651
661
|
start_pt,
|
|
652
662
|
old_end_pt,
|
|
653
|
-
new_end_pt
|
|
663
|
+
new_end_pt
|
|
654
664
|
)
|
|
655
665
|
|
|
656
666
|
@impl.edit(input_edit)
|
|
@@ -723,6 +733,7 @@ module TreeHaver
|
|
|
723
733
|
# Handle Java Optional
|
|
724
734
|
if result.respond_to?(:isPresent)
|
|
725
735
|
return unless result.isPresent
|
|
736
|
+
|
|
726
737
|
java_node = result.get
|
|
727
738
|
else
|
|
728
739
|
# Direct Node return (some jtreesitter versions)
|
|
@@ -747,6 +758,7 @@ module TreeHaver
|
|
|
747
758
|
# Handle Java Optional
|
|
748
759
|
if result.respond_to?(:isPresent)
|
|
749
760
|
return unless result.isPresent
|
|
761
|
+
|
|
750
762
|
java_node = result.get
|
|
751
763
|
else
|
|
752
764
|
# Direct Node return (some jtreesitter versions)
|
|
@@ -762,6 +774,7 @@ module TreeHaver
|
|
|
762
774
|
# @return [void]
|
|
763
775
|
def each
|
|
764
776
|
return enum_for(:each) unless block_given?
|
|
777
|
+
|
|
765
778
|
child_count.times do |i|
|
|
766
779
|
yield child(i)
|
|
767
780
|
end
|
|
@@ -786,7 +799,7 @@ module TreeHaver
|
|
|
786
799
|
# @return [Hash] with :row and :column keys
|
|
787
800
|
def start_point
|
|
788
801
|
pt = @impl.startPoint
|
|
789
|
-
{row: pt.row, column: pt.column}
|
|
802
|
+
{ row: pt.row, column: pt.column }
|
|
790
803
|
end
|
|
791
804
|
|
|
792
805
|
# Get the end point (row, column)
|
|
@@ -794,7 +807,7 @@ module TreeHaver
|
|
|
794
807
|
# @return [Hash] with :row and :column keys
|
|
795
808
|
def end_point
|
|
796
809
|
pt = @impl.endPoint
|
|
797
|
-
{row: pt.row, column: pt.column}
|
|
810
|
+
{ row: pt.row, column: pt.column }
|
|
798
811
|
end
|
|
799
812
|
|
|
800
813
|
# Check if this node has an error
|
|
@@ -829,6 +842,7 @@ module TreeHaver
|
|
|
829
842
|
# Handle Java Optional
|
|
830
843
|
if result.respond_to?(:isPresent)
|
|
831
844
|
return unless result.isPresent
|
|
845
|
+
|
|
832
846
|
java_node = result.get
|
|
833
847
|
else
|
|
834
848
|
java_node = result
|
|
@@ -848,6 +862,7 @@ module TreeHaver
|
|
|
848
862
|
# Handle Java Optional
|
|
849
863
|
if result.respond_to?(:isPresent)
|
|
850
864
|
return unless result.isPresent
|
|
865
|
+
|
|
851
866
|
java_node = result.get
|
|
852
867
|
else
|
|
853
868
|
java_node = result
|
|
@@ -867,6 +882,7 @@ module TreeHaver
|
|
|
867
882
|
# Handle Java Optional
|
|
868
883
|
if result.respond_to?(:isPresent)
|
|
869
884
|
return unless result.isPresent
|
|
885
|
+
|
|
870
886
|
java_node = result.get
|
|
871
887
|
else
|
|
872
888
|
java_node = result
|
|
@@ -882,7 +898,7 @@ module TreeHaver
|
|
|
882
898
|
@impl.text.to_s
|
|
883
899
|
end
|
|
884
900
|
end
|
|
885
|
-
# :
|
|
901
|
+
# simplecov:enable
|
|
886
902
|
|
|
887
903
|
# Register the availability checker for RSpec dependency tags
|
|
888
904
|
TreeHaver::BackendRegistry.register_availability_checker(:java) do
|
|
@@ -57,29 +57,30 @@ module TreeHaver
|
|
|
57
57
|
# end
|
|
58
58
|
class << self
|
|
59
59
|
def available?
|
|
60
|
-
return @loaded if @load_attempted
|
|
61
|
-
|
|
60
|
+
return @loaded if @load_attempted
|
|
61
|
+
|
|
62
|
+
@load_attempted = true
|
|
62
63
|
begin
|
|
63
64
|
# ruby_tree_sitter is a C extension that only works on MRI
|
|
64
65
|
# It doesn't work on JRuby or TruffleRuby
|
|
65
|
-
if RUBY_ENGINE ==
|
|
66
|
-
require
|
|
67
|
-
@loaded = true
|
|
66
|
+
if RUBY_ENGINE == 'ruby'
|
|
67
|
+
require 'tree_sitter'
|
|
68
|
+
@loaded = true
|
|
68
69
|
else
|
|
69
|
-
# :
|
|
70
|
-
@loaded = false
|
|
71
|
-
# :
|
|
70
|
+
# simplecov:disable only runs on non-MRI engines (JRuby, TruffleRuby)
|
|
71
|
+
@loaded = false
|
|
72
|
+
# simplecov:enable
|
|
72
73
|
end
|
|
73
74
|
rescue LoadError
|
|
74
|
-
# :
|
|
75
|
-
@loaded = false
|
|
76
|
-
# :
|
|
75
|
+
# simplecov:disable only runs when ruby_tree_sitter gem is not installed
|
|
76
|
+
@loaded = false
|
|
77
|
+
# simplecov:enable
|
|
77
78
|
rescue StandardError
|
|
78
|
-
# :
|
|
79
|
-
@loaded = false
|
|
80
|
-
# :
|
|
79
|
+
# simplecov:disable defensive code - StandardError during require is extremely rare
|
|
80
|
+
@loaded = false
|
|
81
|
+
# simplecov:enable
|
|
81
82
|
end
|
|
82
|
-
@loaded
|
|
83
|
+
@loaded
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
# Reset the load state (primarily for testing)
|
|
@@ -87,8 +88,8 @@ module TreeHaver
|
|
|
87
88
|
# @return [void]
|
|
88
89
|
# @api private
|
|
89
90
|
def reset!
|
|
90
|
-
@load_attempted = false
|
|
91
|
-
@loaded = false
|
|
91
|
+
@load_attempted = false
|
|
92
|
+
@loaded = false
|
|
92
93
|
end
|
|
93
94
|
|
|
94
95
|
# Get capabilities supported by this backend
|
|
@@ -96,14 +97,16 @@ module TreeHaver
|
|
|
96
97
|
# @return [Hash{Symbol => Object}] capability map
|
|
97
98
|
# @example
|
|
98
99
|
# TreeHaver::Backends::MRI.capabilities
|
|
99
|
-
# # => { backend: :mri, query: true, bytes_field: true, incremental: true }
|
|
100
|
+
# # => { backend: :mri, query: true, bytes_field: true, incremental: true, comment_support: :nodes_only }
|
|
100
101
|
def capabilities
|
|
101
102
|
return {} unless available?
|
|
103
|
+
|
|
102
104
|
{
|
|
103
105
|
backend: :mri,
|
|
104
106
|
query: true,
|
|
105
107
|
bytes_field: true,
|
|
106
108
|
incremental: true,
|
|
109
|
+
comment_support: :nodes_only
|
|
107
110
|
}
|
|
108
111
|
end
|
|
109
112
|
end
|
|
@@ -150,7 +153,7 @@ module TreeHaver
|
|
|
150
153
|
def language_name
|
|
151
154
|
# Try to derive from symbol (e.g., "tree_sitter_toml" -> :toml)
|
|
152
155
|
if @symbol
|
|
153
|
-
name = @symbol.to_s.sub(/^tree_sitter_/,
|
|
156
|
+
name = @symbol.to_s.sub(/^tree_sitter_/, '')
|
|
154
157
|
return name.to_sym
|
|
155
158
|
end
|
|
156
159
|
|
|
@@ -164,7 +167,7 @@ module TreeHaver
|
|
|
164
167
|
end
|
|
165
168
|
|
|
166
169
|
# Alias for language_name (API compatibility)
|
|
167
|
-
|
|
170
|
+
alias name language_name
|
|
168
171
|
|
|
169
172
|
# Compare languages for equality
|
|
170
173
|
#
|
|
@@ -178,10 +181,10 @@ module TreeHaver
|
|
|
178
181
|
return unless other.backend == @backend
|
|
179
182
|
|
|
180
183
|
# Compare by path first, then symbol
|
|
181
|
-
cmp = (@path ||
|
|
184
|
+
cmp = (@path || '') <=> (other.path || '')
|
|
182
185
|
return cmp if cmp.nonzero?
|
|
183
186
|
|
|
184
|
-
(@symbol ||
|
|
187
|
+
(@symbol || '') <=> (other.symbol || '')
|
|
185
188
|
end
|
|
186
189
|
|
|
187
190
|
# Hash value for this language (for use in Sets/Hashes)
|
|
@@ -191,7 +194,7 @@ module TreeHaver
|
|
|
191
194
|
end
|
|
192
195
|
|
|
193
196
|
# Alias eql? to ==
|
|
194
|
-
|
|
197
|
+
alias eql? ==
|
|
195
198
|
|
|
196
199
|
# Convert to the underlying TreeSitter::Language for passing to parser
|
|
197
200
|
#
|
|
@@ -199,7 +202,7 @@ module TreeHaver
|
|
|
199
202
|
def to_language
|
|
200
203
|
@inner_language
|
|
201
204
|
end
|
|
202
|
-
|
|
205
|
+
alias to_ts_language to_language
|
|
203
206
|
|
|
204
207
|
# Load a language from a shared library (preferred method)
|
|
205
208
|
#
|
|
@@ -227,7 +230,7 @@ module TreeHaver
|
|
|
227
230
|
# @return [Language] wrapped language handle
|
|
228
231
|
# @api private
|
|
229
232
|
def from_path(path, symbol: nil, name: nil)
|
|
230
|
-
raise TreeHaver::NotAvailable,
|
|
233
|
+
raise TreeHaver::NotAvailable, 'ruby_tree_sitter not available' unless MRI.available?
|
|
231
234
|
|
|
232
235
|
# ruby_tree_sitter's TreeSitter::Language.load takes (language_name, path_to_so)
|
|
233
236
|
# where language_name is the language identifier (e.g., "toml", "json")
|
|
@@ -235,7 +238,7 @@ module TreeHaver
|
|
|
235
238
|
# and path_to_so is the full path to the .so file
|
|
236
239
|
#
|
|
237
240
|
# If name is not provided, derive it from symbol using shared utility
|
|
238
|
-
language_name = name || LibraryPathUtils.derive_language_name_from_symbol(symbol)
|
|
241
|
+
language_name = (name || LibraryPathUtils.derive_language_name_from_symbol(symbol)).to_s
|
|
239
242
|
ts_lang = ::TreeSitter::Language.load(language_name, path)
|
|
240
243
|
new(ts_lang, path: path, symbol: symbol)
|
|
241
244
|
rescue NameError => e
|
|
@@ -247,9 +250,9 @@ module TreeHaver
|
|
|
247
250
|
# at parse time when TreeSitter constant isn't loaded yet
|
|
248
251
|
if defined?(TreeSitter::TreeSitterError) && e.is_a?(TreeSitter::TreeSitterError)
|
|
249
252
|
raise TreeHaver::NotAvailable, "Could not load language: #{e.message}"
|
|
250
|
-
else
|
|
251
|
-
raise # Re-raise if it's not a TreeSitter error
|
|
252
253
|
end
|
|
254
|
+
|
|
255
|
+
raise # Re-raise if it's not a TreeSitter error
|
|
253
256
|
end
|
|
254
257
|
end
|
|
255
258
|
end
|
|
@@ -262,7 +265,8 @@ module TreeHaver
|
|
|
262
265
|
#
|
|
263
266
|
# @raise [TreeHaver::NotAvailable] if ruby_tree_sitter is not available
|
|
264
267
|
def initialize
|
|
265
|
-
raise TreeHaver::NotAvailable,
|
|
268
|
+
raise TreeHaver::NotAvailable, 'ruby_tree_sitter not available' unless MRI.available?
|
|
269
|
+
|
|
266
270
|
@parser = ::TreeSitter::Parser.new
|
|
267
271
|
rescue NameError => e
|
|
268
272
|
# TreeSitter constant doesn't exist - backend not loaded
|
|
@@ -273,9 +277,9 @@ module TreeHaver
|
|
|
273
277
|
# at parse time when TreeSitter constant isn't loaded yet
|
|
274
278
|
if defined?(TreeSitter::TreeSitterError) && e.is_a?(TreeSitter::TreeSitterError)
|
|
275
279
|
raise TreeHaver::NotAvailable, "Could not create parser: #{e.message}"
|
|
276
|
-
else
|
|
277
|
-
raise # Re-raise if it's not a TreeSitter error
|
|
278
280
|
end
|
|
281
|
+
|
|
282
|
+
raise # Re-raise if it's not a TreeSitter error
|
|
279
283
|
end
|
|
280
284
|
|
|
281
285
|
# Set the language for this parser
|
|
@@ -288,7 +292,7 @@ module TreeHaver
|
|
|
288
292
|
inner_lang = lang.respond_to?(:inner_language) ? lang.inner_language : lang
|
|
289
293
|
@parser.language = inner_lang
|
|
290
294
|
# Verify it was set
|
|
291
|
-
raise TreeHaver::NotAvailable,
|
|
295
|
+
raise TreeHaver::NotAvailable, 'Language not set correctly' if @parser.language.nil?
|
|
292
296
|
|
|
293
297
|
# Return the original language object (wrapped or unwrapped)
|
|
294
298
|
lang
|
|
@@ -298,9 +302,9 @@ module TreeHaver
|
|
|
298
302
|
# at parse time when TreeSitter constant isn't loaded yet
|
|
299
303
|
if defined?(TreeSitter::TreeSitterError) && e.is_a?(TreeSitter::TreeSitterError)
|
|
300
304
|
raise TreeHaver::NotAvailable, "Could not set language: #{e.message}"
|
|
301
|
-
else
|
|
302
|
-
raise # Re-raise if it's not a TreeSitter error
|
|
303
305
|
end
|
|
306
|
+
|
|
307
|
+
raise # Re-raise if it's not a TreeSitter error
|
|
304
308
|
end
|
|
305
309
|
|
|
306
310
|
# Parse source code
|
|
@@ -315,7 +319,8 @@ module TreeHaver
|
|
|
315
319
|
# Pass nil for old_tree (initial parse)
|
|
316
320
|
# Return raw tree - TreeHaver::Parser will wrap it
|
|
317
321
|
tree = @parser.parse_string(nil, source)
|
|
318
|
-
raise TreeHaver::NotAvailable,
|
|
322
|
+
raise TreeHaver::NotAvailable, 'Parse returned nil - is language set?' if tree.nil?
|
|
323
|
+
|
|
319
324
|
tree
|
|
320
325
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
321
326
|
# TreeSitter errors inherit from Exception (not StandardError) in ruby_tree_sitter v2+
|
|
@@ -323,9 +328,9 @@ module TreeHaver
|
|
|
323
328
|
# at parse time when TreeSitter constant isn't loaded yet
|
|
324
329
|
if defined?(TreeSitter::TreeSitterError) && e.is_a?(TreeSitter::TreeSitterError)
|
|
325
330
|
raise TreeHaver::NotAvailable, "Could not parse source: #{e.message}"
|
|
326
|
-
else
|
|
327
|
-
raise # Re-raise if it's not a TreeSitter error
|
|
328
331
|
end
|
|
332
|
+
|
|
333
|
+
raise # Re-raise if it's not a TreeSitter error
|
|
329
334
|
end
|
|
330
335
|
|
|
331
336
|
# Parse source code with optional incremental parsing
|
|
@@ -347,9 +352,9 @@ module TreeHaver
|
|
|
347
352
|
# at parse time when TreeSitter constant isn't loaded yet
|
|
348
353
|
if defined?(TreeSitter::TreeSitterError) && e.is_a?(TreeSitter::TreeSitterError)
|
|
349
354
|
raise TreeHaver::NotAvailable, "Could not parse source: #{e.message}"
|
|
350
|
-
else
|
|
351
|
-
raise # Re-raise if it's not a TreeSitter error
|
|
352
355
|
end
|
|
356
|
+
|
|
357
|
+
raise # Re-raise if it's not a TreeSitter error
|
|
353
358
|
end
|
|
354
359
|
end
|
|
355
360
|
|