Fix bug in ts_node_prev_sibling w/ empty nodes

This commit is contained in:
Max Brunsfeld 2018-08-30 20:19:26 -07:00
parent e56d17a806
commit 068c9841a1
2 changed files with 40 additions and 2 deletions

View file

@ -153,7 +153,18 @@ static inline TSNode ts_node__child(TSNode self, uint32_t child_index, bool incl
return ts_node__null();
}
static bool ts_subtree_has_trailing_empty_descendant(const Subtree *self, const Subtree *other) {
for (unsigned i = self->children.size - 1; i + 1 > 0; i--) {
const Subtree *child = self->children.contents[i];
if (child->size.bytes > 0 || child->padding.bytes > 0) break;
if (child == other || ts_subtree_has_trailing_empty_descendant(child, other)) return true;
}
return false;
}
static inline TSNode ts_node__prev_sibling(TSNode self, bool include_anonymous) {
const Subtree *self_subtree = ts_node__subtree(self);
bool self_is_empty = self_subtree->size.bytes == 0 && self_subtree->padding.bytes == 0;
uint32_t target_end_byte = ts_node_end_byte(self);
TSNode node = ts_node_parent(self);
@ -168,8 +179,13 @@ static inline TSNode ts_node__prev_sibling(TSNode self, bool include_anonymous)
TSNode child;
ChildIterator iterator = ts_node_iterate_children(&node);
while (ts_node_child_iterator_next(&iterator, &child)) {
if (iterator.position.bytes >= target_end_byte) {
found_child_containing_target = ts_node__subtree(child) != ts_node__subtree(self);
if (child.id == self.id) break;
if (iterator.position.bytes > target_end_byte || (
iterator.position.bytes == target_end_byte && (
!self_is_empty ||
ts_subtree_has_trailing_empty_descendant(ts_node__subtree(child), self_subtree)))) {
found_child_containing_target = true;
break;
}

View file

@ -399,6 +399,28 @@ describe("Node", [&]() {
AssertThat(ts_node_next_named_sibling(root_node), Equals(NULL_NODE));
AssertThat(ts_node_prev_named_sibling(root_node), Equals(NULL_NODE));
});
it("works for missing nodes", [&]() {
ts_tree_delete(tree);
string input_string = "<div";
ts_parser_set_language(parser, load_real_language("html"));
tree = ts_parser_parse_string(parser, nullptr, input_string.c_str(), input_string.size());
root_node = ts_tree_root_node(tree);
char *node_string = ts_node_string(root_node);
AssertThat(node_string, Equals("(fragment (element (self_closing_tag (tag_name) (MISSING))))"));
ts_free(node_string);
TSNode tag_node = ts_node_child(ts_node_child(root_node, 0), 0);
TSNode missing_node = ts_node_child(tag_node, 2);
AssertThat(ts_node_type(missing_node), Equals("/>"));
AssertThat(ts_node_is_missing(missing_node), IsTrue());
TSNode tag_name_node = ts_node_prev_sibling(missing_node);
AssertThat(ts_node_type(tag_name_node), Equals("tag_name"));
AssertThat(ts_node_next_sibling(tag_name_node), Equals(missing_node));
});
});
describe("next_named_sibling(), prev_named_sibling()", [&]() {