aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/lua/executor.c
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2026-03-01 12:05:59 +0000
committerLewis Russell <me@lewisr.dev>2026-04-27 11:33:47 +0100
commitc822a2657c78e4b9d3798dfdf93e365460b5b885 (patch)
treeabaf20eaf4f9a41f74638dd521d5f3df8cd55432 /src/nvim/lua/executor.c
parent443171328531e33a7caecda0b81dadd826518a58 (diff)
downloadrneovim-c822a2657c78e4b9d3798dfdf93e365460b5b885.tar.gz
rneovim-c822a2657c78e4b9d3798dfdf93e365460b5b885.tar.bz2
rneovim-c822a2657c78e4b9d3798dfdf93e365460b5b885.zip
refactor(lua): move vim.wait into runtime Lua
Move vim.wait into runtime/lua/vim/_core/editor.lua and replace the C entrypoint with narrow vim._core helpers for polling, UI flushing, and interrupt checks. Keep the existing interval semantics by retaining the dummy timer that wakes the loop while it is otherwise idle. Update the docs to describe the success return values correctly, and adjust the test expectation for the new vim.validate() callback error. AI-assisted: Codex
Diffstat (limited to 'src/nvim/lua/executor.c')
-rw-r--r--src/nvim/lua/executor.c166
1 files changed, 32 insertions, 134 deletions
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 8a31a5dd2b..4a747848f3 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -520,148 +520,36 @@ static int nlua_schedule(lua_State *const lstate)
return 2;
}
-// Dummy timer callback. Used by vim.wait().
-static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
+static int nlua_loop_poll(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
{
- // If the main loop is closing, the condition won't be checked again.
- // Close the timer to avoid leaking resources.
- if (main_loop.closing) {
- time_watcher_stop(tw);
- time_watcher_close(tw, dummy_timer_close_cb);
- }
-}
+ int64_t timeout = (int64_t)luaL_checkinteger(lstate, 1);
+ bool fast_only = lua_toboolean(lstate, 2);
-// Dummy timer close callback. Used by vim.wait().
-static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
-{
- xfree(tw);
-}
+ MultiQueue *loop_events = fast_only ? main_loop.fast_events : main_loop.events;
+ LOOP_PROCESS_EVENTS(&main_loop, loop_events, timeout);
-static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result,
- int *nresults)
-{
- int top = lua_gettop(lstate);
- lua_pushvalue(lstate, 2);
- *status = nlua_pcall(lstate, 0, LUA_MULTRET);
- if (*status) {
- return true; // break on error, but keep error on stack
- }
- *nresults = lua_gettop(lstate) - top;
- if (*nresults == 0) {
- *callback_result = false;
- return false;
- }
- *callback_result = lua_toboolean(lstate, top + 1);
- if (!*callback_result) {
- lua_settop(lstate, top);
- return false;
- }
- lua_remove(lstate, top + 1);
- (*nresults)--;
- return true; // break if true
+ return 0;
}
-/// "vim.wait(timeout, condition[, interval])" function
-static int nlua_wait(lua_State *lstate)
+static int nlua_ui_flush(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
- if (in_fast_callback) {
- return luaL_error(lstate, e_fast_api_disabled, "vim.wait");
- }
-
- double timeout_number = luaL_checknumber(lstate, 1);
- if (timeout_number < 0) {
- return luaL_error(lstate, "timeout must be >= 0");
- }
- int64_t timeout = (isnan(timeout_number) || timeout_number > (double)INT64_MAX)
- ? INT64_MAX
- : (int64_t)timeout_number;
-
- int lua_top = lua_gettop(lstate);
-
- // 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: callback must be callable");
- return lua_error(lstate);
- }
- }
-
- intptr_t interval = 200;
- 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 ? main_loop.fast_events : main_loop.events;
-
- TimeWatcher *tw = xmalloc(sizeof(TimeWatcher));
-
- // Start dummy timer.
- time_watcher_init(&main_loop, tw, NULL);
- // Don't schedule the due callback, as that'll lead to two different types of events
- // on each interval, causing the condition to be checked twice.
- tw->events = NULL;
- time_watcher_start(tw, dummy_timer_due_cb, (uint64_t)interval, (uint64_t)interval);
-
- int pcall_status = 0;
- bool callback_result = false;
- int nresults = 0;
-
- // Flush screen updates before blocking.
ui_flush();
+ return 0;
+}
- LOOP_PROCESS_EVENTS_UNTIL(&main_loop,
- loop_events,
- timeout,
- got_int || (is_function ? nlua_wait_condition(lstate,
- &pcall_status,
- &callback_result,
- &nresults)
- : false));
-
- // Stop dummy timer
- time_watcher_stop(tw);
- time_watcher_close(tw, dummy_timer_close_cb);
-
- if (pcall_status) {
- return lua_error(lstate);
- } else if (callback_result) {
- lua_pushboolean(lstate, 1);
- if (nresults == 0) {
- lua_pushnil(lstate);
- nresults = 1;
- } else {
- lua_insert(lstate, -1 - nresults);
- }
- return nresults + 1;
- } else if (got_int) {
+static int nlua_check_interrupt(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ bool interrupted = got_int;
+ if (got_int) {
got_int = false;
vgetc();
- lua_pushboolean(lstate, 0);
- lua_pushinteger(lstate, -2);
- return 2;
- } else {
- lua_pushboolean(lstate, 0);
- lua_pushinteger(lstate, -1);
- return 2;
}
+
+ lua_pushboolean(lstate, interrupted);
+ return 1;
}
static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread)
@@ -718,6 +606,10 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread, bool is_stan
lua_pushcfunction(lstate, &nlua_is_thread);
lua_setfield(lstate, -2, "is_thread");
+ // vim._core
+ lua_createtable(lstate, 0, 0);
+ lua_setfield(lstate, -2, "_core");
+
// vim.NIL
lua_newuserdata(lstate, 0);
lua_createtable(lstate, 0, 0);
@@ -929,10 +821,6 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_rpcnotify);
lua_setfield(lstate, -2, "rpcnotify");
- // wait
- lua_pushcfunction(lstate, &nlua_wait);
- lua_setfield(lstate, -2, "wait");
-
// ui_attach
lua_pushcfunction(lstate, &nlua_ui_attach);
lua_setfield(lstate, -2, "ui_attach");
@@ -943,6 +831,16 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
nlua_common_vim_init(lstate, false, false);
+ // vim._core wait helpers
+ lua_getfield(lstate, -1, "_core");
+ lua_pushcfunction(lstate, &nlua_loop_poll);
+ lua_setfield(lstate, -2, "loop_poll");
+ lua_pushcfunction(lstate, &nlua_ui_flush);
+ lua_setfield(lstate, -2, "ui_flush");
+ lua_pushcfunction(lstate, &nlua_check_interrupt);
+ lua_setfield(lstate, -2, "check_interrupt");
+ lua_pop(lstate, 1);
+
// patch require() (only for --startuptime)
if (time_fd != NULL) {
lua_getglobal(lstate, "require");