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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.md +13 -0
  4. data/README.md +299 -660
  5. data/lib/tree_haver/backend_api.rb +78 -35
  6. data/lib/tree_haver/backend_context.rb +28 -0
  7. data/lib/tree_haver/backend_registry.rb +119 -382
  8. data/lib/tree_haver/backends/citrus.rb +55 -53
  9. data/lib/tree_haver/backends/ffi.rb +105 -101
  10. data/lib/tree_haver/backends/java.rb +92 -76
  11. data/lib/tree_haver/backends/mri.rb +44 -39
  12. data/lib/tree_haver/backends/parslet.rb +53 -48
  13. data/lib/tree_haver/backends/prism.rb +140 -43
  14. data/lib/tree_haver/backends/psych.rb +30 -26
  15. data/lib/tree_haver/backends/rust.rb +21 -17
  16. data/lib/tree_haver/backends/tslp.rb +274 -0
  17. data/lib/tree_haver/base/comment.rb +320 -0
  18. data/lib/tree_haver/base/language.rb +2 -2
  19. data/lib/tree_haver/base/node.rb +39 -31
  20. data/lib/tree_haver/base/parser.rb +4 -0
  21. data/lib/tree_haver/base/point.rb +3 -3
  22. data/lib/tree_haver/base/tree.rb +1 -1
  23. data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
  24. data/lib/tree_haver/contracts.rb +1025 -0
  25. data/lib/tree_haver/grammar_finder.rb +125 -70
  26. data/lib/tree_haver/kaitai_backend.rb +30 -0
  27. data/lib/tree_haver/language.rb +36 -37
  28. data/lib/tree_haver/language_pack.rb +41 -0
  29. data/lib/tree_haver/language_registry.rb +34 -3
  30. data/lib/tree_haver/library_path_utils.rb +7 -7
  31. data/lib/tree_haver/node.rb +40 -31
  32. data/lib/tree_haver/parser.rb +44 -37
  33. data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
  34. data/lib/tree_haver/path_validator.rb +39 -36
  35. data/lib/tree_haver/peg_backends.rb +76 -0
  36. data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
  37. data/lib/tree_haver/rspec.rb +1 -31
  38. data/lib/tree_haver/tree.rb +16 -7
  39. data/lib/tree_haver/version.rb +5 -14
  40. data/lib/tree_haver.rb +536 -1240
  41. data/sig/tree_haver.rbs +1 -229
  42. data.tar.gz.sig +0 -0
  43. metadata +154 -70
  44. metadata.gz.sig +0 -0
  45. data/CHANGELOG.md +0 -1393
  46. data/CITATION.cff +0 -20
  47. data/CODE_OF_CONDUCT.md +0 -134
  48. data/CONTRIBUTING.md +0 -359
  49. data/FUNDING.md +0 -74
  50. data/LICENSE.txt +0 -21
  51. data/REEK +0 -0
  52. data/RUBOCOP.md +0 -71
  53. data/SECURITY.md +0 -21
  54. data/lib/tree_haver/base.rb +0 -12
  55. data/lib/tree_haver/compat.rb +0 -43
  56. data/lib/tree_haver/rspec/testable_node.rb +0 -217
  57. data/sig/tree_haver/backends.rbs +0 -352
  58. data/sig/tree_haver/grammar_finder.rbs +0 -29
  59. 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 = "io.github.treesitter.jtreesitter"
73
+ JAVA_PACKAGE = 'io.github.treesitter.jtreesitter'
74
74
 
75
75
  @load_attempted = false
76
76
  @loaded = false
77
- @java_classes = {} # rubocop:disable ThreadSafety/MutableClassInstanceVariable
78
- @runtime_lookup = nil # Cached SymbolLookup for libtree-sitter.so
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
- # :nocov:
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 "java"
110
+ require 'java'
111
111
 
112
112
  # Add JARs to classpath
113
- dir = ENV["TREE_SITTER_JAVA_JARS_DIR"]
113
+ dir = ENV['TREE_SITTER_JAVA_JARS_DIR']
114
114
  if dir && Dir.exist?(dir)
115
- Dir[File.join(dir, "**", "*.jar")].each do |jar|
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
- # :nocov:
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
- # :nocov:
135
+ # simplecov:disable
135
136
  # This method requires JRuby and cannot be tested on MRI/CRuby.
136
- lib_path = ENV["TREE_SITTER_RUNTIME_LIB"]
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("java.library.path") || ""
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("java.library.path", new_path)
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("jna.library.path", new_path)
148
+ java.lang.System.setProperty('jna.library.path', new_path)
148
149
  end
149
- # :nocov:
150
- rescue => _error
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 # rubocop:disable ThreadSafety/ClassInstanceVariable
169
- @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
170
- @loaded = check_availability # rubocop:disable ThreadSafety/ClassInstanceVariable
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 # rubocop:disable ThreadSafety/ClassInstanceVariable
179
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
180
- @load_error = nil # rubocop:disable ThreadSafety/ClassInstanceVariable
181
- @loader = nil # rubocop:disable ThreadSafety/ClassInstanceVariable
182
- @java_classes = {} # rubocop:disable ThreadSafety/ClassInstanceVariable
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 == "jruby"
191
+ return false unless RUBY_ENGINE == 'jruby'
190
192
 
191
193
  # 2. Check for required JARs via environment variable
192
- jars_dir = ENV["TREE_SITTER_JAVA_JARS_DIR"]
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
- # :nocov:
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
- # :nocov:
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
- # :nocov:
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 || "") <=> (other.path || "")
297
+ cmp = (@path || '') <=> (other.path || '')
294
298
  return cmp if cmp.nonzero?
295
299
 
296
- (@symbol || "") <=> (other.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
- alias_method :eql?, :==
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, "Java backend not available" unless Java.available?
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("libtree-sitter.so", arena)
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?("ts_language_version")
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
- "tree-sitter < 0.24 (uses ts_language_version), but your runtime library " \
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
- "3. Find a pre-built grammar compatible with tree-sitter 0.24+"
390
- elsif unresolved.include?("ts_language") || unresolved.include?("ts_parser")
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
- "Ensure libtree-sitter.so is in LD_LIBRARY_PATH and version-compatible " \
393
- "with the grammar."
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
- "Native library error loading #{path}: #{e.message}. " \
400
- "Ensure the library is in LD_LIBRARY_PATH."
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
- "Could not find library '#{path}': #{e.message}. " \
404
- "Ensure it's in LD_LIBRARY_PATH or provide an absolute path."
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, "Java backend not available" unless Java.available?
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("libtree-sitter.so", arena)
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
- "Failed to load language '#{name}': Library not found. " \
455
- "Ensure the grammar library (e.g., libtree-sitter-#{name}.so) " \
456
- "is in LD_LIBRARY_PATH."
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
- "Failed to load language '#{name}': #{e.message}. " \
466
- "Ensure the grammar library (e.g., libtree-sitter-#{name}.so) " \
467
- "is in LD_LIBRARY_PATH."
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
- alias_method :from_path, :from_library
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, "Java backend not available" unless Java.available?
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, "Parser returned no tree" unless java_tree
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
- old_tree.impl
542
- else
543
- unwrap_optional(old_tree)
544
- end
547
+ old_tree.impl
548
+ else
549
+ unwrap_optional(old_tree)
550
+ end
545
551
 
546
552
  java_result = if java_old_tree
547
- # jtreesitter 0.26.0 API: parse(String source, Tree oldTree)
548
- @parser.parse(source, java_old_tree)
549
- else
550
- @parser.parse(source)
551
- end
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, "Parser returned no tree" unless java_tree
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
- raise TreeHaver::Error, "Tree has no root node" unless result.isPresent
622
- result.get
623
- else
624
- result
625
- end
626
- raise TreeHaver::Error, "Tree has no root node" unless java_node
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
- # :nocov:
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 # rubocop:disable ThreadSafety/ClassInstanceVariable
61
- @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
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 == "ruby"
66
- require "tree_sitter"
67
- @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
66
+ if RUBY_ENGINE == 'ruby'
67
+ require 'tree_sitter'
68
+ @loaded = true
68
69
  else
69
- # :nocov: only runs on non-MRI engines (JRuby, TruffleRuby)
70
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
71
- # :nocov:
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
- # :nocov: only runs when ruby_tree_sitter gem is not installed
75
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
76
- # :nocov:
75
+ # simplecov:disable only runs when ruby_tree_sitter gem is not installed
76
+ @loaded = false
77
+ # simplecov:enable
77
78
  rescue StandardError
78
- # :nocov: defensive code - StandardError during require is extremely rare
79
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
80
- # :nocov:
79
+ # simplecov:disable defensive code - StandardError during require is extremely rare
80
+ @loaded = false
81
+ # simplecov:enable
81
82
  end
82
- @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
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 # rubocop:disable ThreadSafety/ClassInstanceVariable
91
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
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
- alias_method :name, :language_name
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 || "") <=> (other.path || "")
184
+ cmp = (@path || '') <=> (other.path || '')
182
185
  return cmp if cmp.nonzero?
183
186
 
184
- (@symbol || "") <=> (other.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
- alias_method :eql?, :==
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
- alias_method :to_ts_language, :to_language
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, "ruby_tree_sitter not available" unless MRI.available?
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, "ruby_tree_sitter not available" unless MRI.available?
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, "Language not set correctly" if @parser.language.nil?
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, "Parse returned nil - is language set?" if tree.nil?
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