mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 05:40:08 +00:00
fix(api): nvim_get_option_value tab-local 'cmdheight' #39259
Problem:
API clients cannot query the tab-local value of 'cmdheight'.
Solution:
Allow nvim_get_option_value() to accept { tab = <tab-ID> } for 'cmdheight'.
This commit is contained in:
@@ -3559,6 +3559,8 @@ nvim_get_option_value({name}, {opts}) *nvim_get_option_value()*
|
||||
autocommands for the corresponding filetype.
|
||||
• scope: One of "global" or "local". Analogous to |:setglobal|
|
||||
and |:setlocal|, respectively.
|
||||
• tab: |tab-ID| for tab-local options. Currently only supports
|
||||
"cmdheight". Tabpage `0` means the current tabpage.
|
||||
• win: |window-ID|. Used for getting window local options.
|
||||
|
||||
Return: ~
|
||||
|
||||
2
runtime/lua/vim/_meta/api.gen.lua
generated
2
runtime/lua/vim/_meta/api.gen.lua
generated
@@ -1552,6 +1552,8 @@ function vim.api.nvim_get_option_info2(name, opts) end
|
||||
--- autocommands for the corresponding filetype.
|
||||
--- - scope: One of "global" or "local". Analogous to
|
||||
--- `:setglobal` and `:setlocal`, respectively.
|
||||
--- - tab: `tab-ID` for tab-local options. Currently only
|
||||
--- supports "cmdheight". Tabpage `0` means the current tabpage.
|
||||
--- - win: `window-ID`. Used for getting window local options.
|
||||
--- @return any # Option value
|
||||
function vim.api.nvim_get_option_value(name, opts) end
|
||||
|
||||
1
runtime/lua/vim/_meta/api_keysets.gen.lua
generated
1
runtime/lua/vim/_meta/api_keysets.gen.lua
generated
@@ -381,6 +381,7 @@ error('Cannot require a meta file')
|
||||
--- @field buf? integer
|
||||
--- @field filetype? string
|
||||
--- @field scope? string
|
||||
--- @field tab? integer
|
||||
--- @field win? integer
|
||||
|
||||
--- @class vim.api.keyset.redraw
|
||||
|
||||
@@ -168,6 +168,7 @@ typedef struct {
|
||||
String scope;
|
||||
Window win;
|
||||
Buffer buf;
|
||||
Tabpage tab;
|
||||
String filetype;
|
||||
} Dict(option);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/memory_defs.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/option_vars.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/vim_defs.h"
|
||||
#include "nvim/window.h"
|
||||
@@ -25,9 +26,33 @@
|
||||
|
||||
static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
|
||||
int *opt_flags, OptScope *scope, void **from, char **filetype,
|
||||
Error *err)
|
||||
tabpage_T **opt_tabp, Error *err)
|
||||
{
|
||||
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
|
||||
assert(opt_tabp != NULL);
|
||||
*opt_tabp = NULL;
|
||||
|
||||
// Validate incompatible argument combinations first, then resolve handles and scope.
|
||||
if (HAS_KEY_X(opts, filetype)) {
|
||||
VALIDATE_CON(!HAS_KEY_X(opts, scope) && !HAS_KEY_X(opts, buf)
|
||||
&& !HAS_KEY_X(opts, win) && !HAS_KEY_X(opts, tab),
|
||||
"filetype", "'scope', 'buf', 'win' or 'tab'", {
|
||||
return FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(opts, tab)) {
|
||||
VALIDATE_CON(!HAS_KEY_X(opts, win) && !HAS_KEY_X(opts, buf)
|
||||
&& !HAS_KEY_X(opts, filetype) && !HAS_KEY_X(opts, scope),
|
||||
"tab", "'win', 'buf', 'filetype' or 'scope'", {
|
||||
return FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
VALIDATE_CON(!(HAS_KEY_X(opts, win) && HAS_KEY_X(opts, buf)), "buf", "win", {
|
||||
return FAIL;
|
||||
});
|
||||
|
||||
if (HAS_KEY_X(opts, scope)) {
|
||||
if (!strcmp(opts->scope.data, "local")) {
|
||||
*opt_flags = OPT_LOCAL;
|
||||
@@ -55,8 +80,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(opts, buf)) {
|
||||
VALIDATE(!(HAS_KEY_X(opts, scope) && *opt_flags == OPT_GLOBAL), "%s",
|
||||
"cannot use both global 'scope' and 'buf'", {
|
||||
VALIDATE_CON(!(HAS_KEY_X(opts, scope) && *opt_flags == OPT_GLOBAL), "buf", "global scope", {
|
||||
return FAIL;
|
||||
});
|
||||
*opt_flags = OPT_LOCAL;
|
||||
@@ -67,23 +91,27 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
|
||||
}
|
||||
}
|
||||
|
||||
VALIDATE((!HAS_KEY_X(opts, filetype)
|
||||
|| !(HAS_KEY_X(opts, buf) || HAS_KEY_X(opts, scope) || HAS_KEY_X(opts, win))),
|
||||
"%s", "cannot use 'filetype' with 'scope', 'buf' or 'win'", {
|
||||
return FAIL;
|
||||
});
|
||||
|
||||
VALIDATE((!HAS_KEY_X(opts, win) || !HAS_KEY_X(opts, buf)),
|
||||
"%s", "cannot use both 'buf' and 'win'", {
|
||||
return FAIL;
|
||||
});
|
||||
if (HAS_KEY_X(opts, tab)) {
|
||||
*opt_tabp = find_tab_by_handle(opts->tab, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
*opt_idxp = find_option(name);
|
||||
if (*opt_idxp == kOptInvalid) {
|
||||
// unknown option
|
||||
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
|
||||
} else if (*scope == kOptScopeBuf || *scope == kOptScopeWin) {
|
||||
// if 'buf' or 'win' is passed, make sure the option supports it
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (*opt_tabp != NULL && *opt_idxp != kOptCmdheight) {
|
||||
api_set_error(err, kErrorTypeValidation, "'tab' can only be used with option 'cmdheight'");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// If 'buf' or 'win' is passed, make sure the option supports it.
|
||||
if (*scope == kOptScopeBuf || *scope == kOptScopeWin) {
|
||||
if (!option_has_scope(*opt_idxp, *scope)) {
|
||||
char *tgt = *scope == kOptScopeBuf ? "buf" : "win";
|
||||
char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
|
||||
@@ -93,6 +121,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
|
||||
|
||||
api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'",
|
||||
tgt, global, req, name);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +223,8 @@ static void wipe_ft_buf(buf_T *buf)
|
||||
/// autocommands for the corresponding filetype.
|
||||
/// - scope: One of "global" or "local". Analogous to
|
||||
/// |:setglobal| and |:setlocal|, respectively.
|
||||
/// - tab: |tab-ID| for tab-local options. Currently only
|
||||
/// supports "cmdheight". Tabpage `0` means the current tabpage.
|
||||
/// - win: |window-ID|. Used for getting window local options.
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Option value
|
||||
@@ -205,12 +236,18 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
|
||||
OptScope scope = kOptScopeGlobal;
|
||||
void *from = NULL;
|
||||
char *filetype = NULL;
|
||||
tabpage_T *opt_tab = NULL;
|
||||
|
||||
if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from,
|
||||
&filetype, err)) {
|
||||
&filetype, &opt_tab, err)) {
|
||||
return (Object)OBJECT_INIT;
|
||||
}
|
||||
|
||||
if (opt_tab != NULL) {
|
||||
const OptInt ch = opt_tab == curtab ? p_ch : opt_tab->tp_ch_used;
|
||||
return optval_as_object(NUMBER_OPTVAL(ch));
|
||||
}
|
||||
|
||||
aco_save_T aco;
|
||||
bool aco_used;
|
||||
|
||||
@@ -273,12 +310,19 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
|
||||
Error *err)
|
||||
FUNC_API_SINCE(9)
|
||||
{
|
||||
// TODO(EliWiegman): support tab-local option setting (only nvim_get_option_value supports `tab`).
|
||||
VALIDATE_CON(!(opts && HAS_KEY(opts, option, tab)),
|
||||
"tab", "nvim_set_option_value", {
|
||||
return;
|
||||
});
|
||||
|
||||
OptIndex opt_idx = 0;
|
||||
int opt_flags = 0;
|
||||
OptScope scope = kOptScopeGlobal;
|
||||
void *to = NULL;
|
||||
tabpage_T *opt_tab = NULL;
|
||||
if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &to, NULL,
|
||||
err)) {
|
||||
&opt_tab, err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -361,12 +405,18 @@ DictAs(get_option_info) nvim_get_option_info2(String name, Dict(option) *opts, A
|
||||
Error *err)
|
||||
FUNC_API_SINCE(11)
|
||||
{
|
||||
VALIDATE_CON(!(opts && HAS_KEY(opts, option, tab)),
|
||||
"tab", "nvim_get_option_info2", {
|
||||
return (Dict)ARRAY_DICT_INIT;
|
||||
});
|
||||
|
||||
OptIndex opt_idx = 0;
|
||||
int opt_flags = 0;
|
||||
OptScope scope = kOptScopeGlobal;
|
||||
void *from = NULL;
|
||||
tabpage_T *opt_tab = NULL;
|
||||
if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from, NULL,
|
||||
err)) {
|
||||
&opt_tab, err)) {
|
||||
return (Dict)ARRAY_DICT_INIT;
|
||||
}
|
||||
|
||||
|
||||
@@ -1952,6 +1952,52 @@ describe('API', function()
|
||||
'Invalid value for option \'scrolloff\': expected number, got string "wrong"',
|
||||
pcall_err(api.nvim_set_option_value, 'scrolloff', 'wrong', {})
|
||||
)
|
||||
local tab1 = api.nvim_get_current_tabpage()
|
||||
eq(
|
||||
"Conflict: 'tab' not allowed with 'win', 'buf', 'filetype' or 'scope'",
|
||||
pcall_err(
|
||||
api.nvim_get_option_value,
|
||||
'cmdheight',
|
||||
{ tab = tab1, win = api.nvim_get_current_win() }
|
||||
)
|
||||
)
|
||||
eq(
|
||||
"Conflict: 'tab' not allowed with 'win', 'buf', 'filetype' or 'scope'",
|
||||
pcall_err(api.nvim_get_option_value, 'cmdheight', {
|
||||
tab = tab1,
|
||||
scope = 'local',
|
||||
})
|
||||
)
|
||||
eq(
|
||||
"'tab' can only be used with option 'cmdheight'",
|
||||
pcall_err(api.nvim_get_option_value, 'shiftwidth', { tab = tab1 })
|
||||
)
|
||||
eq(
|
||||
"Conflict: 'tab' not allowed with 'nvim_set_option_value'",
|
||||
pcall_err(api.nvim_set_option_value, 'cmdheight', 2, { tab = tab1 })
|
||||
)
|
||||
eq(
|
||||
"Conflict: 'tab' not allowed with 'nvim_get_option_info2'",
|
||||
pcall_err(api.nvim_get_option_info2, 'cmdheight', { tab = tab1 })
|
||||
)
|
||||
eq(
|
||||
"Conflict: 'filetype' not allowed with 'scope', 'buf', 'win' or 'tab'",
|
||||
pcall_err(api.nvim_get_option_value, 'cmdheight', { filetype = 'c', tab = 0 })
|
||||
)
|
||||
end)
|
||||
|
||||
it("tabpage-local 'cmdheight' #31140", function()
|
||||
api.nvim_set_option_value('cmdheight', 1, {})
|
||||
local tab1 = api.nvim_get_current_tabpage()
|
||||
eq(1, api.nvim_get_option_value('cmdheight', { tab = 0 }))
|
||||
eq(1, api.nvim_get_option_value('cmdheight', { tab = tab1 }))
|
||||
eq(1, api.nvim_get_option_value('cmdheight', {}))
|
||||
command('tabnew')
|
||||
local tab2 = api.nvim_get_current_tabpage()
|
||||
api.nvim_set_option_value('cmdheight', 4, {})
|
||||
eq(4, api.nvim_get_option_value('cmdheight', { tab = tab2 }))
|
||||
eq(1, api.nvim_get_option_value('cmdheight', { tab = tab1 }))
|
||||
eq(4, api.nvim_get_option_value('cmdheight', {}))
|
||||
end)
|
||||
|
||||
it('can get local values when global value is set', function()
|
||||
|
||||
Reference in New Issue
Block a user