From 543e0256c19f397921a332e06b423215fd9aecb5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Nov 2023 15:51:05 +0800 Subject: build: don't define FUNC_ATTR_* as empty in headers (#26317) FUNC_ATTR_* should only be used in .c files with generated headers. Defining FUNC_ATTR_* as empty in headers causes misuses of them to be silently ignored. Instead don't define them by default, and only define them as empty after a .c file has included its generated header. --- src/nvim/lua/converter.c | 1 - src/nvim/lua/stdlib.c | 1 - 2 files changed, 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 4598d48c4a..fd2bdbd677 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -16,7 +16,6 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" -#include "nvim/func_attr.h" #include "nvim/gettext.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 33770b2e62..d7a7abe3c8 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -22,7 +22,6 @@ #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/lua/base64.h" #include "nvim/lua/converter.h" -- cgit From ce56e0a845d68862118f44cb66c5f080e3c1bbed Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Nov 2023 17:16:57 +0800 Subject: refactor(IWYU): move UI and LineFlags to ui_defs.h (#26318) --- src/nvim/lua/spell.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index c261c5105e..e6c38ea9cb 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.c @@ -15,10 +15,11 @@ #include "nvim/spell.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "lua/spell.c.generated.h" // IWYU pragma: export +# include "lua/spell.c.generated.h" #endif int nlua_spell_check(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL { if (lua_gettop(lstate) < 1) { return luaL_error(lstate, "Expected 1 argument"); @@ -99,6 +100,7 @@ static const luaL_Reg spell_functions[] = { }; int luaopen_spell(lua_State *L) + FUNC_ATTR_NONNULL_ALL { lua_newtable(L); luaL_register(L, NULL, spell_functions); -- cgit From 548f03c66c08d0235d62505e884e8088bfda1804 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 1 Dec 2023 15:22:22 +0800 Subject: refactor: change event_create() to a macro (#26343) A varargs functions can never be inlined, so a macro is faster. --- src/nvim/lua/executor.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 06d16efb05..d63a9a1307 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -208,7 +208,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags const char *error = lua_tostring(lstate, -1); multiqueue_put(main_loop.events, nlua_luv_error_event, - 2, xstrdup(error), (intptr_t)kCallback); + xstrdup(error), (void *)(intptr_t)kCallback); lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK @@ -266,11 +266,11 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres const char *error = lua_tostring(lstate, -1); loop_schedule_deferred(&main_loop, - event_create(nlua_luv_error_event, 2, + event_create(nlua_luv_error_event, xstrdup(error), - is_callback - ? (intptr_t)kThreadCallback - : (intptr_t)kThread)); + (void *)(intptr_t)(is_callback + ? kThreadCallback + : kThread))); lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK @@ -379,8 +379,7 @@ static int nlua_schedule(lua_State *const lstate) LuaRef cb = nlua_ref_global(lstate, 1); - multiqueue_put(main_loop.events, nlua_schedule_event, - 1, (void *)(ptrdiff_t)cb); + multiqueue_put(main_loop.events, nlua_schedule_event, (void *)(ptrdiff_t)cb); return 0; } @@ -1022,15 +1021,14 @@ static int nlua_print(lua_State *const lstate) if (is_thread) { loop_schedule_deferred(&main_loop, - event_create(nlua_print_event, 2, + event_create(nlua_print_event, msg_ga.ga_data, - (intptr_t)msg_ga.ga_len)); + (void *)(intptr_t)msg_ga.ga_len)); } else if (in_fast_callback) { multiqueue_put(main_loop.events, nlua_print_event, - 2, msg_ga.ga_data, (intptr_t)msg_ga.ga_len); + msg_ga.ga_data, (void *)(intptr_t)msg_ga.ga_len); } else { - nlua_print_event((void *[]){ msg_ga.ga_data, - (void *)(intptr_t)msg_ga.ga_len }); + nlua_print_event((void *[]){ msg_ga.ga_data, (void *)(intptr_t)msg_ga.ga_len }); } return 0; -- cgit From f22e9e10f9ad77d2cce7f52837c5724877505a08 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 6 Dec 2023 16:49:40 +0800 Subject: vim-patch:8.2.3695: confusing error for missing key (#26420) Problem: Confusing error for missing key. Solution: Use the actualy key for the error. (closes vim/vim#9241) https://github.com/vim/vim/commit/5c1ec439f0a69e9aa7ece9bbb7d916f48f58be1e Co-authored-by: Bram Moolenaar --- src/nvim/lua/executor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index d63a9a1307..e665732c1a 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -115,7 +115,7 @@ lua_State *get_global_lstate(void) /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. -/// @param[in] msg Message base, must contain one `%*s`. +/// @param[in] msg Message base, must contain one `%.*s`. void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { -- cgit From 040369e1e4b86c4655a6885b36ee89ad4f10ca16 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Mon, 4 Dec 2023 11:37:45 +0900 Subject: fix(treesitter): don't forcefully open folds Problem: When `vim._foldupdate()` is invoked inside a scheduled callback, the cursor may have moved to a line with a closed fold, e.g., after `dd` on the line that is one line above a folded region. Then it opens the fold, which is unnecessary and distracting. Legacy foldexprs do not have this issue. Solution: Don't explicitly open folds on cursor. Note: This doesn't completely prevent spurious opening of folds. That is due to bugs in treesitter foldexpr algorithm, which should be addressed separately. --- src/nvim/lua/stdlib.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index d7a7abe3c8..fc2fedeaa1 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -536,11 +536,14 @@ static int nlua_iconv(lua_State *lstate) return 1; } -// Like 'zx' but don't call newFoldLevel() +// Update foldlevels (e.g., by evaluating 'foldexpr') for all lines in the current window without +// invoking other side effects. Unlike `zx`, it does not close manually opened folds and does not +// open folds under the cursor. static int nlua_foldupdate(lua_State *lstate) { curwin->w_foldinvalid = true; // recompute folds - foldOpenCursor(); + foldUpdate(curwin, 1, (linenr_T)MAXLNUM); + curwin->w_foldinvalid = false; return 0; } -- cgit From b2d471ab337e56f660eb7c89ae24f447f7b7a165 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 7 Dec 2023 08:01:27 -0800 Subject: fix(lua): allow nil values in serialized Lua arrays (#26329) When we convert a Lua table to an Object, we consider the table a "dictionary" if it contains only string keys, and an array if it contains all numeric indices with no gaps. While rare, Lua tables can have both strictly numeric indices and gaps (e.g. { [2] = 2 }). These currently cannot be serialized because it is not considered an array. However, we know the maximum index of the table and as long as all of the keys in the table are numeric, it is still possible to serialize this table as an array. The missing indices will have nil values. --- src/nvim/lua/converter.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index fd2bdbd677..ca0be28fac 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -148,7 +148,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) } } else { if (tsize == 0 - || (tsize == ret.maxidx + || (tsize <= ret.maxidx && other_keys_num == 0 && ret.string_keys_num == 0)) { ret.type = kObjectTypeArray; @@ -1129,10 +1129,6 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) } const size_t idx = cur.obj->data.array.size++; lua_rawgeti(lstate, -1, (int)idx + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 2); - continue; - } kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.array.items[idx], -- cgit From 2ebd328a798778825be61015acd975d8a929dfec Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 9 Dec 2023 11:36:11 +0800 Subject: refactor: format casting of negative number better (#26482) --- src/nvim/lua/converter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index ca0be28fac..e26e38f577 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1254,7 +1254,7 @@ handle_T nlua_pop_handle(lua_State *lstate, Error *err) handle_T ret; if (lua_type(lstate, -1) != LUA_TNUMBER) { api_set_error(err, kErrorTypeValidation, "Expected Lua number"); - ret = (handle_T) - 1; + ret = (handle_T)(-1); } else { ret = (handle_T)lua_tonumber(lstate, -1); } -- cgit From 1d4a5cd18537d054a564ff19b9361120597d9dd7 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 12 Dec 2023 19:06:22 +0800 Subject: feat(eval): exists() function supports checking v:lua functions (#26485) Problem: Vimscript function exists() can't check v:lua functions. Solution: Add support for v:lua functions to exists(). --- src/nvim/lua/executor.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index e665732c1a..e0bdbbc1e9 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2313,3 +2313,21 @@ void nlua_init_defaults(void) os_errmsg("\n"); } } + +/// check lua function exist +bool nlua_func_exists(const char *lua_funcname) +{ + MAXSIZE_TEMP_ARRAY(args, 1); + size_t length = strlen(lua_funcname) + 8; + char *str = xmalloc(length); + vim_snprintf(str, length, "return %s", lua_funcname); + ADD_C(args, CSTR_AS_OBJ(str)); + Error err = ERROR_INIT; + Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) =='function'", args, &err); + xfree(str); + + if (result.type != kObjectTypeBoolean) { + return false; + } + return result.data.boolean; +} -- cgit From b40170f7a3ca4bd157eeb52c9d57cb2ebfe3c562 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 12 Dec 2023 20:34:02 +0800 Subject: fix(lua): memory leak when using invalid syntax with exists() (#26530) --- src/nvim/lua/executor.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index e0bdbbc1e9..0763bbd329 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2323,10 +2323,12 @@ bool nlua_func_exists(const char *lua_funcname) vim_snprintf(str, length, "return %s", lua_funcname); ADD_C(args, CSTR_AS_OBJ(str)); Error err = ERROR_INIT; - Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) =='function'", args, &err); + Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) == 'function'", args, &err); xfree(str); + api_clear_error(&err); if (result.type != kObjectTypeBoolean) { + api_free_object(result); return false; } return result.data.boolean; -- cgit From 69bc519b53ebf78fd95c8256468e7d538ebcb948 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 12 Dec 2023 15:40:21 +0100 Subject: refactor: move non-symbols to defs.h headers --- src/nvim/lua/executor.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 0763bbd329..77c3f22da8 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -27,7 +27,6 @@ #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" -- cgit From 7d279a09e0fbdf939d8747270cc642250365ad6c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 18 Dec 2023 12:14:37 +0800 Subject: fix(lua): handle array with holes in luaeval() (#26630) --- src/nvim/lua/converter.c | 57 ++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 36 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index e26e38f577..468fe19cf9 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -170,11 +170,12 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) /// Helper structure for nlua_pop_typval typedef struct { - typval_T *tv; ///< Location where conversion result is saved. - bool container; ///< True if tv is a container. - bool special; ///< If true then tv is a _VAL part of special dictionary - ///< that represents mapping. - int idx; ///< Container index (used to detect self-referencing structures). + typval_T *tv; ///< Location where conversion result is saved. + size_t list_len; ///< Maximum length when tv is a list. + bool container; ///< True if tv is a container. + bool special; ///< If true then tv is a _VAL part of special dictionary + ///< that represents mapping. + int idx; ///< Container index (used to detect self-referencing structures). } TVPopStackItem; /// Convert lua object to Vimscript typval_T @@ -192,7 +193,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) const int initial_size = lua_gettop(lstate); kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE; kvi_init(stack); - kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); + kvi_push(stack, ((TVPopStackItem){ .tv = ret_tv })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); @@ -231,19 +232,14 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) }); kvi_push(stack, cur); tv_list_append_list(cur.tv->vval.v_list, kv_pair); - cur = (TVPopStackItem) { - .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)), - .container = false, - .special = false, - .idx = 0, - }; + cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)) }; } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { abort(); } kvi_push(stack, cur); - cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; + cur = (TVPopStackItem){ .tv = &di->di_tv }; } } else { lua_pop(lstate, 1); @@ -251,23 +247,18 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } } else { assert(cur.tv->v_type == VAR_LIST); - lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 2); + if ((size_t)tv_list_len(cur.tv->vval.v_list) == cur.list_len) { + lua_pop(lstate, 1); continue; } + lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1); // Not populated yet, need to create list item to push. tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) { .v_type = VAR_UNKNOWN, }); kvi_push(stack, cur); // TODO(ZyX-I): Use indexes, here list item *will* be reallocated. - cur = (TVPopStackItem) { - .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)), - .container = false, - .special = false, - .idx = 0, - }; + cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)) }; } } assert(!cur.container); @@ -331,6 +322,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx); cur.tv->vval.v_list->lua_table_ref = table_ref; tv_list_ref(cur.tv->vval.v_list); + cur.list_len = table_props.maxidx; if (table_props.maxidx != 0) { cur.container = true; cur.idx = lua_gettop(lstate); @@ -354,6 +346,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv = &val_di->di_tv; cur.tv->vval.v_list->lua_table_ref = table_ref; assert(cur.tv->v_type == VAR_LIST); + cur.list_len = table_props.string_keys_num; } else { cur.tv->v_type = VAR_DICT; cur.tv->vval.v_dict = tv_dict_alloc(); @@ -371,9 +364,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv->vval.v_float = (float_T)table_props.val; break; case kObjectTypeNil: - emsg(_("E5100: Cannot convert given lua table: table " - "should either have a sequence of positive integer keys " - "or contain only string keys")); + emsg(_("E5100: Cannot convert given lua table: table should " + "contain either only integer keys or only string keys")); ret = false; break; default: @@ -1077,7 +1069,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) const int initial_size = lua_gettop(lstate); kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE; kvi_init(stack); - kvi_push(stack, ((ObjPopStackItem) { &ret, false })); + kvi_push(stack, ((ObjPopStackItem){ .obj = &ret })); while (!ERROR_SET(err) && kv_size(stack)) { ObjPopStackItem cur = kv_pop(stack); if (cur.container) { @@ -1087,8 +1079,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) } if (cur.obj->type == kObjectTypeDictionary) { // stack: …, dict, key - if (cur.obj->data.dictionary.size - == cur.obj->data.dictionary.capacity) { + if (cur.obj->data.dictionary.size == cur.obj->data.dictionary.capacity) { lua_pop(lstate, 2); continue; } @@ -1112,10 +1103,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) .size = len, }; kvi_push(stack, cur); - cur = (ObjPopStackItem) { - .obj = &cur.obj->data.dictionary.items[idx].value, - .container = false, - }; + cur = (ObjPopStackItem){ .obj = &cur.obj->data.dictionary.items[idx].value }; } else { // stack: …, dict lua_pop(lstate, 1); @@ -1130,10 +1118,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) const size_t idx = cur.obj->data.array.size++; lua_rawgeti(lstate, -1, (int)idx + 1); kvi_push(stack, cur); - cur = (ObjPopStackItem) { - .obj = &cur.obj->data.array.items[idx], - .container = false, - }; + cur = (ObjPopStackItem){ .obj = &cur.obj->data.array.items[idx] }; } } assert(!cur.container); -- cgit From 6cb78e2d1c4c6c63c628c965076a07ce5f7adbb6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 16 Dec 2023 22:14:28 +0100 Subject: docs: add style rule regarding initialization Specifically, specify that each initialization should be done on a separate line. --- src/nvim/lua/stdlib.c | 6 ++++-- src/nvim/lua/xdiff.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index fc2fedeaa1..4b7d2dab21 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -82,7 +82,8 @@ static int regex_match_line(lua_State *lstate) handle_T bufnr = (handle_T)luaL_checkinteger(lstate, 2); linenr_T rownr = (linenr_T)luaL_checkinteger(lstate, 3); - int start = 0, end = -1; + int start = 0; + int end = -1; if (narg >= 4) { start = (int)luaL_checkinteger(lstate, 4); } @@ -177,7 +178,8 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } } - size_t codepoints = 0, codeunits = 0; + size_t codepoints = 0; + size_t codeunits = 0; mb_utflen(s1, (size_t)idx, &codepoints, &codeunits); lua_pushinteger(lstate, (lua_Integer)codepoints); diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 16c3aa5e11..5285c1187d 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -74,7 +74,8 @@ static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, int *decisions = NULL; size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite); - int lnuma = start_a, lnumb = start_b; + int lnuma = start_a; + int lnumb = start_b; int hunkstarta = lnuma; int hunkstartb = lnumb; -- cgit From 0c120307ca1ab613e63865c634d7e10ad67fb0ba Mon Sep 17 00:00:00 2001 From: dundargoc Date: Wed, 20 Dec 2023 14:32:22 +0100 Subject: refactor: eliminate cyclic includes --- src/nvim/lua/executor.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 77c3f22da8..43a6a12fda 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -16,6 +16,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" -- cgit From af93a74a0f4afa9a3a4f55ffdf28141eaf776d22 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 18 Dec 2023 10:55:23 +0100 Subject: refactor: run IWYU on entire repo Reference: https://github.com/neovim/neovim/issues/6371. --- src/nvim/lua/executor.c | 4 +--- src/nvim/lua/executor.h | 11 +++-------- src/nvim/lua/stdlib.c | 3 +-- src/nvim/lua/xdiff.c | 1 - 4 files changed, 5 insertions(+), 14 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 43a6a12fda..3e7cdd002e 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -13,7 +13,6 @@ #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/ascii_defs.h" #include "nvim/buffer_defs.h" @@ -24,10 +23,9 @@ #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" diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index b38faddbb3..0b4623cbd3 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -4,18 +4,13 @@ #include #include -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/assert_defs.h" -#include "nvim/cmdexpand_defs.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/ex_cmds_defs.h" +#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep +#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" -#include "nvim/lua/converter.h" #include "nvim/macros_defs.h" -#include "nvim/map_defs.h" #include "nvim/types_defs.h" -#include "nvim/usercmd.h" +#include "nvim/usercmd.h" // IWYU pragma: keep // Generated by msgpack-gen.lua void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 4b7d2dab21..db710457c3 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #ifdef NVIM_VENDOR_BIT # include "bit.h" @@ -14,7 +14,6 @@ #include "cjson/lua_cjson.h" #include "mpack/lmpack.h" -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 5285c1187d..e21bca170f 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -5,7 +5,6 @@ #include #include "luaconf.h" -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/linematch.h" #include "nvim/lua/converter.h" -- cgit From 089b934352437ab310a6dd3b138c7ed9445a3d7b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Dec 2023 12:24:23 +0800 Subject: refactor(options): generate BV_ and WV_ constants (#26705) --- src/nvim/lua/executor.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 0b4623cbd3..16fa5f8bc0 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -9,6 +9,7 @@ #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" #include "nvim/macros_defs.h" +#include "nvim/map_defs.h" #include "nvim/types_defs.h" #include "nvim/usercmd.h" // IWYU pragma: keep -- cgit From eae6727325111e596b49bb04337a467e8833397c Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 21 Dec 2023 15:57:55 +0100 Subject: refactor: remove os_errmsg and os_msg functions Instead replace them with fprintf and printf. --- src/nvim/lua/executor.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3e7cdd002e..6289ea3193 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -252,8 +252,7 @@ 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. - os_errmsg(e_outofmem); - os_errmsg("\n"); + fprintf(stderr, "%s\n", e_outofmem); lua_close(lstate); #ifdef MSWIN ExitThread(0); @@ -640,8 +639,7 @@ static bool nlua_init_packages(lua_State *lstate, bool is_standalone) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - os_errmsg(lua_tostring(lstate, -1)); - os_errmsg("\n"); + fprintf(stderr, "%s\n", lua_tostring(lstate, -1)); return false; } @@ -815,12 +813,12 @@ void nlua_init(char **argv, int argc, int lua_arg0) lua_State *lstate = luaL_newstate(); if (lstate == NULL) { - os_errmsg(_("E970: Failed to initialize lua interpreter\n")); + fprintf(stderr, _("E970: Failed to initialize lua interpreter\n")); os_exit(1); } luaL_openlibs(lstate); if (!nlua_state_init(lstate)) { - os_errmsg(_("E970: Failed to initialize builtin lua modules\n")); + fprintf(stderr, _("E970: Failed to initialize builtin lua modules\n")); #ifdef EXITFREE nlua_common_free_all_mem(lstate); #endif @@ -2307,8 +2305,7 @@ void nlua_init_defaults(void) lua_getglobal(L, "require"); lua_pushstring(L, "vim._defaults"); if (nlua_pcall(L, 1, 0)) { - os_errmsg(lua_tostring(L, -1)); - os_errmsg("\n"); + fprintf(stderr, "%s\n", lua_tostring(L, -1)); } } -- cgit From c89292fcb7f2ebf06efb7c1d00c28f34c6f68fec Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 28 Dec 2023 13:42:24 +0100 Subject: refactor: follow style guide --- src/nvim/lua/executor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 6289ea3193..116e8136cc 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2172,7 +2172,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) // every possible modifier (with room to spare). If the list of possible // modifiers grows this may need to be updated. char buf[200] = { 0 }; - (void)uc_mods(buf, &cmdmod, false); + uc_mods(buf, &cmdmod, false); lua_pushstring(lstate, buf); lua_setfield(lstate, -2, "mods"); -- cgit From 1813661a6197c76ea6621284570aca1d56597099 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 4 Jan 2024 15:38:16 +0100 Subject: refactor(IWYU): fix headers Remove `export` pramgas from defs headers as it causes IWYU to believe that the definitions from the defs headers comes from main header, which is not what we really want. --- src/nvim/lua/converter.c | 2 +- src/nvim/lua/executor.c | 14 +++++++++++++- src/nvim/lua/executor.h | 1 - src/nvim/lua/secure.c | 2 +- src/nvim/lua/spell.c | 2 +- src/nvim/lua/stdlib.c | 4 ++++ src/nvim/lua/xdiff.c | 1 + 7 files changed, 21 insertions(+), 5 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 468fe19cf9..575dc2ce37 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -16,7 +16,7 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 116e8136cc..f48cab6739 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -13,6 +13,7 @@ #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/ascii_defs.h" #include "nvim/buffer_defs.h" @@ -23,7 +24,9 @@ #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" @@ -31,8 +34,9 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/keycodes.h" #include "nvim/lua/converter.h" @@ -43,22 +47,30 @@ #include "nvim/main.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/option_vars.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/strings.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/undo.h" #include "nvim/usercmd.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#ifndef MSWIN +# include +#endif + static int in_fast_callback = 0; static bool in_script = false; diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 16fa5f8bc0..0b4623cbd3 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -9,7 +9,6 @@ #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" #include "nvim/macros_defs.h" -#include "nvim/map_defs.h" #include "nvim/types_defs.h" #include "nvim/usercmd.h" // IWYU pragma: keep diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c index 65c13f8872..f62e0ace04 100644 --- a/src/nvim/lua/secure.c +++ b/src/nvim/lua/secure.c @@ -4,7 +4,7 @@ #include "nvim/charset.h" #include "nvim/ex_cmds_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" #include "nvim/lua/secure.h" diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index e6c38ea9cb..ba83239dc5 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.c @@ -7,7 +7,7 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight_defs.h" #include "nvim/lua/spell.h" diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index db710457c3..5fea1ba5d8 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -14,10 +14,12 @@ #include "cjson/lua_cjson.h" #include "mpack/lmpack.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" @@ -29,10 +31,12 @@ #include "nvim/lua/xdiff.h" #include "nvim/map_defs.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/pos_defs.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/types_defs.h" diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index e21bca170f..5285c1187d 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -5,6 +5,7 @@ #include #include "luaconf.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/linematch.h" #include "nvim/lua/converter.h" -- cgit From 646fdf1073433e2bdeec3433f6cbdf8f4be37098 Mon Sep 17 00:00:00 2001 From: glepnir Date: Wed, 17 Jan 2024 20:14:26 +0800 Subject: refactor(api): use hl id directly in nvim_buf_set_extmark --- src/nvim/lua/converter.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 575dc2ce37..91f2db3ac9 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -17,6 +17,7 @@ #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" #include "nvim/gettext_defs.h" +#include "nvim/highlight_group.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" @@ -1324,7 +1325,14 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ if (field->type == kObjectTypeNil) { *(Object *)mem = nlua_pop_Object(L, true, err); } else if (field->type == kObjectTypeInteger) { - *(Integer *)mem = nlua_pop_Integer(L, err); + if (field->is_hlgroup && lua_type(L, -1) == LUA_TSTRING) { + size_t name_len; + const char *name = lua_tolstring(L, -1, &name_len); + lua_pop(L, 1); + *(Integer *)mem = name_len > 0 ? syn_check_group(name, name_len) : 0; + } else { + *(Integer *)mem = nlua_pop_Integer(L, err); + } } else if (field->type == kObjectTypeBoolean) { *(Boolean *)mem = nlua_pop_Boolean_strict(L, err); } else if (field->type == kObjectTypeString) { -- cgit From 83b51b36aa46d4bb25fada6eda22102e0aa5ef19 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 25 Jan 2024 23:14:41 +0100 Subject: fixup: raise TS min version --- src/nvim/lua/treesitter.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 008b3f2e95..8b62bff496 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -1449,10 +1449,7 @@ static int node_rawquery(lua_State *L) cursor = ts_query_cursor_new(); } -#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH - // reset the start depth ts_query_cursor_set_max_start_depth(cursor, UINT32_MAX); -#endif ts_query_cursor_set_match_limit(cursor, 256); ts_query_cursor_exec(cursor, query, node); @@ -1475,11 +1472,8 @@ static int node_rawquery(lua_State *L) if (lua_type(L, -2) == LUA_TSTRING) { char *k = (char *)lua_tostring(L, -2); if (strequal("max_start_depth", k)) { - // TODO(lewis6991): remove ifdef when min TS version is 0.20.9 -#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1); ts_query_cursor_set_max_start_depth(cursor, max_start_depth); -#endif } } lua_pop(L, 1); // pop the value; lua_next will pop the key. -- cgit From c2433589dca022a7f40cdcbd0cd1ad8aba6ee4a9 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sat, 27 Jan 2024 02:00:50 +0100 Subject: feat(ex_cmds): ranged :lua #27167 :{range}lua executes the specified lines in the current buffer as Lua code, regardless of its extension or 'filetype'. Close #27103 --- src/nvim/lua/executor.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index f48cab6739..3139e924a1 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1649,6 +1649,15 @@ bool nlua_is_deferred_safe(void) void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { + if (eap->addr_count > 0 || *eap->arg == NUL) { + if (eap->addr_count > 0 && *eap->arg == NUL) { + cmd_source_buffer(eap, true); + } else { + semsg(_(e_invarg2), "exactly one of {chunk} and {range} required"); + } + return; + } + size_t len; char *code = script_get(eap, &len); if (eap->skip || code == NULL) { -- cgit From 2cd76a758b4511748d9482e5af58162a608516b4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 27 Jan 2024 10:40:30 -0800 Subject: docs(lua): update ":{range}lua" docs + error message #27231 - `:lua (no file)` is misleading because `:lua` never takes a file arg, unlike `:source`. - Update various related docs. --- src/nvim/lua/executor.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3139e924a1..62e82175c3 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1641,19 +1641,20 @@ bool nlua_is_deferred_safe(void) return in_fast_callback == 0; } -/// Run lua string +/// Executes Lua code. /// -/// Used for :lua. +/// Implements `:lua` and `:lua ={expr}`. /// -/// @param eap Vimscript command being run. +/// @param eap Vimscript `:lua {code}`, `:{range}lua`, or `:lua ={expr}` command. void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { + // ":{range}lua" if (eap->addr_count > 0 || *eap->arg == NUL) { if (eap->addr_count > 0 && *eap->arg == NUL) { cmd_source_buffer(eap, true); } else { - semsg(_(e_invarg2), "exactly one of {chunk} and {range} required"); + semsg(_(e_invarg2), "exactly one of {chunk} or {range} required"); } return; } @@ -1664,13 +1665,14 @@ void ex_lua(exarg_T *const eap) xfree(code); return; } - // When =expr is used transform it to vim.print(expr) + + // ":lua {code}", ":={expr}" or ":lua ={expr}" + // + // When "=expr" is used transform it to "vim.print(expr)". if (eap->cmdidx == CMD_equal || code[0] == '=') { size_t off = (eap->cmdidx == CMD_equal) ? 0 : 1; len += sizeof("vim.print()") - 1 - off; - // code_buf needs to be 1 char larger then len for null byte in the end. - // lua nlua_typval_exec doesn't expect null terminated string so len - // needs to end before null byte. + // `nlua_typval_exec` doesn't expect NUL-terminated string so `len` must end before NUL byte. char *code_buf = xmallocz(len); vim_snprintf(code_buf, len + 1, "vim.print(%s)", code + off); xfree(code); @@ -1682,11 +1684,11 @@ void ex_lua(exarg_T *const eap) xfree(code); } -/// Run lua string for each line in range +/// Executes Lua code for-each line in a buffer range. /// -/// Used for :luado. +/// Implements `:luado`. /// -/// @param eap Vimscript command being run. +/// @param eap Vimscript `:luado {code}` command. void ex_luado(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { @@ -1763,11 +1765,11 @@ void ex_luado(exarg_T *const eap) redraw_curbuf_later(UPD_NOT_VALID); } -/// Run lua file +/// Executes Lua code from a file location. /// -/// Used for :luafile. +/// Implements `:luafile`. /// -/// @param eap Vimscript command being run. +/// @param eap Vimscript `:luafile {file}` command. void ex_luafile(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { -- cgit From 5b1b765610ae12ebd6400aafd068903569ee441a Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Sun, 28 Jan 2024 20:53:14 -0500 Subject: docs: enforce "treesitter" spelling #27110 It's the "tree-sitter" project, but "treesitter" in our code and docs. --- src/nvim/lua/treesitter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 8b62bff496..de17aabca2 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -1,5 +1,5 @@ -// lua bindings for tree-sitter. -// NB: this file mostly contains a generic lua interface for tree-sitter +// lua bindings for treesitter. +// NB: this file mostly contains a generic lua interface for treesitter // trees and nodes, and could be broken out as a reusable lua package #include -- cgit From 4ffc20c9515294481486e81271a8edeeff203140 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jan 2024 08:09:25 +0800 Subject: fix(lua): avoid internal error when :luado deletes lines (#27262) --- src/nvim/lua/executor.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 62e82175c3..5d51d58b1d 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1732,10 +1732,15 @@ void ex_luado(exarg_T *const eap) nlua_error(lstate, _("E5110: Error executing lua: %.*s")); return; } + + buf_T *const was_curbuf = curbuf; + for (linenr_T l = eap->line1; l <= eap->line2; l++) { + // Check the line number, the command may have deleted lines. if (l > curbuf->b_ml.ml_line_count) { break; } + lua_pushvalue(lstate, -1); const char *const old_line = ml_get_buf(curbuf, l); // Get length of old_line here as calling Lua code may free it. @@ -1746,6 +1751,13 @@ void ex_luado(exarg_T *const eap) nlua_error(lstate, _("E5111: Error calling lua: %.*s")); break; } + + // Catch the command switching to another buffer. + // Check the line number, the command may have deleted lines. + if (curbuf != was_curbuf || l > curbuf->b_ml.ml_line_count) { + break; + } + if (lua_isstring(lstate, -1)) { size_t new_line_len; const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); @@ -1760,6 +1772,7 @@ void ex_luado(exarg_T *const eap) } lua_pop(lstate, 1); } + lua_pop(lstate, 1); check_cursor(); redraw_curbuf_later(UPD_NOT_VALID); -- cgit From 4a1ad676ce0bdaead122b092d2ff33b51679ffe9 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Fri, 2 Feb 2024 06:14:10 +0100 Subject: feat(ex_cmds): no error on :lua with {range} and {code} (#27290) Problem: Erroring when both {range} and {code} are supplied to :lua is inconvenient and may break mappings. Solution: Don't error, ignore {range} and execute {code} when both are supplied. --- src/nvim/lua/executor.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5d51d58b1d..85d614fe0d 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1649,12 +1649,12 @@ bool nlua_is_deferred_safe(void) void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { - // ":{range}lua" - if (eap->addr_count > 0 || *eap->arg == NUL) { - if (eap->addr_count > 0 && *eap->arg == NUL) { + // ":{range}lua", only if no {code} + if (*eap->arg == NUL) { + if (eap->addr_count > 0) { cmd_source_buffer(eap, true); } else { - semsg(_(e_invarg2), "exactly one of {chunk} or {range} required"); + emsg(_(e_argreq)); } return; } -- cgit From f9d81c43d2296d212c9cebcbdce401cd76cf0f1f Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 31 Jan 2024 22:02:06 +0100 Subject: refactor(api): use keydict and arena for more api return values Implement api_keydict_to_dict as the complement to api_dict_to_keydict Fix a conversion error when nvim_get_win_config gets called from lua, where Float values "x" and "y" didn't get converted to lua numbers. --- src/nvim/lua/converter.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 91f2db3ac9..3cf5b0e94f 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1360,3 +1360,46 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ lua_pop(L, 1); // [] } + +void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) +{ + lua_createtable(L, 0, 0); + for (size_t i = 0; table[i].str; i++) { + KeySetLink *field = &table[i]; + bool is_set = true; + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)value; + is_set = ks->is_set_ & (1ULL << field->opt_index); + } + + if (!is_set) { + continue; + } + + char *mem = ((char *)value + field->ptr_off); + + lua_pushstring(L, field->str); + if (field->type == kObjectTypeNil) { + nlua_push_Object(L, *(Object *)mem, false); + } else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer + || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { + lua_pushinteger(L, *(Integer *)mem); + } else if (field->type == kObjectTypeFloat) { + lua_pushnumber(L, *(Float *)mem); + } else if (field->type == kObjectTypeBoolean) { + lua_pushboolean(L, *(Boolean *)mem); + } else if (field->type == kObjectTypeString) { + nlua_push_String(L, *(String *)mem, false); + } else if (field->type == kObjectTypeArray) { + nlua_push_Array(L, *(Array *)mem, false); + } else if (field->type == kObjectTypeDictionary) { + nlua_push_Dictionary(L, *(Dictionary *)mem, false); + } else if (field->type == kObjectTypeLuaRef) { + nlua_pushref(L, *(LuaRef *)mem); + } else { + abort(); + } + + lua_rawset(L, -3); + } +} -- cgit From 800134ea5ec60338a40280c8536db6a6a4a10249 Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Thu, 25 Jan 2024 13:27:48 -0500 Subject: refactor(treesitter): typing for Query, TSQuery, and TSQueryInfo - `TSQuery`: userdata object for parsed query. - `vim.treesitter.Query`: renamed from `Query`. - Add a new field `lang`. - `TSQueryInfo`: - Move to `vim/treesitter/_meta.lua`, because C code owns it. - Correct typing for `patterns`, should be a map from `integer` (pattern_id) to `(integer|string)[][]` (list of predicates or directives). - `vim.treesitter.QueryInfo` is added. - This currently has the same structure as `TSQueryInfo` (exported from C code). - Document the fields (see `TSQuery:inspect`). - Add typing for `vim._ts_parse_query()`. --- src/nvim/lua/treesitter.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index de17aabca2..c1816a8860 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -56,6 +56,7 @@ typedef struct { # include "lua/treesitter.c.generated.h" #endif +// TSParser static struct luaL_Reg parser_meta[] = { { "__gc", parser_gc }, { "__tostring", parser_tostring }, @@ -70,6 +71,7 @@ static struct luaL_Reg parser_meta[] = { { NULL, NULL } }; +// TSTree static struct luaL_Reg tree_meta[] = { { "__gc", tree_gc }, { "__tostring", tree_tostring }, @@ -80,6 +82,7 @@ static struct luaL_Reg tree_meta[] = { { NULL, NULL } }; +// TSNode static struct luaL_Reg node_meta[] = { { "__tostring", node_tostring }, { "__eq", node_eq }, @@ -119,6 +122,7 @@ static struct luaL_Reg node_meta[] = { { NULL, NULL } }; +// TSQuery static struct luaL_Reg query_meta[] = { { "__gc", query_gc }, { "__tostring", query_tostring }, @@ -1649,8 +1653,10 @@ static int query_inspect(lua_State *L) return 0; } - uint32_t n_pat = ts_query_pattern_count(query); + // TSQueryInfo lua_createtable(L, 0, 2); // [retval] + + uint32_t n_pat = ts_query_pattern_count(query); lua_createtable(L, (int)n_pat, 1); // [retval, patterns] for (size_t i = 0; i < n_pat; i++) { uint32_t len; -- cgit From 0353dd3029f9ce31c3894530385443a90f6677ee Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 11 Feb 2024 15:46:14 +0100 Subject: refactor(lua): use Arena when converting from lua stack to API args and for return value of nlua_exec/nlua_call_ref, as this uses the same family of functions. NB: the handling of luaref:s is a bit of a mess. add api_luarefs_free_XX functions as a stop-gap as refactoring luarefs is a can of worms for another PR:s. as a minor feature/bug-fix, nvim_buf_call and nvim_win_call now preserves arbitrary return values. --- src/nvim/lua/converter.c | 124 +++++++++++++++++++++++------------------------ src/nvim/lua/executor.c | 80 ++++++++++++++++++------------ src/nvim/lua/executor.h | 13 ++++- src/nvim/lua/xdiff.c | 3 +- 4 files changed, 125 insertions(+), 95 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 3cf5b0e94f..423d2fa775 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -795,8 +795,8 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) /// Convert lua value to string /// /// Always pops one value from the stack. -String nlua_pop_String(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +String nlua_pop_String(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) != LUA_TSTRING) { lua_pop(lstate, 1); @@ -807,7 +807,10 @@ String nlua_pop_String(lua_State *lstate, Error *err) ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size)); assert(ret.data != NULL); - ret.data = xmemdupz(ret.data, ret.size); + // TODO(bfredl): it would be "nice" to just use the memory of the lua string + // directly, although ensuring the lifetime of such strings is a bit tricky + // (an API call could invoke nested lua, which triggers GC, and kaboom?) + ret.data = arena_memdupz(arena, ret.data, ret.size); lua_pop(lstate, 1); return ret; @@ -816,8 +819,8 @@ String nlua_pop_String(lua_State *lstate, Error *err) /// Convert lua value to integer /// /// Always pops one value from the stack. -Integer nlua_pop_Integer(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Integer nlua_pop_Integer(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) != LUA_TNUMBER) { lua_pop(lstate, 1); @@ -840,8 +843,8 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err) /// thus `err` is never set as any lua value can be co-erced into a lua bool /// /// Always pops one value from the stack. -Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Boolean nlua_pop_Boolean(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { const Boolean ret = lua_toboolean(lstate, -1); lua_pop(lstate, 1); @@ -915,7 +918,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons /// Convert lua table to float /// /// Always pops one value from the stack. -Float nlua_pop_Float(lua_State *lstate, Error *err) +Float nlua_pop_Float(lua_State *lstate, Arena *arena, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) == LUA_TNUMBER) { @@ -939,29 +942,29 @@ Float nlua_pop_Float(lua_State *lstate, Error *err) /// @param[in] table_props nlua_traverse_table() output. /// @param[out] err Location where error will be saved. static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTableProps table_props, - Error *const err) + Arena *arena, Error *const err) { - Array ret = { .size = table_props.maxidx, .items = NULL }; + Array ret = arena_array(arena, table_props.maxidx); - if (ret.size == 0) { + if (table_props.maxidx == 0) { lua_pop(lstate, 1); return ret; } - ret.items = xcalloc(ret.size, sizeof(*ret.items)); - for (size_t i = 1; i <= ret.size; i++) { + for (size_t i = 1; i <= table_props.maxidx; i++) { Object val; lua_rawgeti(lstate, -1, (int)i); - val = nlua_pop_Object(lstate, false, err); + val = nlua_pop_Object(lstate, false, arena, err); if (ERROR_SET(err)) { - ret.size = i - 1; lua_pop(lstate, 1); - api_free_array(ret); + if (!arena) { + api_free_array(ret); + } return (Array) { .size = 0, .items = NULL }; } - ret.items[i - 1] = val; + ADD_C(ret, val); } lua_pop(lstate, 1); @@ -971,15 +974,14 @@ static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTablePro /// Convert lua table to array /// /// Always pops one value from the stack. -Array nlua_pop_Array(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Array nlua_pop_Array(lua_State *lstate, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT { - const LuaTableProps table_props = nlua_check_type(lstate, err, - kObjectTypeArray); + const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeArray); if (table_props.type != kObjectTypeArray) { return (Array) { .size = 0, .items = NULL }; } - return nlua_pop_Array_unchecked(lstate, table_props, err); + return nlua_pop_Array_unchecked(lstate, table_props, arena, err); } /// Convert lua table to dictionary @@ -991,30 +993,30 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) /// @param[in] table_props nlua_traverse_table() output. /// @param[out] err Location where error will be saved. static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTableProps table_props, - bool ref, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + bool ref, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 5) FUNC_ATTR_WARN_UNUSED_RESULT { - Dictionary ret = { .size = table_props.string_keys_num, .items = NULL }; + Dictionary ret = arena_dict(arena, table_props.string_keys_num); - if (ret.size == 0) { + if (table_props.string_keys_num == 0) { lua_pop(lstate, 1); return ret; } - ret.items = xcalloc(ret.size, sizeof(*ret.items)); lua_pushnil(lstate); - for (size_t i = 0; lua_next(lstate, -2) && i < ret.size;) { + for (size_t i = 0; lua_next(lstate, -2) && i < table_props.string_keys_num;) { // stack: dict, key, value if (lua_type(lstate, -2) == LUA_TSTRING) { lua_pushvalue(lstate, -2); // stack: dict, key, value, key - ret.items[i].key = nlua_pop_String(lstate, err); + String key = nlua_pop_String(lstate, arena, err); // stack: dict, key, value if (!ERROR_SET(err)) { - ret.items[i].value = nlua_pop_Object(lstate, ref, err); + Object value = nlua_pop_Object(lstate, ref, arena, err); + kv_push_c(ret, ((KeyValuePair) { .key = key, .value = value })); // stack: dict, key } else { lua_pop(lstate, 1); @@ -1022,8 +1024,9 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTabl } if (ERROR_SET(err)) { - ret.size = i; - api_free_dictionary(ret); + if (!arena) { + api_free_dictionary(ret); + } lua_pop(lstate, 2); // stack: return (Dictionary) { .size = 0, .items = NULL }; @@ -1042,8 +1045,8 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTabl /// Convert lua table to dictionary /// /// Always pops one value from the stack. -Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Arena *arena, Error *err) + FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT { const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeDictionary); @@ -1052,7 +1055,7 @@ Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err) return (Dictionary) { .size = 0, .items = NULL }; } - return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, err); + return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, arena, err); } /// Helper structure for nlua_pop_Object @@ -1064,7 +1067,8 @@ typedef struct { /// Convert lua table to object /// /// Always pops one value from the stack. -Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) +Object nlua_pop_Object(lua_State *const lstate, bool ref, Arena *arena, Error *const err) + FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT { Object ret = NIL; const int initial_size = lua_gettop(lstate); @@ -1099,10 +1103,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) size_t len; const char *s = lua_tolstring(lstate, -2, &len); const size_t idx = cur.obj->data.dictionary.size++; - cur.obj->data.dictionary.items[idx].key = (String) { - .data = xmemdupz(s, len), - .size = len, - }; + cur.obj->data.dictionary.items[idx].key = CBUF_TO_ARENA_STR(arena, s, len); kvi_push(stack, cur); cur = (ObjPopStackItem){ .obj = &cur.obj->data.dictionary.items[idx].value }; } else { @@ -1133,7 +1134,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(lstate, -1, &len); - *cur.obj = STRING_OBJ(((String) { .data = xmemdupz(s, len), .size = len })); + *cur.obj = STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len)); break; } case LUA_TNUMBER: { @@ -1151,23 +1152,17 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) switch (table_props.type) { case kObjectTypeArray: - *cur.obj = ARRAY_OBJ(((Array) { .items = NULL, .size = 0, .capacity = 0 })); + *cur.obj = ARRAY_OBJ(((Array)ARRAY_DICT_INIT)); if (table_props.maxidx != 0) { - cur.obj->data.array.items = - xcalloc(table_props.maxidx, - sizeof(cur.obj->data.array.items[0])); - cur.obj->data.array.capacity = table_props.maxidx; + cur.obj->data.array = arena_array(arena, table_props.maxidx); cur.container = true; kvi_push(stack, cur); } break; case kObjectTypeDictionary: - *cur.obj = DICTIONARY_OBJ(((Dictionary) { .items = NULL, .size = 0, .capacity = 0 })); + *cur.obj = DICTIONARY_OBJ(((Dictionary)ARRAY_DICT_INIT)); if (table_props.string_keys_num != 0) { - cur.obj->data.dictionary.items = - xcalloc(table_props.string_keys_num, - sizeof(cur.obj->data.dictionary.items[0])); - cur.obj->data.dictionary.capacity = table_props.string_keys_num; + cur.obj->data.dictionary = arena_dict(arena, table_props.string_keys_num); cur.container = true; kvi_push(stack, cur); lua_pushnil(lstate); @@ -1219,7 +1214,9 @@ type_error: } kvi_destroy(stack); if (ERROR_SET(err)) { - api_free_object(ret); + if (!arena) { + api_free_object(ret); + } ret = NIL; lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); } @@ -1227,14 +1224,14 @@ type_error: return ret; } -LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) +LuaRef nlua_pop_LuaRef(lua_State *const lstate, Arena *arena, Error *err) { LuaRef rv = nlua_ref_global(lstate, -1); lua_pop(lstate, 1); return rv; } -handle_T nlua_pop_handle(lua_State *lstate, Error *err) +handle_T nlua_pop_handle(lua_State *lstate, Arena *arena, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { handle_T ret; @@ -1296,7 +1293,8 @@ void nlua_init_types(lua_State *const lstate) } // lua specific variant of api_dict_to_keydict -void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Error *err) +void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Arena *arena, + Error *err) { if (!lua_istable(L, -1)) { api_set_error(err, kErrorTypeValidation, "Expected Lua table"); @@ -1323,7 +1321,7 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ char *mem = ((char *)retval + field->ptr_off); if (field->type == kObjectTypeNil) { - *(Object *)mem = nlua_pop_Object(L, true, err); + *(Object *)mem = nlua_pop_Object(L, true, arena, err); } else if (field->type == kObjectTypeInteger) { if (field->is_hlgroup && lua_type(L, -1) == LUA_TSTRING) { size_t name_len; @@ -1331,23 +1329,23 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ lua_pop(L, 1); *(Integer *)mem = name_len > 0 ? syn_check_group(name, name_len) : 0; } else { - *(Integer *)mem = nlua_pop_Integer(L, err); + *(Integer *)mem = nlua_pop_Integer(L, arena, err); } } else if (field->type == kObjectTypeBoolean) { *(Boolean *)mem = nlua_pop_Boolean_strict(L, err); } else if (field->type == kObjectTypeString) { - *(String *)mem = nlua_pop_String(L, err); + *(String *)mem = nlua_pop_String(L, arena, err); } else if (field->type == kObjectTypeFloat) { - *(Float *)mem = nlua_pop_Float(L, err); + *(Float *)mem = nlua_pop_Float(L, arena, err); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { - *(handle_T *)mem = nlua_pop_handle(L, err); + *(handle_T *)mem = nlua_pop_handle(L, arena, err); } else if (field->type == kObjectTypeArray) { - *(Array *)mem = nlua_pop_Array(L, err); + *(Array *)mem = nlua_pop_Array(L, arena, err); } else if (field->type == kObjectTypeDictionary) { - *(Dictionary *)mem = nlua_pop_Dictionary(L, false, err); + *(Dictionary *)mem = nlua_pop_Dictionary(L, false, arena, err); } else if (field->type == kObjectTypeLuaRef) { - *(LuaRef *)mem = nlua_pop_LuaRef(L, err); + *(LuaRef *)mem = nlua_pop_LuaRef(L, arena, err); } else { abort(); } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 85d614fe0d..be55bde202 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -311,7 +311,10 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) lua_pop(lstate, 1); Error err = ERROR_INIT; - const Array pat = nlua_pop_Array(lstate, &err); + // TODO(bfredl): we could use an arena here for both "pat" and "ret", but then + // we need a path to not use the freelist but a private block local to the thread. + // We do not want mutex contentionery for the main arena freelist. + const Array pat = nlua_pop_Array(lstate, NULL, &err); if (ERROR_SET(&err)) { luaL_where(lstate, 1); lua_pushstring(lstate, err.msg); @@ -1242,13 +1245,13 @@ static int nlua_rpc(lua_State *lstate, bool request) const char *name = luaL_checklstring(lstate, 2, &name_len); int nargs = lua_gettop(lstate) - 2; Error err = ERROR_INIT; - Array args = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + Array args = arena_array(&arena, (size_t)nargs); for (int i = 0; i < nargs; i++) { lua_pushvalue(lstate, i + 3); - ADD(args, nlua_pop_Object(lstate, false, &err)); + ADD(args, nlua_pop_Object(lstate, false, &arena, &err)); if (ERROR_SET(&err)) { - api_free_array(args); goto check_err; } } @@ -1265,10 +1268,11 @@ static int nlua_rpc(lua_State *lstate, bool request) api_set_error(&err, kErrorTypeValidation, "Invalid channel: %" PRIu64, chan_id); } - api_free_array(args); // TODO(bfredl): no } check_err: + arena_mem_free(arena_finish(&arena)); + if (ERROR_SET(&err)) { lua_pushstring(lstate, err.msg); api_clear_error(&err); @@ -1541,10 +1545,12 @@ int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typ /// /// @param[in] str String to execute. /// @param[in] args array of ... args +/// @param[in] mode Whether and how the the return value should be converted to Object +/// @param[in] arena can be NULL, then nested allocations are used /// @param[out] err Location where error will be saved. /// /// @return Return value of the execution. -Object nlua_exec(const String str, const Array args, Error *err) +Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *arena, Error *err) { lua_State *const lstate = global_lstate; @@ -1568,7 +1574,7 @@ Object nlua_exec(const String str, const Array args, Error *err) return NIL; } - return nlua_pop_Object(lstate, false, err); + return nlua_call_pop_retval(lstate, mode, arena, err); } bool nlua_ref_is_function(LuaRef ref) @@ -1589,12 +1595,12 @@ bool nlua_ref_is_function(LuaRef ref) /// @param ref the reference to call (not consumed) /// @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, only check if return value is truthy +/// @param mode Whether and how the the return value should be converted to Object +/// @param arena can be NULL, then nested allocations are used /// @param err Error details, if any (if NULL, errors are echoed) -/// @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) +/// @return Return value of function, as per mode +Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, Arena *arena, + Error *err) { lua_State *const lstate = global_lstate; nlua_pushref(lstate, ref); @@ -1620,18 +1626,34 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro return NIL; } - if (retval) { - Error dummy = ERROR_INIT; - if (err == NULL) { - err = &dummy; - } - return nlua_pop_Object(lstate, false, err); - } else { - bool value = lua_toboolean(lstate, -1); + return nlua_call_pop_retval(lstate, mode, arena, err); +} + +static Object nlua_call_pop_retval(lua_State *lstate, LuaRetMode mode, Arena *arena, Error *err) +{ + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 1); + return NIL; + } + Error dummy = ERROR_INIT; + + switch (mode) { + case kRetNilBool: { + bool bool_value = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + + return BOOLEAN_OBJ(bool_value); + } + case kRetLuaref: { + LuaRef ref = nlua_ref_global(lstate, -1); lua_pop(lstate, 1); - return value ? BOOLEAN_OBJ(true) : NIL; + return LUAREF_OBJ(ref); + } + case kRetObject: + return nlua_pop_Object(lstate, false, arena, err ? err : &dummy); } + UNREACHABLE; } /// check if the current execution context is safe for calling deferred API @@ -1930,13 +1952,14 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) *num_results = 0; *results = NULL; - int prefix_len = (int)nlua_pop_Integer(lstate, &err); + Arena arena = ARENA_EMPTY; + int prefix_len = (int)nlua_pop_Integer(lstate, &arena, &err); if (ERROR_SET(&err)) { ret = FAIL; goto cleanup; } - Array completions = nlua_pop_Array(lstate, &err); + Array completions = nlua_pop_Array(lstate, &arena, &err); if (ERROR_SET(&err)) { ret = FAIL; goto cleanup_array; @@ -1960,7 +1983,7 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results) *num_results = result_array.ga_len; cleanup_array: - api_free_array(completions); + arena_mem_free(arena_finish(&arena)); cleanup: @@ -2354,13 +2377,10 @@ bool nlua_func_exists(const char *lua_funcname) vim_snprintf(str, length, "return %s", lua_funcname); ADD_C(args, CSTR_AS_OBJ(str)); Error err = ERROR_INIT; - Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) == 'function'", args, &err); + Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) == 'function'", args, + kRetNilBool, NULL, &err); xfree(str); api_clear_error(&err); - if (result.type != kObjectTypeBoolean) { - api_free_object(result); - return false; - } - return result.data.boolean; + return LUARET_TRUTHY(result); } diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 0b4623cbd3..ebcd62122f 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -24,7 +24,8 @@ typedef struct { #endif } nlua_ref_state_t; -#define NLUA_EXEC_STATIC(cstr, arg, err) nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, err) +#define NLUA_EXEC_STATIC(cstr, arg, mode, arena, err) \ + nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, mode, arena, err) #define NLUA_CLEAR_REF(x) \ do { \ @@ -35,6 +36,16 @@ typedef struct { } \ } while (0) +typedef enum { + kRetObject, ///< any object, but doesn't preserve nested luarefs + kRetNilBool, ///< NIL preserved as such, other values return their booleanness + ///< Should also be used when return value is ignored, as it is allocation-free + kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL) +} LuaRetMode; + +/// To use with kRetNilBool for quick thuthyness check +#define LUARET_TRUTHY(res) ((res).type == kObjectTypeBoolean && (res).data.boolean == true) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/executor.h.generated.h" #endif diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 5285c1187d..f5a5949ab6 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -209,7 +209,8 @@ 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, int64_t *linematch, Error *err) { - const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err); + // TODO: this is very much a keydict.. + const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, NULL, err); NluaXdiffMode mode = kNluaXdiffModeUnified; -- cgit From 1a3a8d903e9705ce41867e1cbc629a85c7cb6252 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 11 Feb 2024 19:13:38 +0100 Subject: refactor(lua): use a keyset for vim.diff opts parsing --- src/nvim/lua/xdiff.c | 171 ++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 111 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index f5a5949ab6..035c171a14 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -5,7 +5,9 @@ #include #include "luaconf.h" +#include "nvim/api/keysets_defs.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/linematch.h" #include "nvim/lua/converter.h" @@ -187,137 +189,84 @@ static mmfile_t get_string_arg(lua_State *lstate, int idx) return mf; } -// Helper function for validating option types -static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *name, Error *err) -{ - if (actType != expType) { - const char *type_str = - expType == kObjectTypeString - ? "string" : (expType == kObjectTypeInteger - ? "integer" : (expType == kObjectTypeBoolean - ? "boolean" : (expType == kObjectTypeLuaRef - ? "function" : "NA"))); - - api_set_error(err, kErrorTypeValidation, "%s is not a %s", name, - type_str); - return true; - } - - return false; -} - static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params, int64_t *linematch, Error *err) { - // TODO: this is very much a keydict.. - const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, NULL, err); + Dict(xdl_diff) opts = KEYDICT_INIT; + char *err_param = NULL; + KeySetLink *KeyDict_xdl_diff_get_field(const char *str, size_t len); + nlua_pop_keydict(lstate, &opts, KeyDict_xdl_diff_get_field, &err_param, NULL, err); NluaXdiffMode mode = kNluaXdiffModeUnified; - bool had_on_hunk = false; bool had_result_type_indices = false; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("on_hunk", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeLuaRef, "on_hunk", err)) { - goto exit_1; - } - had_on_hunk = true; - nlua_pushref(lstate, v->data.luaref); - } else if (strequal("result_type", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeString, "result_type", err)) { - goto exit_1; - } - if (strequal("unified", v->data.string.data)) { - // the default - } else if (strequal("indices", v->data.string.data)) { - had_result_type_indices = true; - } else { - api_set_error(err, kErrorTypeValidation, "not a valid result_type"); - goto exit_1; - } - } else if (strequal("algorithm", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeString, "algorithm", err)) { - goto exit_1; - } - if (strequal("myers", v->data.string.data)) { - // default - } else if (strequal("minimal", v->data.string.data)) { - params->flags |= XDF_NEED_MINIMAL; - } else if (strequal("patience", v->data.string.data)) { - params->flags |= XDF_PATIENCE_DIFF; - } else if (strequal("histogram", v->data.string.data)) { - params->flags |= XDF_HISTOGRAM_DIFF; - } else { - api_set_error(err, kErrorTypeValidation, "not a valid algorithm"); - goto exit_1; - } - } else if (strequal("ctxlen", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeInteger, "ctxlen", err)) { - goto exit_1; - } - cfg->ctxlen = (long)v->data.integer; - } else if (strequal("interhunkctxlen", k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeInteger, "interhunkctxlen", - err)) { - goto exit_1; - } - cfg->interhunkctxlen = (long)v->data.integer; - } else if (strequal("linematch", k.data)) { - if (v->type == kObjectTypeBoolean) { - *linematch = v->data.boolean ? INT64_MAX : 0; - } else if (v->type == kObjectTypeInteger) { - *linematch = v->data.integer; - } else { - api_set_error(err, kErrorTypeValidation, "linematch must be a boolean or integer"); - goto exit_1; - } + + if (HAS_KEY(&opts, xdl_diff, result_type)) { + if (strequal("unified", opts.result_type.data)) { + // the default + } else if (strequal("indices", opts.result_type.data)) { + had_result_type_indices = true; } else { - struct { - const char *name; - unsigned long value; - } flags[] = { - { "ignore_whitespace", XDF_IGNORE_WHITESPACE }, - { "ignore_whitespace_change", XDF_IGNORE_WHITESPACE_CHANGE }, - { "ignore_whitespace_change_at_eol", XDF_IGNORE_WHITESPACE_AT_EOL }, - { "ignore_cr_at_eol", XDF_IGNORE_CR_AT_EOL }, - { "ignore_blank_lines", XDF_IGNORE_BLANK_LINES }, - { "indent_heuristic", XDF_INDENT_HEURISTIC }, - { NULL, 0 }, - }; - bool key_used = false; - for (size_t j = 0; flags[j].name; j++) { - if (strequal(flags[j].name, k.data)) { - if (check_xdiff_opt(v->type, kObjectTypeBoolean, flags[j].name, - err)) { - goto exit_1; - } - if (v->data.boolean) { - params->flags |= flags[j].value; - } - key_used = true; - break; - } - } + api_set_error(err, kErrorTypeValidation, "not a valid result_type"); + goto exit_1; + } + } - if (key_used) { - continue; - } + if (HAS_KEY(&opts, xdl_diff, algorithm)) { + if (strequal("myers", opts.algorithm.data)) { + // default + } else if (strequal("minimal", opts.algorithm.data)) { + params->flags |= XDF_NEED_MINIMAL; + } else if (strequal("patience", opts.algorithm.data)) { + params->flags |= XDF_PATIENCE_DIFF; + } else if (strequal("histogram", opts.algorithm.data)) { + params->flags |= XDF_HISTOGRAM_DIFF; + } else { + api_set_error(err, kErrorTypeValidation, "not a valid algorithm"); + goto exit_1; + } + } - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + if (HAS_KEY(&opts, xdl_diff, ctxlen)) { + cfg->ctxlen = (long)opts.ctxlen; + } + + if (HAS_KEY(&opts, xdl_diff, interhunkctxlen)) { + cfg->interhunkctxlen = (long)opts.interhunkctxlen; + } + + if (HAS_KEY(&opts, xdl_diff, linematch)) { + if (opts.linematch.type == kObjectTypeBoolean) { + *linematch = opts.linematch.data.boolean ? INT64_MAX : 0; + } else if (opts.linematch.type == kObjectTypeInteger) { + *linematch = opts.linematch.data.integer; + } else { + api_set_error(err, kErrorTypeValidation, "linematch must be a boolean or integer"); goto exit_1; } } - if (had_on_hunk) { + params->flags |= opts.ignore_whitespace ? XDF_IGNORE_WHITESPACE : 0; + params->flags |= opts.ignore_whitespace_change ? XDF_IGNORE_WHITESPACE_CHANGE : 0; + params->flags |= opts.ignore_whitespace_change_at_eol ? XDF_IGNORE_WHITESPACE_AT_EOL : 0; + params->flags |= opts.ignore_cr_at_eol ? XDF_IGNORE_CR_AT_EOL : 0; + params->flags |= opts.ignore_blank_lines ? XDF_IGNORE_BLANK_LINES : 0; + params->flags |= opts.indent_heuristic ? XDF_INDENT_HEURISTIC : 0; + + if (HAS_KEY(&opts, xdl_diff, on_hunk)) { mode = kNluaXdiffModeOnHunkCB; + nlua_pushref(lstate, opts.on_hunk); + if (lua_type(lstate, -1) != LUA_TFUNCTION) { + api_set_error(err, kErrorTypeValidation, "on_hunk is not a function"); + } } else if (had_result_type_indices) { mode = kNluaXdiffModeLocations; } exit_1: - api_free_dictionary(opts); + api_free_string(opts.result_type); + api_free_string(opts.algorithm); + api_free_luaref(opts.on_hunk); return mode; } -- cgit From d60412b18e4e21f301baa2ac3f3fb7be89655e4b Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 12 Feb 2024 20:40:27 +0100 Subject: refactor(eval): use arena when converting typvals to Object Note: this contains two _temporary_ changes which can be reverted once the Arena vs no-Arena distinction in API wrappers has been removed. Both nlua_push_Object and object_to_vim_take_luaref() has been changed to take the object argument as a pointer. This is not going to be necessary once these are only used with arena (or not at all) allocated Objects. The object_to_vim() variant which leaves luaref untouched might need to stay for a little longer. --- src/nvim/lua/converter.c | 18 ++++++++++-------- src/nvim/lua/executor.c | 6 +++--- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 423d2fa775..0b7f58cec6 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -719,7 +719,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special } for (size_t i = 0; i < dict.size; i++) { nlua_push_String(lstate, dict.items[i].key, special); - nlua_push_Object(lstate, dict.items[i].value, special); + nlua_push_Object(lstate, &dict.items[i].value, special); lua_rawset(lstate, -3); } } @@ -732,7 +732,7 @@ void nlua_push_Array(lua_State *lstate, const Array array, bool special) { lua_createtable(lstate, (int)array.size, 0); for (size_t i = 0; i < array.size; i++) { - nlua_push_Object(lstate, array.items[i], special); + nlua_push_Object(lstate, &array.items[i], special); lua_rawseti(lstate, -2, (int)i + 1); } } @@ -753,10 +753,10 @@ GENERATE_INDEX_FUNCTION(Tabpage) /// Convert given Object to lua value /// /// Leaves converted value on top of the stack. -void nlua_push_Object(lua_State *lstate, const Object obj, bool special) +void nlua_push_Object(lua_State *lstate, Object *obj, bool special) FUNC_ATTR_NONNULL_ALL { - switch (obj.type) { + switch (obj->type) { case kObjectTypeNil: if (special) { lua_pushnil(lstate); @@ -765,12 +765,14 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) } break; case kObjectTypeLuaRef: { - nlua_pushref(lstate, obj.data.luaref); + nlua_pushref(lstate, obj->data.luaref); + api_free_luaref(obj->data.luaref); + obj->data.luaref = LUA_NOREF; break; } #define ADD_TYPE(type, data_key) \ case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key, special); \ + nlua_push_##type(lstate, obj->data.data_key, special); \ break; \ } ADD_TYPE(Boolean, boolean) @@ -782,7 +784,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer, special); \ + nlua_push_##type(lstate, (type)obj->data.integer, special); \ break; \ } ADD_REMOTE_TYPE(Buffer) @@ -1378,7 +1380,7 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) lua_pushstring(L, field->str); if (field->type == kObjectTypeNil) { - nlua_push_Object(L, *(Object *)mem, false); + nlua_push_Object(L, (Object *)mem, false); } else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { lua_pushinteger(L, *(Integer *)mem); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index be55bde202..db44288b16 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1260,7 +1260,7 @@ static int nlua_rpc(lua_State *lstate, bool request) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); if (!ERROR_SET(&err)) { - nlua_push_Object(lstate, result, false); + nlua_push_Object(lstate, &result, false); arena_mem_free(res_mem); } } else { @@ -1563,7 +1563,7 @@ Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *are } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, args.items[i], false); + nlua_push_Object(lstate, &args.items[i], false); } if (nlua_pcall(lstate, (int)args.size, 1)) { @@ -1610,7 +1610,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, nargs++; } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, args.items[i], false); + nlua_push_Object(lstate, &args.items[i], false); } if (nlua_pcall(lstate, nargs, 1)) { -- cgit From bd5008de07d29a6457ddc7fe13f9f85c9c4619d2 Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Fri, 16 Feb 2024 18:54:47 +0100 Subject: fix(treesitter): correctly handle query quantifiers (#24738) Query patterns can contain quantifiers (e.g. (foo)+ @bar), so a single capture can map to multiple nodes. The iter_matches API can not handle this situation because the match table incorrectly maps capture indices to a single node instead of to an array of nodes. The match table should be updated to map capture indices to an array of nodes. However, this is a massively breaking change, so must be done with a proper deprecation period. `iter_matches`, `add_predicate` and `add_directive` must opt-in to the correct behavior for backward compatibility. This is done with a new "all" option. This option will become the default and removed after the 0.10 release. Co-authored-by: Christian Clason Co-authored-by: MDeiml Co-authored-by: Gregory Anders --- src/nvim/lua/treesitter.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index c1816a8860..25a753b179 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -1364,9 +1364,16 @@ static int node_equal(lua_State *L) /// assumes the match table being on top of the stack static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { - for (int i = 0; i < match->capture_count; i++) { - push_node(L, match->captures[i].node, nodeidx); - lua_rawseti(L, -2, (int)match->captures[i].index + 1); + // [match] + for (size_t i = 0; i < match->capture_count; i++) { + lua_rawgeti(L, -1, (int)match->captures[i].index + 1); // [match, captures] + if (lua_isnil(L, -1)) { // [match, nil] + lua_pop(L, 1); // [match] + lua_createtable(L, 1, 0); // [match, captures] + } + push_node(L, match->captures[i].node, nodeidx); // [match, captures, node] + lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1); // [match, captures] + lua_rawseti(L, -2, (int)match->captures[i].index + 1); // [match] } } @@ -1379,7 +1386,7 @@ static int query_next_match(lua_State *L) TSQueryMatch match; if (ts_query_cursor_next_match(cursor, &match)) { lua_pushinteger(L, match.pattern_index + 1); // [index] - lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, match] + lua_createtable(L, (int)ts_query_capture_count(query), 0); // [index, match] set_match(L, &match, lua_upvalueindex(2)); return 2; } @@ -1421,7 +1428,8 @@ static int query_next_capture(lua_State *L) if (n_pred > 0 && (ud->max_match_id < (int)match.id)) { ud->max_match_id = (int)match.id; - lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] + // Create a new cleared match table + lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, node, match] set_match(L, &match, lua_upvalueindex(2)); lua_pushinteger(L, match.pattern_index + 1); lua_setfield(L, -2, "pattern"); @@ -1431,6 +1439,10 @@ static int query_next_capture(lua_State *L) lua_pushboolean(L, false); lua_setfield(L, -2, "active"); } + + // Set current_match to the new match + lua_replace(L, lua_upvalueindex(4)); // [index, node] + lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] return 3; } return 2; -- cgit From f25fcc68a34c2d51b0715fadc62cb50509de338b Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 17 Feb 2024 20:31:21 +0100 Subject: refactor(api): use an arena for mappings --- src/nvim/lua/executor.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index db44288b16..58f329b76f 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2327,11 +2327,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) /// String representation of a Lua function reference /// /// @return Allocated string -char *nlua_funcref_str(LuaRef ref) +char *nlua_funcref_str(LuaRef ref, Arena *arena) { lua_State *const lstate = global_lstate; - StringBuilder str = KV_INITIAL_VALUE; - kv_resize(str, 16); if (!lua_checkstack(lstate, 1)) { goto plain; @@ -2345,14 +2343,13 @@ char *nlua_funcref_str(LuaRef ref) lua_Debug ar; if (lua_getinfo(lstate, ">S", &ar) && *ar.source == '@' && ar.linedefined >= 0) { char *src = home_replace_save(NULL, ar.source + 1); - kv_printf(str, "", ref, src, ar.linedefined); + String str = arena_printf(arena, "", ref, src, ar.linedefined); xfree(src); - return str.items; + return str.data; } -plain: - kv_printf(str, "", ref); - return str.items; +plain: {} + return arena_printf(arena, "", ref).data; } /// Execute the vim._defaults module to set up default mappings and autocommands -- cgit From 77e928fd3e92f35182237b663437d7ebde7ebde7 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 24 Feb 2024 10:17:20 +0100 Subject: refactor(fileio): remove API shell layer encouraging unnecessary allocations Functions like file_open_new() and file_open_fd_new() which just is a wrapper around the real functions but with an extra xmalloc/xfree around is an anti-pattern. If the caller really needs to allocate a FileDescriptor as a heap object, it can do that directly. FileDescriptor by itself is pretty much a pointer, or rather two: the OS fd index and a pointer to a buffer. So most of the time an extra pointer layer is just wasteful. In the case of scriptin[curscript] in getchar.c, curscript used to mean in practice: N+1 open scripts when curscript>0 zero or one open scripts when curscript==0 Which means scriptin[0] had to be compared to NULL to disambiguate the curscript=0 case. Instead, use curscript==-1 to mean that are no script, then all pointer comparisons dissappear and we can just use an array of structs without extra pointers. --- src/nvim/lua/executor.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 58f329b76f..3859dd922a 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1828,7 +1828,11 @@ bool nlua_exec_file(const char *path) lua_getglobal(lstate, "loadfile"); lua_pushstring(lstate, path); } else { - FileDescriptor *stdin_dup = file_open_stdin(); + FileDescriptor stdin_dup; + int error = file_open_stdin(&stdin_dup); + if (error) { + return false; + } StringBuilder sb = KV_INITIAL_VALUE; kv_resize(sb, 64); @@ -1837,7 +1841,7 @@ bool nlua_exec_file(const char *path) if (got_int) { // User canceled. return false; } - ptrdiff_t read_size = file_read(stdin_dup, IObuff, 64); + ptrdiff_t read_size = file_read(&stdin_dup, IObuff, 64); if (read_size < 0) { // Error. return false; } @@ -1849,7 +1853,7 @@ bool nlua_exec_file(const char *path) } } kv_push(sb, NUL); - file_free(stdin_dup, false); + file_close(&stdin_dup, false); lua_getglobal(lstate, "loadstring"); lua_pushstring(lstate, sb.items); -- cgit From ad5a155b1f4b387d3aaa54c91d0146cb0287bb9f Mon Sep 17 00:00:00 2001 From: VanaIgr Date: Mon, 26 Feb 2024 04:12:55 -0600 Subject: fix(mbyte): fix bugs in utf_cp_*_off() functions Problems: - Illegal bytes after valid UTF-8 char cause utf_cp_*_off() to fail. - When stream isn't NUL-terminated, utf_cp_*_off() may go over the end. Solution: Don't go over end of the char of end of the string. --- src/nvim/lua/stdlib.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 5fea1ba5d8..8f58fd1a1a 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -226,11 +226,12 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); ptrdiff_t offset = luaL_checkinteger(lstate, 2); - if (offset < 0 || offset > (intptr_t)s1_len) { + if (offset <= 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int head_offset = -utf_cp_head_off(s1, s1 + offset - 1); - lua_pushinteger(lstate, head_offset); + size_t const off = (size_t)(offset - 1); + int head_off = -utf_cp_bounds_len(s1, s1 + off, (int)(s1_len - off)).begin_off; + lua_pushinteger(lstate, head_off); return 1; } @@ -246,11 +247,12 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); ptrdiff_t offset = luaL_checkinteger(lstate, 2); - if (offset < 0 || offset > (intptr_t)s1_len) { + if (offset <= 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int tail_offset = utf_cp_tail_off(s1, s1 + offset - 1); - lua_pushinteger(lstate, tail_offset); + size_t const off = (size_t)(offset - 1); + int tail_off = utf_cp_bounds_len(s1, s1 + off, (int)(s1_len - off)).end_off - 1; + lua_pushinteger(lstate, tail_off); return 1; } -- cgit From de5cf09cf98e20d8d3296ad6933ff2741acf83f7 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 26 Feb 2024 18:00:46 +0100 Subject: refactor(metadata): generate all metadata in lua Then we can just load metadata in C as a single msgpack blob. Which also can be used directly as binarly data, instead of first unpacking all the functions and ui_events metadata to immediately pack it again, which was a bit of a silly walk (and one extra usecase of `msgpack_rpc_from_object` which will get yak shaved in the next PR) --- src/nvim/lua/executor.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3859dd922a..1a9bd026b5 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -15,6 +15,7 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/ui.h" #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" -- cgit From dc37c1550bed46fffbb677d343cdc5bc94056219 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 25 Feb 2024 15:02:48 +0100 Subject: refactor(msgpack): allow flushing buffer while packing msgpack Before, we needed to always pack an entire msgpack_rpc Object to a continous memory buffer before sending it out to a channel. But this is generally wasteful. it is better to just flush whatever is in the buffer and then continue packing to a new buffer. This is also done for the UI event packer where there are some extra logic to "finish" of an existing batch of nevents/ncalls. This doesn't really stop us from flushing the buffer, just that we need to update the state machine accordingly so the next call to prepare_call() always will start with a new event (even though the buffer might contain overflow data from a large event). --- src/nvim/lua/converter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/lua') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 0b7f58cec6..bba771f8a5 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1381,9 +1381,11 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) lua_pushstring(L, field->str); if (field->type == kObjectTypeNil) { nlua_push_Object(L, (Object *)mem, false); - } else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer - || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { + } else if (field->type == kObjectTypeInteger) { lua_pushinteger(L, *(Integer *)mem); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + lua_pushinteger(L, *(handle_T *)mem); } else if (field->type == kObjectTypeFloat) { lua_pushnumber(L, *(Float *)mem); } else if (field->type == kObjectTypeBoolean) { -- cgit