tree_haver 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -3
- data/CHANGELOG.md +96 -1
- data/CONTRIBUTING.md +46 -14
- data/README.md +248 -86
- data/lib/tree_haver/backends/citrus.rb +36 -0
- data/lib/tree_haver/backends/commonmarker.rb +490 -0
- data/lib/tree_haver/backends/ffi.rb +15 -13
- data/lib/tree_haver/backends/java.rb +1 -1
- data/lib/tree_haver/backends/markly.rb +559 -0
- data/lib/tree_haver/backends/mri.rb +41 -12
- data/lib/tree_haver/backends/prism.rb +624 -0
- data/lib/tree_haver/backends/psych.rb +597 -0
- data/lib/tree_haver/backends/rust.rb +1 -1
- data/lib/tree_haver/grammar_finder.rb +74 -5
- data/lib/tree_haver/node.rb +72 -6
- data/lib/tree_haver/version.rb +1 -1
- data/lib/tree_haver.rb +143 -24
- data/sig/tree_haver.rbs +18 -1
- data.tar.gz.sig +0 -0
- metadata +8 -4
- metadata.gz.sig +0 -0
data/lib/tree_haver/node.rb
CHANGED
|
@@ -165,10 +165,20 @@ module TreeHaver
|
|
|
165
165
|
def start_point
|
|
166
166
|
if @inner_node.respond_to?(:start_point)
|
|
167
167
|
point = @inner_node.start_point
|
|
168
|
-
Point
|
|
168
|
+
# Handle both Point objects and hashes
|
|
169
|
+
if point.is_a?(Hash)
|
|
170
|
+
Point.new(point[:row], point[:column])
|
|
171
|
+
else
|
|
172
|
+
Point.new(point.row, point.column)
|
|
173
|
+
end
|
|
169
174
|
elsif @inner_node.respond_to?(:start_position)
|
|
170
175
|
point = @inner_node.start_position
|
|
171
|
-
Point
|
|
176
|
+
# Handle both Point objects and hashes
|
|
177
|
+
if point.is_a?(Hash)
|
|
178
|
+
Point.new(point[:row], point[:column])
|
|
179
|
+
else
|
|
180
|
+
Point.new(point.row, point.column)
|
|
181
|
+
end
|
|
172
182
|
else
|
|
173
183
|
raise TreeHaver::Error, "Backend node does not support start_point/start_position"
|
|
174
184
|
end
|
|
@@ -180,15 +190,71 @@ module TreeHaver
|
|
|
180
190
|
def end_point
|
|
181
191
|
if @inner_node.respond_to?(:end_point)
|
|
182
192
|
point = @inner_node.end_point
|
|
183
|
-
Point
|
|
193
|
+
# Handle both Point objects and hashes
|
|
194
|
+
if point.is_a?(Hash)
|
|
195
|
+
Point.new(point[:row], point[:column])
|
|
196
|
+
else
|
|
197
|
+
Point.new(point.row, point.column)
|
|
198
|
+
end
|
|
184
199
|
elsif @inner_node.respond_to?(:end_position)
|
|
185
200
|
point = @inner_node.end_position
|
|
186
|
-
Point
|
|
201
|
+
# Handle both Point objects and hashes
|
|
202
|
+
if point.is_a?(Hash)
|
|
203
|
+
Point.new(point[:row], point[:column])
|
|
204
|
+
else
|
|
205
|
+
Point.new(point.row, point.column)
|
|
206
|
+
end
|
|
187
207
|
else
|
|
188
208
|
raise TreeHaver::Error, "Backend node does not support end_point/end_position"
|
|
189
209
|
end
|
|
190
210
|
end
|
|
191
211
|
|
|
212
|
+
# Get the 1-based line number where this node starts
|
|
213
|
+
#
|
|
214
|
+
# Convenience method that converts 0-based row to 1-based line number.
|
|
215
|
+
# This is useful for error messages and matching with editor line numbers.
|
|
216
|
+
#
|
|
217
|
+
# @return [Integer] 1-based line number
|
|
218
|
+
def start_line
|
|
219
|
+
start_point.row + 1
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Get the 1-based line number where this node ends
|
|
223
|
+
#
|
|
224
|
+
# Convenience method that converts 0-based row to 1-based line number.
|
|
225
|
+
#
|
|
226
|
+
# @return [Integer] 1-based line number
|
|
227
|
+
def end_line
|
|
228
|
+
end_point.row + 1
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Get position information as a hash
|
|
232
|
+
#
|
|
233
|
+
# Returns a hash with 1-based line numbers and 0-based columns.
|
|
234
|
+
# This format is compatible with *-merge gems' FileAnalysisBase.
|
|
235
|
+
#
|
|
236
|
+
# @return [Hash{Symbol => Integer}] Position hash
|
|
237
|
+
# @example
|
|
238
|
+
# node.source_position
|
|
239
|
+
# # => { start_line: 1, end_line: 3, start_column: 0, end_column: 10 }
|
|
240
|
+
def source_position
|
|
241
|
+
{
|
|
242
|
+
start_line: start_line,
|
|
243
|
+
end_line: end_line,
|
|
244
|
+
start_column: start_point.column,
|
|
245
|
+
end_column: end_point.column,
|
|
246
|
+
}
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Get the first child node
|
|
250
|
+
#
|
|
251
|
+
# Convenience method for iteration patterns that expect first_child.
|
|
252
|
+
#
|
|
253
|
+
# @return [Node, nil] First child node or nil if no children
|
|
254
|
+
def first_child
|
|
255
|
+
child(0)
|
|
256
|
+
end
|
|
257
|
+
|
|
192
258
|
# Get the node's text content
|
|
193
259
|
#
|
|
194
260
|
# @return [String]
|
|
@@ -435,10 +501,10 @@ module TreeHaver
|
|
|
435
501
|
|
|
436
502
|
# Compare by position first (start_byte, then end_byte)
|
|
437
503
|
cmp = start_byte <=> other.start_byte
|
|
438
|
-
return cmp
|
|
504
|
+
return cmp if cmp.nonzero?
|
|
439
505
|
|
|
440
506
|
cmp = end_byte <=> other.end_byte
|
|
441
|
-
return cmp
|
|
507
|
+
return cmp if cmp.nonzero?
|
|
442
508
|
|
|
443
509
|
# For nodes at the same position with same span, compare by type
|
|
444
510
|
type <=> other.type
|
data/lib/tree_haver/version.rb
CHANGED
data/lib/tree_haver.rb
CHANGED
|
@@ -10,12 +10,17 @@ require "set"
|
|
|
10
10
|
require_relative "tree_haver/version"
|
|
11
11
|
require_relative "tree_haver/language_registry"
|
|
12
12
|
|
|
13
|
-
# TreeHaver is a cross-Ruby adapter for
|
|
13
|
+
# TreeHaver is a cross-Ruby adapter for code parsing with 10 backends.
|
|
14
14
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
15
|
+
# Provides a unified API for parsing source code across MRI Ruby, JRuby, and TruffleRuby
|
|
16
|
+
# using tree-sitter grammars or language-specific native parsers.
|
|
17
17
|
#
|
|
18
|
-
#
|
|
18
|
+
# Supports 10 backends:
|
|
19
|
+
# - Tree-sitter: MRI (C), Rust, FFI, Java
|
|
20
|
+
# - Native parsers: Prism (Ruby), Psych (YAML), Commonmarker (Markdown), Markly (GFM)
|
|
21
|
+
# - Pure Ruby: Citrus (portable fallback)
|
|
22
|
+
#
|
|
23
|
+
# @example Basic usage with tree-sitter
|
|
19
24
|
# # Load a language grammar
|
|
20
25
|
# language = TreeHaver::Language.from_library(
|
|
21
26
|
# "/usr/local/lib/libtree-sitter-toml.so",
|
|
@@ -30,8 +35,28 @@ require_relative "tree_haver/language_registry"
|
|
|
30
35
|
# tree = parser.parse("[package]\nname = \"my-app\"")
|
|
31
36
|
# root = tree.root_node
|
|
32
37
|
#
|
|
33
|
-
# #
|
|
34
|
-
# root.
|
|
38
|
+
# # Use unified Position API (works across all backends)
|
|
39
|
+
# puts root.start_line # => 1 (1-based)
|
|
40
|
+
# puts root.source_position # => {start_line:, end_line:, start_column:, end_column:}
|
|
41
|
+
#
|
|
42
|
+
# @example Using language-specific backends
|
|
43
|
+
# # Parse Ruby with Prism
|
|
44
|
+
# TreeHaver.backend = :prism
|
|
45
|
+
# parser = TreeHaver::Parser.new
|
|
46
|
+
# parser.language = TreeHaver::Backends::Prism::Language.ruby
|
|
47
|
+
# tree = parser.parse("class Example; end")
|
|
48
|
+
#
|
|
49
|
+
# # Parse YAML with Psych
|
|
50
|
+
# TreeHaver.backend = :psych
|
|
51
|
+
# parser = TreeHaver::Parser.new
|
|
52
|
+
# parser.language = TreeHaver::Backends::Psych::Language.yaml
|
|
53
|
+
# tree = parser.parse("key: value")
|
|
54
|
+
#
|
|
55
|
+
# # Parse Markdown with Commonmarker
|
|
56
|
+
# TreeHaver.backend = :commonmarker
|
|
57
|
+
# parser = TreeHaver::Parser.new
|
|
58
|
+
# parser.language = TreeHaver::Backends::Commonmarker::Language.markdown
|
|
59
|
+
# tree = parser.parse("# Heading\nParagraph")
|
|
35
60
|
#
|
|
36
61
|
# @example Using language registration
|
|
37
62
|
# TreeHaver.register_language(:toml, path: "/usr/local/lib/libtree-sitter-toml.so")
|
|
@@ -43,22 +68,21 @@ require_relative "tree_haver/language_registry"
|
|
|
43
68
|
# finder.register! if finder.available?
|
|
44
69
|
# language = TreeHaver::Language.toml
|
|
45
70
|
#
|
|
46
|
-
# @example Using GrammarFinder in a *-merge gem
|
|
47
|
-
# # Each merge gem (toml-merge, json-merge, bash-merge) uses the same pattern
|
|
48
|
-
# finder = TreeHaver::GrammarFinder.new(:toml) # or :json, :bash, etc.
|
|
49
|
-
# if finder.available?
|
|
50
|
-
# finder.register!
|
|
51
|
-
# else
|
|
52
|
-
# warn finder.not_found_message
|
|
53
|
-
# end
|
|
54
|
-
#
|
|
55
71
|
# @example Selecting a backend
|
|
56
|
-
# TreeHaver.backend = :
|
|
57
|
-
# TreeHaver.backend = :
|
|
58
|
-
# TreeHaver.backend = :
|
|
72
|
+
# TreeHaver.backend = :mri # Force MRI (ruby_tree_sitter)
|
|
73
|
+
# TreeHaver.backend = :rust # Force Rust (tree_stump)
|
|
74
|
+
# TreeHaver.backend = :ffi # Force FFI
|
|
75
|
+
# TreeHaver.backend = :java # Force Java (JRuby)
|
|
76
|
+
# TreeHaver.backend = :prism # Force Prism (Ruby)
|
|
77
|
+
# TreeHaver.backend = :psych # Force Psych (YAML)
|
|
78
|
+
# TreeHaver.backend = :commonmarker # Force Commonmarker (Markdown)
|
|
79
|
+
# TreeHaver.backend = :markly # Force Markly (GFM)
|
|
80
|
+
# TreeHaver.backend = :citrus # Force Citrus (pure Ruby)
|
|
81
|
+
# TreeHaver.backend = :auto # Auto-select (default)
|
|
59
82
|
#
|
|
60
83
|
# @see https://tree-sitter.github.io/tree-sitter/ tree-sitter documentation
|
|
61
84
|
# @see GrammarFinder For automatic grammar library discovery
|
|
85
|
+
# @see Backends For available parsing backends
|
|
62
86
|
module TreeHaver
|
|
63
87
|
# Base error class for TreeHaver exceptions
|
|
64
88
|
# @see https://github.com/Faveod/ruby-tree-sitter/pull/83 for inherit from Exception reasoning
|
|
@@ -115,12 +139,17 @@ module TreeHaver
|
|
|
115
139
|
# - {Backends::FFI} - Uses Ruby FFI to call libtree-sitter directly
|
|
116
140
|
# - {Backends::Java} - Uses JRuby's Java integration
|
|
117
141
|
# - {Backends::Citrus} - Uses Citrus PEG parser (pure Ruby, portable)
|
|
142
|
+
# - {Backends::Prism} - Uses Ruby's built-in Prism parser (Ruby-only, stdlib in 3.4+)
|
|
118
143
|
module Backends
|
|
119
144
|
autoload :MRI, File.join(__dir__, "tree_haver", "backends", "mri")
|
|
120
145
|
autoload :Rust, File.join(__dir__, "tree_haver", "backends", "rust")
|
|
121
146
|
autoload :FFI, File.join(__dir__, "tree_haver", "backends", "ffi")
|
|
122
147
|
autoload :Java, File.join(__dir__, "tree_haver", "backends", "java")
|
|
123
148
|
autoload :Citrus, File.join(__dir__, "tree_haver", "backends", "citrus")
|
|
149
|
+
autoload :Prism, File.join(__dir__, "tree_haver", "backends", "prism")
|
|
150
|
+
autoload :Psych, File.join(__dir__, "tree_haver", "backends", "psych")
|
|
151
|
+
autoload :Commonmarker, File.join(__dir__, "tree_haver", "backends", "commonmarker")
|
|
152
|
+
autoload :Markly, File.join(__dir__, "tree_haver", "backends", "markly")
|
|
124
153
|
|
|
125
154
|
# Known backend conflicts
|
|
126
155
|
#
|
|
@@ -135,6 +164,10 @@ module TreeHaver
|
|
|
135
164
|
ffi: [:mri], # FFI segfaults if MRI (ruby_tree_sitter) has been loaded
|
|
136
165
|
java: [],
|
|
137
166
|
citrus: [],
|
|
167
|
+
prism: [], # Prism has no conflicts with other backends
|
|
168
|
+
psych: [], # Psych has no conflicts with other backends
|
|
169
|
+
commonmarker: [], # Commonmarker has no conflicts with other backends
|
|
170
|
+
markly: [], # Markly has no conflicts with other backends
|
|
138
171
|
}.freeze
|
|
139
172
|
end
|
|
140
173
|
|
|
@@ -201,7 +234,10 @@ module TreeHaver
|
|
|
201
234
|
# @return [Boolean]
|
|
202
235
|
# @example Disable protection for testing
|
|
203
236
|
# TreeHaver.backend_protect = false
|
|
204
|
-
|
|
237
|
+
def backend_protect=(value)
|
|
238
|
+
@backend_protect_mutex ||= Mutex.new
|
|
239
|
+
@backend_protect_mutex.synchronize { @backend_protect = value }
|
|
240
|
+
end
|
|
205
241
|
|
|
206
242
|
# Check if backend conflict protection is enabled
|
|
207
243
|
#
|
|
@@ -267,6 +303,10 @@ module TreeHaver
|
|
|
267
303
|
when "ffi" then :ffi
|
|
268
304
|
when "java" then :java
|
|
269
305
|
when "citrus" then :citrus
|
|
306
|
+
when "prism" then :prism
|
|
307
|
+
when "psych" then :psych
|
|
308
|
+
when "commonmarker" then :commonmarker
|
|
309
|
+
when "markly" then :markly
|
|
270
310
|
else :auto
|
|
271
311
|
end
|
|
272
312
|
end
|
|
@@ -463,6 +503,14 @@ module TreeHaver
|
|
|
463
503
|
Backends::Java
|
|
464
504
|
when :citrus
|
|
465
505
|
Backends::Citrus
|
|
506
|
+
when :prism
|
|
507
|
+
Backends::Prism
|
|
508
|
+
when :psych
|
|
509
|
+
Backends::Psych
|
|
510
|
+
when :commonmarker
|
|
511
|
+
Backends::Commonmarker
|
|
512
|
+
when :markly
|
|
513
|
+
Backends::Markly
|
|
466
514
|
when :auto
|
|
467
515
|
backend_module # Fall back to normal resolution for :auto
|
|
468
516
|
else
|
|
@@ -519,6 +567,14 @@ module TreeHaver
|
|
|
519
567
|
Backends::Java
|
|
520
568
|
when :citrus
|
|
521
569
|
Backends::Citrus
|
|
570
|
+
when :prism
|
|
571
|
+
Backends::Prism
|
|
572
|
+
when :psych
|
|
573
|
+
Backends::Psych
|
|
574
|
+
when :commonmarker
|
|
575
|
+
Backends::Commonmarker
|
|
576
|
+
when :markly
|
|
577
|
+
Backends::Markly
|
|
522
578
|
else
|
|
523
579
|
# auto-select: prefer native/fast backends, fall back to pure Ruby (Citrus)
|
|
524
580
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby" && Backends::Java.available?
|
|
@@ -827,7 +883,8 @@ module TreeHaver
|
|
|
827
883
|
"Registered backends: #{all_backends.keys.inspect}"
|
|
828
884
|
end
|
|
829
885
|
|
|
830
|
-
# For tree-sitter backends,
|
|
886
|
+
# For tree-sitter backends, try to load from path
|
|
887
|
+
# If that fails, fall back to Citrus if available
|
|
831
888
|
if reg && reg[:path]
|
|
832
889
|
path = kwargs[:path] || args.first || reg[:path]
|
|
833
890
|
# Symbol priority: kwargs override > registration > derive from method_name
|
|
@@ -842,7 +899,30 @@ module TreeHaver
|
|
|
842
899
|
# Using symbol-derived name ensures ruby_tree_sitter gets the correct language name
|
|
843
900
|
# e.g., "toml" not "toml_both" when symbol is "tree_sitter_toml"
|
|
844
901
|
name = kwargs[:name] || symbol&.sub(/\Atree_sitter_/, "")
|
|
845
|
-
|
|
902
|
+
|
|
903
|
+
begin
|
|
904
|
+
return from_library(path, symbol: symbol, name: name)
|
|
905
|
+
rescue NotAvailable, ArgumentError, LoadError, FFI::NotFoundError => _e
|
|
906
|
+
# Tree-sitter failed to load - check for Citrus fallback
|
|
907
|
+
# This handles cases where:
|
|
908
|
+
# - The .so file doesn't exist or can't be loaded (NotAvailable, LoadError)
|
|
909
|
+
# - FFI can't find required symbols like ts_parser_new (FFI::NotFoundError)
|
|
910
|
+
# - Invalid arguments were provided (ArgumentError)
|
|
911
|
+
citrus_reg = all_backends[:citrus]
|
|
912
|
+
if citrus_reg && citrus_reg[:grammar_module]
|
|
913
|
+
return Backends::Citrus::Language.new(citrus_reg[:grammar_module])
|
|
914
|
+
end
|
|
915
|
+
# No Citrus fallback available, re-raise the original error
|
|
916
|
+
raise
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
# No tree-sitter path registered - check for Citrus fallback
|
|
921
|
+
# This enables auto-fallback when tree-sitter grammar is not installed
|
|
922
|
+
# but a Citrus grammar (pure Ruby) is available
|
|
923
|
+
citrus_reg = all_backends[:citrus]
|
|
924
|
+
if citrus_reg && citrus_reg[:grammar_module]
|
|
925
|
+
return Backends::Citrus::Language.new(citrus_reg[:grammar_module])
|
|
846
926
|
end
|
|
847
927
|
|
|
848
928
|
# No appropriate registration found
|
|
@@ -913,8 +993,28 @@ module TreeHaver
|
|
|
913
993
|
end
|
|
914
994
|
end
|
|
915
995
|
|
|
916
|
-
|
|
917
|
-
|
|
996
|
+
# Try to create the parser, with fallback to Citrus if tree-sitter fails
|
|
997
|
+
# This enables auto-fallback when tree-sitter runtime isn't available
|
|
998
|
+
begin
|
|
999
|
+
@impl = mod::Parser.new
|
|
1000
|
+
@explicit_backend = backend # Remember for introspection (always a Symbol or nil)
|
|
1001
|
+
rescue NoMethodError, FFI::NotFoundError, LoadError => e
|
|
1002
|
+
# Tree-sitter backend failed (likely missing runtime library)
|
|
1003
|
+
# Try Citrus as fallback if we weren't explicitly asked for a specific backend
|
|
1004
|
+
if backend.nil? || backend == :auto
|
|
1005
|
+
if Backends::Citrus.available?
|
|
1006
|
+
@impl = Backends::Citrus::Parser.new
|
|
1007
|
+
@explicit_backend = :citrus
|
|
1008
|
+
else
|
|
1009
|
+
# No fallback available, re-raise original error
|
|
1010
|
+
raise NotAvailable, "Tree-sitter backend failed: #{e.message}. " \
|
|
1011
|
+
"Citrus fallback not available. Install tree-sitter runtime or citrus gem."
|
|
1012
|
+
end
|
|
1013
|
+
else
|
|
1014
|
+
# Explicit backend was requested, don't fallback
|
|
1015
|
+
raise
|
|
1016
|
+
end
|
|
1017
|
+
end
|
|
918
1018
|
end
|
|
919
1019
|
|
|
920
1020
|
# Get the backend this parser is using (for introspection)
|
|
@@ -952,12 +1052,23 @@ module TreeHaver
|
|
|
952
1052
|
# @example
|
|
953
1053
|
# parser.language = TreeHaver::Language.from_library("/path/to/grammar.so")
|
|
954
1054
|
def language=(lang)
|
|
1055
|
+
# Check if this is a Citrus language - if so, we need a Citrus parser
|
|
1056
|
+
# This enables automatic backend switching when tree-sitter fails and
|
|
1057
|
+
# falls back to Citrus
|
|
1058
|
+
if lang.is_a?(Backends::Citrus::Language)
|
|
1059
|
+
unless @impl.is_a?(Backends::Citrus::Parser)
|
|
1060
|
+
# Switch to Citrus parser to match the Citrus language
|
|
1061
|
+
@impl = Backends::Citrus::Parser.new
|
|
1062
|
+
@explicit_backend = :citrus
|
|
1063
|
+
end
|
|
1064
|
+
end
|
|
1065
|
+
|
|
955
1066
|
# Unwrap the language before passing to backend
|
|
956
1067
|
# Backends receive raw language objects, never TreeHaver wrappers
|
|
957
1068
|
inner_lang = unwrap_language(lang)
|
|
958
1069
|
@impl.language = inner_lang
|
|
959
1070
|
# Return the original (possibly wrapped) language for consistency
|
|
960
|
-
lang
|
|
1071
|
+
lang # rubocop:disable Lint/Void (intentional return value)
|
|
961
1072
|
end
|
|
962
1073
|
|
|
963
1074
|
private
|
|
@@ -1028,6 +1139,14 @@ module TreeHaver
|
|
|
1028
1139
|
return lang.impl if lang.respond_to?(:impl)
|
|
1029
1140
|
when :citrus
|
|
1030
1141
|
return lang.grammar_module if lang.respond_to?(:grammar_module)
|
|
1142
|
+
when :prism
|
|
1143
|
+
return lang # Prism backend expects the Language wrapper
|
|
1144
|
+
when :psych
|
|
1145
|
+
return lang # Psych backend expects the Language wrapper
|
|
1146
|
+
when :commonmarker
|
|
1147
|
+
return lang # Commonmarker backend expects the Language wrapper
|
|
1148
|
+
when :markly
|
|
1149
|
+
return lang # Markly backend expects the Language wrapper
|
|
1031
1150
|
else
|
|
1032
1151
|
# Unknown backend (e.g., test backend)
|
|
1033
1152
|
# Try generic unwrapping methods for flexibility in testing
|
data/sig/tree_haver.rbs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Type definitions for TreeHaver
|
|
2
2
|
#
|
|
3
|
-
# TreeHaver is a cross-Ruby adapter for
|
|
3
|
+
# TreeHaver is a cross-Ruby adapter for code parsing with 10 backends:
|
|
4
|
+
# - Tree-sitter: MRI, Rust, FFI, Java
|
|
5
|
+
# - Native parsers: Prism (Ruby), Psych (YAML), Commonmarker (Markdown), Markly (GFM)
|
|
6
|
+
# - Pure Ruby: Citrus
|
|
4
7
|
|
|
5
8
|
module TreeHaver
|
|
6
9
|
VERSION: String
|
|
@@ -134,6 +137,20 @@ module TreeHaver
|
|
|
134
137
|
# Get the end point (row, column)
|
|
135
138
|
def end_point: () -> Point
|
|
136
139
|
|
|
140
|
+
# Position API - consistent across all backends
|
|
141
|
+
# Get 1-based line number where node starts
|
|
142
|
+
def start_line: () -> Integer
|
|
143
|
+
|
|
144
|
+
# Get 1-based line number where node ends
|
|
145
|
+
def end_line: () -> Integer
|
|
146
|
+
|
|
147
|
+
# Get complete position information as hash
|
|
148
|
+
# Returns {start_line:, end_line:, start_column:, end_column:}
|
|
149
|
+
def source_position: () -> Hash[Symbol, Integer]
|
|
150
|
+
|
|
151
|
+
# Get first child node (convenience method)
|
|
152
|
+
def first_child: () -> Node?
|
|
153
|
+
|
|
137
154
|
# Get the number of child nodes
|
|
138
155
|
def child_count: () -> Integer
|
|
139
156
|
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tree_haver
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -241,9 +241,13 @@ files:
|
|
|
241
241
|
- SECURITY.md
|
|
242
242
|
- lib/tree_haver.rb
|
|
243
243
|
- lib/tree_haver/backends/citrus.rb
|
|
244
|
+
- lib/tree_haver/backends/commonmarker.rb
|
|
244
245
|
- lib/tree_haver/backends/ffi.rb
|
|
245
246
|
- lib/tree_haver/backends/java.rb
|
|
247
|
+
- lib/tree_haver/backends/markly.rb
|
|
246
248
|
- lib/tree_haver/backends/mri.rb
|
|
249
|
+
- lib/tree_haver/backends/prism.rb
|
|
250
|
+
- lib/tree_haver/backends/psych.rb
|
|
247
251
|
- lib/tree_haver/backends/rust.rb
|
|
248
252
|
- lib/tree_haver/citrus_grammar_finder.rb
|
|
249
253
|
- lib/tree_haver/compat.rb
|
|
@@ -262,10 +266,10 @@ licenses:
|
|
|
262
266
|
- MIT
|
|
263
267
|
metadata:
|
|
264
268
|
homepage_uri: https://tree-haver.galtzo.com/
|
|
265
|
-
source_code_uri: https://github.com/kettle-rb/tree_haver/tree/v3.
|
|
266
|
-
changelog_uri: https://github.com/kettle-rb/tree_haver/blob/v3.
|
|
269
|
+
source_code_uri: https://github.com/kettle-rb/tree_haver/tree/v3.1.0
|
|
270
|
+
changelog_uri: https://github.com/kettle-rb/tree_haver/blob/v3.1.0/CHANGELOG.md
|
|
267
271
|
bug_tracker_uri: https://github.com/kettle-rb/tree_haver/issues
|
|
268
|
-
documentation_uri: https://www.rubydoc.info/gems/tree_haver/3.
|
|
272
|
+
documentation_uri: https://www.rubydoc.info/gems/tree_haver/3.1.0
|
|
269
273
|
funding_uri: https://github.com/sponsors/pboling
|
|
270
274
|
wiki_uri: https://github.com/kettle-rb/tree_haver/wiki
|
|
271
275
|
news_uri: https://www.railsbling.com/tags/tree_haver
|
metadata.gz.sig
CHANGED
|
Binary file
|