tree_house_bindings/tree_cursor.rs
1use ::std::os::raw;
2use std::cell::Cell;
3use std::ffi::{c_char, CStr};
4use std::marker::PhantomData;
5use std::{fmt, mem};
6
7use crate::node::NodeRaw;
8use crate::{Node, Tree};
9
10thread_local! {
11 static CACHE: Cell<Option<TreeCursorGuard>> = const { Cell::new(None) };
12}
13
14#[repr(C)]
15#[derive(Clone)]
16struct TreeCursorRaw {
17 tree: *const raw::c_void,
18 id: *const raw::c_void,
19 context: [u32; 3usize],
20}
21
22#[repr(C)]
23struct TreeCursorGuard(TreeCursorRaw);
24
25impl Drop for TreeCursorGuard {
26 fn drop(&mut self) {
27 unsafe { ts_tree_cursor_delete(&mut self.0) }
28 }
29}
30
31pub struct TreeCursor<'a> {
32 inner: TreeCursorRaw,
33 tree: PhantomData<&'a Tree>,
34}
35
36impl<'tree> TreeCursor<'tree> {
37 pub(crate) fn new(node: &Node<'tree>) -> Self {
38 Self {
39 inner: match CACHE.take() {
40 Some(guard) => unsafe {
41 let mut cursor = guard.0.clone();
42 mem::forget(guard);
43 ts_tree_cursor_reset(&mut cursor, node.as_raw());
44 cursor
45 },
46 None => unsafe { ts_tree_cursor_new(node.as_raw()) },
47 },
48 tree: PhantomData,
49 }
50 }
51
52 pub fn goto_parent(&mut self) -> bool {
53 unsafe { ts_tree_cursor_goto_parent(&mut self.inner) }
54 }
55
56 pub fn goto_next_sibling(&mut self) -> bool {
57 unsafe { ts_tree_cursor_goto_next_sibling(&mut self.inner) }
58 }
59
60 pub fn goto_previous_sibling(&mut self) -> bool {
61 unsafe { ts_tree_cursor_goto_previous_sibling(&mut self.inner) }
62 }
63
64 pub fn goto_first_child(&mut self) -> bool {
65 unsafe { ts_tree_cursor_goto_first_child(&mut self.inner) }
66 }
67
68 pub fn goto_last_child(&mut self) -> bool {
69 unsafe { ts_tree_cursor_goto_last_child(&mut self.inner) }
70 }
71
72 pub fn goto_first_child_for_byte(&mut self, byte_idx: u32) -> Option<u32> {
73 match unsafe { ts_tree_cursor_goto_first_child_for_byte(&mut self.inner, byte_idx) } {
74 -1 => None,
75 n => Some(n as u32),
76 }
77 }
78
79 pub fn node(&self) -> Node<'tree> {
80 unsafe { Node::from_raw(ts_tree_cursor_current_node(&self.inner)).unwrap_unchecked() }
81 }
82
83 pub fn field_name(&self) -> Option<&'tree str> {
84 unsafe {
85 let ptr = ts_tree_cursor_current_field_name(&self.inner);
86 (!ptr.is_null()).then(|| CStr::from_ptr(ptr).to_str().unwrap())
87 }
88 }
89}
90
91impl fmt::Debug for TreeCursorRaw {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 f.debug_struct("InactiveTreeCursor").finish_non_exhaustive()
94 }
95}
96
97impl Drop for TreeCursor<'_> {
98 fn drop(&mut self) {
99 CACHE.set(Some(TreeCursorGuard(self.inner.clone())))
100 }
101}
102
103impl Clone for TreeCursor<'_> {
104 fn clone(&self) -> Self {
105 TreeCursor {
106 inner: unsafe { ts_tree_cursor_copy(&self.inner) },
107 tree: PhantomData,
108 }
109 }
110}
111
112extern "C" {
113 /// Create a new tree cursor starting from the given node.
114 ///
115 /// A tree cursor allows you to walk a syntax tree more efficiently than is
116 /// possible using the `TSNode` functions. It is a mutable object that is always
117 /// on a certain syntax node, and can be moved imperatively to different nodes.
118 ///
119 /// Note that the given node is considered the root of the cursor,
120 /// and the cursor cannot walk outside this node.
121 fn ts_tree_cursor_new(node: NodeRaw) -> TreeCursorRaw;
122 /// Delete a tree cursor, freeing all of the memory that it used.
123 fn ts_tree_cursor_delete(self_: *mut TreeCursorRaw);
124 /// Re-initialize a tree cursor to start at a different node.
125 fn ts_tree_cursor_reset(self_: *mut TreeCursorRaw, node: NodeRaw);
126 // /// Re-initialize a tree cursor to the same position as another cursor.
127 // /// Unlike [`ts_tree_cursor_reset`], this will not lose parent information and
128 // /// allows reusing already created cursors.
129 // fn ts_tree_cursor_reset_to(dst: *mut TreeCursorRaw, src: *const TreeCursorRaw);
130 /// Get the tree cursor's current node.
131 fn ts_tree_cursor_current_node(self_: *const TreeCursorRaw) -> NodeRaw;
132 // /// Get the field name of the tree cursor's current node.
133 // /// This returns `NULL` if the current node doesn't have a field.
134 // /// See also [`ts_node_child_by_field_name`].
135 // fn ts_tree_cursor_current_field_name(self_: *const TreeCursorRaw) -> *const raw::c_char;
136 // /// Get the field id of the tree cursor's current node.
137 // /// This returns zero if the current node doesn't have a field.
138 // /// See also [`ts_node_child_by_field_id`], [`ts_language_field_id_for_name`].
139 // fn ts_tree_cursor_current_field_id(self_: *const TreeCursorRaw) -> TSFieldId;
140 /// Move the cursor to the parent of its current node.
141 /// This returns `true` if the cursor successfully moved, and returns `false`
142 /// if there was no parent node (the cursor was already on the root node).
143 fn ts_tree_cursor_goto_parent(self_: *mut TreeCursorRaw) -> bool;
144 /// Move the cursor to the next sibling of its current node.
145 /// This returns `true` if the cursor successfully moved, and returns `false`
146 /// if there was no next sibling node.
147 fn ts_tree_cursor_goto_next_sibling(self_: *mut TreeCursorRaw) -> bool;
148 /// Move the cursor to the previous sibling of its current node.
149 /// This returns `true` if the cursor successfully moved, and returns `false` if
150 /// there was no previous sibling node.
151 /// Note, that this function may be slower than
152 /// [`ts_tree_cursor_goto_next_sibling`] due to how node positions are stored. In
153 /// the worst case, this will need to iterate through all the children upto the
154 /// previous sibling node to recalculate its position.
155 fn ts_tree_cursor_goto_previous_sibling(self_: *mut TreeCursorRaw) -> bool;
156 /// Move the cursor to the first child of its current node.
157 /// This returns `true` if the cursor successfully moved, and returns `false`
158 /// if there were no children.
159 fn ts_tree_cursor_goto_first_child(self_: *mut TreeCursorRaw) -> bool;
160 /// Move the cursor to the last child of its current node.
161 /// This returns `true` if the cursor successfully moved, and returns `false` if
162 /// there were no children.
163 /// Note that this function may be slower than [`ts_tree_cursor_goto_first_child`]
164 /// because it needs to iterate through all the children to compute the child's
165 /// position.
166 fn ts_tree_cursor_goto_last_child(self_: *mut TreeCursorRaw) -> bool;
167 /*
168 /// Move the cursor to the node that is the nth descendant of
169 /// the original node that the cursor was constructed with, where
170 /// zero represents the original node itself.
171 fn ts_tree_cursor_goto_descendant(self_: *mut TreeCursorRaw, goal_descendant_index: u32);
172 /// Get the index of the cursor's current node out of all of the
173 /// descendants of the original node that the cursor was constructed with.
174 fn ts_tree_cursor_current_descendant_index(self_: *const TreeCursorRaw) -> u32;
175 /// Get the depth of the cursor's current node relative to the original
176 /// node that the cursor was constructed with.
177 fn ts_tree_cursor_current_depth(self_: *const TreeCursorRaw) -> u32;
178 */
179 /// Move the cursor to the first child of its current node that extends beyond
180 /// the given byte offset or point.
181 /// This returns the index of the child node if one was found, and returns -1
182 /// if no such child was found.
183 fn ts_tree_cursor_goto_first_child_for_byte(self_: *mut TreeCursorRaw, goal_byte: u32) -> i64;
184 fn ts_tree_cursor_copy(cursor: *const TreeCursorRaw) -> TreeCursorRaw;
185 /// Get the field name of the tree cursor's curren tnode.
186 ///
187 /// This returns `NULL` if the current node doesn't have a field. See also
188 /// `ts_node_child_by_field_name`.
189 fn ts_tree_cursor_current_field_name(cursor: *const TreeCursorRaw) -> *const c_char;
190}