tree_haver 5.0.5 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.md +13 -0
  4. data/README.md +299 -660
  5. data/lib/tree_haver/backend_api.rb +78 -35
  6. data/lib/tree_haver/backend_context.rb +28 -0
  7. data/lib/tree_haver/backend_registry.rb +119 -382
  8. data/lib/tree_haver/backends/citrus.rb +55 -53
  9. data/lib/tree_haver/backends/ffi.rb +105 -101
  10. data/lib/tree_haver/backends/java.rb +92 -76
  11. data/lib/tree_haver/backends/mri.rb +44 -39
  12. data/lib/tree_haver/backends/parslet.rb +53 -48
  13. data/lib/tree_haver/backends/prism.rb +140 -43
  14. data/lib/tree_haver/backends/psych.rb +30 -26
  15. data/lib/tree_haver/backends/rust.rb +21 -17
  16. data/lib/tree_haver/backends/tslp.rb +274 -0
  17. data/lib/tree_haver/base/comment.rb +320 -0
  18. data/lib/tree_haver/base/language.rb +2 -2
  19. data/lib/tree_haver/base/node.rb +39 -31
  20. data/lib/tree_haver/base/parser.rb +4 -0
  21. data/lib/tree_haver/base/point.rb +3 -3
  22. data/lib/tree_haver/base/tree.rb +1 -1
  23. data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
  24. data/lib/tree_haver/contracts.rb +1025 -0
  25. data/lib/tree_haver/grammar_finder.rb +125 -70
  26. data/lib/tree_haver/kaitai_backend.rb +30 -0
  27. data/lib/tree_haver/language.rb +36 -37
  28. data/lib/tree_haver/language_pack.rb +41 -0
  29. data/lib/tree_haver/language_registry.rb +34 -3
  30. data/lib/tree_haver/library_path_utils.rb +7 -7
  31. data/lib/tree_haver/node.rb +40 -31
  32. data/lib/tree_haver/parser.rb +44 -37
  33. data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
  34. data/lib/tree_haver/path_validator.rb +39 -36
  35. data/lib/tree_haver/peg_backends.rb +76 -0
  36. data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
  37. data/lib/tree_haver/rspec.rb +1 -31
  38. data/lib/tree_haver/tree.rb +16 -7
  39. data/lib/tree_haver/version.rb +5 -14
  40. data/lib/tree_haver.rb +536 -1240
  41. data/sig/tree_haver.rbs +1 -229
  42. data.tar.gz.sig +0 -0
  43. metadata +154 -70
  44. metadata.gz.sig +0 -0
  45. data/CHANGELOG.md +0 -1393
  46. data/CITATION.cff +0 -20
  47. data/CODE_OF_CONDUCT.md +0 -134
  48. data/CONTRIBUTING.md +0 -359
  49. data/FUNDING.md +0 -74
  50. data/LICENSE.txt +0 -21
  51. data/REEK +0 -0
  52. data/RUBOCOP.md +0 -71
  53. data/SECURITY.md +0 -21
  54. data/lib/tree_haver/base.rb +0 -12
  55. data/lib/tree_haver/compat.rb +0 -43
  56. data/lib/tree_haver/rspec/testable_node.rb +0 -217
  57. data/sig/tree_haver/backends.rbs +0 -352
  58. data/sig/tree_haver/grammar_finder.rbs +0 -29
  59. data/sig/tree_haver/path_validator.rbs +0 -32
@@ -39,17 +39,18 @@ module TreeHaver
39
39
  # end
40
40
  class << self
41
41
  def available?
42
- return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
43
- @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
42
+ return @loaded if @load_attempted
43
+
44
+ @load_attempted = true
44
45
  begin
45
- require "parslet"
46
- @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
46
+ require 'parslet'
47
+ @loaded = true
47
48
  rescue LoadError
48
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
49
+ @loaded = false
49
50
  rescue StandardError
50
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
51
+ @loaded = false
51
52
  end
52
- @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
53
+ @loaded
53
54
  end
54
55
 
55
56
  # Reset the load state (primarily for testing)
@@ -57,8 +58,8 @@ module TreeHaver
57
58
  # @return [void]
58
59
  # @api private
59
60
  def reset!
60
- @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
61
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
61
+ @load_attempted = false
62
+ @loaded = false
62
63
  end
63
64
 
64
65
  # Get capabilities supported by this backend
@@ -66,15 +67,17 @@ module TreeHaver
66
67
  # @return [Hash{Symbol => Object}] capability map
67
68
  # @example
68
69
  # TreeHaver::Backends::Parslet.capabilities
69
- # # => { backend: :parslet, query: false, bytes_field: true, incremental: false }
70
+ # # => { backend: :parslet, query: false, bytes_field: true, incremental: false, comment_support: :none }
70
71
  def capabilities
71
72
  return {} unless available?
73
+
72
74
  {
73
75
  backend: :parslet,
74
76
  query: false, # Parslet doesn't have a query API like tree-sitter
75
77
  bytes_field: true, # Parslet::Slice provides offset and length
76
78
  incremental: false, # Parslet doesn't support incremental parsing
77
79
  pure_ruby: true, # Parslet is pure Ruby (portable)
80
+ comment_support: :none
78
81
  }
79
82
  end
80
83
  end
@@ -102,8 +105,8 @@ module TreeHaver
102
105
  def initialize(grammar_class)
103
106
  unless valid_grammar_class?(grammar_class)
104
107
  raise TreeHaver::NotAvailable,
105
- "Grammar class must be a Parslet::Parser subclass or respond to :new and return a parser with :parse. " \
106
- "Expected a Parslet grammar class (e.g., TOML::Parslet)."
108
+ 'Grammar class must be a Parslet::Parser subclass or respond to :new and return a parser with :parse. ' \
109
+ 'Expected a Parslet grammar class (e.g., TOML::Parslet).'
107
110
  end
108
111
  @grammar_class = grammar_class
109
112
  @backend = :parslet
@@ -118,12 +121,12 @@ module TreeHaver
118
121
  # Derive name from grammar class (e.g., TOML::Parslet -> :toml)
119
122
  return :unknown unless @grammar_class.respond_to?(:name) && @grammar_class.name
120
123
 
121
- name = @grammar_class.name.to_s.split("::").first.downcase
124
+ name = @grammar_class.name.to_s.split('::').first.downcase
122
125
  name.to_sym
123
126
  end
124
127
 
125
128
  # Alias for language_name (API compatibility)
126
- alias_method :name, :language_name
129
+ alias name language_name
127
130
 
128
131
  # Compare languages for equality
129
132
  #
@@ -147,7 +150,7 @@ module TreeHaver
147
150
  end
148
151
 
149
152
  # Alias eql? to ==
150
- alias_method :eql?, :==
153
+ alias eql? ==
151
154
 
152
155
  # Load language from library path (API compatibility)
153
156
  #
@@ -169,13 +172,13 @@ module TreeHaver
169
172
  def from_library(path = nil, symbol: nil, name: nil)
170
173
  # Derive language name from path, symbol, or explicit name
171
174
  lang_name = name&.to_sym ||
172
- symbol&.to_s&.sub(/^tree_sitter_/, "")&.to_sym ||
173
- path && TreeHaver::LibraryPathUtils.derive_language_name_from_path(path)&.to_sym
175
+ symbol&.to_s&.sub(/^tree_sitter_/, '')&.to_sym ||
176
+ path && TreeHaver::LibraryPathUtils.derive_language_name_from_path(path)&.to_sym
174
177
 
175
178
  unless lang_name
176
179
  raise TreeHaver::NotAvailable,
177
- "Parslet backend requires a language name. " \
178
- "Provide name: parameter or register a grammar with TreeHaver.register_language."
180
+ 'Parslet backend requires a language name. ' \
181
+ 'Provide name: parameter or register a grammar with TreeHaver.register_language.'
179
182
  end
180
183
 
181
184
  # Look up registered Parslet grammar
@@ -183,15 +186,15 @@ module TreeHaver
183
186
 
184
187
  unless registration
185
188
  raise TreeHaver::NotAvailable,
186
- "No Parslet grammar registered for #{lang_name.inspect}. " \
187
- "Register one with: TreeHaver.register_language(:#{lang_name}, grammar_class: YourGrammar)"
189
+ "No Parslet grammar registered for #{lang_name.inspect}. " \
190
+ "Register one with: TreeHaver.register_language(:#{lang_name}, grammar_class: YourGrammar)"
188
191
  end
189
192
 
190
193
  grammar_class = registration[:grammar_class]
191
194
  new(grammar_class)
192
195
  end
193
196
 
194
- alias_method :from_path, :from_library
197
+ alias from_path from_library
195
198
  end
196
199
 
197
200
  private
@@ -200,9 +203,7 @@ module TreeHaver
200
203
  return false unless klass.respond_to?(:new)
201
204
 
202
205
  # Check if it's a Parslet::Parser subclass
203
- if defined?(::Parslet::Parser)
204
- return true if klass < ::Parslet::Parser
205
- end
206
+ return true if defined?(::Parslet::Parser) && (klass < ::Parslet::Parser)
206
207
 
207
208
  # Fallback: check if it can create an instance that responds to parse
208
209
  begin
@@ -222,10 +223,14 @@ module TreeHaver
222
223
  #
223
224
  # @raise [TreeHaver::NotAvailable] if parslet gem is not available
224
225
  def initialize
225
- raise TreeHaver::NotAvailable, "parslet gem not available" unless Parslet.available?
226
+ raise TreeHaver::NotAvailable, 'parslet gem not available' unless Parslet.available?
227
+
226
228
  @grammar = nil
229
+ @backend = :parslet
227
230
  end
228
231
 
232
+ attr_reader :backend
233
+
229
234
  # Set the grammar for this parser
230
235
  #
231
236
  # Accepts either a Parslet::Language wrapper or a raw Parslet grammar class.
@@ -241,16 +246,16 @@ module TreeHaver
241
246
  def language=(grammar)
242
247
  # Accept Language wrapper or raw grammar class
243
248
  actual_grammar = case grammar
244
- when Language
245
- grammar.grammar_class
246
- else
247
- grammar
248
- end
249
+ when Language
250
+ grammar.grammar_class
251
+ else
252
+ grammar
253
+ end
249
254
 
250
255
  unless actual_grammar.respond_to?(:new)
251
256
  raise ArgumentError,
252
- "Expected Parslet grammar class with new method or Language wrapper, " \
253
- "got #{grammar.class}"
257
+ 'Expected Parslet grammar class with new method or Language wrapper, ' \
258
+ "got #{grammar.class}"
254
259
  end
255
260
  @grammar = actual_grammar
256
261
  end
@@ -262,7 +267,7 @@ module TreeHaver
262
267
  # @raise [TreeHaver::NotAvailable] if no grammar is set
263
268
  # @raise [::Parslet::ParseFailed] if parsing fails
264
269
  def parse(source)
265
- raise TreeHaver::NotAvailable, "No grammar loaded" unless @grammar
270
+ raise TreeHaver::NotAvailable, 'No grammar loaded' unless @grammar
266
271
 
267
272
  begin
268
273
  parser_instance = @grammar.new
@@ -283,7 +288,7 @@ module TreeHaver
283
288
  # @param source [String] the source code to parse
284
289
  # @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
285
290
  def parse_string(old_tree, source) # rubocop:disable Lint/UnusedMethodArgument
286
- parse(source) # Parslet doesn't support incremental parsing
291
+ parse(source) # Parslet doesn't support incremental parsing
287
292
  end
288
293
  end
289
294
 
@@ -307,7 +312,7 @@ module TreeHaver
307
312
  end
308
313
 
309
314
  def root_node
310
- Node.new(@parslet_result, @source, type: "document")
315
+ Node.new(@parslet_result, @source, type: 'document')
311
316
  end
312
317
  end
313
318
 
@@ -426,7 +431,7 @@ module TreeHaver
426
431
  when String
427
432
  @value
428
433
  when Hash, Array
429
- @source[start_byte...end_byte] || ""
434
+ @source[start_byte...end_byte] || ''
430
435
  else
431
436
  @value.to_s
432
437
  end
@@ -442,11 +447,13 @@ module TreeHaver
442
447
  when Hash
443
448
  keys = @value.keys
444
449
  return if index >= keys.size
450
+
445
451
  key = keys[index]
446
452
  Node.new(@value[key], @source, key: key)
447
453
  when Array
448
454
  return if index >= @value.size
449
- Node.new(@value[index], @source, type: "element")
455
+
456
+ Node.new(@value[index], @source, type: 'element')
450
457
  end
451
458
  end
452
459
 
@@ -482,15 +489,13 @@ module TreeHaver
482
489
  private
483
490
 
484
491
  def calculate_point(offset)
485
- return {row: 0, column: 0} if offset <= 0
492
+ return { row: 0, column: 0 } if offset <= 0
486
493
 
487
494
  lines_before = @source[0...offset].count("\n")
488
- line_start = if offset > 0
489
- @source.rindex("\n", offset - 1)
490
- end
495
+ line_start = (@source.rindex("\n", offset - 1) if offset > 0)
491
496
  line_start ||= -1
492
497
  column = offset - line_start - 1
493
- {row: lines_before, column: column}
498
+ { row: lines_before, column: column }
494
499
  end
495
500
 
496
501
  def infer_type(key)
@@ -498,15 +503,15 @@ module TreeHaver
498
503
 
499
504
  case @value
500
505
  when ::Parslet::Slice
501
- "slice"
506
+ 'slice'
502
507
  when Hash
503
- "hash"
508
+ 'hash'
504
509
  when Array
505
- "array"
510
+ 'array'
506
511
  when String
507
- "string"
512
+ 'string'
508
513
  else
509
- "unknown"
514
+ 'unknown'
510
515
  end
511
516
  end
512
517
 
@@ -37,19 +37,20 @@ module TreeHaver
37
37
  # end
38
38
  class << self
39
39
  def available?
40
- return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
41
- @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
40
+ return @loaded if @load_attempted
41
+
42
+ @load_attempted = true
42
43
  begin
43
- require "prism"
44
- @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
44
+ require 'prism'
45
+ @loaded = true
45
46
  rescue LoadError
46
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
47
+ @loaded = false
47
48
  rescue StandardError
48
- # :nocov: defensive code - StandardError during require is extremely rare
49
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
50
- # :nocov:
49
+ # simplecov:disable defensive code - StandardError during require is extremely rare
50
+ @loaded = false
51
+ # simplecov:enable
51
52
  end
52
- @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
53
+ @loaded
53
54
  end
54
55
 
55
56
  # Reset the load state (primarily for testing)
@@ -57,8 +58,8 @@ module TreeHaver
57
58
  # @return [void]
58
59
  # @api private
59
60
  def reset!
60
- @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
61
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
61
+ @load_attempted = false
62
+ @loaded = false
62
63
  end
63
64
 
64
65
  # Get capabilities supported by this backend
@@ -66,9 +67,10 @@ module TreeHaver
66
67
  # @return [Hash{Symbol => Object}] capability map
67
68
  # @example
68
69
  # TreeHaver::Backends::Prism.capabilities
69
- # # => { backend: :prism, query: false, bytes_field: true, incremental: false, ruby_only: true }
70
+ # # => { backend: :prism, query: false, bytes_field: true, incremental: false, ruby_only: true, comment_support: :partial }
70
71
  def capabilities
71
72
  return {} unless available?
73
+
72
74
  {
73
75
  backend: :prism,
74
76
  query: false, # Prism doesn't have tree-sitter-style queries (has pattern matching)
@@ -77,6 +79,8 @@ module TreeHaver
77
79
  pure_ruby: false, # Prism has native C extension (but also pure Ruby mode)
78
80
  ruby_only: true, # Prism only parses Ruby source code
79
81
  error_tolerant: true, # Prism has excellent error recovery
82
+ comment_support: :partial,
83
+ comment_attachment_hints: true
80
84
  }
81
85
  end
82
86
  end
@@ -96,11 +100,11 @@ module TreeHaver
96
100
  def initialize(name = :ruby, options: {})
97
101
  super(name, backend: :prism, options: options)
98
102
 
99
- unless self.name == :ruby
100
- raise TreeHaver::NotAvailable,
101
- "Prism only supports Ruby parsing. " \
102
- "Got language: #{name.inspect}"
103
- end
103
+ return if self.name == :ruby
104
+
105
+ raise TreeHaver::NotAvailable,
106
+ 'Prism only supports Ruby parsing. ' \
107
+ "Got language: #{name.inspect}"
104
108
  end
105
109
 
106
110
  # Compare languages for equality by options (since name is always :ruby)
@@ -143,14 +147,14 @@ module TreeHaver
143
147
 
144
148
  unless lang_name == :ruby
145
149
  raise TreeHaver::NotAvailable,
146
- "Prism backend only supports Ruby, not #{lang_name}. " \
147
- "Use a tree-sitter backend for #{lang_name} support."
150
+ "Prism backend only supports Ruby, not #{lang_name}. " \
151
+ "Use a tree-sitter backend for #{lang_name} support."
148
152
  end
149
153
 
150
154
  ruby
151
155
  end
152
156
 
153
- alias_method :from_path, :from_library
157
+ alias from_path from_library
154
158
  end
155
159
  end
156
160
 
@@ -163,7 +167,8 @@ module TreeHaver
163
167
  # @raise [TreeHaver::NotAvailable] if prism is not available
164
168
  def initialize
165
169
  super
166
- raise TreeHaver::NotAvailable, "prism not available" unless Prism.available?
170
+ raise TreeHaver::NotAvailable, 'prism not available' unless TreeHaver::Backends::Prism.available?
171
+
167
172
  @options = {}
168
173
  end
169
174
 
@@ -185,11 +190,11 @@ module TreeHaver
185
190
  @options = {}
186
191
  else
187
192
  raise ArgumentError,
188
- "Prism only supports Ruby parsing. Got: #{lang.inspect}"
193
+ "Prism only supports Ruby parsing. Got: #{lang.inspect}"
189
194
  end
190
195
  else
191
196
  raise ArgumentError,
192
- "Expected Prism::Language or :ruby, got #{lang.class}"
197
+ "Expected Prism::Language or :ruby, got #{lang.class}"
193
198
  end
194
199
  end
195
200
 
@@ -199,7 +204,7 @@ module TreeHaver
199
204
  # @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
200
205
  # @raise [TreeHaver::NotAvailable] if no language is set
201
206
  def parse(source)
202
- raise TreeHaver::NotAvailable, "No language loaded (use parser.language = :ruby)" unless @language
207
+ raise TreeHaver::NotAvailable, 'No language loaded (use parser.language = :ruby)' unless @language
203
208
 
204
209
  # Use Prism.parse with options
205
210
  prism_result = ::Prism.parse(source, **@options)
@@ -214,7 +219,7 @@ module TreeHaver
214
219
  # @param source [String] the Ruby source code to parse
215
220
  # @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
216
221
  def parse_string(old_tree, source) # rubocop:disable Lint/UnusedMethodArgument
217
- parse(source) # Prism doesn't support incremental parsing
222
+ parse(source) # Prism doesn't support incremental parsing
218
223
  end
219
224
  end
220
225
 
@@ -262,9 +267,14 @@ module TreeHaver
262
267
 
263
268
  # Get comments from the parse
264
269
  #
265
- # @return [Array<::Prism::Comment>]
270
+ # @return [Array<Comment>]
266
271
  def comments
267
- @parse_result.comments
272
+ @comments ||= begin
273
+ hint_map = comment_hint_map
274
+ @parse_result.comments.map do |comment|
275
+ Comment.new(comment, source: source, attachment_hint: hint_map[comment.object_id])
276
+ end
277
+ end
268
278
  end
269
279
 
270
280
  # Get magic comments (e.g., frozen_string_literal)
@@ -280,6 +290,87 @@ module TreeHaver
280
290
  def data_loc
281
291
  @parse_result.data_loc
282
292
  end
293
+
294
+ private
295
+
296
+ def comment_hint_map
297
+ comments = @parse_result.comments
298
+ lines = source&.lines || []
299
+
300
+ comments.each_with_object({}) do |comment, hints|
301
+ hints[comment.object_id] = classify_comment_hint(comment, lines)
302
+ end
303
+ end
304
+
305
+ def classify_comment_hint(comment, lines)
306
+ line = lines[comment.location.start_line - 1].to_s
307
+ prefix = line[0...comment.location.start_column]
308
+ return :inline if prefix.match?(/\S/)
309
+
310
+ remaining_nonempty = Array(lines[comment.location.end_line..]).reject { |candidate| candidate.strip.empty? }
311
+ return :trailing if remaining_nonempty.empty? || remaining_nonempty.all? do |candidate|
312
+ candidate.lstrip.start_with?('#')
313
+ end
314
+
315
+ :leading
316
+ end
317
+ end
318
+
319
+ # Prism comment wrapper.
320
+ #
321
+ # Prism exposes native Ruby comments via Prism::Comment subclasses and
322
+ # Prism::Location objects. This wrapper normalizes those objects onto a
323
+ # small parser-facing contract for downstream consumers.
324
+ class Comment < TreeHaver::Base::Comment
325
+ def location
326
+ inner_comment.location
327
+ end
328
+
329
+ def type
330
+ self.class.comment_type_for(inner_comment.class)
331
+ end
332
+
333
+ def text
334
+ inner_comment.slice
335
+ end
336
+
337
+ def start_byte
338
+ location.start_offset
339
+ end
340
+
341
+ def end_byte
342
+ location.end_offset
343
+ end
344
+
345
+ def start_point
346
+ {
347
+ row: location.start_line - 1,
348
+ column: location.start_column
349
+ }
350
+ end
351
+
352
+ def end_point
353
+ {
354
+ row: location.end_line - 1,
355
+ column: location.end_column
356
+ }
357
+ end
358
+
359
+ def style
360
+ :line
361
+ end
362
+
363
+ def opening_delimiter
364
+ '#'
365
+ end
366
+
367
+ class << self
368
+ def comment_type_for(klass)
369
+ klass.name.split('::').last
370
+ .gsub(/([a-z\d])([A-Z])/, '\\1_\\2')
371
+ .downcase
372
+ end
373
+ end
283
374
  end
284
375
 
285
376
  # Prism node wrapper
@@ -305,11 +396,11 @@ module TreeHaver
305
396
  #
306
397
  # @return [String] node type in snake_case
307
398
  def type
308
- return "nil" if inner_node.nil?
399
+ return 'nil' if inner_node.nil?
309
400
 
310
401
  # Convert class name to snake_case type
311
- class_name = inner_node.class.name.split("::").last
312
- class_name.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, "")
402
+ class_name = inner_node.class.name.split('::').last
403
+ class_name.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '')
313
404
  end
314
405
 
315
406
  # Alias for type (API compatibility)
@@ -323,6 +414,7 @@ module TreeHaver
323
414
  # @return [Integer]
324
415
  def start_byte
325
416
  return 0 if inner_node.nil? || !inner_node.respond_to?(:location)
417
+
326
418
  loc = inner_node.location
327
419
  loc&.start_offset || 0
328
420
  end
@@ -332,6 +424,7 @@ module TreeHaver
332
424
  # @return [Integer]
333
425
  def end_byte
334
426
  return 0 if inner_node.nil? || !inner_node.respond_to?(:location)
427
+
335
428
  loc = inner_node.location
336
429
  loc&.end_offset || 0
337
430
  end
@@ -340,22 +433,24 @@ module TreeHaver
340
433
  #
341
434
  # @return [Hash{Symbol => Integer}]
342
435
  def start_point
343
- return {row: 0, column: 0} if inner_node.nil? || !inner_node.respond_to?(:location)
436
+ return { row: 0, column: 0 } if inner_node.nil? || !inner_node.respond_to?(:location)
437
+
344
438
  loc = inner_node.location
345
- return {row: 0, column: 0} unless loc
439
+ return { row: 0, column: 0 } unless loc
346
440
 
347
- {row: (loc.start_line - 1), column: loc.start_column}
441
+ { row: (loc.start_line - 1), column: loc.start_column }
348
442
  end
349
443
 
350
444
  # Get the end position as row/column (0-based)
351
445
  #
352
446
  # @return [Hash{Symbol => Integer}]
353
447
  def end_point
354
- return {row: 0, column: 0} if inner_node.nil? || !inner_node.respond_to?(:location)
448
+ return { row: 0, column: 0 } if inner_node.nil? || !inner_node.respond_to?(:location)
449
+
355
450
  loc = inner_node.location
356
- return {row: 0, column: 0} unless loc
451
+ return { row: 0, column: 0 } unless loc
357
452
 
358
- {row: (loc.end_line - 1), column: loc.end_column}
453
+ { row: (loc.end_line - 1), column: loc.end_column }
359
454
  end
360
455
 
361
456
  # Get all child nodes
@@ -372,7 +467,7 @@ module TreeHaver
372
467
  #
373
468
  # @return [String]
374
469
  def text
375
- return "" if inner_node.nil?
470
+ return '' if inner_node.nil?
376
471
 
377
472
  if inner_node.respond_to?(:slice)
378
473
  inner_node.slice
@@ -382,7 +477,7 @@ module TreeHaver
382
477
  end
383
478
 
384
479
  # Alias for Prism compatibility
385
- alias_method :slice, :text
480
+ alias slice text
386
481
 
387
482
  # Check if this node has errors
388
483
  #
@@ -391,11 +486,12 @@ module TreeHaver
391
486
  return false if inner_node.nil?
392
487
 
393
488
  # Check if this is an error node type
394
- return true if type.include?("missing") || type.include?("error")
489
+ return true if type.include?('missing') || type.include?('error')
395
490
 
396
491
  # Check children recursively (Prism error nodes are usually children)
397
492
  return false unless inner_node.respond_to?(:child_nodes)
398
- inner_node.child_nodes.compact.any? { |n| n.class.name.to_s.include?("Missing") }
493
+
494
+ inner_node.child_nodes.compact.any? { |n| n.class.name.to_s.include?('Missing') }
399
495
  end
400
496
 
401
497
  # Check if this node is a "missing" node (error recovery)
@@ -403,7 +499,8 @@ module TreeHaver
403
499
  # @return [Boolean]
404
500
  def missing?
405
501
  return false if inner_node.nil?
406
- type.include?("missing")
502
+
503
+ type.include?('missing')
407
504
  end
408
505
 
409
506
  # Get a child by field name (Prism node accessor)
@@ -423,7 +520,7 @@ module TreeHaver
423
520
  result.is_a?(::Prism::Node) ? Node.new(result, source) : nil
424
521
  end
425
522
 
426
- alias_method :field, :child_by_field_name
523
+ alias field child_by_field_name
427
524
 
428
525
  # String representation
429
526
  #
@@ -439,6 +536,7 @@ module TreeHaver
439
536
  # @return [Boolean]
440
537
  def respond_to_missing?(method_name, include_private = false)
441
538
  return false if inner_node.nil?
539
+
442
540
  inner_node.respond_to?(method_name, include_private) || super
443
541
  end
444
542
 
@@ -468,4 +566,3 @@ module TreeHaver
468
566
  end
469
567
  end
470
568
  end
471
-