tree_haver 3.2.6 → 4.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.
@@ -15,6 +15,23 @@ module TreeHaver
15
15
  # - The Query API for pattern matching
16
16
  # - Tree editing for incremental re-parsing
17
17
  #
18
+ # == Tree/Node Architecture
19
+ #
20
+ # This backend (like all tree-sitter backends: MRI, Rust, FFI, Java) does NOT
21
+ # define its own Tree or Node classes at the Ruby level. Instead:
22
+ #
23
+ # - Parser#parse returns raw Java tree objects (via JRuby interop)
24
+ # - These are wrapped by `TreeHaver::Tree` (inherits from `Base::Tree`)
25
+ # - `TreeHaver::Tree#root_node` wraps raw nodes in `TreeHaver::Node`
26
+ #
27
+ # This differs from pure-Ruby backends (Citrus, Prism, Psych) which define
28
+ # their own `Backend::X::Tree` and `Backend::X::Node` classes.
29
+ #
30
+ # @see TreeHaver::Tree The wrapper class for tree-sitter Tree objects
31
+ # @see TreeHaver::Node The wrapper class for tree-sitter Node objects
32
+ # @see TreeHaver::Base::Tree Base class documenting the Tree API contract
33
+ # @see TreeHaver::Base::Node Base class documenting the Node API contract
34
+ #
18
35
  # == Version Requirements
19
36
  #
20
37
  # - jtreesitter >= 0.26.0 (required)
@@ -127,66 +144,53 @@ module TreeHaver
127
144
 
128
145
  # Check if the Java backend is available
129
146
  #
130
- # Returns true if running on JRuby and java-tree-sitter classes can be loaded.
131
- # Automatically attempts to load JARs from ENV["TREE_SITTER_JAVA_JARS_DIR"] if set.
147
+ # Checks if:
148
+ # 1. We're running on JRuby
149
+ # 2. Environment variable TREE_SITTER_JAVA_JARS_DIR is set
150
+ # 3. Required JARs (jtreesitter, tree-sitter) are present in that directory
132
151
  #
133
152
  # @return [Boolean] true if Java backend is available
134
153
  # @example
135
154
  # if TreeHaver::Backends::Java.available?
136
- # puts "Java backend is ready"
155
+ # puts "Java backend ready"
137
156
  # end
138
- def available?
139
- return @loaded if @load_attempted
140
- @load_attempted = true
141
- @loaded = false
142
- @load_error = nil
157
+ class << self
158
+ def available?
159
+ return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
160
+ @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
161
+ @loaded = check_availability # rubocop:disable ThreadSafety/ClassInstanceVariable
162
+ end
143
163
 
144
- return false unless defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
164
+ # Reset the load state (primarily for testing)
165
+ #
166
+ # @return [void]
167
+ # @api private
168
+ def reset!
169
+ @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
170
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
171
+ @load_error = nil # rubocop:disable ThreadSafety/ClassInstanceVariable
172
+ @loader = nil # rubocop:disable ThreadSafety/ClassInstanceVariable
173
+ @java_classes = {} # rubocop:disable ThreadSafety/ClassInstanceVariable
174
+ end
145
175
 
146
- # :nocov:
147
- # Everything below requires JRuby and cannot be tested on MRI/CRuby.
148
- # JRuby-specific CI jobs would test this code.
149
- begin
150
- require "java"
151
- rescue LoadError
152
- @load_error = "JRuby java bridge not available"
153
- return false
154
- end
155
-
156
- # Optionally augment classpath and configure native library path
157
- add_jars_from_env!
158
-
159
- # Try to load the java-tree-sitter classes
160
- # Load Parser first as it doesn't trigger native library loading
161
- # Language class triggers native lib loading in its static initializer
162
- begin
163
- # These classes don't require native library initialization
164
- @java_classes[:Parser] = ::Java::IoGithubTreesitterJtreesitter::Parser
165
- @java_classes[:Tree] = ::Java::IoGithubTreesitterJtreesitter::Tree
166
- @java_classes[:Node] = ::Java::IoGithubTreesitterJtreesitter::Node
167
- @java_classes[:InputEdit] = ::Java::IoGithubTreesitterJtreesitter::InputEdit
168
- @java_classes[:Point] = ::Java::IoGithubTreesitterJtreesitter::Point
169
-
170
- # Language class may fail if native library isn't found - try it last
171
- # and provide a helpful error message
176
+ private
177
+
178
+ def check_availability
179
+ # 1. Check Ruby engine
180
+ return false unless RUBY_ENGINE == "jruby"
181
+
182
+ # 2. Check for required JARs via environment variable
183
+ jars_dir = ENV["TREE_SITTER_JAVA_JARS_DIR"]
184
+ return false unless jars_dir && Dir.exist?(jars_dir)
185
+
186
+ # 3. Check if we can load the classes
172
187
  begin
173
- @java_classes[:Language] = ::Java::IoGithubTreesitterJtreesitter::Language
174
- rescue NameError => e
175
- # Language failed but other classes loaded - native lib issue
176
- @load_error = "Language class failed to initialize (native library issue): #{e.message}"
177
- # Clear loaded classes since we can't fully function without Language
178
- @java_classes.clear
179
- return false
188
+ ensure_loader_initialized!
189
+ true
190
+ rescue LoadError, NameError
191
+ false
180
192
  end
181
-
182
- @loaded = true
183
- rescue NameError => e
184
- @load_error = "java-tree-sitter classes not found: #{e.message}"
185
- @loaded = false
186
193
  end
187
-
188
- @loaded
189
- # :nocov:
190
194
  end
191
195
 
192
196
  # Get the last load error message (for debugging)
@@ -196,17 +200,6 @@ module TreeHaver
196
200
  @load_error
197
201
  end
198
202
 
199
- # Reset the load state (primarily for testing)
200
- #
201
- # @return [void]
202
- # @api private
203
- def reset!
204
- @load_attempted = false
205
- @loaded = false
206
- @load_error = nil
207
- @java_classes = {}
208
- end
209
-
210
203
  # Get the loaded Java classes
211
204
  #
212
205
  # @return [Hash] the Java class references
@@ -805,6 +798,11 @@ module TreeHaver
805
798
  end
806
799
  end
807
800
  # :nocov:
801
+
802
+ # Register availability checker for RSpec dependency tags
803
+ TreeHaver::BackendRegistry.register_availability_checker(:java) do
804
+ available?
805
+ end
808
806
  end
809
807
  end
810
808
  end
@@ -8,6 +8,23 @@ module TreeHaver
8
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
+ # == Tree/Node Architecture
12
+ #
13
+ # This backend (like all tree-sitter backends: MRI, Rust, FFI, Java) does NOT
14
+ # define its own Tree or Node classes. Instead:
15
+ #
16
+ # - Parser#parse returns raw `::TreeSitter::Tree` objects
17
+ # - These are wrapped by `TreeHaver::Tree` (inherits from `Base::Tree`)
18
+ # - `TreeHaver::Tree#root_node` wraps raw nodes in `TreeHaver::Node`
19
+ #
20
+ # This differs from pure-Ruby backends (Citrus, Prism, Psych) which define
21
+ # their own `Backend::X::Tree` and `Backend::X::Node` classes.
22
+ #
23
+ # @see TreeHaver::Tree The wrapper class for tree-sitter Tree objects
24
+ # @see TreeHaver::Node The wrapper class for tree-sitter Node objects
25
+ # @see TreeHaver::Base::Tree Base class documenting the Tree API contract
26
+ # @see TreeHaver::Base::Node Base class documenting the Node API contract
27
+ #
11
28
  # == Platform Compatibility
12
29
  #
13
30
  # - MRI Ruby: ✓ Full support (fastest tree-sitter backend on MRI)
@@ -42,20 +59,25 @@ module TreeHaver
42
59
  def available?
43
60
  return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
44
61
  @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
45
-
46
- # ruby_tree_sitter is a C extension that only works on MRI
47
- # It doesn't work on JRuby or TruffleRuby
48
- unless RUBY_ENGINE == "ruby"
49
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
50
- return @loaded
51
- end
52
-
53
62
  begin
54
- require "tree_sitter" # Note: gem is ruby_tree_sitter but requires tree_sitter
55
-
56
- @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
63
+ # ruby_tree_sitter is a C extension that only works on MRI
64
+ # It doesn't work on JRuby or TruffleRuby
65
+ if RUBY_ENGINE == "ruby"
66
+ require "tree_sitter"
67
+ @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
68
+ else
69
+ # :nocov: only runs on non-MRI engines (JRuby, TruffleRuby)
70
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
71
+ # :nocov:
72
+ end
57
73
  rescue LoadError
74
+ # :nocov: only runs when ruby_tree_sitter gem is not installed
58
75
  @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
76
+ # :nocov:
77
+ rescue StandardError
78
+ # :nocov: defensive code - StandardError during require is extremely rare
79
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
80
+ # :nocov:
59
81
  end
60
82
  @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
61
83
  end
@@ -306,6 +328,11 @@ module TreeHaver
306
328
  end
307
329
  end
308
330
  end
331
+
332
+ # Register availability checker for RSpec dependency tags
333
+ TreeHaver::BackendRegistry.register_availability_checker(:mri) do
334
+ available?
335
+ end
309
336
  end
310
337
  end
311
338
  end