aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Anders <greg@gpanders.com>2021-10-11 22:09:08 -0600
committerGregory Anders <greg@gpanders.com>2021-12-04 14:04:23 -0700
commit71ac00ccb523383411b907b5fdf00a376e24a6f0 (patch)
tree2814a60964d9522692d5c81401e565d40693d6a1
parent7b910a1716eabb18fd05f8c27370a7ab1e771074 (diff)
downloadrneovim-71ac00ccb523383411b907b5fdf00a376e24a6f0.tar.gz
rneovim-71ac00ccb523383411b907b5fdf00a376e24a6f0.tar.bz2
rneovim-71ac00ccb523383411b907b5fdf00a376e24a6f0.zip
feat(api): add nvim_get_option_value
-rw-r--r--runtime/lua/vim/_meta.lua117
-rw-r--r--src/nvim/api/keysets.lua3
-rw-r--r--src/nvim/api/vim.c113
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/option.c25
-rw-r--r--test/functional/api/buffer_spec.lua7
-rw-r--r--test/functional/api/vim_spec.lua27
-rw-r--r--test/functional/api/window_spec.lua10
-rw-r--r--test/functional/lua/vim_spec.lua8
9 files changed, 192 insertions, 120 deletions
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
index a3d5f64630..280222e28a 100644
--- a/runtime/lua/vim/_meta.lua
+++ b/runtime/lua/vim/_meta.lua
@@ -133,107 +133,18 @@ do -- window option accessor
vim.wo = new_win_opt_accessor(nil)
end
---[[
-Local window setter
-
-buffer options: does not get copied when split
- nvim_set_option(buf_opt, value) -> sets the default for NEW buffers
- this sets the hidden global default for buffer options
-
- nvim_buf_set_option(...) -> sets the local value for the buffer
-
- set opt=value, does BOTH global default AND buffer local value
- setlocal opt=value, does ONLY buffer local value
-
-window options: gets copied
- does not need to call nvim_set_option because nobody knows what the heck this does⸮
- We call it anyway for more readable code.
-
-
- Command global value local value
- :set option=value set set
- :setlocal option=value - set
-:setglobal option=value set -
---]]
-local function set_scoped_option(k, v, set_type)
- local info = options_info[k]
-
- -- Don't let people do setlocal with global options.
- -- That is a feature that doesn't make sense.
- if set_type == SET_TYPES.LOCAL and is_global_option(info) then
- error(string.format("Unable to setlocal option: '%s', which is a global option.", k))
- end
-
- -- Only `setlocal` skips setting the default/global value
- -- This will more-or-less noop for window options, but that's OK
- if set_type ~= SET_TYPES.LOCAL then
- a.nvim_set_option(k, v)
- end
-
- if is_window_option(info) then
- if set_type ~= SET_TYPES.GLOBAL then
- a.nvim_win_set_option(0, k, v)
- end
- elseif is_buffer_option(info) then
- if set_type == SET_TYPES.LOCAL
- or (set_type == SET_TYPES.SET and not info.global_local) then
- a.nvim_buf_set_option(0, k, v)
- end
- end
-end
-
---[[
-Local window getter
-
- Command global value local value
- :set option? - display
- :setlocal option? - display
-:setglobal option? display -
---]]
-local function get_scoped_option(k, set_type)
- local info = assert(options_info[k], "Must be a valid option: " .. tostring(k))
-
- if set_type == SET_TYPES.GLOBAL or is_global_option(info) then
- return a.nvim_get_option(k)
- end
-
- if is_buffer_option(info) then
- local was_set, value = pcall(a.nvim_buf_get_option, 0, k)
- if was_set then return value end
-
- if info.global_local then
- return a.nvim_get_option(k)
- end
-
- error("buf_get: This should not be able to happen, given my understanding of options // " .. k)
- end
-
- if is_window_option(info) then
- local ok, value = pcall(a.nvim_win_get_option, 0, k)
- if ok then
- return value
- end
-
- local global_ok, global_val = pcall(a.nvim_get_option, k)
- if global_ok then
- return global_val
- end
-
- error("win_get: This should never happen. File an issue and tag @tjdevries")
- end
-
- error("This fallback case should not be possible. " .. k)
-end
-
-- vim global option
-- this ONLY sets the global option. like `setglobal`
-vim.go = make_meta_accessor(a.nvim_get_option, a.nvim_set_option)
+vim.go = make_meta_accessor(
+ function(k) return a.nvim_get_option_value(k, {scope = "global"}) end,
+ function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end
+)
-- vim `set` style options.
-- it has no additional metamethod magic.
vim.o = make_meta_accessor(
- function(k) return get_scoped_option(k, SET_TYPES.SET) end,
- function(k, v) return set_scoped_option(k, v, SET_TYPES.SET) end
+ function(k) return a.nvim_get_option_value(k, {}) end,
+ function(k, v) return a.nvim_set_option_value(k, v, {}) end
)
---@brief [[
@@ -389,6 +300,10 @@ local convert_value_to_vim = (function()
}
return function(name, info, value)
+ if value == nil then
+ return vim.NIL
+ end
+
local option_type = get_option_type(name, info)
assert_valid_value(name, value, valid_types[option_type])
@@ -671,15 +586,19 @@ local create_option_metatable = function(set_type)
}, option_mt)
end
- -- TODO(tjdevries): consider supporting `nil` for set to remove the local option.
- -- vim.cmd [[set option<]]
+ local scope
+ if set_type == SET_TYPES.GLOBAL then
+ scope = "global"
+ elseif set_type == SET_TYPES.LOCAL then
+ scope = "local"
+ end
option_mt = {
-- To set a value, instead use:
-- opt[my_option] = value
_set = function(self)
local value = convert_value_to_vim(self._name, self._info, self._value)
- set_scoped_option(self._name, value, set_type)
+ a.nvim_set_option_value(self._name, value, {scope = scope})
return self
end,
@@ -716,7 +635,7 @@ local create_option_metatable = function(set_type)
set_mt = {
__index = function(_, k)
- return make_option(k, get_scoped_option(k, set_type))
+ return make_option(k, a.nvim_get_option_value(k, {scope = scope}))
end,
__newindex = function(_, k, v)
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 144c252687..e956a54dbc 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -58,5 +58,8 @@ return {
"highlights";
"use_tabline";
};
+ option = {
+ "scope";
+ };
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 3103905819..36179b1fab 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -642,7 +642,7 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, err);
}
-/// Gets an option value string.
+/// Gets the global value of an option.
///
/// @param name Option name
/// @param[out] err Error details, if any
@@ -653,6 +653,115 @@ Object nvim_get_option(String name, Error *err)
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
+/// 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,
+/// the global value is returned. Local values always correspond to the current
+/// buffer or window. To get a buffer-local or window-local option for a
+/// specific buffer or window, use |nvim_buf_get_option()| or
+/// |nvim_win_get_option()|.
+///
+/// @param name Option name
+/// @param opts Optional parameters
+/// - scope: One of 'global' or 'local'. Analagous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// @param[out] err Error details, if any
+/// @return Option value
+Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ Object rv = OBJECT_INIT;
+
+ int scope = 0;
+ if (opts->scope.type == kObjectTypeString) {
+ if (!strcmp(opts->scope.data.string.data, "local")) {
+ scope = OPT_LOCAL;
+ } else if (!strcmp(opts->scope.data.string.data, "global")) {
+ scope = OPT_GLOBAL;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
+ goto end;
+ }
+ } else if (HAS_KEY(opts->scope)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
+ goto end;
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+ switch (get_option_value(name.data, &numval, (char_u **)&stringval, scope)) {
+ case 0:
+ rv = STRING_OBJ(cstr_as_string(stringval));
+ break;
+ case 1:
+ rv = INTEGER_OBJ(numval);
+ break;
+ case 2:
+ rv = BOOLEAN_OBJ(!!numval);
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
+ goto end;
+ }
+
+end:
+ return rv;
+}
+
+/// Sets the value of an option. The behavior of this function matches that of
+/// |:set|: for global-local options, both the global and local value are set
+/// unless otherwise specified with {scope}.
+///
+/// @param name Option name
+/// @param value New option value
+/// @param opts Optional parameters
+/// - scope: One of 'global' or 'local'. Analagous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// @param[out] err Error details, if any
+void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ int scope = 0;
+ if (opts->scope.type == kObjectTypeString) {
+ if (!strcmp(opts->scope.data.string.data, "local")) {
+ scope = OPT_LOCAL;
+ } else if (!strcmp(opts->scope.data.string.data, "global")) {
+ scope = OPT_GLOBAL;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
+ return;
+ }
+ } else if (HAS_KEY(opts->scope)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
+ return;
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+
+ switch (value.type) {
+ case kObjectTypeInteger:
+ numval = value.data.integer;
+ break;
+ case kObjectTypeBoolean:
+ numval = value.data.boolean ? 1 : 0;
+ break;
+ case kObjectTypeString:
+ stringval = value.data.string.data;
+ break;
+ case kObjectTypeNil:
+ // Do nothing
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "invalid value for option");
+ return;
+ }
+
+ char *e = set_option_value(name.data, numval, stringval, scope);
+ if (e) {
+ api_set_error(err, kErrorTypeException, "%s", e);
+ }
+}
+
/// Gets the option information for all options.
///
/// The dictionary has the full option names as keys and option metadata
@@ -694,7 +803,7 @@ Dictionary nvim_get_option_info(String name, Error *err)
return get_vimoption(name, err);
}
-/// Sets an option value.
+/// Sets the global value of an option.
///
/// @param channel_id
/// @param name Option name
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 29740283c6..2faae08fc8 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -4828,7 +4828,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
} else if (opt_type == -1) { // hidden number option
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
- } else if (opt_type == 1) { // number option
+ } else if (opt_type == 1 || opt_type == 2) { // number or boolean option
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = numval;
} else { // string option
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 45e2032b35..ee5f62d826 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4771,7 +4771,8 @@ static int findoption(const char *const arg)
/// @param stringval NULL when only checking existence
///
/// @returns:
-/// Number or Toggle option: 1, *numval gets value.
+/// Toggle option: 2, *numval gets value.
+/// Number option: 1, *numval gets value.
/// String option: 0, *stringval gets allocated string.
/// Hidden Number or Toggle option: -1.
/// hidden String option: -2.
@@ -4804,16 +4805,18 @@ int get_option_value(const char *name, long *numval, char_u **stringval, int opt
}
if (options[opt_idx].flags & P_NUM) {
*numval = *(long *)varp;
+ return 1;
+ }
+
+ // Special case: 'modified' is b_changed, but we also want to consider
+ // it set when 'ff' or 'fenc' changed.
+ if ((int *)varp == &curbuf->b_changed) {
+ *numval = curbufIsChanged();
} else {
- // Special case: 'modified' is b_changed, but we also want to consider
- // it set when 'ff' or 'fenc' changed.
- if ((int *)varp == &curbuf->b_changed) {
- *numval = curbufIsChanged();
- } else {
- *numval = (long)*(int *)varp; // NOLINT(whitespace/cast)
- }
+ *numval = (long)*(int *)varp; // NOLINT(whitespace/cast)
}
- return 1;
+
+ return 2;
}
// Returns the option attributes and its value. Unlike the above function it
@@ -4909,7 +4912,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
// only getting a pointer, no need to use aucmd_prepbuf()
curbuf = (buf_T *)from;
curwin->w_buffer = curbuf;
- varp = get_varp(p);
+ varp = get_varp_scope(p, OPT_LOCAL);
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
}
@@ -4917,7 +4920,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
win_T *save_curwin = curwin;
curwin = (win_T *)from;
curbuf = curwin->w_buffer;
- varp = get_varp(p);
+ varp = get_varp_scope(p, OPT_LOCAL);
curwin = save_curwin;
curbuf = curwin->w_buffer;
}
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index a0c97804b7..688f3abba5 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -629,6 +629,13 @@ describe('api/buf', function()
-- Doesn't change the global value
eq([[^\s*#\s*define]], nvim('get_option', 'define'))
end)
+
+ it('returns values for unset local options', function()
+ -- 'undolevels' is only set to its "unset" value when a new buffer is
+ -- created
+ command('enew')
+ eq(-123456, curbuf('get_option', 'undolevels'))
+ end)
end)
describe('nvim_buf_get_name, nvim_buf_set_name', function()
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 21de4925b5..d53208a915 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -949,6 +949,33 @@ describe('API', function()
end)
end)
+ describe('nvim_get_option_value, nvim_set_option_value', function()
+ it('works', function()
+ ok(nvim('get_option_value', 'equalalways', {}))
+ nvim('set_option_value', 'equalalways', false, {})
+ ok(not nvim('get_option_value', 'equalalways', {}))
+ end)
+
+ it('can get local values when global value is set', function()
+ eq(0, nvim('get_option_value', 'scrolloff', {}))
+ eq(-1, nvim('get_option_value', 'scrolloff', {scope = 'local'}))
+ end)
+
+ it('can set global and local values', function()
+ nvim('set_option_value', 'makeprg', 'hello', {})
+ eq('hello', nvim('get_option_value', 'makeprg', {}))
+ eq('', nvim('get_option_value', 'makeprg', {scope = 'local'}))
+ nvim('set_option_value', 'makeprg', 'world', {scope = 'local'})
+ eq('world', nvim('get_option_value', 'makeprg', {scope = 'local'}))
+ nvim('set_option_value', 'makeprg', 'goodbye', {scope = 'global'})
+ eq('goodbye', nvim('get_option_value', 'makeprg', {scope = 'global'}))
+ nvim('set_option_value', 'makeprg', 'hello', {})
+ eq('hello', nvim('get_option_value', 'makeprg', {scope = 'global'}))
+ eq('hello', nvim('get_option_value', 'makeprg', {}))
+ eq('', nvim('get_option_value', 'makeprg', {scope = 'local'}))
+ end)
+ end)
+
describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
it('works', function()
eq(1, #nvim('list_bufs'))
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 11755a9d97..4d2ffa316a 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -222,9 +222,9 @@ describe('API/win', function()
eq('', nvim('get_option', 'statusline'))
command("set modified")
command("enew") -- global-local: not preserved in new buffer
- eq("Failed to get value for option 'statusline'",
- pcall_err(curwin, 'get_option', 'statusline'))
- eq('', eval('&l:statusline')) -- confirm local value was not copied
+ -- confirm local value was not copied
+ eq('', curwin('get_option', 'statusline'))
+ eq('', eval('&l:statusline'))
end)
it('after switching windows #15390', function()
@@ -238,6 +238,10 @@ describe('API/win', function()
eq('window-status', window('get_option', win1, 'statusline'))
assert_alive()
end)
+
+ it('returns values for unset local options', function()
+ eq(-1, curwin('get_option', 'scrolloff'))
+ end)
end)
describe('get_position', function()
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 28471bdd46..2515b37beb 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -1240,7 +1240,7 @@ describe('lua stdlib', function()
vim.opt.makeprg = "global-local"
table.insert(result, vim.api.nvim_get_option('makeprg'))
- table.insert(result, (pcall(vim.api.nvim_buf_get_option, 0, 'makeprg')))
+ table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg'))
vim.opt_local.mp = "only-local"
table.insert(result, vim.api.nvim_get_option('makeprg'))
@@ -1258,7 +1258,7 @@ describe('lua stdlib', function()
-- Set -> global & local
eq("global-local", result[1])
- eq(false, result[2])
+ eq("", result[2])
-- Setlocal -> only local
eq("global-local", result[3])
@@ -1268,9 +1268,9 @@ describe('lua stdlib', function()
eq("only-global", result[5])
eq("only-local", result[6])
- -- set -> doesn't override previously set value
+ -- Set -> sets global value and resets local value
eq("global-local", result[7])
- eq("only-local", result[8])
+ eq("", result[8])
end)
it('should allow you to retrieve window opts even if they have not been set', function()