From b87bdef2a8649ce40a8f722432934bef3d6d4b57 Mon Sep 17 00:00:00 2001 From: skewb1k Date: Wed, 10 Dec 2025 17:44:05 +0300 Subject: [PATCH] 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. --- runtime/doc/lua.txt | 7 ++++--- runtime/lua/vim/_meta/builtin.lua | 7 ++++--- src/nvim/lua/executor.c | 12 +++--------- test/functional/lua/vim_spec.lua | 2 +- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index b0bbbde452..2cc98bd420 100644 --- 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 index 50b242f765..80524ab148 100644 --- 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 index 30d3f26f0e..4073e0f9a8 100644 --- 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 index 6aef76f476..cb7402c14d 100644 --- 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 ]] )