tree_haver 5.0.4 → 7.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/tree_haver/backend_context.rb +28 -0
- data/lib/tree_haver/backend_registry.rb +19 -432
- data/lib/tree_haver/contracts.rb +460 -0
- data/lib/tree_haver/kaitai_backend.rb +30 -0
- data/lib/tree_haver/language_pack.rb +190 -0
- data/lib/tree_haver/peg_backends.rb +76 -0
- data/lib/tree_haver/version.rb +1 -12
- data/lib/tree_haver.rb +7 -1316
- data.tar.gz.sig +0 -0
- metadata +34 -245
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -1366
- 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/README.md +0 -2347
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
- data/lib/tree_haver/backend_api.rb +0 -349
- data/lib/tree_haver/backends/citrus.rb +0 -487
- data/lib/tree_haver/backends/ffi.rb +0 -1009
- data/lib/tree_haver/backends/java.rb +0 -893
- data/lib/tree_haver/backends/mri.rb +0 -362
- data/lib/tree_haver/backends/parslet.rb +0 -560
- data/lib/tree_haver/backends/prism.rb +0 -471
- data/lib/tree_haver/backends/psych.rb +0 -375
- data/lib/tree_haver/backends/rust.rb +0 -239
- data/lib/tree_haver/base/language.rb +0 -98
- data/lib/tree_haver/base/node.rb +0 -322
- data/lib/tree_haver/base/parser.rb +0 -24
- data/lib/tree_haver/base/point.rb +0 -48
- data/lib/tree_haver/base/tree.rb +0 -128
- data/lib/tree_haver/base.rb +0 -12
- data/lib/tree_haver/citrus_grammar_finder.rb +0 -218
- data/lib/tree_haver/compat.rb +0 -43
- data/lib/tree_haver/grammar_finder.rb +0 -374
- data/lib/tree_haver/language.rb +0 -295
- data/lib/tree_haver/language_registry.rb +0 -190
- data/lib/tree_haver/library_path_utils.rb +0 -80
- data/lib/tree_haver/node.rb +0 -579
- data/lib/tree_haver/parser.rb +0 -438
- data/lib/tree_haver/parslet_grammar_finder.rb +0 -224
- data/lib/tree_haver/path_validator.rb +0 -353
- data/lib/tree_haver/point.rb +0 -27
- data/lib/tree_haver/rspec/dependency_tags.rb +0 -1392
- data/lib/tree_haver/rspec/testable_node.rb +0 -217
- data/lib/tree_haver/rspec.rb +0 -33
- data/lib/tree_haver/tree.rb +0 -258
- 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
- data/sig/tree_haver.rbs +0 -234
|
@@ -1,893 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module TreeHaver
|
|
4
|
-
module Backends
|
|
5
|
-
# Java backend for JRuby using jtreesitter (java-tree-sitter)
|
|
6
|
-
#
|
|
7
|
-
# This backend integrates with jtreesitter JARs on JRuby,
|
|
8
|
-
# leveraging JRuby's native Java integration for optimal performance.
|
|
9
|
-
#
|
|
10
|
-
# == Features
|
|
11
|
-
#
|
|
12
|
-
# jtreesitter (java-tree-sitter) provides Java bindings to tree-sitter and supports:
|
|
13
|
-
# - Parsing source code into syntax trees
|
|
14
|
-
# - Incremental parsing via Parser.parse(Tree, String)
|
|
15
|
-
# - The Query API for pattern matching
|
|
16
|
-
# - Tree editing for incremental re-parsing
|
|
17
|
-
#
|
|
18
|
-
# == Tree/Node Architecture
|
|
19
|
-
#
|
|
20
|
-
# This backend defines Ruby wrapper classes (`Java::Language`, `Java::Parser`,
|
|
21
|
-
# `Java::Tree`, `Java::Node`) that wrap the raw jtreesitter Java objects via
|
|
22
|
-
# JRuby's Java interop. These are **raw backend wrappers** not intended for
|
|
23
|
-
# direct use by application code.
|
|
24
|
-
#
|
|
25
|
-
# The wrapping hierarchy is:
|
|
26
|
-
# Java::Tree/Node (this backend) → TreeHaver::Tree/Node → Base::Tree/Node
|
|
27
|
-
#
|
|
28
|
-
# When you use `TreeHaver::Parser#parse`:
|
|
29
|
-
# 1. `Java::Parser#parse` returns a `Java::Tree` (wrapper around jtreesitter Tree)
|
|
30
|
-
# 2. `TreeHaver::Parser` wraps it in `TreeHaver::Tree` (adds source storage)
|
|
31
|
-
# 3. `TreeHaver::Tree#root_node` wraps `Java::Node` in `TreeHaver::Node`
|
|
32
|
-
#
|
|
33
|
-
# The `TreeHaver::Tree` and `TreeHaver::Node` wrappers provide the full unified
|
|
34
|
-
# API including `#children`, `#text`, `#source`, `#source_position`, etc.
|
|
35
|
-
#
|
|
36
|
-
# This differs from pure-Ruby backends (Citrus, Parslet, Prism, Psych) which
|
|
37
|
-
# define Tree/Node classes that directly inherit from Base::Tree/Base::Node.
|
|
38
|
-
#
|
|
39
|
-
# @see TreeHaver::Tree The wrapper class users should interact with
|
|
40
|
-
# @see TreeHaver::Node The wrapper class users should interact with
|
|
41
|
-
# @see TreeHaver::Base::Tree Base class documenting the Tree API contract
|
|
42
|
-
# @see TreeHaver::Base::Node Base class documenting the Node API contract
|
|
43
|
-
#
|
|
44
|
-
# == Version Requirements
|
|
45
|
-
#
|
|
46
|
-
# - jtreesitter >= 0.26.0 (required)
|
|
47
|
-
# - tree-sitter runtime library >= 0.26.0 (must match jtreesitter version)
|
|
48
|
-
#
|
|
49
|
-
# Older versions of jtreesitter are NOT supported due to API changes.
|
|
50
|
-
#
|
|
51
|
-
# == Platform Compatibility
|
|
52
|
-
#
|
|
53
|
-
# - MRI Ruby: ✗ Not available (no JVM)
|
|
54
|
-
# - JRuby: ✓ Full support (native Java integration)
|
|
55
|
-
# - TruffleRuby: ✗ Not available (jtreesitter requires JRuby's Java interop)
|
|
56
|
-
#
|
|
57
|
-
# == Installation
|
|
58
|
-
#
|
|
59
|
-
# 1. Download jtreesitter 0.26.0+ JAR from Maven Central:
|
|
60
|
-
# https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter
|
|
61
|
-
#
|
|
62
|
-
# 2. Set the environment variable to point to the JAR directory:
|
|
63
|
-
# export TREE_SITTER_JAVA_JARS_DIR=/path/to/jars
|
|
64
|
-
#
|
|
65
|
-
# 3. Use JRuby to run your code:
|
|
66
|
-
# jruby -e "require 'tree_haver'; puts TreeHaver::Backends::Java.available?"
|
|
67
|
-
#
|
|
68
|
-
# @see https://github.com/tree-sitter/java-tree-sitter source
|
|
69
|
-
# @see https://tree-sitter.github.io/java-tree-sitter jtreesitter documentation
|
|
70
|
-
# @see https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter Maven Central
|
|
71
|
-
module Java
|
|
72
|
-
# The Java package for java-tree-sitter
|
|
73
|
-
JAVA_PACKAGE = "io.github.treesitter.jtreesitter"
|
|
74
|
-
|
|
75
|
-
@load_attempted = false
|
|
76
|
-
@loaded = false
|
|
77
|
-
@java_classes = {} # rubocop:disable ThreadSafety/MutableClassInstanceVariable
|
|
78
|
-
@runtime_lookup = nil # Cached SymbolLookup for libtree-sitter.so
|
|
79
|
-
|
|
80
|
-
module_function
|
|
81
|
-
|
|
82
|
-
# Get the cached runtime library SymbolLookup
|
|
83
|
-
# @return [Object, nil] the SymbolLookup for libtree-sitter.so
|
|
84
|
-
# @api private
|
|
85
|
-
def runtime_lookup
|
|
86
|
-
@runtime_lookup
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Set the cached runtime library SymbolLookup
|
|
90
|
-
# @param lookup [Object] the SymbolLookup
|
|
91
|
-
# @api private
|
|
92
|
-
def runtime_lookup=(lookup)
|
|
93
|
-
@runtime_lookup = lookup
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Attempt to append JARs from TREE_SITTER_JAVA_JARS_DIR to JRuby classpath
|
|
97
|
-
# and configure native library path from TREE_SITTER_RUNTIME_LIB
|
|
98
|
-
#
|
|
99
|
-
# If the environment variable is set and points to a directory, all .jar files
|
|
100
|
-
# in that directory (recursively) are added to the JRuby classpath.
|
|
101
|
-
#
|
|
102
|
-
# @return [void]
|
|
103
|
-
# @example
|
|
104
|
-
# ENV["TREE_SITTER_JAVA_JARS_DIR"] = "/path/to/java-tree-sitter/jars"
|
|
105
|
-
# TreeHaver::Backends::Java.add_jars_from_env!
|
|
106
|
-
def add_jars_from_env!
|
|
107
|
-
# :nocov:
|
|
108
|
-
# This method requires JRuby and cannot be tested on MRI/CRuby.
|
|
109
|
-
# JRuby-specific CI jobs would test this code.
|
|
110
|
-
require "java"
|
|
111
|
-
|
|
112
|
-
# Add JARs to classpath
|
|
113
|
-
dir = ENV["TREE_SITTER_JAVA_JARS_DIR"]
|
|
114
|
-
if dir && Dir.exist?(dir)
|
|
115
|
-
Dir[File.join(dir, "**", "*.jar")].each do |jar|
|
|
116
|
-
next if $CLASSPATH.include?(jar)
|
|
117
|
-
$CLASSPATH << jar
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Configure native library path for libtree-sitter
|
|
122
|
-
# java-tree-sitter uses JNI and needs to find the native library
|
|
123
|
-
configure_native_library_path!
|
|
124
|
-
# :nocov:
|
|
125
|
-
rescue LoadError
|
|
126
|
-
# ignore; not JRuby or Java bridge not available
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Configure java.library.path to include the directory containing libtree-sitter
|
|
130
|
-
#
|
|
131
|
-
# @return [void]
|
|
132
|
-
# @api private
|
|
133
|
-
def configure_native_library_path!
|
|
134
|
-
# :nocov:
|
|
135
|
-
# This method requires JRuby and cannot be tested on MRI/CRuby.
|
|
136
|
-
lib_path = ENV["TREE_SITTER_RUNTIME_LIB"]
|
|
137
|
-
return unless lib_path && File.exist?(lib_path)
|
|
138
|
-
|
|
139
|
-
lib_dir = File.dirname(lib_path)
|
|
140
|
-
current_path = java.lang.System.getProperty("java.library.path") || ""
|
|
141
|
-
|
|
142
|
-
unless current_path.include?(lib_dir)
|
|
143
|
-
new_path = current_path.empty? ? lib_dir : "#{lib_dir}:#{current_path}"
|
|
144
|
-
java.lang.System.setProperty("java.library.path", new_path)
|
|
145
|
-
|
|
146
|
-
# Also set jna.library.path in case it uses JNA
|
|
147
|
-
java.lang.System.setProperty("jna.library.path", new_path)
|
|
148
|
-
end
|
|
149
|
-
# :nocov:
|
|
150
|
-
rescue => _error
|
|
151
|
-
# Ignore errors setting library path
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# Check if the Java backend is available
|
|
155
|
-
#
|
|
156
|
-
# Checks if:
|
|
157
|
-
# 1. We're running on JRuby
|
|
158
|
-
# 2. Environment variable TREE_SITTER_JAVA_JARS_DIR is set
|
|
159
|
-
# 3. Required JARs (jtreesitter, tree-sitter) are present in that directory
|
|
160
|
-
#
|
|
161
|
-
# @return [Boolean] true if Java backend is available
|
|
162
|
-
# @example
|
|
163
|
-
# if TreeHaver::Backends::Java.available?
|
|
164
|
-
# puts "Java backend ready"
|
|
165
|
-
# end
|
|
166
|
-
class << self
|
|
167
|
-
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
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Reset the load state (primarily for testing)
|
|
174
|
-
#
|
|
175
|
-
# @return [void]
|
|
176
|
-
# @api private
|
|
177
|
-
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
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
private
|
|
186
|
-
|
|
187
|
-
def check_availability
|
|
188
|
-
# 1. Check Ruby engine
|
|
189
|
-
return false unless RUBY_ENGINE == "jruby"
|
|
190
|
-
|
|
191
|
-
# 2. Check for required JARs via environment variable
|
|
192
|
-
jars_dir = ENV["TREE_SITTER_JAVA_JARS_DIR"]
|
|
193
|
-
return false unless jars_dir && Dir.exist?(jars_dir)
|
|
194
|
-
|
|
195
|
-
# 3. Check if we can load the classes
|
|
196
|
-
begin
|
|
197
|
-
ensure_loader_initialized!
|
|
198
|
-
true
|
|
199
|
-
rescue LoadError, NameError
|
|
200
|
-
false
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
# Get the last load error message (for debugging)
|
|
206
|
-
#
|
|
207
|
-
# @return [String, nil] the error message or nil if no error
|
|
208
|
-
def load_error
|
|
209
|
-
@load_error
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# Get the loaded Java classes
|
|
213
|
-
#
|
|
214
|
-
# @return [Hash] the Java class references
|
|
215
|
-
# @api private
|
|
216
|
-
def java_classes
|
|
217
|
-
@java_classes
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Get capabilities supported by this backend
|
|
221
|
-
#
|
|
222
|
-
# @return [Hash{Symbol => Object}] capability map
|
|
223
|
-
# @example
|
|
224
|
-
# TreeHaver::Backends::Java.capabilities
|
|
225
|
-
# # => { backend: :java, parse: true, query: true, bytes_field: true, incremental: true }
|
|
226
|
-
def capabilities
|
|
227
|
-
# :nocov:
|
|
228
|
-
# This method returns meaningful data only on JRuby when java-tree-sitter is available.
|
|
229
|
-
return {} unless available?
|
|
230
|
-
{
|
|
231
|
-
backend: :java,
|
|
232
|
-
parse: true,
|
|
233
|
-
query: true, # java-tree-sitter supports the Query API
|
|
234
|
-
bytes_field: true,
|
|
235
|
-
incremental: true, # java-tree-sitter supports Parser.parse(Tree, String)
|
|
236
|
-
}
|
|
237
|
-
# :nocov:
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Java backend language wrapper (raw backend language)
|
|
241
|
-
#
|
|
242
|
-
# This is a **raw backend language** that wraps a jtreesitter Language object
|
|
243
|
-
# via JRuby's Java interop. It is used to configure the parser for a specific
|
|
244
|
-
# grammar (e.g., TOML, JSON, etc.).
|
|
245
|
-
#
|
|
246
|
-
# Unlike `TreeHaver::Language` (which is a module with factory methods), this
|
|
247
|
-
# class holds the actual loaded language data from a grammar shared library.
|
|
248
|
-
#
|
|
249
|
-
# @api private
|
|
250
|
-
# @see TreeHaver::Language The factory module users should interact with
|
|
251
|
-
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Language.html
|
|
252
|
-
#
|
|
253
|
-
# :nocov:
|
|
254
|
-
# All Java backend implementation classes require JRuby and cannot be tested on MRI/CRuby.
|
|
255
|
-
# JRuby-specific CI jobs would test this code.
|
|
256
|
-
class Language
|
|
257
|
-
include Comparable
|
|
258
|
-
|
|
259
|
-
attr_reader :impl
|
|
260
|
-
|
|
261
|
-
# The backend this language is for
|
|
262
|
-
# @return [Symbol]
|
|
263
|
-
attr_reader :backend
|
|
264
|
-
|
|
265
|
-
# The path this language was loaded from (if known)
|
|
266
|
-
# @return [String, nil]
|
|
267
|
-
attr_reader :path
|
|
268
|
-
|
|
269
|
-
# The symbol name (if known)
|
|
270
|
-
# @return [String, nil]
|
|
271
|
-
attr_reader :symbol
|
|
272
|
-
|
|
273
|
-
# @api private
|
|
274
|
-
def initialize(impl, path: nil, symbol: nil)
|
|
275
|
-
@impl = impl
|
|
276
|
-
@backend = :java
|
|
277
|
-
@path = path
|
|
278
|
-
@symbol = symbol
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# Compare languages for equality
|
|
282
|
-
#
|
|
283
|
-
# Java languages are equal if they have the same backend, path, and symbol.
|
|
284
|
-
# Path and symbol uniquely identify a loaded language.
|
|
285
|
-
#
|
|
286
|
-
# @param other [Object] object to compare with
|
|
287
|
-
# @return [Integer, nil] -1, 0, 1, or nil if not comparable
|
|
288
|
-
def <=>(other)
|
|
289
|
-
return unless other.is_a?(Language)
|
|
290
|
-
return unless other.backend == @backend
|
|
291
|
-
|
|
292
|
-
# Compare by path first, then symbol
|
|
293
|
-
cmp = (@path || "") <=> (other.path || "")
|
|
294
|
-
return cmp if cmp.nonzero?
|
|
295
|
-
|
|
296
|
-
(@symbol || "") <=> (other.symbol || "")
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
# Hash value for this language (for use in Sets/Hashes)
|
|
300
|
-
# @return [Integer]
|
|
301
|
-
def hash
|
|
302
|
-
[@backend, @path, @symbol].hash
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
# Alias eql? to ==
|
|
306
|
-
alias_method :eql?, :==
|
|
307
|
-
|
|
308
|
-
# Load a language from a shared library
|
|
309
|
-
#
|
|
310
|
-
# There are three ways java-tree-sitter can load shared libraries:
|
|
311
|
-
#
|
|
312
|
-
# 1. Libraries in OS library search path (LD_LIBRARY_PATH on Linux,
|
|
313
|
-
# DYLD_LIBRARY_PATH on macOS, PATH on Windows) - loaded via
|
|
314
|
-
# SymbolLookup.libraryLookup(String, Arena)
|
|
315
|
-
#
|
|
316
|
-
# 2. Libraries in java.library.path - loaded via SymbolLookup.loaderLookup()
|
|
317
|
-
#
|
|
318
|
-
# 3. Custom NativeLibraryLookup implementation (e.g., for JARs)
|
|
319
|
-
#
|
|
320
|
-
# @param path [String] path to language shared library (.so/.dylib) or library name
|
|
321
|
-
# @param symbol [String, nil] exported symbol name (e.g., "tree_sitter_toml")
|
|
322
|
-
# @param name [String, nil] logical name (used to derive symbol if not provided)
|
|
323
|
-
# @return [Language] the loaded language
|
|
324
|
-
# @raise [TreeHaver::NotAvailable] if Java backend is not available
|
|
325
|
-
# @example Load by path
|
|
326
|
-
# lang = TreeHaver::Backends::Java::Language.from_library(
|
|
327
|
-
# "/usr/lib/libtree-sitter-toml.so",
|
|
328
|
-
# symbol: "tree_sitter_toml"
|
|
329
|
-
# )
|
|
330
|
-
# @example Load by name (searches LD_LIBRARY_PATH)
|
|
331
|
-
# lang = TreeHaver::Backends::Java::Language.from_library(
|
|
332
|
-
# "tree-sitter-toml",
|
|
333
|
-
# symbol: "tree_sitter_toml"
|
|
334
|
-
# )
|
|
335
|
-
class << self
|
|
336
|
-
def from_library(path, symbol: nil, name: nil)
|
|
337
|
-
raise TreeHaver::NotAvailable, "Java backend not available" unless Java.available?
|
|
338
|
-
|
|
339
|
-
# Use shared utility for consistent symbol derivation across backends
|
|
340
|
-
# If symbol not provided, derive from name or path
|
|
341
|
-
sym = symbol || LibraryPathUtils.derive_symbol_from_path(path)
|
|
342
|
-
# If name was provided, use it to override the derived symbol
|
|
343
|
-
sym = "tree_sitter_#{name}" if name && !symbol
|
|
344
|
-
|
|
345
|
-
begin
|
|
346
|
-
arena = ::Java::JavaLangForeign::Arena.global
|
|
347
|
-
symbol_lookup_class = ::Java::JavaLangForeign::SymbolLookup
|
|
348
|
-
|
|
349
|
-
# IMPORTANT: Load libtree-sitter.so FIRST by name so its symbols are available
|
|
350
|
-
# Grammar libraries need symbols like ts_language_version from the runtime
|
|
351
|
-
# We cache this lookup at the module level
|
|
352
|
-
unless Java.runtime_lookup
|
|
353
|
-
# Use libraryLookup(String, Arena) to search LD_LIBRARY_PATH
|
|
354
|
-
Java.runtime_lookup = symbol_lookup_class.libraryLookup("libtree-sitter.so", arena)
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
# Now load the grammar library
|
|
358
|
-
if File.exist?(path)
|
|
359
|
-
# Explicit path provided - use libraryLookup(Path, Arena)
|
|
360
|
-
java_path = ::Java::JavaNioFile::Paths.get(path)
|
|
361
|
-
grammar_lookup = symbol_lookup_class.libraryLookup(java_path, arena)
|
|
362
|
-
else
|
|
363
|
-
# Library name provided - use libraryLookup(String, Arena) to search
|
|
364
|
-
# LD_LIBRARY_PATH / DYLD_LIBRARY_PATH / PATH
|
|
365
|
-
grammar_lookup = symbol_lookup_class.libraryLookup(path, arena)
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
# Chain the lookups: grammar first, then runtime library for ts_* symbols
|
|
369
|
-
# This makes ts_language_version available when Language.load() needs it
|
|
370
|
-
combined_lookup = grammar_lookup.or(Java.runtime_lookup)
|
|
371
|
-
|
|
372
|
-
java_lang = Java.java_classes[:Language].load(combined_lookup, sym)
|
|
373
|
-
new(java_lang, path: path, symbol: symbol)
|
|
374
|
-
rescue ::Java::JavaLang::RuntimeException => e
|
|
375
|
-
cause = e.cause
|
|
376
|
-
root_cause = cause&.cause || cause
|
|
377
|
-
|
|
378
|
-
error_msg = "Failed to load language '#{sym}' from #{path}: #{e.message}"
|
|
379
|
-
if root_cause.is_a?(::Java::JavaLang::UnsatisfiedLinkError)
|
|
380
|
-
unresolved = root_cause.message.to_s
|
|
381
|
-
if unresolved.include?("ts_language_version")
|
|
382
|
-
# This specific symbol was renamed in tree-sitter 0.24
|
|
383
|
-
error_msg += "\n\nVersion mismatch detected: The grammar was built against " \
|
|
384
|
-
"tree-sitter < 0.24 (uses ts_language_version), but your runtime library " \
|
|
385
|
-
"is tree-sitter >= 0.24 (uses ts_language_abi_version).\n\n" \
|
|
386
|
-
"Solutions:\n" \
|
|
387
|
-
"1. Rebuild the grammar against your version of tree-sitter\n" \
|
|
388
|
-
"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")
|
|
391
|
-
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."
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
raise TreeHaver::NotAvailable, error_msg
|
|
397
|
-
rescue ::Java::JavaLang::UnsatisfiedLinkError => e
|
|
398
|
-
raise TreeHaver::NotAvailable,
|
|
399
|
-
"Native library error loading #{path}: #{e.message}. " \
|
|
400
|
-
"Ensure the library is in LD_LIBRARY_PATH."
|
|
401
|
-
rescue ::Java::JavaLang::IllegalArgumentException => e
|
|
402
|
-
raise TreeHaver::NotAvailable,
|
|
403
|
-
"Could not find library '#{path}': #{e.message}. " \
|
|
404
|
-
"Ensure it's in LD_LIBRARY_PATH or provide an absolute path."
|
|
405
|
-
end
|
|
406
|
-
end
|
|
407
|
-
|
|
408
|
-
# Load a language by name from java-tree-sitter grammar JARs
|
|
409
|
-
#
|
|
410
|
-
# This method loads grammars that are packaged as java-tree-sitter JARs
|
|
411
|
-
# from Maven Central. These JARs include the native grammar library
|
|
412
|
-
# pre-built for Java's Foreign Function API.
|
|
413
|
-
#
|
|
414
|
-
# @param name [String] the language name (e.g., "java", "python", "toml")
|
|
415
|
-
# @return [Language] the loaded language
|
|
416
|
-
# @raise [TreeHaver::NotAvailable] if the language JAR is not available
|
|
417
|
-
#
|
|
418
|
-
# @example
|
|
419
|
-
# # First, add the grammar JAR to TREE_SITTER_JAVA_JARS_DIR:
|
|
420
|
-
# # tree-sitter-toml-0.23.2.jar from Maven Central
|
|
421
|
-
# lang = TreeHaver::Backends::Java::Language.load_by_name("toml")
|
|
422
|
-
def load_by_name(name)
|
|
423
|
-
raise TreeHaver::NotAvailable, "Java backend not available" unless Java.available?
|
|
424
|
-
|
|
425
|
-
# Try to find the grammar library in standard locations
|
|
426
|
-
# Look for library names like "tree-sitter-toml" or "libtree-sitter-toml"
|
|
427
|
-
lib_names = [
|
|
428
|
-
"tree-sitter-#{name}",
|
|
429
|
-
"libtree-sitter-#{name}",
|
|
430
|
-
"tree_sitter_#{name}",
|
|
431
|
-
]
|
|
432
|
-
|
|
433
|
-
begin
|
|
434
|
-
arena = ::Java::JavaLangForeign::Arena.global
|
|
435
|
-
symbol_lookup_class = ::Java::JavaLangForeign::SymbolLookup
|
|
436
|
-
|
|
437
|
-
# Ensure runtime lookup is available
|
|
438
|
-
unless Java.runtime_lookup
|
|
439
|
-
Java.runtime_lookup = symbol_lookup_class.libraryLookup("libtree-sitter.so", arena)
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
# Try each library name
|
|
443
|
-
grammar_lookup = nil
|
|
444
|
-
lib_names.each do |lib_name|
|
|
445
|
-
grammar_lookup = symbol_lookup_class.libraryLookup(lib_name, arena)
|
|
446
|
-
break
|
|
447
|
-
rescue ::Java::JavaLang::IllegalArgumentException
|
|
448
|
-
# Library not found in search path, try next name
|
|
449
|
-
next
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
unless grammar_lookup
|
|
453
|
-
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."
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
combined_lookup = grammar_lookup.or(Java.runtime_lookup)
|
|
460
|
-
sym = "tree_sitter_#{name}"
|
|
461
|
-
java_lang = Java.java_classes[:Language].load(combined_lookup, sym)
|
|
462
|
-
new(java_lang, symbol: sym)
|
|
463
|
-
rescue ::Java::JavaLang::RuntimeException => e
|
|
464
|
-
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."
|
|
468
|
-
end
|
|
469
|
-
end
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
class << self
|
|
473
|
-
alias_method :from_path, :from_library
|
|
474
|
-
end
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
# Java backend parser wrapper (raw backend parser)
|
|
478
|
-
#
|
|
479
|
-
# This is a **raw backend parser** that wraps a jtreesitter Parser object via
|
|
480
|
-
# JRuby's Java interop. It is NOT intended for direct use by application code.
|
|
481
|
-
#
|
|
482
|
-
# Users should use `TreeHaver::Parser` which wraps this class and provides:
|
|
483
|
-
# - Automatic backend selection
|
|
484
|
-
# - Language wrapper unwrapping
|
|
485
|
-
# - Tree wrapping with source storage
|
|
486
|
-
# - Unified API across all backends
|
|
487
|
-
#
|
|
488
|
-
# @api private
|
|
489
|
-
# @see TreeHaver::Parser The wrapper class users should interact with
|
|
490
|
-
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Parser.html
|
|
491
|
-
class Parser
|
|
492
|
-
# Create a new parser instance
|
|
493
|
-
#
|
|
494
|
-
# @raise [TreeHaver::NotAvailable] if Java backend is not available
|
|
495
|
-
def initialize
|
|
496
|
-
raise TreeHaver::NotAvailable, "Java backend not available" unless Java.available?
|
|
497
|
-
@parser = Java.java_classes[:Parser].new
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
# Set the language for this parser
|
|
501
|
-
#
|
|
502
|
-
# Note: TreeHaver::Parser unwraps language objects before calling this method.
|
|
503
|
-
# This backend receives the Language wrapper's inner impl (java Language object).
|
|
504
|
-
#
|
|
505
|
-
# @param lang [Object] the Java language object (already unwrapped)
|
|
506
|
-
# @return [void]
|
|
507
|
-
def language=(lang)
|
|
508
|
-
# lang is already unwrapped by TreeHaver::Parser
|
|
509
|
-
@parser.language = lang
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
# Parse source code
|
|
513
|
-
#
|
|
514
|
-
# @param source [String] the source code to parse
|
|
515
|
-
# @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
|
|
516
|
-
def parse(source)
|
|
517
|
-
java_result = @parser.parse(source)
|
|
518
|
-
# jtreesitter 0.26.0 returns Optional<Tree>
|
|
519
|
-
java_tree = unwrap_optional(java_result)
|
|
520
|
-
raise TreeHaver::Error, "Parser returned no tree" unless java_tree
|
|
521
|
-
Tree.new(java_tree)
|
|
522
|
-
end
|
|
523
|
-
|
|
524
|
-
# Parse source code with optional incremental parsing
|
|
525
|
-
#
|
|
526
|
-
# Note: old_tree is already unwrapped by TreeHaver::Parser before reaching this method.
|
|
527
|
-
# The backend receives the raw Tree wrapper's impl, not a TreeHaver::Tree.
|
|
528
|
-
#
|
|
529
|
-
# When old_tree is provided and has been edited, tree-sitter will reuse
|
|
530
|
-
# unchanged nodes for better performance.
|
|
531
|
-
#
|
|
532
|
-
# @param old_tree [Tree, nil] previous backend tree for incremental parsing (already unwrapped)
|
|
533
|
-
# @param source [String] the source code to parse
|
|
534
|
-
# @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
|
|
535
|
-
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Parser.html#parse(java.lang.String,io.github.treesitter.jtreesitter.Tree)
|
|
536
|
-
def parse_string(old_tree, source)
|
|
537
|
-
# old_tree is already unwrapped to Tree wrapper's impl by TreeHaver::Parser
|
|
538
|
-
if old_tree
|
|
539
|
-
# Get the actual Java Tree object
|
|
540
|
-
java_old_tree = if old_tree.is_a?(Tree)
|
|
541
|
-
old_tree.impl
|
|
542
|
-
else
|
|
543
|
-
unwrap_optional(old_tree)
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
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
|
|
552
|
-
else
|
|
553
|
-
java_result = @parser.parse(source)
|
|
554
|
-
end
|
|
555
|
-
# jtreesitter 0.26.0 returns Optional<Tree>
|
|
556
|
-
java_tree = unwrap_optional(java_result)
|
|
557
|
-
raise TreeHaver::Error, "Parser returned no tree" unless java_tree
|
|
558
|
-
Tree.new(java_tree)
|
|
559
|
-
end
|
|
560
|
-
|
|
561
|
-
private
|
|
562
|
-
|
|
563
|
-
# Unwrap Java Optional
|
|
564
|
-
#
|
|
565
|
-
# jtreesitter 0.26.0 returns Optional<T> from many methods.
|
|
566
|
-
#
|
|
567
|
-
# @param value [Object] an Optional or direct value
|
|
568
|
-
# @return [Object, nil] unwrapped value or nil if empty
|
|
569
|
-
def unwrap_optional(value)
|
|
570
|
-
return value unless value.respond_to?(:isPresent)
|
|
571
|
-
value.isPresent ? value.get : nil
|
|
572
|
-
end
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
# Java backend tree wrapper (raw backend tree)
|
|
576
|
-
#
|
|
577
|
-
# This is a **raw backend tree** that wraps a jtreesitter Tree object via
|
|
578
|
-
# JRuby's Java interop. It is NOT intended for direct use by application code.
|
|
579
|
-
#
|
|
580
|
-
# == Architecture Note
|
|
581
|
-
#
|
|
582
|
-
# Unlike pure-Ruby backends (Citrus, Parslet, Prism, Psych) which define Tree
|
|
583
|
-
# classes that inherit from `TreeHaver::Base::Tree`, tree-sitter backends (MRI,
|
|
584
|
-
# Rust, FFI, Java) define raw wrapper classes that get wrapped by `TreeHaver::Tree`.
|
|
585
|
-
#
|
|
586
|
-
# The wrapping hierarchy is:
|
|
587
|
-
# Java::Tree (this class) → TreeHaver::Tree → Base::Tree
|
|
588
|
-
#
|
|
589
|
-
# When you use `TreeHaver::Parser#parse`, the returned tree is already wrapped
|
|
590
|
-
# in `TreeHaver::Tree`, which provides the full unified API including:
|
|
591
|
-
# - `#source` - The original source text
|
|
592
|
-
# - `#root_node` - Returns a `TreeHaver::Node` (not raw `Java::Node`)
|
|
593
|
-
# - `#errors`, `#warnings`, `#comments` - Parse diagnostics
|
|
594
|
-
# - `#edit` - Mark tree as edited for incremental parsing
|
|
595
|
-
# - `#to_s`, `#inspect` - String representations
|
|
596
|
-
#
|
|
597
|
-
# This raw class only implements methods that require direct calls to jtreesitter.
|
|
598
|
-
# The wrapper adds Ruby-level conveniences and stores the source text needed for
|
|
599
|
-
# `Node#text` extraction.
|
|
600
|
-
#
|
|
601
|
-
# @api private
|
|
602
|
-
# @see TreeHaver::Tree The wrapper class users should interact with
|
|
603
|
-
# @see TreeHaver::Base::Tree The base class documenting the full Tree API
|
|
604
|
-
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Tree.html
|
|
605
|
-
class Tree
|
|
606
|
-
attr_reader :impl
|
|
607
|
-
|
|
608
|
-
# @api private
|
|
609
|
-
def initialize(impl)
|
|
610
|
-
@impl = impl
|
|
611
|
-
end
|
|
612
|
-
|
|
613
|
-
# Get the root node of the tree
|
|
614
|
-
#
|
|
615
|
-
# @return [Node] the root node
|
|
616
|
-
# @raise [TreeHaver::Error] if tree has no root node
|
|
617
|
-
def root_node
|
|
618
|
-
result = @impl.rootNode
|
|
619
|
-
# jtreesitter 0.26.0: rootNode() may return Optional<Node> or Node directly
|
|
620
|
-
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
|
|
627
|
-
Node.new(java_node)
|
|
628
|
-
end
|
|
629
|
-
|
|
630
|
-
# Mark the tree as edited for incremental re-parsing
|
|
631
|
-
#
|
|
632
|
-
# @param start_byte [Integer] byte offset where the edit starts
|
|
633
|
-
# @param old_end_byte [Integer] byte offset where the old text ended
|
|
634
|
-
# @param new_end_byte [Integer] byte offset where the new text ends
|
|
635
|
-
# @param start_point [Hash] starting position as `{ row:, column: }`
|
|
636
|
-
# @param old_end_point [Hash] old ending position as `{ row:, column: }`
|
|
637
|
-
# @param new_end_point [Hash] new ending position as `{ row:, column: }`
|
|
638
|
-
# @return [void]
|
|
639
|
-
def edit(start_byte:, old_end_byte:, new_end_byte:, start_point:, old_end_point:, new_end_point:)
|
|
640
|
-
point_class = Java.java_classes[:Point]
|
|
641
|
-
input_edit_class = Java.java_classes[:InputEdit]
|
|
642
|
-
|
|
643
|
-
start_pt = point_class.new(start_point[:row], start_point[:column])
|
|
644
|
-
old_end_pt = point_class.new(old_end_point[:row], old_end_point[:column])
|
|
645
|
-
new_end_pt = point_class.new(new_end_point[:row], new_end_point[:column])
|
|
646
|
-
|
|
647
|
-
input_edit = input_edit_class.new(
|
|
648
|
-
start_byte,
|
|
649
|
-
old_end_byte,
|
|
650
|
-
new_end_byte,
|
|
651
|
-
start_pt,
|
|
652
|
-
old_end_pt,
|
|
653
|
-
new_end_pt,
|
|
654
|
-
)
|
|
655
|
-
|
|
656
|
-
@impl.edit(input_edit)
|
|
657
|
-
end
|
|
658
|
-
end
|
|
659
|
-
|
|
660
|
-
# Java backend node wrapper (raw backend node)
|
|
661
|
-
#
|
|
662
|
-
# This is a **raw backend node** that wraps a jtreesitter Node object via
|
|
663
|
-
# JRuby's Java interop. It provides the minimal interface needed for tree-sitter
|
|
664
|
-
# operations but is NOT intended for direct use by application code.
|
|
665
|
-
#
|
|
666
|
-
# == Architecture Note
|
|
667
|
-
#
|
|
668
|
-
# Unlike pure-Ruby backends (Citrus, Parslet, Prism, Psych) which define Node
|
|
669
|
-
# classes that inherit from `TreeHaver::Base::Node`, tree-sitter backends (MRI,
|
|
670
|
-
# Rust, FFI, Java) define raw wrapper classes that get wrapped by `TreeHaver::Node`.
|
|
671
|
-
#
|
|
672
|
-
# The wrapping hierarchy is:
|
|
673
|
-
# Java::Node (this class) → TreeHaver::Node → Base::Node
|
|
674
|
-
#
|
|
675
|
-
# When you use `TreeHaver::Parser#parse`, the returned tree's nodes are already
|
|
676
|
-
# wrapped in `TreeHaver::Node`, which provides the full unified API including:
|
|
677
|
-
# - `#children` - Array of child nodes
|
|
678
|
-
# - `#text` - Extract text from source
|
|
679
|
-
# - `#first_child`, `#last_child` - Convenience accessors
|
|
680
|
-
# - `#start_line`, `#end_line` - 1-based line numbers
|
|
681
|
-
# - `#source_position` - Hash with position info
|
|
682
|
-
# - `#each`, `#map`, etc. - Enumerable methods
|
|
683
|
-
# - `#to_s`, `#inspect` - String representations
|
|
684
|
-
#
|
|
685
|
-
# This raw class only implements methods that require direct calls to jtreesitter.
|
|
686
|
-
# The wrapper adds Ruby-level conveniences.
|
|
687
|
-
#
|
|
688
|
-
# @api private
|
|
689
|
-
# @see TreeHaver::Node The wrapper class users should interact with
|
|
690
|
-
# @see TreeHaver::Base::Node The base class documenting the full Node API
|
|
691
|
-
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Node.html
|
|
692
|
-
class Node
|
|
693
|
-
attr_reader :impl
|
|
694
|
-
|
|
695
|
-
# @api private
|
|
696
|
-
def initialize(impl)
|
|
697
|
-
@impl = impl
|
|
698
|
-
end
|
|
699
|
-
|
|
700
|
-
# Get the type of this node
|
|
701
|
-
#
|
|
702
|
-
# @return [String] the node type
|
|
703
|
-
def type
|
|
704
|
-
@impl.type
|
|
705
|
-
end
|
|
706
|
-
|
|
707
|
-
# Get the number of children
|
|
708
|
-
#
|
|
709
|
-
# @return [Integer] child count
|
|
710
|
-
def child_count
|
|
711
|
-
@impl.childCount
|
|
712
|
-
end
|
|
713
|
-
|
|
714
|
-
# Get a child by index
|
|
715
|
-
#
|
|
716
|
-
# @param index [Integer] the child index
|
|
717
|
-
# @return [Node, nil] the child node or nil if index out of bounds
|
|
718
|
-
def child(index)
|
|
719
|
-
# jtreesitter 0.26.0: getChild returns Optional<Node> or throws IndexOutOfBoundsException
|
|
720
|
-
result = @impl.getChild(index)
|
|
721
|
-
return if result.nil?
|
|
722
|
-
|
|
723
|
-
# Handle Java Optional
|
|
724
|
-
if result.respond_to?(:isPresent)
|
|
725
|
-
return unless result.isPresent
|
|
726
|
-
java_node = result.get
|
|
727
|
-
else
|
|
728
|
-
# Direct Node return (some jtreesitter versions)
|
|
729
|
-
java_node = result
|
|
730
|
-
end
|
|
731
|
-
|
|
732
|
-
Node.new(java_node)
|
|
733
|
-
rescue ::Java::JavaLang::IndexOutOfBoundsException
|
|
734
|
-
nil
|
|
735
|
-
end
|
|
736
|
-
|
|
737
|
-
# Get a child by field name
|
|
738
|
-
#
|
|
739
|
-
# @param name [String] the field name
|
|
740
|
-
# @return [Node, nil] the child node or nil if not found
|
|
741
|
-
def child_by_field_name(name)
|
|
742
|
-
# jtreesitter 0.26.0: getChildByFieldName returns Optional<Node>
|
|
743
|
-
# However, some versions or scenarios may return null directly
|
|
744
|
-
result = @impl.getChildByFieldName(name)
|
|
745
|
-
return if result.nil?
|
|
746
|
-
|
|
747
|
-
# Handle Java Optional
|
|
748
|
-
if result.respond_to?(:isPresent)
|
|
749
|
-
return unless result.isPresent
|
|
750
|
-
java_node = result.get
|
|
751
|
-
else
|
|
752
|
-
# Direct Node return (some jtreesitter versions)
|
|
753
|
-
java_node = result
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
Node.new(java_node)
|
|
757
|
-
end
|
|
758
|
-
|
|
759
|
-
# Iterate over children
|
|
760
|
-
#
|
|
761
|
-
# @yield [Node] each child node
|
|
762
|
-
# @return [void]
|
|
763
|
-
def each
|
|
764
|
-
return enum_for(:each) unless block_given?
|
|
765
|
-
child_count.times do |i|
|
|
766
|
-
yield child(i)
|
|
767
|
-
end
|
|
768
|
-
end
|
|
769
|
-
|
|
770
|
-
# Get the start byte position
|
|
771
|
-
#
|
|
772
|
-
# @return [Integer] start byte
|
|
773
|
-
def start_byte
|
|
774
|
-
@impl.startByte
|
|
775
|
-
end
|
|
776
|
-
|
|
777
|
-
# Get the end byte position
|
|
778
|
-
#
|
|
779
|
-
# @return [Integer] end byte
|
|
780
|
-
def end_byte
|
|
781
|
-
@impl.endByte
|
|
782
|
-
end
|
|
783
|
-
|
|
784
|
-
# Get the start point (row, column)
|
|
785
|
-
#
|
|
786
|
-
# @return [Hash] with :row and :column keys
|
|
787
|
-
def start_point
|
|
788
|
-
pt = @impl.startPoint
|
|
789
|
-
{row: pt.row, column: pt.column}
|
|
790
|
-
end
|
|
791
|
-
|
|
792
|
-
# Get the end point (row, column)
|
|
793
|
-
#
|
|
794
|
-
# @return [Hash] with :row and :column keys
|
|
795
|
-
def end_point
|
|
796
|
-
pt = @impl.endPoint
|
|
797
|
-
{row: pt.row, column: pt.column}
|
|
798
|
-
end
|
|
799
|
-
|
|
800
|
-
# Check if this node has an error
|
|
801
|
-
#
|
|
802
|
-
# @return [Boolean] true if the node or any descendant has an error
|
|
803
|
-
def has_error?
|
|
804
|
-
@impl.hasError
|
|
805
|
-
end
|
|
806
|
-
|
|
807
|
-
# Check if this node is missing
|
|
808
|
-
#
|
|
809
|
-
# @return [Boolean] true if this is a MISSING node
|
|
810
|
-
def missing?
|
|
811
|
-
@impl.isMissing
|
|
812
|
-
end
|
|
813
|
-
|
|
814
|
-
# Check if this is a named node
|
|
815
|
-
#
|
|
816
|
-
# @return [Boolean] true if this is a named node
|
|
817
|
-
def named?
|
|
818
|
-
@impl.isNamed
|
|
819
|
-
end
|
|
820
|
-
|
|
821
|
-
# Get the parent node
|
|
822
|
-
#
|
|
823
|
-
# @return [Node, nil] the parent node or nil if this is the root
|
|
824
|
-
def parent
|
|
825
|
-
# jtreesitter 0.26.0: getParent returns Optional<Node>
|
|
826
|
-
result = @impl.getParent
|
|
827
|
-
return if result.nil?
|
|
828
|
-
|
|
829
|
-
# Handle Java Optional
|
|
830
|
-
if result.respond_to?(:isPresent)
|
|
831
|
-
return unless result.isPresent
|
|
832
|
-
java_node = result.get
|
|
833
|
-
else
|
|
834
|
-
java_node = result
|
|
835
|
-
end
|
|
836
|
-
|
|
837
|
-
Node.new(java_node)
|
|
838
|
-
end
|
|
839
|
-
|
|
840
|
-
# Get the next sibling node
|
|
841
|
-
#
|
|
842
|
-
# @return [Node, nil] the next sibling or nil if none
|
|
843
|
-
def next_sibling
|
|
844
|
-
# jtreesitter 0.26.0: getNextSibling returns Optional<Node>
|
|
845
|
-
result = @impl.getNextSibling
|
|
846
|
-
return if result.nil?
|
|
847
|
-
|
|
848
|
-
# Handle Java Optional
|
|
849
|
-
if result.respond_to?(:isPresent)
|
|
850
|
-
return unless result.isPresent
|
|
851
|
-
java_node = result.get
|
|
852
|
-
else
|
|
853
|
-
java_node = result
|
|
854
|
-
end
|
|
855
|
-
|
|
856
|
-
Node.new(java_node)
|
|
857
|
-
end
|
|
858
|
-
|
|
859
|
-
# Get the previous sibling node
|
|
860
|
-
#
|
|
861
|
-
# @return [Node, nil] the previous sibling or nil if none
|
|
862
|
-
def prev_sibling
|
|
863
|
-
# jtreesitter 0.26.0: getPrevSibling returns Optional<Node>
|
|
864
|
-
result = @impl.getPrevSibling
|
|
865
|
-
return if result.nil?
|
|
866
|
-
|
|
867
|
-
# Handle Java Optional
|
|
868
|
-
if result.respond_to?(:isPresent)
|
|
869
|
-
return unless result.isPresent
|
|
870
|
-
java_node = result.get
|
|
871
|
-
else
|
|
872
|
-
java_node = result
|
|
873
|
-
end
|
|
874
|
-
|
|
875
|
-
Node.new(java_node)
|
|
876
|
-
end
|
|
877
|
-
|
|
878
|
-
# Get the text of this node
|
|
879
|
-
#
|
|
880
|
-
# @return [String] the source text
|
|
881
|
-
def text
|
|
882
|
-
@impl.text.to_s
|
|
883
|
-
end
|
|
884
|
-
end
|
|
885
|
-
# :nocov:
|
|
886
|
-
|
|
887
|
-
# Register the availability checker for RSpec dependency tags
|
|
888
|
-
TreeHaver::BackendRegistry.register_availability_checker(:java) do
|
|
889
|
-
available?
|
|
890
|
-
end
|
|
891
|
-
end
|
|
892
|
-
end
|
|
893
|
-
end
|