Class: Rbs::Merge::NodeWrapper

Inherits:
Ast::Merge::NodeWrapperBase
  • Object
show all
Defined in:
lib/rbs/merge/node_wrapper.rb

Overview

Wraps RBS AST nodes with a unified interface for merging. Supports both the RBS gem's native AST and tree-sitter-rbs nodes.

Inherits common functionality from Ast::Merge::NodeWrapperBase:

  • Source context (lines, source, comments)
  • Line info extraction
  • Basic methods: #type, #text, #signature

Adds RBS-specific functionality:

  • Backend awareness for RBS gem/tree-sitter normalization
  • Type predicates using NodeTypeNormalizer
  • Name extraction for declarations and members

Examples:

Basic usage with RBS gem

analysis = FileAnalysis.new(source)
analysis.statements.each do |wrapper|
  puts wrapper.canonical_type  # => :class, :module, etc.
  puts wrapper.name            # => "Foo", "Bar::Baz"
end

See Also:

  • Ast::Merge::NodeWrapperBase

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#backendSymbol (readonly)



52
53
54
# File 'lib/rbs/merge/node_wrapper.rb', line 52

def backend
  @backend
end

Class Method Details

.wrap(node, lines, source: nil, leading_comments: [], inline_comment: nil, backend: :rbs) ⇒ NodeWrapper?

Wrap an RBS node, returning nil for nil input.



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/rbs/merge/node_wrapper.rb', line 37

def wrap(node, lines, source: nil, leading_comments: [], inline_comment: nil, backend: :rbs)
  return if node.nil?

  new(
    node,
    lines: lines,
    source: source,
    leading_comments: leading_comments,
    inline_comment: inline_comment,
    backend: backend
  )
end

Instance Method Details

#alias?Boolean

Check if this is a method alias



152
153
154
# File 'lib/rbs/merge/node_wrapper.rb', line 152

def alias?
  canonical_type == :alias
end

#alias_new_nameString?

Get alias new name



367
368
369
370
371
372
373
374
375
376
# File 'lib/rbs/merge/node_wrapper.rb', line 367

def alias_new_name
  return unless alias?

  if @backend == :rbs
    @node.respond_to?(:new_name) ? @node.new_name.to_s : nil
  else
    method_names = extract_child_texts('method_name')
    method_names.first || extract_child_text('new_name') || extract_child_text('alias_name')
  end
end

#alias_old_nameString?

Get alias old name



380
381
382
383
384
385
386
387
388
389
# File 'lib/rbs/merge/node_wrapper.rb', line 380

def alias_old_name
  return unless alias?

  if @backend == :rbs
    @node.respond_to?(:old_name) ? @node.old_name.to_s : nil
  else
    method_names = extract_child_texts('method_name')
    method_names[1] || extract_child_text('old_name') || extract_child_text('aliased_name')
  end
end

#attr?Boolean

Check if this is any kind of attribute



176
177
178
# File 'lib/rbs/merge/node_wrapper.rb', line 176

def attr?
  i[attr_reader attr_writer attr_accessor].include?(canonical_type)
end

#attr_accessor?Boolean

Check if this is an attribute accessor



170
171
172
# File 'lib/rbs/merge/node_wrapper.rb', line 170

def attr_accessor?
  canonical_type == :attr_accessor
end

#attr_reader?Boolean

Check if this is an attribute reader



158
159
160
# File 'lib/rbs/merge/node_wrapper.rb', line 158

def attr_reader?
  canonical_type == :attr_reader
end

#attr_writer?Boolean

Check if this is an attribute writer



164
165
166
# File 'lib/rbs/merge/node_wrapper.rb', line 164

def attr_writer?
  canonical_type == :attr_writer
end

#canonical_typeSymbol

Get the canonical (normalized) type for this node



74
75
76
# File 'lib/rbs/merge/node_wrapper.rb', line 74

def canonical_type
  NodeTypeNormalizer.canonical_type_for_node(@node, @backend)
end

#civar?Boolean

Check if this is a class instance variable



206
207
208
# File 'lib/rbs/merge/node_wrapper.rb', line 206

def civar?
  canonical_type == :civar
end

#class?Boolean

Check if this is a class declaration



110
111
112
# File 'lib/rbs/merge/node_wrapper.rb', line 110

def class?
  canonical_type == :class
end

#commentObject?

Get the associated comment for this declaration (RBS gem only) RBS gem associates comments with declarations via the comment attribute



101
102
103
104
105
106
# File 'lib/rbs/merge/node_wrapper.rb', line 101

def comment
  return unless @backend == :rbs
  return unless @node.respond_to?(:comment)

  @node.comment
end

#constant?Boolean

Check if this is a constant declaration



134
135
136
# File 'lib/rbs/merge/node_wrapper.rb', line 134

def constant?
  canonical_type == :constant
end

#container?Boolean

Check if this is a container (can have children/members)



236
237
238
# File 'lib/rbs/merge/node_wrapper.rb', line 236

def container?
  NodeTypeNormalizer.container_type?(canonical_type)
end

#cvar?Boolean

Check if this is a class variable



212
213
214
# File 'lib/rbs/merge/node_wrapper.rb', line 212

def cvar?
  canonical_type == :cvar
end

#declaration?Boolean

Check if this is a declaration (class, module, interface, etc.)



224
225
226
# File 'lib/rbs/merge/node_wrapper.rb', line 224

def declaration?
  NodeTypeNormalizer.declaration_type?(canonical_type)
end

#end_lineInteger?

Get the end line of this node



252
253
254
255
256
257
258
# File 'lib/rbs/merge/node_wrapper.rb', line 252

def end_line
  if @backend == :rbs
    @node.location&.end_line
  else
    super
  end
end

#extend?Boolean

Check if this is an extend



188
189
190
# File 'lib/rbs/merge/node_wrapper.rb', line 188

def extend?
  canonical_type == :extend
end

#global?Boolean

Check if this is a global variable declaration



140
141
142
# File 'lib/rbs/merge/node_wrapper.rb', line 140

def global?
  canonical_type == :global
end

#include?Boolean

Check if this is an include



182
183
184
# File 'lib/rbs/merge/node_wrapper.rb', line 182

def include?
  canonical_type == :include
end

#interface?Boolean

Check if this is an interface declaration



122
123
124
# File 'lib/rbs/merge/node_wrapper.rb', line 122

def interface?
  canonical_type == :interface
end

#ivar?Boolean

Check if this is an instance variable



200
201
202
# File 'lib/rbs/merge/node_wrapper.rb', line 200

def ivar?
  canonical_type == :ivar
end

#member?Boolean

Check if this is a member (method, attr, include, etc.)



230
231
232
# File 'lib/rbs/merge/node_wrapper.rb', line 230

def member?
  NodeTypeNormalizer.member_type?(canonical_type)
end

#membersArray<NodeWrapper>

Get members of this container (for class/module/interface)



287
288
289
290
291
292
293
294
295
# File 'lib/rbs/merge/node_wrapper.rb', line 287

def members
  return [] unless container?

  if @backend == :rbs
    extract_rbs_members
  else
    extract_tree_sitter_members
  end
end

#method?Boolean

Check if this is a method definition



146
147
148
# File 'lib/rbs/merge/node_wrapper.rb', line 146

def method?
  canonical_type == :method
end

#method_kindSymbol?

Get method kind (instance, singleton, singleton_instance)



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/rbs/merge/node_wrapper.rb', line 349

def method_kind
  return unless method?

  if @backend == :rbs
    @node.respond_to?(:kind) ? @node.kind : :instance
  else
    return :instance unless @node.respond_to?(:each)

    @node.each do |child|
      return :singleton if child.respond_to?(:type) && child.type.to_sym == :self
    end

    :instance
  end
end

#module?Boolean

Check if this is a module declaration



116
117
118
# File 'lib/rbs/merge/node_wrapper.rb', line 116

def module?
  canonical_type == :module
end

#nameString?

Get the name of this declaration/member



88
89
90
91
92
93
94
95
96
# File 'lib/rbs/merge/node_wrapper.rb', line 88

def name
  return visibility_kind.to_s if visibility_kind

  if @backend == :rbs
    extract_rbs_name
  else
    extract_tree_sitter_name
  end
end

#prepend?Boolean

Check if this is a prepend



194
195
196
# File 'lib/rbs/merge/node_wrapper.rb', line 194

def prepend?
  canonical_type == :prepend
end

#process_additional_options(options) ⇒ Object

Process RBS-specific options (backend)



56
57
58
# File 'lib/rbs/merge/node_wrapper.rb', line 56

def process_additional_options(options)
  @backend = options.fetch(:backend, :rbs)
end

#signatureArray?

Generate a signature for this node



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/rbs/merge/node_wrapper.rb', line 299

def signature
  case canonical_type
  when :class
    [:class, name]
  when :module
    [:module, name]
  when :interface
    [:interface, name]
  when :type_alias
    [:type_alias, name]
  when :constant
    [:constant, name]
  when :global
    [:global, name]
  when :class_alias
    [:class_alias, name]
  when :module_alias
    [:module_alias, name]
  when :method
    kind = method_kind
    [:method, name, kind]
  when :alias
    [:alias, alias_new_name, alias_old_name]
  when :attr_reader
    [:attr_reader, name]
  when :attr_writer
    [:attr_writer, name]
  when :attr_accessor
    [:attr_accessor, name]
  when :include
    [:include, name]
  when :extend
    [:extend, name]
  when :prepend
    [:prepend, name]
  when :ivar
    [:ivar, name]
  when :civar
    [:civar, name]
  when :cvar
    [:cvar, name]
  when :visibility
    [:visibility, visibility_kind]
  else
    [:unknown, canonical_type, start_line]
  end
end

#start_lineInteger?

Get the start line of this node



242
243
244
245
246
247
248
# File 'lib/rbs/merge/node_wrapper.rb', line 242

def start_line
  if @backend == :rbs
    @node.location&.start_line
  else
    super
  end
end

#textString?

Get source text for this node



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/rbs/merge/node_wrapper.rb', line 262

def text
  if @source
    if @backend == :rbs
      location = @node.respond_to?(:location) ? @node.location : nil
      if location&.start_pos && location.end_pos
        exact = @source.byteslice(location.start_pos...location.end_pos)
        return exact unless exact.nil?
      end
    elsif @node.respond_to?(:start_byte) && @node.respond_to?(:end_byte)
      exact = @source.byteslice(@node.start_byte...@node.end_byte)
      return exact unless exact.nil?
    end
  end

  return unless start_line && end_line

  if @lines && start_line.positive? && end_line <= @lines.length
    @lines[(start_line - 1)..(end_line - 1)].join("\n")
  elsif @source
    @source.lines[(start_line - 1)..(end_line - 1)]&.join
  end
end

#typeSymbol

Get the raw type from the underlying node



62
63
64
65
66
67
68
69
70
# File 'lib/rbs/merge/node_wrapper.rb', line 62

def type
  if @backend == :rbs
    # For RBS gem, use class name as type
    @node.class.name.to_sym
  else
    # For tree-sitter, use the node's type
    @node.type.to_sym
  end
end

#type?(type_name) ⇒ Boolean

Check if this node has a specific type (checks both raw and canonical)



81
82
83
84
# File 'lib/rbs/merge/node_wrapper.rb', line 81

def type?(type_name)
  type_sym = type_name.to_sym
  type == type_sym || canonical_type == type_sym
end

#type_alias?Boolean

Check if this is a type alias declaration



128
129
130
# File 'lib/rbs/merge/node_wrapper.rb', line 128

def type_alias?
  canonical_type == :type_alias
end

#visibility?Boolean

Check if this is a visibility member



218
219
220
# File 'lib/rbs/merge/node_wrapper.rb', line 218

def visibility?
  canonical_type == :visibility
end

#visibility_kindSymbol?

Get visibility kind (:public or :private)



393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/rbs/merge/node_wrapper.rb', line 393

def visibility_kind
  return unless visibility?

  if @backend == :rbs
    raw_type = @node.class.name.to_s
    return :public if raw_type.end_with?('::Public')
    return :private if raw_type.end_with?('::Private')

    nil
  else
    extract_tree_sitter_visibility_kind
  end
end