diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
commit | 21cb7d04c387e4198ca8098a884c78b56ffcf4c2 (patch) | |
tree | 84fe5690df1551f0bb2bdfe1a13aacd29ebc1de7 /src/nvim/lua | |
parent | d9c904f85a23a496df4eb6be42aa43f007b22d50 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-colorcolchar.tar.gz rneovim-colorcolchar.tar.bz2 rneovim-colorcolchar.zip |
Merge remote-tracking branch 'upstream/master' into colorcolcharcolorcolchar
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] == '=') { + size_t off = (eap->cmdidx == CMD_equal) ? 0 : 1; + len += sizeof("vim.print()") - 1 - off; // code_buf needs to be 1 char larger then len for null byte in the end. // lua nlua_typval_exec doesn't expect null terminated string so len // needs to end before null byte. char *code_buf = xmallocz(len); - vim_snprintf(code_buf, len + 1, "vim.pretty_print(%s)", code + 1); + vim_snprintf(code_buf, len + 1, "vim.print(%s)", code + off); xfree(code); code = code_buf; } @@ -1625,7 +1671,7 @@ void ex_lua(exarg_T *const eap) /// /// Used for :luado. /// -/// @param eap VimL command being run. +/// @param eap Vimscript command being run. void ex_luado(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { @@ -1633,7 +1679,7 @@ void ex_luado(exarg_T *const eap) emsg(_("cannot save undo information")); return; } - const char *const cmd = (const char *)eap->arg; + const char *const cmd = eap->arg; const size_t cmd_len = strlen(cmd); lua_State *const lstate = global_lstate; @@ -1674,7 +1720,9 @@ void ex_luado(exarg_T *const eap) break; } lua_pushvalue(lstate, -1); - const char *old_line = (const char *)ml_get_buf(curbuf, l, false); + const char *const old_line = ml_get_buf(curbuf, l); + // Get length of old_line here as calling Lua code may free it. + const size_t old_line_len = strlen(old_line); lua_pushstring(lstate, old_line); lua_pushnumber(lstate, (lua_Number)l); if (nlua_pcall(lstate, 2, 1)) { @@ -1682,8 +1730,6 @@ void ex_luado(exarg_T *const eap) break; } if (lua_isstring(lstate, -1)) { - size_t old_line_len = strlen(old_line); - size_t new_line_len; const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); char *const new_line_transformed = xmemdupz(new_line, new_line_len); @@ -1706,11 +1752,11 @@ void ex_luado(exarg_T *const eap) /// /// Used for :luafile. /// -/// @param eap VimL command being run. +/// @param eap Vimscript command being run. void ex_luafile(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { - nlua_exec_file((const char *)eap->arg); + nlua_exec_file(eap->arg); } /// Executes Lua code from a file or "-" (stdin). @@ -1734,13 +1780,12 @@ bool nlua_exec_file(const char *path) StringBuilder sb = KV_INITIAL_VALUE; kv_resize(sb, 64); - ptrdiff_t read_size = -1; // Read all input from stdin, unless interrupted (ctrl-c). while (true) { if (got_int) { // User canceled. return false; } - read_size = file_read(stdin_dup, IObuff, 64); + ptrdiff_t read_size = file_read(stdin_dup, IObuff, 64); if (read_size < 0) { // Error. return false; } @@ -1842,7 +1887,7 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) luaL_checktype(lstate, -1, LUA_TFUNCTION); // [ vim, vim._expand_pat, buf ] - lua_pushlstring(lstate, (const char *)pat, strlen(pat)); + lua_pushlstring(lstate, pat, strlen(pat)); if (nlua_pcall(lstate, 1, 2) != 0) { nlua_error(lstate, @@ -1961,7 +2006,7 @@ char *nlua_register_table_as_callable(const typval_T *const arg) void nlua_execute_on_key(int c) { char buf[NUMBUFLEN]; - size_t buf_len = special_to_buf(c, mod_mask, false, (char_u *)buf); + size_t buf_len = special_to_buf(c, mod_mask, false, buf); lua_State *const lstate = global_lstate; @@ -2007,9 +2052,9 @@ void nlua_set_sctx(sctx_T *current) lua_Debug *info = (lua_Debug *)xmalloc(sizeof(lua_Debug)); // Files where internal wrappers are defined so we can ignore them - // like vim.o/opt etc are defined in _meta.lua + // like vim.o/opt etc are defined in _options.lua char *ignorelist[] = { - "vim/_meta.lua", + "vim/_options.lua", "vim/keymap.lua", }; int ignorelist_size = sizeof(ignorelist) / sizeof(ignorelist[0]); @@ -2039,10 +2084,15 @@ void nlua_set_sctx(sctx_T *current) break; } char *source_path = fix_fname(info->source + 1); - get_current_script_id(&source_path, current); - xfree(source_path); - current->sc_lnum = info->currentline; + int sid = find_script_by_name(source_path); + if (sid > 0) { + xfree(source_path); + } else { + new_script_item(source_path, &sid); + } + current->sc_sid = sid; current->sc_seq = -1; + current->sc_lnum = info->currentline; cleanup: xfree(info); @@ -2069,7 +2119,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) lua_setfield(lstate, -2, "line2"); lua_newtable(lstate); // f-args table - lua_pushstring(lstate, (const char *)eap->arg); + lua_pushstring(lstate, eap->arg); lua_pushvalue(lstate, -1); // Reference for potential use on f-args lua_setfield(lstate, -4, "args"); @@ -2252,79 +2302,16 @@ plain: return str.items; } -char *nlua_read_secure(const char *path) -{ - lua_State *const lstate = global_lstate; - const int top = lua_gettop(lstate); - - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "secure"); - lua_getfield(lstate, -1, "read"); - lua_pushstring(lstate, path); - if (nlua_pcall(lstate, 1, 1)) { - nlua_error(lstate, _("Error executing vim.secure.read: %.*s")); - lua_settop(lstate, top); - return NULL; - } - - size_t len = 0; - const char *contents = lua_tolstring(lstate, -1, &len); - char *buf = NULL; - if (contents != NULL) { - // Add one to include trailing null byte - buf = xcalloc(len + 1, sizeof(char)); - memcpy(buf, contents, len + 1); - } - - lua_settop(lstate, top); - return buf; -} - -bool nlua_trust(const char *action, const char *path) +/// Execute the vim._defaults module to set up default mappings and autocommands +void nlua_init_defaults(void) { - lua_State *const lstate = global_lstate; - const int top = lua_gettop(lstate); - - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "secure"); - lua_getfield(lstate, -1, "trust"); - - lua_newtable(lstate); - lua_pushstring(lstate, "action"); - lua_pushstring(lstate, action); - lua_settable(lstate, -3); - if (path == NULL) { - lua_pushstring(lstate, "bufnr"); - lua_pushnumber(lstate, 0); - lua_settable(lstate, -3); - } else { - lua_pushstring(lstate, "path"); - lua_pushstring(lstate, path); - lua_settable(lstate, -3); - } - - if (nlua_pcall(lstate, 1, 2)) { - nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); - lua_settop(lstate, top); - return false; - } + lua_State *const L = global_lstate; + assert(L); - bool success = lua_toboolean(lstate, -2); - const char *msg = lua_tostring(lstate, -1); - if (msg != NULL) { - if (success) { - if (strcmp(action, "allow") == 0) { - smsg("Allowed \"%s\" in trust database.", msg); - } else if (strcmp(action, "deny") == 0) { - smsg("Denied \"%s\" in trust database.", msg); - } else if (strcmp(action, "remove") == 0) { - smsg("Removed \"%s\" from trust database.", msg); - } - } else { - semsg(e_trustfile, msg); - } + lua_getglobal(L, "require"); + lua_pushstring(L, "vim._defaults"); + if (nlua_pcall(L, 1, 0)) { + os_errmsg(lua_tostring(L, -1)); + os_errmsg("\n"); } - - lua_settop(lstate, top); - return success; } diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index c6747833e5..b38faddbb3 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -1,5 +1,4 @@ -#ifndef NVIM_LUA_EXECUTOR_H -#define NVIM_LUA_EXECUTOR_H +#pragma once #include <lauxlib.h> #include <lua.h> @@ -7,13 +6,15 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/assert.h" -#include "nvim/eval/typval.h" +#include "nvim/assert_defs.h" +#include "nvim/cmdexpand_defs.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/func_attr.h" #include "nvim/lua/converter.h" -#include "nvim/macros.h" -#include "nvim/types.h" +#include "nvim/macros_defs.h" +#include "nvim/map_defs.h" +#include "nvim/types_defs.h" #include "nvim/usercmd.h" // Generated by msgpack-gen.lua @@ -24,7 +25,7 @@ typedef struct { LuaRef empty_dict_ref; int ref_count; #if __has_feature(address_sanitizer) - PMap(handle_T) ref_markers; + PMap(int) ref_markers; #endif } nlua_ref_state_t; @@ -43,7 +44,5 @@ typedef struct { # include "lua/executor.h.generated.h" #endif -EXTERN nlua_ref_state_t *nlua_global_refs INIT(= NULL); -EXTERN bool nlua_disable_preload INIT(= false); - -#endif // NVIM_LUA_EXECUTOR_H +EXTERN nlua_ref_state_t *nlua_global_refs INIT( = NULL); +EXTERN bool nlua_disable_preload INIT( = false); diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c new file mode 100644 index 0000000000..65c13f8872 --- /dev/null +++ b/src/nvim/lua/secure.c @@ -0,0 +1,119 @@ +#include <lua.h> +#include <stdbool.h> +#include <string.h> + +#include "nvim/charset.h" +#include "nvim/ex_cmds_defs.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" +#include "nvim/memory.h" +#include "nvim/message.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/secure.c.generated.h" +#endif + +char *nlua_read_secure(const char *path) +{ + lua_State *const lstate = get_global_lstate(); + const int top = lua_gettop(lstate); + + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "secure"); + lua_getfield(lstate, -1, "read"); + lua_pushstring(lstate, path); + if (nlua_pcall(lstate, 1, 1)) { + nlua_error(lstate, _("Error executing vim.secure.read: %.*s")); + lua_settop(lstate, top); + return NULL; + } + + size_t len = 0; + const char *contents = lua_tolstring(lstate, -1, &len); + char *buf = NULL; + if (contents != NULL) { + // Add one to include trailing null byte + buf = xcalloc(len + 1, sizeof(char)); + memcpy(buf, contents, len + 1); + } + + lua_settop(lstate, top); + return buf; +} + +static bool nlua_trust(const char *action, const char *path) +{ + lua_State *const lstate = get_global_lstate(); + const int top = lua_gettop(lstate); + + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "secure"); + lua_getfield(lstate, -1, "trust"); + + lua_newtable(lstate); + lua_pushstring(lstate, "action"); + lua_pushstring(lstate, action); + lua_settable(lstate, -3); + if (path == NULL) { + lua_pushstring(lstate, "bufnr"); + lua_pushnumber(lstate, 0); + lua_settable(lstate, -3); + } else { + lua_pushstring(lstate, "path"); + lua_pushstring(lstate, path); + lua_settable(lstate, -3); + } + + if (nlua_pcall(lstate, 1, 2)) { + nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); + lua_settop(lstate, top); + return false; + } + + bool success = lua_toboolean(lstate, -2); + const char *msg = lua_tostring(lstate, -1); + if (msg != NULL) { + if (success) { + if (strcmp(action, "allow") == 0) { + smsg(0, "Allowed \"%s\" in trust database.", msg); + } else if (strcmp(action, "deny") == 0) { + smsg(0, "Denied \"%s\" in trust database.", msg); + } else if (strcmp(action, "remove") == 0) { + smsg(0, "Removed \"%s\" from trust database.", msg); + } + } else { + semsg(e_trustfile, msg); + } + } + + lua_settop(lstate, top); + return success; +} + +void ex_trust(exarg_T *eap) +{ + const char *const p = skiptowhite(eap->arg); + char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg)); + const char *action = "allow"; + const char *path = skipwhite(p); + + if (strcmp(arg1, "++deny") == 0) { + action = "deny"; + } else if (strcmp(arg1, "++remove") == 0) { + action = "remove"; + } else if (*arg1 != '\0') { + semsg(e_invarg2, arg1); + goto theend; + } + + if (path[0] == '\0') { + path = NULL; + } + + nlua_trust(action, path); + +theend: + xfree(arg1); +} diff --git a/src/nvim/lua/secure.h b/src/nvim/lua/secure.h new file mode 100644 index 0000000000..c69c0d4f8f --- /dev/null +++ b/src/nvim/lua/secure.h @@ -0,0 +1,7 @@ +#pragma once + +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/secure.h.generated.h" +#endif diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index d510d25e90..c261c5105e 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.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 <limits.h> @@ -8,7 +5,7 @@ #include <stdbool.h> #include <stddef.h> -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/gettext.h" #include "nvim/globals.h" @@ -16,7 +13,6 @@ #include "nvim/lua/spell.h" #include "nvim/message.h" #include "nvim/spell.h" -#include "nvim/types.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/spell.c.generated.h" // IWYU pragma: export @@ -39,7 +35,7 @@ int nlua_spell_check(lua_State *lstate) const int wo_spell_save = curwin->w_p_spell; if (!curwin->w_p_spell) { - did_set_spelllang(curwin); + parse_spelllang(curwin); curwin->w_p_spell = true; } @@ -51,7 +47,6 @@ int nlua_spell_check(lua_State *lstate) } hlf_T attr = HLF_COUNT; - size_t len = 0; size_t pos = 0; int capcol = -1; int no_res = 0; @@ -61,7 +56,7 @@ int nlua_spell_check(lua_State *lstate) while (*str != NUL) { attr = HLF_COUNT; - len = spell_check(curwin, (char *)str, &attr, &capcol, false); + size_t len = spell_check(curwin, (char *)str, &attr, &capcol, false); assert(len <= INT_MAX); if (attr != HLF_COUNT) { @@ -70,11 +65,11 @@ int nlua_spell_check(lua_State *lstate) lua_pushlstring(lstate, str, len); lua_rawseti(lstate, -2, 1); - result = attr == HLF_SPB ? "bad" : - attr == HLF_SPR ? "rare" : - attr == HLF_SPL ? "local" : - attr == HLF_SPC ? "caps" : - NULL; + result = attr == HLF_SPB + ? "bad" : (attr == HLF_SPR + ? "rare" : (attr == HLF_SPL + ? "local" : (attr == HLF_SPC + ? "caps" : NULL))); assert(result != NULL); @@ -82,7 +77,7 @@ int nlua_spell_check(lua_State *lstate) lua_rawseti(lstate, -2, 2); // +1 for 1-indexing - lua_pushinteger(lstate, (long)pos + 1); + lua_pushinteger(lstate, (lua_Integer)pos + 1); lua_rawseti(lstate, -2, 3); lua_rawseti(lstate, -2, ++no_res); diff --git a/src/nvim/lua/spell.h b/src/nvim/lua/spell.h index 8f798a5191..6f1b322e5b 100644 --- a/src/nvim/lua/spell.h +++ b/src/nvim/lua/spell.h @@ -1,12 +1,7 @@ -#ifndef NVIM_LUA_SPELL_H -#define NVIM_LUA_SPELL_H +#pragma once -#include <lauxlib.h> -#include <lua.h> -#include <lualib.h> +#include <lua.h> // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/spell.h.generated.h" #endif - -#endif // NVIM_LUA_SPELL_H diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 6ebca6d97e..33770b2e62 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.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> @@ -11,30 +8,36 @@ #include <string.h> #include <sys/types.h> -#include "auto/config.h" +#ifdef NVIM_VENDOR_BIT +# include "bit.h" +#endif + #include "cjson/lua_cjson.h" #include "mpack/lmpack.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" -#include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" +#include "nvim/eval/vars.h" #include "nvim/ex_eval.h" +#include "nvim/fold.h" +#include "nvim/func_attr.h" #include "nvim/globals.h" +#include "nvim/lua/base64.h" #include "nvim/lua/converter.h" #include "nvim/lua/spell.h" #include "nvim/lua/stdlib.h" #include "nvim/lua/xdiff.h" -#include "nvim/map.h" +#include "nvim/map_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/regexp.h" -#include "nvim/types.h" -#include "nvim/vim.h" +#include "nvim/runtime.h" +#include "nvim/strings.h" +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/stdlib.c.generated.h" @@ -78,20 +81,20 @@ static int regex_match_line(lua_State *lstate) return luaL_error(lstate, "not enough args"); } - long bufnr = luaL_checkinteger(lstate, 2); + handle_T bufnr = (handle_T)luaL_checkinteger(lstate, 2); linenr_T rownr = (linenr_T)luaL_checkinteger(lstate, 3); - long start = 0, end = -1; + int start = 0, end = -1; if (narg >= 4) { - start = luaL_checkinteger(lstate, 4); + start = (int)luaL_checkinteger(lstate, 4); } if (narg >= 5) { - end = luaL_checkinteger(lstate, 5); + end = (int)luaL_checkinteger(lstate, 5); if (end < 0) { return luaL_error(lstate, "invalid end"); } } - buf_T *buf = bufnr ? handle_get_buffer((int)bufnr) : curbuf; + buf_T *buf = bufnr ? handle_get_buffer(bufnr) : curbuf; if (!buf || buf->b_ml.ml_mfp == NULL) { return luaL_error(lstate, "invalid buffer"); } @@ -100,7 +103,7 @@ static int regex_match_line(lua_State *lstate) return luaL_error(lstate, "invalid row"); } - char *line = ml_get_buf(buf, rownr + 1, false); + char *line = ml_get_buf(buf, rownr + 1); size_t len = strlen(line); if (start < 0 || (size_t)start > len) { @@ -178,8 +181,8 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t codepoints = 0, codeunits = 0; mb_utflen(s1, (size_t)idx, &codepoints, &codeunits); - lua_pushinteger(lstate, (long)codepoints); - lua_pushinteger(lstate, (long)codeunits); + lua_pushinteger(lstate, (lua_Integer)codepoints); + lua_pushinteger(lstate, (lua_Integer)codeunits); return 2; } @@ -199,7 +202,7 @@ static int nlua_str_utf_pos(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t clen; for (size_t i = 0; i < s1_len && s1[i] != NUL; i += clen) { clen = (size_t)utf_ptr2len_len(s1 + i, (int)(s1_len - i)); - lua_pushinteger(lstate, (long)i + 1); + lua_pushinteger(lstate, (lua_Integer)i + 1); lua_rawseti(lstate, -2, (int)idx); idx++; } @@ -218,11 +221,11 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); - long offset = luaL_checkinteger(lstate, 2); + ptrdiff_t offset = luaL_checkinteger(lstate, 2); if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int head_offset = utf_cp_head_off((char_u *)s1, (char_u *)s1 + offset - 1); + int head_offset = -utf_cp_head_off(s1, s1 + offset - 1); lua_pushinteger(lstate, head_offset); return 1; } @@ -238,7 +241,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); - long offset = luaL_checkinteger(lstate, 2); + ptrdiff_t offset = luaL_checkinteger(lstate, 2); if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } @@ -271,7 +274,7 @@ int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return luaL_error(lstate, "index out of range"); } - lua_pushinteger(lstate, (long)byteidx); + lua_pushinteger(lstate, (lua_Integer)byteidx); return 1; } @@ -282,10 +285,8 @@ int nlua_regex(lua_State *lstate) const char *text = luaL_checkstring(lstate, 1); regprog_T *prog = NULL; - TRY_WRAP({ - try_start(); - prog = vim_regcomp((char *)text, RE_AUTO | RE_MAGIC | RE_STRICT); - try_end(&err); + TRY_WRAP(&err, { + prog = vim_regcomp(text, RE_AUTO | RE_MAGIC | RE_STRICT); }); if (ERROR_SET(&err)) { @@ -356,6 +357,9 @@ int nlua_setvar(lua_State *lstate) Error err = ERROR_INIT; dictitem_T *di = dict_check_writable(dict, key, del, &err); if (ERROR_SET(&err)) { + nlua_push_errstr(lstate, "%s", err.msg); + api_clear_error(&err); + lua_error(lstate); return 0; } @@ -391,6 +395,15 @@ int nlua_setvar(lua_State *lstate) di = tv_dict_item_alloc_len(key.data, key.size); tv_dict_add(dict, di); } else { + bool type_error = false; + if (dict == &vimvardict + && !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) { + tv_clear(&tv); + if (type_error) { + return luaL_error(lstate, "Setting v:%s to value with wrong type", key.data); + } + return 0; + } if (watched) { tv_copy(&di->di_tv, &oldtv); } @@ -452,7 +465,7 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL int ret = 0; assert(s1[s1_len] == NUL); assert(s2[s2_len] == NUL); - do { + while (true) { nul1 = memchr(s1, NUL, s1_len); nul2 = memchr(s2, NUL, s2_len); ret = STRICMP(s1, s2); @@ -476,7 +489,7 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } else { break; } - } while (true); + } lua_pop(lstate, 2); lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); return 1; @@ -501,7 +514,7 @@ static int nlua_iconv(lua_State *lstate) const char *str = lua_tolstring(lstate, 1, &str_len); char *from = enc_canonize(enc_skip((char *)lua_tolstring(lstate, 2, NULL))); - char *to = enc_canonize(enc_skip((char *)lua_tolstring(lstate, 3, NULL))); + char *to = enc_canonize(enc_skip((char *)lua_tolstring(lstate, 3, NULL))); vimconv_T vimconv; vimconv.vc_type = CONV_NONE; @@ -524,6 +537,31 @@ static int nlua_iconv(lua_State *lstate) return 1; } +// Like 'zx' but don't call newFoldLevel() +static int nlua_foldupdate(lua_State *lstate) +{ + curwin->w_foldinvalid = true; // recompute folds + foldOpenCursor(); + + return 0; +} + +// Access to internal functions. For use in runtime/ +static void nlua_state_add_internal(lua_State *const lstate) +{ + // _getvar + lua_pushcfunction(lstate, &nlua_getvar); + lua_setfield(lstate, -2, "_getvar"); + + // _setvar + lua_pushcfunction(lstate, &nlua_setvar); + lua_setfield(lstate, -2, "_setvar"); + + // _updatefolds + lua_pushcfunction(lstate, &nlua_foldupdate); + lua_setfield(lstate, -2, "_foldupdate"); +} + void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) { if (!is_thread) { @@ -558,14 +596,6 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) lua_setfield(lstate, -2, "__index"); // [meta] lua_pop(lstate, 1); // don't use metatable now - // _getvar - lua_pushcfunction(lstate, &nlua_getvar); - lua_setfield(lstate, -2, "_getvar"); - - // _setvar - lua_pushcfunction(lstate, &nlua_setvar); - lua_setfield(lstate, -2, "_setvar"); - // vim.spell luaopen_spell(lstate); lua_setfield(lstate, -2, "spell"); @@ -574,6 +604,12 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) // depends on p_ambw, p_emoji lua_pushcfunction(lstate, &nlua_iconv); lua_setfield(lstate, -2, "iconv"); + + // vim.base64 + luaopen_base64(lstate); + lua_setfield(lstate, -2, "base64"); + + nlua_state_add_internal(lstate); } // vim.mpack @@ -589,6 +625,19 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) lua_setfield(lstate, -2, "mpack"); lua_pop(lstate, 3); + // vim.lpeg + int luaopen_lpeg(lua_State *); + luaopen_lpeg(lstate); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, -4, "lpeg"); + + // package.loaded.lpeg = vim.lpeg + lua_getglobal(lstate, "package"); + lua_getfield(lstate, -1, "loaded"); + lua_pushvalue(lstate, -3); + lua_setfield(lstate, -2, "lpeg"); + lua_pop(lstate, 4); + // vim.diff lua_pushcfunction(lstate, &nlua_xdl_diff); lua_setfield(lstate, -2, "diff"); @@ -596,6 +645,13 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) // vim.json lua_cjson_new(lstate); lua_setfield(lstate, -2, "json"); + +#ifdef NVIM_VENDOR_BIT + // if building with puc lua, use internal fallback for require'bit' + int top = lua_gettop(lstate); + luaopen_bit(lstate); + lua_settop(lstate, top); +#endif } /// like luaL_error, but allow cleanup diff --git a/src/nvim/lua/stdlib.h b/src/nvim/lua/stdlib.h index 17aec6714d..26e96055ae 100644 --- a/src/nvim/lua/stdlib.h +++ b/src/nvim/lua/stdlib.h @@ -1,10 +1,7 @@ -#ifndef NVIM_LUA_STDLIB_H -#define NVIM_LUA_STDLIB_H +#pragma once -#include <lua.h> +#include <lua.h> // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/stdlib.h.generated.h" #endif - -#endif // NVIM_LUA_STDLIB_H diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 56f4daed1a..008b3f2e95 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -1,11 +1,9 @@ -// 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 - // lua bindings for tree-sitter. // NB: this file mostly contains a generic lua interface for tree-sitter // trees and nodes, and could be broken out as a reusable lua package #include <assert.h> +#include <ctype.h> #include <lauxlib.h> #include <limits.h> #include <lua.h> @@ -14,6 +12,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <tree_sitter/api.h> #include <uv.h> #include "klib/kvec.h" @@ -21,14 +20,13 @@ #include "nvim/buffer_defs.h" #include "nvim/globals.h" #include "nvim/lua/treesitter.h" -#include "nvim/macros.h" -#include "nvim/map.h" +#include "nvim/macros_defs.h" +#include "nvim/map_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/strings.h" -#include "nvim/types.h" -#include "tree_sitter/api.h" +#include "nvim/types_defs.h" #define TS_META_PARSER "treesitter_parser" #define TS_META_TREE "treesitter_tree" @@ -43,6 +41,17 @@ typedef struct { int max_match_id; } TSLua_cursor; +typedef struct { + LuaRef cb; + lua_State *lstate; + bool lex; + bool parse; +} TSLuaLoggerOpts; + +typedef struct { + TSTree *tree; +} TSLuaTree; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/treesitter.c.generated.h" #endif @@ -51,8 +60,13 @@ static struct luaL_Reg parser_meta[] = { { "__gc", parser_gc }, { "__tostring", parser_tostring }, { "parse", parser_parse }, + { "reset", parser_reset }, { "set_included_ranges", parser_set_ranges }, { "included_ranges", parser_get_ranges }, + { "set_timeout", parser_set_timeout }, + { "timeout", parser_get_timeout }, + { "_set_logger", parser_set_logger }, + { "_logger", parser_get_logger }, { NULL, NULL } }; @@ -61,6 +75,7 @@ static struct luaL_Reg tree_meta[] = { { "__tostring", tree_tostring }, { "root", tree_root }, { "edit", tree_edit }, + { "included_ranges", tree_get_ranges }, { "copy", tree_copy }, { NULL, NULL } }; @@ -78,6 +93,8 @@ static struct luaL_Reg node_meta[] = { { "field", node_field }, { "named", node_named }, { "missing", node_missing }, + { "extra", node_extra }, + { "has_changes", node_has_changes }, { "has_error", node_has_error }, { "sexpr", node_sexpr }, { "child_count", node_child_count }, @@ -95,7 +112,9 @@ static struct luaL_Reg node_meta[] = { { "prev_named_sibling", node_prev_named_sibling }, { "named_children", node_named_children }, { "root", node_root }, + { "tree", node_tree }, { "byte_length", node_byte_length }, + { "equal", node_equal }, { NULL, NULL } }; @@ -132,9 +151,9 @@ static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) lua_pop(L, 1); // [] (don't use it now) } -/// init the tslua library +/// Init the tslua library. /// -/// all global state is stored in the regirstry of the lua_State +/// All global state is stored in the registry of the lua_State. void tslua_init(lua_State *L) { // type metatables @@ -145,15 +164,13 @@ void tslua_init(lua_State *L) build_meta(L, TS_META_QUERYCURSOR, querycursor_meta); build_meta(L, TS_META_TREECURSOR, treecursor_meta); -#ifdef NVIM_TS_HAS_SET_ALLOCATOR ts_set_allocator(xmalloc, xcalloc, xrealloc, xfree); -#endif } int tslua_has_language(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); - lua_pushboolean(L, pmap_has(cstr_t)(&langs, lang_name)); + lua_pushboolean(L, map_has(cstr_t, &langs, lang_name)); return 1; } @@ -170,7 +187,7 @@ int tslua_add_language(lua_State *L) symbol_name = luaL_checkstring(L, 3); } - if (pmap_has(cstr_t)(&langs, lang_name)) { + if (map_has(cstr_t, &langs, lang_name)) { lua_pushboolean(L, true); return 1; } @@ -223,11 +240,11 @@ int tslua_add_language(lua_State *L) int tslua_remove_lang(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); - bool present = pmap_has(cstr_t)(&langs, lang_name); + bool present = map_has(cstr_t, &langs, lang_name); if (present) { - char *key = (char *)pmap_key(cstr_t)(&langs, lang_name); - pmap_del(cstr_t)(&langs, lang_name); - xfree(key); + cstr_t key; + pmap_del(cstr_t)(&langs, lang_name, &key); + xfree((void *)key); } lua_pushboolean(L, present); return 1; @@ -309,6 +326,17 @@ static TSParser **parser_check(lua_State *L, uint16_t index) return luaL_checkudata(L, index, TS_META_PARSER); } +static void logger_gc(TSLogger logger) +{ + if (!logger.log) { + return; + } + + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; + luaL_unref(opts->lstate, LUA_REGISTRYINDEX, opts->cb); + xfree(opts); +} + static int parser_gc(lua_State *L) { TSParser **p = parser_check(L, 1); @@ -316,6 +344,7 @@ static int parser_gc(lua_State *L) return 0; } + logger_gc(ts_parser_logger(*p)); ts_parser_delete(*p); return 0; } @@ -329,7 +358,7 @@ static int parser_tostring(lua_State *L) static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position, uint32_t *bytes_read) { - buf_T *bp = payload; + buf_T *bp = payload; #define BUFSIZE 256 static char buf[BUFSIZE]; @@ -337,7 +366,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position *bytes_read = 0; return ""; } - char *line = ml_get_buf(bp, (linenr_T)position.row + 1, false); + char *line = ml_get_buf(bp, (linenr_T)position.row + 1); size_t len = strlen(line); if (position.column > len) { *bytes_read = 0; @@ -359,19 +388,29 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position #undef BUFSIZE } -static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length) +static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length, + bool include_bytes) { lua_createtable(L, (int)length, 0); for (size_t i = 0; i < length; i++) { - lua_createtable(L, 4, 0); + lua_createtable(L, include_bytes ? 6 : 4, 0); + int j = 1; lua_pushinteger(L, ranges[i].start_point.row); - lua_rawseti(L, -2, 1); + lua_rawseti(L, -2, j++); lua_pushinteger(L, ranges[i].start_point.column); - lua_rawseti(L, -2, 2); + lua_rawseti(L, -2, j++); + if (include_bytes) { + lua_pushinteger(L, ranges[i].start_byte); + lua_rawseti(L, -2, j++); + } lua_pushinteger(L, ranges[i].end_point.row); - lua_rawseti(L, -2, 3); + lua_rawseti(L, -2, j++); lua_pushinteger(L, ranges[i].end_point.column); - lua_rawseti(L, -2, 4); + lua_rawseti(L, -2, j++); + if (include_bytes) { + lua_pushinteger(L, ranges[i].end_byte); + lua_rawseti(L, -2, j++); + } lua_rawseti(L, -2, (int)(i + 1)); } @@ -386,14 +425,14 @@ static int parser_parse(lua_State *L) TSTree *old_tree = NULL; if (!lua_isnil(L, 2)) { - TSTree **tmp = tree_check(L, 2); - old_tree = tmp ? *tmp : NULL; + TSLuaTree *ud = tree_check(L, 2); + old_tree = ud ? ud->tree : NULL; } TSTree *new_tree = NULL; size_t len; const char *str; - long bufnr; + handle_T bufnr; buf_T *buf; TSInput input; @@ -406,13 +445,13 @@ static int parser_parse(lua_State *L) break; case LUA_TNUMBER: - bufnr = lua_tointeger(L, 3); - buf = handle_get_buffer((handle_T)bufnr); + bufnr = (handle_T)lua_tointeger(L, 3); + buf = handle_get_buffer(bufnr); if (!buf) { #define BUFSIZE 256 char ebuf[BUFSIZE] = { 0 }; - vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %ld", bufnr); + vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %d", bufnr); return luaL_argerror(L, 3, ebuf); #undef BUFSIZE } @@ -426,34 +465,46 @@ static int parser_parse(lua_State *L) return luaL_argerror(L, 3, "expected either string or buffer handle"); } + bool include_bytes = (lua_gettop(L) >= 4) && lua_toboolean(L, 4); + // Sometimes parsing fails (timeout, or wrong parser ABI) // In those case, just return an error. if (!new_tree) { return luaL_error(L, "An error occurred when parsing."); } - // The new tree will be pushed to the stack, without copy, ownership is now to - // the lua GC. - // Old tree is still owned by the lua GC. + // The new tree will be pushed to the stack, without copy, ownership is now to the lua GC. + // Old tree is owned by lua GC since before uint32_t n_ranges = 0; - TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL; + TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL; - push_tree(L, new_tree, false); // [tree] + push_tree(L, new_tree); // [tree] - push_ranges(L, changed, n_ranges); // [tree, ranges] + push_ranges(L, changed, n_ranges, include_bytes); // [tree, ranges] xfree(changed); return 2; } +static int parser_reset(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (p && *p) { + ts_parser_reset(*p); + } + + return 0; +} + static int tree_copy(lua_State *L) { - TSTree **tree = tree_check(L, 1); - if (!(*tree)) { + TSLuaTree *ud = tree_check(L, 1); + if (!ud) { return 0; } - push_tree(L, *tree, true); // [tree] + TSTree *copy = ts_tree_copy(ud->tree); + push_tree(L, copy); // [tree] return 1; } @@ -465,8 +516,8 @@ static int tree_edit(lua_State *L) return lua_error(L); } - TSTree **tree = tree_check(L, 1); - if (!(*tree)) { + TSLuaTree *ud = tree_check(L, 1); + if (!ud) { return 0; } @@ -480,11 +531,29 @@ static int tree_edit(lua_State *L) TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, start_point, old_end_point, new_end_point }; - ts_tree_edit(*tree, &edit); + ts_tree_edit(ud->tree, &edit); return 0; } +static int tree_get_ranges(lua_State *L) +{ + TSLuaTree *ud = tree_check(L, 1); + if (!ud) { + return 0; + } + + bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); + + uint32_t len; + TSRange *ranges = ts_tree_included_ranges(ud->tree, &len); + + push_ranges(L, ranges, len, include_bytes); + + xfree(ranges); + return 1; +} + // Use the top of the stack (without popping it) to create a TSRange, it can be // either a lua table or a TSNode static void range_from_lua(lua_State *L, TSRange *range) @@ -590,58 +659,159 @@ static int parser_get_ranges(lua_State *L) return 0; } + bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); + uint32_t len; const TSRange *ranges = ts_parser_included_ranges(*p, &len); - push_ranges(L, ranges, len); + push_ranges(L, ranges, len, include_bytes); + return 1; +} + +static int parser_set_timeout(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (!p) { + return 0; + } + + if (lua_gettop(L) < 2) { + luaL_error(L, "integer expected"); + } + + uint32_t timeout = (uint32_t)luaL_checkinteger(L, 2); + ts_parser_set_timeout_micros(*p, timeout); + return 0; +} + +static int parser_get_timeout(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (!p) { + return 0; + } + + lua_pushinteger(L, (lua_Integer)ts_parser_timeout_micros(*p)); + return 1; +} + +static void logger_cb(void *payload, TSLogType logtype, const char *s) +{ + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)payload; + if ((!opts->lex && logtype == TSLogTypeLex) + || (!opts->parse && logtype == TSLogTypeParse)) { + return; + } + + lua_State *lstate = opts->lstate; + + lua_rawgeti(lstate, LUA_REGISTRYINDEX, opts->cb); + lua_pushstring(lstate, logtype == TSLogTypeParse ? "parse" : "lex"); + lua_pushstring(lstate, s); + if (lua_pcall(lstate, 2, 0, 0)) { + luaL_error(lstate, "Error executing treesitter logger callback"); + } +} + +static int parser_set_logger(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (!p) { + return 0; + } + + if (!lua_isboolean(L, 2)) { + return luaL_argerror(L, 2, "boolean expected"); + } + + if (!lua_isboolean(L, 3)) { + return luaL_argerror(L, 3, "boolean expected"); + } + + if (!lua_isfunction(L, 4)) { + return luaL_argerror(L, 4, "function expected"); + } + + TSLuaLoggerOpts *opts = xmalloc(sizeof(TSLuaLoggerOpts)); + lua_pushvalue(L, 4); + LuaRef ref = luaL_ref(L, LUA_REGISTRYINDEX); + + *opts = (TSLuaLoggerOpts){ + .lex = lua_toboolean(L, 2), + .parse = lua_toboolean(L, 3), + .cb = ref, + .lstate = L + }; + + TSLogger logger = { + .payload = (void *)opts, + .log = logger_cb + }; + + ts_parser_set_logger(*p, logger); + return 0; +} + +static int parser_get_logger(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (!p) { + return 0; + } + + TSLogger logger = ts_parser_logger(*p); + if (logger.log) { + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; + lua_rawgeti(L, LUA_REGISTRYINDEX, opts->cb); + } else { + lua_pushnil(L); + } + return 1; } // Tree methods -/// push tree interface on lua stack. +/// Push tree interface on to the lua stack. /// -/// This makes a copy of the tree, so ownership of the argument is unaffected. -void push_tree(lua_State *L, TSTree *tree, bool do_copy) +/// The tree is not copied. Ownership of the tree is transferred from C to +/// Lua. If needed use ts_tree_copy() in the caller +static void push_tree(lua_State *L, TSTree *tree) { if (tree == NULL) { lua_pushnil(L); return; } - TSTree **ud = lua_newuserdata(L, sizeof(TSTree *)); // [udata] - if (do_copy) { - *ud = ts_tree_copy(tree); - } else { - *ud = tree; - } + TSLuaTree *ud = lua_newuserdata(L, sizeof(TSLuaTree)); // [udata] + + ud->tree = tree; lua_getfield(L, LUA_REGISTRYINDEX, TS_META_TREE); // [udata, meta] lua_setmetatable(L, -2); // [udata] - // table used for node wrappers to keep a reference to tree wrapper - // NB: in lua 5.3 the uservalue for the node could just be the tree, but - // in lua 5.1 the uservalue (fenv) must be a table. + // To prevent the tree from being garbage collected, create a reference to it + // in the fenv which will be passed to userdata nodes of the tree. + // Note: environments (fenvs) associated with userdata have no meaning in Lua + // and are only used to associate a table. lua_createtable(L, 1, 0); // [udata, reftable] lua_pushvalue(L, -2); // [udata, reftable, udata] lua_rawseti(L, -2, 1); // [udata, reftable] lua_setfenv(L, -2); // [udata] } -static TSTree **tree_check(lua_State *L, int index) +static TSLuaTree *tree_check(lua_State *L, int index) { - TSTree **ud = luaL_checkudata(L, index, TS_META_TREE); + TSLuaTree *ud = luaL_checkudata(L, index, TS_META_TREE); return ud; } static int tree_gc(lua_State *L) { - TSTree **tree = tree_check(L, 1); - if (!tree) { - return 0; + TSLuaTree *ud = tree_check(L, 1); + if (ud) { + ts_tree_delete(ud->tree); } - - ts_tree_delete(*tree); return 0; } @@ -653,20 +823,20 @@ static int tree_tostring(lua_State *L) static int tree_root(lua_State *L) { - TSTree **tree = tree_check(L, 1); - if (!tree) { + TSLuaTree *ud = tree_check(L, 1); + if (!ud) { return 0; } - TSNode root = ts_tree_root_node(*tree); + TSNode root = ts_tree_root_node(ud->tree); push_node(L, root, 1); return 1; } // Node methods -/// push node interface on lua stack +/// Push node interface on to the Lua stack /// -/// top of stack must either be the tree this node belongs to or another node +/// Top of stack must either be the tree this node belongs to or another node /// of the same tree! This value is not popped. Can only be called inside a /// cfunction with the tslua environment. static void push_node(lua_State *L, TSNode node, int uindex) @@ -680,6 +850,8 @@ static void push_node(lua_State *L, TSNode node, int uindex) *ud = node; lua_getfield(L, LUA_REGISTRYINDEX, TS_META_NODE); // [udata, meta] lua_setmetatable(L, -2); // [udata] + + // Copy the fenv which contains the nodes tree. lua_getfenv(L, uindex); // [udata, reftable] lua_setfenv(L, -2); // [udata] } @@ -740,12 +912,26 @@ static int node_range(lua_State *L) if (!node_check(L, 1, &node)) { return 0; } + + bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); + TSPoint start = ts_node_start_point(node); TSPoint end = ts_node_end_point(node); - lua_pushnumber(L, start.row); - lua_pushnumber(L, start.column); - lua_pushnumber(L, end.row); - lua_pushnumber(L, end.column); + + if (include_bytes) { + lua_pushinteger(L, start.row); + lua_pushinteger(L, start.column); + lua_pushinteger(L, ts_node_start_byte(node)); + lua_pushinteger(L, end.row); + lua_pushinteger(L, end.column); + lua_pushinteger(L, ts_node_end_byte(node)); + return 6; + } + + lua_pushinteger(L, start.row); + lua_pushinteger(L, start.column); + lua_pushinteger(L, end.row); + lua_pushinteger(L, end.column); return 4; } @@ -757,9 +943,9 @@ static int node_start(lua_State *L) } TSPoint start = ts_node_start_point(node); uint32_t start_byte = ts_node_start_byte(node); - lua_pushnumber(L, start.row); - lua_pushnumber(L, start.column); - lua_pushnumber(L, start_byte); + lua_pushinteger(L, start.row); + lua_pushinteger(L, start.column); + lua_pushinteger(L, start_byte); return 3; } @@ -771,9 +957,9 @@ static int node_end(lua_State *L) } TSPoint end = ts_node_end_point(node); uint32_t end_byte = ts_node_end_byte(node); - lua_pushnumber(L, end.row); - lua_pushnumber(L, end.column); - lua_pushnumber(L, end_byte); + lua_pushinteger(L, end.row); + lua_pushinteger(L, end.column); + lua_pushinteger(L, end_byte); return 3; } @@ -784,7 +970,7 @@ static int node_child_count(lua_State *L) return 0; } uint32_t count = ts_node_child_count(node); - lua_pushnumber(L, count); + lua_pushinteger(L, count); return 1; } @@ -795,7 +981,7 @@ static int node_named_child_count(lua_State *L) return 0; } uint32_t count = ts_node_named_child_count(node); - lua_pushnumber(L, count); + lua_pushinteger(L, count); return 1; } @@ -816,7 +1002,7 @@ static int node_symbol(lua_State *L) return 0; } TSSymbol symbol = ts_node_symbol(node); - lua_pushnumber(L, symbol); + lua_pushinteger(L, symbol); return 1; } @@ -882,6 +1068,26 @@ static int node_missing(lua_State *L) return 1; } +static int node_extra(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + lua_pushboolean(L, ts_node_is_extra(node)); + return 1; +} + +static int node_has_changes(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + lua_pushboolean(L, ts_node_has_changes(node)); + return 1; +} + static int node_has_error(lua_State *L) { TSNode node; @@ -898,8 +1104,8 @@ static int node_child(lua_State *L) if (!node_check(L, 1, &node)) { return 0; } - long num = lua_tointeger(L, 2); - TSNode child = ts_node_child(node, (uint32_t)num); + uint32_t num = (uint32_t)lua_tointeger(L, 2); + TSNode child = ts_node_child(node, num); push_node(L, child, 1); return 1; @@ -911,8 +1117,8 @@ static int node_named_child(lua_State *L) if (!node_check(L, 1, &node)) { return 0; } - long num = lua_tointeger(L, 2); - TSNode child = ts_node_named_child(node, (uint32_t)num); + uint32_t num = (uint32_t)lua_tointeger(L, 2); + TSNode child = ts_node_named_child(node, num); push_node(L, child, 1); return 1; @@ -1108,6 +1314,19 @@ static int node_root(lua_State *L) return 1; } +static int node_tree(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + + lua_getfenv(L, 1); // [udata, reftable] + lua_rawgeti(L, -1, 1); // [udata, reftable, tree_udata] + + return 1; +} + static int node_byte_length(lua_State *L) { TSNode node; @@ -1118,7 +1337,23 @@ static int node_byte_length(lua_State *L) uint32_t start_byte = ts_node_start_byte(node); uint32_t end_byte = ts_node_end_byte(node); - lua_pushnumber(L, end_byte - start_byte); + lua_pushinteger(L, end_byte - start_byte); + return 1; +} + +static int node_equal(lua_State *L) +{ + TSNode node1; + if (!node_check(L, 1, &node1)) { + return 0; + } + + TSNode node2; + if (!node_check(L, 2, &node2)) { + return luaL_error(L, "TSNode expected"); + } + + lua_pushboolean(L, ts_node_eq(node1, node2)); return 1; } @@ -1213,11 +1448,12 @@ static int node_rawquery(lua_State *L) } else { cursor = ts_query_cursor_new(); } - // TODO(clason): API introduced after tree-sitter release 0.19.5 - // remove guard when minimum ts version is bumped to 0.19.6+ -#ifdef NVIM_TS_HAS_SET_MATCH_LIMIT - ts_query_cursor_set_match_limit(cursor, 64); + +#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH + // reset the start depth + ts_query_cursor_set_max_start_depth(cursor, UINT32_MAX); #endif + ts_query_cursor_set_match_limit(cursor, 256); ts_query_cursor_exec(cursor, query, node); bool captures = lua_toboolean(L, 3); @@ -1228,6 +1464,29 @@ static int node_rawquery(lua_State *L) ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); } + if (lua_gettop(L) >= 6 && !lua_isnil(L, 6)) { + if (!lua_istable(L, 6)) { + return luaL_error(L, "table expected"); + } + lua_pushnil(L); + // stack: [dict, ..., nil] + while (lua_next(L, 6)) { + // stack: [dict, ..., key, value] + if (lua_type(L, -2) == LUA_TSTRING) { + char *k = (char *)lua_tostring(L, -2); + if (strequal("max_start_depth", k)) { + // TODO(lewis6991): remove ifdef when min TS version is 0.20.9 +#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH + uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1); + ts_query_cursor_set_max_start_depth(cursor, max_start_depth); +#endif + } + } + lua_pop(L, 1); // pop the value; lua_next will pop the key. + // stack: [dict, ..., key] + } + } + TSLua_cursor *ud = lua_newuserdata(L, sizeof(*ud)); // [udata] ud->cursor = cursor; ud->predicated_match = -1; @@ -1281,8 +1540,9 @@ int tslua_parse_query(lua_State *L) TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type); if (!query) { - return luaL_error(L, "query: %s at position %d for language %s", - query_err_string(error_type), (int)error_offset, lang_name); + char err_msg[IOSIZE]; + query_err_string(src, (int)error_offset, error_type, err_msg, sizeof(err_msg)); + return luaL_error(L, "%s", err_msg); } TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata] @@ -1292,24 +1552,79 @@ int tslua_parse_query(lua_State *L) return 1; } -static const char *query_err_string(TSQueryError err) +static const char *query_err_to_string(TSQueryError error_type) { - switch (err) { + switch (error_type) { case TSQueryErrorSyntax: - return "invalid syntax"; + return "Invalid syntax:\n"; case TSQueryErrorNodeType: - return "invalid node type"; + return "Invalid node type "; case TSQueryErrorField: - return "invalid field"; + return "Invalid field name "; case TSQueryErrorCapture: - return "invalid capture"; + return "Invalid capture name "; case TSQueryErrorStructure: - return "invalid structure"; + return "Impossible pattern:\n"; default: return "error"; } } +static void query_err_string(const char *src, int error_offset, TSQueryError error_type, char *err, + size_t errlen) +{ + int line_start = 0; + int row = 0; + const char *error_line = NULL; + int error_line_len = 0; + + const char *end_str; + do { + const char *src_tmp = src + line_start; + end_str = strchr(src_tmp, '\n'); + int line_length = end_str != NULL ? (int)(end_str - src_tmp) : (int)strlen(src_tmp); + int line_end = line_start + line_length; + if (line_end > error_offset) { + error_line = src_tmp; + error_line_len = line_length; + break; + } + line_start = line_end + 1; + row++; + } while (end_str != NULL); + + int column = error_offset - line_start; + + const char *type_msg = query_err_to_string(error_type); + snprintf(err, errlen, "Query error at %d:%d. %s", row + 1, column + 1, type_msg); + size_t offset = strlen(err); + errlen = errlen - offset; + err = err + offset; + + // Error types that report names + if (error_type == TSQueryErrorNodeType + || error_type == TSQueryErrorField + || error_type == TSQueryErrorCapture) { + const char *suffix = src + error_offset; + int suffix_len = 0; + char c = suffix[suffix_len]; + while (isalnum(c) || c == '_' || c == '-' || c == '.') { + c = suffix[++suffix_len]; + } + snprintf(err, errlen, "\"%.*s\":\n", suffix_len, suffix); + offset = strlen(err); + errlen = errlen - offset; + err = err + offset; + } + + if (!error_line) { + snprintf(err, errlen, "Unexpected EOF\n"); + return; + } + + snprintf(err, errlen, "%.*s\n%*s^\n", error_line_len, error_line, column, ""); +} + static TSQuery *query_check(lua_State *L, int index) { TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY); @@ -1367,7 +1682,7 @@ static int query_inspect(lua_State *L) &strlen); lua_pushlstring(L, str, strlen); // [retval, patterns, pat, pred, item] } else if (step[k].type == TSQueryPredicateStepTypeCapture) { - lua_pushnumber(L, step[k].value_id + 1); // [..., pat, pred, item] + lua_pushinteger(L, step[k].value_id + 1); // [..., pat, pred, item] } else { abort(); } diff --git a/src/nvim/lua/treesitter.h b/src/nvim/lua/treesitter.h index b69fb9dfae..4ef9a10602 100644 --- a/src/nvim/lua/treesitter.h +++ b/src/nvim/lua/treesitter.h @@ -1,14 +1,7 @@ -#ifndef NVIM_LUA_TREESITTER_H -#define NVIM_LUA_TREESITTER_H +#pragma once -#include <lauxlib.h> -#include <lua.h> -#include <lualib.h> - -#include "tree_sitter/api.h" +#include <lua.h> // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/treesitter.h.generated.h" #endif - -#endif // NVIM_LUA_TREESITTER_H diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 857b159af5..16c3aa5e11 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -1,9 +1,7 @@ -// 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 <lauxlib.h> #include <lua.h> #include <stdbool.h> +#include <stdint.h> #include <string.h> #include "luaconf.h" @@ -13,9 +11,9 @@ #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/lua/xdiff.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/memory.h" -#include "nvim/vim.h" +#include "nvim/pos_defs.h" #include "xdiff/xdiff.h" #define COMPARED_BUFFER0 (1 << 0) @@ -32,7 +30,7 @@ typedef struct { Error *err; mmfile_t *ma; mmfile_t *mb; - bool linematch; + int64_t linematch; bool iwhite; } hunkpriv_t; @@ -63,25 +61,25 @@ static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long sta lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1); } -static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, long start_a, - long count_a, long start_b, long count_b, bool iwhite) +static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, int start_a, + int count_a, int start_b, int count_b, bool iwhite) { // get the pointer to char of the start of the diff to pass it to linematch algorithm const char *diff_begin[2] = { ma->ptr, mb->ptr }; - int diff_length[2] = { (int)count_a, (int)count_b }; + int diff_length[2] = { count_a, count_b }; - fastforward_buf_to_lnum(&diff_begin[0], start_a + 1); - fastforward_buf_to_lnum(&diff_begin[1], start_b + 1); + fastforward_buf_to_lnum(&diff_begin[0], (linenr_T)start_a + 1); + fastforward_buf_to_lnum(&diff_begin[1], (linenr_T)start_b + 1); int *decisions = NULL; size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite); - long lnuma = start_a, lnumb = start_b; + int lnuma = start_a, lnumb = start_b; - long hunkstarta = lnuma; - long hunkstartb = lnumb; - long hunkcounta = 0; - long hunkcountb = 0; + int hunkstarta = lnuma; + int hunkstartb = lnumb; + int hunkcounta = 0; + int hunkcountb = 0; for (size_t i = 0; i < decisions_length; i++) { if (i && (decisions[i - 1] != decisions[i])) { lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb); @@ -109,8 +107,8 @@ static int write_string(void *priv, mmbuffer_t *mb, int nbuf) { luaL_Buffer *buf = (luaL_Buffer *)priv; for (int i = 0; i < nbuf; i++) { - const long size = mb[i].size; - for (long total = 0; total < size; total += LUAL_BUFFERSIZE) { + const int size = mb[i].size; + for (int total = 0; total < size; total += LUAL_BUFFERSIZE) { const int tocopy = MIN((int)(size - total), LUAL_BUFFERSIZE); char *p = luaL_prepbuffer(buf); if (!p) { @@ -124,11 +122,11 @@ static int write_string(void *priv, mmbuffer_t *mb, int nbuf) } // hunk_func callback used when opts.hunk_lines = true -static int hunk_locations_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data) +static int hunk_locations_cb(int start_a, int count_a, int start_b, int count_b, void *cb_data) { hunkpriv_t *priv = (hunkpriv_t *)cb_data; lua_State *lstate = priv->lstate; - if (priv->linematch) { + if (priv->linematch > 0 && count_a + count_b <= priv->linematch) { get_linematch_results(lstate, priv->ma, priv->mb, start_a, count_a, start_b, count_b, priv->iwhite); } else { @@ -139,7 +137,7 @@ static int hunk_locations_cb(long start_a, long count_a, long start_b, long coun } // hunk_func callback used when opts.on_hunk is given -static int call_on_hunk_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data) +static int call_on_hunk_cb(int start_a, int count_a, int start_b, int count_b, void *cb_data) { // Mimic extra offsets done by xdiff, see: // src/xdiff/xemit.c:284 @@ -193,11 +191,11 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char * { if (actType != expType) { const char *type_str = - expType == kObjectTypeString ? "string" : - expType == kObjectTypeInteger ? "integer" : - expType == kObjectTypeBoolean ? "boolean" : - expType == kObjectTypeLuaRef ? "function" : - "NA"; + expType == kObjectTypeString + ? "string" : (expType == kObjectTypeInteger + ? "integer" : (expType == kObjectTypeBoolean + ? "boolean" : (expType == kObjectTypeLuaRef + ? "function" : "NA"))); api_set_error(err, kErrorTypeValidation, "%s is not a %s", name, type_str); @@ -208,7 +206,7 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char * } static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params, - bool *linematch, Error *err) + int64_t *linematch, Error *err) { const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err); @@ -257,16 +255,20 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, if (check_xdiff_opt(v->type, kObjectTypeInteger, "ctxlen", err)) { goto exit_1; } - cfg->ctxlen = v->data.integer; + cfg->ctxlen = (long)v->data.integer; } else if (strequal("interhunkctxlen", k.data)) { if (check_xdiff_opt(v->type, kObjectTypeInteger, "interhunkctxlen", err)) { goto exit_1; } - cfg->interhunkctxlen = v->data.integer; + cfg->interhunkctxlen = (long)v->data.integer; } else if (strequal("linematch", k.data)) { - *linematch = api_object_to_bool(*v, "linematch", false, err); - if (ERROR_SET(err)) { + if (v->type == kObjectTypeBoolean) { + *linematch = v->data.boolean ? INT64_MAX : 0; + } else if (v->type == kObjectTypeInteger) { + *linematch = v->data.integer; + } else { + api_set_error(err, kErrorTypeValidation, "linematch must be a boolean or integer"); goto exit_1; } } else { @@ -330,7 +332,7 @@ int nlua_xdl_diff(lua_State *lstate) xdemitconf_t cfg; xpparam_t params; xdemitcb_t ecb; - bool linematch = false; + int64_t linematch = 0; CLEAR_FIELD(cfg); CLEAR_FIELD(params); @@ -362,18 +364,18 @@ int nlua_xdl_diff(lua_State *lstate) cfg.hunk_func = call_on_hunk_cb; priv = (hunkpriv_t) { .lstate = lstate, - .err = &err, + .err = &err, }; ecb.priv = &priv; break; case kNluaXdiffModeLocations: cfg.hunk_func = hunk_locations_cb; priv = (hunkpriv_t) { - .lstate = lstate, - .ma = &ma, - .mb = &mb, + .lstate = lstate, + .ma = &ma, + .mb = &mb, .linematch = linematch, - .iwhite = (params.flags & XDF_IGNORE_WHITESPACE) > 0 + .iwhite = (params.flags & XDF_IGNORE_WHITESPACE) > 0 }; ecb.priv = &priv; lua_createtable(lstate, 0, 0); diff --git a/src/nvim/lua/xdiff.h b/src/nvim/lua/xdiff.h index b172d2f922..2ea74a79e8 100644 --- a/src/nvim/lua/xdiff.h +++ b/src/nvim/lua/xdiff.h @@ -1,12 +1,7 @@ -#ifndef NVIM_LUA_XDIFF_H -#define NVIM_LUA_XDIFF_H +#pragma once -#include <lauxlib.h> -#include <lua.h> -#include <lualib.h> +#include <lua.h> // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/xdiff.h.generated.h" #endif - -#endif // NVIM_LUA_XDIFF_H |