aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/event
Commit message (Collapse)AuthorAge
* feat(input)!: CmdAtom event #41297Justin M. Keyes2026-08-14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: There is no unified notion of a "user action". Vim processes input by one-char-at-a-time, and mostly throws away any hints it might gather about the user's action, with one exception: it stores the last _edit_ action (the "redo buffer", encoded as unstructured `["x][v][count]body` bytes). Plugins can only observe individual keys (vim.on_key) and high-level effects (TextChanged, CursorMoved). Solution: - Users can subscribe to `CmdAtom` events to handle any user action. - Event is deferred; handlers cannot cancel or interfere with user actions. - Capture `CmdSpec` from the normal/insert/visual subsystems. - typeahead/readahead stay unstructured (`buffheader_T`): they are key streams, not commands. - the redo/record buffers become `StringBuilder`: fewer allocations/copies. - Repurpose the input/redo engine to accept `CmdSpec` objects. "atom": one repeatable unit of user input, as a resolved (post-mapping) keysequence plus structured fields. Only user actions, not `:normal`, API calls, or non-"t" `feedkeys`. BREAKING: dot-repeat of an Insert session, replays the entire session including cursor-moves (:help ins-repeat). BREAKING: dot-repeat of a Visual operation, replays the selection instead of operating on a fixed-size region.
* feat(wasm): implement initial web demorawan101012026-08-02
| | | | | | Introduce an initial browser-based Neovim demo with automatic startup and UI integration. The demo is functional but still requires persistence, additional testing, and further stabilization. Acknowledgment: UiState in app.js is based on the implementation from github.com/MuNeNiCK/nvim-wasm, with additional modifications and updates
* build(wasm): use pthreads and correct uv_fs_write offset handlingrawan101012026-07-26
|
* fix(path): normalize separators (slashes) #39155tao2026-07-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: Continue separators normalization, and try to keep it at the nvim <-> external boundary, e.g., `fn.xxx`, `api.xxx`, `:xxx` Solution: some key changes - normalize `$HOME-windows` - normalize entry points of changing directory - `nvim_set_current_dir()`, `chdir()` - normalize the named pipe - `--listen` arg, `--server` arg, `:restart` - `serverstart()`, `sockconnect()`, `serverstop()` - make `expand()` respect 'shellslash' again - clean up `did_set_shellslash` - replace `forward_slash` with `TO_SLASH` - remove obsolete `TMP_PATHSEPSTR` - remove `slash_adjust` in `do_autocmd_dirchanged`? - make `fnamemodify()` always return `/` (except when substituting separators via `:s`, `:gs` ?) Note: - these funcs still apply `slash_adjust` on return, as before: `:pwd`, `chdir()`, `exepath()`, `getcwd()` - these funcs alwarys return `/`, unlike before the normalization PRs - `getcompletion()`, `finddir()`, `findfile()` Also - clean up code, comments, formatting and tests - move `TO_SLASH` from `f_chdir` to `changedir_func` - move `TO_SLASH` from `f_bufadd` to `buflist_new` Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* fix(socket): --listen silently truncates long paths #40625MAAZIZ Adel Ayoub2026-07-09
| | | | | | | | | | | | | | | | | | | | | | | | | | Problem: A `--listen` path longer than the system socket path limit (~104 bytes on macOS, 108 on Linux) is silently truncated by `uv_pipe_bind()`. Nvim either serves a socket at a different path than `v:servername` reports, or fails with an error that blames the full untruncated path (confusing): nvim: Failed to --listen: address already in use: "<full path>" Steps to reproduce: $ nvim --listen /var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide --embed -p nvim: Failed to --listen: address already in use: "/var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide" $ ls /var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide "/var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide": No such file or directory (os error 2) $ ls -l /var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/ srwxr-xr-x@ - rpatterson 31 Mar 10:24 -Users-rpatterson-Projects-src-github.com-neovid Solution: Bind with `uv_pipe_bind2()` and `UV_PIPE_NO_TRUNCATE` (libuv 1.46+), so a too-long path fails up front with the actual reason: nvim: Failed to --listen: invalid argument: "<full path>"
* refactor(os): libuv_proc_stdio #40506Justin M. Keyes2026-06-30
|
* fix(process): avoid inherited stdio for Windows jobs #40074Sanzhar Kuandyk2026-06-29
| | | | | | | | | | | Problem: On Windows, channel jobs inherit Nvim's stdio, so a background job writing to CON (e.g. gutentags) draws onto the TUI and stays until redraw. Solution: Give Windows job stdin/stderr libuv-created pipes (UV_CREATE_PIPE) instead of inherited fds, so libuv spawns the child with CREATE_NO_WINDOW and CON writes no longer leak onto the TUI.
* feat(ui_client): "press ENTER" free nvim crash debuggingbfredl2026-05-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This feature might be a little silly and niche, but it is very useful for _my_ workflow (and open source is about mee) An issue which is never present on high quality RELEASE builds, but might occur on Debug builds is that the Nvim server crashes on some error in your unfinished PR code. If you compile your debug builds with sanitizers enabled, as you should, the ASAN/UBSAN runtime will print some useful info about your mistake to stderr or a log file, such as a stack trace. This can be used to jump to the error in the code. This allows the nvim server to install a signal hander in the ui client, which can load this log file in a good safe version of nvim and parse it using 'errorformat' This is inspired by the "press ENTER" free workflow of ui2 and applies it beyond the lifetime cycle of the nvim instance. example config: ```lua local asan = vim.env.ASAN_OPTIONS if asan ~= nil and string.match(asan, "log_path=/tmp/nvim_asan") then local myname = "/tmp/nvim_asan."..vim.uv.getpid() local args = {"--embed", "-n", "+set efm=%+A%*[^/]%f:%l:%c", "+silent cfile "..myname, "+silent cfirst", "+silent copen"} vim.api.nvim__set_restart_on_crash("nvim", args) end ``` and run your debug nvim like so ASAN_OPTIONS=handle_abort=1,handle_sigill=1,log_path=/tmp/nvim_asan ./build/bin/nvim
* fix(channel): stack-buffer-overflow with exit during connection (#39387)zeertzjq2026-04-25
| | | | | | Problem: When Nvim exits while connecting to a socket it leads to stack-buffer-overflow. Solution: Associate the handle with the Stream and use the Stream's internal_close_cb to update the "closed" status.
* fix(path): normalize path slashes on Windows #37729tao2026-04-24
| | | | | | | | | | | | | | | | | | | | Problem: On Windows, path separators may become inconsistent for various reasons, which makes normalization quite painful. Solution: Normalize paths to `/` at the entry boundaries and always use it internally, converting back only in rare cases where `\` is really needed (e.g. cmd.exe/bat scripts?). This is the first commit in a series of incremental steps. Note: * some funcs won't respect shellslash. e.g. `expand/fnamemodify` * some funcs still respect shellslash, but will be updated in a follow PR. e.g. `ex_pwd/f_chdir/f_getcwd` * uv's built-in funcs always return `\`. e.g. `uv.cwd/uv.exepath` Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* build: update clang v21, fix warningsdundargoc2026-04-14
| | | | | - `src/nvim/ex_cmds_defs.h`: use "U" instead of "u" per `readability-uppercase-literal-suffix`
* revert: "refactor(process): don't read from PTY master using uv_pipe_t" (#37401)zeertzjq2026-03-30
| | | This reverts commit 75c8f755015e4724ba45bd84137d41efa40a3025.
* fix(:restart): formalize restart event #35223Sathya Pramodh2026-03-28
| | | | | | | | | | | | | | | | | | | Problem: The "restart" event has some problems: - all UI clients must implement a somewhat complex set of setups - UI must be on the same machine as the server - only works for the "current" UI - race/edge case: If the user config has errors / waiting for input, are all UIs able to attach while Nvim is waiting for input? Solution: - Perform the restart on the server, not the client. - Pass listen address (instead of CLI args) in the UI event. - Simplifies UI logic: they only need to attach to new address. - Opens the door for more enhancements in the future, such as allowing all UIs to reattach instead of only the "current" UI. Co-authored-by: zeertzjq <zeertzjq@outlook.com> Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* fix(pty): prevent orphan conhost.exe on Windows 10 #38244tao2026-03-20
| | | | | | | | | | | | | | | | | Problem: On Windows 10, the ConPTY closing order is strict. Calling ClosePseudoConsole after closing the output stream can easily lead to accidental deadlocks, leaving orphan conhost.exe processes. See https://learn.microsoft.com/en-us/windows/console/closepseudoconsole#remarks and https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#ending-the-pseudoconsole-session Solution: Based on the warning in the docs above, we need to call ClosePseudoConsole on a separate thread first so that the output pipe can fully drain on the main thread. Also, `wait_eof_timer` is outdated, so I removed it to avoid extra cleanup logic around it (it was introduced in the early winpty days). Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* fix(process): wrong exit code for SIGHUP on Windows (#38242)zeertzjq2026-03-12
| | | | | | | | | Problem: When stopping a PTY process on Windows, the exit code indicates that the process is stopped by SIGTERM even when closing all streams is enough to terminate the process. This is inconsistent with other platforms. Solution: Set exit_signal to SIGHUP instead of SIGTERM when using SIGHUP.
* revert: "fix(tui): server --listen error sometimes not visible (#38027)" ↵zeertzjq2026-03-06
| | | | | | | | (#38175) This reverts commit ab8371a26cf47a40d26af637455ea71da0d5a59d. Need to think of a different solution, which may require adding new flags to nvim_ui_attach() (e.g. passing stdout or stderr fd).
* fix(build): glibc 2.43 happenedbfredl2026-03-05
| | | | | | | | | | using the GNU compiler we just get a bunch of const warnings we can fix. clang, however, gets really upset that the standard library suddenly starts using a lot of c11 features, despite us being in -std=gnu99 mode. Basically, _GNU_SOURCE which we set is taken as a _carte blanche_ by the glibc headers to do whatever they please, and thus we must inform clang that everything is still OK.
* fix(tui): server --listen error sometimes not visible (#38027)zeertzjq2026-03-02
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: If Nvim server fails to --listen and prints error before the TUI enters alternate screen, the error isn't visible. Solution: Forward server stderr using client side stderr handler instead of having the server inherit client stderr file descriptor. This does mean that `stderr_isatty` will be `false` in the server, but that value doesn't matter in embedded mode. Always pass `stdin_fd` to embedded server to avoid a hang when reading from stdin when it's a TTY (not sure why one wants to do that, perhaps by mistake), because if `stdin_fd` isn't passed, the server will try to use stderr as stdin. Example test failure on CI: FAILED test/functional/terminal/tui_spec.lua @ 41: TUI exit status 1 and error message with server --listen error #34365 test/functional/terminal\tui_spec.lua:55: Failed to match any screen lines. Expected (anywhere): "nvim%.exe: Failed to %-%-listen: address already in use:" Actual: |{114:nvim.exe -h"} | | | |[Process exited 1]^ | | | | | | | | | | | | | | | | | | | | | | | | | | | |{5:-- TERMINAL --} | Snapshot: screen:expect([[ {114:nvim.exe -h"} | | [Process exited 1]^ | |*13 {5:-- TERMINAL --} | ]]) stack traceback: test\functional\ui\screen.lua:909: in function '_wait' test\functional\ui\screen.lua:537: in function 'expect' test/functional/terminal\tui_spec.lua:55: in function <test/functional/terminal\tui_spec.lua:41> In this case, it appears that the client entered alternate screen in the middle of the server's print_mainerr().
* fix(coverity/644326): NULL ptr deref in socket_address_is_tcp #38010Sathya Pramodh2026-02-23
|
* fix(process): handle poll() interrupted by a signal (#38024)zeertzjq2026-02-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | It is indeed possible that a signal may arrive during flush_stream() (e.g. SIGCHLD from another child process), so poll() again on EINTR. Fixes the following Coverity warning: _____________________________________________________________________________________________ *** CID 644345: Error handling issues (CHECKED_RETURN) /src/nvim/event/proc.c: 405 in flush_stream() 399 400 #ifdef __linux__ 401 // On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending 402 // work on kernel workqueue, so use an explcit poll() before that. #37982 403 if (proc->type == kProcTypePty && !stream->did_eof) { 404 struct pollfd pollfd = { .fd = ((PtyProc *)proc)->tty_fd, .events = POLLIN }; >>> CID 644345: Error handling issues (CHECKED_RETURN) >>> Calling "poll(&pollfd, 1UL, 0)" without checking return value. This library function may fail and return an error code. 405 poll(&pollfd, 1, 0); 406 } 407 #endif 408 // Poll for data and process the generated events. 409 loop_poll_events(proc->loop, 0); 410 if (stream->s.events) { Another possible error is ENOMEM, which is probably not worth handling. For reference, #23308 previously removed a case of ENOMEM handling, and this PR also removes an outdated mention of that. Also reduce the number of #ifdefs in non-OS-specific files.
* fix(process): use poll() to drain PTY master on Linux (#38005)zeertzjq2026-02-23
| | | | On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending work on kernel workqueue, so use an explicit poll() before that.
* refactor(net): socket_address_is_tcp #37947Sathya Pramodh2026-02-20
|
* fix(event-loop): don't call uv_stop() when loop isn't running (#37984)zeertzjq2026-02-21
| | | Otherwise it will cause the next uv_run() to stop immediately.
* fix(rpc): include write failure reason in log (#37959)zeertzjq2026-02-19
| | | Also don't use LOGLVL_ERR on UV_EPIPE.
* feat(terminal): detect suspended PTY process (#37845)zeertzjq2026-02-13
| | | | | | | Problem: Terminal doesn't detect if the PTY process is suspended or offer a convenient way for the user to resume the process. Solution: Detect suspended PTY process on SIGCHLD and show virtual text "[Process suspended]" at the bottom-left. Resume the process when the user presses a key.
* fix(channel): possible hang after connecting with TCP times out (#37813)zeertzjq2026-02-11
| | | | Problem: Possible hang after connecting with TCP times out. Solution: Wait for the close callback to arrive.
* fix(socket): remove stale socket files (#37378)James Fotherby2026-01-20
| | | | | | | | | Problem: Crashed Nvim leaves socket files. Subsequent starts fail with "address already in use". Solution: On bind failure, test socket liveness via connect(). Remove if stale, retry bind(). Fixes #36581
* fix(process): don't limit PTY master remaining data sizezeertzjq2026-01-15
|
* refactor(process): don't read from PTY master using uv_pipe_tzeertzjq2026-01-15
| | | | | | | | | Using uv_pipe_t to read from PTY master may drop data if the PTY process exits immediately after output, as libuv treats a partial read after POLLHUP as EOF, which isn't true for PTY master on Linux. Therefore use uv_poll_t instead. This commit can be reverted if libuv/libuv#4992 is fixed for uv_pipe_t.
* refactor(event/stream.c): fix confusing indent (#37398)zeertzjq2026-01-15
|
* fix(shell): ceci n'est pas une pipebfredl2026-01-13
| | | | | | | | | | | | | | | | | | On linux /dev/stdin is defined as a symlink to /proc/self/fd/0 This in turn is defined as a "magic" symlink which is allowed to point to internal kernel objects which really does not have a file name. As a glaring inconsistency, fopen("/proc/self/fd/0", "r") works if fd was originally opened using pipe() but not using socketpair(). As it happens UV_CREATE_PIPE does not create pipes but creates socket pairs. These two unfortunate conditions means that using /dev/stdin and similar does not work in shell commands in nvim on linux. as a work around, override libuv's descicion and create an actual pipe pair. This change is not needed on BSD:s but done unconditionally for simplicity, except for on windows where it is not done for stdout because of windows fixes #35984
* fix(socket): avoid stack-use-after-return after timeout (#36405)zeertzjq2025-11-01
| | | | | | | | | | | | | | | | | | | Problem: In socket_connect(), if connecting to the given TCP address times out, libuv is still trying to connect to the address, and connect_cb may be called when running the libuv event loop after the `status` variable referenced by `req.data` goes out of scope. Solution: Close the uv_tcp_t handle and wait for connect_cb to be called before retrying or failing in socket_connect(). This also avoid leaking libuv handles. The tests added here only check that the non-timeout case still works, as checking the timeout case is very hard without modifications to the code. Removing the first LOOP_PROCESS_EVENT_UNTIL() in socket_connect() (the one with the timeout) is a way to check that manually. Also add a comment about the cause of the ASAN error in #34586.
* Merge pull request #36393 from zeertzjq/rstream-close-cbzeertzjq2025-10-31
| | | fix(channel): closing socket with pending writes leaks memory
* feat(ui): :connect command #34586Siddhant Agarwal2025-08-14
| | | | Add the `:connect <address>` command which connects the currently running TUI to the server at the given address.
* refactor(build): remove INCLUDE_GENERATED_DECLARATIONS guardsbfredl2025-08-14
| | | | | | | | | These are not needed after #35129 but making uncrustify still play nice with them was a bit tricky. Unfortunately `uncrustify --update-config-with-doc` breaks strings with backslashes. This issue has been reported upstream, and in the meanwhile auto-update on every single run has been disabled.
* fix(tui): wait for embedded server's exit codezeertzjq2025-06-10
| | | | | | Uses the undocumented "error_exit" UI event for a different purpose: When :detach is used on the server, send an "error_exit" with 0 `status` to indicate that the server shouldn't wait for client exit.
* fix(mswin): UI may hang on exitJustin M. Keyes2025-03-25
| | | | | | | | | Problem: On Windows, since b360c0608556, UI may hang on exit. #33019 Solution: Restore the hack in on_proc_exit, until we can figure out why rpc_close is not called in the UI client when the server closes the channel.
* refactor(channel): redundant channel_close()Justin M. Keyes2025-03-24
| | | | | | | | | | Problem: `receive_msgpack()` calls `channel_close()` just before calling `chan_close_on_err()`, even though `chan_close_on_err()` always calls `channel_close()`. Solution: Remove the redunant `channel_close()` in `receive_msgpack()`.
* refactor(multiqueue): rename multiqueue_new_parent #32767Justin M. Keyes2025-03-08
|
* fix(terminal): avoid rescheduling events onto the same queue (#32755)Gregory Anders2025-03-07
| | | | | | | | | | | | | | | | | Problem: When a function like vim.wait() is used, we continuously drain the main event queue until it is empty, never stopping for user input. This means the libuv timer never runs and the terminal never gets refreshed, so emit_termrequest continously reschedules itself onto the same event queue, causing an infinite loop. Solution: Use a separate "pending" event queue, where events that require a terminal refresh are temporarily placed. Drain this queue after a terminal refresh and events are copied back onto the main queue. This prevents infinite loops since the main event queue will always be able to properly drain.
* refactor(channel): eliminate special case in on_proc_exit() #32485Justin M. Keyes2025-02-17
| | | | | | | | | | | Problem: on_proc_exit() has a special-case that assumes that the UI client will never spawn more than 1 child process. Solution: If the Nvim server exits, the stream EOF will trigger `rpc_close()` in the UI client, so we don't need the special case in `on_proc_exit`. Pass `Channel.exit_status` from `rpc_close()` so that the correct exit code is reflected.
* fix(ui): Windows :detach is opt-inJustin M. Keyes2025-02-10
| | | | | | | | | Problem: On Windows, spawning the `nvim --embed` server with `detach=true` breaks various `tt.setup_child_nvim` tests. Solution: Make this behavior opt-in with an env var, temporarily.
* feat(ui): UI :detach commandJustin M. Keyes2025-02-10
| | | | | | | | | | | Problem: Cannot detach the current UI. Solution: - Introduce `:detach`. - Introduce `Channel.detach`. Co-authored-by: bfredl <bjorn.linse@gmail.com>
* docs: miscdundargoc2025-01-30
| | | | | | | | | Co-authored-by: Dustin S. <dstackmasta27@gmail.com> Co-authored-by: Ferenc Fejes <fejes@inf.elte.hu> Co-authored-by: Maria José Solano <majosolano99@gmail.com> Co-authored-by: Yochem van Rosmalen <git@yochem.nl> Co-authored-by: brianhuster <phambinhanctb2004@gmail.com> Co-authored-by: zeertzjq <zeertzjq@outlook.com>
* build: fix lint error on macosdundargoc2025-01-15
|
* feat(messages): add :!cmd shell message kindsLuuk van Baal2025-01-15
| | | | Also print stderr error messages with ErrorMsg highlight group.
* refactor: iwyu #31637Justin M. Keyes2024-12-23
| | | Result of `make iwyu` (after some "fixups").
* refactor(event): change last use of klist to kvecbfredl2024-09-28
| | | | | | | | | | | loop->children might have been a linked list because used to be modified in place while looped over. However the loops that exists rather schedules events to be processed later, outside of the loop, so this can not happen anymore. When a linked list is otherwise useful it is better to use lib/queue_defs.h which defines an _intrusive_ linked list (i e it doesn't need to do allocations for list items like klist ).
* fix(channel): handle writing to file instead of pipe (#30519)zeertzjq2024-09-26
|
* build: fix or silence new clang-tidy warningsdundargoc2024-09-25
|