mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.1054: not so easy to pass a lua function to Vim
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)
This commit is contained in:
101
src/if_lua.c
101
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
|
||||
|
Reference in New Issue
Block a user