diff --git a/README.md b/README.md index a9596ba..0b54a1b 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ crib ./filename.ext ``` *If `filename.ext` does not exist, it will be created*
+*memory usage is average case 10MB + 2x size of file*
## Keybindings diff --git a/TODO.md b/TODO.md index 237b61b..ef18d8d 100644 --- a/TODO.md +++ b/TODO.md @@ -2,19 +2,12 @@ Copyright 2025 Syed Daanish # TODO -# memory usage for debug build (release build will be smaller by about 25%) -``` -8K -> 13.2M -128K -> 13.2M (expected worst case 16.6M) -128M -> 412.0M (expected worst case 2.3G) -``` - ## Next few super long boring things to do:: * redo lsp threads such that no mutex needed for any rope stuff (done) - Also make the classes own the methods in lsp (done) - This will mean that parsers/renderers and keystrokes will not need to be individually locked (done) - 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 fast + - 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) * Add a recursive tiling class for windows (done) * Handled by a single renderer that calls and renders each window (done) @@ -44,6 +37,7 @@ Copyright 2025 Syed Daanish * 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 +* 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 * 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) diff --git a/include/editor/editor.h b/include/editor/editor.h index 7a19e36..f652f32 100644 --- a/include/editor/editor.h +++ b/include/editor/editor.h @@ -2,6 +2,7 @@ #define EDITOR_H #include "editor/indents.h" +#include "extentions/hover.h" #include "io/knot.h" #include "io/sysio.h" #include "syntax/extras.h" @@ -37,7 +38,7 @@ struct Editor : Window { VAI ai = {}; std::shared_mutex lsp_mtx; std::atomic lsp = nullptr; - bool hover_active = false; + HoverBox *hover_popup = init_hover(); bool diagnostics_active = false; std::atomic lsp_version = 1; IndentationEngine indents = {}; diff --git a/include/extentions/hover.h b/include/extentions/hover.h index a591479..85ac9a5 100644 --- a/include/extentions/hover.h +++ b/include/extentions/hover.h @@ -6,9 +6,7 @@ #include "utils/utils.h" #include "windows/decl.h" -TileRoot *init_hover(); - -struct HoverBox : Window { +struct HoverBox : Popup { std::string text; std::atomic is_markup; uint32_t scroll_; @@ -24,8 +22,16 @@ struct HoverBox : Window { } void scroll(int32_t number); void render(std::vector &buffer, Coord size, Coord pos) override; - void handle_click(KeyEvent, Coord) override { this->hidden = true; }; + 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 diff --git a/include/io/sysio.h b/include/io/sysio.h index b52bc62..4368cf9 100644 --- a/include/io/sysio.h +++ b/include/io/sysio.h @@ -96,14 +96,19 @@ inline bool is_empty_cell(const ScreenCell &c) { return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b"; } +namespace io { extern std::vector new_screen; +extern uint32_t rows, cols; +extern bool show_cursor; +extern std::vector old_screen; +extern termios orig_termios; +} // namespace io Coord start_screen(); void end_screen(); void set_cursor(uint8_t row, uint8_t col, uint32_t type, bool show_cursor_param); void io_render(); -Coord get_size(); KeyEvent read_key(); diff --git a/include/main.h b/include/main.h index 0028266..739766e 100644 --- a/include/main.h +++ b/include/main.h @@ -16,7 +16,6 @@ extern fs::path pwd; namespace ui { extern Bar bar; -extern TileRoot *hover_popup; } // namespace ui #endif diff --git a/include/syntax/decl.h b/include/syntax/decl.h index a229d9d..71a8f97 100644 --- a/include/syntax/decl.h +++ b/include/syntax/decl.h @@ -6,6 +6,8 @@ #include "pch.h" #include "syntax/trie.h" +#define MAX_LINES_LOOKBEHIND 80 + struct Highlight { uint32_t fg{0xFFFFFF}; uint32_t bg{0x000000}; @@ -35,10 +37,23 @@ struct Token { TokenKind type; }; +struct StateBase { + virtual ~StateBase() = default; + virtual std::unique_ptr clone() const = 0; +}; + +struct CustomState : StateBase { + std::string value; + explicit CustomState(std::string v) : value(std::move(v)) {} + std::unique_ptr clone() const override { + return std::make_unique(value); + } +}; + struct LineData { - std::shared_ptr in_state{nullptr}; + std::unique_ptr out_state; std::vector tokens; - std::shared_ptr out_state{nullptr}; + std::unique_ptr in_state; }; #endif diff --git a/include/syntax/langs.h b/include/syntax/langs.h index 384628a..3e0acbc 100644 --- a/include/syntax/langs.h +++ b/include/syntax/langs.h @@ -4,29 +4,19 @@ #include "syntax/decl.h" #define DEF_LANG(name) \ - std::shared_ptr name##_parse( \ - std::vector *tokens, std::shared_ptr in_state, \ - const char *text, uint32_t len, uint32_t line_num); \ - bool name##_state_match(std::shared_ptr state_1, \ - std::shared_ptr state_2); + std::unique_ptr name##_parse( \ + std::vector *tokens, StateBase *in_state, const char *text, \ + uint32_t len, uint32_t line_num); \ + bool name##_state_match(StateBase *state_1, StateBase *state_2); #define LANG_A(name) \ { \ #name, { name##_parse, name##_state_match } \ } -template -inline std::shared_ptr ensure_state(std::shared_ptr state) { - using U = typename T::full_state_type; - if (!state) - state = std::make_shared(); - if (!state.unique()) - state = std::make_shared(*state); - if (!state->full_state) - state->full_state = std::make_shared(); - else if (!state->full_state.unique()) - state->full_state = std::make_shared(*state->full_state); - return state; +template +std::unique_ptr static_unique_ptr_cast(std::unique_ptr &&p) { + return std::unique_ptr(static_cast(p.release())); } DEF_LANG(ruby); @@ -34,11 +24,10 @@ DEF_LANG(bash); inline static const std::unordered_map< std::string, - std::tuple (*)( - std::vector *tokens, std::shared_ptr in_state, + std::tuple (*)( + std::vector *tokens, StateBase *in_state, const char *text, uint32_t len, uint32_t line_num), - bool (*)(std::shared_ptr state_1, - std::shared_ptr state_2)>> + bool (*)(StateBase *state_1, StateBase *state_2)>> parsers = { LANG_A(ruby), LANG_A(bash), diff --git a/include/syntax/line_map.h b/include/syntax/line_map.h new file mode 100644 index 0000000..4e4f15b --- /dev/null +++ b/include/syntax/line_map.h @@ -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()(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 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(); + } + 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, LineKeyHash> lines; + + std::vector edit_log; + + uint32_t current_version; + + std::optional 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 diff --git a/include/syntax/line_tree.h b/include/syntax/line_tree.h deleted file mode 100644 index dba264c..0000000 --- a/include/syntax/line_tree.h +++ /dev/null @@ -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 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 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 diff --git a/include/syntax/parser.h b/include/syntax/parser.h index daf7ee9..ea1b254 100644 --- a/include/syntax/parser.h +++ b/include/syntax/parser.h @@ -3,24 +3,22 @@ #include "ruby/decl.h" #include "syntax/decl.h" -#include "syntax/line_tree.h" +#include "syntax/line_map.h" struct Parser { struct Editor *editor = nullptr; std::string lang; - std::shared_ptr (*parse_func)(std::vector *tokens, - std::shared_ptr in_state, - const char *text, uint32_t len, - uint32_t line_num); - bool (*state_match_func)(std::shared_ptr state_1, - std::shared_ptr state_2); + std::unique_ptr (*parse_func)(std::vector *tokens, + StateBase *in_state, + const char *text, uint32_t len, + uint32_t line_num); + bool (*state_match_func)(StateBase *state_1, StateBase *state_2); mrb_value parser_block = mrb_nil_value(); mrb_value match_block = mrb_nil_value(); bool is_custom{false}; std::atomic scroll_max{0}; std::atomic scroll_dirty{false}; - LineTree line_tree; - UniqueQueue dirty_lines; + LineMap line_map = LineMap(); 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); diff --git a/include/utils/utils.h b/include/utils/utils.h index c29aa14..bb7e574 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -92,6 +92,11 @@ struct Coord { 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 { size_t start; size_t end; diff --git a/include/windows/decl.h b/include/windows/decl.h index 26fd310..65fa22c 100644 --- a/include/windows/decl.h +++ b/include/windows/decl.h @@ -22,6 +22,12 @@ struct Window : Tile { virtual std::array bar_info() { return {}; }; }; +struct Popup : Window { + Coord pos; + Coord size; + virtual ~Popup() = default; +}; + struct TileBlock : Tile { bool vertical; std::vector> tiles; @@ -82,11 +88,12 @@ struct TileBlock : Tile { } }; -struct TileRoot { +struct TileBase { std::unique_ptr tile; Coord pos; Coord size; + inline bool hidden() { return this->tile->hidden; } void render(std::vector &buffer) { if (this->tile->hidden) return; @@ -97,21 +104,23 @@ struct TileRoot { event.mouse_y -= this->pos.row; this->tile->handle_click(event, size); } - bool inside(uint32_t x, uint32_t y) { - return x >= pos.col && x < pos.col + size.col && y >= pos.row && - y < pos.row + size.row; - } }; -extern TileRoot root_tile; -extern std::vector> popups; +namespace layout { +extern TileBase root_tile; extern Window *focused_window; +extern std::vector> popups; +extern std::vector> floating_tiles; +} // namespace layout -inline void close_popup(TileRoot *handle) { - auto it = std::find_if(popups.begin(), popups.end(), - [handle](const auto &p) { return p.get() == handle; }); - if (it != popups.end()) - popups.erase(it); +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(); diff --git a/src/editor/events.cc b/src/editor/events.cc index c6fe274..e7c0bc1 100644 --- a/src/editor/events.cc +++ b/src/editor/events.cc @@ -6,8 +6,8 @@ void Editor::handle_event(KeyEvent event) { uint8_t old_mode = mode; - if (this->hover_active) - this->hover_active = false; + if (!this->hover_popup->hidden) + this->hover_popup->hidden = true; if (event.key_type == KEY_SPECIAL) { switch (event.special_modifier) { case 0: @@ -67,12 +67,12 @@ void Editor::handle_event(KeyEvent event) { this->select_all(); break; case CTRL('h'): - static_cast(ui::hover_popup->tile.get())->scroll(-1); - this->hover_active = true; + this->hover_popup->scroll(-1); + this->hover_popup->hidden = false; break; case CTRL('l'): - static_cast(ui::hover_popup->tile.get())->scroll(1); - this->hover_active = true; + this->hover_popup->scroll(1); + this->hover_popup->hidden = false; break; case 'h': this->fetch_lsp_hover(); diff --git a/src/editor/helpers.cc b/src/editor/helpers.cc index f4adf3c..9398170 100644 --- a/src/editor/helpers.cc +++ b/src/editor/helpers.cc @@ -410,11 +410,10 @@ void Editor::fetch_lsp_hover() { hover_text += contents.get(); } if (!hover_text.empty()) { - auto hover_box = static_cast(ui::hover_popup->tile.get()); - hover_box->clear(); - hover_box->text = clean_text(hover_text); - hover_box->is_markup = is_markup; - message.editor->hover_active = true; + message.editor->hover_popup->clear(); + message.editor->hover_popup->text = clean_text(hover_text); + message.editor->hover_popup->is_markup = is_markup; + message.editor->hover_popup->hidden = false; } } }; @@ -423,12 +422,14 @@ void Editor::fetch_lsp_hover() { } void Editor::handle_click(KeyEvent event, Coord size) { - focused_window = this; + layout::focused_window = this; this->size = size; static std::chrono::steady_clock::time_point last_click_time = std::chrono::steady_clock::now(); static uint32_t click_count = 0; 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) { auto now = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast( diff --git a/src/editor/renderer.cc b/src/editor/renderer.cc index 6d18d5d..4e54d7c 100644 --- a/src/editor/renderer.cc +++ b/src/editor/renderer.cc @@ -31,7 +31,7 @@ void Editor::render(std::vector &buffer, Coord size, Coord pos) { return (int)token.type; return 0; }; - Coord screen = get_size(); + 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) { @@ -115,12 +115,8 @@ void Editor::render(std::vector &buffer, Coord size, Coord pos) { while (rendered_rows < this->size.row) { uint32_t line_len; char *line = next_line(it, &line_len); - if (this->parser) { - if (line_data) - line_data = this->parser->line_tree.next(); - else - line_data = this->parser->line_tree.start_iter(line_index); - } + if (this->parser) + line_data = this->parser->line_map.at(line_index); if (!line) break; if (line_len > 0 && line[line_len - 1] == '\n') @@ -477,8 +473,8 @@ void Editor::render(std::vector &buffer, Coord size, Coord pos) { // this->hover.render(cursor); // else if (this->diagnostics_active) // this->diagnostics.render(cursor); - if (this->hover_active) - ui::hover_popup->pos = cursor; + if (!this->hover_popup->hidden) + this->hover_popup->pos = cursor; } free(it->buffer); free(it); diff --git a/src/extentions/hover.cc b/src/extentions/hover.cc index 8e49f7a..01d57c0 100644 --- a/src/extentions/hover.cc +++ b/src/extentions/hover.cc @@ -1,15 +1,16 @@ #include "extentions/hover.h" +#include "io/sysio.h" #include "syntax/decl.h" #include "windows/decl.h" -TileRoot *init_hover() { - auto root = std::make_unique(); - root->tile = std::make_unique(); - root->pos = {0, 0}; - root->size = {1, 1}; - root->tile->hidden = true; - popups.push_back(std::move(root)); - return popups.back().get(); +HoverBox *init_hover() { + auto hover = std::make_unique(); + hover->pos = {0, 0}; + hover->size = {1, 1}; + hover->hidden = true; + HoverBox *ptr = hover.get(); + layout::popups.push_back(std::move(hover)); + return ptr; } void HoverBox::scroll(int32_t number) { @@ -25,20 +26,25 @@ void HoverBox::scroll(int32_t number) { scroll_dirty = true; } -void HoverBox::render(std::vector &buffer, Coord size, Coord pos) { +void HoverBox::render(std::vector &buffer, Coord n_size, + Coord n_pos) { + pos = n_pos; + size = n_size; if (scroll_dirty) { // TODO: call syntax highlighter here } - // int32_t start_row = (int32_t)pos.row - (int32_t)size.row; - // if (start_row < 0) - // start_row = pos.row + 1; - // int32_t start_col = pos.col; // pos here is the cursor pos - Coord screen_size = get_size(); - // if (start_col + size.col > screen_size.col) { - // start_col = screen_size.col - size.col; - // if (start_col < 0) - // start_col = 0; - // } + 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 current_width = 0; for (size_t j = 0; j < text.length(); j++) { @@ -69,6 +75,8 @@ void HoverBox::render(std::vector &buffer, Coord size, Coord pos) { 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; @@ -103,6 +111,8 @@ void HoverBox::render(std::vector &buffer, Coord size, Coord pos) { for (int w = 1; w < width; w++) 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++; } if (scroll_dirty) diff --git a/src/io/renderer.cc b/src/io/renderer.cc index 92b3010..3718c62 100644 --- a/src/io/renderer.cc +++ b/src/io/renderer.cc @@ -1,15 +1,19 @@ #include "io/sysio.h" +namespace io { std::vector new_screen; -static uint32_t rows, cols; -static bool show_cursor = 0; -static std::vector old_screen; -static termios orig_termios; +uint32_t rows, cols; +bool show_cursor = 0; +std::vector old_screen; +termios orig_termios; +} // namespace io + +using namespace io; void disable_raw_mode() { std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l"; 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"); exit(EXIT_FAILURE); } @@ -45,8 +49,6 @@ Coord start_screen() { void end_screen() { disable_raw_mode(); } -Coord get_size() { return {rows, cols}; } - void io_render() { static bool first_render = true; uint32_t current_fg = 0; diff --git a/src/main.cc b/src/main.cc index 0893134..18b6c0b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,5 @@ #include "main.h" #include "editor/editor.h" -#include "extentions/hover.h" #include "io/sysio.h" #include "lsp/lsp.h" #include "ruby/decl.h" @@ -15,7 +14,6 @@ std::atomic mode = NORMAL; namespace ui { Bar bar; -TileRoot *hover_popup = nullptr; } // namespace ui void background_lsp() { @@ -33,13 +31,14 @@ int main(int argc, char *argv[]) { const char *filename = (argc > 1) ? argv[1] : ""; uint8_t eol = read_line_endings(); - ui::hover_popup = init_hover(); + layout::root_tile.pos = {0, 0}; + layout::root_tile.size = {screen.row - 2, screen.col - 1}; + auto editor = std::make_unique(filename, eol); + layout::focused_window = editor.get(); + auto block = std::make_unique(); + block->tiles.push_back(std::move(editor)); + layout::root_tile.tile = std::move(block); - root_tile.size = {screen.row - 2, screen.col - 1}; - root_tile.pos = {0, 0}; - - root_tile.tile = std::make_unique(filename, eol); - focused_window = static_cast(root_tile.tile.get()); ui::bar.init(screen); std::thread lsp_thread(background_lsp); @@ -57,7 +56,7 @@ int main(int argc, char *argv[]) { if (event.key_type == KEY_MOUSE) { handle_click(event); } else { - focused_window->handle_event(event); + layout::focused_window->handle_event(event); } } else { ui::bar.handle_event(event); @@ -68,7 +67,7 @@ int main(int argc, char *argv[]) { } for (auto &lsp_inst : lsp::active_lsps) lsp_inst.second->callbacks(); - focused_window->work(); + layout::focused_window->work(); throttle(4ms, render); throttle(4ms, io_render); } diff --git a/src/syntax/bash.cc b/src/syntax/bash.cc index baf8a75..b91b2bc 100644 --- a/src/syntax/bash.cc +++ b/src/syntax/bash.cc @@ -10,7 +10,7 @@ struct BashFullState { bool line_cont = false; struct Lit { - std::string delim = ""; // Only 1 wide for strings + std::string delim = ""; bool allow_interp = false; bool operator==(const BashFullState::Lit &other) const { @@ -24,7 +24,7 @@ struct BashFullState { } }; -struct BashState { +struct BashState : StateBase { using full_state_type = BashFullState; int interp_level = 0; @@ -37,26 +37,32 @@ struct BashState { ((full_state && other.full_state && *full_state == *other.full_state)); } + + std::unique_ptr clone() const override { + return std::make_unique(*this); + } }; -bool bash_state_match(std::shared_ptr state_1, - std::shared_ptr state_2) { +bool bash_state_match(StateBase *state_1, StateBase *state_2) { if (!state_1 || !state_2) return false; - return *std::static_pointer_cast(state_1) == - *std::static_pointer_cast(state_2); + return *static_cast(state_1) == + *static_cast(state_2); } -std::shared_ptr bash_parse(std::vector *tokens, - std::shared_ptr in_state, - const char *text, uint32_t len, - uint32_t line_num) { +std::unique_ptr bash_parse(std::vector *tokens, + StateBase *in_state, const char *text, + uint32_t len, uint32_t line_num) { static bool keywords_trie_init = false; if (!keywords_trie_init) { keywords_trie_init = true; } tokens->clear(); - auto state = ensure_state(std::static_pointer_cast(in_state)); + std::unique_ptr state; + if (in_state) + state = static_unique_ptr_cast(in_state->clone()); + else + state = std::make_unique(); uint32_t i = 0; while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || text[len - 1] == '\t' || text[len - 1] == ' ')) diff --git a/src/syntax/parser.cc b/src/syntax/parser.cc index 454392e..12b3193 100644 --- a/src/syntax/parser.cc +++ b/src/syntax/parser.cc @@ -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); } } - 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, uint32_t inserted_rows) { int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows; - if (delta < 0) - line_tree.erase(start_line, (uint32_t)(-delta)); - else if (delta > 0) - line_tree.insert(start_line, (uint32_t)delta); + line_map.apply_edit(start_line, delta); uint32_t span = MAX(removed_rows, inserted_rows); uint32_t begin = (start_line > 0) ? start_line - 1 : 0; uint32_t end = start_line + span; 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() { @@ -48,32 +46,30 @@ void Parser::work() { return; std::vector batch; uint32_t c_line; - while (dirty_lines.pop(c_line)) - batch.push_back(c_line); - uint32_t i = MAX(0, (int64_t)scroll_max - 60); - LineData *l_iter = line_tree.start_iter(i); - while (l_iter && i < scroll_max + 10) { - if (!l_iter->out_state) + uint32_t line_count = editor->root->line_count + 1; + for (uint32_t i = MAX(0, (int64_t)scroll_max - MAX_LINES_LOOKBEHIND); + i <= scroll_max + 10 && i < line_count; ++i) { + auto l_opt = line_map.at(i); + if (!l_opt || (l_opt && !l_opt->out_state)) batch.push_back(i); i++; - l_iter = line_tree.next(); } - line_tree.end_iter(); for (uint32_t c_line : batch) { if (!running.load(std::memory_order_relaxed)) 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; - if (c_line < min_line || c_line > max_line) { - dirty_lines.push(c_line); + if (c_line < min_line || c_line > max_line) continue; - } uint32_t scroll_snapshot = scroll_max; - std::shared_ptr prev_state = nullptr; - uint32_t line_count; - line_count = line_tree.count(); - if (c_line > 0 && c_line < line_count) - prev_state = line_tree.at(c_line - 1)->out_state; + std::unique_ptr prev_state = nullptr; + if (c_line > 0 && c_line < line_count) { + auto lm = line_map.at(c_line - 1); + if (lm && lm->out_state) + prev_state = lm->out_state ? lm->out_state->clone() : nullptr; + } LineIterator *it = begin_l_iter(editor->root, c_line); if (!it) continue; @@ -81,60 +77,57 @@ void Parser::work() { while (cur_line < line_count) { if (!running.load(std::memory_order_relaxed)) break; - 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); + if (scroll_snapshot != scroll_max) break; - } - if (cur_line < min_line || cur_line > max_line) { - dirty_lines.push(cur_line); + if (cur_line < min_line || cur_line > max_line) break; - } uint32_t len; char *line = next_line(it, &len); if (!line) break; - LineData *line_data = line_tree.at(cur_line); - if (!line_data) { - cur_line++; - continue; - } - std::shared_ptr new_state; + LineData *line_data = line_map.create_at(cur_line); + std::unique_ptr new_state; if (is_custom) { - std::string state = ""; - if (prev_state) - state = std::static_pointer_cast(prev_state)->c_str(); - std::string out_state = parse_custom(&line_data->tokens, parser_block, - line, len, state, cur_line); - new_state = std::make_shared(out_state); + std::string prev_value = ""; + if (prev_state) { + CustomState *prev_custom = + static_cast(prev_state.get()); + prev_value = prev_custom->value; + } + std::string out_value = parse_custom(&line_data->tokens, parser_block, + line, len, prev_value, cur_line); + new_state = std::make_unique(out_value); } else { - new_state = - parse_func(&line_data->tokens, prev_state, line, len, cur_line); + new_state = parse_func(&line_data->tokens, prev_state.get(), line, len, + cur_line); } - line_data->in_state = prev_state; - line_data->out_state = new_state; + line_data->in_state = std::move(prev_state); + line_data->out_state = std::move(new_state); bool done = false; 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 (is_custom) { - std::string a = - prev_state - ? std::static_pointer_cast(new_state)->c_str() - : ""; - std::string b = next_line_data->in_state - ? std::static_pointer_cast( - next_line_data->in_state) - ->c_str() - : ""; + std::string a = ""; + if (new_state) { + CustomState *cs = static_cast(new_state.get()); + a = cs->value; + } + std::string b = ""; + if (next_line_data->in_state) { + CustomState *cs = + static_cast(next_line_data->in_state.get()); + b = cs->value; + } done = custom_compare(match_block, a, b); } 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++; if (done) break; diff --git a/src/syntax/ruby.cc b/src/syntax/ruby.cc index 5b442f2..15b81ee 100644 --- a/src/syntax/ruby.cc +++ b/src/syntax/ruby.cc @@ -241,20 +241,22 @@ struct RubyFullState { } }; -struct RubyState { +struct RubyState : StateBase { using full_state_type = RubyFullState; int interp_level = 0; - std::stack> interp_stack; - std::shared_ptr full_state; + std::stack interp_stack; + RubyFullState full_state; std::deque heredocs; bool operator==(const RubyState &other) const { return interp_level == other.interp_level && 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 clone() const override { + return std::make_unique(*this); } }; @@ -278,18 +280,16 @@ inline static uint32_t get_next_word(const char *text, uint32_t i, return width; } -bool ruby_state_match(std::shared_ptr state_1, - std::shared_ptr state_2) { +bool ruby_state_match(StateBase *state_1, StateBase *state_2) { if (!state_1 || !state_2) return false; - return *std::static_pointer_cast(state_1) == - *std::static_pointer_cast(state_2); + return *static_cast(state_1) == + *static_cast(state_2); } -std::shared_ptr ruby_parse(std::vector *tokens, - std::shared_ptr in_state, - const char *text, uint32_t len, - uint32_t line_num) { +std::unique_ptr ruby_parse(std::vector *tokens, + StateBase *in_state, const char *text, + uint32_t len, uint32_t line_num) { static bool keywords_trie_init = false; static Trie base_keywords_trie; static Trie expecting_keywords_trie; @@ -313,7 +313,11 @@ std::shared_ptr ruby_parse(std::vector *tokens, keywords_trie_init = true; } tokens->clear(); - auto state = ensure_state(std::static_pointer_cast(in_state)); + std::unique_ptr state; + if (in_state) + state = static_unique_ptr_cast(in_state->clone()); + else + state = std::make_unique(); uint32_t i = 0; while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || text[len - 1] == '\t' || text[len - 1] == ' ')) @@ -322,18 +326,18 @@ std::shared_ptr ruby_parse(std::vector *tokens, return state; bool heredoc_first = false; while (i < len) { - if (state->full_state->in_state == RubyFullState::END) + if (state->full_state.in_state == RubyFullState::END) 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}); if (i == 0 && len == 4 && text[i] == '=' && text[i + 1] == 'e' && text[i + 2] == 'n' && text[i + 3] == 'd') { - state->full_state->in_state = RubyFullState::NONE; + state->full_state.in_state = RubyFullState::NONE; } return state; } if (!heredoc_first && - state->full_state->in_state == RubyFullState::HEREDOC) { + state->full_state.in_state == RubyFullState::HEREDOC) { if (i == 0) { uint32_t start = 0; if (state->heredocs.front().allow_indentation) @@ -344,7 +348,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, state->heredocs.front().delim.length())) { state->heredocs.pop_front(); 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}); return state; } @@ -421,7 +425,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); i += 2; state->interp_stack.push(state->full_state); - state->full_state = std::make_shared(); + state->full_state = RubyFullState(); state->interp_level = 1; break; } @@ -432,7 +436,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, continue; } } - if (state->full_state->in_state == RubyFullState::STRING) { + if (state->full_state.in_state == RubyFullState::STRING) { uint32_t start = i; while (i < len) { if (text[i] == '\\') { @@ -496,36 +500,36 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, i, TokenKind::K_ESCAPE}); 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] == '{') { tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); i += 2; state->interp_stack.push(state->full_state); - state->full_state = std::make_shared(); + state->full_state = RubyFullState(); state->interp_level = 1; break; } - if (text[i] == state->full_state->lit.delim_start && - state->full_state->lit.delim_start != - state->full_state->lit.delim_end) { - state->full_state->lit.brace_level++; + if (text[i] == state->full_state.lit.delim_start && + state->full_state.lit.delim_start != + state->full_state.lit.delim_end) { + state->full_state.lit.brace_level++; } - if (text[i] == state->full_state->lit.delim_end) { - if (state->full_state->lit.delim_start == - state->full_state->lit.delim_end) { + if (text[i] == state->full_state.lit.delim_end) { + if (state->full_state.lit.delim_start == + state->full_state.lit.delim_end) { i++; tokens->push_back({start, i, TokenKind::K_STRING}); - state->full_state->in_state = RubyFullState::NONE; - state->full_state->expecting_expr = false; + state->full_state.in_state = RubyFullState::NONE; + state->full_state.expecting_expr = false; break; } else { - state->full_state->lit.brace_level--; - if (state->full_state->lit.brace_level == 0) { + state->full_state.lit.brace_level--; + if (state->full_state.lit.brace_level == 0) { i++; tokens->push_back({start, i, TokenKind::K_STRING}); - state->full_state->in_state = RubyFullState::NONE; - state->full_state->expecting_expr = false; + state->full_state.in_state = RubyFullState::NONE; + state->full_state.expecting_expr = false; break; } } @@ -536,7 +540,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, len, TokenKind::K_STRING}); continue; } - if (state->full_state->in_state == RubyFullState::REGEXP) { + if (state->full_state.in_state == RubyFullState::REGEXP) { uint32_t start = i; while (i < len) { if (text[i] == '\\') { @@ -605,30 +609,30 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); i += 2; state->interp_stack.push(state->full_state); - state->full_state = std::make_shared(); + state->full_state = RubyFullState(); state->interp_level = 1; break; } - if (text[i] == state->full_state->lit.delim_start && - state->full_state->lit.delim_start != - state->full_state->lit.delim_end) { - state->full_state->lit.brace_level++; + if (text[i] == state->full_state.lit.delim_start && + state->full_state.lit.delim_start != + state->full_state.lit.delim_end) { + state->full_state.lit.brace_level++; } - if (text[i] == state->full_state->lit.delim_end) { - if (state->full_state->lit.delim_start == - state->full_state->lit.delim_end) { + if (text[i] == state->full_state.lit.delim_end) { + if (state->full_state.lit.delim_start == + state->full_state.lit.delim_end) { i += 1; tokens->push_back({start, i, TokenKind::K_REGEXP}); - state->full_state->in_state = RubyFullState::NONE; - state->full_state->expecting_expr = false; + state->full_state.in_state = RubyFullState::NONE; + state->full_state.expecting_expr = false; break; } else { - state->full_state->lit.brace_level--; - if (state->full_state->lit.brace_level == 0) { + state->full_state.lit.brace_level--; + if (state->full_state.lit.brace_level == 0) { i += 1; tokens->push_back({start, i, TokenKind::K_REGEXP}); - state->full_state->in_state = RubyFullState::NONE; - state->full_state->expecting_expr = false; + state->full_state.in_state = RubyFullState::NONE; + state->full_state.expecting_expr = false; break; } } @@ -642,8 +646,8 @@ std::shared_ptr ruby_parse(std::vector *tokens, if (i == 0 && len == 6) { if (text[i] == '=' && text[i + 1] == 'b' && text[i + 2] == 'e' && text[i + 3] == 'g' && text[i + 4] == 'i' && text[i + 5] == 'n') { - state->full_state->in_state = RubyFullState::COMMENT; - state->full_state->expecting_expr = false; + state->full_state.in_state = RubyFullState::COMMENT; + state->full_state.expecting_expr = false; tokens->push_back({0, len, TokenKind::K_COMMENT}); return state; } @@ -653,8 +657,8 @@ std::shared_ptr ruby_parse(std::vector *tokens, text[i + 3] == 'N' && text[i + 4] == 'D' && text[i + 5] == '_' && text[i + 6] == '_') { tokens->clear(); - state->full_state->in_state = RubyFullState::END; - state->full_state->expecting_expr = false; + state->full_state.in_state = RubyFullState::END; + state->full_state.expecting_expr = false; return state; } } @@ -684,33 +688,33 @@ std::shared_ptr ruby_parse(std::vector *tokens, delim += text[j++]; } } - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; if (!delim.empty()) { tokens->push_back({s, j, TokenKind::K_ANNOTATION}); state->heredocs.push_back({delim, interpolation, indented}); - state->full_state->in_state = RubyFullState::HEREDOC; + state->full_state.in_state = RubyFullState::HEREDOC; heredoc_first = true; } i = j; 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}); - state->full_state->in_state = RubyFullState::REGEXP; - state->full_state->expecting_expr = false; - state->full_state->lit.delim_start = '/'; - state->full_state->lit.delim_end = '/'; - state->full_state->lit.allow_interp = true; + state->full_state.in_state = RubyFullState::REGEXP; + state->full_state.expecting_expr = false; + state->full_state.lit.delim_start = '/'; + state->full_state.lit.delim_end = '/'; + state->full_state.lit.allow_interp = true; i++; continue; } else if (text[i] == '#') { 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}); return state; } tokens->push_back({i, len, TokenKind::K_COMMENT}); - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; return state; } else if (text[i] == '.') { uint32_t start = i; @@ -722,15 +726,15 @@ std::shared_ptr ruby_parse(std::vector *tokens, } } tokens->push_back({start, i, TokenKind::K_OPERATOR}); - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; continue; } else if (text[i] == ':') { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; uint32_t start = i; i++; if (i >= len) { tokens->push_back({start, i, TokenKind::K_OPERATOR}); - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; continue; } if (text[i] == ':') { @@ -739,7 +743,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, } if (text[i] == '\'' || text[i] == '"') { tokens->push_back({start, i, TokenKind::K_LABEL}); - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; continue; } if (text[i] == '$' || text[i] == '@') { @@ -767,7 +771,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, i, TokenKind::K_OPERATOR}); continue; } else if (text[i] == '@') { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; uint32_t start = i; i++; if (i >= len) @@ -783,7 +787,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, i, TokenKind::K_VARIABLEINSTANCE}); continue; } else if (text[i] == '$') { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; uint32_t start = i; i++; if (i >= len) @@ -806,7 +810,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, i, TokenKind::K_VARIABLEGLOBAL}); continue; } else if (text[i] == '?') { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; uint32_t start = i; i++; if (i < len && text[i] == '\\') { @@ -851,95 +855,95 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, i, TokenKind::K_CHAR}); continue; } else { - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; tokens->push_back({start, i, TokenKind::K_OPERATOR}); continue; } } else if (text[i] == '{') { - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; 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}); state->interp_level++; - state->full_state->brace_level++; + state->full_state.brace_level++; i++; continue; } else if (text[i] == '}') { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; state->interp_level--; if (state->interp_level == 0 && !state->interp_stack.empty()) { state->full_state = state->interp_stack.top(); state->interp_stack.pop(); tokens->push_back({i, i + 1, TokenKind::K_INTERPOLATION}); } else { - state->full_state->brace_level--; + state->full_state.brace_level--; 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}); } i++; continue; } else if (text[i] == '(') { - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; 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}); - state->full_state->brace_level++; + state->full_state.brace_level++; i++; continue; } else if (text[i] == ')') { - state->full_state->expecting_expr = false; - state->full_state->brace_level--; + state->full_state.expecting_expr = false; + state->full_state.brace_level--; 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}); i++; continue; } else if (text[i] == '[') { - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; 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}); - state->full_state->brace_level++; + state->full_state.brace_level++; i++; continue; } else if (text[i] == ']') { - state->full_state->expecting_expr = false; - state->full_state->brace_level--; + state->full_state.expecting_expr = false; + state->full_state.brace_level--; 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}); i++; continue; } 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}); - state->full_state->in_state = RubyFullState::STRING; - state->full_state->lit.delim_start = '\''; - state->full_state->lit.delim_end = '\''; - state->full_state->lit.allow_interp = false; + state->full_state.in_state = RubyFullState::STRING; + state->full_state.lit.delim_start = '\''; + state->full_state.lit.delim_end = '\''; + state->full_state.lit.allow_interp = false; i++; continue; } 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}); - state->full_state->in_state = RubyFullState::STRING; - state->full_state->lit.delim_start = '"'; - state->full_state->lit.delim_end = '"'; - state->full_state->lit.allow_interp = true; + state->full_state.in_state = RubyFullState::STRING; + state->full_state.lit.delim_start = '"'; + state->full_state.lit.delim_end = '"'; + state->full_state.lit.allow_interp = true; i++; continue; } 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}); - state->full_state->in_state = RubyFullState::STRING; - state->full_state->lit.delim_start = '`'; - state->full_state->lit.delim_end = '`'; - state->full_state->lit.allow_interp = true; + state->full_state.in_state = RubyFullState::STRING; + state->full_state.lit.delim_start = '`'; + state->full_state.lit.delim_end = '`'; + state->full_state.lit.allow_interp = true; i++; continue; } else if (text[i] == '%') { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; if (i + 1 >= len) { i++; continue; @@ -1005,16 +1009,16 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back( {i, i + prefix_len + 1, (is_regexp ? TokenKind::K_REGEXP : TokenKind::K_STRING)}); - state->full_state->in_state = + state->full_state.in_state = is_regexp ? RubyFullState::REGEXP : RubyFullState::STRING; - state->full_state->lit.delim_start = delim_start; - state->full_state->lit.delim_end = delim_end; - state->full_state->lit.allow_interp = allow_interp; - state->full_state->lit.brace_level = 1; + state->full_state.lit.delim_start = delim_start; + state->full_state.lit.delim_end = delim_end; + state->full_state.lit.allow_interp = allow_interp; + state->full_state.lit.brace_level = 1; i += prefix_len + 1; continue; } else if (isdigit(text[i])) { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; uint32_t start = i; if (text[i] == '0') { i++; @@ -1115,7 +1119,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, tokens->push_back({start, i, TokenKind::K_NUMBER}); continue; } else if (identifier_start_char(text[i])) { - state->full_state->expecting_expr = false; + state->full_state.expecting_expr = false; uint32_t length; if ((length = base_keywords_trie.match(text, i, len, identifier_char))) { tokens->push_back({i, i + length, TokenKind::K_KEYWORD}); @@ -1123,7 +1127,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, continue; } else if ((length = expecting_keywords_trie.match(text, i, len, identifier_char))) { - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; tokens->push_back({i, i + length, TokenKind::K_KEYWORD}); i += length; continue; @@ -1134,7 +1138,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, continue; } else if ((length = expecting_operators_trie.match( 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}); i += length; continue; @@ -1256,7 +1260,7 @@ std::shared_ptr ruby_parse(std::vector *tokens, operator_trie.match(text, i, len, [](char) { return false; }))) { tokens->push_back({i, i + op_len, TokenKind::K_OPERATOR}); i += op_len; - state->full_state->expecting_expr = true; + state->full_state.expecting_expr = true; continue; } else { i += utf8_codepoint_width(text[i]); diff --git a/src/ui/bar.cc b/src/ui/bar.cc index 583a3df..684baa6 100644 --- a/src/ui/bar.cc +++ b/src/ui/bar.cc @@ -10,7 +10,8 @@ void Bar::log(std::string message) { log_line = message; } void Bar::render(std::vector &buffer) { USING(LSPInstance); BarLine bar_line; - bar_line = bar_contents(mode, screen.col, pwd.string(), focused_window); + bar_line = + bar_contents(mode, screen.col, pwd.string(), layout::focused_window); auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg, uint32_t bg, uint8_t flags, uint32_t width) { ScreenCell &c = buffer[row * screen.col + col]; @@ -61,8 +62,8 @@ void Bar::handle_command(std::string &command) { running = false; return; } - if (focused_window) - focused_window->handle_command(command); + if (layout::focused_window) + layout::focused_window->handle_command(command); } void Bar::handle_event(KeyEvent event) { diff --git a/src/windows/renderer.cc b/src/windows/renderer.cc index bed82e8..6126675 100644 --- a/src/windows/renderer.cc +++ b/src/windows/renderer.cc @@ -1,23 +1,49 @@ +#include "io/sysio.h" #include "main.h" #include "windows/decl.h" -TileRoot root_tile; -std::vector> popups; +namespace layout { +TileBase root_tile; Window *focused_window; +std::vector> popups; +std::vector> floating_tiles; +} // namespace layout void render() { - ui::bar.render(new_screen); - root_tile.render(new_screen); - for (auto &popup : popups) - popup->render(new_screen); + 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 : popups) { - if (popup->inside(event.mouse_x, event.mouse_y)) { - popup->handle_click(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; } } - root_tile.handle_click(event); + 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); }