From 9e8bec458de2f03c75cf353302bb6d4960af6a7e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 11 Jul 2018 16:17:46 -0700 Subject: [PATCH] Add ts_node_edit API --- include/tree_sitter/runtime.h | 1 + src/runtime/node.c | 17 ++++++ test/helpers/stream_methods.cc | 10 ++++ test/helpers/stream_methods.h | 3 ++ test/helpers/tree_helpers.cc | 5 +- test/runtime/node_test.cc | 98 +++++++++++++++++++++++++++++++++- 6 files changed, 132 insertions(+), 2 deletions(-) diff --git a/include/tree_sitter/runtime.h b/include/tree_sitter/runtime.h index 3c50c3f2..b2ee39a3 100644 --- a/include/tree_sitter/runtime.h +++ b/include/tree_sitter/runtime.h @@ -131,6 +131,7 @@ TSNode ts_node_descendant_for_byte_range(TSNode, uint32_t, uint32_t); TSNode ts_node_named_descendant_for_byte_range(TSNode, uint32_t, uint32_t); TSNode ts_node_descendant_for_point_range(TSNode, TSPoint, TSPoint); TSNode ts_node_named_descendant_for_point_range(TSNode, TSPoint, TSPoint); +void ts_node_edit(TSNode *, const TSInputEdit *); TSTreeCursor ts_tree_cursor_new(TSNode); void ts_tree_cursor_delete(TSTreeCursor *); diff --git a/src/runtime/node.c b/src/runtime/node.c index f64703b7..717eed29 100644 --- a/src/runtime/node.c +++ b/src/runtime/node.c @@ -494,3 +494,20 @@ TSNode ts_node_named_descendant_for_point_range(TSNode self, TSPoint min, TSPoint max) { return ts_node__descendant_for_point_range(self, min, max, false); } + +void ts_node_edit(TSNode *self, const TSInputEdit *edit) { + uint32_t start_byte = ts_node_start_byte(*self); + TSPoint start_point = ts_node_start_point(*self); + + if (start_byte >= edit->old_end_byte) { + start_byte = edit->new_end_byte + (start_byte - edit->old_end_byte); + start_point = point_add(edit->new_end_point, point_sub(start_point, edit->old_end_point)); + } else if (start_byte > edit->start_byte) { + start_byte = edit->new_end_byte; + start_point = edit->new_end_point; + } + + self->context[0] = start_byte; + self->context[1] = start_point.row; + self->context[2] = start_point.column; +} diff --git a/test/helpers/stream_methods.cc b/test/helpers/stream_methods.cc index 1ff791b6..3e5ed020 100644 --- a/test/helpers/stream_methods.cc +++ b/test/helpers/stream_methods.cc @@ -7,6 +7,16 @@ #include "compiler/lexical_grammar.h" #include "compiler/build_tables/parse_item.h" #include "compiler/build_tables/lex_item.h" +#include "helpers/point_helpers.h" + +ostream &operator<<(ostream &stream, const TSInputEdit &edit) { + return stream << "{TSInputEdit start_byte: " << edit.start_byte << + ", old_end_byte: " << edit.old_end_byte << + ", new_end_byte: " << edit.new_end_byte << + ", start_point: " << edit.start_point << + ", old_end_point: " << edit.old_end_point << + ", new_end_point: " << edit.new_end_point << "}"; +} namespace tree_sitter { diff --git a/test/helpers/stream_methods.h b/test/helpers/stream_methods.h index 07fcd6e0..66c86d52 100644 --- a/test/helpers/stream_methods.h +++ b/test/helpers/stream_methods.h @@ -7,6 +7,7 @@ #include #include #include +#include "tree_sitter/runtime.h" #include "compiler/grammar.h" #include "compiler/prepare_grammar/interned_grammar.h" #include "compiler/prepare_grammar/initial_syntax_grammar.h" @@ -91,6 +92,8 @@ inline std::ostream& operator<<(std::ostream &stream, const std::pair &p } // namespace std +std::ostream &operator<<(std::ostream &, const TSInputEdit &); + namespace tree_sitter { using std::ostream; diff --git a/test/helpers/tree_helpers.cc b/test/helpers/tree_helpers.cc index c7196713..9d58b831 100644 --- a/test/helpers/tree_helpers.cc +++ b/test/helpers/tree_helpers.cc @@ -46,7 +46,10 @@ ostream &operator<<(ostream &stream, const TSNode &node) { } bool operator==(const TSNode &left, const TSNode &right) { - return ts_node_eq(left, right); + return + left.id == right.id && + ts_node_start_byte(left) == ts_node_start_byte(right) && + ts_node_start_point(left) == ts_node_start_point(right); } bool operator==(const std::vector &vec, const SubtreeArray &array) { diff --git a/test/runtime/node_test.cc b/test/runtime/node_test.cc index 1b953351..7b29f96c 100644 --- a/test/runtime/node_test.cc +++ b/test/runtime/node_test.cc @@ -1,4 +1,3 @@ - #include "test_helper.h" #include "runtime/alloc.h" #include "helpers/tree_helpers.h" @@ -6,6 +5,8 @@ #include "helpers/load_language.h" #include "helpers/record_alloc.h" #include "helpers/stream_methods.h" +#include "helpers/random_helpers.h" +#include "helpers/spy_input.h" START_TEST @@ -556,6 +557,101 @@ describe("Node", [&]() { AssertThat(ts_node_start_point(array_node), Equals({7, 0})); }); }); + + describe("edit(edit)", [&]() { + vector> test_table = { + { + "insert 5 lines at the beginning", + { + 0, 0, 5, + {0, 0}, {0, 0}, {5, 0} + }, + }, + + { + "delete first 5 lines", + { + 0, (uint32_t)object_index, 0, + {0, 0}, {5, 2}, {0, 0} + }, + }, + + { + "replace entire text", + { + 0, (uint32_t)json_string.size(), 5, + {0, 0}, {9, 0}, {0, 5} + } + } + }; + + auto get_all_nodes = [&]() { + vector result; + bool visited_children = false; + TSTreeCursor cursor = ts_tree_cursor_new(ts_tree_root_node(tree)); + while (true) { + result.push_back(ts_tree_cursor_current_node(&cursor)); + if (!visited_children && ts_tree_cursor_goto_first_child(&cursor)) continue; + if (ts_tree_cursor_goto_next_sibling(&cursor)) { + visited_children = false; + } else if (ts_tree_cursor_goto_parent(&cursor)) { + visited_children = true; + } else { + break; + } + } + ts_tree_cursor_delete(&cursor); + return result; + }; + + for (auto &entry : test_table) { + const string &description = entry.first; + const TSInputEdit &edit = entry.second; + + it(("updates the node's start position according to edit - " + description).c_str(), [&]() { + auto nodes_before = get_all_nodes(); + + ts_tree_edit(tree, &edit); + for (TSNode &node : nodes_before) { + ts_node_edit(&node, &edit); + } + + auto nodes_after = get_all_nodes(); + + for (unsigned i = 0; i < nodes_before.size(); i++) { + TSNode &node_before = nodes_before[i]; + TSNode &node_after = nodes_after[i]; + AssertThat(node_before, Equals(node_after)); + } + }); + } + + it("updates the node's start position according to edit - random edits", [&]() { + SpyInput input(json_string, 3); + + for (unsigned i = 0; i < 10; i++) { + auto nodes_before = get_all_nodes(); + + size_t edit_start = default_generator(input.content.size()); + size_t deletion_size = default_generator(2) ? 0 : default_generator(input.content.size() - edit_start); + string inserted_text = default_generator.words(default_generator(4) + 1); + + TSInputEdit edit = input.replace(edit_start, deletion_size, inserted_text); + ts_tree_edit(tree, &edit); + for (TSNode &node : nodes_before) { + ts_node_edit(&node, &edit); + } + + auto nodes_after = get_all_nodes(); + + for (unsigned i = 0; i < nodes_before.size(); i++) { + TSNode &node_before = nodes_before[i]; + TSNode &node_after = nodes_after[i]; + AssertThat(node_before, Equals(node_after)); + } + } + }); + }); }); describe("TreeCursor", [&]() {