]> granicus.if.org Git - vim/commitdiff
patch 8.2.0775: not easy to call a Vim function from Lua v8.2.0775
authorBram Moolenaar <Bram@vim.org>
Sun, 17 May 2020 12:32:35 +0000 (14:32 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 17 May 2020 12:32:35 +0000 (14:32 +0200)
Problem:    Not easy to call a Vim function from Lua.
Solution:   Add vim.call() and vim.fn(). (Prabir Shrestha, closes #6063)

runtime/doc/if_lua.txt
src/if_lua.c
src/testdir/test_lua.vim
src/version.c

index 75bb963a9f2a7a3de60e6b52f1fef65131135e92..ff108eebb63b095c1084c473169e5dbe2db8b8d3 100644 (file)
@@ -199,6 +199,15 @@ Vim evaluation and command execution, and others.
                                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*
index 19842912fbbaff4a445c951532301223c3627b24..d4cc21f3d0f5c3f803a345c81ecbdf04a37dee99 100644 (file)
@@ -568,8 +568,21 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
            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;
 }
 
index 25300b67eed1e3c9e2d0982f96d2f96acb30a0d3..f92208c209657b8b91128f47b0ee0448d03b65a8 100644 (file)
@@ -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')
index 951137912b9d7017ab9acd00f4d4fdd2c957db50..cc092c29fb70b25a5ebb272cb6df592d4b6dd552 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    775,
 /**/
     774,
 /**/