4 Commits

Author SHA1 Message Date
5b66f503e4 Improve highlighting structure
- switched to a sparse delta based map
- true lazy-loading to avoid any unneccessary allocations
- fixed windows management api
2026-02-11 18:18:28 +00:00
d79ea4e75a Fix parser bug on newlines 2026-02-05 14:23:52 +00:00
dd474cc38d Fix deadlock 2026-02-05 13:03:41 +00:00
a62d4a18a8 Switch to OOP style code 2026-02-04 00:38:11 +00:00
59 changed files with 3417 additions and 3559 deletions

View File

@@ -19,10 +19,11 @@ CFLAGS_DEBUG :=\
-std=c++20 -Wall -Wextra \ -std=c++20 -Wall -Wextra \
-O0 -fno-inline -gsplit-dwarf \ -O0 -fno-inline -gsplit-dwarf \
-g -fno-omit-frame-pointer \ -g -fno-omit-frame-pointer \
-fsanitize=address \
-Wno-unused-command-line-argument \ -Wno-unused-command-line-argument \
-I./include -I./libs -I/home/syed/main/crib/libs/mruby/include -I./include -I./libs -I/home/syed/main/crib/libs/mruby/include
# C_SANITIZER := -fsanitize=address
CFLAGS_RELEASE :=\ CFLAGS_RELEASE :=\
-static --target=x86_64-linux-musl \ -static --target=x86_64-linux-musl \
-std=c++20 -O3 -march=x86-64 -mtune=generic \ -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) $(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG)
mkdir -p $(BIN_DIR) 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) $(TARGET_RELEASE): $(PCH_RELEASE) $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE)
mkdir -p $(BIN_DIR) mkdir -p $(BIN_DIR)

View File

@@ -134,6 +134,7 @@ crib ./filename.ext
``` ```
*If `filename.ext` does not exist, it will be created*<br> *If `filename.ext` does not exist, it will be created*<br>
*memory usage is average case 10MB + 2x size of file*<br>
## Keybindings ## Keybindings

33
TODO.md
View File

@@ -2,27 +2,23 @@ Copyright 2025 Syed Daanish
# TODO # TODO
# memory usage for debug build (release build will be smaller by about 25%) ## Next few super long boring things to do::
``` * redo lsp threads such that no mutex needed for any rope stuff (done)
8K -> 13.2M - Also make the classes own the methods in lsp (done)
128K -> 13.2M (expected worst case 16.6M) - This will mean that parsers/renderers and keystrokes will not need to be individually locked (done)
128M -> 412.0M (expected worst case 2.3G) - And so it will be much faster (done)
``` - At which point the main thread can also be blocked on user input or lsp responses and still be faster
* Add a superclass for editor called Window (which can be popup or tiled) (done)
* Next few super long boring things to do * Add a recursive tiling class for windows (done)
* redo lsp threads such that no mutex needed for any rope stuff * Handled by a single renderer that calls and renders each window (done)
- Also make the classes own the methods in lsp * Make editor's functions into its own methods (classify it) (done)
- 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)
- While at it - While at it
- Seperate system functions into a class that branches to support local / ssh / server modes. - 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 - Even lsp shouldnt be directly controlled because it can branch on local and server modes
- check wolfSSH stuff for remote editing
- Thick and thin mode
- thin mode only shares screen diffing over the network - uses server settings / themes
- thick only shared fileio and lsp data with local used for all else - uses local settings / themes
- Redo hooks as a engine of its own. - 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) - 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 - which is just managed by the renderer
@@ -41,6 +37,7 @@ Copyright 2025 Syed Daanish
* also move default bar and clipboard back into cpp * also move default bar and clipboard back into cpp
* all lsp popups are no longer their own classes but instead windows (extention like) in popup mode * all lsp popups are no longer their own classes but instead windows (extention like) in popup mode
* also **fix click handling to send mouse unclick to the same focus window as the click And also for drag to be same as the starter**
* skip opening binary files * skip opening binary files
* apply themeing in bg log bar lsp popus etc. to keep visual consistency * apply themeing in bg log bar lsp popus etc. to keep visual consistency
* searching/replace/Multi-Cursor (for just one lsp command for all) with pcre2 with regex (started by a slash) (disabled for large files) * searching/replace/Multi-Cursor (for just one lsp command for all) with pcre2 with regex (started by a slash) (disabled for large files)

View File

@@ -23,6 +23,7 @@ struct CompletionItem {
}; };
struct CompletionSession { struct CompletionSession {
struct Editor *editor;
std::shared_mutex mtx; std::shared_mutex mtx;
bool active = false; bool active = false;
Coord hook; Coord hook;
@@ -40,7 +41,14 @@ struct CompletionSession {
std::atomic<bool> hover_dirty = false; std::atomic<bool> hover_dirty = false;
int version; 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 #endif

View File

@@ -1,16 +1,15 @@
#ifndef EDITOR_H #ifndef EDITOR_H
#define EDITOR_H #define EDITOR_H
#include "editor/completions.h"
#include "editor/indents.h" #include "editor/indents.h"
#include "extentions/hover.h"
#include "io/knot.h" #include "io/knot.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "syntax/extras.h" #include "syntax/extras.h"
#include "syntax/parser.h" #include "syntax/parser.h"
#include "ui/completionbox.h"
#include "ui/diagnostics.h" #include "ui/diagnostics.h"
#include "ui/hover.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "windows/decl.h"
#define CHAR 0 #define CHAR 0
#define WORD 1 #define WORD 1
@@ -19,144 +18,159 @@
#define EXTRA_META 2 #define EXTRA_META 2
#define INDENT_WIDTH 2 #define INDENT_WIDTH 2
struct Editor { struct Editor : Window {
std::string filename; std::string filename = "";
std::string uri; std::string uri = "";
Knot *root; Knot *root = nullptr;
std::shared_mutex knot_mtx; Coord cursor = {0, 0};
Coord cursor; uint32_t cursor_preffered = 0;
uint32_t cursor_preffered; Coord selection = {0, 0};
Coord selection; bool selection_active = false;
bool selection_active; bool unix_eol = true;
bool unix_eol; int selection_type = 0;
int selection_type; Coord size = {0, 0};
Coord position; Coord scroll = {0, 0};
Coord size; Language lang = {};
Coord scroll; uint32_t hooks[94] = {0};
Language lang; bool jumper_set = false;
uint32_t hooks[94]; std::vector<VWarn> warnings = {};
bool jumper_set; bool warnings_dirty = false;
std::shared_mutex v_mtx; VAI ai = {};
std::vector<VWarn> warnings;
bool warnings_dirty;
VAI ai;
std::shared_mutex lsp_mtx; std::shared_mutex lsp_mtx;
std::shared_ptr<struct LSPInstance> lsp; std::atomic<struct LSPInstance *> lsp = nullptr;
bool hover_active; HoverBox *hover_popup = init_hover();
HoverBox hover; bool diagnostics_active = false;
bool diagnostics_active;
DiagnosticBox diagnostics;
std::atomic<int> lsp_version = 1; std::atomic<int> lsp_version = 1;
CompletionSession completion; IndentationEngine indents = {};
IndentationEngine indents; Parser *parser = nullptr;
Parser *parser; ExtraHighlighter extra_hl = {};
ExtraHighlighter extra_hl; bool is_css_color = false;
bool is_css_color;
};
Editor *new_editor(const char *filename_arg, Coord position, Coord size, Editor(const char *filename_arg, uint8_t eol);
uint8_t eol); ~Editor();
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<TextEdit> 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);
inline void apply_hook_insertion(Editor *editor, uint32_t line, uint32_t rows) { void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override;
for (auto &hook : editor->hooks) void handle_event(KeyEvent event) override;
if (hook > line) void handle_click(KeyEvent event, Coord size) override;
hook += rows; void handle_command(std::string &command) override;
} void work() override;
std::array<std::string, 5> bar_info() override { return {}; };
inline void apply_hook_deletion(Editor *editor, uint32_t removal_start, void save();
uint32_t removal_end) { void cursor_up(uint32_t number);
for (auto &hook : editor->hooks) void cursor_down(uint32_t number);
if (hook > removal_start) void cursor_left(uint32_t number);
hook -= removal_end - removal_start + 1; void cursor_right(uint32_t number);
} void move_line_down();
void move_line_up();
inline static void utf8_normalize_edit(Editor *editor, TextEdit *edit) { void scroll_up(uint32_t number);
std::shared_lock lock(editor->knot_mtx); void scroll_down(uint32_t number);
if (edit->start.row > editor->root->line_count) { void ensure_cursor();
edit->start.row = editor->root->line_count; void ensure_scroll();
edit->start.col = UINT32_MAX;
} void edit_erase(Coord pos, int64_t len);
if (edit->end.row > editor->root->line_count) { void edit_insert(Coord pos, char *data, uint32_t len);
edit->end.row = editor->root->line_count; void edit_replace(Coord start, Coord end, const char *text, uint32_t len);
edit->end.col = UINT32_MAX;
} Coord click_coord(uint32_t x, uint32_t y);
LineIterator *it = begin_l_iter(editor->root, edit->start.row);
if (!it) char *get_selection(uint32_t *out_len, Coord *out_start);
return; void selection_bounds(Coord *out_start, Coord *out_end);
uint32_t len;
char *line = next_line(it, &len); void insert_str(char *c, uint32_t len);
if (!line) { 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<TextEdit> 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->buffer);
free(it); free(it);
return; it = begin_l_iter(this->root, edit->end.row);
} if (!it)
if (edit->start.col < len) return;
edit->start.col = utf16_offset_to_utf8(line, len, edit->start.col); line = next_line(it, &len);
else if (!line) {
edit->start.col = len; free(it->buffer);
if (edit->end.row == edit->start.row) { free(it);
return;
}
if (edit->end.col < len) if (edit->end.col < len)
edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col); edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col);
else else
edit->end.col = len; edit->end.col = len;
free(it->buffer); free(it->buffer);
free(it); free(it);
return;
} }
free(it->buffer);
free(it); inline void apply_hook_insertion(uint32_t line, uint32_t rows) {
it = begin_l_iter(editor->root, edit->end.row); for (auto &hook : this->hooks)
if (!it) if (hook > line)
return; hook += rows;
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); inline void apply_hook_deletion(uint32_t removal_start,
else uint32_t removal_end) {
edit->end.col = len; for (auto &hook : this->hooks)
free(it->buffer); if (hook > removal_start)
free(it); hook -= removal_end - removal_start + 1;
} }
};
#endif #endif

View File

@@ -3,24 +3,4 @@
#include "editor/editor.h" #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 #endif

View File

@@ -0,0 +1,37 @@
#ifndef EXTENTION_HOVER_H
#define EXTENTION_HOVER_H
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
#include "windows/decl.h"
struct HoverBox : Popup {
std::string text;
std::atomic<bool> 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<ScreenCell> &buffer, Coord size, Coord pos) override;
void handle_click(KeyEvent ev, Coord) override {
if (ev.mouse_button == SCROLL_BTN && ev.mouse_state == SCROLL) {
this->scroll(ev.mouse_direction == SCROLL_UP ? -1 : 1);
return;
}
this->hidden = true;
};
~HoverBox() {};
};
HoverBox *init_hover();
#endif

View File

@@ -96,20 +96,19 @@ inline bool is_empty_cell(const ScreenCell &c) {
return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b"; return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b";
} }
namespace io {
extern std::vector<ScreenCell> new_screen;
extern uint32_t rows, cols;
extern bool show_cursor;
extern std::vector<ScreenCell> old_screen;
extern termios orig_termios;
} // namespace io
Coord start_screen(); Coord start_screen();
void end_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, void set_cursor(uint8_t row, uint8_t col, uint32_t type,
bool show_cursor_param); bool show_cursor_param);
void render(); void io_render();
Coord get_size();
KeyEvent read_key(); KeyEvent read_key();

View File

@@ -5,56 +5,20 @@
#include "pch.h" #include "pch.h"
#include "utils/utils.h" #include "utils/utils.h"
struct LSPPending { #define LSP_TIMEOUT 3000
Editor *editor = nullptr;
std::function<void(Editor *, const json &)> callback;
};
// TODO: Defer any editor mutation to main thread to get rid of namespace lsp {
// all mutex locks on the editor rope. extern std::mutex lsp_mutex;
// struct LSPPendingResponse { extern std::unordered_map<std::string, std::unique_ptr<LSPInstance>>
// 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<bool> initialized = false;
std::atomic<bool> 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<char> format_chars;
std::vector<char> trigger_chars;
std::vector<char> end_chars;
uint32_t last_id = 0;
Queue<json> inbox;
Queue<json> outbox;
Queue<std::pair<Language, Editor *>> open_queue;
std::unordered_map<uint32_t, LSPPending *> pending;
std::vector<Editor *> editors;
};
extern std::shared_mutex active_lsps_mtx;
extern std::unordered_map<std::string, std::shared_ptr<LSPInstance>>
active_lsps; active_lsps;
extern Queue<LSPOpenRequest> lsp_open_queue; extern Queue<std::string> need_opening;
extern std::unordered_set<std::string> opened;
extern std::vector<Editor *> new_editors;
} // namespace lsp
static json client_capabilities = { void lsp_worker();
static const json client_capabilities = {
{"general", {{"positionEncodings", {"utf-16"}}}}, {"general", {{"positionEncodings", {"utf-16"}}}},
{"textDocument", {"textDocument",
{{"publishDiagnostics", {{"relatedInformation", true}}}, {{"publishDiagnostics", {{"relatedInformation", true}}},
@@ -78,20 +42,398 @@ static json client_capabilities = {
{"contextSupport", true}, {"contextSupport", true},
{"insertTextMode", 1}}}}}}; {"insertTextMode", 1}}}}}};
void lsp_send(std::shared_ptr<LSPInstance> lsp, json message, struct LSPMessage {
LSPPending *pending); Editor *editor = nullptr;
void lsp_worker(); json message;
std::function<void(const LSPMessage &)> callback;
};
std::shared_ptr<LSPInstance> get_or_init_lsp(std::string lsp_id); struct LSPInstance {
void clean_lsp(std::shared_ptr<LSPInstance> lsp, std::string lsp_id); const LSP *lsp_info;
void close_lsp(std::string lsp_id); std::string root_dir;
std::optional<json> read_lsp_message(int fd); int pid{-1};
int stdin_fd{-1};
int stdout_fd{-1};
std::atomic<bool> initialized = false;
std::atomic<bool> 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<char> format_chars;
std::vector<char> trigger_chars;
std::vector<char> end_chars;
uint32_t last_id = 0;
Queue<json> inbox;
Queue<json> outbox;
std::unordered_map<uint32_t, std::unique_ptr<LSPMessage>> pending;
std::vector<std::unique_ptr<LSPMessage>> lsp_response_queue;
std::vector<Editor *> editors;
void open_editor(std::shared_ptr<LSPInstance> lsp, LSPInstance(std::string lsp_id) {
std::pair<Language, Editor *> entry); lsp_info = &lsps[lsp_id];
void request_add_to_lsp(Language language, Editor *editor); if (!init_process()) {
void add_to_lsp(Language language, Editor *editor); exited = true;
void remove_from_lsp(Editor *editor); return;
void lsp_handle(std::shared_ptr<LSPInstance> lsp, json message); }
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();
if (response.contains("result") &&
response["result"].contains("capabilities")) {
auto &caps = response["result"]["capabilities"];
// if (caps.contains("positionEncoding")) {
// std::string s = caps["positionEncoding"].get<std::string>();
// 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<int>();
incremental_sync = (change_type == 2);
} else if (sync.is_object() && sync.contains("change")) {
int change_type = sync["change"].get<int>();
incremental_sync = (change_type == 2);
}
}
allow_formatting = caps.value("documentFormattingProvider", false);
if (caps.contains("documentOnTypeFormattingProvider")) {
auto &fmt = caps["documentOnTypeFormattingProvider"];
if (fmt.is_object()) {
if (fmt.contains("firstTriggerCharacter")) {
std::string s = fmt["firstTriggerCharacter"].get<std::string>();
if (s.size() == 1)
format_chars.push_back(s[0]);
}
if (fmt.contains("moreTriggerCharacter")) {
for (auto &c : fmt["moreTriggerCharacter"]) {
std::string s = c.get<std::string>();
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<bool>();
}
}
if (caps.contains("hoverProvider")) {
auto &hover = caps["hoverProvider"];
allow_hover =
hover.is_boolean() ? hover.get<bool>() : 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<bool>();
if (caps["completionProvider"].contains("triggerCharacters")) {
auto &chars = caps["completionProvider"]["triggerCharacters"];
if (chars.is_array()) {
for (auto &c : chars) {
std::string str = c.get<std::string>();
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<std::string>();
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, 500);
json exit_msg = {{"method", "exit"}};
send_raw(exit_msg);
int waited = 0;
while (waited < 100) {
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<char *> argv;
argv.push_back(const_cast<char *>(lsp_info->command.c_str()));
for (auto &arg : lsp_info->args)
argv.push_back(const_cast<char *>(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) {
editors.push_back(ed);
ed->lsp.store(this);
char *buf = read(ed->root, 0, ed->root->char_count);
std::string text(buf);
free(buf);
json message = {{"jsonrpc", "2.0"},
{"method", "textDocument/didOpen"},
{"params",
{{"textDocument",
{{"uri", ed->uri},
{"languageId", ed->lang.name},
{"version", 1},
{"text", text}}}}}};
send_raw(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<LSPMessage>();
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<uint32_t>();
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<std::string>();
else if (p.contains("uri"))
uri = p["uri"].get<std::string>();
}
Editor *ed = resolve_uri(uri);
auto response = std::make_unique<LSPMessage>();
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<json> 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<std::string>().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<std::string>().c_str());
}
}
}
void send(std::unique_ptr<LSPMessage> 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 #endif

View File

@@ -12,8 +12,10 @@
extern std::atomic<bool> running; extern std::atomic<bool> running;
extern std::atomic<uint8_t> mode; extern std::atomic<uint8_t> mode;
extern std::vector<struct Editor *> editors; extern fs::path pwd;
extern uint8_t current_editor;
namespace ui {
extern Bar bar; extern Bar bar;
} // namespace ui
#endif #endif

View File

@@ -50,6 +50,7 @@ extern "C" {
#include <thread> #include <thread>
#include <unistd.h> #include <unistd.h>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
using json = nlohmann::json; using json = nlohmann::json;

View File

@@ -3,6 +3,7 @@
#include "syntax/decl.h" #include "syntax/decl.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "windows/decl.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@@ -45,9 +46,7 @@ bool custom_compare(mrb_value match_block, std::string state1,
std::string parse_custom(std::vector<Token> *tokens, mrb_value parser_block, std::string parse_custom(std::vector<Token> *tokens, mrb_value parser_block,
const char *line, uint32_t len, std::string state, const char *line, uint32_t len, std::string state,
uint32_t c_line); uint32_t c_line);
BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
std::string lsp_name, std::string filename, Window *window);
std::string foldername, uint32_t line, uint32_t max_line,
uint32_t width);
#endif #endif

View File

@@ -177,7 +177,7 @@ module C
color: 0x6e1516, color: 0x6e1516,
symbol: "", symbol: "",
extensions: ["erb"], extensions: ["erb"],
lsp: "ruby-lsp" lsp: "emmet-language-server"
}, },
lua: { lua: {
color: 0x36a3d9, color: 0x36a3d9,
@@ -331,7 +331,7 @@ module C
@b_startup = nil @b_startup = nil
@b_shutdown = nil @b_shutdown = nil
@b_bar = proc do |info| @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 # puts info.inspect
mode_color = 0x82AAFF mode_color = 0x82AAFF
mode_symbol = " " mode_symbol = " "
@@ -352,11 +352,15 @@ module C
mode_color = 0xF29CC3 mode_color = 0xF29CC3
mode_symbol = "" mode_symbol = ""
end end
lang_info = C.languages[info[:lang_name]] lang_info = C.languages[:default]
if lang_info.nil? filename = ""
lang_info = C.languages[:default] 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 end
filename = File.basename(info[:filename])
starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}" starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}"
highlights = [] highlights = []
highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 } highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 }

View File

@@ -1,8 +1,8 @@
#pragma once #pragma once
constexpr unsigned char _tmp___crib_precompiled_mrb[] = { 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, 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, 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, 0x00, 0x69, 0x01, 0x00, 0x6b, 0x01, 0x01, 0x01,
0x11, 0x01, 0x68, 0x01, 0x02, 0x69, 0x01, 0x02, 0x11, 0x01, 0x68, 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, 0x05, 0x5c, 0x21, 0x0f, 0x5e, 0x1a, 0x04, 0x10, 0x1b, 0x12, 0x10, 0x1c,
0x02, 0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f, 0x02, 0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f,
0x38, 0x10, 0x20, 0x04, 0x5c, 0x21, 0x42, 0x52, 0x21, 0x01, 0x10, 0x22, 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, 0x02, 0x0f, 0x1f, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x20, 0x03, 0x5c, 0x21,
0x43, 0x10, 0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24, 0x43, 0x10, 0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24,
0x05, 0x5c, 0x25, 0x17, 0x5e, 0x1e, 0x04, 0x10, 0x1f, 0x14, 0x10, 0x20, 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, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61,
0x73, 0x74, 0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x73, 0x74, 0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03,
0x55, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb4, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x1f, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x7b, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c,
0x04, 0x00, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a, 0x04, 0x00, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a,
0x01, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x01, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c,
0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x01, 0x26, 0x00, 0x68, 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, 0x5c, 0x04, 0x04, 0x26, 0x00, 0x1a, 0x10, 0x0a, 0x06, 0x01, 0x0b, 0x09,
0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xf2, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xf2,
0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07, 0x33, 0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07, 0x33,
0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x09, 0x23, 0x0a, 0x23, 0x09, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x5c, 0x06,
0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29, 0x09, 0x00, 0x03, 0x26, 0x00, 0x06, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x06, 0x0a, 0x23,
0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x09, 0x5c, 0x0a, 0x07, 0x4d, 0x09, 0x28, 0x09, 0x00, 0x73, 0x01, 0x09,
0x01, 0x05, 0x09, 0x1d, 0x09, 0x0b, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0c, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x08, 0x0a, 0x23, 0x09, 0x5c, 0x0a,
0x23, 0x0a, 0x32, 0x09, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x06, 0x06, 0x32, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x17, 0x1d, 0x09, 0x07,
0x01, 0x0a, 0x04, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a, 0x33, 0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x08,
0x01, 0x10, 0x0b, 0x00, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x0b, 0x23, 0x0a, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29,
0x5d, 0x09, 0x5c, 0x0a, 0x07, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x09, 0x00, 0x03, 0x26, 0x00, 0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08,
0x10, 0x23, 0x0a, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x01, 0x10,
0x06, 0x5d, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x0a, 0x0a, 0x23, 0x09, 0x07, 0x0a, 0x23, 0x09, 0x5c, 0x0a, 0x06, 0x32,
0x08, 0x00, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x16, 0x1d, 0x09, 0x0c, 0x01, 0x0a,
0x0e, 0x14, 0x10, 0x0c, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x07, 0x0b, 0x23, 0x0a, 0x32, 0x09,
0x0f, 0x10, 0x10, 0x14, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x08, 0x01, 0x0a, 0x04, 0x5d,
0x5e, 0x0a, 0x05, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x00,
0x11, 0x01, 0x0b, 0x03, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x5d, 0x09, 0x5c, 0x0a,
0x3c, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11, 0x09, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x10, 0x23, 0x0a, 0x5d,
0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x06, 0x5d, 0x09, 0x5c,
0x11, 0x0f, 0x0b, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x0a, 0x0a, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x08, 0x00, 0x01, 0x09,
0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x0e, 0x14, 0x10, 0x0c,
0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x0f, 0x10, 0x10, 0x14,
0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x5e, 0x0a, 0x05, 0x32,
0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x03,
0x03, 0x0f, 0x0d, 0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 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, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00,
0xce, 0xd4, 0xdf, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d,
0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e,
0x33, 0x11, 0x15, 0x46, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11,
0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x10, 0x0c, 0x12, 0x0f,
0x2d, 0x10, 0x0c, 0x12, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0d, 0x10,
0x01, 0x10, 0x06, 0x33, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01,
0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0xce, 0xd4, 0xdf, 0x10,
0x09, 0x18, 0x01, 0x0a, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03,
0x09, 0x02, 0x3d, 0x09, 0x00, 0x09, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x33, 0x11, 0x15, 0x46,
0x00, 0x00, 0x04, 0xee, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08,
0xb1, 0x93, 0xa7, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0c, 0x12,
0x20, 0x00, 0x00, 0x00, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x01, 0x10, 0x06, 0x33,
0x04, 0xee, 0xba, 0xa2, 0x20, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x07, 0x11,
0x00, 0x08, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x18, 0x01, 0x0a,
0x00, 0x03, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x09, 0x02, 0x3d, 0x09,
0x64, 0x65, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee,
0x00, 0x03, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0x93, 0xa7, 0x20,
0x72, 0x74, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, 0x20, 0x00, 0x00, 0x00,
0x00, 0x06, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee, 0xba, 0xa2,
0x75, 0x6d, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x65, 0x64, 0x69,
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x09, 0x74, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x08,
0x6c, 0x61, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x07, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, 0x00, 0x03,
0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65,
0x6c, 0x65, 0x00, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, 0x00, 0x03,
0x65, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74,
0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x00, 0x06,
0x63, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, 0x75, 0x6d,
0x6c, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, 0x6c, 0x61,
0x00, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x07, 0x64, 0x65,
0x61, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61,
0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x00, 0x00, 0x02, 0x21, 0x3d, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65,
0x72, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00,
0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, 0x63,
0x00, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
0x00, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, 0x00,
0x32, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, 0x61,
0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, 0x69,
0x00, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, 0x32,
0xc8, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x43,
0x3a, 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, 0x63,
0x08, 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x04,
0x1d, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00,
0x01, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00,
0x08, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, 0xc8,
0x05, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3a,
0x01, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x08,
0x27, 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x1d,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, 0x01,
0x08, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, 0x00,
0x08, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, 0x08,
0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, 0x05,
0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x01,
0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x06, 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, 0x32,
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, 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, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, 0x08,
0x00, 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x23, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x00,
0x00, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09,
0x00, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06,
0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c,
0x61, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a,
0x14, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x23, 0x66,
0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08,
0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x0f, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, 0x00, 0x01, 0x03,
0x00, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x08, 0x3d, 0x03, 0x10, 0x07, 0x10, 0x10, 0x08, 0x10, 0x32, 0x07, 0x11,
0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x01, 0x27, 0x07, 0x00, 0x0a, 0x10, 0x07, 0x10, 0x10, 0x08, 0x12, 0x32,
0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x07, 0x11, 0x01, 0x28, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x5c, 0x08, 0x07,
0x00, 0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1b, 0x00, 0x07, 0x64, 0x65, 0x2f, 0x07, 0x13, 0x01, 0x33, 0x07, 0x14, 0x28, 0x07, 0x00, 0x02, 0x3d,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x03, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x5c, 0x09, 0x09,
0x00, 0x00, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x3f, 0x00, 0x00, 0x08, 0x5d, 0x08, 0x2f, 0x07, 0x15, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06, 0x07,
0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x04, 0x6f, 0x01, 0x07, 0x06, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a, 0x32,
0x70, 0x65, 0x6e, 0x00, 0x00, 0x05, 0x63, 0x68, 0x6f, 0x6d, 0x70, 0x00, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08,
0x00, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xd1, 0x66, 0x08, 0x1f, 0x08,
0x3f, 0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, 0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x00, 0x00, 0x06, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00, 0x00, 0x07, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00,
0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x03, 0x3d, 0x3d, 0xb4, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08, 0x09,
0x3d, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x04, 0x66, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06,
0x69, 0x73, 0x68, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x10, 0x08, 0x16, 0x26, 0x00, 0x97, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c,
0x00, 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x09, 0x0c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a,
0x61, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x02, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0x7a, 0x66,
0x21, 0x3d, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x0f, 0x63, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, 0x01,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08,
0x73, 0x3f, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x0d, 0x26, 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0d,
0x04, 0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28,
0x00, 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x08, 0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f,
0x74, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x08, 0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07,
0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x01, 0x00, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x19, 0x26,
0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x10, 0x02, 0x00, 0x00, 0x23, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0f, 0x32, 0x08,
0x10, 0x03, 0x01, 0x10, 0x04, 0x02, 0x10, 0x05, 0x03, 0x10, 0x06, 0x04, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00,
0x2f, 0x01, 0x05, 0x05, 0x10, 0x02, 0x06, 0x10, 0x03, 0x07, 0x10, 0x04, 0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00,
0x08, 0x10, 0x05, 0x09, 0x10, 0x06, 0x0a, 0x10, 0x07, 0x0b, 0x10, 0x08, 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, 0x23,
0x0c, 0x2f, 0x01, 0x0d, 0x07, 0x6b, 0x01, 0x0e, 0x00, 0x6b, 0x01, 0x0f, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x00,
0x01, 0x6b, 0x01, 0x10, 0x02, 0x6b, 0x01, 0x11, 0x03, 0x6b, 0x01, 0x12, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00,
0x04, 0x6b, 0x01, 0x13, 0x05, 0x6b, 0x01, 0x14, 0x06, 0x6b, 0x01, 0x15, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x00,
0x07, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61,
0x6d, 0x65, 0x00, 0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x14,
0x66, 0x69, 0x67, 0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, 0x2d,
0x67, 0x65, 0x73, 0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, 0x00,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, 0x72,
0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x00,
0x61, 0x74, 0x74, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x6a,
0x72, 0x00, 0x00, 0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00,
0x70, 0x00, 0x00, 0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00,
0x77, 0x6e, 0x00, 0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 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, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00,
0x00, 0x05, 0x62, 0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f, 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39,
0x74, 0x65, 0x00, 0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00,
0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f, 0x62,
0x72, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04, 0x00,
0x61, 0x72, 0x3d, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x70, 0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
0x73, 0x74, 0x65, 0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00,
0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64,
0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x04,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x79,
0x74, 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00,
0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0e,
0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65,
0x79, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x01, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61,
0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00,
0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00,
0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26,
0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x04,
0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x02,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x02,
0x00, 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x04,
0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x08,
0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x28,
0x26, 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01,
0x04, 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01,
0x08, 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00,
0x02, 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x28,
0x02, 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01,
0x04, 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01,
0x08, 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00,
0x00, 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x41,
0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x3f,
0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03,
0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x17,
0x00, 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00,
0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x65,
0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
0x00, 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61, 0x63,
0x41, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x68, 0x00, 0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26, 0x00,
0x17, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x02, 0x11, 0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21, 0x06,
0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x01, 0x00, 0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04, 0x34,
0x00, 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x05, 0x00, 0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x62,
0x65, 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05,
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, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00,
0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 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, 0x00, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21,
0x04, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 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, 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, 0x23, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00,
0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03,
0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01,
0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05, 0x01, 0x32, 0x03, 0x01, 0x01,
0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x40, 0x6b, 0x65, 0x79,
0x5f, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x21,
0x3d, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x03,
0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00,
0x00, 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04,
0x39, 0x04, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 0x19,
0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, 0x04, 0x19,
0x00, 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x23,
0x05, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00, 0x21,
0x02, 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x25,
0x01, 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04,
0x01, 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, 0x3d,
0x32, 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f,
0x09, 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00,
0x07, 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x03, 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x3d,
0x00, 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69,
0x3f, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00,
0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00,
0x6c, 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x39,
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, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01,
0x04, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x00,
0x0b, 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x05,
0x1d, 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x02,
0x33, 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x01,
0x01, 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x01,
0x01, 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32,
0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x09,
0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x07,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x03,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00,
0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f,
0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00,
0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c,
0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00,
0x75, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x00, 0x0b, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74,
0x65, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x68, 0x00, 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00,
0x00, 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x00, 0x08, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00, 0x00,
0x74, 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00, 0x00,
0x64, 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x09, 0x24, 0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00, 0x00,
0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x04, 0x65, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x04,
0x65, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39, 0x04,
0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x04,
0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x0b,
0x69, 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x1d,
0x68, 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x33,
0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73, 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x01,
0x68, 0x65, 0x62, 0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01,
0x74, 0x79, 0x70, 0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x00,
0x05, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x09,
0x00, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x04,
0x70, 0x00, 0x03, 0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x63,
0x63, 0x74, 0x00, 0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00,
0x65, 0x00, 0x03, 0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x65,
0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x64,
0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75,
0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65,
0x00, 0x04, 0xff, 0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x00,
0x00, 0x09, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74,
0xff, 0xff, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff, 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64,
0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x6e,
0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65,
0x00, 0x18, 0xff, 0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69,
0x00, 0x19, 0xff, 0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c, 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68,
0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00, 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x69,
0x00, 0x08 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;

View File

@@ -6,6 +6,8 @@
#include "pch.h" #include "pch.h"
#include "syntax/trie.h" #include "syntax/trie.h"
#define MAX_LINES_LOOKBEHIND 80
struct Highlight { struct Highlight {
uint32_t fg{0xFFFFFF}; uint32_t fg{0xFFFFFF};
uint32_t bg{0x000000}; uint32_t bg{0x000000};
@@ -35,10 +37,23 @@ struct Token {
TokenKind type; TokenKind type;
}; };
struct StateBase {
virtual ~StateBase() = default;
virtual std::unique_ptr<StateBase> clone() const = 0;
};
struct CustomState : StateBase {
std::string value;
explicit CustomState(std::string v) : value(std::move(v)) {}
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<CustomState>(value);
}
};
struct LineData { struct LineData {
std::shared_ptr<void> in_state{nullptr}; std::unique_ptr<StateBase> out_state;
std::vector<Token> tokens; std::vector<Token> tokens;
std::shared_ptr<void> out_state{nullptr}; std::unique_ptr<StateBase> in_state;
}; };
#endif #endif

View File

@@ -4,29 +4,19 @@
#include "syntax/decl.h" #include "syntax/decl.h"
#define DEF_LANG(name) \ #define DEF_LANG(name) \
std::shared_ptr<void> name##_parse( \ std::unique_ptr<StateBase> name##_parse( \
std::vector<Token> *tokens, std::shared_ptr<void> in_state, \ std::vector<Token> *tokens, StateBase *in_state, const char *text, \
const char *text, uint32_t len, uint32_t line_num); \ uint32_t len, uint32_t line_num); \
bool name##_state_match(std::shared_ptr<void> state_1, \ bool name##_state_match(StateBase *state_1, StateBase *state_2);
std::shared_ptr<void> state_2);
#define LANG_A(name) \ #define LANG_A(name) \
{ \ { \
#name, { name##_parse, name##_state_match } \ #name, { name##_parse, name##_state_match } \
} }
template <typename T> template <typename B, typename A>
inline std::shared_ptr<T> ensure_state(std::shared_ptr<T> state) { std::unique_ptr<B> static_unique_ptr_cast(std::unique_ptr<A> &&p) {
using U = typename T::full_state_type; return std::unique_ptr<B>(static_cast<B *>(p.release()));
if (!state)
state = std::make_shared<T>();
if (!state.unique())
state = std::make_shared<T>(*state);
if (!state->full_state)
state->full_state = std::make_shared<U>();
else if (!state->full_state.unique())
state->full_state = std::make_shared<U>(*state->full_state);
return state;
} }
DEF_LANG(ruby); DEF_LANG(ruby);
@@ -34,11 +24,10 @@ DEF_LANG(bash);
inline static const std::unordered_map< inline static const std::unordered_map<
std::string, std::string,
std::tuple<std::shared_ptr<void> (*)( std::tuple<std::unique_ptr<StateBase> (*)(
std::vector<Token> *tokens, std::shared_ptr<void> in_state, std::vector<Token> *tokens, StateBase *in_state,
const char *text, uint32_t len, uint32_t line_num), const char *text, uint32_t len, uint32_t line_num),
bool (*)(std::shared_ptr<void> state_1, bool (*)(StateBase *state_1, StateBase *state_2)>>
std::shared_ptr<void> state_2)>>
parsers = { parsers = {
LANG_A(ruby), LANG_A(ruby),
LANG_A(bash), LANG_A(bash),

104
include/syntax/line_map.h Normal file
View File

@@ -0,0 +1,104 @@
#ifndef LINE_MAP_H
#define LINE_MAP_H
#include "syntax/decl.h"
struct LineKey {
uint32_t line;
uint32_t version;
bool operator==(const LineKey &other) const {
return line == other.line && version == other.version;
}
};
struct LineKeyHash {
size_t operator()(const LineKey &key) const {
uint64_t combined = (uint64_t(key.line) << 32) | key.version;
return std::hash<uint64_t>()(combined);
}
};
struct EditDelta {
uint32_t start_line;
int64_t delta;
};
struct LineMap {
LineMap() : current_version(0) { edit_log.push_back({0, 0}); }
LineData *at(uint32_t line) {
auto key_opt = resolve_line(line);
if (!key_opt)
return nullptr;
LineKey key = *key_opt;
if (key.version == current_version)
return lines[key].get();
auto data_ptr = std::move(lines[key]);
lines.erase(key);
key = {line, current_version};
lines[key] = std::move(data_ptr);
return lines[key].get();
}
LineData *create_at(uint32_t line) {
auto key_opt = resolve_line(line);
LineKey key;
std::unique_ptr<LineData> data_ptr;
if (key_opt) {
key = *key_opt;
if (key.version == current_version)
return lines[key].get();
data_ptr = std::move(lines[key]);
lines.erase(key);
} else {
data_ptr = std::make_unique<LineData>();
}
key = {line, current_version};
lines[key] = std::move(data_ptr);
return lines[key].get();
}
void apply_edit(uint32_t start, int64_t delta) {
if (delta < 0) {
int64_t count = -delta;
for (int64_t i = 0; i < count; i++) {
auto key = resolve_line(start + i);
if (!key)
continue;
lines.erase(*key);
}
}
current_version++;
edit_log.push_back({start, delta});
}
private:
std::unordered_map<LineKey, std::unique_ptr<LineData>, LineKeyHash> lines;
std::vector<EditDelta> edit_log;
uint32_t current_version;
std::optional<LineKey> resolve_line(uint32_t line) {
uint32_t current_line = line;
for (int64_t v = current_version; v >= 0; v--) {
LineKey key = {current_line, (uint32_t)v};
if (lines.find(key) != lines.end())
return key;
const auto &edit = edit_log[v];
if (edit.delta > 0) {
if (current_line >= edit.start_line) {
if (current_line < edit.start_line + edit.delta)
return std::nullopt;
current_line -= edit.delta;
}
} else {
if (current_line >= edit.start_line)
current_line -= edit.delta;
}
}
return std::nullopt;
}
};
#endif

View File

@@ -1,232 +0,0 @@
#ifndef LINE_TREE_H
#define LINE_TREE_H
#include "syntax/decl.h"
struct LineTree {
void clear() {
clear_node(root);
root = nullptr;
stack_size = 0;
}
void build(uint32_t x) { root = build_node(x); }
LineData *at(uint32_t x) {
LineNode *n = root;
while (n) {
uint32_t left_size = n->left ? n->left->size : 0;
if (x < left_size) {
n = n->left;
} else if (x < left_size + n->data.size()) {
return &n->data[x - left_size];
} else {
x -= left_size + n->data.size();
n = n->right;
}
}
return nullptr;
}
LineData *start_iter(uint32_t x) {
stack_size = 0;
LineNode *n = root;
while (n) {
uint32_t left_size = n->left ? n->left->size : 0;
if (x < left_size) {
push(n, 0);
n = n->left;
} else if (x < left_size + n->data.size()) {
push(n, x - left_size + 1);
return &n->data[x - left_size];
} else {
x -= left_size + n->data.size();
push(n, UINT32_MAX);
n = n->right;
}
}
return nullptr;
}
void end_iter() { stack_size = 0; }
LineData *next() {
while (stack_size) {
auto &f = stack[stack_size - 1];
LineNode *n = f.node;
if (f.index < n->data.size())
return &n->data[f.index++];
stack_size--;
if (n->right) {
n = n->right;
while (n) {
push(n, 0);
if (!n->left)
break;
n = n->left;
}
return &stack[stack_size - 1].node->data[0];
}
}
return nullptr;
}
void insert(uint32_t x, uint32_t y) {
if (x > subtree_size(root))
x = subtree_size(root);
root = insert_node(root, x, y);
}
void erase(uint32_t x, uint32_t y) {
if (x + y > subtree_size(root))
x = subtree_size(root) - y;
root = erase_node(root, x, y);
}
uint32_t count() { return subtree_size(root); }
~LineTree() { clear(); }
private:
struct LineNode {
LineNode *left = nullptr;
LineNode *right = nullptr;
uint8_t depth = 1;
uint32_t size = 0;
std::vector<LineData> data;
};
struct Frame {
LineNode *node;
uint32_t index;
};
void push(LineNode *n, uint32_t x) {
stack[stack_size].node = n;
stack[stack_size].index = x;
stack_size++;
}
static void clear_node(LineNode *n) {
if (!n)
return;
clear_node(n->left);
clear_node(n->right);
delete n;
}
LineNode *root = nullptr;
Frame stack[32];
std::atomic<uint8_t> stack_size = 0;
static constexpr uint32_t LEAF_TARGET = 256;
LineTree::LineNode *erase_node(LineNode *n, uint32_t x, uint32_t y) {
if (!n || y == 0)
return n;
uint32_t left_sz = subtree_size(n->left);
uint32_t mid_sz = n->data.size();
if (x < left_sz) {
uint32_t len = std::min(y, left_sz - x);
n->left = erase_node(n->left, x, len);
y -= len;
x = left_sz;
}
if (y > 0 && x < left_sz + mid_sz) {
uint32_t mid_x = x - left_sz;
uint32_t len = std::min(y, mid_sz - mid_x);
n->data.erase(n->data.begin() + mid_x, n->data.begin() + mid_x + len);
y -= len;
x += len;
}
if (y > 0) {
n->right = erase_node(n->right, x - left_sz - n->data.size(), y);
}
if (n->left && n->right &&
subtree_size(n->left) + subtree_size(n->right) < 256) {
return merge(n->left, n->right);
}
return rebalance(n);
}
LineTree::LineNode *insert_node(LineNode *n, uint32_t x, uint32_t y) {
if (!n) {
auto *leaf = new LineNode();
leaf->data.resize(y);
leaf->size = y;
return leaf;
}
if (!n->left && !n->right) {
n->data.insert(n->data.begin() + x, y, LineData());
fix(n);
if (n->data.size() > 512)
return split_leaf(n);
return n;
}
uint32_t left_size = subtree_size(n->left);
if (x <= left_size)
n->left = insert_node(n->left, x, y);
else
n->right = insert_node(n->right, x - left_size - n->data.size(), y);
return rebalance(n);
}
LineNode *build_node(uint32_t count) {
if (count <= LEAF_TARGET) {
auto *n = new LineNode();
n->data.resize(count);
n->size = count;
return n;
}
uint32_t left_count = count / 2;
uint32_t right_count = count - left_count;
auto *n = new LineNode();
n->left = build_node(left_count);
n->right = build_node(right_count);
fix(n);
return n;
}
static LineNode *split_leaf(LineNode *n) {
auto *right = new LineNode();
size_t mid = n->data.size() / 2;
right->data.assign(n->data.begin() + mid, n->data.end());
n->data.resize(mid);
fix(n);
fix(right);
auto *parent = new LineNode();
parent->left = n;
parent->right = right;
fix(parent);
return parent;
}
static LineNode *merge(LineNode *a, LineNode *b) {
a->data.insert(a->data.end(), b->data.begin(), b->data.end());
delete b;
fix(a);
return a;
}
static void fix(LineNode *n) {
n->depth = 1 + MAX(height(n->left), height(n->right));
n->size = subtree_size(n->left) + n->data.size() + subtree_size(n->right);
}
static LineNode *rotate_right(LineNode *y) {
LineNode *x = y->left;
LineNode *T2 = x->right;
x->right = y;
y->left = T2;
fix(y);
fix(x);
return x;
}
static LineNode *rotate_left(LineNode *x) {
LineNode *y = x->right;
LineNode *T2 = y->left;
y->left = x;
x->right = T2;
fix(x);
fix(y);
return y;
}
static LineNode *rebalance(LineNode *n) {
fix(n);
int balance = int(height(n->left)) - int(height(n->right));
if (balance > 1) {
if (height(n->left->left) < height(n->left->right))
n->left = rotate_left(n->left);
return rotate_right(n);
}
if (balance < -1) {
if (height(n->right->right) < height(n->right->left))
n->right = rotate_right(n->right);
return rotate_left(n);
}
return n;
}
static uint8_t height(LineNode *n) { return n ? n->depth : 0; }
static uint32_t subtree_size(LineNode *n) { return n ? n->size : 0; }
};
#endif

View File

@@ -3,24 +3,22 @@
#include "ruby/decl.h" #include "ruby/decl.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "syntax/line_tree.h" #include "syntax/line_map.h"
struct Parser { struct Parser {
struct Editor *editor = nullptr; struct Editor *editor = nullptr;
std::string lang; std::string lang;
std::shared_ptr<void> (*parse_func)(std::vector<Token> *tokens, std::unique_ptr<StateBase> (*parse_func)(std::vector<Token> *tokens,
std::shared_ptr<void> in_state, StateBase *in_state,
const char *text, uint32_t len, const char *text, uint32_t len,
uint32_t line_num); uint32_t line_num);
bool (*state_match_func)(std::shared_ptr<void> state_1, bool (*state_match_func)(StateBase *state_1, StateBase *state_2);
std::shared_ptr<void> state_2);
mrb_value parser_block = mrb_nil_value(); mrb_value parser_block = mrb_nil_value();
mrb_value match_block = mrb_nil_value(); mrb_value match_block = mrb_nil_value();
bool is_custom{false}; bool is_custom{false};
std::atomic<uint32_t> scroll_max{0}; std::atomic<uint32_t> scroll_max{0};
std::atomic<bool> scroll_dirty{false}; std::atomic<bool> scroll_dirty{false};
LineTree line_tree; LineMap line_map = LineMap();
UniqueQueue<uint32_t> dirty_lines;
Parser(Editor *editor, std::string n_lang, uint32_t n_scroll_max); Parser(Editor *editor, std::string n_lang, uint32_t n_scroll_max);
void edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows); void edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows);

View File

@@ -1,10 +0,0 @@
#ifndef TILING_DECL_H
#define TILING_DECL_H
#include "utils/utils.h"
struct Window {};
struct TileBlock {};
#endif

View File

@@ -12,8 +12,9 @@ struct Bar {
uint32_t cursor = 0; uint32_t cursor = 0;
void init(Coord screen) { this->screen = screen; } void init(Coord screen) { this->screen = screen; }
void render(); void render(std::vector<ScreenCell> &buffer);
void handle(KeyEvent event); void handle_event(KeyEvent event);
void handle_command(std::string &command);
void log(std::string message); void log(std::string message);
}; };

View File

@@ -1,21 +1,21 @@
#ifndef UI_COMPLETIONBOX_H // #ifndef UI_COMPLETIONBOX_H
#define UI_COMPLETIONBOX_H // #define UI_COMPLETIONBOX_H
//
#include "io/sysio.h" // #include "io/sysio.h"
#include "pch.h" // #include "pch.h"
#include "utils/utils.h" // #include "utils/utils.h"
//
struct CompletionBox { // struct CompletionBox {
std::shared_mutex mtx; // std::shared_mutex mtx;
struct CompletionSession *session; // struct CompletionSession *session;
bool hidden = true; // bool hidden = true;
std::vector<ScreenCell> cells; // std::vector<ScreenCell> cells;
Coord size; // Coord size;
Coord position; // Coord position;
//
CompletionBox(CompletionSession *s) : session(s) {} // CompletionBox(CompletionSession *s) : session(s) {}
void render_update(); // void render_update();
void render(Coord pos); // void render(Coord pos);
}; // };
//
#endif // #endif

View File

@@ -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<bool> is_markup;
uint32_t scroll_;
std::vector<ScreenCell> cells;
Coord size;
void clear();
void scroll(int32_t number);
void render_first(bool scroll = false);
void render(Coord pos);
};
#endif

View File

@@ -92,6 +92,11 @@ struct Coord {
bool operator>=(const Coord &other) const { return !(*this < other); } bool operator>=(const Coord &other) const { return !(*this < other); }
}; };
static inline bool inside(Coord c, Coord pos, Coord size) {
return c.row >= pos.row && c.col >= pos.col && c.row - pos.row < size.row &&
c.col - pos.col < size.col;
}
struct Match { struct Match {
size_t start; size_t start;
size_t end; size_t end;

129
include/windows/decl.h Normal file
View File

@@ -0,0 +1,129 @@
#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<ScreenCell> &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<std::string, 5> bar_info() { return {}; };
};
struct Popup : Window {
Coord pos;
Coord size;
virtual ~Popup() = default;
};
struct TileBlock : Tile {
bool vertical;
std::vector<std::unique_ptr<Tile>> tiles;
void render(std::vector<ScreenCell> &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 TileBase {
std::unique_ptr<Tile> tile;
Coord pos;
Coord size;
inline bool hidden() { return this->tile->hidden; }
void render(std::vector<ScreenCell> &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);
}
};
namespace layout {
extern TileBase root_tile;
extern Window *focused_window;
extern std::vector<std::unique_ptr<Popup>> popups;
extern std::vector<std::unique_ptr<TileBase>> floating_tiles;
} // namespace layout
inline void close(Popup *handle) {
std::erase_if(layout::popups,
[handle](const auto &p) { return p.get() == handle; });
}
inline void close(TileBase *handle) {
std::erase_if(layout::floating_tiles,
[handle](const auto &p) { return p.get() == handle; });
}
void render();
void handle_click(KeyEvent event);
#endif

View File

@@ -25,6 +25,7 @@ colorize() {
handle_error() { handle_error() {
log ERROR "An error occurred on line $1" log ERROR "An error occurred on line $1"
} }
trap 'handle_error $LINENO' ERR trap 'handle_error $LINENO' ERR
# Multiline string test # Multiline string test
read -r -d '' MULTI <<'CPP' read -r -d '' MULTI <<'CPP'

View File

@@ -1,25 +1,24 @@
#include "editor/editor.h" #include "editor/editor.h"
void ensure_cursor(Editor *editor) { void Editor::ensure_cursor() {
std::shared_lock knot_lock(editor->knot_mtx); if (this->cursor < this->scroll) {
if (editor->cursor < editor->scroll) { this->cursor.row = this->scroll.row;
editor->cursor.row = editor->scroll.row; this->cursor.col = this->scroll.col;
editor->cursor.col = editor->scroll.col; this->cursor_preffered = UINT32_MAX;
editor->cursor_preffered = UINT32_MAX;
return; return;
} }
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
uint32_t visual_rows = 0; uint32_t visual_rows = 0;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
bool first_visual_line = true; 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) if (!it)
return; return;
Coord last_visible = editor->scroll; Coord last_visible = this->scroll;
while (true) { while (true) {
if (visual_rows >= editor->size.row) if (visual_rows >= this->size.row)
break; break;
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
@@ -27,13 +26,13 @@ void ensure_cursor(Editor *editor) {
break; break;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; 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; first_visual_line = false;
while (offset < line_len || (line_len == 0 && offset == 0)) { while (offset < line_len || (line_len == 0 && offset == 0)) {
Coord current = {line_index, offset}; Coord current = {line_index, offset};
last_visible = current; last_visible = current;
visual_rows++; visual_rows++;
if (visual_rows >= editor->size.row) if (visual_rows >= this->size.row)
break; break;
uint32_t col = 0; uint32_t col = 0;
uint32_t advance = 0; uint32_t advance = 0;
@@ -48,9 +47,9 @@ void ensure_cursor(Editor *editor) {
left -= g; left -= g;
col += w; col += w;
} }
if (line_index == editor->cursor.row) { if (line_index == this->cursor.row) {
if (editor->cursor.col >= offset && if (this->cursor.col >= offset &&
editor->cursor.col <= offset + advance) { this->cursor.col <= offset + advance) {
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -64,20 +63,19 @@ void ensure_cursor(Editor *editor) {
} }
line_index++; line_index++;
} }
editor->cursor.row = last_visible.row; this->cursor.row = last_visible.row;
editor->cursor.col = last_visible.col; this->cursor.col = last_visible.col;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
free(it->buffer); free(it->buffer);
free(it); free(it);
} }
void ensure_scroll(Editor *editor) { void Editor::ensure_scroll() {
std::shared_lock knot_lock(editor->knot_mtx);
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
if (editor->cursor < editor->scroll) { if (this->cursor < this->scroll) {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -98,9 +96,9 @@ void ensure_scroll(Editor *editor) {
int width = display_width(line + offset, inc); int width = display_width(line + offset, inc);
if (cols + width > render_width) { if (cols + width > render_width) {
cols = 0; cols = 0;
if (editor->cursor.col > old_offset && editor->cursor.col <= offset) { if (this->cursor.col > old_offset && this->cursor.col <= offset) {
editor->scroll.row = editor->cursor.row; this->scroll.row = this->cursor.row;
editor->scroll.col = old_offset; this->scroll.col = old_offset;
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -112,14 +110,14 @@ void ensure_scroll(Editor *editor) {
} }
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->scroll.row = editor->cursor.row; this->scroll.row = this->cursor.row;
editor->scroll.col = (editor->cursor.col == 0) ? 0 : old_offset; this->scroll.col = (this->cursor.col == 0) ? 0 : old_offset;
} else if (editor->cursor.row - editor->scroll.row < editor->size.row * 2) { } else if (this->cursor.row - this->scroll.row < this->size.row * 2) {
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; 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); Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines);
uint32_t q_head = 0; uint32_t q_head = 0;
uint32_t q_size = 0; uint32_t q_size = 0;
@@ -133,7 +131,7 @@ void ensure_scroll(Editor *editor) {
line_len--; line_len--;
uint32_t current_byte_offset = 0; uint32_t current_byte_offset = 0;
if (first_visual_line) { if (first_visual_line) {
current_byte_offset += editor->scroll.col; current_byte_offset += this->scroll.col;
first_visual_line = false; first_visual_line = false;
} }
while (current_byte_offset < line_len || while (current_byte_offset < line_len ||
@@ -160,16 +158,16 @@ void ensure_scroll(Editor *editor) {
line_left -= cluster_len; line_left -= cluster_len;
col += width; col += width;
} }
if (line_index == editor->cursor.row) { if (line_index == this->cursor.row) {
bool cursor_found = false; bool cursor_found = false;
if (editor->cursor.col >= current_byte_offset && if (this->cursor.col >= current_byte_offset &&
editor->cursor.col < current_byte_offset + local_render_offset) this->cursor.col < current_byte_offset + local_render_offset)
cursor_found = true; 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) current_byte_offset + local_render_offset == line_len)
cursor_found = true; cursor_found = true;
if (cursor_found) { if (cursor_found) {
editor->scroll = scroll_queue[q_head]; this->scroll = scroll_queue[q_head];
free(scroll_queue); free(scroll_queue);
free(it->buffer); free(it->buffer);
free(it); free(it);
@@ -186,10 +184,10 @@ void ensure_scroll(Editor *editor) {
free(it->buffer); free(it->buffer);
free(it); free(it);
} else { } else {
editor->scroll.row = (editor->cursor.row > editor->size.row * 1.5) this->scroll.row = (this->cursor.row > this->size.row * 1.5)
? editor->cursor.row - editor->size.row * 1.5 ? this->cursor.row - this->size.row * 1.5
: 0; : 0;
editor->scroll.col = 0; this->scroll.col = 0;
ensure_scroll(editor); this->ensure_scroll();
} }
} }

View File

@@ -32,12 +32,9 @@ uint32_t scan_right(const char *line, uint32_t len, uint32_t off) {
return i; return i;
} }
void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col, void Editor::word_boundaries_exclusive(Coord coord, uint32_t *prev_col,
uint32_t *next_col) { uint32_t *next_col) {
if (!editor) LineIterator *it = begin_l_iter(this->root, coord.row);
return;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, coord.row);
if (!it) if (!it)
return; return;
uint32_t line_len; 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<uint32_t>(last); return static_cast<uint32_t>(last);
} }
void word_boundaries(Editor *editor, Coord coord, uint32_t *prev_col, void Editor::word_boundaries(Coord coord, uint32_t *prev_col,
uint32_t *next_col, uint32_t *prev_clusters, uint32_t *next_col, uint32_t *prev_clusters,
uint32_t *next_clusters) { uint32_t *next_clusters) {
if (!editor) LineIterator *it = begin_l_iter(this->root, coord.row);
return;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, coord.row);
if (!it) if (!it)
return; return;
uint32_t line_len; uint32_t line_len;

View File

@@ -1,23 +1,19 @@
#include "editor/editor.h" #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 = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
x = MAX(x, numlen) - numlen + 1; x = MAX(x, numlen) - numlen - 1;
uint32_t target_visual_row = y; uint32_t target_visual_row = y;
uint32_t visual_row = 0; uint32_t visual_row = 0;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
uint32_t last_line_index = editor->scroll.row; uint32_t last_line_index = this->scroll.row;
uint32_t last_col = editor->scroll.col; uint32_t last_col = this->scroll.col;
bool first_visual_line = true; bool first_visual_line = true;
std::shared_lock knot_lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, line_index);
LineIterator *it = begin_l_iter(editor->root, line_index);
if (!it) if (!it)
return editor->scroll; return this->scroll;
while (visual_row <= target_visual_row) { while (visual_row <= target_visual_row) {
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
@@ -27,7 +23,7 @@ Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) {
line_len--; line_len--;
last_line_index = line_index; last_line_index = line_index;
last_col = line_len; 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; first_visual_line = false;
while (offset < line_len || (line_len == 0 && offset == 0)) { while (offset < line_len || (line_len == 0 && offset == 0)) {
uint32_t col = 0; uint32_t col = 0;

13
src/editor/commands.cc Normal file
View File

@@ -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);
}
}

View File

@@ -1,459 +1,458 @@
#include "editor/decl.h" // #include "editor/decl.h"
#include "editor/editor.h" // #include "editor/editor.h"
#include "io/knot.h" // #include "io/knot.h"
#include "io/sysio.h" // #include "io/sysio.h"
#include "lsp/lsp.h" // #include "lsp/lsp.h"
#include "main.h" // #include "main.h"
#include "utils/utils.h" // #include "ui/completionbox.h"
// #include "utils/utils.h"
inline static std::string completion_prefix(Editor *editor) { //
Coord hook = editor->completion.hook; // inline static std::string completion_prefix(Editor *editor) {
Coord cur = editor->cursor; // Coord hook = editor->completion.hook;
if (hook.row != cur.row || cur.col < hook.col) // Coord cur = editor->cursor;
return ""; // if (hook.row != cur.row || cur.col < hook.col)
LineIterator *it = begin_l_iter(editor->root, hook.row); // return "";
uint32_t line_len; // LineIterator *it = begin_l_iter(editor->root, hook.row);
char *line = next_line(it, &line_len); // uint32_t line_len;
if (!line) { // char *line = next_line(it, &line_len);
free(it->buffer); // if (!line) {
free(it); // free(it->buffer);
return ""; // free(it);
} // return "";
std::string prefix(line + hook.col, cur.col - hook.col); // }
free(it->buffer); // std::string prefix(line + hook.col, cur.col - hook.col);
free(it); // free(it->buffer);
return prefix; // free(it);
} // return prefix;
// }
inline static void completion_adjust_scroll(CompletionSession &s) { //
if (s.visible.empty()) // inline static void completion_adjust_scroll(CompletionSession &s) {
return; // if (s.visible.empty())
int vi = -1; // return;
for (size_t i = 0; i < s.visible.size(); i++) // int vi = -1;
if (s.visible[i] == s.select) { // for (size_t i = 0; i < s.visible.size(); i++)
vi = (int)i; // if (s.visible[i] == s.select) {
break; // vi = (int)i;
} // break;
if (vi < 0) // }
return; // if (vi < 0)
if ((uint32_t)vi < s.scroll) // return;
s.scroll = vi; // if ((uint32_t)vi < s.scroll)
else if ((uint32_t)vi >= s.scroll + 8) // s.scroll = vi;
s.scroll = vi - 7; // else if ((uint32_t)vi >= s.scroll + 8)
} // s.scroll = vi - 7;
// }
void completion_filter(Editor *editor) { //
auto &session = editor->completion; // void completion_filter(Editor *editor) {
std::string prefix = completion_prefix(editor); // auto &session = editor->completion;
session.visible.clear(); // std::string prefix = completion_prefix(editor);
for (size_t i = 0; i < session.items.size(); ++i) { // session.visible.clear();
const auto &item = session.items[i]; // for (size_t i = 0; i < session.items.size(); ++i) {
const std::string &key = item.filter; // const auto &item = session.items[i];
if (key.size() >= prefix.size() && // const std::string &key = item.filter;
key.compare(0, prefix.size(), prefix) == 0) // if (key.size() >= prefix.size() &&
session.visible.push_back(i); // key.compare(0, prefix.size(), prefix) == 0)
} // session.visible.push_back(i);
if (session.visible.empty()) { // }
session.box.hidden = true; // if (session.visible.empty()) {
return; // session.box.hidden = true;
} // return;
if (std::find(session.visible.begin(), session.visible.end(), // }
session.select) == session.visible.end()) // if (std::find(session.visible.begin(), session.visible.end(),
session.select = session.visible[0]; // session.select) == session.visible.end())
session.box.hidden = false; // session.select = session.visible[0];
session.scroll = 0; // session.box.hidden = false;
completion_adjust_scroll(session); // session.scroll = 0;
session.box.render_update(); // completion_adjust_scroll(session);
} // session.box.render_update();
// }
void completion_request(Editor *editor) { //
Coord hook = editor->cursor; // void completion_request(Editor *editor) {
word_boundaries(editor, editor->cursor, &hook.col, nullptr, nullptr, nullptr); // Coord hook = editor->cursor;
editor->completion.hook = hook; // editor->word_boundaries(editor->cursor, &hook.col, nullptr, nullptr,
editor->completion.complete = false; // nullptr); editor->completion.hook = hook; editor->completion.complete =
editor->completion.active = false; // false; editor->completion.active = false; editor->completion.items.clear();
editor->completion.items.clear(); // editor->completion.visible.clear();
editor->completion.visible.clear(); // editor->completion.select = 0;
editor->completion.select = 0; // editor->completion.version = editor->lsp_version;
editor->completion.version = editor->lsp_version; // LSPPending *pending = new LSPPending();
LSPPending *pending = new LSPPending(); // pending->editor = editor;
pending->editor = editor; // pending->callback = [](Editor *editor, const json &message) {
pending->callback = [](Editor *editor, const json &message) { // auto &session = editor->completion;
auto &session = editor->completion; // std::unique_lock lock(session.mtx);
std::unique_lock lock(session.mtx); // std::vector<json> items_json;
std::vector<json> items_json; // std::vector<char> end_chars_def;
std::vector<char> end_chars_def; // int insert_text_format = 1;
int insert_text_format = 1; // int insert_text_mode = 1;
int insert_text_mode = 1; // if (message.contains("result")) {
if (message.contains("result")) { // auto &result = message["result"];
auto &result = message["result"]; // if (result.is_array()) {
if (result.is_array()) { // items_json = result.get<std::vector<json>>();
items_json = result.get<std::vector<json>>(); // session.complete = true;
session.complete = true; // if (items_json.empty())
if (items_json.empty()) // return;
return; // editor->completion.active = true;
editor->completion.active = true; // } else if (result.is_object() && result.contains("items")) {
} else if (result.is_object() && result.contains("items")) { // auto &list = result;
auto &list = result; // items_json = list["items"].get<std::vector<json>>();
items_json = list["items"].get<std::vector<json>>(); // if (items_json.empty())
if (items_json.empty()) // return;
return; // editor->completion.active = true;
editor->completion.active = true; // session.complete = !list.value("isIncomplete", false);
session.complete = !list.value("isIncomplete", false); // if (list.contains("itemDefaults") &&
if (list.contains("itemDefaults") && list["itemDefaults"].is_object()) { // list["itemDefaults"].is_object()) {
auto &defs = list["itemDefaults"]; // auto &defs = list["itemDefaults"];
if (defs.contains("insertTextFormat") && // if (defs.contains("insertTextFormat") &&
defs["insertTextFormat"].is_number()) // defs["insertTextFormat"].is_number())
insert_text_format = defs["insertTextFormat"].get<int>(); // insert_text_format = defs["insertTextFormat"].get<int>();
if (defs.contains("insertTextMode") && // if (defs.contains("insertTextMode") &&
defs["insertTextMode"].is_number()) // defs["insertTextMode"].is_number())
insert_text_mode = defs["insertTextMode"].get<int>(); // insert_text_mode = defs["insertTextMode"].get<int>();
if (defs.contains("textEdit")) // if (defs.contains("textEdit"))
if (defs["textEdit"].is_array()) // if (defs["textEdit"].is_array())
for (auto &c : defs["textEdit"]) { // for (auto &c : defs["textEdit"]) {
if (!c.is_string()) // if (!c.is_string())
continue; // continue;
std::string str = c.get<std::string>(); // std::string str = c.get<std::string>();
if (str.size() != 1) // if (str.size() != 1)
continue; // continue;
end_chars_def.push_back(str[0]); // end_chars_def.push_back(str[0]);
} // }
} // }
} // }
} // }
session.items.reserve(items_json.size() + 1); // session.items.reserve(items_json.size() + 1);
session.visible.reserve(items_json.size() + 1); // session.visible.reserve(items_json.size() + 1);
for (auto &item_json : items_json) { // for (auto &item_json : items_json) {
CompletionItem item; // CompletionItem item;
item.original = item_json; // item.original = item_json;
item.label = item_json.value("label", ""); // item.label = item_json.value("label", "");
item.kind = item_json.value("kind", 0); // item.kind = item_json.value("kind", 0);
if (item_json.contains("detail") && item_json["detail"].is_string()) // if (item_json.contains("detail") && item_json["detail"].is_string())
item.detail = item_json["detail"].get<std::string>(); // item.detail = item_json["detail"].get<std::string>();
if (item_json.contains("documentation")) { // if (item_json.contains("documentation")) {
if (item_json["documentation"].is_string()) { // if (item_json["documentation"].is_string()) {
item.documentation = item_json["documentation"].get<std::string>(); // item.documentation = item_json["documentation"].get<std::string>();
} else if (item_json["documentation"].contains("value") && // } else if (item_json["documentation"].contains("value") &&
item_json["documentation"]["value"].is_string()) { // item_json["documentation"]["value"].is_string()) {
item.is_markup = // item.is_markup =
item_json["documentation"]["kind"].get<std::string>() == // item_json["documentation"]["kind"].get<std::string>() ==
"markdown"; // "markdown";
std::string documentation = // std::string documentation =
item_json["documentation"]["value"].get<std::string>(); // item_json["documentation"]["value"].get<std::string>();
if (documentation.size() > 1024) // if (documentation.size() > 1024)
item.is_markup = false; // item.is_markup = false;
if (item.is_markup) { // if (item.is_markup) {
item.documentation = // item.documentation =
substitute_fence(documentation, editor->lang.name); // substitute_fence(documentation, editor->lang.name);
} else { // } else {
item.documentation = documentation; // item.documentation = documentation;
} // }
} // }
} // }
if (item_json.contains("deprecated") && // if (item_json.contains("deprecated") &&
item_json["deprecated"].is_boolean()) // item_json["deprecated"].is_boolean())
item.deprecated = item_json["deprecated"].get<bool>(); // item.deprecated = item_json["deprecated"].get<bool>();
auto tags = item_json.value("tags", std::vector<int>()); // auto tags = item_json.value("tags", std::vector<int>());
for (auto tag : tags) // for (auto tag : tags)
if (tag == 1) // if (tag == 1)
item.deprecated = true; // item.deprecated = true;
item.sort = item_json.value("sortText", item.label); // item.sort = item_json.value("sortText", item.label);
item.filter = item_json.value("filterText", item.label); // item.filter = item_json.value("filterText", item.label);
if (item_json.contains("preselect") && // if (item_json.contains("preselect") &&
item_json["preselect"].is_boolean() && // item_json["preselect"].is_boolean() &&
item_json["preselect"].get<bool>()) // item_json["preselect"].get<bool>())
session.select = session.items.size() - 1; // session.select = session.items.size() - 1;
TextEdit edit; // TextEdit edit;
if (item_json.contains("textEdit")) { // if (item_json.contains("textEdit")) {
auto &te = item_json["textEdit"]; // auto &te = item_json["textEdit"];
if (te.contains("newText")) { // if (te.contains("newText")) {
edit.text = te.value("newText", ""); // edit.text = te.value("newText", "");
if (te.contains("replace")) { // if (te.contains("replace")) {
edit.start.row = te["replace"]["start"]["line"]; // edit.start.row = te["replace"]["start"]["line"];
edit.start.col = te["replace"]["start"]["character"]; // edit.start.col = te["replace"]["start"]["character"];
edit.end.row = te["replace"]["end"]["line"]; // edit.end.row = te["replace"]["end"]["line"];
edit.end.col = te["replace"]["end"]["character"]; // edit.end.col = te["replace"]["end"]["character"];
} else if (te.contains("insert")) { // } else if (te.contains("insert")) {
edit.start.row = te["insert"]["start"]["line"]; // edit.start.row = te["insert"]["start"]["line"];
edit.start.col = te["insert"]["start"]["character"]; // edit.start.col = te["insert"]["start"]["character"];
edit.end.row = te["insert"]["end"]["line"]; // edit.end.row = te["insert"]["end"]["line"];
edit.end.col = te["insert"]["end"]["character"]; // edit.end.col = te["insert"]["end"]["character"];
} else if (te.contains("range")) { // } else if (te.contains("range")) {
edit.start.row = te["range"]["start"]["line"]; // edit.start.row = te["range"]["start"]["line"];
edit.start.col = te["range"]["start"]["character"]; // edit.start.col = te["range"]["start"]["character"];
edit.end.row = te["range"]["end"]["line"]; // edit.end.row = te["range"]["end"]["line"];
edit.end.col = te["range"]["end"]["character"]; // edit.end.col = te["range"]["end"]["character"];
} else { // } else {
edit.start = session.hook; // edit.start = session.hook;
edit.end = editor->cursor; // edit.end = editor->cursor;
} // }
} // }
} else if (item_json.contains("insertText") && // } else if (item_json.contains("insertText") &&
item_json["insertText"].is_string()) { // item_json["insertText"].is_string()) {
edit.text = item_json["insertText"].get<std::string>(); // edit.text = item_json["insertText"].get<std::string>();
edit.start = session.hook; // edit.start = session.hook;
edit.end = editor->cursor; // edit.end = editor->cursor;
} else { // } else {
edit.text = item.label; // edit.text = item.label;
edit.start = session.hook; // edit.start = session.hook;
edit.end = editor->cursor; // edit.end = editor->cursor;
} // }
utf8_normalize_edit(editor, &edit); // editor->utf8_normalize_edit(&edit);
item.edits.push_back(edit); // item.edits.push_back(edit);
if (item_json.contains("additionalTextEdits")) { // if (item_json.contains("additionalTextEdits")) {
for (auto &te : item_json["additionalTextEdits"]) { // for (auto &te : item_json["additionalTextEdits"]) {
TextEdit edit; // TextEdit edit;
edit.text = te.value("newText", ""); // edit.text = te.value("newText", "");
edit.start.row = te["range"]["start"]["line"]; // edit.start.row = te["range"]["start"]["line"];
edit.start.col = te["range"]["start"]["character"]; // edit.start.col = te["range"]["start"]["character"];
edit.end.row = te["range"]["end"]["line"]; // edit.end.row = te["range"]["end"]["line"];
edit.end.col = te["range"]["end"]["character"]; // edit.end.col = te["range"]["end"]["character"];
utf8_normalize_edit(editor, &edit); // editor->utf8_normalize_edit(&edit);
item.edits.push_back(edit); // item.edits.push_back(edit);
} // }
} // }
item.snippet = insert_text_format == 2; // item.snippet = insert_text_format == 2;
if (item_json.contains("insertTextFormat")) // if (item_json.contains("insertTextFormat"))
item.snippet = item_json["insertTextFormat"].get<int>() == 2; // item.snippet = item_json["insertTextFormat"].get<int>() == 2;
if (item_json.contains("insertTextMode")) // if (item_json.contains("insertTextMode"))
item.asis = item_json["insertTextMode"].get<int>() == 1; // item.asis = item_json["insertTextMode"].get<int>() == 1;
if (item_json.contains("commitCharacters")) // if (item_json.contains("commitCharacters"))
for (auto &c : item_json["commitCharacters"]) // for (auto &c : item_json["commitCharacters"])
if (c.is_string() && c.get<std::string>().size() == 1) // if (c.is_string() && c.get<std::string>().size() == 1)
item.end_chars.push_back(c.get<std::string>()[0]); // item.end_chars.push_back(c.get<std::string>()[0]);
session.items.push_back(std::move(item)); // session.items.push_back(std::move(item));
session.visible.push_back(session.items.size() - 1); // session.visible.push_back(session.items.size() - 1);
} // }
session.box.hidden = false; // session.box.hidden = false;
session.box.render_update(); // session.box.render_update();
}; // };
std::shared_lock lock(editor->knot_mtx); // LineIterator *it = begin_l_iter(editor->root, hook.row);
LineIterator *it = begin_l_iter(editor->root, hook.row); // uint32_t length;
uint32_t length; // char *line = next_line(it, &length);
char *line = next_line(it, &length); // if (!line) {
if (!line) { // free(it->buffer);
free(it->buffer); // free(it);
free(it); // return;
return; // }
} // uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col);
uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col); // free(it->buffer);
free(it->buffer); // free(it);
free(it); // json message = {
lock.unlock(); // {"jsonrpc", "2.0"},
json message = { // {"method", "textDocument/completion"},
{"jsonrpc", "2.0"}, // {"params",
{"method", "textDocument/completion"}, // {{"textDocument", {{"uri", editor->uri}}},
{"params", // {"position", {{"line", editor->cursor.row}, {"character", col}}}}}};
{{"textDocument", {{"uri", editor->uri}}}, // if (editor->completion.trigger > 0) {
{"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; // json context = {{"triggerKind", editor->completion.trigger}};
if (editor->completion.trigger > 0) { // if (editor->completion.trigger == 2 && editor->completion.trigger_char)
json context = {{"triggerKind", editor->completion.trigger}}; // context["triggerCharacter"] =
if (editor->completion.trigger == 2 && editor->completion.trigger_char) // std::string(1, *editor->completion.trigger_char);
context["triggerCharacter"] = // message["params"]["context"] = context;
std::string(1, *editor->completion.trigger_char); // }
message["params"]["context"] = context; // lsp_send(editor->lsp, message, pending);
} // }
lsp_send(editor->lsp, message, pending); //
} // // Move this into the box and merge the box and this guy
// void CompletionSession::handle(KeyEvent event) {
void handle_completion(Editor *editor, KeyEvent event) { // if (!editor->lsp || !editor->lsp->allow_completion)
if (!editor->lsp || !editor->lsp->allow_completion) // return;
return; // if (mode != INSERT) {
if (mode != INSERT) { // this->active = false;
editor->completion.active = false; // return;
return; // }
} // std::unique_lock lock(this->mtx);
std::unique_lock lock(editor->completion.mtx); // if (event.key_type == KEY_PASTE) {
if (event.key_type == KEY_PASTE) { // this->active = false;
editor->completion.active = false; // return;
return; // } else if (event.key_type == KEY_CHAR) {
} else if (event.key_type == KEY_CHAR) { // char ch = *event.c;
char ch = *event.c; // if (!this->active) {
if (!editor->completion.active) { // for (char c : editor->lsp->trigger_chars)
for (char c : editor->lsp->trigger_chars) // if (c == ch) {
if (c == ch) { // this->trigger = 2;
editor->completion.trigger = 2; // this->trigger_char = c;
editor->completion.trigger_char = c; // completion_request(editor);
completion_request(editor); // return;
return; // }
} // } else {
} else { // if (!editor->completion.items.empty()) {
if (!editor->completion.items.empty()) { // const auto &item =
const auto &item = editor->completion.items[editor->completion.select]; // editor->completion.items[editor->completion.select]; const
const std::vector<char> &end_chars = // std::vector<char> &end_chars =
item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars; // item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars;
for (char c : end_chars) // for (char c : end_chars)
if (c == ch) { // if (c == ch) {
complete_accept(editor); // this->accept();
return; // return;
} // }
} // }
} // }
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || // if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch == '_') { // (ch >= '0' && ch <= '9') || ch == '_') {
if (editor->completion.active) { // if (this->active) {
if (editor->completion.complete) // if (this->complete)
completion_filter(editor); // completion_filter(editor);
else // else
completion_request(editor); // completion_request(editor);
} else { // } else {
editor->completion.trigger = 3; // this->trigger = 3;
completion_request(editor); // completion_request(editor);
} // }
} else if (ch == CTRL('\\')) { // } else if (ch == CTRL('\\')) {
if (editor->completion.active && !editor->completion.visible.empty()) { // if (this->active && !this->visible.empty()) {
complete_accept(editor); // this->accept();
} else { // } else {
editor->completion.trigger = 1; // this->trigger = 1;
completion_request(editor); // completion_request(editor);
} // }
} else if (ch == CTRL('p')) { // } else if (ch == CTRL('p')) {
if (editor->completion.active) // if (this->active)
complete_next(editor); // this->next();
} else if (ch == CTRL('o')) { // } else if (ch == CTRL('o')) {
if (editor->completion.active) // if (this->active)
complete_prev(editor); // this->prev();
} else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) { // } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) {
if (editor->completion.active) { // if (this->active) {
if (editor->completion.complete) { // if (this->complete) {
if (editor->cursor <= editor->completion.hook) // if (editor->cursor <= this->hook)
editor->completion.active = false; // this->active = false;
else // else
completion_filter(editor); // completion_filter(editor);
} else { // } else {
if (editor->cursor <= editor->completion.hook) // if (editor->cursor <= this->hook)
editor->completion.active = false; // this->active = false;
else // else
completion_request(editor); // completion_request(editor);
} // }
} // }
} else { // } else {
editor->completion.active = false; // this->active = false;
} // }
} else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) { // } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) {
// Prolly remove mouse support here // // Prolly add mouse support here
// auto &box = editor->completion.box; // // auto &box = this->box;
// if (event.mouse_y >= box.position.row && // // if (event.mouse_y >= box.position.row &&
// event.mouse_x >= box.position.col) { // // event.mouse_x >= box.position.col) {
// uint32_t row = event.mouse_y - box.position.row; // // uint32_t row = event.mouse_y - box.position.row;
// uint32_t col = event.mouse_x - box.position.col; // // uint32_t col = event.mouse_x - box.position.col;
// if (row < box.size.row && col < box.size.col) { // // if (row < box.size.row && col < box.size.col) {
// uint8_t idx = 0; // // uint8_t idx = 0;
// /* TODO: fix index relative to scroll */ // // /* TODO: fix index relative to scroll */
// complete_select(editor, idx); // // complete_select(editor, idx);
// } // // }
// } // // }
// if it is being implemented then stop main event handler from processing // // if it is being implemented then stop main event handler from
// when click inside the box // processing
editor->completion.active = false; // // when click inside the box
} else { // this->active = false;
editor->completion.active = false; // } else {
} // this->active = false;
} // }
// }
void completion_resolve_doc(Editor *editor) { //
auto &item = editor->completion.items[editor->completion.select]; // void CompletionSession::resolve_doc() {
if (item.documentation) // auto &item = this->items[this->select];
return; // if (item.documentation)
item.documentation = ""; // return;
LSPPending *pending = new LSPPending(); // item.documentation = "";
pending->editor = editor; // LSPPending *pending = new LSPPending();
pending->callback = [](Editor *editor, const json &message) { // pending->editor = editor;
std::unique_lock lock(editor->completion.mtx); // pending->callback = [](Editor *editor, const json &message) {
auto &item = editor->completion.items[editor->completion.select]; // std::unique_lock lock(editor->completion.mtx);
if (message["result"].contains("documentation")) { // auto &item = editor->completion.items[editor->completion.select];
if (message["result"]["documentation"].is_string()) { // if (message["result"].contains("documentation")) {
item.documentation = // if (message["result"]["documentation"].is_string()) {
message["result"]["documentation"].get<std::string>(); // item.documentation =
} else if (message["result"]["documentation"].contains("value") && // message["result"]["documentation"].get<std::string>();
message["result"]["documentation"]["value"].is_string()) { // } else if (message["result"]["documentation"].contains("value") &&
item.is_markup = // message["result"]["documentation"]["value"].is_string()) {
message["result"]["documentation"]["kind"].get<std::string>() == // item.is_markup =
"markdown"; // message["result"]["documentation"]["kind"].get<std::string>() ==
std::string documentation = // "markdown";
message["result"]["documentation"]["value"].get<std::string>(); // std::string documentation =
if (documentation.size() > 1024) // message["result"]["documentation"]["value"].get<std::string>();
item.is_markup = false; // if (documentation.size() > 1024)
if (item.is_markup) { // item.is_markup = false;
item.documentation = // if (item.is_markup) {
substitute_fence(documentation, editor->lang.name); // item.documentation =
} else { // substitute_fence(documentation, editor->lang.name);
item.documentation = documentation; // } else {
} // item.documentation = documentation;
} // }
} // }
editor->completion.box.render_update(); // }
}; // editor->completion.box.render_update();
json message = {{"jsonrpc", "2.0"}, // };
{"method", "completionItem/resolve"}, // json message = {{"jsonrpc", "2.0"},
{"params", item.original}}; // {"method", "completionItem/resolve"},
lsp_send(editor->lsp, message, pending); // {"params", item.original}};
} // lsp_send(editor->lsp, message, pending);
// }
void complete_accept(Editor *editor) { //
if (!editor->completion.active || editor->completion.box.hidden) // void CompletionSession::accept() {
return; // if (!this->active || this->box.hidden)
auto &item = editor->completion.items[editor->completion.select]; // return;
// TODO: support snippets and asis here // auto &item = this->items[this->select];
// once indentation engine is implemented // // TODO: support snippets and asis here
if (editor->completion.version != editor->lsp_version) { // // once indentation engine is implemented
int delta_col = 0; // if (this->version != editor->lsp_version) {
TextEdit &e = item.edits[0]; // int delta_col = 0;
if (e.end.row == editor->cursor.row) { // TextEdit &e = item.edits[0];
delta_col = editor->cursor.col - e.end.col; // if (e.end.row == editor->cursor.row) {
e.end.col = editor->cursor.col; // delta_col = editor->cursor.col - e.end.col;
for (size_t i = 1; i < item.edits.size(); ++i) { // e.end.col = editor->cursor.col;
TextEdit &e = item.edits[i]; // for (size_t i = 1; i < item.edits.size(); ++i) {
if (e.start.row == editor->cursor.row) { // TextEdit &e = item.edits[i];
e.start.col += delta_col; // if (e.start.row == editor->cursor.row) {
if (e.end.row == editor->cursor.row) // e.start.col += delta_col;
e.end.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; // 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) // inline static int visible_index(const CompletionSession &s) {
if (s.visible[i] == s.select) // for (size_t i = 0; i < s.visible.size(); ++i)
return (int)i; // if (s.visible[i] == s.select)
return -1; // return (int)i;
} // return -1;
// }
void complete_next(Editor *editor) { //
auto &s = editor->completion; // void CompletionSession::next() {
if (!s.active || s.box.hidden || s.visible.empty()) // if (!this->active || this->box.hidden || this->visible.empty())
return; // return;
int vi = visible_index(s); // int vi = visible_index(*this);
if (vi < 0) // if (vi < 0)
vi = 0; // vi = 0;
else // else
vi = (vi + 1) % s.visible.size(); // vi = (vi + 1) % this->visible.size();
s.select = s.visible[vi]; // this->select = this->visible[vi];
completion_resolve_doc(editor); // this->resolve_doc();
completion_adjust_scroll(editor->completion); // completion_adjust_scroll(*this);
editor->completion.box.render_update(); // this->box.render_update();
} // }
//
void complete_prev(Editor *editor) { // void CompletionSession::prev() {
auto &s = editor->completion; // if (!this->active || this->box.hidden || this->visible.empty())
if (!s.active || s.box.hidden || s.visible.empty()) // return;
return; // int vi = visible_index(*this);
int vi = visible_index(s); // if (vi < 0)
if (vi < 0) // vi = 0;
vi = 0; // else
else // vi = (vi + this->visible.size() - 1) % this->visible.size();
vi = (vi + s.visible.size() - 1) % s.visible.size(); // this->select = this->visible[vi];
s.select = s.visible[vi]; // this->resolve_doc();
completion_resolve_doc(editor); // completion_adjust_scroll(*this);
completion_adjust_scroll(editor->completion); // this->box.render_update();
editor->completion.box.render_update(); // }
} //
// void CompletionSession::choose(uint8_t index) {
void complete_select(Editor *editor, uint8_t index) { // this->select = index;
editor->completion.select = index; // this->accept();
complete_accept(editor); // }
}

View File

@@ -1,14 +1,14 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "utils/utils.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; Coord result = cursor;
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return result; return result;
uint32_t row = result.row; uint32_t row = result.row;
uint32_t col = result.col; uint32_t col = result.col;
uint32_t line_len = 0; uint32_t line_len = 0;
LineIterator *it = begin_l_iter(editor->root, row); LineIterator *it = begin_l_iter(this->root, row);
if (!it) if (!it)
return result; return result;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
@@ -22,7 +22,7 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
while (number > 0) { while (number > 0) {
if (col >= line_len) { if (col >= line_len) {
uint32_t next_row = row + 1; uint32_t next_row = row + 1;
if (next_row >= editor->root->line_count) { if (next_row >= this->root->line_count) {
col = line_len; col = line_len;
break; break;
} }
@@ -49,14 +49,16 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
return result; return result;
} }
Coord move_left(Editor *editor, Coord cursor, uint32_t number) { Coord Editor::move_left(Coord cursor, uint32_t number) {
Coord result = cursor; Coord result = cursor;
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return result; return result;
uint32_t row = result.row; uint32_t row = result.row;
uint32_t col = result.col; uint32_t col = result.col;
uint32_t len = 0; 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); char *line = next_line(it, &len);
if (!line) { if (!line) {
free(it->buffer); free(it->buffer);
@@ -102,29 +104,28 @@ Coord move_left(Editor *editor, Coord cursor, uint32_t number) {
return result; return result;
} }
void cursor_down(Editor *editor, uint32_t number) { void Editor::cursor_down(uint32_t number) {
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return; return;
uint32_t visual_col = editor->cursor_preffered; uint32_t visual_col = this->cursor_preffered;
if (visual_col == UINT32_MAX) { if (visual_col == UINT32_MAX) {
uint32_t len; 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); char *line = next_line(it, &len);
if (!line) { if (!line) {
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
} }
editor->cursor_preffered = this->cursor_preffered =
get_visual_col_from_bytes(line, len, editor->cursor.col); get_visual_col_from_bytes(line, len, this->cursor.col);
visual_col = editor->cursor_preffered; visual_col = this->cursor_preffered;
free(it->buffer); free(it->buffer);
free(it); free(it);
} }
editor->cursor.row = this->cursor.row = MIN(this->cursor.row + number, this->root->line_count - 1);
MIN(editor->cursor.row + number, editor->root->line_count - 1);
uint32_t len; 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); char *line = next_line(it, &len);
if (!line) { if (!line) {
free(it->buffer); free(it->buffer);
@@ -133,26 +134,26 @@ void cursor_down(Editor *editor, uint32_t number) {
} }
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --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->buffer);
free(it); free(it);
} }
void cursor_up(Editor *editor, uint32_t number) { void Editor::cursor_up(uint32_t number) {
if (!editor || !editor->root || number == 0 || editor->cursor.row == 0) if (!this->root || number == 0 || this->cursor.row == 0)
return; return;
uint32_t len; 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); char *line_content = next_line(it, &len);
if (!line_content) if (!line_content)
return; return;
if (editor->cursor_preffered == UINT32_MAX) if (this->cursor_preffered == UINT32_MAX)
editor->cursor_preffered = this->cursor_preffered =
get_visual_col_from_bytes(line_content, len, editor->cursor.col); get_visual_col_from_bytes(line_content, len, this->cursor.col);
uint32_t visual_col = editor->cursor_preffered; uint32_t visual_col = this->cursor_preffered;
free(it->buffer); free(it->buffer);
free(it); free(it);
uint32_t target_row = editor->cursor.row; uint32_t target_row = this->cursor.row;
while (number > 0 && target_row > 0) { while (number > 0 && target_row > 0) {
target_row--; target_row--;
if (target_row == 0) { if (target_row == 0) {
@@ -161,32 +162,31 @@ void cursor_up(Editor *editor, uint32_t number) {
} }
number--; number--;
} }
it = begin_l_iter(editor->root, target_row); it = begin_l_iter(this->root, target_row);
line_content = next_line(it, &len); line_content = next_line(it, &len);
if (line_content) { if (line_content) {
if (len > 0 && line_content[len - 1] == '\n') if (len > 0 && line_content[len - 1] == '\n')
--len; --len;
editor->cursor.row = target_row; this->cursor.row = target_row;
editor->cursor.col = this->cursor.col = get_bytes_from_visual_col(line_content, len, visual_col);
get_bytes_from_visual_col(line_content, len, visual_col);
} else { } else {
editor->cursor.row = 0; this->cursor.row = 0;
editor->cursor.col = 0; this->cursor.col = 0;
} }
free(it->buffer); free(it->buffer);
free(it); free(it);
} }
void cursor_right(Editor *editor, uint32_t number) { void Editor::cursor_right(uint32_t number) {
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return; return;
editor->cursor = move_right(editor, editor->cursor, number); this->cursor = this->move_right(this->cursor, number);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
void cursor_left(Editor *editor, uint32_t number) { void Editor::cursor_left(uint32_t number) {
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return; return;
editor->cursor = move_left(editor, editor->cursor, number); this->cursor = this->move_left(this->cursor, number);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }

View File

@@ -2,20 +2,18 @@
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "utils/utils.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) if (len == 0)
return; return;
if (len < 0) { if (len < 0) {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original = uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
editor->cursor.col; uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; Coord point = this->move_left(pos, -len);
Coord point = move_left(editor, pos, -len);
json lsp_range; json lsp_range;
bool do_lsp = (editor->lsp != nullptr); bool do_lsp = (this->lsp != nullptr);
if (do_lsp) { if (do_lsp) {
LineIterator *it = begin_l_iter(editor->root, point.row); LineIterator *it = begin_l_iter(this->root, point.row);
uint32_t len; uint32_t len;
char *line = next_line(it, &len); char *line = next_line(it, &len);
int utf16_start = 0; 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); utf16_start = utf8_offset_to_utf16(line, len, point.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, pos.row); it = begin_l_iter(this->root, pos.row);
line = next_line(it, &len); line = next_line(it, &len);
int utf16_end = 0; int utf16_end = 0;
if (line) 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}}}, lsp_range = {{"start", {{"line", point.row}, {"character", utf16_start}}},
{"end", {{"line", pos.row}, {"character", utf16_end}}}}; {"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) { if (cursor_original > start && cursor_original <= byte_pos) {
editor->cursor = point; this->cursor = point;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} else if (cursor_original > byte_pos) { } else if (cursor_original > byte_pos) {
uint32_t cursor_new = cursor_original - (byte_pos - start); uint32_t cursor_new = cursor_original - (byte_pos - start);
uint32_t new_col; uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col}; this->cursor = {new_row, new_col};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
lock_1.unlock();
uint32_t start_row = point.row; uint32_t start_row = point.row;
uint32_t end_row = pos.row; uint32_t end_row = pos.row;
apply_hook_deletion(editor, start_row + 1, end_row); this->apply_hook_deletion(start_row + 1, end_row);
std::unique_lock lock_2(editor->knot_mtx); this->root = erase(this->root, start, byte_pos - start);
editor->root = erase(editor->root, start, byte_pos - start); if (this->parser)
lock_2.unlock(); this->parser->edit(start_row, end_row - start_row, 0);
if (editor->parser)
editor->parser->edit(start_row, end_row - start_row, 0);
if (do_lsp) { if (do_lsp) {
if (editor->lsp->incremental_sync) { auto lsp = this->lsp.load();
json message = { if (lsp->incremental_sync) {
{"jsonrpc", "2.0"}, auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}}; json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}}; {"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} }
} }
} else { } else {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original = uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
editor->cursor.col; uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; Coord point = this->move_right(pos, len);
Coord point = move_right(editor, pos, len);
json lsp_range; json lsp_range;
bool do_lsp = (editor->lsp != nullptr); bool do_lsp = (this->lsp != nullptr);
if (do_lsp) { 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; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
int utf16_start = 0; 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); utf16_start = utf8_offset_to_utf16(line, line_len, pos.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, point.row); it = begin_l_iter(this->root, point.row);
line = next_line(it, &line_len); line = next_line(it, &line_len);
int utf16_end = 0; int utf16_end = 0;
if (line) 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}}}, lsp_range = {{"start", {{"line", pos.row}, {"character", utf16_start}}},
{"end", {{"line", point.row}, {"character", utf16_end}}}}; {"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) { if (cursor_original > byte_pos && cursor_original <= end) {
editor->cursor = pos; this->cursor = pos;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} else if (cursor_original > end) { } else if (cursor_original > end) {
uint32_t cursor_new = cursor_original - (end - byte_pos); uint32_t cursor_new = cursor_original - (end - byte_pos);
uint32_t new_col; uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col}; this->cursor = {new_row, new_col};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
lock_1.unlock();
uint32_t start_row = pos.row; uint32_t start_row = pos.row;
uint32_t end_row = point.row; uint32_t end_row = point.row;
apply_hook_deletion(editor, start_row + 1, end_row); this->apply_hook_deletion(start_row + 1, end_row);
std::unique_lock lock_2(editor->knot_mtx); this->root = erase(this->root, byte_pos, end - byte_pos);
editor->root = erase(editor->root, byte_pos, end - byte_pos); if (this->parser)
lock_2.unlock(); this->parser->edit(start_row, end_row - start_row, 0);
if (editor->parser)
editor->parser->edit(start_row, end_row - start_row, 0);
if (do_lsp) { if (do_lsp) {
if (editor->lsp->incremental_sync) { auto lsp = this->lsp.load();
json message = { if (lsp->incremental_sync) {
{"jsonrpc", "2.0"}, auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}}; json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}}; {"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) { void Editor::edit_insert(Coord pos, char *data, uint32_t len) {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original = uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
editor->cursor.col; uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col;
if (cursor_original > byte_pos) { if (cursor_original > byte_pos) {
uint32_t cursor_new = cursor_original + len; uint32_t cursor_new = cursor_original + len;
uint32_t new_col; uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
editor->cursor = {new_row, 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; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
int utf16_col = 0; 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); utf16_col = utf8_offset_to_utf16(line, line_len, pos.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
lock_1.unlock(); this->root = insert(this->root, byte_pos, data, len);
std::unique_lock lock_2(editor->knot_mtx);
editor->root = insert(editor->root, byte_pos, data, len);
uint32_t rows = 0; uint32_t rows = 0;
for (uint32_t i = 0; i < len; i++) for (uint32_t i = 0; i < len; i++)
if (data[i] == '\n') if (data[i] == '\n')
rows++; rows++;
apply_hook_insertion(editor, pos.row, rows); this->apply_hook_insertion(pos.row, rows);
lock_2.unlock(); if (this->parser)
if (editor->parser) this->parser->edit(pos.row, 0, rows);
editor->parser->edit(pos.row, 0, rows); auto lsp = this->lsp.load();
if (editor->lsp) { if (lsp) {
if (editor->lsp->incremental_sync) { if (lsp->incremental_sync) {
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array( json::array(
{{{"range", {{{"range",
{{"start", {{"line", pos.row}, {"character", utf16_col}}}, {{"start", {{"line", pos.row}, {"character", utf16_col}}},
{"end", {{"line", pos.row}, {"character", utf16_col}}}}}, {"end", {{"line", pos.row}, {"character", utf16_col}}}}},
{"text", std::string(data, len)}}})}}}}; {"text", std::string(data, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}}; {"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, void Editor::edit_replace(Coord start, Coord end, const char *text,
uint32_t len) { uint32_t len) {
std::unique_lock lock(editor->knot_mtx);
uint32_t start_byte = uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col; line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col; uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
LineIterator *it = begin_l_iter(editor->root, start.row); LineIterator *it = begin_l_iter(this->root, start.row);
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
int utf16_start = 0; 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); utf16_start = utf8_offset_to_utf16(line, line_len, start.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, end.row); it = begin_l_iter(this->root, end.row);
line = next_line(it, &line_len); line = next_line(it, &line_len);
int utf16_end = 0; int utf16_end = 0;
if (line) if (line)
@@ -238,25 +225,24 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
free(it->buffer); free(it->buffer);
free(it); free(it);
if (start_byte != end_byte) 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) 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; uint32_t rows = 0;
for (uint32_t i = 0; i < len; i++) for (uint32_t i = 0; i < len; i++)
if (text[i] == '\n') if (text[i] == '\n')
rows++; rows++;
if (rows > 0) if (this->parser)
rows--; this->parser->edit(start.row, end.row - start.row, rows);
if (editor->parser) auto lsp = this->lsp.load();
editor->parser->edit(start.row, end.row - start.row, rows); if (lsp) {
if (editor->lsp) { if (lsp->incremental_sync) {
if (editor->lsp->incremental_sync) { auto message = std::make_unique<LSPMessage>();
json message = { message->message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array( json::array(
{{{"range", {{{"range",
@@ -264,19 +250,19 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
{{"line", start.row}, {"character", utf16_start}}}, {{"line", start.row}, {"character", utf16_start}}},
{"end", {{"line", end.row}, {"character", utf16_end}}}}}, {"end", {{"line", end.row}, {"character", utf16_end}}}}},
{"text", std::string(text, len)}}})}}}}; {"text", std::string(text, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string full_text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", full_text}}})}}}}; {"contentChanges", json::array({{{"text", full_text}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} }
} }
} }

View File

@@ -5,69 +5,66 @@
#include "syntax/langs.h" #include "syntax/langs.h"
#include "utils/utils.h" #include "utils/utils.h"
Editor *new_editor(const char *filename_arg, Coord position, Coord size, Editor::Editor(const char *filename_arg, uint8_t eol) {
uint8_t eol) {
Editor *editor = new Editor();
if (!editor)
return nullptr;
uint32_t len = 0; uint32_t len = 0;
std::string filename = path_abs(filename_arg); std::string filename = path_abs(filename_arg);
editor->unix_eol = eol & 1; this->unix_eol = eol & 1;
char *str = load_file(filename.c_str(), &len, &editor->unix_eol); char *str = load_file(filename.c_str(), &len, &this->unix_eol);
if (!str) { if (!str) {
str = (char *)malloc(1); str = (char *)malloc(1);
*str = '\n'; *str = '\n';
len = 1; len = 1;
} }
if ((eol >> 1) & 1) if ((eol >> 1) & 1)
editor->unix_eol = eol & 1; this->unix_eol = eol & 1;
editor->filename = filename; this->filename = filename;
editor->uri = path_to_file_uri(filename); this->uri = path_to_file_uri(filename);
editor->position = position; this->cursor_preffered = UINT32_MAX;
editor->size = size;
editor->cursor_preffered = UINT32_MAX;
if (len == 0) { if (len == 0) {
free(str); free(str);
str = (char *)malloc(1); str = (char *)malloc(1);
*str = '\n'; *str = '\n';
len = 1; 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); free(str);
editor->lang = language_for_file(filename.c_str()); this->lang = language_for_file(filename.c_str());
if (parsers.find(editor->lang.name) != parsers.end()) if (parsers.find(this->lang.name) != parsers.end())
editor->parser = new Parser(editor, editor->lang.name, size.row + 5); this->parser = new Parser(this, this->lang.name, size.row + 5);
if (editor->lang.name == "css" || editor->lang.name == "html" || if (this->lang.name == "css" || this->lang.name == "html" ||
editor->lang.name == "javascript" || editor->lang.name == "markdown" || this->lang.name == "javascript" || this->lang.name == "markdown" ||
editor->lang.name == "typescript") this->lang.name == "typescript")
editor->is_css_color = true; this->is_css_color = true;
if (len <= (1024 * 28)) if (len <= (1024 * 28)) {
request_add_to_lsp(editor->lang, editor); std::lock_guard lock(lsp::lsp_mutex);
editor->indents.compute_indent(editor); lsp::new_editors.push_back(this);
return editor; }
this->indents.compute_indent(this);
} }
void free_editor(Editor *editor) { Editor::~Editor() {
remove_from_lsp(editor); auto lsp = this->lsp.load();
if (editor->parser) if (lsp)
delete editor->parser; lsp->remove(this);
editor->parser = nullptr; if (this->parser)
free_rope(editor->root); delete this->parser;
delete editor; this->parser = nullptr;
free_rope(this->root);
} }
void save_file(Editor *editor) { void Editor::save() {
if (!editor || !editor->root) if (!this->root)
return; return;
std::shared_lock lock(editor->knot_mtx); int version = this->lsp_version;
int version = editor->lsp_version; uint32_t char_count = this->root->char_count;
uint32_t char_count = editor->root->char_count; char *str = read(this->root, 0, char_count);
char *str = read(editor->root, 0, char_count);
if (!str) if (!str)
return; return;
lock.unlock(); std::ofstream out(this->filename);
std::ofstream out(editor->filename); if (!this->unix_eol) {
if (!editor->unix_eol) {
for (uint32_t i = 0; i < char_count; ++i) { for (uint32_t i = 0; i < char_count; ++i) {
if (str[i] == '\n') if (str[i] == '\n')
out.put('\r'); out.put('\r');
@@ -78,30 +75,33 @@ void save_file(Editor *editor) {
} }
out.close(); out.close();
free(str); free(str);
bar.log("Written " + std::to_string(char_count) + " bytes to " + ui::bar.log("Written " + std::to_string(char_count) + " bytes to " +
editor->filename); this->filename);
if (editor->lsp) { auto lsp = this->lsp.load();
json save_msg = {{"jsonrpc", "2.0"}, if (lsp) {
{"method", "textDocument/didSave"}, log("Saving %s", this->filename.c_str());
{"params", {{"textDocument", {{"uri", editor->uri}}}}}}; auto message = std::make_unique<LSPMessage>();
lsp_send(editor->lsp, save_msg, nullptr); message->message = {{"method", "textDocument/didSave"},
if (editor->lsp->allow_formatting) { {"params", {{"textDocument", {{"uri", this->uri}}}}}};
json msg = {{"jsonrpc", "2.0"}, lsp->send(std::move(message));
{"method", "textDocument/formatting"}, if (lsp->allow_formatting) {
{"params", log("Formatting %s", this->filename.c_str());
{{"textDocument", {{"uri", editor->uri}}}, json s_msg = {{"method", "textDocument/formatting"},
{"options", {"params",
{{"tabSize", 2}, {{"textDocument", {{"uri", this->uri}}},
{"insertSpaces", true}, {"options",
{"trimTrailingWhitespace", true}, {{"tabSize", 2},
{"trimFinalNewlines", true}}}}}}; {"insertSpaces", true},
LSPPending *pending = new LSPPending(); {"trimTrailingWhitespace", true},
pending->editor = editor; {"trimFinalNewlines", true}}}}}};
pending->callback = [save_msg, version](Editor *editor, auto save_msg = std::make_unique<LSPMessage>();
const json &message) { save_msg->editor = this;
if (version != editor->lsp_version) save_msg->message = s_msg;
save_msg->callback = [s_msg, version](const LSPMessage &msg) {
log("Formattin");
if (version != msg.editor->lsp_version)
return; return;
auto &edits = message["result"]; auto &edits = msg.message["result"];
if (edits.is_array()) { if (edits.is_array()) {
std::vector<TextEdit> t_edits; std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size()); t_edits.reserve(edits.size());
@@ -112,19 +112,17 @@ void save_file(Editor *editor) {
t_edit.start.col = edit["range"]["start"]["character"]; t_edit.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"]; 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); t_edits.push_back(t_edit);
} }
apply_lsp_edits(editor, t_edits, false); msg.editor->apply_lsp_edits(t_edits, false);
ensure_scroll(editor); msg.editor->ensure_cursor();
std::shared_lock lock(editor->knot_mtx); uint32_t char_count = msg.editor->root->char_count;
uint32_t char_count = editor->root->char_count; char *str = read(msg.editor->root, 0, char_count);
char *str = read(editor->root, 0, char_count);
if (!str) if (!str)
return; return;
lock.unlock(); std::ofstream out(msg.editor->filename);
std::ofstream out(editor->filename); if (!msg.editor->unix_eol) {
if (!editor->unix_eol) {
for (uint32_t i = 0; i < char_count; ++i) { for (uint32_t i = 0; i < char_count; ++i) {
if (str[i] == '\n') if (str[i] == '\n')
out.put('\r'); out.put('\r');
@@ -135,10 +133,14 @@ void save_file(Editor *editor) {
} }
out.close(); out.close();
free(str); free(str);
lsp_send(editor->lsp, save_msg, nullptr); auto save_msg = std::make_unique<LSPMessage>();
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));
} }
} }
} }

View File

@@ -1,60 +1,59 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "editor/helpers.h" #include "extentions/hover.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "main.h" #include "main.h"
#include "utils/utils.h" #include "utils/utils.h"
void handle_editor_event(Editor *editor, KeyEvent event) { void Editor::handle_event(KeyEvent event) {
uint8_t old_mode = mode; uint8_t old_mode = mode;
if (editor->hover_active) if (!this->hover_popup->hidden)
editor->hover_active = false; this->hover_popup->hidden = true;
handle_mouse(editor, event);
if (event.key_type == KEY_SPECIAL) { if (event.key_type == KEY_SPECIAL) {
switch (event.special_modifier) { switch (event.special_modifier) {
case 0: case 0:
switch (event.special_key) { switch (event.special_key) {
case KEY_DOWN: case KEY_DOWN:
cursor_down(editor, 1); this->cursor_down(1);
break; break;
case KEY_UP: case KEY_UP:
cursor_up(editor, 1); this->cursor_up(1);
break; break;
case KEY_LEFT: case KEY_LEFT:
cursor_left(editor, 1); this->cursor_left(1);
break; break;
case KEY_RIGHT: case KEY_RIGHT:
cursor_right(editor, 1); this->cursor_right(1);
break; break;
} }
break; break;
case CNTRL: case CNTRL:
switch (event.special_key) { switch (event.special_key) {
case KEY_DOWN: case KEY_DOWN:
cursor_down(editor, 5); this->cursor_down(5);
break; break;
case KEY_UP: case KEY_UP:
cursor_up(editor, 5); this->cursor_up(5);
break; break;
case KEY_LEFT: case KEY_LEFT:
cursor_prev_word(editor); this->cursor_prev_word();
case KEY_RIGHT: case KEY_RIGHT:
cursor_next_word(editor); this->cursor_next_word();
break; break;
} }
break; break;
case ALT: case ALT:
switch (event.special_key) { switch (event.special_key) {
case KEY_DOWN: case KEY_DOWN:
move_line_down(editor); this->move_line_down();
break; break;
case KEY_UP: case KEY_UP:
move_line_up(editor); this->move_line_up();
break; break;
case KEY_LEFT: case KEY_LEFT:
cursor_left(editor, 8); this->cursor_left(8);
break; break;
case KEY_RIGHT: case KEY_RIGHT:
cursor_right(editor, 8); this->cursor_right(8);
break; break;
} }
break; break;
@@ -65,86 +64,86 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
if (event.key_type == KEY_CHAR && event.len == 1) { if (event.key_type == KEY_CHAR && event.len == 1) {
switch (event.c[0]) { switch (event.c[0]) {
case 'u': case 'u':
select_all(editor); this->select_all();
break; break;
case CTRL('h'): case CTRL('h'):
editor->hover.scroll(-1); this->hover_popup->scroll(-1);
editor->hover_active = true; this->hover_popup->hidden = false;
break; break;
case CTRL('l'): case CTRL('l'):
editor->hover.scroll(1); this->hover_popup->scroll(1);
editor->hover_active = true; this->hover_popup->hidden = false;
break; break;
case 'h': case 'h':
fetch_lsp_hover(editor); this->fetch_lsp_hover();
break; break;
case 'a': { case 'a': {
mode = INSERT; mode = INSERT;
Coord start = editor->cursor; Coord start = this->cursor;
cursor_right(editor, 1); this->cursor_right(1);
if (start.row != editor->cursor.row) if (start.row != this->cursor.row)
cursor_left(editor, 1); this->cursor_left(1);
} break; } break;
case 'i': case 'i':
mode = INSERT; mode = INSERT;
break; break;
case 'n': case 'n':
mode = JUMPER; mode = JUMPER;
editor->jumper_set = true; this->jumper_set = true;
break; break;
case 'm': case 'm':
mode = JUMPER; mode = JUMPER;
editor->jumper_set = false; this->jumper_set = false;
break; break;
case 'N': case 'N':
clear_hooks_at_line(editor, editor->cursor.row); this->clear_hooks_at_line(this->cursor.row);
break; break;
case 's': case 's':
case 'v': case 'v':
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
editor->selection = editor->cursor; this->selection = this->cursor;
editor->selection_type = CHAR; this->selection_type = CHAR;
break; break;
case ';': case ';':
case ':': case ':':
mode = RUNNER; mode = RUNNER;
break; break;
case 0x7F: case 0x7F:
cursor_left(editor, 1); this->cursor_left(1);
break; break;
case ' ': case ' ':
cursor_right(editor, 1); this->cursor_right(1);
break; break;
case '\r': case '\r':
case '\n': case '\n':
cursor_down(editor, 1); this->cursor_down(1);
break; break;
case '\\': case '\\':
case '|': case '|':
cursor_up(editor, 1); this->cursor_up(1);
break; break;
case CTRL('d'): case CTRL('d'):
scroll_down(editor, 1); this->scroll_down(1);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case CTRL('u'): case CTRL('u'):
scroll_up(editor, 1); this->scroll_up(1);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case '>': case '>':
case '.': case '.':
indent_current_line(editor); this->indent_current_line();
break; break;
case '<': case '<':
case ',': case ',':
dedent_current_line(editor); this->dedent_current_line();
break; break;
case CTRL('s'): case CTRL('s'):
save_file(editor); this->save();
break; break;
case 'p': case 'p':
paste(editor); this->paste();
break; break;
} }
} }
@@ -153,34 +152,34 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
if (event.key_type == KEY_CHAR) { if (event.key_type == KEY_CHAR) {
if (event.len == 1) { if (event.len == 1) {
if (event.c[0] == '\t') { 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') { } 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')) { } else if (event.c[0] == CTRL('W')) {
delete_prev_word(editor); this->delete_prev_word();
} else if (isprint((unsigned char)(event.c[0]))) { } 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) { } else if (event.c[0] == 0x7F || event.c[0] == 0x08) {
backspace_edit(editor); this->backspace_edit();
} else if (event.c[0] == 0x1B) { } else if (event.c[0] == 0x1B) {
normal_mode(editor); this->normal_mode();
} }
} else if (event.len > 1) { } else if (event.len > 1) {
edit_insert(editor, editor->cursor, event.c, event.len); this->edit_insert(this->cursor, event.c, event.len);
cursor_right(editor, 1); this->cursor_right(1);
} }
} else if (event.key_type == KEY_SPECIAL && } else if (event.key_type == KEY_SPECIAL &&
event.special_key == KEY_DELETE) { event.special_key == KEY_DELETE) {
switch (event.special_modifier) { switch (event.special_modifier) {
case 0: case 0:
edit_erase(editor, editor->cursor, 1); this->edit_erase(this->cursor, 1);
break; break;
case CNTRL: case CNTRL:
delete_next_word(editor); this->delete_next_word();
break; break;
} }
} else if (event.key_type == KEY_PASTE) { } else if (event.key_type == KEY_PASTE) {
insert_str(editor, event.c, event.len); this->insert_str(event.c, event.len);
} }
break; break;
case SELECT: case SELECT:
@@ -189,28 +188,28 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case 0x1B: case 0x1B:
case 's': case 's':
case 'v': case 'v':
editor->selection_active = false; this->selection_active = false;
mode = NORMAL; mode = NORMAL;
break; break;
case 'y': case 'y':
copy(editor); this->copy();
mode = NORMAL; mode = NORMAL;
break; break;
case 'x': case 'x':
cut(editor); this->cut();
mode = NORMAL; mode = NORMAL;
break; break;
case 'p': case 'p':
paste(editor); this->paste();
mode = NORMAL; mode = NORMAL;
break; break;
case '<': case '<':
case ',': case ',':
dedent_selection(editor); this->dedent_selection();
break; break;
case '>': case '>':
case '.': case '.':
indent_selection(editor); this->indent_selection();
break; break;
} }
} }
@@ -218,25 +217,25 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case JUMPER: case JUMPER:
if (event.key_type == KEY_CHAR && event.len == 1 && if (event.key_type == KEY_CHAR && event.len == 1 &&
(event.c[0] >= '!' && event.c[0] <= '~')) { (event.c[0] >= '!' && event.c[0] <= '~')) {
if (editor->jumper_set) { if (this->jumper_set) {
for (uint8_t i = 0; i < 94; i++) for (uint8_t i = 0; i < 94; i++)
if (editor->hooks[i] == editor->cursor.row + 1) { if (this->hooks[i] == this->cursor.row + 1) {
editor->hooks[i] = 0; this->hooks[i] = 0;
break; break;
} }
editor->hooks[event.c[0] - '!'] = editor->cursor.row + 1; this->hooks[event.c[0] - '!'] = this->cursor.row + 1;
} else { } else {
uint32_t line = editor->hooks[event.c[0] - '!'] - 1; uint32_t line = this->hooks[event.c[0] - '!'] - 1;
if (line > 0) { if (line > 0) {
editor->cursor = {line, 0}; this->cursor = {line, 0};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
} }
} }
mode = NORMAL; mode = NORMAL;
break; break;
} }
if (old_mode == mode || mode != INSERT) // if (old_mode == mode || mode != INSERT)
handle_completion(editor, event); // this->completion.handle(event);
ensure_scroll(editor); this->ensure_scroll();
} }

View File

@@ -1,86 +1,83 @@
#include "editor/helpers.h" #include "editor/helpers.h"
#include "editor/editor.h" #include "editor/editor.h"
#include "extentions/hover.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <sys/types.h>
void cut(Editor *editor) { void Editor::cut() {
if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
1500) { ui::bar.log("Selection too large!");
bar.log("Selection too large!");
return; return;
} }
if (mode != SELECT) if (mode != SELECT)
return; return;
Coord start; Coord start;
uint32_t len; uint32_t len;
char *text = get_selection(editor, &len, &start); char *text = this->get_selection(&len, &start);
ruby_copy(text, len); ruby_copy(text, len);
len = count_clusters(text, len, 0, len); len = count_clusters(text, len, 0, len);
edit_erase(editor, start, len); this->edit_erase(start, len);
free(text); free(text);
editor->selection_active = false; this->selection_active = false;
} }
void copy(Editor *editor) { void Editor::copy() {
if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
1500) { ui::bar.log("Selection too large!");
bar.log("Selection too large!");
return; return;
} }
if (mode != SELECT) if (mode != SELECT)
return; return;
uint32_t len; uint32_t len;
char *text = get_selection(editor, &len, nullptr); char *text = this->get_selection(&len, nullptr);
ruby_copy(text, len); ruby_copy(text, len);
free(text); free(text);
editor->selection_active = false; this->selection_active = false;
} }
void paste(Editor *editor) { void Editor::paste() {
if (mode == NORMAL) { if (mode == NORMAL) {
std::string text = ruby_paste(); std::string text = ruby_paste();
if (text.empty()) if (text.empty())
return; return;
insert_str(editor, (char *)text.c_str(), text.length()); this->insert_str((char *)text.c_str(), text.length());
} else if (mode == SELECT) { } else if (mode == SELECT) {
std::string text = ruby_paste(); std::string text = ruby_paste();
if (!text.empty()) { if (!text.empty()) {
Coord start, end; Coord start, end;
selection_bounds(editor, &start, &end); this->selection_bounds(&start, &end);
uint32_t start_byte = uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col; line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
line_to_byte(editor->root, end.row, nullptr) + end.col; this->edit_erase(start, end_byte - start_byte);
edit_erase(editor, start, end_byte - start_byte); this->edit_insert(this->cursor, (char *)text.c_str(), text.length());
edit_insert(editor, editor->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) { 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); 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) { void Editor::indent_current_line() {
Coord start = editor->cursor; Coord start = this->cursor;
uint32_t delta = editor->indents.indent_line(editor->cursor.row); uint32_t delta = this->indents.indent_line(this->cursor.row);
editor->cursor.col = start.col + delta; this->cursor.col = start.col + delta;
editor->cursor.row = start.row; this->cursor.row = start.row;
} }
void dedent_current_line(Editor *editor) { void Editor::dedent_current_line() {
Coord start = editor->cursor; Coord start = this->cursor;
uint32_t delta = editor->indents.dedent_line(editor->cursor.row); uint32_t delta = this->indents.dedent_line(this->cursor.row);
editor->cursor.col = MAX((int64_t)start.col - delta, 0); this->cursor.col = MAX((int64_t)start.col - delta, 0);
editor->cursor.row = start.row; this->cursor.row = start.row;
} }
static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) { 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) { void Editor::indent_selection() {
uint32_t top = MIN(editor->cursor.row, editor->selection.row); uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(editor->cursor.row, editor->selection.row); uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) { 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; return;
} }
if (bot - top >= 2) if (bot - top >= 2)
editor->indents.indent_block(top + 1, bot - 1); this->indents.indent_block(top + 1, bot - 1);
uint32_t delta_top = editor->indents.indent_line(top); uint32_t delta_top = this->indents.indent_line(top);
uint32_t delta_bot = uint32_t delta_bot =
(bot == top) ? delta_top : editor->indents.indent_line(bot); (bot == top) ? delta_top : this->indents.indent_line(bot);
move_coord_by_delta(editor->cursor, top, delta_top); move_coord_by_delta(this->cursor, top, delta_top);
move_coord_by_delta(editor->selection, top, delta_top); move_coord_by_delta(this->selection, top, delta_top);
if (bot != top) { if (bot != top) {
move_coord_by_delta(editor->cursor, bot, delta_bot); move_coord_by_delta(this->cursor, bot, delta_bot);
move_coord_by_delta(editor->selection, bot, delta_bot); move_coord_by_delta(this->selection, bot, delta_bot);
} }
} }
void dedent_selection(Editor *editor) { void Editor::dedent_selection() {
uint32_t top = MIN(editor->cursor.row, editor->selection.row); uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(editor->cursor.row, editor->selection.row); uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) { 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; return;
} }
if (bot - top >= 2) if (bot - top >= 2)
editor->indents.dedent_block(top + 1, bot - 1); this->indents.dedent_block(top + 1, bot - 1);
uint32_t delta_top = editor->indents.dedent_line(top); uint32_t delta_top = this->indents.dedent_line(top);
uint32_t delta_bot = uint32_t delta_bot =
(bot == top) ? delta_top : editor->indents.dedent_line(bot); (bot == top) ? delta_top : this->indents.dedent_line(bot);
move_coord_by_delta(editor->cursor, top, -(int64_t)delta_top); move_coord_by_delta(this->cursor, top, -(int64_t)delta_top);
move_coord_by_delta(editor->selection, top, -(int64_t)delta_top); move_coord_by_delta(this->selection, top, -(int64_t)delta_top);
if (bot != top) { if (bot != top) {
move_coord_by_delta(editor->cursor, bot, -(int64_t)delta_bot); move_coord_by_delta(this->cursor, bot, -(int64_t)delta_bot);
move_coord_by_delta(editor->selection, bot, -(int64_t)delta_bot); move_coord_by_delta(this->selection, bot, -(int64_t)delta_bot);
} }
} }
void insert_char(Editor *editor, char c) { void Editor::insert_char(char c) {
uint32_t col = editor->cursor.col; uint32_t col = this->cursor.col;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -148,7 +145,7 @@ void insert_char(Editor *editor, char c) {
if ((c == '}' && next == '}') || (c == ')' && next == ')') || if ((c == '}' && next == '}') || (c == ')' && next == ')') ||
(c == ']' && next == ']') || (c == '"' && next == '"') || (c == ']' && next == ']') || (c == '"' && next == '"') ||
(c == '\'' && next == '\'')) { (c == '\'' && next == '\'')) {
cursor_right(editor, 1); this->cursor_right(1);
skip_insert = true; skip_insert = true;
} }
} }
@@ -175,16 +172,17 @@ void insert_char(Editor *editor, char c) {
} }
if (closing) { if (closing) {
char pair[2] = {c, closing}; char pair[2] = {c, closing};
edit_insert(editor, editor->cursor, pair, 2); this->edit_insert(this->cursor, pair, 2);
cursor_right(editor, 1); this->cursor_right(1);
} else { } else {
edit_insert(editor, editor->cursor, &c, 1); this->edit_insert(this->cursor, &c, 1);
cursor_right(editor, 1); this->cursor_right(1);
} }
if (editor->lsp && editor->lsp->allow_formatting_on_type) { auto lsp = this->lsp.load();
for (char ch : editor->lsp->format_chars) { if (lsp && lsp->allow_formatting_on_type) {
for (char ch : lsp->format_chars) {
if (ch == c) { 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) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -194,29 +192,27 @@ void insert_char(Editor *editor, char c) {
free(it); free(it);
return; 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->buffer);
free(it); free(it);
int version = editor->lsp_version; int version = this->lsp_version;
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/onTypeFormatting"}, {"method", "textDocument/onTypeFormatting"},
{"params", {"params",
{{"textDocument", {{"uri", editor->uri}}}, {{"textDocument", {{"uri", this->uri}}},
{"position", {"position", {{"line", this->cursor.row}, {"character", col}}},
{{"line", editor->cursor.row}, {"character", col}}},
{"ch", std::string(1, c)}, {"ch", std::string(1, c)},
{"options", {"options",
{{"tabSize", 2}, {{"tabSize", 2},
{"insertSpaces", true}, {"insertSpaces", true},
{"trimTrailingWhitespace", true}, {"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}}; {"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending(); message->editor = this;
pending->editor = editor; message->callback = [version](const LSPMessage &message) {
pending->callback = [version](Editor *editor, const json &message) { if (version != message.editor->lsp_version)
if (version != editor->lsp_version)
return; return;
auto &edits = message["result"]; auto &edits = message.message["result"];
if (edits.is_array()) { if (edits.is_array()) {
std::vector<TextEdit> t_edits; std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size()); 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.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"]; 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); t_edits.push_back(t_edit);
} }
apply_lsp_edits(editor, t_edits, false); message.editor->apply_lsp_edits(t_edits, false);
ensure_scroll(editor); message.editor->ensure_scroll();
} }
}; };
lsp_send(editor->lsp, message, pending); lsp->send(std::move(message));
break; break;
} }
} }
@@ -242,19 +238,19 @@ void insert_char(Editor *editor, char c) {
} }
} }
void normal_mode(Editor *editor) { void Editor::normal_mode() {
Coord prev_pos = editor->cursor; Coord prev_pos = this->cursor;
mode = NORMAL; mode = NORMAL;
cursor_left(editor, 1); this->cursor_left(1);
if (prev_pos.row != editor->cursor.row) if (prev_pos.row != this->cursor.row)
cursor_right(editor, 1); this->cursor_right(1);
} }
void backspace_edit(Editor *editor) { void Editor::backspace_edit() {
Coord prev_pos = editor->cursor; Coord prev_pos = this->cursor;
if (prev_pos.col > 0) if (prev_pos.col > 0)
prev_pos.col--; 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) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -267,11 +263,11 @@ void backspace_edit(Editor *editor) {
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
char prev_char = (prev_pos.col < len) ? line[prev_pos.col] : 0; 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; bool before_content = false;
if (editor->cursor.col > 0) { if (this->cursor.col > 0) {
before_content = true; 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') { if (line[i] != ' ' && line[i] != '\t') {
before_content = false; before_content = false;
break; break;
@@ -280,7 +276,7 @@ void backspace_edit(Editor *editor) {
free(it->buffer); free(it->buffer);
free(it); free(it);
if (before_content) { if (before_content) {
dedent_current_line(editor); this->dedent_current_line();
return; return;
} }
bool is_pair = (prev_char == '{' && next_char == '}') || bool is_pair = (prev_char == '{' && next_char == '}') ||
@@ -289,65 +285,65 @@ void backspace_edit(Editor *editor) {
(prev_char == '"' && next_char == '"') || (prev_char == '"' && next_char == '"') ||
(prev_char == '\'' && next_char == '\''); (prev_char == '\'' && next_char == '\'');
if (is_pair) { if (is_pair) {
edit_erase(editor, editor->cursor, 1); this->edit_erase(this->cursor, 1);
edit_erase(editor, prev_pos, 1); this->edit_erase(prev_pos, 1);
} else { } 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; uint32_t prev_col_byte, prev_col_cluster;
word_boundaries(editor, editor->cursor, &prev_col_byte, nullptr, this->word_boundaries(this->cursor, &prev_col_byte, nullptr,
&prev_col_cluster, nullptr); &prev_col_cluster, nullptr);
if (prev_col_byte == editor->cursor.col) if (prev_col_byte == this->cursor.col)
edit_erase(editor, editor->cursor, -1); this->edit_erase(this->cursor, -1);
else 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; uint32_t next_col_byte, next_col_cluster;
word_boundaries(editor, editor->cursor, nullptr, &next_col_byte, nullptr, this->word_boundaries(this->cursor, nullptr, &next_col_byte, nullptr,
&next_col_cluster); &next_col_cluster);
if (next_col_byte == editor->cursor.col) if (next_col_byte == this->cursor.col)
edit_erase(editor, editor->cursor, 1); this->edit_erase(this->cursor, 1);
else 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++) for (uint8_t i = 0; i < 94; i++)
if (editor->hooks[i] == line + 1) { if (this->hooks[i] == line + 1) {
editor->hooks[i] = 0; this->hooks[i] = 0;
break; break;
} }
} }
void cursor_prev_word(Editor *editor) { void Editor::cursor_prev_word() {
uint32_t prev_col; uint32_t prev_col;
word_boundaries(editor, editor->cursor, &prev_col, nullptr, nullptr, nullptr); word_boundaries(this->cursor, &prev_col, nullptr, nullptr, nullptr);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
if (prev_col == editor->cursor.col) if (prev_col == this->cursor.col)
cursor_left(editor, 1); cursor_left(1);
else 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; uint32_t next_col;
word_boundaries(editor, editor->cursor, nullptr, &next_col, nullptr, nullptr); word_boundaries(this->cursor, nullptr, &next_col, nullptr, nullptr);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
if (next_col == editor->cursor.col) if (next_col == this->cursor.col)
cursor_right(editor, 1); this->cursor_right(1);
else else
editor->cursor = {editor->cursor.row, next_col}; this->cursor = {this->cursor.row, next_col};
} }
void select_all(Editor *editor) { void Editor::select_all() {
if (editor->root->line_count > 0) { if (this->root->line_count > 0) {
editor->cursor.row = editor->root->line_count - 1; this->cursor.row = this->root->line_count - 1;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t line_len; uint32_t line_len;
@@ -359,18 +355,19 @@ void select_all(Editor *editor) {
line_len = count_clusters(line, line_len, 0, line_len); line_len = count_clusters(line, line_len, 0, line_len);
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor.col = line_len; this->cursor.col = line_len;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
editor->selection = {0, 0}; this->selection = {0, 0};
editor->selection_type = LINE; this->selection_type = LINE;
} }
} }
void fetch_lsp_hover(Editor *editor) { void Editor::fetch_lsp_hover() {
if (editor->lsp && editor->lsp->allow_hover) { auto lsp = this->lsp.load();
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); if (lsp && lsp->allow_hover) {
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (!line) { if (!line) {
@@ -378,18 +375,18 @@ void fetch_lsp_hover(Editor *editor) {
free(it); free(it);
return; 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->buffer);
free(it); free(it);
json hover_request = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/hover"}, {"method", "textDocument/hover"},
{"params", {"params",
{{"textDocument", {{"uri", editor->uri}}}, {{"textDocument", {{"uri", this->uri}}},
{"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; {"position", {{"line", this->cursor.row}, {"character", col}}}}}};
LSPPending *pending = new LSPPending(); message->editor = this;
pending->editor = editor; message->callback = [](const LSPMessage &message) {
pending->callback = [](Editor *editor, const json &hover) { auto &hover = message.message;
if (hover.contains("result") && !hover["result"].is_null()) { if (hover.contains("result") && !hover["result"].is_null()) {
auto &contents = hover["result"]["contents"]; auto &contents = hover["result"]["contents"];
std::string hover_text = ""; std::string hover_text = "";
@@ -413,23 +410,26 @@ void fetch_lsp_hover(Editor *editor) {
hover_text += contents.get<std::string>(); hover_text += contents.get<std::string>();
} }
if (!hover_text.empty()) { if (!hover_text.empty()) {
editor->hover.clear(); message.editor->hover_popup->clear();
editor->hover.text = clean_text(hover_text); message.editor->hover_popup->text = clean_text(hover_text);
editor->hover.is_markup = is_markup; message.editor->hover_popup->is_markup = is_markup;
editor->hover.render_first(); message.editor->hover_popup->hidden = false;
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) {
layout::focused_window = this;
this->size = size;
static std::chrono::steady_clock::time_point last_click_time = static std::chrono::steady_clock::time_point last_click_time =
std::chrono::steady_clock::now(); std::chrono::steady_clock::now();
static uint32_t click_count = 0; static uint32_t click_count = 0;
static Coord last_click_pos = {UINT32_MAX, UINT32_MAX}; static Coord last_click_pos = {UINT32_MAX, UINT32_MAX};
if (!this->hover_popup->hidden)
this->hover_popup->hidden = true;
if (event.key_type == KEY_MOUSE) { if (event.key_type == KEY_MOUSE) {
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -439,18 +439,18 @@ void handle_mouse(Editor *editor, KeyEvent event) {
case SCROLL: case SCROLL:
switch (event.mouse_direction) { switch (event.mouse_direction) {
case SCROLL_UP: case SCROLL_UP:
scroll_up(editor, 4); this->scroll_up(4);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case SCROLL_DOWN: case SCROLL_DOWN:
scroll_down(editor, 4); this->scroll_down(4);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case SCROLL_LEFT: case SCROLL_LEFT:
cursor_left(editor, 10); this->cursor_left(10);
break; break;
case SCROLL_RIGHT: case SCROLL_RIGHT:
cursor_right(editor, 10); this->cursor_right(10);
break; break;
} }
break; break;
@@ -463,35 +463,35 @@ void handle_mouse(Editor *editor, KeyEvent event) {
click_count = 1; click_count = 1;
last_click_time = now; last_click_time = now;
last_click_pos = cur_pos; 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) if (p.row == UINT32_MAX && p.col == UINT32_MAX)
return; return;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
if (click_count == 1) { if (click_count == 1) {
editor->cursor = p; this->cursor = p;
editor->selection = p; this->selection = p;
if (mode == SELECT) { if (mode == SELECT) {
mode = NORMAL; mode = NORMAL;
editor->selection_active = false; this->selection_active = false;
} }
} else if (click_count == 2) { } else if (click_count == 2) {
uint32_t prev_col, next_col; uint32_t prev_col, next_col;
word_boundaries(editor, editor->cursor, &prev_col, &next_col, nullptr, this->word_boundaries(this->cursor, &prev_col, &next_col, nullptr,
nullptr); nullptr);
if (editor->cursor < editor->selection) if (this->cursor < this->selection)
editor->cursor = {editor->cursor.row, prev_col}; this->cursor = {this->cursor.row, prev_col};
else else
editor->cursor = {editor->cursor.row, next_col}; this->cursor = {this->cursor.row, next_col};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
editor->selection_type = WORD; this->selection_type = WORD;
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
} else if (click_count >= 3) { } else if (click_count >= 3) {
if (editor->cursor < editor->selection) { if (this->cursor < this->selection) {
editor->cursor = {p.row, 0}; this->cursor = {p.row, 0};
} else { } else {
uint32_t line_len; 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
@@ -499,44 +499,44 @@ void handle_mouse(Editor *editor, KeyEvent event) {
line_len--; line_len--;
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {p.row, line_len}; this->cursor = {p.row, line_len};
} }
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
editor->selection_type = LINE; this->selection_type = LINE;
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
click_count = 3; click_count = 3;
} }
} }
break; break;
case DRAG: case DRAG:
if (event.mouse_button == LEFT_BTN) { 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) if (p.row == UINT32_MAX && p.col == UINT32_MAX)
return; return;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
mode = SELECT; mode = SELECT;
if (!editor->selection_active) { if (!this->selection_active) {
editor->selection_active = true; this->selection_active = true;
editor->selection_type = CHAR; this->selection_type = CHAR;
} }
uint32_t prev_col, next_col, line_len; uint32_t prev_col, next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
editor->cursor = p; this->cursor = p;
break; break;
case WORD: case WORD:
word_boundaries(editor, p, &prev_col, &next_col, nullptr, nullptr); this->word_boundaries(p, &prev_col, &next_col, nullptr, nullptr);
if (editor->cursor < editor->selection) if (this->cursor < this->selection)
editor->cursor = {p.row, prev_col}; this->cursor = {p.row, prev_col};
else else
editor->cursor = {p.row, next_col}; this->cursor = {p.row, next_col};
break; break;
case LINE: case LINE:
if (editor->cursor < editor->selection) { if (this->cursor < this->selection) {
editor->cursor = {p.row, 0}; this->cursor = {p.row, 0};
} else { } 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
@@ -544,7 +544,7 @@ void handle_mouse(Editor *editor, KeyEvent event) {
line_len--; line_len--;
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {p.row, line_len}; this->cursor = {p.row, line_len};
} }
break; break;
} }
@@ -552,10 +552,10 @@ void handle_mouse(Editor *editor, KeyEvent event) {
break; break;
case RELEASE: case RELEASE:
if (event.mouse_button == LEFT_BTN) if (event.mouse_button == LEFT_BTN)
if (editor->cursor.row == editor->selection.row && if (this->cursor.row == this->selection.row &&
editor->cursor.col == editor->selection.col) { this->cursor.col == this->selection.col) {
mode = NORMAL; mode = NORMAL;
editor->selection_active = false; this->selection_active = false;
} }
break; break;
} }

View File

@@ -68,7 +68,6 @@ uint32_t IndentationEngine::indent_real(char *line, uint32_t len) {
} }
uint32_t IndentationEngine::indent_expected(uint32_t row) { uint32_t IndentationEngine::indent_expected(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
uint32_t line_idx = row; uint32_t line_idx = row;
if (row == 0) if (row == 0)
return 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) { 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); LineIterator *it = begin_l_iter(editor->root, row);
if (!it) if (!it)
return 0; 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') if (len > 0 && line[len - 1] == '\n')
--len; --len;
lock.unlock();
if (new_indent <= 0) if (new_indent <= 0)
new_indent = 0; new_indent = 0;
uint32_t ws_len = 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, ' '); new_ws.assign(new_indent * indent, ' ');
Coord start = {row, 0}; Coord start = {row, 0};
Coord end = {row, ws_len}; 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->buffer);
free(it); free(it);
return len - ws_len + (new_indent * indent); return len - ws_len + (new_indent * indent);
} }
uint32_t IndentationEngine::indent_line(uint32_t row) { uint32_t IndentationEngine::indent_line(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, row); LineIterator *it = begin_l_iter(editor->root, row);
if (!it) if (!it)
return 0; return 0;
@@ -153,7 +149,6 @@ uint32_t IndentationEngine::indent_line(uint32_t row) {
free(it); free(it);
return 0; return 0;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
uint32_t new_indent = indent_real(line, len) + 1; 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'); new_ws.assign(new_indent, '\t');
else else
new_ws.assign(new_indent * indent, ' '); new_ws.assign(new_indent * indent, ' ');
edit_replace(editor, {row, 0}, {row, ws_len}, new_ws.c_str(), editor->edit_replace({row, 0}, {row, ws_len}, new_ws.c_str(),
new_indent * indent); new_indent * indent);
free(it->buffer); free(it->buffer);
free(it); free(it);
return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent));
} }
uint32_t IndentationEngine::dedent_line(uint32_t row) { uint32_t IndentationEngine::dedent_line(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, row); LineIterator *it = begin_l_iter(editor->root, row);
if (!it) if (!it)
return 0; return 0;
@@ -184,7 +178,6 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) {
free(it); free(it);
return 0; return 0;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
int64_t new_indent = (int64_t)indent_real(line, len) - 1; 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'); new_ws.assign(new_indent, '\t');
else else
new_ws.assign(new_indent * indent, ' '); new_ws.assign(new_indent * indent, ' ');
edit_replace(editor, {row, 0}, {row, ws_len}, new_ws.c_str(), editor->edit_replace({row, 0}, {row, ws_len}, new_ws.c_str(),
new_indent * indent); new_indent * indent);
free(it->buffer); free(it->buffer);
free(it); free(it);
return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); 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); 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); free(out);
} }
void IndentationEngine::insert_tab(Coord cursor) { void IndentationEngine::insert_tab(Coord cursor) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row); LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it) if (!it)
return; return;
@@ -278,7 +270,6 @@ void IndentationEngine::insert_tab(Coord cursor) {
free(it); free(it);
return; return;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
uint32_t ws_len = 0; uint32_t ws_len = 0;
@@ -295,13 +286,12 @@ void IndentationEngine::insert_tab(Coord cursor) {
} }
free(it->buffer); free(it->buffer);
free(it); 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(); editor->cursor.col += insert.size();
} }
void IndentationEngine::insert_new_line(Coord cursor) { void IndentationEngine::insert_new_line(Coord cursor) {
std::string formatted; std::string formatted;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row); LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it) if (!it)
return; return;
@@ -312,7 +302,6 @@ void IndentationEngine::insert_new_line(Coord cursor) {
free(it); free(it);
return; return;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
if (cursor.col >= 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); cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1);
break; break;
} }
lock.lock();
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, cursor.row); 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') if (len > 0 && line[len - 1] == '\n')
--len; --len;
lock.unlock();
} }
std::string ending = trim(std::string(line + cursor.col, len - cursor.col)); std::string ending = trim(std::string(line + cursor.col, len - cursor.col));
std::string before = trim(std::string(line, 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, ' ')) + : std::string(c_indent * indent, ' ')) +
ending; ending;
Coord new_cursor = {cursor.row + 1, (uint32_t)c_indent * indent}; Coord new_cursor = {cursor.row + 1, (uint32_t)c_indent * indent};
edit_replace(editor, cursor, {cursor.row, len}, formatted.data(), editor->edit_replace(cursor, {cursor.row, len}, formatted.data(),
formatted.size()); formatted.size());
editor->cursor = new_cursor; editor->cursor = new_cursor;
editor->cursor_preffered = UINT32_MAX; editor->cursor_preffered = UINT32_MAX;
free(it->buffer); free(it->buffer);
free(it); free(it);
if (!editor->lsp || !editor->lsp->allow_formatting_on_type) auto lsp = editor->lsp.load();
if (!lsp || !lsp->allow_formatting_on_type)
return; return;
for (char ch : editor->lsp->format_chars) { for (char ch : lsp->format_chars) {
if (ch == '\n') { if (ch == '\n') {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
if (!it) if (!it)
@@ -431,7 +419,9 @@ void IndentationEngine::insert_new_line(Coord cursor) {
free(it->buffer); free(it->buffer);
free(it); free(it);
int version = editor->lsp_version; int version = editor->lsp_version;
json message = { auto message = std::make_unique<LSPMessage>();
message->editor = editor;
message->message = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"method", "textDocument/onTypeFormatting"}, {"method", "textDocument/onTypeFormatting"},
{"params", {"params",
@@ -443,12 +433,10 @@ void IndentationEngine::insert_new_line(Coord cursor) {
{"insertSpaces", true}, {"insertSpaces", true},
{"trimTrailingWhitespace", true}, {"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}}; {"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending(); message->callback = [version](const LSPMessage &message) {
pending->editor = editor; if (version != message.editor->lsp_version)
pending->callback = [version](Editor *editor, const json &message) {
if (version != editor->lsp_version)
return; return;
auto &edits = message["result"]; auto &edits = message.message["result"];
if (edits.is_array()) { if (edits.is_array()) {
std::vector<TextEdit> t_edits; std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size()); 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.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"]; 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); t_edits.push_back(t_edit);
} }
apply_lsp_edits(editor, t_edits, false); message.editor->apply_lsp_edits(t_edits, false);
ensure_scroll(editor); message.editor->ensure_scroll();
} }
}; };
lsp_send(editor->lsp, message, pending); lsp->send(std::move(message));
break; break;
} }
} }

View File

@@ -2,52 +2,47 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "utils/utils.h" #include "utils/utils.h"
void apply_lsp_edits(Editor *editor, std::vector<TextEdit> edits, bool move) { void Editor::apply_lsp_edits(std::vector<TextEdit> edits, bool move) {
if (!edits.size()) if (!edits.size())
return; return;
TextEdit first = edits[0]; TextEdit first = edits[0];
Coord cursor = editor->cursor; Coord cursor = this->cursor;
std::sort( std::sort(
edits.begin(), edits.end(), edits.begin(), edits.end(),
[](const TextEdit &a, const TextEdit &b) { return a.start > b.start; }); [](const TextEdit &a, const TextEdit &b) { return a.start > b.start; });
for (const auto &edit : edits) for (const auto &edit : edits)
edit_replace(editor, edit.start, edit.end, edit.text.c_str(), this->edit_replace(edit.start, edit.end, edit.text.c_str(),
edit.text.size()); edit.text.size());
if (move) { if (move) {
std::shared_lock lock(editor->knot_mtx); this->cursor = first.start;
editor->cursor = first.start; this->cursor = this->move_right(
editor->cursor = this->cursor, count_clusters(first.text.c_str(), first.text.size(), 0,
move_right(editor, editor->cursor, first.text.size()));
count_clusters(first.text.c_str(), first.text.size(), 0,
first.text.size()));
} else { } else {
if (cursor.row >= editor->root->line_count) { if (cursor.row >= this->root->line_count) {
editor->cursor.row = editor->root->line_count - 1; this->cursor.row = this->root->line_count - 1;
editor->cursor.col = 0; this->cursor.col = 0;
} else { } else {
std::shared_lock lock(editor->knot_mtx);
uint32_t len; uint32_t len;
line_to_byte(editor->root, cursor.row, &len); line_to_byte(this->root, cursor.row, &len);
len--; len--;
editor->cursor.row = cursor.row; this->cursor.row = cursor.row;
editor->cursor.col = cursor.col < len ? cursor.col : len; 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") && if (msg.contains("method") &&
msg["method"] == "textDocument/publishDiagnostics") { msg["method"] == "textDocument/publishDiagnostics") {
std::unique_lock lock(editor->v_mtx); this->warnings.clear();
editor->warnings.clear();
json diagnostics = msg["params"]["diagnostics"]; json diagnostics = msg["params"]["diagnostics"];
for (size_t i = 0; i < diagnostics.size(); i++) { for (size_t i = 0; i < diagnostics.size(); i++) {
json d = diagnostics[i]; json d = diagnostics[i];
VWarn w; VWarn w;
w.line = d["range"]["start"]["line"]; w.line = d["range"]["start"]["line"];
w.start = d["range"]["start"]["character"]; w.start = d["range"]["start"]["character"];
std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, w.line);
LineIterator *it = begin_l_iter(editor->root, w.line);
if (!it) if (!it)
continue; continue;
uint32_t len; uint32_t len;
@@ -59,7 +54,6 @@ void editor_lsp_handle(Editor *editor, json msg) {
} }
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
lock.unlock();
w.start = utf16_offset_to_utf8(line, len, w.start); w.start = utf16_offset_to_utf8(line, len, w.start);
uint32_t end = d["range"]["end"]["character"]; uint32_t end = d["range"]["end"]["character"];
if (d["range"]["end"]["line"] == w.line) if (d["range"]["end"]["line"] == w.line)
@@ -102,9 +96,9 @@ void editor_lsp_handle(Editor *editor, json msg) {
w.type = 1; w.type = 1;
if (d.contains("severity")) if (d.contains("severity"))
w.type = d["severity"].get<int>(); w.type = d["severity"].get<int>();
editor->warnings.push_back(w); this->warnings.push_back(w);
} }
std::sort(editor->warnings.begin(), editor->warnings.end()); std::sort(this->warnings.begin(), this->warnings.end());
editor->warnings_dirty = true; this->warnings_dirty = true;
} }
} }

View File

@@ -1,120 +1,108 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "main.h" #include "main.h"
void move_line_up(Editor *editor) { void Editor::move_line_up() {
if (!editor || !editor->root || editor->cursor.row == 0) if (!this->root || this->cursor.row == 0)
return; return;
if (mode == NORMAL || mode == INSERT) { if (mode == NORMAL || mode == INSERT) {
uint32_t line_len, line_cluster_len; uint32_t line_len, line_cluster_len;
std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (!line) { if (!line)
lock.unlock();
return; return;
}
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
line_cluster_len = count_clusters(line, line_len, 0, line_len); line_cluster_len = count_clusters(line, line_len, 0, line_len);
uint32_t target_row = editor->cursor.row - 1; uint32_t target_row = this->cursor.row - 1;
uint32_t up_by = editor->cursor.row - target_row; uint32_t up_by = this->cursor.row - target_row;
if (up_by > 1) if (up_by > 1)
up_by--; up_by--;
lock.unlock(); Coord cursor = this->cursor;
Coord cursor = editor->cursor; edit_erase({cursor.row, 0}, line_cluster_len);
edit_erase(editor, {cursor.row, 0}, line_cluster_len); edit_erase({cursor.row, 0}, -1);
edit_erase(editor, {cursor.row, 0}, -1); edit_insert({cursor.row - up_by, 0}, (char *)"\n", 1);
edit_insert(editor, {cursor.row - up_by, 0}, (char *)"\n", 1); edit_insert({cursor.row - up_by, 0}, line, line_len);
edit_insert(editor, {cursor.row - up_by, 0}, line, line_len);
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {cursor.row - up_by, cursor.col}; this->cursor = {cursor.row - up_by, cursor.col};
} else if (mode == SELECT) { } else if (mode == SELECT) {
uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); uint32_t start_row = MIN(this->cursor.row, this->selection.row);
uint32_t end_row = MAX(editor->cursor.row, editor->selection.row); uint32_t end_row = MAX(this->cursor.row, this->selection.row);
uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr); uint32_t start_byte = line_to_byte(this->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr); uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr);
char *selected_text = read(editor->root, start_byte, end_byte - start_byte); char *selected_text = read(this->root, start_byte, end_byte - start_byte);
if (!selected_text) if (!selected_text)
return; return;
uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte, uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
0, end_byte - start_byte); 0, end_byte - start_byte);
Coord cursor = editor->cursor; Coord cursor = this->cursor;
Coord selection = editor->selection; Coord selection = this->selection;
edit_erase(editor, {start_row, 0}, selected_len); edit_erase({start_row, 0}, selected_len);
edit_insert(editor, {start_row - 1, 0}, selected_text, edit_insert({start_row - 1, 0}, selected_text, end_byte - start_byte);
end_byte - start_byte);
free(selected_text); free(selected_text);
editor->cursor = {cursor.row - 1, cursor.col}; this->cursor = {cursor.row - 1, cursor.col};
editor->selection = {selection.row - 1, selection.col}; this->selection = {selection.row - 1, selection.col};
} }
} }
void move_line_down(Editor *editor) { void Editor::move_line_down() {
if (!editor || !editor->root) if (!this->root)
return; return;
if (mode == NORMAL || mode == INSERT) { if (mode == NORMAL || mode == INSERT) {
if (editor->cursor.row >= editor->root->line_count - 1) if (this->cursor.row >= this->root->line_count - 1)
return; return;
uint32_t line_len, line_cluster_len; uint32_t line_len, line_cluster_len;
std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (!line) { if (!line)
lock.unlock();
return; return;
}
if (line_len && line[line_len - 1] == '\n') if (line_len && line[line_len - 1] == '\n')
line_len--; line_len--;
line_cluster_len = count_clusters(line, line_len, 0, line_len); line_cluster_len = count_clusters(line, line_len, 0, line_len);
uint32_t target_row = editor->cursor.row + 1; uint32_t target_row = this->cursor.row + 1;
if (target_row >= editor->root->line_count) { if (target_row >= this->root->line_count) {
free(line); free(it->buffer);
lock.unlock(); free(it);
return; return;
} }
uint32_t down_by = target_row - editor->cursor.row; uint32_t down_by = target_row - this->cursor.row;
if (down_by > 1) if (down_by > 1)
down_by--; down_by--;
uint32_t ln; uint32_t ln;
line_to_byte(editor->root, editor->cursor.row + down_by - 1, &ln); line_to_byte(this->root, this->cursor.row + down_by - 1, &ln);
lock.unlock(); Coord cursor = this->cursor;
Coord cursor = editor->cursor; edit_erase({cursor.row, 0}, line_cluster_len);
edit_erase(editor, {cursor.row, 0}, line_cluster_len); edit_erase({cursor.row, 0}, -1);
edit_erase(editor, {cursor.row, 0}, -1); edit_insert({cursor.row + down_by, 0}, (char *)"\n", 1);
edit_insert(editor, {cursor.row + down_by, 0}, (char *)"\n", 1); edit_insert({cursor.row + down_by, 0}, line, line_len);
edit_insert(editor, {cursor.row + down_by, 0}, line, line_len);
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {cursor.row + down_by, cursor.col}; this->cursor = {cursor.row + down_by, cursor.col};
} else if (mode == SELECT) { } else if (mode == SELECT) {
if (editor->cursor.row >= editor->root->line_count - 1 || if (this->cursor.row >= this->root->line_count - 1 ||
editor->selection.row >= editor->root->line_count - 1) this->selection.row >= this->root->line_count - 1)
return; return;
std::shared_lock lock(editor->knot_mtx); uint32_t start_row = MIN(this->cursor.row, this->selection.row);
uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); uint32_t end_row = MAX(this->cursor.row, this->selection.row);
uint32_t end_row = MAX(editor->cursor.row, editor->selection.row);
uint32_t target_row = end_row + 1; uint32_t target_row = end_row + 1;
if (target_row >= editor->root->line_count) if (target_row >= this->root->line_count)
return; return;
uint32_t down_by = target_row - end_row; uint32_t down_by = target_row - end_row;
if (down_by > 1) if (down_by > 1)
down_by--; down_by--;
uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr); uint32_t start_byte = line_to_byte(this->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr); uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr);
char *selected_text = read(editor->root, start_byte, end_byte - start_byte); char *selected_text = read(this->root, start_byte, end_byte - start_byte);
lock.unlock();
if (!selected_text) if (!selected_text)
return; return;
uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte, uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
0, end_byte - start_byte); 0, end_byte - start_byte);
Coord cursor = editor->cursor; Coord cursor = this->cursor;
Coord selection = editor->selection; Coord selection = this->selection;
edit_erase(editor, {start_row, 0}, selected_len); edit_erase({start_row, 0}, selected_len);
edit_insert(editor, {start_row + down_by, 0}, selected_text, edit_insert({start_row + down_by, 0}, selected_text, end_byte - start_byte);
end_byte - start_byte);
free(selected_text); free(selected_text);
editor->cursor = {cursor.row + down_by, cursor.col}; this->cursor = {cursor.row + down_by, cursor.col};
editor->selection = {selection.row + down_by, selection.col}; this->selection = {selection.row + down_by, selection.col};
} }
} }

View File

@@ -3,27 +3,24 @@
#include "main.h" #include "main.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "syntax/parser.h" #include "syntax/parser.h"
#include <cstdint>
void render_editor(Editor *editor) { void Editor::render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) {
this->size = size;
uint32_t sel_start = 0, sel_end = 0; uint32_t sel_start = 0, sel_end = 0;
std::shared_lock knot_lock(editor->knot_mtx);
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = size.col - numlen;
uint32_t render_x = editor->position.col + numlen + 1; uint32_t render_x = pos.col + numlen + 1;
std::vector<std::pair<uint32_t, char>> v; std::vector<std::pair<uint32_t, char>> v;
for (size_t i = 0; i < 94; ++i) for (size_t i = 0; i < 94; ++i)
if (editor->hooks[i] != 0) if (this->hooks[i] != 0)
v.push_back({editor->hooks[i], '!' + i}); v.push_back({this->hooks[i], '!' + i});
std::sort(v.begin(), v.end()); std::sort(v.begin(), v.end());
auto hook_it = v.begin(); 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; ++hook_it;
std::unique_lock warn_lock(editor->v_mtx); auto warn_it = this->warnings.begin();
auto warn_it = editor->warnings.begin(); while (warn_it != this->warnings.end() && warn_it->line < this->scroll.row)
while (warn_it != editor->warnings.end() &&
warn_it->line < editor->scroll.row)
++warn_it; ++warn_it;
LineData *line_data = nullptr; LineData *line_data = nullptr;
auto get_type = [&](uint32_t col) { auto get_type = [&](uint32_t col) {
@@ -34,40 +31,54 @@ void render_editor(Editor *editor) {
return (int)token.type; return (int)token.type;
return 0; return 0;
}; };
if (editor->selection_active) { Coord screen = {io::rows, io::cols};
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; Coord start, end;
if (editor->cursor >= editor->selection) { if (this->cursor >= this->selection) {
uint32_t prev_col, next_col; uint32_t prev_col, next_col;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
start = editor->selection; start = this->selection;
end = move_right(editor, editor->cursor, 1); end = this->move_right(this->cursor, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, &next_col, this->word_boundaries(this->selection, &prev_col, &next_col, nullptr,
nullptr, nullptr); nullptr);
start = {editor->selection.row, prev_col}; start = {this->selection.row, prev_col};
end = editor->cursor; end = this->cursor;
break; break;
case LINE: case LINE:
start = {editor->selection.row, 0}; start = {this->selection.row, 0};
end = editor->cursor; end = this->cursor;
break; break;
} }
} else { } else {
start = editor->cursor; start = this->cursor;
uint32_t prev_col, next_col, line_len; uint32_t prev_col, next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
end = move_right(editor, editor->selection, 1); end = this->move_right(this->selection, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, &next_col, this->word_boundaries(this->selection, &prev_col, &next_col, nullptr,
nullptr, nullptr); nullptr);
end = {editor->selection.row, next_col}; end = {this->selection.row, next_col};
break; break;
case LINE: 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
@@ -75,41 +86,37 @@ void render_editor(Editor *editor) {
line_len--; line_len--;
free(it->buffer); free(it->buffer);
free(it); free(it);
end = {editor->selection.row, line_len}; end = {this->selection.row, line_len};
break; break;
} }
} }
sel_start = line_to_byte(editor->root, start.row, nullptr) + start.col; sel_start = line_to_byte(this->root, start.row, nullptr) + start.col;
sel_end = line_to_byte(editor->root, end.row, nullptr) + end.col; sel_end = line_to_byte(this->root, end.row, nullptr) + end.col;
} }
Coord cursor = {UINT32_MAX, UINT32_MAX}; Coord cursor = {UINT32_MAX, UINT32_MAX};
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; return;
uint32_t prev_col, next_col; uint32_t prev_col, next_col;
std::string word; 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) { if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) {
uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr); uint32_t offset = line_to_byte(this->root, this->cursor.row, nullptr);
char *word_ptr = read(editor->root, offset + prev_col, next_col - prev_col); char *word_ptr = read(this->root, offset + prev_col, next_col - prev_col);
if (word_ptr) { if (word_ptr) {
word = std::string(word_ptr, next_col - prev_col); word = std::string(word_ptr, next_col - prev_col);
free(word_ptr); 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 rendered_rows = 0;
uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr); uint32_t global_byte_offset = line_to_byte(this->root, line_index, nullptr);
while (rendered_rows < editor->size.row) { while (rendered_rows < this->size.row) {
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (editor->parser) { if (this->parser)
if (line_data) line_data = this->parser->line_map.at(line_index);
line_data = editor->parser->line_tree.next();
else
line_data = editor->parser->line_tree.start_iter(line_index);
}
if (!line) if (!line)
break; break;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
@@ -123,54 +130,51 @@ void render_editor(Editor *editor) {
(line[content_start] == ' ' || line[content_start] == '\t')) (line[content_start] == ' ' || line[content_start] == '\t'))
content_start++; content_start++;
std::vector<VWarn> line_warnings; std::vector<VWarn> 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); line_warnings.push_back(*warn_it);
++warn_it; ++warn_it;
} }
uint32_t current_byte_offset = 0; uint32_t current_byte_offset = 0;
if (rendered_rows == 0) if (rendered_rows == 0)
current_byte_offset += editor->scroll.col; current_byte_offset += this->scroll.col;
while (current_byte_offset < line_len && rendered_rows < editor->size.row) { while (current_byte_offset < line_len && rendered_rows < this->size.row) {
uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0; uint32_t color = this->cursor.row == line_index ? 0x222222 : 0;
if (current_byte_offset == 0 || rendered_rows == 0) { if (current_byte_offset == 0 || rendered_rows == 0) {
const char *hook = nullptr; const char *hook = "";
char h[2] = {0, 0}; char h[2] = {0, 0};
if (hook_it != v.end() && hook_it->first == line_index + 1) { if (hook_it != v.end() && hook_it->first == line_index + 1) {
h[0] = hook_it->second; h[0] = hook_it->second;
hook = h; hook = h;
hook_it++; hook_it++;
} }
update(editor->position.row + rendered_rows, editor->position.col, hook, update(pos.row + rendered_rows, pos.col, hook, 0xAAAAAA, 0, 0, 0, 1);
0xAAAAAA, 0, 0);
char buf[16]; char buf[16];
int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1); int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
uint32_t num_color = 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++) for (int i = 0; i < len; i++)
update(editor->position.row + rendered_rows, editor->position.col + i, update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
(char[2]){buf[i], 0}, num_color, 0, 0); num_color, 0, 0, 0, 1);
} else { } else {
for (uint32_t i = 0; i < numlen + 1; i++) for (uint32_t i = 0; i < numlen + 1; i++)
update(editor->position.row + rendered_rows, editor->position.col + i, update(pos.row + rendered_rows, pos.col + i, " ", 0, 0, 0, 0, 1);
" ", 0, 0, 0);
} }
uint32_t col = 0; uint32_t col = 0;
uint32_t local_render_offset = 0; uint32_t local_render_offset = 0;
uint32_t line_left = line_len - current_byte_offset; uint32_t line_left = line_len - current_byte_offset;
while (line_left > 0 && col < render_width) { while (line_left > 0 && col < render_width) {
if (line_index == editor->cursor.row && if (line_index == this->cursor.row &&
editor->cursor.col == (current_byte_offset + local_render_offset)) { this->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = editor->position.row + rendered_rows; cursor.row = pos.row + rendered_rows;
cursor.col = render_x + col; cursor.col = render_x + col;
} }
uint32_t absolute_byte_pos = uint32_t absolute_byte_pos =
global_byte_offset + current_byte_offset + local_render_offset; global_byte_offset + current_byte_offset + local_render_offset;
const Highlight *hl = nullptr; const Highlight *hl = nullptr;
if (editor->parser) if (this->parser)
hl = &highlights[get_type(current_byte_offset + local_render_offset)]; hl = &highlights[get_type(current_byte_offset + local_render_offset)];
std::optional<std::pair<uint32_t, uint32_t>> extra = std::optional<std::pair<uint32_t, uint32_t>> extra = this->extra_hl.get(
editor->extra_hl.get( {line_index, current_byte_offset + local_render_offset});
{line_index, current_byte_offset + local_render_offset});
uint32_t fg = extra && extra->second != UINT32_MAX uint32_t fg = extra && extra->second != UINT32_MAX
? extra->first ? extra->first
: (hl ? hl->fg : 0xFFFFFF); : (hl ? hl->fg : 0xFFFFFF);
@@ -181,7 +185,7 @@ void render_editor(Editor *editor) {
(hl ? hl->flags : 0) | (hl ? hl->flags : 0) |
(extra ? (extra->second != UINT32_MAX ? CF_BOLD : CF_UNDERLINE) (extra ? (extra->second != UINT32_MAX ? CF_BOLD : CF_UNDERLINE)
: 0); : 0);
if (editor->selection_active && absolute_byte_pos >= sel_start && if (this->selection_active && absolute_byte_pos >= sel_start &&
absolute_byte_pos < sel_end) absolute_byte_pos < sel_end)
bg = bg | 0x555555; bg = bg | 0x555555;
uint32_t u_color = 0; uint32_t u_color = 0;
@@ -217,40 +221,39 @@ void render_editor(Editor *editor) {
break; break;
if (current_byte_offset + local_render_offset >= content_start && if (current_byte_offset + local_render_offset >= content_start &&
current_byte_offset + local_render_offset < content_end) { current_byte_offset + local_render_offset < content_end) {
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, cluster.c_str(), fg,
cluster.c_str(), fg, bg | color, fl, u_color); bg | color, fl, u_color, width);
} else { } else {
if (cluster[0] == ' ') { if (cluster[0] == ' ') {
update(editor->position.row + rendered_rows, render_x + col, "·", update(pos.row + rendered_rows, render_x + col, "·", 0x282828,
0x282828, bg | color, fl, u_color); bg | color, fl, u_color, 1);
} else { } else {
update(editor->position.row + rendered_rows, render_x + col, "-> ", update(pos.row + rendered_rows, render_x + col, "-> ", 0x282828,
0x282828, bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color); bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color, 4);
} }
} }
local_render_offset += cluster_len; local_render_offset += cluster_len;
line_left -= cluster_len; line_left -= cluster_len;
col += width; col += width;
while (width-- > 1) while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width, update(pos.row + rendered_rows, render_x + col - width, "\x1b", fg,
"\x1b", fg, bg | color, fl); bg | color, fl, u_color, 0);
} }
if (line_index == editor->cursor.row && if (line_index == this->cursor.row &&
editor->cursor.col == (current_byte_offset + local_render_offset)) { this->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = editor->position.row + rendered_rows; cursor.row = pos.row + rendered_rows;
cursor.col = render_x + col; 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_start &&
global_byte_offset + line_len + 1 <= sel_end && col < render_width) { global_byte_offset + line_len + 1 <= sel_end && col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0); 0x555555 | color, 0, 0, 1);
col++; col++;
} }
if (!line_warnings.empty() && line_left == 0) { if (!line_warnings.empty() && line_left == 0) {
VWarn warn = line_warnings.front(); VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1);
color, 0);
col++; col++;
for (size_t i = 0; i < line_warnings.size(); i++) { for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type) if (line_warnings[i].type < warn.type)
@@ -276,18 +279,18 @@ void render_editor(Editor *editor) {
goto final; goto final;
final: final:
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, err_sym, fg_color,
err_sym, fg_color, color, 0); color, 0, 0, 1);
col++; col++;
update(editor->position.row + rendered_rows, render_x + col, " ", update(pos.row + rendered_rows, render_x + col, " ", fg_color,
fg_color, color, 0); color, 0, 0, 1);
col++; col++;
} }
} }
} }
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0,
0 | color, 0); 0, 1);
col++; col++;
} }
size_t warn_idx = 0; size_t warn_idx = 0;
@@ -313,19 +316,19 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width) if (col + width > render_width)
break; break;
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, cluster.c_str(),
cluster.c_str(), fg_color, color, 0); fg_color, color, 0, 0, width);
col += width; col += width;
warn_idx += cluster_len; warn_idx += cluster_len;
while (width-- > 1) while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width, update(pos.row + rendered_rows, render_x + col - width, "\x1b",
"\x1b", fg_color, color, 0); fg_color, color, 0, 0, 0);
} }
line_warnings.clear(); line_warnings.clear();
} }
while (col < render_width) { while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0,
0 | color, 0); 1);
col++; col++;
} }
rendered_rows++; rendered_rows++;
@@ -333,39 +336,36 @@ void render_editor(Editor *editor) {
} }
if (line_len == 0 || if (line_len == 0 ||
(current_byte_offset >= line_len && rendered_rows == 0)) { (current_byte_offset >= line_len && rendered_rows == 0)) {
uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0; uint32_t color = this->cursor.row == line_index ? 0x222222 : 0;
const char *hook = nullptr; const char *hook = "";
char h[2] = {0, 0}; char h[2] = {0, 0};
if (hook_it != v.end() && hook_it->first == line_index + 1) { if (hook_it != v.end() && hook_it->first == line_index + 1) {
h[0] = hook_it->second; h[0] = hook_it->second;
hook = h; hook = h;
hook_it++; hook_it++;
} }
update(editor->position.row + rendered_rows, editor->position.col, hook, update(pos.row + rendered_rows, pos.col, hook, 0xAAAAAA, 0, 0, 0, 1);
0xAAAAAA, 0, 0);
char buf[16]; char buf[16];
int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1); int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
uint32_t num_color = uint32_t num_color = this->cursor.row == line_index ? 0xFFFFFF : 0x555555;
editor->cursor.row == line_index ? 0xFFFFFF : 0x555555;
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
update(editor->position.row + rendered_rows, editor->position.col + i, update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
(char[2]){buf[i], 0}, num_color, 0, 0); num_color, 0, 0, 0, 1);
if (editor->cursor.row == line_index) { if (this->cursor.row == line_index) {
cursor.row = editor->position.row + rendered_rows; cursor.row = pos.row + rendered_rows;
cursor.col = render_x; cursor.col = render_x;
} }
uint32_t col = 0; 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_start &&
global_byte_offset + line_len + 1 <= sel_end) { global_byte_offset + line_len + 1 <= sel_end) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0); 0x555555 | color, 0, 0, 1);
col++; col++;
} }
if (!line_warnings.empty()) { if (!line_warnings.empty()) {
VWarn warn = line_warnings.front(); VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1);
color, 0);
col++; col++;
for (size_t i = 0; i < line_warnings.size(); i++) { for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type) if (line_warnings[i].type < warn.type)
@@ -391,18 +391,18 @@ void render_editor(Editor *editor) {
goto final2; goto final2;
final2: final2:
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, err_sym, fg_color,
err_sym, fg_color, color, 0); color, 0, 0, 1);
col++; col++;
update(editor->position.row + rendered_rows, render_x + col, " ", update(pos.row + rendered_rows, render_x + col, " ", fg_color,
fg_color, color, 0); color, 0, 0, 1);
col++; col++;
} }
} }
} }
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0,
0 | color, 0); 0, 1);
col++; col++;
} }
size_t warn_idx = 0; size_t warn_idx = 0;
@@ -428,18 +428,18 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width) if (col + width > render_width)
break; break;
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, cluster.c_str(),
cluster.c_str(), fg_color, color, 0); fg_color, color, 0, 0, width);
col += width; col += width;
warn_idx += cluster_len; warn_idx += cluster_len;
while (width-- > 1) while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width, update(pos.row + rendered_rows, render_x + col - width, "\x1b",
"\x1b", fg_color, color, 0); fg_color, color, 0, 0, 0);
} }
} }
while (col < render_width) { while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0,
0 | color, 0); 1);
col++; col++;
} }
rendered_rows++; rendered_rows++;
@@ -447,10 +447,9 @@ void render_editor(Editor *editor) {
global_byte_offset += line_len + 1; global_byte_offset += line_len + 1;
line_index++; line_index++;
} }
while (rendered_rows < editor->size.row) { while (rendered_rows < this->size.row) {
for (uint32_t col = 0; col < editor->size.col; col++) for (uint32_t col = 0; col < this->size.col; col++)
update(editor->position.row + rendered_rows, editor->position.col + col, update(pos.row + rendered_rows, pos.col + col, " ", 0xFFFFFF, 0, 0, 0, 1);
" ", 0xFFFFFF, 0, 0);
rendered_rows++; rendered_rows++;
} }
if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) { if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) {
@@ -468,15 +467,17 @@ void render_editor(Editor *editor) {
break; break;
} }
set_cursor(cursor.row, cursor.col, type, true); set_cursor(cursor.row, cursor.col, type, true);
if (editor->completion.active && !editor->completion.box.hidden) // if (this->completion.active && !this->completion.box.hidden)
editor->completion.box.render(cursor); // this->completion.box.render(cursor);
else if (editor->hover_active) // else if (this->hover_active)
editor->hover.render(cursor); // this->hover.render(cursor);
else if (editor->diagnostics_active) // else if (this->diagnostics_active)
editor->diagnostics.render(cursor); // this->diagnostics.render(cursor);
if (!this->hover_popup->hidden)
this->hover_popup->pos = cursor;
} }
free(it->buffer); free(it->buffer);
free(it); free(it);
if (editor->parser) if (this->parser)
editor->parser->scroll(line_index + 5); this->parser->scroll(line_index + 5);
} }

View File

@@ -1,13 +1,13 @@
#include "editor/editor.h" #include "editor/editor.h"
void scroll_up(Editor *editor, int32_t number) { void Editor::scroll_up(uint32_t number) {
if (!editor || number == 0) if (number == 0)
return; return;
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -23,10 +23,9 @@ void scroll_up(Editor *editor, int32_t number) {
uint32_t col = 0; uint32_t col = 0;
std::vector<uint32_t> segment_starts; std::vector<uint32_t> segment_starts;
segment_starts.reserve(16); segment_starts.reserve(16);
if (current_byte_offset < editor->scroll.col) if (current_byte_offset < this->scroll.col)
segment_starts.push_back(0); segment_starts.push_back(0);
while (current_byte_offset < editor->scroll.col && while (current_byte_offset < this->scroll.col && current_byte_offset < len) {
current_byte_offset < len) {
uint32_t cluster_len = grapheme_next_character_break_utf8( uint32_t cluster_len = grapheme_next_character_break_utf8(
line + current_byte_offset, len - current_byte_offset); line + current_byte_offset, len - current_byte_offset);
int width = display_width(line + current_byte_offset, cluster_len); 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(); for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend();
++it_seg) { ++it_seg) {
if (--number == 0) { if (--number == 0) {
editor->scroll = {line_index, *it_seg}; this->scroll = {line_index, *it_seg};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -48,7 +47,7 @@ void scroll_up(Editor *editor, int32_t number) {
} }
line = prev_line(it, &len); line = prev_line(it, &len);
if (!line) { if (!line) {
editor->scroll = {0, 0}; this->scroll = {0, 0};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -57,7 +56,7 @@ void scroll_up(Editor *editor, int32_t number) {
line_index--; line_index--;
line = prev_line(it, &len); line = prev_line(it, &len);
if (!line) { if (!line) {
editor->scroll = {0, 0}; this->scroll = {0, 0};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; 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(); for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend();
++it_seg) { ++it_seg) {
if (--number == 0) { if (--number == 0) {
editor->scroll = {line_index, *it_seg}; this->scroll = {line_index, *it_seg};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -94,17 +93,17 @@ void scroll_up(Editor *editor, int32_t number) {
free(it); free(it);
} }
void scroll_down(Editor *editor, uint32_t number) { void Editor::scroll_down(uint32_t number) {
if (!editor || number == 0) if (number == 0)
return; return;
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; 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); Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines);
uint32_t q_head = 0; uint32_t q_head = 0;
uint32_t q_size = 0; uint32_t q_size = 0;
@@ -119,7 +118,7 @@ void scroll_down(Editor *editor, uint32_t number) {
line_len--; line_len--;
uint32_t current_byte_offset = 0; uint32_t current_byte_offset = 0;
if (first_visual_line) { if (first_visual_line) {
current_byte_offset += editor->scroll.col; current_byte_offset += this->scroll.col;
first_visual_line = false; first_visual_line = false;
} }
while (current_byte_offset < line_len || while (current_byte_offset < line_len ||
@@ -134,7 +133,7 @@ void scroll_down(Editor *editor, uint32_t number) {
} }
visual_seen++; visual_seen++;
if (visual_seen >= number + max_visual_lines) { if (visual_seen >= number + max_visual_lines) {
editor->scroll = scroll_queue[q_head]; this->scroll = scroll_queue[q_head];
free(scroll_queue); free(scroll_queue);
free(it->buffer); free(it->buffer);
free(it); free(it);
@@ -162,7 +161,7 @@ void scroll_down(Editor *editor, uint32_t number) {
} }
if (q_size > 0) { if (q_size > 0) {
uint32_t advance = (q_size > number) ? number : (q_size - 1); 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->buffer);
free(it); free(it);

View File

@@ -1,47 +1,46 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "utils/utils.h" #include "utils/utils.h"
void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) { void Editor::selection_bounds(Coord *out_start, Coord *out_end) {
std::shared_lock lock(editor->knot_mtx);
Coord start, end; Coord start, end;
if (editor->cursor >= editor->selection) { if (this->cursor >= this->selection) {
uint32_t prev_col; uint32_t prev_col;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
start = editor->selection; start = this->selection;
end = move_right(editor, editor->cursor, 1); end = this->move_right(this->cursor, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr, this->word_boundaries(this->selection, &prev_col, nullptr, nullptr,
nullptr); nullptr);
start = {editor->selection.row, prev_col}; start = {this->selection.row, prev_col};
end = editor->cursor; end = this->cursor;
break; break;
case LINE: case LINE:
start = {editor->selection.row, 0}; start = {this->selection.row, 0};
end = editor->cursor; end = this->cursor;
break; break;
} }
} else { } else {
start = editor->cursor; start = this->cursor;
uint32_t next_col, line_len; uint32_t next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
end = move_right(editor, editor->selection, 1); end = this->move_right(this->selection, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr, this->word_boundaries(this->selection, nullptr, &next_col, nullptr,
nullptr); nullptr);
end = {editor->selection.row, next_col}; end = {this->selection.row, next_col};
break; break;
case LINE: 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
end = {editor->selection.row, line_len}; end = {this->selection.row, line_len};
free(it->buffer); free(it->buffer);
free(it); free(it);
break; break;
@@ -53,47 +52,46 @@ void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) {
*out_end = end; *out_end = end;
} }
char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) { char *Editor::get_selection(uint32_t *out_len, Coord *out_start) {
std::shared_lock lock(editor->knot_mtx);
Coord start, end; Coord start, end;
if (editor->cursor >= editor->selection) { if (this->cursor >= this->selection) {
uint32_t prev_col; uint32_t prev_col;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
start = editor->selection; start = this->selection;
end = move_right(editor, editor->cursor, 1); end = this->move_right(this->cursor, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr, this->word_boundaries(this->selection, &prev_col, nullptr, nullptr,
nullptr); nullptr);
start = {editor->selection.row, prev_col}; start = {this->selection.row, prev_col};
end = editor->cursor; end = this->cursor;
break; break;
case LINE: case LINE:
start = {editor->selection.row, 0}; start = {this->selection.row, 0};
end = editor->cursor; end = this->cursor;
break; break;
} }
} else { } else {
start = editor->cursor; start = this->cursor;
uint32_t next_col, line_len; uint32_t next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
end = move_right(editor, editor->selection, 1); end = this->move_right(this->selection, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr, this->word_boundaries(this->selection, nullptr, &next_col, nullptr,
nullptr); nullptr);
end = {editor->selection.row, next_col}; end = {this->selection.row, next_col};
break; break;
case LINE: 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return nullptr; return nullptr;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
end = {editor->selection.row, line_len}; end = {this->selection.row, line_len};
free(it->buffer); free(it->buffer);
free(it); free(it);
break; break;
@@ -102,9 +100,9 @@ char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) {
if (out_start) if (out_start)
*out_start = start; *out_start = start;
uint32_t start_byte = uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col; line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col; uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
char *text = read(editor->root, start_byte, end_byte - start_byte); char *text = read(this->root, start_byte, end_byte - start_byte);
if (out_len) if (out_len)
*out_len = end_byte - start_byte; *out_len = end_byte - start_byte;
return text; return text;

View File

@@ -1,37 +1,38 @@
#include "editor/editor.h" #include "editor/editor.h"
void hover_diagnostic(Editor *editor) { // void hover_diagnostic(Editor *editor) {
std::shared_lock lock(editor->v_mtx); // static uint32_t last_line = UINT32_MAX;
static uint32_t last_line = UINT32_MAX; // if (last_line == editor->cursor.row && !editor->warnings_dirty)
if (last_line == editor->cursor.row && !editor->warnings_dirty) // return;
return; // VWarn dummy;
VWarn dummy; // dummy.line = editor->cursor.row;
dummy.line = editor->cursor.row; // editor->warnings_dirty = false;
editor->warnings_dirty = false; // last_line = editor->cursor.row;
last_line = editor->cursor.row; // auto first =
auto first = // std::lower_bound(editor->warnings.begin(), editor->warnings.end(),
std::lower_bound(editor->warnings.begin(), editor->warnings.end(), dummy); // dummy);
auto last = // auto last =
std::upper_bound(editor->warnings.begin(), editor->warnings.end(), dummy); // std::upper_bound(editor->warnings.begin(), editor->warnings.end(),
std::vector<VWarn> warnings_at_line(first, last); // dummy);
if (warnings_at_line.size() == 0) { // std::vector<VWarn> warnings_at_line(first, last);
editor->diagnostics_active = false; // if (warnings_at_line.size() == 0) {
return; // editor->diagnostics_active = false;
} // return;
editor->diagnostics.clear(); // }
editor->diagnostics.warnings.swap(warnings_at_line); // editor->diagnostics.clear();
editor->diagnostics.render_first(); // editor->diagnostics.warnings.swap(warnings_at_line);
editor->diagnostics_active = true; // editor->diagnostics.render_first();
} // editor->diagnostics_active = true;
// }
void editor_worker(Editor *editor) { void Editor::work() {
if (!editor || !editor->root) if (!this->root)
return; return;
if (editor->parser) if (this->parser)
editor->parser->work(); this->parser->work();
hover_diagnostic(editor); // hover_diagnostic(this);
if (editor->completion.active && editor->completion.hover_dirty) { // if (this->completion.active && this->completion.hover_dirty) {
editor->completion.hover.render_first(); // this->completion.hover.render_first();
editor->completion.hover_dirty = false; // this->completion.hover_dirty = false;
} // }
} }

View File

View File

@@ -1,12 +1,16 @@
#include "ui/hover.h" #include "extentions/hover.h"
#include "io/sysio.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "windows/decl.h"
void HoverBox::clear() { HoverBox *init_hover() {
text = ""; auto hover = std::make_unique<HoverBox>();
scroll_ = 0; hover->pos = {0, 0};
is_markup = false; hover->size = {1, 1};
size = {0, 0}; hover->hidden = true;
cells.clear(); HoverBox *ptr = hover.get();
layout::popups.push_back(std::move(hover));
return ptr;
} }
void HoverBox::scroll(int32_t number) { void HoverBox::scroll(int32_t number) {
@@ -19,13 +23,28 @@ void HoverBox::scroll(int32_t number) {
scroll_ = MAX((int32_t)scroll_ + number, 0); scroll_ = MAX((int32_t)scroll_ + number, 0);
if (scroll_ > line_count) if (scroll_ > line_count)
scroll_ = line_count; scroll_ = line_count;
render_first(true); scroll_dirty = true;
} }
void HoverBox::render_first(bool scroll) { void HoverBox::render(std::vector<ScreenCell> &buffer, Coord n_size,
if (!scroll) { Coord n_pos) {
pos = n_pos;
size = n_size;
if (scroll_dirty) {
// TODO: call syntax highlighter here // 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 = {io::rows, io::cols};
if (start_col + size.col > screen_size.col) {
start_col = screen_size.col - size.col;
if (start_col < 0)
start_col = 0;
}
pos.col = start_col;
pos.row = start_row;
uint32_t longest_line = 0; uint32_t longest_line = 0;
uint32_t current_width = 0; uint32_t current_width = 0;
for (size_t j = 0; j < text.length(); j++) { for (size_t j = 0; j < text.length(); j++) {
@@ -36,7 +55,7 @@ void HoverBox::render_first(bool scroll) {
current_width += 1; 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 // in the loop instead as it was never meant to wrap in the first place
longest_line = MAX(longest_line, current_width) + 1; longest_line = MAX(longest_line, current_width) + 1;
uint32_t content_width = MIN(longest_line, 130u); uint32_t content_width = MIN(longest_line, 130u);
@@ -50,10 +69,21 @@ void HoverBox::render_first(bool scroll) {
} }
uint32_t border_fg = 0x82AAFF; uint32_t border_fg = 0x82AAFF;
uint32_t base_bg = 0; 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, auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) { uint32_t bg, uint8_t flags, uint32_t width) {
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; if (r < 0 || r >= size.row || c < 0 || c >= size.col)
return;
r += pos.row;
c += pos.col;
if (r < 0 || r >= screen_size.row || c < 0 || c >= screen_size.col)
return;
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; uint32_t r = 0;
while (i < text.length() && r < 24) { while (i < text.length() && r < 24) {
@@ -75,45 +105,29 @@ void HoverBox::render_first(bool scroll) {
uint32_t fg = hl ? hl->fg : 0xFFFFFF; uint32_t fg = hl ? hl->fg : 0xFFFFFF;
uint32_t bg = hl ? hl->bg : 0; uint32_t bg = hl ? hl->bg : 0;
uint32_t flags = hl ? hl->flags : 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; c += width;
i += cluster_len; i += cluster_len;
for (int w = 1; w < width; w++) 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);
} }
while (c < content_width)
set(r + 1, ++c, " ", 0xFFFFFF, base_bg, 0, 0);
r++; r++;
} }
if (!scroll) if (scroll_dirty)
size.row = r + 2; 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++) for (uint32_t i = 1; i < size.col - 1; i++)
set(0, i, "", border_fg, base_bg, 0); set(0, i, "", border_fg, base_bg, 0, 1);
set(0, size.col - 1, "", border_fg, base_bg, 0); set(0, size.col - 1, "", border_fg, base_bg, 0, 1);
for (uint32_t r = 1; r < size.row - 1; r++) { for (uint32_t r = 1; r < size.row - 1; r++) {
set(r, 0, "", border_fg, base_bg, 0); set(r, 0, "", border_fg, base_bg, 0, 1);
set(r, size.col - 1, "", border_fg, base_bg, 0); 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++) for (uint32_t i = 1; i < size.col - 1; i++)
set(size.row - 1, i, "", border_fg, base_bg, 0); set(size.row - 1, i, "", border_fg, base_bg, 0, 1);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0); set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0, 1);
cells.resize(size.col * size.row); scroll_dirty = false;
}
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);
} }

View File

@@ -1,16 +1,19 @@
#include "io/sysio.h" #include "io/sysio.h"
static uint32_t rows, cols; namespace io {
static bool show_cursor = 0; std::vector<ScreenCell> new_screen;
static std::vector<ScreenCell> screen; uint32_t rows, cols;
static std::vector<ScreenCell> old_screen; bool show_cursor = 0;
static std::mutex screen_mutex; std::vector<ScreenCell> old_screen;
static termios orig_termios; termios orig_termios;
} // namespace io
using namespace io;
void disable_raw_mode() { void disable_raw_mode() {
std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l"; std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l";
write(STDOUT_FILENO, os.c_str(), os.size()); write(STDOUT_FILENO, os.c_str(), os.size());
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios) == -1) { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &io::orig_termios) == -1) {
perror("tcsetattr"); perror("tcsetattr");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -24,7 +27,7 @@ void enable_raw_mode() {
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST); raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8); 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[VMIN] = 0;
raw.c_cc[VTIME] = 0; raw.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
@@ -39,80 +42,14 @@ Coord start_screen() {
ioctl(0, TIOCGWINSZ, &w); ioctl(0, TIOCGWINSZ, &w);
rows = w.ws_row; rows = w.ws_row;
cols = w.ws_col; cols = w.ws_col;
screen.assign(rows * cols, {}); new_screen.assign(rows * cols, {});
old_screen.assign(rows * cols, {}); old_screen.assign(rows * cols, {});
return {rows, cols}; return {rows, cols};
} }
void end_screen() { disable_raw_mode(); } void end_screen() { disable_raw_mode(); }
Coord get_size() { return {rows, cols}; } void io_render() {
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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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() {
static bool first_render = true; static bool first_render = true;
uint32_t current_fg = 0; uint32_t current_fg = 0;
uint32_t current_bg = 0; uint32_t current_bg = 0;
@@ -121,7 +58,6 @@ void render() {
bool current_bold = false; bool current_bold = false;
bool current_strikethrough = false; bool current_strikethrough = false;
bool current_underline = false; bool current_underline = false;
std::lock_guard<std::mutex> lock(screen_mutex);
std::string out; std::string out;
out.reserve(static_cast<size_t>(rows) * static_cast<size_t>(cols) * 4 + 256); out.reserve(static_cast<size_t>(rows) * static_cast<size_t>(cols) * 4 + 256);
out += "\x1b[s\x1b[?25l"; out += "\x1b[s\x1b[?25l";
@@ -135,7 +71,7 @@ void render() {
for (uint32_t col = 0; col < cols; ++col) { for (uint32_t col = 0; col < cols; ++col) {
uint32_t idx = row * cols + col; uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx]; ScreenCell &old_cell = old_screen[idx];
ScreenCell &new_cell = screen[idx]; ScreenCell &new_cell = new_screen[idx];
bool content_changed = bool content_changed =
old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg || old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg ||
old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags || old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags ||
@@ -147,7 +83,7 @@ void render() {
for (int64_t back = 1; back <= 4 && first_change_col - back >= 0; for (int64_t back = 1; back <= 4 && first_change_col - back >= 0;
++back) { ++back) {
ScreenCell &prev_cell = ScreenCell &prev_cell =
screen[row * cols + (first_change_col - back)]; new_screen[row * cols + (first_change_col - back)];
if (prev_cell.width > 1) { if (prev_cell.width > 1) {
first_change_col -= back; first_change_col -= back;
break; break;
@@ -165,7 +101,7 @@ void render() {
for (uint32_t col = first_change_col; col <= last_change_col; ++col) { for (uint32_t col = first_change_col; col <= last_change_col; ++col) {
uint32_t idx = row * cols + col; uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx]; 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; uint32_t width = new_cell.width > 0 ? new_cell.width : 1;
bool overlap = false; bool overlap = false;
if (width > 1) { if (width > 1) {
@@ -173,7 +109,7 @@ void render() {
uint32_t next_col = col + i; uint32_t next_col = col + i;
if (next_col >= cols) if (next_col >= cols)
break; break;
const ScreenCell &next = screen[row * cols + next_col]; const ScreenCell &next = new_screen[row * cols + next_col];
if (!is_empty_cell(next)) { if (!is_empty_cell(next)) {
overlap = true; overlap = true;
break; break;
@@ -244,7 +180,7 @@ void render() {
uint32_t next_col = col + i; uint32_t next_col = col + i;
if (next_col >= cols) if (next_col >= cols)
break; break;
const ScreenCell &next = screen[row * cols + next_col]; const ScreenCell &next = new_screen[row * cols + next_col];
if (!is_empty_cell(next)) if (!is_empty_cell(next))
break; break;
out.push_back(' '); out.push_back(' ');
@@ -260,7 +196,7 @@ void render() {
ScreenCell *cell = &new_cell; ScreenCell *cell = &new_cell;
int back = 0; int back = 0;
while ((int)col - back >= 0 && cell->utf8[0] == '\x1b') 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) if (width >= cell->width)
out.append(" "); out.append(" ");
} }

View File

@@ -1,87 +0,0 @@
#include "lsp/lsp.h"
Queue<LSPOpenRequest> 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<LSPInstance> 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<LSPInstance> lsp,
std::pair<Language, Editor *> 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<LSPInstance> 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<LSPInstance>, 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<std::string>().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<std::string>().c_str());
}
}
}

View File

@@ -1,242 +0,0 @@
#include "lsp/lsp.h"
static bool init_lsp(std::shared_ptr<LSPInstance> 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<char *> argv;
argv.push_back(const_cast<char *>(lsp->lsp->command.c_str()));
for (auto &arg : lsp->lsp->args)
argv.push_back(const_cast<char *>(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<LSPInstance> 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<LSPInstance> lsp = std::make_shared<LSPInstance>();
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<std::string>();
// 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<int>();
lsp->incremental_sync = (change_type == 2);
} else if (sync.is_object() && sync.contains("change")) {
int change_type = sync["change"].get<int>();
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<std::string>();
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<std::string>();
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<bool>();
}
}
if (caps.contains("hoverProvider")) {
auto &hover = caps["hoverProvider"];
lsp->allow_hover =
hover.is_boolean() ? hover.get<bool>() : 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<bool>();
if (caps["completionProvider"].contains("triggerCharacters")) {
auto &chars = caps["completionProvider"]["triggerCharacters"];
if (chars.is_array()) {
for (auto &c : chars) {
std::string str = c.get<std::string>();
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<std::string>();
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<LSPInstance> 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<LSPInstance> 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);
}

44
src/lsp/worker.cc Normal file
View File

@@ -0,0 +1,44 @@
#include "lsp/lsp.h"
namespace lsp {
std::mutex lsp_mutex;
std::unordered_map<std::string, std::unique_ptr<LSPInstance>> active_lsps;
std::unordered_set<std::string> opened;
Queue<std::string> need_opening;
std::vector<Editor *> 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<LSPInstance>(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;
}
}

View File

@@ -1,170 +0,0 @@
#include "lsp/lsp.h"
std::shared_mutex active_lsps_mtx;
std::unordered_map<std::string, std::shared_ptr<LSPInstance>> active_lsps;
void lsp_send(std::shared_ptr<LSPInstance> 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<uint32_t>();
lsp->pending[id] = pending;
}
lsp->outbox.push(message);
}
std::optional<json> 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<LSPInstance> 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<LSPInstance> 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<Language, Editor *> 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<uint32_t>();
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<std::string>();
else if (p.contains("uri"))
uri = p["uri"].get<std::string>();
}
Editor *ed = editor_for_uri(lsp, uri);
lock.unlock();
if (ed)
editor_lsp_handle(ed, *msg);
else
lsp_handle(lsp, *msg);
lock.lock();
}
}
}
}

View File

@@ -5,38 +5,22 @@
#include "ruby/decl.h" #include "ruby/decl.h"
#include "ui/bar.h" #include "ui/bar.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "windows/decl.h"
std::atomic<bool> running{true}; std::atomic<bool> running{true};
Queue<KeyEvent> event_queue; Queue<KeyEvent> event_queue;
std::vector<Editor *> editors; fs::path pwd;
Bar bar;
uint8_t current_editor = 0;
std::atomic<uint8_t> mode = NORMAL; std::atomic<uint8_t> mode = NORMAL;
namespace ui {
Bar bar;
} // namespace ui
void background_lsp() { void background_lsp() {
while (running) while (running)
throttle(8ms, lsp_worker); 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[]) { int main(int argc, char *argv[]) {
ruby_start(); ruby_start();
load_theme(); load_theme();
@@ -46,54 +30,46 @@ int main(int argc, char *argv[]) {
Coord screen = start_screen(); Coord screen = start_screen();
const char *filename = (argc > 1) ? argv[1] : ""; const char *filename = (argc > 1) ? argv[1] : "";
uint8_t eol = read_line_endings(); uint8_t eol = read_line_endings();
Editor *editor =
new_editor(filename, {0, 0}, {screen.row - 2, screen.col}, eol);
bar.init(screen);
if (!editor) { layout::root_tile.pos = {0, 0};
end_screen(); layout::root_tile.size = {screen.row - 2, screen.col - 1};
fprintf(stderr, "Failed to load editor\n"); auto editor = std::make_unique<Editor>(filename, eol);
return 1; layout::focused_window = editor.get();
} auto block = std::make_unique<TileBlock>();
block->tiles.push_back(std::move(editor));
layout::root_tile.tile = std::move(block);
editors.push_back(editor); ui::bar.init(screen);
current_editor = editors.size() - 1;
std::thread lsp_thread(background_lsp); std::thread lsp_thread(background_lsp);
while (running) { while (running) {
KeyEvent event = throttle(1ms, read_key); KeyEvent event = throttle(1ms, read_key);
if (event.key_type == KEY_NONE) if (event.key_type != KEY_NONE) {
goto render; if (event.key_type == KEY_CHAR && event.len == 1 &&
if (event.key_type == KEY_CHAR && event.len == 1 && event.c[0] == CTRL('q')) {
event.c[0] == CTRL('q')) { free(event.c);
free(event.c); running = false;
running = false; break;
break; }
} if (mode != RUNNER) {
if (mode != RUNNER) { if (event.key_type == KEY_MOUSE) {
if (event.key_type == KEY_MOUSE) { handle_click(event);
Editor *target = editor_at(event.mouse_x, event.mouse_y); } else {
if (target) { layout::focused_window->handle_event(event);
if (event.mouse_state == PRESS)
current_editor = index_of(target);
event.mouse_x -= target->position.col;
event.mouse_y -= target->position.row;
handle_editor_event(target, event);
} }
} else { } else {
handle_editor_event(editors[current_editor], event); ui::bar.handle_event(event);
} }
} else { if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) &&
bar.handle(event); event.c)
free(event.c);
} }
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c) for (auto &lsp_inst : lsp::active_lsps)
free(event.c); lsp_inst.second->callbacks();
render: layout::focused_window->work();
throttle(4ms, editor_worker, editors[current_editor]);
bar.render();
render_editor(editors[current_editor]);
throttle(4ms, render); throttle(4ms, render);
throttle(4ms, io_render);
} }
if (lsp_thread.joinable()) if (lsp_thread.joinable())
@@ -101,9 +77,6 @@ int main(int argc, char *argv[]) {
end_screen(); end_screen();
for (auto editor : editors)
free_editor(editor);
ruby_shutdown(); ruby_shutdown();
return 0; return 0;

View File

@@ -89,8 +89,8 @@ static mrb_value sym_flags;
static mrb_value sym_start; static mrb_value sym_start;
static mrb_value sym_length; static mrb_value sym_length;
static mrb_value sym_mode; static mrb_value sym_mode;
static mrb_value sym_lang_name; static mrb_value sym_data;
static mrb_value sym_filename; static mrb_value sym_foldername;
static mrb_value sym_width; static mrb_value sym_width;
static mrb_value sym_normal; static mrb_value sym_normal;
static mrb_value sym_insert; static mrb_value sym_insert;
@@ -105,8 +105,8 @@ inline void initialize_symbols() {
sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start")); sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start"));
sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length")); sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length"));
sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode")); sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode"));
sym_lang_name = mrb_symbol_value(mrb_intern_cstr(mrb, "lang_name")); sym_data = mrb_symbol_value(mrb_intern_cstr(mrb, "data"));
sym_filename = mrb_symbol_value(mrb_intern_cstr(mrb, "filename")); sym_foldername = mrb_symbol_value(mrb_intern_cstr(mrb, "foldername"));
sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width")); sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width"));
sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal")); sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal"));
sym_insert = mrb_symbol_value(mrb_intern_cstr(mrb, "insert")); 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; return result;
} }
BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
std::string lsp_name, std::string filename, Window *window) {
std::string foldername, uint32_t line, uint32_t max_line,
uint32_t width) {
BarLine bar_line; BarLine bar_line;
static bool initialed = false; static bool initialed = false;
if (!initialed) { 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); int ai = mrb_gc_arena_save(mrb);
mrb_value info = mrb_hash_new(mrb); mrb_value info = mrb_hash_new(mrb);
mrb_value key_mode = sym_mode;
mrb_value val_mode; mrb_value val_mode;
switch (mode) { switch (mode) {
case NORMAL: case NORMAL:
@@ -177,18 +174,20 @@ BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings,
val_mode = sym_jumper; val_mode = sym_jumper;
break; break;
} }
mrb_hash_set(mrb, info, key_mode, val_mode); mrb_hash_set(mrb, info, sym_mode, val_mode);
mrb_value key_lang_name = sym_lang_name; mrb_value val_foldername =
mrb_value val_lang_name = mrb_str_new(mrb, foldername.c_str(), foldername.length());
mrb_symbol_value(mrb_intern_cstr(mrb, lang_name.c_str())); mrb_hash_set(mrb, info, sym_foldername, val_foldername);
mrb_hash_set(mrb, info, key_lang_name, val_lang_name); std::array<std::string, 5> arr = window->bar_info();
mrb_value key_filename = sym_filename; mrb_value ary = mrb_ary_new(mrb);
mrb_value val_filename = mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[0].c_str(), arr[0].length()));
mrb_str_new(mrb, filename.c_str(), filename.length()); mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[1].c_str(), arr[1].length()));
mrb_hash_set(mrb, info, key_filename, val_filename); mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[2].c_str(), arr[2].length()));
mrb_value key_width = sym_width; 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_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 mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0); mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0);
mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info); mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info);

View File

@@ -10,7 +10,7 @@ struct BashFullState {
bool line_cont = false; bool line_cont = false;
struct Lit { struct Lit {
std::string delim = ""; // Only 1 wide for strings std::string delim = "";
bool allow_interp = false; bool allow_interp = false;
bool operator==(const BashFullState::Lit &other) const { bool operator==(const BashFullState::Lit &other) const {
@@ -24,7 +24,7 @@ struct BashFullState {
} }
}; };
struct BashState { struct BashState : StateBase {
using full_state_type = BashFullState; using full_state_type = BashFullState;
int interp_level = 0; int interp_level = 0;
@@ -37,26 +37,32 @@ struct BashState {
((full_state && other.full_state && ((full_state && other.full_state &&
*full_state == *other.full_state)); *full_state == *other.full_state));
} }
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<BashState>(*this);
}
}; };
bool bash_state_match(std::shared_ptr<void> state_1, bool bash_state_match(StateBase *state_1, StateBase *state_2) {
std::shared_ptr<void> state_2) {
if (!state_1 || !state_2) if (!state_1 || !state_2)
return false; return false;
return *std::static_pointer_cast<BashState>(state_1) == return *static_cast<BashState *>(state_1) ==
*std::static_pointer_cast<BashState>(state_2); *static_cast<BashState *>(state_2);
} }
std::shared_ptr<void> bash_parse(std::vector<Token> *tokens, std::unique_ptr<StateBase> bash_parse(std::vector<Token> *tokens,
std::shared_ptr<void> in_state, StateBase *in_state, const char *text,
const char *text, uint32_t len, uint32_t len, uint32_t line_num) {
uint32_t line_num) {
static bool keywords_trie_init = false; static bool keywords_trie_init = false;
if (!keywords_trie_init) { if (!keywords_trie_init) {
keywords_trie_init = true; keywords_trie_init = true;
} }
tokens->clear(); tokens->clear();
auto state = ensure_state(std::static_pointer_cast<BashState>(in_state)); std::unique_ptr<BashState> state;
if (in_state)
state = static_unique_ptr_cast<BashState>(in_state->clone());
else
state = std::make_unique<BashState>();
uint32_t i = 0; uint32_t i = 0;
while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' ||
text[len - 1] == '\t' || text[len - 1] == ' ')) text[len - 1] == '\t' || text[len - 1] == ' '))

View File

@@ -26,21 +26,19 @@ Parser::Parser(Editor *n_editor, std::string n_lang, uint32_t n_scroll_max) {
assert("unknown lang should be checked by caller" && 0); assert("unknown lang should be checked by caller" && 0);
} }
} }
edit(0, 0, editor->root->line_count + 1); line_map.apply_edit(0, editor->root->line_count + 1);
} }
void Parser::edit(uint32_t start_line, uint32_t removed_rows, void Parser::edit(uint32_t start_line, uint32_t removed_rows,
uint32_t inserted_rows) { uint32_t inserted_rows) {
int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows; int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows;
if (delta < 0) line_map.apply_edit(start_line, delta);
line_tree.erase(start_line, (uint32_t)(-delta));
else if (delta > 0)
line_tree.insert(start_line, (uint32_t)delta);
uint32_t span = MAX(removed_rows, inserted_rows); uint32_t span = MAX(removed_rows, inserted_rows);
uint32_t begin = (start_line > 0) ? start_line - 1 : 0; uint32_t begin = (start_line > 0) ? start_line - 1 : 0;
uint32_t end = start_line + span; uint32_t end = start_line + span;
for (uint32_t line = begin; line <= end + 1; ++line) for (uint32_t line = begin; line <= end + 1; ++line)
dirty_lines.push(line); if (LineData *ld = line_map.at(line))
ld->out_state = nullptr;
} }
void Parser::work() { void Parser::work() {
@@ -48,33 +46,30 @@ void Parser::work() {
return; return;
std::vector<uint32_t> batch; std::vector<uint32_t> batch;
uint32_t c_line; uint32_t c_line;
while (dirty_lines.pop(c_line)) uint32_t line_count = editor->root->line_count + 1;
batch.push_back(c_line); for (uint32_t i = MAX(0, (int64_t)scroll_max - MAX_LINES_LOOKBEHIND);
uint32_t i = MAX(0, (int64_t)scroll_max - 60); i <= scroll_max + 10 && i < line_count; ++i) {
LineData *l_iter = line_tree.start_iter(i); auto l_opt = line_map.at(i);
while (l_iter && i < scroll_max + 10) { if (!l_opt || (l_opt && !l_opt->out_state))
if (!l_iter->out_state)
batch.push_back(i); batch.push_back(i);
i++; i++;
l_iter = line_tree.next();
} }
line_tree.end_iter();
for (uint32_t c_line : batch) { for (uint32_t c_line : batch) {
if (!running.load(std::memory_order_relaxed)) if (!running.load(std::memory_order_relaxed))
break; break;
uint32_t min_line = scroll_max > 60 ? scroll_max - 60 : 0; uint32_t min_line = scroll_max > MAX_LINES_LOOKBEHIND
? scroll_max - MAX_LINES_LOOKBEHIND
: 0;
uint32_t max_line = scroll_max + 10; uint32_t max_line = scroll_max + 10;
if (c_line < min_line || c_line > max_line) { if (c_line < min_line || c_line > max_line)
dirty_lines.push(c_line);
continue; continue;
}
uint32_t scroll_snapshot = scroll_max; uint32_t scroll_snapshot = scroll_max;
std::shared_ptr<void> prev_state = nullptr; std::unique_ptr<StateBase> prev_state = nullptr;
uint32_t line_count; if (c_line > 0 && c_line < line_count) {
line_count = line_tree.count(); auto lm = line_map.at(c_line - 1);
if (c_line > 0 && c_line < line_count) if (lm && lm->out_state)
prev_state = line_tree.at(c_line - 1)->out_state; prev_state = lm->out_state ? lm->out_state->clone() : nullptr;
std::shared_lock k_lock(editor->knot_mtx); }
LineIterator *it = begin_l_iter(editor->root, c_line); LineIterator *it = begin_l_iter(editor->root, c_line);
if (!it) if (!it)
continue; continue;
@@ -82,60 +77,57 @@ void Parser::work() {
while (cur_line < line_count) { while (cur_line < line_count) {
if (!running.load(std::memory_order_relaxed)) if (!running.load(std::memory_order_relaxed))
break; break;
if (scroll_snapshot != scroll_max) { if (scroll_snapshot != scroll_max)
LineData *line_data = line_tree.at(cur_line);
if (line_data && !line_data->out_state)
dirty_lines.push(cur_line);
break; break;
} if (cur_line < min_line || cur_line > max_line)
if (cur_line < min_line || cur_line > max_line) {
dirty_lines.push(cur_line);
break; break;
}
uint32_t len; uint32_t len;
char *line = next_line(it, &len); char *line = next_line(it, &len);
if (!line) if (!line)
break; break;
LineData *line_data = line_tree.at(cur_line); LineData *line_data = line_map.create_at(cur_line);
if (!line_data) { std::unique_ptr<StateBase> new_state;
cur_line++;
continue;
}
std::shared_ptr<void> new_state;
if (is_custom) { if (is_custom) {
std::string state = ""; std::string prev_value = "";
if (prev_state) if (prev_state) {
state = std::static_pointer_cast<std::string>(prev_state)->c_str(); CustomState *prev_custom =
std::string out_state = parse_custom(&line_data->tokens, parser_block, static_cast<CustomState *>(prev_state.get());
line, len, state, cur_line); prev_value = prev_custom->value;
new_state = std::make_shared<std::string>(out_state); }
std::string out_value = parse_custom(&line_data->tokens, parser_block,
line, len, prev_value, cur_line);
new_state = std::make_unique<CustomState>(out_value);
} else { } else {
new_state = new_state = parse_func(&line_data->tokens, prev_state.get(), line, len,
parse_func(&line_data->tokens, prev_state, line, len, cur_line); cur_line);
} }
line_data->in_state = prev_state; line_data->in_state = std::move(prev_state);
line_data->out_state = new_state; line_data->out_state = std::move(new_state);
bool done = false; bool done = false;
if (cur_line + 1 < line_count) { if (cur_line + 1 < line_count) {
LineData *next_line_data = line_tree.at(cur_line + 1); LineData *next_line_data = line_map.at(cur_line + 1);
if (next_line_data) { if (next_line_data) {
if (is_custom) { if (is_custom) {
std::string a = std::string a = "";
prev_state if (new_state) {
? std::static_pointer_cast<std::string>(new_state)->c_str() CustomState *cs = static_cast<CustomState *>(new_state.get());
: ""; a = cs->value;
std::string b = next_line_data->in_state }
? std::static_pointer_cast<std::string>( std::string b = "";
next_line_data->in_state) if (next_line_data->in_state) {
->c_str() CustomState *cs =
: ""; static_cast<CustomState *>(next_line_data->in_state.get());
b = cs->value;
}
done = custom_compare(match_block, a, b); done = custom_compare(match_block, a, b);
} else { } else {
done = state_match_func(new_state, next_line_data->in_state); done = state_match_func(new_state.get(),
next_line_data->in_state.get());
} }
} }
} }
prev_state = new_state; prev_state =
line_data->out_state ? line_data->out_state->clone() : nullptr;
cur_line++; cur_line++;
if (done) if (done)
break; break;

View File

@@ -241,20 +241,22 @@ struct RubyFullState {
} }
}; };
struct RubyState { struct RubyState : StateBase {
using full_state_type = RubyFullState; using full_state_type = RubyFullState;
int interp_level = 0; int interp_level = 0;
std::stack<std::shared_ptr<RubyFullState>> interp_stack; std::stack<RubyFullState> interp_stack;
std::shared_ptr<RubyFullState> full_state; RubyFullState full_state;
std::deque<HeredocInfo> heredocs; std::deque<HeredocInfo> heredocs;
bool operator==(const RubyState &other) const { bool operator==(const RubyState &other) const {
return interp_level == other.interp_level && return interp_level == other.interp_level &&
interp_stack == other.interp_stack && interp_stack == other.interp_stack &&
((full_state && other.full_state && full_state == other.full_state && heredocs == other.heredocs;
*full_state == *other.full_state)) && }
heredocs == other.heredocs;
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<RubyState>(*this);
} }
}; };
@@ -278,18 +280,16 @@ inline static uint32_t get_next_word(const char *text, uint32_t i,
return width; return width;
} }
bool ruby_state_match(std::shared_ptr<void> state_1, bool ruby_state_match(StateBase *state_1, StateBase *state_2) {
std::shared_ptr<void> state_2) {
if (!state_1 || !state_2) if (!state_1 || !state_2)
return false; return false;
return *std::static_pointer_cast<RubyState>(state_1) == return *static_cast<RubyState *>(state_1) ==
*std::static_pointer_cast<RubyState>(state_2); *static_cast<RubyState *>(state_2);
} }
std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens, std::unique_ptr<StateBase> ruby_parse(std::vector<Token> *tokens,
std::shared_ptr<void> in_state, StateBase *in_state, const char *text,
const char *text, uint32_t len, uint32_t len, uint32_t line_num) {
uint32_t line_num) {
static bool keywords_trie_init = false; static bool keywords_trie_init = false;
static Trie<void> base_keywords_trie; static Trie<void> base_keywords_trie;
static Trie<void> expecting_keywords_trie; static Trie<void> expecting_keywords_trie;
@@ -313,7 +313,11 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
keywords_trie_init = true; keywords_trie_init = true;
} }
tokens->clear(); tokens->clear();
auto state = ensure_state(std::static_pointer_cast<RubyState>(in_state)); std::unique_ptr<RubyState> state;
if (in_state)
state = static_unique_ptr_cast<RubyState>(in_state->clone());
else
state = std::make_unique<RubyState>();
uint32_t i = 0; uint32_t i = 0;
while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' ||
text[len - 1] == '\t' || text[len - 1] == ' ')) text[len - 1] == '\t' || text[len - 1] == ' '))
@@ -322,18 +326,18 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
return state; return state;
bool heredoc_first = false; bool heredoc_first = false;
while (i < len) { while (i < len) {
if (state->full_state->in_state == RubyFullState::END) if (state->full_state.in_state == RubyFullState::END)
return state; return state;
if (state->full_state->in_state == RubyFullState::COMMENT) { if (state->full_state.in_state == RubyFullState::COMMENT) {
tokens->push_back({i, len, TokenKind::K_COMMENT}); tokens->push_back({i, len, TokenKind::K_COMMENT});
if (i == 0 && len == 4 && text[i] == '=' && text[i + 1] == 'e' && if (i == 0 && len == 4 && text[i] == '=' && text[i + 1] == 'e' &&
text[i + 2] == 'n' && text[i + 3] == 'd') { text[i + 2] == 'n' && text[i + 3] == 'd') {
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
} }
return state; return state;
} }
if (!heredoc_first && if (!heredoc_first &&
state->full_state->in_state == RubyFullState::HEREDOC) { state->full_state.in_state == RubyFullState::HEREDOC) {
if (i == 0) { if (i == 0) {
uint32_t start = 0; uint32_t start = 0;
if (state->heredocs.front().allow_indentation) if (state->heredocs.front().allow_indentation)
@@ -344,7 +348,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
state->heredocs.front().delim.length())) { state->heredocs.front().delim.length())) {
state->heredocs.pop_front(); state->heredocs.pop_front();
if (state->heredocs.empty()) if (state->heredocs.empty())
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
tokens->push_back({i, len, TokenKind::K_ANNOTATION}); tokens->push_back({i, len, TokenKind::K_ANNOTATION});
return state; return state;
} }
@@ -421,7 +425,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION});
i += 2; i += 2;
state->interp_stack.push(state->full_state); state->interp_stack.push(state->full_state);
state->full_state = std::make_shared<RubyFullState>(); state->full_state = RubyFullState();
state->interp_level = 1; state->interp_level = 1;
break; break;
} }
@@ -432,7 +436,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
continue; continue;
} }
} }
if (state->full_state->in_state == RubyFullState::STRING) { if (state->full_state.in_state == RubyFullState::STRING) {
uint32_t start = i; uint32_t start = i;
while (i < len) { while (i < len) {
if (text[i] == '\\') { if (text[i] == '\\') {
@@ -496,36 +500,36 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_ESCAPE}); tokens->push_back({start, i, TokenKind::K_ESCAPE});
continue; continue;
} }
if (state->full_state->lit.allow_interp && text[i] == '#' && if (state->full_state.lit.allow_interp && text[i] == '#' &&
i + 1 < len && text[i + 1] == '{') { i + 1 < len && text[i + 1] == '{') {
tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({start, i, TokenKind::K_STRING});
tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION});
i += 2; i += 2;
state->interp_stack.push(state->full_state); state->interp_stack.push(state->full_state);
state->full_state = std::make_shared<RubyFullState>(); state->full_state = RubyFullState();
state->interp_level = 1; state->interp_level = 1;
break; break;
} }
if (text[i] == state->full_state->lit.delim_start && if (text[i] == state->full_state.lit.delim_start &&
state->full_state->lit.delim_start != state->full_state.lit.delim_start !=
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
state->full_state->lit.brace_level++; state->full_state.lit.brace_level++;
} }
if (text[i] == state->full_state->lit.delim_end) { if (text[i] == state->full_state.lit.delim_end) {
if (state->full_state->lit.delim_start == if (state->full_state.lit.delim_start ==
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
i++; i++;
tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({start, i, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} else { } else {
state->full_state->lit.brace_level--; state->full_state.lit.brace_level--;
if (state->full_state->lit.brace_level == 0) { if (state->full_state.lit.brace_level == 0) {
i++; i++;
tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({start, i, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} }
} }
@@ -536,7 +540,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, len, TokenKind::K_STRING}); tokens->push_back({start, len, TokenKind::K_STRING});
continue; continue;
} }
if (state->full_state->in_state == RubyFullState::REGEXP) { if (state->full_state.in_state == RubyFullState::REGEXP) {
uint32_t start = i; uint32_t start = i;
while (i < len) { while (i < len) {
if (text[i] == '\\') { if (text[i] == '\\') {
@@ -605,30 +609,30 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION});
i += 2; i += 2;
state->interp_stack.push(state->full_state); state->interp_stack.push(state->full_state);
state->full_state = std::make_shared<RubyFullState>(); state->full_state = RubyFullState();
state->interp_level = 1; state->interp_level = 1;
break; break;
} }
if (text[i] == state->full_state->lit.delim_start && if (text[i] == state->full_state.lit.delim_start &&
state->full_state->lit.delim_start != state->full_state.lit.delim_start !=
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
state->full_state->lit.brace_level++; state->full_state.lit.brace_level++;
} }
if (text[i] == state->full_state->lit.delim_end) { if (text[i] == state->full_state.lit.delim_end) {
if (state->full_state->lit.delim_start == if (state->full_state.lit.delim_start ==
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
i += 1; i += 1;
tokens->push_back({start, i, TokenKind::K_REGEXP}); tokens->push_back({start, i, TokenKind::K_REGEXP});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} else { } else {
state->full_state->lit.brace_level--; state->full_state.lit.brace_level--;
if (state->full_state->lit.brace_level == 0) { if (state->full_state.lit.brace_level == 0) {
i += 1; i += 1;
tokens->push_back({start, i, TokenKind::K_REGEXP}); tokens->push_back({start, i, TokenKind::K_REGEXP});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} }
} }
@@ -642,8 +646,8 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
if (i == 0 && len == 6) { if (i == 0 && len == 6) {
if (text[i] == '=' && text[i + 1] == 'b' && text[i + 2] == 'e' && if (text[i] == '=' && text[i + 1] == 'b' && text[i + 2] == 'e' &&
text[i + 3] == 'g' && text[i + 4] == 'i' && text[i + 5] == 'n') { text[i + 3] == 'g' && text[i + 4] == 'i' && text[i + 5] == 'n') {
state->full_state->in_state = RubyFullState::COMMENT; state->full_state.in_state = RubyFullState::COMMENT;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({0, len, TokenKind::K_COMMENT}); tokens->push_back({0, len, TokenKind::K_COMMENT});
return state; return state;
} }
@@ -653,8 +657,8 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
text[i + 3] == 'N' && text[i + 4] == 'D' && text[i + 5] == '_' && text[i + 3] == 'N' && text[i + 4] == 'D' && text[i + 5] == '_' &&
text[i + 6] == '_') { text[i + 6] == '_') {
tokens->clear(); tokens->clear();
state->full_state->in_state = RubyFullState::END; state->full_state.in_state = RubyFullState::END;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
return state; return state;
} }
} }
@@ -684,33 +688,33 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
delim += text[j++]; delim += text[j++];
} }
} }
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
if (!delim.empty()) { if (!delim.empty()) {
tokens->push_back({s, j, TokenKind::K_ANNOTATION}); tokens->push_back({s, j, TokenKind::K_ANNOTATION});
state->heredocs.push_back({delim, interpolation, indented}); state->heredocs.push_back({delim, interpolation, indented});
state->full_state->in_state = RubyFullState::HEREDOC; state->full_state.in_state = RubyFullState::HEREDOC;
heredoc_first = true; heredoc_first = true;
} }
i = j; i = j;
continue; continue;
} }
if (text[i] == '/' && state->full_state->expecting_expr) { if (text[i] == '/' && state->full_state.expecting_expr) {
tokens->push_back({i, i + 1, TokenKind::K_REGEXP}); tokens->push_back({i, i + 1, TokenKind::K_REGEXP});
state->full_state->in_state = RubyFullState::REGEXP; state->full_state.in_state = RubyFullState::REGEXP;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->full_state->lit.delim_start = '/'; state->full_state.lit.delim_start = '/';
state->full_state->lit.delim_end = '/'; state->full_state.lit.delim_end = '/';
state->full_state->lit.allow_interp = true; state->full_state.lit.allow_interp = true;
i++; i++;
continue; continue;
} else if (text[i] == '#') { } else if (text[i] == '#') {
if (line_num == 0 && i == 0 && len > 4 && text[i + 1] == '!') { if (line_num == 0 && i == 0 && len > 4 && text[i + 1] == '!') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({0, len, TokenKind::K_SHEBANG}); tokens->push_back({0, len, TokenKind::K_SHEBANG});
return state; return state;
} }
tokens->push_back({i, len, TokenKind::K_COMMENT}); tokens->push_back({i, len, TokenKind::K_COMMENT});
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
return state; return state;
} else if (text[i] == '.') { } else if (text[i] == '.') {
uint32_t start = i; uint32_t start = i;
@@ -722,15 +726,15 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
} }
} }
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
continue; continue;
} else if (text[i] == ':') { } else if (text[i] == ':') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i >= len) { if (i >= len) {
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
continue; continue;
} }
if (text[i] == ':') { if (text[i] == ':') {
@@ -739,7 +743,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
} }
if (text[i] == '\'' || text[i] == '"') { if (text[i] == '\'' || text[i] == '"') {
tokens->push_back({start, i, TokenKind::K_LABEL}); tokens->push_back({start, i, TokenKind::K_LABEL});
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
continue; continue;
} }
if (text[i] == '$' || text[i] == '@') { if (text[i] == '$' || text[i] == '@') {
@@ -767,7 +771,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
continue; continue;
} else if (text[i] == '@') { } else if (text[i] == '@') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i >= len) if (i >= len)
@@ -783,7 +787,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_VARIABLEINSTANCE}); tokens->push_back({start, i, TokenKind::K_VARIABLEINSTANCE});
continue; continue;
} else if (text[i] == '$') { } else if (text[i] == '$') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i >= len) if (i >= len)
@@ -806,7 +810,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_VARIABLEGLOBAL}); tokens->push_back({start, i, TokenKind::K_VARIABLEGLOBAL});
continue; continue;
} else if (text[i] == '?') { } else if (text[i] == '?') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i < len && text[i] == '\\') { if (i < len && text[i] == '\\') {
@@ -851,95 +855,95 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_CHAR}); tokens->push_back({start, i, TokenKind::K_CHAR});
continue; continue;
} else { } else {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
continue; continue;
} }
} else if (text[i] == '{') { } else if (text[i] == '{') {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
state->interp_level++; state->interp_level++;
state->full_state->brace_level++; state->full_state.brace_level++;
i++; i++;
continue; continue;
} else if (text[i] == '}') { } else if (text[i] == '}') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->interp_level--; state->interp_level--;
if (state->interp_level == 0 && !state->interp_stack.empty()) { if (state->interp_level == 0 && !state->interp_stack.empty()) {
state->full_state = state->interp_stack.top(); state->full_state = state->interp_stack.top();
state->interp_stack.pop(); state->interp_stack.pop();
tokens->push_back({i, i + 1, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 1, TokenKind::K_INTERPOLATION});
} else { } else {
state->full_state->brace_level--; state->full_state.brace_level--;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
} }
i++; i++;
continue; continue;
} else if (text[i] == '(') { } else if (text[i] == '(') {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
state->full_state->brace_level++; state->full_state.brace_level++;
i++; i++;
continue; continue;
} else if (text[i] == ')') { } else if (text[i] == ')') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->full_state->brace_level--; state->full_state.brace_level--;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
i++; i++;
continue; continue;
} else if (text[i] == '[') { } else if (text[i] == '[') {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
state->full_state->brace_level++; state->full_state.brace_level++;
i++; i++;
continue; continue;
} else if (text[i] == ']') { } else if (text[i] == ']') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->full_state->brace_level--; state->full_state.brace_level--;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
i++; i++;
continue; continue;
} else if (text[i] == '\'') { } else if (text[i] == '\'') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({i, i + 1, TokenKind::K_STRING}); tokens->push_back({i, i + 1, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::STRING; state->full_state.in_state = RubyFullState::STRING;
state->full_state->lit.delim_start = '\''; state->full_state.lit.delim_start = '\'';
state->full_state->lit.delim_end = '\''; state->full_state.lit.delim_end = '\'';
state->full_state->lit.allow_interp = false; state->full_state.lit.allow_interp = false;
i++; i++;
continue; continue;
} else if (text[i] == '"') { } else if (text[i] == '"') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({i, i + 1, TokenKind::K_STRING}); tokens->push_back({i, i + 1, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::STRING; state->full_state.in_state = RubyFullState::STRING;
state->full_state->lit.delim_start = '"'; state->full_state.lit.delim_start = '"';
state->full_state->lit.delim_end = '"'; state->full_state.lit.delim_end = '"';
state->full_state->lit.allow_interp = true; state->full_state.lit.allow_interp = true;
i++; i++;
continue; continue;
} else if (text[i] == '`') { } else if (text[i] == '`') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({i, i + 1, TokenKind::K_STRING}); tokens->push_back({i, i + 1, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::STRING; state->full_state.in_state = RubyFullState::STRING;
state->full_state->lit.delim_start = '`'; state->full_state.lit.delim_start = '`';
state->full_state->lit.delim_end = '`'; state->full_state.lit.delim_end = '`';
state->full_state->lit.allow_interp = true; state->full_state.lit.allow_interp = true;
i++; i++;
continue; continue;
} else if (text[i] == '%') { } else if (text[i] == '%') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
if (i + 1 >= len) { if (i + 1 >= len) {
i++; i++;
continue; continue;
@@ -1005,16 +1009,16 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back( tokens->push_back(
{i, i + prefix_len + 1, {i, i + prefix_len + 1,
(is_regexp ? TokenKind::K_REGEXP : TokenKind::K_STRING)}); (is_regexp ? TokenKind::K_REGEXP : TokenKind::K_STRING)});
state->full_state->in_state = state->full_state.in_state =
is_regexp ? RubyFullState::REGEXP : RubyFullState::STRING; is_regexp ? RubyFullState::REGEXP : RubyFullState::STRING;
state->full_state->lit.delim_start = delim_start; state->full_state.lit.delim_start = delim_start;
state->full_state->lit.delim_end = delim_end; state->full_state.lit.delim_end = delim_end;
state->full_state->lit.allow_interp = allow_interp; state->full_state.lit.allow_interp = allow_interp;
state->full_state->lit.brace_level = 1; state->full_state.lit.brace_level = 1;
i += prefix_len + 1; i += prefix_len + 1;
continue; continue;
} else if (isdigit(text[i])) { } else if (isdigit(text[i])) {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
if (text[i] == '0') { if (text[i] == '0') {
i++; i++;
@@ -1115,7 +1119,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_NUMBER}); tokens->push_back({start, i, TokenKind::K_NUMBER});
continue; continue;
} else if (identifier_start_char(text[i])) { } else if (identifier_start_char(text[i])) {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t length; uint32_t length;
if ((length = base_keywords_trie.match(text, i, len, identifier_char))) { if ((length = base_keywords_trie.match(text, i, len, identifier_char))) {
tokens->push_back({i, i + length, TokenKind::K_KEYWORD}); tokens->push_back({i, i + length, TokenKind::K_KEYWORD});
@@ -1123,7 +1127,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
continue; continue;
} else if ((length = expecting_keywords_trie.match(text, i, len, } else if ((length = expecting_keywords_trie.match(text, i, len,
identifier_char))) { identifier_char))) {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
tokens->push_back({i, i + length, TokenKind::K_KEYWORD}); tokens->push_back({i, i + length, TokenKind::K_KEYWORD});
i += length; i += length;
continue; continue;
@@ -1134,7 +1138,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
continue; continue;
} else if ((length = expecting_operators_trie.match( } else if ((length = expecting_operators_trie.match(
text, i, len, identifier_char)) > 0) { text, i, len, identifier_char)) > 0) {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
tokens->push_back({i, i + length, TokenKind::K_KEYWORDOPERATOR}); tokens->push_back({i, i + length, TokenKind::K_KEYWORDOPERATOR});
i += length; i += length;
continue; continue;
@@ -1256,7 +1260,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
operator_trie.match(text, i, len, [](char) { return false; }))) { operator_trie.match(text, i, len, [](char) { return false; }))) {
tokens->push_back({i, i + op_len, TokenKind::K_OPERATOR}); tokens->push_back({i, i + op_len, TokenKind::K_OPERATOR});
i += op_len; i += op_len;
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
continue; continue;
} else { } else {
i += utf8_codepoint_width(text[i]); i += utf8_codepoint_width(text[i]);

View File

@@ -3,17 +3,25 @@
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "windows/decl.h"
void Bar::log(std::string message) { log_line = message; } void Bar::log(std::string message) { log_line = message; }
void Bar::render() { void Bar::render(std::vector<ScreenCell> &buffer) {
USING(LSPInstance); USING(LSPInstance);
Editor *editor = editors[current_editor]; BarLine bar_line;
BarLine bar_line = bar_line =
bar_contents(mode, editor->lang.name, editor->warnings.size(), bar_contents(mode, screen.col, pwd.string(), layout::focused_window);
editor->lsp ? editor->lsp->lsp->command : "", auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg,
editor->filename, editor->filename, editor->cursor.row + 1, uint32_t bg, uint8_t flags, uint32_t width) {
editor->root->line_count + 1, screen.col); 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 row = screen.row - 2;
uint32_t width = screen.col; uint32_t width = screen.col;
std::string &line = bar_line.line; std::string &line = bar_line.line;
@@ -26,43 +34,46 @@ void Bar::render() {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
Highlight highlight = bar_line.get_highlight(col); Highlight highlight = bar_line.get_highlight(col);
update(row, col, cluster.c_str(), highlight.fg, highlight.bg, update(row, col, cluster.c_str(), highlight.fg, highlight.bg,
highlight.flags); highlight.flags, width);
col += width; col += width;
i += cluster_len; i += cluster_len;
for (int w = 1; w < width; w++) 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) while (col < width)
update(row, col++, " ", 0, 0, 0); update(row, col++, " ", 0, 0, 0, 1);
col = 0; col = 0;
row++; row++;
if (mode == RUNNER) { if (mode == RUNNER) {
update(row, col++, ":", 0xFFFFFF, 0, 0); update(row, col++, ":", 0xFFFFFF, 0, 0, 1);
for (char c : command) 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 { } else {
for (char c : log_line) 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) 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 (layout::focused_window)
layout::focused_window->handle_command(command);
}
void Bar::handle_event(KeyEvent event) {
if (event.key_type == KEY_CHAR && event.len == 1) { if (event.key_type == KEY_CHAR && event.len == 1) {
if (event.c[0] == 0x1B) { if (event.c[0] == 0x1B) {
command = ""; command = "";
mode = NORMAL; mode = NORMAL;
} else if (event.c[0] == '\n' || event.c[0] == '\r') { } else if (event.c[0] == '\n' || event.c[0] == '\r') {
command = trim(command); command = trim(command);
if (command == "w") { handle_command(command);
save_file(editors[current_editor]);
} else if (command == "q") {
running = false;
} else if (command == "wq") {
save_file(editors[current_editor]);
running = false;
}
mode = NORMAL; mode = NORMAL;
command = ""; command = "";
} else if (isprint((unsigned char)(event.c[0]))) { } else if (isprint((unsigned char)(event.c[0]))) {

View File

@@ -1,187 +1,187 @@
#include "ui/completionbox.h" // #include "ui/completionbox.h"
#include "editor/completions.h" // #include "editor/completions.h"
#include "io/sysio.h" // #include "io/sysio.h"
#include "utils/utils.h" // #include "utils/utils.h"
//
std::string item_kind_name(uint8_t kind) { // std::string item_kind_name(uint8_t kind) {
switch (kind) { // switch (kind) {
case 1: // case 1:
return "Text"; // return "Text";
case 2: // case 2:
return "Method"; // return "Method";
case 3: // case 3:
return "Function"; // return "Function";
case 4: // case 4:
return "Constructor"; // return "Constructor";
case 5: // case 5:
return "Field"; // return "Field";
case 6: // case 6:
return "Variable"; // return "Variable";
case 7: // case 7:
return "Class"; // return "Class";
case 8: // case 8:
return "Interface"; // return "Interface";
case 9: // case 9:
return "Module"; // return "Module";
case 10: // case 10:
return "Property"; // return "Property";
case 11: // case 11:
return "Unit"; // return "Unit";
case 12: // case 12:
return "Value"; // return "Value";
case 13: // case 13:
return "Enum"; // return "Enum";
case 14: // case 14:
return "Keyword"; // return "Keyword";
case 15: // case 15:
return "Snippet"; // return "Snippet";
case 16: // case 16:
return "Color"; // return "Color";
case 17: // case 17:
return "File"; // return "File";
case 18: // case 18:
return "Reference"; // return "Reference";
case 19: // case 19:
return "Folder"; // return "Folder";
case 20: // case 20:
return "EnumMember"; // return "EnumMember";
case 21: // case 21:
return "Constant"; // return "Constant";
case 22: // case 22:
return "Struct"; // return "Struct";
case 23: // case 23:
return "Event"; // return "Event";
case 24: // case 24:
return "Operator"; // return "Operator";
case 25: // case 25:
return "TypeParameter"; // return "TypeParameter";
default: // default:
return "Unknown"; // return "Unknown";
} // }
} // }
//
const char *item_symbol(uint8_t kind) { return ""; } // const char *item_symbol(uint8_t kind) { return "●"; }
//
uint32_t kind_color(uint8_t kind) { return 0x82AAFF; } // uint32_t kind_color(uint8_t kind) { return 0x82AAFF; }
//
void CompletionBox::render_update() { // void CompletionBox::render_update() {
if (hidden || session->visible.empty()) // if (hidden || session->visible.empty())
return; // return;
std::unique_lock lock(mtx); // std::unique_lock lock(mtx);
uint32_t max_label_len = 0; // uint32_t max_label_len = 0;
uint32_t max_detail_len = 0; // uint32_t max_detail_len = 0;
uint32_t max_kind_len = 0; // uint32_t max_kind_len = 0;
for (uint32_t x = session->scroll; // for (uint32_t x = session->scroll;
x < session->scroll + 8 && x < session->visible.size(); x++) { // x < session->scroll + 8 && x < session->visible.size(); x++) {
uint32_t i = session->visible[x]; // uint32_t i = session->visible[x];
if (i >= session->items.size()) // if (i >= session->items.size())
continue; // continue;
auto &item = session->items[i]; // auto &item = session->items[i];
max_label_len = MAX(max_label_len, (uint32_t)item.label.size()); // max_label_len = MAX(max_label_len, (uint32_t)item.label.size());
if (item.detail) // if (item.detail)
max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size()); // max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size());
max_kind_len = // max_kind_len =
MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size()); // MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size());
} // }
uint32_t total = session->visible.size(); // uint32_t total = session->visible.size();
uint32_t rows = MIN(total, 8); // uint32_t rows = MIN(total, 8);
size.row = rows + 2; // size.row = rows + 2;
size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + 1; // size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len +
cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); // 1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set =
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, // [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) { // uint32_t bg, uint8_t flags) {
if (r < size.row && c < size.col) // if (r < size.row && c < size.col)
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; // cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
}; // };
uint32_t border_fg = 0x82AAFF; // uint32_t border_fg = 0x82AAFF;
uint32_t sel_bg = 0x174225; // uint32_t sel_bg = 0x174225;
set(0, 0, "", border_fg, 0, 0); // set(0, 0, "┌", border_fg, 0, 0);
for (uint32_t c = 1; c < size.col - 1; c++) // for (uint32_t c = 1; c < size.col - 1; c++)
set(0, c, "", border_fg, 0, 0); // set(0, c, "─", border_fg, 0, 0);
set(0, size.col - 1, "", border_fg, 0, 0); // set(0, size.col - 1, "┐", border_fg, 0, 0);
uint32_t start = session->scroll; // uint32_t start = session->scroll;
uint32_t end = MIN(start + 8, session->visible.size()); // uint32_t end = MIN(start + 8, session->visible.size());
for (uint32_t row_idx = start; row_idx < end; row_idx++) { // for (uint32_t row_idx = start; row_idx < end; row_idx++) {
uint32_t r = (row_idx - start) + 1; // uint32_t r = (row_idx - start) + 1;
auto &item = session->items[session->visible[row_idx]]; // auto &item = session->items[session->visible[row_idx]];
uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : 1; // uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg :
uint32_t fg = 0xFFFFFF; // 1; uint32_t fg = 0xFFFFFF; set(r, 0, "│", border_fg, 0, 0); uint32_t c =
set(r, 0, "", border_fg, 0, 0); // 1; const char *sym = item_symbol(item.kind); set(r, c++, sym,
uint32_t c = 1; // kind_color(item.kind), bg, 0); set(r, c++, " ", fg, bg, 0); for (size_t i
const char *sym = item_symbol(item.kind); // = 0; i < item.label.size(); i++)
set(r, c++, sym, kind_color(item.kind), bg, 0); // set(r, c + i, (char[2]){item.label[i], 0}, fg, bg,
set(r, c++, " ", fg, bg, 0); // item.deprecated ? CF_STRIKETHROUGH : 0);
for (size_t i = 0; i < item.label.size(); i++) // c += item.label.size();
set(r, c + i, (char[2]){item.label[i], 0}, fg, bg, // set(r, c++, " ", fg, bg, 0);
item.deprecated ? CF_STRIKETHROUGH : 0); // uint32_t detail_fg = 0xAAAAAA;
c += item.label.size(); // if (item.detail) {
set(r, c++, " ", fg, bg, 0); // for (size_t i = 0; i < item.detail->size(); i++)
uint32_t detail_fg = 0xAAAAAA; // set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0);
if (item.detail) { // c += item.detail->size();
for (size_t i = 0; i < item.detail->size(); i++) // }
set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0); // uint32_t pad = size.col - 1 - c - max_kind_len;
c += item.detail->size(); // for (uint32_t i = 0; i < pad; i++)
} // set(r, c + i, " ", fg, bg, 0);
uint32_t pad = size.col - 1 - c - max_kind_len; // c += pad;
for (uint32_t i = 0; i < pad; i++) // std::string kind_name = item_kind_name(item.kind);
set(r, c + i, " ", fg, bg, 0); // for (size_t i = 0; i < kind_name.size(); i++)
c += pad; // set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg,
std::string kind_name = item_kind_name(item.kind); // 0);
for (size_t i = 0; i < kind_name.size(); i++) // set(r, size.col - 1, "│", border_fg, 0, 0);
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);
uint32_t bottom = size.row - 1; // for (uint32_t c = 1; c < size.col - 1; c++)
set(bottom, 0, "", border_fg, 0, 0); // set(bottom, c, "─", border_fg, 0, 0);
for (uint32_t c = 1; c < size.col - 1; c++) // set(bottom, size.col - 1, "┘", border_fg, 0, 0);
set(bottom, c, "", border_fg, 0, 0); // if (session->visible.size() > 8) {
set(bottom, size.col - 1, "", border_fg, 0, 0); // std::string info = std::to_string(start + 1) + "-" + std::to_string(end)
if (session->visible.size() > 8) { // +
std::string info = std::to_string(start + 1) + "-" + std::to_string(end) + // "/" + std::to_string(session->visible.size());
"/" + std::to_string(session->visible.size()); // for (size_t i = 0; i < info.size() && i < size.col - 2; i++)
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);
set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0); // }
} // }
} //
// void CompletionBox::render(Coord pos) {
void CompletionBox::render(Coord pos) { // if (hidden || session->visible.empty())
if (hidden || session->visible.empty()) // return;
return; // std::shared_lock lock(mtx);
std::shared_lock lock(mtx); // int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
int32_t start_row = (int32_t)pos.row - (int32_t)size.row; // if (start_row < 0)
if (start_row < 0) // start_row = pos.row + 1;
start_row = pos.row + 1; // int32_t start_col = pos.col;
int32_t start_col = pos.col; // Coord screen_size = get_size();
Coord screen_size = get_size(); // if (start_col + size.col > screen_size.col) {
if (start_col + size.col > screen_size.col) { // start_col = screen_size.col - size.col;
start_col = screen_size.col - size.col; // if (start_col < 0)
if (start_col < 0) // start_col = 0;
start_col = 0; // }
} // position = {(uint32_t)start_row, (uint32_t)start_col};
position = {(uint32_t)start_row, (uint32_t)start_col}; // for (uint32_t r = 0; r < size.row; r++)
for (uint32_t r = 0; r < size.row; r++) // for (uint32_t c = 0; c < size.col; c++)
for (uint32_t c = 0; c < size.col; c++) // update(start_row + r, start_col + c, cells[r * size.col + c].utf8,
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].fg, cells[r * size.col + c].bg, // cells[r * size.col + c].flags);
cells[r * size.col + c].flags); // if (session->items.size() > session->select &&
if (session->items.size() > session->select && // session->items[session->select].documentation &&
session->items[session->select].documentation && // *session->items[session->select].documentation != "" &&
*session->items[session->select].documentation != "" && // !session->hover_dirty) {
!session->hover_dirty) { // if (session->doc != session->select) {
if (session->doc != session->select) { // session->doc = session->select;
session->doc = session->select; // session->hover.clear();
session->hover.clear(); // session->hover.text = *session->items[session->select].documentation;
session->hover.text = *session->items[session->select].documentation; // session->hover.is_markup = true;
session->hover.is_markup = true; // session->hover_dirty = true;
session->hover_dirty = true; // } else {
} else { // if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) {
if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) { // session->hover.render({position.row + session->hover.size.row,
session->hover.render({position.row + session->hover.size.row, // position.col - session->hover.size.col});
position.col - session->hover.size.col}); // } else {
} else { // session->hover.render(
session->hover.render( // {position.row + session->hover.size.row, position.col +
{position.row + session->hover.size.row, position.col + size.col}); // size.col});
} // }
} // }
} // }
} // }

View File

@@ -1,162 +1,164 @@
#include "ui/diagnostics.h" // #include "ui/diagnostics.h"
//
void DiagnosticBox::clear() { // void DiagnosticBox::clear() {
warnings.clear(); // warnings.clear();
cells.clear(); // cells.clear();
size = {0, 0}; // size = {0, 0};
}; // };
//
void DiagnosticBox::render_first() { // void DiagnosticBox::render_first() {
if (warnings.empty()) // if (warnings.empty())
return; // return;
uint32_t longest_line = 8 + warnings[0].source.length(); // uint32_t longest_line = 8 + warnings[0].source.length();
for (auto &warn : warnings) { // for (auto &warn : warnings) {
uint32_t longest = 0; // uint32_t longest = 0;
uint32_t cur = 0; // uint32_t cur = 0;
for (char ch : warn.text_full) // for (char ch : warn.text_full)
if (ch == '\n') { // if (ch == '\n') {
longest = MAX(longest, cur); // longest = MAX(longest, cur);
cur = 0; // cur = 0;
} else { // } else {
if (ch == '\t') // if (ch == '\t')
cur += 3; // cur += 3;
++cur; // ++cur;
} // }
longest = MAX(longest, cur); // longest = MAX(longest, cur);
longest_line = MAX(longest_line, longest + 7); // longest_line = MAX(longest_line, longest + 7);
longest_line = MAX(longest_line, (uint32_t)warn.code.length() + 4); // longest_line = MAX(longest_line, (uint32_t)warn.code.length() + 4);
for (auto &see_also : warn.see_also) // for (auto &see_also : warn.see_also)
longest_line = MAX(longest_line, (uint32_t)see_also.length() + 4); // longest_line = MAX(longest_line, (uint32_t)see_also.length() + 4);
} // }
uint32_t content_width = MIN(longest_line, 150u); // uint32_t content_width = MIN(longest_line, 150u);
size.col = content_width + 2; // size.col = content_width + 2;
cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0}); // cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0});
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, // auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) { // uint32_t bg, uint8_t flags) {
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; // cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
}; // };
uint32_t base_bg = 0; // uint32_t base_bg = 0;
uint32_t border_fg = 0x82AAFF; // uint32_t border_fg = 0x82AAFF;
uint32_t r = 0; // uint32_t r = 0;
if (warnings[0].source != "") { // if (warnings[0].source != "") {
std::string src_txt = "Source: "; // std::string src_txt = "Source: ";
for (uint32_t i = 0; i < src_txt.length() && i < content_width; i++) // 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); // 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; // for (uint32_t i = 0; i < warnings[0].source.length() && i <
i++) // content_width;
set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0}, // i++)
0xffffff, base_bg, 0); // set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0},
r++; // 0xffffff, base_bg, 0);
} // r++;
int idx = 1; // }
for (auto &warn : warnings) { // int idx = 1;
char buf[4]; // for (auto &warn : warnings) {
std::snprintf(buf, sizeof(buf), "%2d", idx % 100); // char buf[4];
std::string line_txt = std::string(buf) + ". "; // std::snprintf(buf, sizeof(buf), "%2d", idx % 100);
for (uint32_t i = 0; i < line_txt.length(); i++) // std::string line_txt = std::string(buf) + ". ";
set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0); // for (uint32_t i = 0; i < line_txt.length(); i++)
if (r >= 23) // set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0);
break; // if (r >= 23)
const char *err_sym = ""; // break;
uint32_t c_sym = 0xAAAAAA; // const char *err_sym = "";
switch (warn.type) { // uint32_t c_sym = 0xAAAAAA;
case 1: // switch (warn.type) {
err_sym = ""; // case 1:
c_sym = 0xFF0000; // err_sym = "";
break; // c_sym = 0xFF0000;
case 2: // break;
err_sym = ""; // case 2:
c_sym = 0xFFFF00; // err_sym = "";
break; // c_sym = 0xFFFF00;
case 3: // break;
err_sym = ""; // case 3:
c_sym = 0xFF00FF; // err_sym = "";
break; // c_sym = 0xFF00FF;
case 4: // break;
err_sym = ""; // case 4:
c_sym = 0xAAAAAA; // err_sym = "";
break; // c_sym = 0xAAAAAA;
} // break;
std::string text = warn.text_full + " " + err_sym; // }
uint32_t i = 0; // std::string text = warn.text_full + " " + err_sym;
while (i < text.length() && r < 23) { // uint32_t i = 0;
uint32_t c = 4; // while (i < text.length() && r < 23) {
while (c < content_width && i < text.length()) { // uint32_t c = 4;
if (text[i] == '\n') { // while (c < content_width && i < text.length()) {
while (i < text.length() && text[i] == '\n') // if (text[i] == '\n') {
i++; // while (i < text.length() && text[i] == '\n')
break; // i++;
} // break;
uint32_t cluster_len = grapheme_next_character_break_utf8( // }
text.c_str() + i, text.length() - i); // uint32_t cluster_len = grapheme_next_character_break_utf8(
std::string cluster = text.substr(i, cluster_len); // text.c_str() + i, text.length() - i);
int width = display_width(cluster.c_str(), cluster_len); // std::string cluster = text.substr(i, cluster_len);
if (c + width > content_width) // int width = display_width(cluster.c_str(), cluster_len);
break; // if (c + width > content_width)
set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0); // break;
c += width; // set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0);
i += cluster_len; // c += width;
for (int w = 1; w < width; w++) // i += cluster_len;
set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0); // for (int w = 1; w < width; w++)
} // set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0);
r++; // }
} // r++;
if (r >= 23) // }
break; // if (r >= 23)
if (warn.code != "") { // break;
for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width; i++) // if (warn.code != "") {
set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0); // for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width;
r++; // i++)
} // set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0);
if (r >= 23) // r++;
break; // }
for (std::string &see_also : warn.see_also) { // if (r >= 23)
uint32_t fg = 0xB55EFF; // break;
uint8_t colon_count = 0; // for (std::string &see_also : warn.see_also) {
for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width; // uint32_t fg = 0xB55EFF;
i++) { // uint8_t colon_count = 0;
set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0); // for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width;
if (see_also[i] == ':') // i++) {
colon_count++; // set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0);
if (colon_count == 2) // if (see_also[i] == ':')
fg = 0xFFFFFF; // colon_count++;
} // if (colon_count == 2)
r++; // fg = 0xFFFFFF;
if (r >= 23) // }
break; // r++;
}; // if (r >= 23)
idx++; // break;
} // };
size.row = 2 + r; // idx++;
set(0, 0, "", border_fg, base_bg, 0); // }
for (uint32_t i = 1; i < size.col - 1; i++) // size.row = 2 + r;
set(0, i, "", border_fg, base_bg, 0); // set(0, 0, "┌", border_fg, base_bg, 0);
set(0, size.col - 1, "", border_fg, base_bg, 0); // for (uint32_t i = 1; i < size.col - 1; i++)
for (uint32_t r = 1; r < size.row - 1; r++) { // set(0, i, "─", border_fg, base_bg, 0);
set(r, 0, "", border_fg, base_bg, 0); // set(0, size.col - 1, "┐", border_fg, base_bg, 0);
set(r, 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(size.row - 1, 0, "", border_fg, base_bg, 0); // set(r, size.col - 1, "│", 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, 0, "└", border_fg, base_bg, 0);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0); // for (uint32_t i = 1; i < size.col - 1; i++)
cells.resize(size.col * size.row); // 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) // void DiagnosticBox::render(Coord pos) {
start_row = pos.row + 1; // int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
int32_t start_col = pos.col; // if (start_row < 0)
Coord screen_size = get_size(); // start_row = pos.row + 1;
if (start_col + size.col > screen_size.col) { // int32_t start_col = pos.col;
start_col = screen_size.col - size.col; // Coord screen_size = get_size();
if (start_col < 0) // if (start_col + size.col > screen_size.col) {
start_col = 0; // start_col = screen_size.col - size.col;
} // if (start_col < 0)
for (uint32_t r = 0; r < size.row; r++) // start_col = 0;
for (uint32_t c = 0; c < size.col; c++) // }
update(start_row + r, start_col + c, cells[r * size.col + c].utf8, // for (uint32_t r = 0; r < size.row; r++)
cells[r * size.col + c].fg, cells[r * size.col + c].bg, // for (uint32_t c = 0; c < size.col; c++)
cells[r * size.col + c].flags); // 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);
// }

49
src/windows/renderer.cc Normal file
View File

@@ -0,0 +1,49 @@
#include "io/sysio.h"
#include "main.h"
#include "windows/decl.h"
namespace layout {
TileBase root_tile;
Window *focused_window;
std::vector<std::unique_ptr<Popup>> popups;
std::vector<std::unique_ptr<TileBase>> floating_tiles;
} // namespace layout
void render() {
ui::bar.render(io::new_screen);
layout::root_tile.render(io::new_screen);
for (auto &tile : layout::floating_tiles) {
if (tile->hidden())
continue;
tile->render(io::new_screen);
}
for (auto &popup : layout::popups) {
if (popup->hidden)
continue;
popup->render(io::new_screen, popup->size, popup->pos);
}
}
void handle_click(KeyEvent event) {
for (auto &popup : layout::popups) {
if (!popup->hidden &&
inside({event.mouse_y, event.mouse_x}, popup->pos, popup->size)) {
event.mouse_x -= popup->pos.col;
event.mouse_y -= popup->pos.row;
popup->handle_click(event, popup->size);
return;
}
}
for (auto &tile : layout::floating_tiles) {
if (!tile->hidden() &&
inside({event.mouse_y, event.mouse_x}, tile->pos, tile->size)) {
event.mouse_x -= tile->pos.col;
event.mouse_y -= tile->pos.row;
tile->handle_click(event);
return;
}
}
if (inside({event.mouse_y, event.mouse_x}, layout::root_tile.pos,
layout::root_tile.size))
layout::root_tile.handle_click(event);
}