aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/lua')
-rw-r--r--src/nvim/lua/converter.c31
-rw-r--r--src/nvim/lua/converter.h8
-rw-r--r--src/nvim/lua/executor.c409
-rw-r--r--src/nvim/lua/executor.h4
-rw-r--r--src/nvim/lua/spell.c18
-rw-r--r--src/nvim/lua/stdlib.c133
-rw-r--r--src/nvim/lua/treesitter.c122
-rw-r--r--src/nvim/lua/xdiff.c147
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, &params, &err);
+ mode = process_xdl_diff_opts(lstate, &cfg, &params, &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);