diff --git a/README.md b/README.md index f9b99f0..62ec04d 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,230 @@ Copyright 2025 Syed Daanish -# Crib +# Crib - a text editor -A TUI IDE. +### About -# TODO +Crib is a TUI based text editor built primaririly for personal use.
+Crib has a vim-style editor modes system but navigation and shortcuts are very different.
+It supports tree-sitter based text highlighting.
+And LSP for auto-completion, diagnostics, hover docs etc.
+It aims to be complete general purpose IDE.
+(It is still very much a work in progress so a lot of things may seem incomplete)
+For now it is just a single file editor. I plan to add a multi-file support with file pickers and tabs soon.
-- [ ] Check why fish is behaving soo off with completions filtering -- [ ] Normalize completions edits if local filtering is used -- [ ] Capture ctrl+h,l for scrolling documentation -- [ ] Documentation fix position and make it async first render -- [ ] Allow completion list to be scrolled up and down and show only x max at a time -- [ ] Dont filter case sensitive. -- [ ] Do not recompute word under cursor if not changed -- [ ] Finish autocomplete box style functions. -- [ ] Add status bar & RUNNER mode -- [ ] Get code context from tree-sitter -- [ ] Maybe hide boxes in !`normal` mode -- [ ] expand color regex to match css colors if in css file -- [ ] Fix indentation logic - tree-sitter indents too if possible - - Make it work by one getting the identation used in a file by first checking if it has any line with 2 or more spaces then the least one is set to be the indent or if it is tabs then tabs but if there are none then use a table of file type to its indentation or use 2 spaces as default. store this info as `1 = tab` and `2 or more = those many spaces`. - - Use this when indenting and unindenting. And also when getting the identation of a line. - - Also indent when going immediately to newline should follow indent of previous line regardless of file default. -- [ ] Fix bug where closing immediately while lsp is loading hangs and then segfaults. -- [ ] For `"insertTextFormat": 2` in `clangd` and similar use only the last word in the signature when replacing -- [ ] Keep a list of words in the current buffer. (for auto completion) (maybe?) -- [ ] Add ecma to js and make tsx -- [ ] Switch to like `RapidJSON` ro something more basic but faster than rn - - also decrease use of `std::string` so much in ui stuff and lsp and warnings etc. -- [ ] Add lsp jumping support for goto definition, hover etc. -- [ ] Add lsp rename support for renaming a symbol. (also see what tree-sitter can do here) -- [ ] Check into more lsp stuff i can add. -- [ ] Add codeium/copilot support for auto-completion (uses the VAI virtual text) as a test phase. -- [ ] Add a whitespace highlighter (nerd font). for spaces and tabs at start/end of line. not as virtual but instead at render time. -- [ ] Once renderer is proven to work well (i.e. redo this commit) merge `experimental` branch into `main`. commit `43f443e` on `experimental`. -- [ ] Add snippets from wherever i get them. (like luasnip or vsnip) -- [ ] Add this thing where select at end of screen scrolls down. (and vice versa) - - Can be acheived by updating `main.cc` to send drag events to the selected editor instead of just under cursor. - - Then a drag event above or below will scroll the selected editor. -- [ ] Add support for virtual cursor where edits apply at all the places. -- [ ] Add alt + click to set multiple cursors. -- [ ] Add search / replace along with search / virtual cursors are searched pos. - - Allow using perl directly for replace maybe? and others with my dfa? - - or add searcher that supports $1 $2 etc. (capture groups) -- [ ] Add support for undo/redo. -- [ ] Add splash screen / minigame jumping. -- [ ] Normalize / validate unicode on file open. so use utf8 purely and fix other types of files -- [ ] Add git stuff. -- [ ] Add SQL support. (viewer and basic editor) -- [ ] Add color picker/palette for hex or other css colors. -- [ ] Fix bug where alt+up at eof adds extra line. -- [ ] Think about how i would keep fold states sensical if i added code prettying/formatting. -- [ ] Use tree-sitter to get the node path of the current node under cursor and add an indicator bar. - - (possibly with a picker to jump to any node) -- [ ] Add the highlight of block edges when cursor is on a bracket (or in). (prolly from lsp) -- [ ] Add this thing where selection double click on a bracket selects whole block. - - (only on the first time) and sets mode to `WORD`. -- [ ] Redo cpp/c/h scm file . also pretty much all of them do manually -- [ ] Try making `lua-typed` and man pages `tree-sitter` grammar. -- [ ] Redo folding system and its relation to move_line_* functions. (Currently its a mess) -- [ ] Make whole thing event driven and not clock driven. +## Building -- [ ] Fix in kutu.rb such that windows arent focused on hover . they are only when they are true windows and not just popups . also popus are focused even without hover when they open. +### Get started + +Make sure the repo is cloned with submodules to get most of the dependencies. + +```bash +git clone --recurse-submodules https://git.syedm.dev/SyedM/crib.git +``` + +### Dependencies + +#### System-wide libraries + +Make sure to install [nlohmann/json](https://github.com/nlohmann/json) from your package manager +(it should be available in the compiler as the header `nlohmann/json.hpp`) and to also have libmagic installed -- +`#include ` should work. And finally for [pcre2](https://github.com/PCRE2Project/pcre2): `#include `
+ +It also uses `xclip` at runtime for copying/pasting. +And any modern terminal should work fine - preferably `kitty` or `wezterm`.
+ +#### `./libs` folder + +Some other dependancies like `libgrapheme` and `tree-sitter*` and `unicode_width` are added as submodules or copied.
+`unicode_width` is compiled by the makefile so nothing to do there.
+`libgrapheme` needs to be compiled using `make` in it's folder.
+`tree-sitter` needs to be compiled using `make` in it's folder.
+For other tree-sitter grammars, run `make` in their folders except some for which `npm install` needs to be used (see their README.md)
+For any problems with `npm install` make sure to have older versions of node installed.
+For some even manual clang or gcc compilation may be required.
+*TODO: Make a detailed list of how to do compile each*
+ +#### LSP's + +The following lsp's are supported and can be installed anywhere in your `$PATH`
+ +* **clangd** — [https://clangd.llvm.org/installation.html](https://clangd.llvm.org/installation.html) +* **solargraph** — [https://solargraph.org/](https://solargraph.org/) +* **bash-language-server** — [https://github.com/bash-lsp/bash-language-server](https://github.com/bash-lsp/bash-language-server) +* **vscode-css-language-server** — [https://github.com/microsoft/vscode-css-languageservice](https://github.com/microsoft/vscode-css-languageservice) +* **vscode-json-language-server** — [https://github.com/microsoft/vscode-langservers-extracted](https://github.com/microsoft/vscode-langservers-extracted) +* **fish-lsp** — [https://github.com/fisho/fish-language-server](https://github.com/fisho/fish-language-server) +* **gopls** — [https://pkg.go.dev/golang.org/x/tools/gopls](https://pkg.go.dev/golang.org/x/tools/gopls) +* **haskell-language-server** — [https://github.com/haskell/haskell-language-server](https://github.com/haskell/haskell-language-server) +* **emmet-ls** — [https://github.com/emmetio/emmet‑ls](https://github.com/emmetio/emmet‑ls) +* **typescript-language-server** — [https://github.com/typescript-language-server/typescript-language-server](https://github.com/typescript-language-server/typescript-language-server) +* **lua-language-server** — [https://github.com/LuaLS/lua-language-server](https://github.com/LuaLS/lua-language-server) +* **pyright-langserver** — [https://github.com/microsoft/pyright](https://github.com/microsoft/pyright) +* **rust-analyzer** — [https://github.com/rust-analyzer/rust-analyzer](https://github.com/rust-analyzer/rust-analyzer) +* **intelephense** — [https://github.com/bmewburn/vscode-intelephense](https://github.com/bmewburn/vscode-intelephense) +* **marksman** — [https://github.com/christianchiarulli/marksman](https://github.com/christianchiarulli/marksman) +* **nginx-language-server** — [https://github.com/nginx/nginx-language-server](https://github.com/nginx/nginx-language-server) +* **taplo** — [https://github.com/taplo‑lang/taplo](https://github.com/taplo‑lang/taplo) +* **yaml-language-server** — [https://github.com/redhat-developer/yaml-language-server](https://github.com/redhat-developer/yaml-language-server) +* **sqls** — [https://github.com/lighttiger2505/sqls](https://github.com/lighttiger2505/sqls) +* **make-language-server** — [https://github.com/make-langserver/make-language-server](https://github.com/make-langserver/make-language-server) + +> As it is still in development, some of these may not work as expected or that well. +> But for c/ruby/lua/python it should work fine (I test more with these). +> It should work even if the lsp is not installed but lsp features will not work. +> See `include/config.h` & `include/ts/decl.h` if you want to add your own lsp and/or tree-sitter grammar.
+ +#### Compiler + +`g++` and `clang++` should both work fine. +The makefile has been set to use g++ if made with `make -j test` and clang++ if made with `make -j release`
+This can be changed but I have found clang++ builds to be slightly faster - also test builds do not have the flags needed to be used system wide or any optimizations.
+ +#### Compliling + +```bash +make -j release +``` + +### Running + +Preferably add `bin` folder to PATH or move `bin/crib` to somewhere in PATH.
+But make sure that `scripts/` and `grammar/` are at `../` relative to the binary or it will crash.
+`scripts/init.sh` and `scripts/exit.sh` can be used to add hooks to the editor on startup and exit +(Make sure to remove my `kitty` hooks from them if you want).
+For some LSP's to work properly `crib` needs to be run from the root folder of the project. *To be fixed*
+then do -
+ +```bash +crib ./filename.ext +``` + +*If `filename.ext` does not exist, it will fail to load the editor - use `touch filename.ext` to create it - to be fixed*
+*Try out with files in `samples/`*
+ +## Keybindings + +### Mouse Interactions + +These interactions work globally or generally across the editor canvas. + +| Action | Function | +| --- | --- | +| **Scroll Up/Down** | Scrolls the view. | +| **Scroll Left/Right** | Moves the cursor left or right. | +| **Left Click (Press)** | Moves cursor to position; resets selection. | +| **Left Click (Double)** | Selects the **word** under the cursor (enters SELECT mode). | +| **Left Click (Triple)** | Selects the **line** under the cursor (enters SELECT mode). | +| **Left Click (Drag)** | Selects text (Character, Word, or Line based on initial click type). | +| **Left Click (Release)** | If cursor and selection start are the same, returns to NORMAL mode. | + +### Navigation (Global / Special Keys) + +These keys work primarily in Normal mode but handle movement logic. + +| Key | Modifier | Function | +| --- | --- | --- | +| **Arrows** (Up/Down/Left/Right) | None | Move cursor 1 step in that direction. | +| **Arrows** (Up/Down) | `CTRL` | Move cursor **5 steps** in that direction. | +| **Arrows** (Left/Right) | `CTRL` | Jump to the previous/next **word boundary**. | +| **Arrows** (Up/Down) | `ALT` | **Move the current line** Up or Down. | +| **Arrows** (Left/Right) | `ALT` | Move cursor **8 steps** in that direction. | + +### NORMAL Mode + +This is the default navigation and command mode. + +| Key | Function | +| --- | --- | +| **i** | Enter **INSERT** mode (at current position). | +| **a** | Enter **INSERT** mode (append: moves cursor right by 1 first). | +| **s** or **v** | Enter **SELECT** mode (start character selection). | +| **:** or **;** | Enter **RUNNER** mode (Command Bar). | +| **u** | Select the **last line** of the file (Jumps to bottom). | +| **h** | Trigger **LSP Hover** information for the symbol under cursor. | +| **Ctrl + h** | Scroll the hover window **Up**. | +| **Ctrl + l** | Scroll the hover window **Down**. | +| **Ctrl + s** | **Save** the file. | +| **Ctrl + d** | Scroll Page **Down** (1 unit). | +| **Ctrl + u** | Scroll Page **Up** (1 unit). | +| **p** | **Paste** from clipboard at cursor position (moves cursor to end of paste). | +| **>** or **.** | **Indent** the current line. | +| **<** or **,** | **Dedent** (un-indent) the current line. | +| **Space** | Move cursor Right. | +| **Backspace** (`0x7F`) | Move cursor Left. | +| **Enter** (`\n`, `\r`) | Move cursor Down. | +| **\| or \\** | Move cursor Up. | +| **n** | Enter **JUMPER** mode (Set Bookmark). | +| **m** | Enter **JUMPER** mode (Jump to Bookmark). | +| **N** | Clear specific Jumper hook (logic attempts to clear hook at current line). | + +### INSERT Mode + +Used for typing text. + +| Key | Function | +| --- | --- | +| **Esc** (`0x1B`) | Return to **NORMAL** mode. | +| **Tab** (`\t`) | Inserts 2 spaces. | +| **Enter** | Inserts newline + **Auto-indents** based on previous line/context. | +| **Backspace** | Deletes previous character or auto-closes empty pairs (e.g., `{ | +| **Ctrl + w** | **Delete Previous Word**. | +| **Del** | Delete character under cursor. | +| **Ctrl + Del** | Delete **Next Word**. | +| **Typing** | Inserts characters. | +| **Ctrl + Shift + v or as configured in your terminal** | System pasting. | +| **{ ( [ " '** | Auto-inserts closing pair (e.g., typing `{` inserts `{}`). | +| **} ) ] " '** | If the next char matches the typed char, skip insertion (overwrite), otherwise insert. | + +#### Autocompletion (Inside Insert Mode) + +These function only if LSP and completion are active. + +| Key | Function | +| --- | --- | +| **Ctrl + p** | Select **Next** completion item. | +| **Ctrl + o** | Select **Previous** completion item. | +| **Ctrl + \\** | **Accept** selected completion OR trigger new completion request. | +| **Trigger Chars** | (e.g., `.`, `>`) Automatically triggers completion popup. | + +### SELECT Mode + +Used for highlighting text. + +| Key | Function | +| --- | --- | +| **Esc**, **s**, **v** | Cancel selection and return to **NORMAL** mode. | +| **y** | **Yank (Copy)** selection to clipboard → Return to Normal. | +| **x** | **Cut** selection to clipboard → Return to Normal. | +| **p** | **Paste** over selection (Replace text) → Return to Normal. | +| **f** | **Fold** the selected range (collapses code) → Return to Normal. | + +### JUMPER Mode + +This mode uses a bookmarking system mapped to keyboard characters. + +* **Entered via `n` (Set Mode):** +* Pressing any key `!` through `~` assigns the current line number to that key. + +* **Entered via `m` (Jump Mode):** +* Pressing any key `!` through `~` jumps the cursor to the line previously assigned to that key. + +### RUNNER Mode (Command Bar) + +Activated by `:` or `;`. + +| Key | Function | +| --- | --- | +| **Esc** | Cancel and return to **NORMAL** mode. | +| **Enter** | Execute the typed command. | +| **Left / Right** | Move cursor within the command bar. | +| **Up / Down** | Intended for command history. (Not implemented) | +| **Typing** | Insert characters into the command bar. (Not implemented) | + +## Features Implemented + +*TODO: Add a list of features*
+ +## Features Planned + +*TODO: Add a list of features*
diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..65a0d64 --- /dev/null +++ b/TODO.md @@ -0,0 +1,60 @@ +Copyright 2025 Syed Daanish + +# TODO + +- [ ] Change this to TODO.md and add proper README.md +- [ ] Check why fish is behaving soo off with completions filtering +- [ ] Dont filter case sensitive. +- [ ] Normalize completions edits if local filtering is used +- [ ] Capture ctrl+h,l for scrolling documentation +- [ ] Allow completion list to be scrolled up and down and show only x max at a time +- [ ] Do not recompute word under cursor if not changed +- [ ] Finish autocomplete box style functions. +- [ ] Add status bar & RUNNER mode +- [ ] Get code context from tree-sitter +- [ ] Maybe hide boxes in !`normal` mode +- [ ] expand color regex to match css colors if in css file +- [ ] Fix indentation logic - tree-sitter indents too if possible + - Make it work by one getting the identation used in a file by first checking if it has any line with 2 or more spaces then the least one is set to be the indent or if it is tabs then tabs but if there are none then use a table of file type to its indentation or use 2 spaces as default. store this info as `1 = tab` and `2 or more = those many spaces`. + - Use this when indenting and unindenting. And also when getting the identation of a line. + - Also indent when going immediately to newline should follow indent of previous line regardless of file default. +- [ ] Fix bug where closing immediately while lsp is loading hangs and then segfaults. +- [ ] For `"insertTextFormat": 2` in `clangd` and similar use only the last word in the signature when replacing +- [ ] Keep a list of words in the current buffer. (for auto completion) (maybe?) +- [ ] Add ecma to js and make tsx +- [ ] Switch to like `RapidJSON` ro something more basic but faster than rn + - also decrease use of `std::string` so much in ui stuff and lsp and warnings etc. +- [ ] Add lsp jumping support for goto definition, hover etc. +- [ ] Add lsp rename support for renaming a symbol. (also see what tree-sitter can do here) +- [ ] Check into more lsp stuff i can add. +- [ ] Add codeium/copilot support for auto-completion (uses the VAI virtual text) as a test phase. +- [ ] Add a whitespace highlighter (nerd font). for spaces and tabs at start/end of line. not as virtual but instead at render time. +- [ ] Once renderer is proven to work well (i.e. redo this commit) merge `experimental` branch into `main`. commit `43f443e` on `experimental`. +- [ ] Add snippets from wherever i get them. (like luasnip or vsnip) +- [ ] Add this thing where select at end of screen scrolls down. (and vice versa) + - Can be acheived by updating `main.cc` to send drag events to the selected editor instead of just under cursor. + - Then a drag event above or below will scroll the selected editor. +- [ ] Add support for virtual cursor where edits apply at all the places. +- [ ] Add alt + click to set multiple cursors. +- [ ] Add search / replace along with search / virtual cursors are searched pos. + - Allow using perl directly for replace maybe? and others with my dfa? + - or add searcher that supports $1 $2 etc. (capture groups) +- [ ] Add support for undo/redo. +- [ ] Add splash screen / minigame jumping. +- [ ] Normalize / validate unicode on file open. so use utf8 purely and fix other types of files +- [ ] Add git stuff. +- [ ] Add SQL support. (viewer and basic editor) +- [ ] Add color picker/palette for hex or other css colors. +- [ ] Fix bug where alt+up at eof adds extra line. +- [ ] Think about how i would keep fold states sensical if i added code prettying/formatting. +- [ ] Use tree-sitter to get the node path of the current node under cursor and add an indicator bar. + - (possibly with a picker to jump to any node) +- [ ] Add the highlight of block edges when cursor is on a bracket (or in). (prolly from lsp) +- [ ] Add this thing where selection double click on a bracket selects whole block. + - (only on the first time) and sets mode to `WORD`. +- [ ] Redo cpp/c/h scm file . also pretty much all of them do manually +- [ ] Try making `lua-typed` and man pages `tree-sitter` grammar. +- [ ] Redo folding system and its relation to move_line_* functions. (Currently its a mess) +- [ ] Make whole thing event driven and not clock driven. + +- [ ] Fix in kutu.rb such that windows arent focused on hover . they are only when they are true windows and not just popups . also popus are focused even without hover when they open. diff --git a/include/editor/completions.h b/include/editor/completions.h index dfc52d2..fb54726 100644 --- a/include/editor/completions.h +++ b/include/editor/completions.h @@ -36,6 +36,7 @@ struct CompletionSession { CompletionBox box; HoverBox hover; uint32_t doc = UINT32_MAX; + std::atomic hover_dirty = false; CompletionSession() : box(this) {} }; diff --git a/include/ts/ts.h b/include/ts/ts.h index 4f5abb2..9bfcf35 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -13,7 +13,8 @@ TSQuery *load_query(const char *query_path, TSSetBase *set); void ts_collect_spans(Editor *editor); bool ts_predicate( TSQuery *query, const TSQueryMatch &match, - std::function subject_fn); + std::function + subject_fn); void clear_regex_cache(); #endif diff --git a/samples/lua.lua b/samples/lua.lua index 933588c..26560c7 100644 --- a/samples/lua.lua +++ b/samples/lua.lua @@ -14,60 +14,60 @@ print(self) -- Functions local function greet(user) - print("Hello, " .. user) + print("Hello, " .. user) end local function add(a, b) - return a + b + return a + b end -- Method definitions local obj = {} function obj:sayHi() - print("Hi from method!") + print("Hi from method!") end obj.sayHello = function() - print("Hello from field function!") + print("Hello from field function!") end -- Arrow-style anonymous function (LuaJIT/CFFI style) local arrow = function(x) - return x * 2 + return x * 2 end -- Table constructors local t = { - foo = 123, - bar = function() - return "bar" - end, - nested = { - a = 1, - b = 2, - }, + foo = 123, + bar = function() + return "bar" + end, + nested = { + a = 1, + b = 2, + }, } -- Loops for i = 1, MAX_COUNT do - counter = counter + i + counter = counter + i end while counter > 0 do - counter = counter - 1 + counter = counter - 1 end repeat - counter = counter + 1 + counter = counter + 1 until counter == 10 -- Conditionals if counter > 5 then - print("Big number") + print("Big number") elseif counter == 5 then - print("Exactly five") + print("Exactly five") else - print("Small number") + print("Small number") end -- Operators @@ -84,7 +84,7 @@ add(5, 10) -- Built-in function calls assert(x > 0) pcall(function() - print("safe") + print("safe") end) tonumber("123") @@ -117,3 +117,4 @@ local tpl = `Value: ${counter}` -- Regex-like string (for testing injection highlighting) local re = "/^%a+$/" + diff --git a/scripts/exit.sh b/scripts/exit.sh index dab984b..b337616 100644 --- a/scripts/exit.sh +++ b/scripts/exit.sh @@ -1 +1 @@ -kitty @ --to="$KITTY_LISTEN_ON" set-spacing padding=8 margin=0 +kitty @ --to="$KITTY_LISTEN_ON" set-spacing padding=8 margin=0 2>/dev/null || true diff --git a/scripts/init.sh b/scripts/init.sh index a41e5a3..812ad6c 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -1 +1 @@ -kitty @ --to="$KITTY_LISTEN_ON" set-spacing padding=0 margin=0 +kitty @ --to="$KITTY_LISTEN_ON" set-spacing padding=0 margin=0 2>/dev/null || true diff --git a/src/editor/worker.cc b/src/editor/worker.cc index 41da33b..96f8d11 100644 --- a/src/editor/worker.cc +++ b/src/editor/worker.cc @@ -103,4 +103,8 @@ void editor_worker(Editor *editor) { } lock2.unlock(); hover_diagnostic(editor); + if (editor->completion.active && editor->completion.hover_dirty) { + editor->completion.hover.render_first(); + editor->completion.hover_dirty = false; + } } diff --git a/src/ts/ts.cc b/src/ts/ts.cc index 0193d0a..54ba326 100644 --- a/src/ts/ts.cc +++ b/src/ts/ts.cc @@ -90,7 +90,8 @@ void ts_collect_spans(Editor *editor) { ts_query_cursor_exec(cursor, q, ts_tree_root_node(item.tsset->tree)); std::unordered_map pending_injections; TSQueryMatch match; - auto subject_fn = [&](const TSNode *node, uint32_t *len) -> char * { + auto subject_fn = [&](const TSNode *node, uint32_t *len, + bool *allocated) -> char * { uint32_t start = ts_node_start_byte(*node); uint32_t end = ts_node_end_byte(*node); if (start == end || end > editor->root->char_count) @@ -98,6 +99,7 @@ void ts_collect_spans(Editor *editor) { std::shared_lock lock(editor->knot_mtx); char *text = read(editor->root, start, end - start); *len = end - start; + *allocated = true; return text; }; while (ts_query_cursor_next_match(cursor, &match)) { diff --git a/src/ts/utils.cc b/src/ts/utils.cc index 2f30a17..e197bbb 100644 --- a/src/ts/utils.cc +++ b/src/ts/utils.cc @@ -117,7 +117,8 @@ TSQuery *load_query(const char *query_path, TSSetBase *set) { bool ts_predicate( TSQuery *query, const TSQueryMatch &match, - std::function subject_fn) { + std::function + subject_fn) { uint32_t step_count; const TSQueryPredicateStep *steps = ts_query_predicates_for_pattern(query, match.pattern_index, &step_count); @@ -152,13 +153,15 @@ bool ts_predicate( const TSNode *node = find_capture_node(match, subject_id); pcre2_code *re = get_re(regex_txt); uint32_t len; - char *subject = subject_fn(node, &len); + bool allocated; + char *subject = subject_fn(node, &len, &allocated); if (!subject) return false; pcre2_match_data *md = pcre2_match_data_create_from_pattern(re, nullptr); int rc = pcre2_match(re, (PCRE2_SPTR)subject, len, 0, 0, md, nullptr); pcre2_match_data_free(md); bool ok = (rc >= 0); - free(subject); + if (allocated) + free(subject); return (command == "match?" ? ok : !ok); } diff --git a/src/ui/completionbox.cc b/src/ui/completionbox.cc index 9e37bfb..79a095b 100644 --- a/src/ui/completionbox.cc +++ b/src/ui/completionbox.cc @@ -152,13 +152,14 @@ void CompletionBox::render(Coord pos) { cells[r * size.col + c].flags); if (session->items.size() > session->select && session->items[session->select].documentation && - *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.render_first(); + 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, diff --git a/src/ui/hover.cc b/src/ui/hover.cc index 0c43d11..2af8f93 100644 --- a/src/ui/hover.cc +++ b/src/ui/hover.cc @@ -44,10 +44,12 @@ void HoverBox::render_first(bool scroll) { TSQueryCursor *cursor = ts_query_cursor_new(); ts_query_cursor_exec(cursor, ts.query, ts_tree_root_node(ts.tree)); TSQueryMatch match; - auto subject_fn = [&](const TSNode *node, uint32_t *len) -> char * { + auto subject_fn = [&](const TSNode *node, uint32_t *len, + bool *allocated) -> char * { uint32_t start = ts_node_start_byte(*node); uint32_t end = ts_node_end_byte(*node); *len = end - start; + *allocated = false; return text.data() + start; }; while (ts_query_cursor_next_match(cursor, &match)) {