aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2023-07-22 09:52:13 +0100
committerGitHub <noreply@github.com>2023-07-22 09:52:13 +0100
commit24e3ee9d07e1433cb13b4d96ec20999f5f02b204 (patch)
treeb8cd26fc0e9413939b547ba0b63be99551e5d422
parentcfcda91827d73dda740e372b237383c19e63dab9 (diff)
downloadrneovim-24e3ee9d07e1433cb13b4d96ec20999f5f02b204.tar.gz
rneovim-24e3ee9d07e1433cb13b4d96ec20999f5f02b204.tar.bz2
rneovim-24e3ee9d07e1433cb13b4d96ec20999f5f02b204.zip
fix(api/options): validate buf and win
Fixes #24398
-rw-r--r--runtime/lua/vim/_options.lua36
-rw-r--r--src/nvim/api/options.c28
-rw-r--r--test/functional/api/vim_spec.lua2
-rw-r--r--test/functional/lua/vim_spec.lua4
4 files changed, 33 insertions, 37 deletions
diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua
index d54e8b447c..e1c125baf2 100644
--- a/runtime/lua/vim/_options.lua
+++ b/runtime/lua/vim/_options.lua
@@ -124,14 +124,12 @@ local function get_option_metatype(name, info)
return info.type
end
-local options_info = setmetatable({}, {
- __index = function(t, k)
- local info = api.nvim_get_option_info(k)
- info.metatype = get_option_metatype(k, info)
- rawset(t, k, info)
- return rawget(t, k)
- end,
-})
+--- @param name string
+local function get_options_info(name)
+ local info = api.nvim_get_option_info(name)
+ info.metatype = get_option_metatype(name, info)
+ return info
+end
---Environment variables defined in the editor session.
---See |expand-env| and |:let-environment| for the Vimscript behavior.
@@ -155,34 +153,16 @@ vim.env = setmetatable({}, {
end,
})
-local function opt_validate(option_name, target_scope)
- local scope = options_info[option_name].scope
- if scope ~= target_scope then
- local scope_to_string = { buf = 'buffer', win = 'window' }
- error(
- string.format(
- [['%s' is a %s option, not a %s option. See ":help %s"]],
- option_name,
- scope_to_string[scope] or scope,
- scope_to_string[target_scope] or target_scope,
- option_name
- )
- )
- end
-end
-
local function new_buf_opt_accessor(bufnr)
return setmetatable({}, {
__index = function(_, k)
if bufnr == nil and type(k) == 'number' then
return new_buf_opt_accessor(k)
end
- opt_validate(k, 'buf')
return api.nvim_get_option_value(k, { buf = bufnr or 0 })
end,
__newindex = function(_, k, v)
- opt_validate(k, 'buf')
return api.nvim_set_option_value(k, v, { buf = bufnr or 0 })
end,
})
@@ -203,7 +183,6 @@ local function new_win_opt_accessor(winid, bufnr)
error('only bufnr=0 is supported')
end
- opt_validate(k, 'win')
-- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value
return api.nvim_get_option_value(k, {
scope = bufnr and 'local' or nil,
@@ -212,7 +191,6 @@ local function new_win_opt_accessor(winid, bufnr)
end,
__newindex = function(_, k, v)
- opt_validate(k, 'win')
-- TODO(lewis6991): allow passing both buf and win to nvim_set_option_value
return api.nvim_set_option_value(k, v, {
scope = bufnr and 'local' or nil,
@@ -680,7 +658,7 @@ local function create_option_accessor(scope)
local option_mt
local function make_option(name, value)
- local info = assert(options_info[name], 'Not a valid option name: ' .. name)
+ local info = assert(get_options_info(name), 'Not a valid option name: ' .. name)
if type(value) == 'table' and getmetatable(value) == option_mt then
assert(name == value._name, "must be the same value, otherwise that's weird.")
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index b9a41adc3b..858a663b9f 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -23,8 +23,8 @@
# include "api/options.c.generated.h"
#endif
-static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_type, void **from,
- char **filetype, Error *err)
+static int validate_option_value_args(Dict(option) *opts, char *name, int *scope, int *opt_type,
+ void **from, char **filetype, Error *err)
{
if (HAS_KEY(opts->scope)) {
VALIDATE_T("scope", kObjectTypeString, opts->scope.type, {
@@ -92,6 +92,24 @@ static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_t
return FAIL;
});
+ int flags = get_option_value_strict(name, NULL, NULL, 0, NULL);
+ if (flags == 0) {
+ // hidden or unknown option
+ api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
+ } else if (*opt_type & (SREQ_BUF | SREQ_WIN)) {
+ // if 'buf' or 'win' is passed, make sure the option supports it
+ int req_flags = *opt_type & SREQ_BUF ? SOPT_BUF : SOPT_WIN;
+ if (!(flags & req_flags)) {
+ char *tgt = *opt_type & SREQ_BUF ? "buf" : "win";
+ char *global = flags & SOPT_GLOBAL ? "global ": "";
+ char *req = flags & SOPT_BUF ? "buffer-local " :
+ flags & SOPT_WIN ? "window-local " : "";
+
+ api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'",
+ tgt, global, req, name);
+ }
+ }
+
return OK;
}
@@ -197,7 +215,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
void *from = NULL;
char *filetype = NULL;
- if (!validate_option_value_args(opts, &scope, &opt_type, &from, &filetype, err)) {
+ if (!validate_option_value_args(opts, name.data, &scope, &opt_type, &from, &filetype, err)) {
goto err;
}
@@ -259,7 +277,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
int scope = 0;
int opt_type = SREQ_GLOBAL;
void *to = NULL;
- if (!validate_option_value_args(opts, &scope, &opt_type, &to, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &scope, &opt_type, &to, NULL, err)) {
return;
}
@@ -343,7 +361,7 @@ Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err)
int scope = 0;
int opt_type = SREQ_GLOBAL;
void *from = NULL;
- if (!validate_option_value_args(opts, &scope, &opt_type, &from, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &scope, &opt_type, &from, NULL, err)) {
return (Dictionary)ARRAY_DICT_INIT;
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 617d0f12ff..6aa9dcc8fa 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -2445,7 +2445,7 @@ describe('API', function()
it('can throw exceptions', function()
local status, err = pcall(nvim, 'get_option_value', 'invalid-option', {})
eq(false, status)
- ok(err:match("Invalid 'option': 'invalid%-option'") ~= nil)
+ ok(err:match("Unknown option 'invalid%-option'") ~= nil)
end)
it('does not truncate error message <1 MB #5984', function()
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 77c724b8e6..f1a617fb70 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -1509,7 +1509,7 @@ describe('lua stdlib', function()
]]
eq('', funcs.luaeval "vim.bo.filetype")
eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
- matches("Invalid option %(not found%): 'nosuchopt'$",
+ matches("Unknown option 'nosuchopt'$",
pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
matches("Expected lua string$",
pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
@@ -1530,7 +1530,7 @@ describe('lua stdlib', function()
eq(0, funcs.luaeval "vim.wo.cole")
eq(0, funcs.luaeval "vim.wo[0].cole")
eq(0, funcs.luaeval "vim.wo[1001].cole")
- matches("Invalid option %(not found%): 'notanopt'$",
+ matches("Unknown option 'notanopt'$",
pcall_err(exec_lua, 'return vim.wo.notanopt'))
matches("Invalid window id: %-1$",
pcall_err(exec_lua, 'return vim.wo[-1].list'))