Class: TreeStand::Tree

Inherits:
Object
  • Object
show all
Extended by:
Forwardable, T::Sig
Includes:
Enumerable
Defined in:
lib/tree_stand/tree.rb

Overview

Wrapper around a TreeSitter tree.

This class exposes a convient API for working with the tree. There are dangers in using this class. The tree is mutable and the document can be changed. This class does not protect against that.

Some of the moetods on this class edit and re-parse the document updating the tree. Because the document is re-parsed, the tree will be different. Which means all outstanding nodes & ranges will be invalid.

Methods that edit the document are suffixed with ‘!`, e.g. `#edit!`.

It’s often the case that you will want perfrom multiple edits. One such pattern is to call #query & #edit on all matches in a loop. It’s important to keep the destructive nature of #edit in mind and re-issue the query after each edit.

Another thing to keep in mind is that edits done later in the document will likely not affect the ranges that occur earlier in the document. This can be a convient property that could allow you to apply edits in a reverse order. This is not always possible and depends on the edits you make, beware that the tree will be different after each edit and this approach may cause bugs.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parser, tree, document) ⇒ 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.

Parameters:



80
81
82
83
84
# File 'lib/tree_stand/tree.rb', line 80

def initialize(parser, tree, document)
  @parser = parser
  @ts_tree = tree
  @document = document
end

Instance Attribute Details

#documentString (readonly)

Returns:

  • (String)


33
34
35
# File 'lib/tree_stand/tree.rb', line 33

def document
  @document
end

#parserTreeStand::Parser (readonly)

Returns:



37
38
39
# File 'lib/tree_stand/tree.rb', line 37

def parser
  @parser
end

#ts_treeTreeSitter::Tree (readonly)

Returns:

  • (TreeSitter::Tree)


35
36
37
# File 'lib/tree_stand/tree.rb', line 35

def ts_tree
  @ts_tree
end

Instance Method Details

#delete!(range) ⇒ void

This method returns an undefined value.

This method deletes the section of the document specified by range. Then it will reparse the document and update the tree!

Parameters:



106
107
108
109
110
111
# File 'lib/tree_stand/tree.rb', line 106

def delete!(range)
  new_document = +""
  new_document << @document[0...range.start_byte]
  new_document << @document[range.end_byte..-1]
  replace_with_new_doc(new_document)
end

#edit!(range, replacement) ⇒ void

This method returns an undefined value.

This method replaces the section of the document specified by range and replaces it with the provided text. Then it will reparse the document and update the tree!

Parameters:



95
96
97
98
99
100
101
# File 'lib/tree_stand/tree.rb', line 95

def edit!(range, replacement)
  new_document = +""
  new_document << @document[0...range.start_byte]
  new_document << replacement
  new_document << @document[range.end_byte..-1]
  replace_with_new_doc(new_document)
end

#find_node(query_string) ⇒ TreeStand::Node?

Note:

This is a convenience method that calls Node#find_node on #root_node.

Returns the first captured node that matches the query string or nil if there was no captured node.

Examples:

Find the first identifier node.

identifier_node = tree.root_node.find_node("(identifier) @identifier")

Parameters:

  • query_string (String)

Returns:

See Also:



67
68
69
70
71
72
73
74
# File 'lib/tree_stand/tree.rb', line 67

def_delegators(
  :root_node,
  :query,
  :find_node,
  :find_node!,
  :walk,
  :text,
)

#find_node!(query_string) ⇒ TreeStand::Node

Note:

This is a convenience method that calls Node#find_node! on #root_node.

Like #find_node, except that if no node is found, raises an NodeNotFound error.

Parameters:

  • query_string (String)

Returns:

Raises:

See Also:



67
68
69
70
71
72
73
74
# File 'lib/tree_stand/tree.rb', line 67

def_delegators(
  :root_node,
  :query,
  :find_node,
  :find_node!,
  :walk,
  :text,
)

#query(query_string) ⇒ Array<Hash{String => TreeStand::Node}>

Note:

This is a convenience method that calls Node#query on #root_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.

Examples:

# This will return a match for each identifier nodes in the tree.
tree_matches = tree.query(<<~QUERY)
  (identifier) @identifier
QUERY

# It is equivalent to:
tree.root_node.query(<<~QUERY)
  (identifier) @identifier
QUERY

Parameters:

  • query_string (String)

Returns:



67
68
69
70
71
72
73
74
# File 'lib/tree_stand/tree.rb', line 67

def_delegators(
  :root_node,
  :query,
  :find_node,
  :find_node!,
  :walk,
  :text,
)

#root_nodeTreeStand::Node

Returns:



87
88
89
# File 'lib/tree_stand/tree.rb', line 87

def root_node
  TreeStand::Node.new(self, @ts_tree.root_node)
end

#textString

Note:

This is a convenience method that calls Node#text on #root_node.

A convenience method for getting the text of the node. Each Node wraps the parent #tree and has access to the source document.

Returns:

  • (String)


67
68
69
70
71
72
73
74
# File 'lib/tree_stand/tree.rb', line 67

def_delegators(
  :root_node,
  :query,
  :find_node,
  :find_node!,
  :walk,
  :text,
)

#walk(&block) {|node| ... } ⇒ T::Enumerator[TreeStand::Node] Also known as: each

Note:

This is a convenience method that calls Node#walk on #root_node.

Backed by Visitors::TreeWalker.

Examples:

Check the subtree for error nodes

node.walk.any? { |node| node.type == :error }

Tree includes Enumerable

tree.any? { |node| node.type == :error }

Parameters:

Yield Parameters:

Returns:

See Also:



67
68
69
70
71
72
73
74
# File 'lib/tree_stand/tree.rb', line 67

def_delegators(
  :root_node,
  :query,
  :find_node,
  :find_node!,
  :walk,
  :text,
)