mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 20:08:17 +00:00
lua: add vim.fn.{func} for direct access to vimL function
compared to vim.api.|nvim_call_function|, this fixes some typing issues due to the indirect conversion via the API. float values are preserved as such (fixes #9389) as well as empty dicts/arrays. Ref https://github.com/norcalli/nvim.lua for the call syntax
This commit is contained in:
@@ -588,6 +588,22 @@ vim.schedule({callback}) *vim.schedule()*
|
|||||||
Schedules {callback} to be invoked soon by the main event-loop. Useful
|
Schedules {callback} to be invoked soon by the main event-loop. Useful
|
||||||
to avoid |textlock| or other temporary restrictions.
|
to avoid |textlock| or other temporary restrictions.
|
||||||
|
|
||||||
|
vim.fn.{func}({...})
|
||||||
|
Call vimL function {func} with arguments. {func} can be both builtin
|
||||||
|
functions and user functions. To call autoload functions, use the
|
||||||
|
syntax `vim.fn['some#function']({...})`
|
||||||
|
|
||||||
|
Note: unlike vim.api.|nvim_call_function| this converts values directly
|
||||||
|
between vimL values and lua values. If the vimL function returns a
|
||||||
|
float, it will be representeted directly as a lua number. Both empty
|
||||||
|
lists and dictonaries will be represented by an empty table.
|
||||||
|
|
||||||
|
Note: vim.fn keys are generated on demand. So `pairs(vim.fn)`
|
||||||
|
does NOT work to enumerate all functions.
|
||||||
|
|
||||||
|
vim.call({func}, {...})
|
||||||
|
Call vim script function {func}. Equivalent to `vim.fn[func]({...})`
|
||||||
|
|
||||||
vim.type_idx *vim.type_idx*
|
vim.type_idx *vim.type_idx*
|
||||||
Type index for use in |lua-special-tbl|. Specifying one of the
|
Type index for use in |lua-special-tbl|. Specifying one of the
|
||||||
values from |vim.types| allows typing the empty table (it is
|
values from |vim.types| allows typing the empty table (it is
|
||||||
|
@@ -401,6 +401,8 @@ nlua_pop_typval_table_processing_end:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool typval_conv_special = false;
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
||||||
@@ -439,7 +441,13 @@ nlua_pop_typval_table_processing_end:
|
|||||||
lua_createtable(lstate, 0, 0)
|
lua_createtable(lstate, 0, 0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
|
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
|
||||||
nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary)
|
do { \
|
||||||
|
if (typval_conv_special) { \
|
||||||
|
nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \
|
||||||
|
} else { \
|
||||||
|
lua_createtable(lstate, 0, 0); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
|
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
|
||||||
do { \
|
do { \
|
||||||
@@ -548,9 +556,11 @@ nlua_pop_typval_table_processing_end:
|
|||||||
/// @param[in] tv typval_T to convert.
|
/// @param[in] tv typval_T to convert.
|
||||||
///
|
///
|
||||||
/// @return true in case of success, false otherwise.
|
/// @return true in case of success, false otherwise.
|
||||||
bool nlua_push_typval(lua_State *lstate, typval_T *const tv)
|
bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special)
|
||||||
{
|
{
|
||||||
|
typval_conv_special = special;
|
||||||
const int initial_size = lua_gettop(lstate);
|
const int initial_size = lua_gettop(lstate);
|
||||||
|
|
||||||
if (!lua_checkstack(lstate, initial_size + 2)) {
|
if (!lua_checkstack(lstate, initial_size + 2)) {
|
||||||
emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
|
emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
|
||||||
return false;
|
return false;
|
||||||
|
@@ -295,6 +295,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
// in_fast_event
|
// in_fast_event
|
||||||
lua_pushcfunction(lstate, &nlua_in_fast_event);
|
lua_pushcfunction(lstate, &nlua_in_fast_event);
|
||||||
lua_setfield(lstate, -2, "in_fast_event");
|
lua_setfield(lstate, -2, "in_fast_event");
|
||||||
|
// call
|
||||||
|
lua_pushcfunction(lstate, &nlua_call);
|
||||||
|
lua_setfield(lstate, -2, "call");
|
||||||
|
|
||||||
// vim.loop
|
// vim.loop
|
||||||
luv_set_loop(lstate, &main_loop.uv);
|
luv_set_loop(lstate, &main_loop.uv);
|
||||||
@@ -539,6 +542,64 @@ int nlua_in_fast_event(lua_State *lstate)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nlua_call(lua_State *lstate)
|
||||||
|
{
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
size_t name_len;
|
||||||
|
const char_u *name = (const char_u *)luaL_checklstring(lstate, 1, &name_len);
|
||||||
|
int nargs = lua_gettop(lstate)-1;
|
||||||
|
if (nargs > MAX_FUNC_ARGS) {
|
||||||
|
return luaL_error(lstate, "Function called with too many arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
typval_T vim_args[MAX_FUNC_ARGS + 1];
|
||||||
|
int i = 0; // also used for freeing the variables
|
||||||
|
for (; i < nargs; i++) {
|
||||||
|
lua_pushvalue(lstate, (int)i+2);
|
||||||
|
if (!nlua_pop_typval(lstate, &vim_args[i])) {
|
||||||
|
api_set_error(&err, kErrorTypeException,
|
||||||
|
"error converting argument %d", i+1);
|
||||||
|
goto free_vim_args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bfredl): this should be simplified in error handling refactor
|
||||||
|
struct msglist **saved_msg_list = msg_list;
|
||||||
|
struct msglist *private_msg_list = NULL;
|
||||||
|
msg_list = &private_msg_list;
|
||||||
|
|
||||||
|
force_abort = false;
|
||||||
|
suppress_errthrow = false;
|
||||||
|
current_exception = NULL;
|
||||||
|
did_emsg = false;
|
||||||
|
|
||||||
|
try_start();
|
||||||
|
typval_T rettv;
|
||||||
|
int dummy;
|
||||||
|
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
|
||||||
|
// (see above) to capture abort-causing non-exception errors.
|
||||||
|
(void)call_func(name, (int)name_len, &rettv, nargs,
|
||||||
|
vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||||
|
&dummy, true, NULL, NULL);
|
||||||
|
if (!try_end(&err)) {
|
||||||
|
nlua_push_typval(lstate, &rettv, false);
|
||||||
|
}
|
||||||
|
tv_clear(&rettv);
|
||||||
|
|
||||||
|
msg_list = saved_msg_list;
|
||||||
|
|
||||||
|
free_vim_args:
|
||||||
|
while (i > 0) {
|
||||||
|
tv_clear(&vim_args[--i]);
|
||||||
|
}
|
||||||
|
if (ERROR_SET(&err)) {
|
||||||
|
lua_pushstring(lstate, err.msg);
|
||||||
|
api_clear_error(&err);
|
||||||
|
return lua_error(lstate);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
/// os.getenv: override os.getenv to maintain coherency. #9681
|
/// os.getenv: override os.getenv to maintain coherency. #9681
|
||||||
///
|
///
|
||||||
@@ -623,7 +684,7 @@ void executor_eval_lua(const String str, typval_T *const arg,
|
|||||||
if (arg->v_type == VAR_UNKNOWN) {
|
if (arg->v_type == VAR_UNKNOWN) {
|
||||||
lua_pushnil(lstate);
|
lua_pushnil(lstate);
|
||||||
} else {
|
} else {
|
||||||
nlua_push_typval(lstate, arg);
|
nlua_push_typval(lstate, arg, true);
|
||||||
}
|
}
|
||||||
if (lua_pcall(lstate, 1, 1, 0)) {
|
if (lua_pcall(lstate, 1, 1, 0)) {
|
||||||
nlua_error(lstate,
|
nlua_error(lstate,
|
||||||
|
@@ -242,6 +242,17 @@ local function __index(t, key)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- vim.fn.{func}(...)
|
||||||
|
local function fn_index(t, key)
|
||||||
|
local function func(...)
|
||||||
|
return vim.call(key, ...)
|
||||||
|
end
|
||||||
|
t[key] = func
|
||||||
|
return func
|
||||||
|
end
|
||||||
|
local fn = setmetatable({}, {__index=fn_index})
|
||||||
|
|
||||||
local module = {
|
local module = {
|
||||||
_update_package_paths = _update_package_paths,
|
_update_package_paths = _update_package_paths,
|
||||||
_os_proc_children = _os_proc_children,
|
_os_proc_children = _os_proc_children,
|
||||||
@@ -249,6 +260,7 @@ local module = {
|
|||||||
_system = _system,
|
_system = _system,
|
||||||
paste = paste,
|
paste = paste,
|
||||||
schedule_wrap = schedule_wrap,
|
schedule_wrap = schedule_wrap,
|
||||||
|
fn=fn,
|
||||||
}
|
}
|
||||||
|
|
||||||
setmetatable(module, {
|
setmetatable(module, {
|
||||||
|
@@ -10,6 +10,7 @@ local feed = helpers.feed
|
|||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
local exec_lua = helpers.exec_lua
|
local exec_lua = helpers.exec_lua
|
||||||
local matches = helpers.matches
|
local matches = helpers.matches
|
||||||
|
local source = helpers.source
|
||||||
|
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
|
|
||||||
@@ -299,4 +300,31 @@ describe('lua stdlib', function()
|
|||||||
eq("Error executing lua: .../shared.lua: Expected string, got number",
|
eq("Error executing lua: .../shared.lua: Expected string, got number",
|
||||||
pcall_err(exec_lua, [[return vim.pesc(2)]]))
|
pcall_err(exec_lua, [[return vim.pesc(2)]]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('vim.call and vim.fn', function()
|
||||||
|
eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]]))
|
||||||
|
eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]]))
|
||||||
|
-- compat: nvim_call_function uses "special" value for vimL float
|
||||||
|
eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]]))
|
||||||
|
|
||||||
|
source([[
|
||||||
|
func! FooFunc(test)
|
||||||
|
let g:test = a:test
|
||||||
|
return {}
|
||||||
|
endfunc
|
||||||
|
func! VarArg(...)
|
||||||
|
return a:000
|
||||||
|
endfunc
|
||||||
|
]])
|
||||||
|
eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]]))
|
||||||
|
eq(3, eval("g:test"))
|
||||||
|
-- compat: nvim_call_function uses "special" value for empty dict
|
||||||
|
eq(true, exec_lua([[return next(vim.api.nvim_call_function("FooFunc", {5})) == true ]]))
|
||||||
|
eq(5, eval("g:test"))
|
||||||
|
|
||||||
|
eq({2, "foo", true}, exec_lua([[return vim.fn.VarArg(2, "foo", true)]]))
|
||||||
|
|
||||||
|
-- error handling
|
||||||
|
eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user