diff --git a/src/runtime/node.c b/src/runtime/node.c index 717eed29..f002d446 100644 --- a/src/runtime/node.c +++ b/src/runtime/node.c @@ -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; } diff --git a/test/runtime/node_test.cc b/test/runtime/node_test.cc index 7b29f96c..d0aa69e3 100644 --- a/test/runtime/node_test.cc +++ b/test/runtime/node_test.cc @@ -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 = "
")); + 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()", [&]() {