diff --git a/.clangd b/.clangd old mode 100755 new mode 100644 diff --git a/.gitattributes b/.gitattributes old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 diff --git a/.gitmodules b/.gitmodules old mode 100755 new mode 100644 diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 index 0b2aba0..4cbfe27 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ CFLAGS_DEBUG :=\ -O0 -fno-inline -gsplit-dwarf \ -g -fno-omit-frame-pointer \ -Wno-unused-command-line-argument \ - -I./include -I./libs -I/home/syed/main/crib/libs/mruby/include + -I./include -I./libs -I./libs/mruby/include # C_SANITIZER := -fsanitize=address @@ -33,7 +33,7 @@ CFLAGS_RELEASE :=\ -fomit-frame-pointer -DNDEBUG -s \ -mllvm -vectorize-loops \ -Wno-unused-command-line-argument \ - -I./include -I./libs -I/home/syed/main/crib/libs/mruby/include + -I./include -I./libs -I./libs/mruby/include PCH_CFLAGS_DEBUG := $(CFLAGS_DEBUG) -x c++-header PCH_CFLAGS_RELEASE := $(CFLAGS_RELEASE) -x c++-header diff --git a/README.md b/README.md old mode 100755 new mode 100644 diff --git a/TODO.md b/TODO.md old mode 100755 new mode 100644 index 0cccdc0..84492d3 --- a/TODO.md +++ b/TODO.md @@ -2,33 +2,42 @@ Copyright 2025 Syed Daanish # TODO +- Fix bug where scroll up at top of screen first line jumps to bottom + +- Make completions work with new system - And factorize renderer into its own class - - which is just managed by the renderer - - and adjusment.cc will call it + - which is just managed by the renderer + - and adjusment.cc will call it - which is reused by scrollers/ensurers too (for knowing screen wrapped end) - this will then allow inlay hints to be possible -- and also make VAI easier to implement +- and also make VAI easier to implement (probably as inlay hints) +- and also allow code lens to work too +- and an api to allow line skipping / virtual text which allows for folding logic as extention + +- Also time extentions and disable if they take too long / for larger files with options to force + * Allow keybinds to be set in ruby * ruby file for project specific runs alongside any system wide ones - Seperate system functions into a class that branches to support local / ssh / server modes. - Even lsp shouldnt be directly controlled because it can branch on local and server modes - - check libssh2 or child process stuff for remote editing (remote mode) - - Thick and thin mode (server mode) (through ssh or port) (for website only port + webhook) - - thin mode only shares screen diffing over the network - uses server settings / themes / shell - - only one user connects at a time - - provides data as a websocket with tui or gui/online modes where tui shares diffing as is (can be connected to by gui or website) - - and gui/online modes try to (if not then render onto a canvas) forward window.render_gui() and dedicated windowing system (i.e windowing is html divs) - - and window.render_gui() returns a markup code (custom markup) - - and for events i could either leave tui as-is and for markup have special event handling or i - - implement the widget system in both and have the same event handling then - - actually for true cross platform i need to make a ui lib with widgets etc. so it is workable on all platforms (so they all need only a single ui lib) - - thick only shared fileio and lsp data with local used for all else - uses local settings / themes - - multiple users connect at once but maybe either make them not be allowed to open same file or make vscode like multiediting (unsure how exactly) - - they all share same lsp instances - - they all have the same shell access - - allow having an instance that forwards thick mode to thin mode (to connect online mode to a thick server for code collaboration) + - check libssh2 or child process stuff for remote editing (remote mode) + - Thick and thin mode (server mode) (through ssh or port) (for website only port + webhook) + - thin mode only shares screen diffing over the network - uses server settings / themes / shell + - only one user connects at a time + - provides data as a websocket with tui or gui/online modes where tui shares diffing as is (can be connected to by gui or website) + - and gui/online modes try to (if not then render onto a canvas) forward window.render_gui() and dedicated windowing system (i.e windowing is html divs) + - and window.render_gui() returns a markup code (custom markup) + - and for events i could either leave tui as-is and for markup have special event handling or i + - implement the widget system in both and have the same event handling then + - actually for true cross platform i need to make a ui lib with widgets etc. so it is workable on all platforms (so they all need only a single ui lib) + - thick only shared fileio and lsp data with local used for all else - uses local settings / themes + - multiple users connect at once but maybe either make them not be allowed to open same file or make vscode like multiediting (unsure how exactly) + - they all share same lsp instances + - they all have the same shell access + - allow having an instance that forwards thick mode to thin mode (to connect online mode to a thick server for code collaboration) + * Then allow ruby code to create and handle windows as extentions * Then 3 inbuilt extentions being file manager, theme picker, tab selector @@ -37,8 +46,8 @@ Copyright 2025 Syed Daanish * also implement info strings for all the stuff that take focus * Mode is a feild on a editor and there is no global "mode system" except in editors (enxtentions do their own thing) - * make mode normal and not atomic if it is truly unused across threads - * this means keybinds can be set to use the editor as insert mode only and so behave like vscode + - make mode normal and not atomic if it is truly unused across threads + - this means keybinds can be set to use the editor as insert mode only and so behave like vscode * split lsp stuff into multiple files (not a monolithic class as it is now) * Extentions can also be used as file openers (for like databases . diffing . images . audio etc) @@ -59,10 +68,10 @@ Copyright 2025 Syed Daanish * Add a file picker suggestion while typing a path (including stuff like ~ and .. and $HOME etc) * allow opening directory after filemanger is sorted out. * commands to: - change pwd - load a rb file - run a ruby command - close a window etc. + change pwd + load a rb file + run a ruby command + close a window etc. * [ ] Add mgems for most common things and a ruby library to allow combining true ruby with mruby * add command to set and use a file type at runtime * [ ] color alpha in ini files @@ -70,12 +79,12 @@ Copyright 2025 Syed Daanish * [ ] **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. - * [ ] Ignore comments/strings from parser when auto-indenting. - * [ ] Support for stuff like bash \ and math operators in other languages and comma and line starting with a dot (like in ruby) - etc. + - [ ] Ignore comments/strings from parser when auto-indenting. + - [ ] Support for stuff like bash \ and math operators in other languages and comma and line starting with a dot (like in ruby) + etc. * [ ] **Readme:** Update readme to show ruby based config in detail. * [ ] **UI Refinement:** - * [ ] Finish autocomplete box style functions. + - [ ] Finish autocomplete box style functions. * [ ] **Documentation UI:** Capture `Ctrl+h` / `Ctrl+l` for scrolling documentation windows. * Cap line_tree data limit for large files and just store about a thousand previous lines maybe? (lazily loaded) @@ -88,17 +97,17 @@ Copyright 2025 Syed Daanish * allow flushing functions in ruby to tell c++ to refresh keybinds/themes etc. * [ ] **LSP:** - support snippets in completion properly - check:: - pull diagnostics for ruby-lsp - lsp selection range - use to highlight start / end of range maybe? - goto definiton - signature help - document symbol for the top bar maybe? (or workspace symbol) - also setup workspaces - Semantic highlighting - Quick fixes - Rename symbols + support snippets in completion properly + check:: + pull diagnostics for ruby-lsp + lsp selection range - use to highlight start / end of range maybe? + goto definiton + signature help + document symbol for the top bar maybe? (or workspace symbol) + also setup workspaces + Semantic highlighting + Quick fixes + Rename symbols * Allow ruby to config lsp capabilities @@ -122,49 +131,45 @@ Copyright 2025 Syed Daanish ### UX -* [ ] **Completion Filtering:** - * [ ] Stop filtering case-sensitive. - * [ ] Normalize completion edits if local filtering is used. +- [ ] **Completion Filtering:** + - [ ] Stop filtering case-sensitive. + - [ ] Normalize completion edits if local filtering is used. -* [ ] **Basic Autocomplete:** Keep a list of words in the current buffer for non-LSP fallback. +- [ ] **Basic Autocomplete:** Keep a list of words in the current buffer for non-LSP fallback. ### Features -* [ ] **Search & Replace:** - * [ ] Add Search/Replace UI. - * [ ] Support capture groups (`$1`, `$2`) or allow Perl regex directly. - * [ ] Ensure virtual cursors are included in search positions. +- [ ] **Search & Replace:** + - [ ] Add Search/Replace UI. + - [ ] Support capture groups (`$1`, `$2`) or allow Perl regex directly. + - [ ] Ensure virtual cursors are included in search positions. -* [ ] **Multi-Cursor:** - * [ ] Add virtual cursor support (edits apply to all locations). - * [ ] Add `Alt+Click` to set multiple cursors. - * [ ] Allow search and place cursor at all matches. +- [ ] **Multi-Cursor:** + - [ ] Add virtual cursor support (edits apply to all locations). + - [ ] Add `Alt+Click` to set multiple cursors. + - [ ] Allow search and place cursor at all matches. -* [ ] **Block Selection:** - * [ ] Double-clicking a bracket selects the whole block and sets mode to `WORD`. +- [ ] **Block Selection:** + - [ ] Double-clicking a bracket selects the whole block and sets mode to `WORD`. ### Visuals, UI & Extensions? -* [ ] Add color picker/palette (as a floating extention). +- [ ] Add color picker/palette (as a floating extention). -* [ ] **Git:** Add Git integration (status, diffs). -* [ ] **AI/Snippets:** - * [ ] Add snippets support (LuaSnip/VSnip style). - * [ ] Add Codeium/Copilot support (using VAI virtual text) as a test phase. - -* [ ] **SQL:** Add SQL support (Viewer and Basic Editor) (as ruby extension). -* [ ] **Prolly?:** Add Splash Screen / Minigame. +- [ ] **Git:** Add Git integration (status, diffs). +- [ ] **AI/Snippets:** + - [ ] Add snippets support (LuaSnip/VSnip style). + - [ ] Add Codeium/Copilot support (using VAI virtual text) as a test phase. +- [ ] **SQL:** Add SQL support (Viewer and Basic Editor) (as ruby extension). +- [ ] **Prolly?:** Add Splash Screen / Minigame. ### Unimportant optimizations -* [ ] **Performance:** - * [ ] Switch JSON parser to `RapidJSON` (or similar high-performance lib). - * [ ] Decrease usage of `std::string` in UI, LSP, warnings etc. - * [ ] Also for vectors into managed memory especially for completions/lsp-stuff. - - - +- [ ] **Performance:** + - [ ] Switch JSON parser to `RapidJSON` (or similar high-performance lib). + - [ ] Decrease usage of `std::string` in UI, LSP, warnings etc. + - [ ] Also for vectors into managed memory especially for completions/lsp-stuff. @@ -173,3 +178,59 @@ Copyright 2025 Syed Daanish + +whyy is visible returnint x1,y2 and x2, 2y2 instead of x1,y1 and x2,y2 + +Render H: 1, Cursor: 0,0, Visible: 0,26 - 0,52 +Render H: 2, Cursor: 0,0, Visible: 1,0 - 1,0 +Render H: 3, Cursor: 0,0, Visible: 2,30 - 2,60 +Render H: 4, Cursor: 0,0, Visible: 3,36 - 3,72 +Render H: 5, Cursor: 0,0, Visible: 4,32 - 4,64 +Render H: 6, Cursor: 0,0, Visible: 5,40 - 5,80 +Render H: 7, Cursor: 0,0, Visible: 6,11 - 6,22 +Render H: 8, Cursor: 0,0, Visible: 7,3 - 7,6 +Render H: 9, Cursor: 0,0, Visible: 8,19 - 8,38 +Render H: 10, Cursor: 0,0, Visible: 9,76 - 9,152 +Render H: 11, Cursor: 0,0, Visible: 10,50 - 10,100 +Render H: 12, Cursor: 0,0, Visible: 11,29 - 11,58 +Render H: 13, Cursor: 0,0, Visible: 12,49 - 12,98 +Render H: 14, Cursor: 0,0, Visible: 13,32 - 13,64 +Render H: 15, Cursor: 0,0, Visible: 14,30 - 14,60 +Render H: 16, Cursor: 0,0, Visible: 15,56 - 15,112 +Render H: 17, Cursor: 0,0, Visible: 16,42 - 16,84 +Render H: 18, Cursor: 0,0, Visible: 17,20 - 17,40 +Render H: 19, Cursor: 0,0, Visible: 18,31 - 18,62 +Render H: 20, Cursor: 0,0, Visible: 19,77 - 19,154 +Render H: 21, Cursor: 0,0, Visible: 20,77 - 20,154 +Render H: 22, Cursor: 0,0, Visible: 21,44 - 21,88 +Render H: 23, Cursor: 0,0, Visible: 22,56 - 22,112 +Render H: 24, Cursor: 0,0, Visible: 23,13 - 23,26 +Render H: 25, Cursor: 0,0, Visible: 24,3 - 24,6 +Render H: 26, Cursor: 0,0, Visible: 25,24 - 25,48 +Render H: 27, Cursor: 0,0, Visible: 26,32 - 26,64 +Render H: 28, Cursor: 0,0, Visible: 27,38 - 27,76 +Render H: 29, Cursor: 0,0, Visible: 28,1 - 28,2 +Render H: 30, Cursor: 0,0, Visible: 29,0 - 29,0 +Render H: 31, Cursor: 0,0, Visible: 30,30 - 30,60 +Render H: 32, Cursor: 0,0, Visible: 31,19 - 31,38 +Render H: 33, Cursor: 0,0, Visible: 32,76 - 32,152 +Render H: 34, Cursor: 0,0, Visible: 33,50 - 33,100 +Render H: 35, Cursor: 0,0, Visible: 34,36 - 34,72 +Render H: 36, Cursor: 0,0, Visible: 35,66 - 35,132 +Render H: 37, Cursor: 0,0, Visible: 36,12 - 36,24 +Render H: 38, Cursor: 0,0, Visible: 37,13 - 37,26 +Render H: 39, Cursor: 0,0, Visible: 38,17 - 38,34 +Render H: 40, Cursor: 0,0, Visible: 39,37 - 39,74 +Render H: 41, Cursor: 0,0, Visible: 40,16 - 40,32 +Render H: 42, Cursor: 0,0, Visible: 41,23 - 41,46 +Render H: 43, Cursor: 0,0, Visible: 42,15 - 42,30 +Render H: 44, Cursor: 0,0, Visible: 43,13 - 43,26 +Render H: 45, Cursor: 0,0, Visible: 44,5 - 44,10 +Render H: 46, Cursor: 0,0, Visible: 45,41 - 45,82 +Render H: 47, Cursor: 0,0, Visible: 46,12 - 46,24 +Render H: 48, Cursor: 0,0, Visible: 47,22 - 47,44 +Render H: 49, Cursor: 0,0, Visible: 48,24 - 48,48 +Render H: 50, Cursor: 0,0, Visible: 49,28 - 49,56 +Render H: 51, Cursor: 0,0, Visible: 50,26 - 50,52 +Render H: 52, Cursor: 0,0, Visible: 51,20 - 51,40 +Render H: 53, Cursor: 0,0, Visible: 52,74 - 52,148 diff --git a/config/main.rb b/config/main.rb old mode 100755 new mode 100644 diff --git a/config/theme.rb b/config/theme.rb old mode 100755 new mode 100644 diff --git a/include/editor/decl.h b/include/editor/decl.h old mode 100755 new mode 100644 diff --git a/include/editor/editor.h b/include/editor/editor.h old mode 100755 new mode 100644 index be19088..ca5ac6a --- a/include/editor/editor.h +++ b/include/editor/editor.h @@ -1,6 +1,7 @@ #ifndef EDITOR_H #define EDITOR_H +#include "editor/decl.h" #include "editor/hooks.h" #include "editor/indents.h" #include "editor/visual.h" @@ -101,6 +102,10 @@ struct Editor : Window { void lsp_handle(json msg); void apply_lsp_edits(std::vector edits, bool move); + // Not used by core editor, but can be used by + // extentions (due to cursor movement complexity) + void format_full(int version); + Coord move_left(Coord cursor, uint32_t number); Coord move_right(Coord cursor, uint32_t number); diff --git a/include/editor/helpers.h b/include/editor/helpers.h old mode 100755 new mode 100644 diff --git a/include/editor/hooks.h b/include/editor/hooks.h old mode 100755 new mode 100644 diff --git a/include/editor/indents.h b/include/editor/indents.h old mode 100755 new mode 100644 diff --git a/include/editor/visual.h b/include/editor/visual.h old mode 100755 new mode 100644 diff --git a/include/editor/completions.h b/include/extentions/completions.h old mode 100755 new mode 100644 similarity index 58% rename from include/editor/completions.h rename to include/extentions/completions.h index c2500cb..8b1e0a2 --- a/include/editor/completions.h +++ b/include/extentions/completions.h @@ -1,10 +1,11 @@ -#ifndef EDITOR_COMPLETIONS_H -#define EDITOR_COMPLETIONS_H +#ifndef EXTENTION_COMPLETION_H +#define EXTENTION_COMPLETION_H +#include "editor/decl.h" +#include "io/sysio.h" #include "pch.h" -#include "ui/completionbox.h" -#include "ui/hover.h" #include "utils/utils.h" +#include "windows/decl.h" struct CompletionItem { std::string label; @@ -22,12 +23,19 @@ struct CompletionItem { std::vector end_chars; }; +struct CompletionBox : Popup { + struct CompletionSession *session; + CompletionBox() { this->hidden = true; } + void render(std::vector &buffer, Coord size, Coord pos) override; + void handle_click(KeyEvent, Coord) override { this->hidden = true; } + ~CompletionBox() {} +}; + struct CompletionSession { struct Editor *editor; std::shared_mutex mtx; bool active = false; Coord hook; - std::optional prefix; uint32_t select = 0; uint32_t scroll = 0; std::vector items; @@ -35,14 +43,12 @@ struct CompletionSession { bool complete = true; std::optional trigger_char; uint8_t trigger = 0; - CompletionBox box; - HoverBox hover; + CompletionBox *box = nullptr; uint32_t doc = UINT32_MAX; std::atomic hover_dirty = false; int version; - CompletionSession(Editor *editor) : editor(editor), box(this) {} - + CompletionSession(Editor *editor) : editor(editor) {} void resolve_doc(); void accept(); void next(); @@ -51,4 +57,8 @@ struct CompletionSession { void handle(KeyEvent event); }; -#endif +CompletionBox *init_completion_box(CompletionSession *session); +void completion_request(Editor *editor); +void completion_filter(Editor *editor); + +#endif \ No newline at end of file diff --git a/include/extentions/diagnostics.h b/include/extentions/diagnostics.h old mode 100755 new mode 100644 diff --git a/include/extentions/hover.h b/include/extentions/hover.h old mode 100755 new mode 100644 diff --git a/include/io/knot.h b/include/io/knot.h old mode 100755 new mode 100644 diff --git a/include/io/sysio.h b/include/io/sysio.h old mode 100755 new mode 100644 diff --git a/include/lsp/lsp.h b/include/lsp/lsp.h old mode 100755 new mode 100644 diff --git a/include/main.h b/include/main.h old mode 100755 new mode 100644 diff --git a/include/pch.h b/include/pch.h old mode 100755 new mode 100644 diff --git a/include/ruby/decl.h b/include/ruby/decl.h old mode 100755 new mode 100644 diff --git a/include/ruby/libcrib.rb b/include/ruby/libcrib.rb old mode 100755 new mode 100644 diff --git a/include/ruby/ruby_compiled.h b/include/ruby/ruby_compiled.h old mode 100755 new mode 100644 diff --git a/include/syntax/decl.h b/include/syntax/decl.h old mode 100755 new mode 100644 diff --git a/include/syntax/extras.h b/include/syntax/extras.h old mode 100755 new mode 100644 diff --git a/include/syntax/langs.h b/include/syntax/langs.h old mode 100755 new mode 100644 diff --git a/include/syntax/line_map.h b/include/syntax/line_map.h old mode 100755 new mode 100644 diff --git a/include/syntax/parser.h b/include/syntax/parser.h old mode 100755 new mode 100644 diff --git a/include/syntax/tokens.def b/include/syntax/tokens.def old mode 100755 new mode 100644 diff --git a/include/syntax/trie.h b/include/syntax/trie.h old mode 100755 new mode 100644 diff --git a/include/ui/bar.h b/include/ui/bar.h old mode 100755 new mode 100644 diff --git a/include/ui/completionbox.h b/include/ui/completionbox.h deleted file mode 100755 index 917d602..0000000 --- a/include/ui/completionbox.h +++ /dev/null @@ -1,21 +0,0 @@ -// #ifndef UI_COMPLETIONBOX_H -// #define UI_COMPLETIONBOX_H -// -// #include "io/sysio.h" -// #include "pch.h" -// #include "utils/utils.h" -// -// struct CompletionBox { -// std::shared_mutex mtx; -// struct CompletionSession *session; -// bool hidden = true; -// std::vector cells; -// Coord size; -// Coord position; -// -// CompletionBox(CompletionSession *s) : session(s) {} -// void render_update(); -// void render(Coord pos); -// }; -// -// #endif diff --git a/include/utils/utils.h b/include/utils/utils.h old mode 100755 new mode 100644 diff --git a/include/windows/decl.h b/include/windows/decl.h old mode 100755 new mode 100644 diff --git a/libs/unicode_width/unicode_width.c b/libs/unicode_width/unicode_width.c old mode 100755 new mode 100644 diff --git a/libs/unicode_width/unicode_width.h b/libs/unicode_width/unicode_width.h old mode 100755 new mode 100644 diff --git a/samples/Makefile b/samples/Makefile old mode 100755 new mode 100644 diff --git a/samples/bash.sh b/samples/bash.sh old mode 100755 new mode 100644 diff --git a/samples/css.css b/samples/css.css old mode 100755 new mode 100644 diff --git a/samples/diff.patch b/samples/diff.patch old mode 100755 new mode 100644 diff --git a/samples/embedded_template.erb b/samples/embedded_template.erb old mode 100755 new mode 100644 diff --git a/samples/fish.fish b/samples/fish.fish old mode 100755 new mode 100644 diff --git a/samples/gdscript.gd b/samples/gdscript.gd old mode 100755 new mode 100644 diff --git a/samples/go.go b/samples/go.go old mode 100755 new mode 100644 diff --git a/samples/go.mod b/samples/go.mod old mode 100755 new mode 100644 diff --git a/samples/haskell.hs b/samples/haskell.hs old mode 100755 new mode 100644 diff --git a/samples/html.html b/samples/html.html old mode 100755 new mode 100644 diff --git a/samples/ini.ini b/samples/ini.ini old mode 100755 new mode 100644 diff --git a/samples/javascript.js b/samples/javascript.js old mode 100755 new mode 100644 diff --git a/samples/json.jsonc b/samples/json.jsonc old mode 100755 new mode 100644 diff --git a/samples/lua.lua b/samples/lua.lua old mode 100755 new mode 100644 diff --git a/samples/markdown.md b/samples/markdown.md old mode 100755 new mode 100644 diff --git a/samples/nginx.conf b/samples/nginx.conf old mode 100755 new mode 100644 diff --git a/samples/php.php b/samples/php.php old mode 100755 new mode 100644 diff --git a/samples/python.py b/samples/python.py old mode 100755 new mode 100644 diff --git a/samples/regex.regex b/samples/regex.regex old mode 100755 new mode 100644 diff --git a/samples/ruby.rb b/samples/ruby.rb old mode 100755 new mode 100644 diff --git a/samples/rust.rs b/samples/rust.rs old mode 100755 new mode 100644 diff --git a/samples/sample.gitattributes b/samples/sample.gitattributes old mode 100755 new mode 100644 diff --git a/samples/sample.gitignore b/samples/sample.gitignore old mode 100755 new mode 100644 diff --git a/samples/sql.sql b/samples/sql.sql old mode 100755 new mode 100644 diff --git a/samples/toml.toml b/samples/toml.toml old mode 100755 new mode 100644 diff --git a/samples/yaml.yaml b/samples/yaml.yaml old mode 100755 new mode 100644 diff --git a/src/editor/adjustment.cc b/src/editor/adjustment.cc old mode 100755 new mode 100644 index 95e24f2..1598b2e --- a/src/editor/adjustment.cc +++ b/src/editor/adjustment.cc @@ -17,7 +17,10 @@ void Editor::ensure_cursor() { render_height < this->size.row) { render_height++; last_visible = vline.first; - if (cursor >= vline.first && cursor < vline.second) + log("Render H: %d, Cursor: %d,%d, Visible: %d,%d - %d,%d", render_height, + this->cursor.row, this->cursor.col, vline.first.row, vline.first.col, + vline.second.row, vline.second.col); + if (cursor >= vline.first && cursor <= vline.second) return; } cursor = last_visible; diff --git a/src/editor/boundaries.cc b/src/editor/boundaries.cc old mode 100755 new mode 100644 diff --git a/src/editor/click.cc b/src/editor/click.cc old mode 100755 new mode 100644 diff --git a/src/editor/commands.cc b/src/editor/commands.cc old mode 100755 new mode 100644 diff --git a/src/editor/completions.cc b/src/editor/completions.cc old mode 100755 new mode 100644 index 0a613fd..c566c4a --- a/src/editor/completions.cc +++ b/src/editor/completions.cc @@ -1,458 +1,456 @@ -// #include "editor/decl.h" -// #include "editor/editor.h" -// #include "io/knot.h" -// #include "io/sysio.h" -// #include "lsp/lsp.h" -// #include "main.h" -// #include "ui/completionbox.h" -// #include "utils/utils.h" -// -// inline static std::string completion_prefix(Editor *editor) { -// Coord hook = editor->completion.hook; -// Coord cur = editor->cursor; -// if (hook.row != cur.row || cur.col < hook.col) -// return ""; -// LineIterator *it = begin_l_iter(editor->root, hook.row); -// uint32_t line_len; -// char *line = next_line(it, &line_len); -// if (!line) { -// free(it->buffer); -// free(it); -// return ""; -// } -// std::string prefix(line + hook.col, cur.col - hook.col); -// free(it->buffer); -// free(it); -// return prefix; -// } -// -// inline static void completion_adjust_scroll(CompletionSession &s) { -// if (s.visible.empty()) -// return; -// int vi = -1; -// for (size_t i = 0; i < s.visible.size(); i++) -// if (s.visible[i] == s.select) { -// vi = (int)i; -// break; -// } -// if (vi < 0) -// return; -// if ((uint32_t)vi < s.scroll) -// s.scroll = vi; -// else if ((uint32_t)vi >= s.scroll + 8) -// s.scroll = vi - 7; -// } -// -// void completion_filter(Editor *editor) { -// auto &session = editor->completion; -// std::string prefix = completion_prefix(editor); -// session.visible.clear(); -// for (size_t i = 0; i < session.items.size(); ++i) { -// const auto &item = session.items[i]; -// const std::string &key = item.filter; -// if (key.size() >= prefix.size() && -// key.compare(0, prefix.size(), prefix) == 0) -// session.visible.push_back(i); -// } -// if (session.visible.empty()) { -// session.box.hidden = true; -// return; -// } -// if (std::find(session.visible.begin(), session.visible.end(), -// session.select) == session.visible.end()) -// session.select = session.visible[0]; -// session.box.hidden = false; -// session.scroll = 0; -// completion_adjust_scroll(session); -// session.box.render_update(); -// } -// -// void completion_request(Editor *editor) { -// Coord hook = editor->cursor; -// editor->word_boundaries(editor->cursor, &hook.col, nullptr, nullptr, -// nullptr); editor->completion.hook = hook; editor->completion.complete = -// false; editor->completion.active = false; editor->completion.items.clear(); -// editor->completion.visible.clear(); -// editor->completion.select = 0; -// editor->completion.version = editor->lsp_version; -// LSPPending *pending = new LSPPending(); -// pending->editor = editor; -// pending->callback = [](Editor *editor, const json &message) { -// auto &session = editor->completion; -// std::unique_lock lock(session.mtx); -// std::vector items_json; -// std::vector end_chars_def; -// int insert_text_format = 1; -// int insert_text_mode = 1; -// if (message.contains("result")) { -// auto &result = message["result"]; -// if (result.is_array()) { -// items_json = result.get>(); -// session.complete = true; -// if (items_json.empty()) -// return; -// editor->completion.active = true; -// } else if (result.is_object() && result.contains("items")) { -// auto &list = result; -// items_json = list["items"].get>(); -// if (items_json.empty()) -// return; -// editor->completion.active = true; -// session.complete = !list.value("isIncomplete", false); -// if (list.contains("itemDefaults") && -// list["itemDefaults"].is_object()) { -// auto &defs = list["itemDefaults"]; -// if (defs.contains("insertTextFormat") && -// defs["insertTextFormat"].is_number()) -// insert_text_format = defs["insertTextFormat"].get(); -// if (defs.contains("insertTextMode") && -// defs["insertTextMode"].is_number()) -// insert_text_mode = defs["insertTextMode"].get(); -// if (defs.contains("textEdit")) -// if (defs["textEdit"].is_array()) -// for (auto &c : defs["textEdit"]) { -// if (!c.is_string()) -// continue; -// std::string str = c.get(); -// if (str.size() != 1) -// continue; -// end_chars_def.push_back(str[0]); -// } -// } -// } -// } -// session.items.reserve(items_json.size() + 1); -// session.visible.reserve(items_json.size() + 1); -// for (auto &item_json : items_json) { -// CompletionItem item; -// item.original = item_json; -// item.label = item_json.value("label", ""); -// item.kind = item_json.value("kind", 0); -// if (item_json.contains("detail") && item_json["detail"].is_string()) -// item.detail = item_json["detail"].get(); -// if (item_json.contains("documentation")) { -// if (item_json["documentation"].is_string()) { -// item.documentation = item_json["documentation"].get(); -// } else if (item_json["documentation"].contains("value") && -// item_json["documentation"]["value"].is_string()) { -// item.is_markup = -// item_json["documentation"]["kind"].get() == -// "markdown"; -// std::string documentation = -// item_json["documentation"]["value"].get(); -// if (documentation.size() > 1024) -// item.is_markup = false; -// if (item.is_markup) { -// item.documentation = -// substitute_fence(documentation, editor->lang.name); -// } else { -// item.documentation = documentation; -// } -// } -// } -// if (item_json.contains("deprecated") && -// item_json["deprecated"].is_boolean()) -// item.deprecated = item_json["deprecated"].get(); -// auto tags = item_json.value("tags", std::vector()); -// for (auto tag : tags) -// if (tag == 1) -// item.deprecated = true; -// item.sort = item_json.value("sortText", item.label); -// item.filter = item_json.value("filterText", item.label); -// if (item_json.contains("preselect") && -// item_json["preselect"].is_boolean() && -// item_json["preselect"].get()) -// session.select = session.items.size() - 1; -// TextEdit edit; -// if (item_json.contains("textEdit")) { -// auto &te = item_json["textEdit"]; -// if (te.contains("newText")) { -// edit.text = te.value("newText", ""); -// if (te.contains("replace")) { -// edit.start.row = te["replace"]["start"]["line"]; -// edit.start.col = te["replace"]["start"]["character"]; -// edit.end.row = te["replace"]["end"]["line"]; -// edit.end.col = te["replace"]["end"]["character"]; -// } else if (te.contains("insert")) { -// edit.start.row = te["insert"]["start"]["line"]; -// edit.start.col = te["insert"]["start"]["character"]; -// edit.end.row = te["insert"]["end"]["line"]; -// edit.end.col = te["insert"]["end"]["character"]; -// } else if (te.contains("range")) { -// edit.start.row = te["range"]["start"]["line"]; -// edit.start.col = te["range"]["start"]["character"]; -// edit.end.row = te["range"]["end"]["line"]; -// edit.end.col = te["range"]["end"]["character"]; -// } else { -// edit.start = session.hook; -// edit.end = editor->cursor; -// } -// } -// } else if (item_json.contains("insertText") && -// item_json["insertText"].is_string()) { -// edit.text = item_json["insertText"].get(); -// edit.start = session.hook; -// edit.end = editor->cursor; -// } else { -// edit.text = item.label; -// edit.start = session.hook; -// edit.end = editor->cursor; -// } -// editor->utf8_normalize_edit(&edit); -// item.edits.push_back(edit); -// if (item_json.contains("additionalTextEdits")) { -// for (auto &te : item_json["additionalTextEdits"]) { -// TextEdit edit; -// edit.text = te.value("newText", ""); -// edit.start.row = te["range"]["start"]["line"]; -// edit.start.col = te["range"]["start"]["character"]; -// edit.end.row = te["range"]["end"]["line"]; -// edit.end.col = te["range"]["end"]["character"]; -// editor->utf8_normalize_edit(&edit); -// item.edits.push_back(edit); -// } -// } -// item.snippet = insert_text_format == 2; -// if (item_json.contains("insertTextFormat")) -// item.snippet = item_json["insertTextFormat"].get() == 2; -// if (item_json.contains("insertTextMode")) -// item.asis = item_json["insertTextMode"].get() == 1; -// if (item_json.contains("commitCharacters")) -// for (auto &c : item_json["commitCharacters"]) -// if (c.is_string() && c.get().size() == 1) -// item.end_chars.push_back(c.get()[0]); -// session.items.push_back(std::move(item)); -// session.visible.push_back(session.items.size() - 1); -// } -// session.box.hidden = false; -// session.box.render_update(); -// }; -// LineIterator *it = begin_l_iter(editor->root, hook.row); -// uint32_t length; -// char *line = next_line(it, &length); -// if (!line) { -// free(it->buffer); -// free(it); -// return; -// } -// uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col); -// free(it->buffer); -// free(it); -// json message = { -// {"jsonrpc", "2.0"}, -// {"method", "textDocument/completion"}, -// {"params", -// {{"textDocument", {{"uri", editor->uri}}}, -// {"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; -// if (editor->completion.trigger > 0) { -// json context = {{"triggerKind", editor->completion.trigger}}; -// if (editor->completion.trigger == 2 && editor->completion.trigger_char) -// context["triggerCharacter"] = -// std::string(1, *editor->completion.trigger_char); -// message["params"]["context"] = context; -// } -// lsp_send(editor->lsp, message, pending); -// } -// -// // Move this into the box and merge the box and this guy -// void CompletionSession::handle(KeyEvent event) { -// if (!editor->lsp || !editor->lsp->allow_completion) -// return; -// if (mode != INSERT) { -// this->active = false; -// return; -// } -// std::unique_lock lock(this->mtx); -// if (event.key_type == KEY_PASTE) { -// this->active = false; -// return; -// } else if (event.key_type == KEY_CHAR) { -// char ch = *event.c; -// if (!this->active) { -// for (char c : editor->lsp->trigger_chars) -// if (c == ch) { -// this->trigger = 2; -// this->trigger_char = c; -// completion_request(editor); -// return; -// } -// } else { -// if (!editor->completion.items.empty()) { -// const auto &item = -// editor->completion.items[editor->completion.select]; const -// std::vector &end_chars = -// item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars; -// for (char c : end_chars) -// if (c == ch) { -// this->accept(); -// return; -// } -// } -// } -// if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || -// (ch >= '0' && ch <= '9') || ch == '_') { -// if (this->active) { -// if (this->complete) -// completion_filter(editor); -// else -// completion_request(editor); -// } else { -// this->trigger = 3; -// completion_request(editor); -// } -// } else if (ch == CTRL('\\')) { -// if (this->active && !this->visible.empty()) { -// this->accept(); -// } else { -// this->trigger = 1; -// completion_request(editor); -// } -// } else if (ch == CTRL('p')) { -// if (this->active) -// this->next(); -// } else if (ch == CTRL('o')) { -// if (this->active) -// this->prev(); -// } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) { -// if (this->active) { -// if (this->complete) { -// if (editor->cursor <= this->hook) -// this->active = false; -// else -// completion_filter(editor); -// } else { -// if (editor->cursor <= this->hook) -// this->active = false; -// else -// completion_request(editor); -// } -// } -// } else { -// this->active = false; -// } -// } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) { -// // Prolly add mouse support here -// // auto &box = this->box; -// // if (event.mouse_y >= box.position.row && -// // event.mouse_x >= box.position.col) { -// // uint32_t row = event.mouse_y - box.position.row; -// // uint32_t col = event.mouse_x - box.position.col; -// // if (row < box.size.row && col < box.size.col) { -// // uint8_t idx = 0; -// // /* TODO: fix index relative to scroll */ -// // complete_select(editor, idx); -// // } -// // } -// // if it is being implemented then stop main event handler from -// processing -// // when click inside the box -// this->active = false; -// } else { -// this->active = false; -// } -// } -// -// void CompletionSession::resolve_doc() { -// auto &item = this->items[this->select]; -// if (item.documentation) -// return; -// item.documentation = ""; -// LSPPending *pending = new LSPPending(); -// pending->editor = editor; -// pending->callback = [](Editor *editor, const json &message) { -// std::unique_lock lock(editor->completion.mtx); -// auto &item = editor->completion.items[editor->completion.select]; -// if (message["result"].contains("documentation")) { -// if (message["result"]["documentation"].is_string()) { -// item.documentation = -// message["result"]["documentation"].get(); -// } else if (message["result"]["documentation"].contains("value") && -// message["result"]["documentation"]["value"].is_string()) { -// item.is_markup = -// message["result"]["documentation"]["kind"].get() == -// "markdown"; -// std::string documentation = -// message["result"]["documentation"]["value"].get(); -// if (documentation.size() > 1024) -// item.is_markup = false; -// if (item.is_markup) { -// item.documentation = -// substitute_fence(documentation, editor->lang.name); -// } else { -// item.documentation = documentation; -// } -// } -// } -// editor->completion.box.render_update(); -// }; -// json message = {{"jsonrpc", "2.0"}, -// {"method", "completionItem/resolve"}, -// {"params", item.original}}; -// lsp_send(editor->lsp, message, pending); -// } -// -// void CompletionSession::accept() { -// if (!this->active || this->box.hidden) -// return; -// auto &item = this->items[this->select]; -// // TODO: support snippets and asis here -// // once indentation engine is implemented -// if (this->version != editor->lsp_version) { -// int delta_col = 0; -// TextEdit &e = item.edits[0]; -// if (e.end.row == editor->cursor.row) { -// delta_col = editor->cursor.col - e.end.col; -// e.end.col = editor->cursor.col; -// for (size_t i = 1; i < item.edits.size(); ++i) { -// TextEdit &e = item.edits[i]; -// if (e.start.row == editor->cursor.row) { -// e.start.col += delta_col; -// if (e.end.row == editor->cursor.row) -// e.end.col += delta_col; -// } -// } -// } -// } -// editor->apply_lsp_edits(item.edits, true); -// this->active = false; -// } -// -// inline static int visible_index(const CompletionSession &s) { -// for (size_t i = 0; i < s.visible.size(); ++i) -// if (s.visible[i] == s.select) -// return (int)i; -// return -1; -// } -// -// void CompletionSession::next() { -// if (!this->active || this->box.hidden || this->visible.empty()) -// return; -// int vi = visible_index(*this); -// if (vi < 0) -// vi = 0; -// else -// vi = (vi + 1) % this->visible.size(); -// this->select = this->visible[vi]; -// this->resolve_doc(); -// completion_adjust_scroll(*this); -// this->box.render_update(); -// } -// -// void CompletionSession::prev() { -// if (!this->active || this->box.hidden || this->visible.empty()) -// return; -// int vi = visible_index(*this); -// if (vi < 0) -// vi = 0; -// else -// vi = (vi + this->visible.size() - 1) % this->visible.size(); -// this->select = this->visible[vi]; -// this->resolve_doc(); -// completion_adjust_scroll(*this); -// this->box.render_update(); -// } -// -// void CompletionSession::choose(uint8_t index) { -// this->select = index; -// this->accept(); -// } +/* #include "editor/editor.h" +#include "io/knot.h" +#include "io/sysio.h" +#include "main.h" +#include "ui/completionbox.h" +#include "utils/utils.h" + +inline static std::string completion_prefix(Editor *editor) { + Coord hook = editor->completion.hook; + Coord cur = editor->cursor; + if (hook.row != cur.row || cur.col < hook.col) + return ""; + LineIterator *it = begin_l_iter(editor->root, hook.row); + uint32_t line_len; + char *line = next_line(it, &line_len); + if (!line) { + free(it->buffer); + free(it); + return ""; + } + std::string prefix(line + hook.col, cur.col - hook.col); + free(it->buffer); + free(it); + return prefix; +} + +inline static void completion_adjust_scroll(CompletionSession &s) { + if (s.visible.empty()) + return; + int vi = -1; + for (size_t i = 0; i < s.visible.size(); i++) + if (s.visible[i] == s.select) { + vi = (int)i; + break; + } + if (vi < 0) + return; + if ((uint32_t)vi < s.scroll) + s.scroll = vi; + else if ((uint32_t)vi >= s.scroll + 8) + s.scroll = vi - 7; +} + +void completion_filter(Editor *editor) { + auto &session = editor->completion; + std::string prefix = completion_prefix(editor); + session.visible.clear(); + for (size_t i = 0; i < session.items.size(); ++i) { + const auto &item = session.items[i]; + const std::string &key = item.filter; + if (key.size() >= prefix.size() && + key.compare(0, prefix.size(), prefix) == 0) + session.visible.push_back(i); + } + if (session.visible.empty()) { + session.box.hidden = true; + return; + } + if (std::find(session.visible.begin(), session.visible.end(), + session.select) == session.visible.end()) + session.select = session.visible[0]; + session.box.hidden = false; + session.scroll = 0; + completion_adjust_scroll(session); + session.box.render_update(); +} + +void completion_request(Editor *editor) { + Coord hook = editor->cursor; + editor->word_boundaries(editor->cursor, &hook.col, nullptr, nullptr, + nullptr); editor->completion.hook = hook; editor->completion.complete = + false; editor->completion.active = false; editor->completion.items.clear(); + editor->completion.visible.clear(); + editor->completion.select = 0; + editor->completion.version = editor->lsp_version; + LSPPending *pending = new LSPPending(); + pending->editor = editor; + pending->callback = [](Editor *editor, const json &message) { + auto &session = editor->completion; + std::unique_lock lock(session.mtx); + std::vector items_json; + std::vector end_chars_def; + int insert_text_format = 1; + int insert_text_mode = 1; + if (message.contains("result")) { + auto &result = message["result"]; + if (result.is_array()) { + items_json = result.get>(); + session.complete = true; + if (items_json.empty()) + return; + editor->completion.active = true; + } else if (result.is_object() && result.contains("items")) { + auto &list = result; + items_json = list["items"].get>(); + if (items_json.empty()) + return; + editor->completion.active = true; + session.complete = !list.value("isIncomplete", false); + if (list.contains("itemDefaults") && + list["itemDefaults"].is_object()) { + auto &defs = list["itemDefaults"]; + if (defs.contains("insertTextFormat") && + defs["insertTextFormat"].is_number()) + insert_text_format = defs["insertTextFormat"].get(); + if (defs.contains("insertTextMode") && + defs["insertTextMode"].is_number()) + insert_text_mode = defs["insertTextMode"].get(); + if (defs.contains("textEdit")) + if (defs["textEdit"].is_array()) + for (auto &c : defs["textEdit"]) { + if (!c.is_string()) + continue; + std::string str = c.get(); + if (str.size() != 1) + continue; + end_chars_def.push_back(str[0]); + } + } + } + } + session.items.reserve(items_json.size() + 1); + session.visible.reserve(items_json.size() + 1); + for (auto &item_json : items_json) { + CompletionItem item; + item.original = item_json; + item.label = item_json.value("label", ""); + item.kind = item_json.value("kind", 0); + if (item_json.contains("detail") && item_json["detail"].is_string()) + item.detail = item_json["detail"].get(); + if (item_json.contains("documentation")) { + if (item_json["documentation"].is_string()) { + item.documentation = item_json["documentation"].get(); + } else if (item_json["documentation"].contains("value") && + item_json["documentation"]["value"].is_string()) { + item.is_markup = + item_json["documentation"]["kind"].get() == + "markdown"; + std::string documentation = + item_json["documentation"]["value"].get(); + if (documentation.size() > 1024) + item.is_markup = false; + if (item.is_markup) { + item.documentation = + substitute_fence(documentation, editor->lang.name); + } else { + item.documentation = documentation; + } + } + } + if (item_json.contains("deprecated") && + item_json["deprecated"].is_boolean()) + item.deprecated = item_json["deprecated"].get(); + auto tags = item_json.value("tags", std::vector()); + for (auto tag : tags) + if (tag == 1) + item.deprecated = true; + item.sort = item_json.value("sortText", item.label); + item.filter = item_json.value("filterText", item.label); + if (item_json.contains("preselect") && + item_json["preselect"].is_boolean() && + item_json["preselect"].get()) + session.select = session.items.size() - 1; + TextEdit edit; + if (item_json.contains("textEdit")) { + auto &te = item_json["textEdit"]; + if (te.contains("newText")) { + edit.text = te.value("newText", ""); + if (te.contains("replace")) { + edit.start.row = te["replace"]["start"]["line"]; + edit.start.col = te["replace"]["start"]["character"]; + edit.end.row = te["replace"]["end"]["line"]; + edit.end.col = te["replace"]["end"]["character"]; + } else if (te.contains("insert")) { + edit.start.row = te["insert"]["start"]["line"]; + edit.start.col = te["insert"]["start"]["character"]; + edit.end.row = te["insert"]["end"]["line"]; + edit.end.col = te["insert"]["end"]["character"]; + } else if (te.contains("range")) { + edit.start.row = te["range"]["start"]["line"]; + edit.start.col = te["range"]["start"]["character"]; + edit.end.row = te["range"]["end"]["line"]; + edit.end.col = te["range"]["end"]["character"]; + } else { + edit.start = session.hook; + edit.end = editor->cursor; + } + } + } else if (item_json.contains("insertText") && + item_json["insertText"].is_string()) { + edit.text = item_json["insertText"].get(); + edit.start = session.hook; + edit.end = editor->cursor; + } else { + edit.text = item.label; + edit.start = session.hook; + edit.end = editor->cursor; + } + editor->utf8_normalize_edit(&edit); + item.edits.push_back(edit); + if (item_json.contains("additionalTextEdits")) { + for (auto &te : item_json["additionalTextEdits"]) { + TextEdit edit; + edit.text = te.value("newText", ""); + edit.start.row = te["range"]["start"]["line"]; + edit.start.col = te["range"]["start"]["character"]; + edit.end.row = te["range"]["end"]["line"]; + edit.end.col = te["range"]["end"]["character"]; + editor->utf8_normalize_edit(&edit); + item.edits.push_back(edit); + } + } + item.snippet = insert_text_format == 2; + if (item_json.contains("insertTextFormat")) + item.snippet = item_json["insertTextFormat"].get() == 2; + if (item_json.contains("insertTextMode")) + item.asis = item_json["insertTextMode"].get() == 1; + if (item_json.contains("commitCharacters")) + for (auto &c : item_json["commitCharacters"]) + if (c.is_string() && c.get().size() == 1) + item.end_chars.push_back(c.get()[0]); + session.items.push_back(std::move(item)); + session.visible.push_back(session.items.size() - 1); + } + session.box.hidden = false; + session.box.render_update(); + }; + LineIterator *it = begin_l_iter(editor->root, hook.row); + uint32_t length; + char *line = next_line(it, &length); + if (!line) { + free(it->buffer); + free(it); + return; + } + uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col); + free(it->buffer); + free(it); + json message = { + {"jsonrpc", "2.0"}, + {"method", "textDocument/completion"}, + {"params", + {{"textDocument", {{"uri", editor->uri}}}, + {"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; + if (editor->completion.trigger > 0) { + json context = {{"triggerKind", editor->completion.trigger}}; + if (editor->completion.trigger == 2 && editor->completion.trigger_char) + context["triggerCharacter"] = + std::string(1, *editor->completion.trigger_char); + message["params"]["context"] = context; + } + lsp_send(editor->lsp, message, pending); +} + +Move this into the box and merge the box and this guy +void CompletionSession::handle(KeyEvent event) { + if (!editor->lsp || !editor->lsp->allow_completion) + return; + if (mode != INSERT) { + this->active = false; + return; + } + std::unique_lock lock(this->mtx); + if (event.key_type == KEY_PASTE) { + this->active = false; + return; + } else if (event.key_type == KEY_CHAR) { + char ch = *event.c; + if (!this->active) { + for (char c : editor->lsp->trigger_chars) + if (c == ch) { + this->trigger = 2; + this->trigger_char = c; + completion_request(editor); + return; + } + } else { + if (!editor->completion.items.empty()) { + const auto &item = + editor->completion.items[editor->completion.select]; const + std::vector &end_chars = + item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars; + for (char c : end_chars) + if (c == ch) { + this->accept(); + return; + } + } + } + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch == '_') { + if (this->active) { + if (this->complete) + completion_filter(editor); + else + completion_request(editor); + } else { + this->trigger = 3; + completion_request(editor); + } + } else if (ch == CTRL('\\')) { + if (this->active && !this->visible.empty()) { + this->accept(); + } else { + this->trigger = 1; + completion_request(editor); + } + } else if (ch == CTRL('p')) { + if (this->active) + this->next(); + } else if (ch == CTRL('o')) { + if (this->active) + this->prev(); + } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) { + if (this->active) { + if (this->complete) { + if (editor->cursor <= this->hook) + this->active = false; + else + completion_filter(editor); + } else { + if (editor->cursor <= this->hook) + this->active = false; + else + completion_request(editor); + } + } + } else { + this->active = false; + } + } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) { + Prolly add mouse support here + auto &box = this->box; + if (event.mouse_y >= box.position.row && + event.mouse_x >= box.position.col) { + uint32_t row = event.mouse_y - box.position.row; + uint32_t col = event.mouse_x - box.position.col; + if (row < box.size.row && col < box.size.col) { + uint8_t idx = 0; + complete_select(editor, idx); + } + } + if it is being implemented then stop main event handler from + processing + when click inside the box + this->active = false; + } else { + this->active = false; + } +} + +void CompletionSession::resolve_doc() { + auto &item = this->items[this->select]; + if (item.documentation) + return; + item.documentation = ""; + LSPPending *pending = new LSPPending(); + pending->editor = editor; + pending->callback = [](Editor *editor, const json &message) { + std::unique_lock lock(editor->completion.mtx); + auto &item = editor->completion.items[editor->completion.select]; + if (message["result"].contains("documentation")) { + if (message["result"]["documentation"].is_string()) { + item.documentation = + message["result"]["documentation"].get(); + } else if (message["result"]["documentation"].contains("value") && + message["result"]["documentation"]["value"].is_string()) { + item.is_markup = + message["result"]["documentation"]["kind"].get() == + "markdown"; + std::string documentation = + message["result"]["documentation"]["value"].get(); + if (documentation.size() > 1024) + item.is_markup = false; + if (item.is_markup) { + item.documentation = + substitute_fence(documentation, editor->lang.name); + } else { + item.documentation = documentation; + } + } + } + editor->completion.box.render_update(); + }; + json message = {{"jsonrpc", "2.0"}, + {"method", "completionItem/resolve"}, + {"params", item.original}}; + lsp_send(editor->lsp, message, pending); +} + +void CompletionSession::accept() { + if (!this->active || this->box.hidden) + return; + auto &item = this->items[this->select]; + TODO: support snippets and asis here + once indentation engine is implemented + if (this->version != editor->lsp_version) { + int delta_col = 0; + TextEdit &e = item.edits[0]; + if (e.end.row == editor->cursor.row) { + delta_col = editor->cursor.col - e.end.col; + e.end.col = editor->cursor.col; + for (size_t i = 1; i < item.edits.size(); ++i) { + TextEdit &e = item.edits[i]; + if (e.start.row == editor->cursor.row) { + e.start.col += delta_col; + if (e.end.row == editor->cursor.row) + e.end.col += delta_col; + } + } + } + } + editor->apply_lsp_edits(item.edits, true); + this->active = false; +} + +inline static int visible_index(const CompletionSession &s) { + for (size_t i = 0; i < s.visible.size(); ++i) + if (s.visible[i] == s.select) + return (int)i; + return -1; +} + +void CompletionSession::next() { + if (!this->active || this->box.hidden || this->visible.empty()) + return; + int vi = visible_index(*this); + if (vi < 0) + vi = 0; + else + vi = (vi + 1) % this->visible.size(); + this->select = this->visible[vi]; + this->resolve_doc(); + completion_adjust_scroll(*this); + this->box.render_update(); +} + +void CompletionSession::prev() { + if (!this->active || this->box.hidden || this->visible.empty()) + return; + int vi = visible_index(*this); + if (vi < 0) + vi = 0; + else + vi = (vi + this->visible.size() - 1) % this->visible.size(); + this->select = this->visible[vi]; + this->resolve_doc(); + completion_adjust_scroll(*this); + this->box.render_update(); +} + +void CompletionSession::choose(uint8_t index) { + this->select = index; + this->accept(); +} + */ \ No newline at end of file diff --git a/src/editor/cursor.cc b/src/editor/cursor.cc old mode 100755 new mode 100644 diff --git a/src/editor/edit.cc b/src/editor/edit.cc old mode 100755 new mode 100644 diff --git a/src/editor/editor.cc b/src/editor/editor.cc old mode 100755 new mode 100644 index e6e069e..ddb80f9 --- a/src/editor/editor.cc +++ b/src/editor/editor.cc @@ -1,5 +1,4 @@ #include "editor/editor.h" -#include "editor/decl.h" #include "lsp/lsp.h" #include "main.h" #include "syntax/langs.h" @@ -58,7 +57,6 @@ Editor::~Editor() { void Editor::save() { if (!this->root) return; - int version = this->lsp_version; uint32_t char_count = this->root->char_count; char *str = read(this->root, 0, char_count); if (!str) @@ -77,70 +75,4 @@ void Editor::save() { free(str); ui::bar.log("Written " + std::to_string(char_count) + " bytes to " + this->filename); - auto lsp = this->lsp.load(); - if (lsp) { - log("Saving %s", this->filename.c_str()); - auto message = std::make_unique(); - message->message = {{"method", "textDocument/didSave"}, - {"params", {{"textDocument", {{"uri", this->uri}}}}}}; - lsp->send(std::move(message)); - if (lsp->allow_formatting) { - log("Formatting %s", this->filename.c_str()); - json s_msg = {{"method", "textDocument/formatting"}, - {"params", - {{"textDocument", {{"uri", this->uri}}}, - {"options", - {{"tabSize", 2}, - {"insertSpaces", true}, - {"trimTrailingWhitespace", true}, - {"trimFinalNewlines", true}}}}}}; - auto save_msg = std::make_unique(); - save_msg->editor = this; - save_msg->message = s_msg; - save_msg->callback = [s_msg, version](const LSPMessage &msg) { - log("Formattin"); - if (version != msg.editor->lsp_version) - return; - auto &edits = msg.message["result"]; - if (edits.is_array()) { - std::vector t_edits; - t_edits.reserve(edits.size()); - for (auto &edit : edits) { - TextEdit t_edit; - t_edit.text = edit.value("newText", ""); - t_edit.start.row = edit["range"]["start"]["line"]; - t_edit.start.col = edit["range"]["start"]["character"]; - t_edit.end.row = edit["range"]["end"]["line"]; - t_edit.end.col = edit["range"]["end"]["character"]; - msg.editor->utf8_normalize_edit(&t_edit); - t_edits.push_back(t_edit); - } - msg.editor->apply_lsp_edits(t_edits, false); - msg.editor->ensure_cursor(); - uint32_t char_count = msg.editor->root->char_count; - char *str = read(msg.editor->root, 0, char_count); - if (!str) - return; - std::ofstream out(msg.editor->filename); - if (!msg.editor->unix_eol) { - for (uint32_t i = 0; i < char_count; ++i) { - if (str[i] == '\n') - out.put('\r'); - out.put(str[i]); - } - } else { - out.write(str, char_count); - } - out.close(); - free(str); - auto save_msg = std::make_unique(); - save_msg->editor = msg.editor; - save_msg->message = s_msg; - save_msg->callback = [](const LSPMessage &) {}; - msg.editor->lsp.load()->send(std::move(save_msg)); - } - }; - lsp->send(std::move(save_msg)); - } - } } diff --git a/src/editor/events.cc b/src/editor/events.cc old mode 100755 new mode 100644 diff --git a/src/editor/fluff.cc b/src/editor/fluff.cc old mode 100755 new mode 100644 diff --git a/src/editor/helpers.cc b/src/editor/helpers.cc old mode 100755 new mode 100644 index ed66b2a..ef87f02 --- a/src/editor/helpers.cc +++ b/src/editor/helpers.cc @@ -238,6 +238,73 @@ void Editor::insert_char(char c) { } } +void Editor::format_full(int version) { + auto lsp = this->lsp.load(); + if (lsp) { + log("Saving %s", this->filename.c_str()); + auto message = std::make_unique(); + message->message = {{"method", "textDocument/didSave"}, + {"params", {{"textDocument", {{"uri", this->uri}}}}}}; + lsp->send(std::move(message)); + if (lsp->allow_formatting) { + json s_msg = {{"method", "textDocument/formatting"}, + {"params", + {{"textDocument", {{"uri", this->uri}}}, + {"options", + {{"tabSize", 2}, + {"insertSpaces", true}, + {"trimTrailingWhitespace", true}, + {"trimFinalNewlines", true}}}}}}; + auto save_msg = std::make_unique(); + save_msg->editor = this; + save_msg->message = s_msg; + save_msg->callback = [s_msg, version](const LSPMessage &msg) { + if (version != msg.editor->lsp_version) + return; + auto &edits = msg.message["result"]; + if (edits.is_array()) { + std::vector t_edits; + t_edits.reserve(edits.size()); + for (auto &edit : edits) { + TextEdit t_edit; + t_edit.text = edit.value("newText", ""); + t_edit.start.row = edit["range"]["start"]["line"]; + t_edit.start.col = edit["range"]["start"]["character"]; + t_edit.end.row = edit["range"]["end"]["line"]; + t_edit.end.col = edit["range"]["end"]["character"]; + msg.editor->utf8_normalize_edit(&t_edit); + t_edits.push_back(t_edit); + } + msg.editor->apply_lsp_edits(t_edits, false); + msg.editor->ensure_cursor(); + uint32_t char_count = msg.editor->root->char_count; + char *str = read(msg.editor->root, 0, char_count); + if (!str) + return; + std::ofstream out(msg.editor->filename); + if (!msg.editor->unix_eol) { + for (uint32_t i = 0; i < char_count; ++i) { + if (str[i] == '\n') + out.put('\r'); + out.put(str[i]); + } + } else { + out.write(str, char_count); + } + out.close(); + free(str); + auto save_msg = std::make_unique(); + save_msg->editor = msg.editor; + save_msg->message = s_msg; + save_msg->callback = [](const LSPMessage &) {}; + msg.editor->lsp.load()->send(std::move(save_msg)); + } + }; + lsp->send(std::move(save_msg)); + } + } +} + void Editor::normal_mode() { Coord prev_pos = this->cursor; mode = NORMAL; @@ -431,19 +498,13 @@ void Editor::handle_click(KeyEvent event, Coord size) { case SCROLL: switch (event.mouse_direction) { case SCROLL_UP: - this->scroll_up(4); + this->scroll_up(1); this->ensure_cursor(); break; case SCROLL_DOWN: - this->scroll_down(4); + this->scroll_down(1); this->ensure_cursor(); break; - case SCROLL_LEFT: - this->cursor_left(10); - break; - case SCROLL_RIGHT: - this->cursor_right(10); - break; } break; case PRESS: diff --git a/src/editor/indents.cc b/src/editor/indents.cc old mode 100755 new mode 100644 diff --git a/src/editor/lsp.cc b/src/editor/lsp.cc old mode 100755 new mode 100644 diff --git a/src/editor/move_line.cc b/src/editor/move_line.cc old mode 100755 new mode 100644 diff --git a/src/editor/renderer.cc b/src/editor/renderer.cc old mode 100755 new mode 100644 index 4e51289..fa53f61 --- a/src/editor/renderer.cc +++ b/src/editor/renderer.cc @@ -9,7 +9,7 @@ void Editor::render(std::vector &buffer, Coord size, Coord pos) { uint32_t sel_start = 0, sel_end = 0; uint32_t numlen = EXTRA_META + static_cast(std::log10(this->root->line_count + 1)); - uint32_t render_width = size.col - numlen; + uint32_t render_width = size.col - numlen - 1; uint32_t render_x = pos.col + numlen + 1; this->hooks.start_iter(this->scroll.row); auto warn_it = this->warnings.begin(); diff --git a/src/editor/scroll.cc b/src/editor/scroll.cc old mode 100755 new mode 100644 diff --git a/src/editor/selection.cc b/src/editor/selection.cc old mode 100755 new mode 100644 diff --git a/src/editor/worker.cc b/src/editor/worker.cc old mode 100755 new mode 100644 diff --git a/src/extentions/completion.cc b/src/extentions/completion.cc old mode 100755 new mode 100644 diff --git a/src/extentions/diagnostics.cc b/src/extentions/diagnostics.cc old mode 100755 new mode 100644 index 8e5c8a0..0907d7b --- a/src/extentions/diagnostics.cc +++ b/src/extentions/diagnostics.cc @@ -1,5 +1,4 @@ #include "extentions/diagnostics.h" -#include DiagnosticBox *init_diagnostic() { auto diagnostic = std::make_unique(); @@ -11,6 +10,8 @@ DiagnosticBox *init_diagnostic() { return ptr; } +// TODO: Make this code more readable, It works fine tho. + void DiagnosticBox::render(std::vector &buffer, Coord n_size, Coord n_pos) { pos = n_pos; @@ -158,7 +159,7 @@ void DiagnosticBox::render(std::vector &buffer, Coord n_size, set(r + 1, i, " ", 0x81cdc6, base_bg, 0, 1); for (; i < see_also.length() + 5 && i < content_width; i++) { set(r + 1, i, (char[2]){see_also[i - 5], 0}, fg, base_bg, 0, 1); - if (see_also[i] == ':') + if (see_also[i - 5] == ':') colon_count++; if (colon_count == 2) fg = 0xFFFFFF; diff --git a/src/extentions/hover.cc b/src/extentions/hover.cc old mode 100755 new mode 100644 diff --git a/src/io/input.cc b/src/io/input.cc old mode 100755 new mode 100644 diff --git a/src/io/knot.cc b/src/io/knot.cc old mode 100755 new mode 100644 diff --git a/src/io/renderer.cc b/src/io/renderer.cc old mode 100755 new mode 100644 diff --git a/src/lsp/handlers.cc b/src/lsp/handlers.cc old mode 100755 new mode 100644 diff --git a/src/lsp/process.cc b/src/lsp/process.cc old mode 100755 new mode 100644 diff --git a/src/lsp/worker.cc b/src/lsp/worker.cc old mode 100755 new mode 100644 index 98aabdc..8e3d8f1 --- a/src/lsp/worker.cc +++ b/src/lsp/worker.cc @@ -7,7 +7,7 @@ std::unordered_set opened; Queue need_opening; std::vector new_editors; Queue> response_queue; -} // namespace lsp +} void lsp_worker() { std::lock_guard lock(lsp::lsp_mutex); @@ -22,6 +22,10 @@ void lsp_worker() { for (auto it = lsp::new_editors.begin(); it != lsp::new_editors.end();) { auto ed = *it; auto lsp_id = ed->lang.lsp_name; + if (lsp_id.empty()) { + it = lsp::new_editors.erase(it); + continue; + } if (lsp::opened.contains(lsp_id)) { auto a_it = lsp::active_lsps.find(lsp_id); if (a_it != lsp::active_lsps.end()) diff --git a/src/main.cc b/src/main.cc old mode 100755 new mode 100644 diff --git a/src/ruby/bindings.cc b/src/ruby/bindings.cc old mode 100755 new mode 100644 index 0035b41..0d5c5e0 --- a/src/ruby/bindings.cc +++ b/src/ruby/bindings.cc @@ -1,6 +1,7 @@ #include "ruby/decl.h" mrb_value get_config_file(mrb_state *mrb, mrb_value self) { + UNUSED(self); return mrb_str_new_cstr(mrb, ruby_config_path.string().c_str()); } diff --git a/src/ruby/process.cc b/src/ruby/process.cc old mode 100755 new mode 100644 diff --git a/src/ruby_compile.sh b/src/ruby_compile.sh old mode 100755 new mode 100644 diff --git a/src/syntax/bash.cc b/src/syntax/bash.cc old mode 100755 new mode 100644 diff --git a/src/syntax/parser.cc b/src/syntax/parser.cc old mode 100755 new mode 100644 diff --git a/src/syntax/ruby.cc b/src/syntax/ruby.cc old mode 100755 new mode 100644 diff --git a/src/ui/bar.cc b/src/ui/bar.cc old mode 100755 new mode 100644 diff --git a/src/ui/completionbox.cc b/src/ui/completionbox.cc old mode 100755 new mode 100644 index daa8cf6..0e9a5df --- a/src/ui/completionbox.cc +++ b/src/ui/completionbox.cc @@ -1,187 +1,187 @@ -// #include "ui/completionbox.h" -// #include "editor/completions.h" -// #include "io/sysio.h" -// #include "utils/utils.h" -// -// std::string item_kind_name(uint8_t kind) { -// switch (kind) { -// case 1: -// return "Text"; -// case 2: -// return "Method"; -// case 3: -// return "Function"; -// case 4: -// return "Constructor"; -// case 5: -// return "Field"; -// case 6: -// return "Variable"; -// case 7: -// return "Class"; -// case 8: -// return "Interface"; -// case 9: -// return "Module"; -// case 10: -// return "Property"; -// case 11: -// return "Unit"; -// case 12: -// return "Value"; -// case 13: -// return "Enum"; -// case 14: -// return "Keyword"; -// case 15: -// return "Snippet"; -// case 16: -// return "Color"; -// case 17: -// return "File"; -// case 18: -// return "Reference"; -// case 19: -// return "Folder"; -// case 20: -// return "EnumMember"; -// case 21: -// return "Constant"; -// case 22: -// return "Struct"; -// case 23: -// return "Event"; -// case 24: -// return "Operator"; -// case 25: -// return "TypeParameter"; -// default: -// return "Unknown"; -// } -// } -// -// const char *item_symbol(uint8_t kind) { return "●"; } -// -// uint32_t kind_color(uint8_t kind) { return 0x82AAFF; } -// -// void CompletionBox::render_update() { -// if (hidden || session->visible.empty()) -// return; -// std::unique_lock lock(mtx); -// uint32_t max_label_len = 0; -// uint32_t max_detail_len = 0; -// uint32_t max_kind_len = 0; -// for (uint32_t x = session->scroll; -// x < session->scroll + 8 && x < session->visible.size(); x++) { -// uint32_t i = session->visible[x]; -// if (i >= session->items.size()) -// continue; -// auto &item = session->items[i]; -// max_label_len = MAX(max_label_len, (uint32_t)item.label.size()); -// if (item.detail) -// max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size()); -// max_kind_len = -// MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size()); -// } -// uint32_t total = session->visible.size(); -// uint32_t rows = MIN(total, 8); -// size.row = rows + 2; -// size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + -// 1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set = -// [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, -// uint32_t bg, uint8_t flags) { -// if (r < size.row && c < size.col) -// cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; -// }; -// uint32_t border_fg = 0x82AAFF; -// uint32_t sel_bg = 0x174225; -// set(0, 0, "┌", border_fg, 0, 0); -// for (uint32_t c = 1; c < size.col - 1; c++) -// set(0, c, "─", border_fg, 0, 0); -// set(0, size.col - 1, "┐", border_fg, 0, 0); -// uint32_t start = session->scroll; -// uint32_t end = MIN(start + 8, session->visible.size()); -// for (uint32_t row_idx = start; row_idx < end; row_idx++) { -// uint32_t r = (row_idx - start) + 1; -// auto &item = session->items[session->visible[row_idx]]; -// uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : -// 1; uint32_t fg = 0xFFFFFF; set(r, 0, "│", border_fg, 0, 0); uint32_t c = -// 1; const char *sym = item_symbol(item.kind); set(r, c++, sym, -// kind_color(item.kind), bg, 0); set(r, c++, " ", fg, bg, 0); for (size_t i -// = 0; i < item.label.size(); i++) -// set(r, c + i, (char[2]){item.label[i], 0}, fg, bg, -// item.deprecated ? CF_STRIKETHROUGH : 0); -// c += item.label.size(); -// set(r, c++, " ", fg, bg, 0); -// uint32_t detail_fg = 0xAAAAAA; -// if (item.detail) { -// for (size_t i = 0; i < item.detail->size(); i++) -// set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0); -// c += item.detail->size(); -// } -// uint32_t pad = size.col - 1 - c - max_kind_len; -// for (uint32_t i = 0; i < pad; i++) -// set(r, c + i, " ", fg, bg, 0); -// c += pad; -// std::string kind_name = item_kind_name(item.kind); -// for (size_t i = 0; i < kind_name.size(); i++) -// set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg, -// 0); -// set(r, size.col - 1, "│", border_fg, 0, 0); -// } -// uint32_t bottom = size.row - 1; -// set(bottom, 0, "└", border_fg, 0, 0); -// for (uint32_t c = 1; c < size.col - 1; c++) -// set(bottom, c, "─", border_fg, 0, 0); -// set(bottom, size.col - 1, "┘", border_fg, 0, 0); -// if (session->visible.size() > 8) { -// std::string info = std::to_string(start + 1) + "-" + std::to_string(end) -// + -// "/" + std::to_string(session->visible.size()); -// for (size_t i = 0; i < info.size() && i < size.col - 2; i++) -// set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0); -// } -// } -// -// void CompletionBox::render(Coord pos) { -// if (hidden || session->visible.empty()) -// return; -// std::shared_lock lock(mtx); -// int32_t start_row = (int32_t)pos.row - (int32_t)size.row; -// if (start_row < 0) -// start_row = pos.row + 1; -// int32_t start_col = pos.col; -// Coord screen_size = get_size(); -// if (start_col + size.col > screen_size.col) { -// start_col = screen_size.col - size.col; -// if (start_col < 0) -// start_col = 0; -// } -// position = {(uint32_t)start_row, (uint32_t)start_col}; -// for (uint32_t r = 0; r < size.row; r++) -// for (uint32_t c = 0; c < size.col; c++) -// update(start_row + r, start_col + c, cells[r * size.col + c].utf8, -// cells[r * size.col + c].fg, cells[r * size.col + c].bg, -// cells[r * size.col + c].flags); -// if (session->items.size() > session->select && -// session->items[session->select].documentation && -// *session->items[session->select].documentation != "" && -// !session->hover_dirty) { -// if (session->doc != session->select) { -// session->doc = session->select; -// session->hover.clear(); -// session->hover.text = *session->items[session->select].documentation; -// session->hover.is_markup = true; -// session->hover_dirty = true; -// } else { -// if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) { -// session->hover.render({position.row + session->hover.size.row, -// position.col - session->hover.size.col}); -// } else { -// session->hover.render( -// {position.row + session->hover.size.row, position.col + -// size.col}); -// } -// } -// } -// } +/* #include "ui/completionbox.h" +#include "editor/completions.h" +#include "io/sysio.h" +#include "utils/utils.h" + +std::string item_kind_name(uint8_t kind) { + switch (kind) { + case 1: + return "Text"; + case 2: + return "Method"; + case 3: + return "Function"; + case 4: + return "Constructor"; + case 5: + return "Field"; + case 6: + return "Variable"; + case 7: + return "Class"; + case 8: + return "Interface"; + case 9: + return "Module"; + case 10: + return "Property"; + case 11: + return "Unit"; + case 12: + return "Value"; + case 13: + return "Enum"; + case 14: + return "Keyword"; + case 15: + return "Snippet"; + case 16: + return "Color"; + case 17: + return "File"; + case 18: + return "Reference"; + case 19: + return "Folder"; + case 20: + return "EnumMember"; + case 21: + return "Constant"; + case 22: + return "Struct"; + case 23: + return "Event"; + case 24: + return "Operator"; + case 25: + return "TypeParameter"; + default: + return "Unknown"; + } +} + +const char *item_symbol(uint8_t kind) { return "●"; } + +uint32_t kind_color(uint8_t kind) { return 0x82AAFF; } + +void CompletionBox::render_update() { + if (hidden || session->visible.empty()) + return; + std::unique_lock lock(mtx); + uint32_t max_label_len = 0; + uint32_t max_detail_len = 0; + uint32_t max_kind_len = 0; + for (uint32_t x = session->scroll; + x < session->scroll + 8 && x < session->visible.size(); x++) { + uint32_t i = session->visible[x]; + if (i >= session->items.size()) + continue; + auto &item = session->items[i]; + max_label_len = MAX(max_label_len, (uint32_t)item.label.size()); + if (item.detail) + max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size()); + max_kind_len = + MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size()); + } + uint32_t total = session->visible.size(); + uint32_t rows = MIN(total, 8); + size.row = rows + 2; + size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + + 1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set = + [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, + uint32_t bg, uint8_t flags) { + if (r < size.row && c < size.col) + cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; + }; + uint32_t border_fg = 0x82AAFF; + uint32_t sel_bg = 0x174225; + set(0, 0, "┌", border_fg, 0, 0); + for (uint32_t c = 1; c < size.col - 1; c++) + set(0, c, "─", border_fg, 0, 0); + set(0, size.col - 1, "┐", border_fg, 0, 0); + uint32_t start = session->scroll; + uint32_t end = MIN(start + 8, session->visible.size()); + for (uint32_t row_idx = start; row_idx < end; row_idx++) { + uint32_t r = (row_idx - start) + 1; + auto &item = session->items[session->visible[row_idx]]; + uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : + 1; uint32_t fg = 0xFFFFFF; set(r, 0, "│", border_fg, 0, 0); uint32_t c = + 1; const char *sym = item_symbol(item.kind); set(r, c++, sym, + kind_color(item.kind), bg, 0); set(r, c++, " ", fg, bg, 0); for (size_t i + = 0; i < item.label.size(); i++) + set(r, c + i, (char[2]){item.label[i], 0}, fg, bg, + item.deprecated ? CF_STRIKETHROUGH : 0); + c += item.label.size(); + set(r, c++, " ", fg, bg, 0); + uint32_t detail_fg = 0xAAAAAA; + if (item.detail) { + for (size_t i = 0; i < item.detail->size(); i++) + set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0); + c += item.detail->size(); + } + uint32_t pad = size.col - 1 - c - max_kind_len; + for (uint32_t i = 0; i < pad; i++) + set(r, c + i, " ", fg, bg, 0); + c += pad; + std::string kind_name = item_kind_name(item.kind); + for (size_t i = 0; i < kind_name.size(); i++) + set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg, + 0); + set(r, size.col - 1, "│", border_fg, 0, 0); + } + uint32_t bottom = size.row - 1; + set(bottom, 0, "└", border_fg, 0, 0); + for (uint32_t c = 1; c < size.col - 1; c++) + set(bottom, c, "─", border_fg, 0, 0); + set(bottom, size.col - 1, "┘", border_fg, 0, 0); + if (session->visible.size() > 8) { + std::string info = std::to_string(start + 1) + "-" + std::to_string(end) + + + "/" + std::to_string(session->visible.size()); + for (size_t i = 0; i < info.size() && i < size.col - 2; i++) + set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0); + } +} + +void CompletionBox::render(Coord pos) { + if (hidden || session->visible.empty()) + return; + std::shared_lock lock(mtx); + int32_t start_row = (int32_t)pos.row - (int32_t)size.row; + if (start_row < 0) + start_row = pos.row + 1; + int32_t start_col = pos.col; + if (start_col + size.col > io::cols) { + start_col = io::cols - size.col; + if (start_col < 0) + start_col = 0; + } + position = {(uint32_t)start_row, (uint32_t)start_col}; + for (uint32_t r = 0; r < size.row; r++) + for (uint32_t c = 0; c < size.col; c++) + update(start_row + r, start_col + c, cells[r * size.col + c].utf8, + cells[r * size.col + c].fg, cells[r * size.col + c].bg, + cells[r * size.col + c].flags); + if (session->items.size() > session->select && + session->items[session->select].documentation && + *session->items[session->select].documentation != "" && + !session->hover_dirty) { + if (session->doc != session->select) { + session->doc = session->select; + session->hover.clear(); + session->hover.text = *session->items[session->select].documentation; + session->hover.is_markup = true; + session->hover_dirty = true; + } else { + if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) { + session->hover.render({position.row + session->hover.size.row, + position.col - session->hover.size.col}); + } else { + session->hover.render( + {position.row + session->hover.size.row, position.col + + size.col}); + } + } + } +} + */ \ No newline at end of file diff --git a/src/utils/system.cc b/src/utils/system.cc old mode 100755 new mode 100644 diff --git a/src/utils/text.cc b/src/utils/text.cc old mode 100755 new mode 100644 diff --git a/src/utils/unicode.cc b/src/utils/unicode.cc old mode 100755 new mode 100644 diff --git a/src/windows/renderer.cc b/src/windows/renderer.cc old mode 100755 new mode 100644