diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/private/helpers.c | 43 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 112 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 40 |
3 files changed, 152 insertions, 43 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 0f7008e150..5abdc33709 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -177,42 +177,47 @@ Object dict_get_value(dict_T *dict, String key, Error *err) return vim_to_object(&di->di_tv); } -/// Set a value in a scope dict. Objects are recursively expanded into their -/// vimscript equivalents. -/// -/// @param dict The vimscript dict -/// @param key The key -/// @param value The new value -/// @param del Delete key in place of setting it. Argument `value` is ignored in -/// this case. -/// @param retval If true the old value will be converted and returned. -/// @param[out] err Details of an error that may have occurred -/// @return The old value if `retval` is true and the key was present, else NIL -Object dict_set_var(dict_T *dict, String key, Object value, bool del, - bool retval, Error *err) +dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) { - Object rv = OBJECT_INIT; dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di != NULL) { if (di->di_flags & DI_FLAGS_RO) { api_set_error(err, kErrorTypeException, "Key is read-only: %s", key.data); - return rv; } else if (di->di_flags & DI_FLAGS_LOCK) { api_set_error(err, kErrorTypeException, "Key is locked: %s", key.data); - return rv; } else if (del && (di->di_flags & DI_FLAGS_FIX)) { api_set_error(err, kErrorTypeException, "Key is fixed: %s", key.data); - return rv; } } else if (dict->dv_lock) { api_set_error(err, kErrorTypeException, "Dictionary is locked"); - return rv; } else if (key.size == 0) { api_set_error(err, kErrorTypeValidation, "Key name is empty"); - return rv; } else if (key.size > INT_MAX) { api_set_error(err, kErrorTypeValidation, "Key name is too long"); + } + + return di; +} + +/// Set a value in a scope dict. Objects are recursively expanded into their +/// vimscript equivalents. +/// +/// @param dict The vimscript dict +/// @param key The key +/// @param value The new value +/// @param del Delete key in place of setting it. Argument `value` is ignored in +/// this case. +/// @param retval If true the old value will be converted and returned. +/// @param[out] err Details of an error that may have occurred +/// @return The old value if `retval` is true and the key was present, else NIL +Object dict_set_var(dict_T *dict, String key, Object value, bool del, + bool retval, Error *err) +{ + Object rv = OBJECT_INIT; + dictitem_T *di = dict_check_writable(dict, key, del, err); + + if (ERROR_SET(err)) { return rv; } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index f99a2dd0fe..0a52cc16cb 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -471,6 +471,15 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_wait); lua_setfield(lstate, -2, "wait"); + // _getvar + lua_pushcfunction(lstate, &nlua_getvar); + lua_setfield(lstate, -2, "_getvar"); + + // _setvar + lua_pushcfunction(lstate, &nlua_setvar); + lua_setfield(lstate, -2, "_setvar"); + + // vim.loop luv_set_loop(lstate, &main_loop.uv); luv_set_callback(lstate, nlua_luv_cfpcall); @@ -870,6 +879,109 @@ check_err: return request ? 1 : 0; } +static dict_T *nlua_get_var_scope(lua_State *lstate) { + const char *scope = luaL_checkstring(lstate, 1); + handle_T handle = (handle_T)luaL_checkinteger(lstate, 2); + dict_T *dict = NULL; + Error err = ERROR_INIT; + if (strequal(scope, "g")) { + dict = &globvardict; + } else if (strequal(scope, "v")) { + dict = &vimvardict; + } else if (strequal(scope, "b")) { + buf_T *buf = find_buffer_by_handle(handle, &err); + if (buf) { + dict = buf->b_vars; + } + } else if (strequal(scope, "w")) { + win_T *win = find_window_by_handle(handle, &err); + if (win) { + dict = win->w_vars; + } + } else if (strequal(scope, "t")) { + tabpage_T *tabpage = find_tab_by_handle(handle, &err); + if (tabpage) { + dict = tabpage->tp_vars; + } + } else { + luaL_error(lstate, "invalid scope", err.msg); + return NULL; + } + + if (ERROR_SET(&err)) { + luaL_error(lstate, "FAIL: %s", err.msg); + return NULL; + } + return dict; +} + + +static int nlua_getvar(lua_State *lstate) +{ + // non-local return if not found + dict_T *dict = nlua_get_var_scope(lstate); + size_t len; + const char *name = luaL_checklstring(lstate, 3, &len); + + dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len); + if (di == NULL) { + return 0; // nil + } + nlua_push_typval(lstate, &di->di_tv, false); + return 1; +} + +static int nlua_setvar(lua_State *lstate) +{ + // non-local return if not found + dict_T *dict = nlua_get_var_scope(lstate); + String key; + key.data = (char *)luaL_checklstring(lstate, 3, &key.size); + + bool del = (lua_gettop(lstate) < 4) || lua_isnil(lstate, 4); + + Error err = ERROR_INIT; + dictitem_T *di = dict_check_writable(dict, key, del, &err); + if (ERROR_SET(&err)) { + return 0; + } + + if (del) { + // Delete the key + if (di == NULL) { + // Doesn't exist, nothing to do + return 0; + } else { + // Delete the entry + tv_dict_item_remove(dict, di); + } + } else { + // Update the key + typval_T tv; + + // Convert the lua value to a vimscript type in the temporary variable + lua_pushvalue(lstate, 4); + if (!nlua_pop_typval(lstate, &tv)) { + return luaL_error(lstate, "Couldn't convert lua value"); + } + + if (di == NULL) { + // Need to create an entry + di = tv_dict_item_alloc_len(key.data, key.size); + tv_dict_add(dict, di); + } else { + // Clear the old value + tv_clear(&di->di_tv); + } + + // Update the value + tv_copy(&tv, &di->di_tv); + // Clear the temporary variable + tv_clear(&tv); + } + return 0; +} + static int nlua_nil_tostring(lua_State *lstate) { lua_pushstring(lstate, "vim.NIL"); diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index a678432dda..bc0770da31 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -341,32 +341,24 @@ do end return setmetatable({}, mt) end - local function pcall_ret(status, ...) - if status then return ... end - end - local function nil_wrap(fn) - return function(...) - return pcall_ret(pcall(fn, ...)) + local function make_dict_accessor(scope) + validate { + scope = {scope, 's'}; + } + local mt = {} + function mt:__newindex(k, v) + return vim._setvar(scope, 0, k, v) end + function mt:__index(k) + return vim._getvar(scope, 0, k) + end + return setmetatable({}, mt) end - - vim.b = make_meta_accessor( - nil_wrap(function(v) return a.nvim_buf_get_var(0, v) end), - function(v, k) return a.nvim_buf_set_var(0, v, k) end, - function(v) return a.nvim_buf_del_var(0, v) end - ) - vim.w = make_meta_accessor( - nil_wrap(function(v) return a.nvim_win_get_var(0, v) end), - function(v, k) return a.nvim_win_set_var(0, v, k) end, - function(v) return a.nvim_win_del_var(0, v) end - ) - vim.t = make_meta_accessor( - nil_wrap(function(v) return a.nvim_tabpage_get_var(0, v) end), - function(v, k) return a.nvim_tabpage_set_var(0, v, k) end, - function(v) return a.nvim_tabpage_del_var(0, v) end - ) - vim.g = make_meta_accessor(nil_wrap(a.nvim_get_var), a.nvim_set_var, a.nvim_del_var) - vim.v = make_meta_accessor(nil_wrap(a.nvim_get_vvar), a.nvim_set_vvar) + vim.g = make_dict_accessor('g') + vim.v = make_dict_accessor('v') + vim.b = make_dict_accessor('b') + vim.w = make_dict_accessor('w') + vim.t = make_dict_accessor('t') vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option) local function getenv(k) |