aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFamiu Haque <famiuhaque@proton.me>2023-12-07 01:34:29 +0600
committerFamiu Haque <famiuhaque@proton.me>2023-12-14 16:46:42 +0600
commit3c2c022e5e299ecac4663c3813e2db5e2b099ffa (patch)
tree8cfefb87b8a675e1c29b0caf3e1adca14bf196b5 /src
parent320e9c1c21817fd76b84345018661f70437fa4b5 (diff)
downloadrneovim-3c2c022e5e299ecac4663c3813e2db5e2b099ffa.tar.gz
rneovim-3c2c022e5e299ecac4663c3813e2db5e2b099ffa.tar.bz2
rneovim-3c2c022e5e299ecac4663c3813e2db5e2b099ffa.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c42
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval/vars.c64
-rw-r--r--src/nvim/generators/gen_options.lua33
-rw-r--r--src/nvim/math.c26
-rw-r--r--src/nvim/math.h9
-rw-r--r--src/nvim/memory.c7
-rw-r--r--src/nvim/option.c486
-rw-r--r--src/nvim/option.h61
-rw-r--r--src/nvim/option_defs.h11
-rw-r--r--src/nvim/option_vars.h72
-rw-r--r--src/nvim/options.lua240
12 files changed, 538 insertions, 514 deletions
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 <stdbool.h>
+#include <stdint.h>
+
+/// 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 <t_>;>
- 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 <t_>;>
+ 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;
+ } else { // Invalid option name, skip.
+ *errmsg = e_unknown_option;
return;
}
- // remember character after option name
- int afterchar = (uint8_t)arg[len];
+ // 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(arg[len])) {
- len++;
+ // Skip white space, allow ":set ai ?".
+ while (ascii_iswhite(*p)) {
+ p++;
}
- set_op_T op = get_op(arg + len);
+ set_op_T op = get_op(p);
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
- *errmsg = e_unknown_option;
- return;
+ p++;
}
- uint32_t flags; // flags for current option
+ 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("");