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:
@@ -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|.
|
||||
|
195
src/if_lua.c
195
src/if_lua.c
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -775,6 +775,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1043,
|
||||
/**/
|
||||
1042,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user