Class: Toml::Merge::NodeWrapper

Inherits:
Object
  • Object
show all
Defined in:
lib/toml/merge/node_wrapper.rb

Overview

Wraps tree-sitter nodes with comment associations, line information, and signatures. This provides a unified interface for working with TOML AST nodes during merging.

Examples:

Basic usage

parser = TreeHaver::Parser.new
parser.language = TreeHaver::Language.load("toml", path)
tree = parser.parse_string(nil, source)
wrapper = NodeWrapper.new(tree.root_node, lines: source.lines, source: source)
wrapper.signature # => [:table, "section"]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(node, lines:, source: nil, leading_comments: [], inline_comment: nil) ⇒ NodeWrapper

Returns a new instance of NodeWrapper.

Parameters:

  • node (TreeHaver::Node)

    tree-sitter node to wrap

  • lines (Array<String>)

    Source lines for content extraction

  • source (String) (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



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/toml/merge/node_wrapper.rb', line 57

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

  # Extract line information from the tree-sitter node (0-indexed to 1-indexed)
  @start_line = node.start_point.row + 1 if node.respond_to?(:start_point)
  @end_line = node.end_point.row + 1 if node.respond_to?(:end_point)

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

    End line (1-based)



47
48
49
# File 'lib/toml/merge/node_wrapper.rb', line 47

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



41
42
43
# File 'lib/toml/merge/node_wrapper.rb', line 41

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



35
36
37
# File 'lib/toml/merge/node_wrapper.rb', line 35

def leading_comments
  @leading_comments
end

#linesArray<String> (readonly)

Returns Source lines.

Returns:

  • (Array<String>)

    Source lines



50
51
52
# File 'lib/toml/merge/node_wrapper.rb', line 50

def lines
  @lines
end

#nodeTreeHaver::Node (readonly)

Returns The wrapped tree-sitter node.

Returns:

  • (TreeHaver::Node)

    The wrapped tree-sitter node



32
33
34
# File 'lib/toml/merge/node_wrapper.rb', line 32

def node
  @node
end

#sourceString (readonly)

Returns The original source string.

Returns:

  • (String)

    The original source string



38
39
40
# File 'lib/toml/merge/node_wrapper.rb', line 38

def source
  @source
end

#start_lineInteger (readonly)

Returns Start line (1-based).

Returns:

  • (Integer)

    Start line (1-based)



44
45
46
# File 'lib/toml/merge/node_wrapper.rb', line 44

def start_line
  @start_line
end

Class Method Details

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

Wrap a tree-sitter node, returning nil for nil input.

Parameters:

  • node (TreeHaver::Node, nil)

    tree-sitter node to wrap

  • 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

Returns:

  • (NodeWrapper, nil)

    Wrapped node or nil if node is nil



24
25
26
27
28
# File 'lib/toml/merge/node_wrapper.rb', line 24

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

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

Instance Method Details

#array?Boolean

Check if this is a TOML array

Returns:

  • (Boolean)


114
115
116
# File 'lib/toml/merge/node_wrapper.rb', line 114

def array?
  @node.type.to_s == "array"
end

#array_of_tables?Boolean

Check if this is a TOML array of tables

Returns:

  • (Boolean)


101
102
103
104
# File 'lib/toml/merge/node_wrapper.rb', line 101

def array_of_tables?
  type_str = @node.type.to_s
  type_str == "array_of_tables" || type_str == "table_array_element"
end

#boolean?Boolean

Check if this is a TOML boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/toml/merge/node_wrapper.rb', line 138

def boolean?
  @node.type.to_s == "boolean"
end

#childrenArray<NodeWrapper>

Get children wrapped as NodeWrappers

Returns:



246
247
248
249
250
251
252
253
254
# File 'lib/toml/merge/node_wrapper.rb', line 246

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

  result = []
  @node.each do |child|
    result << NodeWrapper.new(child, lines: @lines, source: @source)
  end
  result
end

#closing_lineString?

Get the closing line for a container node For tables, this is the last line of content before the next table or EOF

Returns:

  • (String, nil)


305
306
307
308
309
# File 'lib/toml/merge/node_wrapper.rb', line 305

def closing_line
  return unless container? && @end_line

  @lines[@end_line - 1]
end

#comment?Boolean

Check if this is a comment

Returns:

  • (Boolean)


150
151
152
# File 'lib/toml/merge/node_wrapper.rb', line 150

def comment?
  @node.type.to_s == "comment"
end

#container?Boolean

Check if this node is a container (has mergeable children)

Returns:

  • (Boolean)


283
284
285
# File 'lib/toml/merge/node_wrapper.rb', line 283

def container?
  table? || array_of_tables? || inline_table? || array? || document?
end

#contentString

Get the content for this node from source lines

Returns:

  • (String)


328
329
330
331
332
# File 'lib/toml/merge/node_wrapper.rb', line 328

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

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

#datetime?Boolean

Check if this is a datetime

Returns:

  • (Boolean)


156
157
158
# File 'lib/toml/merge/node_wrapper.rb', line 156

def datetime?
  %w[offset_date_time local_date_time local_date local_time].include?(@node.type.to_s)
end

#document?Boolean

Check if this is the document root

Returns:

  • (Boolean)


223
224
225
# File 'lib/toml/merge/node_wrapper.rb', line 223

def document?
  @node.type.to_s == "document"
end

#elementsArray<NodeWrapper>

Get array elements if this is an array

Returns:



229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/toml/merge/node_wrapper.rb', line 229

def elements
  return [] unless array?

  result = []
  @node.each do |child|
    child_type = child.type.to_s
    # Skip punctuation and comments
    next if child_type == "comment"
    next if %w[, \[ \]].include?(child_type)

    result << NodeWrapper.new(child, lines: @lines, source: @source)
  end
  result
end

#float?Boolean

Check if this is a TOML float

Returns:

  • (Boolean)


132
133
134
# File 'lib/toml/merge/node_wrapper.rb', line 132

def float?
  @node.type.to_s == "float"
end

#inline_table?Boolean

Check if this is a TOML inline table

Returns:

  • (Boolean)


108
109
110
# File 'lib/toml/merge/node_wrapper.rb', line 108

def inline_table?
  @node.type.to_s == "inline_table"
end

#inspectString

String representation for debugging

Returns:

  • (String)


336
337
338
# File 'lib/toml/merge/node_wrapper.rb', line 336

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

#integer?Boolean

Check if this is a TOML integer

Returns:

  • (Boolean)


126
127
128
# File 'lib/toml/merge/node_wrapper.rb', line 126

def integer?
  @node.type.to_s == "integer"
end

#key_nameString?

Get the key name if this is a pair node

Returns:

  • (String, nil)


177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/toml/merge/node_wrapper.rb', line 177

def key_name
  return unless pair?

  # In TOML tree-sitter, pair has key children (bare_key, quoted_key, or dotted_key)
  @node.each do |child|
    child_type = child.type.to_s
    if %w[bare_key quoted_key dotted_key].include?(child_type)
      key_text = node_text(child)
      # Remove surrounding quotes if present
      return key_text&.gsub(/\A["']|["']\z/, "")
    end
  end
  nil
end

#leaf?Boolean

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

Returns:

  • (Boolean)


289
290
291
# File 'lib/toml/merge/node_wrapper.rb', line 289

def leaf?
  !container?
end

#mergeable_childrenArray<NodeWrapper>

Get mergeable children - the semantically meaningful children for tree merging For tables, returns pairs. For arrays, returns elements. For other node types, returns empty array (leaf nodes).

Returns:



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/toml/merge/node_wrapper.rb', line 260

def mergeable_children
  case type
  when :table, :inline_table
    pairs
  when :array
    elements
  when :document
    # Return top-level pairs and tables
    result = []
    @node.each do |child|
      child_type = child.type.to_s
      next if child_type == "comment"

      result << NodeWrapper.new(child, lines: @lines, source: @source)
    end
    result
  else
    []
  end
end

#node_text(ts_node) ⇒ String

Extract text from a tree-sitter node using byte positions

Parameters:

  • ts_node (TreeHaver::Node)

    The tree-sitter node

Returns:

  • (String)


320
321
322
323
324
# File 'lib/toml/merge/node_wrapper.rb', line 320

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

#opening_lineString?

Get the opening line for a table (the line with [table_name])

Returns:

  • (String, nil)


295
296
297
298
299
300
# File 'lib/toml/merge/node_wrapper.rb', line 295

def opening_line
  return unless @start_line
  return unless table? || array_of_tables?

  @lines[@start_line - 1]
end

#pair?Boolean

Check if this is a key-value pair

Returns:

  • (Boolean)


144
145
146
# File 'lib/toml/merge/node_wrapper.rb', line 144

def pair?
  @node.type.to_s == "pair"
end

#pairsArray<NodeWrapper>

Get key-value pairs from a table or inline_table

Returns:



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/toml/merge/node_wrapper.rb', line 209

def pairs
  return [] unless table? || inline_table? || document?

  result = []
  @node.each do |child|
    next unless child.type.to_s == "pair"

    result << NodeWrapper.new(child, lines: @lines, source: @source)
  end
  result
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



76
77
78
# File 'lib/toml/merge/node_wrapper.rb', line 76

def signature
  compute_signature(@node)
end

#string?Boolean

Check if this is a TOML string

Returns:

  • (Boolean)


120
121
122
# File 'lib/toml/merge/node_wrapper.rb', line 120

def string?
  %w[string basic_string literal_string multiline_basic_string multiline_literal_string].include?(@node.type.to_s)
end

#table?Boolean

Check if this is a TOML table (section)

Returns:

  • (Boolean)


95
96
97
# File 'lib/toml/merge/node_wrapper.rb', line 95

def table?
  @node.type.to_s == "table"
end

#table_nameString?

Get the table name (header) if this is a table

Returns:

  • (String, nil)


162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/toml/merge/node_wrapper.rb', line 162

def table_name
  return unless table? || array_of_tables?

  # Find the dotted_key or bare_key child that represents the table name
  @node.each do |child|
    child_type = child.type.to_s
    if %w[dotted_key bare_key quoted_key].include?(child_type)
      return node_text(child)
    end
  end
  nil
end

#textString

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

Returns:

  • (String)


313
314
315
# File 'lib/toml/merge/node_wrapper.rb', line 313

def text
  node_text(@node)
end

#typeSymbol

Get the node type as a symbol

Returns:

  • (Symbol)


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

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)


89
90
91
# File 'lib/toml/merge/node_wrapper.rb', line 89

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

#value_nodeNodeWrapper?

Get the value node if this is a pair

Returns:



194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/toml/merge/node_wrapper.rb', line 194

def value_node
  return unless pair?

  @node.each do |child|
    child_type = child.type.to_s
    # Skip keys, get the value
    next if %w[bare_key quoted_key dotted_key =].include?(child_type)

    return NodeWrapper.new(child, lines: @lines, source: @source)
  end
  nil
end