Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tree-sitter/tree-sitter/llms.txt

Use this file to discover all available pages before exploring further.

A Tree represents the syntactic structure of a source code file. Trees are immutable but can be edited to reflect changes in the source code.

Accessing Nodes

root_node

pub fn root_node(&self) -> Node
Get the root node of the syntax tree.
let tree = parser.parse(source_code, None).unwrap();
let root_node = tree.root_node();

root_node_with_offset

pub fn root_node_with_offset(&self, offset_bytes: usize, offset_extent: Point) -> Node
Get the root node of the syntax tree, but with its position shifted forward by the given offset.
offset_bytes
usize
required
The byte offset to shift the node’s position
offset_extent
Point
required
The row/column offset to shift the node’s position

Language Information

language

pub fn language(&self) -> LanguageRef
Get the language that was used to parse the syntax tree.
let language = tree.language();
println!("Parsed with: {}", language.name().unwrap());

Tree Editing

edit

pub fn edit(&mut self, edit: &InputEdit)
Edit the syntax tree to keep it in sync with source code that has been edited. You must describe the edit both in terms of byte offsets and in terms of row/column coordinates.
edit
&InputEdit
required
A description of the edit made to the source code
tree.edit(&InputEdit {
    start_byte: 8,
    old_end_byte: 8,
    new_end_byte: 14,
    start_position: Point::new(0, 8),
    old_end_position: Point::new(0, 8),
    new_end_position: Point::new(0, 14),
});

let new_tree = parser.parse(new_source_code, Some(&tree));

Tree Traversal

walk

pub fn walk(&self) -> TreeCursor
Create a new TreeCursor starting from the root of the tree. This is equivalent to calling tree.root_node().walk().
let mut cursor = tree.walk();
while cursor.goto_first_child() {
    println!("Node: {}", cursor.node().kind());
}

Comparing Trees

changed_ranges

pub fn changed_ranges(&self, other: &Self) -> impl ExactSizeIterator<Item = Range>
Compare this old edited syntax tree to a new syntax tree representing the same document, returning a sequence of ranges whose syntactic structure has changed. For this to work correctly, this syntax tree must have been edited such that its ranges match up to the new tree. Generally, you’ll want to call this method right after calling one of the Parser::parse functions. Call it on the old tree that was passed to parse, and pass the new tree that was returned from parse.
other
&Tree
required
The new tree to compare against
Returns: An iterator over the ranges that changed between the two trees.
let old_tree = parser.parse(old_source, None).unwrap();
old_tree.edit(&edit);
let new_tree = parser.parse(new_source, Some(&old_tree)).unwrap();

for range in old_tree.changed_ranges(&new_tree) {
    println!("Changed range: {:?}", range);
}

Range Information

included_ranges

pub fn included_ranges(&self) -> Vec<Range>
Get the included ranges that were used to parse the syntax tree.

Debugging

#[cfg(feature = "std")]
pub fn print_dot_graph(&self, file: &impl AsRawFd)
Print a graph of the tree to the given file descriptor. The graph is formatted in the DOT language. You may want to pipe this graph directly to a dot(1) process in order to generate SVG output.
file
&impl AsRawFd
required
The file descriptor to write the graph to
This method is only available on Unix platforms with the std feature enabled.

Cloning

The Tree struct implements Clone, allowing you to create copies of syntax trees:
let tree = parser.parse(source_code, None).unwrap();
let tree_copy = tree.clone();

InputEdit

The InputEdit struct describes a change to a text document:
pub struct InputEdit {
    pub start_byte: usize,
    pub old_end_byte: usize,
    pub new_end_byte: usize,
    pub start_position: Point,
    pub old_end_position: Point,
    pub new_end_position: Point,
}
start_byte
usize
The byte offset where the edit begins
old_end_byte
usize
The byte offset where the edit ends in the old text
new_end_byte
usize
The byte offset where the edit ends in the new text
start_position
Point
The row/column position where the edit begins
old_end_position
Point
The row/column position where the edit ends in the old text
new_end_position
Point
The row/column position where the edit ends in the new text

edit_point

pub fn edit_point(&self, point: &mut Point, byte: &mut usize)
Edit a point to keep it in-sync with source code that has been edited. This function updates a single point’s byte offset and row/column position based on this edit operation.
point
&mut Point
required
The point to update
byte
&mut usize
required
The byte offset to update

edit_range

pub fn edit_range(&self, range: &mut Range)
Edit a range to keep it in-sync with source code that has been edited. This function updates a range’s start and end positions based on this edit operation.
range
&mut Range
required
The range to update

Examples

Basic Tree Access

let tree = parser.parse("fn test() {}", None).unwrap();
let root = tree.root_node();

println!("Root kind: {}", root.kind());
println!("Root range: {:?}", root.range());

Incremental Parsing with Edits

// Parse the original source
let source = "fn test() {}";
let mut tree = parser.parse(source, None).unwrap();

// Make an edit: insert "a: u32" at position 8
let edit = InputEdit {
    start_byte: 8,
    old_end_byte: 8,
    new_end_byte: 14,
    start_position: Point::new(0, 8),
    old_end_position: Point::new(0, 8),
    new_end_position: Point::new(0, 14),
};

tree.edit(&edit);

// Re-parse with the edited tree
let new_source = "fn test(a: u32) {}";
let new_tree = parser.parse(new_source, Some(&tree)).unwrap();

Finding Changed Ranges

let old_source = "fn foo() {}";
let new_source = "fn bar() {}";

let mut old_tree = parser.parse(old_source, None).unwrap();

// Edit the tree to reflect the change from "foo" to "bar"
old_tree.edit(&InputEdit {
    start_byte: 3,
    old_end_byte: 6,
    new_end_byte: 6,
    start_position: Point::new(0, 3),
    old_end_position: Point::new(0, 6),
    new_end_position: Point::new(0, 6),
});

let new_tree = parser.parse(new_source, Some(&old_tree)).unwrap();

for range in old_tree.changed_ranges(&new_tree) {
    println!("Changed: {}:{} - {}:{}",
        range.start_point.row, range.start_point.column,
        range.end_point.row, range.end_point.column);
}

Tree Walking

let tree = parser.parse(source_code, None).unwrap();
let mut cursor = tree.walk();

fn walk_tree(cursor: &mut TreeCursor, depth: usize) {
    println!("{}{}", "  ".repeat(depth), cursor.node().kind());
    
    if cursor.goto_first_child() {
        loop {
            walk_tree(cursor, depth + 1);
            if !cursor.goto_next_sibling() {
                break;
            }
        }
        cursor.goto_parent();
    }
}

walk_tree(&mut cursor, 0);