Skip to content

Commit eb04f08

Browse files
committed
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)
1 parent 26e8644 commit eb04f08

File tree

4 files changed

+118
-22
lines changed

4 files changed

+118
-22
lines changed

runtime/doc/if_lua.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@ Vim evaluation and command execution, and others.
199199
returns it. Note that the buffer is not set as
200200
current.
201201

202+
vim.call({name} [,{args}])
203+
Proxy to call Vim function named {name} with
204+
arguments {args}. Example: >
205+
:lua print(vim.call('has', 'timers'))
206+
<
207+
vim.fn Proxy to call Vim functions. Proxy methods are
208+
created on demand. Example: >
209+
:lua print(vim.fn.has('timers'))
210+
<
202211

203212
==============================================================================
204213
3. List userdata *lua-list*

src/if_lua.c

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,21 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
568568
break;
569569
case LUA_TNUMBER:
570570
#ifdef FEAT_FLOAT
571-
tv->v_type = VAR_FLOAT;
572-
tv->vval.v_float = (float_T) lua_tonumber(L, pos);
571+
{
572+
const lua_Number n = lua_tonumber(L, pos);
573+
574+
if (n > (lua_Number)INT64_MAX || n < (lua_Number)INT64_MIN
575+
|| ((lua_Number)((varnumber_T)n)) != n)
576+
{
577+
tv->v_type = VAR_FLOAT;
578+
tv->vval.v_float = (float_T)n;
579+
}
580+
else
581+
{
582+
tv->v_type = VAR_NUMBER;
583+
tv->vval.v_number = (varnumber_T)n;
584+
}
585+
}
573586
#else
574587
tv->v_type = VAR_NUMBER;
575588
tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
@@ -1903,6 +1916,52 @@ luaV_type(lua_State *L)
19031916
return 1;
19041917
}
19051918

1919+
static int
1920+
luaV_call(lua_State *L)
1921+
{
1922+
int argc = lua_gettop(L) - 1;
1923+
size_t funcname_len;
1924+
char_u *funcname;
1925+
char *error = NULL;
1926+
typval_T rettv;
1927+
typval_T argv[MAX_FUNC_ARGS + 1];
1928+
int i = 0;
1929+
1930+
if (argc > MAX_FUNC_ARGS)
1931+
return luaL_error(L, "Function called with too many arguments");
1932+
1933+
funcname = (char_u *)luaL_checklstring(L, 1, &funcname_len);
1934+
1935+
for (; i < argc; i++)
1936+
{
1937+
if (luaV_totypval(L, i + 2, &argv[i]) == FAIL)
1938+
{
1939+
error = "lua: cannot convert value";
1940+
goto free_vim_args;
1941+
}
1942+
}
1943+
1944+
argv[argc].v_type = VAR_UNKNOWN;
1945+
1946+
if (call_vim_function(funcname, argc, argv, &rettv) == FAIL)
1947+
{
1948+
error = "lua: call_vim_function failed";
1949+
goto free_vim_args;
1950+
}
1951+
1952+
luaV_pushtypval(L, &rettv);
1953+
clear_tv(&rettv);
1954+
1955+
free_vim_args:
1956+
while (i > 0)
1957+
clear_tv(&argv[--i]);
1958+
1959+
if (error == NULL)
1960+
return 1;
1961+
else
1962+
return luaL_error(L, error);
1963+
}
1964+
19061965
static const luaL_Reg luaV_module[] = {
19071966
{"command", luaV_command},
19081967
{"eval", luaV_eval},
@@ -1916,6 +1975,7 @@ static const luaL_Reg luaV_module[] = {
19161975
{"window", luaV_window},
19171976
{"open", luaV_open},
19181977
{"type", luaV_type},
1978+
{"call", luaV_call},
19191979
{NULL, NULL}
19201980
};
19211981

@@ -1997,6 +2057,17 @@ luaV_setref(lua_State *L)
19972057
return 1;
19982058
}
19992059

2060+
#define LUA_VIM_FN_CODE \
2061+
"vim.fn = setmetatable({}, {"\
2062+
" __index = function (t, key)"\
2063+
" local function _fn(...)"\
2064+
" return vim.call(key, ...)"\
2065+
" end"\
2066+
" t[key] = _fn"\
2067+
" return _fn"\
2068+
" end"\
2069+
"})"
2070+
20002071
static int
20012072
luaopen_vim(lua_State *L)
20022073
{
@@ -2052,6 +2123,8 @@ luaopen_vim(lua_State *L)
20522123
lua_pushvalue(L, 1); // cache table
20532124
luaV_openlib(L, luaV_module, 1);
20542125
lua_setglobal(L, LUAVIM_NAME);
2126+
// custom code
2127+
luaL_dostring(L, LUA_VIM_FN_CODE);
20552128
return 0;
20562129
}
20572130

src/testdir/test_lua.vim

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func Test_lua_eval()
3333
" lua.eval with a number
3434
lua v = vim.eval('123')
3535
call assert_equal('number', luaeval('vim.type(v)'))
36-
call assert_equal(123.0, luaeval('v'))
36+
call assert_equal(123, luaeval('v'))
3737

3838
" lua.eval with a string
3939
lua v = vim.eval('"abc"')
@@ -121,6 +121,18 @@ func Test_lua_window_line_col()
121121
bwipe!
122122
endfunc
123123

124+
" Test vim.call
125+
func Test_lua_call()
126+
call assert_equal(has('lua'), luaeval('vim.call("has", "lua")'))
127+
call assert_equal(printf("Hello %s", "vim"), luaeval('vim.call("printf", "Hello %s", "vim")'))
128+
endfunc
129+
130+
" Test vim.fn.*
131+
func Test_lua_fn()
132+
call assert_equal(has('lua'), luaeval('vim.fn.has("lua")'))
133+
call assert_equal(printf("Hello %s", "vim"), luaeval('vim.fn.printf("Hello %s", "vim")'))
134+
endfunc
135+
124136
" Test setting the current window
125137
func Test_lua_window_set_current()
126138
new Xfoo1
@@ -252,7 +264,7 @@ endfunc
252264
func Test_lua_buffer_number_lines()
253265
new
254266
call setline(1, ['a', 'b', 'c'])
255-
call assert_equal(3.0, luaeval('#vim.buffer()'))
267+
call assert_equal(3, luaeval('#vim.buffer()'))
256268
bwipe!
257269
endfunc
258270

@@ -311,15 +323,15 @@ func Test_lua_list()
311323
lua l:add(nil)
312324
lua l:add(vim.eval("[1, 2, 3]"))
313325
lua l:add(vim.eval("{'a':1, 'b':2, 'c':3}"))
314-
call assert_equal([123.0, 'abc', v:true, v:false, v:null, [1, 2, 3], {'a': 1, 'b': 2, 'c': 3}], l)
315-
call assert_equal(7.0, luaeval('#l'))
326+
call assert_equal([123, 'abc', v:true, v:false, v:null, [1, 2, 3], {'a': 1, 'b': 2, 'c': 3}], l)
327+
call assert_equal(7, luaeval('#l'))
316328
call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)'))
317329

318330
lua l[0] = 124
319331
lua l[5] = nil
320332
lua l:insert('first')
321333
lua l:insert('xx', 3)
322-
call assert_equal(['first', 124.0, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l)
334+
call assert_equal(['first', 124, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l)
323335

324336
lockvar 1 l
325337
call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is locked')
@@ -355,16 +367,16 @@ func Test_lua_recursive_list()
355367
lua l = vim.list():add(1):add(2)
356368
lua l = l:add(l)
357369

358-
call assert_equal(1.0, luaeval('l[0]'))
359-
call assert_equal(2.0, luaeval('l[1]'))
370+
call assert_equal(1, luaeval('l[0]'))
371+
call assert_equal(2, luaeval('l[1]'))
360372

361-
call assert_equal(1.0, luaeval('l[2][0]'))
362-
call assert_equal(2.0, luaeval('l[2][1]'))
373+
call assert_equal(1, luaeval('l[2][0]'))
374+
call assert_equal(2, luaeval('l[2][1]'))
363375

364-
call assert_equal(1.0, luaeval('l[2][2][0]'))
365-
call assert_equal(2.0, luaeval('l[2][2][1]'))
376+
call assert_equal(1, luaeval('l[2][2][0]'))
377+
call assert_equal(2, luaeval('l[2][2][1]'))
366378

367-
call assert_equal('[1.0, 2.0, [...]]', string(luaeval('l')))
379+
call assert_equal('[1, 2, [...]]', string(luaeval('l')))
368380

369381
call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)'))
370382
call assert_equal(luaeval('tostring(l)'), luaeval('tostring(l[2])'))
@@ -386,15 +398,15 @@ func Test_lua_dict()
386398
lua d[3] = false
387399
lua d[4] = vim.eval("[1, 2, 3]")
388400
lua d[5] = vim.eval("{'a':1, 'b':2, 'c':3}")
389-
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)
390-
call assert_equal(6.0, luaeval('#d'))
401+
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)
402+
call assert_equal(6, luaeval('#d'))
391403
call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)'))
392404

393405
call assert_equal('abc', luaeval('d[1]'))
394406

395407
lua d[0] = 124
396408
lua d[4] = nil
397-
call assert_equal({'0':124.0, '1':'abc', '2':v:true, '3':v:false, '5': {'a':1, 'b':2, 'c':3}}, d)
409+
call assert_equal({'0':124, '1':'abc', '2':v:true, '3':v:false, '5': {'a':1, 'b':2, 'c':3}}, d)
398410

399411
lockvar 1 d
400412
call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked')
@@ -441,16 +453,16 @@ func Test_lua_blob()
441453

442454
lua b = vim.blob("\x00\x00\x00\x00")
443455
call assert_equal(0z00000000, luaeval('b'))
444-
call assert_equal(4.0, luaeval('#b'))
456+
call assert_equal(4, luaeval('#b'))
445457
lua b[0], b[1], b[2], b[3] = 1, 32, 256, 0xff
446458
call assert_equal(0z012000ff, luaeval('b'))
447459
lua b[4] = string.byte("z", 1)
448460
call assert_equal(0z012000ff.7a, luaeval('b'))
449-
call assert_equal(5.0, luaeval('#b'))
461+
call assert_equal(5, luaeval('#b'))
450462
call assert_fails('lua b[#b+1] = 0x80', '[string "vim chunk"]:1: index out of range')
451463
lua b:add("12ab")
452464
call assert_equal(0z012000ff.7a313261.62, luaeval('b'))
453-
call assert_equal(9.0, luaeval('#b'))
465+
call assert_equal(9, luaeval('#b'))
454466
call assert_fails('lua b:add(nil)', '[string "vim chunk"]:1: string expected, got nil')
455467
call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string expected, got boolean')
456468
call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table')
@@ -552,12 +564,12 @@ endfunc
552564
" Test :luafile foo.lua
553565
func Test_luafile()
554566
call delete('Xlua_file')
555-
call writefile(["str = 'hello'", "num = 123.0" ], 'Xlua_file')
567+
call writefile(["str = 'hello'", "num = 123" ], 'Xlua_file')
556568
call setfperm('Xlua_file', 'r-xr-xr-x')
557569

558570
luafile Xlua_file
559571
call assert_equal('hello', luaeval('str'))
560-
call assert_equal(123.0, luaeval('num'))
572+
call assert_equal(123, luaeval('num'))
561573

562574
lua str, num = nil
563575
call delete('Xlua_file')

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,8 @@ static char *(features[]) =
746746

747747
static int included_patches[] =
748748
{ /* Add new patch number below this line */
749+
/**/
750+
775,
749751
/**/
750752
774,
751753
/**/

0 commit comments

Comments
 (0)