aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/health/provider.vim3
-rw-r--r--runtime/autoload/provider/pythonx.vim5
-rw-r--r--runtime/doc/if_lua.txt16
-rw-r--r--src/nvim/lua/converter.c14
-rw-r--r--src/nvim/lua/executor.c63
-rw-r--r--src/nvim/lua/vim.lua12
-rw-r--r--src/nvim/quickfix.c4
-rw-r--r--src/nvim/testdir/test_quickfix.vim21
-rw-r--r--test/functional/lua/utility_functions_spec.lua28
9 files changed, 159 insertions, 7 deletions
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index c750a954fa..ad7a614ff5 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -202,7 +202,8 @@ function! s:version_info(python) abort
let nvim_path = s:trim(s:system([
\ a:python, '-c',
- \ 'import sys; sys.path.remove(""); ' .
+ \ 'import sys; ' .
+ \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' .
\ 'import neovim; print(neovim.__file__)']))
if s:shell_error || empty(nvim_path)
return [python_version, 'unable to load neovim Python module', pypi_version,
diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim
index 6ce7165467..aec18c0508 100644
--- a/runtime/autoload/provider/pythonx.vim
+++ b/runtime/autoload/provider/pythonx.vim
@@ -10,7 +10,8 @@ function! provider#pythonx#Require(host) abort
" Python host arguments
let prog = (ver == '2' ? provider#python#Prog() : provider#python3#Prog())
- let args = [prog, '-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()']
+ let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()']
+
" Collect registered Python plugins into args
let python_plugins = remote#host#PluginsForHost(a:host.name)
@@ -66,7 +67,7 @@ endfunction
function! s:import_module(prog, module) abort
let prog_version = system([a:prog, '-c' , printf(
\ 'import sys; ' .
- \ 'sys.path.remove(""); ' .
+ \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' .
\ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' .
\ 'import pkgutil; ' .
\ 'exit(2*int(pkgutil.get_loader("%s") is None))',
diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt
index 8528085f47..d527a91a93 100644
--- a/runtime/doc/if_lua.txt
+++ b/runtime/doc/if_lua.txt
@@ -588,6 +588,22 @@ vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
+vim.fn.{func}({...})
+ Call vimL function {func} with arguments. {func} can be both builtin
+ functions and user functions. To call autoload functions, use the
+ syntax `vim.fn['some#function']({...})`
+
+ Note: unlike vim.api.|nvim_call_function| this converts values directly
+ between vimL values and lua values. If the vimL function returns a
+ float, it will be representeted directly as a lua number. Both empty
+ lists and dictonaries will be represented by an empty table.
+
+ Note: vim.fn keys are generated on demand. So `pairs(vim.fn)`
+ does NOT work to enumerate all functions.
+
+vim.call({func}, {...})
+ Call vim script function {func}. Equivalent to `vim.fn[func]({...})`
+
vim.type_idx *vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the
values from |vim.types| allows typing the empty table (it is
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 9665655e74..844232c64a 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -401,6 +401,8 @@ nlua_pop_typval_table_processing_end:
return ret;
}
+static bool typval_conv_special = false;
+
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
#define TYPVAL_ENCODE_CONV_NIL(tv) \
@@ -439,7 +441,13 @@ nlua_pop_typval_table_processing_end:
lua_createtable(lstate, 0, 0)
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
- nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary)
+ do { \
+ if (typval_conv_special) { \
+ nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \
+ } else { \
+ lua_createtable(lstate, 0, 0); \
+ } \
+ } while (0)
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
do { \
@@ -548,9 +556,11 @@ nlua_pop_typval_table_processing_end:
/// @param[in] tv typval_T to convert.
///
/// @return true in case of success, false otherwise.
-bool nlua_push_typval(lua_State *lstate, typval_T *const tv)
+bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special)
{
+ typval_conv_special = special;
const int initial_size = lua_gettop(lstate);
+
if (!lua_checkstack(lstate, initial_size + 2)) {
emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
return false;
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 127458fe39..b7c1d57964 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -295,6 +295,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
// in_fast_event
lua_pushcfunction(lstate, &nlua_in_fast_event);
lua_setfield(lstate, -2, "in_fast_event");
+ // call
+ lua_pushcfunction(lstate, &nlua_call);
+ lua_setfield(lstate, -2, "call");
// vim.loop
luv_set_loop(lstate, &main_loop.uv);
@@ -539,6 +542,64 @@ int nlua_in_fast_event(lua_State *lstate)
return 1;
}
+int nlua_call(lua_State *lstate)
+{
+ Error err = ERROR_INIT;
+ size_t name_len;
+ const char_u *name = (const char_u *)luaL_checklstring(lstate, 1, &name_len);
+ int nargs = lua_gettop(lstate)-1;
+ if (nargs > MAX_FUNC_ARGS) {
+ return luaL_error(lstate, "Function called with too many arguments");
+ }
+
+ typval_T vim_args[MAX_FUNC_ARGS + 1];
+ int i = 0; // also used for freeing the variables
+ for (; i < nargs; i++) {
+ lua_pushvalue(lstate, (int)i+2);
+ if (!nlua_pop_typval(lstate, &vim_args[i])) {
+ api_set_error(&err, kErrorTypeException,
+ "error converting argument %d", i+1);
+ goto free_vim_args;
+ }
+ }
+
+ // TODO(bfredl): this should be simplified in error handling refactor
+ struct msglist **saved_msg_list = msg_list;
+ struct msglist *private_msg_list = NULL;
+ msg_list = &private_msg_list;
+
+ force_abort = false;
+ suppress_errthrow = false;
+ current_exception = NULL;
+ did_emsg = false;
+
+ try_start();
+ typval_T rettv;
+ int dummy;
+ // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
+ // (see above) to capture abort-causing non-exception errors.
+ (void)call_func(name, (int)name_len, &rettv, nargs,
+ vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &dummy, true, NULL, NULL);
+ if (!try_end(&err)) {
+ nlua_push_typval(lstate, &rettv, false);
+ }
+ tv_clear(&rettv);
+
+ msg_list = saved_msg_list;
+
+free_vim_args:
+ while (i > 0) {
+ tv_clear(&vim_args[--i]);
+ }
+ if (ERROR_SET(&err)) {
+ lua_pushstring(lstate, err.msg);
+ api_clear_error(&err);
+ return lua_error(lstate);
+ }
+ return 1;
+}
+
#ifdef WIN32
/// os.getenv: override os.getenv to maintain coherency. #9681
///
@@ -623,7 +684,7 @@ void executor_eval_lua(const String str, typval_T *const arg,
if (arg->v_type == VAR_UNKNOWN) {
lua_pushnil(lstate);
} else {
- nlua_push_typval(lstate, arg);
+ nlua_push_typval(lstate, arg, true);
}
if (lua_pcall(lstate, 1, 1, 0)) {
nlua_error(lstate,
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index b67762e48e..5514819a02 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -242,6 +242,17 @@ local function __index(t, key)
end
end
+
+-- vim.fn.{func}(...)
+local function fn_index(t, key)
+ local function func(...)
+ return vim.call(key, ...)
+ end
+ t[key] = func
+ return func
+end
+local fn = setmetatable({}, {__index=fn_index})
+
local module = {
_update_package_paths = _update_package_paths,
_os_proc_children = _os_proc_children,
@@ -249,6 +260,7 @@ local module = {
_system = _system,
paste = paste,
schedule_wrap = schedule_wrap,
+ fn=fn,
}
setmetatable(module, {
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 8f891751d6..4ca9ca2a3e 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3164,7 +3164,9 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz,
if (sz != win->w_width) {
win_setwidth(sz);
}
- } else if (sz != win->w_height) {
+ } else if (sz != win->w_height
+ && (win->w_height + win->w_status_height + tabline_height()
+ < cmdline_row)) {
win_setheight(sz);
}
}
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 6f58b0084c..31b0c0cd2c 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -273,6 +273,27 @@ func Test_cwindow()
call XwindowTests('l')
endfunc
+func Test_copenHeight()
+ copen
+ wincmd H
+ let height = winheight(0)
+ copen 10
+ call assert_equal(height, winheight(0))
+ quit
+endfunc
+
+func Test_copenHeight_tabline()
+ set tabline=foo showtabline=2
+ copen
+ wincmd H
+ let height = winheight(0)
+ copen 10
+ call assert_equal(height, winheight(0))
+ quit
+ set tabline& showtabline&
+endfunc
+
+
" Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile
" commands.
func XfileTests(cchar)
diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/utility_functions_spec.lua
index a51334398c..0b789e84b0 100644
--- a/test/functional/lua/utility_functions_spec.lua
+++ b/test/functional/lua/utility_functions_spec.lua
@@ -10,6 +10,7 @@ local feed = helpers.feed
local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua
local matches = helpers.matches
+local source = helpers.source
before_each(clear)
@@ -299,4 +300,31 @@ describe('lua stdlib', function()
eq("Error executing lua: .../shared.lua: Expected string, got number",
pcall_err(exec_lua, [[return vim.pesc(2)]]))
end)
+
+ it('vim.call and vim.fn', function()
+ eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]]))
+ eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]]))
+ -- compat: nvim_call_function uses "special" value for vimL float
+ eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]]))
+
+ source([[
+ func! FooFunc(test)
+ let g:test = a:test
+ return {}
+ endfunc
+ func! VarArg(...)
+ return a:000
+ endfunc
+ ]])
+ eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]]))
+ eq(3, eval("g:test"))
+ -- compat: nvim_call_function uses "special" value for empty dict
+ eq(true, exec_lua([[return next(vim.api.nvim_call_function("FooFunc", {5})) == true ]]))
+ eq(5, eval("g:test"))
+
+ eq({2, "foo", true}, exec_lua([[return vim.fn.VarArg(2, "foo", true)]]))
+
+ -- error handling
+ eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
+ end)
end)