diff options
Diffstat (limited to 'src/nvim/lua')
| -rw-r--r-- | src/nvim/lua/base64.c | 70 | ||||
| -rw-r--r-- | src/nvim/lua/base64.h | 7 | ||||
| -rw-r--r-- | src/nvim/lua/converter.c | 149 | ||||
| -rw-r--r-- | src/nvim/lua/converter.h | 17 | ||||
| -rw-r--r-- | src/nvim/lua/executor.c | 349 | ||||
| -rw-r--r-- | src/nvim/lua/executor.h | 21 | ||||
| -rw-r--r-- | src/nvim/lua/secure.c | 119 | ||||
| -rw-r--r-- | src/nvim/lua/secure.h | 7 | ||||
| -rw-r--r-- | src/nvim/lua/spell.c | 23 | ||||
| -rw-r--r-- | src/nvim/lua/spell.h | 9 | ||||
| -rw-r--r-- | src/nvim/lua/stdlib.c | 134 | ||||
| -rw-r--r-- | src/nvim/lua/stdlib.h | 7 | ||||
| -rw-r--r-- | src/nvim/lua/treesitter.c | 517 | ||||
| -rw-r--r-- | src/nvim/lua/treesitter.h | 11 | ||||
| -rw-r--r-- | src/nvim/lua/xdiff.c | 76 | ||||
| -rw-r--r-- | src/nvim/lua/xdiff.h | 9 |
16 files changed, 1056 insertions, 469 deletions
diff --git a/src/nvim/lua/base64.c b/src/nvim/lua/base64.c new file mode 100644 index 0000000000..c1f43a37d7 --- /dev/null +++ b/src/nvim/lua/base64.c @@ -0,0 +1,70 @@ +#include <assert.h> +#include <lauxlib.h> +#include <lua.h> +#include <stddef.h> + +#include "nvim/base64.h" +#include "nvim/lua/base64.h" +#include "nvim/memory.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/base64.c.generated.h" +#endif + +static int nlua_base64_encode(lua_State *L) +{ + if (lua_gettop(L) < 1) { + return luaL_error(L, "Expected 1 argument"); + } + + if (lua_type(L, 1) != LUA_TSTRING) { + luaL_argerror(L, 1, "expected string"); + } + + size_t src_len = 0; + const char *src = lua_tolstring(L, 1, &src_len); + + const char *ret = base64_encode(src, src_len); + assert(ret != NULL); + lua_pushstring(L, ret); + xfree((void *)ret); + + return 1; +} + +static int nlua_base64_decode(lua_State *L) +{ + if (lua_gettop(L) < 1) { + return luaL_error(L, "Expected 1 argument"); + } + + if (lua_type(L, 1) != LUA_TSTRING) { + luaL_argerror(L, 1, "expected string"); + } + + size_t src_len = 0; + const char *src = lua_tolstring(L, 1, &src_len); + + const char *ret = base64_decode(src, src_len); + if (ret == NULL) { + return luaL_error(L, "Invalid input"); + } + + lua_pushstring(L, ret); + xfree((void *)ret); + + return 1; +} + +static const luaL_Reg base64_functions[] = { + { "encode", nlua_base64_encode }, + { "decode", nlua_base64_decode }, + { NULL, NULL }, +}; + +int luaopen_base64(lua_State *L) +{ + lua_newtable(L); + luaL_register(L, NULL, base64_functions); + return 1; +} diff --git a/src/nvim/lua/base64.h b/src/nvim/lua/base64.h new file mode 100644 index 0000000000..3c95968cda --- /dev/null +++ b/src/nvim/lua/base64.h @@ -0,0 +1,7 @@ +#pragma once + +#include <lua.h> // IWYU pragma: keep + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/base64.h.generated.h" +#endif diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 6160b84485..4598d48c4a 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include <assert.h> #include <lauxlib.h> #include <lua.h> @@ -10,26 +7,24 @@ #include <stdlib.h> #include <string.h> +#include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/memory.h" -// FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is -// redefined -#include "klib/kvec.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/eval/decode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" -#include "nvim/garray.h" +#include "nvim/func_attr.h" #include "nvim/gettext.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" +#include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/types.h" -#include "nvim/vim.h" +#include "nvim/types_defs.h" +#include "nvim/vim_defs.h" /// Determine, which keys lua table contains typedef struct { @@ -183,7 +178,7 @@ typedef struct { int idx; ///< Container index (used to detect self-referencing structures). } TVPopStackItem; -/// Convert lua object to VimL typval_T +/// Convert lua object to Vimscript typval_T /// /// Should pop exactly one value from lua stack. /// @@ -459,7 +454,7 @@ static bool typval_conv_special = false; TYPVAL_ENCODE_CONV_NUMBER(tv, flt) #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ - lua_pushlstring(lstate, (const char *)(str), (len)) + lua_pushlstring(lstate, (str), (len)) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING @@ -469,9 +464,7 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ do { \ const blob_T *const blob_ = (blob); \ - lua_pushlstring(lstate, \ - blob_ != NULL ? (const char *)blob_->bv_ga.ga_data : "", \ - (size_t)(len)); \ + lua_pushlstring(lstate, blob_ != NULL ? blob_->bv_ga.ga_data : "", (size_t)(len)); \ } while (0) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ @@ -603,7 +596,7 @@ static bool typval_conv_special = false; #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS -/// Convert VimL typval_T to lua value +/// Convert Vimscript typval_T to lua value /// /// Should leave single value in lua stack. May only fail if lua failed to grow /// stack. @@ -630,8 +623,7 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) /// Push value which is a type index /// -/// Used for all “typed” tables: i.e. for all tables which represent VimL -/// values. +/// Used for all “typed” tables: i.e. for all tables which represent Vimscript values. static inline void nlua_push_type_idx(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { @@ -659,7 +651,7 @@ static inline void nlua_push_type(lua_State *lstate, ObjectType type) lua_pushnumber(lstate, (lua_Number)type); } -/// Create lua table which has an entry that determines its VimL type +/// Create Lua table which has an entry that determines its Vimscript type /// /// @param[out] lstate Lua state. /// @param[in] narr Number of “array” entries to be populated later. @@ -816,7 +808,7 @@ String nlua_pop_String(lua_State *lstate, Error *err) { if (lua_type(lstate, -1) != LUA_TSTRING) { lua_pop(lstate, 1); - api_set_error(err, kErrorTypeValidation, "Expected lua string"); + api_set_error(err, kErrorTypeValidation, "Expected Lua string"); return (String) { .size = 0, .data = NULL }; } String ret; @@ -837,7 +829,7 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err) { if (lua_type(lstate, -1) != LUA_TNUMBER) { lua_pop(lstate, 1); - api_set_error(err, kErrorTypeValidation, "Expected lua number"); + api_set_error(err, kErrorTypeValidation, "Expected Lua number"); return 0; } const lua_Number n = lua_tonumber(lstate, -1); @@ -852,6 +844,9 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err) /// Convert lua value to boolean /// +/// Despite the name of the function, this uses lua semantics for booleans. +/// thus `err` is never set as any lua value can be co-erced into a lua bool +/// /// Always pops one value from the stack. Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT @@ -861,6 +856,36 @@ Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) return ret; } +/// Convert lua value to boolean +/// +/// This follows API conventions for a Boolean value, compare api_object_to_bool +/// +/// Always pops one value from the stack. +Boolean nlua_pop_Boolean_strict(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Boolean ret = false; + switch (lua_type(lstate, -1)) { + case LUA_TBOOLEAN: + ret = lua_toboolean(lstate, -1); + break; + + case LUA_TNUMBER: + ret = (lua_tonumber(lstate, -1) != 0); + break; + + case LUA_TNIL: + ret = false; + break; + + default: + api_set_error(err, kErrorTypeValidation, "not a boolean"); + } + + lua_pop(lstate, 1); + return ret; +} + /// Check whether typed table on top of the stack has given type /// /// @param[in] lstate Lua state. @@ -874,7 +899,8 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons { if (lua_type(lstate, -1) != LUA_TTABLE) { if (err) { - api_set_error(err, kErrorTypeValidation, "Expected lua table"); + api_set_error(err, kErrorTypeValidation, "Expected Lua %s", + (type == kObjectTypeFloat) ? "number" : "table"); } return (LuaTableProps) { .type = kObjectTypeNil }; } @@ -887,7 +913,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons if (table_props.type != type) { if (err) { - api_set_error(err, kErrorTypeValidation, "Unexpected type"); + api_set_error(err, kErrorTypeValidation, "Expected %s-like Lua table", api_typename(type)); } } @@ -1171,7 +1197,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; case kObjectTypeNil: api_set_error(err, kErrorTypeValidation, - "Cannot convert given lua table"); + "Cannot convert given Lua table"); break; default: abort(); @@ -1227,26 +1253,19 @@ LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) return rv; } -#define GENERATE_INDEX_FUNCTION(type) \ - type nlua_pop_##type(lua_State *lstate, Error *err) \ - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ - { \ - type ret; \ - if (lua_type(lstate, -1) != LUA_TNUMBER) { \ - api_set_error(err, kErrorTypeValidation, "Expected Lua number"); \ - ret = (type) - 1; \ - } else { \ - ret = (type)lua_tonumber(lstate, -1); \ - } \ - lua_pop(lstate, 1); \ - return ret; \ +handle_T nlua_pop_handle(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + handle_T ret; + if (lua_type(lstate, -1) != LUA_TNUMBER) { + api_set_error(err, kErrorTypeValidation, "Expected Lua number"); + ret = (handle_T) - 1; + } else { + ret = (handle_T)lua_tonumber(lstate, -1); } - -GENERATE_INDEX_FUNCTION(Buffer) -GENERATE_INDEX_FUNCTION(Window) -GENERATE_INDEX_FUNCTION(Tabpage) - -#undef GENERATE_INDEX_FUNCTION + lua_pop(lstate, 1); + return ret; +} /// Record some auxiliary values in vim module /// @@ -1295,10 +1314,11 @@ void nlua_init_types(lua_State *const lstate) lua_rawset(lstate, -3); } -void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err) +// lua specific variant of api_dict_to_keydict +void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Error *err) { if (!lua_istable(L, -1)) { - api_set_error(err, kErrorTypeValidation, "Expected lua table"); + api_set_error(err, kErrorTypeValidation, "Expected Lua table"); lua_pop(L, -1); return; } @@ -1308,14 +1328,45 @@ void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err) // [dict, key, value] size_t len; const char *s = lua_tolstring(L, -2, &len); - Object *field = hashy(retval, s, len); + KeySetLink *field = hashy(s, len); if (!field) { api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s); lua_pop(L, 3); // [] return; } - *field = nlua_pop_Object(L, true, err); + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)retval; + ks->is_set_ |= (1ULL << field->opt_index); + } + char *mem = ((char *)retval + field->ptr_off); + + if (field->type == kObjectTypeNil) { + *(Object *)mem = nlua_pop_Object(L, true, err); + } else if (field->type == kObjectTypeInteger) { + *(Integer *)mem = nlua_pop_Integer(L, err); + } else if (field->type == kObjectTypeBoolean) { + *(Boolean *)mem = nlua_pop_Boolean_strict(L, err); + } else if (field->type == kObjectTypeString) { + *(String *)mem = nlua_pop_String(L, err); + } else if (field->type == kObjectTypeFloat) { + *(Float *)mem = nlua_pop_Float(L, err); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + *(handle_T *)mem = nlua_pop_handle(L, err); + } else if (field->type == kObjectTypeArray) { + *(Array *)mem = nlua_pop_Array(L, err); + } else if (field->type == kObjectTypeDictionary) { + *(Dictionary *)mem = nlua_pop_Dictionary(L, false, err); + } else if (field->type == kObjectTypeLuaRef) { + *(LuaRef *)mem = nlua_pop_LuaRef(L, err); + } else { + abort(); + } + if (ERROR_SET(err)) { + *err_opt = field->str; + break; + } } // [dict] lua_pop(L, 1); diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index ddc0acfbfa..a502df80d9 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -1,15 +1,14 @@ -#ifndef NVIM_LUA_CONVERTER_H -#define NVIM_LUA_CONVERTER_H +#pragma once -#include <lua.h> -#include <stdbool.h> -#include <stdint.h> +#include <lua.h> // IWYU pragma: keep -#include "nvim/api/private/defs.h" -#include "nvim/eval/typval.h" -#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep + +#define nlua_pop_Buffer nlua_pop_handle +#define nlua_pop_Window nlua_pop_handle +#define nlua_pop_Tabpage nlua_pop_handle #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/converter.h.generated.h" #endif -#endif // NVIM_LUA_CONVERTER_H diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5ffd90fddd..06d16efb05 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1,12 +1,11 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include <assert.h> #include <inttypes.h> #include <lauxlib.h> #include <lua.h> #include <lualib.h> #include <stddef.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <tree_sitter/api.h> #include <uv.h> @@ -16,9 +15,9 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/ascii.h" -#include "nvim/buffer_defs.h" +#include "nvim/ascii_defs.h" #include "nvim/change.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" @@ -42,28 +41,28 @@ #include "nvim/lua/executor.h" #include "nvim/lua/stdlib.h" #include "nvim/lua/treesitter.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/option_defs.h" +#include "nvim/option_vars.h" #include "nvim/os/fileio.h" #include "nvim/os/os.h" #include "nvim/path.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/usercmd.h" -#include "nvim/version.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" static int in_fast_callback = 0; +static bool in_script = false; // Initialized in nlua_init(). static lua_State *global_lstate = NULL; @@ -108,11 +107,16 @@ typedef enum luv_err_type { kThreadCallback, } luv_err_t; +lua_State *get_global_lstate(void) +{ + return global_lstate; +} + /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. -/// @param[in] msg Message base, must contain one `%s`. -static void nlua_error(lua_State *const lstate, const char *const msg) +/// @param[in] msg Message base, must contain one `%*s`. +void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { size_t len; @@ -133,8 +137,13 @@ static void nlua_error(lua_State *const lstate, const char *const msg) str = lua_tolstring(lstate, -1, &len); } - msg_ext_set_kind("lua_error"); - semsg_multiline(msg, (int)len, str); + if (in_script) { + fprintf(stderr, msg, (int)len, str); + fprintf(stderr, "\n"); + } else { + msg_ext_set_kind("lua_error"); + semsg_multiline(msg, (int)len, str); + } lua_pop(lstate, 1); } @@ -144,7 +153,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg) /// @param lstate Lua interpreter state /// @param[in] nargs Number of arguments expected by the function being called. /// @param[in] nresults Number of results the function returns. -static int nlua_pcall(lua_State *lstate, int nargs, int nresults) +int nlua_pcall(lua_State *lstate, int nargs, int nresults) { lua_getglobal(lstate, "debug"); lua_getfield(lstate, -1, "traceback"); @@ -159,17 +168,6 @@ static int nlua_pcall(lua_State *lstate, int nargs, int nresults) return status; } -/// Gets the version of the current Nvim build. -/// -/// @param lstate Lua interpreter state. -static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - Dictionary version = version_dict(); - nlua_push_Dictionary(lstate, version, true); - api_free_dictionary(version); - return 1; -} - static void nlua_luv_error_event(void **argv) { char *error = (char *)argv[0]; @@ -205,9 +203,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags if (status) { if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) { // consider out of memory errors unrecoverable, just like xmalloc() - os_errmsg(e_outofmem); - os_errmsg("\n"); - preserve_exit(); + preserve_exit(e_outofmem); } const char *error = lua_tostring(lstate, -1); @@ -343,7 +339,7 @@ static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg lua_pushstring(L, argv[lua_arg0 - 1]); lua_rawseti(L, -2, 0); // _G.arg[0] = "foo.lua" - for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) { + for (; i + lua_arg0 < argc; i++) { lua_pushstring(L, argv[i + lua_arg0]); lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "--foo" } @@ -375,6 +371,12 @@ static int nlua_schedule(lua_State *const lstate) return lua_error(lstate); } + // If main_loop is closing don't schedule tasks to run in the future, + // otherwise any refs allocated here will not be cleaned up. + if (main_loop.closing) { + return 0; + } + LuaRef cb = nlua_ref_global(lstate, 1); multiqueue_put(main_loop.events, nlua_schedule_event, @@ -384,7 +386,8 @@ static int nlua_schedule(lua_State *const lstate) // Dummy timer callback. Used by f_wait(). static void dummy_timer_due_cb(TimeWatcher *tw, void *data) -{} +{ +} // Dummy timer close callback. Used by f_wait(). static void dummy_timer_close_cb(TimeWatcher *tw, void *data) @@ -408,6 +411,10 @@ static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_r static int nlua_wait(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { + if (in_fast_callback) { + return luaL_error(lstate, e_luv_api_disabled, "vim.wait"); + } + intptr_t timeout = luaL_checkinteger(lstate, 1); if (timeout < 0) { return luaL_error(lstate, "timeout must be >= 0"); @@ -446,8 +453,7 @@ static int nlua_wait(lua_State *lstate) fast_only = lua_toboolean(lstate, 4); } - MultiQueue *loop_events = fast_only || in_fast_callback > 0 - ? main_loop.fast_events : main_loop.events; + MultiQueue *loop_events = fast_only ? main_loop.fast_events : main_loop.events; TimeWatcher *tw = xmalloc(sizeof(TimeWatcher)); @@ -463,12 +469,16 @@ static int nlua_wait(lua_State *lstate) int pcall_status = 0; bool callback_result = false; + // Flush screen updates before blocking. + ui_flush(); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, loop_events, (int)timeout, got_int || (is_function ? nlua_wait_condition(lstate, &pcall_status, - &callback_result) : false)); + &callback_result) + : false)); // Stop dummy timer time_watcher_stop(tw); @@ -534,7 +544,7 @@ int nlua_get_global_ref_count(void) return nlua_global_refs->ref_count; } -static void nlua_common_vim_init(lua_State *lstate, bool is_thread) +static void nlua_common_vim_init(lua_State *lstate, bool is_thread, bool is_standalone) FUNC_ATTR_NONNULL_ARG(1) { nlua_ref_state_t *ref_state = nlua_new_ref_state(lstate, is_thread); @@ -566,8 +576,10 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); lua_setfield(lstate, -2, "_empty_dict_mt"); - // vim.loop - if (is_thread) { + // vim.uv + if (is_standalone) { + // do nothing, use libluv like in a standalone interpreter + } else if (is_thread) { luv_set_callback(lstate, nlua_luv_thread_cb_cfpcall); luv_set_thread(lstate, nlua_luv_thread_cfpcall); luv_set_cthread(lstate, nlua_luv_thread_cfcpcall); @@ -577,9 +589,12 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) } luaopen_luv(lstate); lua_pushvalue(lstate, -1); - lua_setfield(lstate, -3, "loop"); + lua_setfield(lstate, -3, "uv"); + + lua_pushvalue(lstate, -1); + lua_setfield(lstate, -3, "loop"); // deprecated - // package.loaded.luv = vim.loop + // package.loaded.luv = vim.uv // otherwise luv will be reinitialized when require'luv' lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); @@ -606,7 +621,7 @@ static int nlua_module_preloader(lua_State *lstate) return 1; } -static bool nlua_init_packages(lua_State *lstate) +static bool nlua_init_packages(lua_State *lstate, bool is_standalone) FUNC_ATTR_NONNULL_ALL { // put builtin packages in preload @@ -614,11 +629,11 @@ static bool nlua_init_packages(lua_State *lstate) lua_getfield(lstate, -1, "preload"); // [package, preload] for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) { ModuleDef def = builtin_modules[i]; - lua_pushinteger(lstate, (long)i); // [package, preload, i] + lua_pushinteger(lstate, (lua_Integer)i); // [package, preload, i] lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] lua_setfield(lstate, -2, def.name); // [package, preload] - if (nlua_disable_preload && strequal(def.name, "vim.inspect")) { + if ((nlua_disable_preload && !is_standalone) && strequal(def.name, "vim.inspect")) { break; } } @@ -628,7 +643,7 @@ static bool nlua_init_packages(lua_State *lstate) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - os_errmsg((char *)lua_tostring(lstate, -1)); + os_errmsg(lua_tostring(lstate, -1)); os_errmsg("\n"); return false; } @@ -733,10 +748,6 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL // vim.types, vim.type_idx, vim.val_idx nlua_init_types(lstate); - // neovim version - lua_pushcfunction(lstate, &nlua_nvim_version); - lua_setfield(lstate, -2, "version"); - // schedule lua_pushcfunction(lstate, &nlua_schedule); lua_setfield(lstate, -2, "schedule"); @@ -769,7 +780,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_ui_detach); lua_setfield(lstate, -2, "ui_detach"); - nlua_common_vim_init(lstate, false); + nlua_common_vim_init(lstate, false, false); // patch require() (only for --startuptime) if (time_fd != NULL) { @@ -788,7 +799,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setglobal(lstate, "vim"); - if (!nlua_init_packages(lstate)) { + if (!nlua_init_packages(lstate, false)) { return false; } @@ -813,6 +824,9 @@ void nlua_init(char **argv, int argc, int lua_arg0) luaL_openlibs(lstate); if (!nlua_state_init(lstate)) { os_errmsg(_("E970: Failed to initialize builtin lua modules\n")); +#ifdef EXITFREE + nlua_common_free_all_mem(lstate); +#endif os_exit(1); } @@ -824,9 +838,28 @@ void nlua_init(char **argv, int argc, int lua_arg0) static lua_State *nlua_thread_acquire_vm(void) { + return nlua_init_state(true); +} + +void nlua_run_script(char **argv, int argc, int lua_arg0) + FUNC_ATTR_NORETURN +{ + in_script = true; + global_lstate = nlua_init_state(false); + luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem); + nlua_init_argv(global_lstate, argv, argc, lua_arg0); + bool lua_ok = nlua_exec_file(argv[lua_arg0 - 1]); +#ifdef EXITFREE + nlua_free_all_mem(); +#endif + exit(lua_ok ? 0 : 1); +} + +static lua_State *nlua_init_state(bool thread) +{ // If it is called from the main thread, it will attempt to rebuild the cache. const uv_thread_t self = uv_thread_self(); - if (uv_thread_equal(&main_thread, &self)) { + if (!in_script && uv_thread_equal(&main_thread, &self)) { runtime_search_path_validate(); } @@ -835,9 +868,11 @@ static lua_State *nlua_thread_acquire_vm(void) // Add in the lua standard libraries luaL_openlibs(lstate); - // print - lua_pushcfunction(lstate, &nlua_print); - lua_setglobal(lstate, "print"); + if (!in_script) { + // print + lua_pushcfunction(lstate, &nlua_print); + lua_setglobal(lstate, "print"); + } lua_pushinteger(lstate, 0); lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.refcount"); @@ -845,18 +880,20 @@ static lua_State *nlua_thread_acquire_vm(void) // vim lua_newtable(lstate); - nlua_common_vim_init(lstate, true); + nlua_common_vim_init(lstate, thread, in_script); nlua_state_add_stdlib(lstate, true); - lua_createtable(lstate, 0, 0); - lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); - lua_setfield(lstate, -2, "nvim__get_runtime"); - lua_setfield(lstate, -2, "api"); + if (!in_script) { + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); + lua_setfield(lstate, -2, "nvim__get_runtime"); + lua_setfield(lstate, -2, "api"); + } lua_setglobal(lstate, "vim"); - nlua_init_packages(lstate); + nlua_init_packages(lstate, in_script); lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); @@ -892,12 +929,13 @@ static void nlua_common_free_all_mem(lua_State *lstate) if (nlua_track_refs) { // in case there are leaked luarefs, leak the associated memory // to get LeakSanitizer stacktraces on exit - pmap_destroy(handle_T)(&ref_state->ref_markers); + map_destroy(int, &ref_state->ref_markers); } #endif lua_close(lstate); } + static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -926,10 +964,13 @@ static void nlua_print_event(void **argv) } break; } - msg(str + start); + msg(str + start, 0); + if (msg_silent == 0) { + msg_didout = true; // Make blank lines work properly + } } if (len && str[len - 1] == NUL) { // Last was newline - msg(""); + msg("", 0); } xfree(str); } @@ -1076,7 +1117,7 @@ static int nlua_debug(lua_State *lstate) .v_type = VAR_UNKNOWN, }, }; - for (;;) { + while (true) { lua_settop(lstate, 0); typval_T input; get_user_input(input_args, &input, false, false); @@ -1088,7 +1129,7 @@ static int nlua_debug(lua_State *lstate) tv_clear(&input); return 0; } - if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string, + if (luaL_loadbuffer(lstate, input.vval.v_string, strlen(input.vval.v_string), "=(debug command)")) { nlua_error(lstate, _("E5115: Error while loading debug string: %.*s")); } else if (nlua_pcall(lstate, 0, 0)) { @@ -1111,7 +1152,7 @@ static bool viml_func_is_fast(const char *name) if (fdef) { return fdef->fast; } - // Not a vimL function + // Not a Vimscript function return false; } @@ -1121,7 +1162,7 @@ int nlua_call(lua_State *lstate) size_t name_len; const char *name = luaL_checklstring(lstate, 1, &name_len); if (!nlua_is_deferred_safe() && !viml_func_is_fast(name)) { - return luaL_error(lstate, e_luv_api_disabled, "vimL function"); + return luaL_error(lstate, e_luv_api_disabled, "Vimscript function"); } int nargs = lua_gettop(lstate) - 1; @@ -1140,28 +1181,29 @@ int nlua_call(lua_State *lstate) } } - TRY_WRAP({ - // TODO(bfredl): this should be simplified in error handling refactor - force_abort = false; - suppress_errthrow = false; - did_throw = false; - did_emsg = false; + // TODO(bfredl): this should be simplified in error handling refactor + force_abort = false; + suppress_errthrow = false; + did_throw = false; + did_emsg = false; - try_start(); - typval_T rettv; - funcexe_T funcexe = FUNCEXE_INIT; - funcexe.fe_firstline = curwin->w_cursor.lnum; - funcexe.fe_lastline = curwin->w_cursor.lnum; - funcexe.fe_evaluate = true; + typval_T rettv; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = true; + + TRY_WRAP(&err, { // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (TRY_WRAP) to capture abort-causing non-exception errors. - (void)call_func((char *)name, (int)name_len, &rettv, nargs, vim_args, &funcexe); - if (!try_end(&err)) { - nlua_push_typval(lstate, &rettv, false); - } - tv_clear(&rettv); + (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe); }); + if (!ERROR_SET(&err)) { + nlua_push_typval(lstate, &rettv, false); + } + tv_clear(&rettv); + free_vim_args: while (i > 0) { tv_clear(&vim_args[--i]); @@ -1266,13 +1308,16 @@ LuaRef nlua_ref(lua_State *lstate, nlua_ref_state_t *ref_state, int index) #ifdef NLUA_TRACK_REFS if (nlua_track_refs) { // dummy allocation to make LeakSanitizer track our luarefs - pmap_put(handle_T)(&ref_state->ref_markers, ref, xmalloc(3)); + pmap_put(int)(&ref_state->ref_markers, ref, xmalloc(3)); } #endif } return ref; } +// TODO(lewis6991): Currently cannot be run in __gc metamethods as they are +// invoked in lua_close() which can be invoked after the ref_markers map is +// destroyed in nlua_common_free_all_mem. LuaRef nlua_ref_global(lua_State *lstate, int index) { return nlua_ref(lstate, nlua_global_refs, index); @@ -1286,7 +1331,7 @@ void nlua_unref(lua_State *lstate, nlua_ref_state_t *ref_state, LuaRef ref) #ifdef NLUA_TRACK_REFS // NB: don't remove entry from map to track double-unref if (nlua_track_refs) { - xfree(pmap_get(handle_T)(&ref_state->ref_markers, ref)); + xfree(pmap_get(int)(&ref_state->ref_markers, ref)); } #endif luaL_unref(lstate, LUA_REGISTRYINDEX, ref); @@ -1459,7 +1504,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) /// Call a LuaCallable given some typvals /// -/// Used to call any lua callable passed from Lua into VimL +/// Used to call any Lua callable passed from Lua into Vimscript. /// /// @param[in] lstate Lua State /// @param[in] lua_cb Lua Callable @@ -1594,24 +1639,25 @@ bool nlua_is_deferred_safe(void) /// /// Used for :lua. /// -/// @param eap VimL command being run. +/// @param eap Vimscript command being run. void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { size_t len; char *code = script_get(eap, &len); - if (eap->skip) { + if (eap->skip || code == NULL) { xfree(code); return; } - // When =expr is used transform it to print(vim.inspect(expr)) - if (code[0] == '=') { - len += sizeof("vim.pretty_print()") - sizeof("="); + // When =expr is used transform it to vim.print(expr) + if (eap->cmdidx == CMD_equal || code[0] == '=') { |