diff --git a/grammar/bash.scm b/grammar/bash.scm index 4554282..eac7aec 100644 --- a/grammar/bash.scm +++ b/grammar/bash.scm @@ -31,7 +31,7 @@ "&" ] @punctuation.delimiter -;; #ffffff #000000 0 0 0 1 +;; #ffffff #000000 0 1 0 1 [ ">" ">>" diff --git a/include/editor.h b/include/editor.h index 82bad04..c0d48ab 100644 --- a/include/editor.h +++ b/include/editor.h @@ -2,7 +2,7 @@ #define EDITOR_H #include "../libs/tree-sitter/lib/include/tree_sitter/api.h" -#include "./rope.h" +#include "./knot.h" #include "./ui.h" #include "./utils.h" #include @@ -82,26 +82,25 @@ struct SpanCursor { }; struct Editor { - const char *filename; // Filename of the editor - Knot *root; // A rope - std::shared_mutex knot_mtx; // A mutex - Coord cursor; // position of the cursor - uint32_t cursor_preffered; // preffered visual column - Coord selection; // position of the selection - bool selection_active; // true if there is a selection - Coord position; // Position of the editor - Coord size; // Size of the editor - Coord scroll; // Position of the scroll - TSTree *tree; // Tree-sitter tree - TSParser *parser; // Tree-sitter parser - TSQuery *query; // Tree-sitter query - const TSLanguage *language; // Tree-sitter language - Queue edit_queue; // Tree-sitter edit queue - std::vector query_map; // Tree-sitter query map - std::vector folded; // folded lines indexed by line number - Spans spans; // Highlighted spans - std::map folded_node; // maps content hash to fold state - // - built by tree-sitter helpers + const char *filename; + Knot *root; + std::shared_mutex knot_mtx; + Coord cursor; + uint32_t cursor_preffered; + Coord selection; + bool selection_active; + Coord position; + Coord size; + Coord scroll; + TSTree *tree; + TSParser *parser; + TSQuery *query; + const TSLanguage *language; + Queue edit_queue; + std::vector query_map; + std::vector folded; + Spans spans; + std::map folded_node; }; Editor *new_editor(const char *filename, Coord position, Coord size); diff --git a/include/rope.h b/include/knot.h similarity index 98% rename from include/rope.h rename to include/knot.h index 0116c75..f6a97ae 100644 --- a/include/rope.h +++ b/include/knot.h @@ -1,6 +1,7 @@ #ifndef ROPE_H #define ROPE_H +#include "./utils.h" #include #include @@ -10,9 +11,6 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define DEPTH(n) ((n) ? (n)->depth : 0) -#define PCRE2_CODE_UNIT_WIDTH 8 -#define PCRE_WORKSPACE_SIZE 512 - // Rope node definition typedef struct Knot { Knot *left; diff --git a/include/ts.h b/include/ts.h index 5c39ef0..3618d18 100644 --- a/include/ts.h +++ b/include/ts.h @@ -2,6 +2,7 @@ #define TS_H #include "./editor.h" +#include "./utils.h" #include #define HEX(s) (static_cast(std::stoul(s, nullptr, 16))) diff --git a/include/ts_def.h b/include/ts_def.h index e535a97..4a0d4a5 100644 --- a/include/ts_def.h +++ b/include/ts_def.h @@ -7,18 +7,18 @@ struct Language { }; extern "C" { -const TSLanguage *tree_sitter_bash(void); -const TSLanguage *tree_sitter_c(void); -const TSLanguage *tree_sitter_cpp(void); -const TSLanguage *tree_sitter_css(void); -const TSLanguage *tree_sitter_fish(void); -const TSLanguage *tree_sitter_go(void); -const TSLanguage *tree_sitter_haskell(void); -const TSLanguage *tree_sitter_html(void); -const TSLanguage *tree_sitter_javascript(void); -const TSLanguage *tree_sitter_json(void); -const TSLanguage *tree_sitter_lua(void); -const TSLanguage *tree_sitter_make(void); -const TSLanguage *tree_sitter_python(void); -const TSLanguage *tree_sitter_ruby(void); +const TSLanguage *tree_sitter_bash(); +const TSLanguage *tree_sitter_c(); +const TSLanguage *tree_sitter_cpp(); +const TSLanguage *tree_sitter_css(); +const TSLanguage *tree_sitter_fish(); +const TSLanguage *tree_sitter_go(); +const TSLanguage *tree_sitter_haskell(); +const TSLanguage *tree_sitter_html(); +const TSLanguage *tree_sitter_javascript(); +const TSLanguage *tree_sitter_json(); +const TSLanguage *tree_sitter_lua(); +const TSLanguage *tree_sitter_make(); +const TSLanguage *tree_sitter_python(); +const TSLanguage *tree_sitter_ruby(); } diff --git a/include/ui.h b/include/ui.h index 8a60a78..40bcd9b 100644 --- a/include/ui.h +++ b/include/ui.h @@ -1,6 +1,7 @@ #ifndef UI_H #define UI_H +#include "./utils.h" #include #include #include @@ -63,11 +64,6 @@ struct ScreenCell { uint8_t flags = CF_NONE; }; -struct Coord { - uint32_t row; - uint32_t col; -}; - struct KeyEvent { uint8_t key_type; @@ -91,7 +87,6 @@ extern std::mutex screen_mutex; extern std::atomic running; void get_terminal_size(); -void die(const char *s); void enable_raw_mode(); void disable_raw_mode(); Coord start_screen(); diff --git a/include/utils.h b/include/utils.h index 627387f..0e3fcca 100644 --- a/include/utils.h +++ b/include/utils.h @@ -6,6 +6,9 @@ #include #include +#define PCRE2_CODE_UNIT_WIDTH 8 +#define PCRE_WORKSPACE_SIZE 512 + template struct Queue { std::queue q; std::mutex m; @@ -28,6 +31,11 @@ template struct Queue { } }; +struct Coord { + uint32_t row; + uint32_t col; +}; + uint32_t grapheme_strlen(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, diff --git a/src/editor.cc b/src/editor.cc index b5b7d2e..10b790c 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -14,7 +14,6 @@ Editor *new_editor(const char *filename, Coord position, Coord size) { char *str = load_file(filename, &len); if (!str) { free_editor(editor); - log("me?"); return nullptr; } editor->filename = filename; @@ -438,8 +437,7 @@ void apply_edit(std::vector &spans, uint32_t x, int64_t y) { } void render_editor(Editor *editor) { - uint32_t screen_rows = editor->size.row; - uint32_t screen_cols = editor->size.col; + Coord cursor = {UINT32_MAX, UINT32_MAX}; uint32_t line_index = editor->scroll.row; SpanCursor span_cursor(editor->spans); std::shared_lock knot_lock(editor->knot_mtx); @@ -449,10 +447,10 @@ void render_editor(Editor *editor) { uint32_t rendered_rows = 0; uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr); span_cursor.sync(global_byte_offset); - while (rendered_rows < screen_rows) { + while (rendered_rows < editor->size.row) { if (editor->folded[line_index]) { if (editor->folded[line_index] == 2) { - update_render_fold_marker(rendered_rows, screen_cols); + update_render_fold_marker(rendered_rows, editor->size.col); rendered_rows++; } do { @@ -486,11 +484,11 @@ void render_editor(Editor *editor) { skipped_cols++; } } - while (current_byte_offset < line_len && rendered_rows < screen_rows) { + while (current_byte_offset < line_len && rendered_rows < editor->size.row) { uint32_t slice_byte_len = 0; uint32_t slice_visual_cols = 0; uint32_t probe_offset = current_byte_offset; - while (slice_visual_cols < screen_cols && probe_offset < line_len) { + while (slice_visual_cols < editor->size.col && probe_offset < line_len) { uint32_t len = grapheme_next_character_break_utf8( line + probe_offset, line_len - probe_offset); slice_byte_len += len; @@ -501,9 +499,10 @@ void render_editor(Editor *editor) { uint32_t local_render_offset = 0; while (local_render_offset < slice_byte_len) { if (line_index == editor->cursor.row && - editor->cursor.col == (current_byte_offset + local_render_offset)) - set_cursor(editor->position.row + rendered_rows, - editor->position.col + col, 1); + editor->cursor.col == (current_byte_offset + local_render_offset)) { + cursor.row = editor->position.row + rendered_rows; + cursor.col = editor->position.col + col; + } uint32_t absolute_byte_pos = global_byte_offset + current_byte_offset + local_render_offset; Highlight *hl = span_cursor.get_highlight(absolute_byte_pos); @@ -523,10 +522,11 @@ void render_editor(Editor *editor) { col++; } if (line_index == editor->cursor.row && - editor->cursor.col == (current_byte_offset + slice_byte_len)) - set_cursor(editor->position.row + rendered_rows, - editor->position.col + col, 1); - while (col < screen_cols) { + editor->cursor.col == (current_byte_offset + slice_byte_len)) { + cursor.row = editor->position.row + rendered_rows; + cursor.col = editor->position.col + col; + } + while (col < editor->size.col) { update(editor->position.row + rendered_rows, editor->position.col + col, " ", 0xFFFFFF, 0, 0); col++; @@ -536,11 +536,12 @@ void render_editor(Editor *editor) { } if (line_len == 0 || (current_byte_offset >= line_len && rendered_rows == 0)) { - if (editor->cursor.row == line_index) - set_cursor(editor->position.row + rendered_rows, editor->position.col, - 1); + if (editor->cursor.row == line_index) { + cursor.row = editor->position.row + rendered_rows; + cursor.col = editor->position.col; + } uint32_t col = 0; - while (col < screen_cols) { + while (col < editor->size.col) { update(editor->position.row + rendered_rows, editor->position.col + col, " ", 0xFFFFFF, 0, 0); col++; @@ -551,8 +552,10 @@ void render_editor(Editor *editor) { line_index++; free(line); } - while (rendered_rows < screen_rows) { - for (uint32_t col = 0; col < screen_cols; col++) + if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) + set_cursor(cursor.row, cursor.col, 1); + while (rendered_rows < editor->size.row) { + for (uint32_t col = 0; col < editor->size.col; col++) update(editor->position.row + rendered_rows, editor->position.col + col, " ", 0xFFFFFF, 0, 0); rendered_rows++; diff --git a/src/input.cc b/src/input.cc index 483a395..962b24f 100644 --- a/src/input.cc +++ b/src/input.cc @@ -46,7 +46,7 @@ void capture_mouse(char *buf, KeyEvent *ret) { } } -KeyEvent read_key_nonblock() { +KeyEvent read_key() { KeyEvent ret; char buf[7]; int n = read_input(buf, sizeof(buf)); @@ -113,11 +113,3 @@ KeyEvent read_key_nonblock() { } return ret; } - -KeyEvent read_key() { - while (true) { - KeyEvent ret = read_key_nonblock(); - if (ret.key_type != KEY_NONE) - return ret; - } -} diff --git a/src/rope.cc b/src/knot.cc similarity index 99% rename from src/rope.cc rename to src/knot.cc index ee8230f..49de155 100644 --- a/src/rope.cc +++ b/src/knot.cc @@ -1,4 +1,4 @@ -#include "../include/rope.h" +#include "../include/knot.h" #include #include #include diff --git a/src/main.cc b/src/main.cc index dce00ef..2cb9a9b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -22,6 +22,8 @@ void background_worker(Editor *editor) { void input_listener() { while (running) { KeyEvent event = read_key(); + if (event.key_type == KEY_NONE) + continue; if (event.key_type == KEY_CHAR && event.c == CTRL('q')) running = false; event_queue.push(event); diff --git a/src/renderer.cc b/src/renderer.cc index 9d36fa1..aaa8563 100644 --- a/src/renderer.cc +++ b/src/renderer.cc @@ -5,18 +5,15 @@ extern "C" { } #include "../include/ui.h" -termios orig_termios; - uint32_t rows, cols; int show_cursor = 0; std::vector screen; std::vector old_screen; std::mutex screen_mutex; +termios orig_termios; int display_width(const char *str) { - if (!str) - return 0; - if (!strlen(str)) + if (!str || !*str) return 0; if (str[0] == '\t') return 4; @@ -40,7 +37,6 @@ int display_width(const char *str) { } } } - return width; } @@ -51,15 +47,9 @@ void get_terminal_size() { cols = w.ws_col; } -void die(const char *s) { - perror(s); - disable_raw_mode(); - exit(EXIT_FAILURE); -} - void enable_raw_mode() { if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) - die("tcgetattr"); + exit(EXIT_FAILURE); atexit(disable_raw_mode); struct termios raw = orig_termios; @@ -72,7 +62,7 @@ void enable_raw_mode() { raw.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) - die("tcsetattr"); + exit(EXIT_FAILURE); std::string os = "\x1b[?1049h\x1b[2 q\x1b[?1002h\x1b[?25l"; write(STDOUT_FILENO, os.c_str(), os.size()); @@ -246,7 +236,7 @@ void render() { } else if (written == -1) { if (errno == EINTR) continue; - die("write failed"); + exit(EXIT_FAILURE); break; } else { ptr += written; diff --git a/src/ts.cc b/src/ts.cc index c5f23b2..1977f89 100644 --- a/src/ts.cc +++ b/src/ts.cc @@ -1,10 +1,9 @@ #include "../include/ts.h" #include "../include/editor.h" -#include "../include/rope.h" +#include "../include/knot.h" #include #include #include -#include #include #include @@ -30,9 +29,6 @@ pcre2_code *get_re(const std::string &pattern) { return re; } -static const std::regex scm_regex( - R"((@[A-Za-z0-9_.]+)|(;; \#[0-9a-fA-F]{6} \#[0-9a-fA-F]{6} [01] [01] [01] \d+))"); - TSQuery *load_query(const char *query_path, Editor *editor) { const TSLanguage *lang = editor->language; std::ifstream file(query_path, std::ios::in | std::ios::binary); @@ -40,17 +36,31 @@ TSQuery *load_query(const char *query_path, Editor *editor) { return nullptr; std::string highlight_query((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - std::smatch match; + int errornumber = 0; + PCRE2_SIZE erroroffset = 0; + pcre2_code *re = pcre2_compile( + (PCRE2_SPTR) R"((@[A-Za-z0-9_.]+)|(;; \#[0-9a-fA-F]{6} \#[0-9a-fA-F]{6} [01] [01] [01] \d+))", + PCRE2_ZERO_TERMINATED, 0, &errornumber, &erroroffset, nullptr); + if (!re) + return nullptr; + pcre2_match_data *match_data = + pcre2_match_data_create_from_pattern(re, nullptr); std::map capture_name_cache; Highlight *c_hl = nullptr; int i = 0; int limit = 20; editor->query_map.resize(limit); - std::string::const_iterator searchStart(highlight_query.cbegin()); - while (std::regex_search(searchStart, highlight_query.cend(), match, - scm_regex)) { - std::string mct = match.str(); - if (mct.substr(0, 1) == "@") { + PCRE2_SIZE offset = 0; + PCRE2_SIZE subject_length = highlight_query.size(); + while (offset < subject_length) { + int rc = pcre2_match(re, (PCRE2_SPTR)highlight_query.c_str(), + subject_length, offset, 0, match_data, nullptr); + if (rc <= 0) + break; + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); + std::string mct = + highlight_query.substr(ovector[0], ovector[1] - ovector[0]); + if (!mct.empty() && mct[0] == '@') { std::string capture_name = mct; if (!capture_name_cache.count(capture_name)) { if (c_hl) { @@ -65,7 +75,7 @@ TSQuery *load_query(const char *query_path, Editor *editor) { capture_name_cache[capture_name] = i; i++; } - } else if (mct.substr(0, 2) == ";;") { + } else if (mct.size() >= 2 && mct[0] == ';' && mct[1] == ';') { if (c_hl) delete c_hl; c_hl = new Highlight(); @@ -78,10 +88,12 @@ TSQuery *load_query(const char *query_path, Editor *editor) { c_hl->flags = (bold ? CF_BOLD : 0) | (italic ? CF_ITALIC : 0) | (underline ? CF_UNDERLINE : 0); } - searchStart = match.suffix().first; + offset = ovector[1]; } if (c_hl) delete c_hl; + pcre2_match_data_free(match_data); + pcre2_code_free(re); uint32_t error_offset = 0; TSQueryError error_type = (TSQueryError)0; TSQuery *q = ts_query_new(lang, highlight_query.c_str(), @@ -202,10 +214,7 @@ void ts_collect_spans(Editor *editor) { } parse_counter = 0; editor->spans.mid_parse = true; - // TODO: Remove this lock and replace with an index - // modifier based on edits made in the `read_ts` function. std::shared_lock lock(editor->knot_mtx); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tree = ts_parser_parse(editor->parser, copy, tsinput); lock.unlock(); if (copy)