aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/lua/executor.c
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2026-04-17 19:10:20 -0400
committerGitHub <noreply@github.com>2026-04-17 19:10:20 -0400
commit0d4d285bd2c19697164175280b32bf93e3a7b1da (patch)
tree0aa20f3e342279e9cba36e5c8daa0d38b2507c8d /src/nvim/lua/executor.c
parent03193e2963a6f9333afc3090ad2c8002b648fe11 (diff)
downloadrneovim-0d4d285bd2c19697164175280b32bf93e3a7b1da.tar.gz
rneovim-0d4d285bd2c19697164175280b32bf93e3a7b1da.tar.bz2
rneovim-0d4d285bd2c19697164175280b32bf93e3a7b1da.zip
perf(vim.fn): call Lua-implemented vim.fn.xx() directly #39166
Problem: - Builtin "Vimscript" functions (f_xx) are mostly implemented in C. Partly that's because there is some boilerplate required to call out to Lua. - Calls to `vim.fn.foo()` always marshall over the Lua <=> Vimscript ("typval") bridge, even if `fn.foo()` is implemented entirely in Lua: ``` Lua => typval => Object => Lua => Object => typval => Lua. ``` Solution: Functions declared in eval.lua with `func_lua` are implemented in entirely in Lua (`_core/vimfn.lua`). - `gen_eval.lua` wires `func_lua` entries to `lua_wrapper`, which handles the typval conversion for Vimscript callers (slow path). - `nlua_call()` detects `func_lua` functions and calls the Lua implementation directly. This eliminates all conversion overhead for Lua callers (fast path). - Validate at build-time that `func`, `func_float`, and `func_lua` are mutually exclusive. - Migrate `hostname()` as a toy example, to show the idea.
Diffstat (limited to 'src/nvim/lua/executor.c')
-rw-r--r--src/nvim/lua/executor.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 7c4e42420a..4cd81e365d 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1207,6 +1207,7 @@ static bool viml_func_is_fast(const char *name)
return false;
}
+/// "vim.call()", aka "vim.fn".
int nlua_call(lua_State *lstate)
{
Error err = ERROR_INIT;
@@ -1224,6 +1225,27 @@ int nlua_call(lua_State *lstate)
return luaL_error(lstate, "Function called with too many arguments");
}
+ // Fast path: if the vimfn is defined with `func_lua`, call the Lua function
+ // directly without Lua→typval→Object→Lua round-trip.
+ const char *func_lua = find_internal_func_lua(name);
+ if (func_lua) {
+ lua_getglobal(lstate, "require");
+ lua_pushliteral(lstate, "vim._core.vimfn");
+ if (lua_pcall(lstate, 1, 1, 0) != 0) {
+ return lua_error(lstate);
+ }
+ lua_getfield(lstate, -1, func_lua);
+ lua_remove(lstate, -2); // remove module table
+ // Push args (still on the stack as Lua values).
+ for (int j = 0; j < nargs; j++) {
+ lua_pushvalue(lstate, j + 2);
+ }
+ if (lua_pcall(lstate, nargs, 1, 0) != 0) {
+ return lua_error(lstate);
+ }
+ return 1;
+ }
+
typval_T vim_args[MAX_FUNC_ARGS + 1];
int i = 0; // also used for freeing the variables
for (; i < nargs; i++) {