aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lua.txt9
-rw-r--r--src/nvim/lua/executor.c53
-rw-r--r--test/functional/lua/vim_spec.lua45
3 files changed, 84 insertions, 23 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 2b638a8539..a53024d420 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -737,13 +737,20 @@ vim.defer_fn({fn}, {timeout}) *vim.defer_fn*
Returns: ~
|vim.loop|.new_timer() object
-vim.wait({time}, {callback} [, {interval}]) *vim.wait()*
+vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()*
Wait for {time} in milliseconds until {callback} returns `true`.
Executes {callback} immediately and at approximately {interval}
milliseconds (default 200). Nvim still processes other events during
this time.
+ Parameters: ~
+ {time} Number of milliseconds to wait
+ {callback} Optional callback. Waits until {callback} returns true
+ {interval} (Approximate) number of milliseconds to wait between polls
+ {fast_only} If true, only |api-fast| events will be processed.
+ If called from while in an |api-fast| event, will
+ automatically be set to `true`.
Returns: ~
If {callback} returns `true` during the {time}:
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 7722f9cdc3..5c665920b5 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -299,45 +299,66 @@ static int nlua_wait(lua_State *lstate)
return luaL_error(lstate, "timeout must be > 0");
}
- // Check if condition can be called.
- bool is_function = (lua_type(lstate, 2) == LUA_TFUNCTION);
+ int lua_top = lua_gettop(lstate);
- // Check if condition is callable table
- if (!is_function && luaL_getmetafield(lstate, 2, "__call") != 0) {
- is_function = (lua_type(lstate, -1) == LUA_TFUNCTION);
- lua_pop(lstate, 1);
- }
+ // Check if condition can be called.
+ bool is_function = false;
+ if (lua_top >= 2 && !lua_isnil(lstate, 2)) {
+ is_function = (lua_type(lstate, 2) == LUA_TFUNCTION);
+
+ // Check if condition is callable table
+ if (!is_function && luaL_getmetafield(lstate, 2, "__call") != 0) {
+ is_function = (lua_type(lstate, -1) == LUA_TFUNCTION);
+ lua_pop(lstate, 1);
+ }
- if (!is_function) {
- lua_pushliteral(lstate, "vim.wait: condition must be a function");
- return lua_error(lstate);
+ if (!is_function) {
+ lua_pushliteral(
+ lstate,
+ "vim.wait: if passed, condition must be a function");
+ return lua_error(lstate);
+ }
}
intptr_t interval = 200;
- if (lua_gettop(lstate) >= 3) {
+ if (lua_top >= 3 && !lua_isnil(lstate, 3)) {
interval = luaL_checkinteger(lstate, 3);
if (interval < 0) {
return luaL_error(lstate, "interval must be > 0");
}
}
+ bool fast_only = false;
+ if (lua_top >= 4) {
+ fast_only = lua_toboolean(lstate, 4);
+ }
+
+ MultiQueue *loop_events = fast_only || in_fast_callback > 0
+ ? main_loop.fast_events : main_loop.events;
+
TimeWatcher *tw = xmalloc(sizeof(TimeWatcher));
// Start dummy timer.
time_watcher_init(&main_loop, tw, NULL);
- tw->events = main_loop.events;
+ tw->events = loop_events;
tw->blockable = true;
- time_watcher_start(tw, dummy_timer_due_cb,
- (uint64_t)interval, (uint64_t)interval);
+ time_watcher_start(
+ tw,
+ dummy_timer_due_cb,
+ (uint64_t)interval,
+ (uint64_t)interval);
int pcall_status = 0;
bool callback_result = false;
LOOP_PROCESS_EVENTS_UNTIL(
&main_loop,
- main_loop.events,
+ loop_events,
(int)timeout,
- nlua_wait_condition(lstate, &pcall_status, &callback_result) || got_int);
+ is_function ? nlua_wait_condition(
+ lstate,
+ &pcall_status,
+ &callback_result) : false || got_int);
// Stop dummy timer
time_watcher_stop(tw);
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 61447f1152..63e48a18ca 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -1214,6 +1214,23 @@ describe('lua stdlib', function()
]])
end)
+ it('should not process non-fast events when commanded', function()
+ eq({wait_result = false}, exec_lua[[
+ start_time = get_time()
+
+ vim.g.timer_result = false
+ timer = vim.loop.new_timer()
+ timer:start(100, 0, vim.schedule_wrap(function()
+ vim.g.timer_result = true
+ end))
+
+ wait_result = vim.wait(300, function() return vim.g.timer_result end, nil, true)
+
+ return {
+ wait_result = wait_result,
+ }
+ ]])
+ end)
it('should work with vim.defer_fn', function()
eq({time = true, wait_result = true}, exec_lua[[
start_time = get_time()
@@ -1228,22 +1245,38 @@ describe('lua stdlib', function()
]])
end)
- it('should require functions to be passed', function()
+ it('should not crash when callback errors', function()
local pcall_result = exec_lua [[
- return {pcall(function() vim.wait(1000, 13) end)}
+ return {pcall(function() vim.wait(1000, function() error("As Expected") end) end)}
]]
eq(pcall_result[1], false)
- matches('condition must be a function', pcall_result[2])
+ matches('As Expected', pcall_result[2])
end)
- it('should not crash when callback errors', function()
+ it('if callback is passed, it must be a function', function()
local pcall_result = exec_lua [[
- return {pcall(function() vim.wait(1000, function() error("As Expected") end) end)}
+ return {pcall(function() vim.wait(1000, 13) end)}
]]
eq(pcall_result[1], false)
- matches('As Expected', pcall_result[2])
+ matches('if passed, condition must be a function', pcall_result[2])
+ end)
+
+ it('should allow waiting with no callback, explicit', function()
+ eq(true, exec_lua [[
+ local start_time = vim.loop.hrtime()
+ vim.wait(50, nil)
+ return vim.loop.hrtime() - start_time > 25000
+ ]])
+ end)
+
+ it('should allow waiting with no callback, implicit', function()
+ eq(true, exec_lua [[
+ local start_time = vim.loop.hrtime()
+ vim.wait(50)
+ return vim.loop.hrtime() - start_time > 25000
+ ]])
end)
it('should call callbacks exactly once if they return true immediately', function()