From 12fb634fe691a6ccfbd9bc7b44510c782af6c70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Tue, 9 May 2017 19:59:45 +0200 Subject: [PATCH 1/2] API version bump --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d1c0e8fd5..44784b042f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,9 +68,9 @@ set(NVIM_VERSION_PATCH 1) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level -set(NVIM_API_LEVEL 2) # Bump this after any API change. +set(NVIM_API_LEVEL 3) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. -set(NVIM_API_PRERELEASE false) +set(NVIM_API_PRERELEASE true) file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) include(GetGitRevisionDescription) From f424189093ab23fb727a996d317ff19d4d3f0b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Tue, 9 May 2017 08:55:04 +0200 Subject: [PATCH 2/2] api: execute lua directly from the remote api --- src/nvim/api/vim.c | 20 +++++++++++ src/nvim/lua/executor.c | 62 ++++++++++++++++++++++++++++++++ test/functional/api/vim_spec.lua | 30 ++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d0bb840b8d..1fedaf30ef 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -15,6 +15,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/buffer.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/lua/executor.h" #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/file_search.h" @@ -254,6 +255,25 @@ free_vim_args: return rv; } +/// Execute lua code. Parameters might be passed, they are available inside +/// the chunk as `...`. The chunk can return a value. +/// +/// To evaluate an expression, it must be prefixed with "return ". For +/// instance, to call a lua function with arguments sent in and get its +/// return value back, use the code "return my_function(...)". +/// +/// @param code lua code to execute +/// @param args Arguments to the code +/// @param[out] err Details of an error encountered while parsing +/// or executing the lua code. +/// +/// @return Return value of lua code if present or NIL. +Object nvim_execute_lua(String code, Array args, Error *err) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY +{ + return executor_exec_lua_api(code, args, err); +} + /// Calculates the number of display cells occupied by `text`. /// counts as one cell. /// diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 7cf326aef5..a7d5af36a1 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -373,6 +373,46 @@ static int nlua_eval_lua_string(lua_State *const lstate) return 0; } +/// Evaluate lua string +/// +/// Expects four values on the stack: string to evaluate, pointer to args array, +/// and locations where result and error are saved, respectively. Always +/// returns nothing (from the lua point of view). +static int nlua_exec_lua_string_api(lua_State *const lstate) + FUNC_ATTR_NONNULL_ALL +{ + const String *str = (const String *)lua_touserdata(lstate, 1); + const Array *args = (const Array *)lua_touserdata(lstate, 2); + Object *retval = (Object *)lua_touserdata(lstate, 3); + Error *err = (Error *)lua_touserdata(lstate, 4); + + lua_pop(lstate, 4); + + if (luaL_loadbuffer(lstate, str->data, str->size, "")) { + size_t len; + const char *str = lua_tolstring(lstate, -1, &len); + api_set_error(err, kErrorTypeValidation, + "Error loading lua: %.*s", (int)len, str); + return 0; + } + + for (size_t i = 0; i < args->size; i++) { + nlua_push_Object(lstate, args->items[i]); + } + + if (lua_pcall(lstate, (int)args->size, 1, 0)) { + size_t len; + const char *str = lua_tolstring(lstate, -1, &len); + api_set_error(err, kErrorTypeException, + "Error executing lua: %.*s", (int)len, str); + return 0; + } + + *retval = nlua_pop_Object(lstate, err); + + return 0; +} + /// Print as a Vim message /// /// @param lstate Lua interpreter state. @@ -516,6 +556,28 @@ void executor_eval_lua(const String str, typval_T *const arg, (void *)&str, arg, ret_tv); } +/// Execute lua string +/// +/// Used for nvim_execute_lua(). +/// +/// @param[in] str String to execute. +/// @param[in] args array of ... args +/// @param[out] err Location where error will be saved. +/// +/// @return Return value of the execution. +Object executor_exec_lua_api(const String str, const Array args, Error *err) +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + Object retval = NIL; + NLUA_CALL_C_FUNCTION_4(global_lstate, nlua_exec_lua_string_api, 0, + (void *)&str, (void *)&args, &retval, err); + return retval; +} + + /// Run lua string /// /// Used for :lua. diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 282ecbfd87..61ec6ea829 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -81,6 +81,36 @@ describe('api', function() end) end) + describe('nvim_execute_lua', function() + it('works', function() + meths.execute_lua('vim.api.nvim_set_var("test", 3)', {}) + eq(3, meths.get_var('test')) + + eq(17, meths.execute_lua('a, b = ...\nreturn a + b', {10,7})) + + eq(NIL, meths.execute_lua('function xx(a,b)\nreturn a..b\nend',{})) + eq("xy", meths.execute_lua('return xx(...)', {'x','y'})) + end) + + it('reports errors', function() + eq({false, 'Error loading lua: [string ""]:1: '.. + "'=' expected near '+'"}, + meth_pcall(meths.execute_lua, 'a+*b', {})) + + eq({false, 'Error loading lua: [string ""]:1: '.. + "unexpected symbol near '1'"}, + meth_pcall(meths.execute_lua, '1+2', {})) + + eq({false, 'Error loading lua: [string ""]:1: '.. + "unexpected symbol"}, + meth_pcall(meths.execute_lua, 'aa=bb\0', {})) + + eq({false, 'Error executing lua: [string ""]:1: '.. + "attempt to call global 'bork' (a nil value)"}, + meth_pcall(meths.execute_lua, 'bork()', {})) + end) + end) + describe('nvim_input', function() it("VimL error: does NOT fail, updates v:errmsg", function() local status, _ = pcall(nvim, "input", ":call bogus_fn()")