diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2024-11-25 19:15:05 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2024-11-25 19:27:38 +0000 |
commit | c5d770d311841ea5230426cc4c868e8db27300a8 (patch) | |
tree | dd21f70127b4b8b5f109baefc8ecc5016f507c91 /src/nvim/option.c | |
parent | 9be89f131f87608f224f0ee06d199fcd09d32176 (diff) | |
parent | 081beb3659bd6d8efc3e977a160b1e72becbd8a2 (diff) | |
download | rneovim-c5d770d311841ea5230426cc4c868e8db27300a8.tar.gz rneovim-c5d770d311841ea5230426cc4c868e8db27300a8.tar.bz2 rneovim-c5d770d311841ea5230426cc4c868e8db27300a8.zip |
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'src/nvim/option.c')
-rw-r--r-- | src/nvim/option.c | 2078 |
1 files changed, 976 insertions, 1102 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c index ff0c0e2acf..bb86d10425 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -176,7 +176,7 @@ static int p_paste_dep_opts[] = { void set_init_tablocal(void) { // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal! - p_ch = options[kOptCmdheight].def_val.number; + p_ch = options[kOptCmdheight].def_val.data.number; } /// Initialize the 'shell' option to a default value. @@ -291,8 +291,8 @@ static void set_init_default_cdpath(void) } } buf[j] = NUL; - options[kOptCdpath].def_val.string = buf; - options[kOptCdpath].flags |= P_DEF_ALLOCED; + change_option_default(kOptCdpath, CSTR_AS_OPTVAL(buf)); + xfree(cdpath); } @@ -301,29 +301,22 @@ static void set_init_default_cdpath(void) /// only happen for non-indirect options. /// Also set the default to the expanded value, so ":set" does not list /// them. -/// Don't set the P_ALLOCED flag, because we don't want to free the -/// default. static void set_init_expand_env(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { vimoption_T *opt = &options[opt_idx]; - if (opt->flags & P_NO_DEF_EXP) { + if (opt->flags & kOptFlagNoDefExp) { continue; } char *p; - if ((opt->flags & P_GETTEXT) && opt->var != NULL) { + if ((opt->flags & kOptFlagGettext) && opt->var != NULL) { p = _(*(char **)opt->var); } else { p = option_expand(opt_idx, NULL); } if (p != NULL) { - p = xstrdup(p); - *(char **)opt->var = p; - if (opt->flags & P_DEF_ALLOCED) { - xfree(opt->def_val.string); - } - opt->def_val.string = p; - opt->flags |= P_DEF_ALLOCED; + set_option_varp(opt_idx, opt->var, CSTR_TO_OPTVAL(p), true); + change_option_default(opt_idx, CSTR_TO_OPTVAL(p)); } } } @@ -354,6 +347,9 @@ void set_init_1(bool clean_arg) { langmap_init(); + // Allocate the default option values. + alloc_options_default(); + set_init_default_shell(); set_init_default_backupskip(); set_init_default_cdpath(); @@ -430,71 +426,73 @@ void set_init_1(bool clean_arg) set_helplang_default(get_mess_lang()); } -/// Set an option to its default value. -/// This does not take care of side effects! +/// Get default value for option, based on the option's type and scope. /// -/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL. +/// @param opt_idx Option index in options[] table. +/// @param opt_flags Option flags. /// -/// TODO(famiu): Refactor this when def_val uses OptVal. -static void set_option_default(const OptIndex opt_idx, int opt_flags) +/// @return Default value of option for the scope specified in opt_flags. +static OptVal get_option_default(const OptIndex opt_idx, int opt_flags) { - bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - - // pointer to variable for current option vimoption_T *opt = &options[opt_idx]; - 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 (option_has_type(opt_idx, kOptValTypeString)) { - // Use set_option_direct() for local options to handle freeing and allocating the value. - if (opt->indir != PV_NONE) { - set_option_direct(opt_idx, CSTR_AS_OPTVAL(opt->def_val.string), opt_flags, 0); - } else { - if (flags & P_ALLOCED) { - free_string_option(*(char **)(varp)); - } - *(char **)varp = opt->def_val.string; - opt->flags &= ~P_ALLOCED; - } - } else if (option_has_type(opt_idx, kOptValTypeNumber)) { - if (opt->indir == PV_SCROLL) { - win_comp_scroll(curwin); - } else { - OptInt def_val = opt->def_val.number; - if ((OptInt *)varp == &curwin->w_p_so - || (OptInt *)varp == &curwin->w_p_siso) { - // 'scrolloff' and 'sidescrolloff' local values have a - // different default value than the global default. - *(OptInt *)varp = -1; - } else { - *(OptInt *)varp = def_val; - } - // May also set global value for local option. - if (both) { - *(OptInt *)get_varp_scope(opt, OPT_GLOBAL) = def_val; - } - } - } else { // boolean - *(int *)varp = opt->def_val.boolean; + bool is_global_local_option = option_is_global_local(opt_idx); + #ifdef UNIX - // 'modeline' defaults to off for root - if (opt->indir == PV_ML && getuid() == ROOT_UID) { - *(int *)varp = false; - } + if (opt_idx == kOptModeline && getuid() == ROOT_UID) { + // 'modeline' defaults to off for root. + return BOOLEAN_OPTVAL(false); + } #endif - // May also set global value for local option. - if (both) { - *(int *)get_varp_scope(opt, OPT_GLOBAL) = - *(int *)varp; - } - } - // The default value is not insecure. - uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags); - *flagsp = *flagsp & ~P_INSECURE; + if ((opt_flags & OPT_LOCAL) && is_global_local_option) { + // Use unset local value instead of default value for local scope of global-local options. + return get_option_unset_value(opt_idx); + } else if (option_has_type(opt_idx, kOptValTypeString) && !(opt->flags & kOptFlagNoDefExp)) { + // For string options, expand environment variables and ~ since the default value was already + // expanded, only required when an environment variable was set later. + char *s = option_expand(opt_idx, opt->def_val.data.string.data); + return s == NULL ? opt->def_val : CSTR_AS_OPTVAL(s); + } else { + return opt->def_val; } +} - set_option_sctx(opt_idx, opt_flags, current_sctx); +/// Allocate the default values for all options by copying them from the stack. +/// This ensures that we don't need to always check if the option default is allocated or not. +static void alloc_options_default(void) +{ + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { + options[opt_idx].def_val = optval_copy(options[opt_idx].def_val); + } +} + +/// Change the default value for an option. +/// +/// @param opt_idx Option index in options[] table. +/// @param value New default value. Must be allocated. +static void change_option_default(const OptIndex opt_idx, OptVal value) +{ + optval_free(options[opt_idx].def_val); + options[opt_idx].def_val = value; +} + +/// Set an option to its default value. +/// This does not take care of side effects! +/// +/// @param opt_idx Option index in options[] table. +/// @param opt_flags Option flags. +static void set_option_default(const OptIndex opt_idx, int opt_flags) +{ + OptVal def_val = get_option_default(opt_idx, opt_flags); + set_option_direct(opt_idx, def_val, opt_flags, current_sctx.sc_sid); + + if (opt_idx == kOptScroll) { + win_comp_scroll(curwin); + } + + // The default value is not insecure. + uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags); + *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure; } /// Set all options (except terminal options) to their default value. @@ -502,8 +500,8 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) /// @param opt_flags Option flags. static void set_options_default(int opt_flags) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { - if (!(options[opt_idx].flags & P_NODEFAULT)) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { + if (!(options[opt_idx].flags & kOptFlagNoDefault)) { set_option_default(opt_idx, opt_flags); } } @@ -522,20 +520,13 @@ static void set_options_default(int opt_flags) /// @param opt_idx Option index in options[] table. /// @param val The value of the option. /// @param allocated If true, do not copy default as it was already allocated. +/// +/// TODO(famiu): Remove this. static void set_string_default(OptIndex opt_idx, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { - if (opt_idx == kOptInvalid) { - return; - } - - vimoption_T *opt = &options[opt_idx]; - if (opt->flags & P_DEF_ALLOCED) { - xfree(opt->def_val.string); - } - - opt->def_val.string = allocated ? val : xstrdup(val); - opt->flags |= P_DEF_ALLOCED; + assert(opt_idx != kOptInvalid); + change_option_default(opt_idx, CSTR_AS_OPTVAL(allocated ? val : xstrdup(val))); } /// For an option value that contains comma separated items, find "newval" in @@ -551,9 +542,9 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva int bs = 0; for (char *s = origval; *s != NUL; s++) { - if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1))) + if ((!(flags & kOptFlagComma) || s == origval || (s[-1] == ',' && !(bs & 1))) && strncmp(s, newval, newvallen) == 0 - && (!(flags & P_COMMA) || s[newvallen] == ',' || s[newvallen] == NUL)) { + && (!(flags & kOptFlagComma) || s[newvallen] == ',' || s[newvallen] == NUL)) { return s; } // Count backslashes. Only a comma with an even number of backslashes @@ -569,35 +560,28 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva return NULL; } -/// Set the Vi-default value of a number option. -/// Used for 'lines' and 'columns'. -void set_number_default(OptIndex opt_idx, OptInt val) -{ - if (opt_idx != kOptInvalid) { - options[opt_idx].def_val.number = val; - } -} - #if defined(EXITFREE) /// Free all options. void free_all_options(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { - if (options[opt_idx].indir == PV_NONE) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { + bool hidden = is_option_hidden(opt_idx); + + if (option_is_global_only(opt_idx) || hidden) { // global option: free value and default value. - if ((options[opt_idx].flags & P_ALLOCED) && options[opt_idx].var != NULL) { + // hidden option: free default value only. + if (!hidden) { optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); } - if (options[opt_idx].flags & P_DEF_ALLOCED) { - optval_free(optval_from_varp(opt_idx, &options[opt_idx].def_val)); - } - } else if (options[opt_idx].var != VAR_WIN) { - // buffer-local option: free global value + } else if (!option_is_window_local(opt_idx)) { + // buffer-local option: free global value. optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); } + optval_free(options[opt_idx].def_val); } free_operatorfunc_option(); free_tagfunc_option(); + free_findfunc_option(); XFREE_CLEAR(fenc_default); XFREE_CLEAR(p_term); XFREE_CLEAR(p_ttytype); @@ -612,7 +596,7 @@ void set_init_2(bool headless) // 'scroll' defaults to half the window height. The stored default is zero, // which results in the actual value computed from the window height. - if (!(options[kOptScroll].flags & P_WAS_SET)) { + if (!(options[kOptScroll].flags & kOptFlagWasSet)) { set_option_default(kOptScroll, OPT_LOCAL); } comp_col(); @@ -622,7 +606,7 @@ void set_init_2(bool headless) if (!option_was_set(kOptWindow)) { p_window = Rows - 1; } - set_number_default(kOptWindow, Rows - 1); + change_option_default(kOptWindow, NUMBER_OPTVAL(Rows - 1)); } /// Initialize the options, part three: After reading the .vimrc @@ -633,56 +617,40 @@ void set_init_3(void) // Set 'shellpipe' and 'shellredir', depending on the 'shell' option. // This is done after other initializations, where 'shell' might have been // set, but only if they have not been set before. - bool do_srr = !(options[kOptShellredir].flags & P_WAS_SET); - bool do_sp = !(options[kOptShellpipe].flags & P_WAS_SET); + bool do_srr = !(options[kOptShellredir].flags & kOptFlagWasSet); + bool do_sp = !(options[kOptShellpipe].flags & kOptFlagWasSet); size_t len = 0; char *p = (char *)invocation_path_tail(p_sh, &len); p = xmemdupz(p, len); - { - // - // Default for p_sp is "| tee", for p_srr is ">". - // For known shells it is changed here to include stderr. - // - if (path_fnamecmp(p, "csh") == 0 - || path_fnamecmp(p, "tcsh") == 0) { - if (do_sp) { - p_sp = "|& tee"; - options[kOptShellpipe].def_val.string = p_sp; - } - if (do_srr) { - p_srr = ">&"; - options[kOptShellredir].def_val.string = p_srr; - } - } else if (path_fnamecmp(p, "sh") == 0 - || path_fnamecmp(p, "ksh") == 0 - || path_fnamecmp(p, "mksh") == 0 - || path_fnamecmp(p, "pdksh") == 0 - || path_fnamecmp(p, "zsh") == 0 - || path_fnamecmp(p, "zsh-beta") == 0 - || path_fnamecmp(p, "bash") == 0 - || path_fnamecmp(p, "fish") == 0 - || path_fnamecmp(p, "ash") == 0 - || path_fnamecmp(p, "dash") == 0) { - // Always use POSIX shell style redirection if we reach this - if (do_sp) { - p_sp = "2>&1| tee"; - options[kOptShellpipe].def_val.string = p_sp; - } - if (do_srr) { - p_srr = ">%s 2>&1"; - options[kOptShellredir].def_val.string = p_srr; - } + bool is_csh = path_fnamecmp(p, "csh") == 0 || path_fnamecmp(p, "tcsh") == 0; + bool is_known_shell = path_fnamecmp(p, "sh") == 0 || path_fnamecmp(p, "ksh") == 0 + || path_fnamecmp(p, "mksh") == 0 || path_fnamecmp(p, "pdksh") == 0 + || path_fnamecmp(p, "zsh") == 0 || path_fnamecmp(p, "zsh-beta") == 0 + || path_fnamecmp(p, "bash") == 0 || path_fnamecmp(p, "fish") == 0 + || path_fnamecmp(p, "ash") == 0 || path_fnamecmp(p, "dash") == 0; + + // Default for p_sp is "| tee", for p_srr is ">". + // For known shells it is changed here to include stderr. + if (is_csh || is_known_shell) { + if (do_sp) { + const OptVal sp = + is_csh ? STATIC_CSTR_AS_OPTVAL("|& tee") : STATIC_CSTR_AS_OPTVAL("2>&1| tee"); + set_option_direct(kOptShellpipe, sp, 0, SID_NONE); + change_option_default(kOptShellpipe, optval_copy(sp)); + } + if (do_srr) { + const OptVal srr = is_csh ? STATIC_CSTR_AS_OPTVAL(">&") : STATIC_CSTR_AS_OPTVAL(">%s 2>&1"); + set_option_direct(kOptShellredir, srr, 0, SID_NONE); + change_option_default(kOptShellredir, optval_copy(srr)); } - xfree(p); } + xfree(p); if (buf_is_empty(curbuf)) { - int idx_ffs = find_option("ffs"); - // Apply the first entry of 'fileformats' to the initial buffer. - if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { + if (options[kOptFileformats].flags & kOptFlagWasSet) { set_fileformat(default_fileformat(), OPT_LOCAL); } } @@ -702,13 +670,11 @@ void set_helplang_default(const char *lang) if (lang_len < 2) { // safety check return; } - if (options[kOptHelplang].flags & P_WAS_SET) { + if (options[kOptHelplang].flags & kOptFlagWasSet) { return; } - if (options[kOptHelplang].flags & P_ALLOCED) { - free_string_option(p_hlg); - } + free_string_option(p_hlg); p_hlg = xmemdupz(lang, lang_len); // zh_CN becomes "cn", zh_TW becomes "tw". if (STRNICMP(p_hlg, "zh_", 3) == 0 && lang_len >= 5) { @@ -720,7 +686,6 @@ void set_helplang_default(const char *lang) p_hlg[1] = 'n'; } p_hlg[2] = NUL; - options[kOptHelplang].flags |= P_ALLOCED; } /// 'title' and 'icon' only default to true if they have not been set or reset @@ -733,13 +698,13 @@ void set_title_defaults(void) // If GUI is (going to be) used, we can always set the window title and // icon name. Saves a bit of time, because the X11 display server does // not need to be contacted. - if (!(options[kOptTitle].flags & P_WAS_SET)) { - options[kOptTitle].def_val.boolean = false; - p_title = false; + if (!(options[kOptTitle].flags & kOptFlagWasSet)) { + change_option_default(kOptTitle, BOOLEAN_OPTVAL(false)); + p_title = 0; } - if (!(options[kOptIcon].flags & P_WAS_SET)) { - options[kOptIcon].def_val.boolean = false; - p_icon = false; + if (!(options[kOptIcon].flags & kOptFlagWasSet)) { + change_option_default(kOptIcon, BOOLEAN_OPTVAL(false)); + p_icon = 0; } } @@ -758,27 +723,6 @@ void ex_set(exarg_T *eap) do_set(eap->arg, flags); } -/// Get the default value for a string option. -static char *stropt_get_default_val(OptIndex opt_idx, uint64_t flags) -{ - char *newval = options[opt_idx].def_val.string; - // expand environment variables and ~ since the default value was - // already expanded, only required when an environment variable was set - // later - if (newval == NULL) { - newval = empty_string_option; - } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) { - char *s = option_expand(opt_idx, newval); - if (s == NULL) { - s = newval; - } - newval = xstrdup(s); - } else { - newval = xstrdup(newval); - } - return newval; -} - /// Copy the new string value into allocated memory for the option. /// Can't use set_option_direct(), because we need to remove the backslashes. static char *stropt_copy_value(char *origval, char **argp, set_op_T op, @@ -802,7 +746,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op, while (*arg != NUL && !ascii_iswhite(*arg)) { if (*arg == '\\' && arg[1] != NUL #ifdef BACKSLASH_IN_FILENAME - && !((flags & P_EXPAND) + && !((flags & kOptFlagExpand) && vim_isfilec((uint8_t)arg[1]) && !ascii_iswhite(arg[1]) && (arg[1] != '\\' @@ -851,12 +795,12 @@ static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval, static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, uint32_t flags) { int len = 0; - int comma = ((flags & P_COMMA) && *origval != NUL && *newval != NUL); + int comma = ((flags & kOptFlagComma) && *origval != NUL && *newval != NUL); if (op == OP_ADDING) { len = (int)strlen(origval); // Strip a trailing comma, would get 2. if (comma && len > 1 - && (flags & P_ONECOMMA) == P_ONECOMMA + && (flags & kOptFlagOneComma) == kOptFlagOneComma && origval[len - 1] == ',' && origval[len - 2] != '\\') { len--; @@ -881,7 +825,7 @@ static void stropt_remove_val(char *origval, char *newval, uint32_t flags, char STRCPY(newval, origval); if (*strval) { // may need to remove a comma - if (flags & P_COMMA) { + if (flags & kOptFlagComma) { if (strval == origval) { // include comma after string if (strval[len] == ',') { @@ -903,8 +847,8 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags) char *s = newval; // Remove flags that appear twice. for (s = newval; *s;) { - // if options have P_FLAGLIST and P_ONECOMMA such as 'whichwrap' - if (flags & P_ONECOMMA) { + // if options have kOptFlagFlagList and kOptFlagOneComma such as 'whichwrap' + if (flags & kOptFlagOneComma) { if (*s != ',' && *(s + 1) == ',' && vim_strchr(s + 2, (uint8_t)(*s)) != NULL) { // Remove the duplicated value and the next comma. @@ -912,7 +856,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags) continue; } } else { - if ((!(flags & P_COMMA) || *s != ',') + if ((!(flags & kOptFlagComma) || *s != ',') && vim_strchr(s + 1, (uint8_t)(*s)) != NULL) { STRMOVE(s, s + 1); continue; @@ -922,10 +866,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags) } } -/// Get the string value specified for a ":set" command. The following set -/// options are supported: -/// set {opt}& -/// set {opt}< +/// Get the string value specified for a ":set" command. The following set options are supported: /// set {opt}={val} /// set {opt}:{val} static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp, @@ -936,61 +877,56 @@ static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void char *save_arg = NULL; char *newval; char *s = NULL; - if (nextchar == '&') { // set to default val - newval = stropt_get_default_val(opt_idx, flags); - } else if (nextchar == '<') { // set to global val - newval = xstrdup(*(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL)); - } else { - arg++; // jump to after the '=' or ':' - // Set 'keywordprg' to ":help" if an empty - // value was passed to :set by the user. - if (varp == &p_kp && (*arg == NUL || *arg == ' ')) { - save_arg = arg; - arg = ":help"; - } + arg++; // jump to after the '=' or ':' - // Copy the new string into allocated memory. - newval = stropt_copy_value(origval, &arg, op, flags); + // Set 'keywordprg' to ":help" if an empty + // value was passed to :set by the user. + if (varp == &p_kp && (*arg == NUL || *arg == ' ')) { + save_arg = arg; + arg = ":help"; + } - // Expand environment variables and ~. - // Don't do it when adding without inserting a comma. - if (op == OP_NONE || (flags & P_COMMA)) { - newval = stropt_expand_envvar(opt_idx, origval, newval, op); - } + // Copy the new string into allocated memory. + newval = stropt_copy_value(origval, &arg, op, flags); - // locate newval[] in origval[] when removing it - // and when adding to avoid duplicates - int len = 0; - if (op == OP_REMOVING || (flags & P_NODUP)) { - len = (int)strlen(newval); - s = find_dup_item(origval, newval, (size_t)len, flags); + // Expand environment variables and ~. + // Don't do it when adding without inserting a comma. + if (op == OP_NONE || (flags & kOptFlagComma)) { + newval = stropt_expand_envvar(opt_idx, origval, newval, op); + } - // do not add if already there - if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) { - op = OP_NONE; - STRCPY(newval, origval); - } + // locate newval[] in origval[] when removing it + // and when adding to avoid duplicates + int len = 0; + if (op == OP_REMOVING || (flags & kOptFlagNoDup)) { + len = (int)strlen(newval); + s = find_dup_item(origval, newval, (size_t)len, flags); - // if no duplicate, move pointer to end of original value - if (s == NULL) { - s = origval + (int)strlen(origval); - } + // do not add if already there + if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) { + op = OP_NONE; + STRCPY(newval, origval); } - // concatenate the two strings; add a ',' if needed - if (op == OP_ADDING || op == OP_PREPENDING) { - stropt_concat_with_comma(origval, newval, op, flags); - } else if (op == OP_REMOVING) { - // Remove newval[] from origval[]. (Note: "len" has been set above - // and is used here). - stropt_remove_val(origval, newval, flags, s, len); + // if no duplicate, move pointer to end of original value + if (s == NULL) { + s = origval + (int)strlen(origval); } + } - if (flags & P_FLAGLIST) { - // Remove flags that appear twice. - stropt_remove_dupflags(newval, flags); - } + // concatenate the two strings; add a ',' if needed + if (op == OP_ADDING || op == OP_PREPENDING) { + stropt_concat_with_comma(origval, newval, op, flags); + } else if (op == OP_REMOVING) { + // Remove newval[] from origval[]. (Note: "len" has been set above + // and is used here). + stropt_remove_val(origval, newval, flags, s, len); + } + + if (flags & kOptFlagFlagList) { + // Remove flags that appear twice. + stropt_remove_dupflags(newval, flags); } if (save_arg != NULL) { @@ -1041,38 +977,35 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_ // 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 || !option_is_window_local(opt_idx))) { 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 && option_is_window_local(opt_idx)) { return FAIL; } // Disallow changing some options from modelines. if (opt_flags & OPT_MODELINE) { - if (flags & (P_SECURE | P_NO_ML)) { + if (flags & (kOptFlagSecure | kOptFlagNoML)) { *errmsg = e_not_allowed_in_modeline; return FAIL; } - if ((flags & P_MLE) && !p_mle) { + if ((flags & kOptFlagMLE) && !p_mle) { *errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off; return FAIL; } // In diff mode some options are overruled. This avoids that // 'foldmethod' becomes "marker" instead of "diff" and that // "wrap" gets set. - if (win->w_p_diff - && opt_idx != kOptInvalid // shut up coverity warning - && (options[opt_idx].indir == PV_FDM - || options[opt_idx].indir == PV_WRAP)) { + if (win->w_p_diff && (opt_idx == kOptFoldmethod || opt_idx == kOptWrap)) { return FAIL; } } // Disallow changing some options in the sandbox - if (sandbox != 0 && (flags & P_SECURE)) { + if (sandbox != 0 && (flags & kOptFlagSecure)) { *errmsg = e_sandbox; return FAIL; } @@ -1152,6 +1085,7 @@ const char *find_option_end(const char *arg, OptIndex *opt_idxp) } /// Get new option value from argp. Allocated OptVal must be freed by caller. +/// Can unset local value of an option when ":set {option}<" is used. 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, const size_t errbuflen, const char **errmsg) @@ -1162,10 +1096,24 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr vimoption_T *opt = &options[opt_idx]; char *arg = *argp; // When setting the local value of a global option, the old value may be the global value. - const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL); + const bool oldval_is_global = option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL); OptVal oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp); OptVal newval = NIL_OPTVAL; + if (nextchar == '&') { + // ":set opt&": Reset to default value. + // NOTE: Use OPT_GLOBAL instead of opt_flags to ensure we don't use the unset local value for + // global-local options when OPT_LOCAL is used. + return optval_copy(get_option_default(opt_idx, OPT_GLOBAL)); + } else if (nextchar == '<') { + // ":set opt<": Reset to global value. + // ":setlocal opt<": Copy global value to local value. + if (option_is_global_local(opt_idx) && !(opt_flags & OPT_LOCAL)) { + unset_option_local_value(opt_idx); + } + return get_option_value(opt_idx, OPT_GLOBAL); + } + switch (oldval.type) { case kOptValTypeNil: abort(); @@ -1173,8 +1121,6 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr 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: @@ -1187,15 +1133,6 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr newval_bool = kTrue; break; } - } else if (nextchar == '&') { - newval_bool = TRISTATE_FROM_INT(options[opt_idx].def_val.boolean); - } 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 @@ -1214,31 +1151,15 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr 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' or 'wildcharm' // ^x accept ctrl key codes for 'wildchar' or 'wildcharm' // c accept any non-digit for 'wildchar' or 'wildcharm' // [-]0-9 set number // other error arg++; - if (nextchar == '&') { - newval_num = options[opt_idx].def_val.number; - } 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)))) { + 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) { *errmsg = e_invarg; @@ -1318,21 +1239,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * } 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 (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; - } - return; - } - - flags = options[opt_idx].flags; - varp = get_varp_scope(&(options[opt_idx]), opt_flags); + // flags for current option + uint32_t flags = options[opt_idx].flags; + // pointer to variable for current option + void *varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) { return; @@ -1372,10 +1282,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * // 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 if (option_has_scope(opt_idx, kOptScopeWin)) { + option_last_set_msg(curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)]); + } else if (option_has_scope(opt_idx, kOptScopeBuf)) { + option_last_set_msg(curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)]); } } @@ -1402,11 +1312,6 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * } } - // 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); @@ -1414,8 +1319,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * return; } - *errmsg = set_option(opt_idx, varp, newval, opt_flags, 0, false, op == OP_NONE, errbuf, - errbuflen); + *errmsg = set_option(opt_idx, newval, opt_flags, 0, false, op == OP_NONE, errbuf, errbuflen); } /// Parse 'arg' for option settings. @@ -1664,7 +1568,7 @@ char *find_shada_parameter(int type) static char *option_expand(OptIndex opt_idx, char *val) { // if option doesn't need expansion nothing to do - if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) { + if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) { return NULL; } @@ -1705,7 +1609,7 @@ static void didset_options(void) spell_check_msm(); spell_check_sps(); compile_cap_prog(curwin->w_s); - did_set_spell_option(true); + did_set_spell_option(); // set cedit_key did_set_cedit(NULL); // initialize the table for 'breakat'. @@ -1736,7 +1640,7 @@ static void didset_options2(void) /// Check for string options that are NULL (normally only termcap options). void check_options(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) { check_string_option((char **)get_varp(&(options[opt_idx]))); } @@ -1755,10 +1659,10 @@ int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags) assert(opt_idx != kOptInvalid); uint32_t *flagp = insecure_flag(wp, opt_idx, opt_flags); - return (*flagp & P_INSECURE) != 0; + return (*flagp & kOptFlagInsecure) != 0; } -/// Get a pointer to the flags used for the P_INSECURE flag of option +/// Get a pointer to the flags used for the kOptFlagInsecure flag of option /// "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. @@ -1766,24 +1670,25 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags) { if (opt_flags & OPT_LOCAL) { assert(wp != NULL); - switch ((int)options[opt_idx].indir) { - case PV_STL: + switch (opt_idx) { + case kOptStatusline: return &wp->w_p_stl_flags; - case PV_WBR: + case kOptWinbar: return &wp->w_p_wbr_flags; - case PV_FDE: + case kOptFoldexpr: return &wp->w_p_fde_flags; - case PV_FDT: + case kOptFoldtext: return &wp->w_p_fdt_flags; - case PV_INDE: + case kOptIndentexpr: return &wp->w_buffer->b_p_inde_flags; - case PV_FEX: + case kOptFormatexpr: return &wp->w_buffer->b_p_fex_flags; - case PV_INEX: + case kOptIncludeexpr: return &wp->w_buffer->b_p_inex_flags; + default: + break; } } - // Nothing special, return global flags field. return &options[opt_idx].flags; } @@ -1816,12 +1721,22 @@ void check_blending(win_T *wp) } /// Handle setting `winhighlight' in window "wp" -bool parse_winhl_opt(win_T *wp) +/// +/// @param winhl when NULL: use "wp->w_p_winhl" +/// @param wp when NULL: only parse "winhl" +/// +/// @return whether the option value is valid. +bool parse_winhl_opt(const char *winhl, win_T *wp) { - const char *p = wp->w_p_winhl; + const char *p = empty_string_option; + if (winhl != NULL) { + p = winhl; + } else if (wp != NULL) { + p = wp->w_p_winhl; + } if (!*p) { - if (wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) { + if (wp != NULL && wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) { wp->w_ns_hl = 0; wp->w_hl_needs_update = true; } @@ -1829,39 +1744,49 @@ bool parse_winhl_opt(win_T *wp) return true; } - if (wp->w_ns_hl_winhl == 0) { - wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING); - } else { - // namespace already exist. invalidate existing items - DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true); - dp->hl_valid++; + int ns_hl = 0; + if (wp != NULL) { + if (wp->w_ns_hl_winhl == 0) { + wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING); + } else { + // Namespace already exists. Invalidate existing items. + DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true); + dp->hl_valid++; + } + wp->w_ns_hl = wp->w_ns_hl_winhl; + ns_hl = wp->w_ns_hl; } - wp->w_ns_hl = wp->w_ns_hl_winhl; - int ns_hl = wp->w_ns_hl; while (*p) { - char *colon = strchr(p, ':'); + const char *colon = strchr(p, ':'); if (!colon) { return false; } size_t nlen = (size_t)(colon - p); - char *hi = colon + 1; - char *commap = xstrchrnul(hi, ','); + const char *hi = colon + 1; + const char *commap = xstrchrnul(hi, ','); size_t len = (size_t)(commap - hi); int hl_id = len ? syn_check_group(hi, len) : -1; if (hl_id == 0) { return false; } int hl_id_link = nlen ? syn_check_group(p, nlen) : 0; + if (hl_id_link == 0) { + return false; + } - HlAttrs attrs = HLATTRS_INIT; - attrs.rgb_ae_attr |= HL_GLOBAL; - ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL); + if (wp != NULL) { + HlAttrs attrs = HLATTRS_INIT; + attrs.rgb_ae_attr |= HL_GLOBAL; + ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL); + } p = *commap ? commap + 1 : ""; } - wp->w_hl_needs_update = true; + if (wp != NULL) { + wp->w_hl_needs_update = true; + } return true; } @@ -1877,7 +1802,6 @@ sctx_T *get_option_sctx(OptIndex opt_idx) void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx) { bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - int indir = (int)options[opt_idx].indir; nlua_set_sctx(&script_ctx); LastSet last_set = { .script_ctx = script_ctx, @@ -1891,17 +1815,17 @@ void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx) // Remember where the option was set. For local options need to do that // in the buffer or window structure. - if (both || (opt_flags & OPT_GLOBAL) || (indir & (PV_BUF|PV_WIN)) == 0) { + if (both || (opt_flags & OPT_GLOBAL) || option_is_global_only(opt_idx)) { options[opt_idx].last_set = last_set; } if (both || (opt_flags & OPT_LOCAL)) { - if (indir & PV_BUF) { - curbuf->b_p_script_ctx[indir & PV_MASK] = last_set; - } else if (indir & PV_WIN) { - curwin->w_p_script_ctx[indir & PV_MASK] = last_set; + if (option_has_scope(opt_idx, kOptScopeBuf)) { + curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)] = last_set; + } else if ((option_has_scope(opt_idx, kOptScopeWin))) { + curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = last_set; if (both) { // also setting the "all buffers" value - curwin->w_allbuf_opt.wo_script_ctx[indir & PV_MASK] = last_set; + curwin->w_allbuf_opt.wo_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = last_set; } } } @@ -1959,7 +1883,7 @@ static const char *did_set_arabic(optset_T *args) // set rightleft mode if (!win->w_p_rl) { win->w_p_rl = true; - changed_window_setting(curwin); + changed_window_setting(win); } // Enable Arabic shaping (major part of what Arabic requires) @@ -1974,8 +1898,8 @@ static const char *did_set_arabic(optset_T *args) if (strcmp(p_enc, "utf-8") != 0) { static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'"); - msg_source(HL_ATTR(HLF_W)); - msg(_(w_arabic), HL_ATTR(HLF_W)); + msg_source(HLF_W); + msg(_(w_arabic), HLF_W); set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1); } @@ -1990,7 +1914,7 @@ static const char *did_set_arabic(optset_T *args) // reset rightleft mode if (win->w_p_rl) { win->w_p_rl = false; - changed_window_setting(curwin); + changed_window_setting(win); } // 'arabicshape' isn't reset, it is a global option and @@ -2001,8 +1925,8 @@ static const char *did_set_arabic(optset_T *args) // window may still want it "on". // Revert to the default keymap - curbuf->b_p_iminsert = B_IMODE_NONE; - curbuf->b_p_imsearch = B_IMODE_USE_INSERT; + win->w_buffer->b_p_iminsert = B_IMODE_NONE; + win->w_buffer->b_p_imsearch = B_IMODE_USE_INSERT; } return errmsg; @@ -2124,9 +2048,7 @@ static const char *did_set_helpheight(optset_T *args) { // Change window height NOW if (!ONE_WINDOW) { - buf_T *buf = (buf_T *)args->os_buf; - win_T *win = (win_T *)args->os_win; - if (buf->b_help && win->w_height < p_hh) { + if (curbuf->b_help && curwin->w_height < p_hh) { win_setheight((int)p_hh); } } @@ -2278,7 +2200,7 @@ static const char *did_set_number_relativenumber(optset_T *args) // When 'relativenumber'/'number' is changed and 'statuscolumn' is set, reset width. win->w_nrwidth_line_count = 0; } - check_signcolumn(win); + check_signcolumn(NULL, win); return NULL; } @@ -2455,14 +2377,16 @@ static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED) /// Process the updated 'readonly' option value. static const char *did_set_readonly(optset_T *args) { + buf_T *buf = (buf_T *)args->os_buf; + // when 'readonly' is reset globally, also reset readonlymode - if (!curbuf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) { + if (!buf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) { readonlymode = false; } // when 'readonly' is set may give W10 again - if (curbuf->b_p_ro) { - curbuf->b_did_warn = false; + if (buf->b_p_ro) { + buf->b_did_warn = false; } redraw_titles(); @@ -2578,8 +2502,7 @@ static const char *did_set_swapfile(optset_T *args) if (buf->b_p_swf && p_uc) { ml_open_file(buf); // create the swap file } else { - // no need to reset curbuf->b_may_swap, ml_open_file() will check - // buf->b_p_swf + // no need to reset buf->b_may_swap, ml_open_file() will check buf->b_p_swf mf_close_file(buf, true); // remove the swap file } return NULL; @@ -2589,7 +2512,7 @@ static const char *did_set_swapfile(optset_T *args) static const char *did_set_textwidth(optset_T *args FUNC_ATTR_UNUSED) { FOR_ALL_TAB_WINDOWS(tp, wp) { - check_colorcolumn(wp); + check_colorcolumn(NULL, wp); } return NULL; @@ -2619,8 +2542,10 @@ static const char *did_set_titlelen(optset_T *args) /// Process the updated 'undofile' option value. static const char *did_set_undofile(optset_T *args) { + buf_T *buf = (buf_T *)args->os_buf; + // Only take action when the option was set. - if (!curbuf->b_p_udf && !p_udf) { + if (!buf->b_p_udf && !p_udf) { return NULL; } @@ -2633,7 +2558,7 @@ static const char *did_set_undofile(optset_T *args) // only for the current buffer: Try to read in the undofile, // if one exists, the buffer wasn't changed and the buffer was // loaded - if ((curbuf == bp + if ((buf == bp || (args->os_flags & OPT_GLOBAL) || args->os_flags == 0) && !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) { u_compute_hash(bp, hash); @@ -2673,7 +2598,7 @@ static const char *did_set_undolevels(optset_T *args) if (pp == &p_ul) { // global 'undolevels' did_set_global_undolevels(args->os_newval.number, args->os_oldval.number); - } else if (pp == &curbuf->b_p_ul) { // buffer local 'undolevels' + } else if (pp == &buf->b_p_ul) { // buffer local 'undolevels' did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number); } @@ -2738,8 +2663,7 @@ static const char *did_set_winheight(optset_T *args) { // Change window height NOW if (!ONE_WINDOW) { - win_T *win = (win_T *)args->os_win; - if (win->w_height < p_wh) { + if (curwin->w_height < p_wh) { win_setheight((int)p_wh); } } @@ -2750,9 +2674,7 @@ static const char *did_set_winheight(optset_T *args) /// Process the new 'winwidth' option value. static const char *did_set_winwidth(optset_T *args) { - win_T *win = (win_T *)args->os_win; - - if (!ONE_WINDOW && win->w_width < p_wiw) { + if (!ONE_WINDOW && curwin->w_width < p_wiw) { win_setwidth((int)p_wiw); } return NULL; @@ -2815,15 +2737,14 @@ static void do_spelllang_source(win_T *win) /// Check the bounds of numeric options. /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. -/// @param[in] varp Pointer to option variable. /// @param[in,out] newval Pointer to new option value. Will be set to bound checked value. /// @param[out] errbuf Buffer for error message. Cannot be NULL. /// @param errbuflen Length of error buffer. /// /// @return Error message, if any. -static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt *newval, - char *errbuf, size_t errbuflen) - FUNC_ATTR_NONNULL_ARG(4) +static const char *check_num_option_bounds(OptIndex opt_idx, OptInt *newval, char *errbuf, + size_t errbuflen) + FUNC_ATTR_NONNULL_ARG(3) { const char *errmsg = NULL; @@ -2856,9 +2777,7 @@ static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt } break; case kOptScroll: - if (varp == &(curwin->w_p_scr) - && (*newval <= 0 - || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0)) + if ((*newval <= 0 || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0)) && full_screen) { if (*newval != 0) { errmsg = e_scroll; @@ -2876,13 +2795,12 @@ static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt /// Validate and bound check option value. /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. -/// @param[in] varp Pointer to option variable. /// @param[in,out] newval Pointer to new option value. Will be set to bound checked value. /// @param[out] errbuf Buffer for error message. Cannot be NULL. /// @param errbuflen Length of error buffer. /// /// @return Error message, if any. -static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *newval, char *errbuf, +static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *errbuf, size_t errbuflen) { OptInt value = *newval; @@ -2892,167 +2810,168 @@ static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *new return e_invarg; } - if (varp == &p_wh) { + switch (opt_idx) { + case kOptHelpheight: + case kOptTitlelen: + case kOptUpdatecount: + case kOptReport: + case kOptUpdatetime: + case kOptSidescroll: + case kOptFoldlevel: + case kOptShiftwidth: + case kOptTextwidth: + case kOptWritedelay: + case kOptTimeoutlen: + if (value < 0) { + return e_positive; + } + break; + case kOptWinheight: if (value < 1) { return e_positive; } else if (p_wmh > value) { return e_winheight; } - } else if (varp == &p_hh) { - if (value < 0) { - return e_positive; - } - } else if (varp == &p_wmh) { + break; + case kOptWinminheight: if (value < 0) { return e_positive; } else if (value > p_wh) { return e_winheight; } - } else if (varp == &p_wiw) { + break; + case kOptWinwidth: if (value < 1) { return e_positive; } else if (p_wmw > value) { return e_winwidth; } - } else if (varp == &p_wmw) { + break; + case kOptWinminwidth: if (value < 0) { return e_positive; } else if (value > p_wiw) { return e_winwidth; } - } else if (varp == &p_titlelen) { - if (value < 0) { - return e_positive; - } - } else if (varp == &p_uc) { - if (value < 0) { - return e_positive; - } - } else if (varp == &p_ch) { + break; + case kOptMaxcombine: + *newval = MAX_MCO; + break; + case kOptCmdheight: if (value < 0) { return e_positive; } else { p_ch_was_zero = value == 0; } - } else if (varp == &p_tm) { + break; + case kOptHistory: if (value < 0) { return e_positive; + } else if (value > 10000) { + return e_invarg; } - } else if (varp == &p_hi) { + break; + case kOptMsghistory: if (value < 0) { return e_positive; } else if (value > 10000) { return e_invarg; } - } else if (varp == &p_pyx) { + break; + case kOptPyxversion: if (value == 0) { *newval = 3; } else if (value != 3) { return e_invarg; } - } else if (varp == &p_re) { + break; + case kOptRegexpengine: if (value < 0 || value > 2) { return e_invarg; } - } else if (varp == &p_report) { - if (value < 0) { - return e_positive; - } - } else if (varp == &p_so) { + break; + case kOptScrolloff: if (value < 0 && full_screen) { return e_positive; } - } else if (varp == &p_siso) { + break; + case kOptSidescrolloff: if (value < 0 && full_screen) { return e_positive; } - } else if (varp == &p_cwh) { + break; + case kOptCmdwinheight: if (value < 1) { return e_positive; } - } else if (varp == &p_ut) { - if (value < 0) { - return e_positive; - } - } else if (varp == &p_ss) { - if (value < 0) { - return e_positive; - } - } else if (varp == &curwin->w_p_fdl || varp == &curwin->w_allbuf_opt.wo_fdl) { - if (value < 0) { - return e_positive; - } - } else if (varp == &curwin->w_p_cole || varp == &curwin->w_allbuf_opt.wo_cole) { + break; + case kOptConceallevel: if (value < 0) { return e_positive; } else if (value > 3) { return e_invarg; } - } else if (varp == &curwin->w_p_nuw || varp == &curwin->w_allbuf_opt.wo_nuw) { + break; + case kOptNumberwidth: if (value < 1) { return e_positive; } else if (value > MAX_NUMBERWIDTH) { return e_invarg; } - } else if (varp == &curbuf->b_p_iminsert || varp == &p_iminsert) { + break; + case kOptIminsert: if (value < 0 || value > B_IMODE_LAST) { return e_invarg; } - } else if (varp == &curbuf->b_p_imsearch || varp == &p_imsearch) { + break; + case kOptImsearch: if (value < -1 || value > B_IMODE_LAST) { return e_invarg; } - } else if (varp == &curbuf->b_p_channel || varp == &p_channel) { + break; + case kOptChannel: return e_invarg; - } else if (varp == &curbuf->b_p_scbk || varp == &p_scbk) { + case kOptScrollback: if (value < -1 || value > SB_MAX) { return e_invarg; } - } else if (varp == &curbuf->b_p_sw || varp == &p_sw) { - if (value < 0) { - return e_positive; - } - } else if (varp == &curbuf->b_p_ts || varp == &p_ts) { + break; + case kOptTabstop: if (value < 1) { return e_positive; } else if (value > TABSTOP_MAX) { return e_invarg; } - } else if (varp == &curbuf->b_p_tw || varp == &p_tw) { - if (value < 0) { - return e_positive; - } - } else if (varp == &p_wd) { - if (value < 0) { - return e_positive; - } + break; + default: + break; } - return check_num_option_bounds(opt_idx, varp, newval, errbuf, errbuflen); + return check_num_option_bounds(opt_idx, newval, errbuf, errbuflen); } /// Called after an option changed: check if something needs to be redrawn. void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags) { - // Careful: P_RALL is a combination of other P_ flags - bool all = (flags & P_RALL) == P_RALL; + // Careful: kOptFlagRedrAll is a combination of other redraw flags + bool all = (flags & kOptFlagRedrAll) == kOptFlagRedrAll; - if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty + if ((flags & kOptFlagRedrStat) || all) { // mark all status lines and window bars dirty status_redraw_all(); } - if ((flags & P_RTABL) || all) { // mark tablines dirty + if ((flags & kOptFlagRedrTabl) || all) { // mark tablines dirty redraw_tabline = true; } - if ((flags & P_RBUF) || (flags & P_RWIN) || all) { - if (flags & P_HLONLY) { + if ((flags & kOptFlagRedrBuf) || (flags & kOptFlagRedrWin) || all) { + if (flags & kOptFlagHLOnly) { redraw_later(win, UPD_NOT_VALID); } else { changed_window_setting(win); } } - if (flags & P_RBUF) { + if (flags & kOptFlagRedrBuf) { redraw_buf_later(buf, UPD_NOT_VALID); } if (all) { @@ -3193,11 +3112,25 @@ 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 - && strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size); + && (o1.data.string.data == o2.data.string.data + || strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size)); } UNREACHABLE; } +/// Get type of option. Does not support multitype options. +static OptValType option_get_type(const OptIndex opt_idx) +{ + assert(!option_is_multitype(opt_idx)); + + // 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); + return type; +} + /// Create OptVal from var pointer. /// /// @param opt_idx Option index in options[] table. @@ -3205,6 +3138,7 @@ bool optval_equal(OptVal o1, OptVal o2) /// /// @return Option value stored in varp. OptVal optval_from_varp(OptIndex opt_idx, void *varp) + FUNC_ATTR_NONNULL_ARG(2) { // Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc' // changed. @@ -3214,24 +3148,20 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp) if (option_is_multitype(opt_idx)) { // Multitype options are stored as OptVal. - return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp; + return *(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); + OptValType type = option_get_type(opt_idx); switch (type) { case kOptValTypeNil: return NIL_OPTVAL; case kOptValTypeBoolean: - return BOOLEAN_OPTVAL(varp == NULL ? false : TRISTATE_FROM_INT(*(int *)varp)); + return BOOLEAN_OPTVAL(TRISTATE_FROM_INT(*(int *)varp)); case kOptValTypeNumber: - return NUMBER_OPTVAL(varp == NULL ? 0 : *(OptInt *)varp); + return NUMBER_OPTVAL(*(OptInt *)varp); case kOptValTypeString: - return STRING_OPTVAL(varp == NULL ? (String)STRING_INIT : cstr_as_string(*(char **)varp)); + return STRING_OPTVAL(cstr_as_string(*(char **)varp)); } UNREACHABLE; } @@ -3334,7 +3264,7 @@ OptVal object_as_optval(Object o, bool *error) /// Get an allocated string containing a list of valid types for an option. /// For options with a singular type, it returns the name of the type. For options with multiple /// possible types, it returns a slash separated list of types. For example, if an option can be a -/// number, boolean or string, the function returns "Number/Boolean/String" +/// number, boolean or string, the function returns "number/boolean/string" static char *option_get_valid_types(OptIndex opt_idx) { StringBuilder str = KV_INITIAL_VALUE; @@ -3356,8 +3286,6 @@ static char *option_get_valid_types(OptIndex opt_idx) // Ensure that the string is NUL-terminated. kv_push(str, NUL); return str.items; - -#undef OPTION_ADD_TYPE } /// Check if option is hidden. @@ -3367,7 +3295,74 @@ static char *option_get_valid_types(OptIndex opt_idx) /// @return True if option is hidden, false otherwise. Returns false if option name is invalid. bool is_option_hidden(OptIndex opt_idx) { - return opt_idx == kOptInvalid ? false : get_varp(&options[opt_idx]) == NULL; + // Hidden options are always immutable and point to their default value + return opt_idx != kOptInvalid && options[opt_idx].immutable + && options[opt_idx].var == &options[opt_idx].def_val.data; +} + +/// Check if option is multitype (supports multiple types). +static 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); +} + +/// Check if option supports a specific type. +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 supports a specific scope. +bool option_has_scope(OptIndex opt_idx, OptScope scope) +{ + // Ensure that scope flags variable can hold all scopes. + STATIC_ASSERT(kOptScopeSize <= sizeof(OptScopeFlags) * 8, + "Option scope_flags cannot fit all option scopes"); + // Ensure that the scope is valid before accessing scope_flags. + assert(scope >= kOptScopeGlobal && scope < kOptScopeSize); + // Bitshift 1 by the value of scope to get the scope's corresponding flag, and check if it's set + // in the scope_flags bit field. + return get_option(opt_idx)->scope_flags & (1 << scope); +} + +/// Check if option is global-local. +static inline bool option_is_global_local(OptIndex opt_idx) +{ + // Global-local options have at least two types, so their type flag cannot be a power of two. + return opt_idx != kOptInvalid && !is_power_of_two(options[opt_idx].scope_flags); +} + +/// Check if option only supports global scope. +static inline bool option_is_global_only(OptIndex opt_idx) +{ + // For an option to be global-only, it has to only have a single scope, which means the scope + // flags must be a power of two, and it must have the global scope. + return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags) + && option_has_scope(opt_idx, kOptScopeGlobal); +} + +/// Check if option only supports window scope. +static inline bool option_is_window_local(OptIndex opt_idx) +{ + // For an option to be window-local it has to only have a single scope, which means the scope + // flags must be a power of two, and it must have the window scope. + return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags) + && option_has_scope(opt_idx, kOptScopeWin); +} + +/// Get option index for scope. +ssize_t option_scope_idx(OptIndex opt_idx, OptScope scope) +{ + return options[opt_idx].scope_idx[scope]; } /// Get option flags. @@ -3418,7 +3413,7 @@ static OptVal get_option_unset_value(OptIndex opt_idx) vimoption_T *opt = &options[opt_idx]; // For global-local options, use the unset value of the local value. - if (opt->indir & PV_BOTH) { + if (option_is_global_local(opt_idx)) { // String global-local options always use an empty string for the unset value. if (option_has_type(opt_idx, kOptValTypeString)) { return STATIC_CSTR_AS_OPTVAL(""); @@ -3450,7 +3445,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx) vimoption_T *opt = get_option(opt_idx); // Local value of option that isn't global-local is always considered set. - if (!((int)opt->indir & PV_BOTH)) { + if (!option_is_global_local(opt_idx)) { return false; } @@ -3484,7 +3479,6 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value vimoption_T *opt = &options[opt_idx]; const char *errmsg = NULL; bool restore_chartab = false; - bool free_oldval = (opt->flags & P_ALLOCED); bool value_changed = false; bool value_checked = false; @@ -3503,15 +3497,15 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value .os_win = curwin }; - if (direct || opt->hidden) { - // Don't do any extra processing if setting directly or if option is hidden. + if (direct) { + // Don't do any extra processing if setting directly. } // Disallow changing immutable options. else if (opt->immutable && !optval_equal(old_value, new_value)) { errmsg = e_unsupportedoption; } // Disallow changing some options from secure mode. - else if ((secure || sandbox != 0) && (opt->flags & P_SECURE)) { + else if ((secure || sandbox != 0) && (opt->flags & kOptFlagSecure)) { errmsg = e_secure; } // Check for a "normal" directory or file name in some string options. @@ -3533,7 +3527,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value // If option is hidden or if an error is detected, restore the previous value and don't do any // further processing. - if (opt->hidden || errmsg != NULL) { + if (errmsg != NULL) { set_option_varp(opt_idx, varp, old_value, true); // When resetting some values, need to act on it. if (restore_chartab) { @@ -3560,18 +3554,12 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value set_option_sctx(opt_idx, opt_flags, script_ctx); } - // Free options that are in allocated memory. - // Use "free_oldval", because recursiveness may change the flags (esp. init_highlight()). - if (free_oldval) { - optval_free(old_value); - } - opt->flags |= P_ALLOCED; + optval_free(old_value); const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - const bool opt_is_global_local = opt->indir & PV_BOTH; if (scope_both) { - if (opt_is_global_local) { + if (option_is_global_local(opt_idx)) { // Global option with local value set to use global value. // Free the local value and clear it. void *varp_local = get_varp_scope(opt, OPT_LOCAL); @@ -3618,7 +3606,8 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value } if (curwin->w_curswant != MAXCOL - && (opt->flags & (P_CURSWANT | P_RALL)) != 0 && (opt->flags & P_HLONLY) == 0) { + && (opt->flags & (kOptFlagCurswant | kOptFlagRedrAll)) != 0 + && (opt->flags & kOptFlagHLOnly) == 0) { curwin->w_set_curswant = true; } @@ -3626,14 +3615,14 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value if (errmsg == NULL) { uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags); - opt->flags |= P_WAS_SET; + opt->flags |= kOptFlagWasSet; - // When an option is set in the sandbox, from a modeline or in secure mode set the P_INSECURE + // When an option is set in the sandbox, from a modeline or in secure mode set the kOptFlagInsecure // flag. Otherwise, if a new value is stored reset the flag. if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) { - *p |= P_INSECURE; + *p |= kOptFlagInsecure; } else if (value_replaced) { - *p &= ~P_INSECURE; + *p &= ~(unsigned)kOptFlagInsecure; } } @@ -3643,14 +3632,19 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value /// Validate the new value for an option. /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. -/// @param varp Pointer to option variable. /// @param newval[in,out] New option value. Might be modified. -static const char *validate_option_value(const OptIndex opt_idx, void *varp, OptVal *newval, - int opt_flags, char *errbuf, size_t errbuflen) +static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, int opt_flags, + char *errbuf, size_t errbuflen) { const char *errmsg = NULL; vimoption_T *opt = &options[opt_idx]; + // Always allow unsetting local value of global-local option. + if (option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL) + && optval_equal(*newval, get_option_unset_value(opt_idx))) { + return NULL; + } + if (newval->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 @@ -3658,7 +3652,6 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt if (opt_flags == OPT_GLOBAL) { errmsg = _("Cannot unset global option value"); } else { - optval_free(*newval); *newval = optval_copy(get_option_unset_value(opt_idx)); } } else if (!option_has_type(opt_idx, newval->type)) { @@ -3671,7 +3664,7 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt errmsg = errbuf; } else if (newval->type == kOptValTypeNumber) { // Validate and bound check num option values. - errmsg = validate_num_option(opt_idx, varp, &newval->data.number, errbuf, errbuflen); + errmsg = validate_num_option(opt_idx, &newval->data.number, errbuf, errbuflen); } return errmsg; @@ -3680,7 +3673,6 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt /// Set the value of an option using an OptVal. /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. -/// @param[in] varp Option variable pointer, cannot be NULL. /// @param value New option value. Might get freed. /// @param opt_flags Option flags. /// @param set_sid Script ID. Special values: @@ -3692,35 +3684,35 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt /// @param errbuflen Length of error buffer. /// /// @return NULL on success, an untranslated error message on error. -static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, - scid_T set_sid, const bool direct, const bool value_replaced, - char *errbuf, size_t errbuflen) - FUNC_ATTR_NONNULL_ARG(2) +static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid, + const bool direct, const bool value_replaced, char *errbuf, + size_t errbuflen) { assert(opt_idx != kOptInvalid); - const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen); + const char *errmsg = NULL; - if (errmsg != NULL) { - optval_free(value); - return errmsg; + if (!direct) { + errmsg = validate_option_value(opt_idx, &value, opt_flags, errbuf, errbuflen); + + if (errmsg != NULL) { + optval_free(value); + return errmsg; + } } vimoption_T *opt = &options[opt_idx]; const bool scope_local = opt_flags & OPT_LOCAL; const bool scope_global = opt_flags & OPT_GLOBAL; const bool scope_both = !scope_local && !scope_global; - const bool opt_is_global_local = opt->indir & PV_BOTH; // Whether local value of global-local option is unset. - // NOTE: When this is true, it also implies that opt_is_global_local is true. + // NOTE: When this is true, it also implies that the option is global-local. const bool is_opt_local_unset = is_option_local_value_unset(opt_idx); // 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 (scope_both && opt_is_global_local) { - varp = opt->var; - } - + // use the global value in that case. + void *varp + = scope_both && option_is_global_local(opt_idx) ? opt->var : get_varp_scope(opt, opt_flags); void *varp_local = get_varp_scope(opt, OPT_LOCAL); void *varp_global = get_varp_scope(opt, OPT_GLOBAL); @@ -3749,9 +3741,9 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, 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 + // effects in secure mode. Also when the value was set with the kOptFlagInsecure flag and is not // completely replaced. - if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & P_INSECURE))) { + if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & kOptFlagInsecure))) { secure = 1; } @@ -3768,7 +3760,7 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, apply_optionset_autocmd(opt_idx, opt_flags, saved_used_value, saved_old_global_value, saved_old_local_value, saved_new_value, errmsg); } - if (opt->flags & P_UI_OPTION) { + if (opt->flags & kOptFlagUIOption) { ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value)); } } @@ -3794,17 +3786,14 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set { static char errbuf[IOSIZE]; - vimoption_T *opt = get_option(opt_idx); - - if (opt->var == NULL) { + if (is_option_hidden(opt_idx)) { return; } - const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - void *varp = get_varp_scope(opt, scope_both ? OPT_LOCAL : opt_flags); - - set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true, errbuf, - sizeof(errbuf)); + const char *errmsg = set_option(opt_idx, optval_copy(value), opt_flags, set_sid, true, true, + errbuf, sizeof(errbuf)); + assert(errmsg == NULL); + (void)errmsg; // ignore unused warning } /// Set option value directly for buffer / window, without processing any side effects. @@ -3815,10 +3804,10 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set /// @param set_sid Script ID. Special values: /// 0: Use current script ID. /// SID_NONE: Don't set script ID. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid, - OptReqScope req_scope, void *const from) + OptScope req_scope, void *const from) { buf_T *save_curbuf = curbuf; win_T *save_curwin = curwin; @@ -3827,15 +3816,15 @@ void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T // side-effects when setting an option directly. Just change the values of curbuf and curwin if // needed, no need to properly switch the window / buffer. switch (req_scope) { - case kOptReqGlobal: - break; - case kOptReqBuf: - curbuf = (buf_T *)from; + case kOptScopeGlobal: break; - case kOptReqWin: + case kOptScopeWin: curwin = (win_T *)from; curbuf = curwin->w_buffer; break; + case kOptScopeBuf: + curbuf = (buf_T *)from; + break; } set_option_direct(opt_idx, value, opt_flags, set_sid); @@ -3859,18 +3848,22 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox - if (sandbox > 0 && (flags & P_SECURE)) { + if (sandbox > 0 && (flags & kOptFlagSecure)) { return _(e_sandbox); } - void *varp = get_varp_scope(&(options[opt_idx]), opt_flags); - if (varp == NULL) { - // hidden option is not changed - return NULL; - } + return set_option(opt_idx, optval_copy(value), opt_flags, 0, false, true, errbuf, sizeof(errbuf)); +} - return set_option(opt_idx, varp, optval_copy(value), opt_flags, 0, false, true, errbuf, - sizeof(errbuf)); +/// Unset the local value of a global-local option. +/// +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. +/// +/// @return NULL on success, an untranslated error message on error. +static inline const char *unset_option_local_value(const OptIndex opt_idx) +{ + assert(option_is_global_local(opt_idx)); + return set_option_value(opt_idx, get_option_unset_value(opt_idx), OPT_LOCAL); } /// Set the value of an option. Supports TTY options, unlike set_option_value(). @@ -3918,16 +3911,17 @@ void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_fla /// Switch current context to get/set option value for window/buffer. /// /// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. /// /// @return true if context was switched, false otherwise. -static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from, - Error *err) +static bool switch_option_context(void *const ctx, OptScope req_scope, void *const from, Error *err) { switch (req_scope) { - case kOptReqWin: { + case kOptScopeGlobal: + return false; + case kOptScopeWin: { win_T *const win = (win_T *)from; switchwin_T *const switchwin = (switchwin_T *)ctx; @@ -3947,7 +3941,7 @@ static bool switch_option_context(void *const ctx, OptReqScope req_scope, void * } return true; } - case kOptReqBuf: { + case kOptScopeBuf: { buf_T *const buf = (buf_T *)from; aco_save_T *const aco = (aco_save_T *)ctx; @@ -3957,150 +3951,44 @@ static bool switch_option_context(void *const ctx, OptReqScope req_scope, void * aucmd_prepbuf(aco, buf); return true; } - case kOptReqGlobal: - return false; } UNREACHABLE; } /// Restore context after getting/setting option for window/buffer. See switch_option_context() for /// params. -static void restore_option_context(void *const ctx, OptReqScope req_scope) +static void restore_option_context(void *const ctx, OptScope req_scope) { switch (req_scope) { - case kOptReqWin: + case kOptScopeGlobal: + break; + case kOptScopeWin: restore_win_noblock((switchwin_T *)ctx, true); break; - case kOptReqBuf: + case kOptScopeBuf: aucmd_restbuf((aco_save_T *)ctx); break; - case kOptReqGlobal: - break; } } -/// Get attributes for an option. -/// -/// @param opt_idx Option index in options[] table. -/// -/// @return Option attributes. -/// 0 for hidden or unknown option. -/// See SOPT_* in option_defs.h for other flags. -int get_option_attrs(OptIndex opt_idx) -{ - if (opt_idx == kOptInvalid) { - return 0; - } - - vimoption_T *opt = get_option(opt_idx); - - // Hidden option - if (opt->var == NULL) { - return 0; - } - - int attrs = 0; - - if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { - attrs |= SOPT_GLOBAL; - } - if (opt->indir & PV_WIN) { - attrs |= SOPT_WIN; - } else if (opt->indir & PV_BUF) { - attrs |= SOPT_BUF; - } - - return attrs; -} - -/// Check if option has a value in the requested scope. -/// -/// @param opt_idx Option index in options[] table. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// -/// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) -{ - if (opt_idx == kOptInvalid) { - return false; - } - - vimoption_T *opt = get_option(opt_idx); - - // Hidden option. - if (opt->var == NULL) { - return false; - } - // TTY option. - if (is_tty_option(opt->fullname)) { - return req_scope == kOptReqGlobal; - } - - switch (req_scope) { - case kOptReqGlobal: - return opt->var != VAR_WIN; - case kOptReqBuf: - return opt->indir & PV_BUF; - case kOptReqWin: - return opt->indir & PV_WIN; - } - UNREACHABLE; -} - -/// Get the option value in the requested scope. -/// -/// @param opt_idx Option index in options[] table. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// @param[in] from Pointer to buffer or window for local option value. -/// @param[out] err Error message, if any. -/// -/// @return Option value in the requested scope. Returns a Nil option value if option is not found, -/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or -/// buffer-local value depending on opt_scope). -OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *from, Error *err) -{ - if (opt_idx == kOptInvalid || !option_has_scope(opt_idx, req_scope)) { - return NIL_OPTVAL; - } - - vimoption_T *opt = get_option(opt_idx); - switchwin_T switchwin; - aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); - bool switched = switch_option_context(ctx, req_scope, from, err); - if (ERROR_SET(err)) { - return NIL_OPTVAL; - } - - char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL); - OptVal retv = optval_from_varp(opt_idx, varp); - - if (switched) { - restore_option_context(ctx, req_scope); - } - - return retv; -} - /// Get option value for buffer / window. /// /// @param opt_idx Option index in options[] table. -/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). +/// @param[out] flagsp Set to the option flags (see OptFlags) (if not NULL). /// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). /// @param[out] hidden Whether option is hidden. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. /// /// @return Option value. Must be freed by caller. -OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_scope, - void *const from, Error *err) +OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scope, void *const from, + Error *err) { switchwin_T switchwin; aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin + : (req_scope == kOptScopeBuf ? (void *)&aco : NULL); bool switched = switch_option_context(ctx, req_scope, from, err); if (ERROR_SET(err)) { @@ -4122,17 +4010,17 @@ OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_s /// @param opt_idx Option index in options[] table. /// @param[in] value Option value. /// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags, - const OptReqScope req_scope, void *const from, Error *err) + const OptScope req_scope, void *const from, Error *err) FUNC_ATTR_NONNULL_ARG(1) { switchwin_T switchwin; aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin + : (req_scope == kOptScopeBuf ? (void *)&aco : NULL); bool switched = switch_option_context(ctx, req_scope, from, err); if (ERROR_SET(err)) { @@ -4177,7 +4065,7 @@ static void showoptions(bool all, int opt_flags) // collect the items in items[] int item_count = 0; vimoption_T *opt; - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { opt = &options[opt_idx]; // apply :filter /pat/ if (message_filtered(opt->fullname)) { @@ -4186,7 +4074,7 @@ static void showoptions(bool all, int opt_flags) void *varp = NULL; if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) { - if (opt->indir != PV_NONE) { + if (!option_is_global_only(opt_idx)) { varp = get_varp_scope(opt, opt_flags); } } else { @@ -4247,13 +4135,13 @@ static int optval_default(OptIndex opt_idx, void *varp) { vimoption_T *opt = &options[opt_idx]; - // Hidden or immutable options always use their default value. - if (varp == NULL || opt->hidden || opt->immutable) { + // Hidden options always use their default value. + if (is_option_hidden(opt_idx)) { return true; } OptVal current_val = optval_from_varp(opt_idx, varp); - OptVal default_val = optval_from_varp(opt_idx, &opt->def_val); + OptVal default_val = opt->def_val; return optval_equal(current_val, default_val); } @@ -4261,9 +4149,9 @@ static int optval_default(OptIndex opt_idx, void *varp) /// Send update to UIs with values of UI relevant options void ui_refresh_options(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { uint32_t flags = options[opt_idx].flags; - if (!(flags & P_UI_OPTION)) { + if (!(flags & kOptFlagUIOption)) { continue; } String name = cstr_as_string(options[opt_idx].fullname); @@ -4303,7 +4191,7 @@ static void showoneopt(vimoption_T *opt, int opt_flags) msg_putchar('='); // put value string in NameBuff option_value2string(opt, opt_flags); - msg_outtrans(NameBuff, 0); + msg_outtrans(NameBuff, 0, false); } silent_mode = save_silent; @@ -4338,22 +4226,22 @@ int makeset(FILE *fd, int opt_flags, int local_only) // - Hidden options. // // Do the loop over "options[]" twice: once for options with the - // P_PRI_MKRC flag and once without. + // kOptFlagPriMkrc flag and once without. for (int pri = 1; pri >= 0; pri--) { vimoption_T *opt; - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { opt = &options[opt_idx]; - if (!(opt->flags & P_NO_MKRC) - && ((pri == 1) == ((opt->flags & P_PRI_MKRC) != 0))) { + if (!(opt->flags & kOptFlagNoMkrc) + && ((pri == 1) == ((opt->flags & kOptFlagPriMkrc) != 0))) { // skip global option when only doing locals - if (opt->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) { + if (option_is_global_only(opt_idx) && !(opt_flags & OPT_GLOBAL)) { continue; } // Do not store options like 'bufhidden' and 'syntax' in a vimrc // file, they are always buffer-specific. - if ((opt_flags & OPT_GLOBAL) && (opt->flags & P_NOGLOB)) { + if ((opt_flags & OPT_GLOBAL) && (opt->flags & kOptFlagNoGlob)) { continue; } @@ -4374,21 +4262,19 @@ int makeset(FILE *fd, int opt_flags, int local_only) int round = 2; void *varp_local = NULL; // fresh value - if (opt->indir != PV_NONE) { - if (opt->var == VAR_WIN) { - // skip window-local option when only doing globals - if (!(opt_flags & OPT_LOCAL)) { - continue; - } - // When fresh value of window-local option is not at the - // 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_idx, varp_fresh)) { - round = 1; - varp_local = varp; - varp = varp_fresh; - } + if (option_is_window_local(opt_idx)) { + // skip window-local option when only doing globals + if (!(opt_flags & OPT_LOCAL)) { + continue; + } + // When fresh value of window-local option is not at the + // 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_idx, varp_fresh)) { + round = 1; + varp_local = varp; + varp = varp_fresh; } } } @@ -4403,35 +4289,24 @@ int makeset(FILE *fd, int opt_flags, int local_only) cmd = "setlocal"; } - if (option_has_type(opt_idx, kOptValTypeBoolean)) { - if (put_setbool(fd, cmd, opt->fullname, *(int *)varp) == FAIL) { - return FAIL; - } - } else if (option_has_type(opt_idx, kOptValTypeNumber)) { - if (put_setnum(fd, cmd, opt->fullname, (OptInt *)varp) == FAIL) { + bool do_endif = false; + // Don't set 'syntax' and 'filetype' again if the value is already right, avoids reloading + // the syntax file. + if (opt_idx == kOptSyntax || opt_idx == kOptFiletype) { + if (fprintf(fd, "if &%s != '%s'", opt->fullname, + *(char **)(varp)) < 0 + || put_eol(fd) < 0) { return FAIL; } - } else { // string - bool do_endif = false; - - // Don't set 'syntax' and 'filetype' again if the value is - // already right, avoids reloading the syntax file. - if (opt->indir == PV_SYN || opt->indir == PV_FT) { - if (fprintf(fd, "if &%s != '%s'", opt->fullname, - *(char **)(varp)) < 0 - || put_eol(fd) < 0) { - return FAIL; - } - do_endif = true; - } - if (put_setstring(fd, cmd, opt->fullname, (char **)varp, opt->flags) == FAIL) { + do_endif = true; + } + if (put_set(fd, cmd, opt_idx, varp) == FAIL) { + return FAIL; + } + if (do_endif) { + if (put_line(fd, "endif") == FAIL) { return FAIL; } - if (do_endif) { - if (put_line(fd, "endif") == FAIL) { - return FAIL; - } - } } } } @@ -4444,110 +4319,133 @@ int makeset(FILE *fd, int opt_flags, int local_only) /// 'sessionoptions' or 'viewoptions' contains "folds" but not "options". int makefoldset(FILE *fd) { - if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL - || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0) == FAIL - || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0) == FAIL - || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0) == FAIL - || put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL - || put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL - || put_setnum(fd, "setlocal", "fdn", &curwin->w_p_fdn) == FAIL - || put_setbool(fd, "setlocal", "fen", curwin->w_p_fen) == FAIL) { + if (put_set(fd, "setlocal", kOptFoldmethod, &curwin->w_p_fdm) == FAIL + || put_set(fd, "setlocal", kOptFoldexpr, &curwin->w_p_fde) == FAIL + || put_set(fd, "setlocal", kOptFoldmarker, &curwin->w_p_fmr) == FAIL + || put_set(fd, "setlocal", kOptFoldignore, &curwin->w_p_fdi) == FAIL + || put_set(fd, "setlocal", kOptFoldlevel, &curwin->w_p_fdl) == FAIL + || put_set(fd, "setlocal", kOptFoldminlines, &curwin->w_p_fml) == FAIL + || put_set(fd, "setlocal", kOptFoldnestmax, &curwin->w_p_fdn) == FAIL + || put_set(fd, "setlocal", kOptFoldenable, &curwin->w_p_fen) == FAIL) { return FAIL; } return OK; } -static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_t flags) +/// Print the ":set" command to set a single option to file. +/// +/// @param fd File descriptor. +/// @param cmd Command name. +/// @param opt_idx Option index in options[] table. +/// @param varp Pointer to option variable. +/// +/// @return FAIL on error, OK otherwise. +static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp) { - if (fprintf(fd, "%s %s=", cmd, name) < 0) { - return FAIL; + OptVal value = optval_from_varp(opt_idx, varp); + vimoption_T *opt = &options[opt_idx]; + char *name = opt->fullname; + uint64_t flags = opt->flags; + + if (option_is_global_local(opt_idx) && varp != opt->var + && optval_equal(value, get_option_unset_value(opt_idx))) { + // Processing unset local value of global-local option. Do nothing. + return OK; + } + + switch (value.type) { + case kOptValTypeNil: + abort(); + case kOptValTypeBoolean: { + assert(value.data.boolean != kNone); + bool value_bool = TRISTATE_TO_BOOL(value.data.boolean, false); + + if (fprintf(fd, "%s %s%s", cmd, value_bool ? "" : "no", name) < 0) { + return FAIL; + } + break; + } + case kOptValTypeNumber: { + if (fprintf(fd, "%s %s=", cmd, name) < 0) { + return FAIL; + } + + OptInt value_num = value.data.number; + + OptInt wc; + if (wc_use_keyname(varp, &wc)) { + // print 'wildchar' and 'wildcharm' as a key name + if (fputs(get_special_key_name((int)wc, 0), fd) < 0) { + return FAIL; + } + } else if (fprintf(fd, "%" PRId64, value_num) < 0) { + return FAIL; + } + break; } + case kOptValTypeString: { + if (fprintf(fd, "%s %s=", cmd, name) < 0) { + return FAIL; + } - char *buf = NULL; - char *part = NULL; + char *value_str = value.data.string.data; + char *buf = NULL; + char *part = NULL; - if (*valuep != NULL) { - if ((flags & P_EXPAND) != 0) { - size_t size = (size_t)strlen(*valuep) + 1; + if (value_str != NULL) { + if ((flags & kOptFlagExpand) != 0) { + size_t size = (size_t)strlen(value_str) + 1; - // replace home directory in the whole option value into "buf" - buf = xmalloc(size); - home_replace(NULL, *valuep, buf, size, false); + // replace home directory in the whole option value into "buf" + buf = xmalloc(size); + home_replace(NULL, value_str, buf, size, false); - // If the option value is longer than MAXPATHL, we need to append - // each comma separated part of the option separately, so that it - // can be expanded when read back. - if (size >= MAXPATHL && (flags & P_COMMA) != 0 - && vim_strchr(*valuep, ',') != NULL) { - part = xmalloc(size); + // If the option value is longer than MAXPATHL, we need to append + // each comma separated part of the option separately, so that it + // can be expanded when read back. + if (size >= MAXPATHL && (flags & kOptFlagComma) != 0 + && vim_strchr(value_str, ',') != NULL) { + part = xmalloc(size); - // write line break to clear the option, e.g. ':set rtp=' - if (put_eol(fd) == FAIL) { - goto fail; - } - char *p = buf; - while (*p != NUL) { - // for each comma separated option part, append value to - // the option, :set rtp+=value - if (fprintf(fd, "%s %s+=", cmd, name) < 0) { + // write line break to clear the option, e.g. ':set rtp=' + if (put_eol(fd) == FAIL) { goto fail; } - copy_option_part(&p, part, size, ","); - if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) { - goto fail; + char *p = buf; + while (*p != NUL) { + // for each comma separated option part, append value to + // the option, :set rtp+=value + if (fprintf(fd, "%s %s+=", cmd, name) < 0) { + goto fail; + } + copy_option_part(&p, part, size, ","); + if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) { + goto fail; + } } + xfree(buf); + xfree(part); + return OK; + } + if (put_escstr(fd, buf, 2) == FAIL) { + xfree(buf); + return FAIL; } xfree(buf); - xfree(part); - return OK; - } - if (put_escstr(fd, buf, 2) == FAIL) { - xfree(buf); + } else if (put_escstr(fd, value_str, 2) == FAIL) { return FAIL; } - xfree(buf); - } else if (put_escstr(fd, *valuep, 2) == FAIL) { - return FAIL; } - } - if (put_eol(fd) < 0) { - return FAIL; - } - return OK; -fail: - xfree(buf); - xfree(part); - return FAIL; -} - -static int put_setnum(FILE *fd, char *cmd, char *name, OptInt *valuep) -{ - if (fprintf(fd, "%s %s=", cmd, name) < 0) { - return FAIL; - } - OptInt wc; - if (wc_use_keyname(valuep, &wc)) { - // print 'wildchar' and 'wildcharm' as a key name - if (fputs(get_special_key_name((int)wc, 0), fd) < 0) { - return FAIL; - } - } else if (fprintf(fd, "%" PRId64, (int64_t)(*valuep)) < 0) { + break; + fail: + xfree(buf); + xfree(part); return FAIL; } - if (put_eol(fd) < 0) { - return FAIL; } - return OK; -} -static int put_setbool(FILE *fd, char *cmd, char *name, int value) -{ - if (value < 0) { // global/local option using global value - return OK; - } - if (fprintf(fd, "%s %s%s", cmd, value ? "" : "no", name) < 0 - || put_eol(fd) < 0) { + if (put_eol(fd) < 0) { return FAIL; } return OK; @@ -4555,74 +4453,80 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value) void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) { - if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) { - if (p->var == VAR_WIN) { + OptIndex opt_idx = get_opt_idx(p); + + if ((scope & OPT_GLOBAL) && !option_is_global_only(opt_idx)) { + if (option_is_window_local(opt_idx)) { return GLOBAL_WO(get_varp_from(p, buf, win)); } return p->var; } - if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) { - switch ((int)p->indir) { - case PV_FP: + + if ((scope & OPT_LOCAL) && option_is_global_local(opt_idx)) { + switch (opt_idx) { + case kOptFormatprg: return &(buf->b_p_fp); - case PV_EFM: + case kOptFindfunc: + return &(buf->b_p_ffu); + case kOptErrorformat: return &(buf->b_p_efm); - case PV_GP: + case kOptGrepprg: return &(buf->b_p_gp); - case PV_MP: + case kOptMakeprg: return &(buf->b_p_mp); - case PV_EP: + case kOptEqualprg: return &(buf->b_p_ep); - case PV_KP: + case kOptKeywordprg: return &(buf->b_p_kp); - case PV_PATH: + case kOptPath: return &(buf->b_p_path); - case PV_AR: + case kOptAutoread: return &(buf->b_p_ar); - case PV_TAGS: + case kOptTags: return &(buf->b_p_tags); - case PV_TC: + case kOptTagcase: return &(buf->b_p_tc); - case PV_SISO: + case kOptSidescrolloff: return &(win->w_p_siso); - case PV_SO: + case kOptScrolloff: return &(win->w_p_so); - case PV_DEF: + case kOptDefine: return &(buf->b_p_def); - case PV_INC: + case kOptInclude: return &(buf->b_p_inc); - case PV_COT: + case kOptCompleteopt: return &(buf->b_p_cot); - case PV_DICT: + case kOptDictionary: return &(buf->b_p_dict); - case PV_TSR: + case kOptThesaurus: return &(buf->b_p_tsr); - case PV_TSRFU: + case kOptThesaurusfunc: return &(buf->b_p_tsrfu); - case PV_TFU: + case kOptTagfunc: return &(buf->b_p_tfu); - case PV_SBR: + case kOptShowbreak: return &(win->w_p_sbr); - case PV_STL: + case kOptStatusline: return &(win->w_p_stl); - case PV_WBR: + case kOptWinbar: return &(win->w_p_wbr); - case PV_UL: + case kOptUndolevels: return &(buf->b_p_ul); - case PV_LW: + case kOptLispwords: return &(buf->b_p_lw); - case PV_BKC: + case kOptBackupcopy: return &(buf->b_p_bkc); - case PV_MENC: + case kOptMakeencoding: return &(buf->b_p_menc); - case PV_FCS: + case kOptFillchars: return &(win->w_p_fcs); - case PV_LCS: + case kOptListchars: return &(win->w_p_lcs); - case PV_VE: + case kOptVirtualedit: return &(win->w_p_ve); + default: + abort(); } - return NULL; // "cannot happen" } return get_varp_from(p, buf, win); } @@ -4644,291 +4548,292 @@ void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) { - // hidden option, always return NULL - if (p->var == NULL) { - return NULL; - } + OptIndex opt_idx = get_opt_idx(p); - switch ((int)p->indir) { - case PV_NONE: + // Hidden options and global-only options always use the same var pointer + if (is_option_hidden(opt_idx) || option_is_global_only(opt_idx)) { return p->var; + } + switch (opt_idx) { // global option with local value: use local value if it's been set - case PV_EP: + case kOptEqualprg: return *buf->b_p_ep != NUL ? &buf->b_p_ep : p->var; - case PV_KP: + case kOptKeywordprg: return *buf->b_p_kp != NUL ? &buf->b_p_kp : p->var; - case PV_PATH: + case kOptPath: return *buf->b_p_path != NUL ? &(buf->b_p_path) : p->var; - case PV_AR: + case kOptAutoread: return buf->b_p_ar >= 0 ? &(buf->b_p_ar) : p->var; - case PV_TAGS: + case kOptTags: return *buf->b_p_tags != NUL ? &(buf->b_p_tags) : p->var; - case PV_TC: + case kOptTagcase: return *buf->b_p_tc != NUL ? &(buf->b_p_tc) : p->var; - case PV_SISO: + case kOptSidescrolloff: return win->w_p_siso >= 0 ? &(win->w_p_siso) : p->var; - case PV_SO: + case kOptScrolloff: return win->w_p_so >= 0 ? &(win->w_p_so) : p->var; - case PV_BKC: + case kOptBackupcopy: return *buf->b_p_bkc != NUL ? &(buf->b_p_bkc) : p->var; - case PV_DEF: + case kOptDefine: return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var; - case PV_INC: + case kOptInclude: return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var; - case PV_COT: + case kOptCompleteopt: return *buf->b_p_cot != NUL ? &(buf->b_p_cot) : p->var; - case PV_DICT: + case kOptDictionary: return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var; - case PV_TSR: + case kOptThesaurus: return *buf->b_p_tsr != NUL ? &(buf->b_p_tsr) : p->var; - case PV_TSRFU: + case kOptThesaurusfunc: return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var; - case PV_FP: + case kOptFormatprg: return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var; - case PV_EFM: + case kOptFindfunc: + return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var; + case kOptErrorformat: return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var; - case PV_GP: + case kOptGrepprg: return *buf->b_p_gp != NUL ? &(buf->b_p_gp) : p->var; - case PV_MP: + case kOptMakeprg: return *buf->b_p_mp != NUL ? &(buf->b_p_mp) : p->var; - case PV_SBR: + case kOptShowbreak: return *win->w_p_sbr != NUL ? &(win->w_p_sbr) : p->var; - case PV_STL: + case kOptStatusline: return *win->w_p_stl != NUL ? &(win->w_p_stl) : p->var; - case PV_WBR: + case kOptWinbar: return *win->w_p_wbr != NUL ? &(win->w_p_wbr) : p->var; - case PV_UL: + case kOptUndolevels: return buf->b_p_ul != NO_LOCAL_UNDOLEVEL ? &(buf->b_p_ul) : p->var; - case PV_LW: + case kOptLispwords: return *buf->b_p_lw != NUL ? &(buf->b_p_lw) : p->var; - case PV_MENC: + case kOptMakeencoding: return *buf->b_p_menc != NUL ? &(buf->b_p_menc) : p->var; - case PV_FCS: + case kOptFillchars: return *win->w_p_fcs != NUL ? &(win->w_p_fcs) : p->var; - case PV_LCS: + case kOptListchars: return *win->w_p_lcs != NUL ? &(win->w_p_lcs) : p->var; - case PV_VE: + case kOptVirtualedit: return *win->w_p_ve != NUL ? &win->w_p_ve : p->var; - case PV_ARAB: + case kOptArabic: return &(win->w_p_arab); - case PV_LIST: + case kOptList: return &(win->w_p_list); - case PV_SPELL: + case kOptSpell: return &(win->w_p_spell); - case PV_CUC: + case kOptCursorcolumn: return &(win->w_p_cuc); - case PV_CUL: + case kOptCursorline: return &(win->w_p_cul); - case PV_CULOPT: + case kOptCursorlineopt: return &(win->w_p_culopt); - case PV_CC: + case kOptColorcolumn: return &(win->w_p_cc); - case PV_DIFF: + case kOptDiff: return &(win->w_p_diff); - case PV_FDC: + case kOptFoldcolumn: return &(win->w_p_fdc); - case PV_FEN: + case kOptFoldenable: return &(win->w_p_fen); - case PV_FDI: + case kOptFoldignore: return &(win->w_p_fdi); - case PV_FDL: + case kOptFoldlevel: return &(win->w_p_fdl); - case PV_FDM: + case kOptFoldmethod: return &(win->w_p_fdm); - case PV_FML: + case kOptFoldminlines: return &(win->w_p_fml); - case PV_FDN: + case kOptFoldnestmax: return &(win->w_p_fdn); - case PV_FDE: + case kOptFoldexpr: return &(win->w_p_fde); - case PV_FDT: + case kOptFoldtext: return &(win->w_p_fdt); - case PV_FMR: + case kOptFoldmarker: return &(win->w_p_fmr); - case PV_NU: + case kOptNumber: return &(win->w_p_nu); - case PV_RNU: + case kOptRelativenumber: return &(win->w_p_rnu); - case PV_NUW: + case kOptNumberwidth: return &(win->w_p_nuw); - case PV_WFB: + case kOptWinfixbuf: return &(win->w_p_wfb); - case PV_WFH: + case kOptWinfixheight: return &(win->w_p_wfh); - case PV_WFW: + case kOptWinfixwidth: return &(win->w_p_wfw); - case PV_PVW: + case kOptPreviewwindow: return &(win->w_p_pvw); - case PV_RL: + case kOptRightleft: return &(win->w_p_rl); - case PV_RLC: + case kOptRightleftcmd: return &(win->w_p_rlc); - case PV_SCROLL: + case kOptScroll: return &(win->w_p_scr); - case PV_SMS: + case kOptSmoothscroll: return &(win->w_p_sms); - case PV_WRAP: + case kOptWrap: return &(win->w_p_wrap); - case PV_LBR: + case kOptLinebreak: return &(win->w_p_lbr); - case PV_BRI: + case kOptBreakindent: return &(win->w_p_bri); - case PV_BRIOPT: + case kOptBreakindentopt: return &(win->w_p_briopt); - case PV_SCBIND: + case kOptScrollbind: return &(win->w_p_scb); - case PV_CRBIND: + case kOptCursorbind: return &(win->w_p_crb); - case PV_COCU: + case kOptConcealcursor: return &(win->w_p_cocu); - case PV_COLE: + case kOptConceallevel: return &(win->w_p_cole); - case PV_AI: + case kOptAutoindent: return &(buf->b_p_ai); - case PV_BIN: + case kOptBinary: return &(buf->b_p_bin); - case PV_BOMB: + case kOptBomb: return &(buf->b_p_bomb); - case PV_BH: + case kOptBufhidden: return &(buf->b_p_bh); - case PV_BT: + case kOptBuftype: return &(buf->b_p_bt); - case PV_BL: + case kOptBuflisted: return &(buf->b_p_bl); - case PV_CHANNEL: + case kOptChannel: return &(buf->b_p_channel); - case PV_CI: + case kOptCopyindent: return &(buf->b_p_ci); - case PV_CIN: + case kOptCindent: return &(buf->b_p_cin); - case PV_CINK: + case kOptCinkeys: return &(buf->b_p_cink); - case PV_CINO: + case kOptCinoptions: return &(buf->b_p_cino); - case PV_CINSD: + case kOptCinscopedecls: return &(buf->b_p_cinsd); - case PV_CINW: + case kOptCinwords: return &(buf->b_p_cinw); - case PV_COM: + case kOptComments: return &(buf->b_p_com); - case PV_CMS: + case kOptCommentstring: return &(buf->b_p_cms); - case PV_CPT: + case kOptComplete: return &(buf->b_p_cpt); #ifdef BACKSLASH_IN_FILENAME - case PV_CSL: + case kOptCompleteslash: return &(buf->b_p_csl); #endif - case PV_CFU: + case kOptCompletefunc: return &(buf->b_p_cfu); - case PV_OFU: + case kOptOmnifunc: return &(buf->b_p_ofu); - case PV_URF: + case kOptUserregfunc: return &(buf->b_p_urf); - case PV_EOF: + case kOptEndoffile: return &(buf->b_p_eof); - case PV_EOL: + case kOptEndofline: return &(buf->b_p_eol); - case PV_FIXEOL: + case kOptFixendofline: return &(buf->b_p_fixeol); - case PV_ET: + case kOptExpandtab: return &(buf->b_p_et); - case PV_FENC: + case kOptFileencoding: return &(buf->b_p_fenc); - case PV_FF: + case kOptFileformat: return &(buf->b_p_ff); - case PV_FT: + case kOptFiletype: return &(buf->b_p_ft); - case PV_FO: + case kOptFormatoptions: return &(buf->b_p_fo); - case PV_FLP: + case kOptFormatlistpat: return &(buf->b_p_flp); - case PV_IMI: + case kOptIminsert: return &(buf->b_p_iminsert); - case PV_IMS: + case kOptImsearch: return &(buf->b_p_imsearch); - case PV_INF: + case kOptInfercase: return &(buf->b_p_inf); - case PV_ISK: + case kOptIskeyword: return &(buf->b_p_isk); - case PV_INEX: + case kOptIncludeexpr: return &(buf->b_p_inex); - case PV_INDE: + case kOptIndentexpr: return &(buf->b_p_inde); - case PV_INDK: + case kOptIndentkeys: return &(buf->b_p_indk); - case PV_FEX: + case kOptFormatexpr: return &(buf->b_p_fex); - case PV_LISP: + case kOptLisp: return &(buf->b_p_lisp); - case PV_LOP: + case kOptLispoptions: return &(buf->b_p_lop); - case PV_ML: + case kOptModeline: return &(buf->b_p_ml); - case PV_MPS: + case kOptMatchpairs: return &(buf->b_p_mps); - case PV_MA: + case kOptModifiable: return &(buf->b_p_ma); - case PV_MOD: + case kOptModified: return &(buf->b_changed); - case PV_NF: + case kOptNrformats: return &(buf->b_p_nf); - case PV_PI: + case kOptPreserveindent: return &(buf->b_p_pi); - case PV_QE: + case kOptQuoteescape: return &(buf->b_p_qe); - case PV_RO: + case kOptReadonly: return &(buf->b_p_ro); - case PV_SCBK: + case kOptScrollback: return &(buf->b_p_scbk); - case PV_SI: + case kOptSmartindent: return &(buf->b_p_si); - case PV_STS: + case kOptSofttabstop: return &(buf->b_p_sts); - case PV_SUA: + case kOptSuffixesadd: return &(buf->b_p_sua); - case PV_SWF: + case kOptSwapfile: return &(buf->b_p_swf); - case PV_SMC: + case kOptSynmaxcol: return &(buf->b_p_smc); - case PV_SYN: + case kOptSyntax: return &(buf->b_p_syn); - case PV_SPC: + case kOptSpellcapcheck: return &(win->w_s->b_p_spc); - case PV_SPF: + case kOptSpellfile: return &(win->w_s->b_p_spf); - case PV_SPL: + case kOptSpelllang: return &(win->w_s->b_p_spl); - case PV_SPO: + case kOptSpelloptions: return &(win->w_s->b_p_spo); - case PV_SW: + case kOptShiftwidth: return &(buf->b_p_sw); - case PV_TFU: + case kOptTagfunc: return &(buf->b_p_tfu); - case PV_TS: + case kOptTabstop: return &(buf->b_p_ts); - case PV_TW: + case kOptTextwidth: return &(buf->b_p_tw); - case PV_UDF: + case kOptUndofile: return &(buf->b_p_udf); - case PV_WM: + case kOptWrapmargin: return &(buf->b_p_wm); - case PV_VSTS: + case kOptVarsofttabstop: return &(buf->b_p_vsts); - case PV_VTS: + case kOptVartabstop: return &(buf->b_p_vts); - case PV_KMAP: + case kOptKeymap: return &(buf->b_p_keymap); - case PV_SCL: + case kOptSigncolumn: return &(win->w_p_scl); - case PV_WINHL: + case kOptWinhighlight: return &(win->w_p_winhl); - case PV_WINBL: + case kOptWinblend: return &(win->w_p_winbl); - case PV_STC: + case kOptStatuscolumn: return &(win->w_p_stc); default: iemsg(_("E356: get_varp ERROR")); @@ -4959,6 +4864,15 @@ char *get_equalprg(void) return curbuf->b_p_ep; } +/// Get the value of 'findfunc', either the buffer-local one or the global one. +char *get_findfunc(void) +{ + if (*curbuf->b_p_ffu == NUL) { + return p_ffu; + } + return curbuf->b_p_ffu; +} + /// Copy options from one window to another. /// Used when splitting a window. void win_copy_options(win_T *wp_from, win_T *wp_to) @@ -5110,38 +5024,20 @@ void didset_window_options(win_T *wp, bool valid_cursor) } else { wp->w_skipcol = 0; } - check_colorcolumn(wp); - briopt_check(wp); + check_colorcolumn(NULL, wp); + briopt_check(NULL, wp); fill_culopt_flags(NULL, wp); set_chars_option(wp, wp->w_p_fcs, kFillchars, true, NULL, 0); set_chars_option(wp, wp->w_p_lcs, kListchars, true, NULL, 0); - parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl + parse_winhl_opt(NULL, wp); // sets w_hl_needs_update also for w_p_winbl check_blending(wp); set_winbar_win(wp, false, valid_cursor); - check_signcolumn(wp); + check_signcolumn(NULL, wp); wp->w_grid_alloc.blending = wp->w_p_winbl > 0; } -/// Index into the options table for a buffer-local option enum. -static OptIndex buf_opt_idx[BV_COUNT]; #define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].last_set -/// Initialize buf_opt_idx[] if not done already. -static void init_buf_opt_idx(void) -{ - static bool did_init_buf_opt_idx = false; - - if (did_init_buf_opt_idx) { - return; - } - did_init_buf_opt_idx = true; - for (OptIndex i = 0; i < kOptIndexCount; i++) { - if (options[i].indir & PV_BUF) { - buf_opt_idx[options[i].indir & PV_MASK] = i; - } - } -} - /// Copy global option values to local options for one buffer. /// Used when creating a new buffer and sometimes when entering a buffer. /// flags: @@ -5179,7 +5075,6 @@ void buf_copy_options(buf_T *buf, int flags) if (should_copy || (flags & BCO_ALWAYS)) { CLEAR_FIELD(buf->b_p_script_ctx); - init_buf_opt_idx(); // Don't copy the options specific to a help buffer when // BCO_NOHELP is given or the options were initialized already // (jumping back to a help file with CTRL-T or CTRL-O) @@ -5196,13 +5091,13 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_fenc = xstrdup(p_fenc); switch (*p_ffs) { case 'm': - buf->b_p_ff = xstrdup(FF_MAC); + buf->b_p_ff = xstrdup("mac"); break; case 'd': - buf->b_p_ff = xstrdup(FF_DOS); + buf->b_p_ff = xstrdup("dos"); break; case 'u': - buf->b_p_ff = xstrdup(FF_UNIX); + buf->b_p_ff = xstrdup("unix"); break; default: buf->b_p_ff = xstrdup(p_ff); @@ -5215,63 +5110,63 @@ void buf_copy_options(buf_T *buf, int flags) } buf->b_p_ai = p_ai; - COPY_OPT_SCTX(buf, BV_AI); + COPY_OPT_SCTX(buf, kBufOptAutoindent); buf->b_p_ai_nopaste = p_ai_nopaste; buf->b_p_sw = p_sw; - COPY_OPT_SCTX(buf, BV_SW); + COPY_OPT_SCTX(buf, kBufOptShiftwidth); buf->b_p_scbk = p_scbk; - COPY_OPT_SCTX(buf, BV_SCBK); + COPY_OPT_SCTX(buf, kBufOptScrollback); buf->b_p_tw = p_tw; - COPY_OPT_SCTX(buf, BV_TW); + COPY_OPT_SCTX(buf, kBufOptTextwidth); buf->b_p_tw_nopaste = p_tw_nopaste; buf->b_p_tw_nobin = p_tw_nobin; buf->b_p_wm = p_wm; - COPY_OPT_SCTX(buf, BV_WM); + COPY_OPT_SCTX(buf, kBufOptWrapmargin); buf->b_p_wm_nopaste = p_wm_nopaste; buf->b_p_wm_nobin = p_wm_nobin; buf->b_p_bin = p_bin; - COPY_OPT_SCTX(buf, BV_BIN); + COPY_OPT_SCTX(buf, kBufOptBinary); buf->b_p_bomb = p_bomb; - COPY_OPT_SCTX(buf, BV_BOMB); + COPY_OPT_SCTX(buf, kBufOptBomb); buf->b_p_et = p_et; - COPY_OPT_SCTX(buf, BV_ET); + COPY_OPT_SCTX(buf, kBufOptExpandtab); buf->b_p_fixeol = p_fixeol; - COPY_OPT_SCTX(buf, BV_FIXEOL); + COPY_OPT_SCTX(buf, kBufOptFixendofline); buf->b_p_et_nobin = p_et_nobin; buf->b_p_et_nopaste = p_et_nopaste; buf->b_p_ml = p_ml; - COPY_OPT_SCTX(buf, BV_ML); + COPY_OPT_SCTX(buf, kBufOptModeline); buf->b_p_ml_nobin = p_ml_nobin; buf->b_p_inf = p_inf; - COPY_OPT_SCTX(buf, BV_INF); + COPY_OPT_SCTX(buf, kBufOptInfercase); if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) { buf->b_p_swf = false; } else { buf->b_p_swf = p_swf; - COPY_OPT_SCTX(buf, BV_SWF); + COPY_OPT_SCTX(buf, kBufOptSwapfile); } buf->b_p_cpt = xstrdup(p_cpt); - COPY_OPT_SCTX(buf, BV_CPT); + COPY_OPT_SCTX(buf, kBufOptComplete); #ifdef BACKSLASH_IN_FILENAME buf->b_p_csl = xstrdup(p_csl); - COPY_OPT_SCTX(buf, BV_CSL); + COPY_OPT_SCTX(buf, kBufOptCompleteslash); #endif buf->b_p_cfu = xstrdup(p_cfu); - COPY_OPT_SCTX(buf, BV_CFU); + COPY_OPT_SCTX(buf, kBufOptCompletefunc); set_buflocal_cfu_callback(buf); buf->b_p_ofu = xstrdup(p_ofu); - COPY_OPT_SCTX(buf, BV_OFU); + COPY_OPT_SCTX(buf, kBufOptOmnifunc); buf->b_p_urf = xstrdup(p_urf); - COPY_OPT_SCTX(buf, BV_URF); set_buflocal_ofu_callback(buf); + COPY_OPT_SCTX(buf, kBufOptUserregfunc); buf->b_p_tfu = xstrdup(p_tfu); - COPY_OPT_SCTX(buf, BV_TFU); + COPY_OPT_SCTX(buf, kBufOptTagfunc); set_buflocal_tfu_callback(buf); buf->b_p_sts = p_sts; - COPY_OPT_SCTX(buf, BV_STS); + COPY_OPT_SCTX(buf, kBufOptSofttabstop); buf->b_p_sts_nopaste = p_sts_nopaste; buf->b_p_vsts = xstrdup(p_vsts); - COPY_OPT_SCTX(buf, BV_VSTS); + COPY_OPT_SCTX(buf, kBufOptVarsofttabstop); if (p_vsts && p_vsts != empty_string_option) { tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { @@ -5279,74 +5174,75 @@ void buf_copy_options(buf_T *buf, int flags) } buf->b_p_vsts_nopaste = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : NULL; buf->b_p_com = xstrdup(p_com); - COPY_OPT_SCTX(buf, BV_COM); + COPY_OPT_SCTX(buf, kBufOptComments); buf->b_p_cms = xstrdup(p_cms); - COPY_OPT_SCTX(buf, BV_CMS); + COPY_OPT_SCTX(buf, kBufOptCommentstring); buf->b_p_fo = xstrdup(p_fo); - COPY_OPT_SCTX(buf, BV_FO); + COPY_OPT_SCTX(buf, kBufOptFormatoptions); buf->b_p_flp = xstrdup(p_flp); - COPY_OPT_SCTX(buf, BV_FLP); + COPY_OPT_SCTX(buf, kBufOptFormatlistpat); buf->b_p_nf = xstrdup(p_nf); - COPY_OPT_SCTX(buf, BV_NF); + COPY_OPT_SCTX(buf, kBufOptNrformats); buf->b_p_mps = xstrdup(p_mps); - COPY_OPT_SCTX(buf, BV_MPS); + COPY_OPT_SCTX(buf, kBufOptMatchpairs); buf->b_p_si = p_si; - COPY_OPT_SCTX(buf, BV_SI); + COPY_OPT_SCTX(buf, kBufOptSmartindent); buf->b_p_channel = 0; buf->b_p_ci = p_ci; - COPY_OPT_SCTX(buf, BV_CI); + COPY_OPT_SCTX(buf, kBufOptCopyindent); buf->b_p_cin = p_cin; - COPY_OPT_SCTX(buf, BV_CIN); + COPY_OPT_SCTX(buf, kBufOptCindent); buf->b_p_cink = xstrdup(p_cink); - COPY_OPT_SCTX(buf, BV_CINK); + COPY_OPT_SCTX(buf, kBufOptCinkeys); buf->b_p_cino = xstrdup(p_cino); - COPY_OPT_SCTX(buf, BV_CINO); + COPY_OPT_SCTX(buf, kBufOptCinoptions); buf->b_p_cinsd = xstrdup(p_cinsd); - COPY_OPT_SCTX(buf, BV_CINSD); + COPY_OPT_SCTX(buf, kBufOptCinscopedecls); buf->b_p_lop = xstrdup(p_lop); - COPY_OPT_SCTX(buf, BV_LOP); + COPY_OPT_SCTX(buf, kBufOptLispoptions); // Don't copy 'filetype', it must be detected buf->b_p_ft = empty_string_option; buf->b_p_pi = p_pi; - COPY_OPT_SCTX(buf, BV_PI); + COPY_OPT_SCTX(buf, kBufOptPreserveindent); buf->b_p_cinw = xstrdup(p_cinw); - COPY_OPT_SCTX(buf, BV_CINW); + COPY_OPT_SCTX(buf, kBufOptCinwords); buf->b_p_lisp = p_lisp; - COPY_OPT_SCTX(buf, BV_LISP); + COPY_OPT_SCTX(buf, kBufOptLisp); // Don't copy 'syntax', it must be set buf->b_p_syn = empty_string_option; buf->b_p_smc = p_smc; - COPY_OPT_SCTX(buf, BV_SMC); + COPY_OPT_SCTX(buf, kBufOptSynmaxcol); buf->b_s.b_syn_isk = empty_string_option; buf->b_s.b_p_spc = xstrdup(p_spc); - COPY_OPT_SCTX(buf, BV_SPC); + COPY_OPT_SCTX(buf, kBufOptSpellcapcheck); compile_cap_prog(&buf->b_s); buf->b_s.b_p_spf = xstrdup(p_spf); - COPY_OPT_SCTX(buf, BV_SPF); + COPY_OPT_SCTX(buf, kBufOptSpellfile); buf->b_s.b_p_spl = xstrdup(p_spl); - COPY_OPT_SCTX(buf, BV_SPL); + COPY_OPT_SCTX(buf, kBufOptSpelllang); buf->b_s.b_p_spo = xstrdup(p_spo); - COPY_OPT_SCTX(buf, BV_SPO); + COPY_OPT_SCTX(buf, kBufOptSpelloptions); + buf->b_s.b_p_spo_flags = spo_flags; buf->b_p_inde = xstrdup(p_inde); - COPY_OPT_SCTX(buf, BV_INDE); + COPY_OPT_SCTX(buf, kBufOptIndentexpr); buf->b_p_indk = xstrdup(p_indk); - COPY_OPT_SCTX(buf, BV_INDK); + COPY_OPT_SCTX(buf, kBufOptIndentkeys); buf->b_p_fp = empty_string_option; buf->b_p_fex = xstrdup(p_fex); - COPY_OPT_SCTX(buf, BV_FEX); + COPY_OPT_SCTX(buf, kBufOptFormatexpr); buf->b_p_sua = xstrdup(p_sua); - COPY_OPT_SCTX(buf, BV_SUA); + COPY_OPT_SCTX(buf, kBufOptSuffixesadd); buf->b_p_keymap = xstrdup(p_keymap); - COPY_OPT_SCTX(buf, BV_KMAP); + COPY_OPT_SCTX(buf, kBufOptKeymap); buf->b_kmap_state |= KEYMAP_INIT; // This isn't really an option, but copying the langmap and IME // state from the current buffer is better than resetting it. buf->b_p_iminsert = p_iminsert; - COPY_OPT_SCTX(buf, BV_IMI); + COPY_OPT_SCTX(buf, kBufOptIminsert); buf->b_p_imsearch = p_imsearch; - COPY_OPT_SCTX(buf, BV_IMS); + COPY_OPT_SCTX(buf, kBufOptImsearch); // options that are normally global but also have a local value // are not copied, start using the global value @@ -5358,6 +5254,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_mp = empty_string_option; buf->b_p_efm = empty_string_option; buf->b_p_ep = empty_string_option; + buf->b_p_ffu = empty_string_option; buf->b_p_kp = empty_string_option; buf->b_p_path = empty_string_option; buf->b_p_tags = empty_string_option; @@ -5366,16 +5263,16 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_def = empty_string_option; buf->b_p_inc = empty_string_option; buf->b_p_inex = xstrdup(p_inex); - COPY_OPT_SCTX(buf, BV_INEX); + COPY_OPT_SCTX(buf, kBufOptIncludeexpr); buf->b_p_cot = empty_string_option; buf->b_cot_flags = 0; buf->b_p_dict = empty_string_option; buf->b_p_tsr = empty_string_option; buf->b_p_tsrfu = empty_string_option; buf->b_p_qe = xstrdup(p_qe); - COPY_OPT_SCTX(buf, BV_QE); + COPY_OPT_SCTX(buf, kBufOptQuoteescape); buf->b_p_udf = p_udf; - COPY_OPT_SCTX(buf, BV_UDF); + COPY_OPT_SCTX(buf, kBufOptUndofile); buf->b_p_lw = empty_string_option; buf->b_p_menc = empty_string_option; @@ -5392,12 +5289,12 @@ void buf_copy_options(buf_T *buf, int flags) } } else { buf->b_p_isk = xstrdup(p_isk); - COPY_OPT_SCTX(buf, BV_ISK); + COPY_OPT_SCTX(buf, kBufOptIskeyword); did_isk = true; buf->b_p_ts = p_ts; - COPY_OPT_SCTX(buf, BV_TS); + COPY_OPT_SCTX(buf, kBufOptTabstop); buf->b_p_vts = xstrdup(p_vts); - COPY_OPT_SCTX(buf, BV_VTS); + COPY_OPT_SCTX(buf, kBufOptVartabstop); if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) { tabstop_set(p_vts, &buf->b_p_vts_array); } else { @@ -5408,7 +5305,7 @@ void buf_copy_options(buf_T *buf, int flags) clear_string_option(&buf->b_p_bt); } buf->b_p_ma = p_ma; - COPY_OPT_SCTX(buf, BV_MA); + COPY_OPT_SCTX(buf, kBufOptModifiable); } } @@ -5430,7 +5327,7 @@ void reset_modifiable(void) { curbuf->b_p_ma = false; p_ma = false; - options[kOptModifiable].def_val.boolean = false; + change_option_default(kOptModifiable, BOOLEAN_OPTVAL(false)); } /// Set the global value for 'iminsert' to the local value. @@ -5537,7 +5434,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) } nextchar = *p; opt_idx = find_option_len(arg, (size_t)(p - arg)); - if (opt_idx == kOptInvalid || options[opt_idx].var == NULL) { + if (opt_idx == kOptInvalid || is_option_hidden(opt_idx)) { xp->xp_context = EXPAND_NOTHING; return; } @@ -5613,8 +5510,8 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) // Only string options below - // Options that have P_EXPAND are considered to all use file/dir expansion. - if (flags & P_EXPAND) { + // Options that have kOptFlagExpand are considered to all use file/dir expansion. + if (flags & kOptFlagExpand) { p = options[opt_idx].var; if (p == (char *)&p_bdir || p == (char *)&p_dir @@ -5638,7 +5535,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) xp->xp_backslash = XP_BS_ONE; } } - if (flags & P_COMMA) { + if (flags & kOptFlagComma) { xp->xp_backslash |= XP_BS_COMMA; } } @@ -5648,21 +5545,21 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) // pattern, while accounting for backslash-escaped space/commas/colons. // Triple-backslashed escaped file names (e.g. 'path') can also be // delimited by space. - if ((flags & P_EXPAND) || (flags & P_COMMA) || (flags & P_COLON)) { + if ((flags & kOptFlagExpand) || (flags & kOptFlagComma) || (flags & kOptFlagColon)) { for (p = argend - 1; p > xp->xp_pattern; p--) { // count number of backslashes before ' ' or ',' - if (*p == ' ' || *p == ',' || (*p == ':' && (flags & P_COLON))) { + if (*p == ' ' || *p == ',' || (*p == ':' && (flags & kOptFlagColon))) { char *s = p; while (s > xp->xp_pattern && *(s - 1) == '\\') { s--; } if ((*p == ' ' && ((xp->xp_backslash & XP_BS_THREE) && (p - s) < 3)) #if defined(BACKSLASH_IN_FILENAME) - || (*p == ',' && (flags & P_COMMA) && (p - s) < 1) + || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 1) #else - || (*p == ',' && (flags & P_COMMA) && (p - s) < 2) + || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 2) #endif - || (*p == ':' && (flags & P_COLON))) { + || (*p == ':' && (flags & kOptFlagColon))) { xp->xp_pattern = p + 1; break; } @@ -5672,7 +5569,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) // An option that is a list of single-character flags should always start // at the end as we don't complete words. - if (flags & P_FLAGLIST) { + if (flags & kOptFlagFlagList) { xp->xp_pattern = argend; } @@ -5759,9 +5656,9 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM } } char *str; - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { str = options[opt_idx].fullname; - if (options[opt_idx].var == NULL) { + if (is_option_hidden(opt_idx)) { continue; } if (xp->xp_context == EXPAND_BOOL_SETTINGS @@ -5825,7 +5722,7 @@ static char *escape_option_str_cmdline(char *var) for (var = buf; *var != NUL; MB_PTR_ADV(var)) { if (var[0] == '\\' && var[1] == '\\' && expand_option_idx != kOptInvalid - && (options[expand_option_idx].flags & P_EXPAND) + && (options[expand_option_idx].flags & kOptFlagExpand) && vim_isfilec((uint8_t)var[2]) && (var[2] != '\\' || (var == buf && var[4] != '\\'))) { STRMOVE(var, var + 1); @@ -5911,11 +5808,11 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c if (option_has_type(expand_option_idx, kOptValTypeNumber)) { return ExpandOldSetting(numMatches, matches); - } else if (option_flags & P_COMMA) { + } else if (option_flags & kOptFlagComma) { // Split the option by comma, then present each option to the user if // it matches the pattern. // This condition needs to go first, because 'whichwrap' has both - // P_COMMA and P_FLAGLIST. + // kOptFlagComma and kOptFlagFlagList. if (*option_val == NUL) { return FAIL; @@ -5962,7 +5859,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c *matches = ga.ga_data; *numMatches = ga.ga_len; return OK; - } else if (option_flags & P_FLAGLIST) { + } else if (option_flags & kOptFlagFlagList) { // Only present the flags that are set on the option as the other flags // are not meaningful to do set-= on. @@ -6009,6 +5906,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c static void option_value2string(vimoption_T *opt, int scope) { void *varp = get_varp_scope(opt, scope); + assert(varp != NULL); if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) { OptInt wc = 0; @@ -6025,9 +5923,8 @@ static void option_value2string(vimoption_T *opt, int scope) } } else { // string varp = *(char **)(varp); - if (varp == NULL) { // Just in case. - NameBuff[0] = NUL; - } else if (opt->flags & P_EXPAND) { + + if (opt->flags & kOptFlagExpand) { home_replace(NULL, varp, NameBuff, MAXPATHL, false); } else { xstrlcpy(NameBuff, varp, MAXPATHL); @@ -6088,7 +5985,7 @@ void vimrc_found(char *fname, char *envname) bool option_was_set(OptIndex opt_idx) { assert(opt_idx != kOptInvalid); - return options[opt_idx].flags & P_WAS_SET; + return options[opt_idx].flags & kOptFlagWasSet; } /// Reset the flag indicating option "name" was set. @@ -6097,7 +5994,7 @@ bool option_was_set(OptIndex opt_idx) void reset_option_was_set(OptIndex opt_idx) { assert(opt_idx != kOptInvalid); - options[opt_idx].flags &= ~P_WAS_SET; + options[opt_idx].flags &= ~(unsigned)kOptFlagWasSet; } /// fill_culopt_flags() -- called when 'culopt' changes value @@ -6331,13 +6228,13 @@ void set_fileformat(int eol_style, int opt_flags) switch (eol_style) { case EOL_UNIX: - p = FF_UNIX; + p = "unix"; break; case EOL_MAC: - p = FF_MAC; + p = "mac"; break; case EOL_DOS: - p = FF_DOS; + p = "dos"; break; } @@ -6420,11 +6317,11 @@ dict_T *get_winbuf_options(const int bufopt) { dict_T *const d = tv_dict_alloc(); - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { vimoption_T *opt = &options[opt_idx]; - if ((bufopt && (opt->indir & PV_BUF)) - || (!bufopt && (opt->indir & PV_WIN))) { + if ((bufopt && (option_has_scope(opt_idx, kOptScopeBuf))) + || (!bufopt && (option_has_scope(opt_idx, kOptScopeWin)))) { void *varp = get_varp(opt); if (varp != NULL) { @@ -6467,8 +6364,8 @@ Dict get_vimoption(String name, int scope, buf_T *buf, win_T *win, Arena *arena, Dict get_all_vimoptions(Arena *arena) { - Dict retval = arena_dict(arena, kOptIndexCount); - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + Dict retval = arena_dict(arena, kOptCount); + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { Dict opt_dict = vimoption2dict(&options[opt_idx], OPT_GLOBAL, curbuf, curwin, arena); PUT_C(retval, options[opt_idx].fullname, DICT_OBJ(opt_dict)); } @@ -6477,15 +6374,16 @@ Dict get_all_vimoptions(Arena *arena) static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win, Arena *arena) { + OptIndex opt_idx = get_opt_idx(opt); Dict dict = arena_dict(arena, 13); PUT_C(dict, "name", CSTR_AS_OBJ(opt->fullname)); PUT_C(dict, "shortname", CSTR_AS_OBJ(opt->shortname)); const char *scope; - if (opt->indir & PV_BUF) { + if (option_has_scope(opt_idx, kOptScopeBuf)) { scope = "buf"; - } else if (opt->indir & PV_WIN) { + } else if (option_has_scope(opt_idx, kOptScopeWin)) { scope = "win"; } else { scope = "global"; @@ -6494,22 +6392,22 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w PUT_C(dict, "scope", CSTR_AS_OBJ(scope)); // welcome to the jungle - PUT_C(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH)); - PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & P_COMMA)); - PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & P_FLAGLIST)); + PUT_C(dict, "global_local", BOOLEAN_OBJ(option_is_global_local(opt_idx))); + PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & kOptFlagComma)); + PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & kOptFlagFlagList)); - PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & P_WAS_SET)); + PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & kOptFlagWasSet)); LastSet last_set = { .channel_id = 0 }; if (req_scope == OPT_GLOBAL) { last_set = opt->last_set; } else { // Scope is either OPT_LOCAL or a fallback mode was requested. - if (opt->indir & PV_BUF) { - last_set = buf->b_p_script_ctx[opt->indir & PV_MASK]; + if (option_has_scope(opt_idx, kOptScopeBuf)) { + last_set = buf->b_p_script_ctx[opt->scope_idx[kOptScopeBuf]]; } - if (opt->indir & PV_WIN) { - last_set = win->w_p_script_ctx[opt->indir & PV_MASK]; + if (option_has_scope(opt_idx, kOptScopeWin)) { + last_set = win->w_p_script_ctx[opt->scope_idx[kOptScopeWin]]; } if (req_scope != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) { last_set = opt->last_set; @@ -6520,33 +6418,9 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w PUT_C(dict, "last_set_linenr", INTEGER_OBJ(last_set.script_ctx.sc_lnum)); PUT_C(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set.channel_id)); - // TODO(bfredl): do you even nocp? - OptVal def = optval_from_varp(get_opt_idx(opt), &opt->def_val); - - PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(def.type))); - PUT_C(dict, "default", optval_as_object(def)); - PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP))); + PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(option_get_type(get_opt_idx(opt))))); + PUT_C(dict, "default", optval_as_object(opt->def_val)); + PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & kOptFlagNoDup))); return dict; } - -/// Check if option is multitype (supports multiple types). -static 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); -} - -/// Check if option supports a specific type. -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); -} |