mirror of
https://github.com/neovim/neovim
synced 2025-07-20 13:22:26 +00:00
feat(lua): make vim.mpack support vim.NIL and vim.empty_dict()
This commit is contained in:
@ -686,6 +686,19 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
See {opts.result_type}. nil if {opts.on_hunk} is given.
|
See {opts.result_type}. nil if {opts.on_hunk} is given.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
VIM.MPACK *lua-mpack*
|
||||||
|
|
||||||
|
The *vim.mpack* module provides packing and unpacking of lua objects to
|
||||||
|
msgpack encoded strings. |vim.NIL| and |vim.empty_dict()| are supported.
|
||||||
|
|
||||||
|
vim.mpack.pack({obj}) *vim.mpack.pack*
|
||||||
|
Packs a lua object {obj} and returns the msgpack representation as
|
||||||
|
a string
|
||||||
|
|
||||||
|
vim.mpack.unpack({str}) *vim.mpack.unpack*
|
||||||
|
Unpacks the msgpack encoded {str} and returns a lua object
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
VIM *lua-builtin*
|
VIM *lua-builtin*
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#define PACKER_META_NAME "mpack.Packer"
|
#define PACKER_META_NAME "mpack.Packer"
|
||||||
#define SESSION_META_NAME "mpack.Session"
|
#define SESSION_META_NAME "mpack.Session"
|
||||||
#define NIL_NAME "mpack.NIL"
|
#define NIL_NAME "mpack.NIL"
|
||||||
|
#define EMPTY_DICT_NAME "mpack.empty_dict"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO(tarruda): When targeting lua 5.3 and being compiled with `long long`
|
* TODO(tarruda): When targeting lua 5.3 and being compiled with `long long`
|
||||||
@ -55,14 +56,14 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
mpack_parser_t *parser;
|
mpack_parser_t *parser;
|
||||||
int reg, ext, unpacking;
|
int reg, ext, unpacking, mtdict;
|
||||||
char *string_buffer;
|
char *string_buffer;
|
||||||
} Unpacker;
|
} Unpacker;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
mpack_parser_t *parser;
|
mpack_parser_t *parser;
|
||||||
int reg, ext, root, packing;
|
int reg, ext, root, packing, mtdict;
|
||||||
int is_bin, is_bin_fn;
|
int is_bin, is_bin_fn;
|
||||||
} Packer;
|
} Packer;
|
||||||
|
|
||||||
@ -234,7 +235,10 @@ static mpack_uint32_t lmpack_objlen(lua_State *L, int *is_array)
|
|||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
*is_array = isarr && max == len;
|
// when len==0, the caller should guess the type!
|
||||||
|
if (len > 0) {
|
||||||
|
*is_array = isarr && max == len;
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1)
|
if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1)
|
||||||
@ -268,6 +272,9 @@ static int lmpack_unpacker_new(lua_State *L)
|
|||||||
#endif
|
#endif
|
||||||
rv->ext = LUA_NOREF;
|
rv->ext = LUA_NOREF;
|
||||||
|
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
|
||||||
|
rv->mtdict = lmpack_ref(L, rv->reg);
|
||||||
|
|
||||||
if (lua_istable(L, 1)) {
|
if (lua_istable(L, 1)) {
|
||||||
/* parse options */
|
/* parse options */
|
||||||
lua_getfield(L, 1, "ext");
|
lua_getfield(L, 1, "ext");
|
||||||
@ -377,6 +384,11 @@ static void lmpack_parse_exit(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
case MPACK_TOKEN_MAP:
|
case MPACK_TOKEN_MAP:
|
||||||
lmpack_geti(L, unpacker->reg, (int)node->data[0].i);
|
lmpack_geti(L, unpacker->reg, (int)node->data[0].i);
|
||||||
lmpack_unref(L, unpacker->reg, (int)node->data[0].i);
|
lmpack_unref(L, unpacker->reg, (int)node->data[0].i);
|
||||||
|
if (node->key_visited == 0 && node->tok.type == MPACK_TOKEN_MAP) {
|
||||||
|
lmpack_geti(L, unpacker->reg, unpacker->mtdict); // [table, mtdict]
|
||||||
|
lua_setmetatable(L, -2); // [table]
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -506,6 +518,9 @@ static int lmpack_packer_new(lua_State *L)
|
|||||||
#endif
|
#endif
|
||||||
rv->ext = LUA_NOREF;
|
rv->ext = LUA_NOREF;
|
||||||
|
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
|
||||||
|
rv->mtdict = lmpack_ref(L, rv->reg);
|
||||||
|
|
||||||
if (lua_istable(L, 1)) {
|
if (lua_istable(L, 1)) {
|
||||||
/* parse options */
|
/* parse options */
|
||||||
lua_getfield(L, 1, "ext");
|
lua_getfield(L, 1, "ext");
|
||||||
@ -620,11 +635,11 @@ static void lmpack_unparse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LUA_TTABLE: {
|
case LUA_TTABLE: {
|
||||||
int is_array;
|
|
||||||
mpack_uint32_t len;
|
mpack_uint32_t len;
|
||||||
mpack_node_t *n;
|
mpack_node_t *n;
|
||||||
|
|
||||||
if (packer->ext != LUA_NOREF && lua_getmetatable(L, -1)) {
|
int has_meta = lua_getmetatable(L, -1);
|
||||||
|
if (packer->ext != LUA_NOREF && has_meta) {
|
||||||
/* check if there's a handler for this metatable */
|
/* check if there's a handler for this metatable */
|
||||||
lmpack_geti(L, packer->reg, packer->ext);
|
lmpack_geti(L, packer->reg, packer->ext);
|
||||||
lua_pushvalue(L, -2);
|
lua_pushvalue(L, -2);
|
||||||
@ -674,13 +689,24 @@ static void lmpack_unparse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
* -2: metatable
|
* -2: metatable
|
||||||
* -3: original table
|
* -3: original table
|
||||||
*
|
*
|
||||||
* We want to leave only the original table since it will be handled
|
* We want to leave only the original table and metatable since they
|
||||||
* below, so pop 2
|
* will be handled below, so pop 1
|
||||||
*/
|
*/
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int is_array = 1;
|
||||||
|
if (has_meta) {
|
||||||
|
// stack: [table, metatable]
|
||||||
|
if (packer->mtdict != LUA_NOREF) {
|
||||||
|
lmpack_geti(L, packer->reg, packer->mtdict); // [table, metatable, mtdict]
|
||||||
|
is_array = !lua_rawequal(L, -1, -2);
|
||||||
|
lua_pop(L, 1); // [table, metatable];
|
||||||
|
}
|
||||||
|
lua_pop(L, 1); // [table]
|
||||||
|
}
|
||||||
|
|
||||||
/* check for cycles */
|
/* check for cycles */
|
||||||
n = node;
|
n = node;
|
||||||
while ((n = MPACK_PARENT_NODE(n))) {
|
while ((n = MPACK_PARENT_NODE(n))) {
|
||||||
@ -1031,6 +1057,9 @@ static int lmpack_unpack(lua_State *L)
|
|||||||
unpacker.string_buffer = NULL;
|
unpacker.string_buffer = NULL;
|
||||||
unpacker.L = L;
|
unpacker.L = L;
|
||||||
|
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
|
||||||
|
unpacker.mtdict = lmpack_ref(L, unpacker.reg);
|
||||||
|
|
||||||
result = mpack_parse(&parser, &str, &len, lmpack_parse_enter,
|
result = mpack_parse(&parser, &str, &len, lmpack_parse_enter,
|
||||||
lmpack_parse_exit);
|
lmpack_parse_exit);
|
||||||
|
|
||||||
@ -1072,6 +1101,10 @@ static int lmpack_pack(lua_State *L)
|
|||||||
packer.L = L;
|
packer.L = L;
|
||||||
packer.root = lmpack_ref(L, packer.reg);
|
packer.root = lmpack_ref(L, packer.reg);
|
||||||
|
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
|
||||||
|
packer.mtdict = lmpack_ref(L, packer.reg);
|
||||||
|
|
||||||
|
|
||||||
luaL_buffinit(L, &buffer);
|
luaL_buffinit(L, &buffer);
|
||||||
b = luaL_prepbuffer(&buffer);
|
b = luaL_prepbuffer(&buffer);
|
||||||
bl = LUAL_BUFFERSIZE;
|
bl = LUAL_BUFFERSIZE;
|
||||||
|
@ -507,8 +507,18 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
lua_setfield(lstate, -2, "__tostring");
|
lua_setfield(lstate, -2, "__tostring");
|
||||||
lua_setmetatable(lstate, -2);
|
lua_setmetatable(lstate, -2);
|
||||||
nlua_nil_ref = nlua_ref(lstate, -1);
|
nlua_nil_ref = nlua_ref(lstate, -1);
|
||||||
|
lua_pushvalue(lstate, -1);
|
||||||
|
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL");
|
||||||
lua_setfield(lstate, -2, "NIL");
|
lua_setfield(lstate, -2, "NIL");
|
||||||
|
|
||||||
|
// vim._empty_dict_mt
|
||||||
|
lua_createtable(lstate, 0, 0);
|
||||||
|
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
|
// vim.mpack
|
||||||
luaopen_mpack(lstate);
|
luaopen_mpack(lstate);
|
||||||
@ -523,14 +533,6 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
lua_setfield(lstate, -2, "mpack");
|
lua_setfield(lstate, -2, "mpack");
|
||||||
lua_pop(lstate, 3);
|
lua_pop(lstate, 3);
|
||||||
|
|
||||||
|
|
||||||
// vim._empty_dict_mt
|
|
||||||
lua_createtable(lstate, 0, 0);
|
|
||||||
lua_pushcfunction(lstate, &nlua_empty_dict_tostring);
|
|
||||||
lua_setfield(lstate, -2, "__tostring");
|
|
||||||
nlua_empty_dict_ref = nlua_ref(lstate, -1);
|
|
||||||
lua_setfield(lstate, -2, "_empty_dict_mt");
|
|
||||||
|
|
||||||
// internal vim._treesitter... API
|
// internal vim._treesitter... API
|
||||||
nlua_add_treesitter(lstate);
|
nlua_add_treesitter(lstate);
|
||||||
|
|
||||||
|
23
test/functional/lua/mpack_spec.lua
Normal file
23
test/functional/lua/mpack_spec.lua
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
-- Test suite for testing interactions with API bindings
|
||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
|
||||||
|
local clear = helpers.clear
|
||||||
|
local eq = helpers.eq
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
|
||||||
|
describe('lua vim.mpack', function()
|
||||||
|
before_each(clear)
|
||||||
|
it('can pack vim.NIL', function()
|
||||||
|
eq({true, true, true, true}, exec_lua [[
|
||||||
|
local var = vim.mpack.unpack(vim.mpack.pack({33, vim.NIL, 77}))
|
||||||
|
return {var[1]==33, var[2]==vim.NIL, var[3]==77, var[4]==nil}
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can pack vim.empty_dict()', function()
|
||||||
|
eq({{{}, "foo", {}}, true, false}, exec_lua [[
|
||||||
|
local var = vim.mpack.unpack(vim.mpack.pack({{}, "foo", vim.empty_dict()}))
|
||||||
|
return {var, vim.tbl_islist(var[1]), vim.tbl_islist(var[3])}
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
end)
|
Reference in New Issue
Block a user