Class: Ast::Merge::NodeWrapperBase Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/ast/merge/node_wrapper_base.rb

Overview

This class is abstract.

Subclass and implement #compute_signature

Base class for format-specific node wrappers used in *-merge gems.

This provides common functionality for wrapping TreeHaver nodes with:

  • Source context (lines, source string)
  • Line information (start_line, end_line)
  • Comment associations (leading_comments, inline_comment)
  • Content extraction (text, content)
  • Signature generation (abstract)

Relationship to NodeTyping::Wrapper

This class is DIFFERENT from Ast::Merge::NodeTyping::Wrapper:

  • NodeWrapperBase: Provides format-specific functionality (line info,
    signatures, comments, type predicates). Used to wrap raw TreeHaver nodes
    with rich context needed for merging.

  • NodeTyping::Wrapper: Adds a custom merge_type attribute for merge
    classification. Used by SmartMergerBase to apply custom typing rules.

A node CAN be wrapped by both:

NodeTyping::Wrapper(Toml::Merge::NodeWrapper(tree_sitter_node))

The NodeTyping.unwrap method handles unwrapping NodeTyping::Wrapper,
while NodeWrapperBase#node provides access to the underlying TreeHaver node.

Subclass Responsibilities

Subclasses MUST implement:

  • #compute_signature(node) - Generate a signature for node matching

Subclasses SHOULD implement format-specific type predicates:

  • TOML: #table?, #pair?, #array_of_tables?, etc.
  • JSON: #object?, #array?, #pair?, etc.
  • Bash: #function_definition?, #variable_assignment?, etc.

Examples:

Creating a format-specific wrapper

class NodeWrapper < Ast::Merge::NodeWrapperBase
  def table?
    type == :table
  end

  private

  def compute_signature(node)
    case node.type.to_sym
    when :table
      [:table, table_name]
    else
      [node.type.to_sym]
    end
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(node, lines:, source: nil, leading_comments: [], inline_comment: nil, **options) ⇒ NodeWrapperBase

Initialize the node wrapper with source context.

Parameters:

  • node (Object)

    TreeHaver node to wrap

  • lines (Array<String>)

    Source lines for content extraction

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

    Original source string for byte-based text extraction

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

    Comments before this node

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

    Inline comment on the node’s line

  • options (Hash)

    Additional options for subclasses (forward compatibility)



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ast/merge/node_wrapper_base.rb', line 92

def initialize(node, lines:, source: nil, leading_comments: [], inline_comment: nil, **options)
  @node = node
  @lines = lines
  @source = source || lines.join("\n")
  @leading_comments = leading_comments
  @inline_comment = inline_comment

  # Store additional options for subclasses to use
  process_additional_options(options)

  # Extract line information from the node (0-indexed to 1-indexed)
  extract_line_info(node)

  # Handle edge case where end_line might be before start_line
  @end_line = @start_line if @start_line && @end_line && @end_line < @start_line
end

Instance Attribute Details

#end_lineInteger? (readonly)

Returns End line (1-based).

Returns:

  • (Integer, nil)

    End line (1-based)



82
83
84
# File 'lib/ast/merge/node_wrapper_base.rb', line 82

def end_line
  @end_line
end

#inline_commentHash? (readonly)

Returns Inline/trailing comment on the same line.

Returns:

  • (Hash, nil)

    Inline/trailing comment on the same line



76
77
78
# File 'lib/ast/merge/node_wrapper_base.rb', line 76

def inline_comment
  @inline_comment
end

#leading_commentsArray<Hash> (readonly)

Returns Leading comments associated with this node.

Returns:

  • (Array<Hash>)

    Leading comments associated with this node



73
74
75
# File 'lib/ast/merge/node_wrapper_base.rb', line 73

def leading_comments
  @leading_comments
end

#linesArray<String> (readonly)

Returns Source lines for content extraction.

Returns:

  • (Array<String>)

    Source lines for content extraction



67
68
69
# File 'lib/ast/merge/node_wrapper_base.rb', line 67

def lines
  @lines
end

#nodeObject (readonly)

Returns The wrapped TreeHaver node.

Returns:

  • (Object)

    The wrapped TreeHaver node



64
65
66
# File 'lib/ast/merge/node_wrapper_base.rb', line 64

def node
  @node
end

#sourceString (readonly)

Returns The original source string.

Returns:

  • (String)

    The original source string



70
71
72
# File 'lib/ast/merge/node_wrapper_base.rb', line 70

def source
  @source
end

#start_lineInteger? (readonly)

Returns Start line (1-based).

Returns:

  • (Integer, nil)

    Start line (1-based)



79
80
81
# File 'lib/ast/merge/node_wrapper_base.rb', line 79

def start_line
  @start_line
end

Instance Method Details

#childrenArray<NodeWrapperBase>

Get children wrapped as NodeWrappers.
Override in subclasses to return wrapped children.

Returns:



182
183
184
185
186
187
188
189
190
# File 'lib/ast/merge/node_wrapper_base.rb', line 182

def children
  return [] unless @node.respond_to?(:each)

  result = []
  @node.each do |child|
    result << wrap_child(child)
  end
  result
end

#container?Boolean

Check if this node is a container (has children for merging).
Override in subclasses to define container types.

Returns:

  • (Boolean)


169
170
171
# File 'lib/ast/merge/node_wrapper_base.rb', line 169

def container?
  false
end

#contentString

Get the content for this node from source lines.

Returns:

  • (String)


160
161
162
163
164
# File 'lib/ast/merge/node_wrapper_base.rb', line 160

def content
  return "" unless @start_line && @end_line

  (@start_line..@end_line).map { |ln| @lines[ln - 1] }.compact.join("\n")
end

#freeze_node?Boolean

Check if this is a freeze node.
Override in subclasses if freeze node detection differs.

Returns:

  • (Boolean)


139
140
141
# File 'lib/ast/merge/node_wrapper_base.rb', line 139

def freeze_node?
  false
end

#inspectString

String representation for debugging.

Returns:

  • (String)


201
202
203
# File 'lib/ast/merge/node_wrapper_base.rb', line 201

def inspect
  "#<#{self.class.name} type=#{@node.type} lines=#{@start_line}..#{@end_line}>"
end

#leaf?Boolean

Check if this node is a leaf (no mergeable children).

Returns:

  • (Boolean)


175
176
177
# File 'lib/ast/merge/node_wrapper_base.rb', line 175

def leaf?
  !container?
end

#mergeable_childrenArray<NodeWrapperBase>

Get mergeable children - the semantically meaningful children for tree merging.
Override in subclasses to return format-specific mergeable children.

Returns:



195
196
197
# File 'lib/ast/merge/node_wrapper_base.rb', line 195

def mergeable_children
  children
end

#node_text(ts_node) ⇒ String

Extract text from a node using byte positions.

Parameters:

  • ts_node (Object)

    The TreeHaver node

Returns:

  • (String)


152
153
154
155
156
# File 'lib/ast/merge/node_wrapper_base.rb', line 152

def node_text(ts_node)
  return "" unless ts_node.respond_to?(:start_byte) && ts_node.respond_to?(:end_byte)

  @source[ts_node.start_byte...ts_node.end_byte] || ""
end

#node_wrapper?Boolean

Returns true to indicate this is a node wrapper.
Used to distinguish from NodeTyping::Wrapper.

Returns:

  • (Boolean)


208
209
210
# File 'lib/ast/merge/node_wrapper_base.rb', line 208

def node_wrapper?
  true
end

#process_additional_options(options) ⇒ Object

Process additional options. Override in subclasses to handle format-specific options.

Parameters:

  • options (Hash)

    Additional options



111
112
113
# File 'lib/ast/merge/node_wrapper_base.rb', line 111

def process_additional_options(options)
  # Default: no-op. Subclasses can override to process options like :backend, :document_root
end

#signatureArray?

Generate a signature for this node for matching purposes.
Signatures are used to identify corresponding nodes between template and destination.

Returns:

  • (Array, nil)

    Signature array or nil if not signaturable



119
120
121
# File 'lib/ast/merge/node_wrapper_base.rb', line 119

def signature
  compute_signature(@node)
end

#textString

Get the text content for this node by extracting from source using byte positions.

Returns:

  • (String)


145
146
147
# File 'lib/ast/merge/node_wrapper_base.rb', line 145

def text
  node_text(@node)
end

#typeSymbol

Get the node type as a symbol.

Returns:

  • (Symbol)


125
126
127
# File 'lib/ast/merge/node_wrapper_base.rb', line 125

def type
  @node.type.to_sym
end

#type?(type_name) ⇒ Boolean

Check if this node has a specific type.

Parameters:

  • type_name (Symbol, String)

    Type to check

Returns:

  • (Boolean)


132
133
134
# File 'lib/ast/merge/node_wrapper_base.rb', line 132

def type?(type_name)
  @node.type.to_s == type_name.to_s
end

#underlying_nodeObject

Get the underlying TreeHaver node.
Note: This is NOT the same as NodeTyping::Wrapper#unwrap which removes
the typing wrapper. This method provides access to the raw parser node.

Returns:

  • (Object)

    The underlying TreeHaver node



216
217
218
# File 'lib/ast/merge/node_wrapper_base.rb', line 216

def underlying_node
  @node
end