From 8b93b955e8911ecf469639ea7f238bcb0a3fcfef Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Sun, 1 Feb 2026 20:40:52 +0000 Subject: [PATCH] Fix parsing bugs and add better indentation support --- TODO.md | 32 +++++--- include/editor/helpers.h | 2 + include/editor/indents.h | 6 ++ include/lsp/lsp.h | 11 ++- include/syntax/parser.h | 2 +- samples/bash.sh | 3 - samples/ruby.rb | 8 +- src/editor/completions.cc | 6 +- src/editor/edit.cc | 10 ++- src/editor/editor.cc | 5 +- src/editor/events.cc | 11 ++- src/editor/helpers.cc | 92 ++++++++++++++++++++-- src/editor/indents.cc | 159 ++++++++++++++++++++++++++++++-------- src/lsp/handlers.cc | 1 - src/lsp/process.cc | 3 +- src/lsp/workers.cc | 2 +- src/syntax/parser.cc | 54 ++++++------- 17 files changed, 304 insertions(+), 103 deletions(-) diff --git a/TODO.md b/TODO.md index d85ca65..ab76869 100644 --- a/TODO.md +++ b/TODO.md @@ -2,25 +2,32 @@ 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) +``` + ##### BTW Check each lsp with each of the features implemented -* Add ruby config for file detection (with `file` command by default) +* Possibly in the future limit memory usage by parser for larger files +* Also redo lsp threads such that no mutex needed for any rope stuff + - This will mean that parsers/renderers and keystrokes will not need to be individually locked + - And so it will be much faster + - While at it also make the lsp instead of a single thread be a pool of threads blocking on their stdio + - So one lsp being slower wont affect others and fps based reading wont be necessary saving cpu + - At which point the main thread can also be blocked on user input or lsp responses and still be fast * [ ] Add mgems for most common things and a ruby library to allow combining true ruby with mruby - Or revert to cruby and retry with manual linking . maybe it might work? +* add command to set and use a file type at runtime * [ ] color alpha in ini files * [ ] Make warning before ctrl+q for saving * [ ] **LSP Bug:** Check why `fish-lsp` is behaving so off with completions filtering. * [ ] **Line move:** fix the move line functions to work without the calculations from folds as folds are removed. * [ ] **Editor Indentation Fix:** - Main : merger indentation with the parser for more accurate results. - * [ ] Keep cache of language maps in engine to reduce lookup time. - * [ ] In indents add function to support tab which indents if before any content and inserts a pure \t otherwise. - * [ ] And backspace which undents if before any content. - * [ ] Add block indentation support. * [ ] Ignore comments/strings from parser when auto-indenting. - * [ ] These will dedent when the block immediately after them is dedented - * [ ] Dont dedent if ending is valid starting is invalid but also empty - * [ ] Just leave asis if starting is empty -* [ ] **Readme:** Update readme to show ruby based config. +* [ ] **Readme:** Update readme to show ruby based config in detail. * [ ] **UI Refinement:** * [ ] Allow completion list to be scrolled; show only `x` max items. * [ ] Finish autocomplete box style functions. @@ -31,6 +38,13 @@ Copyright 2025 Syed Daanish * Try to make all functions better now that folds have been purged * Cleanup syntax and renderer files +* add `:j` command to jump to line \ in the current file +* and many more stuff like ruby execution +* and give warning for invalid commands +* and upon escape clear the current command +* allow multiline logging which captures the input entirely and y will copy the log and anything else will exit +* it will then collapse to being the first line from the log only + * **RN** Add a thing called view which is a rect with speacial type editor . but otherwise a ruby or c++ view * can be used for stuff like file manager/git manager/theme picker. * allow flushing functions in ruby to tell c++ to refresh keybinds/themes etc. diff --git a/include/editor/helpers.h b/include/editor/helpers.h index 7e9a381..013736e 100644 --- a/include/editor/helpers.h +++ b/include/editor/helpers.h @@ -17,6 +17,8 @@ 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); diff --git a/include/editor/indents.h b/include/editor/indents.h index 3e0f537..15534de 100644 --- a/include/editor/indents.h +++ b/include/editor/indents.h @@ -112,12 +112,18 @@ struct IndentationEngine { uint32_t set_indent(uint32_t row, int64_t indent_level); uint32_t indent_line(uint32_t row); uint32_t dedent_line(uint32_t row); + void indent_block(uint32_t start_row, uint32_t end_row, int delta); void indent_block(uint32_t start, uint32_t end); void dedent_block(uint32_t start, uint32_t end); // fixes a autocomplete block's indentation char *block_to_asis(Coord cursor, std::string source, uint32_t *out_len); private: + const std::vector *start_end = nullptr; + const std::vector *start_start = nullptr; + const std::vector *end_full = nullptr; + const std::vector *end_start = nullptr; + // TODO: Ignore comments/strings too // returns the indent level of the line itself or of the previous non-empty uint32_t indent_expected(uint32_t row); diff --git a/include/lsp/lsp.h b/include/lsp/lsp.h index cf88c96..e815bef 100644 --- a/include/lsp/lsp.h +++ b/include/lsp/lsp.h @@ -6,12 +6,17 @@ #include "utils/utils.h" struct LSPPending { - std::string method; Editor *editor = nullptr; - - std::function callback; + std::function callback; }; +// TODO: Defer any editor mutation to main thread to get rid of +// all mutex locks on the editor rope. +// struct LSPPendingResponse { +// LSPPending *pending = nullptr; +// json message; +// }; + struct LSPOpenRequest { Language language; Editor *editor; diff --git a/include/syntax/parser.h b/include/syntax/parser.h index 2784cda..4b1bd15 100644 --- a/include/syntax/parser.h +++ b/include/syntax/parser.h @@ -23,7 +23,7 @@ struct Parser { UniqueQueue dirty_lines; Parser(Editor *editor, std::string n_lang, uint32_t n_scroll_max); - void edit(uint32_t start_line, uint32_t old_end_line, uint32_t inserted_rows); + void edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows); void work(); void scroll(uint32_t line); }; diff --git a/samples/bash.sh b/samples/bash.sh index 3e63339..f65dcec 100644 --- a/samples/bash.sh +++ b/samples/bash.sh @@ -22,15 +22,12 @@ colorize() { } # Example of error handling - handle_error() { log ERROR "An error occurred on line $1" } trap 'handle_error $LINENO' ERR - # Multiline string test read -r -d '' MULTI <<'CPP' - int main() { } diff --git a/samples/ruby.rb b/samples/ruby.rb index 8e4733e..9f86f8c 100644 --- a/samples/ruby.rb +++ b/samples/ruby.rb @@ -24,10 +24,10 @@ s wow 々〆〤]/ UNICORE = / -s -{#{ss}} -\C-s\u{10} -/ + s + {#{ss}} + \C-s\u{10} + / UNINITCORE = %( diff --git a/src/editor/completions.cc b/src/editor/completions.cc index 2dd4e7a..ee2f065 100644 --- a/src/editor/completions.cc +++ b/src/editor/completions.cc @@ -78,8 +78,7 @@ void completion_request(Editor *editor) { editor->completion.version = editor->lsp_version; LSPPending *pending = new LSPPending(); pending->editor = editor; - pending->method = "textDocument/completion"; - pending->callback = [](Editor *editor, std::string, json message) { + pending->callback = [](Editor *editor, const json &message) { auto &session = editor->completion; std::unique_lock lock(session.mtx); std::vector items_json; @@ -359,8 +358,7 @@ void completion_resolve_doc(Editor *editor) { item.documentation = ""; LSPPending *pending = new LSPPending(); pending->editor = editor; - pending->method = "completionItem/resolve"; - pending->callback = [](Editor *editor, std::string, json message) { + pending->callback = [](Editor *editor, const json &message) { std::unique_lock lock(editor->completion.mtx); auto &item = editor->completion.items[editor->completion.select]; if (message["result"].contains("documentation")) { diff --git a/src/editor/edit.cc b/src/editor/edit.cc index 1214447..45c1d8d 100644 --- a/src/editor/edit.cc +++ b/src/editor/edit.cc @@ -52,7 +52,7 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { editor->root = erase(editor->root, start, byte_pos - start); lock_2.unlock(); if (editor->parser) - editor->parser->edit(start_row, end_row, 0); + editor->parser->edit(start_row, end_row - start_row, 0); if (do_lsp) { if (editor->lsp->incremental_sync) { json message = { @@ -125,7 +125,7 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { editor->root = erase(editor->root, byte_pos, end - byte_pos); lock_2.unlock(); if (editor->parser) - editor->parser->edit(start_row, end_row, 0); + editor->parser->edit(start_row, end_row - start_row, 0); if (do_lsp) { if (editor->lsp->incremental_sync) { json message = { @@ -184,7 +184,7 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) { apply_hook_insertion(editor, pos.row, rows); lock_2.unlock(); if (editor->parser) - editor->parser->edit(pos.row, pos.row, rows); + editor->parser->edit(pos.row, 0, rows); if (editor->lsp) { if (editor->lsp->incremental_sync) { json message = { @@ -245,8 +245,10 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text, for (uint32_t i = 0; i < len; i++) if (text[i] == '\n') rows++; + if (rows > 0) + rows--; if (editor->parser) - editor->parser->edit(start.row, end.row - 1, rows); + editor->parser->edit(start.row, end.row - start.row, rows); if (editor->lsp) { if (editor->lsp->incremental_sync) { json message = { diff --git a/src/editor/editor.cc b/src/editor/editor.cc index daeded4..ea09f53 100644 --- a/src/editor/editor.cc +++ b/src/editor/editor.cc @@ -97,9 +97,8 @@ void save_file(Editor *editor) { {"trimFinalNewlines", true}}}}}}; LSPPending *pending = new LSPPending(); pending->editor = editor; - pending->method = "textDocument/formatting"; - pending->callback = [save_msg, version](Editor *editor, std::string, - json message) { + pending->callback = [save_msg, version](Editor *editor, + const json &message) { if (version != editor->lsp_version) return; auto &edits = message["result"]; diff --git a/src/editor/events.cc b/src/editor/events.cc index 39c1615..a4d1657 100644 --- a/src/editor/events.cc +++ b/src/editor/events.cc @@ -153,8 +153,7 @@ void handle_editor_event(Editor *editor, KeyEvent event) { if (event.key_type == KEY_CHAR) { if (event.len == 1) { if (event.c[0] == '\t') { - edit_insert(editor, editor->cursor, (char *)" ", 2); - cursor_right(editor, 2); + editor->indents.insert_tab(editor->cursor); } else if (event.c[0] == '\n' || event.c[0] == '\r') { editor->indents.insert_new_line(editor->cursor); } else if (event.c[0] == CTRL('W')) { @@ -205,6 +204,14 @@ void handle_editor_event(Editor *editor, KeyEvent event) { paste(editor); mode = NORMAL; break; + case '<': + case ',': + dedent_selection(editor); + break; + case '>': + case '.': + indent_selection(editor); + break; } } break; diff --git a/src/editor/helpers.cc b/src/editor/helpers.cc index 8a50ced..99f7f9a 100644 --- a/src/editor/helpers.cc +++ b/src/editor/helpers.cc @@ -4,8 +4,14 @@ #include "lsp/lsp.h" #include "main.h" #include "utils/utils.h" +#include void cut(Editor *editor) { + if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > + 1500) { + bar.log("Selection too large!"); + return; + } if (mode != SELECT) return; Coord start; @@ -19,6 +25,11 @@ void cut(Editor *editor) { } void copy(Editor *editor) { + if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > + 1500) { + bar.log("Selection too large!"); + return; + } if (mode != SELECT) return; uint32_t len; @@ -72,6 +83,53 @@ void dedent_current_line(Editor *editor) { editor->cursor.row = start.row; } +static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) { + if (c.row == row) { + int64_t new_col = (int64_t)c.col + delta; + c.col = (uint32_t)MAX(new_col, 0); + } +} + +void indent_selection(Editor *editor) { + uint32_t top = MIN(editor->cursor.row, editor->selection.row); + uint32_t bot = MAX(editor->cursor.row, editor->selection.row); + if (bot - top > 1500) { + bar.log("Can't indent more than 1500 lines at once!"); + return; + } + if (bot - top >= 2) + editor->indents.indent_block(top + 1, bot - 1); + uint32_t delta_top = editor->indents.indent_line(top); + uint32_t delta_bot = + (bot == top) ? delta_top : editor->indents.indent_line(bot); + move_coord_by_delta(editor->cursor, top, delta_top); + move_coord_by_delta(editor->selection, top, delta_top); + if (bot != top) { + move_coord_by_delta(editor->cursor, bot, delta_bot); + move_coord_by_delta(editor->selection, bot, delta_bot); + } +} + +void dedent_selection(Editor *editor) { + uint32_t top = MIN(editor->cursor.row, editor->selection.row); + uint32_t bot = MAX(editor->cursor.row, editor->selection.row); + if (bot - top > 1500) { + bar.log("Can't dedent more than 1500 lines at once!"); + return; + } + if (bot - top >= 2) + editor->indents.dedent_block(top + 1, bot - 1); + uint32_t delta_top = editor->indents.dedent_line(top); + uint32_t delta_bot = + (bot == top) ? delta_top : editor->indents.dedent_line(bot); + move_coord_by_delta(editor->cursor, top, -(int64_t)delta_top); + move_coord_by_delta(editor->selection, top, -(int64_t)delta_top); + if (bot != top) { + move_coord_by_delta(editor->cursor, bot, -(int64_t)delta_bot); + move_coord_by_delta(editor->selection, bot, -(int64_t)delta_bot); + } +} + void insert_char(Editor *editor, char c) { uint32_t col = editor->cursor.col; LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); @@ -155,9 +213,7 @@ void insert_char(Editor *editor, char c) { {"trimFinalNewlines", true}}}}}}; LSPPending *pending = new LSPPending(); pending->editor = editor; - pending->method = "textDocument/onTypeFormatting"; - pending->callback = [version](Editor *editor, std::string, - json message) { + pending->callback = [version](Editor *editor, const json &message) { if (version != editor->lsp_version) return; auto &edits = message["result"]; @@ -201,11 +257,32 @@ void backspace_edit(Editor *editor) { LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); if (!it) return; - char *line = next_line(it, nullptr); - char prev_char = line[prev_pos.col]; - char next_char = line[editor->cursor.col]; + uint32_t len; + char *line = next_line(it, &len); + if (!line) { + free(it->buffer); + free(it); + return; + } + if (len > 0 && line[len - 1] == '\n') + --len; + char prev_char = (prev_pos.col < len) ? line[prev_pos.col] : 0; + char next_char = (editor->cursor.col < len) ? line[editor->cursor.col] : 0; + bool before_content = false; + if (editor->cursor.col > 0) { + before_content = true; + for (uint32_t i = 0; i < editor->cursor.col; i++) + if (line[i] != ' ' && line[i] != '\t') { + before_content = false; + break; + } + } free(it->buffer); free(it); + if (before_content) { + dedent_current_line(editor); + return; + } bool is_pair = (prev_char == '{' && next_char == '}') || (prev_char == '(' && next_char == ')') || (prev_char == '[' && next_char == ']') || @@ -312,8 +389,7 @@ void fetch_lsp_hover(Editor *editor) { {"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; LSPPending *pending = new LSPPending(); pending->editor = editor; - pending->method = "textDocument/hover"; - pending->callback = [](Editor *editor, std::string, json hover) { + pending->callback = [](Editor *editor, const json &hover) { if (hover.contains("result") && !hover["result"].is_null()) { auto &contents = hover["result"]["contents"]; std::string hover_text = ""; diff --git a/src/editor/indents.cc b/src/editor/indents.cc index 6abecb0..91799d6 100644 --- a/src/editor/indents.cc +++ b/src/editor/indents.cc @@ -38,6 +38,18 @@ void IndentationEngine::compute_indent(Editor *n_editor) { indent = 2; free(it->buffer); free(it); + auto x = kLangtoBlockStartsEnd.find(editor->lang.name); + if (x != kLangtoBlockStartsEnd.end()) + start_end = &x->second; + x = kLangtoBlockStartsStart.find(editor->lang.name); + if (x != kLangtoBlockStartsStart.end()) + start_start = &x->second; + x = kLangtoBlockEndsFull.find(editor->lang.name); + if (x != kLangtoBlockEndsFull.end()) + end_full = &x->second; + x = kLangtoBlockEndsStart.find(editor->lang.name); + if (x != kLangtoBlockEndsStart.end()) + end_start = &x->second; } uint32_t IndentationEngine::indent_real(char *line, uint32_t len) { @@ -75,18 +87,16 @@ uint32_t IndentationEngine::indent_expected(uint32_t row) { if (len == 0) continue; c_indent = indent_real(line, len); - auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name); - auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name); bool is_end = false; - if (is_end_set != kLangtoBlockStartsEnd.end()) - for (auto end : is_end_set->second) + if (start_end) + for (auto end : *start_end) if (ends_with(line, end)) { c_indent++; is_end = true; break; } - if (!is_end && is_start_set != kLangtoBlockStartsStart.end()) - for (auto end : is_start_set->second) + if (!is_end && start_start) + for (auto end : *start_start) if (starts_with(line, end)) { c_indent++; break; @@ -195,6 +205,100 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) { return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); } +void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row) { + indent_block(start_row, end_row, +1); +} + +void IndentationEngine::dedent_block(uint32_t start_row, uint32_t end_row) { + indent_block(start_row, end_row, -1); +} + +void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row, + int delta) { + if (start_row > end_row) + std::swap(start_row, end_row); + uint32_t start_len, end_len; + uint32_t start_off = line_to_byte(editor->root, start_row, &start_len); + uint32_t end_off = line_to_byte(editor->root, end_row, &end_len); + uint32_t total_len = (end_off - start_off) + end_len; + char *block = read(editor->root, start_off, total_len); + if (!block) + return; + uint32_t cap = total_len + 128; + char *out = (char *)malloc(cap); + uint32_t out_len = 0; + char *p = block; + char *end = block + total_len; + while (p < end) { + char *line_start = p; + while (p < end && *p != '\n') + p++; + uint32_t len = (uint32_t)(p - line_start); + uint32_t ws = 0; + while (ws < len && (line_start[ws] == ' ' || line_start[ws] == '\t')) + ws++; + uint32_t real_indent = indent_real(line_start, len); + int64_t new_indent = (int64_t)real_indent + delta; + if (new_indent < 0) + new_indent = 0; + uint32_t indent_chars = (indent == 1) ? new_indent : new_indent * indent; + uint32_t new_line_len = indent_chars + (len - ws); + if (out_len + new_line_len + 2 >= cap) { + cap = (cap * 2) + new_line_len + 32; + out = (char *)realloc(out, cap); + } + if (indent == 1) { + memset(out + out_len, '\t', indent_chars); + out_len += indent_chars; + } else { + memset(out + out_len, ' ', indent_chars); + out_len += indent_chars; + } + memcpy(out + out_len, line_start + ws, len - ws); + out_len += (len - ws); + if (p < end && *p == '\n') { + out[out_len++] = '\n'; + p++; + } + } + free(block); + edit_replace(editor, {start_row, 0}, {end_row, end_len}, out, out_len); + free(out); +} + +void IndentationEngine::insert_tab(Coord cursor) { + std::shared_lock lock(editor->knot_mtx); + LineIterator *it = begin_l_iter(editor->root, cursor.row); + if (!it) + return; + uint32_t len; + char *line = next_line(it, &len); + if (!line) { + free(it->buffer); + free(it); + return; + } + lock.unlock(); + if (len > 0 && line[len - 1] == '\n') + --len; + uint32_t ws_len = 0; + while (ws_len < len && (line[ws_len] == ' ' || line[ws_len] == '\t')) + ws_len++; + std::string insert; + if (cursor.col <= ws_len) { + if (indent == 1) + insert = "\t"; + else + insert.assign(indent - ((cursor.col) % indent), ' '); + } else { + insert = "\t"; + } + free(it->buffer); + free(it); + edit_insert(editor, cursor, (char *)insert.c_str(), insert.size()); + editor->cursor.col += insert.size(); +} + void IndentationEngine::insert_new_line(Coord cursor) { std::string formatted; std::shared_lock lock(editor->knot_mtx); @@ -212,19 +316,17 @@ void IndentationEngine::insert_new_line(Coord cursor) { if (len > 0 && line[len - 1] == '\n') --len; if (cursor.col >= len) { - auto is_end_full = kLangtoBlockEndsFull.find(editor->lang.name); - auto is_end_start = kLangtoBlockEndsStart.find(editor->lang.name); bool end_matched = false; - if (is_end_full != kLangtoBlockEndsFull.end()) - for (auto end : is_end_full->second) + if (end_full) + for (auto end : *end_full) if (end == trim(line)) { cursor.col = set_indent( cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1); end_matched = true; break; } - if (!end_matched && is_end_start != kLangtoBlockEndsStart.end()) - for (auto end : is_end_start->second) + if (!end_matched && end_start) + for (auto end : *end_start) if (starts_with(trim(line), end)) { cursor.col = set_indent( cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1); @@ -252,30 +354,26 @@ void IndentationEngine::insert_new_line(Coord cursor) { if (!ending.empty()) { bool ending_valid = false; bool starting_valid = false; - auto is_end_full = kLangtoBlockEndsFull.find(editor->lang.name); - auto is_end_start = kLangtoBlockEndsStart.find(editor->lang.name); - auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name); - auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name); - if (is_end_full != kLangtoBlockEndsFull.end()) - for (auto end : is_end_full->second) + if (end_full) + for (auto end : *end_full) if (ending == end) { ending_valid = true; break; } - if (!ending_valid && is_end_start != kLangtoBlockEndsStart.end()) - for (auto end : is_end_start->second) + if (!ending_valid && end_start) + for (auto end : *end_start) if (starts_with(ending, end)) { ending_valid = true; break; } - if (is_end_set != kLangtoBlockStartsEnd.end()) - for (auto end : is_end_set->second) + if (start_end) + for (auto end : *start_end) if (ends_with(before, end)) { starting_valid = true; break; } - if (!starting_valid && is_start_set != kLangtoBlockStartsStart.end()) - for (auto end : is_start_set->second) + if (!starting_valid && start_start) + for (auto end : *start_start) if (starts_with(before, end)) { starting_valid = true; break; @@ -288,18 +386,16 @@ void IndentationEngine::insert_new_line(Coord cursor) { else if (ending_valid) c_indent--; } - auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name); - auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name); bool is_end = false; - if (is_end_set != kLangtoBlockStartsEnd.end()) - for (auto end : is_end_set->second) + if (start_end) + for (auto end : *start_end) if (ends_with(before, end)) { c_indent++; is_end = true; break; } - if (!is_end && is_start_set != kLangtoBlockStartsStart.end()) - for (auto end : is_start_set->second) + if (!is_end && start_start) + for (auto end : *start_start) if (starts_with(before, end)) { c_indent++; break; @@ -349,8 +445,7 @@ void IndentationEngine::insert_new_line(Coord cursor) { {"trimFinalNewlines", true}}}}}}; LSPPending *pending = new LSPPending(); pending->editor = editor; - pending->method = "textDocument/onTypeFormatting"; - pending->callback = [version](Editor *editor, std::string, json message) { + pending->callback = [version](Editor *editor, const json &message) { if (version != editor->lsp_version) return; auto &edits = message["result"]; diff --git a/src/lsp/handlers.cc b/src/lsp/handlers.cc index 28e5b86..555f1de 100644 --- a/src/lsp/handlers.cc +++ b/src/lsp/handlers.cc @@ -1,5 +1,4 @@ #include "lsp/lsp.h" -#include "main.h" Queue lsp_open_queue; diff --git a/src/lsp/process.cc b/src/lsp/process.cc index 84e98cd..0531b11 100644 --- a/src/lsp/process.cc +++ b/src/lsp/process.cc @@ -53,9 +53,8 @@ std::shared_ptr get_or_init_lsp(std::string lsp_id) { if (!init_lsp(lsp)) return nullptr; LSPPending *pending = new LSPPending(); - pending->method = "initialize"; pending->editor = nullptr; - pending->callback = [lsp, lsp_id](Editor *, std::string, json msg) { + 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")) { diff --git a/src/lsp/workers.cc b/src/lsp/workers.cc index 2362bc3..8a6132d 100644 --- a/src/lsp/workers.cc +++ b/src/lsp/workers.cc @@ -143,7 +143,7 @@ void lsp_worker() { LSPPending *pend = it->second; lock.unlock(); if (pend->callback) - pend->callback(pend->editor, pend->method, *msg); + pend->callback(pend->editor, *msg); delete pend; lock.lock(); lsp->pending.erase(it); diff --git a/src/syntax/parser.cc b/src/syntax/parser.cc index 6ff33f3..da7e554 100644 --- a/src/syntax/parser.cc +++ b/src/syntax/parser.cc @@ -26,19 +26,21 @@ 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); + edit(0, 0, editor->root->line_count + 1); } -void Parser::edit(uint32_t start_line, uint32_t old_end_line, +void Parser::edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows) { - if (((int64_t)old_end_line - (int64_t)start_line) > 0) - line_tree.erase(start_line, old_end_line - start_line); - if (inserted_rows > 0) - line_tree.insert(start_line, inserted_rows); - if (start_line > 0) - dirty_lines.push(start_line - 1); - dirty_lines.push(start_line); - dirty_lines.push(start_line + 1); + 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); + 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); } void Parser::work() { @@ -48,10 +50,19 @@ void Parser::work() { 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) + 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 > 50 ? scroll_max - 50 : 0; + uint32_t min_line = scroll_max > 60 ? scroll_max - 60 : 0; uint32_t max_line = scroll_max + 10; if (c_line < min_line || c_line > max_line) { dirty_lines.push(c_line); @@ -65,12 +76,16 @@ void Parser::work() { prev_state = line_tree.at(c_line - 1)->out_state; std::shared_lock k_lock(editor->knot_mtx); LineIterator *it = begin_l_iter(editor->root, c_line); + if (!it) + continue; uint32_t cur_line = c_line; while (cur_line < line_count) { if (!running.load(std::memory_order_relaxed)) break; if (scroll_snapshot != scroll_max) { - dirty_lines.push(cur_line); + LineData *line_data = line_tree.at(cur_line); + if (line_data && !line_data->out_state) + dirty_lines.push(cur_line); break; } if (cur_line < min_line || cur_line > max_line) { @@ -130,17 +145,4 @@ void Parser::work() { } } -void Parser::scroll(uint32_t line) { - if (line != scroll_max) { - scroll_max = line; - uint32_t c_line = line > 50 ? line - 50 : 0; - if (c_line >= line_tree.count()) - return; - if (line_tree.at(c_line)->in_state || line_tree.at(c_line)->out_state) - return; - scroll_dirty = true; - dirty_lines.push(c_line); - } else { - scroll_max = line; - } -} +void Parser::scroll(uint32_t line) { scroll_max = line; }