fix(lua): vim.wait(0) does not call loop_poll #39679

Problem:
Regression from c822a2657c: `vim.wait(0)` does not call `loop_poll`,
so `vim.wait(1)` is needed to "yield" from Lua.

Solution:
- Ensure that `vim._core.loop_poll()` is always called, even when `time=0`.
- Document how to interrupt Lua code (ctrl-c).

ref https://github.com/neovim/neovim/issues/6800
This commit is contained in:
Justin M. Keyes
2026-05-10 14:22:31 -04:00
committed by GitHub
parent c2d7dd781a
commit 5370eb0146
7 changed files with 81 additions and 14 deletions

View File

@@ -69,6 +69,15 @@ vim._extra = {
--- if vim.wait(10000, function() return vim.g.timer_result end) then
--- print('Only waiting a little bit of time!')
--- end
---
--- -- Yield via vim.wait() to allow CTRL-C to interrupt Lua code.
--- while true do
--- -- ...work...
--- local _, code = vim.wait(0, nil, 0)
--- if code == -2 then -- CTRL-C.
--- break
--- end
--- end
--- ```
---
--- @param time number Number of milliseconds to wait. Must be non-negative number, any fractional
@@ -156,19 +165,26 @@ function vim.wait(time, callback, interval, fast_only)
local poll_timeout = -1
if has_deadline then
local remaining_ms = time - (vim.uv.hrtime() - start) / 1e6
if remaining_ms <= 0 then
cleanup()
return false, -1
end
-- loop_poll() takes an integer timeout, so cap each individual poll
-- while still honoring larger overall waits.
poll_timeout = math.min(math.ceil(remaining_ms), vim._maxint)
-- loop_poll() takes an integer timeout, so cap each individual poll while still honoring
-- larger overall waits. Always poll at least once, so vim.wait(0) still pumps the event loop
-- (lets user code "yield" to CTRL-C).
poll_timeout = math.max(0, math.min(math.ceil(remaining_ms), vim._maxint))
end
-- The dummy timer wakes `vim._core.loop_poll()` on interval boundaries. Without it,
-- polling would only resume for unrelated events or the final timeout.
vim._core.loop_poll(poll_timeout, fast_only)
-- Check deadline AFTER polling (mirrors LOOP_PROCESS_EVENTS_UNTIL behavior).
-- `got_int` may have been set during poll; let check_interrupt() catch it.
if has_deadline and time - (vim.uv.hrtime() - start) / 1e6 <= 0 then
if vim._core.check_interrupt() then
cleanup()
return false, -2
end
cleanup()
return false, -1
end
end
end