Tree Cursors โ
What you'll learn โ
- Depth-first traversal with a reusable cursor.
- Depth and current-field introspection.
When to use this โ
Walk whole subtrees (highlighting, analysis) without allocating per node.
Complete example โ
examples/tree_cursor.zig over a + b * c:
zig
var cursor = tree.cursor();
defer cursor.deinit();
outer: while (true) {
const node = cursor.currentNode();
std.debug.print("depth={d} {s} [{d}, {d}]\n", .{ cursor.depth(), node.nodeType(), node.startByte(), node.endByte() });
if (cursor.gotoFirstChild()) continue;
while (true) {
if (cursor.gotoNextSibling()) break;
if (!cursor.gotoParent()) break :outer;
}
}Expected output โ
text
depth=0 program [0, 9]
depth=1 expression [0, 9]
depth=2 expression [0, 1]
depth=3 term [0, 1]
depth=4 factor [0, 1]
depth=5 identifier [0, 1]
depth=2 + [2, 3]
depth=2 term [4, 9]
depth=3 term [4, 5]
depth=4 factor [4, 5]
depth=5 identifier [4, 5]
depth=3 * [6, 7]
depth=3 factor [8, 9]
depth=4 identifier [8, 9]How it works โ
- The cursor keeps a small stack of
(node, child position)entries. Movement rewrites the top entry โ ordinary movement never touches the allocator. depth()reports how far below the start node you are;currentFieldName()reports the field binding (e.g."left") of the current child, if any.gotoDescendant(byte)dives to the deepest node containing a byte offset โ ideal for hover and click handling.reset(node)reuses the cursor for another walk;copy()snapshots it.
Memory ownership โ
tree.cursor() inherits the tree's allocator. cursor.deinit() releases the cursor's own stack; the tree is untouched.
API used โ
- Tree Cursor, Tree โ
cursor().
