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.

Introduction

You can access every node in a syntax tree using the TSNode APIs described in Basic Parsing, but if you need to access a large number of nodes, the fastest way to do so is with a tree cursor. A cursor is a stateful object that allows you to walk a syntax tree with maximum efficiency by maintaining state about the current position in the tree.

Creating a Tree Cursor

You can initialize a cursor from any node:
TSTreeCursor ts_tree_cursor_new(TSNode);
TSNode root = ts_tree_root_node(tree);
TSTreeCursor cursor = ts_tree_cursor_new(root);

// Use the cursor...

// Clean up
ts_tree_cursor_delete(&cursor);
The node you pass to the cursor is considered the root of the cursor. The cursor cannot walk outside this node. Going to the parent or any sibling of the root node will always return false.This has no unexpected effects if the given input node is the actual root node of the tree, but is something to keep in mind when using cursors constructed with a non-root node.

Moving the Cursor

Once you have a cursor, you can move it around the tree imperatively:
bool ts_tree_cursor_goto_first_child(TSTreeCursor *);
bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *);
bool ts_tree_cursor_goto_previous_sibling(TSTreeCursor *);
bool ts_tree_cursor_goto_parent(TSTreeCursor *);
These methods return true if the cursor successfully moved and false if there was no node to move to.
goto_first_child
bool
Move the cursor to the first child of its current node. Returns true if the cursor successfully moved, false if there were no children.
goto_next_sibling
bool
Move the cursor to the next sibling of its current node. Returns true if the cursor successfully moved, false if there was no next sibling.
goto_previous_sibling
bool
Move the cursor to the previous sibling of its current node. Returns true if the cursor successfully moved, false if there was no previous sibling.
This function may be slower than goto_next_sibling due to how node positions are stored. In the worst case, this will need to iterate through all the children up to the previous sibling node to recalculate its position.
goto_parent
bool
Move the cursor to the parent of its current node. Returns true if the cursor successfully moved, false if there was no parent node (cursor was already on the root).

Retrieving the Current Node

You can always retrieve the cursor’s current node, as well as the field name associated with the current node:
TSNode ts_tree_cursor_current_node(const TSTreeCursor *);
const char *ts_tree_cursor_current_field_name(const TSTreeCursor *);
TSFieldId ts_tree_cursor_current_field_id(const TSTreeCursor *);
TSTreeCursor cursor = ts_tree_cursor_new(root);

TSNode node = ts_tree_cursor_current_node(&cursor);
const char *field_name = ts_tree_cursor_current_field_name(&cursor);

if (field_name) {
  printf("Current node type: %s, field: %s\n",
         ts_node_type(node), field_name);
} else {
  printf("Current node type: %s\n", ts_node_type(node));
}

Complete Example: Tree Traversal

Here’s a complete example that demonstrates how to traverse an entire tree using a cursor:
#include <stdio.h>
#include <tree_sitter/api.h>

void print_tree(TSTreeCursor *cursor, int depth) {
  TSNode node = ts_tree_cursor_current_node(cursor);
  const char *type = ts_node_type(node);
  const char *field = ts_tree_cursor_current_field_name(cursor);
  
  // Print indentation
  for (int i = 0; i < depth; i++) {
    printf("  ");
  }
  
  // Print node info
  if (field) {
    printf("%s: %s\n", field, type);
  } else {
    printf("%s\n", type);
  }
  
  // Visit children
  if (ts_tree_cursor_goto_first_child(cursor)) {
    do {
      print_tree(cursor, depth + 1);
    } while (ts_tree_cursor_goto_next_sibling(cursor));
    ts_tree_cursor_goto_parent(cursor);
  }
}

int main() {
  // ... setup parser and parse code ...
  TSNode root = ts_tree_root_node(tree);
  TSTreeCursor cursor = ts_tree_cursor_new(root);
  
  print_tree(&cursor, 0);
  
  ts_tree_cursor_delete(&cursor);
  ts_tree_delete(tree);
  ts_parser_delete(parser);
  return 0;
}

Advanced Cursor Operations

Jumping to a Specific Descendant

You can jump directly to the nth descendant of the cursor’s root:
void ts_tree_cursor_goto_descendant(TSTreeCursor *self, uint32_t goal_descendant_index);
uint32_t ts_tree_cursor_current_descendant_index(const TSTreeCursor *self);

Getting Depth Information

uint32_t ts_tree_cursor_current_depth(const TSTreeCursor *self);

Position-based Navigation

You can jump to the first child that contains or starts after a given position:
int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *self, uint32_t goal_byte);
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *self, TSPoint goal_point);
These functions return the index of the child node if one was found, and -1 if no such child was found.

Copying and Resetting Cursors

TSTreeCursor ts_tree_cursor_copy(const TSTreeCursor *cursor);
void ts_tree_cursor_reset(TSTreeCursor *self, TSNode node);
void ts_tree_cursor_reset_to(TSTreeCursor *dst, const TSTreeCursor *src);
Use ts_tree_cursor_reset_to to reset a cursor to the same position as another cursor. Unlike ts_tree_cursor_reset, this will not lose parent information and allows reusing already created cursors.

Performance Considerations

Tree cursors are significantly more efficient than TSNode operations when traversing large portions of the tree because:
  • Cursors maintain state about the current position, avoiding repeated lookups
  • Memory allocation is minimized
  • Navigation operations are optimized for sequential access
For accessing a small number of nodes, the TSNode API is more convenient and sufficient.
The goto_previous_sibling operation may be slower than goto_next_sibling because node positions are stored in a way that’s optimized for forward traversal. In the worst case, it needs to iterate through all children up to the previous sibling to recalculate its position.If you need to traverse siblings in reverse order, consider collecting them in forward order first, then iterating the collection in reverse.
Remember that cursors are scoped to the node they were created from. If you create a cursor from a non-root node, the cursor cannot navigate outside that subtree. This is useful for limiting traversal to a specific portion of the tree.

Next Steps

Pattern Matching

Learn how to query syntax trees with patterns

Static Node Types

Generate type information for your language