mirror of
https://github.com/neovim/neovim.git
synced 2025-10-09 03:16:31 +00:00
lua: use proper conversion of vim.g values
This commit is contained in:
@@ -177,6 +177,29 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
|
|||||||
return vim_to_object(&di->di_tv);
|
return vim_to_object(&di->di_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
} else if (di->di_flags & DI_FLAGS_LOCK) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Key is locked: %s", key.data);
|
||||||
|
} else if (del && (di->di_flags & DI_FLAGS_FIX)) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Key is fixed: %s", key.data);
|
||||||
|
}
|
||||||
|
} else if (dict->dv_lock) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Dictionary is locked");
|
||||||
|
} else if (key.size == 0) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Key name is empty");
|
||||||
|
} 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
|
/// Set a value in a scope dict. Objects are recursively expanded into their
|
||||||
/// vimscript equivalents.
|
/// vimscript equivalents.
|
||||||
///
|
///
|
||||||
@@ -192,27 +215,9 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del,
|
|||||||
bool retval, Error *err)
|
bool retval, Error *err)
|
||||||
{
|
{
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
|
dictitem_T *di = dict_check_writable(dict, key, del, err);
|
||||||
|
|
||||||
if (di != NULL) {
|
if (ERROR_SET(err)) {
|
||||||
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 rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -471,6 +471,15 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
lua_pushcfunction(lstate, &nlua_wait);
|
lua_pushcfunction(lstate, &nlua_wait);
|
||||||
lua_setfield(lstate, -2, "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
|
// vim.loop
|
||||||
luv_set_loop(lstate, &main_loop.uv);
|
luv_set_loop(lstate, &main_loop.uv);
|
||||||
luv_set_callback(lstate, nlua_luv_cfpcall);
|
luv_set_callback(lstate, nlua_luv_cfpcall);
|
||||||
@@ -870,6 +879,109 @@ check_err:
|
|||||||
return request ? 1 : 0;
|
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)
|
static int nlua_nil_tostring(lua_State *lstate)
|
||||||
{
|
{
|
||||||
lua_pushstring(lstate, "vim.NIL");
|
lua_pushstring(lstate, "vim.NIL");
|
||||||
|
@@ -341,32 +341,24 @@ do
|
|||||||
end
|
end
|
||||||
return setmetatable({}, mt)
|
return setmetatable({}, mt)
|
||||||
end
|
end
|
||||||
local function pcall_ret(status, ...)
|
local function make_dict_accessor(scope)
|
||||||
if status then return ... end
|
validate {
|
||||||
end
|
scope = {scope, 's'};
|
||||||
local function nil_wrap(fn)
|
}
|
||||||
return function(...)
|
local mt = {}
|
||||||
return pcall_ret(pcall(fn, ...))
|
function mt:__newindex(k, v)
|
||||||
|
return vim._setvar(scope, 0, k, v)
|
||||||
end
|
end
|
||||||
|
function mt:__index(k)
|
||||||
|
return vim._getvar(scope, 0, k)
|
||||||
|
end
|
||||||
|
return setmetatable({}, mt)
|
||||||
end
|
end
|
||||||
|
vim.g = make_dict_accessor('g')
|
||||||
vim.b = make_meta_accessor(
|
vim.v = make_dict_accessor('v')
|
||||||
nil_wrap(function(v) return a.nvim_buf_get_var(0, v) end),
|
vim.b = make_dict_accessor('b')
|
||||||
function(v, k) return a.nvim_buf_set_var(0, v, k) end,
|
vim.w = make_dict_accessor('w')
|
||||||
function(v) return a.nvim_buf_del_var(0, v) end
|
vim.t = make_dict_accessor('t')
|
||||||
)
|
|
||||||
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.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option)
|
vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option)
|
||||||
|
|
||||||
local function getenv(k)
|
local function getenv(k)
|
||||||
|
@@ -945,12 +945,20 @@ describe('lua stdlib', function()
|
|||||||
exec_lua [[
|
exec_lua [[
|
||||||
vim.api.nvim_set_var("testing", "hi")
|
vim.api.nvim_set_var("testing", "hi")
|
||||||
vim.api.nvim_set_var("other", 123)
|
vim.api.nvim_set_var("other", 123)
|
||||||
|
vim.api.nvim_set_var("floaty", 5120.1)
|
||||||
|
vim.api.nvim_set_var("nullvar", vim.NIL)
|
||||||
vim.api.nvim_set_var("to_delete", {hello="world"})
|
vim.api.nvim_set_var("to_delete", {hello="world"})
|
||||||
]]
|
]]
|
||||||
|
|
||||||
eq('hi', funcs.luaeval "vim.g.testing")
|
eq('hi', funcs.luaeval "vim.g.testing")
|
||||||
eq(123, funcs.luaeval "vim.g.other")
|
eq(123, funcs.luaeval "vim.g.other")
|
||||||
|
eq(5120.1, funcs.luaeval "vim.g.floaty")
|
||||||
eq(NIL, funcs.luaeval "vim.g.nonexistant")
|
eq(NIL, funcs.luaeval "vim.g.nonexistant")
|
||||||
|
eq(NIL, funcs.luaeval "vim.g.nullvar")
|
||||||
|
-- lost over RPC, so test locally:
|
||||||
|
eq({false, true}, exec_lua [[
|
||||||
|
return {vim.g.nonexistant == vim.NIL, vim.g.nullvar == vim.NIL}
|
||||||
|
]])
|
||||||
|
|
||||||
eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
|
eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
@@ -963,12 +971,20 @@ describe('lua stdlib', function()
|
|||||||
exec_lua [[
|
exec_lua [[
|
||||||
vim.api.nvim_buf_set_var(0, "testing", "hi")
|
vim.api.nvim_buf_set_var(0, "testing", "hi")
|
||||||
vim.api.nvim_buf_set_var(0, "other", 123)
|
vim.api.nvim_buf_set_var(0, "other", 123)
|
||||||
|
vim.api.nvim_buf_set_var(0, "floaty", 5120.1)
|
||||||
|
vim.api.nvim_buf_set_var(0, "nullvar", vim.NIL)
|
||||||
vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
|
vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
|
||||||
]]
|
]]
|
||||||
|
|
||||||
eq('hi', funcs.luaeval "vim.b.testing")
|
eq('hi', funcs.luaeval "vim.b.testing")
|
||||||
eq(123, funcs.luaeval "vim.b.other")
|
eq(123, funcs.luaeval "vim.b.other")
|
||||||
|
eq(5120.1, funcs.luaeval "vim.b.floaty")
|
||||||
eq(NIL, funcs.luaeval "vim.b.nonexistant")
|
eq(NIL, funcs.luaeval "vim.b.nonexistant")
|
||||||
|
eq(NIL, funcs.luaeval "vim.b.nullvar")
|
||||||
|
-- lost over RPC, so test locally:
|
||||||
|
eq({false, true}, exec_lua [[
|
||||||
|
return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL}
|
||||||
|
]])
|
||||||
|
|
||||||
eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
|
eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
|
Reference in New Issue
Block a user