aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2023-10-16 20:41:35 +0200
committerGitHub <noreply@github.com>2023-10-16 20:41:35 +0200
commita63c67005b9ea17214d86391e2fd649658c1bdec (patch)
treebd732635435cb15d0d7e0bbe4eb49ceae73c0999 /src/nvim
parentb80a8e2c16b6d6eb16ac84232c27eb7cfa4a434a (diff)
parent3642f2fb44b6a3681e6a637671690258aa83cc62 (diff)
downloadrneovim-a63c67005b9ea17214d86391e2fd649658c1bdec.tar.gz
rneovim-a63c67005b9ea17214d86391e2fd649658c1bdec.tar.bz2
rneovim-a63c67005b9ea17214d86391e2fd649658c1bdec.zip
Merge pull request #25394 from famiu/refactor/options/set_option
refactor(options)!: unify interfaces for setting options
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/api/options.c41
-rw-r--r--src/nvim/arglist.c2
-rw-r--r--src/nvim/eval.c17
-rw-r--r--src/nvim/eval/funcs.c12
-rw-r--r--src/nvim/eval/vars.c45
-rw-r--r--src/nvim/eval/vars.h1
-rw-r--r--src/nvim/ex_eval.c2
-rw-r--r--src/nvim/globals.h9
-rw-r--r--src/nvim/normal.c4
-rw-r--r--src/nvim/ops.c4
-rw-r--r--src/nvim/option.c1008
-rw-r--r--src/nvim/option_defs.h35
-rw-r--r--src/nvim/options.lua1
-rw-r--r--src/nvim/optionstr.c80
-rw-r--r--src/nvim/quickfix.c8
-rw-r--r--src/nvim/syntax.c12
-rw-r--r--src/nvim/types.h2
17 files changed, 609 insertions, 674 deletions
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 867d1d5e5c..498638d606 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -133,47 +133,6 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err)
return ftbuf;
}
-/// Consume an OptVal and convert it to an API Object.
-static Object optval_as_object(OptVal o)
-{
- switch (o.type) {
- case kOptValTypeNil:
- return NIL;
- case kOptValTypeBoolean:
- switch (o.data.boolean) {
- case kFalse:
- case kTrue:
- return BOOLEAN_OBJ(o.data.boolean);
- case kNone:
- return NIL;
- }
- UNREACHABLE;
- case kOptValTypeNumber:
- return INTEGER_OBJ(o.data.number);
- case kOptValTypeString:
- return STRING_OBJ(o.data.string);
- }
- UNREACHABLE;
-}
-
-/// Consume an API Object and convert it to an OptVal.
-static OptVal object_as_optval(Object o, bool *error)
-{
- switch (o.type) {
- case kObjectTypeNil:
- return NIL_OPTVAL;
- case kObjectTypeBoolean:
- return BOOLEAN_OPTVAL(o.data.boolean);
- case kObjectTypeInteger:
- return NUMBER_OPTVAL((OptInt)o.data.integer);
- case kObjectTypeString:
- return STRING_OPTVAL(o.data.string);
- default:
- *error = true;
- return NIL_OPTVAL;
- }
-}
-
/// Gets the value of an option. The behavior of this function matches that of
/// |:set|: the local value of an option is returned if it exists; otherwise,
/// the global value is returned. Local values always correspond to the current
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index fb8849541d..bcf343a5b4 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -140,7 +140,7 @@ void alist_expand(int *fnum_list, int fnum_len)
// Don't use 'suffixes' here. This should work like the shell did the
// expansion. Also, the vimrc file isn't read yet, thus the user
// can't set the options.
- p_su = empty_option;
+ p_su = empty_string_option;
for (int i = 0; i < GARGCOUNT; i++) {
old_arg_files[i] = xstrdup(GARGLIST[i].ae_fname);
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index b5b40061b5..20d76334f6 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2237,7 +2237,7 @@ int pattern_match(const char *pat, const char *text, bool ic)
// avoid 'l' flag in 'cpoptions'
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = ic;
@@ -7226,6 +7226,17 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
tv_dict_set_keys_readonly(val);
}
+/// Set v:variable to tv.
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in,out] val Value to set to. Reference count will be incremented.
+/// Also keys of the dictionary will be made read-only.
+void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv)
+{
+ tv_clear(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_di.di_tv = *tv;
+}
+
/// Set the v:argv list.
void set_argv_var(char **argv, int argc)
{
@@ -8634,7 +8645,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
ga_init(&ga, 1, 200);
@@ -8699,7 +8710,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data);
ga_clear(&ga);
- if (p_cpo == empty_option) {
+ if (p_cpo == empty_string_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating {sub} expression or {expr} changed the value.
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 0979087c79..8e5bf68be1 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -482,7 +482,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
int save_magic = p_magic;
p_magic = true;
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name),
true, false, curtab_only));
@@ -1733,7 +1733,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char *p_csl_save = p_csl;
// avoid using 'completeslash' here
- p_csl = empty_option;
+ p_csl = empty_string_option;
#endif
rettv->v_type = VAR_STRING;
@@ -4516,7 +4516,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
rettv->vval.v_number = -1;
switch (type) {
@@ -7108,7 +7108,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
// Set the time limit, if there is one.
proftime_T tm = profile_setlimit(time_limit);
@@ -7234,7 +7234,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
xfree(pat2);
xfree(pat3);
- if (p_cpo == empty_option) {
+ if (p_cpo == empty_string_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating the {skip} expression changed the value.
@@ -7966,7 +7966,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
const char *str = tv_get_string(&argvars[0]);
const char *pat = NULL;
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index f9dcfb3d9d..ed400b2ee9 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -20,6 +20,7 @@
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
@@ -822,7 +823,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
if (curval.type == kOptValTypeNumber) {
newval = NUMBER_OPTVAL(new_n);
} else {
- newval = BOOLEAN_OPTVAL(new_n == 0 ? kFalse : (new_n >= 1 ? kTrue : kNone));
+ newval = BOOLEAN_OPTVAL(TRISTATE_FROM_INT(new_n));
}
} else if (!hidden && is_string
&& curval.data.string.data != NULL && newval.data.string.data != NULL) { // string
@@ -1875,8 +1876,7 @@ 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(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone));
+ value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n));
} else if ((flags & P_STRING) || is_tty_option(option)) {
// Avoid setting string option to a boolean or a special value.
if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
@@ -1897,6 +1897,45 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo
return value;
}
+/// Convert an option value to typval.
+///
+/// @param[in] value Option value to convert.
+///
+/// @return OptVal converted to typval.
+typval_T optval_as_tv(OptVal value)
+{
+ typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } };
+
+ switch (value.type) {
+ 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
+ }
+ break;
+ case kOptValTypeNumber:
+ rettv.v_type = VAR_NUMBER;
+ rettv.vval.v_number = value.data.number;
+ break;
+ case kOptValTypeString:
+ rettv.v_type = VAR_STRING;
+ rettv.vval.v_string = value.data.string.data;
+ break;
+ }
+
+ return rettv;
+}
+
/// Set option "varname" to the value of "varp" for the current buffer/window.
static void set_option_from_tv(const char *varname, typval_T *varp)
{
diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h
index b87c9d62cb..12ff5b11cb 100644
--- a/src/nvim/eval/vars.h
+++ b/src/nvim/eval/vars.h
@@ -2,6 +2,7 @@
#define NVIM_EVAL_VARS_H
#include "nvim/ex_cmds_defs.h"
+#include "nvim/option_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/vars.h.generated.h"
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index b49563f44d..c656f785c9 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -1342,7 +1342,7 @@ void ex_catch(exarg_T *eap)
*end = NUL;
}
save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
// Disable error messages, it will make current exception
// invalid
emsg_off++;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index d462c83710..a719fedb22 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -789,10 +789,11 @@ EXTERN char *escape_chars INIT(= " \t\\\"|"); // need backslash in cmd line
EXTERN bool keep_help_flag INIT(= false); // doing :ta from help file
-// When a string option is NULL (which only happens in out-of-memory
-// situations), it is set to empty_option, to avoid having to check for NULL
-// everywhere.
-EXTERN char *empty_option INIT(= "");
+// When a string option is NULL (which only happens in out-of-memory situations), it is set to
+// empty_string_option, to avoid having to check for NULL everywhere.
+//
+// TODO(famiu): Remove this when refcounted strings are used for string options.
+EXTERN char *empty_string_option INIT(= "");
EXTERN bool redir_off INIT(= false); // no redirection for a moment
EXTERN FILE *redir_fd INIT(= NULL); // message redirection file
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f1bc46412f..1f2403450f 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1878,8 +1878,8 @@ void clear_showcmd(void)
char *const saved_w_sbr = curwin->w_p_sbr;
// Make 'sbr' empty for a moment to get the correct size.
- p_sbr = empty_option;
- curwin->w_p_sbr = empty_option;
+ p_sbr = empty_string_option;
+ curwin->w_p_sbr = empty_string_option;
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index cb8d1dabc0..260019da33 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -5365,8 +5365,8 @@ void cursor_pos_info(dict_T *dict)
char *const saved_w_sbr = curwin->w_p_sbr;
// Make 'sbr' empty for a moment to get the correct size.
- p_sbr = empty_option;
- curwin->w_p_sbr = empty_option;
+ p_sbr = empty_string_option;
+ curwin->w_p_sbr = empty_string_option;
oparg.is_VIsual = true;
oparg.motion_type = kMTBlockWise;
oparg.op_type = OP_NOP;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 4b50f27ef2..0c8230d7c9 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -13,8 +13,7 @@
// add some code to didset_window_options().
// - For a buffer option, add some code to buf_copy_options().
// - For a buffer string option, add code to check_buf_options().
-// - If it's a numeric option, add any necessary bounds checks to
-// set_num_option().
+// - If it's a numeric option, add any necessary bounds checks to check_num_option_bounds().
// - If it's a list of flags, add some code in do_set(), search for WW_ALL.
// - Add documentation! doc/options.txt, and any other related places.
// - Add an entry in runtime/optwin.vim.
@@ -47,6 +46,7 @@
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/vars.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
@@ -753,103 +753,6 @@ void ex_set(exarg_T *eap)
(void)do_set(eap->arg, flags);
}
-static void do_set_bool(int opt_idx, int opt_flags, set_prefix_T prefix, int nextchar,
- const void *varp, const char **errmsg)
-{
- varnumber_T value;
-
- // ":set opt!": invert
- // ":set opt&": reset to default value
- // ":set opt<": reset to global value
- if (nextchar == '!') {
- value = *(int *)(varp) ^ 1;
- } else if (nextchar == '&') {
- value = (int)(intptr_t)options[opt_idx].def_val;
- } else if (nextchar == '<') {
- // For 'autoread' -1 means to use global value.
- if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL) {
- value = -1;
- } else {
- value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
- } else {
- // ":set invopt": invert
- // ":set opt" or ":set noopt": set or reset
- if (prefix == PREFIX_INV) {
- value = *(int *)varp ^ 1;
- } else {
- value = prefix == PREFIX_NO ? 0 : 1;
- }
- }
-
- *errmsg = set_bool_option(opt_idx, (void *)varp, (int)value, opt_flags);
-}
-
-static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, const set_op_T op,
- const void *varp, char *errbuf, size_t errbuflen, const char **errmsg)
-{
- varnumber_T value;
- char *arg = *argp;
-
- // Different ways to set a number option:
- // & set to default value
- // < set to global value
- // <xx> accept special key codes for 'wildchar'
- // c accept any non-digit for 'wildchar'
- // [-]0-9 set number
- // other error
- arg++;
- if (nextchar == '&') {
- value = (varnumber_T)options[opt_idx].def_val;
- } else if (nextchar == '<') {
- if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
- // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global value
- value = NO_LOCAL_UNDOLEVEL;
- } else if (opt_flags == OPT_LOCAL
- && ((OptInt *)varp == &curwin->w_p_siso
- || (OptInt *)varp == &curwin->w_p_so)) {
- // for 'scrolloff'/'sidescrolloff' -1 means using the global value
- value = -1;
- } else {
- value = *(OptInt *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
- } else if (((OptInt *)varp == &p_wc
- || (OptInt *)varp == &p_wcm)
- && (*arg == '<'
- || *arg == '^'
- || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
- && !ascii_isdigit(*arg)))) {
- value = string_to_key(arg);
- if (value == 0 && (OptInt *)varp != &p_wcm) {
- *errmsg = e_invarg;
- return;
- }
- } else if (*arg == '-' || ascii_isdigit(*arg)) {
- int i;
- // Allow negative, octal and hex numbers.
- vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true, NULL);
- if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
- *errmsg = e_number_required_after_equal;
- return;
- }
- } else {
- *errmsg = e_number_required_after_equal;
- return;
- }
-
- if (op == OP_ADDING) {
- value = *(OptInt *)varp + value;
- }
- if (op == OP_PREPENDING) {
- value = *(OptInt *)varp * value;
- }
- if (op == OP_REMOVING) {
- value = *(OptInt *)varp - value;
- }
- *errmsg = set_num_option(opt_idx, (void *)varp, value,
- errbuf, errbuflen, opt_flags);
-}
-
/// Get the default value for a string option.
static char *stropt_get_default_val(int opt_idx, uint64_t flags)
{
@@ -858,7 +761,7 @@ static char *stropt_get_default_val(int opt_idx, uint64_t flags)
// already expanded, only required when an environment variable was set
// later
if (newval == NULL) {
- newval = empty_option;
+ newval = empty_string_option;
} else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
char *s = option_expand(opt_idx, newval);
if (s == NULL) {
@@ -1095,103 +998,6 @@ static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, void *var
return newval;
}
-/// Part of do_set() for string options.
-static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int nextchar,
- set_op_T op_arg, uint32_t flags, void *varp_arg, char *errbuf,
- size_t errbuflen, bool *value_checked, const char **errmsg)
-{
- vimoption_T *opt = get_option(opt_idx);
-
- set_op_T op = op_arg;
- void *varp = varp_arg;
- char *origval_l = NULL;
- char *origval_g = NULL;
-
- // When using ":set opt=val" for a global option
- // with a local value the local value will be
- // reset, use the global value here.
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && ((int)opt->indir & PV_BOTH)) {
- varp = opt->var;
- }
-
- // The old value is kept until we are sure that the new value is valid.
- char *oldval = *(char **)varp;
-
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- origval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
- origval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
-
- // A global-local string option might have an empty option as value to
- // indicate that the global value should be used.
- if (((int)opt->indir & PV_BOTH) && origval_l == empty_option) {
- origval_l = origval_g;
- }
- }
-
- char *origval;
- // When setting the local value of a global option, the old value may be
- // the global value.
- if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
- origval = *(char **)get_varp(opt);
- } else {
- origval = oldval;
- }
-
- // Get the new value for the option
- char *newval = stropt_get_newval(nextchar, opt_idx, argp, varp, origval, &op, flags);
-
- // Set the new value.
- *(char **)(varp) = newval != NULL ? newval : empty_option;
-
- // origval may be freed by did_set_string_option(), make a copy.
- char *const saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
- char *const saved_origval_l = (origval_l != NULL) ? xstrdup(origval_l) : NULL;
- char *const saved_origval_g = (origval_g != NULL) ? xstrdup(origval_g) : NULL;
-
- // newval (and varp) may become invalid if the buffer is closed by
- // autocommands.
- char *const saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
-
- uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- const int secure_saved = secure;
-
- // When an option is set in the sandbox, from a modeline or in secure
- // mode, then deal with side effects in secure mode. Also when the
- // value was set with the P_INSECURE flag and is not completely
- // replaced.
- if ((opt_flags & OPT_MODELINE)
- || sandbox != 0
- || (op != OP_NONE && (*p & P_INSECURE))) {
- secure = 1;
- }
-
- // Handle side effects, and set the global value for ":set" on local
- // options. Note: when setting 'syntax' or 'filetype' autocommands may
- // be triggered that can cause havoc.
- *errmsg = did_set_string_option(curbuf, curwin, opt_idx, (char **)varp, oldval,
- errbuf, errbuflen,
- opt_flags, op, value_checked);
-
- secure = secure_saved;
-
- // call autocommand after handling side effects
- if (*errmsg == NULL) {
- if (!starting) {
- trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_origval_l,
- saved_origval_g, saved_newval);
- }
- if (options[opt_idx].flags & P_UI_OPTION) {
- ui_call_option_set(cstr_as_string(opt->fullname),
- CSTR_AS_OBJ(saved_newval));
- }
- }
- xfree(saved_origval);
- xfree(saved_origval_l);
- xfree(saved_origval_g);
- xfree(saved_newval);
-}
-
static set_op_T get_op(const char *arg)
{
set_op_T op = OP_NONE;
@@ -1328,34 +1134,145 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla
return OK;
}
-static void do_set_option_value(int opt_idx, int opt_flags, char **argp, set_prefix_T prefix,
+/// Get new option value from argp. Allocated OptVal must be freed by caller.
+static OptVal get_option_newval(int opt_idx, int opt_flags, set_prefix_T prefix, char **argp,
int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf,
- size_t errbuflen, const char **errmsg)
+ const size_t errbuflen, const char **errmsg)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
- bool value_checked = false;
- if (flags & P_BOOL) { // boolean
- do_set_bool(opt_idx, opt_flags, prefix, nextchar, varp, errmsg);
- } else if (flags & P_NUM) { // numeric
- do_set_num(opt_idx, opt_flags, argp, nextchar, op, varp, errbuf, errbuflen, errmsg);
- } else if (opt_idx >= 0) { // string.
- do_set_option_string(opt_idx, opt_flags, argp, nextchar, op, flags, varp, errbuf,
- errbuflen, &value_checked, errmsg);
- } else {
- // key code option(FIXME(tarruda): Show a warning or something
- // similar)
+ assert(varp != NULL);
+
+ char *arg = *argp;
+ OptVal oldval = optval_from_varp_flags(varp, flags);
+ OptVal newval = NIL_OPTVAL;
+
+ switch (oldval.type) {
+ case kOptValTypeNil:
+ abort();
+ case kOptValTypeBoolean: {
+ TriState newval_bool;
+
+ // ":set opt!": invert
+ // ":set opt&": reset to default value
+ // ":set opt<": reset to global value
+ if (nextchar == '!') {
+ switch (oldval.data.boolean) {
+ case kNone:
+ newval_bool = kNone;
+ break;
+ case kTrue:
+ newval_bool = kFalse;
+ break;
+ case kFalse:
+ newval_bool = kTrue;
+ break;
+ }
+ } else if (nextchar == '&') {
+ newval_bool = TRISTATE_FROM_INT((int)(intptr_t)options[opt_idx].def_val);
+ } else if (nextchar == '<') {
+ // For 'autoread', kNone means to use global value.
+ if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL) {
+ newval_bool = kNone;
+ } else {
+ newval_bool = TRISTATE_FROM_INT(*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
+ }
+ } else {
+ // ":set invopt": invert
+ // ":set opt" or ":set noopt": set or reset
+ if (prefix == PREFIX_INV) {
+ newval_bool = *(int *)varp ^ 1;
+ } else {
+ newval_bool = prefix == PREFIX_NO ? 0 : 1;
+ }
+ }
+
+ newval = BOOLEAN_OPTVAL(newval_bool);
+ break;
}
+ case kOptValTypeNumber: {
+ OptInt oldval_num = oldval.data.number;
+ OptInt newval_num;
+
+ // Different ways to set a number option:
+ // & set to default value
+ // < set to global value
+ // <xx> accept special key codes for 'wildchar'
+ // c accept any non-digit for 'wildchar'
+ // [-]0-9 set number
+ // other error
+ arg++;
+ if (nextchar == '&') {
+ newval_num = (OptInt)(intptr_t)options[opt_idx].def_val;
+ } else if (nextchar == '<') {
+ if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
+ // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global newval_num
+ newval_num = NO_LOCAL_UNDOLEVEL;
+ } else if (opt_flags == OPT_LOCAL
+ && ((OptInt *)varp == &curwin->w_p_siso || (OptInt *)varp == &curwin->w_p_so)) {
+ // for 'scrolloff'/'sidescrolloff' -1 means using the global newval_num
+ newval_num = -1;
+ } else {
+ newval_num = *(OptInt *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ }
+ } else if (((OptInt *)varp == &p_wc || (OptInt *)varp == &p_wcm)
+ && (*arg == '<' || *arg == '^'
+ || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
+ && !ascii_isdigit(*arg)))) {
+ newval_num = string_to_key(arg);
+ if (newval_num == 0 && (OptInt *)varp != &p_wcm) {
+ *errmsg = e_invarg;
+ return newval;
+ }
+ } else if (*arg == '-' || ascii_isdigit(*arg)) {
+ int i;
+ // Allow negative, octal and hex numbers.
+ vim_str2nr(arg, NULL, &i, STR2NR_ALL, &newval_num, NULL, 0, true, NULL);
+ if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
+ *errmsg = e_number_required_after_equal;
+ return newval;
+ }
+ } else {
+ *errmsg = e_number_required_after_equal;
+ return newval;
+ }
- if (*errmsg != NULL) {
- return;
+ if (op == OP_ADDING) {
+ newval_num = oldval_num + newval_num;
+ }
+ if (op == OP_PREPENDING) {
+ newval_num = oldval_num * newval_num;
+ }
+ if (op == OP_REMOVING) {
+ newval_num = oldval_num - newval_num;
+ }
+
+ newval = NUMBER_OPTVAL(newval_num);
+ break;
}
+ case kOptValTypeString: {
+ char *oldval_str;
+ vimoption_T *opt = get_option(opt_idx);
- if (opt_idx >= 0) {
- did_set_option(opt_idx, opt_flags, op == OP_NONE, value_checked);
+ // When setting the local value of a global option, the old value may be
+ // the global value.
+ if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ oldval_str = *(char **)get_varp(opt);
+ } else {
+ oldval_str = *(char **)varp;
+ }
+
+ // Get the new value for the option
+ char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op, flags);
+ newval = CSTR_AS_OPTVAL(newval_str);
+ break;
}
+ }
+
+ return newval;
}
-static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errbuf,
- size_t errbuflen, const char **errmsg)
+static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *errbuf,
+ size_t errbuflen, const char **errmsg)
{
// 1: nothing, 0: "no", 2: "inv" in front of name
set_prefix_T prefix = get_option_prefix(argp);
@@ -1486,8 +1403,21 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb
}
}
- do_set_option_value(opt_idx, opt_flags, argp, prefix, nextchar, op, flags, varp,
- errbuf, errbuflen, errmsg);
+ // Don't try to change hidden option.
+ if (varp == NULL) {
+ return;
+ }
+
+ OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp,
+ errbuf, errbuflen, errmsg);
+
+ if (newval.type == kOptValTypeNil || *errmsg != NULL) {
+ return;
+ }
+
+ *errmsg = set_option(opt_idx, varp, newval, opt_flags, op == OP_NONE, errbuf, errbuflen);
+ // `set_option` copies the new option value, so it needs to be freed here.
+ optval_free(newval);
}
/// Parse 'arg' for option settings.
@@ -1536,7 +1466,7 @@ int do_set(char *arg, int opt_flags)
const char *errmsg = NULL;
char errbuf[80];
- do_set_option(opt_flags, &arg, &did_show, errbuf, sizeof(errbuf), &errmsg);
+ do_one_set_option(opt_flags, &arg, &did_show, errbuf, sizeof(errbuf), &errmsg);
// Advance to next argument.
// - skip until a blank found, taking care of backslashes
@@ -1587,29 +1517,6 @@ int do_set(char *arg, int opt_flags)
return OK;
}
-/// Call this when an option has been given a new value through a user command.
-/// Sets the P_WAS_SET flag and takes care of the P_INSECURE flag.
-///
-/// @param opt_flags possibly with OPT_MODELINE
-/// @param new_value value was replaced completely
-/// @param value_checked value was checked to be safe, no need to set P_INSECURE
-void did_set_option(int opt_idx, int opt_flags, bool new_value, bool value_checked)
-{
- options[opt_idx].flags |= P_WAS_SET;
-
- // When an option is set in the sandbox, from a modeline or in secure mode
- // set the P_INSECURE flag. Otherwise, if a new value is stored reset the
- // flag.
- uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- if (!value_checked && (secure
- || sandbox != 0
- || (opt_flags & OPT_MODELINE))) {
- *p = *p | P_INSECURE;
- } else if (new_value) {
- *p = *p & ~P_INSECURE;
- }
-}
-
/// Convert a key name or string into a key value.
/// Used for 'wildchar' and 'cedit' options.
int string_to_key(char *arg)
@@ -1828,7 +1735,7 @@ int was_set_insecurely(win_T *const wp, char *opt, int opt_flags)
/// "opt_idx". For some local options a local flags field is used.
/// NOTE: Caller must make sure that "wp" is set to the window from which
/// the option is used.
-static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
+uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
{
if (opt_flags & OPT_LOCAL) {
assert(wp != NULL);
@@ -1979,62 +1886,62 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
}
/// Apply the OptionSet autocommand.
-static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptInt oldval, OptInt oldval_g,
- OptInt newval, const char *errmsg)
+static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptVal oldval, OptVal oldval_g,
+ OptVal oldval_l, OptVal newval, const char *errmsg)
{
// Don't do this while starting up, failure or recursively.
if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
return;
}
- char buf_old[12], buf_old_global[12], buf_new[12], buf_type[12];
+ 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);
- vim_snprintf(buf_old, sizeof(buf_old), "%" PRId64, oldval);
- vim_snprintf(buf_old_global, sizeof(buf_old_global), "%" PRId64, oldval_g);
- vim_snprintf(buf_new, sizeof(buf_new), "%" PRId64, newval);
- vim_snprintf(buf_type, sizeof(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
- set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
+ vim_snprintf(buf_type, sizeof(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global");
+ set_vim_var_tv(VV_OPTION_NEW, &newval_tv);
+ set_vim_var_tv(VV_OPTION_OLD, &oldval_tv);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
if (opt_flags &