fix(input): resume async vim.ui.input await when the prompt closes without a callback - #1982
Open
dmbhatti wants to merge 2 commits into
Open
fix(input): resume async vim.ui.input await when the prompt closes without a callback#1982dmbhatti wants to merge 2 commits into
dmbhatti wants to merge 2 commits into
Conversation
Contributor
Author
|
requires more consideration. I experienced a case where issue was encountered even with this fix in place this unblocked it :lua local L=require("neogit.lib.popup").__lock; print(("permits=%d waiting=%d"):format(L.permits, #L._waiting)); L.permits=1; L._waiting={} |
added 2 commits
August 13, 2026 08:57
…thout a callback Some vim.ui.input implementations (e.g. snacks.nvim) only invoke on_confirm via their own confirm/cancel actions and skip it entirely when the prompt window is dismissed another way (:q, <C-w>c, a focus steal, a programmatic close). A dropped callback parked the awaiting coroutine forever -- and because popup actions run under a single shared permit lock (popup M.__lock), one parked action silently turned every subsequent popup action into a no-op until Neovim was restarted. Harden the async input wrapper to guarantee its callback fires exactly once: the real value if the user responds, or nil (a normal cancel) if the prompt's floating window closes first, via a one-shot WinClosed autocmd (deferred so a genuine value always wins the race). This releases the popup action permit on every path without changing the locking model. Add a regression spec covering stray-close-as-cancel, value-wins-race, explicit-cancel, and a synchronous cmdline-style implementation.
…s its callback Popup actions run under a single shared permit lock. An action that awaits an operation (a git process, a finder selection, user input) holds the permit while its coroutine is suspended and only returns it once the coroutine resumes and reaches permit:forget(). If that resume callback is ever dropped, the coroutine parks forever and the permit is never returned -- silently turning every subsequent popup action into a no-op until Neovim restarts (e.g. `fa` fetch-all or `la` log-all open their popup, close it, and nothing happens). The prior input fix closed one leaf (vim.ui.input). Close the remaining common ones and add a defensive backstop: - process: on_exit invoked the resume callback only after its presentation and console-management logic; a throw there (e.g. auto_close_console closing an already-gone window) skipped the callback. Wrap that logic in pcall so cb(res) always fires. Also fix the spawn-failure path, where the cb(nil) call was dead code after error(). - async: a wrapped leaf that throws synchronously before invoking its callback (a failed spawn, a picker that errors on open) left the coroutine suspended forever. Resume it with the error instead, so the await re-raises and a surrounding pcall can run its cleanup (releasing the permit). - finder: guarantee find's on_select fires exactly once on every dismissal path, and add a close/abort net to each picker branch that lacked one (telescope BufWipeout, fzf-lua on_close, mini.pick MiniPickStop, vim.ui.select WinClosed). Add regression specs for each layer.
dmbhatti
force-pushed
the
fix/popup-action-lock-input-leak
branch
from
August 13, 2026 07:03
1744914 to
5e4863b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Some vim.ui.input implementations (e.g. snacks.nvim) only invoke on_confirm via their own confirm/cancel actions and skip it entirely when the prompt window is dismissed another way (:q, c, a focus steal, a programmatic close). A dropped callback parked the awaiting coroutine forever -- and because popup actions run under a single shared permit lock (popup M.__lock), one parked action silently turned every subsequent popup action into a no-op until Neovim was restarted.
Harden the async input wrapper to guarantee its callback fires exactly once: the real value if the user responds, or nil (a normal cancel) if the prompt's floating window closes first, via a one-shot WinClosed autocmd (deferred so a genuine value always wins the race). This releases the popup action permit on every path without changing the locking model.
Add a regression spec covering stray-close-as-cancel, value-wins-race, explicit-cancel, and a synchronous cmdline-style implementation.