mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.0775: not easy to call a Vim function from Lua
Problem: Not easy to call a Vim function from Lua. Solution: Add vim.call() and vim.fn(). (Prabir Shrestha, closes #6063)
This commit is contained in:
parent
26e86445bf
commit
eb04f0893a
@ -199,6 +199,15 @@ Vim evaluation and command execution, and others.
|
|||||||
returns it. Note that the buffer is not set as
|
returns it. Note that the buffer is not set as
|
||||||
current.
|
current.
|
||||||
|
|
||||||
|
vim.call({name} [,{args}])
|
||||||
|
Proxy to call Vim function named {name} with
|
||||||
|
arguments {args}. Example: >
|
||||||
|
:lua print(vim.call('has', 'timers'))
|
||||||
|
<
|
||||||
|
vim.fn Proxy to call Vim functions. Proxy methods are
|
||||||
|
created on demand. Example: >
|
||||||
|
:lua print(vim.fn.has('timers'))
|
||||||
|
<
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
3. List userdata *lua-list*
|
3. List userdata *lua-list*
|
||||||
|
77
src/if_lua.c
77
src/if_lua.c
@ -568,8 +568,21 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
|
|||||||
break;
|
break;
|
||||||
case LUA_TNUMBER:
|
case LUA_TNUMBER:
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
tv->v_type = VAR_FLOAT;
|
{
|
||||||
tv->vval.v_float = (float_T) lua_tonumber(L, pos);
|
const lua_Number n = lua_tonumber(L, pos);
|
||||||
|
|
||||||
|
if (n > (lua_Number)INT64_MAX || n < (lua_Number)INT64_MIN
|
||||||
|
|| ((lua_Number)((varnumber_T)n)) != n)
|
||||||
|
{
|
||||||
|
tv->v_type = VAR_FLOAT;
|
||||||
|
tv->vval.v_float = (float_T)n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tv->v_type = VAR_NUMBER;
|
||||||
|
tv->vval.v_number = (varnumber_T)n;
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
tv->v_type = VAR_NUMBER;
|
tv->v_type = VAR_NUMBER;
|
||||||
tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
|
tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
|
||||||
@ -1903,6 +1916,52 @@ luaV_type(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
luaV_call(lua_State *L)
|
||||||
|
{
|
||||||
|
int argc = lua_gettop(L) - 1;
|
||||||
|
size_t funcname_len;
|
||||||
|
char_u *funcname;
|
||||||
|
char *error = NULL;
|
||||||
|
typval_T rettv;
|
||||||
|
typval_T argv[MAX_FUNC_ARGS + 1];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (argc > MAX_FUNC_ARGS)
|
||||||
|
return luaL_error(L, "Function called with too many arguments");
|
||||||
|
|
||||||
|
funcname = (char_u *)luaL_checklstring(L, 1, &funcname_len);
|
||||||
|
|
||||||
|
for (; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (luaV_totypval(L, i + 2, &argv[i]) == FAIL)
|
||||||
|
{
|
||||||
|
error = "lua: cannot convert value";
|
||||||
|
goto free_vim_args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argv[argc].v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
|
if (call_vim_function(funcname, argc, argv, &rettv) == FAIL)
|
||||||
|
{
|
||||||
|
error = "lua: call_vim_function failed";
|
||||||
|
goto free_vim_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
luaV_pushtypval(L, &rettv);
|
||||||
|
clear_tv(&rettv);
|
||||||
|
|
||||||
|
free_vim_args:
|
||||||
|
while (i > 0)
|
||||||
|
clear_tv(&argv[--i]);
|
||||||
|
|
||||||
|
if (error == NULL)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return luaL_error(L, error);
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg luaV_module[] = {
|
static const luaL_Reg luaV_module[] = {
|
||||||
{"command", luaV_command},
|
{"command", luaV_command},
|
||||||
{"eval", luaV_eval},
|
{"eval", luaV_eval},
|
||||||
@ -1916,6 +1975,7 @@ static const luaL_Reg luaV_module[] = {
|
|||||||
{"window", luaV_window},
|
{"window", luaV_window},
|
||||||
{"open", luaV_open},
|
{"open", luaV_open},
|
||||||
{"type", luaV_type},
|
{"type", luaV_type},
|
||||||
|
{"call", luaV_call},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1997,6 +2057,17 @@ luaV_setref(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define LUA_VIM_FN_CODE \
|
||||||
|
"vim.fn = setmetatable({}, {"\
|
||||||
|
" __index = function (t, key)"\
|
||||||
|
" local function _fn(...)"\
|
||||||
|
" return vim.call(key, ...)"\
|
||||||
|
" end"\
|
||||||
|
" t[key] = _fn"\
|
||||||
|
" return _fn"\
|
||||||
|
" end"\
|
||||||
|
"})"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
luaopen_vim(lua_State *L)
|
luaopen_vim(lua_State *L)
|
||||||
{
|
{
|
||||||
@ -2052,6 +2123,8 @@ luaopen_vim(lua_State *L)
|
|||||||
lua_pushvalue(L, 1); // cache table
|
lua_pushvalue(L, 1); // cache table
|
||||||
luaV_openlib(L, luaV_module, 1);
|
luaV_openlib(L, luaV_module, 1);
|
||||||
lua_setglobal(L, LUAVIM_NAME);
|
lua_setglobal(L, LUAVIM_NAME);
|
||||||
|
// custom code
|
||||||
|
luaL_dostring(L, LUA_VIM_FN_CODE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ func Test_lua_eval()
|
|||||||
" lua.eval with a number
|
" lua.eval with a number
|
||||||
lua v = vim.eval('123')
|
lua v = vim.eval('123')
|
||||||
call assert_equal('number', luaeval('vim.type(v)'))
|
call assert_equal('number', luaeval('vim.type(v)'))
|
||||||
call assert_equal(123.0, luaeval('v'))
|
call assert_equal(123, luaeval('v'))
|
||||||
|
|
||||||
" lua.eval with a string
|
" lua.eval with a string
|
||||||
lua v = vim.eval('"abc"')
|
lua v = vim.eval('"abc"')
|
||||||
@ -121,6 +121,18 @@ func Test_lua_window_line_col()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test vim.call
|
||||||
|
func Test_lua_call()
|
||||||
|
call assert_equal(has('lua'), luaeval('vim.call("has", "lua")'))
|
||||||
|
call assert_equal(printf("Hello %s", "vim"), luaeval('vim.call("printf", "Hello %s", "vim")'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Test vim.fn.*
|
||||||
|
func Test_lua_fn()
|
||||||
|
call assert_equal(has('lua'), luaeval('vim.fn.has("lua")'))
|
||||||
|
call assert_equal(printf("Hello %s", "vim"), luaeval('vim.fn.printf("Hello %s", "vim")'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Test setting the current window
|
" Test setting the current window
|
||||||
func Test_lua_window_set_current()
|
func Test_lua_window_set_current()
|
||||||
new Xfoo1
|
new Xfoo1
|
||||||
@ -252,7 +264,7 @@ endfunc
|
|||||||
func Test_lua_buffer_number_lines()
|
func Test_lua_buffer_number_lines()
|
||||||
new
|
new
|
||||||
call setline(1, ['a', 'b', 'c'])
|
call setline(1, ['a', 'b', 'c'])
|
||||||
call assert_equal(3.0, luaeval('#vim.buffer()'))
|
call assert_equal(3, luaeval('#vim.buffer()'))
|
||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
@ -311,15 +323,15 @@ func Test_lua_list()
|
|||||||
lua l:add(nil)
|
lua l:add(nil)
|
||||||
lua l:add(vim.eval("[1, 2, 3]"))
|
lua l:add(vim.eval("[1, 2, 3]"))
|
||||||
lua l:add(vim.eval("{'a':1, 'b':2, 'c':3}"))
|
lua l:add(vim.eval("{'a':1, 'b':2, 'c':3}"))
|
||||||
call assert_equal([123.0, 'abc', v:true, v:false, v:null, [1, 2, 3], {'a': 1, 'b': 2, 'c': 3}], l)
|
call assert_equal([123, 'abc', v:true, v:false, v:null, [1, 2, 3], {'a': 1, 'b': 2, 'c': 3}], l)
|
||||||
call assert_equal(7.0, luaeval('#l'))
|
call assert_equal(7, luaeval('#l'))
|
||||||
call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)'))
|
call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)'))
|
||||||
|
|
||||||
lua l[0] = 124
|
lua l[0] = 124
|
||||||
lua l[5] = nil
|
lua l[5] = nil
|
||||||
lua l:insert('first')
|
lua l:insert('first')
|
||||||
lua l:insert('xx', 3)
|
lua l:insert('xx', 3)
|
||||||
call assert_equal(['first', 124.0, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l)
|
call assert_equal(['first', 124, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l)
|
||||||
|
|
||||||
lockvar 1 l
|
lockvar 1 l
|
||||||
call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is locked')
|
call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is locked')
|
||||||
@ -355,16 +367,16 @@ func Test_lua_recursive_list()
|
|||||||
lua l = vim.list():add(1):add(2)
|
lua l = vim.list():add(1):add(2)
|
||||||
lua l = l:add(l)
|
lua l = l:add(l)
|
||||||
|
|
||||||
call assert_equal(1.0, luaeval('l[0]'))
|
call assert_equal(1, luaeval('l[0]'))
|
||||||
call assert_equal(2.0, luaeval('l[1]'))
|
call assert_equal(2, luaeval('l[1]'))
|
||||||
|
|
||||||
call assert_equal(1.0, luaeval('l[2][0]'))
|
call assert_equal(1, luaeval('l[2][0]'))
|
||||||
call assert_equal(2.0, luaeval('l[2][1]'))
|
call assert_equal(2, luaeval('l[2][1]'))
|
||||||
|
|
||||||
call assert_equal(1.0, luaeval('l[2][2][0]'))
|
call assert_equal(1, luaeval('l[2][2][0]'))
|
||||||
call assert_equal(2.0, luaeval('l[2][2][1]'))
|
call assert_equal(2, luaeval('l[2][2][1]'))
|
||||||
|
|
||||||
call assert_equal('[1.0, 2.0, [...]]', string(luaeval('l')))
|
call assert_equal('[1, 2, [...]]', string(luaeval('l')))
|
||||||
|
|
||||||
call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)'))
|
call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)'))
|
||||||
call assert_equal(luaeval('tostring(l)'), luaeval('tostring(l[2])'))
|
call assert_equal(luaeval('tostring(l)'), luaeval('tostring(l[2])'))
|
||||||
@ -386,15 +398,15 @@ func Test_lua_dict()
|
|||||||
lua d[3] = false
|
lua d[3] = false
|
||||||
lua d[4] = vim.eval("[1, 2, 3]")
|
lua d[4] = vim.eval("[1, 2, 3]")
|
||||||
lua d[5] = vim.eval("{'a':1, 'b':2, 'c':3}")
|
lua d[5] = vim.eval("{'a':1, 'b':2, 'c':3}")
|
||||||
call assert_equal({'0':123.0, '1':'abc', '2':v:true, '3':v:false, '4': [1, 2, 3], '5': {'a':1, 'b':2, 'c':3}}, d)
|
call assert_equal({'0':123, '1':'abc', '2':v:true, '3':v:false, '4': [1, 2, 3], '5': {'a':1, 'b':2, 'c':3}}, d)
|
||||||
call assert_equal(6.0, luaeval('#d'))
|
call assert_equal(6, luaeval('#d'))
|
||||||
call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)'))
|
call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)'))
|
||||||
|
|
||||||
call assert_equal('abc', luaeval('d[1]'))
|
call assert_equal('abc', luaeval('d[1]'))
|
||||||
|
|
||||||
lua d[0] = 124
|
lua d[0] = 124
|
||||||
lua d[4] = nil
|
lua d[4] = nil
|
||||||
call assert_equal({'0':124.0, '1':'abc', '2':v:true, '3':v:false, '5': {'a':1, 'b':2, 'c':3}}, d)
|
call assert_equal({'0':124, '1':'abc', '2':v:true, '3':v:false, '5': {'a':1, 'b':2, 'c':3}}, d)
|
||||||
|
|
||||||
lockvar 1 d
|
lockvar 1 d
|
||||||
call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked')
|
call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked')
|
||||||
@ -441,16 +453,16 @@ func Test_lua_blob()
|
|||||||
|
|
||||||
lua b = vim.blob("\x00\x00\x00\x00")
|
lua b = vim.blob("\x00\x00\x00\x00")
|
||||||
call assert_equal(0z00000000, luaeval('b'))
|
call assert_equal(0z00000000, luaeval('b'))
|
||||||
call assert_equal(4.0, luaeval('#b'))
|
call assert_equal(4, luaeval('#b'))
|
||||||
lua b[0], b[1], b[2], b[3] = 1, 32, 256, 0xff
|
lua b[0], b[1], b[2], b[3] = 1, 32, 256, 0xff
|
||||||
call assert_equal(0z012000ff, luaeval('b'))
|
call assert_equal(0z012000ff, luaeval('b'))
|
||||||
lua b[4] = string.byte("z", 1)
|
lua b[4] = string.byte("z", 1)
|
||||||
call assert_equal(0z012000ff.7a, luaeval('b'))
|
call assert_equal(0z012000ff.7a, luaeval('b'))
|
||||||
call assert_equal(5.0, luaeval('#b'))
|
call assert_equal(5, luaeval('#b'))
|
||||||
call assert_fails('lua b[#b+1] = 0x80', '[string "vim chunk"]:1: index out of range')
|
call assert_fails('lua b[#b+1] = 0x80', '[string "vim chunk"]:1: index out of range')
|
||||||
lua b:add("12ab")
|
lua b:add("12ab")
|
||||||
call assert_equal(0z012000ff.7a313261.62, luaeval('b'))
|
call assert_equal(0z012000ff.7a313261.62, luaeval('b'))
|
||||||
call assert_equal(9.0, luaeval('#b'))
|
call assert_equal(9, luaeval('#b'))
|
||||||
call assert_fails('lua b:add(nil)', '[string "vim chunk"]:1: string expected, got nil')
|
call assert_fails('lua b:add(nil)', '[string "vim chunk"]:1: string expected, got nil')
|
||||||
call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string expected, got boolean')
|
call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string expected, got boolean')
|
||||||
call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table')
|
call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table')
|
||||||
@ -552,12 +564,12 @@ endfunc
|
|||||||
" Test :luafile foo.lua
|
" Test :luafile foo.lua
|
||||||
func Test_luafile()
|
func Test_luafile()
|
||||||
call delete('Xlua_file')
|
call delete('Xlua_file')
|
||||||
call writefile(["str = 'hello'", "num = 123.0" ], 'Xlua_file')
|
call writefile(["str = 'hello'", "num = 123" ], 'Xlua_file')
|
||||||
call setfperm('Xlua_file', 'r-xr-xr-x')
|
call setfperm('Xlua_file', 'r-xr-xr-x')
|
||||||
|
|
||||||
luafile Xlua_file
|
luafile Xlua_file
|
||||||
call assert_equal('hello', luaeval('str'))
|
call assert_equal('hello', luaeval('str'))
|
||||||
call assert_equal(123.0, luaeval('num'))
|
call assert_equal(123, luaeval('num'))
|
||||||
|
|
||||||
lua str, num = nil
|
lua str, num = nil
|
||||||
call delete('Xlua_file')
|
call delete('Xlua_file')
|
||||||
|
@ -746,6 +746,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
775,
|
||||||
/**/
|
/**/
|
||||||
774,
|
774,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user