Skip to content

Commit

Permalink
fix(api): use local LastSet structure in nvim_get_option_info
Browse files Browse the repository at this point in the history
* nvim_get_option_info is changed to use the local LastSet information
  for the current buffer or window (if available). Fixes #15232.
* a new option is added that also accepts 'opts' table (scope, buf, win)
  allowing to query local options from another buffer or window.
  • Loading branch information
mliszcz committed Mar 21, 2023
1 parent 4cba53e commit f087673
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 8 deletions.
23 changes: 23 additions & 0 deletions runtime/doc/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1949,12 +1949,35 @@ nvim_get_option_info({name}) *nvim_get_option_info()*
• commalist: List of comma separated values
• flaglist: List of single char flags

The last set information applies to the local value in the current buffer
or window if it is available, otherwise the global value information is
returned.

Parameters: ~
{name} Option name

Return: ~
Option Information

nvim_get_option_info_ex({name}, {*opts}) *nvim_get_option_info_ex()*
Gets the option information for one option from arbitrary buffer or window

This function is similar to nvim_get_option_info except that it accepts an
additional options table {opts}, allowing to specify option scope, buffer
number or window used for retrieving the last set location information.

Parameters: ~
{name} Option name
{opts} Optional parameters
• scope: One of "global" or "local". Analogous to |:setglobal|
and |:setlocal|, respectively.
• win: |window-ID|. Used for getting window local options.
• buf: Buffer number. Used for getting buffer local options.
Implies {scope} is "local".

Return: ~
Option Information

nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
Gets the value of an option. The behavior of this function matches that of
|:set|: the local value of an option is returned if it exists; otherwise,
Expand Down
42 changes: 41 additions & 1 deletion src/nvim/api/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,15 +309,55 @@ Dictionary nvim_get_all_options_info(Error *err)
/// - commalist: List of comma separated values
/// - flaglist: List of single char flags
///
/// The last set information applies to the local value in the current buffer
/// or window if it is available, otherwise the global value information is
/// returned.
///
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option Information
Dictionary nvim_get_option_info(String name, Error *err)
FUNC_API_SINCE(7)
{
return get_vimoption(name, err);
return get_vimoption(name, curbuf, curwin, err);
}

/// Gets the option information for one option from arbitrary buffer or window
///
/// This function is similar to @ref nvim_get_option_info except that it
/// accepts an additional options table {opts}, allowing to specify option
/// scope, buffer number or window used for retrieving the last set location
/// information.
///
/// @param name Option name
/// @param opts Optional parameters
/// - scope: One of "global" or "local". Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// - win: |window-ID|. Used for getting window local options.
/// - buf: Buffer number. Used for getting buffer local options.
/// Implies {scope} is "local".
/// @param[out] err Error details, if any
/// @return Option Information
Dictionary nvim_get_option_info_ex(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(11)
{
int scope = 0;
int opt_type = SREQ_GLOBAL;
void *from = NULL;
if (!validate_option_value_args(opts, &scope, &opt_type, &from, NULL, err)) {
return (Dictionary)ARRAY_DICT_INIT;
}

buf_T *buf = NULL;
win_T *win = NULL;
if (scope != OPT_GLOBAL) {
buf = (opt_type == SREQ_BUF) ? (buf_T *)from : curbuf;
win = (opt_type == SREQ_WIN) ? (win_T *)from : curwin;
}

return get_vimoption(name, buf, win, err);
}

/// Sets the global value of an option.
///
/// @param channel_id
Expand Down
26 changes: 19 additions & 7 deletions src/nvim/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -5605,26 +5605,27 @@ long get_sidescrolloff_value(win_T *wp)
return wp->w_p_siso < 0 ? p_siso : wp->w_p_siso;
}

Dictionary get_vimoption(String name, Error *err)
Dictionary get_vimoption(String name, buf_T *buf, win_T *win, Error *err)
{
int opt_idx = findoption_len((const char *)name.data, name.size);
VALIDATE_S(opt_idx >= 0, "option (not found)", name.data, {
return (Dictionary)ARRAY_DICT_INIT;
});
return vimoption2dict(&options[opt_idx]);

return vimoption2dict(&options[opt_idx], buf, win);
}

Dictionary get_all_vimoptions(void)
{
Dictionary retval = ARRAY_DICT_INIT;
for (size_t i = 0; options[i].fullname != NULL; i++) {
Dictionary opt_dict = vimoption2dict(&options[i]);
Dictionary opt_dict = vimoption2dict(&options[i], curbuf, curwin);
PUT(retval, options[i].fullname, DICTIONARY_OBJ(opt_dict));
}
return retval;
}

static Dictionary vimoption2dict(vimoption_T *opt)
static Dictionary vimoption2dict(vimoption_T *opt, buf_T *buf, win_T *win)
{
Dictionary dict = ARRAY_DICT_INIT;

Expand All @@ -5649,9 +5650,20 @@ static Dictionary vimoption2dict(vimoption_T *opt)

PUT(dict, "was_set", BOOL(opt->flags & P_WAS_SET));

PUT(dict, "last_set_sid", INTEGER_OBJ(opt->last_set.script_ctx.sc_sid));
PUT(dict, "last_set_linenr", INTEGER_OBJ(opt->last_set.script_ctx.sc_lnum));
PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)opt->last_set.channel_id));
LastSet *last_set = NULL;
if ((opt->indir & PV_BUF) && buf != NULL) {
last_set = &buf->b_p_script_ctx[opt->indir & PV_MASK];
}
if ((opt->indir & PV_WIN) && win != NULL) {
last_set = &win->w_p_script_ctx[opt->indir & PV_MASK];
}
if (last_set == NULL || last_set->script_ctx.sc_sid == 0) {
last_set = &opt->last_set;
}

PUT(dict, "last_set_sid", INTEGER_OBJ(last_set->script_ctx.sc_sid));
PUT(dict, "last_set_linenr", INTEGER_OBJ(last_set->script_ctx.sc_lnum));
PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set->channel_id));

const char *type;
Object def;
Expand Down
119 changes: 119 additions & 0 deletions test/functional/api/vim_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2817,6 +2817,13 @@ describe('API', function()
type = "string";
was_set = false;
}, meths.get_option_info'winhl')

meths.set_option_value('winhl', "A:B", {scope = 'local'})
local info = meths.get_option_info'winhl'
eq(1, info.last_set_chan)
eq(0, info.last_set_linenr)
eq(-9, info.last_set_sid)
eq(true, info.was_set)
end)

it('should have information about buffer options', function()
Expand All @@ -2835,6 +2842,13 @@ describe('API', function()
type = "string",
was_set = false
}, meths.get_option_info'filetype')

meths.set_option_value('filetype', "c", {scope = 'local'})
local info = meths.get_option_info'filetype'
eq(1, info.last_set_chan)
eq(0, info.last_set_linenr)
eq(-9, info.last_set_sid)
eq(true, info.was_set)
end)

it('should have information about global options', function()
Expand Down Expand Up @@ -2878,6 +2892,111 @@ describe('API', function()
end)
end)

describe('nvim_get_option_info_ex', function()
local fname
local bufs
local wins

before_each(function()
fname = tmpname()
write_file(fname, [[
setglobal completeopt=menu " 1, global
setglobal completefunc=myfunc " 2, buffer-local
setglobal concealcursor=n " 3, window-local
setglobal dictionary=mydict " 4, global-local (buffer)
setglobal showbreak=aaa " 5, global-local (window)
setlocal wildoptions=pum " 6, global
setlocal spelloptions=camel " 7, buffer-local
setlocal signcolumn=yes " 8, window-local
setlocal formatprg=myprg " 9, global-local (buffer)
setlocal virtualedit=block " 10, global-local (window)
]])

exec_lua 'vim.cmd.vsplit()'
meths.create_buf(false, false)

bufs = meths.list_bufs()
wins = meths.list_wins()

meths.win_set_buf(wins[1].id, bufs[1].id)
meths.win_set_buf(wins[2].id, bufs[2].id)

meths.set_current_win(wins[2].id)
meths.exec('source ' .. fname, false)

meths.set_current_win(wins[1].id)
end)

after_each(function()
os.remove(fname)
end)

it('should return option information', function()
eq(meths.get_option_info('completeopt'), meths.get_option_info_ex('completeopt', {}))
eq(meths.get_option_info('completefunc'), meths.get_option_info_ex('completefunc', {}))
eq(meths.get_option_info('concealcursor'), meths.get_option_info_ex('concealcursor', {}))
eq(meths.get_option_info('dictionary'), meths.get_option_info_ex('dictionary', {}))
eq(meths.get_option_info('showbreak'), meths.get_option_info_ex('showbreak', {}))
eq(meths.get_option_info('wildoptions'), meths.get_option_info_ex('wildoptions', {}))
eq(meths.get_option_info('spelloptions'), meths.get_option_info_ex('spelloptions', {}))
eq(meths.get_option_info('signcolumn'), meths.get_option_info_ex('signcolumn', {}))
eq(meths.get_option_info('formatprg'), meths.get_option_info_ex('formatprg', {}))
eq(meths.get_option_info('virtualedit'), meths.get_option_info_ex('virtualedit', {}))
end)

describe('last set', function()
it('should point to the global value if set', function()
local info = meths.get_option_info_ex('completefunc', {scope = 'global'})
eq(2, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('should fall back to the global value if buffer local value is not set', function()
local info = meths.get_option_info_ex('completefunc', {buf = bufs[2].id})
eq(2, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('should point to the buffer local value if set', function()
local info = meths.get_option_info_ex('spelloptions', {buf = bufs[2].id})
eq(7, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('should default to the local value for the current buffer', function()
local info = meths.get_option_info_ex('spelloptions', {})
eq(0, info.last_set_linenr)
eq(0, info.last_set_sid)
meths.set_current_win(wins[2].id)
info = meths.get_option_info_ex('spelloptions', {})
eq(7, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('should fall back to the global value if window local value is not set', function()
local info = meths.get_option_info_ex('concealcursor', {win = wins[2].id})
eq(3, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('should point to the window local value if set', function()
local info = meths.get_option_info_ex('signcolumn', {win = wins[2].id})
eq(8, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('should default to the local value for the current window', function()
local info = meths.get_option_info_ex('signcolumn', {})
eq(0, info.last_set_linenr)
eq(0, info.last_set_sid)
meths.set_current_win(wins[2].id)
info = meths.get_option_info_ex('signcolumn', {})
eq(8, info.last_set_linenr)
eq(1, info.last_set_sid)
end)
end)
end)

describe('nvim_echo', function()
local screen

Expand Down

0 comments on commit f087673

Please sign in to comment.