tree_haver 4.0.5 → 5.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/CHANGELOG.md +144 -1
- data/README.md +497 -356
- data/lib/tree_haver/backends/citrus.rb +98 -114
- data/lib/tree_haver/backends/ffi.rb +76 -13
- data/lib/tree_haver/backends/java.rb +99 -14
- data/lib/tree_haver/backends/mri.rb +25 -1
- data/lib/tree_haver/backends/parslet.rb +560 -0
- data/lib/tree_haver/backends/prism.rb +1 -1
- data/lib/tree_haver/backends/psych.rb +1 -1
- data/lib/tree_haver/backends/rust.rb +1 -1
- data/lib/tree_haver/base/node.rb +8 -1
- data/lib/tree_haver/language.rb +44 -13
- data/lib/tree_haver/parser.rb +128 -38
- data/lib/tree_haver/parslet_grammar_finder.rb +224 -0
- data/lib/tree_haver/point.rb +6 -44
- data/lib/tree_haver/rspec/dependency_tags.rb +40 -1
- data/lib/tree_haver/version.rb +1 -1
- data/lib/tree_haver.rb +100 -13
- data.tar.gz.sig +0 -0
- metadata +15 -14
- metadata.gz.sig +0 -0
|
@@ -45,8 +45,10 @@ module TreeHaver
|
|
|
45
45
|
@loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
46
46
|
rescue LoadError
|
|
47
47
|
@loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
48
|
+
# :nocov: defensive code - StandardError during require is extremely rare
|
|
48
49
|
rescue StandardError
|
|
49
50
|
@loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
51
|
+
# :nocov:
|
|
50
52
|
end
|
|
51
53
|
@loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
52
54
|
end
|
|
@@ -108,6 +110,22 @@ module TreeHaver
|
|
|
108
110
|
@backend = :citrus
|
|
109
111
|
end
|
|
110
112
|
|
|
113
|
+
# Get the language name
|
|
114
|
+
#
|
|
115
|
+
# Derives a name from the grammar module name.
|
|
116
|
+
#
|
|
117
|
+
# @return [Symbol] language name
|
|
118
|
+
def language_name
|
|
119
|
+
# Derive name from grammar module (e.g., TomlRB::Document -> :toml)
|
|
120
|
+
return :unknown unless @grammar_module.respond_to?(:name) && @grammar_module.name
|
|
121
|
+
|
|
122
|
+
name = @grammar_module.name.to_s.split("::").first.downcase
|
|
123
|
+
name.sub(/rb$/, "").to_sym
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Alias for language_name (API compatibility)
|
|
127
|
+
alias_method :name, :language_name
|
|
128
|
+
|
|
111
129
|
# Compare languages for equality
|
|
112
130
|
#
|
|
113
131
|
# Citrus languages are equal if they have the same backend and grammar_module.
|
|
@@ -192,23 +210,31 @@ module TreeHaver
|
|
|
192
210
|
|
|
193
211
|
# Set the grammar for this parser
|
|
194
212
|
#
|
|
195
|
-
#
|
|
196
|
-
#
|
|
213
|
+
# Accepts either a Citrus::Language wrapper or a raw Citrus grammar module.
|
|
214
|
+
# When passed a Language wrapper, extracts the grammar_module from it.
|
|
215
|
+
# When passed a raw grammar module, uses it directly.
|
|
197
216
|
#
|
|
198
|
-
#
|
|
217
|
+
# This flexibility allows both patterns:
|
|
218
|
+
# parser.language = TreeHaver::Backends::Citrus::Language.new(TomlRB::Document)
|
|
219
|
+
# parser.language = TomlRB::Document # Also works
|
|
220
|
+
#
|
|
221
|
+
# @param grammar [Language, Module] Citrus Language wrapper or grammar module
|
|
199
222
|
# @return [void]
|
|
200
|
-
# @example
|
|
201
|
-
# require "toml-rb"
|
|
202
|
-
# # TreeHaver::Parser unwraps Language.new(TomlRB::Document) to just TomlRB::Document
|
|
203
|
-
# parser.language = TomlRB::Document # Backend receives unwrapped module
|
|
204
223
|
def language=(grammar)
|
|
205
|
-
#
|
|
206
|
-
|
|
224
|
+
# Accept Language wrapper or raw grammar module
|
|
225
|
+
actual_grammar = case grammar
|
|
226
|
+
when Language
|
|
227
|
+
grammar.grammar_module
|
|
228
|
+
else
|
|
229
|
+
grammar
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
unless actual_grammar.respond_to?(:parse)
|
|
207
233
|
raise ArgumentError,
|
|
208
|
-
"Expected Citrus grammar module with parse method, " \
|
|
234
|
+
"Expected Citrus grammar module with parse method or Language wrapper, " \
|
|
209
235
|
"got #{grammar.class}"
|
|
210
236
|
end
|
|
211
|
-
@grammar =
|
|
237
|
+
@grammar = actual_grammar
|
|
212
238
|
end
|
|
213
239
|
|
|
214
240
|
# Parse source code
|
|
@@ -247,13 +273,18 @@ module TreeHaver
|
|
|
247
273
|
# Wraps a Citrus::Match (which represents the parse tree) to provide
|
|
248
274
|
# tree-sitter-compatible API.
|
|
249
275
|
#
|
|
276
|
+
# Inherits from Base::Tree to get shared methods like #errors, #warnings,
|
|
277
|
+
# #comments, #has_error?, and #inspect.
|
|
278
|
+
#
|
|
250
279
|
# @api private
|
|
251
|
-
class Tree
|
|
252
|
-
|
|
280
|
+
class Tree < TreeHaver::Base::Tree
|
|
281
|
+
# The raw Citrus::Match root
|
|
282
|
+
# @return [Citrus::Match] The root match
|
|
283
|
+
attr_reader :root_match
|
|
253
284
|
|
|
254
285
|
def initialize(root_match, source)
|
|
255
286
|
@root_match = root_match
|
|
256
|
-
|
|
287
|
+
super(root_match, source: source)
|
|
257
288
|
end
|
|
258
289
|
|
|
259
290
|
def root_node
|
|
@@ -273,22 +304,24 @@ module TreeHaver
|
|
|
273
304
|
# - matches: child matches
|
|
274
305
|
# - captures: named groups
|
|
275
306
|
#
|
|
307
|
+
# Inherits from Base::Node to get shared methods like #first_child, #last_child,
|
|
308
|
+
# #to_s, #inspect, #==, #<=>, #source_position, #start_line, #end_line, etc.
|
|
309
|
+
#
|
|
276
310
|
# Language-specific helpers can be mixed in for convenience:
|
|
277
311
|
# require "tree_haver/backends/citrus/toml_helpers"
|
|
278
312
|
# TreeHaver::Backends::Citrus::Node.include(TreeHaver::Backends::Citrus::TomlHelpers)
|
|
279
313
|
#
|
|
280
314
|
# @api private
|
|
281
|
-
class Node
|
|
282
|
-
|
|
283
|
-
include Enumerable
|
|
284
|
-
|
|
285
|
-
attr_reader :match, :source
|
|
315
|
+
class Node < TreeHaver::Base::Node
|
|
316
|
+
attr_reader :match
|
|
286
317
|
|
|
287
318
|
def initialize(match, source)
|
|
288
319
|
@match = match
|
|
289
|
-
|
|
320
|
+
super(match, source: source)
|
|
290
321
|
end
|
|
291
322
|
|
|
323
|
+
# -- Required API Methods (from Base::Node) ----------------------------
|
|
324
|
+
|
|
292
325
|
# Get node type from Citrus rule name
|
|
293
326
|
#
|
|
294
327
|
# Uses Citrus grammar introspection to dynamically determine node types.
|
|
@@ -308,6 +341,50 @@ module TreeHaver
|
|
|
308
341
|
extract_type_from_event(@match.events.first)
|
|
309
342
|
end
|
|
310
343
|
|
|
344
|
+
def start_byte
|
|
345
|
+
@match.offset
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def end_byte
|
|
349
|
+
@match.offset + @match.length
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def children
|
|
353
|
+
return [] unless @match.respond_to?(:matches)
|
|
354
|
+
@match.matches.map { |m| Node.new(m, @source) }
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# -- Overridden Methods ------------------------------------------------
|
|
358
|
+
|
|
359
|
+
# Override start_point to calculate from source
|
|
360
|
+
def start_point
|
|
361
|
+
calculate_point(@match.offset)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Override end_point to calculate from source
|
|
365
|
+
def end_point
|
|
366
|
+
calculate_point(@match.offset + @match.length)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Override text to use Citrus match string
|
|
370
|
+
def text
|
|
371
|
+
@match.string
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Override child_count for efficiency (avoid building full children array)
|
|
375
|
+
def child_count
|
|
376
|
+
@match.respond_to?(:matches) ? @match.matches.size : 0
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Override child to handle negative indices properly
|
|
380
|
+
def child(index)
|
|
381
|
+
return if index.negative?
|
|
382
|
+
return unless @match.respond_to?(:matches)
|
|
383
|
+
return if index >= @match.matches.size
|
|
384
|
+
|
|
385
|
+
Node.new(@match.matches[index], @source)
|
|
386
|
+
end
|
|
387
|
+
|
|
311
388
|
# Check if this node represents a structural element vs a terminal/token
|
|
312
389
|
#
|
|
313
390
|
# Uses Citrus grammar's terminal? method to determine if this is
|
|
@@ -387,99 +464,6 @@ module TreeHaver
|
|
|
387
464
|
"unknown"
|
|
388
465
|
end
|
|
389
466
|
|
|
390
|
-
public
|
|
391
|
-
|
|
392
|
-
def start_byte
|
|
393
|
-
@match.offset
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
def end_byte
|
|
397
|
-
@match.offset + @match.length
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
def start_point
|
|
401
|
-
calculate_point(@match.offset)
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
def end_point
|
|
405
|
-
calculate_point(@match.offset + @match.length)
|
|
406
|
-
end
|
|
407
|
-
|
|
408
|
-
# Get the 1-based line number where this node starts
|
|
409
|
-
#
|
|
410
|
-
# @return [Integer] 1-based line number
|
|
411
|
-
def start_line
|
|
412
|
-
start_point[:row] + 1
|
|
413
|
-
end
|
|
414
|
-
|
|
415
|
-
# Get the 1-based line number where this node ends
|
|
416
|
-
#
|
|
417
|
-
# @return [Integer] 1-based line number
|
|
418
|
-
def end_line
|
|
419
|
-
end_point[:row] + 1
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
# Get position information as a hash
|
|
423
|
-
#
|
|
424
|
-
# Returns a hash with 1-based line numbers and 0-based columns.
|
|
425
|
-
# Compatible with *-merge gems' FileAnalysisBase.
|
|
426
|
-
#
|
|
427
|
-
# @return [Hash{Symbol => Integer}] Position hash
|
|
428
|
-
def source_position
|
|
429
|
-
{
|
|
430
|
-
start_line: start_line,
|
|
431
|
-
end_line: end_line,
|
|
432
|
-
start_column: start_point[:column],
|
|
433
|
-
end_column: end_point[:column],
|
|
434
|
-
}
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
# Get the first child node
|
|
438
|
-
#
|
|
439
|
-
# @return [Node, nil] First child or nil
|
|
440
|
-
def first_child
|
|
441
|
-
child(0)
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
def text
|
|
445
|
-
@match.string
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
def child_count
|
|
449
|
-
@match.respond_to?(:matches) ? @match.matches.size : 0
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
def child(index)
|
|
453
|
-
return unless @match.respond_to?(:matches)
|
|
454
|
-
return if index >= @match.matches.size
|
|
455
|
-
|
|
456
|
-
Node.new(@match.matches[index], @source)
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
def children
|
|
460
|
-
return [] unless @match.respond_to?(:matches)
|
|
461
|
-
@match.matches.map { |m| Node.new(m, @source) }
|
|
462
|
-
end
|
|
463
|
-
|
|
464
|
-
def each(&block)
|
|
465
|
-
return to_enum(__method__) unless block_given?
|
|
466
|
-
children.each(&block)
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
def has_error?
|
|
470
|
-
false # Citrus raises on parse error, so successful parse has no errors
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
def missing?
|
|
474
|
-
false # Citrus doesn't have the concept of missing nodes
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
def named?
|
|
478
|
-
true # Citrus matches are typically "named" in tree-sitter terminology
|
|
479
|
-
end
|
|
480
|
-
|
|
481
|
-
private
|
|
482
|
-
|
|
483
467
|
def calculate_point(offset)
|
|
484
468
|
return {row: 0, column: 0} if offset <= 0
|
|
485
469
|
|
|
@@ -494,7 +478,7 @@ module TreeHaver
|
|
|
494
478
|
end
|
|
495
479
|
end
|
|
496
480
|
|
|
497
|
-
# Register availability checker for RSpec dependency tags
|
|
481
|
+
# Register the availability checker for RSpec dependency tags
|
|
498
482
|
TreeHaver::BackendRegistry.register_availability_checker(:citrus) do
|
|
499
483
|
available?
|
|
500
484
|
end
|
|
@@ -17,18 +17,26 @@ module TreeHaver
|
|
|
17
17
|
#
|
|
18
18
|
# == Tree/Node Architecture
|
|
19
19
|
#
|
|
20
|
-
# This backend
|
|
21
|
-
#
|
|
20
|
+
# This backend defines raw `FFI::Tree` and `FFI::Node` wrapper classes that
|
|
21
|
+
# provide minimal FFI bindings to the tree-sitter C structs. These are **not**
|
|
22
|
+
# intended for direct use by application code.
|
|
22
23
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# - `TreeHaver::Tree#root_node` wraps raw nodes in `TreeHaver::Node`
|
|
24
|
+
# The wrapping hierarchy is:
|
|
25
|
+
# FFI::Tree/Node (raw FFI wrappers) → TreeHaver::Tree/Node → Base::Tree/Node
|
|
26
26
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
27
|
+
# When you use `TreeHaver::Parser#parse`:
|
|
28
|
+
# 1. `FFI::Parser#parse` returns an `FFI::Tree` (raw pointer wrapper)
|
|
29
|
+
# 2. `TreeHaver::Parser` wraps it in `TreeHaver::Tree` (adds source storage)
|
|
30
|
+
# 3. `TreeHaver::Tree#root_node` wraps `FFI::Node` in `TreeHaver::Node`
|
|
29
31
|
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
+
# The `TreeHaver::Tree` and `TreeHaver::Node` wrappers provide the full unified
|
|
33
|
+
# API including `#children`, `#text`, `#source`, `#source_position`, etc.
|
|
34
|
+
#
|
|
35
|
+
# This differs from pure-Ruby backends (Citrus, Parslet, Prism, Psych) which
|
|
36
|
+
# define Tree/Node classes that directly inherit from Base::Tree/Base::Node.
|
|
37
|
+
#
|
|
38
|
+
# @see TreeHaver::Tree The wrapper class users should interact with
|
|
39
|
+
# @see TreeHaver::Node The wrapper class users should interact with
|
|
32
40
|
# @see TreeHaver::Base::Tree Base class documenting the Tree API contract
|
|
33
41
|
# @see TreeHaver::Base::Node Base class documenting the Node API contract
|
|
34
42
|
#
|
|
@@ -79,16 +87,20 @@ module TreeHaver
|
|
|
79
87
|
@loaded = begin # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
80
88
|
# TruffleRuby's FFI doesn't support STRUCT_BY_VALUE return types
|
|
81
89
|
# which tree-sitter uses extensively (ts_tree_root_node, ts_node_child, etc.)
|
|
90
|
+
# :nocov: TruffleRuby returns false early - subsequent FFI code paths unreachable on TruffleRuby
|
|
82
91
|
if RUBY_ENGINE == "truffleruby"
|
|
83
92
|
false
|
|
93
|
+
# :nocov:
|
|
84
94
|
else
|
|
85
95
|
require "ffi"
|
|
86
96
|
true
|
|
87
97
|
end
|
|
88
98
|
rescue LoadError
|
|
89
99
|
false
|
|
100
|
+
# :nocov: defensive code - StandardError during require is extremely rare
|
|
90
101
|
rescue StandardError
|
|
91
102
|
false
|
|
103
|
+
# :nocov:
|
|
92
104
|
end
|
|
93
105
|
@loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
94
106
|
end
|
|
@@ -371,6 +383,30 @@ module TreeHaver
|
|
|
371
383
|
# Alias eql? to ==
|
|
372
384
|
alias_method :eql?, :==
|
|
373
385
|
|
|
386
|
+
# Get the language name
|
|
387
|
+
#
|
|
388
|
+
# Derives a name from the symbol or path.
|
|
389
|
+
#
|
|
390
|
+
# @return [Symbol] language name
|
|
391
|
+
def language_name
|
|
392
|
+
# Try to derive from symbol (e.g., "tree_sitter_toml" -> :toml)
|
|
393
|
+
if @symbol
|
|
394
|
+
name = @symbol.to_s.sub(/^tree_sitter_/, "")
|
|
395
|
+
return name.to_sym
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Try to derive from path (e.g., "/path/to/libtree-sitter-toml.so" -> :toml)
|
|
399
|
+
if @path
|
|
400
|
+
name = LibraryPathUtils.derive_language_name_from_path(@path)
|
|
401
|
+
return name.to_sym if name
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
:unknown
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Alias for language_name (API compatibility)
|
|
408
|
+
alias_method :name, :language_name
|
|
409
|
+
|
|
374
410
|
# Convert to FFI pointer for passing to native functions
|
|
375
411
|
#
|
|
376
412
|
# @return [FFI::Pointer]
|
|
@@ -642,10 +678,37 @@ module TreeHaver
|
|
|
642
678
|
end
|
|
643
679
|
end
|
|
644
680
|
|
|
645
|
-
# FFI-based tree-sitter node
|
|
681
|
+
# FFI-based tree-sitter node (raw backend node)
|
|
682
|
+
#
|
|
683
|
+
# This is a **raw backend node** that wraps a TSNode by-value struct from the
|
|
684
|
+
# tree-sitter C API. It provides the minimal interface needed for tree-sitter
|
|
685
|
+
# operations but is NOT intended for direct use by application code.
|
|
686
|
+
#
|
|
687
|
+
# == Architecture Note
|
|
688
|
+
#
|
|
689
|
+
# Unlike pure-Ruby backends (Citrus, Parslet, Prism, Psych) which define Node
|
|
690
|
+
# classes that inherit from `TreeHaver::Base::Node`, tree-sitter backends (MRI,
|
|
691
|
+
# Rust, FFI, Java) define raw wrapper classes that get wrapped by `TreeHaver::Node`.
|
|
646
692
|
#
|
|
647
|
-
#
|
|
648
|
-
#
|
|
693
|
+
# The wrapping hierarchy is:
|
|
694
|
+
# FFI::Node (this class) → TreeHaver::Node → Base::Node
|
|
695
|
+
#
|
|
696
|
+
# When you use `TreeHaver::Parser#parse`, the returned tree's nodes are already
|
|
697
|
+
# wrapped in `TreeHaver::Node`, which provides the full unified API including:
|
|
698
|
+
# - `#children` - Array of child nodes
|
|
699
|
+
# - `#text` - Extract text from source
|
|
700
|
+
# - `#first_child`, `#last_child` - Convenience accessors
|
|
701
|
+
# - `#start_line`, `#end_line` - 1-based line numbers
|
|
702
|
+
# - `#source_position` - Hash with position info
|
|
703
|
+
# - `#each`, `#map`, etc. - Enumerable methods
|
|
704
|
+
# - `#to_s`, `#inspect` - String representations
|
|
705
|
+
#
|
|
706
|
+
# This raw class only implements methods that require direct FFI calls to the
|
|
707
|
+
# tree-sitter C library. The wrapper adds Ruby-level conveniences.
|
|
708
|
+
#
|
|
709
|
+
# @api private
|
|
710
|
+
# @see TreeHaver::Node The wrapper class users should interact with
|
|
711
|
+
# @see TreeHaver::Base::Node The base class documenting the full Node API
|
|
649
712
|
class Node
|
|
650
713
|
include Enumerable
|
|
651
714
|
|
|
@@ -937,7 +1000,7 @@ module TreeHaver
|
|
|
937
1000
|
end
|
|
938
1001
|
end
|
|
939
1002
|
|
|
940
|
-
# Register availability checker for RSpec dependency tags
|
|
1003
|
+
# Register the availability checker for RSpec dependency tags
|
|
941
1004
|
TreeHaver::BackendRegistry.register_availability_checker(:ffi) do
|
|
942
1005
|
available?
|
|
943
1006
|
end
|
|
@@ -17,18 +17,27 @@ module TreeHaver
|
|
|
17
17
|
#
|
|
18
18
|
# == Tree/Node Architecture
|
|
19
19
|
#
|
|
20
|
-
# This backend
|
|
21
|
-
#
|
|
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.
|
|
22
24
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# - `TreeHaver::Tree#root_node` wraps raw nodes in `TreeHaver::Node`
|
|
25
|
+
# The wrapping hierarchy is:
|
|
26
|
+
# Java::Tree/Node (this backend) → TreeHaver::Tree/Node → Base::Tree/Node
|
|
26
27
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
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`
|
|
29
32
|
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
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
|
|
32
41
|
# @see TreeHaver::Base::Tree Base class documenting the Tree API contract
|
|
33
42
|
# @see TreeHaver::Base::Node Base class documenting the Node API contract
|
|
34
43
|
#
|
|
@@ -228,8 +237,17 @@ module TreeHaver
|
|
|
228
237
|
# :nocov:
|
|
229
238
|
end
|
|
230
239
|
|
|
231
|
-
#
|
|
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.).
|
|
232
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
|
|
233
251
|
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Language.html
|
|
234
252
|
#
|
|
235
253
|
# :nocov:
|
|
@@ -456,8 +474,19 @@ module TreeHaver
|
|
|
456
474
|
end
|
|
457
475
|
end
|
|
458
476
|
|
|
459
|
-
#
|
|
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.
|
|
460
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
|
|
461
490
|
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Parser.html
|
|
462
491
|
class Parser
|
|
463
492
|
# Create a new parser instance
|
|
@@ -543,8 +572,35 @@ module TreeHaver
|
|
|
543
572
|
end
|
|
544
573
|
end
|
|
545
574
|
|
|
546
|
-
#
|
|
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`.
|
|
547
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
|
|
548
604
|
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Tree.html
|
|
549
605
|
class Tree
|
|
550
606
|
attr_reader :impl
|
|
@@ -601,8 +657,37 @@ module TreeHaver
|
|
|
601
657
|
end
|
|
602
658
|
end
|
|
603
659
|
|
|
604
|
-
#
|
|
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
|
|
605
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
|
|
606
691
|
# @see https://tree-sitter.github.io/java-tree-sitter/io/github/treesitter/jtreesitter/Node.html
|
|
607
692
|
class Node
|
|
608
693
|
attr_reader :impl
|
|
@@ -799,7 +884,7 @@ module TreeHaver
|
|
|
799
884
|
end
|
|
800
885
|
# :nocov:
|
|
801
886
|
|
|
802
|
-
# Register availability checker for RSpec dependency tags
|
|
887
|
+
# Register the availability checker for RSpec dependency tags
|
|
803
888
|
TreeHaver::BackendRegistry.register_availability_checker(:java) do
|
|
804
889
|
available?
|
|
805
890
|
end
|
|
@@ -142,6 +142,30 @@ module TreeHaver
|
|
|
142
142
|
@symbol = symbol
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
# Get the language name
|
|
146
|
+
#
|
|
147
|
+
# Derives a name from the symbol or path.
|
|
148
|
+
#
|
|
149
|
+
# @return [Symbol] language name
|
|
150
|
+
def language_name
|
|
151
|
+
# Try to derive from symbol (e.g., "tree_sitter_toml" -> :toml)
|
|
152
|
+
if @symbol
|
|
153
|
+
name = @symbol.to_s.sub(/^tree_sitter_/, "")
|
|
154
|
+
return name.to_sym
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Try to derive from path (e.g., "/path/to/libtree-sitter-toml.so" -> :toml)
|
|
158
|
+
if @path
|
|
159
|
+
name = LibraryPathUtils.derive_language_name_from_path(@path)
|
|
160
|
+
return name.to_sym if name
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
:unknown
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Alias for language_name (API compatibility)
|
|
167
|
+
alias_method :name, :language_name
|
|
168
|
+
|
|
145
169
|
# Compare languages for equality
|
|
146
170
|
#
|
|
147
171
|
# MRI languages are equal if they have the same backend, path, and symbol.
|
|
@@ -329,7 +353,7 @@ module TreeHaver
|
|
|
329
353
|
end
|
|
330
354
|
end
|
|
331
355
|
|
|
332
|
-
# Register availability checker for RSpec dependency tags
|
|
356
|
+
# Register the availability checker for RSpec dependency tags
|
|
333
357
|
TreeHaver::BackendRegistry.register_availability_checker(:mri) do
|
|
334
358
|
available?
|
|
335
359
|
end
|