diff --git a/Makefile b/Makefile index 54c648c..0b2aba0 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,11 @@ CFLAGS_DEBUG :=\ -std=c++20 -Wall -Wextra \ -O0 -fno-inline -gsplit-dwarf \ -g -fno-omit-frame-pointer \ - -fsanitize=address \ -Wno-unused-command-line-argument \ -I./include -I./libs -I/home/syed/main/crib/libs/mruby/include +# C_SANITIZER := -fsanitize=address + CFLAGS_RELEASE :=\ -static --target=x86_64-linux-musl \ -std=c++20 -O3 -march=x86-64 -mtune=generic \ @@ -78,7 +79,7 @@ $(PCH_RELEASE): $(INCLUDE_DIR)/pch.h $(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) mkdir -p $(BIN_DIR) - $(CXX) $(CFLAGS_DEBUG) -o $@ $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(LIBS_DEBUG) + $(CXX) $(CFLAGS_DEBUG) $(C_SANITIZER) -o $@ $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(LIBS_DEBUG) $(TARGET_RELEASE): $(PCH_RELEASE) $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE) mkdir -p $(BIN_DIR) diff --git a/TODO.md b/TODO.md index 60982df..7501cf9 100644 --- a/TODO.md +++ b/TODO.md @@ -10,19 +10,19 @@ Copyright 2025 Syed Daanish ``` * Next few super long boring things to do -* redo lsp threads such that no mutex needed for any rope stuff +* redo lsp threads such that no mutex needed for any rope stuff (necessary rn) - Also make the classes own the methods in lsp - This will mean that parsers/renderers and keystrokes will not need to be individually locked - And so it will be much faster - At which point the main thread can also be blocked on user input or lsp responses and still be fast -* Add a superclass for editor called Window (which can be popup or tiled) -* Add a recursive tiling class for windows -* Handled by a single renderer that calls and renders each window - - And a bg if no window open -* Make editor's functions into its own methods (classify it) +* Add a superclass for editor called Window (which can be popup or tiled) (done) +* Add a recursive tiling class for windows (done) +* Handled by a single renderer that calls and renders each window (done) +* Make editor's functions into its own methods (classify it) (done) - While at it - Seperate system functions into a class that branches to support local / ssh / server modes. - Even lsp shouldnt be directly controlled because it can branch on local and server modes + - check wolfSSH stuff for remote editing - Redo hooks as a engine of its own. - And factorize renderer into its own class (and make it just return an array of the render without knowing teh x,y) - which is just managed by the renderer diff --git a/include/editor/completions.h b/include/editor/completions.h index 9cdc3f4..c2500cb 100644 --- a/include/editor/completions.h +++ b/include/editor/completions.h @@ -23,6 +23,7 @@ struct CompletionItem { }; struct CompletionSession { + struct Editor *editor; std::shared_mutex mtx; bool active = false; Coord hook; @@ -40,7 +41,14 @@ struct CompletionSession { std::atomic hover_dirty = false; int version; - CompletionSession() : box(this) {} + CompletionSession(Editor *editor) : editor(editor), box(this) {} + + void resolve_doc(); + void accept(); + void next(); + void prev(); + void choose(uint8_t index); + void handle(KeyEvent event); }; #endif diff --git a/include/editor/editor.h b/include/editor/editor.h index 4857231..7a19e36 100644 --- a/include/editor/editor.h +++ b/include/editor/editor.h @@ -1,16 +1,14 @@ #ifndef EDITOR_H #define EDITOR_H -#include "editor/completions.h" #include "editor/indents.h" #include "io/knot.h" #include "io/sysio.h" #include "syntax/extras.h" #include "syntax/parser.h" -#include "ui/completionbox.h" #include "ui/diagnostics.h" -#include "ui/hover.h" #include "utils/utils.h" +#include "windows/decl.h" #define CHAR 0 #define WORD 1 @@ -19,144 +17,159 @@ #define EXTRA_META 2 #define INDENT_WIDTH 2 -struct Editor { - std::string filename; - std::string uri; - Knot *root; - std::shared_mutex knot_mtx; - Coord cursor; - uint32_t cursor_preffered; - Coord selection; - bool selection_active; - bool unix_eol; - int selection_type; - Coord position; - Coord size; - Coord scroll; - Language lang; - uint32_t hooks[94]; - bool jumper_set; - std::shared_mutex v_mtx; - std::vector warnings; - bool warnings_dirty; - VAI ai; +struct Editor : Window { + std::string filename = ""; + std::string uri = ""; + Knot *root = nullptr; + Coord cursor = {0, 0}; + uint32_t cursor_preffered = 0; + Coord selection = {0, 0}; + bool selection_active = false; + bool unix_eol = true; + int selection_type = 0; + Coord size = {0, 0}; + Coord scroll = {0, 0}; + Language lang = {}; + uint32_t hooks[94] = {0}; + bool jumper_set = false; + std::vector warnings = {}; + bool warnings_dirty = false; + VAI ai = {}; std::shared_mutex lsp_mtx; - std::shared_ptr lsp; - bool hover_active; - HoverBox hover; - bool diagnostics_active; - DiagnosticBox diagnostics; + std::atomic lsp = nullptr; + bool hover_active = false; + bool diagnostics_active = false; std::atomic lsp_version = 1; - CompletionSession completion; - IndentationEngine indents; - Parser *parser; - ExtraHighlighter extra_hl; - bool is_css_color; -}; + IndentationEngine indents = {}; + Parser *parser = nullptr; + ExtraHighlighter extra_hl = {}; + bool is_css_color = false; -Editor *new_editor(const char *filename_arg, Coord position, Coord size, - uint8_t eol); -void save_file(Editor *editor); -void free_editor(Editor *editor); -void render_editor(Editor *editor); -void cursor_up(Editor *editor, uint32_t number); -void cursor_down(Editor *editor, uint32_t number); -Coord move_left(Editor *editor, Coord cursor, uint32_t number); -Coord move_right(Editor *editor, Coord cursor, uint32_t number); -void cursor_left(Editor *editor, uint32_t number); -void cursor_right(Editor *editor, uint32_t number); -void scroll_up(Editor *editor, int32_t number); -void scroll_down(Editor *editor, uint32_t number); -void ensure_cursor(Editor *editor); -void ensure_scroll(Editor *editor); -void handle_editor_event(Editor *editor, KeyEvent event); -void edit_erase(Editor *editor, Coord pos, int64_t len); -void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len); -void edit_replace(Editor *editor, Coord start, Coord end, const char *text, - uint32_t len); -Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y); -char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start); -void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end); -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); -void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col, - uint32_t *next_col); -void editor_lsp_handle(Editor *editor, json msg); -void apply_lsp_edits(Editor *editor, std::vector edits, bool move); -void completion_resolve_doc(Editor *editor); -void complete_accept(Editor *editor); -void complete_next(Editor *editor); -void complete_prev(Editor *editor); -void complete_select(Editor *editor, uint8_t index); -void handle_completion(Editor *editor, KeyEvent event); + Editor(const char *filename_arg, uint8_t eol); + ~Editor(); -inline void apply_hook_insertion(Editor *editor, uint32_t line, uint32_t rows) { - for (auto &hook : editor->hooks) - if (hook > line) - hook += rows; -} + void render(std::vector &buffer, Coord size, Coord pos) override; + void handle_event(KeyEvent event) override; + void handle_click(KeyEvent event, Coord size) override; + void handle_command(std::string &command) override; + void work() override; + std::array bar_info() override { return {}; }; -inline void apply_hook_deletion(Editor *editor, uint32_t removal_start, - uint32_t removal_end) { - for (auto &hook : editor->hooks) - if (hook > removal_start) - hook -= removal_end - removal_start + 1; -} + void save(); + void cursor_up(uint32_t number); + void cursor_down(uint32_t number); + void cursor_left(uint32_t number); + void cursor_right(uint32_t number); + void move_line_down(); + void move_line_up(); -inline static void utf8_normalize_edit(Editor *editor, TextEdit *edit) { - std::shared_lock lock(editor->knot_mtx); - if (edit->start.row > editor->root->line_count) { - edit->start.row = editor->root->line_count; - edit->start.col = UINT32_MAX; - } - if (edit->end.row > editor->root->line_count) { - edit->end.row = editor->root->line_count; - edit->end.col = UINT32_MAX; - } - LineIterator *it = begin_l_iter(editor->root, edit->start.row); - if (!it) - return; - uint32_t len; - char *line = next_line(it, &len); - if (!line) { + void scroll_up(uint32_t number); + void scroll_down(uint32_t number); + void ensure_cursor(); + void ensure_scroll(); + + void edit_erase(Coord pos, int64_t len); + void edit_insert(Coord pos, char *data, uint32_t len); + void edit_replace(Coord start, Coord end, const char *text, uint32_t len); + + Coord click_coord(uint32_t x, uint32_t y); + + char *get_selection(uint32_t *out_len, Coord *out_start); + void selection_bounds(Coord *out_start, Coord *out_end); + + void insert_str(char *c, uint32_t len); + void insert_char(char c); + void normal_mode(); + void backspace_edit(); + void delete_prev_word(); + void delete_next_word(); + void clear_hooks_at_line(uint32_t line); + void cursor_prev_word(); + void cursor_next_word(); + void select_all(); + void fetch_lsp_hover(); + void indent_current_line(); + void dedent_current_line(); + void indent_selection(); + void dedent_selection(); + void paste(); + void copy(); + void cut(); + + void lsp_handle(json msg); + void apply_lsp_edits(std::vector edits, bool move); + + Coord move_left(Coord cursor, uint32_t number); + Coord move_right(Coord cursor, uint32_t number); + + void word_boundaries(Coord coord, uint32_t *prev_col, uint32_t *next_col, + uint32_t *prev_clusters, uint32_t *next_clusters); + void word_boundaries_exclusive(Coord coord, uint32_t *prev_col, + uint32_t *next_col); + + void utf8_normalize_edit(TextEdit *edit) { + if (edit->start.row > this->root->line_count) { + edit->start.row = this->root->line_count; + edit->start.col = UINT32_MAX; + } + if (edit->end.row > this->root->line_count) { + edit->end.row = this->root->line_count; + edit->end.col = UINT32_MAX; + } + LineIterator *it = begin_l_iter(this->root, edit->start.row); + if (!it) + return; + uint32_t len; + char *line = next_line(it, &len); + if (!line) { + free(it->buffer); + free(it); + return; + } + if (edit->start.col < len) + edit->start.col = utf16_offset_to_utf8(line, len, edit->start.col); + else + edit->start.col = len; + if (edit->end.row == edit->start.row) { + if (edit->end.col < len) + edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col); + else + edit->end.col = len; + free(it->buffer); + free(it); + return; + } free(it->buffer); free(it); - return; - } - if (edit->start.col < len) - edit->start.col = utf16_offset_to_utf8(line, len, edit->start.col); - else - edit->start.col = len; - if (edit->end.row == edit->start.row) { + it = begin_l_iter(this->root, edit->end.row); + if (!it) + return; + line = next_line(it, &len); + if (!line) { + free(it->buffer); + free(it); + return; + } if (edit->end.col < len) edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col); else edit->end.col = len; free(it->buffer); free(it); - return; } - free(it->buffer); - free(it); - it = begin_l_iter(editor->root, edit->end.row); - if (!it) - return; - line = next_line(it, &len); - if (!line) { - free(it->buffer); - free(it); - return; + + inline void apply_hook_insertion(uint32_t line, uint32_t rows) { + for (auto &hook : this->hooks) + if (hook > line) + hook += rows; } - if (edit->end.col < len) - edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col); - else - edit->end.col = len; - free(it->buffer); - free(it); -} + + inline void apply_hook_deletion(uint32_t removal_start, + uint32_t removal_end) { + for (auto &hook : this->hooks) + if (hook > removal_start) + hook -= removal_end - removal_start + 1; + } +}; #endif diff --git a/include/editor/helpers.h b/include/editor/helpers.h index 013736e..561fcf4 100644 --- a/include/editor/helpers.h +++ b/include/editor/helpers.h @@ -3,24 +3,4 @@ #include "editor/editor.h" -void insert_str(Editor *editor, char *c, uint32_t len); -void insert_char(Editor *editor, char c); -void normal_mode(Editor *editor); -void backspace_edit(Editor *editor); -void delete_prev_word(Editor *editor); -void delete_next_word(Editor *editor); -void clear_hooks_at_line(Editor *editor, uint32_t line); -void cursor_prev_word(Editor *editor); -void cursor_next_word(Editor *editor); -void select_all(Editor *editor); -void fetch_lsp_hover(Editor *editor); -void handle_mouse(Editor *editor, KeyEvent event); -void indent_current_line(Editor *editor); -void dedent_current_line(Editor *editor); -void indent_selection(Editor *editor); -void dedent_selection(Editor *editor); -void paste(Editor *editor); -void copy(Editor *editor); -void cut(Editor *editor); - #endif diff --git a/include/extentions/hover.h b/include/extentions/hover.h new file mode 100644 index 0000000..a591479 --- /dev/null +++ b/include/extentions/hover.h @@ -0,0 +1,31 @@ +#ifndef EXTENTION_HOVER_H +#define EXTENTION_HOVER_H + +#include "io/sysio.h" +#include "pch.h" +#include "utils/utils.h" +#include "windows/decl.h" + +TileRoot *init_hover(); + +struct HoverBox : Window { + std::string text; + std::atomic is_markup; + uint32_t scroll_; + bool scroll_dirty; + + HoverBox() : scroll_(0) { this->hidden = true; } + void clear() { + this->text = ""; + this->hidden = true; + this->is_markup = false; + this->scroll_ = 0; + this->scroll_dirty = true; + } + void scroll(int32_t number); + void render(std::vector &buffer, Coord size, Coord pos) override; + void handle_click(KeyEvent, Coord) override { this->hidden = true; }; + ~HoverBox() {}; +}; + +#endif diff --git a/include/io/sysio.h b/include/io/sysio.h index 44b1e37..b52bc62 100644 --- a/include/io/sysio.h +++ b/include/io/sysio.h @@ -96,19 +96,13 @@ inline bool is_empty_cell(const ScreenCell &c) { return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b"; } +extern std::vector new_screen; + Coord start_screen(); void end_screen(); -void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg, - uint32_t bg, uint8_t flags); -void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg, - uint32_t bg, uint8_t flags); -void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg, - uint32_t bg, uint8_t flags, uint32_t ul_color); -void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg, - uint32_t bg, uint8_t flags, uint32_t ul_color); void set_cursor(uint8_t row, uint8_t col, uint32_t type, bool show_cursor_param); -void render(); +void io_render(); Coord get_size(); KeyEvent read_key(); diff --git a/include/lsp/lsp.h b/include/lsp/lsp.h index e815bef..56db3e2 100644 --- a/include/lsp/lsp.h +++ b/include/lsp/lsp.h @@ -5,56 +5,20 @@ #include "pch.h" #include "utils/utils.h" -struct LSPPending { - Editor *editor = nullptr; - std::function callback; -}; +#define LSP_TIMEOUT 3000 -// TODO: Defer any editor mutation to main thread to get rid of -// all mutex locks on the editor rope. -// struct LSPPendingResponse { -// LSPPending *pending = nullptr; -// json message; -// }; - -struct LSPOpenRequest { - Language language; - Editor *editor; -}; - -struct LSPInstance { - std::shared_mutex mtx; - const LSP *lsp; - std::string root_dir; - int pid{-1}; - int stdin_fd{-1}; - int stdout_fd{-1}; - std::atomic initialized = false; - std::atomic exited = false; - bool incremental_sync = false; - bool allow_hover = false; - bool allow_completion = false; - bool allow_resolve = false; - bool allow_formatting = false; - bool allow_formatting_on_type = false; - bool is_utf8 = false; - std::vector format_chars; - std::vector trigger_chars; - std::vector end_chars; - uint32_t last_id = 0; - Queue inbox; - Queue outbox; - Queue> open_queue; - std::unordered_map pending; - std::vector editors; -}; - -extern std::shared_mutex active_lsps_mtx; -extern std::unordered_map> +namespace lsp { +extern std::mutex lsp_mutex; +extern std::unordered_map> active_lsps; -extern Queue lsp_open_queue; +extern Queue need_opening; +extern std::unordered_set opened; +extern std::vector new_editors; +} // namespace lsp -static json client_capabilities = { +void lsp_worker(); + +static const json client_capabilities = { {"general", {{"positionEncodings", {"utf-16"}}}}, {"textDocument", {{"publishDiagnostics", {{"relatedInformation", true}}}, @@ -78,20 +42,403 @@ static json client_capabilities = { {"contextSupport", true}, {"insertTextMode", 1}}}}}}; -void lsp_send(std::shared_ptr lsp, json message, - LSPPending *pending); -void lsp_worker(); +struct LSPMessage { + Editor *editor = nullptr; + json message; + std::function callback; +}; -std::shared_ptr get_or_init_lsp(std::string lsp_id); -void clean_lsp(std::shared_ptr lsp, std::string lsp_id); -void close_lsp(std::string lsp_id); -std::optional read_lsp_message(int fd); +struct LSPInstance { + const LSP *lsp_info; + std::string root_dir; + int pid{-1}; + int stdin_fd{-1}; + int stdout_fd{-1}; + std::atomic initialized = false; + std::atomic exited = false; + bool incremental_sync = false; + bool allow_hover = false; + bool allow_completion = false; + bool allow_resolve = false; + bool allow_formatting = false; + bool allow_formatting_on_type = false; + bool is_utf8 = false; + std::vector format_chars; + std::vector trigger_chars; + std::vector end_chars; + uint32_t last_id = 0; + Queue inbox; + Queue outbox; + std::unordered_map> pending; + std::vector> lsp_response_queue; + std::vector editors; -void open_editor(std::shared_ptr lsp, - std::pair entry); -void request_add_to_lsp(Language language, Editor *editor); -void add_to_lsp(Language language, Editor *editor); -void remove_from_lsp(Editor *editor); -void lsp_handle(std::shared_ptr lsp, json message); + LSPInstance(std::string lsp_id) { + lsp_info = &lsps[lsp_id]; + if (!init_process()) { + exited = true; + return; + } + json initialize_message = { + {"jsonrpc", "2.0"}, + {"id", ++last_id}, + {"method", "initialize"}, + {"params", + {{"processId", getpid()}, + {"rootUri", "file://" + percent_encode(path_abs("."))}, + {"capabilities", client_capabilities}}}}; + send_raw(initialize_message); + pollfd pfd{stdout_fd, POLLIN, 0}; + poll(&pfd, 1, LSP_TIMEOUT); + if (!(pfd.revents & POLLIN)) { + exited = true; + return; + } + json response = *read_lsp_message(); + log("Lsp response: %s", response.dump().c_str()); + if (response.contains("result") && + response["result"].contains("capabilities")) { + auto &caps = response["result"]["capabilities"]; + // if (caps.contains("positionEncoding")) { + // std::string s = caps["positionEncoding"].get(); + // if (s == "utf-8") + // is_utf8 = true; + // log("Lsp name: %s, supports: %s", lsp->command.c_str(), + // s.c_str()); + // } + if (caps.contains("textDocumentSync")) { + auto &sync = caps["textDocumentSync"]; + if (sync.is_number()) { + int change_type = sync.get(); + incremental_sync = (change_type == 2); + } else if (sync.is_object() && sync.contains("change")) { + int change_type = sync["change"].get(); + incremental_sync = (change_type == 2); + } + } + allow_formatting = caps.value("documentFormattingProvider", false); + if (lsp_id != "lua-language-server" /* Lua ls gives terrible ontype + formatting so disable */ + && caps.contains("documentOnTypeFormattingProvider")) { + auto &fmt = caps["documentOnTypeFormattingProvider"]; + if (fmt.is_object()) { + if (fmt.contains("firstTriggerCharacter")) { + std::string s = fmt["firstTriggerCharacter"].get(); + if (s.size() == 1) + format_chars.push_back(s[0]); + } + if (fmt.contains("moreTriggerCharacter")) { + for (auto &c : fmt["moreTriggerCharacter"]) { + std::string s = c.get(); + if (s.size() == 1) + format_chars.push_back(s[0]); + } + } + allow_formatting_on_type = true; + } else if (fmt.is_boolean()) { + allow_formatting_on_type = fmt.get(); + } + } + if (caps.contains("hoverProvider")) { + auto &hover = caps["hoverProvider"]; + allow_hover = + hover.is_boolean() ? hover.get() : hover.is_object(); + } else { + allow_hover = false; + } + if (caps.contains("completionProvider")) { + allow_completion = true; + if (caps["completionProvider"].contains("resolveProvider")) + allow_resolve = + caps["completionProvider"]["resolveProvider"].get(); + if (caps["completionProvider"].contains("triggerCharacters")) { + auto &chars = caps["completionProvider"]["triggerCharacters"]; + if (chars.is_array()) { + for (auto &c : chars) { + std::string str = c.get(); + if (str.size() != 1) + continue; + trigger_chars.push_back(str[0]); + } + } + } + if (caps["completionProvider"].contains("allCommitCharacters")) { + auto &chars = caps["completionProvider"]["allCommitCharacters"]; + if (chars.is_array()) { + for (auto &c : chars) { + std::string str = c.get(); + if (str.size() != 1) + continue; + end_chars.push_back(str[0]); + } + } + } + } + } + initialized = true; + json initialized_message = {{"jsonrpc", "2.0"}, + {"method", "initialized"}, + {"params", json::object()}}; + send_raw(initialized_message); + } + ~LSPInstance() { + for (auto &ed : editors) + ed->lsp.store(nullptr); + initialized = false; + exited = true; + if (pid == -1) + return; + json shutdown = {{"id", ++last_id}, {"method", "shutdown"}}; + send_raw(shutdown); + pollfd pfd{stdout_fd, POLLIN, 0}; + poll(&pfd, 1, LSP_TIMEOUT); + json exit_msg = {{"method", "exit"}}; + send_raw(exit_msg); + int waited = 0; + while (waited < LSP_TIMEOUT) { + int status; + pid_t res = waitpid(pid, &status, WNOHANG); + if (res == pid) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + waited += 10; + } + if (kill(pid, 0) == 0) { + kill(pid, SIGKILL); + waitpid(pid, nullptr, 0); + } + pid = -1; + close(stdin_fd); + close(stdout_fd); + } + bool init_process() { + int in_pipe[2]; + int out_pipe[2]; + if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) { + perror("pipe"); + return false; + } + pid_t pid_tmp = fork(); + if (pid_tmp == -1) { + perror("fork"); + return false; + } + if (pid_tmp == 0) { + dup2(in_pipe[0], STDIN_FILENO); + dup2(out_pipe[1], STDOUT_FILENO); + int devnull = open("/dev/null", O_WRONLY); + if (devnull >= 0) { + dup2(devnull, STDERR_FILENO); + close(devnull); + } + close(in_pipe[0]); + close(in_pipe[1]); + close(out_pipe[0]); + close(out_pipe[1]); + std::vector argv; + argv.push_back(const_cast(lsp_info->command.c_str())); + for (auto &arg : lsp_info->args) + argv.push_back(const_cast(arg.c_str())); + argv.push_back(nullptr); + execvp(lsp_info->command.c_str(), argv.data()); + perror("execvp"); + _exit(127); + } + pid = pid_tmp; + stdin_fd = in_pipe[1]; + stdout_fd = out_pipe[0]; + close(in_pipe[0]); + close(out_pipe[1]); + return true; + } + void add(Editor *ed) { + std::unique_lock lock(lsp::lsp_mutex); + editors.push_back(ed); + lock.unlock(); + ed->lsp.store(this); + char *buf = read(ed->root, 0, ed->root->char_count); + std::string text(buf); + free(buf); + auto message = std::make_unique(); + message->message = {{"method", "textDocument/didOpen"}, + {"params", + {{"textDocument", + {{"uri", ed->uri}, + {"languageId", ed->lang.name}, + {"version", 1}, + {"text", text}}}}}}; + send(std::move(message)); + } + void remove(Editor *ed) { + std::unique_lock lock(lsp::lsp_mutex); + editors.erase(std::remove(editors.begin(), editors.end(), ed), + editors.end()); + lock.unlock(); + auto message = std::make_unique(); + message->message = {{"method", "textDocument/didClose"}, + {"params", {{"textDocument", {{"uri", ed->uri}}}}}}; + send(std::move(message)); + } + void work() { + if (exited) + return; + int status; + pid_t res = waitpid(pid, &status, WNOHANG); + if (res == pid) { + exited = true; + pid = -1; + return; + } + while (!outbox.empty()) { + json message = outbox.front(); + std::string m = message.value("method", ""); + outbox.pop(); + send_raw(message); + } + pollfd pfd{stdout_fd, POLLIN | POLLHUP | POLLERR, 0}; + int r = poll(&pfd, 1, 0); + if (r > 0 && pfd.revents & (POLLHUP | POLLERR)) { + exited = true; + pid = -1; + return; + } + while ((r = poll(&pfd, 1, 0)) > 0) { + if (pfd.revents & (POLLHUP | POLLERR)) { + exited = true; + pid = -1; + return; + } + auto msg = read_lsp_message(); + if (!msg) + break; + if (msg->contains("id")) { + uint32_t id = msg->at("id").get(); + auto it = pending.find(id); + if (it != pending.end()) { + if (it->second->editor) { + it->second->message = *msg; + lsp_response_queue.push_back(std::move(it->second)); + } else { + auto message = *std::move(it->second); + message.message = *msg; + message.callback(message); + } + pending.erase(it); + } + } else if (msg->contains("method")) { + std::string uri; + if (msg->contains("params")) { + auto &p = (*msg)["params"]; + if (p.contains("textDocument") && p["textDocument"].contains("uri")) + uri = p["textDocument"]["uri"].get(); + else if (p.contains("uri")) + uri = p["uri"].get(); + } + Editor *ed = resolve_uri(uri); + auto response = std::make_unique(); + response->editor = ed; + response->message = *msg; + response->callback = editor_handle_wrapper; + if (ed) + lsp_response_queue.push_back(std::move(response)); + else + lsp_handle(*msg); + } + } + } + inline static void editor_handle_wrapper(const LSPMessage &message) { + message.editor->lsp_handle(message.message); + } + void callbacks() { + for (auto &message : lsp_response_queue) + message->callback(*message); + lsp_response_queue.clear(); + } + inline void send_raw(const json &msg) { + std::string payload = msg.dump(); + std::string header = + "Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n"; + std::string out = header + payload; + const char *ptr = out.data(); + size_t remaining = out.size(); + while (remaining > 0) { + ssize_t n = write(stdin_fd, ptr, remaining); + if (n <= 0) { + if (errno == EINTR) + continue; + break; + } + ptr += n; + remaining -= n; + } + }; + inline std::optional read_lsp_message() { + std::string header; + char c; + while (true) { + ssize_t n = read(stdout_fd, &c, 1); + if (n <= 0) + return std::nullopt; + header.push_back(c); + if (header.size() >= 4 && header.substr(header.size() - 4) == "\r\n\r\n") + break; + } + size_t pos = header.find("Content-Length:"); + if (pos == std::string::npos) + return std::nullopt; + pos += strlen("Content-Length:"); + while (pos < header.size() && std::isspace(header[pos])) + pos++; + size_t end = pos; + while (end < header.size() && std::isdigit(header[end])) + end++; + size_t len = std::stoul(header.substr(pos, end - pos)); + std::string body(len, '\0'); + size_t got = 0; + while (got < len) { + ssize_t n = read(stdout_fd, &body[got], len - got); + if (n <= 0) + return std::nullopt; + got += n; + } + return json::parse(body); + } + inline Editor *resolve_uri(std::string uri) { + if (uri.empty()) + return nullptr; + for (auto &editor : editors) + if (editor->uri == uri) + return editor; + return nullptr; + } + inline void lsp_handle(json &message) { + std::string method = message.value("method", ""); + if (method == "window/showMessage") { + if (message.contains("params")) { + auto &p = message["params"]; + if (p.contains("message")) + log("%s\n", p["message"].get().c_str()); + } + } else if (method == "window/logMessage") { + if (message.contains("params")) { + auto &p = message["params"]; + if (p.contains("message")) + log("%s\n", p["message"].get().c_str()); + } + } + } + void send(std::unique_ptr message) { + if (pid == -1) + return; + message->message["jsonrpc"] = "2.0"; + if (message->callback) + message->message["id"] = ++last_id; + outbox.push(message->message); + if (!message->callback) + return; + std::lock_guard lock(lsp::lsp_mutex); + pending[last_id] = std::move(message); + } +}; #endif diff --git a/include/main.h b/include/main.h index cc94987..0028266 100644 --- a/include/main.h +++ b/include/main.h @@ -12,8 +12,11 @@ extern std::atomic running; extern std::atomic mode; -extern std::vector editors; -extern uint8_t current_editor; +extern fs::path pwd; + +namespace ui { extern Bar bar; +extern TileRoot *hover_popup; +} // namespace ui #endif diff --git a/include/pch.h b/include/pch.h index 5a0c479..096ba85 100644 --- a/include/pch.h +++ b/include/pch.h @@ -50,6 +50,7 @@ extern "C" { #include #include #include +#include #include using json = nlohmann::json; diff --git a/include/ruby/decl.h b/include/ruby/decl.h index 203762a..6b87980 100644 --- a/include/ruby/decl.h +++ b/include/ruby/decl.h @@ -3,6 +3,7 @@ #include "syntax/decl.h" #include "utils/utils.h" +#include "windows/decl.h" namespace fs = std::filesystem; @@ -45,9 +46,7 @@ bool custom_compare(mrb_value match_block, std::string state1, std::string parse_custom(std::vector *tokens, mrb_value parser_block, const char *line, uint32_t len, std::string state, uint32_t c_line); -BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, - std::string lsp_name, std::string filename, - std::string foldername, uint32_t line, uint32_t max_line, - uint32_t width); +BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername, + Window *window); #endif diff --git a/include/ruby/libcrib.rb b/include/ruby/libcrib.rb index df89757..050d887 100644 --- a/include/ruby/libcrib.rb +++ b/include/ruby/libcrib.rb @@ -177,7 +177,7 @@ module C color: 0x6e1516, symbol: " ", extensions: ["erb"], - lsp: "ruby-lsp" + lsp: "emmet-language-server" }, lua: { color: 0x36a3d9, @@ -331,7 +331,7 @@ module C @b_startup = nil @b_shutdown = nil @b_bar = proc do |info| - # mode, lang_name, warnings, lsp_name, filename, foldername, line, max_line, width + # mode, width, data[5] : strings (any data about the focused window) # puts info.inspect mode_color = 0x82AAFF mode_symbol = " " @@ -352,11 +352,15 @@ module C mode_color = 0xF29CC3 mode_symbol = " " end - lang_info = C.languages[info[:lang_name]] - if lang_info.nil? - lang_info = C.languages[:default] + lang_info = C.languages[:default] + filename = "" + if info[:data][0] == "editor" + lang_info = C.languages[info[:data][2]] if info[:data][2] != "" + if lang_info.nil? + lang_info = C.languages[:default] + end + filename = File.basename(info[:data][1]) if info[:data][1] != "" end - filename = File.basename(info[:filename]) starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}" highlights = [] highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 } diff --git a/include/ruby/ruby_compiled.h b/include/ruby/ruby_compiled.h index 62e4ad4..80cef00 100644 --- a/include/ruby/ruby_compiled.h +++ b/include/ruby/ruby_compiled.h @@ -1,8 +1,8 @@ #pragma once constexpr unsigned char _tmp___crib_precompiled_mrb[] = { - 0x52, 0x49, 0x54, 0x45, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x2b, 0xbe, + 0x52, 0x49, 0x54, 0x45, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x2c, 0x1d, 0x4d, 0x41, 0x54, 0x5a, 0x30, 0x30, 0x30, 0x30, 0x49, 0x52, 0x45, 0x50, - 0x00, 0x00, 0x2a, 0x2a, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x00, 0xa2, + 0x00, 0x00, 0x2a, 0x89, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x11, 0x01, 0x68, 0x01, 0x00, 0x69, 0x01, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x11, 0x01, 0x68, 0x01, 0x02, 0x69, 0x01, 0x02, 0x11, 0x01, 0x68, 0x01, @@ -301,7 +301,7 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = { 0x05, 0x5c, 0x21, 0x0f, 0x5e, 0x1a, 0x04, 0x10, 0x1b, 0x12, 0x10, 0x1c, 0x02, 0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f, 0x38, 0x10, 0x20, 0x04, 0x5c, 0x21, 0x42, 0x52, 0x21, 0x01, 0x10, 0x22, - 0x05, 0x5c, 0x23, 0x08, 0x5e, 0x1c, 0x04, 0x10, 0x1d, 0x13, 0x10, 0x1e, + 0x05, 0x5c, 0x23, 0x15, 0x5e, 0x1c, 0x04, 0x10, 0x1d, 0x13, 0x10, 0x1e, 0x02, 0x0f, 0x1f, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x20, 0x03, 0x5c, 0x21, 0x43, 0x10, 0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24, 0x05, 0x5c, 0x25, 0x17, 0x5e, 0x1e, 0x04, 0x10, 0x1f, 0x14, 0x10, 0x20, @@ -576,8 +576,8 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = { 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03, - 0x55, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x1f, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, + 0xb4, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x7b, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x00, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a, 0x01, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x01, 0x26, 0x00, 0x68, @@ -590,263 +590,256 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = { 0x5c, 0x04, 0x04, 0x26, 0x00, 0x1a, 0x10, 0x0a, 0x06, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xf2, 0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07, 0x33, - 0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x09, 0x23, 0x0a, 0x23, 0x09, - 0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29, 0x09, 0x00, 0x03, 0x26, 0x00, - 0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08, 0x10, 0x0a, 0x0a, 0x23, 0x09, - 0x01, 0x05, 0x09, 0x1d, 0x09, 0x0b, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0c, - 0x23, 0x0a, 0x32, 0x09, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x06, - 0x01, 0x0a, 0x04, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a, - 0x01, 0x10, 0x0b, 0x00, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, - 0x5d, 0x09, 0x5c, 0x0a, 0x07, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, - 0x10, 0x23, 0x0a, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a, - 0x06, 0x5d, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, - 0x08, 0x00, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, - 0x0e, 0x14, 0x10, 0x0c, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, - 0x0f, 0x10, 0x10, 0x14, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, - 0x5e, 0x0a, 0x05, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, - 0x11, 0x01, 0x0b, 0x03, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36, - 0x3c, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11, - 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, - 0x11, 0x0f, 0x0b, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, - 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, - 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, - 0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, - 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, - 0x03, 0x0f, 0x0d, 0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, + 0x09, 0x08, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x5c, 0x06, + 0x06, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x06, 0x0a, 0x23, + 0x09, 0x5c, 0x0a, 0x07, 0x4d, 0x09, 0x28, 0x09, 0x00, 0x73, 0x01, 0x09, + 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x08, 0x0a, 0x23, 0x09, 0x5c, 0x0a, + 0x06, 0x32, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x17, 0x1d, 0x09, 0x07, + 0x33, 0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x08, + 0x0b, 0x23, 0x0a, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29, + 0x09, 0x00, 0x03, 0x26, 0x00, 0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08, + 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x01, 0x10, + 0x0a, 0x0a, 0x23, 0x09, 0x07, 0x0a, 0x23, 0x09, 0x5c, 0x0a, 0x06, 0x32, + 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x16, 0x1d, 0x09, 0x0c, 0x01, 0x0a, + 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x07, 0x0b, 0x23, 0x0a, 0x32, 0x09, + 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x08, 0x01, 0x0a, 0x04, 0x5d, + 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x00, + 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x5d, 0x09, 0x5c, 0x0a, + 0x09, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x10, 0x23, 0x0a, 0x5d, + 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x06, 0x5d, 0x09, 0x5c, + 0x0a, 0x0a, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x08, 0x00, 0x01, 0x09, + 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x0e, 0x14, 0x10, 0x0c, + 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x0f, 0x10, 0x10, 0x14, + 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x5e, 0x0a, 0x05, 0x32, + 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x03, + 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0e, 0x14, + 0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, - 0xce, 0xd4, 0xdf, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, - 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, - 0x33, 0x11, 0x15, 0x46, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, - 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, - 0x2d, 0x10, 0x0c, 0x12, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, - 0x01, 0x10, 0x06, 0x33, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, - 0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, - 0x09, 0x18, 0x01, 0x0a, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, - 0x09, 0x02, 0x3d, 0x09, 0x00, 0x09, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, - 0x00, 0x00, 0x04, 0xee, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, - 0xb1, 0x93, 0xa7, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, - 0x20, 0x00, 0x00, 0x00, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, - 0x04, 0xee, 0xba, 0xa2, 0x20, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, - 0x00, 0x08, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, - 0x00, 0x03, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, - 0x64, 0x65, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, - 0x00, 0x03, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, - 0x72, 0x74, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, - 0x00, 0x06, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, - 0x75, 0x6d, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x09, - 0x6c, 0x61, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x07, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, - 0x6c, 0x65, 0x00, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, - 0x63, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, - 0x6c, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, - 0x00, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, - 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, - 0x00, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, - 0x32, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, - 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, - 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, - 0x00, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, - 0x00, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, - 0xc8, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x3a, 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, - 0x08, 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, - 0x1d, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, - 0x01, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, - 0x00, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, - 0x08, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, - 0x05, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, - 0x01, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, - 0x27, 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, - 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, - 0x08, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, - 0x08, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, - 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, - 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, - 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, - 0x06, 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, - 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, - 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x23, - 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01, - 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, - 0x08, 0x0f, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, 0x00, 0x01, - 0x03, 0x08, 0x3d, 0x03, 0x10, 0x07, 0x10, 0x10, 0x08, 0x10, 0x32, 0x07, - 0x11, 0x01, 0x27, 0x07, 0x00, 0x0a, 0x10, 0x07, 0x10, 0x10, 0x08, 0x12, - 0x32, 0x07, 0x11, 0x01, 0x28, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x5c, 0x08, - 0x07, 0x2f, 0x07, 0x13, 0x01, 0x33, 0x07, 0x14, 0x28, 0x07, 0x00, 0x02, - 0x3d, 0x03, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x5c, 0x09, - 0x09, 0x5d, 0x08, 0x2f, 0x07, 0x15, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06, - 0x07, 0x01, 0x07, 0x06, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a, - 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, - 0x08, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xd1, 0x66, 0x08, 0x1f, - 0x08, 0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, - 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, - 0x00, 0xb4, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08, - 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, - 0x06, 0x10, 0x08, 0x16, 0x26, 0x00, 0x97, 0x66, 0x08, 0x1f, 0x08, 0x08, - 0x5c, 0x09, 0x0c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, - 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0x7a, - 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, - 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, - 0x08, 0x0d, 0x26, 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, - 0x0d, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, - 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x40, 0x66, 0x08, - 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, - 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x19, - 0x26, 0x00, 0x23, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0f, 0x32, + 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, + 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e, + 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, + 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x10, 0x0c, 0x12, 0x0f, + 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0d, 0x10, + 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, + 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0xce, 0xd4, 0xdf, 0x10, + 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, + 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x33, 0x11, 0x15, 0x46, + 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, + 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0c, 0x12, + 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x01, 0x10, 0x06, 0x33, + 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x07, 0x11, + 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x18, 0x01, 0x0a, + 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x09, 0x02, 0x3d, 0x09, + 0x00, 0x0b, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee, + 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0x93, 0xa7, 0x20, + 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, 0x20, 0x00, 0x00, 0x00, + 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee, 0xba, 0xa2, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x65, 0x64, 0x69, + 0x74, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x08, + 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, 0x00, 0x03, + 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65, + 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, 0x00, 0x03, + 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, + 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x00, 0x06, + 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, 0x75, 0x6d, + 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x07, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x00, 0x00, 0x02, 0x21, 0x3d, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, + 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, + 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, 0x63, + 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, 0x00, + 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, + 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, + 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, 0x69, + 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, 0x32, + 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x43, + 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, 0x63, + 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, + 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, + 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, 0xc8, + 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3a, + 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x08, + 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x1d, + 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, 0x01, + 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, 0x00, + 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, 0x08, + 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, 0x05, + 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x01, + 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27, + 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, - 0x00, 0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, - 0x00, 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, - 0x23, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, - 0x00, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, - 0x00, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, - 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, - 0x61, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, - 0x14, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, - 0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, - 0x00, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, - 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, - 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, - 0x00, 0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1b, 0x00, 0x07, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, - 0x00, 0x00, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x3f, 0x00, 0x00, 0x08, - 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x04, 0x6f, - 0x70, 0x65, 0x6e, 0x00, 0x00, 0x05, 0x63, 0x68, 0x6f, 0x6d, 0x70, 0x00, - 0x00, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, - 0x3f, 0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, - 0x00, 0x00, 0x06, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00, 0x00, 0x07, - 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x03, 0x3d, 0x3d, - 0x3d, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x04, 0x66, - 0x69, 0x73, 0x68, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, - 0x00, 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x03, 0x6c, 0x75, - 0x61, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x02, - 0x21, 0x3d, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x0f, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, - 0x73, 0x3f, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, - 0x04, 0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, - 0x00, 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, - 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x01, 0x00, - 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x10, 0x02, 0x00, - 0x10, 0x03, 0x01, 0x10, 0x04, 0x02, 0x10, 0x05, 0x03, 0x10, 0x06, 0x04, - 0x2f, 0x01, 0x05, 0x05, 0x10, 0x02, 0x06, 0x10, 0x03, 0x07, 0x10, 0x04, - 0x08, 0x10, 0x05, 0x09, 0x10, 0x06, 0x0a, 0x10, 0x07, 0x0b, 0x10, 0x08, - 0x0c, 0x2f, 0x01, 0x0d, 0x07, 0x6b, 0x01, 0x0e, 0x00, 0x6b, 0x01, 0x0f, - 0x01, 0x6b, 0x01, 0x10, 0x02, 0x6b, 0x01, 0x11, 0x03, 0x6b, 0x01, 0x12, - 0x04, 0x6b, 0x01, 0x13, 0x05, 0x6b, 0x01, 0x14, 0x06, 0x6b, 0x01, 0x15, - 0x07, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65, - 0x6d, 0x65, 0x00, 0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x73, 0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67, - 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d, - 0x61, 0x74, 0x74, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, - 0x72, 0x00, 0x00, 0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, - 0x70, 0x00, 0x00, 0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, - 0x77, 0x6e, 0x00, 0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, + 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, 0x08, + 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, + 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x00, + 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, + 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, + 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, + 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, + 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x23, 0x66, + 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01, 0x01, + 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, + 0x0f, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, 0x00, 0x01, 0x03, + 0x08, 0x3d, 0x03, 0x10, 0x07, 0x10, 0x10, 0x08, 0x10, 0x32, 0x07, 0x11, + 0x01, 0x27, 0x07, 0x00, 0x0a, 0x10, 0x07, 0x10, 0x10, 0x08, 0x12, 0x32, + 0x07, 0x11, 0x01, 0x28, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x5c, 0x08, 0x07, + 0x2f, 0x07, 0x13, 0x01, 0x33, 0x07, 0x14, 0x28, 0x07, 0x00, 0x02, 0x3d, + 0x03, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x5c, 0x09, 0x09, + 0x5d, 0x08, 0x2f, 0x07, 0x15, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06, 0x07, + 0x01, 0x07, 0x06, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a, 0x32, + 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, + 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xd1, 0x66, 0x08, 0x1f, 0x08, + 0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, + 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, + 0xb4, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08, 0x09, + 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, + 0x10, 0x08, 0x16, 0x26, 0x00, 0x97, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, + 0x09, 0x0c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, + 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0x7a, 0x66, + 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, 0x01, + 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, + 0x0d, 0x26, 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0d, + 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, + 0x08, 0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, + 0x08, 0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, + 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x19, 0x26, + 0x00, 0x23, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0f, 0x32, 0x08, + 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, + 0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, + 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, 0x23, + 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x00, + 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00, + 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x00, + 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61, + 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x14, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x6a, + 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, + 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, + 0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1b, 0x00, 0x07, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, + 0x00, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x3f, 0x00, 0x00, 0x08, 0x72, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x04, 0x6f, 0x70, + 0x65, 0x6e, 0x00, 0x00, 0x05, 0x63, 0x68, 0x6f, 0x6d, 0x70, 0x00, 0x00, + 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, + 0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, 0x00, + 0x00, 0x06, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00, 0x00, 0x07, 0x63, + 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x03, 0x3d, 0x3d, 0x3d, + 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x04, 0x66, 0x69, + 0x73, 0x68, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, + 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61, + 0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x02, 0x21, + 0x3d, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x0f, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, + 0x3f, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x04, + 0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, + 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, + 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x01, 0x00, 0x0a, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x10, 0x02, 0x00, 0x10, + 0x03, 0x01, 0x10, 0x04, 0x02, 0x10, 0x05, 0x03, 0x10, 0x06, 0x04, 0x2f, + 0x01, 0x05, 0x05, 0x10, 0x02, 0x06, 0x10, 0x03, 0x07, 0x10, 0x04, 0x08, + 0x10, 0x05, 0x09, 0x10, 0x06, 0x0a, 0x10, 0x07, 0x0b, 0x10, 0x08, 0x0c, + 0x2f, 0x01, 0x0d, 0x07, 0x6b, 0x01, 0x0e, 0x00, 0x6b, 0x01, 0x0f, 0x01, + 0x6b, 0x01, 0x10, 0x02, 0x6b, 0x01, 0x11, 0x03, 0x6b, 0x01, 0x12, 0x04, + 0x6b, 0x01, 0x13, 0x05, 0x6b, 0x01, 0x14, 0x06, 0x6b, 0x01, 0x15, 0x07, + 0x3d, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65, 0x6d, + 0x65, 0x00, 0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x73, 0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67, 0x68, + 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d, 0x61, + 0x74, 0x74, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, + 0x00, 0x00, 0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, + 0x00, 0x00, 0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, + 0x6e, 0x00, 0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, + 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, + 0x05, 0x62, 0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f, 0x63, + 0x6f, 0x70, 0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, + 0x65, 0x00, 0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74, 0x72, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62, 0x61, + 0x72, 0x3d, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, + 0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x00, + 0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, 0x73, + 0x74, 0x65, 0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, - 0x00, 0x05, 0x62, 0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f, - 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73, - 0x74, 0x65, 0x00, 0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74, - 0x72, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62, - 0x61, 0x72, 0x3d, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, - 0x70, 0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, - 0x00, 0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, - 0x73, 0x74, 0x65, 0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, - 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, - 0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, - 0x00, 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, - 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, - 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, - 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f, - 0x62, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, + 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, + 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f, 0x62, + 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, + 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, - 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, - 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, - 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, - 0x79, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, - 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, - 0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, - 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, - 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, - 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, - 0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, - 0x26, 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, - 0x04, 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, - 0x08, 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, - 0x02, 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, - 0x02, 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, - 0x04, 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, - 0x08, 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, - 0x00, 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, - 0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, - 0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, - 0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, - 0x00, 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, - 0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, - 0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, - 0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, - 0x00, 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, - 0x41, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, - 0x3f, 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, - 0x03, 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, - 0x17, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, - 0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x00, 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, - 0x65, 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61, - 0x63, 0x68, 0x00, 0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26, - 0x00, 0x02, 0x11, 0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21, - 0x06, 0x01, 0x00, 0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04, - 0x34, 0x05, 0x00, 0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, - 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, - 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, - 0x00, 0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, - 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, - 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, - 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, - 0x00, 0x19, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, - 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, - 0x01, 0x23, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, - 0x00, 0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, - 0x03, 0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, - 0x01, 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05, 0x01, 0x32, 0x03, 0x01, - 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x40, 0x6b, 0x65, - 0x79, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, - 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, + 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, + 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, + 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0e, + 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, + 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, + 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26, + 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x04, + 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x08, + 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x02, + 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x02, + 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x04, + 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x08, + 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x00, + 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x28, + 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, + 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01, + 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00, + 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x28, + 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, + 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01, + 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00, + 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x3f, + 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03, + 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x17, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00, + 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61, 0x63, + 0x68, 0x00, 0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26, 0x00, + 0x02, 0x11, 0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21, 0x06, + 0x01, 0x00, 0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04, 0x34, + 0x05, 0x00, 0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x62, + 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x87, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 0x19, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, 0x04, @@ -854,85 +847,100 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = { 0x23, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, - 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, - 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, - 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, - 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, - 0x3d, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x00, 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, - 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, - 0x39, 0x04, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, - 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, - 0x00, 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, - 0x05, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, - 0x02, 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, - 0x01, 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, - 0x01, 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, - 0x32, 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, - 0x09, 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, - 0x07, 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, - 0x03, 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, - 0x00, 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, - 0x3f, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, - 0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, - 0x6c, 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x00, 0x00, 0x0b, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x00, 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, - 0x00, 0x00, 0x08, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00, - 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00, - 0x00, 0x09, 0x24, 0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00, - 0x00, 0x04, 0x65, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, - 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39, + 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05, 0x01, 0x32, 0x03, 0x01, 0x01, + 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x40, 0x6b, 0x65, 0x79, + 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, 0x02, + 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x21, + 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00, + 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04, + 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 0x19, + 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, 0x04, 0x19, + 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x23, + 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00, 0x21, + 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x25, + 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, + 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, 0x3d, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f, + 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x3d, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00, + 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x04, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, - 0x04, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, - 0x0b, 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, - 0x1d, 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, - 0x33, 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, - 0x01, 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, - 0x01, 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, - 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, - 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, - 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, - 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, - 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, - 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, - 0x00, 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, - 0x74, 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, - 0x64, 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, - 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, - 0x65, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, - 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x69, 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, - 0x68, 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, - 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73, - 0x68, 0x65, 0x62, 0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65, - 0x74, 0x79, 0x70, 0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, - 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73, - 0x00, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70, - 0x70, 0x00, 0x03, 0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61, - 0x63, 0x74, 0x00, 0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64, - 0x65, 0x00, 0x03, 0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, - 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, - 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03, - 0x00, 0x04, 0xff, 0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, - 0x00, 0x09, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff, - 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, - 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, - 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f, - 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17, - 0x00, 0x18, 0xff, 0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff, - 0x00, 0x19, 0xff, 0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c, - 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00, - 0x00, 0x08 + 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x00, + 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x05, + 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x02, + 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x01, + 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x01, + 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, + 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x09, + 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x07, + 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x03, + 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, + 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, + 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, + 0x00, 0x0b, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x00, 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, + 0x00, 0x08, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00, 0x00, + 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00, 0x00, + 0x09, 0x24, 0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00, 0x00, + 0x04, 0x65, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x04, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39, 0x04, + 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x04, + 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x0b, + 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x1d, + 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x33, + 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x01, + 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01, + 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x00, + 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x09, + 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x04, + 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, + 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x65, + 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00, + 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, + 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x65, + 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x65, + 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x6e, + 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73, 0x68, + 0x65, 0x62, 0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, + 0x79, 0x70, 0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x05, + 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x00, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70, 0x70, + 0x00, 0x03, 0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61, 0x63, + 0x74, 0x00, 0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65, + 0x00, 0x03, 0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68, 0x00, + 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, + 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, + 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03, 0x00, + 0x04, 0xff, 0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x09, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0xff, + 0xff, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff, 0x00, + 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, + 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, + 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17, 0x00, + 0x18, 0xff, 0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff, 0x00, + 0x19, 0xff, 0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c, 0x00, + 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x08 }; -constexpr unsigned int _tmp___crib_precompiled_mrb_len = 11198; +constexpr unsigned int _tmp___crib_precompiled_mrb_len = 11293; diff --git a/include/tiling/decl.h b/include/tiling/decl.h deleted file mode 100644 index 18575ec..0000000 --- a/include/tiling/decl.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef TILING_DECL_H -#define TILING_DECL_H - -#include "utils/utils.h" - -struct Window {}; - -struct TileBlock {}; - -#endif diff --git a/include/ui/bar.h b/include/ui/bar.h index 61f4f06..84e1f5f 100644 --- a/include/ui/bar.h +++ b/include/ui/bar.h @@ -12,8 +12,9 @@ struct Bar { uint32_t cursor = 0; void init(Coord screen) { this->screen = screen; } - void render(); - void handle(KeyEvent event); + void render(std::vector &buffer); + void handle_event(KeyEvent event); + void handle_command(std::string &command); void log(std::string message); }; diff --git a/include/ui/completionbox.h b/include/ui/completionbox.h index a99f4c7..917d602 100644 --- a/include/ui/completionbox.h +++ b/include/ui/completionbox.h @@ -1,21 +1,21 @@ -#ifndef UI_COMPLETIONBOX_H -#define UI_COMPLETIONBOX_H - -#include "io/sysio.h" -#include "pch.h" -#include "utils/utils.h" - -struct CompletionBox { - std::shared_mutex mtx; - struct CompletionSession *session; - bool hidden = true; - std::vector cells; - Coord size; - Coord position; - - CompletionBox(CompletionSession *s) : session(s) {} - void render_update(); - void render(Coord pos); -}; - -#endif +// #ifndef UI_COMPLETIONBOX_H +// #define UI_COMPLETIONBOX_H +// +// #include "io/sysio.h" +// #include "pch.h" +// #include "utils/utils.h" +// +// struct CompletionBox { +// std::shared_mutex mtx; +// struct CompletionSession *session; +// bool hidden = true; +// std::vector cells; +// Coord size; +// Coord position; +// +// CompletionBox(CompletionSession *s) : session(s) {} +// void render_update(); +// void render(Coord pos); +// }; +// +// #endif diff --git a/include/ui/hover.h b/include/ui/hover.h deleted file mode 100644 index 3255281..0000000 --- a/include/ui/hover.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef UI_HOVER_H -#define UI_HOVER_H - -#include "editor/decl.h" -#include "io/sysio.h" -#include "pch.h" -#include "utils/utils.h" - -struct HoverBox { - std::string text; - std::atomic is_markup; - uint32_t scroll_; - std::vector cells; - Coord size; - - void clear(); - void scroll(int32_t number); - void render_first(bool scroll = false); - void render(Coord pos); -}; - -#endif diff --git a/include/windows/decl.h b/include/windows/decl.h new file mode 100644 index 0000000..26fd310 --- /dev/null +++ b/include/windows/decl.h @@ -0,0 +1,120 @@ +#ifndef TILING_DECL_H +#define TILING_DECL_H + +#include "io/sysio.h" +#include "utils/utils.h" + +struct Tile { + bool hidden = false; + uint32_t weight = 100; + + virtual void render(std::vector &buffer, Coord size, + Coord pos) = 0; + virtual void handle_click(KeyEvent event, Coord size) = 0; + virtual ~Tile() = default; +}; + +struct Window : Tile { + virtual ~Window() = default; + virtual void handle_event(KeyEvent){}; + virtual void handle_command(std::string &) {}; + virtual void work() {}; + virtual std::array bar_info() { return {}; }; +}; + +struct TileBlock : Tile { + bool vertical; + std::vector> tiles; + + void render(std::vector &buffer, Coord size, Coord pos) override { + uint32_t total_weight = 0; + for (auto &t : tiles) { + if (!t->hidden) + total_weight += t->weight; + } + if (total_weight == 0) + return; + for (auto &t : tiles) { + if (t->hidden) + continue; + uint32_t proportion = + t->weight * (vertical ? size.row : size.col) / total_weight; + Coord tile_size = vertical ? Coord{.row = proportion, .col = size.col} + : Coord{.row = size.row, .col = proportion}; + t->render(buffer, tile_size, pos); + if (vertical) + pos.row += tile_size.row; + else + pos.col += tile_size.col; + } + } + + void handle_click(KeyEvent event, Coord size) override { + uint32_t total_weight = 0; + for (auto &t : tiles) + if (!t->hidden) + total_weight += t->weight; + if (total_weight == 0) + return; + uint32_t i = 0; + for (auto &t : tiles) { + if (t->hidden) + continue; + uint32_t proportion = + t->weight * (vertical ? size.row : size.col) / total_weight; + Coord tile_size = vertical ? Coord{.row = proportion, .col = size.col} + : Coord{.row = size.row, .col = proportion}; + if (vertical) { + if (event.mouse_y < i + proportion) { + event.mouse_y -= i; + t->handle_click(event, tile_size); + return; + } + } else { + if (event.mouse_x < i + proportion) { + event.mouse_x -= i; + t->handle_click(event, tile_size); + return; + } + } + i += proportion; + } + } +}; + +struct TileRoot { + std::unique_ptr tile; + Coord pos; + Coord size; + + void render(std::vector &buffer) { + if (this->tile->hidden) + return; + this->tile->render(buffer, size, pos); + } + void handle_click(KeyEvent event) { + event.mouse_x -= this->pos.col; + event.mouse_y -= this->pos.row; + this->tile->handle_click(event, size); + } + bool inside(uint32_t x, uint32_t y) { + return x >= pos.col && x < pos.col + size.col && y >= pos.row && + y < pos.row + size.row; + } +}; + +extern TileRoot root_tile; +extern std::vector> popups; +extern Window *focused_window; + +inline void close_popup(TileRoot *handle) { + auto it = std::find_if(popups.begin(), popups.end(), + [handle](const auto &p) { return p.get() == handle; }); + if (it != popups.end()) + popups.erase(it); +} + +void render(); +void handle_click(KeyEvent event); + +#endif diff --git a/samples/bash.sh b/samples/bash.sh index f65dcec..2b3c4c9 100644 --- a/samples/bash.sh +++ b/samples/bash.sh @@ -25,6 +25,7 @@ colorize() { handle_error() { log ERROR "An error occurred on line $1" } + trap 'handle_error $LINENO' ERR # Multiline string test read -r -d '' MULTI <<'CPP' diff --git a/src/editor/adjustment.cc b/src/editor/adjustment.cc index 8ca1ffe..5304af1 100644 --- a/src/editor/adjustment.cc +++ b/src/editor/adjustment.cc @@ -1,25 +1,24 @@ #include "editor/editor.h" -void ensure_cursor(Editor *editor) { - std::shared_lock knot_lock(editor->knot_mtx); - if (editor->cursor < editor->scroll) { - editor->cursor.row = editor->scroll.row; - editor->cursor.col = editor->scroll.col; - editor->cursor_preffered = UINT32_MAX; +void Editor::ensure_cursor() { + if (this->cursor < this->scroll) { + this->cursor.row = this->scroll.row; + this->cursor.col = this->scroll.col; + this->cursor_preffered = UINT32_MAX; return; } uint32_t numlen = - EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - uint32_t render_width = editor->size.col - numlen; + EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); + uint32_t render_width = this->size.col - numlen; uint32_t visual_rows = 0; - uint32_t line_index = editor->scroll.row; + uint32_t line_index = this->scroll.row; bool first_visual_line = true; - LineIterator *it = begin_l_iter(editor->root, line_index); + LineIterator *it = begin_l_iter(this->root, line_index); if (!it) return; - Coord last_visible = editor->scroll; + Coord last_visible = this->scroll; while (true) { - if (visual_rows >= editor->size.row) + if (visual_rows >= this->size.row) break; uint32_t line_len; char *line = next_line(it, &line_len); @@ -27,13 +26,13 @@ void ensure_cursor(Editor *editor) { break; if (line_len > 0 && line[line_len - 1] == '\n') line_len--; - uint32_t offset = first_visual_line ? editor->scroll.col : 0; + uint32_t offset = first_visual_line ? this->scroll.col : 0; first_visual_line = false; while (offset < line_len || (line_len == 0 && offset == 0)) { Coord current = {line_index, offset}; last_visible = current; visual_rows++; - if (visual_rows >= editor->size.row) + if (visual_rows >= this->size.row) break; uint32_t col = 0; uint32_t advance = 0; @@ -48,9 +47,9 @@ void ensure_cursor(Editor *editor) { left -= g; col += w; } - if (line_index == editor->cursor.row) { - if (editor->cursor.col >= offset && - editor->cursor.col <= offset + advance) { + if (line_index == this->cursor.row) { + if (this->cursor.col >= offset && + this->cursor.col <= offset + advance) { free(it->buffer); free(it); return; @@ -64,20 +63,19 @@ void ensure_cursor(Editor *editor) { } line_index++; } - editor->cursor.row = last_visible.row; - editor->cursor.col = last_visible.col; - editor->cursor_preffered = UINT32_MAX; + this->cursor.row = last_visible.row; + this->cursor.col = last_visible.col; + this->cursor_preffered = UINT32_MAX; free(it->buffer); free(it); } -void ensure_scroll(Editor *editor) { - std::shared_lock knot_lock(editor->knot_mtx); +void Editor::ensure_scroll() { uint32_t numlen = - EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - uint32_t render_width = editor->size.col - numlen; - if (editor->cursor < editor->scroll) { - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); + uint32_t render_width = this->size.col - numlen; + if (this->cursor < this->scroll) { + LineIterator *it = begin_l_iter(this->root, this->cursor.row); if (!it) return; uint32_t len; @@ -98,9 +96,9 @@ void ensure_scroll(Editor *editor) { int width = display_width(line + offset, inc); if (cols + width > render_width) { cols = 0; - if (editor->cursor.col > old_offset && editor->cursor.col <= offset) { - editor->scroll.row = editor->cursor.row; - editor->scroll.col = old_offset; + if (this->cursor.col > old_offset && this->cursor.col <= offset) { + this->scroll.row = this->cursor.row; + this->scroll.col = old_offset; free(it->buffer); free(it); return; @@ -112,14 +110,14 @@ void ensure_scroll(Editor *editor) { } free(it->buffer); free(it); - editor->scroll.row = editor->cursor.row; - editor->scroll.col = (editor->cursor.col == 0) ? 0 : old_offset; - } else if (editor->cursor.row - editor->scroll.row < editor->size.row * 2) { - uint32_t line_index = editor->scroll.row; - LineIterator *it = begin_l_iter(editor->root, line_index); + this->scroll.row = this->cursor.row; + this->scroll.col = (this->cursor.col == 0) ? 0 : old_offset; + } else if (this->cursor.row - this->scroll.row < this->size.row * 2) { + uint32_t line_index = this->scroll.row; + LineIterator *it = begin_l_iter(this->root, line_index); if (!it) return; - uint32_t max_visual_lines = editor->size.row; + uint32_t max_visual_lines = this->size.row; Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines); uint32_t q_head = 0; uint32_t q_size = 0; @@ -133,7 +131,7 @@ void ensure_scroll(Editor *editor) { line_len--; uint32_t current_byte_offset = 0; if (first_visual_line) { - current_byte_offset += editor->scroll.col; + current_byte_offset += this->scroll.col; first_visual_line = false; } while (current_byte_offset < line_len || @@ -160,16 +158,16 @@ void ensure_scroll(Editor *editor) { line_left -= cluster_len; col += width; } - if (line_index == editor->cursor.row) { + if (line_index == this->cursor.row) { bool cursor_found = false; - if (editor->cursor.col >= current_byte_offset && - editor->cursor.col < current_byte_offset + local_render_offset) + if (this->cursor.col >= current_byte_offset && + this->cursor.col < current_byte_offset + local_render_offset) cursor_found = true; - else if (editor->cursor.col == line_len && + else if (this->cursor.col == line_len && current_byte_offset + local_render_offset == line_len) cursor_found = true; if (cursor_found) { - editor->scroll = scroll_queue[q_head]; + this->scroll = scroll_queue[q_head]; free(scroll_queue); free(it->buffer); free(it); @@ -186,10 +184,10 @@ void ensure_scroll(Editor *editor) { free(it->buffer); free(it); } else { - editor->scroll.row = (editor->cursor.row > editor->size.row * 1.5) - ? editor->cursor.row - editor->size.row * 1.5 - : 0; - editor->scroll.col = 0; - ensure_scroll(editor); + this->scroll.row = (this->cursor.row > this->size.row * 1.5) + ? this->cursor.row - this->size.row * 1.5 + : 0; + this->scroll.col = 0; + this->ensure_scroll(); } } diff --git a/src/editor/boundaries.cc b/src/editor/boundaries.cc index c9182d1..dd848c9 100644 --- a/src/editor/boundaries.cc +++ b/src/editor/boundaries.cc @@ -32,12 +32,9 @@ uint32_t scan_right(const char *line, uint32_t len, uint32_t off) { return i; } -void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col, - uint32_t *next_col) { - if (!editor) - return; - std::shared_lock lock(editor->knot_mtx); - LineIterator *it = begin_l_iter(editor->root, coord.row); +void Editor::word_boundaries_exclusive(Coord coord, uint32_t *prev_col, + uint32_t *next_col) { + LineIterator *it = begin_l_iter(this->root, coord.row); if (!it) return; uint32_t line_len; @@ -85,13 +82,10 @@ uint32_t word_jump_left(const char *line, size_t len, uint32_t col) { return static_cast(last); } -void word_boundaries(Editor *editor, Coord coord, uint32_t *prev_col, - uint32_t *next_col, uint32_t *prev_clusters, - uint32_t *next_clusters) { - if (!editor) - return; - std::shared_lock lock(editor->knot_mtx); - LineIterator *it = begin_l_iter(editor->root, coord.row); +void Editor::word_boundaries(Coord coord, uint32_t *prev_col, + uint32_t *next_col, uint32_t *prev_clusters, + uint32_t *next_clusters) { + LineIterator *it = begin_l_iter(this->root, coord.row); if (!it) return; uint32_t line_len; diff --git a/src/editor/click.cc b/src/editor/click.cc index a76016d..aba4047 100644 --- a/src/editor/click.cc +++ b/src/editor/click.cc @@ -1,23 +1,22 @@ #include "editor/editor.h" #include "main.h" -Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) { +Coord Editor::click_coord(uint32_t x, uint32_t y) { if (mode == INSERT) x++; uint32_t numlen = - EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - uint32_t render_width = editor->size.col - numlen; + EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); + uint32_t render_width = this->size.col - numlen; x = MAX(x, numlen) - numlen + 1; uint32_t target_visual_row = y; uint32_t visual_row = 0; - uint32_t line_index = editor->scroll.row; - uint32_t last_line_index = editor->scroll.row; - uint32_t last_col = editor->scroll.col; + uint32_t line_index = this->scroll.row; + uint32_t last_line_index = this->scroll.row; + uint32_t last_col = this->scroll.col; bool first_visual_line = true; - std::shared_lock knot_lock(editor->knot_mtx); - LineIterator *it = begin_l_iter(editor->root, line_index); + LineIterator *it = begin_l_iter(this->root, line_index); if (!it) - return editor->scroll; + return this->scroll; while (visual_row <= target_visual_row) { uint32_t line_len; char *line = next_line(it, &line_len); @@ -27,7 +26,7 @@ Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) { line_len--; last_line_index = line_index; last_col = line_len; - uint32_t offset = first_visual_line ? editor->scroll.col : 0; + uint32_t offset = first_visual_line ? this->scroll.col : 0; first_visual_line = false; while (offset < line_len || (line_len == 0 && offset == 0)) { uint32_t col = 0; diff --git a/src/editor/commands.cc b/src/editor/commands.cc new file mode 100644 index 0000000..62984c5 --- /dev/null +++ b/src/editor/commands.cc @@ -0,0 +1,13 @@ +#include "editor/editor.h" +#include "main.h" + +void Editor::handle_command(std::string &command) { + if (command == "w") { + this->save(); + } + if (command == "wq") { + this->save(); + command = "q"; + ui::bar.handle_command(command); + } +} diff --git a/src/editor/completions.cc b/src/editor/completions.cc index ee2f065..0a613fd 100644 --- a/src/editor/completions.cc +++ b/src/editor/completions.cc @@ -1,459 +1,458 @@ -#include "editor/decl.h" -#include "editor/editor.h" -#include "io/knot.h" -#include "io/sysio.h" -#include "lsp/lsp.h" -#include "main.h" -#include "utils/utils.h" - -inline static std::string completion_prefix(Editor *editor) { - Coord hook = editor->completion.hook; - Coord cur = editor->cursor; - if (hook.row != cur.row || cur.col < hook.col) - return ""; - LineIterator *it = begin_l_iter(editor->root, hook.row); - uint32_t line_len; - char *line = next_line(it, &line_len); - if (!line) { - free(it->buffer); - free(it); - return ""; - } - std::string prefix(line + hook.col, cur.col - hook.col); - free(it->buffer); - free(it); - return prefix; -} - -inline static void completion_adjust_scroll(CompletionSession &s) { - if (s.visible.empty()) - return; - int vi = -1; - for (size_t i = 0; i < s.visible.size(); i++) - if (s.visible[i] == s.select) { - vi = (int)i; - break; - } - if (vi < 0) - return; - if ((uint32_t)vi < s.scroll) - s.scroll = vi; - else if ((uint32_t)vi >= s.scroll + 8) - s.scroll = vi - 7; -} - -void completion_filter(Editor *editor) { - auto &session = editor->completion; - std::string prefix = completion_prefix(editor); - session.visible.clear(); - for (size_t i = 0; i < session.items.size(); ++i) { - const auto &item = session.items[i]; - const std::string &key = item.filter; - if (key.size() >= prefix.size() && - key.compare(0, prefix.size(), prefix) == 0) - session.visible.push_back(i); - } - if (session.visible.empty()) { - session.box.hidden = true; - return; - } - if (std::find(session.visible.begin(), session.visible.end(), - session.select) == session.visible.end()) - session.select = session.visible[0]; - session.box.hidden = false; - session.scroll = 0; - completion_adjust_scroll(session); - session.box.render_update(); -} - -void completion_request(Editor *editor) { - Coord hook = editor->cursor; - word_boundaries(editor, editor->cursor, &hook.col, nullptr, nullptr, nullptr); - editor->completion.hook = hook; - editor->completion.complete = false; - editor->completion.active = false; - editor->completion.items.clear(); - editor->completion.visible.clear(); - editor->completion.select = 0; - editor->completion.version = editor->lsp_version; - LSPPending *pending = new LSPPending(); - pending->editor = editor; - pending->callback = [](Editor *editor, const json &message) { - auto &session = editor->completion; - std::unique_lock lock(session.mtx); - std::vector items_json; - std::vector end_chars_def; - int insert_text_format = 1; - int insert_text_mode = 1; - if (message.contains("result")) { - auto &result = message["result"]; - if (result.is_array()) { - items_json = result.get>(); - session.complete = true; - if (items_json.empty()) - return; - editor->completion.active = true; - } else if (result.is_object() && result.contains("items")) { - auto &list = result; - items_json = list["items"].get>(); - if (items_json.empty()) - return; - editor->completion.active = true; - session.complete = !list.value("isIncomplete", false); - if (list.contains("itemDefaults") && list["itemDefaults"].is_object()) { - auto &defs = list["itemDefaults"]; - if (defs.contains("insertTextFormat") && - defs["insertTextFormat"].is_number()) - insert_text_format = defs["insertTextFormat"].get(); - if (defs.contains("insertTextMode") && - defs["insertTextMode"].is_number()) - insert_text_mode = defs["insertTextMode"].get(); - if (defs.contains("textEdit")) - if (defs["textEdit"].is_array()) - for (auto &c : defs["textEdit"]) { - if (!c.is_string()) - continue; - std::string str = c.get(); - if (str.size() != 1) - continue; - end_chars_def.push_back(str[0]); - } - } - } - } - session.items.reserve(items_json.size() + 1); - session.visible.reserve(items_json.size() + 1); - for (auto &item_json : items_json) { - CompletionItem item; - item.original = item_json; - item.label = item_json.value("label", ""); - item.kind = item_json.value("kind", 0); - if (item_json.contains("detail") && item_json["detail"].is_string()) - item.detail = item_json["detail"].get(); - if (item_json.contains("documentation")) { - if (item_json["documentation"].is_string()) { - item.documentation = item_json["documentation"].get(); - } else if (item_json["documentation"].contains("value") && - item_json["documentation"]["value"].is_string()) { - item.is_markup = - item_json["documentation"]["kind"].get() == - "markdown"; - std::string documentation = - item_json["documentation"]["value"].get(); - if (documentation.size() > 1024) - item.is_markup = false; - if (item.is_markup) { - item.documentation = - substitute_fence(documentation, editor->lang.name); - } else { - item.documentation = documentation; - } - } - } - if (item_json.contains("deprecated") && - item_json["deprecated"].is_boolean()) - item.deprecated = item_json["deprecated"].get(); - auto tags = item_json.value("tags", std::vector()); - for (auto tag : tags) - if (tag == 1) - item.deprecated = true; - item.sort = item_json.value("sortText", item.label); - item.filter = item_json.value("filterText", item.label); - if (item_json.contains("preselect") && - item_json["preselect"].is_boolean() && - item_json["preselect"].get()) - session.select = session.items.size() - 1; - TextEdit edit; - if (item_json.contains("textEdit")) { - auto &te = item_json["textEdit"]; - if (te.contains("newText")) { - edit.text = te.value("newText", ""); - if (te.contains("replace")) { - edit.start.row = te["replace"]["start"]["line"]; - edit.start.col = te["replace"]["start"]["character"]; - edit.end.row = te["replace"]["end"]["line"]; - edit.end.col = te["replace"]["end"]["character"]; - } else if (te.contains("insert")) { - edit.start.row = te["insert"]["start"]["line"]; - edit.start.col = te["insert"]["start"]["character"]; - edit.end.row = te["insert"]["end"]["line"]; - edit.end.col = te["insert"]["end"]["character"]; - } else if (te.contains("range")) { - edit.start.row = te["range"]["start"]["line"]; - edit.start.col = te["range"]["start"]["character"]; - edit.end.row = te["range"]["end"]["line"]; - edit.end.col = te["range"]["end"]["character"]; - } else { - edit.start = session.hook; - edit.end = editor->cursor; - } - } - } else if (item_json.contains("insertText") && - item_json["insertText"].is_string()) { - edit.text = item_json["insertText"].get(); - edit.start = session.hook; - edit.end = editor->cursor; - } else { - edit.text = item.label; - edit.start = session.hook; - edit.end = editor->cursor; - } - utf8_normalize_edit(editor, &edit); - item.edits.push_back(edit); - if (item_json.contains("additionalTextEdits")) { - for (auto &te : item_json["additionalTextEdits"]) { - TextEdit edit; - edit.text = te.value("newText", ""); - edit.start.row = te["range"]["start"]["line"]; - edit.start.col = te["range"]["start"]["character"]; - edit.end.row = te["range"]["end"]["line"]; - edit.end.col = te["range"]["end"]["character"]; - utf8_normalize_edit(editor, &edit); - item.edits.push_back(edit); - } - } - item.snippet = insert_text_format == 2; - if (item_json.contains("insertTextFormat")) - item.snippet = item_json["insertTextFormat"].get() == 2; - if (item_json.contains("insertTextMode")) - item.asis = item_json["insertTextMode"].get() == 1; - if (item_json.contains("commitCharacters")) - for (auto &c : item_json["commitCharacters"]) - if (c.is_string() && c.get().size() == 1) - item.end_chars.push_back(c.get()[0]); - session.items.push_back(std::move(item)); - session.visible.push_back(session.items.size() - 1); - } - session.box.hidden = false; - session.box.render_update(); - }; - std::shared_lock lock(editor->knot_mtx); - LineIterator *it = begin_l_iter(editor->root, hook.row); - uint32_t length; - char *line = next_line(it, &length); - if (!line) { - free(it->buffer); - free(it); - return; - } - uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col); - free(it->buffer); - free(it); - lock.unlock(); - json message = { - {"jsonrpc", "2.0"}, - {"method", "textDocument/completion"}, - {"params", - {{"textDocument", {{"uri", editor->uri}}}, - {"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; - if (editor->completion.trigger > 0) { - json context = {{"triggerKind", editor->completion.trigger}}; - if (editor->completion.trigger == 2 && editor->completion.trigger_char) - context["triggerCharacter"] = - std::string(1, *editor->completion.trigger_char); - message["params"]["context"] = context; - } - lsp_send(editor->lsp, message, pending); -} - -void handle_completion(Editor *editor, KeyEvent event) { - if (!editor->lsp || !editor->lsp->allow_completion) - return; - if (mode != INSERT) { - editor->completion.active = false; - return; - } - std::unique_lock lock(editor->completion.mtx); - if (event.key_type == KEY_PASTE) { - editor->completion.active = false; - return; - } else if (event.key_type == KEY_CHAR) { - char ch = *event.c; - if (!editor->completion.active) { - for (char c : editor->lsp->trigger_chars) - if (c == ch) { - editor->completion.trigger = 2; - editor->completion.trigger_char = c; - completion_request(editor); - return; - } - } else { - if (!editor->completion.items.empty()) { - const auto &item = editor->completion.items[editor->completion.select]; - const std::vector &end_chars = - item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars; - for (char c : end_chars) - if (c == ch) { - complete_accept(editor); - return; - } - } - } - if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9') || ch == '_') { - if (editor->completion.active) { - if (editor->completion.complete) - completion_filter(editor); - else - completion_request(editor); - } else { - editor->completion.trigger = 3; - completion_request(editor); - } - } else if (ch == CTRL('\\')) { - if (editor->completion.active && !editor->completion.visible.empty()) { - complete_accept(editor); - } else { - editor->completion.trigger = 1; - completion_request(editor); - } - } else if (ch == CTRL('p')) { - if (editor->completion.active) - complete_next(editor); - } else if (ch == CTRL('o')) { - if (editor->completion.active) - complete_prev(editor); - } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) { - if (editor->completion.active) { - if (editor->completion.complete) { - if (editor->cursor <= editor->completion.hook) - editor->completion.active = false; - else - completion_filter(editor); - } else { - if (editor->cursor <= editor->completion.hook) - editor->completion.active = false; - else - completion_request(editor); - } - } - } else { - editor->completion.active = false; - } - } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) { - // Prolly remove mouse support here - // auto &box = editor->completion.box; - // if (event.mouse_y >= box.position.row && - // event.mouse_x >= box.position.col) { - // uint32_t row = event.mouse_y - box.position.row; - // uint32_t col = event.mouse_x - box.position.col; - // if (row < box.size.row && col < box.size.col) { - // uint8_t idx = 0; - // /* TODO: fix index relative to scroll */ - // complete_select(editor, idx); - // } - // } - // if it is being implemented then stop main event handler from processing - // when click inside the box - editor->completion.active = false; - } else { - editor->completion.active = false; - } -} - -void completion_resolve_doc(Editor *editor) { - auto &item = editor->completion.items[editor->completion.select]; - if (item.documentation) - return; - item.documentation = ""; - LSPPending *pending = new LSPPending(); - pending->editor = editor; - pending->callback = [](Editor *editor, const json &message) { - std::unique_lock lock(editor->completion.mtx); - auto &item = editor->completion.items[editor->completion.select]; - if (message["result"].contains("documentation")) { - if (message["result"]["documentation"].is_string()) { - item.documentation = - message["result"]["documentation"].get(); - } else if (message["result"]["documentation"].contains("value") && - message["result"]["documentation"]["value"].is_string()) { - item.is_markup = - message["result"]["documentation"]["kind"].get() == - "markdown"; - std::string documentation = - message["result"]["documentation"]["value"].get(); - if (documentation.size() > 1024) - item.is_markup = false; - if (item.is_markup) { - item.documentation = - substitute_fence(documentation, editor->lang.name); - } else { - item.documentation = documentation; - } - } - } - editor->completion.box.render_update(); - }; - json message = {{"jsonrpc", "2.0"}, - {"method", "completionItem/resolve"}, - {"params", item.original}}; - lsp_send(editor->lsp, message, pending); -} - -void complete_accept(Editor *editor) { - if (!editor->completion.active || editor->completion.box.hidden) - return; - auto &item = editor->completion.items[editor->completion.select]; - // TODO: support snippets and asis here - // once indentation engine is implemented - if (editor->completion.version != editor->lsp_version) { - int delta_col = 0; - TextEdit &e = item.edits[0]; - if (e.end.row == editor->cursor.row) { - delta_col = editor->cursor.col - e.end.col; - e.end.col = editor->cursor.col; - for (size_t i = 1; i < item.edits.size(); ++i) { - TextEdit &e = item.edits[i]; - if (e.start.row == editor->cursor.row) { - e.start.col += delta_col; - if (e.end.row == editor->cursor.row) - e.end.col += delta_col; - } - } - } - } - apply_lsp_edits(editor, item.edits, true); - editor->completion.active = false; -} - -inline static int visible_index(const CompletionSession &s) { - for (size_t i = 0; i < s.visible.size(); ++i) - if (s.visible[i] == s.select) - return (int)i; - return -1; -} - -void complete_next(Editor *editor) { - auto &s = editor->completion; - if (!s.active || s.box.hidden || s.visible.empty()) - return; - int vi = visible_index(s); - if (vi < 0) - vi = 0; - else - vi = (vi + 1) % s.visible.size(); - s.select = s.visible[vi]; - completion_resolve_doc(editor); - completion_adjust_scroll(editor->completion); - editor->completion.box.render_update(); -} - -void complete_prev(Editor *editor) { - auto &s = editor->completion; - if (!s.active || s.box.hidden || s.visible.empty()) - return; - int vi = visible_index(s); - if (vi < 0) - vi = 0; - else - vi = (vi + s.visible.size() - 1) % s.visible.size(); - s.select = s.visible[vi]; - completion_resolve_doc(editor); - completion_adjust_scroll(editor->completion); - editor->completion.box.render_update(); -} - -void complete_select(Editor *editor, uint8_t index) { - editor->completion.select = index; - complete_accept(editor); -} +// #include "editor/decl.h" +// #include "editor/editor.h" +// #include "io/knot.h" +// #include "io/sysio.h" +// #include "lsp/lsp.h" +// #include "main.h" +// #include "ui/completionbox.h" +// #include "utils/utils.h" +// +// inline static std::string completion_prefix(Editor *editor) { +// Coord hook = editor->completion.hook; +// Coord cur = editor->cursor; +// if (hook.row != cur.row || cur.col < hook.col) +// return ""; +// LineIterator *it = begin_l_iter(editor->root, hook.row); +// uint32_t line_len; +// char *line = next_line(it, &line_len); +// if (!line) { +// free(it->buffer); +// free(it); +// return ""; +// } +// std::string prefix(line + hook.col, cur.col - hook.col); +// free(it->buffer); +// free(it); +// return prefix; +// } +// +// inline static void completion_adjust_scroll(CompletionSession &s) { +// if (s.visible.empty()) +// return; +// int vi = -1; +// for (size_t i = 0; i < s.visible.size(); i++) +// if (s.visible[i] == s.select) { +// vi = (int)i; +// break; +// } +// if (vi < 0) +// return; +// if ((uint32_t)vi < s.scroll) +// s.scroll = vi; +// else if ((uint32_t)vi >= s.scroll + 8) +// s.scroll = vi - 7; +// } +// +// void completion_filter(Editor *editor) { +// auto &session = editor->completion; +// std::string prefix = completion_prefix(editor); +// session.visible.clear(); +// for (size_t i = 0; i < session.items.size(); ++i) { +// const auto &item = session.items[i]; +// const std::string &key = item.filter; +// if (key.size() >= prefix.size() && +// key.compare(0, prefix.size(), prefix) == 0) +// session.visible.push_back(i); +// } +// if (session.visible.empty()) { +// session.box.hidden = true; +// return; +// } +// if (std::find(session.visible.begin(), session.visible.end(), +// session.select) == session.visible.end()) +// session.select = session.visible[0]; +// session.box.hidden = false; +// session.scroll = 0; +// completion_adjust_scroll(session); +// session.box.render_update(); +// } +// +// void completion_request(Editor *editor) { +// Coord hook = editor->cursor; +// editor->word_boundaries(editor->cursor, &hook.col, nullptr, nullptr, +// nullptr); editor->completion.hook = hook; editor->completion.complete = +// false; editor->completion.active = false; editor->completion.items.clear(); +// editor->completion.visible.clear(); +// editor->completion.select = 0; +// editor->completion.version = editor->lsp_version; +// LSPPending *pending = new LSPPending(); +// pending->editor = editor; +// pending->callback = [](Editor *editor, const json &message) { +// auto &session = editor->completion; +// std::unique_lock lock(session.mtx); +// std::vector items_json; +// std::vector end_chars_def; +// int insert_text_format = 1; +// int insert_text_mode = 1; +// if (message.contains("result")) { +// auto &result = message["result"]; +// if (result.is_array()) { +// items_json = result.get>(); +// session.complete = true; +// if (items_json.empty()) +// return; +// editor->completion.active = true; +// } else if (result.is_object() && result.contains("items")) { +// auto &list = result; +// items_json = list["items"].get>(); +// if (items_json.empty()) +// return; +// editor->completion.active = true; +// session.complete = !list.value("isIncomplete", false); +// if (list.contains("itemDefaults") && +// list["itemDefaults"].is_object()) { +// auto &defs = list["itemDefaults"]; +// if (defs.contains("insertTextFormat") && +// defs["insertTextFormat"].is_number()) +// insert_text_format = defs["insertTextFormat"].get(); +// if (defs.contains("insertTextMode") && +// defs["insertTextMode"].is_number()) +// insert_text_mode = defs["insertTextMode"].get(); +// if (defs.contains("textEdit")) +// if (defs["textEdit"].is_array()) +// for (auto &c : defs["textEdit"]) { +// if (!c.is_string()) +// continue; +// std::string str = c.get(); +// if (str.size() != 1) +// continue; +// end_chars_def.push_back(str[0]); +// } +// } +// } +// } +// session.items.reserve(items_json.size() + 1); +// session.visible.reserve(items_json.size() + 1); +// for (auto &item_json : items_json) { +// CompletionItem item; +// item.original = item_json; +// item.label = item_json.value("label", ""); +// item.kind = item_json.value("kind", 0); +// if (item_json.contains("detail") && item_json["detail"].is_string()) +// item.detail = item_json["detail"].get(); +// if (item_json.contains("documentation")) { +// if (item_json["documentation"].is_string()) { +// item.documentation = item_json["documentation"].get(); +// } else if (item_json["documentation"].contains("value") && +// item_json["documentation"]["value"].is_string()) { +// item.is_markup = +// item_json["documentation"]["kind"].get() == +// "markdown"; +// std::string documentation = +// item_json["documentation"]["value"].get(); +// if (documentation.size() > 1024) +// item.is_markup = false; +// if (item.is_markup) { +// item.documentation = +// substitute_fence(documentation, editor->lang.name); +// } else { +// item.documentation = documentation; +// } +// } +// } +// if (item_json.contains("deprecated") && +// item_json["deprecated"].is_boolean()) +// item.deprecated = item_json["deprecated"].get(); +// auto tags = item_json.value("tags", std::vector()); +// for (auto tag : tags) +// if (tag == 1) +// item.deprecated = true; +// item.sort = item_json.value("sortText", item.label); +// item.filter = item_json.value("filterText", item.label); +// if (item_json.contains("preselect") && +// item_json["preselect"].is_boolean() && +// item_json["preselect"].get()) +// session.select = session.items.size() - 1; +// TextEdit edit; +// if (item_json.contains("textEdit")) { +// auto &te = item_json["textEdit"]; +// if (te.contains("newText")) { +// edit.text = te.value("newText", ""); +// if (te.contains("replace")) { +// edit.start.row = te["replace"]["start"]["line"]; +// edit.start.col = te["replace"]["start"]["character"]; +// edit.end.row = te["replace"]["end"]["line"]; +// edit.end.col = te["replace"]["end"]["character"]; +// } else if (te.contains("insert")) { +// edit.start.row = te["insert"]["start"]["line"]; +// edit.start.col = te["insert"]["start"]["character"]; +// edit.end.row = te["insert"]["end"]["line"]; +// edit.end.col = te["insert"]["end"]["character"]; +// } else if (te.contains("range")) { +// edit.start.row = te["range"]["start"]["line"]; +// edit.start.col = te["range"]["start"]["character"]; +// edit.end.row = te["range"]["end"]["line"]; +// edit.end.col = te["range"]["end"]["character"]; +// } else { +// edit.start = session.hook; +// edit.end = editor->cursor; +// } +// } +// } else if (item_json.contains("insertText") && +// item_json["insertText"].is_string()) { +// edit.text = item_json["insertText"].get(); +// edit.start = session.hook; +// edit.end = editor->cursor; +// } else { +// edit.text = item.label; +// edit.start = session.hook; +// edit.end = editor->cursor; +// } +// editor->utf8_normalize_edit(&edit); +// item.edits.push_back(edit); +// if (item_json.contains("additionalTextEdits")) { +// for (auto &te : item_json["additionalTextEdits"]) { +// TextEdit edit; +// edit.text = te.value("newText", ""); +// edit.start.row = te["range"]["start"]["line"]; +// edit.start.col = te["range"]["start"]["character"]; +// edit.end.row = te["range"]["end"]["line"]; +// edit.end.col = te["range"]["end"]["character"]; +// editor->utf8_normalize_edit(&edit); +// item.edits.push_back(edit); +// } +// } +// item.snippet = insert_text_format == 2; +// if (item_json.contains("insertTextFormat")) +// item.snippet = item_json["insertTextFormat"].get() == 2; +// if (item_json.contains("insertTextMode")) +// item.asis = item_json["insertTextMode"].get() == 1; +// if (item_json.contains("commitCharacters")) +// for (auto &c : item_json["commitCharacters"]) +// if (c.is_string() && c.get().size() == 1) +// item.end_chars.push_back(c.get()[0]); +// session.items.push_back(std::move(item)); +// session.visible.push_back(session.items.size() - 1); +// } +// session.box.hidden = false; +// session.box.render_update(); +// }; +// LineIterator *it = begin_l_iter(editor->root, hook.row); +// uint32_t length; +// char *line = next_line(it, &length); +// if (!line) { +// free(it->buffer); +// free(it); +// return; +// } +// uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col); +// free(it->buffer); +// free(it); +// json message = { +// {"jsonrpc", "2.0"}, +// {"method", "textDocument/completion"}, +// {"params", +// {{"textDocument", {{"uri", editor->uri}}}, +// {"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; +// if (editor->completion.trigger > 0) { +// json context = {{"triggerKind", editor->completion.trigger}}; +// if (editor->completion.trigger == 2 && editor->completion.trigger_char) +// context["triggerCharacter"] = +// std::string(1, *editor->completion.trigger_char); +// message["params"]["context"] = context; +// } +// lsp_send(editor->lsp, message, pending); +// } +// +// // Move this into the box and merge the box and this guy +// void CompletionSession::handle(KeyEvent event) { +// if (!editor->lsp || !editor->lsp->allow_completion) +// return; +// if (mode != INSERT) { +// this->active = false; +// return; +// } +// std::unique_lock lock(this->mtx); +// if (event.key_type == KEY_PASTE) { +// this->active = false; +// return; +// } else if (event.key_type == KEY_CHAR) { +// char ch = *event.c; +// if (!this->active) { +// for (char c : editor->lsp->trigger_chars) +// if (c == ch) { +// this->trigger = 2; +// this->trigger_char = c; +// completion_request(editor); +// return; +// } +// } else { +// if (!editor->completion.items.empty()) { +// const auto &item = +// editor->completion.items[editor->completion.select]; const +// std::vector &end_chars = +// item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars; +// for (char c : end_chars) +// if (c == ch) { +// this->accept(); +// return; +// } +// } +// } +// if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || +// (ch >= '0' && ch <= '9') || ch == '_') { +// if (this->active) { +// if (this->complete) +// completion_filter(editor); +// else +// completion_request(editor); +// } else { +// this->trigger = 3; +// completion_request(editor); +// } +// } else if (ch == CTRL('\\')) { +// if (this->active && !this->visible.empty()) { +// this->accept(); +// } else { +// this->trigger = 1; +// completion_request(editor); +// } +// } else if (ch == CTRL('p')) { +// if (this->active) +// this->next(); +// } else if (ch == CTRL('o')) { +// if (this->active) +// this->prev(); +// } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) { +// if (this->active) { +// if (this->complete) { +// if (editor->cursor <= this->hook) +// this->active = false; +// else +// completion_filter(editor); +// } else { +// if (editor->cursor <= this->hook) +// this->active = false; +// else +// completion_request(editor); +// } +// } +// } else { +// this->active = false; +// } +// } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) { +// // Prolly add mouse support here +// // auto &box = this->box; +// // if (event.mouse_y >= box.position.row && +// // event.mouse_x >= box.position.col) { +// // uint32_t row = event.mouse_y - box.position.row; +// // uint32_t col = event.mouse_x - box.position.col; +// // if (row < box.size.row && col < box.size.col) { +// // uint8_t idx = 0; +// // /* TODO: fix index relative to scroll */ +// // complete_select(editor, idx); +// // } +// // } +// // if it is being implemented then stop main event handler from +// processing +// // when click inside the box +// this->active = false; +// } else { +// this->active = false; +// } +// } +// +// void CompletionSession::resolve_doc() { +// auto &item = this->items[this->select]; +// if (item.documentation) +// return; +// item.documentation = ""; +// LSPPending *pending = new LSPPending(); +// pending->editor = editor; +// pending->callback = [](Editor *editor, const json &message) { +// std::unique_lock lock(editor->completion.mtx); +// auto &item = editor->completion.items[editor->completion.select]; +// if (message["result"].contains("documentation")) { +// if (message["result"]["documentation"].is_string()) { +// item.documentation = +// message["result"]["documentation"].get(); +// } else if (message["result"]["documentation"].contains("value") && +// message["result"]["documentation"]["value"].is_string()) { +// item.is_markup = +// message["result"]["documentation"]["kind"].get() == +// "markdown"; +// std::string documentation = +// message["result"]["documentation"]["value"].get(); +// if (documentation.size() > 1024) +// item.is_markup = false; +// if (item.is_markup) { +// item.documentation = +// substitute_fence(documentation, editor->lang.name); +// } else { +// item.documentation = documentation; +// } +// } +// } +// editor->completion.box.render_update(); +// }; +// json message = {{"jsonrpc", "2.0"}, +// {"method", "completionItem/resolve"}, +// {"params", item.original}}; +// lsp_send(editor->lsp, message, pending); +// } +// +// void CompletionSession::accept() { +// if (!this->active || this->box.hidden) +// return; +// auto &item = this->items[this->select]; +// // TODO: support snippets and asis here +// // once indentation engine is implemented +// if (this->version != editor->lsp_version) { +// int delta_col = 0; +// TextEdit &e = item.edits[0]; +// if (e.end.row == editor->cursor.row) { +// delta_col = editor->cursor.col - e.end.col; +// e.end.col = editor->cursor.col; +// for (size_t i = 1; i < item.edits.size(); ++i) { +// TextEdit &e = item.edits[i]; +// if (e.start.row == editor->cursor.row) { +// e.start.col += delta_col; +// if (e.end.row == editor->cursor.row) +// e.end.col += delta_col; +// } +// } +// } +// } +// editor->apply_lsp_edits(item.edits, true); +// this->active = false; +// } +// +// inline static int visible_index(const CompletionSession &s) { +// for (size_t i = 0; i < s.visible.size(); ++i) +// if (s.visible[i] == s.select) +// return (int)i; +// return -1; +// } +// +// void CompletionSession::next() { +// if (!this->active || this->box.hidden || this->visible.empty()) +// return; +// int vi = visible_index(*this); +// if (vi < 0) +// vi = 0; +// else +// vi = (vi + 1) % this->visible.size(); +// this->select = this->visible[vi]; +// this->resolve_doc(); +// completion_adjust_scroll(*this); +// this->box.render_update(); +// } +// +// void CompletionSession::prev() { +// if (!this->active || this->box.hidden || this->visible.empty()) +// return; +// int vi = visible_index(*this); +// if (vi < 0) +// vi = 0; +// else +// vi = (vi + this->visible.size() - 1) % this->visible.size(); +// this->select = this->visible[vi]; +// this->resolve_doc(); +// completion_adjust_scroll(*this); +// this->box.render_update(); +// } +// +// void CompletionSession::choose(uint8_t index) { +// this->select = index; +// this->accept(); +// } diff --git a/src/editor/cursor.cc b/src/editor/cursor.cc index 90b7ed2..e7b0398 100644 --- a/src/editor/cursor.cc +++ b/src/editor/cursor.cc @@ -1,14 +1,14 @@ #include "editor/editor.h" #include "utils/utils.h" -Coord move_right(Editor *editor, Coord cursor, uint32_t number) { +Coord Editor::move_right(Coord cursor, uint32_t number) { Coord result = cursor; - if (!editor || !editor->root || number == 0) + if (!this->root || number == 0) return result; uint32_t row = result.row; uint32_t col = result.col; uint32_t line_len = 0; - LineIterator *it = begin_l_iter(editor->root, row); + LineIterator *it = begin_l_iter(this->root, row); if (!it) return result; char *line = next_line(it, &line_len); @@ -22,7 +22,7 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) { while (number > 0) { if (col >= line_len) { uint32_t next_row = row + 1; - if (next_row >= editor->root->line_count) { + if (next_row >= this->root->line_count) { col = line_len; break; } @@ -49,14 +49,16 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) { return result; } -Coord move_left(Editor *editor, Coord cursor, uint32_t number) { +Coord Editor::move_left(Coord cursor, uint32_t number) { Coord result = cursor; - if (!editor || !editor->root || number == 0) + if (!this->root || number == 0) return result; uint32_t row = result.row; uint32_t col = result.col; uint32_t len = 0; - LineIterator *it = begin_l_iter(editor->root, row); + LineIterator *it = begin_l_iter(this->root, row); + if (!it) + return result; char *line = next_line(it, &len); if (!line) { free(it->buffer); @@ -102,29 +104,28 @@ Coord move_left(Editor *editor, Coord cursor, uint32_t number) { return result; } -void cursor_down(Editor *editor, uint32_t number) { - if (!editor || !editor->root || number == 0) +void Editor::cursor_down(uint32_t number) { + if (!this->root || number == 0) return; - uint32_t visual_col = editor->cursor_preffered; + uint32_t visual_col = this->cursor_preffered; if (visual_col == UINT32_MAX) { uint32_t len; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); char *line = next_line(it, &len); if (!line) { free(it->buffer); free(it); return; } - editor->cursor_preffered = - get_visual_col_from_bytes(line, len, editor->cursor.col); - visual_col = editor->cursor_preffered; + this->cursor_preffered = + get_visual_col_from_bytes(line, len, this->cursor.col); + visual_col = this->cursor_preffered; free(it->buffer); free(it); } - editor->cursor.row = - MIN(editor->cursor.row + number, editor->root->line_count - 1); + this->cursor.row = MIN(this->cursor.row + number, this->root->line_count - 1); uint32_t len; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); char *line = next_line(it, &len); if (!line) { free(it->buffer); @@ -133,26 +134,26 @@ void cursor_down(Editor *editor, uint32_t number) { } if (len > 0 && line[len - 1] == '\n') --len; - editor->cursor.col = get_bytes_from_visual_col(line, len, visual_col); + this->cursor.col = get_bytes_from_visual_col(line, len, visual_col); free(it->buffer); free(it); } -void cursor_up(Editor *editor, uint32_t number) { - if (!editor || !editor->root || number == 0 || editor->cursor.row == 0) +void Editor::cursor_up(uint32_t number) { + if (!this->root || number == 0 || this->cursor.row == 0) return; uint32_t len; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); char *line_content = next_line(it, &len); if (!line_content) return; - if (editor->cursor_preffered == UINT32_MAX) - editor->cursor_preffered = - get_visual_col_from_bytes(line_content, len, editor->cursor.col); - uint32_t visual_col = editor->cursor_preffered; + if (this->cursor_preffered == UINT32_MAX) + this->cursor_preffered = + get_visual_col_from_bytes(line_content, len, this->cursor.col); + uint32_t visual_col = this->cursor_preffered; free(it->buffer); free(it); - uint32_t target_row = editor->cursor.row; + uint32_t target_row = this->cursor.row; while (number > 0 && target_row > 0) { target_row--; if (target_row == 0) { @@ -161,32 +162,31 @@ void cursor_up(Editor *editor, uint32_t number) { } number--; } - it = begin_l_iter(editor->root, target_row); + it = begin_l_iter(this->root, target_row); line_content = next_line(it, &len); if (line_content) { if (len > 0 && line_content[len - 1] == '\n') --len; - editor->cursor.row = target_row; - editor->cursor.col = - get_bytes_from_visual_col(line_content, len, visual_col); + this->cursor.row = target_row; + this->cursor.col = get_bytes_from_visual_col(line_content, len, visual_col); } else { - editor->cursor.row = 0; - editor->cursor.col = 0; + this->cursor.row = 0; + this->cursor.col = 0; } free(it->buffer); free(it); } -void cursor_right(Editor *editor, uint32_t number) { - if (!editor || !editor->root || number == 0) +void Editor::cursor_right(uint32_t number) { + if (!this->root || number == 0) return; - editor->cursor = move_right(editor, editor->cursor, number); - editor->cursor_preffered = UINT32_MAX; + this->cursor = this->move_right(this->cursor, number); + this->cursor_preffered = UINT32_MAX; } -void cursor_left(Editor *editor, uint32_t number) { - if (!editor || !editor->root || number == 0) +void Editor::cursor_left(uint32_t number) { + if (!this->root || number == 0) return; - editor->cursor = move_left(editor, editor->cursor, number); - editor->cursor_preffered = UINT32_MAX; + this->cursor = this->move_left(this->cursor, number); + this->cursor_preffered = UINT32_MAX; } diff --git a/src/editor/edit.cc b/src/editor/edit.cc index 45c1d8d..cda9876 100644 --- a/src/editor/edit.cc +++ b/src/editor/edit.cc @@ -2,20 +2,18 @@ #include "lsp/lsp.h" #include "utils/utils.h" -void edit_erase(Editor *editor, Coord pos, int64_t len) { +void Editor::edit_erase(Coord pos, int64_t len) { if (len == 0) return; if (len < 0) { - std::shared_lock lock_1(editor->knot_mtx); uint32_t cursor_original = - line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; - Coord point = move_left(editor, pos, -len); + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col; + uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col; + Coord point = this->move_left(pos, -len); json lsp_range; - bool do_lsp = (editor->lsp != nullptr); + bool do_lsp = (this->lsp != nullptr); if (do_lsp) { - LineIterator *it = begin_l_iter(editor->root, point.row); + LineIterator *it = begin_l_iter(this->root, point.row); uint32_t len; char *line = next_line(it, &len); int utf16_start = 0; @@ -23,7 +21,7 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { utf16_start = utf8_offset_to_utf16(line, len, point.col); free(it->buffer); free(it); - it = begin_l_iter(editor->root, pos.row); + it = begin_l_iter(this->root, pos.row); line = next_line(it, &len); int utf16_end = 0; if (line) @@ -33,62 +31,58 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { lsp_range = {{"start", {{"line", point.row}, {"character", utf16_start}}}, {"end", {{"line", pos.row}, {"character", utf16_end}}}}; } - uint32_t start = line_to_byte(editor->root, point.row, nullptr) + point.col; + uint32_t start = line_to_byte(this->root, point.row, nullptr) + point.col; if (cursor_original > start && cursor_original <= byte_pos) { - editor->cursor = point; - editor->cursor_preffered = UINT32_MAX; + this->cursor = point; + this->cursor_preffered = UINT32_MAX; } else if (cursor_original > byte_pos) { uint32_t cursor_new = cursor_original - (byte_pos - start); uint32_t new_col; - uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); - editor->cursor = {new_row, new_col}; - editor->cursor_preffered = UINT32_MAX; + uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col); + this->cursor = {new_row, new_col}; + this->cursor_preffered = UINT32_MAX; } - lock_1.unlock(); uint32_t start_row = point.row; uint32_t end_row = pos.row; - apply_hook_deletion(editor, start_row + 1, end_row); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = erase(editor->root, start, byte_pos - start); - lock_2.unlock(); - if (editor->parser) - editor->parser->edit(start_row, end_row - start_row, 0); + this->apply_hook_deletion(start_row + 1, end_row); + this->root = erase(this->root, start, byte_pos - start); + if (this->parser) + this->parser->edit(start_row, end_row - start_row, 0); if (do_lsp) { - if (editor->lsp->incremental_sync) { - json message = { - {"jsonrpc", "2.0"}, + auto lsp = this->lsp.load(); + if (lsp->incremental_sync) { + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array({{{"range", lsp_range}, {"text", ""}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } else { - char *buf = read(editor->root, 0, editor->root->char_count); + char *buf = read(this->root, 0, this->root->char_count); std::string text(buf); free(buf); - json message = { - {"jsonrpc", "2.0"}, + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array({{{"text", text}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } } } else { - std::shared_lock lock_1(editor->knot_mtx); uint32_t cursor_original = - line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; - Coord point = move_right(editor, pos, len); + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col; + uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col; + Coord point = this->move_right(pos, len); json lsp_range; - bool do_lsp = (editor->lsp != nullptr); + bool do_lsp = (this->lsp != nullptr); if (do_lsp) { - LineIterator *it = begin_l_iter(editor->root, pos.row); + LineIterator *it = begin_l_iter(this->root, pos.row); uint32_t line_len; char *line = next_line(it, &line_len); int utf16_start = 0; @@ -96,7 +90,7 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { utf16_start = utf8_offset_to_utf16(line, line_len, pos.col); free(it->buffer); free(it); - it = begin_l_iter(editor->root, point.row); + it = begin_l_iter(this->root, point.row); line = next_line(it, &line_len); int utf16_end = 0; if (line) @@ -106,67 +100,63 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { lsp_range = {{"start", {{"line", pos.row}, {"character", utf16_start}}}, {"end", {{"line", point.row}, {"character", utf16_end}}}}; } - uint32_t end = line_to_byte(editor->root, point.row, nullptr) + point.col; + uint32_t end = line_to_byte(this->root, point.row, nullptr) + point.col; if (cursor_original > byte_pos && cursor_original <= end) { - editor->cursor = pos; - editor->cursor_preffered = UINT32_MAX; + this->cursor = pos; + this->cursor_preffered = UINT32_MAX; } else if (cursor_original > end) { uint32_t cursor_new = cursor_original - (end - byte_pos); uint32_t new_col; - uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); - editor->cursor = {new_row, new_col}; - editor->cursor_preffered = UINT32_MAX; + uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col); + this->cursor = {new_row, new_col}; + this->cursor_preffered = UINT32_MAX; } - lock_1.unlock(); uint32_t start_row = pos.row; uint32_t end_row = point.row; - apply_hook_deletion(editor, start_row + 1, end_row); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = erase(editor->root, byte_pos, end - byte_pos); - lock_2.unlock(); - if (editor->parser) - editor->parser->edit(start_row, end_row - start_row, 0); + this->apply_hook_deletion(start_row + 1, end_row); + this->root = erase(this->root, byte_pos, end - byte_pos); + if (this->parser) + this->parser->edit(start_row, end_row - start_row, 0); if (do_lsp) { - if (editor->lsp->incremental_sync) { - json message = { - {"jsonrpc", "2.0"}, + auto lsp = this->lsp.load(); + if (lsp->incremental_sync) { + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array({{{"range", lsp_range}, {"text", ""}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } else { - char *buf = read(editor->root, 0, editor->root->char_count); + char *buf = read(this->root, 0, this->root->char_count); std::string text(buf); free(buf); - json message = { - {"jsonrpc", "2.0"}, + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array({{{"text", text}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } } } } -void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) { - std::shared_lock lock_1(editor->knot_mtx); +void Editor::edit_insert(Coord pos, char *data, uint32_t len) { uint32_t cursor_original = - line_to_byte(editor->root, editor->cursor.row, nullptr) + - editor->cursor.col; - uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col; + uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col; if (cursor_original > byte_pos) { uint32_t cursor_new = cursor_original + len; uint32_t new_col; - uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); - editor->cursor = {new_row, new_col}; + uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col); + this->cursor = {new_row, new_col}; } - LineIterator *it = begin_l_iter(editor->root, pos.row); + LineIterator *it = begin_l_iter(this->root, pos.row); uint32_t line_len; char *line = next_line(it, &line_len); int utf16_col = 0; @@ -174,55 +164,52 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) { utf16_col = utf8_offset_to_utf16(line, line_len, pos.col); free(it->buffer); free(it); - lock_1.unlock(); - std::unique_lock lock_2(editor->knot_mtx); - editor->root = insert(editor->root, byte_pos, data, len); + this->root = insert(this->root, byte_pos, data, len); uint32_t rows = 0; for (uint32_t i = 0; i < len; i++) if (data[i] == '\n') rows++; - apply_hook_insertion(editor, pos.row, rows); - lock_2.unlock(); - if (editor->parser) - editor->parser->edit(pos.row, 0, rows); - if (editor->lsp) { - if (editor->lsp->incremental_sync) { - json message = { - {"jsonrpc", "2.0"}, + this->apply_hook_insertion(pos.row, rows); + if (this->parser) + this->parser->edit(pos.row, 0, rows); + auto lsp = this->lsp.load(); + if (lsp) { + if (lsp->incremental_sync) { + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array( {{{"range", {{"start", {{"line", pos.row}, {"character", utf16_col}}}, {"end", {{"line", pos.row}, {"character", utf16_col}}}}}, {"text", std::string(data, len)}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } else { - char *buf = read(editor->root, 0, editor->root->char_count); + char *buf = read(this->root, 0, this->root->char_count); std::string text(buf); free(buf); - json message = { - {"jsonrpc", "2.0"}, + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array({{{"text", text}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } } } -void edit_replace(Editor *editor, Coord start, Coord end, const char *text, - uint32_t len) { - std::unique_lock lock(editor->knot_mtx); +void Editor::edit_replace(Coord start, Coord end, const char *text, + uint32_t len) { uint32_t start_byte = - line_to_byte(editor->root, start.row, nullptr) + start.col; - uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col; - LineIterator *it = begin_l_iter(editor->root, start.row); + line_to_byte(this->root, start.row, nullptr) + start.col; + uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col; + LineIterator *it = begin_l_iter(this->root, start.row); uint32_t line_len; char *line = next_line(it, &line_len); int utf16_start = 0; @@ -230,7 +217,7 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text, utf16_start = utf8_offset_to_utf16(line, line_len, start.col); free(it->buffer); free(it); - it = begin_l_iter(editor->root, end.row); + it = begin_l_iter(this->root, end.row); line = next_line(it, &line_len); int utf16_end = 0; if (line) @@ -238,25 +225,26 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text, free(it->buffer); free(it); if (start_byte != end_byte) - editor->root = erase(editor->root, start_byte, end_byte - start_byte); + this->root = erase(this->root, start_byte, end_byte - start_byte); if (len > 0) - editor->root = insert(editor->root, start_byte, (char *)text, len); + this->root = insert(this->root, start_byte, (char *)text, len); uint32_t rows = 0; for (uint32_t i = 0; i < len; i++) if (text[i] == '\n') rows++; if (rows > 0) rows--; - if (editor->parser) - editor->parser->edit(start.row, end.row - start.row, rows); - if (editor->lsp) { - if (editor->lsp->incremental_sync) { - json message = { - {"jsonrpc", "2.0"}, + if (this->parser) + this->parser->edit(start.row, end.row - start.row, rows); + auto lsp = this->lsp.load(); + if (lsp) { + if (lsp->incremental_sync) { + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array( {{{"range", @@ -264,19 +252,19 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text, {{"line", start.row}, {"character", utf16_start}}}, {"end", {{"line", end.row}, {"character", utf16_end}}}}}, {"text", std::string(text, len)}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } else { - char *buf = read(editor->root, 0, editor->root->char_count); + char *buf = read(this->root, 0, this->root->char_count); std::string full_text(buf); free(buf); - json message = { - {"jsonrpc", "2.0"}, + auto message = std::make_unique(); + message->message = { {"method", "textDocument/didChange"}, {"params", {{"textDocument", - {{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, + {{"uri", this->uri}, {"version", ++this->lsp_version}}}, {"contentChanges", json::array({{{"text", full_text}}})}}}}; - lsp_send(editor->lsp, message, nullptr); + lsp->send(std::move(message)); } } } diff --git a/src/editor/editor.cc b/src/editor/editor.cc index ea09f53..e6e069e 100644 --- a/src/editor/editor.cc +++ b/src/editor/editor.cc @@ -5,69 +5,66 @@ #include "syntax/langs.h" #include "utils/utils.h" -Editor *new_editor(const char *filename_arg, Coord position, Coord size, - uint8_t eol) { - Editor *editor = new Editor(); - if (!editor) - return nullptr; +Editor::Editor(const char *filename_arg, uint8_t eol) { uint32_t len = 0; std::string filename = path_abs(filename_arg); - editor->unix_eol = eol & 1; - char *str = load_file(filename.c_str(), &len, &editor->unix_eol); + this->unix_eol = eol & 1; + char *str = load_file(filename.c_str(), &len, &this->unix_eol); if (!str) { str = (char *)malloc(1); *str = '\n'; len = 1; } if ((eol >> 1) & 1) - editor->unix_eol = eol & 1; - editor->filename = filename; - editor->uri = path_to_file_uri(filename); - editor->position = position; - editor->size = size; - editor->cursor_preffered = UINT32_MAX; + this->unix_eol = eol & 1; + this->filename = filename; + this->uri = path_to_file_uri(filename); + this->cursor_preffered = UINT32_MAX; if (len == 0) { free(str); str = (char *)malloc(1); *str = '\n'; len = 1; } - editor->root = load(str, len, optimal_chunk_size(len)); + this->scroll = {0, 0}; + this->cursor = {0, 0}; + this->size = {20, 20}; + this->root = load(str, len, optimal_chunk_size(len)); free(str); - editor->lang = language_for_file(filename.c_str()); - if (parsers.find(editor->lang.name) != parsers.end()) - editor->parser = new Parser(editor, editor->lang.name, size.row + 5); - if (editor->lang.name == "css" || editor->lang.name == "html" || - editor->lang.name == "javascript" || editor->lang.name == "markdown" || - editor->lang.name == "typescript") - editor->is_css_color = true; - if (len <= (1024 * 28)) - request_add_to_lsp(editor->lang, editor); - editor->indents.compute_indent(editor); - return editor; + this->lang = language_for_file(filename.c_str()); + if (parsers.find(this->lang.name) != parsers.end()) + this->parser = new Parser(this, this->lang.name, size.row + 5); + if (this->lang.name == "css" || this->lang.name == "html" || + this->lang.name == "javascript" || this->lang.name == "markdown" || + this->lang.name == "typescript") + this->is_css_color = true; + if (len <= (1024 * 28)) { + std::lock_guard lock(lsp::lsp_mutex); + lsp::new_editors.push_back(this); + } + this->indents.compute_indent(this); } -void free_editor(Editor *editor) { - remove_from_lsp(editor); - if (editor->parser) - delete editor->parser; - editor->parser = nullptr; - free_rope(editor->root); - delete editor; +Editor::~Editor() { + auto lsp = this->lsp.load(); + if (lsp) + lsp->remove(this); + if (this->parser) + delete this->parser; + this->parser = nullptr; + free_rope(this->root); } -void save_file(Editor *editor) { - if (!editor || !editor->root) +void Editor::save() { + if (!this->root) return; - std::shared_lock lock(editor->knot_mtx); - int version = editor->lsp_version; - uint32_t char_count = editor->root->char_count; - char *str = read(editor->root, 0, char_count); + int version = this->lsp_version; + uint32_t char_count = this->root->char_count; + char *str = read(this->root, 0, char_count); if (!str) return; - lock.unlock(); - std::ofstream out(editor->filename); - if (!editor->unix_eol) { + std::ofstream out(this->filename); + if (!this->unix_eol) { for (uint32_t i = 0; i < char_count; ++i) { if (str[i] == '\n') out.put('\r'); @@ -78,30 +75,33 @@ void save_file(Editor *editor) { } out.close(); free(str); - bar.log("Written " + std::to_string(char_count) + " bytes to " + - editor->filename); - if (editor->lsp) { - json save_msg = {{"jsonrpc", "2.0"}, - {"method", "textDocument/didSave"}, - {"params", {{"textDocument", {{"uri", editor->uri}}}}}}; - lsp_send(editor->lsp, save_msg, nullptr); - if (editor->lsp->allow_formatting) { - json msg = {{"jsonrpc", "2.0"}, - {"method", "textDocument/formatting"}, - {"params", - {{"textDocument", {{"uri", editor->uri}}}, - {"options", - {{"tabSize", 2}, - {"insertSpaces", true}, - {"trimTrailingWhitespace", true}, - {"trimFinalNewlines", true}}}}}}; - LSPPending *pending = new LSPPending(); - pending->editor = editor; - pending->callback = [save_msg, version](Editor *editor, - const json &message) { - if (version != editor->lsp_version) + ui::bar.log("Written " + std::to_string(char_count) + " bytes to " + + this->filename); + auto lsp = this->lsp.load(); + if (lsp) { + log("Saving %s", this->filename.c_str()); + auto message = std::make_unique(); + message->message = {{"method", "textDocument/didSave"}, + {"params", {{"textDocument", {{"uri", this->uri}}}}}}; + lsp->send(std::move(message)); + if (lsp->allow_formatting) { + log("Formatting %s", this->filename.c_str()); + json s_msg = {{"method", "textDocument/formatting"}, + {"params", + {{"textDocument", {{"uri", this->uri}}}, + {"options", + {{"tabSize", 2}, + {"insertSpaces", true}, + {"trimTrailingWhitespace", true}, + {"trimFinalNewlines", true}}}}}}; + auto save_msg = std::make_unique(); + save_msg->editor = this; + save_msg->message = s_msg; + save_msg->callback = [s_msg, version](const LSPMessage &msg) { + log("Formattin"); + if (version != msg.editor->lsp_version) return; - auto &edits = message["result"]; + auto &edits = msg.message["result"]; if (edits.is_array()) { std::vector t_edits; t_edits.reserve(edits.size()); @@ -112,19 +112,17 @@ void save_file(Editor *editor) { t_edit.start.col = edit["range"]["start"]["character"]; t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.col = edit["range"]["end"]["character"]; - utf8_normalize_edit(editor, &t_edit); + msg.editor->utf8_normalize_edit(&t_edit); t_edits.push_back(t_edit); } - apply_lsp_edits(editor, t_edits, false); - ensure_scroll(editor); - std::shared_lock lock(editor->knot_mtx); - uint32_t char_count = editor->root->char_count; - char *str = read(editor->root, 0, char_count); + msg.editor->apply_lsp_edits(t_edits, false); + msg.editor->ensure_cursor(); + uint32_t char_count = msg.editor->root->char_count; + char *str = read(msg.editor->root, 0, char_count); if (!str) return; - lock.unlock(); - std::ofstream out(editor->filename); - if (!editor->unix_eol) { + std::ofstream out(msg.editor->filename); + if (!msg.editor->unix_eol) { for (uint32_t i = 0; i < char_count; ++i) { if (str[i] == '\n') out.put('\r'); @@ -135,10 +133,14 @@ void save_file(Editor *editor) { } out.close(); free(str); - lsp_send(editor->lsp, save_msg, nullptr); + auto save_msg = std::make_unique(); + save_msg->editor = msg.editor; + save_msg->message = s_msg; + save_msg->callback = [](const LSPMessage &) {}; + msg.editor->lsp.load()->send(std::move(save_msg)); } }; - lsp_send(editor->lsp, msg, pending); + lsp->send(std::move(save_msg)); } } } diff --git a/src/editor/events.cc b/src/editor/events.cc index a4d1657..c6fe274 100644 --- a/src/editor/events.cc +++ b/src/editor/events.cc @@ -1,60 +1,59 @@ #include "editor/editor.h" -#include "editor/helpers.h" +#include "extentions/hover.h" #include "io/sysio.h" #include "main.h" #include "utils/utils.h" -void handle_editor_event(Editor *editor, KeyEvent event) { +void Editor::handle_event(KeyEvent event) { uint8_t old_mode = mode; - if (editor->hover_active) - editor->hover_active = false; - handle_mouse(editor, event); + if (this->hover_active) + this->hover_active = false; if (event.key_type == KEY_SPECIAL) { switch (event.special_modifier) { case 0: switch (event.special_key) { case KEY_DOWN: - cursor_down(editor, 1); + this->cursor_down(1); break; case KEY_UP: - cursor_up(editor, 1); + this->cursor_up(1); break; case KEY_LEFT: - cursor_left(editor, 1); + this->cursor_left(1); break; case KEY_RIGHT: - cursor_right(editor, 1); + this->cursor_right(1); break; } break; case CNTRL: switch (event.special_key) { case KEY_DOWN: - cursor_down(editor, 5); + this->cursor_down(5); break; case KEY_UP: - cursor_up(editor, 5); + this->cursor_up(5); break; case KEY_LEFT: - cursor_prev_word(editor); + this->cursor_prev_word(); case KEY_RIGHT: - cursor_next_word(editor); + this->cursor_next_word(); break; } break; case ALT: switch (event.special_key) { case KEY_DOWN: - move_line_down(editor); + this->move_line_down(); break; case KEY_UP: - move_line_up(editor); + this->move_line_up(); break; case KEY_LEFT: - cursor_left(editor, 8); + this->cursor_left(8); break; case KEY_RIGHT: - cursor_right(editor, 8); + this->cursor_right(8); break; } break; @@ -65,86 +64,86 @@ void handle_editor_event(Editor *editor, KeyEvent event) { if (event.key_type == KEY_CHAR && event.len == 1) { switch (event.c[0]) { case 'u': - select_all(editor); + this->select_all(); break; case CTRL('h'): - editor->hover.scroll(-1); - editor->hover_active = true; + static_cast(ui::hover_popup->tile.get())->scroll(-1); + this->hover_active = true; break; case CTRL('l'): - editor->hover.scroll(1); - editor->hover_active = true; + static_cast(ui::hover_popup->tile.get())->scroll(1); + this->hover_active = true; break; case 'h': - fetch_lsp_hover(editor); + this->fetch_lsp_hover(); break; case 'a': { mode = INSERT; - Coord start = editor->cursor; - cursor_right(editor, 1); - if (start.row != editor->cursor.row) - cursor_left(editor, 1); + Coord start = this->cursor; + this->cursor_right(1); + if (start.row != this->cursor.row) + this->cursor_left(1); } break; case 'i': mode = INSERT; break; case 'n': mode = JUMPER; - editor->jumper_set = true; + this->jumper_set = true; break; case 'm': mode = JUMPER; - editor->jumper_set = false; + this->jumper_set = false; break; case 'N': - clear_hooks_at_line(editor, editor->cursor.row); + this->clear_hooks_at_line(this->cursor.row); break; case 's': case 'v': mode = SELECT; - editor->selection_active = true; - editor->selection = editor->cursor; - editor->selection_type = CHAR; + this->selection_active = true; + this->selection = this->cursor; + this->selection_type = CHAR; break; case ';': case ':': mode = RUNNER; break; case 0x7F: - cursor_left(editor, 1); + this->cursor_left(1); break; case ' ': - cursor_right(editor, 1); + this->cursor_right(1); break; case '\r': case '\n': - cursor_down(editor, 1); + this->cursor_down(1); break; case '\\': case '|': - cursor_up(editor, 1); + this->cursor_up(1); break; case CTRL('d'): - scroll_down(editor, 1); - ensure_cursor(editor); + this->scroll_down(1); + this->ensure_cursor(); break; case CTRL('u'): - scroll_up(editor, 1); - ensure_cursor(editor); + this->scroll_up(1); + this->ensure_cursor(); break; case '>': case '.': - indent_current_line(editor); + this->indent_current_line(); break; case '<': case ',': - dedent_current_line(editor); + this->dedent_current_line(); break; case CTRL('s'): - save_file(editor); + this->save(); break; case 'p': - paste(editor); + this->paste(); break; } } @@ -153,34 +152,34 @@ void handle_editor_event(Editor *editor, KeyEvent event) { if (event.key_type == KEY_CHAR) { if (event.len == 1) { if (event.c[0] == '\t') { - editor->indents.insert_tab(editor->cursor); + this->indents.insert_tab(this->cursor); } else if (event.c[0] == '\n' || event.c[0] == '\r') { - editor->indents.insert_new_line(editor->cursor); + this->indents.insert_new_line(this->cursor); } else if (event.c[0] == CTRL('W')) { - delete_prev_word(editor); + this->delete_prev_word(); } else if (isprint((unsigned char)(event.c[0]))) { - insert_char(editor, event.c[0]); + this->insert_char(event.c[0]); } else if (event.c[0] == 0x7F || event.c[0] == 0x08) { - backspace_edit(editor); + this->backspace_edit(); } else if (event.c[0] == 0x1B) { - normal_mode(editor); + this->normal_mode(); } } else if (event.len > 1) { - edit_insert(editor, editor->cursor, event.c, event.len); - cursor_right(editor, 1); + this->edit_insert(this->cursor, event.c, event.len); + this->cursor_right(1); } } else if (event.key_type == KEY_SPECIAL && event.special_key == KEY_DELETE) { switch (event.special_modifier) { case 0: - edit_erase(editor, editor->cursor, 1); + this->edit_erase(this->cursor, 1); break; case CNTRL: - delete_next_word(editor); + this->delete_next_word(); break; } } else if (event.key_type == KEY_PASTE) { - insert_str(editor, event.c, event.len); + this->insert_str(event.c, event.len); } break; case SELECT: @@ -189,28 +188,28 @@ void handle_editor_event(Editor *editor, KeyEvent event) { case 0x1B: case 's': case 'v': - editor->selection_active = false; + this->selection_active = false; mode = NORMAL; break; case 'y': - copy(editor); + this->copy(); mode = NORMAL; break; case 'x': - cut(editor); + this->cut(); mode = NORMAL; break; case 'p': - paste(editor); + this->paste(); mode = NORMAL; break; case '<': case ',': - dedent_selection(editor); + this->dedent_selection(); break; case '>': case '.': - indent_selection(editor); + this->indent_selection(); break; } } @@ -218,25 +217,25 @@ void handle_editor_event(Editor *editor, KeyEvent event) { case JUMPER: if (event.key_type == KEY_CHAR && event.len == 1 && (event.c[0] >= '!' && event.c[0] <= '~')) { - if (editor->jumper_set) { + if (this->jumper_set) { for (uint8_t i = 0; i < 94; i++) - if (editor->hooks[i] == editor->cursor.row + 1) { - editor->hooks[i] = 0; + if (this->hooks[i] == this->cursor.row + 1) { + this->hooks[i] = 0; break; } - editor->hooks[event.c[0] - '!'] = editor->cursor.row + 1; + this->hooks[event.c[0] - '!'] = this->cursor.row + 1; } else { - uint32_t line = editor->hooks[event.c[0] - '!'] - 1; + uint32_t line = this->hooks[event.c[0] - '!'] - 1; if (line > 0) { - editor->cursor = {line, 0}; - editor->cursor_preffered = UINT32_MAX; + this->cursor = {line, 0}; + this->cursor_preffered = UINT32_MAX; } } } mode = NORMAL; break; } - if (old_mode == mode || mode != INSERT) - handle_completion(editor, event); - ensure_scroll(editor); + // if (old_mode == mode || mode != INSERT) + // this->completion.handle(event); + this->ensure_scroll(); } diff --git a/src/editor/helpers.cc b/src/editor/helpers.cc index 99f7f9a..f4adf3c 100644 --- a/src/editor/helpers.cc +++ b/src/editor/helpers.cc @@ -1,86 +1,83 @@ #include "editor/helpers.h" #include "editor/editor.h" +#include "extentions/hover.h" #include "io/sysio.h" #include "lsp/lsp.h" #include "main.h" #include "utils/utils.h" -#include -void cut(Editor *editor) { - if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > - 1500) { - bar.log("Selection too large!"); +void Editor::cut() { + if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) { + ui::bar.log("Selection too large!"); return; } if (mode != SELECT) return; Coord start; uint32_t len; - char *text = get_selection(editor, &len, &start); + char *text = this->get_selection(&len, &start); ruby_copy(text, len); len = count_clusters(text, len, 0, len); - edit_erase(editor, start, len); + this->edit_erase(start, len); free(text); - editor->selection_active = false; + this->selection_active = false; } -void copy(Editor *editor) { - if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > - 1500) { - bar.log("Selection too large!"); +void Editor::copy() { + if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) { + ui::bar.log("Selection too large!"); return; } if (mode != SELECT) return; uint32_t len; - char *text = get_selection(editor, &len, nullptr); + char *text = this->get_selection(&len, nullptr); ruby_copy(text, len); free(text); - editor->selection_active = false; + this->selection_active = false; } -void paste(Editor *editor) { +void Editor::paste() { if (mode == NORMAL) { std::string text = ruby_paste(); if (text.empty()) return; - insert_str(editor, (char *)text.c_str(), text.length()); + this->insert_str((char *)text.c_str(), text.length()); } else if (mode == SELECT) { std::string text = ruby_paste(); if (!text.empty()) { Coord start, end; - selection_bounds(editor, &start, &end); + this->selection_bounds(&start, &end); uint32_t start_byte = - line_to_byte(editor->root, start.row, nullptr) + start.col; - uint32_t end_byte = - line_to_byte(editor->root, end.row, nullptr) + end.col; - edit_erase(editor, start, end_byte - start_byte); - edit_insert(editor, editor->cursor, (char *)text.c_str(), text.length()); + line_to_byte(this->root, start.row, nullptr) + start.col; + uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col; + this->edit_erase(start, end_byte - start_byte); + this->edit_insert(this->cursor, (char *)text.c_str(), text.length()); } - editor->selection_active = false; + this->selection_active = false; } } -void insert_str(Editor *editor, char *c, uint32_t len) { +void Editor::insert_str(char *c, uint32_t len) { if (c) { - edit_insert(editor, editor->cursor, c, len); + this->edit_insert(this->cursor, c, len); uint32_t grapheme_len = count_clusters(c, len, 0, len); - cursor_right(editor, grapheme_len); + this->cursor_right(grapheme_len); } } -void indent_current_line(Editor *editor) { - Coord start = editor->cursor; - uint32_t delta = editor->indents.indent_line(editor->cursor.row); - editor->cursor.col = start.col + delta; - editor->cursor.row = start.row; +void Editor::indent_current_line() { + Coord start = this->cursor; + uint32_t delta = this->indents.indent_line(this->cursor.row); + this->cursor.col = start.col + delta; + this->cursor.row = start.row; } -void dedent_current_line(Editor *editor) { - Coord start = editor->cursor; - uint32_t delta = editor->indents.dedent_line(editor->cursor.row); - editor->cursor.col = MAX((int64_t)start.col - delta, 0); - editor->cursor.row = start.row; +void Editor::dedent_current_line() { + Coord start = this->cursor; + uint32_t delta = this->indents.dedent_line(this->cursor.row); + this->cursor.col = MAX((int64_t)start.col - delta, 0); + this->cursor.row = start.row; } static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) { @@ -90,49 +87,49 @@ static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) { } } -void indent_selection(Editor *editor) { - uint32_t top = MIN(editor->cursor.row, editor->selection.row); - uint32_t bot = MAX(editor->cursor.row, editor->selection.row); +void Editor::indent_selection() { + uint32_t top = MIN(this->cursor.row, this->selection.row); + uint32_t bot = MAX(this->cursor.row, this->selection.row); if (bot - top > 1500) { - bar.log("Can't indent more than 1500 lines at once!"); + ui::bar.log("Can't indent more than 1500 lines at once!"); return; } if (bot - top >= 2) - editor->indents.indent_block(top + 1, bot - 1); - uint32_t delta_top = editor->indents.indent_line(top); + this->indents.indent_block(top + 1, bot - 1); + uint32_t delta_top = this->indents.indent_line(top); uint32_t delta_bot = - (bot == top) ? delta_top : editor->indents.indent_line(bot); - move_coord_by_delta(editor->cursor, top, delta_top); - move_coord_by_delta(editor->selection, top, delta_top); + (bot == top) ? delta_top : this->indents.indent_line(bot); + move_coord_by_delta(this->cursor, top, delta_top); + move_coord_by_delta(this->selection, top, delta_top); if (bot != top) { - move_coord_by_delta(editor->cursor, bot, delta_bot); - move_coord_by_delta(editor->selection, bot, delta_bot); + move_coord_by_delta(this->cursor, bot, delta_bot); + move_coord_by_delta(this->selection, bot, delta_bot); } } -void dedent_selection(Editor *editor) { - uint32_t top = MIN(editor->cursor.row, editor->selection.row); - uint32_t bot = MAX(editor->cursor.row, editor->selection.row); +void Editor::dedent_selection() { + uint32_t top = MIN(this->cursor.row, this->selection.row); + uint32_t bot = MAX(this->cursor.row, this->selection.row); if (bot - top > 1500) { - bar.log("Can't dedent more than 1500 lines at once!"); + ui::bar.log("Can't dedent more than 1500 lines at once!"); return; } if (bot - top >= 2) - editor->indents.dedent_block(top + 1, bot - 1); - uint32_t delta_top = editor->indents.dedent_line(top); + this->indents.dedent_block(top + 1, bot - 1); + uint32_t delta_top = this->indents.dedent_line(top); uint32_t delta_bot = - (bot == top) ? delta_top : editor->indents.dedent_line(bot); - move_coord_by_delta(editor->cursor, top, -(int64_t)delta_top); - move_coord_by_delta(editor->selection, top, -(int64_t)delta_top); + (bot == top) ? delta_top : this->indents.dedent_line(bot); + move_coord_by_delta(this->cursor, top, -(int64_t)delta_top); + move_coord_by_delta(this->selection, top, -(int64_t)delta_top); if (bot != top) { - move_coord_by_delta(editor->cursor, bot, -(int64_t)delta_bot); - move_coord_by_delta(editor->selection, bot, -(int64_t)delta_bot); + move_coord_by_delta(this->cursor, bot, -(int64_t)delta_bot); + move_coord_by_delta(this->selection, bot, -(int64_t)delta_bot); } } -void insert_char(Editor *editor, char c) { - uint32_t col = editor->cursor.col; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); +void Editor::insert_char(char c) { + uint32_t col = this->cursor.col; + LineIterator *it = begin_l_iter(this->root, this->cursor.row); if (!it) return; uint32_t len; @@ -148,7 +145,7 @@ void insert_char(Editor *editor, char c) { if ((c == '}' && next == '}') || (c == ')' && next == ')') || (c == ']' && next == ']') || (c == '"' && next == '"') || (c == '\'' && next == '\'')) { - cursor_right(editor, 1); + this->cursor_right(1); skip_insert = true; } } @@ -175,16 +172,17 @@ void insert_char(Editor *editor, char c) { } if (closing) { char pair[2] = {c, closing}; - edit_insert(editor, editor->cursor, pair, 2); - cursor_right(editor, 1); + this->edit_insert(this->cursor, pair, 2); + this->cursor_right(1); } else { - edit_insert(editor, editor->cursor, &c, 1); - cursor_right(editor, 1); + this->edit_insert(this->cursor, &c, 1); + this->cursor_right(1); } - if (editor->lsp && editor->lsp->allow_formatting_on_type) { - for (char ch : editor->lsp->format_chars) { + auto lsp = this->lsp.load(); + if (lsp && lsp->allow_formatting_on_type) { + for (char ch : lsp->format_chars) { if (ch == c) { - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); if (!it) return; uint32_t len; @@ -194,29 +192,27 @@ void insert_char(Editor *editor, char c) { free(it); return; } - uint32_t col = utf8_offset_to_utf16(line, len, editor->cursor.col); + uint32_t col = utf8_offset_to_utf16(line, len, this->cursor.col); free(it->buffer); free(it); - int version = editor->lsp_version; - json message = { - {"jsonrpc", "2.0"}, + int version = this->lsp_version; + auto message = std::make_unique(); + message->message = { {"method", "textDocument/onTypeFormatting"}, {"params", - {{"textDocument", {{"uri", editor->uri}}}, - {"position", - {{"line", editor->cursor.row}, {"character", col}}}, + {{"textDocument", {{"uri", this->uri}}}, + {"position", {{"line", this->cursor.row}, {"character", col}}}, {"ch", std::string(1, c)}, {"options", {{"tabSize", 2}, {"insertSpaces", true}, {"trimTrailingWhitespace", true}, {"trimFinalNewlines", true}}}}}}; - LSPPending *pending = new LSPPending(); - pending->editor = editor; - pending->callback = [version](Editor *editor, const json &message) { - if (version != editor->lsp_version) + message->editor = this; + message->callback = [version](const LSPMessage &message) { + if (version != message.editor->lsp_version) return; - auto &edits = message["result"]; + auto &edits = message.message["result"]; if (edits.is_array()) { std::vector t_edits; t_edits.reserve(edits.size()); @@ -227,14 +223,14 @@ void insert_char(Editor *editor, char c) { t_edit.start.col = edit["range"]["start"]["character"]; t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.col = edit["range"]["end"]["character"]; - utf8_normalize_edit(editor, &t_edit); + message.editor->utf8_normalize_edit(&t_edit); t_edits.push_back(t_edit); } - apply_lsp_edits(editor, t_edits, false); - ensure_scroll(editor); + message.editor->apply_lsp_edits(t_edits, false); + message.editor->ensure_scroll(); } }; - lsp_send(editor->lsp, message, pending); + lsp->send(std::move(message)); break; } } @@ -242,19 +238,19 @@ void insert_char(Editor *editor, char c) { } } -void normal_mode(Editor *editor) { - Coord prev_pos = editor->cursor; +void Editor::normal_mode() { + Coord prev_pos = this->cursor; mode = NORMAL; - cursor_left(editor, 1); - if (prev_pos.row != editor->cursor.row) - cursor_right(editor, 1); + this->cursor_left(1); + if (prev_pos.row != this->cursor.row) + this->cursor_right(1); } -void backspace_edit(Editor *editor) { - Coord prev_pos = editor->cursor; +void Editor::backspace_edit() { + Coord prev_pos = this->cursor; if (prev_pos.col > 0) prev_pos.col--; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); if (!it) return; uint32_t len; @@ -267,11 +263,11 @@ void backspace_edit(Editor *editor) { if (len > 0 && line[len - 1] == '\n') --len; char prev_char = (prev_pos.col < len) ? line[prev_pos.col] : 0; - char next_char = (editor->cursor.col < len) ? line[editor->cursor.col] : 0; + char next_char = (this->cursor.col < len) ? line[this->cursor.col] : 0; bool before_content = false; - if (editor->cursor.col > 0) { + if (this->cursor.col > 0) { before_content = true; - for (uint32_t i = 0; i < editor->cursor.col; i++) + for (uint32_t i = 0; i < this->cursor.col; i++) if (line[i] != ' ' && line[i] != '\t') { before_content = false; break; @@ -280,7 +276,7 @@ void backspace_edit(Editor *editor) { free(it->buffer); free(it); if (before_content) { - dedent_current_line(editor); + this->dedent_current_line(); return; } bool is_pair = (prev_char == '{' && next_char == '}') || @@ -289,65 +285,65 @@ void backspace_edit(Editor *editor) { (prev_char == '"' && next_char == '"') || (prev_char == '\'' && next_char == '\''); if (is_pair) { - edit_erase(editor, editor->cursor, 1); - edit_erase(editor, prev_pos, 1); + this->edit_erase(this->cursor, 1); + this->edit_erase(prev_pos, 1); } else { - edit_erase(editor, editor->cursor, -1); + this->edit_erase(this->cursor, -1); } } -void delete_prev_word(Editor *editor) { +void Editor::delete_prev_word() { uint32_t prev_col_byte, prev_col_cluster; - word_boundaries(editor, editor->cursor, &prev_col_byte, nullptr, - &prev_col_cluster, nullptr); - if (prev_col_byte == editor->cursor.col) - edit_erase(editor, editor->cursor, -1); + this->word_boundaries(this->cursor, &prev_col_byte, nullptr, + &prev_col_cluster, nullptr); + if (prev_col_byte == this->cursor.col) + this->edit_erase(this->cursor, -1); else - edit_erase(editor, editor->cursor, -(int64_t)prev_col_cluster); + this->edit_erase(this->cursor, -(int64_t)prev_col_cluster); } -void delete_next_word(Editor *editor) { +void Editor::delete_next_word() { uint32_t next_col_byte, next_col_cluster; - word_boundaries(editor, editor->cursor, nullptr, &next_col_byte, nullptr, - &next_col_cluster); - if (next_col_byte == editor->cursor.col) - edit_erase(editor, editor->cursor, 1); + this->word_boundaries(this->cursor, nullptr, &next_col_byte, nullptr, + &next_col_cluster); + if (next_col_byte == this->cursor.col) + this->edit_erase(this->cursor, 1); else - edit_erase(editor, editor->cursor, next_col_cluster); + this->edit_erase(this->cursor, next_col_cluster); } -void clear_hooks_at_line(Editor *editor, uint32_t line) { +void Editor::clear_hooks_at_line(uint32_t line) { for (uint8_t i = 0; i < 94; i++) - if (editor->hooks[i] == line + 1) { - editor->hooks[i] = 0; + if (this->hooks[i] == line + 1) { + this->hooks[i] = 0; break; } } -void cursor_prev_word(Editor *editor) { +void Editor::cursor_prev_word() { uint32_t prev_col; - word_boundaries(editor, editor->cursor, &prev_col, nullptr, nullptr, nullptr); - editor->cursor_preffered = UINT32_MAX; - if (prev_col == editor->cursor.col) - cursor_left(editor, 1); + word_boundaries(this->cursor, &prev_col, nullptr, nullptr, nullptr); + this->cursor_preffered = UINT32_MAX; + if (prev_col == this->cursor.col) + cursor_left(1); else - editor->cursor = {editor->cursor.row, prev_col}; + this->cursor = {this->cursor.row, prev_col}; } -void cursor_next_word(Editor *editor) { +void Editor::cursor_next_word() { uint32_t next_col; - word_boundaries(editor, editor->cursor, nullptr, &next_col, nullptr, nullptr); - editor->cursor_preffered = UINT32_MAX; - if (next_col == editor->cursor.col) - cursor_right(editor, 1); + word_boundaries(this->cursor, nullptr, &next_col, nullptr, nullptr); + this->cursor_preffered = UINT32_MAX; + if (next_col == this->cursor.col) + this->cursor_right(1); else - editor->cursor = {editor->cursor.row, next_col}; + this->cursor = {this->cursor.row, next_col}; } -void select_all(Editor *editor) { - if (editor->root->line_count > 0) { - editor->cursor.row = editor->root->line_count - 1; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); +void Editor::select_all() { + if (this->root->line_count > 0) { + this->cursor.row = this->root->line_count - 1; + LineIterator *it = begin_l_iter(this->root, this->cursor.row); if (!it) return; uint32_t line_len; @@ -359,18 +355,19 @@ void select_all(Editor *editor) { line_len = count_clusters(line, line_len, 0, line_len); free(it->buffer); free(it); - editor->cursor.col = line_len; - editor->cursor_preffered = UINT32_MAX; + this->cursor.col = line_len; + this->cursor_preffered = UINT32_MAX; mode = SELECT; - editor->selection_active = true; - editor->selection = {0, 0}; - editor->selection_type = LINE; + this->selection_active = true; + this->selection = {0, 0}; + this->selection_type = LINE; } } -void fetch_lsp_hover(Editor *editor) { - if (editor->lsp && editor->lsp->allow_hover) { - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); +void Editor::fetch_lsp_hover() { + auto lsp = this->lsp.load(); + if (lsp && lsp->allow_hover) { + LineIterator *it = begin_l_iter(this->root, this->cursor.row); uint32_t line_len; char *line = next_line(it, &line_len); if (!line) { @@ -378,18 +375,18 @@ void fetch_lsp_hover(Editor *editor) { free(it); return; } - uint32_t col = utf8_offset_to_utf16(line, line_len, editor->cursor.col); + uint32_t col = utf8_offset_to_utf16(line, line_len, this->cursor.col); free(it->buffer); free(it); - json hover_request = { - {"jsonrpc", "2.0"}, + auto message = std::make_unique(); + message->message = { {"method", "textDocument/hover"}, {"params", - {{"textDocument", {{"uri", editor->uri}}}, - {"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; - LSPPending *pending = new LSPPending(); - pending->editor = editor; - pending->callback = [](Editor *editor, const json &hover) { + {{"textDocument", {{"uri", this->uri}}}, + {"position", {{"line", this->cursor.row}, {"character", col}}}}}}; + message->editor = this; + message->callback = [](const LSPMessage &message) { + auto &hover = message.message; if (hover.contains("result") && !hover["result"].is_null()) { auto &contents = hover["result"]["contents"]; std::string hover_text = ""; @@ -413,19 +410,21 @@ void fetch_lsp_hover(Editor *editor) { hover_text += contents.get(); } if (!hover_text.empty()) { - editor->hover.clear(); - editor->hover.text = clean_text(hover_text); - editor->hover.is_markup = is_markup; - editor->hover.render_first(); - editor->hover_active = true; + auto hover_box = static_cast(ui::hover_popup->tile.get()); + hover_box->clear(); + hover_box->text = clean_text(hover_text); + hover_box->is_markup = is_markup; + message.editor->hover_active = true; } } }; - lsp_send(editor->lsp, hover_request, pending); + lsp->send(std::move(message)); } } -void handle_mouse(Editor *editor, KeyEvent event) { +void Editor::handle_click(KeyEvent event, Coord size) { + focused_window = this; + this->size = size; static std::chrono::steady_clock::time_point last_click_time = std::chrono::steady_clock::now(); static uint32_t click_count = 0; @@ -439,18 +438,18 @@ void handle_mouse(Editor *editor, KeyEvent event) { case SCROLL: switch (event.mouse_direction) { case SCROLL_UP: - scroll_up(editor, 4); - ensure_cursor(editor); + this->scroll_up(4); + this->ensure_cursor(); break; case SCROLL_DOWN: - scroll_down(editor, 4); - ensure_cursor(editor); + this->scroll_down(4); + this->ensure_cursor(); break; case SCROLL_LEFT: - cursor_left(editor, 10); + this->cursor_left(10); break; case SCROLL_RIGHT: - cursor_right(editor, 10); + this->cursor_right(10); break; } break; @@ -463,35 +462,35 @@ void handle_mouse(Editor *editor, KeyEvent event) { click_count = 1; last_click_time = now; last_click_pos = cur_pos; - Coord p = editor_hit_test(editor, event.mouse_x, event.mouse_y); + Coord p = this->click_coord(event.mouse_x, event.mouse_y); if (p.row == UINT32_MAX && p.col == UINT32_MAX) return; - editor->cursor_preffered = UINT32_MAX; + this->cursor_preffered = UINT32_MAX; if (click_count == 1) { - editor->cursor = p; - editor->selection = p; + this->cursor = p; + this->selection = p; if (mode == SELECT) { mode = NORMAL; - editor->selection_active = false; + this->selection_active = false; } } else if (click_count == 2) { uint32_t prev_col, next_col; - word_boundaries(editor, editor->cursor, &prev_col, &next_col, nullptr, - nullptr); - if (editor->cursor < editor->selection) - editor->cursor = {editor->cursor.row, prev_col}; + this->word_boundaries(this->cursor, &prev_col, &next_col, nullptr, + nullptr); + if (this->cursor < this->selection) + this->cursor = {this->cursor.row, prev_col}; else - editor->cursor = {editor->cursor.row, next_col}; - editor->cursor_preffered = UINT32_MAX; - editor->selection_type = WORD; + this->cursor = {this->cursor.row, next_col}; + this->cursor_preffered = UINT32_MAX; + this->selection_type = WORD; mode = SELECT; - editor->selection_active = true; + this->selection_active = true; } else if (click_count >= 3) { - if (editor->cursor < editor->selection) { - editor->cursor = {p.row, 0}; + if (this->cursor < this->selection) { + this->cursor = {p.row, 0}; } else { uint32_t line_len; - LineIterator *it = begin_l_iter(editor->root, p.row); + LineIterator *it = begin_l_iter(this->root, p.row); char *line = next_line(it, &line_len); if (!line) return; @@ -499,44 +498,44 @@ void handle_mouse(Editor *editor, KeyEvent event) { line_len--; free(it->buffer); free(it); - editor->cursor = {p.row, line_len}; + this->cursor = {p.row, line_len}; } - editor->cursor_preffered = UINT32_MAX; - editor->selection_type = LINE; + this->cursor_preffered = UINT32_MAX; + this->selection_type = LINE; mode = SELECT; - editor->selection_active = true; + this->selection_active = true; click_count = 3; } } break; case DRAG: if (event.mouse_button == LEFT_BTN) { - Coord p = editor_hit_test(editor, event.mouse_x, event.mouse_y); + Coord p = this->click_coord(event.mouse_x, event.mouse_y); if (p.row == UINT32_MAX && p.col == UINT32_MAX) return; - editor->cursor_preffered = UINT32_MAX; + this->cursor_preffered = UINT32_MAX; mode = SELECT; - if (!editor->selection_active) { - editor->selection_active = true; - editor->selection_type = CHAR; + if (!this->selection_active) { + this->selection_active = true; + this->selection_type = CHAR; } uint32_t prev_col, next_col, line_len; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - editor->cursor = p; + this->cursor = p; break; case WORD: - word_boundaries(editor, p, &prev_col, &next_col, nullptr, nullptr); - if (editor->cursor < editor->selection) - editor->cursor = {p.row, prev_col}; + this->word_boundaries(p, &prev_col, &next_col, nullptr, nullptr); + if (this->cursor < this->selection) + this->cursor = {p.row, prev_col}; else - editor->cursor = {p.row, next_col}; + this->cursor = {p.row, next_col}; break; case LINE: - if (editor->cursor < editor->selection) { - editor->cursor = {p.row, 0}; + if (this->cursor < this->selection) { + this->cursor = {p.row, 0}; } else { - LineIterator *it = begin_l_iter(editor->root, p.row); + LineIterator *it = begin_l_iter(this->root, p.row); char *line = next_line(it, &line_len); if (!line) return; @@ -544,7 +543,7 @@ void handle_mouse(Editor *editor, KeyEvent event) { line_len--; free(it->buffer); free(it); - editor->cursor = {p.row, line_len}; + this->cursor = {p.row, line_len}; } break; } @@ -552,10 +551,10 @@ void handle_mouse(Editor *editor, KeyEvent event) { break; case RELEASE: if (event.mouse_button == LEFT_BTN) - if (editor->cursor.row == editor->selection.row && - editor->cursor.col == editor->selection.col) { + if (this->cursor.row == this->selection.row && + this->cursor.col == this->selection.col) { mode = NORMAL; - editor->selection_active = false; + this->selection_active = false; } break; } diff --git a/src/editor/indents.cc b/src/editor/indents.cc index 91799d6..db94ac9 100644 --- a/src/editor/indents.cc +++ b/src/editor/indents.cc @@ -68,7 +68,6 @@ uint32_t IndentationEngine::indent_real(char *line, uint32_t len) { } uint32_t IndentationEngine::indent_expected(uint32_t row) { - std::shared_lock lock(editor->knot_mtx); uint32_t line_idx = row; if (row == 0) return 0; @@ -109,7 +108,6 @@ uint32_t IndentationEngine::indent_expected(uint32_t row) { } uint32_t IndentationEngine::set_indent(uint32_t row, int64_t new_indent) { - std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, row); if (!it) return 0; @@ -122,7 +120,6 @@ uint32_t IndentationEngine::set_indent(uint32_t row, int64_t new_indent) { } if (len > 0 && line[len - 1] == '\n') --len; - lock.unlock(); if (new_indent <= 0) new_indent = 0; uint32_t ws_len = 0; @@ -135,14 +132,13 @@ uint32_t IndentationEngine::set_indent(uint32_t row, int64_t new_indent) { new_ws.assign(new_indent * indent, ' '); Coord start = {row, 0}; Coord end = {row, ws_len}; - edit_replace(editor, start, end, new_ws.c_str(), new_ws.length()); + editor->edit_replace(start, end, new_ws.c_str(), new_ws.length()); free(it->buffer); free(it); return len - ws_len + (new_indent * indent); } uint32_t IndentationEngine::indent_line(uint32_t row) { - std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, row); if (!it) return 0; @@ -153,7 +149,6 @@ uint32_t IndentationEngine::indent_line(uint32_t row) { free(it); return 0; } - lock.unlock(); if (len > 0 && line[len - 1] == '\n') --len; uint32_t new_indent = indent_real(line, len) + 1; @@ -165,15 +160,14 @@ uint32_t IndentationEngine::indent_line(uint32_t row) { new_ws.assign(new_indent, '\t'); else new_ws.assign(new_indent * indent, ' '); - edit_replace(editor, {row, 0}, {row, ws_len}, new_ws.c_str(), - new_indent * indent); + editor->edit_replace({row, 0}, {row, ws_len}, new_ws.c_str(), + new_indent * indent); free(it->buffer); free(it); return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); } uint32_t IndentationEngine::dedent_line(uint32_t row) { - std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, row); if (!it) return 0; @@ -184,7 +178,6 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) { free(it); return 0; } - lock.unlock(); if (len > 0 && line[len - 1] == '\n') --len; int64_t new_indent = (int64_t)indent_real(line, len) - 1; @@ -198,8 +191,8 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) { new_ws.assign(new_indent, '\t'); else new_ws.assign(new_indent * indent, ' '); - edit_replace(editor, {row, 0}, {row, ws_len}, new_ws.c_str(), - new_indent * indent); + editor->edit_replace({row, 0}, {row, ws_len}, new_ws.c_str(), + new_indent * indent); free(it->buffer); free(it); return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); @@ -262,12 +255,11 @@ void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row, } } free(block); - edit_replace(editor, {start_row, 0}, {end_row, end_len}, out, out_len); + editor->edit_replace({start_row, 0}, {end_row, end_len}, out, out_len); free(out); } void IndentationEngine::insert_tab(Coord cursor) { - std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, cursor.row); if (!it) return; @@ -278,7 +270,6 @@ void IndentationEngine::insert_tab(Coord cursor) { free(it); return; } - lock.unlock(); if (len > 0 && line[len - 1] == '\n') --len; uint32_t ws_len = 0; @@ -295,13 +286,12 @@ void IndentationEngine::insert_tab(Coord cursor) { } free(it->buffer); free(it); - edit_insert(editor, cursor, (char *)insert.c_str(), insert.size()); + editor->edit_insert(cursor, (char *)insert.c_str(), insert.size()); editor->cursor.col += insert.size(); } void IndentationEngine::insert_new_line(Coord cursor) { std::string formatted; - std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, cursor.row); if (!it) return; @@ -312,7 +302,6 @@ void IndentationEngine::insert_new_line(Coord cursor) { free(it); return; } - lock.unlock(); if (len > 0 && line[len - 1] == '\n') --len; if (cursor.col >= len) { @@ -332,7 +321,6 @@ void IndentationEngine::insert_new_line(Coord cursor) { cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1); break; } - lock.lock(); free(it->buffer); free(it); it = begin_l_iter(editor->root, cursor.row); @@ -346,7 +334,6 @@ void IndentationEngine::insert_new_line(Coord cursor) { } if (len > 0 && line[len - 1] == '\n') --len; - lock.unlock(); } std::string ending = trim(std::string(line + cursor.col, len - cursor.col)); std::string before = trim(std::string(line, cursor.col)); @@ -407,15 +394,16 @@ void IndentationEngine::insert_new_line(Coord cursor) { : std::string(c_indent * indent, ' ')) + ending; Coord new_cursor = {cursor.row + 1, (uint32_t)c_indent * indent}; - edit_replace(editor, cursor, {cursor.row, len}, formatted.data(), - formatted.size()); + editor->edit_replace(cursor, {cursor.row, len}, formatted.data(), + formatted.size()); editor->cursor = new_cursor; editor->cursor_preffered = UINT32_MAX; free(it->buffer); free(it); - if (!editor->lsp || !editor->lsp->allow_formatting_on_type) + auto lsp = editor->lsp.load(); + if (!lsp || !lsp->allow_formatting_on_type) return; - for (char ch : editor->lsp->format_chars) { + for (char ch : lsp->format_chars) { if (ch == '\n') { LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); if (!it) @@ -431,7 +419,9 @@ void IndentationEngine::insert_new_line(Coord cursor) { free(it->buffer); free(it); int version = editor->lsp_version; - json message = { + auto message = std::make_unique(); + message->editor = editor; + message->message = { {"jsonrpc", "2.0"}, {"method", "textDocument/onTypeFormatting"}, {"params", @@ -443,12 +433,10 @@ void IndentationEngine::insert_new_line(Coord cursor) { {"insertSpaces", true}, {"trimTrailingWhitespace", true}, {"trimFinalNewlines", true}}}}}}; - LSPPending *pending = new LSPPending(); - pending->editor = editor; - pending->callback = [version](Editor *editor, const json &message) { - if (version != editor->lsp_version) + message->callback = [version](const LSPMessage &message) { + if (version != message.editor->lsp_version) return; - auto &edits = message["result"]; + auto &edits = message.message["result"]; if (edits.is_array()) { std::vector t_edits; t_edits.reserve(edits.size()); @@ -459,14 +447,14 @@ void IndentationEngine::insert_new_line(Coord cursor) { t_edit.start.col = edit["range"]["start"]["character"]; t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.col = edit["range"]["end"]["character"]; - utf8_normalize_edit(editor, &t_edit); + message.editor->utf8_normalize_edit(&t_edit); t_edits.push_back(t_edit); } - apply_lsp_edits(editor, t_edits, false); - ensure_scroll(editor); + message.editor->apply_lsp_edits(t_edits, false); + message.editor->ensure_scroll(); } }; - lsp_send(editor->lsp, message, pending); + lsp->send(std::move(message)); break; } } diff --git a/src/editor/lsp.cc b/src/editor/lsp.cc index 357c7ac..419332b 100644 --- a/src/editor/lsp.cc +++ b/src/editor/lsp.cc @@ -2,52 +2,47 @@ #include "editor/editor.h" #include "utils/utils.h" -void apply_lsp_edits(Editor *editor, std::vector edits, bool move) { +void Editor::apply_lsp_edits(std::vector edits, bool move) { if (!edits.size()) return; TextEdit first = edits[0]; - Coord cursor = editor->cursor; + Coord cursor = this->cursor; std::sort( edits.begin(), edits.end(), [](const TextEdit &a, const TextEdit &b) { return a.start > b.start; }); for (const auto &edit : edits) - edit_replace(editor, edit.start, edit.end, edit.text.c_str(), - edit.text.size()); + this->edit_replace(edit.start, edit.end, edit.text.c_str(), + edit.text.size()); if (move) { - std::shared_lock lock(editor->knot_mtx); - editor->cursor = first.start; - editor->cursor = - move_right(editor, editor->cursor, - count_clusters(first.text.c_str(), first.text.size(), 0, - first.text.size())); + this->cursor = first.start; + this->cursor = this->move_right( + this->cursor, count_clusters(first.text.c_str(), first.text.size(), 0, + first.text.size())); } else { - if (cursor.row >= editor->root->line_count) { - editor->cursor.row = editor->root->line_count - 1; - editor->cursor.col = 0; + if (cursor.row >= this->root->line_count) { + this->cursor.row = this->root->line_count - 1; + this->cursor.col = 0; } else { - std::shared_lock lock(editor->knot_mtx); uint32_t len; - line_to_byte(editor->root, cursor.row, &len); + line_to_byte(this->root, cursor.row, &len); len--; - editor->cursor.row = cursor.row; - editor->cursor.col = cursor.col < len ? cursor.col : len; + this->cursor.row = cursor.row; + this->cursor.col = cursor.col < len ? cursor.col : len; } } } -void editor_lsp_handle(Editor *editor, json msg) { +void Editor::lsp_handle(json msg) { if (msg.contains("method") && msg["method"] == "textDocument/publishDiagnostics") { - std::unique_lock lock(editor->v_mtx); - editor->warnings.clear(); + this->warnings.clear(); json diagnostics = msg["params"]["diagnostics"]; for (size_t i = 0; i < diagnostics.size(); i++) { json d = diagnostics[i]; VWarn w; w.line = d["range"]["start"]["line"]; w.start = d["range"]["start"]["character"]; - std::shared_lock lock(editor->knot_mtx); - LineIterator *it = begin_l_iter(editor->root, w.line); + LineIterator *it = begin_l_iter(this->root, w.line); if (!it) continue; uint32_t len; @@ -59,7 +54,6 @@ void editor_lsp_handle(Editor *editor, json msg) { } if (len > 0 && line[len - 1] == '\n') --len; - lock.unlock(); w.start = utf16_offset_to_utf8(line, len, w.start); uint32_t end = d["range"]["end"]["character"]; if (d["range"]["end"]["line"] == w.line) @@ -102,9 +96,9 @@ void editor_lsp_handle(Editor *editor, json msg) { w.type = 1; if (d.contains("severity")) w.type = d["severity"].get(); - editor->warnings.push_back(w); + this->warnings.push_back(w); } - std::sort(editor->warnings.begin(), editor->warnings.end()); - editor->warnings_dirty = true; + std::sort(this->warnings.begin(), this->warnings.end()); + this->warnings_dirty = true; } } diff --git a/src/editor/move_line.cc b/src/editor/move_line.cc index 8cd8611..43e8033 100644 --- a/src/editor/move_line.cc +++ b/src/editor/move_line.cc @@ -1,120 +1,108 @@ #include "editor/editor.h" #include "main.h" -void move_line_up(Editor *editor) { - if (!editor || !editor->root || editor->cursor.row == 0) +void Editor::move_line_up() { + if (!this->root || this->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); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); char *line = next_line(it, &line_len); - if (!line) { - lock.unlock(); + if (!line) return; - } if (line_len > 0 && line[line_len - 1] == '\n') line_len--; line_cluster_len = count_clusters(line, line_len, 0, line_len); - uint32_t target_row = editor->cursor.row - 1; - uint32_t up_by = editor->cursor.row - target_row; + uint32_t target_row = this->cursor.row - 1; + uint32_t up_by = this->cursor.row - target_row; if (up_by > 1) up_by--; - lock.unlock(); - Coord cursor = editor->cursor; - edit_erase(editor, {cursor.row, 0}, line_cluster_len); - edit_erase(editor, {cursor.row, 0}, -1); - edit_insert(editor, {cursor.row - up_by, 0}, (char *)"\n", 1); - edit_insert(editor, {cursor.row - up_by, 0}, line, line_len); + Coord cursor = this->cursor; + edit_erase({cursor.row, 0}, line_cluster_len); + edit_erase({cursor.row, 0}, -1); + edit_insert({cursor.row - up_by, 0}, (char *)"\n", 1); + edit_insert({cursor.row - up_by, 0}, line, line_len); free(it->buffer); free(it); - editor->cursor = {cursor.row - up_by, cursor.col}; + this->cursor = {cursor.row - up_by, 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); + uint32_t start_row = MIN(this->cursor.row, this->selection.row); + uint32_t end_row = MAX(this->cursor.row, this->selection.row); + uint32_t start_byte = line_to_byte(this->root, start_row, nullptr); + uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr); + char *selected_text = read(this->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); + Coord cursor = this->cursor; + Coord selection = this->selection; + edit_erase({start_row, 0}, selected_len); + edit_insert({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}; + this->cursor = {cursor.row - 1, cursor.col}; + this->selection = {selection.row - 1, selection.col}; } } -void move_line_down(Editor *editor) { - if (!editor || !editor->root) +void Editor::move_line_down() { + if (!this->root) return; if (mode == NORMAL || mode == INSERT) { - if (editor->cursor.row >= editor->root->line_count - 1) + if (this->cursor.row >= this->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); + LineIterator *it = begin_l_iter(this->root, this->cursor.row); char *line = next_line(it, &line_len); - if (!line) { - lock.unlock(); + if (!line) return; - } if (line_len && line[line_len - 1] == '\n') line_len--; line_cluster_len = count_clusters(line, line_len, 0, line_len); - uint32_t target_row = editor->cursor.row + 1; - if (target_row >= editor->root->line_count) { - free(line); - lock.unlock(); + uint32_t target_row = this->cursor.row + 1; + if (target_row >= this->root->line_count) { + free(it->buffer); + free(it); return; } - uint32_t down_by = target_row - editor->cursor.row; + uint32_t down_by = target_row - this->cursor.row; if (down_by > 1) down_by--; uint32_t ln; - line_to_byte(editor->root, editor->cursor.row + down_by - 1, &ln); - lock.unlock(); - Coord cursor = editor->cursor; - edit_erase(editor, {cursor.row, 0}, line_cluster_len); - edit_erase(editor, {cursor.row, 0}, -1); - edit_insert(editor, {cursor.row + down_by, 0}, (char *)"\n", 1); - edit_insert(editor, {cursor.row + down_by, 0}, line, line_len); + line_to_byte(this->root, this->cursor.row + down_by - 1, &ln); + Coord cursor = this->cursor; + edit_erase({cursor.row, 0}, line_cluster_len); + edit_erase({cursor.row, 0}, -1); + edit_insert({cursor.row + down_by, 0}, (char *)"\n", 1); + edit_insert({cursor.row + down_by, 0}, line, line_len); free(it->buffer); free(it); - editor->cursor = {cursor.row + down_by, cursor.col}; + this->cursor = {cursor.row + down_by, cursor.col}; } else if (mode == SELECT) { - if (editor->cursor.row >= editor->root->line_count - 1 || - editor->selection.row >= editor->root->line_count - 1) + if (this->cursor.row >= this->root->line_count - 1 || + this->selection.row >= this->root->line_count - 1) return; - std::shared_lock lock(editor->knot_mtx); - 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_row = MIN(this->cursor.row, this->selection.row); + uint32_t end_row = MAX(this->cursor.row, this->selection.row); uint32_t target_row = end_row + 1; - if (target_row >= editor->root->line_count) + if (target_row >= this->root->line_count) return; uint32_t down_by = target_row - end_row; if (down_by > 1) down_by--; - 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); - lock.unlock(); + uint32_t start_byte = line_to_byte(this->root, start_row, nullptr); + uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr); + char *selected_text = read(this->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 + down_by, 0}, selected_text, - end_byte - start_byte); + Coord cursor = this->cursor; + Coord selection = this->selection; + edit_erase({start_row, 0}, selected_len); + edit_insert({start_row + down_by, 0}, selected_text, end_byte - start_byte); free(selected_text); - editor->cursor = {cursor.row + down_by, cursor.col}; - editor->selection = {selection.row + down_by, selection.col}; + this->cursor = {cursor.row + down_by, cursor.col}; + this->selection = {selection.row + down_by, selection.col}; } } diff --git a/src/editor/renderer.cc b/src/editor/renderer.cc index 3523d8a..6d18d5d 100644 --- a/src/editor/renderer.cc +++ b/src/editor/renderer.cc @@ -3,27 +3,24 @@ #include "main.h" #include "syntax/decl.h" #include "syntax/parser.h" -#include -void render_editor(Editor *editor) { +void Editor::render(std::vector &buffer, Coord size, Coord pos) { + this->size = size; uint32_t sel_start = 0, sel_end = 0; - std::shared_lock knot_lock(editor->knot_mtx); uint32_t numlen = - EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - uint32_t render_width = editor->size.col - numlen; - uint32_t render_x = editor->position.col + numlen + 1; + EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); + uint32_t render_width = size.col - numlen; + uint32_t render_x = pos.col + numlen + 1; std::vector> v; for (size_t i = 0; i < 94; ++i) - if (editor->hooks[i] != 0) - v.push_back({editor->hooks[i], '!' + i}); + if (this->hooks[i] != 0) + v.push_back({this->hooks[i], '!' + i}); std::sort(v.begin(), v.end()); auto hook_it = v.begin(); - while (hook_it != v.end() && hook_it->first <= editor->scroll.row) + while (hook_it != v.end() && hook_it->first <= this->scroll.row) ++hook_it; - std::unique_lock warn_lock(editor->v_mtx); - auto warn_it = editor->warnings.begin(); - while (warn_it != editor->warnings.end() && - warn_it->line < editor->scroll.row) + auto warn_it = this->warnings.begin(); + while (warn_it != this->warnings.end() && warn_it->line < this->scroll.row) ++warn_it; LineData *line_data = nullptr; auto get_type = [&](uint32_t col) { @@ -34,40 +31,54 @@ void render_editor(Editor *editor) { return (int)token.type; return 0; }; - if (editor->selection_active) { + Coord screen = get_size(); + auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg, + uint32_t bg, uint8_t flags, uint32_t u_color, + uint32_t width) { + if (row >= screen.row || col >= screen.col) + return; + ScreenCell &c = buffer[row * screen.col + col]; + c.utf8 = text; + c.width = width; + c.fg = fg; + c.bg = bg; + c.flags = flags; + c.ul_color = u_color; + }; + if (this->selection_active) { Coord start, end; - if (editor->cursor >= editor->selection) { + if (this->cursor >= this->selection) { uint32_t prev_col, next_col; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - start = editor->selection; - end = move_right(editor, editor->cursor, 1); + start = this->selection; + end = this->move_right(this->cursor, 1); break; case WORD: - word_boundaries(editor, editor->selection, &prev_col, &next_col, - nullptr, nullptr); - start = {editor->selection.row, prev_col}; - end = editor->cursor; + this->word_boundaries(this->selection, &prev_col, &next_col, nullptr, + nullptr); + start = {this->selection.row, prev_col}; + end = this->cursor; break; case LINE: - start = {editor->selection.row, 0}; - end = editor->cursor; + start = {this->selection.row, 0}; + end = this->cursor; break; } } else { - start = editor->cursor; + start = this->cursor; uint32_t prev_col, next_col, line_len; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - end = move_right(editor, editor->selection, 1); + end = this->move_right(this->selection, 1); break; case WORD: - word_boundaries(editor, editor->selection, &prev_col, &next_col, - nullptr, nullptr); - end = {editor->selection.row, next_col}; + this->word_boundaries(this->selection, &prev_col, &next_col, nullptr, + nullptr); + end = {this->selection.row, next_col}; break; case LINE: - LineIterator *it = begin_l_iter(editor->root, editor->selection.row); + LineIterator *it = begin_l_iter(this->root, this->selection.row); char *line = next_line(it, &line_len); if (!line) return; @@ -75,40 +86,40 @@ void render_editor(Editor *editor) { line_len--; free(it->buffer); free(it); - end = {editor->selection.row, line_len}; + end = {this->selection.row, line_len}; break; } } - sel_start = line_to_byte(editor->root, start.row, nullptr) + start.col; - sel_end = line_to_byte(editor->root, end.row, nullptr) + end.col; + sel_start = line_to_byte(this->root, start.row, nullptr) + start.col; + sel_end = line_to_byte(this->root, end.row, nullptr) + end.col; } Coord cursor = {UINT32_MAX, UINT32_MAX}; - uint32_t line_index = editor->scroll.row; - LineIterator *it = begin_l_iter(editor->root, line_index); + uint32_t line_index = this->scroll.row; + LineIterator *it = begin_l_iter(this->root, line_index); if (!it) return; uint32_t prev_col, next_col; std::string word; - word_boundaries_exclusive(editor, editor->cursor, &prev_col, &next_col); + this->word_boundaries_exclusive(this->cursor, &prev_col, &next_col); if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) { - uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr); - char *word_ptr = read(editor->root, offset + prev_col, next_col - prev_col); + uint32_t offset = line_to_byte(this->root, this->cursor.row, nullptr); + char *word_ptr = read(this->root, offset + prev_col, next_col - prev_col); if (word_ptr) { word = std::string(word_ptr, next_col - prev_col); free(word_ptr); } } - editor->extra_hl.render(editor->root, line_index, word, editor->is_css_color); + this->extra_hl.render(this->root, line_index, word, this->is_css_color); uint32_t rendered_rows = 0; - uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr); - while (rendered_rows < editor->size.row) { + uint32_t global_byte_offset = line_to_byte(this->root, line_index, nullptr); + while (rendered_rows < this->size.row) { uint32_t line_len; char *line = next_line(it, &line_len); - if (editor->parser) { + if (this->parser) { if (line_data) - line_data = editor->parser->line_tree.next(); + line_data = this->parser->line_tree.next(); else - line_data = editor->parser->line_tree.start_iter(line_index); + line_data = this->parser->line_tree.start_iter(line_index); } if (!line) break; @@ -123,54 +134,51 @@ void render_editor(Editor *editor) { (line[content_start] == ' ' || line[content_start] == '\t')) content_start++; std::vector line_warnings; - while (warn_it != editor->warnings.end() && warn_it->line == line_index) { + while (warn_it != this->warnings.end() && warn_it->line == line_index) { line_warnings.push_back(*warn_it); ++warn_it; } uint32_t current_byte_offset = 0; if (rendered_rows == 0) - current_byte_offset += editor->scroll.col; - while (current_byte_offset < line_len && rendered_rows < editor->size.row) { - uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0; + current_byte_offset += this->scroll.col; + while (current_byte_offset < line_len && rendered_rows < this->size.row) { + uint32_t color = this->cursor.row == line_index ? 0x222222 : 0; if (current_byte_offset == 0 || rendered_rows == 0) { - const char *hook = nullptr; + const char *hook = ""; char h[2] = {0, 0}; if (hook_it != v.end() && hook_it->first == line_index + 1) { h[0] = hook_it->second; hook = h; hook_it++; } - update(editor->position.row + rendered_rows, editor->position.col, hook, - 0xAAAAAA, 0, 0); + update(pos.row + rendered_rows, pos.col, hook, 0xAAAAAA, 0, 0, 0, 1); char buf[16]; int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1); uint32_t num_color = - editor->cursor.row == line_index ? 0xFFFFFF : 0x555555; + this->cursor.row == line_index ? 0xFFFFFF : 0x555555; for (int i = 0; i < len; i++) - update(editor->position.row + rendered_rows, editor->position.col + i, - (char[2]){buf[i], 0}, num_color, 0, 0); + update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0}, + num_color, 0, 0, 0, 1); } else { for (uint32_t i = 0; i < numlen + 1; i++) - update(editor->position.row + rendered_rows, editor->position.col + i, - " ", 0, 0, 0); + update(pos.row + rendered_rows, pos.col + i, " ", 0, 0, 0, 0, 1); } uint32_t col = 0; uint32_t local_render_offset = 0; uint32_t line_left = line_len - current_byte_offset; while (line_left > 0 && col < render_width) { - if (line_index == editor->cursor.row && - editor->cursor.col == (current_byte_offset + local_render_offset)) { - cursor.row = editor->position.row + rendered_rows; + if (line_index == this->cursor.row && + this->cursor.col == (current_byte_offset + local_render_offset)) { + cursor.row = pos.row + rendered_rows; cursor.col = render_x + col; } uint32_t absolute_byte_pos = global_byte_offset + current_byte_offset + local_render_offset; const Highlight *hl = nullptr; - if (editor->parser) + if (this->parser) hl = &highlights[get_type(current_byte_offset + local_render_offset)]; - std::optional> extra = - editor->extra_hl.get( - {line_index, current_byte_offset + local_render_offset}); + std::optional> extra = this->extra_hl.get( + {line_index, current_byte_offset + local_render_offset}); uint32_t fg = extra && extra->second != UINT32_MAX ? extra->first : (hl ? hl->fg : 0xFFFFFF); @@ -181,7 +189,7 @@ void render_editor(Editor *editor) { (hl ? hl->flags : 0) | (extra ? (extra->second != UINT32_MAX ? CF_BOLD : CF_UNDERLINE) : 0); - if (editor->selection_active && absolute_byte_pos >= sel_start && + if (this->selection_active && absolute_byte_pos >= sel_start && absolute_byte_pos < sel_end) bg = bg | 0x555555; uint32_t u_color = 0; @@ -217,40 +225,39 @@ void render_editor(Editor *editor) { break; if (current_byte_offset + local_render_offset >= content_start && current_byte_offset + local_render_offset < content_end) { - update(editor->position.row + rendered_rows, render_x + col, - cluster.c_str(), fg, bg | color, fl, u_color); + update(pos.row + rendered_rows, render_x + col, cluster.c_str(), fg, + bg | color, fl, u_color, width); } else { if (cluster[0] == ' ') { - update(editor->position.row + rendered_rows, render_x + col, "·", - 0x282828, bg | color, fl, u_color); + update(pos.row + rendered_rows, render_x + col, "·", 0x282828, + bg | color, fl, u_color, 1); } else { - update(editor->position.row + rendered_rows, render_x + col, "-> ", - 0x282828, bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color); + update(pos.row + rendered_rows, render_x + col, "-> ", 0x282828, + bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color, 4); } } local_render_offset += cluster_len; line_left -= cluster_len; col += width; while (width-- > 1) - update(editor->position.row + rendered_rows, render_x + col - width, - "\x1b", fg, bg | color, fl); + update(pos.row + rendered_rows, render_x + col - width, "\x1b", fg, + bg | color, fl, u_color, 0); } - if (line_index == editor->cursor.row && - editor->cursor.col == (current_byte_offset + local_render_offset)) { - cursor.row = editor->position.row + rendered_rows; + if (line_index == this->cursor.row && + this->cursor.col == (current_byte_offset + local_render_offset)) { + cursor.row = pos.row + rendered_rows; cursor.col = render_x + col; } - if (editor->selection_active && + if (this->selection_active && global_byte_offset + line_len + 1 > sel_start && global_byte_offset + line_len + 1 <= sel_end && col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - 0x555555 | color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, + 0x555555 | color, 0, 0, 1); col++; } if (!line_warnings.empty() && line_left == 0) { VWarn warn = line_warnings.front(); - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1); col++; for (size_t i = 0; i < line_warnings.size(); i++) { if (line_warnings[i].type < warn.type) @@ -276,18 +283,18 @@ void render_editor(Editor *editor) { goto final; final: if (col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, - err_sym, fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col, err_sym, fg_color, + color, 0, 0, 1); col++; - update(editor->position.row + rendered_rows, render_x + col, " ", - fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col, " ", fg_color, + color, 0, 0, 1); col++; } } } if (col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - 0 | color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, + 0, 1); col++; } size_t warn_idx = 0; @@ -313,19 +320,19 @@ void render_editor(Editor *editor) { int width = display_width(cluster.c_str(), cluster_len); if (col + width > render_width) break; - update(editor->position.row + rendered_rows, render_x + col, - cluster.c_str(), fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col, cluster.c_str(), + fg_color, color, 0, 0, width); col += width; warn_idx += cluster_len; while (width-- > 1) - update(editor->position.row + rendered_rows, render_x + col - width, - "\x1b", fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col - width, "\x1b", + fg_color, color, 0, 0, 0); } line_warnings.clear(); } while (col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - 0 | color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0, + 1); col++; } rendered_rows++; @@ -333,39 +340,36 @@ void render_editor(Editor *editor) { } if (line_len == 0 || (current_byte_offset >= line_len && rendered_rows == 0)) { - uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0; - const char *hook = nullptr; + uint32_t color = this->cursor.row == line_index ? 0x222222 : 0; + const char *hook = ""; char h[2] = {0, 0}; if (hook_it != v.end() && hook_it->first == line_index + 1) { h[0] = hook_it->second; hook = h; hook_it++; } - update(editor->position.row + rendered_rows, editor->position.col, hook, - 0xAAAAAA, 0, 0); + update(pos.row + rendered_rows, pos.col, hook, 0xAAAAAA, 0, 0, 0, 1); char buf[16]; int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1); - uint32_t num_color = - editor->cursor.row == line_index ? 0xFFFFFF : 0x555555; + uint32_t num_color = this->cursor.row == line_index ? 0xFFFFFF : 0x555555; for (int i = 0; i < len; i++) - update(editor->position.row + rendered_rows, editor->position.col + i, - (char[2]){buf[i], 0}, num_color, 0, 0); - if (editor->cursor.row == line_index) { - cursor.row = editor->position.row + rendered_rows; + update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0}, + num_color, 0, 0, 0, 1); + if (this->cursor.row == line_index) { + cursor.row = pos.row + rendered_rows; cursor.col = render_x; } uint32_t col = 0; - if (editor->selection_active && + if (this->selection_active && global_byte_offset + line_len + 1 > sel_start && global_byte_offset + line_len + 1 <= sel_end) { - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - 0x555555 | color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, + 0x555555 | color, 0, 0, 1); col++; } if (!line_warnings.empty()) { VWarn warn = line_warnings.front(); - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1); col++; for (size_t i = 0; i < line_warnings.size(); i++) { if (line_warnings[i].type < warn.type) @@ -391,18 +395,18 @@ void render_editor(Editor *editor) { goto final2; final2: if (col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, - err_sym, fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col, err_sym, fg_color, + color, 0, 0, 1); col++; - update(editor->position.row + rendered_rows, render_x + col, " ", - fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col, " ", fg_color, + color, 0, 0, 1); col++; } } } if (col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - 0 | color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, + 0, 1); col++; } size_t warn_idx = 0; @@ -428,18 +432,18 @@ void render_editor(Editor *editor) { int width = display_width(cluster.c_str(), cluster_len); if (col + width > render_width) break; - update(editor->position.row + rendered_rows, render_x + col, - cluster.c_str(), fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col, cluster.c_str(), + fg_color, color, 0, 0, width); col += width; warn_idx += cluster_len; while (width-- > 1) - update(editor->position.row + rendered_rows, render_x + col - width, - "\x1b", fg_color, color, 0); + update(pos.row + rendered_rows, render_x + col - width, "\x1b", + fg_color, color, 0, 0, 0); } } while (col < render_width) { - update(editor->position.row + rendered_rows, render_x + col, " ", 0, - 0 | color, 0); + update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0, + 1); col++; } rendered_rows++; @@ -447,10 +451,9 @@ void render_editor(Editor *editor) { global_byte_offset += line_len + 1; line_index++; } - 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); + while (rendered_rows < this->size.row) { + for (uint32_t col = 0; col < this->size.col; col++) + update(pos.row + rendered_rows, pos.col + col, " ", 0xFFFFFF, 0, 0, 0, 1); rendered_rows++; } if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) { @@ -468,15 +471,17 @@ void render_editor(Editor *editor) { break; } set_cursor(cursor.row, cursor.col, type, true); - if (editor->completion.active && !editor->completion.box.hidden) - editor->completion.box.render(cursor); - else if (editor->hover_active) - editor->hover.render(cursor); - else if (editor->diagnostics_active) - editor->diagnostics.render(cursor); + // if (this->completion.active && !this->completion.box.hidden) + // this->completion.box.render(cursor); + // else if (this->hover_active) + // this->hover.render(cursor); + // else if (this->diagnostics_active) + // this->diagnostics.render(cursor); + if (this->hover_active) + ui::hover_popup->pos = cursor; } free(it->buffer); free(it); - if (editor->parser) - editor->parser->scroll(line_index + 5); + if (this->parser) + this->parser->scroll(line_index + 5); } diff --git a/src/editor/scroll.cc b/src/editor/scroll.cc index cf68698..2fd1aee 100644 --- a/src/editor/scroll.cc +++ b/src/editor/scroll.cc @@ -1,13 +1,13 @@ #include "editor/editor.h" -void scroll_up(Editor *editor, int32_t number) { - if (!editor || number == 0) +void Editor::scroll_up(uint32_t number) { + if (number == 0) return; uint32_t numlen = - EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - uint32_t render_width = editor->size.col - numlen; - uint32_t line_index = editor->scroll.row; - LineIterator *it = begin_l_iter(editor->root, line_index); + EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); + uint32_t render_width = this->size.col - numlen; + uint32_t line_index = this->scroll.row; + LineIterator *it = begin_l_iter(this->root, line_index); if (!it) return; uint32_t len; @@ -23,10 +23,9 @@ void scroll_up(Editor *editor, int32_t number) { uint32_t col = 0; std::vector segment_starts; segment_starts.reserve(16); - if (current_byte_offset < editor->scroll.col) + if (current_byte_offset < this->scroll.col) segment_starts.push_back(0); - while (current_byte_offset < editor->scroll.col && - current_byte_offset < len) { + while (current_byte_offset < this->scroll.col && current_byte_offset < len) { uint32_t cluster_len = grapheme_next_character_break_utf8( line + current_byte_offset, len - current_byte_offset); int width = display_width(line + current_byte_offset, cluster_len); @@ -40,7 +39,7 @@ void scroll_up(Editor *editor, int32_t number) { for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend(); ++it_seg) { if (--number == 0) { - editor->scroll = {line_index, *it_seg}; + this->scroll = {line_index, *it_seg}; free(it->buffer); free(it); return; @@ -48,7 +47,7 @@ void scroll_up(Editor *editor, int32_t number) { } line = prev_line(it, &len); if (!line) { - editor->scroll = {0, 0}; + this->scroll = {0, 0}; free(it->buffer); free(it); return; @@ -57,7 +56,7 @@ void scroll_up(Editor *editor, int32_t number) { line_index--; line = prev_line(it, &len); if (!line) { - editor->scroll = {0, 0}; + this->scroll = {0, 0}; free(it->buffer); free(it); return; @@ -83,7 +82,7 @@ void scroll_up(Editor *editor, int32_t number) { for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend(); ++it_seg) { if (--number == 0) { - editor->scroll = {line_index, *it_seg}; + this->scroll = {line_index, *it_seg}; free(it->buffer); free(it); return; @@ -94,17 +93,17 @@ void scroll_up(Editor *editor, int32_t number) { free(it); } -void scroll_down(Editor *editor, uint32_t number) { - if (!editor || number == 0) +void Editor::scroll_down(uint32_t number) { + if (number == 0) return; uint32_t numlen = - EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - uint32_t render_width = editor->size.col - numlen; - uint32_t line_index = editor->scroll.row; - LineIterator *it = begin_l_iter(editor->root, line_index); + EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); + uint32_t render_width = this->size.col - numlen; + uint32_t line_index = this->scroll.row; + LineIterator *it = begin_l_iter(this->root, line_index); if (!it) return; - const uint32_t max_visual_lines = editor->size.row; + const uint32_t max_visual_lines = this->size.row; Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines); uint32_t q_head = 0; uint32_t q_size = 0; @@ -119,7 +118,7 @@ void scroll_down(Editor *editor, uint32_t number) { line_len--; uint32_t current_byte_offset = 0; if (first_visual_line) { - current_byte_offset += editor->scroll.col; + current_byte_offset += this->scroll.col; first_visual_line = false; } while (current_byte_offset < line_len || @@ -134,7 +133,7 @@ void scroll_down(Editor *editor, uint32_t number) { } visual_seen++; if (visual_seen >= number + max_visual_lines) { - editor->scroll = scroll_queue[q_head]; + this->scroll = scroll_queue[q_head]; free(scroll_queue); free(it->buffer); free(it); @@ -162,7 +161,7 @@ void scroll_down(Editor *editor, uint32_t number) { } if (q_size > 0) { uint32_t advance = (q_size > number) ? number : (q_size - 1); - editor->scroll = scroll_queue[(q_head + advance) % max_visual_lines]; + this->scroll = scroll_queue[(q_head + advance) % max_visual_lines]; } free(it->buffer); free(it); diff --git a/src/editor/selection.cc b/src/editor/selection.cc index 609663c..2e3b374 100644 --- a/src/editor/selection.cc +++ b/src/editor/selection.cc @@ -1,47 +1,46 @@ #include "editor/editor.h" #include "utils/utils.h" -void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) { - std::shared_lock lock(editor->knot_mtx); +void Editor::selection_bounds(Coord *out_start, Coord *out_end) { Coord start, end; - if (editor->cursor >= editor->selection) { + if (this->cursor >= this->selection) { uint32_t prev_col; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - start = editor->selection; - end = move_right(editor, editor->cursor, 1); + start = this->selection; + end = this->move_right(this->cursor, 1); break; case WORD: - word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr, - nullptr); - start = {editor->selection.row, prev_col}; - end = editor->cursor; + this->word_boundaries(this->selection, &prev_col, nullptr, nullptr, + nullptr); + start = {this->selection.row, prev_col}; + end = this->cursor; break; case LINE: - start = {editor->selection.row, 0}; - end = editor->cursor; + start = {this->selection.row, 0}; + end = this->cursor; break; } } else { - start = editor->cursor; + start = this->cursor; uint32_t next_col, line_len; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - end = move_right(editor, editor->selection, 1); + end = this->move_right(this->selection, 1); break; case WORD: - word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr, - nullptr); - end = {editor->selection.row, next_col}; + this->word_boundaries(this->selection, nullptr, &next_col, nullptr, + nullptr); + end = {this->selection.row, next_col}; break; case LINE: - LineIterator *it = begin_l_iter(editor->root, editor->selection.row); + LineIterator *it = begin_l_iter(this->root, this->selection.row); char *line = next_line(it, &line_len); if (!line) return; if (line_len > 0 && line[line_len - 1] == '\n') line_len--; - end = {editor->selection.row, line_len}; + end = {this->selection.row, line_len}; free(it->buffer); free(it); break; @@ -53,47 +52,46 @@ void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) { *out_end = end; } -char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) { - std::shared_lock lock(editor->knot_mtx); +char *Editor::get_selection(uint32_t *out_len, Coord *out_start) { Coord start, end; - if (editor->cursor >= editor->selection) { + if (this->cursor >= this->selection) { uint32_t prev_col; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - start = editor->selection; - end = move_right(editor, editor->cursor, 1); + start = this->selection; + end = this->move_right(this->cursor, 1); break; case WORD: - word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr, - nullptr); - start = {editor->selection.row, prev_col}; - end = editor->cursor; + this->word_boundaries(this->selection, &prev_col, nullptr, nullptr, + nullptr); + start = {this->selection.row, prev_col}; + end = this->cursor; break; case LINE: - start = {editor->selection.row, 0}; - end = editor->cursor; + start = {this->selection.row, 0}; + end = this->cursor; break; } } else { - start = editor->cursor; + start = this->cursor; uint32_t next_col, line_len; - switch (editor->selection_type) { + switch (this->selection_type) { case CHAR: - end = move_right(editor, editor->selection, 1); + end = this->move_right(this->selection, 1); break; case WORD: - word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr, - nullptr); - end = {editor->selection.row, next_col}; + this->word_boundaries(this->selection, nullptr, &next_col, nullptr, + nullptr); + end = {this->selection.row, next_col}; break; case LINE: - LineIterator *it = begin_l_iter(editor->root, editor->selection.row); + LineIterator *it = begin_l_iter(this->root, this->selection.row); char *line = next_line(it, &line_len); if (!line) return nullptr; if (line_len > 0 && line[line_len - 1] == '\n') line_len--; - end = {editor->selection.row, line_len}; + end = {this->selection.row, line_len}; free(it->buffer); free(it); break; @@ -102,9 +100,9 @@ char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) { if (out_start) *out_start = start; uint32_t start_byte = - line_to_byte(editor->root, start.row, nullptr) + start.col; - uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col; - char *text = read(editor->root, start_byte, end_byte - start_byte); + line_to_byte(this->root, start.row, nullptr) + start.col; + uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col; + char *text = read(this->root, start_byte, end_byte - start_byte); if (out_len) *out_len = end_byte - start_byte; return text; diff --git a/src/editor/worker.cc b/src/editor/worker.cc index 2329a08..2c73aea 100644 --- a/src/editor/worker.cc +++ b/src/editor/worker.cc @@ -1,37 +1,38 @@ #include "editor/editor.h" -void hover_diagnostic(Editor *editor) { - std::shared_lock lock(editor->v_mtx); - static uint32_t last_line = UINT32_MAX; - if (last_line == editor->cursor.row && !editor->warnings_dirty) - return; - VWarn dummy; - dummy.line = editor->cursor.row; - editor->warnings_dirty = false; - last_line = editor->cursor.row; - auto first = - std::lower_bound(editor->warnings.begin(), editor->warnings.end(), dummy); - auto last = - std::upper_bound(editor->warnings.begin(), editor->warnings.end(), dummy); - std::vector warnings_at_line(first, last); - if (warnings_at_line.size() == 0) { - editor->diagnostics_active = false; - return; - } - editor->diagnostics.clear(); - editor->diagnostics.warnings.swap(warnings_at_line); - editor->diagnostics.render_first(); - editor->diagnostics_active = true; -} +// void hover_diagnostic(Editor *editor) { +// static uint32_t last_line = UINT32_MAX; +// if (last_line == editor->cursor.row && !editor->warnings_dirty) +// return; +// VWarn dummy; +// dummy.line = editor->cursor.row; +// editor->warnings_dirty = false; +// last_line = editor->cursor.row; +// auto first = +// std::lower_bound(editor->warnings.begin(), editor->warnings.end(), +// dummy); +// auto last = +// std::upper_bound(editor->warnings.begin(), editor->warnings.end(), +// dummy); +// std::vector warnings_at_line(first, last); +// if (warnings_at_line.size() == 0) { +// editor->diagnostics_active = false; +// return; +// } +// editor->diagnostics.clear(); +// editor->diagnostics.warnings.swap(warnings_at_line); +// editor->diagnostics.render_first(); +// editor->diagnostics_active = true; +// } -void editor_worker(Editor *editor) { - if (!editor || !editor->root) +void Editor::work() { + if (!this->root) return; - if (editor->parser) - editor->parser->work(); - hover_diagnostic(editor); - if (editor->completion.active && editor->completion.hover_dirty) { - editor->completion.hover.render_first(); - editor->completion.hover_dirty = false; - } + if (this->parser) + this->parser->work(); + // hover_diagnostic(this); + // if (this->completion.active && this->completion.hover_dirty) { + // this->completion.hover.render_first(); + // this->completion.hover_dirty = false; + // } } diff --git a/src/extentions/completion.cc b/src/extentions/completion.cc new file mode 100644 index 0000000..e69de29 diff --git a/src/ui/hover.cc b/src/extentions/hover.cc similarity index 57% rename from src/ui/hover.cc rename to src/extentions/hover.cc index d0df429..8e49f7a 100644 --- a/src/ui/hover.cc +++ b/src/extentions/hover.cc @@ -1,12 +1,15 @@ -#include "ui/hover.h" +#include "extentions/hover.h" #include "syntax/decl.h" +#include "windows/decl.h" -void HoverBox::clear() { - text = ""; - scroll_ = 0; - is_markup = false; - size = {0, 0}; - cells.clear(); +TileRoot *init_hover() { + auto root = std::make_unique(); + root->tile = std::make_unique(); + root->pos = {0, 0}; + root->size = {1, 1}; + root->tile->hidden = true; + popups.push_back(std::move(root)); + return popups.back().get(); } void HoverBox::scroll(int32_t number) { @@ -19,13 +22,23 @@ void HoverBox::scroll(int32_t number) { scroll_ = MAX((int32_t)scroll_ + number, 0); if (scroll_ > line_count) scroll_ = line_count; - render_first(true); + scroll_dirty = true; } -void HoverBox::render_first(bool scroll) { - if (!scroll) { +void HoverBox::render(std::vector &buffer, Coord size, Coord pos) { + if (scroll_dirty) { // TODO: call syntax highlighter here } + // int32_t start_row = (int32_t)pos.row - (int32_t)size.row; + // if (start_row < 0) + // start_row = pos.row + 1; + // int32_t start_col = pos.col; // pos here is the cursor pos + Coord screen_size = get_size(); + // if (start_col + size.col > screen_size.col) { + // start_col = screen_size.col - size.col; + // if (start_col < 0) + // start_col = 0; + // } uint32_t longest_line = 0; uint32_t current_width = 0; for (size_t j = 0; j < text.length(); j++) { @@ -36,7 +49,7 @@ void HoverBox::render_first(bool scroll) { current_width += 1; } } - // HACK: the 1 is added so the longest line doesnt wrap which should be fixed + // HACK: the 1 is added so the longest line doesnt wrap which should befixed // in the loop instead as it was never meant to wrap in the first place longest_line = MAX(longest_line, current_width) + 1; uint32_t content_width = MIN(longest_line, 130u); @@ -50,10 +63,19 @@ void HoverBox::render_first(bool scroll) { } uint32_t border_fg = 0x82AAFF; uint32_t base_bg = 0; - cells.assign(size.col * 26, ScreenCell{" ", 0, 0, 0, 0, 0}); auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, - uint32_t bg, uint8_t flags) { - cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; + uint32_t bg, uint8_t flags, uint32_t width) { + if (r < 0 || r >= size.row || c < 0 || c >= size.col) + return; + r += pos.row; + c += pos.col; + ScreenCell &cell = buffer[r * screen_size.col + c]; + cell.utf8 = text; + cell.width = width; + cell.fg = fg; + cell.bg = bg; + cell.flags = flags; + cell.ul_color = 0; }; uint32_t r = 0; while (i < text.length() && r < 24) { @@ -75,45 +97,27 @@ void HoverBox::render_first(bool scroll) { uint32_t fg = hl ? hl->fg : 0xFFFFFF; uint32_t bg = hl ? hl->bg : 0; uint32_t flags = hl ? hl->flags : 0; - set(r + 1, c + 1, cluster.c_str(), fg, bg | base_bg, flags); + set(r + 1, c + 1, cluster.c_str(), fg, bg | base_bg, flags, width); c += width; i += cluster_len; for (int w = 1; w < width; w++) - set(r + 1, c - w + 1, "\x1b", 0xFFFFFF, base_bg, 0); + set(r + 1, c - w + 1, "\x1b", 0xFFFFFF, base_bg, 0, 0); } r++; } - if (!scroll) + if (scroll_dirty) size.row = r + 2; - set(0, 0, "┌", border_fg, base_bg, 0); + set(0, 0, "┌", border_fg, base_bg, 0, 1); for (uint32_t i = 1; i < size.col - 1; i++) - set(0, i, "─", border_fg, base_bg, 0); - set(0, size.col - 1, "┐", border_fg, base_bg, 0); + set(0, i, "─", border_fg, base_bg, 0, 1); + set(0, size.col - 1, "┐", border_fg, base_bg, 0, 1); for (uint32_t r = 1; r < size.row - 1; r++) { - set(r, 0, "│", border_fg, base_bg, 0); - set(r, size.col - 1, "│", border_fg, base_bg, 0); + set(r, 0, "│", border_fg, base_bg, 0, 1); + set(r, size.col - 1, "│", border_fg, base_bg, 0, 1); } - set(size.row - 1, 0, "└", border_fg, base_bg, 0); + set(size.row - 1, 0, "└", border_fg, base_bg, 0, 1); for (uint32_t i = 1; i < size.col - 1; i++) - set(size.row - 1, i, "─", border_fg, base_bg, 0); - set(size.row - 1, size.col - 1, "┘", border_fg, base_bg, 0); - cells.resize(size.col * size.row); -} - -void HoverBox::render(Coord pos) { - int32_t start_row = (int32_t)pos.row - (int32_t)size.row; - if (start_row < 0) - start_row = pos.row + 1; - int32_t start_col = pos.col; - Coord screen_size = get_size(); - if (start_col + size.col > screen_size.col) { - start_col = screen_size.col - size.col; - if (start_col < 0) - start_col = 0; - } - for (uint32_t r = 0; r < size.row; r++) - for (uint32_t c = 0; c < size.col; c++) - update(start_row + r, start_col + c, cells[r * size.col + c].utf8, - cells[r * size.col + c].fg, cells[r * size.col + c].bg, - cells[r * size.col + c].flags); + set(size.row - 1, i, "─", border_fg, base_bg, 0, 1); + set(size.row - 1, size.col - 1, "┘", border_fg, base_bg, 0, 1); + scroll_dirty = false; } diff --git a/src/io/renderer.cc b/src/io/renderer.cc index 6d33cfe..92b3010 100644 --- a/src/io/renderer.cc +++ b/src/io/renderer.cc @@ -1,10 +1,9 @@ #include "io/sysio.h" +std::vector new_screen; static uint32_t rows, cols; static bool show_cursor = 0; -static std::vector screen; static std::vector old_screen; -static std::mutex screen_mutex; static termios orig_termios; void disable_raw_mode() { @@ -24,7 +23,7 @@ void enable_raw_mode() { raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_oflag &= ~(OPOST); raw.c_cflag |= (CS8); - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); // | ISIG); raw.c_cc[VMIN] = 0; raw.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) @@ -39,7 +38,7 @@ Coord start_screen() { ioctl(0, TIOCGWINSZ, &w); rows = w.ws_row; cols = w.ws_col; - screen.assign(rows * cols, {}); + new_screen.assign(rows * cols, {}); old_screen.assign(rows * cols, {}); return {rows, cols}; } @@ -48,71 +47,7 @@ void end_screen() { disable_raw_mode(); } Coord get_size() { return {rows, cols}; } -void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg, - uint32_t bg, uint8_t flags) { - if (row >= rows || col >= cols) - return; - uint32_t idx = row * cols + col; - std::lock_guard lock(screen_mutex); - screen[idx].utf8 = utf8 != "" ? utf8 : ""; - if (utf8 == "") - return; - screen[idx].width = display_width(utf8.c_str(), utf8.size()); - screen[idx].fg = fg; - screen[idx].bg = bg; - screen[idx].flags = flags; - screen[idx].ul_color = 0; -} - -void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg, - uint32_t bg, uint8_t flags) { - if (row >= rows || col >= cols) - return; - uint32_t idx = row * cols + col; - std::lock_guard lock(screen_mutex); - screen[idx].utf8 = utf8 ? utf8 : ""; - if (utf8 == nullptr) - return; - screen[idx].width = display_width(utf8, strlen(utf8)); - screen[idx].fg = fg; - screen[idx].bg = bg; - screen[idx].flags = flags; - screen[idx].ul_color = 0; -} - -void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg, - uint32_t bg, uint8_t flags, uint32_t ul_color) { - if (row >= rows || col >= cols) - return; - uint32_t idx = row * cols + col; - std::lock_guard lock(screen_mutex); - screen[idx].utf8 = utf8 != "" ? utf8 : ""; - if (utf8 == "") - return; - screen[idx].width = display_width(utf8.c_str(), utf8.size()); - screen[idx].fg = fg; - screen[idx].bg = bg; - screen[idx].flags = flags; - screen[idx].ul_color = ul_color; -} - -void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg, - uint32_t bg, uint8_t flags, uint32_t ul_color) { - if (row >= rows || col >= cols) - return; - uint32_t idx = row * cols + col; - std::lock_guard lock(screen_mutex); - screen[idx].utf8 = utf8 ? utf8 : ""; - if (utf8 == nullptr) - return; - screen[idx].width = display_width(utf8, strlen(utf8)); - screen[idx].fg = fg; - screen[idx].bg = bg; - screen[idx].flags = flags; - screen[idx].ul_color = ul_color; -} - -void render() { +void io_render() { static bool first_render = true; uint32_t current_fg = 0; uint32_t current_bg = 0; @@ -121,7 +56,6 @@ void render() { bool current_bold = false; bool current_strikethrough = false; bool current_underline = false; - std::lock_guard lock(screen_mutex); std::string out; out.reserve(static_cast(rows) * static_cast(cols) * 4 + 256); out += "\x1b[s\x1b[?25l"; @@ -135,7 +69,7 @@ void render() { for (uint32_t col = 0; col < cols; ++col) { uint32_t idx = row * cols + col; ScreenCell &old_cell = old_screen[idx]; - ScreenCell &new_cell = screen[idx]; + ScreenCell &new_cell = new_screen[idx]; bool content_changed = old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg || old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags || @@ -147,7 +81,7 @@ void render() { for (int64_t back = 1; back <= 4 && first_change_col - back >= 0; ++back) { ScreenCell &prev_cell = - screen[row * cols + (first_change_col - back)]; + new_screen[row * cols + (first_change_col - back)]; if (prev_cell.width > 1) { first_change_col -= back; break; @@ -165,7 +99,7 @@ void render() { for (uint32_t col = first_change_col; col <= last_change_col; ++col) { uint32_t idx = row * cols + col; ScreenCell &old_cell = old_screen[idx]; - ScreenCell &new_cell = screen[idx]; + ScreenCell &new_cell = new_screen[idx]; uint32_t width = new_cell.width > 0 ? new_cell.width : 1; bool overlap = false; if (width > 1) { @@ -173,7 +107,7 @@ void render() { uint32_t next_col = col + i; if (next_col >= cols) break; - const ScreenCell &next = screen[row * cols + next_col]; + const ScreenCell &next = new_screen[row * cols + next_col]; if (!is_empty_cell(next)) { overlap = true; break; @@ -244,7 +178,7 @@ void render() { uint32_t next_col = col + i; if (next_col >= cols) break; - const ScreenCell &next = screen[row * cols + next_col]; + const ScreenCell &next = new_screen[row * cols + next_col]; if (!is_empty_cell(next)) break; out.push_back(' '); @@ -260,7 +194,7 @@ void render() { ScreenCell *cell = &new_cell; int back = 0; while ((int)col - back >= 0 && cell->utf8[0] == '\x1b') - cell = &screen[row * cols + col - ++back]; + cell = &new_screen[row * cols + col - ++back]; if (width >= cell->width) out.append(" "); } diff --git a/src/lsp/handlers.cc b/src/lsp/handlers.cc index 555f1de..e69de29 100644 --- a/src/lsp/handlers.cc +++ b/src/lsp/handlers.cc @@ -1,87 +0,0 @@ -#include "lsp/lsp.h" - -Queue lsp_open_queue; - -void request_add_to_lsp(Language language, Editor *editor) { - lsp_open_queue.push({language, editor}); -} - -void add_to_lsp(Language language, Editor *editor) { - std::shared_ptr lsp = get_or_init_lsp(language.lsp_name); - if (!lsp) - return; - std::unique_lock lock(lsp->mtx); - if (editor->lsp == lsp) - return; - lsp->editors.push_back(editor); - lsp->open_queue.push({language, editor}); - lock.unlock(); -} - -void open_editor(std::shared_ptr lsp, - std::pair entry) { - Language language = entry.first; - Editor *editor = entry.second; - if (editor->lsp == lsp) - return; - editor->lsp = lsp; - std::unique_lock lock3(editor->knot_mtx); - char *buf = read(editor->root, 0, editor->root->char_count); - std::string text(buf); - free(buf); - json message = {{"jsonrpc", "2.0"}, - {"method", "textDocument/didOpen"}, - {"params", - {{"textDocument", - {{"uri", editor->uri}, - {"languageId", language.name}, - {"version", 1}, - {"text", text}}}}}}; - lock3.unlock(); - lsp_send(lsp, message, nullptr); -} - -static std::string find_lsp_id(std::shared_ptr needle) { - for (const auto &[id, lsp] : active_lsps) - if (lsp == needle) - return id; - return ""; -} - -void remove_from_lsp(Editor *editor) { - auto lsp = editor->lsp; - if (!lsp) - return; - std::unique_lock lock1(lsp->mtx); - lsp->editors.erase( - std::remove(lsp->editors.begin(), lsp->editors.end(), editor), - lsp->editors.end()); - lock1.unlock(); - std::unique_lock lock2(editor->lsp_mtx); - editor->lsp = nullptr; - lock2.unlock(); - json message = {{"jsonrpc", "2.0"}, - {"method", "textDocument/didClose"}, - {"params", {{"textDocument", {{"uri", editor->uri}}}}}}; - lsp_send(lsp, message, nullptr); - std::string lsp_id = find_lsp_id(lsp); - if (!lsp_id.empty() && lsp->editors.empty()) - close_lsp(lsp_id); -} - -void lsp_handle(std::shared_ptr, json message) { - std::string method = message.value("method", ""); - if (method == "window/showMessage") { - if (message.contains("params")) { - auto &p = message["params"]; - if (p.contains("message")) - log("%s\n", p["message"].get().c_str()); - } - } else if (method == "window/logMessage") { - if (message.contains("params")) { - auto &p = message["params"]; - if (p.contains("message")) - log("%s\n", p["message"].get().c_str()); - } - } -} diff --git a/src/lsp/process.cc b/src/lsp/process.cc index 0531b11..e69de29 100644 --- a/src/lsp/process.cc +++ b/src/lsp/process.cc @@ -1,242 +0,0 @@ -#include "lsp/lsp.h" - -static bool init_lsp(std::shared_ptr lsp) { - int in_pipe[2]; - int out_pipe[2]; - if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) { - perror("pipe"); - return false; - } - pid_t pid = fork(); - if (pid == -1) { - perror("fork"); - return false; - } - if (pid == 0) { - dup2(in_pipe[0], STDIN_FILENO); - dup2(out_pipe[1], STDOUT_FILENO); - int devnull = open("/dev/null", O_WRONLY); - if (devnull >= 0) { - dup2(devnull, STDERR_FILENO); - close(devnull); - } - close(in_pipe[0]); - close(in_pipe[1]); - close(out_pipe[0]); - close(out_pipe[1]); - std::vector argv; - argv.push_back(const_cast(lsp->lsp->command.c_str())); - for (auto &arg : lsp->lsp->args) - argv.push_back(const_cast(arg.c_str())); - argv.push_back(nullptr); - execvp(lsp->lsp->command.c_str(), argv.data()); - perror("execvp"); - _exit(127); - } - lsp->pid = pid; - lsp->stdin_fd = in_pipe[1]; - lsp->stdout_fd = out_pipe[0]; - close(in_pipe[0]); - close(out_pipe[1]); - return true; -} - -std::shared_ptr get_or_init_lsp(std::string lsp_id) { - std::unique_lock lock(active_lsps_mtx); - auto it = active_lsps.find(lsp_id); - if (it == active_lsps.end()) { - auto map_it = lsps.find(lsp_id); - if (map_it == lsps.end()) - return nullptr; - std::shared_ptr lsp = std::make_shared(); - lsp->lsp = &map_it->second; - if (!init_lsp(lsp)) - return nullptr; - LSPPending *pending = new LSPPending(); - pending->editor = nullptr; - pending->callback = [lsp, lsp_id](Editor *, const json &msg) { - if (msg.contains("result") && msg["result"].contains("capabilities")) { - auto &caps = msg["result"]["capabilities"]; - // if (caps.contains("positionEncoding")) { - // std::string s = caps["positionEncoding"].get(); - // if (s == "utf-8") - // lsp->is_utf8 = true; - // log("Lsp name: %s, supports: %s", lsp->lsp->command.c_str(), - // s.c_str()); - // } - if (caps.contains("textDocumentSync")) { - auto &sync = caps["textDocumentSync"]; - if (sync.is_number()) { - int change_type = sync.get(); - lsp->incremental_sync = (change_type == 2); - } else if (sync.is_object() && sync.contains("change")) { - int change_type = sync["change"].get(); - lsp->incremental_sync = (change_type == 2); - } - } - lsp->allow_formatting = caps.value("documentFormattingProvider", false); - if (lsp_id != "lua-language-server" /* Lua ls gives terrible ontype - formatting so disable */ - && caps.contains("documentOnTypeFormattingProvider")) { - auto &fmt = caps["documentOnTypeFormattingProvider"]; - if (fmt.is_object()) { - if (fmt.contains("firstTriggerCharacter")) { - std::string s = fmt["firstTriggerCharacter"].get(); - if (s.size() == 1) - lsp->format_chars.push_back(s[0]); - } - if (fmt.contains("moreTriggerCharacter")) { - for (auto &c : fmt["moreTriggerCharacter"]) { - std::string s = c.get(); - if (s.size() == 1) - lsp->format_chars.push_back(s[0]); - } - } - lsp->allow_formatting_on_type = true; - } else if (fmt.is_boolean()) { - lsp->allow_formatting_on_type = fmt.get(); - } - } - if (caps.contains("hoverProvider")) { - auto &hover = caps["hoverProvider"]; - lsp->allow_hover = - hover.is_boolean() ? hover.get() : hover.is_object(); - } else { - lsp->allow_hover = false; - } - if (caps.contains("completionProvider")) { - lsp->allow_completion = true; - if (caps["completionProvider"].contains("resolveProvider")) - lsp->allow_resolve = - caps["completionProvider"]["resolveProvider"].get(); - if (caps["completionProvider"].contains("triggerCharacters")) { - auto &chars = caps["completionProvider"]["triggerCharacters"]; - if (chars.is_array()) { - for (auto &c : chars) { - std::string str = c.get(); - if (str.size() != 1) - continue; - lsp->trigger_chars.push_back(str[0]); - } - } - } - if (caps["completionProvider"].contains("allCommitCharacters")) { - auto &chars = caps["completionProvider"]["allCommitCharacters"]; - if (chars.is_array()) { - for (auto &c : chars) { - std::string str = c.get(); - if (str.size() != 1) - continue; - lsp->end_chars.push_back(str[0]); - } - } - } - } - } - lsp->initialized = true; - json initialized = {{"jsonrpc", "2.0"}, - {"method", "initialized"}, - {"params", json::object()}}; - lsp_send(lsp, initialized, nullptr); - }; - json init_message = { - {"jsonrpc", "2.0"}, - {"method", "initialize"}, - {"params", - {{"processId", getpid()}, - {"rootUri", "file://" + percent_encode(path_abs("."))}, - {"capabilities", client_capabilities}}}}; - lsp_send(lsp, init_message, pending); - active_lsps[lsp_id] = lsp; - return lsp; - } - return it->second; -} - -void close_lsp(std::string lsp_id) { - std::shared_ptr lsp; - { - std::shared_lock lock(active_lsps_mtx); - auto it = active_lsps.find(lsp_id); - if (it == active_lsps.end()) - return; - lsp = it->second; - } - if (!lsp || lsp->pid == -1 || lsp->exited) - return; - lsp->exited = true; - lsp->initialized = false; - auto send_raw = [&](const json &msg) { - std::string payload = msg.dump(); - std::string header = - "Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n"; - std::string out = header + payload; - const char *ptr = out.data(); - size_t remaining = out.size(); - while (remaining > 0) { - ssize_t n = write(lsp->stdin_fd, ptr, remaining); - if (n <= 0) { - if (errno == EINTR) - continue; - break; - } - ptr += n; - remaining -= n; - } - }; - json shutdown = {{"jsonrpc", "2.0"}, {"id", 1}, {"method", "shutdown"}}; - send_raw(shutdown); - { - pollfd pfd{lsp->stdout_fd, POLLIN, 0}; - int timeout_ms = 300; - if (poll(&pfd, 1, timeout_ms) > 0) { - auto msg = read_lsp_message(lsp->stdout_fd); - (void)msg; - } - } - json exit_msg = {{"jsonrpc", "2.0"}, {"method", "exit"}}; - send_raw(exit_msg); - const int max_wait_ms = 500; - int waited = 0; - while (waited < max_wait_ms) { - int status; - pid_t res = waitpid(lsp->pid, &status, WNOHANG); - if (res == lsp->pid) - break; - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - waited += 10; - } - if (kill(lsp->pid, 0) == 0) { - kill(lsp->pid, SIGKILL); - waitpid(lsp->pid, nullptr, 0); - } - close(lsp->stdin_fd); - close(lsp->stdout_fd); - { - std::unique_lock lock(lsp->mtx); - for (auto &kv : lsp->pending) - delete kv.second; - lsp->pending.clear(); - } - for (auto &editor : lsp->editors) { - std::unique_lock editor_lock(editor->lsp_mtx); - editor->lsp = nullptr; - } - { - std::unique_lock lock(active_lsps_mtx); - active_lsps.erase(lsp_id); - } -} - -void clean_lsp(std::shared_ptr lsp, std::string lsp_id) { - for (auto &kv : lsp->pending) - delete kv.second; - lsp->pid = -1; - close(lsp->stdin_fd); - close(lsp->stdout_fd); - for (auto &editor : lsp->editors) { - std::unique_lock editor_lock(editor->lsp_mtx); - editor->lsp = nullptr; - } - active_lsps.erase(lsp_id); -} diff --git a/src/lsp/worker.cc b/src/lsp/worker.cc new file mode 100644 index 0000000..9a2281f --- /dev/null +++ b/src/lsp/worker.cc @@ -0,0 +1,44 @@ +#include "lsp/lsp.h" + +namespace lsp { +std::mutex lsp_mutex; +std::unordered_map> active_lsps; +std::unordered_set opened; +Queue need_opening; +std::vector new_editors; +} // namespace lsp + +void lsp_worker() { + std::lock_guard lock(lsp::lsp_mutex); + while (!lsp::need_opening.empty()) { + auto lsp_id = *lsp::need_opening.front(); + if (!lsp::opened.contains(lsp_id)) { + lsp::active_lsps[lsp_id] = std::make_unique(lsp_id); + lsp::opened.insert(lsp_id); + } + lsp::need_opening.pop(); + } + for (auto it = lsp::new_editors.begin(); it != lsp::new_editors.end();) { + auto ed = *it; + auto lsp_id = ed->lang.lsp_name; + if (lsp::opened.contains(lsp_id)) { + auto a_it = lsp::active_lsps.find(lsp_id); + if (a_it != lsp::active_lsps.end()) + a_it->second->add(ed); + it = lsp::new_editors.erase(it); + } else { + lsp::need_opening.push(lsp_id); + it++; + } + } + for (auto it = lsp::active_lsps.begin(); it != lsp::active_lsps.end();) { + auto &lsp_inst = it->second; + if (!lsp_inst || lsp_inst->pid == -1 || lsp_inst->editors.empty() || + lsp_inst->exited) { + it = lsp::active_lsps.erase(it); + continue; + } + lsp_inst->work(); + ++it; + } +} diff --git a/src/lsp/workers.cc b/src/lsp/workers.cc deleted file mode 100644 index 8a6132d..0000000 --- a/src/lsp/workers.cc +++ /dev/null @@ -1,170 +0,0 @@ -#include "lsp/lsp.h" - -std::shared_mutex active_lsps_mtx; -std::unordered_map> active_lsps; - -void lsp_send(std::shared_ptr lsp, json message, - LSPPending *pending) { - if (!lsp || lsp->stdin_fd == -1) - return; - std::unique_lock lock(lsp->mtx); - if (pending) { - message["id"] = lsp->last_id++; - uint32_t id = message["id"].get(); - lsp->pending[id] = pending; - } - lsp->outbox.push(message); -} - -std::optional read_lsp_message(int fd) { - std::string header; - char c; - while (true) { - ssize_t n = read(fd, &c, 1); - if (n <= 0) - return std::nullopt; - header.push_back(c); - if (header.size() >= 4 && header.substr(header.size() - 4) == "\r\n\r\n") - break; - } - size_t pos = header.find("Content-Length:"); - if (pos == std::string::npos) - return std::nullopt; - pos += strlen("Content-Length:"); - while (pos < header.size() && std::isspace(header[pos])) - pos++; - size_t end = pos; - while (end < header.size() && std::isdigit(header[end])) - end++; - size_t len = std::stoul(header.substr(pos, end - pos)); - std::string body(len, '\0'); - size_t got = 0; - while (got < len) { - ssize_t n = read(fd, &body[got], len - got); - if (n <= 0) - return std::nullopt; - got += n; - } - return json::parse(body); -} - -static Editor *editor_for_uri(std::shared_ptr lsp, - std::string uri) { - if (uri.empty()) - return nullptr; - for (auto &editor : lsp->editors) - if (editor->uri == uri) - return editor; - return nullptr; -} - -void lsp_worker() { - LSPOpenRequest request; - while (lsp_open_queue.pop(request)) - add_to_lsp(request.language, request.editor); - std::unique_lock active_lsps_lock(active_lsps_mtx); - for (auto &kv : active_lsps) { - std::shared_ptr lsp = kv.second; - std::unique_lock lock(lsp->mtx); - int status; - pid_t res = waitpid(lsp->pid, &status, WNOHANG); - if (res == lsp->pid) { - clean_lsp(lsp, kv.first); - return; - } - if (lsp->initialized) { - std::pair request; - while (lsp->open_queue.pop(request)) { - lock.unlock(); - open_editor(lsp, request); - lock.lock(); - } - } - while (!lsp->outbox.empty()) { - json message = lsp->outbox.front(); - std::string m = message.value("method", ""); - if (lsp->exited) { - if (m != "exit" && m != "shutdown") { - lsp->outbox.pop(message); - continue; - } - } - if (!lsp->initialized) { - if (m != "initialize" && m != "exit" && m != "shutdown") - break; - } - lsp->outbox.pop(message); - std::string payload = message.dump(); - std::string header = - "Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n"; - std::string out = header + payload; - const char *ptr = out.data(); - size_t remaining = out.size(); - while (remaining > 0) { - int status; - pid_t res = waitpid(lsp->pid, &status, WNOHANG); - if (res == lsp->pid) { - clean_lsp(lsp, kv.first); - return; - } - ssize_t written = write(lsp->stdin_fd, ptr, remaining); - if (written == 0) - break; - else if (written == -1) { - if (errno == EINTR) - continue; - perror("write"); - clean_lsp(lsp, kv.first); - return; - } else { - ptr += written; - remaining -= written; - } - } - } - pollfd pfd{lsp->stdout_fd, POLLIN | POLLHUP | POLLERR, 0}; - int r = poll(&pfd, 1, 0); - if (r > 0 && pfd.revents & (POLLHUP | POLLERR)) { - clean_lsp(lsp, kv.first); - return; - } - while ((r = poll(&pfd, 1, 0) > 0)) { - if (r > 0 && pfd.revents & (POLLHUP | POLLERR)) { - clean_lsp(lsp, kv.first); - return; - } - auto msg = read_lsp_message(lsp->stdout_fd); - if (!msg) - break; - if (msg->contains("id")) { - uint32_t id = msg->at("id").get(); - auto it = lsp->pending.find(id); - if (it != lsp->pending.end()) { - LSPPending *pend = it->second; - lock.unlock(); - if (pend->callback) - pend->callback(pend->editor, *msg); - delete pend; - lock.lock(); - lsp->pending.erase(it); - } - } else if (msg->contains("method")) { - std::string uri; - if (msg->contains("params")) { - auto &p = (*msg)["params"]; - if (p.contains("textDocument") && p["textDocument"].contains("uri")) - uri = p["textDocument"]["uri"].get(); - else if (p.contains("uri")) - uri = p["uri"].get(); - } - Editor *ed = editor_for_uri(lsp, uri); - lock.unlock(); - if (ed) - editor_lsp_handle(ed, *msg); - else - lsp_handle(lsp, *msg); - lock.lock(); - } - } - } -} diff --git a/src/main.cc b/src/main.cc index d70d002..0893134 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,42 +1,28 @@ #include "main.h" #include "editor/editor.h" +#include "extentions/hover.h" #include "io/sysio.h" #include "lsp/lsp.h" #include "ruby/decl.h" #include "ui/bar.h" #include "utils/utils.h" +#include "windows/decl.h" std::atomic running{true}; Queue event_queue; -std::vector editors; -Bar bar; - -uint8_t current_editor = 0; +fs::path pwd; std::atomic mode = NORMAL; +namespace ui { +Bar bar; +TileRoot *hover_popup = nullptr; +} // namespace ui + void background_lsp() { while (running) throttle(8ms, lsp_worker); } -inline Editor *editor_at(uint8_t x, uint8_t y) { - for (Editor *ed : editors) { - Coord pos = ed->position; - Coord size = ed->size; - if (x >= pos.col && x < pos.col + size.col && y >= pos.row && - y < pos.row + size.row) - return ed; - } - return nullptr; -} - -inline uint8_t index_of(Editor *ed) { - for (uint8_t i = 0; i < editors.size(); i++) - if (editors[i] == ed) - return i; - return 0; -} - int main(int argc, char *argv[]) { ruby_start(); load_theme(); @@ -46,54 +32,45 @@ int main(int argc, char *argv[]) { Coord screen = start_screen(); const char *filename = (argc > 1) ? argv[1] : ""; uint8_t eol = read_line_endings(); - Editor *editor = - new_editor(filename, {0, 0}, {screen.row - 2, screen.col}, eol); - bar.init(screen); - if (!editor) { - end_screen(); - fprintf(stderr, "Failed to load editor\n"); - return 1; - } + ui::hover_popup = init_hover(); - editors.push_back(editor); - current_editor = editors.size() - 1; + root_tile.size = {screen.row - 2, screen.col - 1}; + root_tile.pos = {0, 0}; + + root_tile.tile = std::make_unique(filename, eol); + focused_window = static_cast(root_tile.tile.get()); + ui::bar.init(screen); std::thread lsp_thread(background_lsp); while (running) { KeyEvent event = throttle(1ms, read_key); - if (event.key_type == KEY_NONE) - goto render; - if (event.key_type == KEY_CHAR && event.len == 1 && - event.c[0] == CTRL('q')) { - free(event.c); - running = false; - break; - } - if (mode != RUNNER) { - if (event.key_type == KEY_MOUSE) { - Editor *target = editor_at(event.mouse_x, event.mouse_y); - if (target) { - 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); + if (event.key_type != KEY_NONE) { + if (event.key_type == KEY_CHAR && event.len == 1 && + event.c[0] == CTRL('q')) { + free(event.c); + running = false; + break; + } + if (mode != RUNNER) { + if (event.key_type == KEY_MOUSE) { + handle_click(event); + } else { + focused_window->handle_event(event); } } else { - handle_editor_event(editors[current_editor], event); + ui::bar.handle_event(event); } - } else { - bar.handle(event); + if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && + event.c) + free(event.c); } - if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c) - free(event.c); - render: - throttle(4ms, editor_worker, editors[current_editor]); - bar.render(); - render_editor(editors[current_editor]); + for (auto &lsp_inst : lsp::active_lsps) + lsp_inst.second->callbacks(); + focused_window->work(); throttle(4ms, render); + throttle(4ms, io_render); } if (lsp_thread.joinable()) @@ -101,9 +78,6 @@ int main(int argc, char *argv[]) { end_screen(); - for (auto editor : editors) - free_editor(editor); - ruby_shutdown(); return 0; diff --git a/src/ruby/process.cc b/src/ruby/process.cc index 2117ee3..182ac2e 100644 --- a/src/ruby/process.cc +++ b/src/ruby/process.cc @@ -89,8 +89,8 @@ static mrb_value sym_flags; static mrb_value sym_start; static mrb_value sym_length; static mrb_value sym_mode; -static mrb_value sym_lang_name; -static mrb_value sym_filename; +static mrb_value sym_data; +static mrb_value sym_foldername; static mrb_value sym_width; static mrb_value sym_normal; static mrb_value sym_insert; @@ -105,8 +105,8 @@ inline void initialize_symbols() { sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start")); sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length")); sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode")); - sym_lang_name = mrb_symbol_value(mrb_intern_cstr(mrb, "lang_name")); - sym_filename = mrb_symbol_value(mrb_intern_cstr(mrb, "filename")); + sym_data = mrb_symbol_value(mrb_intern_cstr(mrb, "data")); + sym_foldername = mrb_symbol_value(mrb_intern_cstr(mrb, "foldername")); sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width")); sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal")); sym_insert = mrb_symbol_value(mrb_intern_cstr(mrb, "insert")); @@ -146,10 +146,8 @@ convert_highlights(mrb_state *mrb, mrb_value highlights_val) { return result; } -BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, - std::string lsp_name, std::string filename, - std::string foldername, uint32_t line, uint32_t max_line, - uint32_t width) { +BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername, + Window *window) { BarLine bar_line; static bool initialed = false; if (!initialed) { @@ -158,7 +156,6 @@ BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, } int ai = mrb_gc_arena_save(mrb); mrb_value info = mrb_hash_new(mrb); - mrb_value key_mode = sym_mode; mrb_value val_mode; switch (mode) { case NORMAL: @@ -177,18 +174,20 @@ BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, val_mode = sym_jumper; break; } - mrb_hash_set(mrb, info, key_mode, val_mode); - mrb_value key_lang_name = sym_lang_name; - mrb_value val_lang_name = - mrb_symbol_value(mrb_intern_cstr(mrb, lang_name.c_str())); - mrb_hash_set(mrb, info, key_lang_name, val_lang_name); - mrb_value key_filename = sym_filename; - mrb_value val_filename = - mrb_str_new(mrb, filename.c_str(), filename.length()); - mrb_hash_set(mrb, info, key_filename, val_filename); - mrb_value key_width = sym_width; + mrb_hash_set(mrb, info, sym_mode, val_mode); + mrb_value val_foldername = + mrb_str_new(mrb, foldername.c_str(), foldername.length()); + mrb_hash_set(mrb, info, sym_foldername, val_foldername); + std::array arr = window->bar_info(); + mrb_value ary = mrb_ary_new(mrb); + mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[0].c_str(), arr[0].length())); + mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[1].c_str(), arr[1].length())); + mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[2].c_str(), arr[2].length())); + mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[3].c_str(), arr[3].length())); + mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[4].c_str(), arr[4].length())); + mrb_hash_set(mrb, info, sym_data, ary); mrb_value val_width = mrb_fixnum_value(width); - mrb_hash_set(mrb, info, key_width, val_width); + mrb_hash_set(mrb, info, sym_width, val_width); mrb_value mod_val = mrb_obj_value(C_module); mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0); mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info); diff --git a/src/syntax/parser.cc b/src/syntax/parser.cc index da7e554..454392e 100644 --- a/src/syntax/parser.cc +++ b/src/syntax/parser.cc @@ -74,7 +74,6 @@ void Parser::work() { line_count = line_tree.count(); if (c_line > 0 && c_line < line_count) prev_state = line_tree.at(c_line - 1)->out_state; - std::shared_lock k_lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, c_line); if (!it) continue; diff --git a/src/ui/bar.cc b/src/ui/bar.cc index 79d003c..583a3df 100644 --- a/src/ui/bar.cc +++ b/src/ui/bar.cc @@ -3,17 +3,24 @@ #include "lsp/lsp.h" #include "main.h" #include "syntax/decl.h" +#include "windows/decl.h" void Bar::log(std::string message) { log_line = message; } -void Bar::render() { +void Bar::render(std::vector &buffer) { USING(LSPInstance); - Editor *editor = editors[current_editor]; - BarLine bar_line = - bar_contents(mode, editor->lang.name, editor->warnings.size(), - editor->lsp ? editor->lsp->lsp->command : "", - editor->filename, editor->filename, editor->cursor.row + 1, - editor->root->line_count + 1, screen.col); + BarLine bar_line; + bar_line = bar_contents(mode, screen.col, pwd.string(), focused_window); + auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg, + uint32_t bg, uint8_t flags, uint32_t width) { + ScreenCell &c = buffer[row * screen.col + col]; + c.utf8 = text; + c.width = width; + c.fg = fg; + c.bg = bg; + c.flags = flags; + c.ul_color = 0; + }; uint32_t row = screen.row - 2; uint32_t width = screen.col; std::string &line = bar_line.line; @@ -26,43 +33,46 @@ void Bar::render() { int width = display_width(cluster.c_str(), cluster_len); Highlight highlight = bar_line.get_highlight(col); update(row, col, cluster.c_str(), highlight.fg, highlight.bg, - highlight.flags); + highlight.flags, width); col += width; i += cluster_len; for (int w = 1; w < width; w++) - update(row, col - w, "\x1b", highlight.fg, highlight.bg, highlight.flags); + update(row, col - w, "\x1b", highlight.fg, highlight.bg, highlight.flags, + 0); } while (col < width) - update(row, col++, " ", 0, 0, 0); + update(row, col++, " ", 0, 0, 0, 1); col = 0; row++; if (mode == RUNNER) { - update(row, col++, ":", 0xFFFFFF, 0, 0); + update(row, col++, ":", 0xFFFFFF, 0, 0, 1); for (char c : command) - update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0); + update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0, 1); } else { for (char c : log_line) - update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0); + update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0, 1); } while (col < width) - update(row, col++, " ", 0, 0, 0); + update(row, col++, " ", 0, 0, 0, 1); } -void Bar::handle(KeyEvent event) { +void Bar::handle_command(std::string &command) { + if (command == "q") { + running = false; + return; + } + if (focused_window) + focused_window->handle_command(command); +} + +void Bar::handle_event(KeyEvent event) { if (event.key_type == KEY_CHAR && event.len == 1) { if (event.c[0] == 0x1B) { command = ""; mode = NORMAL; } else if (event.c[0] == '\n' || event.c[0] == '\r') { command = trim(command); - if (command == "w") { - save_file(editors[current_editor]); - } else if (command == "q") { - running = false; - } else if (command == "wq") { - save_file(editors[current_editor]); - running = false; - } + handle_command(command); mode = NORMAL; command = ""; } else if (isprint((unsigned char)(event.c[0]))) { diff --git a/src/ui/completionbox.cc b/src/ui/completionbox.cc index be39ae7..daa8cf6 100644 --- a/src/ui/completionbox.cc +++ b/src/ui/completionbox.cc @@ -1,187 +1,187 @@ -#include "ui/completionbox.h" -#include "editor/completions.h" -#include "io/sysio.h" -#include "utils/utils.h" - -std::string item_kind_name(uint8_t kind) { - switch (kind) { - case 1: - return "Text"; - case 2: - return "Method"; - case 3: - return "Function"; - case 4: - return "Constructor"; - case 5: - return "Field"; - case 6: - return "Variable"; - case 7: - return "Class"; - case 8: - return "Interface"; - case 9: - return "Module"; - case 10: - return "Property"; - case 11: - return "Unit"; - case 12: - return "Value"; - case 13: - return "Enum"; - case 14: - return "Keyword"; - case 15: - return "Snippet"; - case 16: - return "Color"; - case 17: - return "File"; - case 18: - return "Reference"; - case 19: - return "Folder"; - case 20: - return "EnumMember"; - case 21: - return "Constant"; - case 22: - return "Struct"; - case 23: - return "Event"; - case 24: - return "Operator"; - case 25: - return "TypeParameter"; - default: - return "Unknown"; - } -} - -const char *item_symbol(uint8_t kind) { return "●"; } - -uint32_t kind_color(uint8_t kind) { return 0x82AAFF; } - -void CompletionBox::render_update() { - if (hidden || session->visible.empty()) - return; - std::unique_lock lock(mtx); - uint32_t max_label_len = 0; - uint32_t max_detail_len = 0; - uint32_t max_kind_len = 0; - for (uint32_t x = session->scroll; - x < session->scroll + 8 && x < session->visible.size(); x++) { - uint32_t i = session->visible[x]; - if (i >= session->items.size()) - continue; - auto &item = session->items[i]; - max_label_len = MAX(max_label_len, (uint32_t)item.label.size()); - if (item.detail) - max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size()); - max_kind_len = - MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size()); - } - uint32_t total = session->visible.size(); - uint32_t rows = MIN(total, 8); - size.row = rows + 2; - size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + 1; - cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); - auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, - uint32_t bg, uint8_t flags) { - if (r < size.row && c < size.col) - cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; - }; - uint32_t border_fg = 0x82AAFF; - uint32_t sel_bg = 0x174225; - set(0, 0, "┌", border_fg, 0, 0); - for (uint32_t c = 1; c < size.col - 1; c++) - set(0, c, "─", border_fg, 0, 0); - set(0, size.col - 1, "┐", border_fg, 0, 0); - uint32_t start = session->scroll; - uint32_t end = MIN(start + 8, session->visible.size()); - for (uint32_t row_idx = start; row_idx < end; row_idx++) { - uint32_t r = (row_idx - start) + 1; - auto &item = session->items[session->visible[row_idx]]; - uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : 1; - uint32_t fg = 0xFFFFFF; - set(r, 0, "│", border_fg, 0, 0); - uint32_t c = 1; - const char *sym = item_symbol(item.kind); - set(r, c++, sym, kind_color(item.kind), bg, 0); - set(r, c++, " ", fg, bg, 0); - for (size_t i = 0; i < item.label.size(); i++) - set(r, c + i, (char[2]){item.label[i], 0}, fg, bg, - item.deprecated ? CF_STRIKETHROUGH : 0); - c += item.label.size(); - set(r, c++, " ", fg, bg, 0); - uint32_t detail_fg = 0xAAAAAA; - if (item.detail) { - for (size_t i = 0; i < item.detail->size(); i++) - set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0); - c += item.detail->size(); - } - uint32_t pad = size.col - 1 - c - max_kind_len; - for (uint32_t i = 0; i < pad; i++) - set(r, c + i, " ", fg, bg, 0); - c += pad; - std::string kind_name = item_kind_name(item.kind); - for (size_t i = 0; i < kind_name.size(); i++) - set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg, 0); - set(r, size.col - 1, "│", border_fg, 0, 0); - } - uint32_t bottom = size.row - 1; - set(bottom, 0, "└", border_fg, 0, 0); - for (uint32_t c = 1; c < size.col - 1; c++) - set(bottom, c, "─", border_fg, 0, 0); - set(bottom, size.col - 1, "┘", border_fg, 0, 0); - if (session->visible.size() > 8) { - std::string info = std::to_string(start + 1) + "-" + std::to_string(end) + - "/" + std::to_string(session->visible.size()); - for (size_t i = 0; i < info.size() && i < size.col - 2; i++) - set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0); - } -} - -void CompletionBox::render(Coord pos) { - if (hidden || session->visible.empty()) - return; - std::shared_lock lock(mtx); - int32_t start_row = (int32_t)pos.row - (int32_t)size.row; - if (start_row < 0) - start_row = pos.row + 1; - int32_t start_col = pos.col; - Coord screen_size = get_size(); - if (start_col + size.col > screen_size.col) { - start_col = screen_size.col - size.col; - if (start_col < 0) - start_col = 0; - } - position = {(uint32_t)start_row, (uint32_t)start_col}; - for (uint32_t r = 0; r < size.row; r++) - for (uint32_t c = 0; c < size.col; c++) - update(start_row + r, start_col + c, cells[r * size.col + c].utf8, - cells[r * size.col + c].fg, cells[r * size.col + c].bg, - cells[r * size.col + c].flags); - if (session->items.size() > session->select && - session->items[session->select].documentation && - *session->items[session->select].documentation != "" && - !session->hover_dirty) { - if (session->doc != session->select) { - session->doc = session->select; - session->hover.clear(); - session->hover.text = *session->items[session->select].documentation; - session->hover.is_markup = true; - session->hover_dirty = true; - } else { - if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) { - session->hover.render({position.row + session->hover.size.row, - position.col - session->hover.size.col}); - } else { - session->hover.render( - {position.row + session->hover.size.row, position.col + size.col}); - } - } - } -} +// #include "ui/completionbox.h" +// #include "editor/completions.h" +// #include "io/sysio.h" +// #include "utils/utils.h" +// +// std::string item_kind_name(uint8_t kind) { +// switch (kind) { +// case 1: +// return "Text"; +// case 2: +// return "Method"; +// case 3: +// return "Function"; +// case 4: +// return "Constructor"; +// case 5: +// return "Field"; +// case 6: +// return "Variable"; +// case 7: +// return "Class"; +// case 8: +// return "Interface"; +// case 9: +// return "Module"; +// case 10: +// return "Property"; +// case 11: +// return "Unit"; +// case 12: +// return "Value"; +// case 13: +// return "Enum"; +// case 14: +// return "Keyword"; +// case 15: +// return "Snippet"; +// case 16: +// return "Color"; +// case 17: +// return "File"; +// case 18: +// return "Reference"; +// case 19: +// return "Folder"; +// case 20: +// return "EnumMember"; +// case 21: +// return "Constant"; +// case 22: +// return "Struct"; +// case 23: +// return "Event"; +// case 24: +// return "Operator"; +// case 25: +// return "TypeParameter"; +// default: +// return "Unknown"; +// } +// } +// +// const char *item_symbol(uint8_t kind) { return "●"; } +// +// uint32_t kind_color(uint8_t kind) { return 0x82AAFF; } +// +// void CompletionBox::render_update() { +// if (hidden || session->visible.empty()) +// return; +// std::unique_lock lock(mtx); +// uint32_t max_label_len = 0; +// uint32_t max_detail_len = 0; +// uint32_t max_kind_len = 0; +// for (uint32_t x = session->scroll; +// x < session->scroll + 8 && x < session->visible.size(); x++) { +// uint32_t i = session->visible[x]; +// if (i >= session->items.size()) +// continue; +// auto &item = session->items[i]; +// max_label_len = MAX(max_label_len, (uint32_t)item.label.size()); +// if (item.detail) +// max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size()); +// max_kind_len = +// MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size()); +// } +// uint32_t total = session->visible.size(); +// uint32_t rows = MIN(total, 8); +// size.row = rows + 2; +// size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + +// 1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set = +// [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, +// uint32_t bg, uint8_t flags) { +// if (r < size.row && c < size.col) +// cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; +// }; +// uint32_t border_fg = 0x82AAFF; +// uint32_t sel_bg = 0x174225; +// set(0, 0, "┌", border_fg, 0, 0); +// for (uint32_t c = 1; c < size.col - 1; c++) +// set(0, c, "─", border_fg, 0, 0); +// set(0, size.col - 1, "┐", border_fg, 0, 0); +// uint32_t start = session->scroll; +// uint32_t end = MIN(start + 8, session->visible.size()); +// for (uint32_t row_idx = start; row_idx < end; row_idx++) { +// uint32_t r = (row_idx - start) + 1; +// auto &item = session->items[session->visible[row_idx]]; +// uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : +// 1; uint32_t fg = 0xFFFFFF; set(r, 0, "│", border_fg, 0, 0); uint32_t c = +// 1; const char *sym = item_symbol(item.kind); set(r, c++, sym, +// kind_color(item.kind), bg, 0); set(r, c++, " ", fg, bg, 0); for (size_t i +// = 0; i < item.label.size(); i++) +// set(r, c + i, (char[2]){item.label[i], 0}, fg, bg, +// item.deprecated ? CF_STRIKETHROUGH : 0); +// c += item.label.size(); +// set(r, c++, " ", fg, bg, 0); +// uint32_t detail_fg = 0xAAAAAA; +// if (item.detail) { +// for (size_t i = 0; i < item.detail->size(); i++) +// set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0); +// c += item.detail->size(); +// } +// uint32_t pad = size.col - 1 - c - max_kind_len; +// for (uint32_t i = 0; i < pad; i++) +// set(r, c + i, " ", fg, bg, 0); +// c += pad; +// std::string kind_name = item_kind_name(item.kind); +// for (size_t i = 0; i < kind_name.size(); i++) +// set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg, +// 0); +// set(r, size.col - 1, "│", border_fg, 0, 0); +// } +// uint32_t bottom = size.row - 1; +// set(bottom, 0, "└", border_fg, 0, 0); +// for (uint32_t c = 1; c < size.col - 1; c++) +// set(bottom, c, "─", border_fg, 0, 0); +// set(bottom, size.col - 1, "┘", border_fg, 0, 0); +// if (session->visible.size() > 8) { +// std::string info = std::to_string(start + 1) + "-" + std::to_string(end) +// + +// "/" + std::to_string(session->visible.size()); +// for (size_t i = 0; i < info.size() && i < size.col - 2; i++) +// set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0); +// } +// } +// +// void CompletionBox::render(Coord pos) { +// if (hidden || session->visible.empty()) +// return; +// std::shared_lock lock(mtx); +// int32_t start_row = (int32_t)pos.row - (int32_t)size.row; +// if (start_row < 0) +// start_row = pos.row + 1; +// int32_t start_col = pos.col; +// Coord screen_size = get_size(); +// if (start_col + size.col > screen_size.col) { +// start_col = screen_size.col - size.col; +// if (start_col < 0) +// start_col = 0; +// } +// position = {(uint32_t)start_row, (uint32_t)start_col}; +// for (uint32_t r = 0; r < size.row; r++) +// for (uint32_t c = 0; c < size.col; c++) +// update(start_row + r, start_col + c, cells[r * size.col + c].utf8, +// cells[r * size.col + c].fg, cells[r * size.col + c].bg, +// cells[r * size.col + c].flags); +// if (session->items.size() > session->select && +// session->items[session->select].documentation && +// *session->items[session->select].documentation != "" && +// !session->hover_dirty) { +// if (session->doc != session->select) { +// session->doc = session->select; +// session->hover.clear(); +// session->hover.text = *session->items[session->select].documentation; +// session->hover.is_markup = true; +// session->hover_dirty = true; +// } else { +// if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) { +// session->hover.render({position.row + session->hover.size.row, +// position.col - session->hover.size.col}); +// } else { +// session->hover.render( +// {position.row + session->hover.size.row, position.col + +// size.col}); +// } +// } +// } +// } diff --git a/src/ui/diagnostics.cc b/src/ui/diagnostics.cc index ad54561..ae98033 100644 --- a/src/ui/diagnostics.cc +++ b/src/ui/diagnostics.cc @@ -1,162 +1,164 @@ -#include "ui/diagnostics.h" - -void DiagnosticBox::clear() { - warnings.clear(); - cells.clear(); - size = {0, 0}; -}; - -void DiagnosticBox::render_first() { - if (warnings.empty()) - return; - uint32_t longest_line = 8 + warnings[0].source.length(); - for (auto &warn : warnings) { - uint32_t longest = 0; - uint32_t cur = 0; - for (char ch : warn.text_full) - if (ch == '\n') { - longest = MAX(longest, cur); - cur = 0; - } else { - if (ch == '\t') - cur += 3; - ++cur; - } - longest = MAX(longest, cur); - longest_line = MAX(longest_line, longest + 7); - longest_line = MAX(longest_line, (uint32_t)warn.code.length() + 4); - for (auto &see_also : warn.see_also) - longest_line = MAX(longest_line, (uint32_t)see_also.length() + 4); - } - uint32_t content_width = MIN(longest_line, 150u); - size.col = content_width + 2; - cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0}); - auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, - uint32_t bg, uint8_t flags) { - cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; - }; - uint32_t base_bg = 0; - uint32_t border_fg = 0x82AAFF; - uint32_t r = 0; - if (warnings[0].source != "") { - std::string src_txt = "Source: "; - for (uint32_t i = 0; i < src_txt.length() && i < content_width; i++) - set(1, i + 1, (char[2]){src_txt[i], 0}, 0x3EAAFF, base_bg, 0); - for (uint32_t i = 0; i < warnings[0].source.length() && i < content_width; - i++) - set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0}, - 0xffffff, base_bg, 0); - r++; - } - int idx = 1; - for (auto &warn : warnings) { - char buf[4]; - std::snprintf(buf, sizeof(buf), "%2d", idx % 100); - std::string line_txt = std::string(buf) + ". "; - for (uint32_t i = 0; i < line_txt.length(); i++) - set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0); - if (r >= 23) - break; - const char *err_sym = ""; - uint32_t c_sym = 0xAAAAAA; - switch (warn.type) { - case 1: - err_sym = ""; - c_sym = 0xFF0000; - break; - case 2: - err_sym = ""; - c_sym = 0xFFFF00; - break; - case 3: - err_sym = ""; - c_sym = 0xFF00FF; - break; - case 4: - err_sym = ""; - c_sym = 0xAAAAAA; - break; - } - std::string text = warn.text_full + " " + err_sym; - uint32_t i = 0; - while (i < text.length() && r < 23) { - uint32_t c = 4; - while (c < content_width && i < text.length()) { - if (text[i] == '\n') { - while (i < text.length() && text[i] == '\n') - i++; - break; - } - uint32_t cluster_len = grapheme_next_character_break_utf8( - text.c_str() + i, text.length() - i); - std::string cluster = text.substr(i, cluster_len); - int width = display_width(cluster.c_str(), cluster_len); - if (c + width > content_width) - break; - set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0); - c += width; - i += cluster_len; - for (int w = 1; w < width; w++) - set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0); - } - r++; - } - if (r >= 23) - break; - if (warn.code != "") { - for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width; i++) - set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0); - r++; - } - if (r >= 23) - break; - for (std::string &see_also : warn.see_also) { - uint32_t fg = 0xB55EFF; - uint8_t colon_count = 0; - for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width; - i++) { - set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0); - if (see_also[i] == ':') - colon_count++; - if (colon_count == 2) - fg = 0xFFFFFF; - } - r++; - if (r >= 23) - break; - }; - idx++; - } - size.row = 2 + r; - set(0, 0, "┌", border_fg, base_bg, 0); - for (uint32_t i = 1; i < size.col - 1; i++) - set(0, i, "─", border_fg, base_bg, 0); - set(0, size.col - 1, "┐", border_fg, base_bg, 0); - for (uint32_t r = 1; r < size.row - 1; r++) { - set(r, 0, "│", border_fg, base_bg, 0); - set(r, size.col - 1, "│", border_fg, base_bg, 0); - } - set(size.row - 1, 0, "└", border_fg, base_bg, 0); - for (uint32_t i = 1; i < size.col - 1; i++) - set(size.row - 1, i, "─", border_fg, base_bg, 0); - set(size.row - 1, size.col - 1, "┘", border_fg, base_bg, 0); - cells.resize(size.col * size.row); -} - -void DiagnosticBox::render(Coord pos) { - int32_t start_row = (int32_t)pos.row - (int32_t)size.row; - if (start_row < 0) - start_row = pos.row + 1; - int32_t start_col = pos.col; - Coord screen_size = get_size(); - if (start_col + size.col > screen_size.col) { - start_col = screen_size.col - size.col; - if (start_col < 0) - start_col = 0; - } - for (uint32_t r = 0; r < size.row; r++) - for (uint32_t c = 0; c < size.col; c++) - update(start_row + r, start_col + c, cells[r * size.col + c].utf8, - cells[r * size.col + c].fg, cells[r * size.col + c].bg, - cells[r * size.col + c].flags); -} +// #include "ui/diagnostics.h" +// +// void DiagnosticBox::clear() { +// warnings.clear(); +// cells.clear(); +// size = {0, 0}; +// }; +// +// void DiagnosticBox::render_first() { +// if (warnings.empty()) +// return; +// uint32_t longest_line = 8 + warnings[0].source.length(); +// for (auto &warn : warnings) { +// uint32_t longest = 0; +// uint32_t cur = 0; +// for (char ch : warn.text_full) +// if (ch == '\n') { +// longest = MAX(longest, cur); +// cur = 0; +// } else { +// if (ch == '\t') +// cur += 3; +// ++cur; +// } +// longest = MAX(longest, cur); +// longest_line = MAX(longest_line, longest + 7); +// longest_line = MAX(longest_line, (uint32_t)warn.code.length() + 4); +// for (auto &see_also : warn.see_also) +// longest_line = MAX(longest_line, (uint32_t)see_also.length() + 4); +// } +// uint32_t content_width = MIN(longest_line, 150u); +// size.col = content_width + 2; +// cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0}); +// auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, +// uint32_t bg, uint8_t flags) { +// cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; +// }; +// uint32_t base_bg = 0; +// uint32_t border_fg = 0x82AAFF; +// uint32_t r = 0; +// if (warnings[0].source != "") { +// std::string src_txt = "Source: "; +// for (uint32_t i = 0; i < src_txt.length() && i < content_width; i++) +// set(1, i + 1, (char[2]){src_txt[i], 0}, 0x3EAAFF, base_bg, 0); +// for (uint32_t i = 0; i < warnings[0].source.length() && i < +// content_width; +// i++) +// set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0}, +// 0xffffff, base_bg, 0); +// r++; +// } +// int idx = 1; +// for (auto &warn : warnings) { +// char buf[4]; +// std::snprintf(buf, sizeof(buf), "%2d", idx % 100); +// std::string line_txt = std::string(buf) + ". "; +// for (uint32_t i = 0; i < line_txt.length(); i++) +// set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0); +// if (r >= 23) +// break; +// const char *err_sym = ""; +// uint32_t c_sym = 0xAAAAAA; +// switch (warn.type) { +// case 1: +// err_sym = ""; +// c_sym = 0xFF0000; +// break; +// case 2: +// err_sym = ""; +// c_sym = 0xFFFF00; +// break; +// case 3: +// err_sym = ""; +// c_sym = 0xFF00FF; +// break; +// case 4: +// err_sym = ""; +// c_sym = 0xAAAAAA; +// break; +// } +// std::string text = warn.text_full + " " + err_sym; +// uint32_t i = 0; +// while (i < text.length() && r < 23) { +// uint32_t c = 4; +// while (c < content_width && i < text.length()) { +// if (text[i] == '\n') { +// while (i < text.length() && text[i] == '\n') +// i++; +// break; +// } +// uint32_t cluster_len = grapheme_next_character_break_utf8( +// text.c_str() + i, text.length() - i); +// std::string cluster = text.substr(i, cluster_len); +// int width = display_width(cluster.c_str(), cluster_len); +// if (c + width > content_width) +// break; +// set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0); +// c += width; +// i += cluster_len; +// for (int w = 1; w < width; w++) +// set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0); +// } +// r++; +// } +// if (r >= 23) +// break; +// if (warn.code != "") { +// for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width; +// i++) +// set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0); +// r++; +// } +// if (r >= 23) +// break; +// for (std::string &see_also : warn.see_also) { +// uint32_t fg = 0xB55EFF; +// uint8_t colon_count = 0; +// for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width; +// i++) { +// set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0); +// if (see_also[i] == ':') +// colon_count++; +// if (colon_count == 2) +// fg = 0xFFFFFF; +// } +// r++; +// if (r >= 23) +// break; +// }; +// idx++; +// } +// size.row = 2 + r; +// set(0, 0, "┌", border_fg, base_bg, 0); +// for (uint32_t i = 1; i < size.col - 1; i++) +// set(0, i, "─", border_fg, base_bg, 0); +// set(0, size.col - 1, "┐", border_fg, base_bg, 0); +// for (uint32_t r = 1; r < size.row - 1; r++) { +// set(r, 0, "│", border_fg, base_bg, 0); +// set(r, size.col - 1, "│", border_fg, base_bg, 0); +// } +// set(size.row - 1, 0, "└", border_fg, base_bg, 0); +// for (uint32_t i = 1; i < size.col - 1; i++) +// set(size.row - 1, i, "─", border_fg, base_bg, 0); +// set(size.row - 1, size.col - 1, "┘", border_fg, base_bg, 0); +// cells.resize(size.col * size.row); +// } +// +// void DiagnosticBox::render(Coord pos) { +// int32_t start_row = (int32_t)pos.row - (int32_t)size.row; +// if (start_row < 0) +// start_row = pos.row + 1; +// int32_t start_col = pos.col; +// Coord screen_size = get_size(); +// if (start_col + size.col > screen_size.col) { +// start_col = screen_size.col - size.col; +// if (start_col < 0) +// start_col = 0; +// } +// for (uint32_t r = 0; r < size.row; r++) +// for (uint32_t c = 0; c < size.col; c++) +// update(start_row + r, start_col + c, cells[r * size.col + c].utf8, +// cells[r * size.col + c].fg, cells[r * size.col + c].bg, +// cells[r * size.col + c].flags); +// } diff --git a/src/windows/renderer.cc b/src/windows/renderer.cc new file mode 100644 index 0000000..bed82e8 --- /dev/null +++ b/src/windows/renderer.cc @@ -0,0 +1,23 @@ +#include "main.h" +#include "windows/decl.h" + +TileRoot root_tile; +std::vector> popups; +Window *focused_window; + +void render() { + ui::bar.render(new_screen); + root_tile.render(new_screen); + for (auto &popup : popups) + popup->render(new_screen); +} + +void handle_click(KeyEvent event) { + for (auto &popup : popups) { + if (popup->inside(event.mouse_x, event.mouse_y)) { + popup->handle_click(event); + return; + } + } + root_tile.handle_click(event); +}