commit b87bdef2a8649ce40a8f722432934bef3d6d4b57
parent d2e445e1bd321ea43b976d6aa7759d90b826ce62
Author: skewb1k <skewb1kunix@gmail.com>
Date: Wed, 10 Dec 2025 17:44:05 +0300
fix(lua): relax `vim.wait()` timeout validation #36900
Problem:
After bc0635a9fc9c15a2423d4eb35f627d127d00fa46 `vim.wait()` rejects floats
and NaN values.
Solution:
Restore the prior behavior, while still supporting `math.huge`. Update
tests to cover float case.
Diffstat:
4 files changed, 12 insertions(+), 16 deletions(-)
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
@@ -862,8 +862,8 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
-- Wait up to 1000 ms or until `vim.g.foo` is true, at intervals of ~500 ms.
vim.wait(1000, function() return vim.g.foo end, 500)
- -- Wait up to 100 ms or until `vim.g.foo` is true, and get the callback results.
- local ok, rv1, rv2, rv3 = vim.wait(100, function()
+ -- Wait indefinitely until `vim.g.foo` is true, and get the callback results.
+ local ok, rv1, rv2, rv3 = vim.wait(math.huge, function()
return vim.g.foo, 'a', 42, { ok = { 'yes' } }
end)
@@ -876,7 +876,8 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
<
Parameters: ~
- • {time} (`integer`) Number of milliseconds to wait
+ • {time} (`number`) Number of milliseconds to wait. Must be
+ non-negative number, any fractional part is truncated.
• {callback} (`fun(): boolean, ...?`) Optional callback. Waits until
{callback} returns true
• {interval} (`integer?`) (Approximate) number of milliseconds to wait
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
@@ -195,8 +195,8 @@ function vim.schedule(fn) end
--- -- Wait up to 1000 ms or until `vim.g.foo` is true, at intervals of ~500 ms.
--- vim.wait(1000, function() return vim.g.foo end, 500)
---
---- -- Wait up to 100 ms or until `vim.g.foo` is true, and get the callback results.
---- local ok, rv1, rv2, rv3 = vim.wait(100, function()
+--- -- Wait indefinitely until `vim.g.foo` is true, and get the callback results.
+--- local ok, rv1, rv2, rv3 = vim.wait(math.huge, function()
--- return vim.g.foo, 'a', 42, { ok = { 'yes' } }
--- end)
---
@@ -208,7 +208,8 @@ function vim.schedule(fn) end
--- end
--- ```
---
---- @param time integer Number of milliseconds to wait
+--- @param time number Number of milliseconds to wait. Must be non-negative number, any fractional
+--- part is truncated.
--- @param callback? fun(): boolean, ... Optional callback. Waits until {callback} returns true
--- @param interval? integer (Approximate) number of milliseconds to wait between polls
--- @param fast_only? boolean If true, only |api-fast| events will be processed.
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
@@ -456,15 +456,9 @@ static int nlua_wait(lua_State *lstate)
if (timeout_number < 0) {
return luaL_error(lstate, "timeout must be >= 0");
}
- int64_t timeout;
- if (isinf(timeout_number) || timeout_number > (double)INT64_MAX) {
- timeout = INT64_MAX;
- } else {
- if (isnan(timeout_number) || timeout_number != trunc(timeout_number)) {
- return luaL_error(lstate, "timeout has no integer representation");
- }
- timeout = (int64_t)timeout_number;
- }
+ int64_t timeout = (isnan(timeout_number) || timeout_number > (double)INT64_MAX)
+ ? INT64_MAX
+ : (int64_t)timeout_number;
int lua_top = lua_gettop(lstate);
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
@@ -2319,7 +2319,7 @@ stack traceback:
true,
exec_lua [[
local start_time = vim.uv.hrtime()
- vim.wait(50, nil) -- select('#', ...) == 1
+ vim.wait(50.1, nil) -- select('#', ...) == 1
return vim.uv.hrtime() - start_time > 25000
]]
)