diff options
Diffstat (limited to 'src/nvim/lua')
-rw-r--r-- | src/nvim/lua/converter.c | 769 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 355 | ||||
-rw-r--r-- | src/nvim/lua/treesitter.c | 200 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 108 | ||||
-rw-r--r-- | src/nvim/lua/xdiff.c | 332 | ||||
-rw-r--r-- | src/nvim/lua/xdiff.h | 12 |
6 files changed, 1077 insertions, 699 deletions
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index ce8c9b0d06..fac5bab664 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1,33 +1,31 @@ // 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 <lauxlib.h> -#include <assert.h> -#include <stdint.h> #include <stdbool.h> +#include <stdint.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" -#include "nvim/assert.h" // FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is // redefined -#include "nvim/vim.h" -#include "nvim/globals.h" -#include "nvim/message.h" +#include "nvim/ascii.h" +#include "nvim/eval/decode.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" -#include "nvim/ascii.h" -#include "nvim/macros.h" - +#include "nvim/globals.h" #include "nvim/lib/kvec.h" -#include "nvim/eval/decode.h" - #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" +#include "nvim/macros.h" +#include "nvim/message.h" +#include "nvim/vim.h" /// Determine, which keys lua table contains typedef struct { @@ -49,7 +47,7 @@ typedef struct { #define VAL_IDX_VALUE false #define LUA_PUSH_STATIC_STRING(lstate, s) \ - lua_pushlstring(lstate, s, sizeof(s) - 1) + lua_pushlstring(lstate, s, sizeof(s) - 1) static LuaTableProps nlua_traverse_table(lua_State *const lstate) @@ -71,57 +69,56 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) lua_pushnil(lstate); while (lua_next(lstate, -2)) { switch (lua_type(lstate, -2)) { - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -2, &len); - if (memchr(s, NUL, len) != NULL) { - ret.has_string_with_nul = true; - } - ret.string_keys_num++; - break; + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + if (memchr(s, NUL, len) != NULL) { + ret.has_string_with_nul = true; } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -2); - if (n > (lua_Number)SIZE_MAX || n <= 0 - || ((lua_Number)((size_t)n)) != n) { - other_keys_num++; - } else { - const size_t idx = (size_t)n; - if (idx > ret.maxidx) { - ret.maxidx = idx; - } + ret.string_keys_num++; + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -2); + if (n > (lua_Number)SIZE_MAX || n <= 0 + || ((lua_Number)((size_t)n)) != n) { + other_keys_num++; + } else { + const size_t idx = (size_t)n; + if (idx > ret.maxidx) { + ret.maxidx = idx; } - break; } - case LUA_TBOOLEAN: { - const bool b = lua_toboolean(lstate, -2); - if (b == TYPE_IDX_VALUE) { - if (lua_type(lstate, -1) == LUA_TNUMBER) { - lua_Number n = lua_tonumber(lstate, -1); - if (n == (lua_Number)kObjectTypeFloat - || n == (lua_Number)kObjectTypeArray - || n == (lua_Number)kObjectTypeDictionary) { - ret.has_type_key = true; - ret.type = (ObjectType)n; - } else { - other_keys_num++; - } + break; + } + case LUA_TBOOLEAN: { + const bool b = lua_toboolean(lstate, -2); + if (b == TYPE_IDX_VALUE) { + if (lua_type(lstate, -1) == LUA_TNUMBER) { + lua_Number n = lua_tonumber(lstate, -1); + if (n == (lua_Number)kObjectTypeFloat + || n == (lua_Number)kObjectTypeArray + || n == (lua_Number)kObjectTypeDictionary) { + ret.has_type_key = true; + ret.type = (ObjectType)n; } else { other_keys_num++; } } else { - has_val_key = true; - val_type = lua_type(lstate, -1); - if (val_type == LUA_TNUMBER) { - ret.val = lua_tonumber(lstate, -1); - } + other_keys_num++; + } + } else { + has_val_key = true; + val_type = lua_type(lstate, -1); + if (val_type == LUA_TNUMBER) { + ret.val = lua_tonumber(lstate, -1); } - break; - } - default: { - other_keys_num++; - break; } + break; + } + default: + other_keys_num++; + break; } tsize++; lua_pop(lstate, 1); @@ -196,8 +193,9 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) { bool ret = true; const int initial_size = lua_gettop(lstate); - kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); + kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); @@ -234,7 +232,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) tv_list_append_owned_tv(kv_pair, (typval_T) { .v_type = VAR_UNKNOWN, }); - kv_push(stack, cur); + 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)), @@ -247,7 +245,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { abort(); } - kv_push(stack, cur); + kvi_push(stack, cur); cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; } } else { @@ -265,7 +263,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) { .v_type = VAR_UNKNOWN, }); - kv_push(stack, cur); + 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)), @@ -282,159 +280,149 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) .vval = { .v_number = 0 }, }; switch (lua_type(lstate, -1)) { - case LUA_TNIL: { - cur.tv->v_type = VAR_SPECIAL; - cur.tv->vval.v_special = kSpecialVarNull; - break; - } - case LUA_TBOOLEAN: { - cur.tv->v_type = VAR_BOOL; - cur.tv->vval.v_bool = (lua_toboolean(lstate, -1) + case LUA_TNIL: + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = kSpecialVarNull; + break; + case LUA_TBOOLEAN: + cur.tv->v_type = VAR_BOOL; + cur.tv->vval.v_bool = (lua_toboolean(lstate, -1) ? kBoolVarTrue : kBoolVarFalse); - break; + break; + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -1, &len); + *cur.tv = decode_string(s, len, kNone, true, false); + if (cur.tv->v_type == VAR_UNKNOWN) { + ret = false; } - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -1, &len); - *cur.tv = decode_string(s, len, kNone, true, false); - if (cur.tv->v_type == VAR_UNKNOWN) { - ret = false; - } - break; + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN + || ((lua_Number)((varnumber_T)n)) != n) { + cur.tv->v_type = VAR_FLOAT; + cur.tv->vval.v_float = (float_T)n; + } else { + cur.tv->v_type = VAR_NUMBER; + cur.tv->vval.v_number = (varnumber_T)n; } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -1); - if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN - || ((lua_Number)((varnumber_T)n)) != n) { - cur.tv->v_type = VAR_FLOAT; - cur.tv->vval.v_float = (float_T)n; - } else { - cur.tv->v_type = VAR_NUMBER; - cur.tv->vval.v_number = (varnumber_T)n; - } - break; + break; + } + case LUA_TTABLE: { + // Only need to track table refs if we have a metatable associated. + LuaRef table_ref = LUA_NOREF; + if (lua_getmetatable(lstate, -1)) { + lua_pop(lstate, 1); + table_ref = nlua_ref(lstate, -1); } - case LUA_TTABLE: { - // Only need to track table refs if we have a metatable associated. - LuaRef table_ref = LUA_NOREF; - if (lua_getmetatable(lstate, -1)) { - lua_pop(lstate, 1); - table_ref = nlua_ref(lstate, -1); - } - const LuaTableProps table_props = nlua_traverse_table(lstate); + const LuaTableProps table_props = nlua_traverse_table(lstate); - for (size_t i = 0; i < kv_size(stack); i++) { - const TVPopStackItem item = kv_A(stack, i); - if (item.container && lua_rawequal(lstate, -1, item.idx)) { - tv_copy(item.tv, cur.tv); - cur.container = false; - goto nlua_pop_typval_table_processing_end; - } + for (size_t i = 0; i < kv_size(stack); i++) { + const TVPopStackItem item = kv_A(stack, i); + if (item.container && lua_rawequal(lstate, -1, item.idx)) { + tv_copy(item.tv, cur.tv); + cur.container = false; + goto nlua_pop_typval_table_processing_end; } + } - switch (table_props.type) { - case kObjectTypeArray: { - cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx); + switch (table_props.type) { + case kObjectTypeArray: + cur.tv->v_type = VAR_LIST; + 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); + if (table_props.maxidx != 0) { + cur.container = true; + cur.idx = lua_gettop(lstate); + kvi_push(stack, cur); + } + break; + case kObjectTypeDictionary: + if (table_props.string_keys_num == 0) { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = tv_dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + cur.tv->vval.v_dict->lua_table_ref = table_ref; + } else { + cur.special = table_props.has_string_with_nul; + if (table_props.has_string_with_nul) { + decode_create_map_special_dict(cur.tv, (ptrdiff_t)table_props.string_keys_num); + assert(cur.tv->v_type == VAR_DICT); + dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict, + S_LEN("_VAL")); + assert(val_di != NULL); + cur.tv = &val_di->di_tv; cur.tv->vval.v_list->lua_table_ref = table_ref; - tv_list_ref(cur.tv->vval.v_list); - if (table_props.maxidx != 0) { - cur.container = true; - cur.idx = lua_gettop(lstate); - kv_push(stack, cur); - } - break; - } - case kObjectTypeDictionary: { - if (table_props.string_keys_num == 0) { - cur.tv->v_type = VAR_DICT; - cur.tv->vval.v_dict = tv_dict_alloc(); - cur.tv->vval.v_dict->dv_refcount++; - cur.tv->vval.v_dict->lua_table_ref = table_ref; - } else { - cur.special = table_props.has_string_with_nul; - if (table_props.has_string_with_nul) { - decode_create_map_special_dict( - cur.tv, (ptrdiff_t)table_props.string_keys_num); - assert(cur.tv->v_type == VAR_DICT); - dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict, - S_LEN("_VAL")); - assert(val_di != NULL); - cur.tv = &val_di->di_tv; - cur.tv->vval.v_list->lua_table_ref = table_ref; - assert(cur.tv->v_type == VAR_LIST); - } else { - cur.tv->v_type = VAR_DICT; - cur.tv->vval.v_dict = tv_dict_alloc(); - cur.tv->vval.v_dict->dv_refcount++; - cur.tv->vval.v_dict->lua_table_ref = table_ref; - } - cur.container = true; - cur.idx = lua_gettop(lstate); - kv_push(stack, cur); - lua_pushnil(lstate); - } - break; - } - case kObjectTypeFloat: { - cur.tv->v_type = VAR_FLOAT; - 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")); - ret = false; - break; - } - default: { - abort(); + assert(cur.tv->v_type == VAR_LIST); + } else { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = tv_dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + cur.tv->vval.v_dict->lua_table_ref = table_ref; } + cur.container = true; + cur.idx = lua_gettop(lstate); + kvi_push(stack, cur); + lua_pushnil(lstate); } -nlua_pop_typval_table_processing_end: break; - } - case LUA_TFUNCTION: { - LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); - state->lua_callable.func_ref = nlua_ref(lstate, -1); - - char_u *name = register_cfunc( - &nlua_CFunction_func_call, - &nlua_CFunction_func_free, - state); - - cur.tv->v_type = VAR_FUNC; - cur.tv->vval.v_string = vim_strsave(name); + case kObjectTypeFloat: + cur.tv->v_type = VAR_FLOAT; + cur.tv->vval.v_float = (float_T)table_props.val; break; - } - case LUA_TUSERDATA: { - // TODO(bfredl): check mt.__call and convert to function? - nlua_pushref(lstate, nlua_nil_ref); - bool is_nil = lua_rawequal(lstate, -2, -1); - lua_pop(lstate, 1); - if (is_nil) { - cur.tv->v_type = VAR_SPECIAL; - cur.tv->vval.v_special = kSpecialVarNull; - } else { - EMSG(_("E5101: Cannot convert given lua type")); - ret = false; - } + case kObjectTypeNil: + EMSG(_("E5100: Cannot convert given lua table: table " + "should either have a sequence of positive integer keys " + "or contain only string keys")); + ret = false; break; + default: + abort(); } - default: { +nlua_pop_typval_table_processing_end: + break; + } + case LUA_TFUNCTION: { + LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); + state->lua_callable.func_ref = nlua_ref(lstate, -1); + + char_u *name = register_cfunc(&nlua_CFunction_func_call, + &nlua_CFunction_func_free, + state); + + cur.tv->v_type = VAR_FUNC; + cur.tv->vval.v_string = vim_strsave(name); + break; + } + case LUA_TUSERDATA: { + // TODO(bfredl): check mt.__call and convert to function? + nlua_pushref(lstate, nlua_nil_ref); + bool is_nil = lua_rawequal(lstate, -2, -1); + lua_pop(lstate, 1); + if (is_nil) { + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = kSpecialVarNull; + } else { EMSG(_("E5101: Cannot convert given lua type")); ret = false; - break; } + break; + } + default: + EMSG(_("E5101: Cannot convert given lua type")); + ret = false; + break; } if (!cur.container) { lua_pop(lstate, 1); } } - kv_destroy(stack); + kvi_destroy(stack); if (!ret) { tv_clear(ret_tv); *ret_tv = (typval_T) { @@ -453,89 +441,97 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_ALLOW_SPECIALS true #define TYPVAL_ENCODE_CONV_NIL(tv) \ - do { \ - if (typval_conv_special) { \ - lua_pushnil(lstate); \ - } else { \ - nlua_pushref(lstate, nlua_nil_ref); \ - } \ - } while (0) + do { \ + if (typval_conv_special) { \ + lua_pushnil(lstate); \ + } else { \ + nlua_pushref(lstate, nlua_nil_ref); \ + } \ + } while (0) #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - lua_pushboolean(lstate, (bool)(num)) + lua_pushboolean(lstate, (bool)(num)) #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - lua_pushnumber(lstate, (lua_Number)(num)) + lua_pushnumber(lstate, (lua_Number)(num)) #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - TYPVAL_ENCODE_CONV_NUMBER(tv, flt) + TYPVAL_ENCODE_CONV_NUMBER(tv, flt) #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ - lua_pushlstring(lstate, (const char *)(str), (len)) + lua_pushlstring(lstate, (const char *)(str), (len)) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ - TYPVAL_ENCODE_CONV_NIL(tv) + TYPVAL_ENCODE_CONV_NIL(tv) + +#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ + do { \ + const blob_T *const blob_ = (blob); \ + lua_pushlstring(lstate, \ + blob_ != NULL ? (const char *)blob_->bv_ga.ga_data : "", \ + (size_t)(len)); \ + } while (0) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ - goto typval_encode_stop_converting_one_item; \ - } while (0) + do { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + goto typval_encode_stop_converting_one_item; \ + } while (0) #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) #define TYPVAL_ENCODE_CONV_FUNC_END(tv) #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - lua_createtable(lstate, 0, 0) + lua_createtable(lstate, 0, 0) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - do { \ - if (typval_conv_special) { \ - nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \ - } else { \ - lua_createtable(lstate, 0, 0); \ - nlua_pushref(lstate, nlua_empty_dict_ref); \ - lua_setmetatable(lstate, -2); \ - } \ - } while (0) + do { \ + if (typval_conv_special) { \ + nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \ + } else { \ + lua_createtable(lstate, 0, 0); \ + nlua_pushref(lstate, nlua_empty_dict_ref); \ + lua_setmetatable(lstate, -2); \ + } \ + } while (0) #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ - do { \ - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ - emsgf(_("E5102: Lua failed to grow stack to %i"), \ - lua_gettop(lstate) + 3); \ - return false; \ - } \ - lua_createtable(lstate, (int)(len), 0); \ - lua_pushnumber(lstate, 1); \ - } while (0) + do { \ + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ + emsgf(_("E5102: Lua failed to grow stack to %i"), \ + lua_gettop(lstate) + 3); \ + return false; \ + } \ + lua_createtable(lstate, (int)(len), 0); \ + lua_pushnumber(lstate, 1); \ + } while (0) #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ - do { \ - lua_Number idx = lua_tonumber(lstate, -2); \ - lua_rawset(lstate, -3); \ - lua_pushnumber(lstate, idx + 1); \ - } while (0) + do { \ + lua_Number idx = lua_tonumber(lstate, -2); \ + lua_rawset(lstate, -3); \ + lua_pushnumber(lstate, idx + 1); \ + } while (0) #define TYPVAL_ENCODE_CONV_LIST_END(tv) \ - lua_rawset(lstate, -3) + lua_rawset(lstate, -3) #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ - do { \ - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ - emsgf(_("E5102: Lua failed to grow stack to %i"), \ - lua_gettop(lstate) + 3); \ - return false; \ - } \ - lua_createtable(lstate, 0, (int)(len)); \ - } while (0) + do { \ + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ + emsgf(_("E5102: Lua failed to grow stack to %i"), \ + lua_gettop(lstate) + 3); \ + return false; \ + } \ + lua_createtable(lstate, 0, (int)(len)); \ + } while (0) #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair) @@ -544,26 +540,26 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \ - lua_rawset(lstate, -3) + lua_rawset(lstate, -3) #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ - TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ - do { \ - for (size_t backref = kv_size(*mpstack); backref; backref--) { \ - const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ - if (mpval.type == conv_type) { \ - if (conv_type == kMPConvDict \ + do { \ + for (size_t backref = kv_size(*mpstack); backref; backref--) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict \ ? (void *)mpval.data.d.dict == (void *)(val) \ : (void *)mpval.data.l.list == (void *)(val)) { \ - lua_pushvalue(lstate, \ - -((int)((kv_size(*mpstack) - backref + 1) * 2))); \ - break; \ - } \ + lua_pushvalue(lstate, \ + -((int)((kv_size(*mpstack) - backref + 1) * 2))); \ + break; \ } \ } \ - } while (0) + } \ + } while (0) #define TYPVAL_ENCODE_SCOPE static #define TYPVAL_ENCODE_NAME lua @@ -578,6 +574,7 @@ static bool typval_conv_special = false; #undef TYPVAL_ENCODE_CONV_STRING #undef TYPVAL_ENCODE_CONV_STR_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_BLOB #undef TYPVAL_ENCODE_CONV_NUMBER #undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FUNC_START @@ -664,9 +661,7 @@ static inline void nlua_push_type(lua_State *lstate, ObjectType type) /// @param[in] narr Number of “array” entries to be populated later. /// @param[in] nrec Number of “dictionary” entries to be populated later. /// @param[in] type Type of the table. -static inline void nlua_create_typed_table(lua_State *lstate, - const size_t narr, - const size_t nrec, +static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr, const size_t nrec, const ObjectType type) FUNC_ATTR_NONNULL_ALL { @@ -723,8 +718,7 @@ void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special) /// Convert given Dictionary to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, - bool special) +void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special) FUNC_ATTR_NONNULL_ALL { if (dict.size == 0 && special) { @@ -757,11 +751,11 @@ void nlua_push_Array(lua_State *lstate, const Array array, bool special) } #define GENERATE_INDEX_FUNCTION(type) \ -void nlua_push_##type(lua_State *lstate, const type item, bool special) \ + void nlua_push_##type(lua_State *lstate, const type item, bool special) \ FUNC_ATTR_NONNULL_ALL \ -{ \ - lua_pushnumber(lstate, (lua_Number)(item)); \ -} + { \ + lua_pushnumber(lstate, (lua_Number)(item)); \ + } GENERATE_INDEX_FUNCTION(Buffer) GENERATE_INDEX_FUNCTION(Window) @@ -776,23 +770,22 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) FUNC_ATTR_NONNULL_ALL { switch (obj.type) { - case kObjectTypeNil: { - if (special) { - lua_pushnil(lstate); - } else { - nlua_pushref(lstate, nlua_nil_ref); - } - break; - } - case kObjectTypeLuaRef: { - nlua_pushref(lstate, obj.data.luaref); - break; + case kObjectTypeNil: + if (special) { + lua_pushnil(lstate); + } else { + nlua_pushref(lstate, nlua_nil_ref); } + break; + case kObjectTypeLuaRef: { + nlua_pushref(lstate, obj.data.luaref); + break; + } #define ADD_TYPE(type, data_key) \ - case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key, special); \ - break; \ - } +case kObjectType##type: { \ + nlua_push_##type(lstate, obj.data.data_key, special); \ + break; \ +} ADD_TYPE(Boolean, boolean) ADD_TYPE(Integer, integer) ADD_TYPE(Float, floating) @@ -801,10 +794,10 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) ADD_TYPE(Dictionary, dictionary) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ - case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer, special); \ - break; \ - } +case kObjectType##type: { \ + nlua_push_##type(lstate, (type)obj.data.integer, special); \ + break; \ +} ADD_REMOTE_TYPE(Buffer) ADD_REMOTE_TYPE(Window) ADD_REMOTE_TYPE(Tabpage) @@ -873,8 +866,7 @@ Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) /// @param[in] type Type to check. /// /// @return @see nlua_traverse_table(). -static inline LuaTableProps nlua_check_type(lua_State *const lstate, - Error *const err, +static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *const err, const ObjectType type) FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -927,8 +919,7 @@ Float nlua_pop_Float(lua_State *lstate, Error *err) /// @param lstate Lua state. /// @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, +static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTableProps table_props, Error *const err) { Array ret = { .size = table_props.maxidx, .items = NULL }; @@ -980,10 +971,8 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) /// @param lstate Lua interpreter state. /// @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) +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 { Dictionary ret = { .size = table_props.string_keys_num, .items = NULL }; @@ -1060,15 +1049,16 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) { Object ret = NIL; const int initial_size = lua_gettop(lstate); - kvec_t(ObjPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((ObjPopStackItem) { &ret, false })); + kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((ObjPopStackItem) { &ret, false })); while (!ERROR_SET(err) && kv_size(stack)) { - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { - api_set_error(err, kErrorTypeException, "Lua failed to grow stack"); - break; - } ObjPopStackItem cur = kv_pop(stack); if (cur.container) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + api_set_error(err, kErrorTypeException, "Lua failed to grow stack"); + break; + } if (cur.obj->type == kObjectTypeDictionary) { // stack: …, dict, key if (cur.obj->data.dictionary.size @@ -1095,7 +1085,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) .data = xmemdupz(s, len), .size = len, }; - kv_push(stack, cur); + kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.dictionary.items[idx].value, .container = false, @@ -1117,7 +1107,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) lua_pop(lstate, 2); continue; } - kv_push(stack, cur); + kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.array.items[idx], .container = false, @@ -1127,119 +1117,110 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) assert(!cur.container); *cur.obj = NIL; switch (lua_type(lstate, -1)) { - case LUA_TNIL: { - break; - } - case LUA_TBOOLEAN: { - *cur.obj = BOOLEAN_OBJ(lua_toboolean(lstate, -1)); - break; - } - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -1, &len); - *cur.obj = STRING_OBJ(((String) { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + *cur.obj = BOOLEAN_OBJ(lua_toboolean(lstate, -1)); + break; + 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, })); - break; + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN + || ((lua_Number)((Integer)n)) != n) { + *cur.obj = FLOAT_OBJ((Float)n); + } else { + *cur.obj = INTEGER_OBJ((Integer)n); } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -1); - if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN - || ((lua_Number)((Integer)n)) != n) { - *cur.obj = FLOAT_OBJ((Float)n); - } else { - *cur.obj = INTEGER_OBJ((Integer)n); + break; + } + case LUA_TTABLE: { + const LuaTableProps table_props = nlua_traverse_table(lstate); + + switch (table_props.type) { + case kObjectTypeArray: + *cur.obj = ARRAY_OBJ(((Array) { + .items = NULL, + .size = 0, + .capacity = 0, + })); + 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.container = true; + kvi_push(stack, cur); } break; - } - case LUA_TTABLE: { - const LuaTableProps table_props = nlua_traverse_table(lstate); - - switch (table_props.type) { - case kObjectTypeArray: { - *cur.obj = ARRAY_OBJ(((Array) { - .items = NULL, - .size = 0, - .capacity = 0, - })); - 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.container = true; - kv_push(stack, cur); - } - break; - } - case kObjectTypeDictionary: { - *cur.obj = DICTIONARY_OBJ(((Dictionary) { - .items = NULL, - .size = 0, - .capacity = 0, - })); - 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.container = true; - kv_push(stack, cur); - lua_pushnil(lstate); - } - break; - } - case kObjectTypeFloat: { - *cur.obj = FLOAT_OBJ((Float)table_props.val); - break; - } - case kObjectTypeNil: { - api_set_error(err, kErrorTypeValidation, - "Cannot convert given lua table"); - break; - } - default: { - abort(); - } + case kObjectTypeDictionary: + *cur.obj = DICTIONARY_OBJ(((Dictionary) { + .items = NULL, + .size = 0, + .capacity = 0, + })); + 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.container = true; + kvi_push(stack, cur); + lua_pushnil(lstate); } break; - } - - case LUA_TFUNCTION: { - if (ref) { - *cur.obj = LUAREF_OBJ(nlua_ref(lstate, -1)); - } else { - goto type_error; - } + case kObjectTypeFloat: + *cur.obj = FLOAT_OBJ((Float)table_props.val); + break; + case kObjectTypeNil: + api_set_error(err, kErrorTypeValidation, + "Cannot convert given lua table"); break; + default: + abort(); } + break; + } - case LUA_TUSERDATA: { - nlua_pushref(lstate, nlua_nil_ref); - bool is_nil = lua_rawequal(lstate, -2, -1); - lua_pop(lstate, 1); - if (is_nil) { - *cur.obj = NIL; - } else { - api_set_error(err, kErrorTypeValidation, - "Cannot convert userdata"); - } - break; + case LUA_TFUNCTION: + if (ref) { + *cur.obj = LUAREF_OBJ(nlua_ref(lstate, -1)); + } else { + goto type_error; } + break; - default: { -type_error: + case LUA_TUSERDATA: { + nlua_pushref(lstate, nlua_nil_ref); + bool is_nil = lua_rawequal(lstate, -2, -1); + lua_pop(lstate, 1); + if (is_nil) { + *cur.obj = NIL; + } else { api_set_error(err, kErrorTypeValidation, - "Cannot convert given lua type"); - break; + "Cannot convert userdata"); } + break; + } + + default: +type_error: + api_set_error(err, kErrorTypeValidation, + "Cannot convert given lua type"); + break; } if (!cur.container) { lua_pop(lstate, 1); } } - kv_destroy(stack); + kvi_destroy(stack); if (ERROR_SET(err)) { api_free_object(ret); ret = NIL; @@ -1257,14 +1238,14 @@ LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) } #define GENERATE_INDEX_FUNCTION(type) \ -type nlua_pop_##type(lua_State *lstate, Error *err) \ + type nlua_pop_##type(lua_State *lstate, Error *err) \ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - type ret; \ - ret = (type)lua_tonumber(lstate, -1); \ - lua_pop(lstate, 1); \ - return ret; \ -} + { \ + type ret; \ + ret = (type)lua_tonumber(lstate, -1); \ + lua_pop(lstate, 1); \ + return ret; \ + } GENERATE_INDEX_FUNCTION(Buffer) GENERATE_INDEX_FUNCTION(Window) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 4d4286354b..9333d781cd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1,58 +1,60 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <lauxlib.h> #include <lua.h> #include <lualib.h> -#include <lauxlib.h> -#include "nvim/assert.h" -#include "nvim/version.h" -#include "nvim/misc1.h" -#include "nvim/getchar.h" -#include "nvim/garray.h" -#include "nvim/func_attr.h" +#include "luv/luv.h" +#include "mpack/lmpack.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/private/handle.h" #include "nvim/api/vim.h" -#include "nvim/msgpack_rpc/channel.h" -#include "nvim/vim.h" -#include "nvim/extmark.h" -#include "nvim/ex_getln.h" -#include "nvim/ex_cmds2.h" -#include "nvim/map.h" -#include "nvim/message.h" -#include "nvim/memline.h" -#include "nvim/buffer_defs.h" -#include "nvim/regexp.h" -#include "nvim/macros.h" -#include "nvim/screen.h" -#include "nvim/cursor.h" -#include "nvim/undo.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/userfunc.h" -#include "nvim/event/time.h" #include "nvim/event/loop.h" - -#include "nvim/os/os.h" - +#include "nvim/event/time.h" +#include "nvim/ex_cmds2.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/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/lua/treesitter.h" - -#include "luv/luv.h" +#include "nvim/lua/xdiff.h" +#include "nvim/macros.h" +#include "nvim/map.h" +#include "nvim/memline.h" +#include "nvim/message.h" +#include "nvim/misc1.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/os/os.h" +#include "nvim/regexp.h" +#include "nvim/screen.h" +#include "nvim/undo.h" +#include "nvim/version.h" +#include "nvim/vim.h" +#include "cjson/lua_cjson.h" static int in_fast_callback = 0; +// Initialized in nlua_init(). +static lua_State *global_lstate = NULL; + typedef struct { Error err; String lua_err_str; } LuaError; #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "lua/vim_module.generated.h" # include "lua/executor.c.generated.h" +# include "lua/vim_module.generated.h" #endif #define PUSH_ALL_TYPVALS(lstate, args, argcount, special) \ @@ -65,7 +67,8 @@ typedef struct { } #if __has_feature(address_sanitizer) - PMap(handle_T) *nlua_ref_markers = NULL; +static PMap(handle_T) nlua_ref_markers = MAP_INIT; +static bool nlua_track_refs = false; # define NLUA_TRACK_REFS #endif @@ -85,7 +88,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg) lua_pop(lstate, 1); } -/// Return version of current neovim build +/// Gets the version of the current Nvim build. /// /// @param lstate Lua interpreter state. static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL @@ -144,12 +147,12 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 1; } -/// convert byte index to UTF-32 and UTF-16 indicies +/// convert byte index to UTF-32 and UTF-16 indices /// /// Expects a string and an optional index. If no index is supplied, the length /// of the string is returned. /// -/// Returns two values: the UTF-32 and UTF-16 indicies. +/// Returns two values: the UTF-32 and UTF-16 indices. static int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { size_t s1_len; @@ -173,7 +176,7 @@ static int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 2; } -/// convert UTF-32 or UTF-16 indicies to byte index. +/// convert UTF-32 or UTF-16 indices to byte index. /// /// Expects up to three args: string, index and use_utf16. /// If use_utf16 is not supplied it defaults to false (use UTF-32) @@ -211,8 +214,7 @@ static void nlua_luv_error_event(void **argv) xfree(error); } -static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, - int flags) +static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags) FUNC_ATTR_NONNULL_ALL { int retval; @@ -234,7 +236,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, multiqueue_put(main_loop.events, nlua_luv_error_event, 1, xstrdup(error)); - lua_pop(lstate, 1); // error mesage + lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK if (nresult == LUA_MULTRET) { @@ -250,7 +252,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, static void nlua_schedule_event(void **argv) { LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; nlua_pushref(lstate, cb); nlua_unref(lstate, cb); if (lua_pcall(lstate, 0, 0, 0)) { @@ -295,8 +297,7 @@ static void dummy_timer_close_cb(TimeWatcher *tw, void *data) xfree(tw); } -static bool nlua_wait_condition(lua_State *lstate, int *status, - bool *callback_result) +static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result) { lua_pushvalue(lstate, 2); *status = lua_pcall(lstate, 0, 1, 0); @@ -331,9 +332,8 @@ static int nlua_wait(lua_State *lstate) } if (!is_function) { - lua_pushliteral( - lstate, - "vim.wait: if passed, condition must be a function"); + lua_pushliteral(lstate, + "vim.wait: if passed, condition must be a function"); return lua_error(lstate); } } @@ -360,23 +360,20 @@ static int nlua_wait(lua_State *lstate) time_watcher_init(&main_loop, tw, NULL); tw->events = loop_events; tw->blockable = true; - time_watcher_start( - tw, - dummy_timer_due_cb, - (uint64_t)interval, - (uint64_t)interval); + time_watcher_start(tw, + dummy_timer_due_cb, + (uint64_t)interval, + (uint64_t)interval); int pcall_status = 0; bool callback_result = false; - LOOP_PROCESS_EVENTS_UNTIL( - &main_loop, - loop_events, - (int)timeout, - is_function ? nlua_wait_condition( - lstate, - &pcall_status, - &callback_result) : false || got_int); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, + loop_events, + (int)timeout, + is_function ? nlua_wait_condition(lstate, + &pcall_status, + &callback_result) : false || got_int); // Stop dummy timer time_watcher_stop(tw); @@ -502,6 +499,8 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "__tostring"); lua_setmetatable(lstate, -2); nlua_nil_ref = nlua_ref(lstate, -1); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL"); lua_setfield(lstate, -2, "NIL"); // vim._empty_dict_mt @@ -509,11 +508,33 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_empty_dict_tostring); lua_setfield(lstate, -2, "__tostring"); nlua_empty_dict_ref = nlua_ref(lstate, -1); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); lua_setfield(lstate, -2, "_empty_dict_mt"); + // vim.mpack + luaopen_mpack(lstate); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, -3, "mpack"); + + // package.loaded.mpack = vim.mpack + // otherwise luv will be reinitialized when require'mpack' + lua_getglobal(lstate, "package"); + lua_getfield(lstate, -1, "loaded"); + lua_pushvalue(lstate, -3); + lua_setfield(lstate, -2, "mpack"); + lua_pop(lstate, 3); + // internal vim._treesitter... API nlua_add_treesitter(lstate); + // vim.diff + lua_pushcfunction(lstate, &nlua_xdl_diff); + lua_setfield(lstate, -2, "diff"); + + lua_cjson_new(lstate); + lua_setfield(lstate, -2, "json"); + lua_setglobal(lstate, "vim"); { @@ -536,8 +557,17 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 1; } // [package, loaded, inspect] - lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] + + code = (char *)&lua_F_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua") + || lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim.F"); // [package, loaded] + lua_pop(lstate, 2); // [] } @@ -550,22 +580,34 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } } + { + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "loaded"); // [package, loaded] + + const char *code = (char *)&lua_meta_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua") + || lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim._meta"); // [package, loaded] + + lua_pop(lstate, 2); // [] + } + return 0; } -/// Initialize lua interpreter -/// -/// Crashes Nvim if initialization fails. Should be called once per lua -/// interpreter instance. +/// Initialize global lua interpreter /// -/// @return New lua interpreter instance. -static lua_State *nlua_init(void) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +/// Crashes Nvim if initialization fails. +void nlua_init(void) { #ifdef NLUA_TRACK_REFS const char *env = os_getenv("NVIM_LUA_NOTRACK"); if (!env || !*env) { - nlua_ref_markers = pmap_new(handle_T)(); + nlua_track_refs = true; } #endif @@ -577,28 +619,9 @@ static lua_State *nlua_init(void) luaL_openlibs(lstate); nlua_state_init(lstate); - return lstate; + global_lstate = lstate; } -// only to be used by nlua_enter and nlua_free_all_mem! -static lua_State *global_lstate = NULL; - -/// Enter lua interpreter -/// -/// Calls nlua_init() if needed. Is responsible for pre-lua call initalization -/// like updating `package.[c]path` with directories derived from &runtimepath. -/// -/// @return Interpreter instance to use. Will either be initialized now or -/// taken from previous initialization. -static lua_State *nlua_enter(void) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (global_lstate == NULL) { - global_lstate = nlua_init(); - } - lua_State *const lstate = global_lstate; - return lstate; -} void nlua_free_all_mem(void) { @@ -615,10 +638,10 @@ void nlua_free_all_mem(void) fprintf(stderr, "%d lua references were leaked!", nlua_refcount); } - if (nlua_ref_markers) { + if (nlua_track_refs) { // in case there are leaked luarefs, leak the associated memory // to get LeakSanitizer stacktraces on exit - pmap_free(handle_T)(nlua_ref_markers); + pmap_destroy(handle_T)(&nlua_ref_markers); } #endif @@ -635,22 +658,19 @@ static void nlua_print_event(void **argv) const size_t start = i; while (i < len) { switch (str[i]) { - case NUL: { - str[i] = NL; - i++; - continue; - } - case NL: { - // TODO(bfredl): use proper multiline msg? Probably should implement - // print() in lua in terms of nvim_message(), when it is available. - str[i] = NUL; - i++; - break; - } - default: { - i++; - continue; - } + case NUL: + str[i] = NL; + i++; + continue; + case NL: + // TODO(bfredl): use proper multiline msg? Probably should implement + // print() in lua in terms of nvim_message(), when it is available. + str[i] = NUL; + i++; + break; + default: + i++; + continue; } break; } @@ -691,8 +711,7 @@ static int nlua_print(lua_State *const lstate) size_t len; const char *const s = lua_tolstring(lstate, -1, &len); if (s == NULL) { - PRINT_ERROR( - "<Unknown error: lua_tolstring returned NULL for tostring result>"); + PRINT_ERROR("<Unknown error: lua_tolstring returned NULL for tostring result>"); } ga_concat_len(&msg_ga, s, len); if (curargidx < nargs) { @@ -800,13 +819,13 @@ int nlua_call(lua_State *lstate) try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.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(name, (int)name_len, &rettv, nargs, - vim_args, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, NULL); + (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe); if (!try_end(&err)) { nlua_push_typval(lstate, &rettv, false); } @@ -865,7 +884,7 @@ static int nlua_rpc(lua_State *lstate, bool request) } else { if (!rpc_send_event(chan_id, name, args)) { api_set_error(&err, kErrorTypeValidation, - "Invalid channel: %"PRIu64, chan_id); + "Invalid channel: %" PRIu64, chan_id); } } @@ -1017,10 +1036,10 @@ LuaRef nlua_ref(lua_State *lstate, int index) if (ref > 0) { nlua_refcount++; #ifdef NLUA_TRACK_REFS - if (nlua_ref_markers) { - // dummy allocation to make LeakSanitizer track our luarefs - pmap_put(handle_T)(nlua_ref_markers, ref, xmalloc(3)); - } + if (nlua_track_refs) { + // dummy allocation to make LeakSanitizer track our luarefs + pmap_put(handle_T)(&nlua_ref_markers, ref, xmalloc(3)); + } #endif } return ref; @@ -1033,8 +1052,8 @@ void nlua_unref(lua_State *lstate, LuaRef ref) nlua_refcount--; #ifdef NLUA_TRACK_REFS // NB: don't remove entry from map to track double-unref - if (nlua_ref_markers) { - xfree(pmap_get(handle_T)(nlua_ref_markers, ref)); + if (nlua_track_refs) { + xfree(pmap_get(handle_T)(&nlua_ref_markers, ref)); } #endif luaL_unref(lstate, LUA_REGISTRYINDEX, ref); @@ -1043,8 +1062,7 @@ void nlua_unref(lua_State *lstate, LuaRef ref) void api_free_luaref(LuaRef ref) { - lua_State *const lstate = nlua_enter(); - nlua_unref(lstate, ref); + nlua_unref(global_lstate, ref); } /// push a value referenced in the registry @@ -1064,7 +1082,7 @@ LuaRef api_new_luaref(LuaRef original_ref) return LUA_NOREF; } - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; nlua_pushref(lstate, original_ref); LuaRef new_ref = nlua_ref(lstate, -1); lua_pop(lstate, 1); @@ -1081,8 +1099,7 @@ LuaRef api_new_luaref(LuaRef original_ref) /// @param[out] ret_tv Location where result will be saved. /// /// @return Result of the execution. -void nlua_typval_eval(const String str, typval_T *const arg, - typval_T *const ret_tv) +void nlua_typval_eval(const String str, typval_T *const arg, typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { #define EVALHEADER "local _A=select(1,...) return (" @@ -1104,8 +1121,8 @@ void nlua_typval_eval(const String str, typval_T *const arg, } } -void nlua_typval_call(const char *str, size_t len, typval_T *const args, - int argcount, typval_T *ret_tv) +void nlua_typval_call(const char *str, size_t len, typval_T *const args, int argcount, + typval_T *ret_tv) FUNC_ATTR_NONNULL_ALL { #define CALLHEADER "return " @@ -1131,9 +1148,8 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, } } -static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, - const char *name, typval_T *const args, - int argcount, bool special, typval_T *ret_tv) +static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name, + typval_T *const args, int argcount, bool special, typval_T *ret_tv) { if (check_secure()) { if (ret_tv) { @@ -1143,7 +1159,7 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, return; } - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; if (luaL_loadbuffer(lstate, lcmd, lcmd_len, name)) { nlua_error(lstate, _("E5107: Error loading lua %.*s")); return; @@ -1161,8 +1177,7 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, } } -int nlua_source_using_linegetter(LineGetter fgetline, - void *cookie, char *name) +int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) { const linenr_T save_sourcing_lnum = sourcing_lnum; const sctx_T save_current_sctx = current_sctx; @@ -1198,13 +1213,8 @@ int nlua_source_using_linegetter(LineGetter fgetline, /// @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(lua_State *lstate, LuaCallable lua_cb, int argcount, typval_T *argvars, + typval_T *rettv) { LuaRef cb = lua_cb.func_ref; @@ -1233,7 +1243,7 @@ int typval_exec_lua_callable( /// @return Return value of the execution. Object nlua_exec(const String str, const Array args, Error *err) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; if (luaL_loadbuffer(lstate, str.data, str.size, "<nvim>")) { size_t len; @@ -1267,10 +1277,9 @@ Object nlua_exec(const String str, const Array args, Error *err) /// if false, discard return value /// @param err Error details, if any (if NULL, errors are echoed) /// @return Return value of function, if retval was set. Otherwise NIL. -Object nlua_call_ref(LuaRef ref, const char *name, Array args, - bool retval, Error *err) +Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; nlua_pushref(lstate, ref); int nargs = (int)args.size; if (name != NULL) { @@ -1346,7 +1355,7 @@ void ex_luado(exarg_T *const eap) const char *const cmd = (const char *)eap->arg; const size_t cmd_len = strlen(cmd); - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; #define DOSTART "return function(line, linenr) " #define DOEND " end" @@ -1431,7 +1440,7 @@ void ex_luafile(exarg_T *const eap) bool nlua_exec_file(const char *path) FUNC_ATTR_NONNULL_ALL { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; if (luaL_loadfile(lstate, path)) { nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); @@ -1475,12 +1484,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_ts_get_language_version"); } -int nlua_expand_pat(expand_T *xp, - char_u *pat, - int *num_results, - char_u ***results) +int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; int ret = OK; // [ vim ] @@ -1490,13 +1496,12 @@ int nlua_expand_pat(expand_T *xp, lua_getfield(lstate, -1, "_expand_pat"); luaL_checktype(lstate, -1, LUA_TFUNCTION); - // [ vim, vim._log_keystroke, buf ] + // [ vim, vim._on_key, buf ] lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); if (lua_pcall(lstate, 1, 2, 0) != 0) { - nlua_error( - lstate, - _("Error executing vim._expand_pat: %.*s")); + nlua_error(lstate, + _("Error executing vim._expand_pat: %.*s")); return FAIL; } @@ -1527,10 +1532,9 @@ int nlua_expand_pat(expand_T *xp, goto cleanup_array; } - GA_APPEND( - char_u *, - &result_array, - vim_strsave((char_u *)v.data.string.data)); + GA_APPEND(char_u *, + &result_array, + vim_strsave((char_u *)v.data.string.data)); } xp->xp_pattern += prefix_len; @@ -1684,26 +1688,22 @@ static int regex_match_line(lua_State *lstate) // Required functions for lua c functions as VimL callbacks -int nlua_CFunction_func_call( - int argcount, - typval_T *argvars, - typval_T *rettv, - void *state) +int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state) { - lua_State *const lstate = nlua_enter(); - LuaCFunctionState *funcstate = (LuaCFunctionState *)state; + lua_State *const lstate = global_lstate; + LuaCFunctionState *funcstate = (LuaCFunctionState *)state; - return typval_exec_lua_callable(lstate, funcstate->lua_callable, - argcount, argvars, rettv); + return typval_exec_lua_callable(lstate, funcstate->lua_callable, + argcount, argvars, rettv); } void nlua_CFunction_func_free(void *state) { - lua_State *const lstate = nlua_enter(); - LuaCFunctionState *funcstate = (LuaCFunctionState *)state; + lua_State *const lstate = global_lstate; + LuaCFunctionState *funcstate = (LuaCFunctionState *)state; - nlua_unref(lstate, funcstate->lua_callable.func_ref); - xfree(funcstate); + nlua_unref(lstate, funcstate->lua_callable.func_ref); + xfree(funcstate); } bool nlua_is_table_from_lua(typval_T *const arg) @@ -1730,7 +1730,7 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) return NULL; } - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; #ifndef NDEBUG int top = lua_gettop(lstate); @@ -1764,12 +1764,12 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) return name; } -void nlua_execute_log_keystroke(int c) +void nlua_execute_on_key(int c) { char_u buf[NUMBUFLEN]; size_t buf_len = special_to_buf(c, mod_mask, false, buf); - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; #ifndef NDEBUG int top = lua_gettop(lstate); @@ -1778,17 +1778,16 @@ void nlua_execute_log_keystroke(int c) // [ vim ] lua_getglobal(lstate, "vim"); - // [ vim, vim._log_keystroke ] - lua_getfield(lstate, -1, "_log_keystroke"); + // [ vim, vim._on_key] + lua_getfield(lstate, -1, "_on_key"); luaL_checktype(lstate, -1, LUA_TFUNCTION); - // [ vim, vim._log_keystroke, buf ] + // [ vim, vim._on_key, buf ] lua_pushlstring(lstate, (const char *)buf, buf_len); if (lua_pcall(lstate, 1, 0, 0)) { - nlua_error( - lstate, - _("Error executing vim.log_keystroke lua callback: %.*s")); + nlua_error(lstate, + _("Error executing vim.on_key Lua callback: %.*s")); } // [ vim ] diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index e3fa48f530..37929093e3 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -5,22 +5,20 @@ // NB: this file mostly contains a generic lua interface for tree-sitter // trees and nodes, and could be broken out as a reusable lua package -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <inttypes.h> #include <assert.h> - +#include <inttypes.h> +#include <lauxlib.h> #include <lua.h> #include <lualib.h> -#include <lauxlib.h> - -#include "tree_sitter/api.h" +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "nvim/api/private/helpers.h" +#include "nvim/buffer.h" #include "nvim/lua/treesitter.h" -#include "nvim/api/private/handle.h" #include "nvim/memline.h" -#include "nvim/buffer.h" +#include "tree_sitter/api.h" #define TS_META_PARSER "treesitter_parser" #define TS_META_TREE "treesitter_tree" @@ -80,6 +78,10 @@ static struct luaL_Reg node_meta[] = { { "parent", node_parent }, { "iter_children", node_iter_children }, { "_rawquery", node_rawquery }, + { "next_sibling", node_next_sibling }, + { "prev_sibling", node_prev_sibling }, + { "next_named_sibling", node_next_named_sibling }, + { "prev_named_sibling", node_prev_named_sibling }, { NULL, NULL } }; @@ -101,7 +103,7 @@ static struct luaL_Reg treecursor_meta[] = { { NULL, NULL } }; -static PMap(cstr_t) *langs; +static PMap(cstr_t) langs = MAP_INIT; static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) { @@ -119,8 +121,6 @@ static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) /// all global state is stored in the regirstry of the lua_State void tslua_init(lua_State *L) { - langs = pmap_new(cstr_t)(); - // type metatables build_meta(L, TS_META_PARSER, parser_meta); build_meta(L, TS_META_TREE, tree_meta); @@ -133,7 +133,7 @@ void tslua_init(lua_State *L) int tslua_has_language(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); - lua_pushboolean(L, pmap_has(cstr_t)(langs, lang_name)); + lua_pushboolean(L, pmap_has(cstr_t)(&langs, lang_name)); return 1; } @@ -142,7 +142,7 @@ int tslua_add_language(lua_State *L) const char *path = luaL_checkstring(L, 1); const char *lang_name = luaL_checkstring(L, 2); - if (pmap_has(cstr_t)(langs, lang_name)) { + if (pmap_has(cstr_t)(&langs, lang_name)) { return 0; } @@ -177,15 +177,14 @@ int tslua_add_language(lua_State *L) uint32_t lang_version = ts_language_version(lang); if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION || lang_version > TREE_SITTER_LANGUAGE_VERSION) { - return luaL_error( - L, - "ABI version mismatch for %s: supported between %d and %d, found %d", - path, - TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, - TREE_SITTER_LANGUAGE_VERSION, lang_version); + return luaL_error(L, + "ABI version mismatch for %s: supported between %d and %d, found %d", + path, + TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, + TREE_SITTER_LANGUAGE_VERSION, lang_version); } - pmap_put(cstr_t)(langs, xstrdup(lang_name), lang); + pmap_put(cstr_t)(&langs, xstrdup(lang_name), lang); lua_pushboolean(L, true); return 1; @@ -195,7 +194,7 @@ int tslua_inspect_lang(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); - TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } @@ -243,7 +242,7 @@ int tslua_push_parser(lua_State *L) // Gather language name const char *lang_name = luaL_checkstring(L, 1); - TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } @@ -261,7 +260,7 @@ int tslua_push_parser(lua_State *L) return 1; } -static TSParser ** parser_check(lua_State *L, uint16_t index) +static TSParser **parser_check(lua_State *L, uint16_t index) { return luaL_checkudata(L, index, TS_META_PARSER); } @@ -283,8 +282,8 @@ static int parser_tostring(lua_State *L) return 1; } -static const char *input_cb(void *payload, uint32_t byte_index, - TSPoint position, uint32_t *bytes_read) +static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position, + uint32_t *bytes_read) { buf_T *bp = payload; #define BUFSIZE 256 @@ -316,9 +315,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, #undef BUFSIZE } -static void push_ranges(lua_State *L, - const TSRange *ranges, - const unsigned int length) +static void push_ranges(lua_State *L, const TSRange *ranges, const unsigned int length) { lua_createtable(L, length, 0); for (size_t i = 0; i < length; i++) { @@ -359,40 +356,39 @@ static int parser_parse(lua_State *L) // This switch is necessary because of the behavior of lua_isstring, that // consider numbers as strings... switch (lua_type(L, 3)) { - case LUA_TSTRING: - str = lua_tolstring(L, 3, &len); - new_tree = ts_parser_parse_string(*p, old_tree, str, len); - break; + case LUA_TSTRING: + str = lua_tolstring(L, 3, &len); + new_tree = ts_parser_parse_string(*p, old_tree, str, len); + break; - case LUA_TNUMBER: - bufnr = lua_tointeger(L, 3); - buf = handle_get_buffer(bufnr); + case LUA_TNUMBER: + bufnr = lua_tointeger(L, 3); + buf = handle_get_buffer(bufnr); - if (!buf) { - return luaL_error(L, "invalid buffer handle: %d", bufnr); - } + if (!buf) { + return luaL_error(L, "invalid buffer handle: %d", bufnr); + } - input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 }; - new_tree = ts_parser_parse(*p, old_tree, input); + input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 }; + new_tree = ts_parser_parse(*p, old_tree, input); - break; + break; - default: - return luaL_error(L, "invalid argument to parser:parse()"); + default: + return luaL_error(L, "invalid argument to parser:parse()"); } // Sometimes parsing fails (timeout, or wrong parser ABI) // In those case, just return an error. if (!new_tree) { - return luaL_error(L, "An error occured when parsing."); + return luaL_error(L, "An error occurred when parsing."); } // The new tree will be pushed to the stack, without copy, owwership is now to // the lua GC. // Old tree is still owned by the lua GC. uint32_t n_ranges = 0; - TSRange *changed = old_tree ? ts_tree_get_changed_ranges( - old_tree, new_tree, &n_ranges) : NULL; + TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL; push_tree(L, new_tree, false); // [tree] @@ -502,17 +498,15 @@ static void range_from_lua(lua_State *L, TSRange *range) } return; error: - luaL_error( - L, - "Ranges can only be made from 6 element long tables or nodes."); + luaL_error(L, + "Ranges can only be made from 6 element long tables or nodes."); } static int parser_set_ranges(lua_State *L) { if (lua_gettop(L) < 2) { - return luaL_error( - L, - "not enough args to parser:set_included_ranges()"); + return luaL_error(L, + "not enough args to parser:set_included_ranges()"); } TSParser **p = parser_check(L, 1); @@ -521,9 +515,8 @@ static int parser_set_ranges(lua_State *L) } if (!lua_istable(L, 2)) { - return luaL_error( - L, - "argument for parser:set_included_ranges() should be a table."); + return luaL_error(L, + "argument for parser:set_included_ranges() should be a table."); } size_t tbl_len = lua_objlen(L, 2); @@ -888,9 +881,9 @@ static int node_descendant_for_range(lua_State *L) return 0; } TSPoint start = { (uint32_t)lua_tointeger(L, 2), - (uint32_t)lua_tointeger(L, 3) }; + (uint32_t)lua_tointeger(L, 3) }; TSPoint end = { (uint32_t)lua_tointeger(L, 4), - (uint32_t)lua_tointeger(L, 5) }; + (uint32_t)lua_tointeger(L, 5) }; TSNode child = ts_node_descendant_for_point_range(node, start, end); push_node(L, child, 1); @@ -904,9 +897,9 @@ static int node_named_descendant_for_range(lua_State *L) return 0; } TSPoint start = { (uint32_t)lua_tointeger(L, 2), - (uint32_t)lua_tointeger(L, 3) }; + (uint32_t)lua_tointeger(L, 3) }; TSPoint end = { (uint32_t)lua_tointeger(L, 4), - (uint32_t)lua_tointeger(L, 5) }; + (uint32_t)lua_tointeger(L, 5) }; TSNode child = ts_node_named_descendant_for_point_range(node, start, end); push_node(L, child, 1); @@ -915,8 +908,7 @@ static int node_named_descendant_for_range(lua_State *L) static int node_next_child(lua_State *L) { - TSTreeCursor *ud = luaL_checkudata( - L, lua_upvalueindex(1), TS_META_TREECURSOR); + TSTreeCursor *ud = luaL_checkudata(L, lua_upvalueindex(1), TS_META_TREECURSOR); if (!ud) { return 0; } @@ -937,19 +929,18 @@ static int node_next_child(lua_State *L) if (ts_tree_cursor_goto_next_sibling(ud)) { push: - push_node( - L, - ts_tree_cursor_current_node(ud), - lua_upvalueindex(2)); // [node] + push_node(L, + ts_tree_cursor_current_node(ud), + lua_upvalueindex(2)); // [node] - const char * field = ts_tree_cursor_current_field_name(ud); + const char * field = ts_tree_cursor_current_field_name(ud); - if (field != NULL) { - lua_pushstring(L, ts_tree_cursor_current_field_name(ud)); - } else { - lua_pushnil(L); - } // [node, field_name_or_nil] - return 2; + if (field != NULL) { + lua_pushstring(L, ts_tree_cursor_current_field_name(ud)); + } else { + lua_pushnil(L); + } // [node, field_name_or_nil] + return 2; } end: @@ -992,6 +983,50 @@ static int node_parent(lua_State *L) return 1; } +static int node_next_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_next_sibling(node); + push_node(L, sibling, 1); + return 1; +} + +static int node_prev_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_prev_sibling(node); + push_node(L, sibling, 1); + return 1; +} + +static int node_next_named_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_next_named_sibling(node); + push_node(L, sibling, 1); + return 1; +} + +static int node_prev_named_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_prev_named_sibling(node); + push_node(L, sibling, 1); + return 1; +} + /// assumes the match table being on top of the stack static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { @@ -1127,7 +1162,7 @@ int tslua_parse_query(lua_State *L) } const char *lang_name = lua_tostring(L, 1); - TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } @@ -1154,11 +1189,16 @@ int tslua_parse_query(lua_State *L) static const char *query_err_string(TSQueryError err) { switch (err) { - case TSQueryErrorSyntax: return "invalid syntax"; - case TSQueryErrorNodeType: return "invalid node type"; - case TSQueryErrorField: return "invalid field"; - case TSQueryErrorCapture: return "invalid capture"; - default: return "error"; + case TSQueryErrorSyntax: + return "invalid syntax"; + case TSQueryErrorNodeType: + return "invalid node type"; + case TSQueryErrorField: + return "invalid field"; + case TSQueryErrorCapture: + return "invalid capture"; + default: + return "error"; } } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 8cecaa51dd..7a209f2d79 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -4,6 +4,7 @@ -- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the -- `inspect` and `lpeg` modules. -- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests. +-- (This will go away if we migrate to nvim as the test-runner.) -- 3. src/nvim/lua/: Compiled-into Nvim itself. -- -- Guideline: "If in doubt, put it in the runtime". @@ -104,6 +105,12 @@ setmetatable(vim, { elseif key == 'highlight' then t.highlight = require('vim.highlight') return t.highlight + elseif key == 'diagnostic' then + t.diagnostic = require('vim.diagnostic') + return t.diagnostic + elseif key == 'ui' then + t.ui = require('vim.ui') + return t.ui end end }) @@ -178,8 +185,8 @@ end --- Return a human-readable representation of the given object. --- ---@see https://github.com/kikito/inspect.lua ---@see https://github.com/mpeterv/vinspect +---@see https://github.com/kikito/inspect.lua +---@see https://github.com/mpeterv/vinspect local function inspect(object, options) -- luacheck: no unused error(object, options) -- Stub for gen_vimdoc.py end @@ -203,15 +210,15 @@ do --- end)(vim.paste) --- </pre> --- - --@see |paste| + ---@see |paste| --- - --@param lines |readfile()|-style list of lines to paste. |channel-lines| - --@param phase -1: "non-streaming" paste: the call contains all lines. + ---@param lines |readfile()|-style list of lines to paste. |channel-lines| + ---@param phase -1: "non-streaming" paste: the call contains all lines. --- If paste is "streamed", `phase` indicates the stream state: --- - 1: starts the paste (exactly once) --- - 2: continues the paste (zero or more times) --- - 3: ends the paste (exactly once) - --@returns false if client should cancel the paste. + ---@returns false if client should cancel the paste. function vim.paste(lines, phase) local call = vim.api.nvim_call_function local now = vim.loop.now() @@ -273,13 +280,13 @@ end ---@see |vim.in_fast_event()| function vim.schedule_wrap(cb) return (function (...) - local args = {...} - vim.schedule(function() cb(unpack(args)) end) + local args = vim.F.pack_len(...) + vim.schedule(function() cb(vim.F.unpack_len(args)) end) end) end --- <Docs described in |vim.empty_dict()| > ---@private +---@private function vim.empty_dict() return setmetatable({}, vim._empty_dict_mt) end @@ -338,12 +345,12 @@ end --- Get a table of lines with start, end columns for a region marked by two points --- ---@param bufnr number of buffer ---@param pos1 (line, column) tuple marking beginning of region ---@param pos2 (line, column) tuple marking end of region ---@param regtype type of selection (:help setreg) ---@param inclusive boolean indicating whether the selection is end-inclusive ---@return region lua table of the form {linenr = {startcol,endcol}} +---@param bufnr number of buffer +---@param pos1 (line, column) tuple marking beginning of region +---@param pos2 (line, column) tuple marking end of region +---@param regtype type of selection (:help setreg) +---@param inclusive boolean indicating whether the selection is end-inclusive +---@return region lua table of the form {linenr = {startcol,endcol}} function vim.region(bufnr, pos1, pos2, regtype, inclusive) if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) @@ -390,9 +397,9 @@ end --- Use to do a one-shot timer that calls `fn` --- Note: The {fn} is |schedule_wrap|ped automatically, so API functions are --- safe to call. ---@param fn Callback to call once `timeout` expires ---@param timeout Number of milliseconds to wait before calling `fn` ---@return timer luv timer object +---@param fn Callback to call once `timeout` expires +---@param timeout Number of milliseconds to wait before calling `fn` +---@return timer luv timer object function vim.defer_fn(fn, timeout) vim.validate { fn = { fn, 'c', true}; } local timer = vim.loop.new_timer() @@ -408,11 +415,12 @@ end --- Notification provider ---- without a runtime, writes to :Messages --- see :help nvim_notify ---@param msg Content of the notification to show to the user ---@param log_level Optional log level ---@param opts Dictionary with optional options (timeout, etc) +--- +--- Without a runtime, writes to :Messages +---@see :help nvim_notify +---@param msg Content of the notification to show to the user +---@param log_level Optional log level +---@param opts Dictionary with optional options (timeout, etc) function vim.notify(msg, log_level, _opts) if log_level == vim.log.levels.ERROR then @@ -425,26 +433,35 @@ function vim.notify(msg, log_level, _opts) end -local on_keystroke_callbacks = {} +function vim.register_keystroke_callback() + error('vim.register_keystroke_callback is deprecated, instead use: vim.on_key') +end + +local on_key_cbs = {} ---- Register a lua {fn} with an {id} to be run after every keystroke. +--- Adds Lua function {fn} with namespace id {ns_id} as a listener to every, +--- yes every, input key. --- ---@param fn function: Function to call. It should take one argument, which is a string. ---- The string will contain the literal keys typed. ---- See |i_CTRL-V| +--- The Nvim command-line option |-w| is related but does not support callbacks +--- and cannot be toggled dynamically. --- +---@param fn function: Callback function. It should take one string argument. +--- On each key press, Nvim passes the key char to fn(). |i_CTRL-V| --- If {fn} is nil, it removes the callback for the associated {ns_id} ---@param ns_id number? Namespace ID. If not passed or 0, will generate and return a new ---- namespace ID from |nvim_create_namesapce()| +---@param ns_id number? Namespace ID. If nil or 0, generates and returns a new +--- |nvim_create_namesapce()| id. --- ---@return number Namespace ID associated with {fn} +---@return number Namespace id associated with {fn}. Or count of all callbacks +---if on_key() is called without arguments. --- ---@note {fn} will be automatically removed if an error occurs while calling. ---- This is to prevent the annoying situation of every keystroke erroring ---- while trying to remove a broken callback. ---@note {fn} will not be cleared from |nvim_buf_clear_namespace()| ---@note {fn} will receive the keystrokes after mappings have been evaluated -function vim.register_keystroke_callback(fn, ns_id) +---@note {fn} will be removed if an error occurs while calling. +---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| +---@note {fn} will receive the keys after mappings have been evaluated +function vim.on_key(fn, ns_id) + if fn == nil and ns_id == nil then + return #on_key_cbs + end + vim.validate { fn = { fn, 'c', true}, ns_id = { ns_id, 'n', true } @@ -454,20 +471,19 @@ function vim.register_keystroke_callback(fn, ns_id) ns_id = vim.api.nvim_create_namespace('') end - on_keystroke_callbacks[ns_id] = fn + on_key_cbs[ns_id] = fn return ns_id end ---- Function that executes the keystroke callbacks. ---@private -function vim._log_keystroke(char) +--- Executes the on_key callbacks. +---@private +function vim._on_key(char) local failed_ns_ids = {} local failed_messages = {} - for k, v in pairs(on_keystroke_callbacks) do + for k, v in pairs(on_key_cbs) do local ok, err_msg = pcall(v, char) if not ok then - vim.register_keystroke_callback(nil, k) - + vim.on_key(nil, k) table.insert(failed_ns_ids, k) table.insert(failed_messages, err_msg) end @@ -475,7 +491,7 @@ function vim._log_keystroke(char) if failed_ns_ids[1] then error(string.format( - "Error executing 'on_keystroke' with ns_ids of '%s'\n With messages: %s", + "Error executing 'on_key' with ns_ids '%s'\n Messages: %s", table.concat(failed_ns_ids, ", "), table.concat(failed_messages, "\n"))) end @@ -641,6 +657,4 @@ vim._expand_pat_get_parts = function(lua_string) return parts, search_index end -pcall(require, 'vim._meta') - return module diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c new file mode 100644 index 0000000000..3955fbe72c --- /dev/null +++ b/src/nvim/lua/xdiff.c @@ -0,0 +1,332 @@ +// 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 <string.h> + +#include "nvim/api/private/helpers.h" +#include "nvim/lua/converter.h" +#include "nvim/lua/executor.h" +#include "nvim/lua/xdiff.h" +#include "nvim/vim.h" +#include "xdiff/xdiff.h" + +typedef enum { + kNluaXdiffModeUnified = 0, + kNluaXdiffModeOnHunkCB, + kNluaXdiffModeLocations, +} NluaXdiffMode; + +typedef struct { + lua_State *lstate; + Error *err; +} hunkpriv_t; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/xdiff.c.generated.h" +#endif + +static int write_string(void *priv, mmbuffer_t *mb, int nbuf) +{ + luaL_Buffer *buf = (luaL_Buffer *)priv; + for (int i = 0; i < nbuf; i++) { + const long size = mb[i].size; + for (long total = 0; total < size; total += LUAL_BUFFERSIZE) { + const int tocopy = MIN((int)(size - total), LUAL_BUFFERSIZE); + char *p = luaL_prepbuffer(buf); + if (!p) { + return -1; + } + memcpy(p, mb[i].ptr + total, (unsigned)tocopy); + luaL_addsize(buf, (unsigned)tocopy); + } + } + return 0; +} + +// 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; + } + + 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; +} + +// hunk_func callback used when opts.on_hunk is given +static int call_on_hunk_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data) +{ + // 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; + Error *err = priv->err; + const int fidx = lua_gettop(lstate); + lua_pushvalue(lstate, fidx); + lua_pushinteger(lstate, start_a); + lua_pushinteger(lstate, count_a); + lua_pushinteger(lstate, start_b); + lua_pushinteger(lstate, count_b); + + if (lua_pcall(lstate, 4, 1, 0) != 0) { + api_set_error(err, kErrorTypeException, + "error running function on_hunk: %s", + lua_tostring(lstate, -1)); + return -1; + } + + int r = 0; + if (lua_isnumber(lstate, -1)) { + r = (int)lua_tonumber(lstate, -1); + } + + lua_pop(lstate, 1); + lua_settop(lstate, fidx); + return r; +} + +static mmfile_t get_string_arg(lua_State *lstate, int idx) +{ + if (lua_type(lstate, idx) != LUA_TSTRING) { + luaL_argerror(lstate, idx, "expected string"); + } + mmfile_t mf; + mf.ptr = (char *)lua_tolstring(lstate, idx, (size_t *)&mf.size); + 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, + Error *err) +{ + const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, 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)) { + } 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)) { + cfg->flags |= XDF_NEED_MINIMAL; + } else if (strequal("patience", v->data.string.data)) { + cfg->flags |= XDF_PATIENCE_DIFF; + } else if (strequal("histogram", v->data.string.data)) { + cfg->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 = v->data.integer; + } else if (strequal("interhunkctxlen", k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeInteger, "interhunkctxlen", + err)) { + goto exit_1; + } + cfg->interhunkctxlen = v->data.integer; + } 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; + } + } + + if (key_used) { + continue; + } + + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + goto exit_1; + } + } + + 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: + api_free_dictionary(opts); + return mode; +} + +int nlua_xdl_diff(lua_State *lstate) +{ + if (lua_gettop(lstate) < 2) { + return luaL_error(lstate, "Expected at least 2 arguments"); + } + mmfile_t ma = get_string_arg(lstate, 1); + mmfile_t mb = get_string_arg(lstate, 2); + + Error err = ERROR_INIT; + + xdemitconf_t cfg; + xpparam_t params; + xdemitcb_t ecb; + + memset(&cfg, 0, sizeof(cfg)); + memset(¶ms, 0, sizeof(params)); + memset(&ecb, 0, sizeof(ecb)); + + NluaXdiffMode mode = kNluaXdiffModeUnified; + + if (lua_gettop(lstate) == 3) { + if (lua_type(lstate, 3) != LUA_TTABLE) { + return luaL_argerror(lstate, 3, "expected table"); + } + + mode = process_xdl_diff_opts(lstate, &cfg, ¶ms, &err); + + if (ERROR_SET(&err)) { + goto exit_0; + } + } + + luaL_Buffer buf; + hunkpriv_t *priv = NULL; + switch (mode) { + case kNluaXdiffModeUnified: + luaL_buffinit(lstate, &buf); + ecb.priv = &buf; + ecb.out_line = write_string; + break; + case kNluaXdiffModeOnHunkCB: + priv = xmalloc(sizeof(*priv)); + priv->lstate = lstate; + priv->err = &err; + ecb.priv = priv; + break; + case kNluaXdiffModeLocations: + lua_createtable(lstate, 0, 0); + ecb.priv = lstate; + break; + } + + if (xdl_diff(&ma, &mb, ¶ms, &cfg, &ecb) == -1) { + if (!ERROR_SET(&err)) { + api_set_error(&err, kErrorTypeException, + "Error while performing diff operation"); + } + } + + XFREE_CLEAR(priv); + +exit_0: + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, 2); + return lua_error(lstate); + } else if (mode == kNluaXdiffModeUnified) { + luaL_pushresult(&buf); + return 1; + } else if (mode == kNluaXdiffModeLocations) { + return 1; + } + return 0; +} diff --git a/src/nvim/lua/xdiff.h b/src/nvim/lua/xdiff.h new file mode 100644 index 0000000000..cae7c98e81 --- /dev/null +++ b/src/nvim/lua/xdiff.h @@ -0,0 +1,12 @@ +#ifndef NVIM_LUA_XDIFF_H +#define NVIM_LUA_XDIFF_H + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/xdiff.h.generated.h" +#endif + +#endif // NVIM_LUA_XDIFF_H |