# HG changeset patch # User Bram Moolenaar # Date 1628279105 -7200 # Node ID d5e9c05b4811c87ab0676640def1ba999aefbbbb # Parent f9ebb5b05597da471c517f1f910c6975b30c55f0 patch 8.2.3300: Lua: can only execute on Vim command at a time Commit: https://github.com/vim/vim/commit/11328bc7df0ecc47f4025a10bb86882a659e9994 Author: Yegappan Lakshmanan Date: Fri Aug 6 21:34:38 2021 +0200 patch 8.2.3300: Lua: can only execute on Vim command at a time Problem: Lua: can only execute on Vim command at a time. Not easy to get the Vim version. Solution: Make vim.command() accept multiple lines. Add vim.version(). (Yegappan Lakshmanan, closes #8716) 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 @@ -175,10 +175,17 @@ Vim evaluation and command execution, an :lua print(type(l), vim.type(l)) :" list < - vim.command({cmd}) Executes the vim (ex-mode) command {cmd}. + vim.command({cmds}) Executes one or more lines of Ex-mode commands + in {cmds}. Examples: > :lua vim.command"set tw=60" :lua vim.command"normal ddp" + lua << trim END + vim.command([[ + new Myfile.js + call search('start') + ]]) + END < vim.eval({expr}) Evaluates expression {expr} (see |expression|), converts the result to Lua, and returns it. @@ -211,6 +218,12 @@ Vim evaluation and command execution, an vim.lua_version The Lua version Vim was compiled with, in the form {major}.{minor}.{patch}, e.g. "5.1.4". + vim.version() Returns a Lua table with the Vim version. + The table will have the following keys: + major - major Vim version. + minor - minor Vim version. + patch - latest patch included. + *lua-vim-variables* The Vim editor global dictionaries |g:| |w:| |b:| |t:| |v:| can be accessed from Lua conveniently and idiomatically by referencing the `vim.*` Lua tables diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -3365,6 +3365,48 @@ execute_redir_str(char_u *value, int val } /* + * Get next line from a string containing NL separated lines. + * Called by do_cmdline() to get the next line. + * Returns an allocated string, or NULL when at the end of the string. + */ + static char_u * +get_str_line( + int c UNUSED, + void *cookie, + int indent UNUSED, + getline_opt_T options UNUSED) +{ + char_u *start = *(char_u **)cookie; + char_u *line; + char_u *p; + + p = start; + if (p == NULL || *p == NUL) + return NULL; + p = vim_strchr(p, '\n'); + if (p == NULL) + line = vim_strsave(start); + else + { + line = vim_strnsave(start, p - start); + p++; + } + + *(char_u **)cookie = p; + return line; +} + +/* + * Execute a series of Ex commands in 'str' + */ + void +execute_cmds_from_string(char_u *str) +{ + do_cmdline(NULL, get_str_line, (void *)&str, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); +} + +/* * Get next line from a list. * Called by do_cmdline() to get the next line. * Returns allocated string, or NULL for end of function. diff --git a/src/if_lua.c b/src/if_lua.c --- a/src/if_lua.c +++ b/src/if_lua.c @@ -10,6 +10,7 @@ */ #include "vim.h" +#include "version.h" #include #include @@ -191,6 +192,7 @@ static void luaV_call_lua_func_free(void #define lua_rawget dll_lua_rawget #define lua_rawgeti dll_lua_rawgeti #define lua_createtable dll_lua_createtable +#define lua_settable dll_lua_settable #if LUA_VERSION_NUM >= 504 #define lua_newuserdatauv dll_lua_newuserdatauv #else @@ -302,6 +304,7 @@ int (*dll_lua_rawget) (lua_State *L, int int (*dll_lua_rawgeti) (lua_State *L, int idx, lua_Integer n); #endif void (*dll_lua_createtable) (lua_State *L, int narr, int nrec); +void (*dll_lua_settable) (lua_State *L, int idx); #if LUA_VERSION_NUM >= 504 void *(*dll_lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); #else @@ -413,6 +416,7 @@ static const luaV_Reg luaV_dll[] = { {"lua_rawget", (luaV_function) &dll_lua_rawget}, {"lua_rawgeti", (luaV_function) &dll_lua_rawgeti}, {"lua_createtable", (luaV_function) &dll_lua_createtable}, + {"lua_settable", (luaV_function) &dll_lua_settable}, #if LUA_VERSION_NUM >= 504 {"lua_newuserdatauv", (luaV_function) &dll_lua_newuserdatauv}, #else @@ -1819,7 +1823,7 @@ luaV_setvar(lua_State *L) if (dict == NULL) return 0; - di = dict_find(dict, (char_u *)name, len); + di = dict_find(dict, (char_u *)name, (int)len); if (di != NULL) { if (di->di_flags & DI_FLAGS_RO) @@ -1893,8 +1897,8 @@ luaV_getvar(lua_State *L) dict_T *dict = luaV_get_var_scope(L); size_t len; const char *name = luaL_checklstring(L, 3, &len); + dictitem_T *di = dict_find(dict, (char_u *)name, (int)len); - dictitem_T *di = dict_find(dict, (char_u *)name, len); if (di == NULL) return 0; // nil @@ -1905,7 +1909,10 @@ luaV_getvar(lua_State *L) static int luaV_command(lua_State *L) { - do_cmdline_cmd((char_u *) luaL_checkstring(L, 1)); + char_u *s = vim_strsave((char_u *)luaL_checkstring(L, 1)); + + execute_cmds_from_string(s); + vim_free(s); update_screen(VALID); return 0; } @@ -1914,6 +1921,7 @@ luaV_command(lua_State *L) luaV_eval(lua_State *L) { typval_T *tv = eval_expr((char_u *) luaL_checkstring(L, 1), NULL); + if (tv == NULL) luaL_error(L, "invalid expression"); luaV_pushtypval(L, tv); free_tv(tv); @@ -2214,6 +2222,25 @@ free_vim_args: return luaL_error(L, error); } +/* + * Return the Vim version as a Lua table + */ + static int +luaV_version(lua_State *L) +{ + lua_newtable(L); + lua_pushstring(L, "major"); + lua_pushinteger(L, VIM_VERSION_MAJOR); + lua_settable(L, -3); + lua_pushstring(L, "minor"); + lua_pushinteger(L, VIM_VERSION_MINOR); + lua_settable(L, -3); + lua_pushstring(L, "patch"); + lua_pushinteger(L, highest_patch()); + lua_settable(L, -3); + return 1; +} + static const luaL_Reg luaV_module[] = { {"command", luaV_command}, {"eval", luaV_eval}, @@ -2230,6 +2257,7 @@ static const luaL_Reg luaV_module[] = { {"call", luaV_call}, {"_getvar", luaV_getvar}, {"_setvar", luaV_setvar}, + {"version", luaV_version}, {"lua_version", NULL}, {NULL, NULL} }; diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -17,6 +17,7 @@ int non_zero_arg(typval_T *argvars); buf_T *get_buf_arg(typval_T *arg); win_T *get_optional_window(typval_T *argvars, int idx); void execute_redir_str(char_u *value, int value_len); +void execute_cmds_from_string(char_u *str); void execute_common(typval_T *argvars, typval_T *rettv, int arg_off); void f_exists(typval_T *argvars, typval_T *rettv); void f_has(typval_T *argvars, typval_T *rettv); 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 @@ -1162,4 +1162,36 @@ func Test_lua_tabpage_var_table() %bw! endfunc +" Test for vim.version() +func Test_lua_vim_version() + lua << trim END + vimver = vim.version() + vimver_n = vimver.major * 100 + vimver.minor + END + call assert_equal(v:version, luaeval('vimver_n')) +endfunc + +" Test for running multiple commands using vim.command() +func Test_lua_multiple_commands() + lua << trim END + vim.command([[ + let Var1 = [] + for i in range(3) + let Var1 += [#{name: 'x'}] + endfor + augroup Luagroup + autocmd! + autocmd User Luatest echo 'Hello' + augroup END + ]]) + END + call assert_equal([{'name': 'x'}, {'name': 'x'}, {'name': 'x'}], Var1) + call assert_true(exists('#Luagroup')) + call assert_true(exists('#Luagroup#User#Luatest')) + augroup Luagroup + autocmd! + augroup END + augroup! Luagroup +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_shell.vim b/src/testdir/test_shell.vim --- a/src/testdir/test_shell.vim +++ b/src/testdir/test_shell.vim @@ -84,7 +84,8 @@ func Test_shell_options() r !echo hello call assert_equal('hello', substitute(getline(2), '\W', '', 'g'), e[0]) catch - call assert_report('Failed to run shell command, shell: ' .. e[0]) + call assert_report('Failed to run shell command, shell: ' .. e[0] + \ .. ', caught ' .. v:exception) finally bwipe! endtry diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3300, +/**/ 3299, /**/ 3298,