diff options
Diffstat (limited to 'src/nvim/lua/executor.c')
| -rw-r--r-- | src/nvim/lua/executor.c | 241 | 
1 files changed, 203 insertions, 38 deletions
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 327ed6d6b7..9f30609d66 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -35,8 +35,8 @@  #include "nvim/os/os.h"  #endif -#include "nvim/lua/executor.h"  #include "nvim/lua/converter.h" +#include "nvim/lua/executor.h"  #include "nvim/lua/treesitter.h"  #include "luv/luv.h" @@ -53,6 +53,15 @@ typedef struct {  # include "lua/executor.c.generated.h"  #endif +#define PUSH_ALL_TYPVALS(lstate, args, argcount, special) \ +  for (int i = 0; i < argcount; i++) { \ +    if (args[i].v_type == VAR_UNKNOWN) { \ +      lua_pushnil(lstate); \ +    } else { \ +      nlua_push_typval(lstate, &args[i], special); \ +    } \ +  } +  /// Convert lua error into a Vim error message  ///  /// @param  lstate  Lua interpreter state. @@ -700,24 +709,25 @@ int nlua_call(lua_State *lstate)    }    TRY_WRAP({ -  // TODO(bfredl): this should be simplified in error handling refactor -  force_abort = false; -  suppress_errthrow = false; -  current_exception = NULL; -  did_emsg = false; - -  try_start(); -  typval_T rettv; -  int dummy; -  // call_func() retval is deceptive, ignore it.  Instead we set `msg_list` -  // (TRY_WRAP) to capture abort-causing non-exception errors. -  (void)call_func(name, (int)name_len, &rettv, nargs, -                  vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, -                  &dummy, true, NULL, NULL); -  if (!try_end(&err)) { -    nlua_push_typval(lstate, &rettv, false); -  } -  tv_clear(&rettv); +    // TODO(bfredl): this should be simplified in error handling refactor +    force_abort = false; +    suppress_errthrow = false; +    current_exception = NULL; +    did_emsg = false; + +    try_start(); +    typval_T rettv; +    int dummy; +    // call_func() retval is deceptive, ignore it.  Instead we set `msg_list` +    // (TRY_WRAP) to capture abort-causing non-exception errors. +    (void)call_func(name, (int)name_len, &rettv, nargs, +                    vim_args, NULL, +                    curwin->w_cursor.lnum, curwin->w_cursor.lnum, +                    &dummy, true, NULL, NULL); +    if (!try_end(&err)) { +      nlua_push_typval(lstate, &rettv, false); +    } +    tv_clear(&rettv);    });  free_vim_args: @@ -833,12 +843,25 @@ void executor_free_luaref(LuaRef ref)    nlua_unref(lstate, ref);  } -/// push a value referenced in the regirstry +/// push a value referenced in the registry  void nlua_pushref(lua_State *lstate, LuaRef ref)  {    lua_rawgeti(lstate, LUA_REGISTRYINDEX, ref);  } +/// Gets a new reference to an object stored at original_ref +/// +/// NOTE: It does not copy the value, it creates a new ref to the lua object. +///       Leaves the stack unchanged. +LuaRef nlua_newref(lua_State *lstate, LuaRef original_ref) +{ +  nlua_pushref(lstate, original_ref); +  LuaRef new_ref = nlua_ref(lstate, -1); +  lua_pop(lstate, 1); + +  return new_ref; +} +  /// Evaluate lua string  ///  /// Used for luaeval(). @@ -916,13 +939,8 @@ static void typval_exec_lua(const char *lcmd, size_t lcmd_len, const char *name,      return;    } -  for (int i = 0; i < argcount; i++) { -    if (args[i].v_type == VAR_UNKNOWN) { -      lua_pushnil(lstate); -    } else { -      nlua_push_typval(lstate, &args[i], special); -    } -  } +  PUSH_ALL_TYPVALS(lstate, args, argcount, special); +    if (lua_pcall(lstate, argcount, ret_tv ? 1 : 0, 0)) {      nlua_error(lstate, _("E5108: Error executing lua %.*s"));      return; @@ -933,6 +951,51 @@ static void typval_exec_lua(const char *lcmd, size_t lcmd_len, const char *name,    }  } +/// Call a LuaCallable given some typvals +/// +/// Used to call any lua callable passed from Lua into VimL +/// +/// @param[in]  lstate Lua State +/// @param[in]  lua_cb Lua Callable +/// @param[in]  argcount Count of typval arguments +/// @param[in]  argvars Typval Arguments +/// @param[out] rettv The return value from the called function. +int typval_exec_lua_callable( +    lua_State *lstate, +    LuaCallable lua_cb, +    int argcount, +    typval_T *argvars, +    typval_T *rettv +) +{ +  int offset = 0; +  LuaRef cb = lua_cb.func_ref; + +  if (cb == LUA_NOREF) { +    // This shouldn't happen. +    luaL_error(lstate, "Invalid function passed to VimL"); +    return ERROR_OTHER; +  } + +  nlua_pushref(lstate, cb); + +  if (lua_cb.table_ref != LUA_NOREF) { +    offset += 1; +    nlua_pushref(lstate, lua_cb.table_ref); +  } + +  PUSH_ALL_TYPVALS(lstate, argvars, argcount, false); + +  if (lua_pcall(lstate, argcount + offset, 1, 0)) { +    luaL_error(lstate, "nlua_CFunction_func_call failed."); +    return ERROR_OTHER; +  } + +  nlua_pop_typval(lstate, rettv); + +  return ERROR_NONE; +} +  /// Execute Lua string  ///  /// Used for nvim_exec_lua(). @@ -1128,21 +1191,11 @@ void ex_luafile(exarg_T *const eap)    }  } -static int create_tslua_parser(lua_State *L) -{ -  if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) { -    return luaL_error(L, "string expected"); -  } - -  const char *lang_name = lua_tostring(L, 1); -  return tslua_push_parser(L, lang_name); -} -  static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL  {    tslua_init(lstate); -  lua_pushcfunction(lstate, create_tslua_parser); +  lua_pushcfunction(lstate, tslua_push_parser);    lua_setfield(lstate, -2, "_create_ts_parser");    lua_pushcfunction(lstate, tslua_add_language); @@ -1290,3 +1343,115 @@ static int regex_match_line(lua_State *lstate)    return nret;  } + +int nlua_CFunction_func_call( +    int argcount, +    typval_T *argvars, +    typval_T *rettv, +    void *state) +{ +    lua_State *const lstate = nlua_enter(); +    LuaCFunctionState *funcstate = (LuaCFunctionState *)state; + +    return typval_exec_lua_callable( +        lstate, +        funcstate->lua_callable, +        argcount, +        argvars, +        rettv); +} +/// Required functions for lua c functions as VimL callbacks +void nlua_CFunction_func_free(void *state) +{ +    lua_State *const lstate = nlua_enter(); +    LuaCFunctionState *funcstate = (LuaCFunctionState *)state; + +    nlua_unref(lstate, funcstate->lua_callable.func_ref); +    nlua_unref(lstate, funcstate->lua_callable.table_ref); +    xfree(funcstate); +} + +bool nlua_is_table_from_lua(typval_T *const arg) +{ +  if (arg->v_type != VAR_DICT && arg->v_type != VAR_LIST) { +    return false; +  } + +  if (arg->v_type == VAR_DICT) { +    return arg->vval.v_dict->lua_table_ref > 0 +      && arg->vval.v_dict->lua_table_ref != LUA_NOREF; +  } else if (arg->v_type == VAR_LIST) { +    return arg->vval.v_list->lua_table_ref > 0 +      && arg->vval.v_list->lua_table_ref != LUA_NOREF; +  } + +  return false; +} + +char_u *nlua_register_table_as_callable(typval_T *const arg) +{ +  if (!nlua_is_table_from_lua(arg)) { +    return NULL; +  } + +  LuaRef table_ref; +  if (arg->v_type == VAR_DICT) { +    table_ref = arg->vval.v_dict->lua_table_ref; +  } else if (arg->v_type == VAR_LIST) { +    table_ref = arg->vval.v_list->lua_table_ref; +  } else { +    return NULL; +  } + +  lua_State *const lstate = nlua_enter(); + +  int top = lua_gettop(lstate); + +  nlua_pushref(lstate, table_ref); +  if (!lua_getmetatable(lstate, -1)) { +    return NULL; +  } + +  lua_getfield(lstate, -1, "__call"); +  if (!lua_isfunction(lstate, -1)) { +    return NULL; +  } + +  LuaRef new_table_ref = nlua_newref(lstate, table_ref); + +  LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); +  state->lua_callable.func_ref = nlua_ref(lstate, -1); +  state->lua_callable.table_ref = new_table_ref; + +  char_u *name = register_cfunc( +      &nlua_CFunction_func_call, +      &nlua_CFunction_func_free, +      state); + + +  lua_pop(lstate, 3); +  assert(top == lua_gettop(lstate)); + +  return name; +} + +/// Helper function to free a list_T +void nlua_free_typval_list(list_T *const l) +{ +  if (l->lua_table_ref != LUA_NOREF && l->lua_table_ref > 0) { +    lua_State *const lstate = nlua_enter(); +    nlua_unref(lstate, l->lua_table_ref); +    l->lua_table_ref = LUA_NOREF; +  } +} + + +/// Helper function to free a dict_T +void nlua_free_typval_dict(dict_T *const d) +{ +  if (d->lua_table_ref != LUA_NOREF && d->lua_table_ref > 0) { +    lua_State *const lstate = nlua_enter(); +    nlua_unref(lstate, d->lua_table_ref); +    d->lua_table_ref = LUA_NOREF; +  } +}  | 
