Add ts_tree_cursor_goto_first_child_for_point function
This function (and the similar `ts_tree_cursor_goto_first_child_for_byte`) allows you to efficiently seek the tree cursor to a given position, exploiting the tree's internal balancing, without having to visit all of the preceding siblings of each node.
This commit is contained in:
parent
036aceed57
commit
919e9745a6
5 changed files with 166 additions and 6 deletions
|
|
@ -262,6 +262,113 @@ fn test_tree_cursor_fields() {
|
|||
assert_eq!(cursor.field_name(), Some("parameters"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree_cursor_child_for_point() {
|
||||
let mut parser = Parser::new();
|
||||
parser.set_language(get_language("javascript")).unwrap();
|
||||
let source = &"
|
||||
[
|
||||
one,
|
||||
{
|
||||
two: tree
|
||||
},
|
||||
four, five, six
|
||||
];"[1..];
|
||||
let tree = parser.parse(source, None).unwrap();
|
||||
|
||||
let mut c = tree.walk();
|
||||
assert_eq!(c.node().kind(), "program");
|
||||
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(7, 0)), None);
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(6, 6)), None);
|
||||
assert_eq!(c.node().kind(), "program");
|
||||
|
||||
// descend to expression statement
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(6, 5)), Some(0));
|
||||
assert_eq!(c.node().kind(), "expression_statement");
|
||||
|
||||
// step into ';' and back up
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(7, 0)), None);
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(6, 5)), Some(1));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
(";", Point::new(6, 5))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
|
||||
// descend into array
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(6, 4)), Some(0));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("array", Point::new(0, 4))
|
||||
);
|
||||
|
||||
// step into '[' and back up
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(0, 4)), Some(0));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("[", Point::new(0, 4))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
|
||||
// step into identifier 'one' and back up
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(0, 5)), Some(1));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("identifier", Point::new(1, 8))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(1, 10)), Some(1));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("identifier", Point::new(1, 8))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
|
||||
// step into first ',' and back up
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(1, 11)), Some(2));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
(",", Point::new(1, 11))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
|
||||
// step into identifier 'four' and back up
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(4, 10)), Some(5));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("identifier", Point::new(5, 8))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(5, 0)), Some(5));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("identifier", Point::new(5, 8))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
|
||||
// step into ']' and back up
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(6, 0)), Some(10));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("]", Point::new(6, 4))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(5, 23)), Some(10));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("]", Point::new(6, 4))
|
||||
);
|
||||
assert!(c.goto_parent());
|
||||
|
||||
// descend into object
|
||||
assert_eq!(c.goto_first_child_for_point(Point::new(2, 0)), Some(3));
|
||||
assert_eq!(
|
||||
(c.node().kind(), c.node().start_position()),
|
||||
("object", Point::new(2, 8))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree_node_equality() {
|
||||
let mut parser = Parser::new();
|
||||
|
|
|
|||
|
|
@ -586,12 +586,16 @@ extern "C" {
|
|||
}
|
||||
extern "C" {
|
||||
#[doc = " Move the cursor to the first child of its current node that extends beyond"]
|
||||
#[doc = " the given byte offset."]
|
||||
#[doc = " the given byte offset or point."]
|
||||
#[doc = ""]
|
||||
#[doc = " This returns the index of the child node if one was found, and returns -1"]
|
||||
#[doc = " if no such child was found."]
|
||||
pub fn ts_tree_cursor_goto_first_child_for_byte(arg1: *mut TSTreeCursor, arg2: u32) -> i64;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ts_tree_cursor_goto_first_child_for_point(arg1: *mut TSTreeCursor, arg2: TSPoint)
|
||||
-> i64;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ts_tree_cursor_copy(arg1: *const TSTreeCursor) -> TSTreeCursor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1172,6 +1172,21 @@ impl<'a> TreeCursor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Move this cursor to the first child of its current node that extends beyond
|
||||
/// the given byte offset.
|
||||
///
|
||||
/// This returns the index of the child node if one was found, and returns `None`
|
||||
/// if no such child was found.
|
||||
pub fn goto_first_child_for_point(&mut self, point: Point) -> Option<usize> {
|
||||
let result =
|
||||
unsafe { ffi::ts_tree_cursor_goto_first_child_for_point(&mut self.0, point.into()) };
|
||||
if result < 0 {
|
||||
None
|
||||
} else {
|
||||
Some(result as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-initialize this tree cursor to start at a different node.
|
||||
pub fn reset(&mut self, node: Node<'a>) {
|
||||
unsafe { ffi::ts_tree_cursor_reset(&mut self.0, node.0) };
|
||||
|
|
|
|||
|
|
@ -651,12 +651,13 @@ bool ts_tree_cursor_goto_first_child(TSTreeCursor *);
|
|||
|
||||
/**
|
||||
* Move the cursor to the first child of its current node that extends beyond
|
||||
* the given byte offset.
|
||||
* the given byte offset or point.
|
||||
*
|
||||
* This returns the index of the child node if one was found, and returns -1
|
||||
* if no such child was found.
|
||||
*/
|
||||
int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *, uint32_t);
|
||||
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *, TSPoint);
|
||||
|
||||
TSTreeCursor ts_tree_cursor_copy(const TSTreeCursor *);
|
||||
|
||||
|
|
|
|||
|
|
@ -159,10 +159,43 @@ int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t g
|
|||
}
|
||||
} while (did_descend);
|
||||
|
||||
if (self->stack.size > initial_size &&
|
||||
ts_tree_cursor_goto_next_sibling((TSTreeCursor *)self)) {
|
||||
return visible_child_index;
|
||||
}
|
||||
self->stack.size = initial_size;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *_self, TSPoint goal_point) {
|
||||
TreeCursor *self = (TreeCursor *)_self;
|
||||
uint32_t initial_size = self->stack.size;
|
||||
uint32_t visible_child_index = 0;
|
||||
|
||||
bool did_descend;
|
||||
do {
|
||||
did_descend = false;
|
||||
|
||||
bool visible;
|
||||
TreeCursorEntry entry;
|
||||
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
|
||||
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
|
||||
TSPoint end_point = point_add(entry.position.extent, ts_subtree_size(*entry.subtree).extent);
|
||||
bool at_goal = point_gt(end_point, goal_point);
|
||||
uint32_t visible_child_count = ts_subtree_visible_child_count(*entry.subtree);
|
||||
if (at_goal) {
|
||||
if (visible) {
|
||||
array_push(&self->stack, entry);
|
||||
return visible_child_index;
|
||||
}
|
||||
if (visible_child_count > 0) {
|
||||
array_push(&self->stack, entry);
|
||||
did_descend = true;
|
||||
break;
|
||||
}
|
||||
} else if (visible) {
|
||||
visible_child_index++;
|
||||
} else {
|
||||
visible_child_index += visible_child_count;
|
||||
}
|
||||
}
|
||||
} while (did_descend);
|
||||
|
||||
self->stack.size = initial_size;
|
||||
return -1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue