diff --git a/src/runtime/document.c b/src/runtime/document.c index ab10ae49..9a13ca30 100644 --- a/src/runtime/document.c +++ b/src/runtime/document.c @@ -97,7 +97,7 @@ void ts_document_edit(TSDocument *self, TSInputEdit edit) { if (edit.bytes_removed > max_bytes - edit.start_byte) edit.bytes_removed = max_bytes - edit.start_byte; - ts_tree_edit(self->tree, &edit); + self->tree = ts_tree_edit(self->tree, &edit, &self->parser.tree_pool); if (self->parser.print_debugging_graphs) { ts_tree_print_dot_graph(self->tree, self->parser.language, stderr); diff --git a/src/runtime/tree.c b/src/runtime/tree.c index 13a2f08d..602392f4 100644 --- a/src/runtime/tree.c +++ b/src/runtime/tree.c @@ -58,8 +58,9 @@ bool ts_tree_array_copy(TreeArray self, TreeArray *dest) { if (self.capacity > 0) { contents = ts_calloc(self.capacity, sizeof(Tree *)); memcpy(contents, self.contents, self.size * sizeof(Tree *)); - for (uint32_t i = 0; i < self.size; i++) + for (uint32_t i = 0; i < self.size; i++) { ts_tree_retain(contents[i]); + } } dest->size = self.size; @@ -172,10 +173,23 @@ Tree *ts_tree_make_error(TreePool *pool, Length size, Length padding, int32_t lo Tree *ts_tree_make_copy(TreePool *pool, Tree *self) { Tree *result = ts_tree_pool_allocate(pool); *result = *self; + if (result->children.size > 0) { + ts_tree_array_copy(self->children, &result->children); + } result->ref_count = 1; return result; } +static Tree *ts_tree_make_mut(TreePool *pool, Tree *self) { + if (self->ref_count == 1) { + return self; + } else { + Tree *result = ts_tree_make_copy(pool, self); + ts_tree_release(pool, self); + return result; + } +} + static void ts_tree__compress(Tree *self, unsigned count, const TSLanguage *language, TreeArray *stack) { unsigned initial_stack_size = stack->size; @@ -438,42 +452,48 @@ int ts_tree_compare(const Tree *left, const Tree *right) { return 0; } -void ts_tree_invalidate_lookahead(Tree *self, uint32_t edit_byte_offset) { - if (edit_byte_offset >= self->bytes_scanned) return; - self->has_changes = true; - if (self->children.size > 0) { +Tree *ts_tree_invalidate_lookahead(Tree *self, uint32_t edit_byte_offset, TreePool *pool) { + if (edit_byte_offset >= self->bytes_scanned) return self; + + Tree *result = ts_tree_make_mut(pool, self); + result->has_changes = true; + + if (result->children.size > 0) { uint32_t child_start_byte = 0; - for (uint32_t i = 0; i < self->children.size; i++) { - Tree *child = self->children.contents[i]; + for (uint32_t i = 0; i < result->children.size; i++) { + Tree **child = &result->children.contents[i]; if (child_start_byte > edit_byte_offset) break; - ts_tree_invalidate_lookahead(child, edit_byte_offset - child_start_byte); - child_start_byte += ts_tree_total_bytes(child); + *child = ts_tree_invalidate_lookahead(*child, edit_byte_offset - child_start_byte, pool); + child_start_byte += ts_tree_total_bytes(*child); } } + + return result; } -void ts_tree__edit(Tree *self, Edit edit) { +Tree *ts_tree__edit(Tree *self, Edit edit, TreePool *pool) { Length new_end = length_add(edit.start, edit.added); Length old_end = length_add(edit.start, edit.removed); - self->has_changes = true; + Tree *result = ts_tree_make_mut(pool, self); + result->has_changes = true; - if (old_end.bytes <= self->padding.bytes) { - self->padding = length_add(new_end, length_sub(self->padding, old_end)); - } else if (edit.start.bytes < self->padding.bytes) { - self->size = length_sub(self->size, length_sub(old_end, self->padding)); - self->padding = new_end; - } else if (edit.start.bytes == self->padding.bytes && edit.removed.bytes == 0) { - self->padding = length_add(self->padding, edit.added); + if (old_end.bytes <= result->padding.bytes) { + result->padding = length_add(new_end, length_sub(result->padding, old_end)); + } else if (edit.start.bytes < result->padding.bytes) { + result->size = length_sub(result->size, length_sub(old_end, result->padding)); + result->padding = new_end; + } else if (edit.start.bytes == result->padding.bytes && edit.removed.bytes == 0) { + result->padding = length_add(result->padding, edit.added); } else { - Length new_total_size = length_add(new_end, length_sub(ts_tree_total_size(self), old_end)); - self->size = length_sub(new_total_size, self->padding); + Length new_total_size = length_add(new_end, length_sub(ts_tree_total_size(result), old_end)); + result->size = length_sub(new_total_size, result->padding); } Length child_left, child_right = length_zero(); - for (uint32_t i = 0; i < self->children.size; i++) { - Tree *child = self->children.contents[i]; - Length child_size = ts_tree_total_size(child); + for (uint32_t i = 0; i < result->children.size; i++) { + Tree **child = &result->children.contents[i]; + Length child_size = ts_tree_total_size(*child); child_left = child_right; child_right = length_add(child_left, child_size); @@ -499,19 +519,21 @@ void ts_tree__edit(Tree *self, Edit edit) { edit.added = length_zero(); edit.removed = length_sub(edit.removed, child_edit.removed); - ts_tree__edit(child, child_edit); + *child = ts_tree__edit(*child, child_edit, pool); } else if (child_left.bytes <= edit.start.bytes) { - ts_tree_invalidate_lookahead(child, edit.start.bytes - child_left.bytes); + *child = ts_tree_invalidate_lookahead(*child, edit.start.bytes - child_left.bytes, pool); } } + + return result; } -void ts_tree_edit(Tree *self, const TSInputEdit *edit) { - ts_tree__edit(self, (Edit) { +Tree *ts_tree_edit(Tree *self, const TSInputEdit *edit, TreePool *pool) { + return ts_tree__edit(self, (Edit) { .start = {edit->start_byte, edit->start_point}, .added = {edit->bytes_added, edit->extent_added}, .removed = {edit->bytes_removed, edit->extent_removed}, - }); + }, pool); } Tree *ts_tree_last_external_token(Tree *tree) { diff --git a/src/runtime/tree.h b/src/runtime/tree.h index ce96e41d..4fa95522 100644 --- a/src/runtime/tree.h +++ b/src/runtime/tree.h @@ -100,7 +100,7 @@ bool ts_tree_eq(const Tree *tree1, const Tree *tree2); int ts_tree_compare(const Tree *tree1, const Tree *tree2); void ts_tree_set_children(Tree *, TreeArray *, const TSLanguage *); void ts_tree_balance(Tree *, TreePool *, const TSLanguage *); -void ts_tree_edit(Tree *, const TSInputEdit *edit); +Tree *ts_tree_edit(Tree *, const TSInputEdit *edit, TreePool *); char *ts_tree_string(const Tree *, const TSLanguage *, bool include_all); void ts_tree_print_dot_graph(const Tree *, const TSLanguage *, FILE *); Tree *ts_tree_last_external_token(Tree *); diff --git a/test/runtime/tree_test.cc b/test/runtime/tree_test.cc index e80743e7..2d26f021 100644 --- a/test/runtime/tree_test.cc +++ b/test/runtime/tree_test.cc @@ -178,7 +178,7 @@ describe("Tree", []() { }); describe("edit", [&]() { - Tree *tree = nullptr; + Tree *tree; before_each([&]() { tree = ts_tree_make_node(&pool, symbol1, tree_array({ @@ -204,7 +204,7 @@ describe("Tree", []() { edit.start_point = {0, 1}; edit.extent_removed = {0, 0}; edit.extent_added = {0, 1}; - ts_tree_edit(tree, &edit); + tree = ts_tree_edit(tree, &edit, &pool); assert_consistent(tree); AssertThat(tree->has_changes, IsTrue()); @@ -230,7 +230,7 @@ describe("Tree", []() { edit.start_point = {0, 1}; edit.extent_removed = {0, 3}; edit.extent_added = {0, 4}; - ts_tree_edit(tree, &edit); + tree = ts_tree_edit(tree, &edit, &pool); assert_consistent(tree); AssertThat(tree->has_changes, IsTrue()); @@ -252,7 +252,7 @@ describe("Tree", []() { edit.start_point = {0, 2}; edit.extent_removed = {0, 0}; edit.extent_added = {0, 2}; - ts_tree_edit(tree, &edit); + tree = ts_tree_edit(tree, &edit, &pool); assert_consistent(tree); AssertThat(tree->has_changes, IsTrue()); @@ -276,7 +276,7 @@ describe("Tree", []() { edit.start_point = {0, 2}; edit.extent_removed = {0, 2}; edit.extent_added = {0, 5}; - ts_tree_edit(tree, &edit); + tree = ts_tree_edit(tree, &edit, &pool); assert_consistent(tree); AssertThat(tree->has_changes, IsTrue()); @@ -300,7 +300,7 @@ describe("Tree", []() { edit.start_point = {0, 1}; edit.extent_removed = {0, 10}; edit.extent_added = {0, 3}; - ts_tree_edit(tree, &edit); + tree = ts_tree_edit(tree, &edit, &pool); assert_consistent(tree); AssertThat(tree->has_changes, IsTrue()); @@ -332,7 +332,7 @@ describe("Tree", []() { edit.start_point = {0, 6}; edit.extent_removed = {0, 1}; edit.extent_added = {0, 1}; - ts_tree_edit(tree, &edit); + tree = ts_tree_edit(tree, &edit, &pool); assert_consistent(tree); AssertThat(tree->children.contents[0]->has_changes, IsTrue());