]> granicus.if.org Git - vim/commitdiff
patch 8.1.1043: Lua interface does not support Blob v8.1.1043
authorBram Moolenaar <Bram@vim.org>
Sat, 23 Mar 2019 12:57:02 +0000 (13:57 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 23 Mar 2019 12:57:02 +0000 (13:57 +0100)
Problem:    Lua interface does not support Blob.
Solution:   Add support to Blob. (Ozaki Kiichi, closes #4151)

runtime/doc/if_lua.txt
src/if_lua.c
src/testdir/test_lua.vim
src/version.c

index a68f972f677858d72faa3a08ce9da3927fa3fb0a..d3576100e75b4b241086f9486618441e4ffc6168 100644 (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|.
index 0dcb63f1b99339a524072a0740e94741787adade..825e8346f66114a54bfc5ac504a7683c73beac05 100644 (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);
index ec28183b17892b99cc7daa6a24cfa177e302305d..604cfedd32a582ea1f898a5782c5820981213d77 100644 (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
index 4dc0df6df1de7b4def9fba4f355748017ad4f18e..f6b37243def6ca0fa767d8d8eee3603a99be1ed5 100644 (file)
@@ -775,6 +775,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1043,
 /**/
     1042,
 /**/