0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

patch 8.1.1043: Lua interface does not support Blob

Problem:    Lua interface does not support Blob.
Solution:   Add support to Blob. (Ozaki Kiichi, closes #4151)
This commit is contained in:
Bram Moolenaar
2019-03-23 13:57:02 +01:00
parent 832615be12
commit b782869033
4 changed files with 264 additions and 26 deletions

View File

@@ -10,11 +10,12 @@ The Lua Interface to Vim *lua* *Lua*
2. The vim module |lua-vim|
3. List userdata |lua-list|
4. Dict userdata |lua-dict|
5. Funcref userdata |lua-funcref|
6. Buffer userdata |lua-buffer|
7. Window userdata |lua-window|
8. The luaeval function |lua-luaeval|
9. Dynamic loading |lua-dynamic|
5. Blob userdata |lua-blob|
6. Funcref userdata |lua-funcref|
7. Buffer userdata |lua-buffer|
8. Window userdata |lua-window|
9. luaeval() Vim function |lua-luaeval|
10. Dynamic loading |lua-dynamic|
{Vi does not have any of these commands}
@@ -140,6 +141,14 @@ Vim evaluation and command execution, and others.
:echo luaeval('vim.dict(t)')
:" {'1': 3.141593, '2': v:false,
:" 'say': 'hi'}
<
vim.blob([arg]) Returns an empty blob or, if "arg" is a Lua
string, returns a blob b such that b is
equivalent to "arg" as a byte string.
Examples: >
:lua s = "12ab\x00\x80\xfe\xff"
:echo luaeval('vim.blob(s)')
:" 0z31326162.0080FEFF
<
vim.funcref({name}) Returns a Funcref to function {name} (see
|Funcref|). It is equivalent to Vim's
@@ -260,7 +269,34 @@ Examples:
<
==============================================================================
5. Funcref userdata *lua-funcref*
5. Blob userdata *lua-blob*
Blob userdata represent vim blobs. A blob "b" has the following properties:
Properties
----------
o "#b" is the length of blob "b", equivalent to "len(b)" in Vim.
o "b[k]" returns the k-th item in "b"; "b" is zero-indexed, as in Vim.
To modify the k-th item, simply do "b[k] = number"; in particular,
"b[#b] = number" can append a byte to tail.
Methods
-------
o "b:add(bytes)" appends "bytes" to the end of "b".
Examples:
>
:let b = 0z001122
:lua b = vim.eval('b') -- same 'b'
:lua print(b, b[0], #b)
:lua b[1] = 32
:lua b[#b] = 0x33 -- append a byte to tail
:lua b:add("\x80\x81\xfe\xff")
:echo b
<
==============================================================================
6. Funcref userdata *lua-funcref*
Funcref userdata represent funcref variables in Vim. Funcrefs that were
defined with a "dict" attribute need to be obtained as a dictionary key
@@ -293,7 +329,7 @@ Examples:
<
==============================================================================
6. Buffer userdata *lua-buffer*
7. Buffer userdata *lua-buffer*
Buffer userdata represent vim buffers. A buffer userdata "b" has the following
properties and methods:
@@ -345,7 +381,7 @@ Examples:
<
==============================================================================
7. Window userdata *lua-window*
8. Window userdata *lua-window*
Window objects represent vim windows. A window userdata "w" has the following
properties and methods:
@@ -377,7 +413,7 @@ Examples:
<
==============================================================================
8. The luaeval function *lua-luaeval* *lua-eval*
9. luaeval() Vim function *lua-luaeval* *lua-eval*
The (dual) equivalent of "vim.eval" for passing Lua values to Vim is
"luaeval". "luaeval" takes an expression string and an optional argument and
@@ -390,10 +426,10 @@ returns the result of the expression. It is semantically equivalent in Lua to:
end
<
Note that "_A" receives the argument to "luaeval". Lua numbers, strings, and
list, dict, and funcref userdata are converted to their Vim respective types,
while Lua booleans are converted to numbers. An error is thrown if conversion
of any of the remaining Lua types, including userdata other than lists, dicts,
and funcrefs, is attempted.
list, dict, blob, and funcref userdata are converted to their Vim respective
types, while Lua booleans are converted to numbers. An error is thrown if
conversion of any of the remaining Lua types, including userdata other than
lists, dicts, blobs, and funcrefs, is attempted.
Examples: >
@@ -408,7 +444,7 @@ Examples: >
==============================================================================
9. Dynamic loading *lua-dynamic*
10. Dynamic loading *lua-dynamic*
On MS-Windows and Unix the Lua library can be loaded dynamically. The
|:version| output then includes |+lua/dyn|.

View File

@@ -28,6 +28,7 @@ typedef buf_T *luaV_Buffer;
typedef win_T *luaV_Window;
typedef dict_T *luaV_Dict;
typedef list_T *luaV_List;
typedef blob_T *luaV_Blob;
typedef struct {
char_u *name; // funcref
dict_T *self; // selfdict
@@ -36,6 +37,7 @@ typedef void (*msgfunc_T)(char_u *);
static const char LUAVIM_DICT[] = "dict";
static const char LUAVIM_LIST[] = "list";
static const char LUAVIM_BLOB[] = "blob";
static const char LUAVIM_FUNCREF[] = "funcref";
static const char LUAVIM_BUFFER[] = "buffer";
static const char LUAVIM_WINDOW[] = "window";
@@ -68,6 +70,7 @@ static const char LUAVIM_SETREF[] = "luaV_setref";
static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
static luaV_Blob *luaV_pushblob(lua_State *L, blob_T *blo);
static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name);
#if LUA_VERSION_NUM <= 501
@@ -541,6 +544,9 @@ luaV_pushtypval(lua_State *L, typval_T *tv)
case VAR_FUNC:
luaV_pushfuncref(L, tv->vval.v_string);
break;
case VAR_BLOB:
luaV_pushblob(L, tv->vval.v_blob);
break;
default:
lua_pushnil(L);
}
@@ -582,43 +588,53 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
{
void *p = lua_touserdata(L, pos);
if (lua_getmetatable(L, pos)) /* has metatable? */
if (lua_getmetatable(L, pos)) // has metatable?
{
/* check list */
// check list
luaV_getfield(L, LUAVIM_LIST);
if (lua_rawequal(L, -1, -2))
{
tv->v_type = VAR_LIST;
tv->vval.v_list = *((luaV_List *) p);
++tv->vval.v_list->lv_refcount;
lua_pop(L, 2); /* MTs */
lua_pop(L, 2); // MTs
break;
}
/* check dict */
// check dict
luaV_getfield(L, LUAVIM_DICT);
if (lua_rawequal(L, -1, -3))
{
tv->v_type = VAR_DICT;
tv->vval.v_dict = *((luaV_Dict *) p);
++tv->vval.v_dict->dv_refcount;
lua_pop(L, 3); /* MTs */
lua_pop(L, 3); // MTs
break;
}
/* check funcref */
luaV_getfield(L, LUAVIM_FUNCREF);
// check blob
luaV_getfield(L, LUAVIM_BLOB);
if (lua_rawequal(L, -1, -4))
{
tv->v_type = VAR_BLOB;
tv->vval.v_blob = *((luaV_Blob *) p);
++tv->vval.v_blob->bv_refcount;
lua_pop(L, 4); // MTs
break;
}
// check funcref
luaV_getfield(L, LUAVIM_FUNCREF);
if (lua_rawequal(L, -1, -5))
{
luaV_Funcref *f = (luaV_Funcref *) p;
func_ref(f->name);
tv->v_type = VAR_FUNC;
tv->vval.v_string = vim_strsave(f->name);
lua_pop(L, 4); /* MTs */
lua_pop(L, 5); // MTs
break;
}
lua_pop(L, 4); /* MTs */
lua_pop(L, 4); // MTs
}
}
/* FALLTHROUGH */
// FALLTHROUGH
default:
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
@@ -753,7 +769,7 @@ luaV_type_tostring(list, LUAVIM_LIST)
luaV_list_len(lua_State *L)
{
list_T *l = luaV_unbox(L, luaV_List, 1);
lua_pushinteger(L, (l == NULL) ? 0 : (int) l->lv_len);
lua_pushinteger(L, (int) list_len(l));
return 1;
}
@@ -909,7 +925,7 @@ luaV_type_tostring(dict, LUAVIM_DICT)
luaV_dict_len(lua_State *L)
{
dict_T *d = luaV_unbox(L, luaV_Dict, 1);
lua_pushinteger(L, (d == NULL) ? 0 : (int) d->dv_hashtab.ht_used);
lua_pushinteger(L, (int) dict_len(d));
return 1;
}
@@ -1029,6 +1045,124 @@ static const luaL_Reg luaV_Dict_mt[] = {
};
/* ======= Blob type ======= */
static luaV_Blob *
luaV_newblob(lua_State *L, blob_T *blo)
{
luaV_Blob *b = (luaV_Blob *) lua_newuserdata(L, sizeof(luaV_Blob));
*b = blo;
blo->bv_refcount++; /* reference in Lua */
luaV_setudata(L, blo); /* cache[blo] = udata */
luaV_getfield(L, LUAVIM_BLOB);
lua_setmetatable(L, -2);
return b;
}
luaV_pushtype(blob_T, blob, luaV_Blob)
luaV_type_tostring(blob, LUAVIM_BLOB)
static int
luaV_blob_gc(lua_State *L)
{
blob_T *b = luaV_unbox(L, luaV_Blob, 1);
blob_unref(b);
return 0;
}
static int
luaV_blob_len(lua_State *L)
{
blob_T *b = luaV_unbox(L, luaV_Blob, 1);
lua_pushinteger(L, (int) blob_len(b));
return 1;
}
static int
luaV_blob_index(lua_State *L)
{
blob_T *b = luaV_unbox(L, luaV_Blob, 1);
if (lua_isnumber(L, 2))
{
int idx = luaL_checkinteger(L, 2);
if (idx < blob_len(b))
lua_pushnumber(L, (lua_Number) blob_get(b, idx));
else
lua_pushnil(L);
}
else if (lua_isstring(L, 2))
{
const char *s = lua_tostring(L, 2);
if (strncmp(s, "add", 3) == 0)
{
lua_getmetatable(L, 1);
lua_getfield(L, -1, s);
}
else
lua_pushnil(L);
}
else
lua_pushnil(L);
return 1;
}
static int
luaV_blob_newindex(lua_State *L)
{
blob_T *b = luaV_unbox(L, luaV_Blob, 1);
if (b->bv_lock)
luaL_error(L, "blob is locked");
if (lua_isnumber(L, 2))
{
long len = blob_len(b);
int idx = luaL_checkinteger(L, 2);
int val = luaL_checkinteger(L, 3);
if (idx < len || (idx == len && ga_grow(&b->bv_ga, 1) == OK))
{
blob_set(b, idx, (char_u) val);
if (idx == len)
++b->bv_ga.ga_len;
}
else
luaL_error(L, "index out of range");
}
return 0;
}
static int
luaV_blob_add(lua_State *L)
{
luaV_Blob *blo = luaV_checkudata(L, 1, LUAVIM_BLOB);
blob_T *b = (blob_T *) luaV_checkcache(L, (void *) *blo);
if (b->bv_lock)
luaL_error(L, "blob is locked");
lua_settop(L, 2);
if (!lua_isstring(L, 2))
luaL_error(L, "string expected, got %s", luaL_typename(L, 2));
else
{
size_t i, l = 0;
const char *s = lua_tolstring(L, 2, &l);
ga_grow(&b->bv_ga, l);
for (i = 0; i < l; ++i)
ga_append(&b->bv_ga, s[i]);
}
lua_settop(L, 1);
return 1;
}
static const luaL_Reg luaV_Blob_mt[] = {
{"__tostring", luaV_blob_tostring},
{"__gc", luaV_blob_gc},
{"__len", luaV_blob_len},
{"__index", luaV_blob_index},
{"__newindex", luaV_blob_newindex},
{"add", luaV_blob_add},
{NULL, NULL}
};
/* ======= Funcref type ======= */
static luaV_Funcref *
@@ -1623,6 +1757,33 @@ luaV_dict(lua_State *L)
return 1;
}
static int
luaV_blob(lua_State *L)
{
blob_T *b;
int initarg = !lua_isnoneornil(L, 1);
if (initarg && !lua_isstring(L, 1))
luaL_error(L, "string expected, got %s", luaL_typename(L, 1));
b = blob_alloc();
if (b == NULL)
lua_pushnil(L);
else
{
luaV_newblob(L, b);
if (initarg)
{
size_t i, l = 0;
const char *s = lua_tolstring(L, 1, &l);
ga_grow(&b->bv_ga, l);
for (i = 0; i < l; ++i)
ga_append(&b->bv_ga, s[i]);
}
}
return 1;
}
static int
luaV_funcref(lua_State *L)
{
@@ -1717,6 +1878,12 @@ luaV_type(lua_State *L)
lua_pushstring(L, "dict");
return 1;
}
luaV_getfield(L, LUAVIM_BLOB);
if (lua_rawequal(L, -1, 2))
{
lua_pushstring(L, "blob");
return 1;
}
luaV_getfield(L, LUAVIM_FUNCREF);
if (lua_rawequal(L, -1, 2))
{
@@ -1748,6 +1915,7 @@ static const luaL_Reg luaV_module[] = {
{"line", luaV_line},
{"list", luaV_list},
{"dict", luaV_dict},
{"blob", luaV_blob},
{"funcref", luaV_funcref},
{"buffer", luaV_buffer},
{"window", luaV_window},
@@ -1883,6 +2051,9 @@ luaopen_vim(lua_State *L)
luaV_newmetatable(L, LUAVIM_DICT);
lua_pushvalue(L, 1);
luaV_openlib(L, luaV_Dict_mt, 1);
luaV_newmetatable(L, LUAVIM_BLOB);
lua_pushvalue(L, 1);
luaV_openlib(L, luaV_Blob_mt, 1);
luaV_newmetatable(L, LUAVIM_FUNCREF);
lua_pushvalue(L, 1);
luaV_openlib(L, luaV_Funcref_mt, 1);

View File

@@ -50,6 +50,11 @@ func Test_eval()
call assert_equal('dict', luaeval('vim.type(v)'))
call assert_equal({'a':'b'}, luaeval('v'))
" lua.eval with a blob
lua v = vim.eval("0z00112233.deadbeef")
call assert_equal('blob', luaeval('vim.type(v)'))
call assert_equal(0z00112233.deadbeef, luaeval('v'))
call assert_fails('lua v = vim.eval(nil)',
\ "[string \"vim chunk\"]:1: bad argument #1 to 'eval' (string expected, got nil)")
call assert_fails('lua v = vim.eval(true)',
@@ -428,6 +433,30 @@ func Test_dict_iter()
lua str, d = nil
endfunc
func Test_blob()
call assert_equal(0z, luaeval('vim.blob("")'))
call assert_equal(0z31326162, luaeval('vim.blob("12ab")'))
call assert_equal(0z00010203, luaeval('vim.blob("\x00\x01\x02\x03")'))
call assert_equal(0z8081FEFF, luaeval('vim.blob("\x80\x81\xfe\xff")'))
lua b = vim.blob("\x00\x00\x00\x00")
call assert_equal(0z00000000, luaeval('b'))
call assert_equal(4.0, luaeval('#b'))
lua b[0], b[1], b[2], b[3] = 1, 32, 256, 0xff
call assert_equal(0z012000ff, luaeval('b'))
lua b[4] = string.byte("z", 1)
call assert_equal(0z012000ff.7a, luaeval('b'))
call assert_equal(5.0, luaeval('#b'))
call assert_fails('lua b[#b+1] = 0x80', '[string "vim chunk"]:1: index out of range')
lua b:add("12ab")
call assert_equal(0z012000ff.7a313261.62, luaeval('b'))
call assert_equal(9.0, luaeval('#b'))
call assert_fails('lua b:add(nil)', '[string "vim chunk"]:1: string expected, got nil')
call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string expected, got boolean')
call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table')
lua b = nil
endfunc
func Test_funcref()
function I(x)
return a:x

View File

@@ -775,6 +775,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1043,
/**/
1042,
/**/