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 -- b...@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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202005171233.04HCX27X008478%40masaka.moolenaar.net.

Raspunde prin e-mail lui