Class: TreeHaver::Base::Node Abstract
- Inherits:
-
Object
- Object
- TreeHaver::Base::Node
- Includes:
- Comparable, Enumerable
- Defined in:
- lib/tree_haver/base/node.rb
Overview
Subclasses must implement #type, #start_byte, #end_byte, and #children
Base class for all backend Node implementations
This class defines the API contract for Node objects across all backends. It provides shared implementation for common behaviors and documents required/optional methods that subclasses must implement.
Backend Architecture
TreeHaver supports two categories of backends:
Tree-sitter Backends (MRI, Rust, FFI, Java)
These backends use the native tree-sitter library (via different bindings). They return raw ::TreeSitter::Node objects which are wrapped by TreeHaver::Node (which inherits from this class).
-
Backend Tree#root_node returns:
::TreeSitter::Node(raw) -
TreeHaver::Tree#root_node wraps it in:
TreeHaver::Node -
These backends do NOT define their own Tree/Node classes
Pure-Ruby/Plugin Backends (Citrus, Prism, Psych, Commonmarker, Markly)
These backends define their own complete implementations:
-
Backend::X::Node- wraps parser-specific node objects -
Backend::X::Tree- wraps parser-specific tree objects
For consistency, these should also inherit from Base::Node and Base::Tree.
Direct Known Subclasses
TreeHaver::Backends::Prism::Node, TreeHaver::Backends::Psych::Node
Instance Attribute Summary collapse
-
#inner_node ⇒ Object
readonly
The underlying backend-specific node object.
-
#lines ⇒ Array<String>
readonly
Source lines for byte offset calculations.
-
#source ⇒ String
readonly
The source text.
Instance Method Summary collapse
-
#<=>(other) ⇒ Integer?
Comparison based on byte range.
-
#==(other) ⇒ Boolean
Equality based on type and byte range.
-
#child(index) ⇒ Node?
Get a child node by index.
-
#child_by_field_name(_name) ⇒ Node?
Get a child by field name.
-
#child_count ⇒ Integer
Get the number of child nodes.
-
#children ⇒ Array<Node>
Get all children as an array.
-
#each {|Node| ... } ⇒ Object
Iterate over children.
-
#end_byte ⇒ Integer
Get byte offset where the node ends.
-
#end_line ⇒ Integer
Get 1-based end line.
-
#end_point ⇒ Hash{Symbol => Integer}
Get end position (row/col) - 0-based.
-
#first_child ⇒ Node?
Retrieve the first child.
-
#has_error? ⇒ Boolean
Check if this node represents a syntax error.
-
#initialize(node, source: nil, lines: nil) ⇒ Node
constructor
Create a new Node wrapper.
-
#inspect ⇒ String
Human-readable representation.
-
#last_child ⇒ Node?
Retrieve the last child.
-
#missing? ⇒ Boolean
Check if this node was inserted for error recovery.
-
#named? ⇒ Boolean
(also: #structural?)
Check if this node is named (structural).
-
#next_sibling ⇒ Node?
Get the next sibling node.
-
#parent ⇒ Node?
Get the parent node.
-
#prev_sibling ⇒ Node?
Get the previous sibling node.
-
#source_position ⇒ Hash{Symbol => Integer}
Get unified source position hash.
-
#start_byte ⇒ Integer
Get byte offset where the node starts.
-
#start_line ⇒ Integer
Get 1-based start line.
-
#start_point ⇒ Hash{Symbol => Integer}
Get start position (row/col) - 0-based.
-
#text ⇒ String
Get the text content of this node.
-
#to_s ⇒ String
String conversion returns the text content.
-
#type ⇒ String
Get the node type as a string.
Constructor Details
#initialize(node, source: nil, lines: nil) ⇒ Node
Create a new Node wrapper
57 58 59 60 61 |
# File 'lib/tree_haver/base/node.rb', line 57 def initialize(node, source: nil, lines: nil) @inner_node = node @source = source @lines = lines || source&.lines || [] end |
Instance Attribute Details
#inner_node ⇒ Object (readonly)
The underlying backend-specific node object
42 43 44 |
# File 'lib/tree_haver/base/node.rb', line 42 def inner_node @inner_node end |
#lines ⇒ Array<String> (readonly)
Source lines for byte offset calculations
50 51 52 |
# File 'lib/tree_haver/base/node.rb', line 50 def lines @lines end |
#source ⇒ String (readonly)
The source text
46 47 48 |
# File 'lib/tree_haver/base/node.rb', line 46 def source @source end |
Instance Method Details
#<=>(other) ⇒ Integer?
Comparison based on byte range
197 198 199 200 201 202 203 204 |
# File 'lib/tree_haver/base/node.rb', line 197 def <=>(other) return unless other.respond_to?(:start_byte) && other.respond_to?(:end_byte) cmp = start_byte <=> other.start_byte return cmp unless cmp == 0 end_byte <=> other.end_byte end |
#==(other) ⇒ Boolean
Equality based on type and byte range
286 287 288 289 290 |
# File 'lib/tree_haver/base/node.rb', line 286 def ==(other) return false unless other.respond_to?(:type) && other.respond_to?(:start_byte) && other.respond_to?(:end_byte) type == other.type && start_byte == other.start_byte && end_byte == other.end_byte end |
#child(index) ⇒ Node?
Get a child node by index
100 101 102 |
# File 'lib/tree_haver/base/node.rb', line 100 def child(index) children[index] end |
#child_by_field_name(_name) ⇒ Node?
Get a child by field name
176 177 178 |
# File 'lib/tree_haver/base/node.rb', line 176 def child_by_field_name(_name) nil end |
#child_count ⇒ Integer
Get the number of child nodes
93 94 95 |
# File 'lib/tree_haver/base/node.rb', line 93 def child_count children.size end |
#children ⇒ Array<Node>
Get all children as an array
85 86 87 |
# File 'lib/tree_haver/base/node.rb', line 85 def children raise NotImplementedError, "#{self.class}#children must be implemented" end |
#each {|Node| ... } ⇒ Object
Iterate over children
106 107 108 109 110 |
# File 'lib/tree_haver/base/node.rb', line 106 def each(&block) return to_enum(__method__) unless block children.each(&block) end |
#end_byte ⇒ Integer
Get byte offset where the node ends
79 80 81 |
# File 'lib/tree_haver/base/node.rb', line 79 def end_byte raise NotImplementedError, "#{self.class}#end_byte must be implemented" end |
#end_line ⇒ Integer
Get 1-based end line
220 221 222 223 224 225 226 227 228 |
# File 'lib/tree_haver/base/node.rb', line 220 def end_line ep = end_point row = if ep.is_a?(Hash) ep[:row] else (ep.respond_to?(:row) ? ep.row : 0) end row + 1 end |
#end_point ⇒ Hash{Symbol => Integer}
Get end position (row/col) - 0-based
188 189 190 |
# File 'lib/tree_haver/base/node.rb', line 188 def end_point {row: 0, column: 0} end |
#first_child ⇒ Node?
Retrieve the first child
114 115 116 |
# File 'lib/tree_haver/base/node.rb', line 114 def first_child children.first end |
#has_error? ⇒ Boolean
Check if this node represents a syntax error
155 156 157 |
# File 'lib/tree_haver/base/node.rb', line 155 def has_error? false end |
#inspect ⇒ String
Human-readable representation
267 268 269 270 271 272 273 274 275 |
# File 'lib/tree_haver/base/node.rb', line 267 def inspect class_name = self.class.name || "#{self.class.superclass&.name}(anonymous)" node_type = begin type rescue NotImplementedError "(not implemented)" end "#<#{class_name} type=#{node_type}>" end |
#last_child ⇒ Node?
Retrieve the last child
120 121 122 |
# File 'lib/tree_haver/base/node.rb', line 120 def last_child children.last end |
#missing? ⇒ Boolean
Check if this node was inserted for error recovery
161 162 163 |
# File 'lib/tree_haver/base/node.rb', line 161 def missing? false end |
#named? ⇒ Boolean Also known as: structural?
Check if this node is named (structural)
146 147 148 |
# File 'lib/tree_haver/base/node.rb', line 146 def named? true end |
#next_sibling ⇒ Node?
Get the next sibling node
134 135 136 |
# File 'lib/tree_haver/base/node.rb', line 134 def next_sibling nil end |
#parent ⇒ Node?
Get the parent node
128 129 130 |
# File 'lib/tree_haver/base/node.rb', line 128 def parent nil end |
#prev_sibling ⇒ Node?
Get the previous sibling node
140 141 142 |
# File 'lib/tree_haver/base/node.rb', line 140 def prev_sibling nil end |
#source_position ⇒ Hash{Symbol => Integer}
Get unified source position hash
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/tree_haver/base/node.rb', line 232 def source_position sp = start_point ep = end_point sp_row = if sp.is_a?(Hash) sp[:row] else (sp.respond_to?(:row) ? sp.row : 0) end sp_col = if sp.is_a?(Hash) sp[:column] else (sp.respond_to?(:column) ? sp.column : 0) end ep_row = if ep.is_a?(Hash) ep[:row] else (ep.respond_to?(:row) ? ep.row : 0) end ep_col = if ep.is_a?(Hash) ep[:column] else (ep.respond_to?(:column) ? ep.column : 0) end { start_line: sp_row + 1, end_line: ep_row + 1, start_column: sp_col, end_column: ep_col, } end |
#start_byte ⇒ Integer
Get byte offset where the node starts
73 74 75 |
# File 'lib/tree_haver/base/node.rb', line 73 def start_byte raise NotImplementedError, "#{self.class}#start_byte must be implemented" end |
#start_line ⇒ Integer
Get 1-based start line
208 209 210 211 212 213 214 215 216 |
# File 'lib/tree_haver/base/node.rb', line 208 def start_line sp = start_point row = if sp.is_a?(Hash) sp[:row] else (sp.respond_to?(:row) ? sp.row : 0) end row + 1 end |
#start_point ⇒ Hash{Symbol => Integer}
Get start position (row/col) - 0-based
182 183 184 |
# File 'lib/tree_haver/base/node.rb', line 182 def start_point {row: 0, column: 0} end |
#text ⇒ String
Get the text content of this node
167 168 169 170 171 |
# File 'lib/tree_haver/base/node.rb', line 167 def text return "" unless source source[start_byte...end_byte] || "" end |
#to_s ⇒ String
String conversion returns the text content
279 280 281 |
# File 'lib/tree_haver/base/node.rb', line 279 def to_s text end |
#type ⇒ String
Get the node type as a string
67 68 69 |
# File 'lib/tree_haver/base/node.rb', line 67 def type raise NotImplementedError, "#{self.class}#type must be implemented" end |