diff options
| author | bfredl <bjorn.linse@gmail.com> | 2023-10-16 20:41:35 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-16 20:41:35 +0200 |
| commit | a63c67005b9ea17214d86391e2fd649658c1bdec (patch) | |
| tree | bd732635435cb15d0d7e0bbe4eb49ceae73c0999 /src | |
| parent | b80a8e2c16b6d6eb16ac84232c27eb7cfa4a434a (diff) | |
| parent | 3642f2fb44b6a3681e6a637671690258aa83cc62 (diff) | |
| download | rneovim-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')
| -rw-r--r-- | src/nvim/api/options.c | 41 | ||||
| -rw-r--r-- | src/nvim/arglist.c | 2 | ||||
| -rw-r--r-- | src/nvim/eval.c | 17 | ||||
| -rw-r--r-- | src/nvim/eval/funcs.c | 12 | ||||
| -rw-r--r-- | src/nvim/eval/vars.c | 45 | ||||
| -rw-r--r-- | src/nvim/eval/vars.h | 1 | ||||
| -rw-r--r-- | src/nvim/ex_eval.c | 2 | ||||
| -rw-r--r-- | src/nvim/globals.h | 9 | ||||
| -rw-r--r-- | src/nvim/normal.c | 4 | ||||
| -rw-r--r-- | src/nvim/ops.c | 4 | ||||
| -rw-r--r-- | src/nvim/option.c | 1008 | ||||
| -rw-r--r-- | src/nvim/option_defs.h | 35 | ||||
| -rw-r--r-- | src/nvim/options.lua | 1 | ||||
| -rw-r--r-- | src/nvim/optionstr.c | 80 | ||||
| -rw-r--r-- | src/nvim/quickfix.c | 8 | ||||
| -rw-r--r-- | src/nvim/syntax.c | 12 | ||||
| -rw-r--r-- | src/nvim/types.h | 2 |
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 & OPT_LOCAL) { set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); - set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, - |