Modularize insertion/deletion
This commit is contained in:
@@ -115,5 +115,7 @@ void cursor_left(Editor *editor, uint32_t number);
|
|||||||
void cursor_right(Editor *editor, uint32_t number);
|
void cursor_right(Editor *editor, uint32_t number);
|
||||||
void ensure_scroll(Editor *editor);
|
void ensure_scroll(Editor *editor);
|
||||||
void apply_edit(std::vector<Span> &spans, uint32_t x, int64_t y);
|
void apply_edit(std::vector<Span> &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
|
#endif
|
||||||
|
|||||||
@@ -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);
|
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
|
// 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)
|
// Used to convert a line number to a byte offset (start of the line)
|
||||||
// also sets out_len to the length of the line
|
// also sets out_len to the length of the line
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ struct Coord {
|
|||||||
uint32_t col;
|
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_visual_col_from_bytes(const char *line, uint32_t byte_limit);
|
||||||
uint32_t get_bytes_from_visual_col(const char *line,
|
uint32_t get_bytes_from_visual_col(const char *line,
|
||||||
uint32_t target_visual_col);
|
uint32_t target_visual_col);
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ void scroll_up(Editor *editor, uint32_t number) {
|
|||||||
if (fold_state == 2) {
|
if (fold_state == 2) {
|
||||||
stack.push_back({1, current_idx});
|
stack.push_back({1, current_idx});
|
||||||
} else if (fold_state == 0) {
|
} else if (fold_state == 0) {
|
||||||
uint32_t len =
|
uint32_t len = (line_content != nullptr) ? visual_width(line_content) : 0;
|
||||||
(line_content != nullptr) ? grapheme_strlen(line_content) : 0;
|
|
||||||
stack.push_back({len, current_idx});
|
stack.push_back({len, current_idx});
|
||||||
}
|
}
|
||||||
if (line_content)
|
if (line_content)
|
||||||
@@ -135,7 +134,7 @@ void scroll_down(Editor *editor, uint32_t number) {
|
|||||||
if (fold_state == 2) {
|
if (fold_state == 2) {
|
||||||
segments = 1;
|
segments = 1;
|
||||||
} else {
|
} 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;
|
segments = (len > 0) ? (len + wrap_limit - 1) / wrap_limit : 1;
|
||||||
}
|
}
|
||||||
uint32_t start_seg = (current_row == editor->scroll.row)
|
uint32_t start_seg = (current_row == editor->scroll.row)
|
||||||
@@ -353,7 +352,7 @@ void ensure_scroll(Editor *editor) {
|
|||||||
char *line = next_line(it);
|
char *line = next_line(it);
|
||||||
if (!line)
|
if (!line)
|
||||||
break;
|
break;
|
||||||
uint32_t len = grapheme_strlen(line);
|
uint32_t len = visual_width(line);
|
||||||
visual_delta += (len + editor->size.col - 1) / editor->size.col;
|
visual_delta += (len + editor->size.col - 1) / editor->size.col;
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
@@ -378,7 +377,7 @@ void ensure_scroll(Editor *editor) {
|
|||||||
free(line);
|
free(line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t len = grapheme_strlen(line);
|
uint32_t len = visual_width(line);
|
||||||
uint32_t visible_len = len - offset;
|
uint32_t visible_len = len - offset;
|
||||||
if (visible_len == 0)
|
if (visible_len == 0)
|
||||||
visible_len = 1;
|
visible_len = 1;
|
||||||
@@ -436,6 +435,75 @@ void apply_edit(std::vector<Span> &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) {
|
void render_editor(Editor *editor) {
|
||||||
Coord cursor = {UINT32_MAX, UINT32_MAX};
|
Coord cursor = {UINT32_MAX, UINT32_MAX};
|
||||||
uint32_t line_index = editor->scroll.row;
|
uint32_t line_index = editor->scroll.row;
|
||||||
|
|||||||
41
src/knot.cc
41
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) {
|
uint32_t byte_to_line(Knot *node, uint32_t offset, uint32_t *out_col) {
|
||||||
if (!node)
|
if (!node) {
|
||||||
|
if (out_col)
|
||||||
|
*out_col = 0;
|
||||||
return 0;
|
return 0;
|
||||||
if (offset >= node->char_count)
|
}
|
||||||
|
if (offset >= node->char_count) {
|
||||||
|
if (out_col)
|
||||||
|
*out_col = 0;
|
||||||
return node->line_count;
|
return node->line_count;
|
||||||
|
}
|
||||||
if (node->depth == 0) {
|
if (node->depth == 0) {
|
||||||
uint32_t lines_before = 0;
|
uint32_t line = 0;
|
||||||
uint32_t limit = (offset < node->char_count) ? offset : node->char_count;
|
uint32_t col = 0;
|
||||||
for (uint32_t i = 0; i < limit; i++)
|
for (uint32_t i = 0; i < offset; i++) {
|
||||||
if (node->data[i] == '\n')
|
if (node->data[i] == '\n') {
|
||||||
lines_before++;
|
line++;
|
||||||
return lines_before;
|
col = 0;
|
||||||
|
} else {
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (out_col)
|
||||||
|
*out_col = col;
|
||||||
|
return line;
|
||||||
}
|
}
|
||||||
uint32_t left_chars = node->left ? node->left->char_count : 0;
|
uint32_t left_chars = node->left ? node->left->char_count : 0;
|
||||||
if (offset < left_chars) {
|
if (offset < left_chars) {
|
||||||
return byte_to_line(node->left, offset);
|
return byte_to_line(node->left, offset, out_col);
|
||||||
} else {
|
} else {
|
||||||
uint32_t left_lines = node->left ? node->left->line_count : 0;
|
uint32_t sub_col = 0;
|
||||||
return left_lines + byte_to_line(node->right, offset - left_chars);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
src/main.cc
109
src/main.cc
@@ -1,10 +1,8 @@
|
|||||||
#include "../include/editor.h"
|
#include "../include/editor.h"
|
||||||
#include "../include/ts.h"
|
#include "../include/ts.h"
|
||||||
#include "../include/ui.h"
|
#include "../include/ui.h"
|
||||||
#include "../libs/tree-sitter/lib/include/tree_sitter/api.h"
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -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 == '>' || 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);
|
edit_insert(editor,
|
||||||
uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
||||||
editor->cursor.col;
|
editor->cursor.col,
|
||||||
lock_1.unlock();
|
&event.c, 1);
|
||||||
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);
|
|
||||||
}
|
|
||||||
cursor_right(editor, 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') {
|
if (event.key_type == KEY_CHAR && event.c == '\t') {
|
||||||
std::shared_lock lock_1(editor->knot_mtx);
|
edit_insert(editor,
|
||||||
uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
||||||
editor->cursor.col;
|
editor->cursor.col,
|
||||||
lock_1.unlock();
|
(char *)" ", 2);
|
||||||
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);
|
|
||||||
}
|
|
||||||
cursor_right(editor, 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')) {
|
if (event.key_type == KEY_CHAR && (event.c == '\n' || event.c == '\r')) {
|
||||||
std::shared_lock lock_1(editor->knot_mtx);
|
edit_insert(editor,
|
||||||
uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
||||||
editor->cursor.col;
|
editor->cursor.col,
|
||||||
lock_1.unlock();
|
(char *)"\n", 1);
|
||||||
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);
|
|
||||||
}
|
|
||||||
cursor_right(editor, 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) {
|
if (event.key_type == KEY_CHAR && event.c == 0x7F) {
|
||||||
std::shared_lock lock_1(editor->knot_mtx);
|
edit_erase(editor,
|
||||||
uint32_t pos = line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
line_to_byte(editor->root, editor->cursor.row, nullptr) +
|
||||||
editor->cursor.col;
|
editor->cursor.col,
|
||||||
TSPoint old_point = {editor->cursor.row, editor->cursor.col};
|
1);
|
||||||
cursor_left(editor, 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);
|
ensure_scroll(editor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ std::string get_exe_dir() {
|
|||||||
return path.substr(0, path.find_last_of('/'));
|
return path.substr(0, path.find_last_of('/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t grapheme_strlen(const char *s) {
|
uint32_t visual_width(const char *s) {
|
||||||
if (!s)
|
if (!s)
|
||||||
return 0;
|
return 0;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user