Fix TreeCursor bugs

* ts_tree_cursor_goto_first_child_for_byte failed to find the child
  in some cases
* ts_tree_cursor_goto_parent did not handle aliased invisible parent
  nodes

Co-Authored-By: Ashi Krishnan <queerviolet@github.com>
This commit is contained in:
Max Brunsfeld 2018-06-28 16:17:16 -07:00
parent 80cab8fd8a
commit 5ab6401478
2 changed files with 83 additions and 1 deletions

View file

@ -153,6 +153,11 @@ 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;
}
@ -194,7 +199,16 @@ bool ts_tree_cursor_goto_parent(TSTreeCursor *_self) {
TreeCursor *self = (TreeCursor *)_self;
for (unsigned i = self->stack.size - 2; i + 1 > 0; i--) {
TreeCursorEntry *entry = &self->stack.contents[i];
if (entry->subtree->visible) {
bool is_aliased = false;
if (i > 0) {
TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
const TSSymbol *alias_sequence = ts_language_alias_sequence(
self->tree->language,
parent_entry->subtree->alias_sequence_id
);
is_aliased = alias_sequence && alias_sequence[entry->structural_child_index];
}
if (entry->subtree->visible || is_aliased) {
self->stack.size = i + 1;
return true;
}

View file

@ -757,6 +757,74 @@ describe("TreeCursor", [&]() {
AssertThat(ts_tree_cursor_goto_parent(&cursor), IsTrue());
AssertThat(ts_tree_cursor_goto_first_child_for_byte(&cursor, 0), Equals(0));
});
it("walks the tree correctly when there are hidden leaf nodes", [&]() {
ts_parser_set_language(parser, load_real_language("javascript"));
ts_tree_cursor_delete(&cursor);
ts_tree_delete(tree);
tree = ts_parser_parse_string(parser, nullptr, "`abc${1}def${2}ghi`", 19);
cursor = ts_tree_cursor_new(ts_tree_root_node(tree));
TSNode node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("program"));
ts_tree_cursor_goto_first_child(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("expression_statement"));
ts_tree_cursor_goto_first_child(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("template_string"));
int index = ts_tree_cursor_goto_first_child_for_byte(&cursor, 9);
AssertThat(index, Equals(2));
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("template_substitution"));
AssertThat(ts_node_start_byte(node), Equals(11u));
AssertThat(ts_node_end_byte(node), Equals(15u));
index = ts_tree_cursor_goto_first_child_for_byte(&cursor, 20);
AssertThat(index, Equals(-1));
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("template_substitution"));
ts_tree_cursor_goto_first_child(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("${"));
index = ts_tree_cursor_goto_first_child_for_byte(&cursor, 20);
AssertThat(index, Equals(-1));
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("${"));
});
it("handles parent nodes that are aliased", [&]() {
ts_parser_set_language(parser, load_real_language("html"));
ts_tree_cursor_delete(&cursor);
ts_tree_delete(tree);
tree = ts_parser_parse_string(parser, nullptr, "<script></script>", 18);
cursor = ts_tree_cursor_new(ts_tree_root_node(tree));
TSNode node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("fragment"));
ts_tree_cursor_goto_first_child(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("raw_element"));
ts_tree_cursor_goto_first_child(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("start_tag"));
ts_tree_cursor_goto_first_child(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("<"));
ts_tree_cursor_goto_parent(&cursor);
node = ts_tree_cursor_current_node(&cursor);
AssertThat(ts_node_type(node), Equals("start_tag"));
});
});
END_TEST