diff --git a/cli/src/tests/node_test.rs b/cli/src/tests/node_test.rs index 28c52116..e7b9640b 100644 --- a/cli/src/tests/node_test.rs +++ b/cli/src/tests/node_test.rs @@ -405,6 +405,28 @@ fn test_node_descendant_count() { } } +#[test] +fn test_descendant_count_single_node_tree() { + let mut parser = Parser::new(); + parser + .set_language(get_language("embedded-template")) + .unwrap(); + let tree = parser.parse("hello", None).unwrap(); + + let nodes = get_all_nodes(&tree); + assert_eq!(nodes.len(), 2); + assert_eq!(tree.root_node().descendant_count(), 2); + + let mut cursor = tree.root_node().walk(); + + cursor.goto_descendant(0); + assert_eq!(cursor.depth(), 0); + assert_eq!(cursor.node(), nodes[0]); + cursor.goto_descendant(1); + assert_eq!(cursor.depth(), 1); + assert_eq!(cursor.node(), nodes[1]); +} + #[test] fn test_node_descendant_for_range() { let tree = parse_json_example(); diff --git a/lib/src/subtree.c b/lib/src/subtree.c index 76e45cee..2bf25dc5 100644 --- a/lib/src/subtree.c +++ b/lib/src/subtree.c @@ -971,6 +971,7 @@ void ts_subtree__print_dot_graph(const Subtree *self, uint32_t start_offset, "error-cost: %u\n" "has-changes: %u\n" "depends-on-column: %u\n" + "descendant-count: %u\n" "repeat-depth: %u\n" "lookahead-bytes: %u", start_offset, end_offset, @@ -978,6 +979,7 @@ void ts_subtree__print_dot_graph(const Subtree *self, uint32_t start_offset, ts_subtree_error_cost(*self), ts_subtree_has_changes(*self), ts_subtree_depends_on_column(*self), + ts_subtree_visible_descendant_count(*self), ts_subtree_repeat_depth(*self), ts_subtree_lookahead_bytes(*self) ); diff --git a/lib/src/tree_cursor.c b/lib/src/tree_cursor.c index 5c102fcc..8e6d9414 100644 --- a/lib/src/tree_cursor.c +++ b/lib/src/tree_cursor.c @@ -18,9 +18,9 @@ typedef struct { static inline bool ts_tree_cursor_is_entry_visible(const TreeCursor *self, uint32_t i) { TreeCursorEntry *entry = &self->stack.contents[i]; - if (ts_subtree_visible(*entry->subtree)) { + if (i == 0 || ts_subtree_visible(*entry->subtree)) { return true; - } else if (i > 0 && !ts_subtree_extra(*entry->subtree)) { + } else if (!ts_subtree_extra(*entry->subtree)) { TreeCursorEntry *parent_entry = &self->stack.contents[i - 1]; return ts_language_alias_at( self->tree->language, @@ -275,12 +275,17 @@ void ts_tree_cursor_goto_descendant( ) { TreeCursor *self = (TreeCursor *)_self; - // Ascend to the lowest ancestor that contains the goal descendant. + // Ascend to the lowest ancestor that contains the goal node. for (;;) { - TreeCursorEntry *entry = &self->stack.contents[self->stack.size - 1]; + uint32_t i = self->stack.size - 1; + TreeCursorEntry *entry = &self->stack.contents[i]; + uint32_t next_descendant_index = + entry->descendant_index + + (ts_tree_cursor_is_entry_visible(self, i) ? 1 : 0) + + ts_subtree_visible_descendant_count(*entry->subtree); if ( (entry->descendant_index <= goal_descendant_index) && - (entry->descendant_index + ts_subtree_visible_descendant_count(*entry->subtree) > goal_descendant_index) + (next_descendant_index > goal_descendant_index) ) { break; } else if (self->stack.size <= 1) { @@ -290,23 +295,19 @@ void ts_tree_cursor_goto_descendant( } } - // Descend to the goal descendant. + // Descend to the goal node. bool did_descend = true; do { did_descend = false; bool visible; TreeCursorEntry entry; CursorChildIterator iterator = ts_tree_cursor_iterate_children(self); - - // If the goal descendant is the current node, then we're done. if (iterator.descendant_index > goal_descendant_index) { return; } while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) { - uint32_t next_descendant_index = entry.descendant_index + ts_subtree_visible_descendant_count(*entry.subtree); - if (visible) next_descendant_index += 1; - if (next_descendant_index > goal_descendant_index) { + if (iterator.descendant_index > goal_descendant_index) { array_push(&self->stack, entry); if (visible && entry.descendant_index == goal_descendant_index) { return;