diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/lua | |
parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
download | rneovim-usermarks.tar.gz rneovim-usermarks.tar.bz2 rneovim-usermarks.zip |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/lua')
-rw-r--r-- | src/nvim/lua/converter.c | 31 | ||||
-rw-r--r-- | src/nvim/lua/converter.h | 8 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 409 | ||||
-rw-r--r-- | src/nvim/lua/executor.h | 4 | ||||
-rw-r--r-- | src/nvim/lua/spell.c | 18 | ||||
-rw-r--r-- | src/nvim/lua/stdlib.c | 133 | ||||
-rw-r--r-- | src/nvim/lua/treesitter.c | 122 | ||||
-rw-r--r-- | src/nvim/lua/xdiff.c | 147 |
8 files changed, 653 insertions, 219 deletions
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 49d49f76b9..6160b84485 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -4,27 +4,31 @@ #include <assert.h> #include <lauxlib.h> #include <lua.h> -#include <lualib.h> #include <stdbool.h> +#include <stddef.h> #include <stdint.h> +#include <stdlib.h> +#include <string.h> #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/assert.h" -#include "nvim/func_attr.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/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/globals.h" -#include "nvim/lib/kvec.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/message.h" +#include "nvim/types.h" #include "nvim/vim.h" /// Determine, which keys lua table contains @@ -385,15 +389,12 @@ nlua_pop_typval_table_processing_end: break; } case LUA_TFUNCTION: { - LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); - state->lua_callable.func_ref = nlua_ref_global(lstate, -1); + LuaRef func = nlua_ref_global(lstate, -1); - char_u *name = register_cfunc(&nlua_CFunction_func_call, - &nlua_CFunction_func_free, - state); + char *name = register_luafunc(func); cur.tv->v_type = VAR_FUNC; - cur.tv->vval.v_string = (char *)vim_strsave(name); + cur.tv->vval.v_string = xstrdup(name); break; } case LUA_TUSERDATA: { @@ -476,8 +477,8 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ ufunc_T *fp = find_func(fun); \ - if (fp != NULL && fp->uf_cb == nlua_CFunction_func_call) { \ - nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ + if (fp != NULL && fp->uf_flags & FC_LUAREF) { \ + nlua_pushref(lstate, fp->uf_luaref); \ } else { \ TYPVAL_ENCODE_CONV_NIL(tv); \ } \ @@ -568,6 +569,7 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const #define TYPVAL_ENCODE_FIRST_ARG_NAME lstate #include "nvim/eval/typval_encode.c.h" + #undef TYPVAL_ENCODE_SCOPE #undef TYPVAL_ENCODE_NAME #undef TYPVAL_ENCODE_FIRST_ARG_TYPE @@ -909,9 +911,8 @@ Float nlua_pop_Float(lua_State *lstate, Error *err) lua_pop(lstate, 1); if (table_props.type != kObjectTypeFloat) { return 0; - } else { - return (Float)table_props.val; } + return (Float)table_props.val; } /// Convert lua table to array without determining whether it is array diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index f6a85900ba..ddc0acfbfa 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -9,14 +9,6 @@ #include "nvim/eval/typval.h" #include "nvim/func_attr.h" -typedef struct { - LuaRef func_ref; -} LuaCallable; - -typedef struct { - LuaCallable lua_callable; -} LuaCFunctionState; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/converter.h.generated.h" #endif diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index d1d1480696..5ffd90fddd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1,17 +1,22 @@ // 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 <string.h> #include <tree_sitter/api.h> +#include <uv.h> +#include "klib/kvec.h" #include "luv/luv.h" +#include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" #include "nvim/ascii.h" -#include "nvim/assert.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/cursor.h" @@ -19,27 +24,39 @@ #include "nvim/eval.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/time.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" -#include "nvim/extmark.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/keycodes.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/lua/stdlib.h" #include "nvim/lua/treesitter.h" #include "nvim/macros.h" -#include "nvim/map.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/os/fileio.h" #include "nvim/os/os.h" +#include "nvim/path.h" +#include "nvim/pos.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" @@ -188,8 +205,8 @@ 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() - mch_errmsg(e_outofmem); - mch_errmsg("\n"); + os_errmsg(e_outofmem); + os_errmsg("\n"); preserve_exit(); } const char *error = lua_tostring(lstate, -1); @@ -241,10 +258,10 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) { // Terminate this thread, as the main thread may be able to continue // execution. - mch_errmsg(e_outofmem); - mch_errmsg("\n"); + os_errmsg(e_outofmem); + os_errmsg("\n"); lua_close(lstate); -#ifdef WIN32 +#ifdef MSWIN ExitThread(0); #else pthread_exit(0); @@ -306,6 +323,36 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) return 1; } +/// Copies args starting at `lua_arg0` to Lua `_G.arg`, and sets `_G.arg[0]` to the scriptname. +/// +/// Example (arg[0] => "foo.lua", arg[1] => "--arg1", …): +/// nvim -l foo.lua --arg1 --arg2 +/// +/// @note Lua CLI sets args before "-e" as _negative_ `_G.arg` indices, but we currently don't. +/// +/// @see https://www.lua.org/pil/1.4.html +/// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 +/// +/// @returns number of args +static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg0) +{ + int i = 0; + lua_newtable(L); // _G.arg + + if (lua_arg0 > 0) { + 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++) { + lua_pushstring(L, argv[i + lua_arg0]); + lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "--foo" + } + } + + lua_setglobal(L, "arg"); + return i; +} + static void nlua_schedule_event(void **argv) { LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; @@ -396,7 +443,7 @@ static int nlua_wait(lua_State *lstate) bool fast_only = false; if (lua_top >= 4) { - fast_only = lua_toboolean(lstate, 4); + fast_only = lua_toboolean(lstate, 4); } MultiQueue *loop_events = fast_only || in_fast_callback > 0 @@ -581,14 +628,79 @@ static bool nlua_init_packages(lua_State *lstate) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - mch_errmsg(lua_tostring(lstate, -1)); - mch_errmsg("\n"); + os_errmsg((char *)lua_tostring(lstate, -1)); + os_errmsg("\n"); return false; } return true; } +/// "vim.ui_attach(ns_id, {ext_foo=true}, cb)" function +static int nlua_ui_attach(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + uint32_t ns_id = (uint32_t)luaL_checkinteger(lstate, 1); + + if (!ns_initialized(ns_id)) { + return luaL_error(lstate, "invalid ns_id"); + } + if (!lua_istable(lstate, 2)) { + return luaL_error(lstate, "ext_widgets must be a table"); + } + if (!lua_isfunction(lstate, 3)) { + return luaL_error(lstate, "callback must be a Lua function"); + } + + bool ext_widgets[kUIGlobalCount] = { false }; + bool tbl_has_true_val = false; + + lua_pushvalue(lstate, 2); + lua_pushnil(lstate); + while (lua_next(lstate, -2)) { + // [dict, key, val] + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + bool val = lua_toboolean(lstate, -1); + + for (size_t i = 0; i < kUIGlobalCount; i++) { + if (strequal(s, ui_ext_names[i])) { + if (val) { + tbl_has_true_val = true; + } + ext_widgets[i] = val; + goto ok; + } + } + + return luaL_error(lstate, "Unexpected key: %s", s); +ok: + lua_pop(lstate, 1); + } + + if (!tbl_has_true_val) { + return luaL_error(lstate, "ext_widgets table must contain at least one 'true' value"); + } + + LuaRef ui_event_cb = nlua_ref_global(lstate, 3); + ui_add_cb(ns_id, ui_event_cb, ext_widgets); + return 0; +} + +/// "vim.ui_detach(ns_id)" function +static int nlua_ui_detach(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + uint32_t ns_id = (uint32_t)luaL_checkinteger(lstate, 1); + + if (!ns_initialized(ns_id)) { + return luaL_error(lstate, "invalid ns_id"); + } + + ui_remove_cb(ns_id); + return 0; +} + /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. @@ -604,7 +716,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "debug"); lua_pop(lstate, 1); -#ifdef WIN32 +#ifdef MSWIN // os.getenv lua_getglobal(lstate, "os"); lua_pushcfunction(lstate, &nlua_getenv); @@ -649,6 +761,14 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_wait); lua_setfield(lstate, -2, "wait"); + // ui_attach + lua_pushcfunction(lstate, &nlua_ui_attach); + lua_setfield(lstate, -2, "ui_attach"); + + // ui_detach + lua_pushcfunction(lstate, &nlua_ui_detach); + lua_setfield(lstate, -2, "ui_detach"); + nlua_common_vim_init(lstate, false); // patch require() (only for --startuptime) @@ -675,10 +795,8 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return true; } -/// Initialize global lua interpreter -/// -/// Crashes Nvim if initialization fails. -void nlua_init(void) +/// Initializes global Lua interpreter, or exits Nvim on failure. +void nlua_init(char **argv, int argc, int lua_arg0) { #ifdef NLUA_TRACK_REFS const char *env = os_getenv("NVIM_LUA_NOTRACK"); @@ -689,20 +807,19 @@ void nlua_init(void) lua_State *lstate = luaL_newstate(); if (lstate == NULL) { - mch_errmsg(_("E970: Failed to initialize lua interpreter\n")); + os_errmsg(_("E970: Failed to initialize lua interpreter\n")); os_exit(1); } luaL_openlibs(lstate); if (!nlua_state_init(lstate)) { - mch_errmsg(_("E970: Failed to initialize builtin lua modules\n")); + os_errmsg(_("E970: Failed to initialize builtin lua modules\n")); os_exit(1); } luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem); - global_lstate = lstate; - main_thread = uv_thread_self(); + nlua_init_argv(lstate, argv, argc, lua_arg0); } static lua_State *nlua_thread_acquire_vm(void) @@ -935,8 +1052,8 @@ static int nlua_require(lua_State *const lstate) time_push(&rel_time, &start_time); int status = lua_pcall(lstate, 1, 1, 0); if (status == 0) { - vim_snprintf((char *)IObuff, IOSIZE, "require('%s')", name); - time_msg((char *)IObuff, &start_time); + vim_snprintf(IObuff, IOSIZE, "require('%s')", name); + time_msg(IObuff, &start_time); } time_pop(rel_time); @@ -967,12 +1084,12 @@ static int nlua_debug(lua_State *lstate) if (input.v_type != VAR_STRING || input.vval.v_string == NULL || *input.vval.v_string == NUL - || STRCMP(input.vval.v_string, "cont") == 0) { + || strcmp(input.vval.v_string, "cont") == 0) { tv_clear(&input); return 0; } if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string, - STRLEN(input.vval.v_string), "=(debug command)")) { + strlen(input.vval.v_string), "=(debug command)")) { nlua_error(lstate, _("E5115: Error while loading debug string: %.*s")); } else if (nlua_pcall(lstate, 0, 0)) { nlua_error(lstate, _("E5116: Error while calling debug string: %.*s")); @@ -990,7 +1107,7 @@ int nlua_in_fast_event(lua_State *lstate) static bool viml_func_is_fast(const char *name) { - const EvalFuncDef *const fdef = find_internal_func((const char *)name); + const EvalFuncDef *const fdef = find_internal_func(name); if (fdef) { return fdef->fast; } @@ -1027,15 +1144,15 @@ int nlua_call(lua_State *lstate) // TODO(bfredl): this should be simplified in error handling refactor force_abort = false; suppress_errthrow = false; - current_exception = NULL; + did_throw = false; did_emsg = false; try_start(); typval_T rettv; funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = true; + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = true; // 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); @@ -1093,13 +1210,14 @@ static int nlua_rpc(lua_State *lstate, bool request) Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); if (!ERROR_SET(&err)) { nlua_push_Object(lstate, result, false); - arena_mem_free(res_mem, NULL); + arena_mem_free(res_mem); } } else { if (!rpc_send_event(chan_id, name, args)) { api_set_error(&err, kErrorTypeValidation, "Invalid channel: %" PRIu64, chan_id); } + api_free_array(args); // TODO(bfredl): no } check_err: @@ -1124,7 +1242,7 @@ static int nlua_empty_dict_tostring(lua_State *lstate) return 1; } -#ifdef WIN32 +#ifdef MSWIN /// os.getenv: override os.getenv to maintain coherency. #9681 /// /// uv_os_setenv uses SetEnvironmentVariableW which does not update _environ. @@ -1224,7 +1342,7 @@ void nlua_typval_eval(const String str, typval_T *const arg, typval_T *const ret const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str.size + 1; char *lcmd; if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; + lcmd = IObuff; } else { lcmd = xmalloc(lcmd_len); } @@ -1234,7 +1352,7 @@ void nlua_typval_eval(const String str, typval_T *const arg, typval_T *const ret #undef EVALHEADER nlua_typval_exec(lcmd, lcmd_len, "luaeval()", arg, 1, true, ret_tv); - if (lcmd != (char *)IObuff) { + if (lcmd != IObuff) { xfree(lcmd); } } @@ -1248,7 +1366,7 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg const size_t lcmd_len = sizeof(CALLHEADER) - 1 + len + sizeof(CALLSUFFIX) - 1; char *lcmd; if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; + lcmd = IObuff; } else { lcmd = xmalloc(lcmd_len); } @@ -1261,7 +1379,7 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg nlua_typval_exec(lcmd, lcmd_len, "v:lua", args, argcount, false, ret_tv); - if (lcmd != (char *)IObuff) { + if (lcmd != IObuff) { xfree(lcmd); } } @@ -1322,11 +1440,11 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) estack_push(ETYPE_SCRIPT, name, 0); garray_T ga; - char_u *line = NULL; + char *line = NULL; - ga_init(&ga, (int)sizeof(char_u *), 10); - while ((line = (char_u *)fgetline(0, cookie, 0, false)) != NULL) { - GA_APPEND(char_u *, &ga, line); + ga_init(&ga, (int)sizeof(char *), 10); + while ((line = fgetline(0, cookie, 0, false)) != NULL) { + GA_APPEND(char *, &ga, line); } char *code = ga_concat_strings_sep(&ga, "\n"); size_t len = strlen(code); @@ -1348,23 +1466,22 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) /// @param[in] argcount Count of typval arguments /// @param[in] argvars Typval Arguments /// @param[out] rettv The return value from the called function. -int typval_exec_lua_callable(lua_State *lstate, LuaCallable lua_cb, int argcount, typval_T *argvars, - typval_T *rettv) +int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typval_T *rettv) { - LuaRef cb = lua_cb.func_ref; + lua_State *lstate = global_lstate; - nlua_pushref(lstate, cb); + nlua_pushref(lstate, lua_cb); PUSH_ALL_TYPVALS(lstate, argvars, argcount, false); if (nlua_pcall(lstate, argcount, 1)) { nlua_print(lstate); - return ERROR_OTHER; + return FCERR_OTHER; } nlua_pop_typval(lstate, rettv); - return ERROR_NONE; + return FCERR_NONE; } /// Execute Lua string @@ -1422,9 +1539,10 @@ bool nlua_ref_is_function(LuaRef ref) /// @param name if non-NULL, sent to callback as first arg /// if NULL, only args are used /// @param retval if true, convert return value to Object -/// if false, discard return value +/// if false, only check if return value is truthy /// @param err Error details, if any (if NULL, errors are echoed) -/// @return Return value of function, if retval was set. Otherwise NIL. +/// @return Return value of function, if retval was set. Otherwise +/// BOOLEAN_OBJ(true) or NIL. Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err) { lua_State *const lstate = global_lstate; @@ -1438,7 +1556,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro nlua_push_Object(lstate, args.items[i], false); } - if (nlua_pcall(lstate, nargs, retval ? 1 : 0)) { + if (nlua_pcall(lstate, nargs, 1)) { // if err is passed, the caller will deal with the error. if (err) { size_t len; @@ -1458,7 +1576,10 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro } return nlua_pop_Object(lstate, false, err); } else { - return NIL; + bool value = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + + return value ? BOOLEAN_OBJ(true) : NIL; } } @@ -1524,7 +1645,7 @@ void ex_luado(exarg_T *const eap) + (sizeof(DOEND) - 1)); char *lcmd; if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; + lcmd = IObuff; } else { lcmd = xmalloc(lcmd_len + 1); } @@ -1561,7 +1682,7 @@ void ex_luado(exarg_T *const eap) break; } if (lua_isstring(lstate, -1)) { - size_t old_line_len = STRLEN(old_line); + 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); @@ -1578,7 +1699,7 @@ void ex_luado(exarg_T *const eap) } lua_pop(lstate, 1); check_cursor(); - update_screen(NOT_VALID); + redraw_curbuf_later(UPD_NOT_VALID); } /// Run lua file @@ -1592,21 +1713,51 @@ void ex_luafile(exarg_T *const eap) nlua_exec_file((const char *)eap->arg); } -/// execute lua code from a file. +/// Executes Lua code from a file or "-" (stdin). /// -/// Note: we call the lua global loadfile as opposed to calling luaL_loadfile -/// in case loadfile has been overridden in the users environment. +/// Calls the Lua `loadfile` global as opposed to `luaL_loadfile` in case `loadfile` was overridden +/// in the user environment. /// -/// @param path path of the file +/// @param path Path to the file, may be "-" (stdin) during startup. /// -/// @return true if everything ok, false if there was an error (echoed) +/// @return true on success, false on error (echoed) or user canceled (CTRL-c) while reading "-" +/// (stdin). bool nlua_exec_file(const char *path) FUNC_ATTR_NONNULL_ALL { lua_State *const lstate = global_lstate; + if (!strequal(path, "-")) { + lua_getglobal(lstate, "loadfile"); + lua_pushstring(lstate, path); + } else { + FileDescriptor *stdin_dup = file_open_stdin(); + + 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); + if (read_size < 0) { // Error. + return false; + } + if (read_size > 0) { + kv_concat_len(sb, IObuff, (size_t)read_size); + } + if (read_size < 64) { // EOF. + break; + } + } + kv_push(sb, NUL); + file_free(stdin_dup, false); - lua_getglobal(lstate, "loadfile"); - lua_pushstring(lstate, path); + lua_getglobal(lstate, "loadstring"); + lua_pushstring(lstate, sb.items); + kv_destroy(sb); + } if (nlua_pcall(lstate, 1, 2)) { nlua_error(lstate, _("E5111: Error calling lua: %.*s")); @@ -1662,6 +1813,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, tslua_has_language); lua_setfield(lstate, -2, "_ts_has_language"); + lua_pushcfunction(lstate, tslua_remove_lang); + lua_setfield(lstate, -2, "_ts_remove_language"); + lua_pushcfunction(lstate, tslua_inspect_lang); lua_setfield(lstate, -2, "_ts_inspect_language"); @@ -1675,7 +1829,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_ts_get_minimum_language_version"); } -int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results) +int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) { lua_State *const lstate = global_lstate; int ret = OK; @@ -1688,7 +1842,7 @@ int nlua_expand_pat(expand_T *xp, char_u *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, (const char *)pat, strlen(pat)); if (nlua_pcall(lstate, 1, 2) != 0) { nlua_error(lstate, @@ -1723,7 +1877,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results goto cleanup_array; } - GA_APPEND(char_u *, &result_array, (char_u *)string_to_cstr(v.data.string)); + GA_APPEND(char *, &result_array, string_to_cstr(v.data.string)); } xp->xp_pattern += prefix_len; @@ -1749,27 +1903,7 @@ static int nlua_is_thread(lua_State *lstate) return 1; } -// Required functions for lua c functions as VimL callbacks - -int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state) -{ - lua_State *const lstate = global_lstate; - LuaCFunctionState *funcstate = (LuaCFunctionState *)state; - - return typval_exec_lua_callable(lstate, funcstate->lua_callable, - argcount, argvars, rettv); -} - -void nlua_CFunction_func_free(void *state) -{ - lua_State *const lstate = global_lstate; - LuaCFunctionState *funcstate = (LuaCFunctionState *)state; - - nlua_unref_global(lstate, funcstate->lua_callable.func_ref); - xfree(funcstate); -} - -bool nlua_is_table_from_lua(typval_T *const arg) +bool nlua_is_table_from_lua(const typval_T *const arg) { if (arg->v_type == VAR_DICT) { return arg->vval.v_dict->lua_table_ref != LUA_NOREF; @@ -1780,7 +1914,7 @@ bool nlua_is_table_from_lua(typval_T *const arg) } } -char_u *nlua_register_table_as_callable(typval_T *const arg) +char *nlua_register_table_as_callable(const typval_T *const arg) { LuaRef table_ref = LUA_NOREF; if (arg->v_type == VAR_DICT) { @@ -1814,11 +1948,9 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) } lua_pop(lstate, 2); // [table] - LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); - state->lua_callable.func_ref = nlua_ref_global(lstate, -1); + LuaRef func = nlua_ref_global(lstate, -1); - char_u *name = register_cfunc(&nlua_CFunction_func_call, - &nlua_CFunction_func_free, state); + char *name = register_luafunc(func); lua_pop(lstate, 1); // [] assert(top == lua_gettop(lstate)); @@ -1828,8 +1960,8 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) void nlua_execute_on_key(int c) { - char_u buf[NUMBUFLEN]; - size_t buf_len = special_to_buf(c, mod_mask, false, buf); + char buf[NUMBUFLEN]; + size_t buf_len = special_to_buf(c, mod_mask, false, (char_u *)buf); lua_State *const lstate = global_lstate; @@ -1845,7 +1977,7 @@ void nlua_execute_on_key(int c) luaL_checktype(lstate, -1, LUA_TFUNCTION); // [ vim, vim._on_key, buf ] - lua_pushlstring(lstate, (const char *)buf, buf_len); + lua_pushlstring(lstate, buf, buf_len); int save_got_int = got_int; got_int = false; // avoid interrupts when the key typed is Ctrl-C @@ -1924,6 +2056,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) nlua_pushref(lstate, preview ? cmd->uc_preview_luaref : cmd->uc_luaref); lua_newtable(lstate); + lua_pushstring(lstate, cmd->uc_name); + lua_setfield(lstate, -2, "name"); + lua_pushboolean(lstate, eap->forceit == 1); lua_setfield(lstate, -2, "bang"); @@ -1940,7 +2075,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) // Split args by unescaped whitespace |<f-args>| (nargs dependent) if (cmd->uc_argt & EX_NOSPC) { - if ((cmd->uc_argt & EX_NEEDARG) || STRLEN(eap->arg)) { + if ((cmd->uc_argt & EX_NEEDARG) || strlen(eap->arg)) { // For commands where nargs is 1 or "?" and argument is passed, fargs = { args } lua_rawseti(lstate, -2, 1); } else { @@ -1950,7 +2085,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) } else if (eap->args == NULL) { // For commands with more than one possible argument, split if argument list isn't available. lua_pop(lstate, 1); // Pop the reference of opts.args - size_t length = STRLEN(eap->arg); + size_t length = strlen(eap->arg); size_t end = 0; size_t len = 0; int i = 1; @@ -1975,7 +2110,8 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) } lua_setfield(lstate, -2, "fargs"); - lua_pushstring(lstate, (const char *)&eap->regname); + char reg[2] = { (char)eap->regname, NUL }; + lua_pushstring(lstate, reg); lua_setfield(lstate, -2, "reg"); lua_pushinteger(lstate, eap->addr_count); @@ -1998,7 +2134,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) lua_newtable(lstate); // smods table - lua_pushinteger(lstate, cmdmod.cmod_tab); + lua_pushinteger(lstate, cmdmod.cmod_tab - 1); lua_setfield(lstate, -2, "tab"); lua_pushinteger(lstate, cmdmod.cmod_verbose - 1); @@ -2019,6 +2155,8 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) lua_pushboolean(lstate, cmdmod.cmod_split & WSP_VERT); lua_setfield(lstate, -2, "vertical"); + lua_pushboolean(lstate, cmdmod.cmod_split & WSP_HOR); + lua_setfield(lstate, -2, "horizontal"); lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_SILENT); lua_setfield(lstate, -2, "silent"); lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_ERRSILENT); @@ -2113,3 +2251,80 @@ plain: kv_printf(str, "<Lua %d>", ref); 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) +{ + 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; + } + + 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_settop(lstate, top); + return success; +} diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 78346fd81f..c6747833e5 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -3,13 +3,17 @@ #include <lauxlib.h> #include <lua.h> +#include <stdbool.h> #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/assert.h" #include "nvim/eval/typval.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/usercmd.h" // Generated by msgpack-gen.lua diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index 31a2b2d19f..d510d25e90 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.c @@ -1,15 +1,25 @@ // 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> #include <lua.h> - +#include <stdbool.h> +#include <stddef.h> + +#include "nvim/ascii.h" +#include "nvim/buffer_defs.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/highlight_defs.h" #include "nvim/lua/spell.h" +#include "nvim/message.h" #include "nvim/spell.h" -#include "nvim/vim.h" +#include "nvim/types.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "lua/spell.c.generated.h" +# include "lua/spell.c.generated.h" // IWYU pragma: export #endif int nlua_spell_check(lua_State *lstate) @@ -51,7 +61,7 @@ int nlua_spell_check(lua_State *lstate) while (*str != NUL) { attr = HLF_COUNT; - len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); + len = spell_check(curwin, (char *)str, &attr, &capcol, false); assert(len <= INT_MAX); if (attr != HLF_COUNT) { diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 5a82ae30b5..6ebca6d97e 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -1,62 +1,51 @@ // 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> -#include <lualib.h> - +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +#include "auto/config.h" #include "cjson/lua_cjson.h" -#include "luv/luv.h" #include "mpack/lmpack.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" #include "nvim/ascii.h" -#include "nvim/assert.h" #include "nvim/buffer_defs.h" -#include "nvim/change.h" -#include "nvim/cursor.h" #include "nvim/eval.h" -#include "nvim/eval/userfunc.h" -#include "nvim/event/loop.h" -#include "nvim/event/time.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_eval.h" -#include "nvim/ex_getln.h" -#include "nvim/extmark.h" -#include "nvim/func_attr.h" -#include "nvim/garray.h" -#include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/lua/converter.h" -#include "nvim/lua/executor.h" #include "nvim/lua/spell.h" #include "nvim/lua/stdlib.h" -#include "nvim/lua/treesitter.h" #include "nvim/lua/xdiff.h" -#include "nvim/macros.h" #include "nvim/map.h" +#include "nvim/mbyte.h" #include "nvim/memline.h" -#include "nvim/message.h" -#include "nvim/msgpack_rpc/channel.h" -#include "nvim/os/os.h" +#include "nvim/memory.h" +#include "nvim/pos.h" #include "nvim/regexp.h" -#include "nvim/regexp_defs.h" -#include "nvim/screen.h" #include "nvim/types.h" -#include "nvim/undo.h" -#include "nvim/version.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/stdlib.c.generated.h" #endif -static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str) +static int regex_match(lua_State *lstate, regprog_T **prog, char *str) { regmatch_T rm; rm.regprog = *prog; rm.rm_ic = false; - bool match = vim_regexec(&rm, (char *)str, 0); + bool match = vim_regexec(&rm, str, 0); *prog = rm.regprog; if (match) { @@ -71,7 +60,7 @@ static int regex_match_str(lua_State *lstate) { regprog_T **prog = regex_check(lstate); const char *str = luaL_checkstring(lstate, 2); - int nret = regex_match(lstate, prog, (char_u *)str); + int nret = regex_match(lstate, prog, (char *)str); if (!*prog) { return luaL_error(lstate, "regex: internal error"); @@ -111,14 +100,14 @@ static int regex_match_line(lua_State *lstate) return luaL_error(lstate, "invalid row"); } - char_u *line = ml_get_buf(buf, rownr + 1, false); - size_t len = STRLEN(line); + char *line = ml_get_buf(buf, rownr + 1, false); + size_t len = strlen(line); if (start < 0 || (size_t)start > len) { return luaL_error(lstate, "invalid start"); } - char_u save = NUL; + char save = NUL; if (end >= 0) { if ((size_t)end > len || end < start) { return luaL_error(lstate, "invalid end"); @@ -187,7 +176,7 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } size_t codepoints = 0, codeunits = 0; - mb_utflen((const char_u *)s1, (size_t)idx, &codepoints, &codeunits); + mb_utflen(s1, (size_t)idx, &codepoints, &codeunits); lua_pushinteger(lstate, (long)codepoints); lua_pushinteger(lstate, (long)codeunits); @@ -209,7 +198,7 @@ static int nlua_str_utf_pos(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t idx = 1; size_t clen; for (size_t i = 0; i < s1_len && s1[i] != NUL; i += clen) { - clen = (size_t)utf_ptr2len_len((const char_u *)(s1) + i, (int)(s1_len - i)); + clen = (size_t)utf_ptr2len_len(s1 + i, (int)(s1_len - i)); lua_pushinteger(lstate, (long)i + 1); lua_rawseti(lstate, -2, (int)idx); idx++; @@ -277,8 +266,7 @@ int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL use_utf16 = lua_toboolean(lstate, 3); } - ssize_t byteidx = mb_utf_index_to_bytes((const char_u *)s1, s1_len, - (size_t)idx, use_utf16); + ssize_t byteidx = mb_utf_index_to_bytes(s1, s1_len, (size_t)idx, use_utf16); if (byteidx == -1) { return luaL_error(lstate, "index out of range"); } @@ -304,8 +292,10 @@ int nlua_regex(lua_State *lstate) nlua_push_errstr(lstate, "couldn't parse regex: %s", err.msg); api_clear_error(&err); return lua_error(lstate); + } else if (prog == NULL) { + nlua_push_errstr(lstate, "couldn't parse regex"); + return lua_error(lstate); } - assert(prog); regprog_T **p = lua_newuserdata(lstate, sizeof(regprog_T *)); *p = prog; @@ -369,15 +359,21 @@ int nlua_setvar(lua_State *lstate) return 0; } + bool watched = tv_dict_is_watched(dict); + if (del) { // Delete the key if (di == NULL) { // Doesn't exist, nothing to do return 0; - } else { - // Delete the entry - tv_dict_item_remove(dict, di); } + // Notify watchers + if (watched) { + tv_dict_watcher_notify(dict, key.data, NULL, &di->di_tv); + } + + // Delete the entry + tv_dict_item_remove(dict, di); } else { // Update the key typval_T tv; @@ -388,17 +384,29 @@ int nlua_setvar(lua_State *lstate) return luaL_error(lstate, "Couldn't convert lua value"); } + typval_T oldtv = TV_INITIAL_VALUE; + if (di == NULL) { // Need to create an entry di = tv_dict_item_alloc_len(key.data, key.size); tv_dict_add(dict, di); } else { + if (watched) { + tv_copy(&di->di_tv, &oldtv); + } // Clear the old value tv_clear(&di->di_tv); } // Update the value tv_copy(&tv, &di->di_tv); + + // Notify watchers + if (watched) { + tv_dict_watcher_notify(dict, key.data, &tv, &oldtv); + tv_clear(&oldtv); + } + // Clear the temporary variable tv_clear(&tv); } @@ -474,6 +482,48 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 1; } +/// Convert string from one encoding to another +static int nlua_iconv(lua_State *lstate) +{ + int narg = lua_gettop(lstate); + + if (narg < 3) { + return luaL_error(lstate, "Expected at least 3 arguments"); + } + + for (int i = 1; i <= 3; i++) { + if (lua_type(lstate, i) != LUA_TSTRING) { + return luaL_argerror(lstate, i, "expected string"); + } + } + + size_t str_len = 0; + 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))); + + vimconv_T vimconv; + vimconv.vc_type = CONV_NONE; + convert_setup_ext(&vimconv, from, false, to, false); + + char *ret = string_convert(&vimconv, (char *)str, &str_len); + + convert_setup(&vimconv, NULL, NULL); + + xfree(from); + xfree(to); + + if (ret == NULL) { + lua_pushnil(lstate); + } else { + lua_pushlstring(lstate, ret, str_len); + xfree(ret); + } + + return 1; +} + void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) { if (!is_thread) { @@ -519,6 +569,11 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) // vim.spell luaopen_spell(lstate); lua_setfield(lstate, -2, "spell"); + + // vim.iconv + // depends on p_ambw, p_emoji + lua_pushcfunction(lstate, &nlua_iconv); + lua_setfield(lstate, -2, "iconv"); } // vim.mpack diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index f0d847e352..56f4daed1a 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -6,20 +6,28 @@ // trees and nodes, and could be broken out as a reusable lua package #include <assert.h> -#include <inttypes.h> #include <lauxlib.h> +#include <limits.h> #include <lua.h> -#include <lualib.h> #include <stdbool.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <uv.h> +#include "klib/kvec.h" #include "nvim/api/private/helpers.h" -#include "nvim/buffer.h" -#include "nvim/lib/kvec.h" +#include "nvim/buffer_defs.h" +#include "nvim/globals.h" #include "nvim/lua/treesitter.h" +#include "nvim/macros.h" +#include "nvim/map.h" #include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/pos.h" +#include "nvim/strings.h" +#include "nvim/types.h" #include "tree_sitter/api.h" #define TS_META_PARSER "treesitter_parser" @@ -85,6 +93,10 @@ static struct luaL_Reg node_meta[] = { { "prev_sibling", node_prev_sibling }, { "next_named_sibling", node_next_named_sibling }, { "prev_named_sibling", node_prev_named_sibling }, + { "named_children", node_named_children }, + { "root", node_root }, + { "byte_length", node_byte_length }, + { NULL, NULL } }; @@ -145,40 +157,50 @@ int tslua_has_language(lua_State *L) return 1; } +// Creates the language into the internal language map. +// +// Returns true if the language is correctly loaded in the language map int tslua_add_language(lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *lang_name = luaL_checkstring(L, 2); + const char *symbol_name = lang_name; + + if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) { + symbol_name = luaL_checkstring(L, 3); + } if (pmap_has(cstr_t)(&langs, lang_name)) { - return 0; + lua_pushboolean(L, true); + return 1; } #define BUFSIZE 128 char symbol_buf[BUFSIZE]; - snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", lang_name); + snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", symbol_name); #undef BUFSIZE uv_lib_t lib; if (uv_dlopen(path, &lib)) { - snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlopen: %s", - uv_dlerror(&lib)); + snprintf(IObuff, IOSIZE, "Failed to load parser for language '%s': uv_dlopen: %s", + lang_name, uv_dlerror(&lib)); uv_dlclose(&lib); - lua_pushstring(L, (char *)IObuff); + lua_pushstring(L, IObuff); return lua_error(L); } TSLanguage *(*lang_parser)(void); if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { - snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlsym: %s", + snprintf(IObuff, IOSIZE, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib)); uv_dlclose(&lib); - lua_pushstring(L, (char *)IObuff); + lua_pushstring(L, IObuff); return lua_error(L); } TSLanguage *lang = lang_parser(); if (lang == NULL) { + uv_dlclose(&lib); return luaL_error(L, "Failed to load parser %s: internal error", path); } @@ -198,6 +220,19 @@ int tslua_add_language(lua_State *L) return 1; } +int tslua_remove_lang(lua_State *L) +{ + const char *lang_name = luaL_checkstring(L, 1); + bool present = pmap_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); + } + lua_pushboolean(L, present); + return 1; +} + int tslua_inspect_lang(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); @@ -302,8 +337,8 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position *bytes_read = 0; return ""; } - char *line = (char *)ml_get_buf(bp, (linenr_T)position.row + 1, false); - size_t len = STRLEN(line); + char *line = ml_get_buf(bp, (linenr_T)position.row + 1, false); + size_t len = strlen(line); if (position.column > len) { *bytes_read = 0; return ""; @@ -593,7 +628,7 @@ void push_tree(lua_State *L, TSTree *tree, bool do_copy) lua_setfenv(L, -2); // [udata] } -static TSTree **tree_check(lua_State *L, uint16_t index) +static TSTree **tree_check(lua_State *L, int index) { TSTree **ud = luaL_checkudata(L, index, TS_META_TREE); return ud; @@ -804,7 +839,7 @@ static int node_field(lua_State *L) do { const char *current_field = ts_tree_cursor_current_field_name(&cursor); - if (current_field != NULL && !STRCMP(field_name, current_field)) { + if (current_field != NULL && !strcmp(field_name, current_field)) { push_node(L, ts_tree_cursor_current_node(&cursor), 1); // [table, node] lua_rawseti(L, -2, (int)++curr_index); } @@ -1036,6 +1071,57 @@ static int node_prev_named_sibling(lua_State *L) return 1; } +static int node_named_children(lua_State *L) +{ + TSNode source; + if (!node_check(L, 1, &source)) { + return 0; + } + TSTreeCursor cursor = ts_tree_cursor_new(source); + + lua_newtable(L); + int curr_index = 0; + + if (ts_tree_cursor_goto_first_child(&cursor)) { + do { + TSNode node = ts_tree_cursor_current_node(&cursor); + if (ts_node_is_named(node)) { + push_node(L, node, 1); + lua_rawseti(L, -2, ++curr_index); + } + } while (ts_tree_cursor_goto_next_sibling(&cursor)); + } + + ts_tree_cursor_delete(&cursor); + return 1; +} + +static int node_root(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + + TSNode root = ts_tree_root_node(node.tree); + push_node(L, root, 1); + return 1; +} + +static int node_byte_length(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + + 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); + return 1; +} + /// assumes the match table being on top of the stack static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { @@ -1195,8 +1281,8 @@ 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", - query_err_string(error_type), (int)error_offset); + return luaL_error(L, "query: %s at position %d for language %s", + query_err_string(error_type), (int)error_offset, lang_name); } TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata] @@ -1288,7 +1374,7 @@ static int query_inspect(lua_State *L) lua_rawseti(L, -2, nextitem++); // [retval, patterns, pat, pred] } // last predicate should have ended with TypeDone - lua_pop(L, 1); // [retval, patters, pat] + lua_pop(L, 1); // [retval, patterns, pat] lua_rawseti(L, -2, (int)i + 1); // [retval, patterns] } lua_setfield(L, -2, "patterns"); // [retval] diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index b2b5dfedee..857b159af5 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -1,21 +1,26 @@ // 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 <errno.h> #include <lauxlib.h> #include <lua.h> -#include <lualib.h> -#include <stdio.h> -#include <stdlib.h> +#include <stdbool.h> #include <string.h> +#include "luaconf.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/linematch.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/lua/xdiff.h" +#include "nvim/macros.h" +#include "nvim/memory.h" #include "nvim/vim.h" #include "xdiff/xdiff.h" +#define COMPARED_BUFFER0 (1 << 0) +#define COMPARED_BUFFER1 (1 << 1) + typedef enum { kNluaXdiffModeUnified = 0, kNluaXdiffModeOnHunkCB, @@ -25,12 +30,81 @@ typedef enum { typedef struct { lua_State *lstate; Error *err; + mmfile_t *ma; + mmfile_t *mb; + bool linematch; + bool iwhite; } hunkpriv_t; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/xdiff.c.generated.h" #endif +static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long start_b, long count_b) +{ + // Mimic extra offsets done by xdiff, see: + // src/xdiff/xemit.c:284 + // src/xdiff/xutils.c:(356,368) + if (count_a > 0) { + start_a += 1; + } + if (count_b > 0) { + start_b += 1; + } + lua_createtable(lstate, 0, 0); + lua_pushinteger(lstate, start_a); + lua_rawseti(lstate, -2, 1); + lua_pushinteger(lstate, count_a); + lua_rawseti(lstate, -2, 2); + lua_pushinteger(lstate, start_b); + lua_rawseti(lstate, -2, 3); + lua_pushinteger(lstate, count_b); + lua_rawseti(lstate, -2, 4); + 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) +{ + // 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 }; + + fastforward_buf_to_lnum(&diff_begin[0], start_a + 1); + fastforward_buf_to_lnum(&diff_begin[1], 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; + + long hunkstarta = lnuma; + long hunkstartb = lnumb; + long hunkcounta = 0; + long 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); + + hunkstarta = lnuma; + hunkstartb = lnumb; + hunkcounta = 0; + hunkcountb = 0; + // create a new hunk + } + if (decisions[i] & COMPARED_BUFFER0) { + lnuma++; + hunkcounta++; + } + if (decisions[i] & COMPARED_BUFFER1) { + lnumb++; + hunkcountb++; + } + } + lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb); + xfree(decisions); +} + static int write_string(void *priv, mmbuffer_t *mb, int nbuf) { luaL_Buffer *buf = (luaL_Buffer *)priv; @@ -52,30 +126,15 @@ 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) { - // Mimic extra offsets done by xdiff, see: - // src/xdiff/xemit.c:284 - // src/xdiff/xutils.c:(356,368) - if (count_a > 0) { - start_a += 1; - } - if (count_b > 0) { - start_b += 1; + hunkpriv_t *priv = (hunkpriv_t *)cb_data; + lua_State *lstate = priv->lstate; + if (priv->linematch) { + get_linematch_results(lstate, priv->ma, priv->mb, start_a, count_a, start_b, count_b, + priv->iwhite); + } else { + lua_pushhunk(lstate, start_a, count_a, start_b, count_b); } - lua_State *lstate = (lua_State *)cb_data; - lua_createtable(lstate, 0, 0); - - lua_pushinteger(lstate, start_a); - lua_rawseti(lstate, -2, 1); - lua_pushinteger(lstate, count_a); - lua_rawseti(lstate, -2, 2); - lua_pushinteger(lstate, start_b); - lua_rawseti(lstate, -2, 3); - lua_pushinteger(lstate, count_b); - lua_rawseti(lstate, -2, 4); - - lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1); - return 0; } @@ -149,7 +208,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, - Error *err) + bool *linematch, Error *err) { const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err); @@ -205,6 +264,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, goto exit_1; } cfg->interhunkctxlen = v->data.integer; + } else if (strequal("linematch", k.data)) { + *linematch = api_object_to_bool(*v, "linematch", false, err); + if (ERROR_SET(err)) { + goto exit_1; + } } else { struct { const char *name; @@ -244,10 +308,8 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, if (had_on_hunk) { mode = kNluaXdiffModeOnHunkCB; - cfg->hunk_func = call_on_hunk_cb; } else if (had_result_type_indices) { mode = kNluaXdiffModeLocations; - cfg->hunk_func = hunk_locations_cb; } exit_1: @@ -268,6 +330,7 @@ int nlua_xdl_diff(lua_State *lstate) xdemitconf_t cfg; xpparam_t params; xdemitcb_t ecb; + bool linematch = false; CLEAR_FIELD(cfg); CLEAR_FIELD(params); @@ -280,7 +343,7 @@ int nlua_xdl_diff(lua_State *lstate) return luaL_argerror(lstate, 3, "expected table"); } - mode = process_xdl_diff_opts(lstate, &cfg, ¶ms, &err); + mode = process_xdl_diff_opts(lstate, &cfg, ¶ms, &linematch, &err); if (ERROR_SET(&err)) { goto exit_0; @@ -288,7 +351,7 @@ int nlua_xdl_diff(lua_State *lstate) } luaL_Buffer buf; - hunkpriv_t *priv = NULL; + hunkpriv_t priv; switch (mode) { case kNluaXdiffModeUnified: luaL_buffinit(lstate, &buf); @@ -296,14 +359,24 @@ int nlua_xdl_diff(lua_State *lstate) ecb.out_line = write_string; break; case kNluaXdiffModeOnHunkCB: - priv = xmalloc(sizeof(*priv)); - priv->lstate = lstate; - priv->err = &err; - ecb.priv = priv; + cfg.hunk_func = call_on_hunk_cb; + priv = (hunkpriv_t) { + .lstate = lstate, + .err = &err, + }; + ecb.priv = &priv; break; case kNluaXdiffModeLocations: + cfg.hunk_func = hunk_locations_cb; + priv = (hunkpriv_t) { + .lstate = lstate, + .ma = &ma, + .mb = &mb, + .linematch = linematch, + .iwhite = (params.flags & XDF_IGNORE_WHITESPACE) > 0 + }; + ecb.priv = &priv; lua_createtable(lstate, 0, 0); - ecb.priv = lstate; break; } @@ -314,8 +387,6 @@ int nlua_xdl_diff(lua_State *lstate) } } - XFREE_CLEAR(priv); - exit_0: if (ERROR_SET(&err)) { luaL_where(lstate, 1); |