To: vim_dev@googlegroups.com Subject: Patch 8.2.0775 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0775 Problem: Not easy to call a Vim function from Lua. Solution: Add vim.call() and vim.fn(). (Prabir Shrestha, closes #6063) Files: runtime/doc/if_lua.txt, src/if_lua.c, src/testdir/test_lua.vim *** ../vim-8.2.0774/runtime/doc/if_lua.txt 2020-04-14 20:15:45.280566202 +0200 --- runtime/doc/if_lua.txt 2020-05-17 14:27:33.117403137 +0200 *************** *** 199,204 **** --- 199,213 ---- 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* *** ../vim-8.2.0774/src/if_lua.c 2020-03-29 20:51:03.081780739 +0200 --- src/if_lua.c 2020-05-17 14:30:18.680896084 +0200 *************** *** 568,575 **** break; case LUA_TNUMBER: #ifdef FEAT_FLOAT ! tv->v_type = VAR_FLOAT; ! tv->vval.v_float = (float_T) lua_tonumber(L, pos); #else tv->v_type = VAR_NUMBER; tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos); --- 568,588 ---- break; case LUA_TNUMBER: #ifdef FEAT_FLOAT ! { ! 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,1908 **** --- 1916,1967 ---- 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,1921 **** --- 1975,1981 ---- {"window", luaV_window}, {"open", luaV_open}, {"type", luaV_type}, + {"call", luaV_call}, {NULL, NULL} }; *************** *** 1997,2002 **** --- 2057,2073 ---- 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,2057 **** --- 2123,2130 ---- 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; } *** ../vim-8.2.0774/src/testdir/test_lua.vim 2020-05-01 14:10:10.188390740 +0200 --- src/testdir/test_lua.vim 2020-05-17 14:23:22.534165474 +0200 *************** *** 33,39 **** " 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')) " lua.eval with a string lua v = vim.eval('"abc"') --- 33,39 ---- " lua.eval with a number lua v = vim.eval('123') call assert_equal('number', luaeval('vim.type(v)')) ! call assert_equal(123, luaeval('v')) " lua.eval with a string lua v = vim.eval('"abc"') *************** *** 121,126 **** --- 121,138 ---- 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,258 **** func Test_lua_buffer_number_lines() new call setline(1, ['a', 'b', 'c']) ! call assert_equal(3.0, luaeval('#vim.buffer()')) bwipe! endfunc --- 264,270 ---- func Test_lua_buffer_number_lines() new call setline(1, ['a', 'b', 'c']) ! call assert_equal(3, luaeval('#vim.buffer()')) bwipe! endfunc *************** *** 311,325 **** 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_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) lockvar 1 l call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is locked') --- 323,337 ---- 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, '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, '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,370 **** 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.0, luaeval('l[2][0]')) ! call assert_equal(2.0, 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.0, 2.0, [...]]', string(luaeval('l'))) call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)')) call assert_equal(luaeval('tostring(l)'), luaeval('tostring(l[2])')) --- 367,382 ---- lua l = vim.list():add(1):add(2) lua l = l:add(l) ! call assert_equal(1, luaeval('l[0]')) ! call assert_equal(2, luaeval('l[1]')) ! call assert_equal(1, luaeval('l[2][0]')) ! call assert_equal(2, luaeval('l[2][1]')) ! call assert_equal(1, luaeval('l[2][2][0]')) ! call assert_equal(2, luaeval('l[2][2][1]')) ! 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,400 **** 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_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) lockvar 1 d call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked') --- 398,412 ---- 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, '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, '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,456 **** lua b = vim.blob("\x00\x00\x00\x00") call assert_equal(0z00000000, luaeval('b')) ! call assert_equal(4.0, 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_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_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') --- 453,468 ---- lua b = vim.blob("\x00\x00\x00\x00") call assert_equal(0z00000000, 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, 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, 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,563 **** " Test :luafile foo.lua func Test_luafile() call delete('Xlua_file') ! call writefile(["str = 'hello'", "num = 123.0" ], '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')) lua str, num = nil call delete('Xlua_file') --- 564,575 ---- " Test :luafile foo.lua func Test_luafile() call delete('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, luaeval('num')) lua str, num = nil call delete('Xlua_file') *** ../vim-8.2.0774/src/version.c 2020-05-17 14:06:07.317201551 +0200 --- src/version.c 2020-05-17 14:31:52.068609222 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 775, /**/ -- hundred-and-one symptoms of being an internet addict: 115. You are late picking up your kid from school and try to explain to the teacher you were stuck in Web traffic. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///