# HG changeset patch # User Bram Moolenaar # Date 1589719504 -7200 # Node ID 86dde5c4b37568f7d107f3c4477d0220f38935cf # Parent 74db00344bf60b361b7ccdb78050d0a1594aa20f patch 8.2.0775: not easy to call a Vim function from Lua Commit: https://github.com/vim/vim/commit/eb04f0893afe01faff272ef84c70d8cc16d8e80a Author: Bram Moolenaar Date: Sun May 17 14:32:35 2020 +0200 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 https://github.com/vim/vim/issues/6063) diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -199,6 +199,15 @@ Vim evaluation and command execution, an returns it. Note that the buffer is not set as 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* diff --git a/src/if_lua.c b/src/if_lua.c --- a/src/if_lua.c +++ b/src/if_lua.c @@ -568,8 +568,21 @@ luaV_totypval(lua_State *L, int pos, typ break; case LUA_TNUMBER: #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 tv->v_type = VAR_NUMBER; tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos); @@ -1903,6 +1916,52 @@ luaV_type(lua_State *L) 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[] = { {"command", luaV_command}, {"eval", luaV_eval}, @@ -1916,6 +1975,7 @@ static const luaL_Reg luaV_module[] = { {"window", luaV_window}, {"open", luaV_open}, {"type", luaV_type}, + {"call", luaV_call}, {NULL, NULL} }; @@ -1997,6 +2057,17 @@ luaV_setref(lua_State *L) 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 luaopen_vim(lua_State *L) { @@ -2052,6 +2123,8 @@ luaopen_vim(lua_State *L) lua_pushvalue(L, 1); // cache table luaV_openlib(L, luaV_module, 1); lua_setglobal(L, LUAVIM_NAME); + // custom code + luaL_dostring(L, LUA_VIM_FN_CODE); return 0; } diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -33,7 +33,7 @@ func Test_lua_eval() " lua.eval with a number lua v = vim.eval('123') 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 v = vim.eval('"abc"') @@ -121,6 +121,18 @@ func Test_lua_window_line_col() bwipe! 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 func Test_lua_window_set_current() new Xfoo1 @@ -252,7 +264,7 @@ endfunc func Test_lua_buffer_number_lines() new call setline(1, ['a', 'b', 'c']) - call assert_equal(3.0, luaeval('#vim.buffer()')) + call assert_equal(3, luaeval('#vim.buffer()')) bwipe! endfunc @@ -311,15 +323,15 @@ func Test_lua_list() lua l:add(nil) lua l:add(vim.eval("[1, 2, 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(7.0, luaeval('#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, luaeval('#l')) call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)')) lua l[0] = 124 lua l[5] = nil lua l:insert('first') 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 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 = l:add(l) - call assert_equal(1.0, luaeval('l[0]')) - call assert_equal(2.0, luaeval('l[1]')) + call assert_equal(1, luaeval('l[0]')) + call assert_equal(2, luaeval('l[1]')) - call assert_equal(1.0, luaeval('l[2][0]')) - call assert_equal(2.0, luaeval('l[2][1]')) + call assert_equal(1, luaeval('l[2][0]')) + call assert_equal(2, luaeval('l[2][1]')) - call assert_equal(1.0, luaeval('l[2][2][0]')) - call assert_equal(2.0, luaeval('l[2][2][1]')) + call assert_equal(1, luaeval('l[2][2][0]')) + 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_equal(luaeval('tostring(l)'), luaeval('tostring(l[2])')) @@ -386,15 +398,15 @@ func Test_lua_dict() lua d[3] = false lua d[4] = vim.eval("[1, 2, 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(6.0, luaeval('#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, luaeval('#d')) call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)')) call assert_equal('abc', luaeval('d[1]')) lua d[0] = 124 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 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") 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 call assert_equal(0z012000ff, luaeval('b')) lua b[4] = string.byte("z", 1) 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') lua b:add("12ab") 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(true)', '[string "vim chunk"]:1: string expected, got boolean') call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table') @@ -552,12 +564,12 @@ endfunc " Test :luafile foo.lua func Test_luafile() 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') luafile Xlua_file call assert_equal('hello', luaeval('str')) - call assert_equal(123.0, luaeval('num')) + call assert_equal(123, luaeval('num')) lua str, num = nil call delete('Xlua_file') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 775, +/**/ 774, /**/ 773,