Class: TreeHaver::Tree
- Inherits:
-
Object
- Object
- TreeHaver::Tree
- Defined in:
- lib/tree_haver/tree.rb
Overview
Unified Tree wrapper providing a consistent API across all backends
This class wraps backend-specific tree objects and provides a unified interface. It stores the source text to enable text extraction from nodes.
Wrapping/Unwrapping Contract
TreeHaver follows a consistent pattern for object wrapping:
-
TreeHaver::Parser (top level) handles ALL wrapping/unwrapping
-
Backends work exclusively with raw backend objects
-
**User-facing API** uses only TreeHaver wrapper classes
Specifically for trees:
-
Backend Parser#parse returns raw backend tree (TreeSitter::Tree, TreeStump::Tree, etc.)
-
TreeHaver::Parser#parse wraps it in TreeHaver::Tree
-
TreeHaver::Parser#parse_string unwraps old_tree before passing to backend
-
Backend Parser#parse_string receives raw backend tree, returns raw backend tree
-
TreeHaver::Parser#parse_string wraps the returned tree
This ensures:
-
Backends are simple and consistent
-
All complexity is in one place (TreeHaver top level)
-
Users always work with TreeHaver wrapper classes
Instance Attribute Summary collapse
-
#inner_tree ⇒ Object
readonly
The wrapped backend-specific tree object.
-
#source ⇒ String
readonly
The source text.
Instance Method Summary collapse
-
#edit(start_byte:, old_end_byte:, new_end_byte:, start_point:, old_end_point:, new_end_point:) ⇒ void
Mark the tree as edited for incremental re-parsing.
-
#initialize(tree, source: nil) ⇒ Tree
constructor
A new instance of Tree.
-
#inspect ⇒ String
String representation.
-
#method_missing(method_name, *args, **kwargs, &block) ⇒ Object
Delegate unknown methods to the underlying backend-specific tree.
-
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Check if tree responds to a method (includes delegation to inner_tree).
-
#root_node ⇒ Node
Get the root node of the tree.
-
#supports_editing? ⇒ Boolean
Check if the current backend supports incremental parsing.
Constructor Details
#initialize(tree, source: nil) ⇒ Tree
80 81 82 83 |
# File 'lib/tree_haver/tree.rb', line 80 def initialize(tree, source: nil) @inner_tree = tree @source = source end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, **kwargs, &block) ⇒ Object
This maintains backward compatibility with code written for specific backends while providing the benefits of the unified API
Delegate unknown methods to the underlying backend-specific tree
This provides passthrough access for advanced usage when you need backend-specific features not exposed by TreeHaver’s unified API.
The delegation is automatic and transparent - you can call backend-specific methods directly on the TreeHaver::Tree and they’ll be forwarded to the underlying tree implementation.
251 252 253 254 255 256 257 |
# File 'lib/tree_haver/tree.rb', line 251 def method_missing(method_name, *args, **kwargs, &block) if @inner_tree.respond_to?(method_name) @inner_tree.public_send(method_name, *args, **kwargs, &block) else super end end |
Instance Attribute Details
#inner_tree ⇒ Object (readonly)
The wrapped backend-specific tree object
This provides direct access to the underlying backend tree for advanced usage when you need backend-specific features not exposed by the unified API.
69 70 71 |
# File 'lib/tree_haver/tree.rb', line 69 def inner_tree @inner_tree end |
#source ⇒ String (readonly)
The source text
Stored to enable text extraction from nodes via byte offsets.
76 77 78 |
# File 'lib/tree_haver/tree.rb', line 76 def source @source end |
Instance Method Details
#edit(start_byte:, old_end_byte:, new_end_byte:, start_point:, old_end_point:, new_end_point:) ⇒ void
This method returns an undefined value.
Mark the tree as edited for incremental re-parsing
Call this method after the source code has been modified but before re-parsing. This tells tree-sitter which parts of the tree are invalidated so it can efficiently re-parse only the affected regions.
Not all backends support incremental parsing. Use #supports_editing? to check before calling this method.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/tree_haver/tree.rb', line 129 def edit(start_byte:, old_end_byte:, new_end_byte:, start_point:, old_end_point:, new_end_point:) # MRI backend (ruby_tree_sitter) requires an InputEdit object if defined?(::TreeSitter::InputEdit) && @inner_tree.is_a?(::TreeSitter::Tree) input_edit = ::TreeSitter::InputEdit.new input_edit.start_byte = start_byte input_edit.old_end_byte = old_end_byte input_edit.new_end_byte = new_end_byte # Convert hash points to Point objects if needed input_edit.start_point = make_point(start_point) input_edit.old_end_point = make_point(old_end_point) input_edit.new_end_point = make_point(new_end_point) @inner_tree.edit(input_edit) else # Other backends may accept keyword arguments directly @inner_tree.edit( start_byte: start_byte, old_end_byte: old_end_byte, new_end_byte: new_end_byte, start_point: start_point, old_end_point: old_end_point, new_end_point: new_end_point, ) end rescue NoMethodError => e # Re-raise as NotAvailable if it's about the edit method raise unless e.name == :edit || e..include?("edit") raise TreeHaver::NotAvailable, "Incremental parsing not supported by current backend. " \ "Use MRI (ruby_tree_sitter), Rust (tree_stump), or Java (java-tree-sitter) backend." end |
#inspect ⇒ String
String representation
206 207 208 209 |
# File 'lib/tree_haver/tree.rb', line 206 def inspect inner_class = @inner_tree ? @inner_tree.class.name : "nil" "#<#{self.class} source_length=#{@source&.bytesize || "unknown"} inner_tree=#{inner_class}>" end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Check if tree responds to a method (includes delegation to inner_tree)
216 217 218 |
# File 'lib/tree_haver/tree.rb', line 216 def respond_to_missing?(method_name, include_private = false) @inner_tree.respond_to?(method_name, include_private) || super end |
#root_node ⇒ Node
Get the root node of the tree
88 89 90 91 92 |
# File 'lib/tree_haver/tree.rb', line 88 def root_node root = @inner_tree.root_node return if root.nil? Node.new(root, source: @source) end |
#supports_editing? ⇒ Boolean
Check if the current backend supports incremental parsing
Incremental parsing allows tree-sitter to reuse unchanged nodes when re-parsing edited source code, improving performance for large files with small edits.
194 195 196 197 198 199 200 201 202 |
# File 'lib/tree_haver/tree.rb', line 194 def supports_editing? # Try to get the edit method to verify it exists # This is more reliable than respond_to? with Delegator wrappers @inner_tree.method(:edit) true rescue NameError # NameError is the parent class of NoMethodError, so this catches both false end |