From 17e8521f6a46aedf3a038ee7cee75cb3338ff0ba Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Thu, 11 Dec 2025 18:00:17 +0000 Subject: [PATCH] Modularize insertion/deletion --- include/editor.h | 2 + include/knot.h | 2 +- include/utils.h | 2 +- src/editor.cc | 78 ++++++++++++++++++++++++++++++--- src/knot.cc | 41 ++++++++++++------ src/main.cc | 109 +++++++---------------------------------------- src/utils.cc | 2 +- 7 files changed, 123 insertions(+), 113 deletions(-) diff --git a/include/editor.h b/include/editor.h index c0d48ab..d1e9ec6 100644 --- a/include/editor.h +++ b/include/editor.h @@ -115,5 +115,7 @@ void cursor_left(Editor *editor, uint32_t number); void cursor_right(Editor *editor, uint32_t number); void ensure_scroll(Editor *editor); void apply_edit(std::vector &spans, uint32_t x, int64_t y); +void edit_erase(Editor *editor, uint32_t pos, uint32_t len); +void edit_insert(Editor *editor, uint32_t pos, char *data, uint32_t len); #endif diff --git a/include/knot.h b/include/knot.h index f6a97ae..91309a5 100644 --- a/include/knot.h +++ b/include/knot.h @@ -100,7 +100,7 @@ char *read(Knot *root, uint32_t offset, uint32_t len); void split(Knot *node, uint32_t offset, Knot **left, Knot **right); // Used to convert a byte offset to a line number that contains that byte -uint32_t byte_to_line(Knot *node, uint32_t offset); +uint32_t byte_to_line(Knot *node, uint32_t offset, uint32_t *out_col); // Used to convert a line number to a byte offset (start of the line) // also sets out_len to the length of the line diff --git a/include/utils.h b/include/utils.h index 0e3fcca..43b4bb0 100644 --- a/include/utils.h +++ b/include/utils.h @@ -36,7 +36,7 @@ struct Coord { uint32_t col; }; -uint32_t grapheme_strlen(const char *s); +uint32_t visual_width(const char *s); uint32_t get_visual_col_from_bytes(const char *line, uint32_t byte_limit); uint32_t get_bytes_from_visual_col(const char *line, uint32_t target_visual_col); diff --git a/src/editor.cc b/src/editor.cc index 10b790c..e771c5b 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -75,8 +75,7 @@ void scroll_up(Editor *editor, uint32_t number) { if (fold_state == 2) { stack.push_back({1, current_idx}); } else if (fold_state == 0) { - uint32_t len = - (line_content != nullptr) ? grapheme_strlen(line_content) : 0; + uint32_t len = (line_content != nullptr) ? visual_width(line_content) : 0; stack.push_back({len, current_idx}); } if (line_content) @@ -135,7 +134,7 @@ void scroll_down(Editor *editor, uint32_t number) { if (fold_state == 2) { segments = 1; } else { - uint32_t len = grapheme_strlen(line_content); + uint32_t len = visual_width(line_content); segments = (len > 0) ? (len + wrap_limit - 1) / wrap_limit : 1; } uint32_t start_seg = (current_row == editor->scroll.row) @@ -353,7 +352,7 @@ void ensure_scroll(Editor *editor) { char *line = next_line(it); if (!line) break; - uint32_t len = grapheme_strlen(line); + uint32_t len = visual_width(line); visual_delta += (len + editor->size.col - 1) / editor->size.col; free(line); } @@ -378,7 +377,7 @@ void ensure_scroll(Editor *editor) { free(line); break; } - uint32_t len = grapheme_strlen(line); + uint32_t len = visual_width(line); uint32_t visible_len = len - offset; if (visible_len == 0) visible_len = 1; @@ -436,6 +435,75 @@ void apply_edit(std::vector &spans, uint32_t x, int64_t y) { } } +void edit_erase(Editor *editor, uint32_t pos, uint32_t len) { + if (len == 0) + return; + std::shared_lock lock_1(editor->knot_mtx); + uint32_t col; + uint32_t row = byte_to_line(editor->root, pos, &col); + TSPoint start_point = {row, col}; + row = byte_to_line(editor->root, pos + len, &col); + TSPoint old_end_point = {row, col}; + lock_1.unlock(); + std::unique_lock lock_2(editor->knot_mtx); + editor->root = erase(editor->root, pos, len); + lock_2.unlock(); + if (editor->tree) { + TSInputEdit edit = { + .start_byte = pos, + .old_end_byte = pos + len, + .new_end_byte = pos, + .start_point = start_point, + .old_end_point = old_end_point, + .new_end_point = start_point, + }; + editor->edit_queue.push(edit); + } + std::unique_lock lock_3(editor->spans.mtx); + apply_edit(editor->spans.spans, pos, -(int32_t)len); + if (editor->spans.mid_parse) + editor->spans.edits.push({pos, -(int32_t)len}); +} + +void edit_insert(Editor *editor, uint32_t pos, char *data, uint32_t len) { + std::shared_lock lock_1(editor->knot_mtx); + uint32_t col; + uint32_t row = byte_to_line(editor->root, pos, &col); + TSPoint start_point = {row, col}; + lock_1.unlock(); + std::unique_lock lock_2(editor->knot_mtx); + editor->root = insert(editor->root, pos, data, len); + if (memchr(data, '\n', len)) + editor->folded.resize(editor->root->line_count + 2); + lock_2.unlock(); + uint32_t cols = 0; + uint32_t rows = 0; + for (uint32_t i = 0; i < len; i++) { + if (data[i] == '\n') { + rows++; + cols = 0; + } else { + cols++; + } + } + if (editor->tree) { + TSInputEdit edit = { + .start_byte = pos, + .old_end_byte = pos, + .new_end_byte = pos + len, + .start_point = start_point, + .old_end_point = start_point, + .new_end_point = {start_point.row + rows, + (rows == 0) ? (start_point.column + cols) : cols}, + }; + editor->edit_queue.push(edit); + } + std::unique_lock lock_3(editor->spans.mtx); + apply_edit(editor->spans.spans, pos, len); + if (editor->spans.mid_parse) + editor->spans.edits.push({pos, len}); +} + void render_editor(Editor *editor) { Coord cursor = {UINT32_MAX, UINT32_MAX}; uint32_t line_index = editor->scroll.row; diff --git a/src/knot.cc b/src/knot.cc index 49de155..702eca9 100644 --- a/src/knot.cc +++ b/src/knot.cc @@ -391,25 +391,42 @@ static uint32_t find_nth_newline_offset(Knot *node, uint32_t n) { } } -uint32_t byte_to_line(Knot *node, uint32_t offset) { - if (!node) +uint32_t byte_to_line(Knot *node, uint32_t offset, uint32_t *out_col) { + if (!node) { + if (out_col) + *out_col = 0; return 0; - if (offset >= node->char_count) + } + if (offset >= node->char_count) { + if (out_col) + *out_col = 0; return node->line_count; + } if (node->depth == 0) { - uint32_t lines_before = 0; - uint32_t limit = (offset < node->char_count) ? offset : node->char_count; - for (uint32_t i = 0; i < limit; i++) - if (node->data[i] == '\n') - lines_before++; - return lines_before; + uint32_t line = 0; + uint32_t col = 0; + for (uint32_t i = 0; i < offset; i++) { + if (node->data[i] == '\n') { + line++; + col = 0; + } else { + col++; + } + } + if (out_col) + *out_col = col; + return line; } uint32_t left_chars = node->left ? node->left->char_count : 0; if (offset < left_chars) { - return byte_to_line(node->left, offset); + return byte_to_line(node->left, offset, out_col); } else { - uint32_t left_lines = node->left ? node->left->line_count : 0; - return left_lines + byte_to_line(node->right, offset - left_chars); + uint32_t sub_col = 0; + uint32_t line = (node->left ? node->left->line_count : 0) + + byte_to_line(node->right, offset - left_chars, &sub_col); + if (out_col) + *out_col = sub_col; + return line; } } diff --git a/src/main.cc b/src/main.cc index 2cb9a9b..6d1eeee 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,10 +1,8 @@ #include "../include/editor.h" #include "../include/ts.h" #include "../include/ui.h" -#include "../libs/tree-sitter/lib/include/tree_sitter/api.h" #include #include -#include #include #include @@ -51,107 +49,32 @@ void handle_editor_event(Editor *editor, KeyEvent event) { event.c == ':' || event.c == '\'' || event.c == '"' || event.c == ',' || event.c == '.' || event.c == '<' || event.c == '>' || event.c == '/' || event.c == '?' || event.c == '`' || event.c == '~')) { - std::shared_lock lock_1(editor->knot_mtx); - uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - lock_1.unlock(); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = insert(editor->root, pos, &event.c, 1); - lock_2.unlock(); - if (editor->tree) { - TSInputEdit edit = { - .start_byte = pos, - .old_end_byte = pos, - .new_end_byte = pos + 1, - .start_point = {editor->cursor.row, editor->cursor.col}, - .old_end_point = {editor->cursor.row, editor->cursor.col}, - .new_end_point = {editor->cursor.row, editor->cursor.col + 1}, - }; - editor->edit_queue.push(edit); - } + edit_insert(editor, + line_to_byte(editor->root, editor->cursor.row, nullptr) + + editor->cursor.col, + &event.c, 1); cursor_right(editor, 1); - apply_edit(editor->spans.spans, pos, 1); - if (editor->spans.mid_parse) - editor->spans.edits.push({pos, 1}); } if (event.key_type == KEY_CHAR && event.c == '\t') { - std::shared_lock lock_1(editor->knot_mtx); - uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - lock_1.unlock(); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = insert(editor->root, pos, (char *)" ", 2); - lock_2.unlock(); - if (editor->tree) { - TSInputEdit edit = { - .start_byte = pos, - .old_end_byte = pos, - .new_end_byte = pos + 2, - .start_point = {editor->cursor.row, editor->cursor.col}, - .old_end_point = {editor->cursor.row, editor->cursor.col}, - .new_end_point = {editor->cursor.row, editor->cursor.col + 2}, - }; - editor->edit_queue.push(edit); - } + edit_insert(editor, + line_to_byte(editor->root, editor->cursor.row, nullptr) + + editor->cursor.col, + (char *)" ", 2); cursor_right(editor, 2); - std::unique_lock lock_3(editor->spans.mtx); - apply_edit(editor->spans.spans, pos, 2); - if (editor->spans.mid_parse) - editor->spans.edits.push({pos, 2}); } if (event.key_type == KEY_CHAR && (event.c == '\n' || event.c == '\r')) { - std::shared_lock lock_1(editor->knot_mtx); - uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - lock_1.unlock(); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = insert(editor->root, pos, (char *)"\n", 1); - editor->folded.resize(editor->root->line_count + 2); - lock_2.unlock(); - if (editor->tree) { - TSInputEdit edit = { - .start_byte = pos, - .old_end_byte = pos, - .new_end_byte = pos + 1, - .start_point = {editor->cursor.row, editor->cursor.col}, - .old_end_point = {editor->cursor.row, editor->cursor.col}, - .new_end_point = {editor->cursor.row + 1, 0}, - }; - editor->edit_queue.push(edit); - } + edit_insert(editor, + line_to_byte(editor->root, editor->cursor.row, nullptr) + + editor->cursor.col, + (char *)"\n", 1); cursor_right(editor, 1); - std::unique_lock lock_3(editor->spans.mtx); - apply_edit(editor->spans.spans, pos + 1, 1); - if (editor->spans.mid_parse) - editor->spans.edits.push({pos + 1, 1}); } if (event.key_type == KEY_CHAR && event.c == 0x7F) { - std::shared_lock lock_1(editor->knot_mtx); - uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - TSPoint old_point = {editor->cursor.row, editor->cursor.col}; + edit_erase(editor, + line_to_byte(editor->root, editor->cursor.row, nullptr) + + editor->cursor.col, + 1); cursor_left(editor, 1); - uint32_t start = line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - lock_1.unlock(); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = erase(editor->root, start, pos - start); - lock_2.unlock(); - if (editor->tree) { - TSInputEdit edit = { - .start_byte = start, - .old_end_byte = pos, - .new_end_byte = start, - .start_point = {editor->cursor.row, editor->cursor.col}, - .old_end_point = old_point, - .new_end_point = {editor->cursor.row, editor->cursor.col}, - }; - editor->edit_queue.push(edit); - } - std::unique_lock lock_3(editor->spans.mtx); - apply_edit(editor->spans.spans, start, start - pos); - if (editor->spans.mid_parse) - editor->spans.edits.push({start, start - pos}); } ensure_scroll(editor); } diff --git a/src/utils.cc b/src/utils.cc index 05ef9e3..20da742 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -25,7 +25,7 @@ std::string get_exe_dir() { return path.substr(0, path.find_last_of('/')); } -uint32_t grapheme_strlen(const char *s) { +uint32_t visual_width(const char *s) { if (!s) return 0; uint32_t count = 0;