diff --git a/Makefile b/Makefile index 8f37eb2..17e0836 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ CFLAGS_DEBUG :=\ -g -fsanitize=address -fno-omit-frame-pointer\ -Wno-unused-command-line-argument \ -I./include -I./libs -CFLAGS_RELEASE := \ +CFLAGS_RELEASE :=\ -std=c++20 -O3 -march=native \ -fno-exceptions -fno-rtti -fstrict-aliasing \ -ffast-math -flto=thin \ diff --git a/README.md b/README.md index b994f21..232bc88 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,15 @@ A TUI IDE. # TODO +- [ ] Finish autocomplete box. - [ ] Add status bar & RUNNER mode +- [ ] Get code context from tree-sitter - [ ] Maybe hide boxes in !`normal` mode +- [ ] expand color regex to match css colors if in css file - [ ] Fix indentation logic + - Make it work by one getting the identation used in a file by first checking if it has any line with 2 or more spaces then the least one is set to be the indent or if it is tabs then tabs but if there are none then use a table of file type to its indentation or use 2 spaces as default. store this info as `1 = tab` and `2 or more = those many spaces`. + - Use this when indenting and unindenting. And also when getting the identation of a line. + - Also indent when going immediately to newline should follow indent of previous line regardless of file default. - [ ] Fix bug where closing immediately while lsp is loading hangs and then segfaults. - [ ] For `"insertTextFormat": 2` in `clangd` and similar use only the last word in the signature when replacing - [ ] Keep a list of words in the current buffer. (for auto completion) (maybe?) diff --git a/grammar/markdown_inline.scm b/grammar/markdown_inline.scm index 906ee2d..ea0b6f3 100644 --- a/grammar/markdown_inline.scm +++ b/grammar/markdown_inline.scm @@ -1,4 +1,4 @@ -;; #AAD94C #000000 0 0 0 0 0 +;; #AAD94C #000000 0 0 0 0 1 (code_span) @markup.raw ;; #FF8F40 #000000 0 1 0 0 1 @@ -75,6 +75,9 @@ "]" ] @markup.link) +;; #FF8F40 #000000 0 0 0 0 0 +">" @markup.quote + ;; #7dcfff #000000 0 0 1 0 1 [ (link_destination) @@ -85,6 +88,5 @@ ;; #7dcfff #000000 0 0 1 0 1 (uri_autolink) @_url -;; #FF8F40 #000000 0 0 0 0 0 ;; !html (html_tag) @injection.html diff --git a/include/config.h b/include/config.h index 2794370..2a91b11 100644 --- a/include/config.h +++ b/include/config.h @@ -167,42 +167,44 @@ static const std::unordered_map kLsps = { }; static const std::unordered_map kLanguages = { - {"bash", {"bash", LANG(bash), 4}}, - {"c", {"c", LANG(cpp), 1}}, - {"cpp", {"cpp", LANG(cpp), 1}}, - {"h", {"h", LANG(cpp), 1}}, - {"css", {"css", LANG(css), 5}}, - {"fish", {"fish", LANG(fish), 7}}, - {"go", {"go", LANG(go), 8}}, - {"gomod", {"gomod", LANG(gomod), 8}}, - {"haskell", {"haskell", LANG(haskell), 9}}, - {"html", {"html", LANG(html), 10}}, - {"javascript", {"javascript", LANG(javascript), 11}}, - {"typescript", {"typescript", LANG(tsx), 11}}, - {"json", {"json", LANG(json), 6}}, - {"jsonc", {"jsonc", LANG(json), 6}}, - {"erb", {"erb", LANG(embedded_template), 10}}, - {"ruby", {"ruby", LANG(ruby), 3}}, - {"lua", {"lua", LANG(lua), 12}}, - {"python", {"python", LANG(python), 13}}, - {"rust", {"rust", LANG(rust), 14}}, - {"php", {"php", LANG(php), 15}}, - {"markdown", {"markdown", LANG(markdown), 16}}, - {"markdown_inline", {"markdown_inline", LANG(markdown_inline), 16}}, - {"nginx", {"nginx", LANG(nginx), 17}}, - {"toml", {"toml", LANG(toml), 18}}, - {"yaml", {"yaml", LANG(yaml), 19}}, - {"sql", {"sql", LANG(sql), 20}}, // Can use `22` for more accuracy but need - // config to connect to database - {"make", {"make", LANG(make), 21}}, - {"gdscript", {"gdscript", LANG(gdscript)}}, // TODO: connect to godot - {"man", {"man", LANG(man)}}, - {"diff", {"diff", LANG(diff)}}, - {"gitattributes", {"gitattributes", LANG(gitattributes)}}, - {"gitignore", {"gitignore", LANG(gitignore)}}, - {"query", {"query", LANG(query)}}, - {"regex", {"regex", LANG(regex)}}, - {"ini", {"ini", LANG(ini)}}, + {"bash", {"bash", LANG(bash), 4, 0x4d5a5e, " "}}, + {"c", {"c", LANG(cpp), 1, 0x555555, " "}}, + {"cpp", {"cpp", LANG(cpp), 1, 0x00599C, " "}}, + {"h", {"h", LANG(cpp), 1, 0xA8B9CC, " "}}, + {"css", {"css", LANG(css), 5, 0x36a3d9, " "}}, + {"fish", {"fish", LANG(fish), 7, 0x4d5a5e, " "}}, + {"go", {"go", LANG(go), 8, 0x00add8, " "}}, + {"gomod", {"gomod", LANG(gomod), 8, 0x00add8, " "}}, + {"haskell", {"haskell", LANG(haskell), 9, 0xa074c4, " "}}, + {"html", {"html", LANG(html), 10, 0xef8a91, " "}}, + {"javascript", {"javascript", LANG(javascript), 11, 0xf0df8a, " "}}, + {"typescript", {"typescript", LANG(tsx), 11, 0x36a3d9, " "}}, + {"json", {"json", LANG(json), 6, 0xcbcb41, "{}"}}, + {"jsonc", {"jsonc", LANG(json), 6, 0xcbcb41, "{}"}}, + {"erb", {"erb", LANG(embedded_template), 10, 0x6e1516, " "}}, + {"ruby", {"ruby", LANG(ruby), 3, 0xff8087, "󰴭 "}}, + {"lua", {"lua", LANG(lua), 12, 0x36a3d9, "󰢱 "}}, + {"python", {"python", LANG(python), 13, 0x95e6cb, "󰌠 "}}, + {"rust", {"rust", LANG(rust), 14, 0xdea584, "󱘗 "}}, + {"php", {"php", LANG(php), 15, 0xa074c4, "󰌟 "}}, + {"markdown", {"markdown", LANG(markdown), 16, 0x36a3d9, " "}}, + {"markdown_inline", + {"markdown_inline", LANG(markdown_inline), 16, 0x36a3d9, " "}}, + {"nginx", {"nginx", LANG(nginx), 17, 0x6d8086, " "}}, + {"toml", {"toml", LANG(toml), 18, 0x36a3d9, " "}}, + {"yaml", {"yaml", LANG(yaml), 19, 0x6d8086, " "}}, + {"sql", {"sql", LANG(sql), 20, 0xdad8d8, " "}}, + {"make", {"make", LANG(make), 21, 0x4e5c61, " "}}, + {"gdscript", {"gdscript", LANG(gdscript), 0, 0x6d8086, " "}}, + {"man", {"man", LANG(man), 0, 0xdad8d8, " "}}, + {"diff", {"diff", LANG(diff), 0, 0xDD4C35, " "}}, + {"gitattributes", + {"gitattributes", LANG(gitattributes), 0, 0xF05032, " "}}, + {"gitignore", {"gitignore", LANG(gitignore), 0, 0xF05032, " "}}, + {"query", {"query", LANG(query), 0, 0x7E57C2, " "}}, + {"regex", {"regex", LANG(regex), 0, 0x9E9E9E, ".*"}}, + {"ini", {"ini", LANG(ini), 0, 0x6d8086, " "}}, + }; static const std::unordered_map kExtToLang = { diff --git a/include/editor/editor.h b/include/editor/editor.h index 239721e..34b1084 100644 --- a/include/editor/editor.h +++ b/include/editor/editor.h @@ -1,12 +1,12 @@ #ifndef EDITOR_H #define EDITOR_H -#include "ui/diagnostics.h" -#include "ui/hover.h" #include "editor/spans.h" #include "io/knot.h" #include "io/sysio.h" #include "ts/decl.h" +#include "ui/diagnostics.h" +#include "ui/hover.h" #include "utils/utils.h" #define CHAR 0 @@ -29,6 +29,7 @@ struct Editor { Coord position; Coord size; Coord scroll; + Language lang; TSSetMain ts; Queue edit_queue; std::vector folds; diff --git a/include/main.h b/include/main.h index fc68180..589576f 100644 --- a/include/main.h +++ b/include/main.h @@ -10,6 +10,8 @@ #define JUMPER 4 extern std::atomic running; -extern uint8_t mode; +extern std::atomic mode; +extern std::vector editors; +extern uint8_t current_editor; #endif diff --git a/include/ts/decl.h b/include/ts/decl.h index c953220..1374afa 100644 --- a/include/ts/decl.h +++ b/include/ts/decl.h @@ -9,7 +9,9 @@ struct Language { std::string name; const TSLanguage *(*fn)(); - uint8_t lsp_id = 0; + uint8_t lsp_id; + uint32_t color = 0xFFFFFF; + const char *symbol = " "; }; struct Highlight { diff --git a/include/ui/bar.h b/include/ui/bar.h index b054ed0..df8f3a2 100644 --- a/include/ui/bar.h +++ b/include/ui/bar.h @@ -1,6 +1,19 @@ #ifndef UI_BAR_H #define UI_BAR_H +#include "editor/editor.h" +#include "io/sysio.h" #include "utils/utils.h" +struct Bar { + Coord screen; + std::string command = ""; + int cursor = 0; + + Bar(Coord screen) : screen(screen) {} + void render(); + void handle(KeyEvent event); + void log(std::string message); +}; + #endif diff --git a/include/utils/utils.h b/include/utils/utils.h index 83164a2..16744da 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -79,6 +79,7 @@ void log(const char *fmt, ...); std::string path_abs(const std::string &path_str); std::string path_to_file_uri(const std::string &path_str); +std::string filename_from_path(const std::string &path); std::string get_exe_dir(); char *load_file(const char *path, uint32_t *out_len); char *detect_file_type(const char *filename); diff --git a/scripts/exit.sh b/scripts/exit.sh new file mode 100644 index 0000000..dab984b --- /dev/null +++ b/scripts/exit.sh @@ -0,0 +1 @@ +kitty @ --to="$KITTY_LISTEN_ON" set-spacing padding=8 margin=0 diff --git a/scripts/init.sh b/scripts/init.sh new file mode 100644 index 0000000..a41e5a3 --- /dev/null +++ b/scripts/init.sh @@ -0,0 +1 @@ +kitty @ --to="$KITTY_LISTEN_ON" set-spacing padding=0 margin=0 diff --git a/src/editor/editor.cc b/src/editor/editor.cc index 1483f18..27eac56 100644 --- a/src/editor/editor.cc +++ b/src/editor/editor.cc @@ -26,16 +26,16 @@ Editor *new_editor(const char *filename_arg, Coord position, Coord size) { } editor->root = load(str, len, optimal_chunk_size(len)); free(str); - Language language = language_for_file(filename.c_str()); - if (language.name != "unknown" && len <= (1024 * 128)) { + editor->lang = language_for_file(filename.c_str()); + if (editor->lang.name != "unknown" && len <= (1024 * 128)) { editor->ts.parser = ts_parser_new(); - editor->ts.language = language.fn(); + editor->ts.language = editor->lang.fn(); ts_parser_set_language(editor->ts.parser, editor->ts.language); editor->ts.query_file = - get_exe_dir() + "/../grammar/" + language.name + ".scm"; + get_exe_dir() + "/../grammar/" + editor->lang.name + ".scm"; } if (len <= (1024 * 28)) - request_add_to_lsp(language, editor); + request_add_to_lsp(editor->lang, editor); return editor; } diff --git a/src/editor/events.cc b/src/editor/events.cc index 69ef048..fd248bc 100644 --- a/src/editor/events.cc +++ b/src/editor/events.cc @@ -649,15 +649,6 @@ void handle_editor_event(Editor *editor, KeyEvent event) { } mode = NORMAL; break; - case RUNNER: - if (event.key_type == KEY_CHAR && event.len == 1) { - switch (event.c[0]) { - case 0x1B: - mode = NORMAL; - break; - } - } - break; } ensure_scroll(editor); if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c) diff --git a/src/editor/lsp.cc b/src/editor/lsp.cc index 48645a0..e44771f 100644 --- a/src/editor/lsp.cc +++ b/src/editor/lsp.cc @@ -42,11 +42,8 @@ void editor_lsp_handle(Editor *editor, json msg) { auto pos = message.find('\n'); message = (pos == std::string::npos) ? message : message.substr(0, pos); - std::string uri = - percent_decode(rel["location"]["uri"].get()); - auto pos2 = uri.find_last_of('/'); - if (pos2 != std::string::npos) - uri = uri.substr(pos2 + 1); + std::string uri = filename_from_path( + percent_decode(rel["location"]["uri"].get())); std::string row = std::to_string( rel["location"]["range"]["start"]["line"].get()); w.see_also.push_back(uri + ":" + row + ": " + message); diff --git a/src/editor/worker.cc b/src/editor/worker.cc index 8cc43ab..41da33b 100644 --- a/src/editor/worker.cc +++ b/src/editor/worker.cc @@ -74,7 +74,7 @@ void editor_worker(Editor *editor) { exit(ENOMEM); std::shared_lock lockk(editor->knot_mtx); std::vector results = - search_rope(editor->root, "(?:0x|#)[0-9a-fA-F]{6}"); + search_rope(editor->root, "(?:0x|#)[0-9a-fA-F]{6,8}\\b"); if (results.size() > limit) { limit = results.size() + 50; free(hl_s); @@ -91,7 +91,7 @@ void editor_worker(Editor *editor) { s.start = results[i].start; s.end = results[i].end; int x = results[i].text[0] == '#' ? 1 : 2; - uint32_t bg = HEX(results[i].text.substr(x)); + uint32_t bg = HEX(results[i].text.substr(x, 6)); uint8_t r = bg >> 16; uint8_t g = (bg >> 8) & 0xFF; uint8_t b = bg & 0xFF; diff --git a/src/main.cc b/src/main.cc index 08d22d8..ddef93c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -3,13 +3,15 @@ #include "io/sysio.h" #include "lsp/lsp.h" #include "ts/ts.h" +#include "ui/bar.h" +#include "utils/utils.h" std::atomic running{true}; Queue event_queue; std::vector editors; uint8_t current_editor = 0; -uint8_t mode = NORMAL; +std::atomic mode = NORMAL; void background_worker() { while (running) @@ -58,7 +60,10 @@ int main(int argc, char *argv[]) { Coord screen = start_screen(); const char *filename = (argc > 1) ? argv[1] : ""; - Editor *editor = new_editor(filename, {0, 0}, screen); + system(("bash " + get_exe_dir() + "/../scripts/init.sh").c_str()); + + Editor *editor = new_editor(filename, {0, 0}, {screen.row - 2, screen.col}); + Bar bar(screen); if (!editor) { end_screen(); @@ -76,20 +81,25 @@ int main(int argc, char *argv[]) { while (running) { KeyEvent event; while (event_queue.pop(event)) { - if (event.key_type == KEY_MOUSE) { - Editor *target = editor_at(event.mouse_x, event.mouse_y); - if (!target) - continue; - if (event.mouse_state == PRESS) - current_editor = index_of(target); - event.mouse_x -= target->position.col; - event.mouse_y -= target->position.row; - throttle(4ms, handle_editor_event, target, event); + if (mode != RUNNER) { + if (event.key_type == KEY_MOUSE) { + Editor *target = editor_at(event.mouse_x, event.mouse_y); + if (!target) + continue; + if (event.mouse_state == PRESS) + current_editor = index_of(target); + event.mouse_x -= target->position.col; + event.mouse_y -= target->position.row; + handle_editor_event(target, event); + } else { + handle_editor_event(editors[current_editor], event); + } } else { - throttle(4ms, handle_editor_event, editors[current_editor], event); + bar.handle(event); } } - throttle(4ms, render_editor, editors[current_editor]); + render_editor(editors[current_editor]); + bar.render(); throttle(4ms, render); } @@ -102,6 +112,8 @@ int main(int argc, char *argv[]) { if (lsp_thread.joinable()) lsp_thread.join(); + system(("bash " + get_exe_dir() + "/../scripts/exit.sh").c_str()); + end_screen(); for (auto editor : editors) diff --git a/src/ui/bar.cc b/src/ui/bar.cc index e08231f..2204adb 100644 --- a/src/ui/bar.cc +++ b/src/ui/bar.cc @@ -1 +1,96 @@ #include "ui/bar.h" +#include "io/sysio.h" +#include "main.h" + +void Bar::render() { + Editor *editor = editors[current_editor]; + uint32_t row = screen.row - 2; + uint32_t col = 0; + uint32_t width = screen.col; + uint32_t color = 0; + uint32_t black = 0x0b0e14; + uint32_t grey = 0x33363c; + uint32_t dark_grey = 0x24272d; + uint32_t name_color = 0xced4df; + uint32_t lang_color = editor->lang.color; + const char *symbol = "󱓧 "; + const char *name = "EDITOR"; + switch (mode) { + case NORMAL: + color = 0x82AAFF; + symbol = " "; + name = "NORMAL"; + break; + case INSERT: + color = 0xFF8F40; + symbol = "󱓧 "; + name = "INSERT"; + break; + case SELECT: + color = 0x9ADE7A; + symbol = "󱩧 "; + name = "SELECT"; + break; + case RUNNER: + color = 0xFFD700; + symbol = " "; + name = "RUNNER"; + break; + case JUMPER: + color = 0xF29CC3; + symbol = " "; + name = "JUMPER"; + break; + } + update(row, col, " ", black, color, CF_BOLD); + update(row, ++col, symbol, black, color, CF_BOLD); + update(row, ++col, "\x1b", black, color, CF_BOLD); + update(row, ++col, " ", black, color, CF_BOLD); + for (uint32_t i = 0; i < 6; i++) + update(row, ++col, {name[i], 0}, black, color, CF_BOLD); + update(row, ++col, " ", black, color, CF_BOLD); + update(row, ++col, "◗", color, grey, CF_BOLD); + update(row, ++col, "◗", grey, dark_grey, CF_BOLD); + update(row, ++col, " ", name_color, dark_grey, CF_BOLD); + update(row, ++col, editor->lang.symbol, lang_color, dark_grey, 0); + update(row, ++col, "\x1b", lang_color, dark_grey, 0); + update(row, ++col, " ", name_color, dark_grey, CF_BOLD); + std::string filename = filename_from_path(editor->filename); + for (uint32_t i = 0; i < filename.length(); i++) + update(row, ++col, {filename[i], 0}, name_color, dark_grey, CF_BOLD); + update(row, ++col, " ", name_color, dark_grey, CF_BOLD); + update(row, ++col, "◗", dark_grey, 1, CF_BOLD); +} + +void Bar::handle(KeyEvent event) { + if (event.key_type == KEY_CHAR && event.len == 1) { + if (event.c[0] == 0x1B) { + mode = NORMAL; + } else if (event.c[0] == '\n' || event.c[0] == '\r') { + // execute command while stripping starting `[:;]` + mode = NORMAL; + command = ""; + } else if (isprint((unsigned char)(event.c[0]))) { + } else if (event.c[0] == 0x7F || event.c[0] == 0x08) { // backspace + } + } else if (event.key_type == KEY_SPECIAL) { + switch (event.special_key) { + case KEY_LEFT: + if (command.length() > 0) + cursor--; + break; + case KEY_RIGHT: + if (cursor < command.length()) + cursor++; + break; + case KEY_UP: + // TODO: history + break; + case KEY_DOWN: + // TODO: history + break; + } + } else if (event.key_type == KEY_PASTE) { + } else if (event.key_type == KEY_MOUSE) { + } +} diff --git a/src/utils/system.cc b/src/utils/system.cc index dd39167..a87c15e 100644 --- a/src/utils/system.cc +++ b/src/utils/system.cc @@ -23,6 +23,13 @@ std::string path_to_file_uri(const std::string &path_str) { return "file://" + percent_encode(path_abs(path_str)); } +std::string filename_from_path(const std::string &path) { + auto pos = path.find_last_of('/'); + if (pos == std::string::npos) + return path; + return path.substr(pos + 1); +} + std::string get_exe_dir() { char exe_path[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", exe_path, PATH_MAX);