diff --git a/README.md b/README.md index ae463bd..c3a05e3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ A TUI IDE. # TODO -- [ ] Add alt+arrows to move line/block up/down. - [ ] Add `hooks` in files that can be set/unset/jumped to. - [ ] Add folding support at tree-sitter level (basic folding is done). - [ ] Add feature where doing enter uses tree-sitter to add newline with indentation. @@ -25,3 +24,4 @@ A TUI IDE. - [ ] Add codeium/copilot support. - [ ] Normalize / validate unicode on file open. - [ ] Add git stuff. +- [ ] Fix bug where alt+up at eof adds extra line. diff --git a/include/editor.h b/include/editor.h index afa2f87..a125b8f 100644 --- a/include/editor.h +++ b/include/editor.h @@ -130,6 +130,8 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len); Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y); char *get_selection(Editor *editor, uint32_t *out_len); void editor_worker(Editor *editor); +void move_line_down(Editor *editor); +void move_line_up(Editor *editor); void word_boundaries(Editor *editor, Coord coord, uint32_t *prev_col, uint32_t *next_col, uint32_t *prev_clusters, uint32_t *next_clusters); diff --git a/src/editor_ctrl.cc b/src/editor_ctrl.cc index 236b553..46efd46 100644 --- a/src/editor_ctrl.cc +++ b/src/editor_ctrl.cc @@ -165,10 +165,10 @@ void handle_editor_event(Editor *editor, KeyEvent event) { nullptr); switch (event.special_key) { case KEY_DOWN: - cursor_down(editor, 1); + cursor_down(editor, 5); break; case KEY_UP: - cursor_up(editor, 1); + cursor_up(editor, 5); break; case KEY_LEFT: editor->cursor_preffered = UINT32_MAX; @@ -187,8 +187,20 @@ void handle_editor_event(Editor *editor, KeyEvent event) { } break; case ALT: - // TODO: For up/down in insert/normal move line and in select move lines - // overlapping selection up/down. right/left are normal + switch (event.special_key) { + case KEY_DOWN: + move_line_down(editor); + break; + case KEY_UP: + move_line_up(editor); + break; + case KEY_LEFT: + cursor_left(editor, 8); + break; + case KEY_RIGHT: + cursor_right(editor, 8); + break; + } break; } } @@ -777,6 +789,93 @@ void cursor_left(Editor *editor, uint32_t number) { editor->cursor_preffered = UINT32_MAX; } +void move_line_up(Editor *editor) { + if (!editor || !editor->root || editor->cursor.row == 0) + return; + if (mode == NORMAL || mode == INSERT) { + uint32_t line_len, line_cluster_len; + std::shared_lock lock(editor->knot_mtx); + LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + char *line = next_line(it, &line_len); + free(it); + if (!line) { + lock.unlock(); + return; + } + line_cluster_len = count_clusters(line, line_len, 0, line_len); + lock.unlock(); + Coord cursor = editor->cursor; + edit_erase(editor, {cursor.row, 0}, line_cluster_len); + edit_insert(editor, {cursor.row - 1, 0}, line, line_len); + free(line); + editor->cursor = {cursor.row - 1, cursor.col}; + } else if (mode == SELECT) { + uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); + uint32_t end_row = MAX(editor->cursor.row, editor->selection.row); + uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr); + uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr); + char *selected_text = read(editor->root, start_byte, end_byte - start_byte); + if (!selected_text) + return; + uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte, + 0, end_byte - start_byte); + Coord cursor = editor->cursor; + Coord selection = editor->selection; + edit_erase(editor, {start_row, 0}, selected_len); + edit_insert(editor, {start_row - 1, 0}, selected_text, + end_byte - start_byte); + free(selected_text); + editor->cursor = {cursor.row - 1, cursor.col}; + editor->selection = {selection.row - 1, selection.col}; + } +} + +void move_line_down(Editor *editor) { + if (!editor || !editor->root) + return; + if (mode == NORMAL || mode == INSERT) { + if (editor->cursor.row >= editor->root->line_count - 1) + return; + uint32_t line_len, line_cluster_len; + std::shared_lock lock(editor->knot_mtx); + LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + char *line = next_line(it, &line_len); + free(it); + if (!line) { + lock.unlock(); + return; + } + line_cluster_len = count_clusters(line, line_len, 0, line_len); + lock.unlock(); + Coord cursor = editor->cursor; + edit_erase(editor, {cursor.row, 0}, line_cluster_len); + edit_insert(editor, {cursor.row + 1, 0}, line, line_len); + free(line); + editor->cursor = {cursor.row + 1, cursor.col}; + } else if (mode == SELECT) { + if (editor->cursor.row >= editor->root->line_count - 1 || + editor->selection.row >= editor->root->line_count - 1) + return; + uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); + uint32_t end_row = MAX(editor->cursor.row, editor->selection.row); + uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr); + uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr); + char *selected_text = read(editor->root, start_byte, end_byte - start_byte); + if (!selected_text) + return; + uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte, + 0, end_byte - start_byte); + Coord cursor = editor->cursor; + Coord selection = editor->selection; + edit_erase(editor, {start_row, 0}, selected_len); + edit_insert(editor, {start_row + 1, 0}, selected_text, + end_byte - start_byte); + free(selected_text); + editor->cursor = {cursor.row + 1, cursor.col}; + editor->selection = {selection.row + 1, selection.col}; + } +} + void edit_erase(Editor *editor, Coord pos, int64_t len) { if (len == 0) return;