Class: TreeStand::Node
- Inherits:
-
Object
- Object
- TreeStand::Node
- Extended by:
- Forwardable, T::Sig
- Includes:
- Enumerable
- Defined in:
- lib/tree_stand/node.rb
Overview
Wrapper around a TreeSitter node and provides convient methods that are missing on the original node. This class overrides the ‘method_missing` method to delegate to a nodes named children.
Constant Summary collapse
- THINLY_REMAPPED_METHODS =
These are methods defined in TreeStand::Node but map to something in TreeSitter::Node, because we want a more idiomatic API.
{ '[]': :[], fetch: :fetch, field: :child_by_field_name, next: :next_sibling, prev: :prev_sibling, next_named: :next_named_sibling, prev_named: :prev_named_sibling, field_names: :fields, }.freeze
- THINLY_WRAPPED_METHODS =
These are methods from TreeSitter that are thinly wrapped to create TreeStand::Node instead.
( %i[ child named_child parent ] + THINLY_REMAPPED_METHODS.keys ).freeze
Instance Attribute Summary collapse
- #tree ⇒ TreeStand::Tree readonly
- #ts_node ⇒ TreeSitter::Node readonly
Instance Method Summary collapse
- #==(other) ⇒ Boolean
-
#changed? ⇒ Boolean
True if a syntax node has been edited.
-
#child_count ⇒ Integer
The number of child nodes.
- #children ⇒ Array<TreeStand::Node>
-
#each(&block) {|child| ... } ⇒ T::Enumerator[TreeStand::Node]
Node includes enumerable so that you can iterate over the child nodes.
-
#each_field(&block) {|field, child| ... } ⇒ T::Enumerator[[Symbol, TreeStand::Node]]
(also: #fields)
Iterate of (field, child).
-
#each_named(&block) {|child| ... } ⇒ T::Enumerator[TreeStand::Node]
(also: #named)
Enumerate named children.
-
#error? ⇒ bool
True if the node is an error node.
-
#extra? ⇒ Boolean
True if the node is extra (e.g. comments).
-
#find_node(query_string) ⇒ TreeStand::Node?
Returns the first captured node that matches the query string or nil if there was no captured node.
-
#find_node!(query_string) ⇒ TreeStand::Node
Like #find_node, except that if no node is found, raises an NodeNotFound error.
-
#has_error? ⇒ Boolean
True if the node is a syntax error or contains any syntax errors.
- #initialize(tree, ts_node) ⇒ void constructor private
-
#method_missing(method, *args, **kwargs, &block) ⇒ Object
This class overrides the ‘method_missing` method to delegate to the node’s named children.
-
#missing? ⇒ Boolean
True if the parser inserted that node to recover from error.
-
#named? ⇒ Boolean
True if the node is not a literal in the grammar.
-
#named_child_count ⇒ Integer
The number of named children.
- #pretty_print(pp) ⇒ void
-
#query(query_string) ⇒ Array<Hash{String => TreeStand::Node}>
TreeSitter uses a ‘TreeSitter::Cursor` to iterate over matches by calling `curser#next_match` repeatedly until it returns `nil`.
- #range ⇒ TreeStand::Range
-
#sexpr ⇒ String
A pretty-printed sexpr.
-
#text ⇒ String
A convenience method for getting the text of the node.
-
#type ⇒ Symbol
The type of the node in the tree-sitter grammar.
-
#walk(&block) {|node| ... } ⇒ T::Enumerator[TreeStand::Node]
Backed by Visitors::TreeWalker.
Constructor Details
#initialize(tree, ts_node) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
79 80 81 82 |
# File 'lib/tree_stand/node.rb', line 79 def initialize(tree, ts_node) @tree = tree @ts_node = ts_node end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(field_name) ⇒ TreeStand::Node #method_missing(method_name, *args, &block) ⇒ Object
This class overrides the ‘method_missing` method to delegate to the node’s named children.
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/tree_stand/node.rb', line 293 def method_missing(method, *args, **kwargs, &block) if thinly_wrapped?(method) from( T.unsafe(@ts_node) .public_send( THINLY_REMAPPED_METHODS[method] || method, *args, **kwargs, &block ), ) else super end end |
Instance Attribute Details
#tree ⇒ TreeStand::Tree (readonly)
72 73 74 |
# File 'lib/tree_stand/node.rb', line 72 def tree @tree end |
#ts_node ⇒ TreeSitter::Node (readonly)
75 76 77 |
# File 'lib/tree_stand/node.rb', line 75 def ts_node @ts_node end |
Instance Method Details
#==(other) ⇒ Boolean
310 311 312 313 314 |
# File 'lib/tree_stand/node.rb', line 310 def ==(other) return false unless other.is_a?(TreeStand::Node) T.must(range == other.range && type == other.type && text == other.text) end |
#changed? ⇒ Boolean
Returns true if a syntax node has been edited.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#child_count ⇒ Integer
Returns the number of child nodes.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#children ⇒ Array<TreeStand::Node>
268 |
# File 'lib/tree_stand/node.rb', line 268 def children = to_a |
#each(&block) {|child| ... } ⇒ T::Enumerator[TreeStand::Node]
Node includes enumerable so that you can iterate over the child nodes.
162 163 164 165 166 167 168 169 170 |
# File 'lib/tree_stand/node.rb', line 162 def each(&block) enumerator = Enumerator.new do |yielder| @ts_node.each do |child| yielder << TreeStand::Node.new(@tree, child) end end enumerator.each(&block) if block_given? enumerator end |
#each_field(&block) {|field, child| ... } ⇒ T::Enumerator[[Symbol, TreeStand::Node]] Also known as: fields
Iterate of (field, child).
222 223 224 225 226 227 228 229 230 |
# File 'lib/tree_stand/node.rb', line 222 def each_field(&block) enumerator = Enumerator.new do |yielder| @ts_node.each_field do |field, child| yielder << [field.to_sym, TreeStand::Node.new(@tree, child)] end end enumerator.each(&block) if block_given? enumerator end |
#each_named(&block) {|child| ... } ⇒ T::Enumerator[TreeStand::Node] Also known as: named
Enumerate named children.
190 191 192 193 194 195 196 197 198 |
# File 'lib/tree_stand/node.rb', line 190 def each_named(&block) enumerator = Enumerator.new do |yielder| @ts_node.each_named do |child| yielder << TreeStand::Node.new(@tree, child) end end enumerator.each(&block) if block_given? enumerator end |
#error? ⇒ bool
Returns true if the node is an error node.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#extra? ⇒ Boolean
Returns true if the node is extra (e.g. comments).
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#find_node(query_string) ⇒ TreeStand::Node?
Returns the first captured node that matches the query string or nil if there was no captured node.
119 120 121 |
# File 'lib/tree_stand/node.rb', line 119 def find_node(query_string) query(query_string).first&.values&.first end |
#find_node!(query_string) ⇒ TreeStand::Node
Like #find_node, except that if no node is found, raises an TreeStand::NodeNotFound error.
129 130 131 |
# File 'lib/tree_stand/node.rb', line 129 def find_node!(query_string) find_node(query_string) || raise(TreeStand::NodeNotFound) end |
#has_error? ⇒ Boolean
Returns true if the node is a syntax error or contains any syntax errors.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#missing? ⇒ Boolean
Returns true if the parser inserted that node to recover from error.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#named? ⇒ Boolean
Returns true if the node is not a literal in the grammar.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#named_child_count ⇒ Integer
Returns the number of named children.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#pretty_print(pp) ⇒ void
This method returns an undefined value.
318 319 320 |
# File 'lib/tree_stand/node.rb', line 318 def pretty_print(pp) pp.output << sexpr(source: text) end |
#query(query_string) ⇒ Array<Hash{String => TreeStand::Node}>
TreeSitter uses a ‘TreeSitter::Cursor` to iterate over matches by calling `curser#next_match` repeatedly until it returns `nil`.
This method does all of that for you and collects all of the matches into an array and each corresponding capture into a hash.
101 102 103 104 105 106 107 108 |
# File 'lib/tree_stand/node.rb', line 101 def query(query_string) ts_query = TreeSitter::Query.new(@tree.parser.ts_language, query_string) TreeSitter::QueryCursor .new .matches(ts_query, @tree.ts_tree.root_node, @tree.document) .each_capture_hash .map { |h| h.transform_values! { |n| TreeStand::Node.new(@tree, n) } } end |
#range ⇒ TreeStand::Range
134 135 136 137 138 139 140 141 |
# File 'lib/tree_stand/node.rb', line 134 def range TreeStand::Range.new( start_byte: @ts_node.start_byte, end_byte: @ts_node.end_byte, start_point: @ts_node.start_point, end_point: @ts_node.end_point, ) end |
#sexpr ⇒ String
Returns a pretty-printed sexpr.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#text ⇒ String
A convenience method for getting the text of the node. Each TreeStand::Node wraps the parent #tree and has access to the source document.
273 274 275 |
# File 'lib/tree_stand/node.rb', line 273 def text T.must(@tree.document.byteslice(@ts_node.start_byte...@ts_node.end_byte)) end |
#type ⇒ Symbol
Returns the type of the node in the tree-sitter grammar.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/tree_stand/node.rb', line 34 def_delegators( :@ts_node, :changed?, :child_count, :error?, :extra?, :has_error?, :missing?, :named?, :named_child_count, :sexpr, :type, ) |
#walk(&block) {|node| ... } ⇒ T::Enumerator[TreeStand::Node]
Backed by Visitors::TreeWalker.
253 254 255 256 257 258 259 260 261 |
# File 'lib/tree_stand/node.rb', line 253 def walk(&block) enumerator = Enumerator.new do |yielder| Visitors::TreeWalker.new(self) do |child| yielder << child end.visit end enumerator.each(&block) if block_given? enumerator end |