Skip to content

Commit

Permalink
fix(api): Use local LastSet structure in nvim_get_option_info (neovim…
Browse files Browse the repository at this point in the history
…#22741)

fix(api): use local LastSet structure in nvim_get_option_info

* nvim_get_option_info is deprecated.
  It is always using the global LastSet information as reported in neovim#15232.
* nvim_get_option_info2 is added.
  The new function additionally accepts an 'opts' table {scope, buf, win}
  allowing to specify the option scope and query local options from another
  buffer or window.
  • Loading branch information
mliszcz authored and craigmac committed Mar 30, 2023
1 parent 4b69571 commit 3cc29bd
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 16 deletions.
17 changes: 14 additions & 3 deletions runtime/doc/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1912,7 +1912,7 @@ nvim_get_all_options_info() *nvim_get_all_options_info()*
Gets the option information for all options.

The dictionary has the full option names as keys and option metadata
dictionaries as detailed at |nvim_get_option_info()|.
dictionaries as detailed at |nvim_get_option_info2()|.

Return: ~
dictionary of all options
Expand All @@ -1926,8 +1926,8 @@ nvim_get_option({name}) *nvim_get_option()*
Return: ~
Option value (global)

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

Resulting dictionary has keys:
• name: Name of the option (like 'filetype')
Expand All @@ -1943,8 +1943,19 @@ nvim_get_option_info({name}) *nvim_get_option_info()*
• commalist: List of comma separated values
• flaglist: List of single char flags

When {scope} is not provided, 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. This behavior can be disabled by
explicitly specifying {scope} in the {opts} table.

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
Expand Down
1 change: 1 addition & 0 deletions runtime/doc/deprecated.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ API
- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead.
- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead.
- *nvim_exec()* Use |nvim_exec2()| instead.
- *nvim_get_option_info()* Use |nvim_get_option_info2()| instead.

COMMANDS
- *:rv* *:rviminfo* Deprecated alias to |:rshada| command.
Expand Down
14 changes: 14 additions & 0 deletions src/nvim/api/deprecated.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
#include "nvim/option.h"
#include "nvim/pos.h"
#include "nvim/types.h"

Expand Down Expand Up @@ -508,3 +509,16 @@ static int64_t convert_index(int64_t index)
{
return index < 0 ? index - 1 : index;
}

/// Gets the option information for one option
///
/// @deprecated Use @ref nvim_get_option_info2 instead.
///
/// @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, OPT_GLOBAL, curbuf, curwin, err);
}
33 changes: 27 additions & 6 deletions src/nvim/api/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
/// Gets the option information for all options.
///
/// The dictionary has the full option names as keys and option metadata
/// dictionaries as detailed at |nvim_get_option_info()|.
/// dictionaries as detailed at |nvim_get_option_info2()|.
///
/// @return dictionary of all options
Dictionary nvim_get_all_options_info(Error *err)
Expand All @@ -292,7 +292,7 @@ Dictionary nvim_get_all_options_info(Error *err)
return get_all_vimoptions();
}

/// Gets the option information for one option
/// Gets the option information for one option from arbitrary buffer or window
///
/// Resulting dictionary has keys:
/// - name: Name of the option (like 'filetype')
Expand All @@ -311,15 +311,36 @@ Dictionary nvim_get_all_options_info(Error *err)
/// - commalist: List of comma separated values
/// - flaglist: List of single char flags
///
/// When {scope} is not provided, 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. This behavior can be disabled by
/// explicitly specifying {scope} in the {opts} table.
///
/// @param name Option name
/// @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(String name, Error *err)
FUNC_API_SINCE(7)
Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(11)
{
return get_vimoption(name, err);
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 = (opt_type == SREQ_BUF) ? (buf_T *)from : curbuf;
win_T *win = (opt_type == SREQ_WIN) ? (win_T *)from : curwin;

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

/// Sets the global value of an option.
///
/// @param channel_id
Expand Down
31 changes: 24 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, int scope, 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], scope, 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], OPT_GLOBAL, 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, int req_scope, buf_T *buf, win_T *win)
{
Dictionary dict = ARRAY_DICT_INIT;

Expand All @@ -5649,9 +5650,25 @@ 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 = { .channel_id = 0 };
if (req_scope == OPT_GLOBAL) {
last_set = opt->last_set;
} else {
// Scope is either OPT_LOCAL or a fallback mode was requested.
if (opt->indir & PV_BUF) {
last_set = buf->b_p_script_ctx[opt->indir & PV_MASK];
}
if (opt->indir & PV_WIN) {
last_set = win->w_p_script_ctx[opt->indir & PV_MASK];
}
if (req_scope != OPT_LOCAL && 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
93 changes: 93 additions & 0 deletions test/functional/api/vim_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,99 @@ describe('API', function()
end)
end)

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

before_each(function()
fname = tmpname()
write_file(fname, [[
setglobal dictionary=mydict " 1, global-local (buffer)
setlocal formatprg=myprg " 2, global-local (buffer)
setglobal equalprg=prg1 " 3, global-local (buffer)
setlocal equalprg=prg2 " 4, global-local (buffer)
setglobal fillchars=stl:x " 5, global-local (window)
setlocal listchars=eol:c " 6, global-local (window)
setglobal showbreak=aaa " 7, global-local (window)
setlocal showbreak=bbb " 8, global-local (window)
setglobal completeopt=menu " 9, global
]])

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('dictionary'), meths.get_option_info2('dictionary', {})) -- buffer
eq(meths.get_option_info('fillchars'), meths.get_option_info2('fillchars', {})) -- window
eq(meths.get_option_info('completeopt'), meths.get_option_info2('completeopt', {})) -- global
end)

describe('last set', function()
local tests = {
{desc="(buf option, global requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {scope='global'}}},
{desc="(buf option, global requested, local set) is not set", linenr=0, sid=0, args={'formatprg', {scope='global'}}},
{desc="(buf option, global requested, both set) points to global", linenr=3, sid=1, args={'equalprg', {scope='global'}}},
{desc="(buf option, local requested, global set) is not set", linenr=0, sid=0, args={'dictionary', {scope='local'}}},
{desc="(buf option, local requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {scope='local'}}},
{desc="(buf option, local requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {scope='local'}}},
{desc="(buf option, fallback requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {}}},
{desc="(buf option, fallback requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {}}},
{desc="(buf option, fallback requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {}}},
{desc="(win option, global requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {scope='global'}}},
{desc="(win option, global requested, local set) is not set", linenr=0, sid=0, args={'listchars', {scope='global'}}},
{desc="(win option, global requested, both set) points to global", linenr=7, sid=1, args={'showbreak', {scope='global'}}},
{desc="(win option, local requested, global set) is not set", linenr=0, sid=0, args={'fillchars', {scope='local'}}},
{desc="(win option, local requested, local set) points to local", linenr=6, sid=1, args={'listchars', {scope='local'}}},
{desc="(win option, local requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {scope='local'}}},
{desc="(win option, fallback requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {}}},
{desc="(win option, fallback requested, local set) points to local", linenr=6, sid=1, args={'listchars', {}}},
{desc="(win option, fallback requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {}}},
{desc="(global option, global requested) points to global", linenr=9, sid=1, args={'completeopt', {scope='global'}}},
{desc="(global option, local requested) is not set", linenr=0, sid=0, args={'completeopt', {scope='local'}}},
{desc="(global option, fallback requested) points to global", linenr=9, sid=1, args={'completeopt', {}}},
}

for _, t in pairs(tests) do
it(t.desc, function()
-- Switch to the target buffer/window so that curbuf/curwin are used.
meths.set_current_win(wins[2].id)
local info = meths.get_option_info2(unpack(t.args))
eq(t.linenr, info.last_set_linenr)
eq(t.sid, info.last_set_sid)
end)
end

it('is provided for cross-buffer requests', function()
local info = meths.get_option_info2('formatprg', {buf=bufs[2].id})
eq(2, info.last_set_linenr)
eq(1, info.last_set_sid)
end)

it('is provided for cross-window requests', function()
local info = meths.get_option_info2('listchars', {win=wins[2].id})
eq(6, 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 3cc29bd

Please sign in to comment.