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)

Returns The backend used for parsing (:rbs or :tree_sitter).

Returns:

  • (Symbol)

    The backend used for parsing (:rbs or :tree_sitter)



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.

Parameters:

  • node (Object, nil)

    RBS node to wrap (RBS::AST::* or tree-sitter node)

  • lines (Array<String>)

    Source lines for content extraction

  • source (String, nil) (defaults to: nil)

    Original source string

  • leading_comments (Array<Hash>) (defaults to: [])

    Comments before this node

  • inline_comment (Hash, nil) (defaults to: nil)

    Inline comment on the node’s line

  • backend (Symbol) (defaults to: :rbs)

    The backend used for parsing (:rbs or :tree_sitter)

Returns:

  • (NodeWrapper, nil)

    Wrapped node or nil if node is nil



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

Returns:

  • (Boolean)


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

Returns:

  • (String, nil)


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

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

Returns:

  • (String, nil)


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

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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Symbol)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Object, nil)

    The comment object or nil



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

Returns:

  • (Boolean)


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)

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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.)

Returns:

  • (Boolean)


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

Returns:

  • (Integer, nil)


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

def end_line
  if @backend == :rbs
    @node.location&.end_line
  else
    pos = @node.end_point
    pos ? pos.row + 1 : nil
  end
end

#extend?Boolean

Check if this is an extend

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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.)

Returns:

  • (Boolean)


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)

Returns:



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

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

Returns:

  • (Boolean)


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)

Returns:

  • (Symbol, nil)


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

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

Returns:

  • (Boolean)


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

Returns:

  • (String, nil)


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

Returns:

  • (Boolean)


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)

Parameters:

  • options (Hash)

    Additional options



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

Returns:

  • (Array, nil)


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
346
347
# File 'lib/rbs/merge/node_wrapper.rb', line 301

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

Returns:

  • (Integer, nil)


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

def start_line
  if @backend == :rbs
    @node.location&.start_line
  else
    pos = @node.start_point
    pos ? pos.row + 1 : nil
  end
end

#textString?

Get source text for this node

Returns:

  • (String, nil)


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

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 > 0 && 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

Returns:

  • (Symbol)


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)

Parameters:

  • type_name (Symbol, String)

    Type to check

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


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)

Returns:

  • (Symbol, nil)


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

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