From 3c2c022e5e299ecac4663c3813e2db5e2b099ffa Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 7 Dec 2023 01:34:29 +0600 Subject: [PATCH] refactor(options): remove option type macros Problem: We have `P_(BOOL|NUM|STRING)` macros to represent an option's type, which is redundant because `OptValType` can already do that. The current implementation of option type flags is also too limited to allow adding multitype options in the future. Solution: Remove `P_(BOOL|NUM|STRING)` and replace it with a new `type_flags` attribute in `vimoption_T`. Also do some groundwork for adding multitype options in the future. Side-effects: Attempting to set an invalid keycode option (e.g. `set t_foo=123`) no longer gives an error. --- runtime/doc/news.txt | 3 + scripts/gen_eval_files.lua | 6 +- src/nvim/eval.c | 42 +-- src/nvim/eval.h | 1 + src/nvim/eval/vars.c | 64 ++-- src/nvim/generators/gen_options.lua | 33 +- src/nvim/math.c | 26 ++ src/nvim/math.h | 9 + src/nvim/memory.c | 7 + src/nvim/option.c | 494 ++++++++++++---------------- src/nvim/option.h | 61 +++- src/nvim/option_defs.h | 11 +- src/nvim/option_vars.h | 68 ++-- src/nvim/options.lua | 240 +++++++------- test/functional/api/vim_spec.lua | 4 +- test/old/testdir/test_options.vim | 8 +- 16 files changed, 553 insertions(+), 524 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 9bfc577e87..ad2de7a40a 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -346,6 +346,9 @@ The following changes to existing APIs or features add new behavior. • Added "force_crlf" option field in |nvim_open_term()|. +• Attempting to set an invalid keycode option (e.g. `set t_foo=123`) no longer + gives an error. + ============================================================================== REMOVED FEATURES *news-removed* diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua index dddc7d000c..2b0ad4431a 100755 --- a/scripts/gen_eval_files.lua +++ b/scripts/gen_eval_files.lua @@ -59,7 +59,7 @@ local LUA_KEYWORDS = { } local OPTION_TYPES = { - bool = 'boolean', + boolean = 'boolean', number = 'integer', string = 'string', } @@ -603,7 +603,7 @@ local function build_option_tags(opt) local tags = { opt.full_name } tags[#tags + 1] = opt.abbreviation - if opt.type == 'bool' then + if opt.type == 'boolean' then for i = 1, #tags do tags[#tags + 1] = 'no' .. tags[i] end @@ -642,7 +642,7 @@ local function render_option_doc(_f, opt, write) name_str = string.format("'%s'", opt.full_name) end - local otype = opt.type == 'bool' and 'boolean' or opt.type + local otype = opt.type == 'boolean' and 'boolean' or opt.type if opt.defaults.doc or opt.defaults.if_true ~= nil or opt.defaults.meta ~= nil then local v = render_option_default(opt.defaults, true) local pad = string.rep('\t', math.max(1, math.ceil((24 - #name_str) / 8))) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1fdaa95076..11c6034cc8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3767,10 +3767,12 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua FUNC_ATTR_NONNULL_ARG(1) { const bool working = (**arg == '+'); // has("+option") + OptIndex opt_idx; int scope; // Isolate the option name and find its value. - char *option_end = (char *)find_option_end(arg, &scope); + char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &scope); + if (option_end == NULL) { if (rettv != NULL) { semsg(_("E112: Option name missing: %s"), *arg); @@ -3783,12 +3785,11 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua return OK; } - int ret = OK; char c = *option_end; *option_end = NUL; + int ret = OK; bool is_tty_opt = is_tty_option(*arg); - OptIndex opt_idx = is_tty_opt ? kOptInvalid : findoption(*arg); if (opt_idx == kOptInvalid && !is_tty_opt) { // Only give error if result is going to be used. @@ -3801,13 +3802,7 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, scope); assert(value.type != kOptValTypeNil); - *rettv = optval_as_tv(value); - - // Convert boolean option value to number for backwards compatibility. - if (rettv->v_type == VAR_BOOL) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = rettv->vval.v_bool == kBoolVarTrue ? 1 : 0; - } + *rettv = optval_as_tv(value, true); } else if (working && !is_tty_opt && is_option_hidden(opt_idx)) { ret = FAIL; } @@ -8138,13 +8133,14 @@ void ex_execute(exarg_T *eap) eap->nextcmd = check_nextcmd(arg); } -/// Skip over the name of an option: "&option", "&g:option" or "&l:option". +/// Skip over the name of an option variable: "&option", "&g:option" or "&l:option". /// -/// @param arg points to the "&" or '+' when called, to "option" when returning. +/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning. +/// @param[out] opt_idxp Set to option index in options[] table. +/// @param[out] scope Set to option scope. /// -/// @return NULL when no option name found. Otherwise pointer to the char -/// after the option name. -const char *find_option_end(const char **const arg, int *const scope) +/// @return NULL when no option name found. Otherwise pointer to the char after the option name. +const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp, int *const scope) { const char *p = *arg; @@ -8159,19 +8155,9 @@ const char *find_option_end(const char **const arg, int *const scope) *scope = 0; } - if (!ASCII_ISALPHA(*p)) { - return NULL; - } - *arg = p; - - if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) { - p += 4; // t_xx/termcap option - } else { - while (ASCII_ISALPHA(*p)) { - p++; - } - } - return p; + const char *end = find_option_end(p, opt_idxp); + *arg = end == NULL ? *arg : p; + return end; } static var_flavour_T var_flavour(char *varname) diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 6d225b2713..65f7020295 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -13,6 +13,7 @@ #include "nvim/hashtab_defs.h" #include "nvim/macros_defs.h" #include "nvim/mbyte_defs.h" // IWYU pragma: keep +#include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/os/fileio_defs.h" // IWYU pragma: keep #include "nvim/os/stdpaths_defs.h" // IWYU pragma: keep #include "nvim/vim_defs.h" // IWYU pragma: keep diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 73c8ae1191..6fd707a0b0 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -761,11 +761,12 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, // Find the end of the name. char *arg_end = NULL; + OptIndex opt_idx; int scope; - char *const p = (char *)find_option_end((const char **)&arg, &scope); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { + + char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &scope); + + if (p == NULL || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { emsg(_(e_letunexp)); return NULL; } @@ -774,8 +775,6 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, *p = NUL; bool is_tty_opt = is_tty_option(arg); - OptIndex opt_idx = is_tty_opt ? kOptInvalid : findoption(arg); - uint32_t opt_p_flags = get_option_flags(opt_idx); bool hidden = is_option_hidden(opt_idx); OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, scope); OptVal newval = NIL_OPTVAL; @@ -792,7 +791,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, } bool error; - newval = tv_to_optval(tv, arg, opt_p_flags, &error); + newval = tv_to_optval(tv, opt_idx, arg, &error); if (error) { goto theend; } @@ -1849,22 +1848,27 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) /// /// @return Typval converted to OptVal. Must be freed by caller. /// Returns NIL_OPTVAL for invalid option name. -static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, bool *error) +/// +/// TODO(famiu): Refactor this to support multitype options. +static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, bool *error) { OptVal value = NIL_OPTVAL; char nbuf[NUMBUFLEN]; bool err = false; + const bool is_tty_opt = is_tty_option(option); + const bool option_has_bool = !is_tty_opt && option_has_type(opt_idx, kOptValTypeBoolean); + const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kOptValTypeNumber); + const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kOptValTypeString); - if ((flags & P_FUNC) && tv_is_func(*tv)) { + if (!is_tty_opt && (get_option(opt_idx)->flags & P_FUNC) && tv_is_func(*tv)) { // If the option can be set to a function reference or a lambda // and the passed value is a function reference, then convert it to // the name (string) of the function reference. char *strval = encode_tv2string(tv, NULL); err = strval == NULL; value = CSTR_AS_OPTVAL(strval); - } else if (flags & (P_NUM | P_BOOL)) { - varnumber_T n = (flags & P_NUM) ? tv_get_number_chk(tv, &err) - : tv_get_bool_chk(tv, &err); + } else if (option_has_bool || option_has_num) { + varnumber_T n = option_has_num ? tv_get_number_chk(tv, &err) : tv_get_bool_chk(tv, &err); // This could be either "0" or a string that's not a number. // So we need to check if it's actually a number. if (!err && tv->v_type == VAR_STRING && n == 0) { @@ -1877,14 +1881,14 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); } } - value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); - } else if ((flags & P_STRING) || is_tty_option(option)) { + value = option_has_num ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); + } else if (option_has_str) { // Avoid setting string option to a boolean or a special value. if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { const char *strval = tv_get_string_buf_chk(tv, nbuf); err = strval == NULL; value = CSTR_TO_OPTVAL(strval); - } else if (flags & P_STRING) { + } else if (!is_tty_opt) { err = true; emsg(_(e_stringreq)); } @@ -1900,10 +1904,12 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo /// Convert an option value to typval. /// -/// @param[in] value Option value to convert. +/// @param[in] value Option value to convert. +/// @param numbool Whether to convert boolean values to number. +/// Used for backwards compatibility. /// /// @return OptVal converted to typval. -typval_T optval_as_tv(OptVal value) +typval_T optval_as_tv(OptVal value, bool numbool) { typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } }; @@ -1911,19 +1917,16 @@ typval_T optval_as_tv(OptVal value) case kOptValTypeNil: break; case kOptValTypeBoolean: - switch (value.data.boolean) { - case kTrue: - rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = kBoolVarTrue; - break; - case kFalse: - rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = kBoolVarFalse; - break; - case kNone: - break; // return v:null for None boolean value + if (value.data.boolean != kNone) { + if (numbool) { + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = value.data.boolean == kTrue; + } else { + rettv.v_type = VAR_BOOL; + rettv.vval.v_bool = value.data.boolean == kTrue; + } } - break; + break; // return v:null for None boolean value. case kOptValTypeNumber: rettv.v_type = VAR_NUMBER; rettv.vval.v_number = value.data.number; @@ -1947,8 +1950,7 @@ static void set_option_from_tv(const char *varname, typval_T *varp) } bool error = false; - uint32_t opt_p_flags = get_option_flags(opt_idx); - OptVal value = tv_to_optval(varp, varname, opt_p_flags, &error); + OptVal value = tv_to_optval(varp, opt_idx, varname, &error); if (!error) { const char *errmsg = set_option_value_handle_tty(varname, opt_idx, value, OPT_LOCAL); diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index b7356a7bb1..61d5df3c84 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -16,12 +16,6 @@ local options = require('options') local cstr = options.cstr -local type_flags = { - bool = 'P_BOOL', - number = 'P_NUM', - string = 'P_STRING', -} - local redraw_flags = { ui_option = 'P_UI_OPTION', tabline = 'P_RTABL', @@ -51,11 +45,14 @@ end --- @param o vim.option_meta --- @return string local function get_flags(o) - --- @type string[] - local ret = { type_flags[o.type] } + --- @type string + local flags = '0' + + --- @param f string local add_flag = function(f) - ret[1] = ret[1] .. '|' .. f + flags = flags .. '|' .. f end + if o.list then add_flag(list_flags[o.list]) end @@ -91,7 +88,22 @@ local function get_flags(o) add_flag(def_name) end end - return ret[1] + return flags +end + +--- @param o vim.option_meta +--- @return string +local function get_type_flags(o) + local opt_types = (type(o.type) == 'table') and o.type or { o.type } + local type_flags = '0' + assert(type(opt_types) == 'table') + + for _, opt_type in ipairs(opt_types) do + assert(type(opt_type) == 'string') + type_flags = ('%s | (1 << kOptValType%s)'):format(type_flags, lowercase_to_titlecase(opt_type)) + end + + return type_flags end --- @param c string|string[] @@ -153,6 +165,7 @@ local function dump_option(i, o) w(' .shortname=' .. cstr(o.abbreviation)) end w(' .flags=' .. get_flags(o)) + w(' .type_flags=' .. get_type_flags(o)) if o.enable_if then w(get_cond(o.enable_if)) end diff --git a/src/nvim/math.c b/src/nvim/math.c index 79e0be691b..9a0825823c 100644 --- a/src/nvim/math.c +++ b/src/nvim/math.c @@ -40,3 +40,29 @@ int xisnan(double d) { return FP_NAN == xfpclassify(d); } + +/// Count trailing zeroes at the end of bit field. +int xctz(uint64_t x) +{ + // If x == 0, that means all bits are zeroes. + if (x == 0) { + return 8 * sizeof(x); + } + + // Use compiler builtin if possible. +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 4)) + return __builtin_ctzll(x); +#else + int count = 0; + // Set x's trailing zeroes to ones and zero the rest. + x = (x ^ (x - 1)) >> 1; + + // Increment count until there are just zero bits remaining. + while (x) { + count++; + x >>= 1; + } + + return count; +#endif +} diff --git a/src/nvim/math.h b/src/nvim/math.h index c88ce1e03d..0321d0c9c6 100644 --- a/src/nvim/math.h +++ b/src/nvim/math.h @@ -1,5 +1,14 @@ #pragma once +#include +#include + +/// Check if number is a power of two +static inline bool is_power_of_two(uint64_t x) +{ + return x != 0 && ((x & (x - 1)) == 0); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "math.h.generated.h" #endif diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 7036c91c9b..a0786391b5 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -515,6 +515,13 @@ bool strequal(const char *a, const char *b) return (a == NULL && b == NULL) || (a && b && strcmp(a, b) == 0); } +/// Returns true if first `n` characters of strings `a` and `b` are equal. Arguments may be NULL. +bool strnequal(const char *a, const char *b, size_t n) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (a == NULL && b == NULL) || (a && b && strncmp(a, b, n) == 0); +} + // Avoid repeating the error message many times (they take 1 second each). // Did_outofmem_msg is reset when a character is read. void do_outofmem_msg(size_t size) diff --git a/src/nvim/option.c b/src/nvim/option.c index 6aa22c19af..1f4f547759 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -108,8 +108,6 @@ static const char e_not_allowed_in_modeline[] = N_("E520: Not allowed in a modeline"); static const char e_not_allowed_in_modeline_when_modelineexpr_is_off[] = N_("E992: Not allowed in a modeline when 'modelineexpr' is off"); -static const char e_key_code_not_set[] - = N_("E846: Key code not set"); static const char e_number_required_after_equal[] = N_("E521: Number required after ="); static const char e_preview_window_already_exists[] @@ -419,7 +417,9 @@ void set_init_1(bool clean_arg) /// Set an option to its default value. /// This does not take care of side effects! /// -/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL +/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL. +/// +/// TODO(famiu): Refactor this when def_val uses OptVal. static void set_option_default(const OptIndex opt_idx, int opt_flags) { int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; @@ -429,7 +429,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) void *varp = get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); uint32_t flags = opt->flags; if (varp != NULL) { // skip hidden option, nothing to do for it - if (flags & P_STRING) { + if (option_has_type(opt_idx, kOptValTypeString)) { // Use set_string_option_direct() for local options to handle freeing and allocating the // value. if (opt->indir != PV_NONE) { @@ -441,7 +441,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) *(char **)varp = opt->def_val; opt->flags &= ~P_ALLOCED; } - } else if (flags & P_NUM) { + } else if (option_has_type(opt_idx, kOptValTypeNumber)) { if (opt->indir == PV_SCROLL) { win_comp_scroll(curwin); } else { @@ -459,7 +459,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) *(OptInt *)get_varp_scope(opt, OPT_GLOBAL) = def_val; } } - } else { // P_BOOL + } else { // boolean *(int *)varp = (int)(intptr_t)opt->def_val; #ifdef UNIX // 'modeline' defaults to off for root @@ -1015,81 +1015,23 @@ static set_prefix_T get_option_prefix(char **argp) return PREFIX_NONE; } -/// @param[in] arg Pointer to start option name -/// @param[out] opt_idxp Option index in options[] table. -/// @param[out] keyp -/// @param[out] len Length of option name -/// @return FAIL if an error is detected, OK otherwise -static int parse_option_name(char *arg, int *keyp, int *lenp, OptIndex *opt_idxp) -{ - // find end of name - int key = 0; - int len; - OptIndex opt_idx; - - if (*arg == '<') { - opt_idx = kOptInvalid; - // look out for ;> - if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) { - len = 5; - } else { - len = 1; - while (arg[len] != NUL && arg[len] != '>') { - len++; - } - } - if (arg[len] != '>') { - return FAIL; - } - if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len(arg + 1, (size_t)(len - 1)); - } - len++; - if (opt_idx == kOptInvalid) { - key = find_key_option(arg + 1, true); - } - } else { - // The two characters after "t_" may not be alphanumeric. - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { - len = 4; - } else { - len = 0; - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { - len++; - } - } - opt_idx = findoption_len(arg, (size_t)len); - if (opt_idx == kOptInvalid) { - key = find_key_option(arg, false); - } - } - - *keyp = key; - *lenp = len; - *opt_idxp = opt_idx; - - return OK; -} - static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_t flags, set_prefix_T prefix, const char **errmsg) { // Only bools can have a prefix of 'inv' or 'no' - if (!(flags & P_BOOL) && prefix != PREFIX_NONE) { + if (!option_has_type(opt_idx, kOptValTypeBoolean) && prefix != PREFIX_NONE) { *errmsg = e_invarg; return FAIL; } // Skip all options that are not window-local (used when showing // an already loaded buffer in a window). - if ((opt_flags & OPT_WINONLY) - && (opt_idx == kOptInvalid || options[opt_idx].var != VAR_WIN)) { + if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || options[opt_idx].var != VAR_WIN)) { return FAIL; } // Skip all options that are window-local (used for :vimgrep). - if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid - && options[opt_idx].var == VAR_WIN) { + if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && options[opt_idx].var == VAR_WIN) { return FAIL; } @@ -1123,6 +1065,77 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_ return OK; } +/// Skip over the name of a TTY option or keycode option. +/// +/// @param[in] arg Start of TTY or keycode option name. +/// +/// @return NULL when option isn't a TTY or keycode option. Otherwise pointer to the char after the +/// option name. +static const char *find_tty_option_end(const char *arg) +{ + if (strequal(arg, "term")) { + return arg + sizeof("term") - 1; + } else if (strequal(arg, "ttytype")) { + return arg + sizeof("ttytype") - 1; + } + + const char *p = arg; + bool delimit = false; // whether to delimit < + + if (arg[0] == '<') { + // look out for ;> + delimit = true; + p++; + } + if (p[0] == 't' && p[1] == '_' && p[2] && p[3]) { + p += 4; + } else if (delimit) { + // Search for delimiting >. + while (*p != NUL && *p != '>') { + p++; + } + } + // Return NULL when delimiting > is not found. + if (delimit) { + if (*p != '>') { + return NULL; + } + p++; + } + + return arg == p ? NULL : p; +} + +/// Skip over the name of an option. +/// +/// @param[in] arg Start of option name. +/// @param[out] opt_idxp Set to option index in options[] table. +/// +/// @return NULL when no option name found. Otherwise pointer to the char after the option name. +const char *find_option_end(const char *arg, OptIndex *opt_idxp) +{ + const char *p; + + // Handle TTY and keycode options separately. + if ((p = find_tty_option_end(arg)) != NULL) { + *opt_idxp = kOptInvalid; + return p; + } else { + p = arg; + } + + if (!ASCII_ISALPHA(*p)) { + *opt_idxp = kOptInvalid; + return NULL; + } + while (ASCII_ISALPHA(*p)) { + p++; + } + + *opt_idxp = findoption_len(arg, (size_t)(p - arg)); + return p; +} + /// Get new option value from argp. Allocated OptVal must be freed by caller. static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp, int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf, @@ -1262,61 +1275,56 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * char *arg = *argp; // find end of name - int key = 0; - int len; OptIndex opt_idx; - if (parse_option_name(arg, &key, &len, &opt_idx) == FAIL) { - *errmsg = e_invarg; + const char *const option_end = find_option_end(arg, &opt_idx); + + if (opt_idx != kOptInvalid) { + assert(option_end >= arg); + } else if (is_tty_option(arg)) { // Silently ignore TTY options. return; - } - - // remember character after option name - int afterchar = (uint8_t)arg[len]; - - // skip white space, allow ":set ai ?" - while (ascii_iswhite(arg[len])) { - len++; - } - - set_op_T op = get_op(arg + len); - if (op != OP_NONE) { - len++; - } - - uint8_t nextchar = (uint8_t)arg[len]; // next non-white char after option name - - if (opt_idx == kOptInvalid && key == 0) { // found a mismatch: skip + } else { // Invalid option name, skip. *errmsg = e_unknown_option; return; } - uint32_t flags; // flags for current option + // Remember character after option name. + uint8_t afterchar = (uint8_t)(*option_end); + char *p = (char *)option_end; + + // Skip white space, allow ":set ai ?". + while (ascii_iswhite(*p)) { + p++; + } + + set_op_T op = get_op(p); + if (op != OP_NONE) { + p++; + } + + uint8_t nextchar = (uint8_t)(*p); // next non-white char after option name + uint32_t flags = 0; // flags for current option void *varp = NULL; // pointer to variable for current option - if (opt_idx != kOptInvalid) { - if (options[opt_idx].var == NULL) { // hidden option: skip - // Only give an error message when requesting the value of - // a hidden option, ignore setting it. - if (vim_strchr("=:!&<", nextchar) == NULL - && (!(options[opt_idx].flags & P_BOOL) - || nextchar == '?')) { - *errmsg = e_unsupportedoption; - } - return; + if (options[opt_idx].var == NULL) { // hidden option: skip + // Only give an error message when requesting the value of + // a hidden option, ignore setting it. + if (vim_strchr("=:!&<", nextchar) == NULL + && (!option_has_type(opt_idx, kOptValTypeBoolean) || nextchar == '?')) { + *errmsg = e_unsupportedoption; } - - flags = options[opt_idx].flags; - varp = get_varp_scope(&(options[opt_idx]), opt_flags); - } else { - flags = P_STRING; + return; } + flags = options[opt_idx].flags; + varp = get_varp_scope(&(options[opt_idx]), opt_flags); + if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) { return; } if (vim_strchr("?=:!&<", nextchar) != NULL) { - *argp += len; + *argp = p; + if (nextchar == '&' && (*argp)[1] == 'v' && (*argp)[2] == 'i') { if ((*argp)[3] == 'm') { // "opt&vim": set to Vim default *argp += 3; @@ -1331,14 +1339,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * } } - // - // allow '=' and ':' as MS-DOS command.com allows only one - // '=' character per "set" command line. grrr. (jw) - // + // Allow '=' and ':' as MS-DOS command.com allows only one '=' character per "set" command line. if (nextchar == '?' - || (prefix == PREFIX_NONE - && vim_strchr("=:&<", nextchar) == NULL - && !(flags & P_BOOL))) { + || (prefix == PREFIX_NONE && vim_strchr("=:&<", nextchar) == NULL + && !option_has_type(opt_idx, kOptValTypeBoolean))) { // print value if (*did_show) { msg_putchar('\n'); // cursor below last one @@ -1346,29 +1350,26 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * gotocmdline(true); // cursor at status line *did_show = true; // remember that we did a line } - if (opt_idx != kOptInvalid) { - showoneopt(&options[opt_idx], opt_flags); - if (p_verbose > 0) { - // Mention where the option was last set. - if (varp == options[opt_idx].var) { - option_last_set_msg(options[opt_idx].last_set); - } else if ((int)options[opt_idx].indir & PV_WIN) { - option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); - } else if ((int)options[opt_idx].indir & PV_BUF) { - option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); - } + showoneopt(&options[opt_idx], opt_flags); + + if (p_verbose > 0) { + // Mention where the option was last set. + if (varp == options[opt_idx].var) { + option_last_set_msg(options[opt_idx].last_set); + } else if ((int)options[opt_idx].indir & PV_WIN) { + option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); + } else if ((int)options[opt_idx].indir & PV_BUF) { + option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); } - } else { - *errmsg = e_key_code_not_set; - return; } + if (nextchar != '?' && nextchar != NUL && !ascii_iswhite(afterchar)) { *errmsg = e_trailing; } return; } - if (flags & P_BOOL) { + if (option_has_type(opt_idx, kOptValTypeBoolean)) { if (vim_strchr("=:", nextchar) != NULL) { *errmsg = e_invarg; return; @@ -1690,7 +1691,7 @@ static void didset_options2(void) void check_options(void) { for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { - if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) { + if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) { check_string_option((char **)get_varp(&(options[opt_idx]))); } } @@ -1870,10 +1871,10 @@ static void apply_optionset_autocmd(OptIndex opt_idx, int opt_flags, OptVal oldv } char buf_type[7]; - typval_T oldval_tv = optval_as_tv(oldval); - typval_T oldval_g_tv = optval_as_tv(oldval_g); - typval_T oldval_l_tv = optval_as_tv(oldval_l); - typval_T newval_tv = optval_as_tv(newval); + typval_T oldval_tv = optval_as_tv(oldval, false); + typval_T oldval_g_tv = optval_as_tv(oldval_g, false); + typval_T oldval_l_tv = optval_as_tv(oldval_l, false); + typval_T newval_tv = optval_as_tv(newval, false); vim_snprintf(buf_type, sizeof(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_tv(VV_OPTION_NEW, &newval_tv); @@ -3092,10 +3093,11 @@ OptIndex findoption_len(const char *const arg, const size_t len) bool is_tty_option(const char *name) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || strequal(name, "term") || strequal(name, "ttytype"); + return find_tty_option_end(name) != NULL; } #define TCO_BUFFER_SIZE 8 + /// Get value of TTY option. /// /// @param name Name of TTY option. @@ -3145,7 +3147,7 @@ bool set_tty_option(const char *name, char *value) return false; } -/// Find index for an option +/// Find index for an option. /// /// @param[in] arg Option name. /// @@ -3203,27 +3205,7 @@ bool optval_equal(OptVal o1, OptVal o2) return o1.data.number == o2.data.number; case kOptValTypeString: return o1.data.string.size == o2.data.string.size - && strequal(o1.data.string.data, o2.data.string.data); - } - UNREACHABLE; -} - -/// Match type of OptVal with the type of the target option. Returns true if the types match and -/// false otherwise. -static bool optval_match_type(OptVal o, OptIndex opt_idx) -{ - assert(opt_idx != kOptInvalid); - uint32_t flags = options[opt_idx].flags; - - switch (o.type) { - case kOptValTypeNil: - return false; - case kOptValTypeBoolean: - return flags & P_BOOL; - case kOptValTypeNumber: - return flags & P_NUM; - case kOptValTypeString: - return flags & P_STRING; + && strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size); } UNREACHABLE; } @@ -3232,6 +3214,8 @@ static bool optval_match_type(OptVal o, OptIndex opt_idx) /// /// @param opt_idx Option index in options[] table. /// @param[out] varp Pointer to option variable. +/// +/// @return Option value stored in varp. OptVal optval_from_varp(OptIndex opt_idx, void *varp) { // Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc' @@ -3240,19 +3224,17 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp) return BOOLEAN_OPTVAL(curbufIsChanged()); } - uint32_t flags = options[opt_idx].flags; - - OptValType type = kOptValTypeNil; - if (flags & P_BOOL) { - type = kOptValTypeBoolean; - } else if (flags & P_NUM) { - type = kOptValTypeNumber; - } else if (flags & P_STRING) { - type = kOptValTypeString; - } else { - abort(); + if (option_is_multitype(opt_idx)) { + // Multitype options are stored as OptVal. + return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp; } + // If the option only supports a single type, it means that the index of the option's type flag + // corresponds to the value of the type enum. So get the index of the type flag using xctz() and + // use that as the option's type. + OptValType type = xctz(options[opt_idx].type_flags); + assert(type > kOptValTypeNil && type < kOptValTypeSize); + switch (type) { case kOptValTypeNil: return NIL_OPTVAL; @@ -3266,7 +3248,7 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp) UNREACHABLE; } -/// Set option var pointer value from Optval. +/// Set option var pointer value from OptVal. /// /// @param opt_idx Option index in options[] table. /// @param[out] varp Pointer to option variable. @@ -3275,7 +3257,7 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp) static void set_option_varp(OptIndex opt_idx, void *varp, OptVal value, bool free_oldval) FUNC_ATTR_NONNULL_ARG(2) { - assert(optval_match_type(value, opt_idx)); + assert(option_has_type(opt_idx, value.type)); if (free_oldval) { optval_free(optval_from_varp(opt_idx, varp)); @@ -3283,7 +3265,7 @@ static void set_option_varp(OptIndex opt_idx, void *varp, OptVal value, bool fre switch (value.type) { case kOptValTypeNil: - return; + abort(); case kOptValTypeBoolean: *(int *)varp = value.data.boolean; return; @@ -3374,7 +3356,7 @@ static OptVal optval_unset_local(OptIndex opt_idx, void *varp) // For global-local options, use the unset value of the local value. if (opt->indir & PV_BOTH) { // String global-local options always use an empty string for the unset value. - if (opt->flags & P_STRING) { + if (option_has_type(opt_idx, kOptValTypeString)) { return STATIC_CSTR_TO_OPTVAL(""); } @@ -3399,34 +3381,20 @@ static OptVal optval_unset_local(OptIndex opt_idx, void *varp) /// number, boolean or string, the function returns "Number/Boolean/String" static char *option_get_valid_types(OptIndex opt_idx) { - uint32_t flags = options[opt_idx].flags; - uint32_t type_count = 0; - StringBuilder str = KV_INITIAL_VALUE; kv_resize(str, 32); -#define OPTION_ADD_TYPE(typename) \ - do { \ - if (type_count == 0) { \ - kv_concat(str, typename); \ - } else { \ - kv_printf(str, "/%s", typename); \ - } \ - type_count++; \ - } while (0); + // Iterate through every valid option value type and check if the option supports that type + for (OptValType type = 0; type < kOptValTypeSize; type++) { + if (option_has_type(opt_idx, type)) { + const char *typename = optval_type_get_name(type); - if (flags & P_NUM) { - OPTION_ADD_TYPE("Number"); - } - if (flags & P_BOOL) { - OPTION_ADD_TYPE("Boolean"); - } - if (flags & P_STRING) { - OPTION_ADD_TYPE("String"); - } - - if (type_count == 0) { - abort(); + if (str.size == 0) { + kv_concat(str, typename); + } else { + kv_printf(str, "/%s", typename); + } + } } // Ensure that the string is NUL-terminated. @@ -3680,13 +3648,6 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, vimoption_T *opt = &options[opt_idx]; - static const char *optval_type_names[] = { - [kOptValTypeNil] = "Nil", - [kOptValTypeBoolean] = "Boolean", - [kOptValTypeNumber] = "Number", - [kOptValTypeString] = "String" - }; - if (value.type == kOptValTypeNil) { // Don't try to unset local value if scope is global. // TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is @@ -3697,11 +3658,11 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, optval_free(value); value = optval_unset_local(opt_idx, varp); } - } else if (!optval_match_type(value, opt_idx)) { + } else if (!option_has_type(opt_idx, value.type)) { char *rep = optval_to_cstr(value); char *valid_types = option_get_valid_types(opt_idx); snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"), - opt->fullname, valid_types, optval_type_names[value.type], rep); + opt->fullname, valid_types, optval_type_get_name(value.type), rep); xfree(rep); xfree(valid_types); errmsg = errbuf; @@ -3943,11 +3904,11 @@ static void showoptions(bool all, int opt_flags) } else { varp = get_varp(opt); } - if (varp != NULL && (all || !optval_default(opt, varp))) { + if (varp != NULL && (all || !optval_default(opt_idx, varp))) { int len; if (opt_flags & OPT_ONECOLUMN) { len = Columns; - } else if (opt->flags & P_BOOL) { + } else if (option_has_type(opt_idx, kOptValTypeBoolean)) { len = 1; // a toggle option fits always } else { option_value2string(opt, opt_flags); @@ -3994,19 +3955,19 @@ static void showoptions(bool all, int opt_flags) } /// Return true if option "p" has its default value. -static int optval_default(vimoption_T *p, const void *varp) +static int optval_default(OptIndex opt_idx, void *varp) { - if (varp == NULL) { - return true; // hidden option is always at default + vimoption_T *opt = &options[opt_idx]; + + // Hidden or immutable options always use their default value. + if (varp == NULL || opt->immutable) { + return true; } - if (p->flags & P_NUM) { - return *(OptInt *)varp == (OptInt)(intptr_t)p->def_val; - } - if (p->flags & P_BOOL) { - return *(int *)varp == (int)(intptr_t)p->def_val; - } - // P_STRING - return strcmp(*(char **)varp, p->def_val) == 0; + + OptVal current_val = optval_from_varp(opt_idx, varp); + OptVal default_val = optval_from_varp(opt_idx, &opt->def_val); + + return optval_equal(current_val, default_val); } /// Send update to UIs with values of UI relevant options @@ -4018,16 +3979,7 @@ void ui_refresh_options(void) continue; } String name = cstr_as_string(options[opt_idx].fullname); - void *varp = options[opt_idx].var; - Object value = OBJECT_INIT; - if (flags & P_BOOL) { - value = BOOLEAN_OBJ(*(int *)varp); - } else if (flags & P_NUM) { - value = INTEGER_OBJ(*(OptInt *)varp); - } else if (flags & P_STRING) { - // cstr_as_string handles NULL string - value = CSTR_AS_OBJ(*(char **)varp); - } + Object value = optval_as_object(optval_from_varp(opt_idx, options[opt_idx].var)); ui_call_option_set(name, value); } if (p_mouse != NULL) { @@ -4039,29 +3991,30 @@ void ui_refresh_options(void) /// must not be called with a hidden option! /// /// @param opt_flags OPT_LOCAL or OPT_GLOBAL -static void showoneopt(vimoption_T *p, int opt_flags) +static void showoneopt(vimoption_T *opt, int opt_flags) { int save_silent = silent_mode; silent_mode = false; info_message = true; // use os_msg(), not os_errmsg() - void *varp = get_varp_scope(p, opt_flags); + OptIndex opt_idx = get_opt_idx(opt); + void *varp = get_varp_scope(opt, opt_flags); // for 'modified' we also need to check if 'ff' or 'fenc' changed. - if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed - ? !curbufIsChanged() : !*(int *)varp)) { + if (option_has_type(opt_idx, kOptValTypeBoolean) + && ((int *)varp == &curbuf->b_changed ? !curbufIsChanged() : !*(int *)varp)) { msg_puts("no"); - } else if ((p->flags & P_BOOL) && *(int *)varp < 0) { + } else if (option_has_type(opt_idx, kOptValTypeBoolean) && *(int *)varp < 0) { msg_puts("--"); } else { msg_puts(" "); } - msg_puts(p->fullname); - if (!(p->flags & P_BOOL)) { + msg_puts(opt->fullname); + if (!(option_has_type(opt_idx, kOptValTypeBoolean))) { msg_putchar('='); // put value string in NameBuff - option_value2string(p, opt_flags); + option_value2string(opt, opt_flags); msg_outtrans(NameBuff, 0); } @@ -4122,7 +4075,7 @@ int makeset(FILE *fd, int opt_flags, int local_only) continue; } // Global values are only written when not at the default value. - if ((opt_flags & OPT_GLOBAL) && optval_default(opt, varp)) { + if ((opt_flags & OPT_GLOBAL) && optval_default(opt_idx, varp)) { continue; } @@ -4143,7 +4096,7 @@ int makeset(FILE *fd, int opt_flags, int local_only) // default, need to write it too. if (!(opt_flags & OPT_GLOBAL) && !local_only) { void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL); // local value - if (!optval_default(opt, varp_fresh)) { + if (!optval_default(opt_idx, varp_fresh)) { round = 1; varp_local = varp; varp = varp_fresh; @@ -4162,15 +4115,15 @@ int makeset(FILE *fd, int opt_flags, int local_only) cmd = "setlocal"; } - if (opt->flags & P_BOOL) { + if (option_has_type(opt_idx, kOptValTypeBoolean)) { if (put_setbool(fd, cmd, opt->fullname, *(int *)varp) == FAIL) { return FAIL; } - } else if (opt->flags & P_NUM) { + } else if (option_has_type(opt_idx, kOptValTypeNumber)) { if (put_setnum(fd, cmd, opt->fullname, (OptInt *)varp) == FAIL) { return FAIL; } - } else { // P_STRING + } else { // string int do_endif = false; // Don't set 'syntax' and 'filetype' again if the value is @@ -4688,6 +4641,13 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(buf->b_p_wm); } +/// Get option index from option pointer +static inline OptIndex get_opt_idx(vimoption_T *opt) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return (OptIndex)(opt - options); +} + /// Get pointer to option variable. static inline void *get_varp(vimoption_T *p) { @@ -5179,7 +5139,7 @@ void set_imsearch_global(buf_T *buf) p_imsearch = buf->b_p_imsearch; } -static int expand_option_idx = kOptInvalid; +static OptIndex expand_option_idx = kOptInvalid; static int expand_option_start_col = 0; static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL }; static int expand_option_flags = 0; @@ -5275,7 +5235,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) return; } flags = options[opt_idx].flags; - if (flags & P_BOOL) { + if (option_has_type(opt_idx, kOptValTypeBoolean)) { xp->xp_context = EXPAND_NOTHING; return; } @@ -5336,7 +5296,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) xp->xp_context = EXPAND_NOTHING; } - if (is_term_option || (flags & P_NUM)) { + if (is_term_option || option_has_type(opt_idx, kOptValTypeNumber)) { return; } @@ -5494,7 +5454,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM continue; } if (xp->xp_context == EXPAND_BOOL_SETTINGS - && !(options[opt_idx].flags & P_BOOL)) { + && !(option_has_type(opt_idx, kOptValTypeBoolean))) { continue; } @@ -5638,7 +5598,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c uint32_t option_flags = options[expand_option_idx].flags; - if (option_flags & P_NUM) { + if (option_has_type(expand_option_idx, kOptValTypeNumber)) { return ExpandOldSetting(numMatches, matches); } else if (option_flags & P_COMMA) { // Split the option by comma, then present each option to the user if @@ -5733,11 +5693,13 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c /// NameBuff[]. Must not be called with a hidden option! /// /// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL -static void option_value2string(vimoption_T *opp, int scope) +/// +/// TODO(famiu): Replace this with optval_to_cstr() if possible. +static void option_value2string(vimoption_T *opt, int scope) { - void *varp = get_varp_scope(opp, scope); + void *varp = get_varp_scope(opt, scope); - if (opp->flags & P_NUM) { + if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) { OptInt wc = 0; if (wc_use_keyname(varp, &wc)) { @@ -5750,11 +5712,11 @@ static void option_value2string(vimoption_T *opp, int scope) "%" PRId64, (int64_t)(*(OptInt *)varp)); } - } else { // P_STRING + } else { // string varp = *(char **)(varp); if (varp == NULL) { // Just in case. NameBuff[0] = NUL; - } else if (opp->flags & P_EXPAND) { + } else if (opt->flags & P_EXPAND) { home_replace(NULL, varp, NameBuff, MAXPATHL, false); } else { xstrlcpy(NameBuff, varp, MAXPATHL); @@ -6155,15 +6117,8 @@ dict_T *get_winbuf_options(const int bufopt) void *varp = get_varp(opt); if (varp != NULL) { - if (opt->flags & P_STRING) { - tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), - *(const char **)varp); - } else if (opt->flags & P_NUM) { - tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), - *(OptInt *)varp); - } else { - tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *(int *)varp); - } + typval_T opt_tv = optval_as_tv(optval_from_varp(opt_idx, varp), true); + tv_dict_add_tv(d, opt->fullname, strlen(opt->fullname), &opt_tv); } } } @@ -6254,24 +6209,11 @@ static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, wi 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; // TODO(bfredl): do you even nocp? - char *def_val = opt->def_val; - if (opt->flags & P_STRING) { - type = "string"; - def = CSTR_TO_OBJ(def_val ? def_val : ""); - } else if (opt->flags & P_NUM) { - type = "number"; - def = INTEGER_OBJ((Integer)(intptr_t)def_val); - } else if (opt->flags & P_BOOL) { - type = "boolean"; - def = BOOLEAN_OBJ((intptr_t)def_val); - } else { - type = ""; def = NIL; - } - PUT(dict, "type", CSTR_TO_OBJ(type)); - PUT(dict, "default", def); + OptVal def = optval_from_varp(get_opt_idx(opt), &opt->def_val); + + PUT(dict, "type", CSTR_TO_OBJ(optval_type_get_name(def.type))); + PUT(dict, "default", optval_as_object(optval_copy(def))); PUT(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP))); return dict; diff --git a/src/nvim/option.h b/src/nvim/option.h index db438342ab..034c3de148 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -8,6 +8,7 @@ #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep +#include "nvim/math.h" #include "nvim/option_defs.h" // IWYU pragma: export #include "nvim/types_defs.h" // IWYU pragma: keep @@ -38,17 +39,16 @@ typedef enum { #define VAR_WIN ((char *)-1) typedef struct vimoption { - char *fullname; ///< full option name - char *shortname; ///< permissible abbreviation - uint32_t flags; ///< see above - void *var; ///< global option: pointer to variable; - ///< window-local option: VAR_WIN; - ///< buffer-local option: global value - idopt_T indir; ///< global option: PV_NONE; - ///< local option: indirect option index - ///< callback function to invoke after an option is modified to validate and - ///< apply the new value. - bool immutable; ///< option value cannot be changed from the default value. + char *fullname; ///< full option name + char *shortname; ///< permissible abbreviation + uint32_t flags; ///< see above + OptTypeFlags type_flags; ///< option type flags, see OptValType + void *var; ///< global option: pointer to variable; + ///< window-local option: VAR_WIN; + ///< buffer-local option: global value + idopt_T indir; ///< global option: PV_NONE; + ///< local option: indirect option index + bool immutable; ///< option value cannot be changed from the default value. /// callback function to invoke after an option is modified to validate and /// apply the new value. @@ -85,7 +85,7 @@ typedef enum { OPT_ONECOLUMN = 0x40, ///< list options one per line OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions' -} OptionFlags; +} OptionSetFlags; /// Return value from get_option_attrs(). enum { @@ -94,6 +94,22 @@ enum { SOPT_BUF = 0x04, ///< Option has buffer-local value }; +/// Get name of OptValType as a string. +static inline const char *optval_type_get_name(const OptValType type) +{ + switch (type) { + case kOptValTypeNil: + return "nil"; + case kOptValTypeBoolean: + return "boolean"; + case kOptValTypeNumber: + return "number"; + case kOptValTypeString: + return "string"; + } + UNREACHABLE; +} + // OptVal helper macros. #define NIL_OPTVAL ((OptVal) { .type = kOptValTypeNil }) #define BOOLEAN_OPTVAL(b) ((OptVal) { .type = kOptValTypeBoolean, .data.boolean = b }) @@ -108,3 +124,24 @@ enum { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.h.generated.h" #endif + +/// Check if option supports a specific type. +static inline bool option_has_type(OptIndex opt_idx, OptValType type) +{ + // Ensure that type flags variable can hold all types. + STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8, + "Option type_flags cannot fit all option types"); + // Ensure that the type is valid before accessing type_flags. + assert(type > kOptValTypeNil && type < kOptValTypeSize); + // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in + // the type_flags bit_field. + return get_option(opt_idx)->type_flags & (1 << type); +} + +/// Check if option is multitype (supports multiple types). +static inline bool option_is_multitype(OptIndex opt_idx) +{ + const OptTypeFlags type_flags = get_option(opt_idx)->type_flags; + assert(type_flags != 0); + return !is_power_of_two(type_flags); +} diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 28718c6269..7ccf09b934 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -8,14 +8,21 @@ #include "nvim/regexp_defs.h" #include "nvim/types_defs.h" -/// Option value type +/// Option value type. +/// These types are also used as type flags by using the type value as an index for the type_flags +/// bit field (@see option_has_type()). typedef enum { - kOptValTypeNil = 0, + kOptValTypeNil = -1, // Make sure Nil can't be bitshifted and used as an option type flag. kOptValTypeBoolean, kOptValTypeNumber, kOptValTypeString, } OptValType; +/// Always update this whenever a new option type is added. +#define kOptValTypeSize (kOptValTypeString + 1) + +typedef uint32_t OptTypeFlags; + typedef union { // boolean options are actually tri-states because they have a third "None" value. TriState boolean; diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index 66185940ca..cbdec2e0db 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -6,55 +6,49 @@ // option_vars.h: definition of global variables for settable options // Option Flags -#define P_BOOL 0x01U ///< the option is boolean -#define P_NUM 0x02U ///< the option is numeric -#define P_STRING 0x04U ///< the option is a string -#define P_ALLOCED 0x08U ///< the string option is in allocated memory, +#define P_ALLOCED 0x01U ///< the option is in allocated memory, ///< must use free_string_option() when ///< assigning new value. Not set if default is ///< the same. -#define P_EXPAND 0x10U ///< environment expansion. NOTE: P_EXPAND can +#define P_EXPAND 0x02U ///< environment expansion. NOTE: P_EXPAND can ///< never be used for local or hidden options -#define P_NO_DEF_EXP 0x20U ///< do not expand default value -#define P_NODEFAULT 0x40U ///< don't set to default value -#define P_DEF_ALLOCED 0x80U ///< default value is in allocated memory, must +#define P_NO_DEF_EXP 0x04U ///< do not expand default value +#define P_NODEFAULT 0x08U ///< don't set to default value +#define P_DEF_ALLOCED 0x10U ///< default value is in allocated memory, must ///< use free() when assigning new value -#define P_WAS_SET 0x100U ///< option has been set/reset -#define P_NO_MKRC 0x200U ///< don't include in :mkvimrc output +#define P_WAS_SET 0x20U ///< option has been set/reset +#define P_NO_MKRC 0x40U ///< don't include in :mkvimrc output // when option changed, what to display: -#define P_UI_OPTION 0x400U ///< send option to remote UI -#define P_RTABL 0x800U ///< redraw tabline -#define P_RSTAT 0x1000U ///< redraw status lines -#define P_RWIN 0x2000U ///< redraw current window and recompute text -#define P_RBUF 0x4000U ///< redraw current buffer and recompute text -#define P_RALL 0x6000U ///< redraw all windows -#define P_RCLR 0x7000U ///< clear and redraw all +#define P_UI_OPTION 0x80U ///< send option to remote UI +#define P_RTABL 0x100U ///< redraw tabline +#define P_RSTAT 0x200U ///< redraw status lines +#define P_RWIN 0x400U ///< redraw current window and recompute text +#define P_RBUF 0x800U ///< redraw current buffer and recompute text +#define P_RALL 0xC00U ///< redraw all windows +#define P_RCLR 0xE00U ///< clear and redraw all -#define P_COMMA 0x8000U ///< comma separated list -#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive +#define P_COMMA 0x1000U ///< comma separated list +#define P_ONECOMMA 0x3000U ///< P_COMMA and cannot have two consecutive ///< commas -#define P_NODUP 0x20000U ///< don't allow duplicate strings -#define P_FLAGLIST 0x40000U ///< list of single-char flags +#define P_NODUP 0x4000U ///< don't allow duplicate strings +#define P_FLAGLIST 0x8000U ///< list of single-char flags -#define P_SECURE 0x80000U ///< cannot change in modeline or secure mode -#define P_GETTEXT 0x100000U ///< expand default value with _() -#define P_NOGLOB 0x200000U ///< do not use local value for global vimrc -#define P_NFNAME 0x400000U ///< only normal file name chars allowed -#define P_INSECURE 0x800000U ///< option was set from a modeline -#define P_PRI_MKRC 0x1000000U ///< priority for :mkvimrc (setting option +#define P_SECURE 0x10000U ///< cannot change in modeline or secure mode +#define P_GETTEXT 0x20000U ///< expand default value with _() +#define P_NOGLOB 0x40000U ///< do not use local value for global vimrc +#define P_NFNAME 0x80000U ///< only normal file name chars allowed +#define P_INSECURE 0x100000U ///< option was set from a modeline +#define P_PRI_MKRC 0x200000U ///< priority for :mkvimrc (setting option ///< has side effects) -#define P_NO_ML 0x2000000U ///< not allowed in modeline -#define P_CURSWANT 0x4000000U ///< update curswant required; not needed +#define P_NO_ML 0x400000U ///< not allowed in modeline +#define P_CURSWANT 0x800000U ///< update curswant required; not needed ///< when there is a redraw flag -#define P_NDNAME 0x8000000U ///< only normal dir name chars allowed -#define P_RWINONLY 0x10000000U ///< only redraw current window -#define P_MLE 0x20000000U ///< under control of 'modelineexpr' -#define P_FUNC 0x40000000U ///< accept a function reference or a lambda -#define P_COLON 0x80000000U ///< values use colons to create sublists -// Warning: Currently we have used all 32 bits for option flags, and adding more -// flags will overflow it. Adding another flag will need to change how -// it's stored first. +#define P_NDNAME 0x1000000U ///< only normal dir name chars allowed +#define P_RWINONLY 0x2000000U ///< only redraw current window +#define P_MLE 0x4000000U ///< under control of 'modelineexpr' +#define P_FUNC 0x8000000U ///< accept a function reference or a lambda +#define P_COLON 0x10000000U ///< values use colons to create sublists #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 8f0be0eb1b..edd7423e69 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -5,7 +5,7 @@ --- @field short_desc? string|fun(): string --- @field varname? string --- @field pv_name? string ---- @field type 'bool'|'number'|'string' +--- @field type 'boolean'|'number'|'string' --- @field immutable? boolean --- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma' --- @field scope vim.option_scope[] @@ -105,7 +105,7 @@ return { full_name = 'allowrevins', scope = { 'global' }, short_desc = N_('allow CTRL-_ in Insert and Command-line mode'), - type = 'bool', + type = 'boolean', varname = 'p_ari', }, { @@ -176,7 +176,7 @@ return { redraw = { 'curswant' }, scope = { 'window' }, short_desc = N_('Arabic as a default second language'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'arshape', @@ -199,7 +199,7 @@ return { redraw = { 'all_windows', 'ui_option' }, scope = { 'global' }, short_desc = N_('do shaping for Arabic characters'), - type = 'bool', + type = 'boolean', varname = 'p_arshape', }, { @@ -217,7 +217,7 @@ return { full_name = 'autochdir', scope = { 'global' }, short_desc = N_('change directory to the file in the current window'), - type = 'bool', + type = 'boolean', varname = 'p_acd', }, { @@ -239,7 +239,7 @@ return { full_name = 'autoindent', scope = { 'buffer' }, short_desc = N_('take indent for new line from previous line'), - type = 'bool', + type = 'boolean', varname = 'p_ai', }, { @@ -259,7 +259,7 @@ return { full_name = 'autoread', scope = { 'global', 'buffer' }, short_desc = N_('autom. read file when changed outside of Vim'), - type = 'bool', + type = 'boolean', varname = 'p_ar', }, { @@ -284,7 +284,7 @@ return { full_name = 'autowrite', scope = { 'global' }, short_desc = N_('automatically write file if changed'), - type = 'bool', + type = 'boolean', varname = 'p_aw', }, { @@ -299,7 +299,7 @@ return { full_name = 'autowriteall', scope = { 'global' }, short_desc = N_("as 'autowrite', but works with more commands"), - type = 'bool', + type = 'boolean', varname = 'p_awa', }, { @@ -387,7 +387,7 @@ return { full_name = 'backup', scope = { 'global' }, short_desc = N_('keep backup file after overwriting a file'), - type = 'bool', + type = 'boolean', varname = 'p_bk', }, { @@ -671,7 +671,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('read/write/edit file in binary mode'), - type = 'bool', + type = 'boolean', varname = 'p_bin', }, { @@ -699,7 +699,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('a Byte Order Mark to the file'), - type = 'bool', + type = 'boolean', varname = 'p_bomb', }, { @@ -733,7 +733,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('wrapped line repeats indent'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'briopt', @@ -848,7 +848,7 @@ return { scope = { 'buffer' }, short_desc = N_('whether the buffer shows up in the buffer list'), tags = { 'E85' }, - type = 'bool', + type = 'boolean', varname = 'p_bl', }, { @@ -950,7 +950,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_(':cd without argument goes to the home directory'), - type = 'bool', + type = 'boolean', varname = 'p_cdh', }, { @@ -1088,7 +1088,7 @@ return { full_name = 'cindent', scope = { 'buffer' }, short_desc = N_('do C program indenting'), - type = 'bool', + type = 'boolean', varname = 'p_cin', }, { @@ -1341,7 +1341,7 @@ return { full_name = 'compatible', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -1558,7 +1558,7 @@ return { full_name = 'confirm', scope = { 'global' }, short_desc = N_('ask what to do about unsaved/read-only files'), - type = 'bool', + type = 'boolean', varname = 'p_confirm', }, { @@ -1578,7 +1578,7 @@ return { full_name = 'copyindent', scope = { 'buffer' }, short_desc = N_("make 'autoindent' use existing indent structure"), - type = 'bool', + type = 'boolean', varname = 'p_ci', }, { @@ -1843,7 +1843,7 @@ return { pv_name = 'p_crbind', scope = { 'window' }, short_desc = N_('move cursor in window as it moves in other windows'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'cuc', @@ -1862,7 +1862,7 @@ return { redraw = { 'current_window_only' }, scope = { 'window' }, short_desc = N_('highlight the screen column of the cursor'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'cul', @@ -1877,7 +1877,7 @@ return { redraw = { 'current_window_only' }, scope = { 'window' }, short_desc = N_('highlight the screen line of the cursor'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'culopt', @@ -1978,7 +1978,7 @@ return { full_name = 'delcombine', scope = { 'global' }, short_desc = N_('delete combining characters on their own'), - type = 'bool', + type = 'boolean', varname = 'p_deco', }, { @@ -2030,7 +2030,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('diff mode for the current window'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'dex', @@ -2183,7 +2183,7 @@ return { full_name = 'digraph', scope = { 'global' }, short_desc = N_('enable the entering of digraphs in Insert mode'), - type = 'bool', + type = 'boolean', varname = 'p_dg', }, { @@ -2299,7 +2299,7 @@ return { full_name = 'edcompatible', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -2317,7 +2317,7 @@ return { redraw = { 'all_windows', 'ui_option' }, scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', varname = 'p_emoji', }, { @@ -2354,7 +2354,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('write CTRL-Z for last line in file'), - type = 'bool', + type = 'boolean', varname = 'p_eof', }, { @@ -2380,7 +2380,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('write for last line in file'), - type = 'bool', + type = 'boolean', varname = 'p_eol', }, { @@ -2406,7 +2406,7 @@ return { full_name = 'equalalways', scope = { 'global' }, short_desc = N_('windows are automatically made the same size'), - type = 'bool', + type = 'boolean', varname = 'p_ea', }, { @@ -2442,7 +2442,7 @@ return { full_name = 'errorbells', scope = { 'global' }, short_desc = N_('ring the bell for error messages'), - type = 'bool', + type = 'boolean', varname = 'p_eb', }, { @@ -2517,7 +2517,7 @@ return { full_name = 'expandtab', scope = { 'buffer' }, short_desc = N_('use spaces when is inserted'), - type = 'bool', + type = 'boolean', varname = 'p_et', }, { @@ -2539,7 +2539,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('read .nvimrc and .exrc in the current directory'), - type = 'bool', + type = 'boolean', varname = 'p_exrc', }, { @@ -2769,7 +2769,7 @@ return { full_name = 'fileignorecase', scope = { 'global' }, short_desc = N_('ignore case when using file names'), - type = 'bool', + type = 'boolean', varname = 'p_fic', }, { @@ -2901,7 +2901,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('make sure last line in file has '), - type = 'bool', + type = 'boolean', varname = 'p_fixeol', }, { @@ -2960,7 +2960,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('set to display all folds open'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'fde', @@ -3329,7 +3329,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('whether to invoke fsync() after file write'), - type = 'bool', + type = 'boolean', varname = 'p_fs', }, { @@ -3353,7 +3353,7 @@ return { full_name = 'gdefault', scope = { 'global' }, short_desc = N_('the ":substitute" flag \'g\' is default on'), - type = 'bool', + type = 'boolean', varname = 'p_gd', }, { @@ -3849,7 +3849,7 @@ return { full_name = 'hidden', scope = { 'global' }, short_desc = N_("don't unload buffer when it is |abandon|ed"), - type = 'bool', + type = 'boolean', varname = 'p_hid', }, { @@ -3885,7 +3885,7 @@ return { full_name = 'hkmap', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -3894,7 +3894,7 @@ return { full_name = 'hkmapp', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -3926,7 +3926,7 @@ return { redraw = { 'all_windows' }, scope = { 'global' }, short_desc = N_('highlight matches with last search pattern'), - type = 'bool', + type = 'boolean', varname = 'p_hls', }, { @@ -3945,7 +3945,7 @@ return { full_name = 'icon', scope = { 'global' }, short_desc = N_('Vim set the text of the window icon'), - type = 'bool', + type = 'boolean', varname = 'p_icon', }, { @@ -3981,7 +3981,7 @@ return { full_name = 'ignorecase', scope = { 'global' }, short_desc = N_('ignore case in search patterns'), - type = 'bool', + type = 'boolean', varname = 'p_ic', }, { @@ -3998,7 +3998,7 @@ return { full_name = 'imcmdline', scope = { 'global' }, short_desc = N_('use IM when starting to edit a command line'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'imd', @@ -4016,7 +4016,7 @@ return { full_name = 'imdisable', scope = { 'global' }, short_desc = N_('do not use the IM in any mode'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'imi', @@ -4195,7 +4195,7 @@ return { full_name = 'incsearch', scope = { 'global' }, short_desc = N_('highlight match while typing search pattern'), - type = 'bool', + type = 'boolean', varname = 'p_is', }, { @@ -4283,7 +4283,7 @@ return { full_name = 'infercase', scope = { 'buffer' }, short_desc = N_('adjust case of match for keyword completion'), - type = 'bool', + type = 'boolean', varname = 'p_inf', }, { @@ -4292,7 +4292,7 @@ return { full_name = 'insertmode', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -4469,7 +4469,7 @@ return { full_name = 'joinspaces', scope = { 'global' }, short_desc = N_('two spaces after a period with a join command'), - type = 'bool', + type = 'boolean', varname = 'p_js', }, { @@ -4664,7 +4664,7 @@ return { full_name = 'langnoremap', scope = { 'global' }, short_desc = N_("do not apply 'langmap' to mapped characters"), - type = 'bool', + type = 'boolean', varname = 'p_lnr', }, { @@ -4679,7 +4679,7 @@ return { full_name = 'langremap', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', varname = 'p_lrm', }, { @@ -4718,7 +4718,7 @@ return { full_name = 'lazyredraw', scope = { 'global' }, short_desc = N_("don't redraw while executing macros"), - type = 'bool', + type = 'boolean', varname = 'p_lz', }, { @@ -4739,7 +4739,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('wrap long lines at a blank'), - type = 'bool', + type = 'boolean', }, { defaults = { @@ -4802,7 +4802,7 @@ return { full_name = 'lisp', scope = { 'buffer' }, short_desc = N_('indenting for Lisp'), - type = 'bool', + type = 'boolean', varname = 'p_lisp', }, { @@ -4868,7 +4868,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_(' and '), - type = 'bool', + type = 'boolean', }, { abbreviation = 'lcs', @@ -4996,7 +4996,7 @@ return { full_name = 'loadplugins', scope = { 'global' }, short_desc = N_('load plugin scripts when starting up'), - type = 'bool', + type = 'boolean', varname = 'p_lpl', }, { @@ -5013,7 +5013,7 @@ return { full_name = 'magic', scope = { 'global' }, short_desc = N_('special characters in search patterns'), - type = 'bool', + type = 'boolean', varname = 'p_magic', }, { @@ -5280,7 +5280,7 @@ return { full_name = 'modeline', scope = { 'buffer' }, short_desc = N_('recognize modelines at start or end of file'), - type = 'bool', + type = 'boolean', varname = 'p_ml', }, { @@ -5297,7 +5297,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('allow some options to be set in modeline'), - type = 'bool', + type = 'boolean', varname = 'p_mle', }, { @@ -5329,7 +5329,7 @@ return { scope = { 'buffer' }, short_desc = N_('changes to the text are not possible'), tags = { 'E21' }, - type = 'bool', + type = 'boolean', varname = 'p_ma', }, { @@ -5364,7 +5364,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('buffer has been modified'), - type = 'bool', + type = 'boolean', varname = 'p_mod', }, { @@ -5377,7 +5377,7 @@ return { full_name = 'more', scope = { 'global' }, short_desc = N_('listings when the whole screen is filled'), - type = 'bool', + type = 'boolean', varname = 'p_more', }, { @@ -5443,7 +5443,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('keyboard focus follows the mouse'), - type = 'bool', + type = 'boolean', varname = 'p_mousef', }, { @@ -5459,7 +5459,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('hide mouse pointer while typing'), - type = 'bool', + type = 'boolean', varname = 'p_mh', }, { @@ -5536,7 +5536,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('deliver mouse move events to input queue'), - type = 'bool', + type = 'boolean', varname = 'p_mousemev', }, { @@ -5733,7 +5733,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('print the line number in front of each line'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'nuw', @@ -5797,7 +5797,7 @@ return { full_name = 'opendevice', scope = { 'global' }, short_desc = N_('allow reading/writing devices on MS-Windows'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'opfunc', @@ -5864,7 +5864,7 @@ return { pri_mkrc = true, scope = { 'global' }, short_desc = N_('pasting text'), - type = 'bool', + type = 'boolean', varname = 'p_paste', }, { @@ -6004,7 +6004,7 @@ return { full_name = 'preserveindent', scope = { 'buffer' }, short_desc = N_('preserve the indent structure when reindenting'), - type = 'bool', + type = 'boolean', varname = 'p_pi', }, { @@ -6035,14 +6035,14 @@ return { scope = { 'window' }, short_desc = N_('identifies the preview window'), tags = { 'E590' }, - type = 'bool', + type = 'boolean', }, { defaults = { if_true = true }, full_name = 'prompt', scope = { 'global' }, short_desc = N_('enable prompt in Ex mode'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -6176,7 +6176,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('disallow writing the buffer'), - type = 'bool', + type = 'boolean', varname = 'p_ro', }, { @@ -6292,14 +6292,14 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('show relative line number in front of each line'), - type = 'bool', + type = 'boolean', }, { defaults = { if_true = true }, full_name = 'remap', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -6328,7 +6328,7 @@ return { full_name = 'revins', scope = { 'global' }, short_desc = N_('inserting characters will work backwards'), - type = 'bool', + type = 'boolean', varname = 'p_ri', }, { @@ -6349,7 +6349,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('window is right-to-left oriented'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'rlc', @@ -6403,7 +6403,7 @@ return { redraw = { 'statuslines' }, scope = { 'global' }, short_desc = N_('show cursor line and column in the status line'), - type = 'bool', + type = 'boolean', varname = 'p_ru', }, { @@ -6598,7 +6598,7 @@ return { pv_name = 'p_scbind', scope = { 'window' }, short_desc = N_('scroll in window as other windows scroll'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'sj', @@ -6700,7 +6700,7 @@ return { scope = { 'global' }, secure = true, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', varname = 'p_secure', }, { @@ -7184,7 +7184,7 @@ return { full_name = 'shellslash', scope = { 'global' }, short_desc = N_('use forward slash for shell file names'), - type = 'bool', + type = 'boolean', varname = 'p_ssl', }, { @@ -7205,7 +7205,7 @@ return { full_name = 'shelltemp', scope = { 'global' }, short_desc = N_('whether to use a temp file for shell commands'), - type = 'bool', + type = 'boolean', varname = 'p_stmp', }, { @@ -7262,7 +7262,7 @@ return { full_name = 'shiftround', scope = { 'global' }, short_desc = N_('round indent to multiple of shiftwidth'), - type = 'bool', + type = 'boolean', varname = 'p_sr', }, { @@ -7394,7 +7394,7 @@ return { full_name = 'showcmd', scope = { 'global' }, short_desc = N_('show (partial) command in status line'), - type = 'bool', + type = 'boolean', varname = 'p_sc', }, { @@ -7437,7 +7437,7 @@ return { full_name = 'showfulltag', scope = { 'global' }, short_desc = N_('show full tag pattern when completing tag'), - type = 'bool', + type = 'boolean', varname = 'p_sft', }, { @@ -7463,7 +7463,7 @@ return { full_name = 'showmatch', scope = { 'global' }, short_desc = N_('briefly jump to matching bracket if insert one'), - type = 'bool', + type = 'boolean', varname = 'p_sm', }, { @@ -7477,7 +7477,7 @@ return { full_name = 'showmode', scope = { 'global' }, short_desc = N_('message on status line to show current mode'), - type = 'bool', + type = 'boolean', varname = 'p_smd', }, { @@ -7592,7 +7592,7 @@ return { full_name = 'smartcase', scope = { 'global' }, short_desc = N_('no ignore case when pattern has uppercase'), - type = 'bool', + type = 'boolean', varname = 'p_scs', }, { @@ -7622,7 +7622,7 @@ return { full_name = 'smartindent', scope = { 'buffer' }, short_desc = N_('smart autoindenting for C programs'), - type = 'bool', + type = 'boolean', varname = 'p_si', }, { @@ -7643,7 +7643,7 @@ return { full_name = 'smarttab', scope = { 'global' }, short_desc = N_("use 'shiftwidth' when inserting "), - type = 'bool', + type = 'boolean', varname = 'p_sta', }, { @@ -7665,7 +7665,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_("scroll by screen lines when 'wrap' is set"), - type = 'bool', + type = 'boolean', }, { abbreviation = 'sts', @@ -7704,7 +7704,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('spell checking'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'spc', @@ -7935,7 +7935,7 @@ return { full_name = 'splitbelow', scope = { 'global' }, short_desc = N_('new window from split is below the current one'), - type = 'bool', + type = 'boolean', varname = 'p_sb', }, { @@ -7973,7 +7973,7 @@ return { full_name = 'splitright', scope = { 'global' }, short_desc = N_('new window is put right of the current one'), - type = 'bool', + type = 'boolean', varname = 'p_spr', }, { @@ -7994,7 +7994,7 @@ return { full_name = 'startofline', scope = { 'global' }, short_desc = N_('commands move cursor to first non-blank in line'), - type = 'bool', + type = 'boolean', varname = 'p_sol', vim = false, }, @@ -8356,7 +8356,7 @@ return { redraw = { 'statuslines' }, scope = { 'buffer' }, short_desc = N_('whether to use a swapfile for a buffer'), - type = 'bool', + type = 'boolean', varname = 'p_swf', }, { @@ -8610,7 +8610,7 @@ return { full_name = 'tagbsearch', scope = { 'global' }, short_desc = N_('use binary searching in tags files'), - type = 'bool', + type = 'boolean', varname = 'p_tbs', }, { @@ -8677,7 +8677,7 @@ return { full_name = 'tagrelative', scope = { 'global' }, short_desc = N_('file names in tag file are relative'), - type = 'bool', + type = 'boolean', varname = 'p_tr', }, { @@ -8727,7 +8727,7 @@ return { full_name = 'tagstack', scope = { 'global' }, short_desc = N_('push tags onto the tag stack'), - type = 'bool', + type = 'boolean', varname = 'p_tgst', }, { @@ -8746,7 +8746,7 @@ return { full_name = 'termbidi', scope = { 'global' }, short_desc = N_('terminal takes care of bi-directionality'), - type = 'bool', + type = 'boolean', varname = 'p_tbidi', }, { @@ -8773,7 +8773,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('Terminal true color support'), - type = 'bool', + type = 'boolean', varname = 'p_tgc', }, { @@ -8820,7 +8820,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('synchronize redraw output with the host terminal'), - type = 'bool', + type = 'boolean', varname = 'p_termsync', }, { @@ -8828,7 +8828,7 @@ return { full_name = 'terse', scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -8910,7 +8910,7 @@ return { full_name = 'tildeop', scope = { 'global' }, short_desc = N_('tilde command "~" behaves like an operator'), - type = 'bool', + type = 'boolean', varname = 'p_to', }, { @@ -8925,7 +8925,7 @@ return { full_name = 'timeout', scope = { 'global' }, short_desc = N_('time out on mappings and key codes'), - type = 'bool', + type = 'boolean', varname = 'p_timeout', }, { @@ -8959,7 +8959,7 @@ return { full_name = 'title', scope = { 'global' }, short_desc = N_('Vim set the title of the window'), - type = 'bool', + type = 'boolean', varname = 'p_title', }, { @@ -9048,7 +9048,7 @@ return { redraw = { 'ui_option' }, scope = { 'global' }, short_desc = N_('out on mappings'), - type = 'bool', + type = 'boolean', varname = 'p_ttimeout', }, { @@ -9073,7 +9073,7 @@ return { no_mkrc = true, scope = { 'global' }, short_desc = N_('No description'), - type = 'bool', + type = 'boolean', immutable = true, }, { @@ -9129,7 +9129,7 @@ return { full_name = 'undofile', scope = { 'buffer' }, short_desc = N_('save undo information in a file'), - type = 'bool', + type = 'boolean', varname = 'p_udf', }, { @@ -9441,7 +9441,7 @@ return { full_name = 'visualbell', scope = { 'global' }, short_desc = N_('use visual bell instead of beeping'), - type = 'bool', + type = 'boolean', varname = 'p_vb', }, { @@ -9453,7 +9453,7 @@ return { full_name = 'warn', scope = { 'global' }, short_desc = N_('for shell command when buffer was changed'), - type = 'bool', + type = 'boolean', varname = 'p_warn', }, { @@ -9578,7 +9578,7 @@ return { full_name = 'wildignorecase', scope = { 'global' }, short_desc = N_('ignore case when completing file names'), - type = 'bool', + type = 'boolean', varname = 'p_wic', }, { @@ -9626,7 +9626,7 @@ return { full_name = 'wildmenu', scope = { 'global' }, short_desc = N_('use menu for command line completion'), - type = 'bool', + type = 'boolean', varname = 'p_wmnu', }, { @@ -9829,7 +9829,7 @@ return { redraw = { 'statuslines' }, scope = { 'window' }, short_desc = N_('keep window height when opening/closing windows'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'wfw', @@ -9843,7 +9843,7 @@ return { redraw = { 'statuslines' }, scope = { 'window' }, short_desc = N_('keep window width when opening/closing windows'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'wh', @@ -9996,7 +9996,7 @@ return { redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('lines wrap and continue on the next line'), - type = 'bool', + type = 'boolean', }, { abbreviation = 'wm', @@ -10027,7 +10027,7 @@ return { scope = { 'global' }, short_desc = N_('searches wrap around the end of the file'), tags = { 'E384', 'E385' }, - type = 'bool', + type = 'boolean', varname = 'p_ws', }, { @@ -10042,7 +10042,7 @@ return { full_name = 'write', scope = { 'global' }, short_desc = N_('to a file is allowed'), - type = 'bool', + type = 'boolean', varname = 'p_write', }, { @@ -10054,7 +10054,7 @@ return { full_name = 'writeany', scope = { 'global' }, short_desc = N_('write to file with no need for "!" override'), - type = 'bool', + type = 'boolean', varname = 'p_wa', }, { @@ -10077,7 +10077,7 @@ return { full_name = 'writebackup', scope = { 'global' }, short_desc = N_('make a backup before overwriting a file'), - type = 'bool', + type = 'boolean', varname = 'p_wb', }, { diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3d3b478d66..104ffd7d6c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1478,9 +1478,9 @@ describe('API', function() pcall_err(nvim, 'get_option_value', 'scrolloff', {scope = 42})) eq("Invalid 'value': expected valid option type, got Array", pcall_err(nvim, 'set_option_value', 'scrolloff', {}, {})) - eq("Invalid value for option 'scrolloff': expected Number, got Boolean true", + eq("Invalid value for option 'scrolloff': expected number, got boolean true", pcall_err(nvim, 'set_option_value', 'scrolloff', true, {})) - eq("Invalid value for option 'scrolloff': expected Number, got String \"wrong\"", + eq("Invalid value for option 'scrolloff': expected number, got string \"wrong\"", pcall_err(nvim, 'set_option_value', 'scrolloff', 'wrong', {})) end) diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index 2bba86fe99..2aa7d3ab65 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -813,7 +813,7 @@ func Test_set_option_errors() call assert_fails('set winwidth=9 winminwidth=10', 'E592:') set winwidth& winminwidth& call assert_fails("set showbreak=\x01", 'E595:') - call assert_fails('set t_foo=', 'E846:') + " call assert_fails('set t_foo=', 'E846:') call assert_fails('set tabstop??', 'E488:') call assert_fails('set wrapscan!!', 'E488:') call assert_fails('set tabstop&&', 'E488:') @@ -1446,8 +1446,10 @@ endfunc " Test for setting keycodes using set func Test_opt_set_keycode() - call assert_fails('set =abcd " call assert_equal('abcd', &t_k9) set &