diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-06-25 19:27:56 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-06-25 19:27:56 +0200 |
commit | 801ab069341c8652680d63c174530fd4feb2911e (patch) | |
tree | ff8d365fe4c9ea1dbd5b5918b3a58568a77ec18e /src/if_lua.c | |
parent | 832adf9bb8cd39d8e982d8a35ed8a6d39b974494 (diff) | |
download | vim-git-801ab069341c8652680d63c174530fd4feb2911e.tar.gz |
patch 8.2.1054: not so easy to pass a lua function to Vimv8.2.1054
Problem: Not so easy to pass a lua function to Vim.
Solution: Convert a Lua function and closure to a Vim funcref. (Prabir
Shrestha, closes #6246)
Diffstat (limited to 'src/if_lua.c')
-rw-r--r-- | src/if_lua.c | 101 |
1 files changed, 100 insertions, 1 deletions
diff --git a/src/if_lua.c b/src/if_lua.c index 75231b4c2..ce0901a20 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -35,6 +35,13 @@ typedef struct { } luaV_Funcref; typedef void (*msgfunc_T)(char_u *); +typedef struct { + int lua_funcref; // ref to a lua func + int lua_tableref; // ref to a lua table if metatable else LUA_NOREF. used + // for __call + lua_State *L; +} luaV_CFuncState; + static const char LUAVIM_DICT[] = "dict"; static const char LUAVIM_LIST[] = "list"; static const char LUAVIM_BLOB[] = "blob"; @@ -45,6 +52,8 @@ static const char LUAVIM_FREE[] = "luaV_free"; static const char LUAVIM_LUAEVAL[] = "luaV_luaeval"; static const char LUAVIM_SETREF[] = "luaV_setref"; +static const char LUA___CALL[] = "__call"; + // most functions are closures with a cache table as first upvalue; // get/setudata manage references to vim userdata in cache table through // object pointers (light userdata) @@ -64,7 +73,7 @@ static const char LUAVIM_SETREF[] = "luaV_setref"; #define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg) #define luaV_checktypval(L, a, v, msg) \ do { \ - if (luaV_totypval(L, a, v) == FAIL) \ + if (luaV_totypval(L, a, v) == FAIL) \ luaL_error(L, msg ": cannot convert value"); \ } while (0) @@ -72,6 +81,8 @@ 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); +static int luaV_call_lua_func(int argcount, typval_T *argvars, typval_T *rettv, void *state); +static void luaV_call_lua_func_free(void *state); #if LUA_VERSION_NUM <= 501 #define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n) @@ -591,6 +602,45 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv) tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos); #endif break; + case LUA_TFUNCTION: + { + char_u *name; + lua_pushvalue(L, pos); + luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState); + state->lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX); + state->L = L; + state->lua_tableref = LUA_NOREF; + name = register_cfunc(&luaV_call_lua_func, + &luaV_call_lua_func_free, state); + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(name); + break; + } + case LUA_TTABLE: + { + lua_pushvalue(L, pos); + int lua_tableref = luaL_ref(L, LUA_REGISTRYINDEX); + if (lua_getmetatable(L, pos)) { + lua_getfield(L, -1, LUA___CALL); + if (lua_isfunction(L, -1)) { + char_u *name; + int lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX); + luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState); + state->lua_funcref = lua_funcref; + state->L = L; + state->lua_tableref = lua_tableref; + name = register_cfunc(&luaV_call_lua_func, + &luaV_call_lua_func_free, state); + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(name); + break; + } + } + tv->v_type = VAR_NUMBER; + tv->vval.v_number = 0; + status = FAIL; + break; + } case LUA_TUSERDATA: { void *p = lua_touserdata(L, pos); @@ -2415,4 +2465,53 @@ update_package_paths_in_lua() } } +/* + * Native C function callback + */ + static int +luaV_call_lua_func( + int argcount, + typval_T *argvars, + typval_T *rettv, + void *state) +{ + int i; + int luaargcount = argcount; + luaV_CFuncState *funcstate = (luaV_CFuncState*)state; + lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_funcref); + + if (funcstate->lua_tableref != LUA_NOREF) + { + // First arg for metatable __call method is a table + luaargcount += 1; + lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_tableref); + } + + for (i = 0; i < argcount; ++i) + luaV_pushtypval(funcstate->L, &argvars[i]); + + if (lua_pcall(funcstate->L, luaargcount, 1, 0)) + { + luaV_emsg(funcstate->L); + return FCERR_OTHER; + } + + luaV_checktypval(funcstate->L, -1, rettv, "get return value"); + return FCERR_NONE; +} + +/* + * Free up any lua references held by the func state. + */ + static void +luaV_call_lua_func_free(void *state) +{ + luaV_CFuncState *funcstate = (luaV_CFuncState*)state; + luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_funcref); + funcstate->L = NULL; + if (funcstate->lua_tableref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_tableref); + VIM_CLEAR(funcstate); +} + #endif |