Skip to content

fix(input): resume async vim.ui.input await when the prompt closes without a callback#1982

Open
dmbhatti wants to merge 1 commit into
NeogitOrg:masterfrom
dmbhatti:fix/popup-action-lock-input-leak
Open

fix(input): resume async vim.ui.input await when the prompt closes without a callback#1982
dmbhatti wants to merge 1 commit into
NeogitOrg:masterfrom
dmbhatti:fix/popup-action-lock-input-leak

Conversation

@dmbhatti

@dmbhatti dmbhatti commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

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.

…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.
@dmbhatti

dmbhatti commented Jul 9, 2026

Copy link
Copy Markdown
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={}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant