mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	refactor(options)!: unify set_option and set_string_option
				
					
				
			While the interfaces for setting number and boolean options are now unified by #25394, there is still a separate `set_string_option` function that is used for setting a string option. This PR removes that function and merges it with set_option. BREAKING CHANGE: `v:option_old` is now the old global value for all global-local options, instead of just string global-local options. Local value for a global-local number/boolean option is now unset when the option is set (e.g. using `:set` or `nvim_set_option_value`) without a scope, which means they now behave the same way as string options. Ref: #25672
This commit is contained in:
		@@ -778,11 +778,10 @@ OptionSet			After setting an option (except during
 | 
			
		||||
				This does not set |<abuf>|, you could use
 | 
			
		||||
				|bufnr()|.
 | 
			
		||||
 | 
			
		||||
				Note that when setting a |global-local| string
 | 
			
		||||
				option with |:set|, then |v:option_old| is the
 | 
			
		||||
				old global value. However, for all other kinds
 | 
			
		||||
				of options (local string options, global-local
 | 
			
		||||
				number options, ...) it is the old local
 | 
			
		||||
				Note that when setting a |global-local| option
 | 
			
		||||
				with |:set|, then |v:option_old| is the old
 | 
			
		||||
				global value. However, for all options that
 | 
			
		||||
				are not global-local it is the old local
 | 
			
		||||
				value.
 | 
			
		||||
 | 
			
		||||
				OptionSet is not triggered on startup and for
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,12 @@ The following changes may require adaptations in user config or plugins.
 | 
			
		||||
 | 
			
		||||
• |OptionSet| autocommand args |v:option_new|, |v:option_old|,
 | 
			
		||||
  |v:option_oldlocal|, |v:option_oldglobal| now have the type of the option
 | 
			
		||||
  instead of always being strings.
 | 
			
		||||
  instead of always being strings. |v:option_old| is now the old global value
 | 
			
		||||
  for all global-local options, instead of just string global-local options.
 | 
			
		||||
 | 
			
		||||
• Local value for a global-local number/boolean option is now unset when
 | 
			
		||||
  the option is set (e.g. using |:set| or |nvim_set_option_value()|) without a
 | 
			
		||||
  scope, which means they now behave the same way as string options.
 | 
			
		||||
 | 
			
		||||
==============================================================================
 | 
			
		||||
NEW FEATURES                                                    *news-features*
 | 
			
		||||
 
 | 
			
		||||
@@ -289,6 +289,10 @@ Normal commands:
 | 
			
		||||
  |Q| replays the last recorded macro instead of switching to Ex mode (|gQ|).
 | 
			
		||||
 | 
			
		||||
Options:
 | 
			
		||||
  Local values for global-local number/boolean options are unset when the
 | 
			
		||||
  option is set without a scope (e.g. by using |:set|), similarly to how
 | 
			
		||||
  global-local string options work.
 | 
			
		||||
 | 
			
		||||
  'autoread'    works in the terminal (if it supports "focus" events)
 | 
			
		||||
  'cpoptions'   flags: |cpo-_|
 | 
			
		||||
  'diffopt'     "linematch" feature
 | 
			
		||||
@@ -381,7 +385,8 @@ Variables:
 | 
			
		||||
  |v:windowid| is always available (for use by external UIs)
 | 
			
		||||
  |OptionSet| autocommand args |v:option_new|, |v:option_old|,
 | 
			
		||||
  |v:option_oldlocal|, |v:option_oldglobal| have the type of the option
 | 
			
		||||
  instead of always being strings.
 | 
			
		||||
  instead of always being strings. |v:option_old| is now the old global value
 | 
			
		||||
  for all global-local options, instead of just string global-local options.
 | 
			
		||||
 | 
			
		||||
Vimscript:
 | 
			
		||||
  |:redir| nested in |execute()| works.
 | 
			
		||||
 
 | 
			
		||||
@@ -681,21 +681,6 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope,
 | 
			
		||||
    return;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (value.type == kObjectTypeNil) {
 | 
			
		||||
    if (req_scope == kOptReqGlobal) {
 | 
			
		||||
      api_set_error(err, kErrorTypeException, "Cannot unset option '%s'", name.data);
 | 
			
		||||
      return;
 | 
			
		||||
    } else if (!(flags & SOPT_GLOBAL)) {
 | 
			
		||||
      api_set_error(err, kErrorTypeException,
 | 
			
		||||
                    "Cannot unset option '%s' because it doesn't have a global value",
 | 
			
		||||
                    name.data);
 | 
			
		||||
      return;
 | 
			
		||||
    } else {
 | 
			
		||||
      unset_global_local_option(name.data, to);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool error = false;
 | 
			
		||||
  OptVal optval = object_as_optval(value, &error);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7229,12 +7229,11 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
 | 
			
		||||
/// Set v:variable to tv.
 | 
			
		||||
///
 | 
			
		||||
/// @param[in]  idx  Index of variable to set.
 | 
			
		||||
/// @param[in,out]  val  Value to set to. Reference count will be incremented.
 | 
			
		||||
///                      Also keys of the dictionary will be made read-only.
 | 
			
		||||
/// @param[in]  val  Value to set to. Will be copied.
 | 
			
		||||
void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv)
 | 
			
		||||
{
 | 
			
		||||
  tv_clear(&vimvars[idx].vv_di.di_tv);
 | 
			
		||||
  vimvars[idx].vv_di.di_tv = *tv;
 | 
			
		||||
  tv_copy(tv, &vimvars[idx].vv_di.di_tv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the v:argv list.
 | 
			
		||||
 
 | 
			
		||||
@@ -574,14 +574,14 @@ void free_all_options(void)
 | 
			
		||||
    if (options[i].indir == PV_NONE) {
 | 
			
		||||
      // global option: free value and default value.
 | 
			
		||||
      if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) {
 | 
			
		||||
        free_string_option(*(char **)options[i].var);
 | 
			
		||||
        optval_free(optval_from_varp(i, options[i].var));
 | 
			
		||||
      }
 | 
			
		||||
      if (options[i].flags & P_DEF_ALLOCED) {
 | 
			
		||||
        free_string_option(options[i].def_val);
 | 
			
		||||
        optval_free(optval_from_varp(i, &options[i].def_val));
 | 
			
		||||
      }
 | 
			
		||||
    } else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) {
 | 
			
		||||
    } else if (options[i].var != VAR_WIN) {
 | 
			
		||||
      // buffer-local option: free global value
 | 
			
		||||
      clear_string_option((char **)options[i].var);
 | 
			
		||||
      optval_free(optval_from_varp(i, options[i].var));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  free_operatorfunc_option();
 | 
			
		||||
@@ -1142,8 +1142,11 @@ static OptVal get_option_newval(int opt_idx, int opt_flags, set_prefix_T prefix,
 | 
			
		||||
{
 | 
			
		||||
  assert(varp != NULL);
 | 
			
		||||
 | 
			
		||||
  vimoption_T *opt = &options[opt_idx];
 | 
			
		||||
  char *arg = *argp;
 | 
			
		||||
  OptVal oldval = optval_from_varp(opt_idx, varp);
 | 
			
		||||
  // 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);
 | 
			
		||||
  OptVal oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp);
 | 
			
		||||
  OptVal newval = NIL_OPTVAL;
 | 
			
		||||
 | 
			
		||||
  switch (oldval.type) {
 | 
			
		||||
@@ -1250,17 +1253,7 @@ static OptVal get_option_newval(int opt_idx, int opt_flags, set_prefix_T prefix,
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case kOptValTypeString: {
 | 
			
		||||
    char *oldval_str;
 | 
			
		||||
    vimoption_T *opt = get_option(opt_idx);
 | 
			
		||||
 | 
			
		||||
    // When setting the local value of a global option, the old value may be
 | 
			
		||||
    // the global value.
 | 
			
		||||
    if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
 | 
			
		||||
      oldval_str = *(char **)get_varp(opt);
 | 
			
		||||
    } else {
 | 
			
		||||
      oldval_str = *(char **)varp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *oldval_str = oldval.data.string.data;
 | 
			
		||||
    // Get the new value for the option
 | 
			
		||||
    char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op, flags);
 | 
			
		||||
    newval = CSTR_AS_OPTVAL(newval_str);
 | 
			
		||||
@@ -1416,8 +1409,6 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *errmsg = set_option(opt_idx, varp, newval, opt_flags, op == OP_NONE, errbuf, errbuflen);
 | 
			
		||||
  // `set_option` copies the new option value, so it needs to be freed here.
 | 
			
		||||
  optval_free(newval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse 'arg' for option settings.
 | 
			
		||||
@@ -2753,6 +2744,47 @@ static const char *did_set_wrap(optset_T *args)
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When 'syntax' is set, load the syntax of that name
 | 
			
		||||
static void do_syntax_autocmd(buf_T *buf, bool value_changed)
 | 
			
		||||
{
 | 
			
		||||
  static int syn_recursive = 0;
 | 
			
		||||
 | 
			
		||||
  syn_recursive++;
 | 
			
		||||
  // Only pass true for "force" when the value changed or not used
 | 
			
		||||
  // recursively, to avoid endless recurrence.
 | 
			
		||||
  apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname,
 | 
			
		||||
                 value_changed || syn_recursive == 1, buf);
 | 
			
		||||
  buf->b_flags |= BF_SYN_SET;
 | 
			
		||||
  syn_recursive--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_spelllang_source(win_T *win)
 | 
			
		||||
{
 | 
			
		||||
  char fname[200];
 | 
			
		||||
  char *q = win->w_s->b_p_spl;
 | 
			
		||||
 | 
			
		||||
  // Skip the first name if it is "cjk".
 | 
			
		||||
  if (strncmp(q, "cjk,", 4) == 0) {
 | 
			
		||||
    q += 4;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Source the spell/LANG.{vim,lua} in 'runtimepath'.
 | 
			
		||||
  // They could set 'spellcapcheck' depending on the language.
 | 
			
		||||
  // Use the first name in 'spelllang' up to '_region' or
 | 
			
		||||
  // '.encoding'.
 | 
			
		||||
  char *p;
 | 
			
		||||
  for (p = q; *p != NUL; p++) {
 | 
			
		||||
    if (!ASCII_ISALNUM(*p) && *p != '-') {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (p > q) {
 | 
			
		||||
    vim_snprintf(fname, sizeof(fname), "spell/%.*s.*", (int)(p - q), q);
 | 
			
		||||
    source_runtime_vim_lua(fname, DIP_ALL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check the bounds of numeric options.
 | 
			
		||||
static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, char *errbuf,
 | 
			
		||||
                                           size_t errbuflen, const char *errmsg)
 | 
			
		||||
@@ -3193,7 +3225,10 @@ void optval_free(OptVal o)
 | 
			
		||||
  case kOptValTypeNumber:
 | 
			
		||||
    break;
 | 
			
		||||
  case kOptValTypeString:
 | 
			
		||||
    api_free_string(o.data.string);
 | 
			
		||||
    // Don't free empty string option
 | 
			
		||||
    if (o.data.string.data != empty_string_option) {
 | 
			
		||||
      api_free_string(o.data.string);
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -3271,11 +3306,19 @@ OptVal optval_from_varp(int opt_idx, void *varp)
 | 
			
		||||
 | 
			
		||||
/// Set option var pointer value from Optval.
 | 
			
		||||
///
 | 
			
		||||
/// @param  varp   Pointer to option variable.
 | 
			
		||||
/// @param  value  Option value.
 | 
			
		||||
static void set_option_varp(void *varp, OptVal value)
 | 
			
		||||
  FUNC_ATTR_NONNULL_ARG(1)
 | 
			
		||||
/// @param       opt_idx      Option index in options[] table.
 | 
			
		||||
/// @param[out]  varp         Pointer to option variable.
 | 
			
		||||
/// @param[in]   value        New option value.
 | 
			
		||||
/// @param       free_oldval  Free old value.
 | 
			
		||||
static void set_option_varp(int opt_idx, void *varp, OptVal value, bool free_oldval)
 | 
			
		||||
  FUNC_ATTR_NONNULL_ARG(2)
 | 
			
		||||
{
 | 
			
		||||
  assert(optval_match_type(value, opt_idx));
 | 
			
		||||
 | 
			
		||||
  if (free_oldval) {
 | 
			
		||||
    optval_free(optval_from_varp(opt_idx, varp));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (value.type) {
 | 
			
		||||
  case kOptValTypeNil:
 | 
			
		||||
    return;
 | 
			
		||||
@@ -3356,43 +3399,36 @@ OptVal object_as_optval(Object o, bool *error)
 | 
			
		||||
  UNREACHABLE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Clear an option value.
 | 
			
		||||
/// Unset the local value of an option. The exact semantics of this depend on the option.
 | 
			
		||||
/// TODO(famiu): Remove this once we have a dedicated OptVal type for unset local options.
 | 
			
		||||
///
 | 
			
		||||
/// The exact semantics of this depend on the option.
 | 
			
		||||
static OptVal optval_clear(const char *name, uint32_t flags, void *varp, buf_T *buf, win_T *win)
 | 
			
		||||
/// @param      opt_idx  Option index in options[] table.
 | 
			
		||||
/// @param[in]  varp  Pointer to option variable.
 | 
			
		||||
///
 | 
			
		||||
/// @return [allocated] Option value equal to the unset value for the option.
 | 
			
		||||
static OptVal optval_unset_local(int opt_idx, void *varp)
 | 
			
		||||
{
 | 
			
		||||
  OptVal v = NIL_OPTVAL;
 | 
			
		||||
 | 
			
		||||
  // Change the type of the OptVal to the type used by the option so that it can be cleared.
 | 
			
		||||
  // TODO(famiu): Clean up all of this after set_(num|bool|string)_option() is unified.
 | 
			
		||||
 | 
			
		||||
  if (flags & P_BOOL) {
 | 
			
		||||
    v.type = kOptValTypeBoolean;
 | 
			
		||||
    if ((int *)varp == &buf->b_p_ar) {
 | 
			
		||||
      // TODO(lewis6991): replace this with a more general condition that
 | 
			
		||||
      // indicates we are setting the local value of a global-local option
 | 
			
		||||
      v.data.boolean = kNone;
 | 
			
		||||
    } else {
 | 
			
		||||
      v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
 | 
			
		||||
  vimoption_T *opt = &options[opt_idx];
 | 
			
		||||
  // For global-local options, use the unset value of the local value.
 | 
			
		||||
  if (opt->indir & PV_BOTH) {
 | 
			
		||||
    // String global-local options always use an empty string for the unset value.
 | 
			
		||||
    if (opt->flags & P_STRING) {
 | 
			
		||||
      return STATIC_CSTR_TO_OPTVAL("");
 | 
			
		||||
    }
 | 
			
		||||
  } else if (flags & P_NUM) {
 | 
			
		||||
    v.type = kOptValTypeNumber;
 | 
			
		||||
    if ((OptInt *)varp == &curbuf->b_p_ul) {
 | 
			
		||||
      // The one true special case
 | 
			
		||||
      v.data.number = NO_LOCAL_UNDOLEVEL;
 | 
			
		||||
    } else if ((OptInt *)varp == &win->w_p_so || (OptInt *)varp == &win->w_p_siso) {
 | 
			
		||||
      // TODO(lewis6991): replace this with a more general condition that
 | 
			
		||||
      // indicates we are setting the local value of a global-local option
 | 
			
		||||
      v.data.number = -1;
 | 
			
		||||
 | 
			
		||||
    if ((int *)varp == &curbuf->b_p_ar) {
 | 
			
		||||
      return BOOLEAN_OPTVAL(kNone);
 | 
			
		||||
    } else if ((OptInt *)varp == &curwin->w_p_so || (OptInt *)varp == &curwin->w_p_siso) {
 | 
			
		||||
      return NUMBER_OPTVAL(-1);
 | 
			
		||||
    } else if ((OptInt *)varp == &curbuf->b_p_ul) {
 | 
			
		||||
      return NUMBER_OPTVAL(NO_LOCAL_UNDOLEVEL);
 | 
			
		||||
    } else {
 | 
			
		||||
      v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
 | 
			
		||||
      // This should never happen.
 | 
			
		||||
      abort();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (flags & P_STRING) {
 | 
			
		||||
    v.type = kOptValTypeString;
 | 
			
		||||
    v.data.string.data = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return v;
 | 
			
		||||
  // For options that aren't global-local, just set the local value to the global value.
 | 
			
		||||
  return get_option_value(opt->fullname, NULL, OPT_GLOBAL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get an allocated string containing a list of valid types for an option.
 | 
			
		||||
@@ -3485,36 +3521,254 @@ vimoption_T *get_option(int opt_idx)
 | 
			
		||||
  return &options[opt_idx];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check if local value of global-local option is unset for current buffer / window.
 | 
			
		||||
/// Always returns false for options that aren't global-local.
 | 
			
		||||
///
 | 
			
		||||
/// TODO(famiu): Remove this once we have an OptVal type to indicate an unset local value.
 | 
			
		||||
static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win)
 | 
			
		||||
{
 | 
			
		||||
  // Local value of option that isn't global-local is always considered set.
 | 
			
		||||
  if (!((int)opt->indir & PV_BOTH)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Get pointer to local value in varp_local, and a pointer to the currently used value in varp.
 | 
			
		||||
  // If the local value is the one currently being used, that indicates that it's set.
 | 
			
		||||
  // Otherwise it indicates the local value is unset.
 | 
			
		||||
  void *varp = get_varp_from(opt, buf, win);
 | 
			
		||||
  void *varp_local = get_varp_scope_from(opt, OPT_LOCAL, buf, win);
 | 
			
		||||
 | 
			
		||||
  return varp != varp_local;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Handle side-effects of setting an option.
 | 
			
		||||
///
 | 
			
		||||
/// @param       opt_idx         Index in options[] table. Must be >= 0.
 | 
			
		||||
/// @param[in]   varp            Option variable pointer, cannot be NULL.
 | 
			
		||||
/// @param       old_value       Old option value.
 | 
			
		||||
/// @param       new_value       New option value.
 | 
			
		||||
/// @param       opt_flags       Option flags.
 | 
			
		||||
/// @param[out]  doskip          Whether option should be processed further.
 | 
			
		||||
/// @param[out]  value_checked   Value was checked to be safe, no need to set P_INSECURE.
 | 
			
		||||
/// @param       value_replaced  Value was replaced completely.
 | 
			
		||||
/// @param[out]  errbuf          Buffer for error message.
 | 
			
		||||
/// @param       errbuflen       Length of error buffer.
 | 
			
		||||
///
 | 
			
		||||
/// @return  NULL on success, an untranslated error message on error.
 | 
			
		||||
static const char *did_set_option(int opt_idx, void *varp, OptVal old_value, OptVal new_value,
 | 
			
		||||
                                  int opt_flags, bool *doskip, bool *value_checked,
 | 
			
		||||
                                  bool value_replaced, char *errbuf, size_t errbuflen)
 | 
			
		||||
{
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  opt_did_set_cb_T did_set_cb;
 | 
			
		||||
  optset_T did_set_cb_args = {
 | 
			
		||||
    .os_varp = varp,
 | 
			
		||||
    .os_idx = opt_idx,
 | 
			
		||||
    .os_flags = opt_flags,
 | 
			
		||||
    .os_oldval = old_value.data,
 | 
			
		||||
    .os_newval = new_value.data,
 | 
			
		||||
    .os_value_checked = false,
 | 
			
		||||
    .os_value_changed = false,
 | 
			
		||||
    .os_restore_chartab = false,
 | 
			
		||||
    .os_doskip = false,
 | 
			
		||||
    .os_errbuf = errbuf,
 | 
			
		||||
    .os_errbuflen = errbuflen,
 | 
			
		||||
    .os_buf = curbuf,
 | 
			
		||||
    .os_win = curwin
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if ((int *)varp == &p_force_on) {
 | 
			
		||||
    did_set_cb = did_set_force_on;
 | 
			
		||||
  } else if ((int *)varp == &p_force_off) {
 | 
			
		||||
    did_set_cb = did_set_force_off;
 | 
			
		||||
  } else {
 | 
			
		||||
    did_set_cb = opt->opt_did_set_cb;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Disallow changing some options from secure mode
 | 
			
		||||
  if ((secure || sandbox != 0) && (opt->flags & P_SECURE)) {
 | 
			
		||||
    errmsg = e_secure;
 | 
			
		||||
    // Check for a "normal" directory or file name in some string options.
 | 
			
		||||
  } else if (new_value.type == kOptValTypeString
 | 
			
		||||
             && check_illegal_path_names(*(char **)varp, opt->flags)) {
 | 
			
		||||
    errmsg = e_invarg;
 | 
			
		||||
  } else if (did_set_cb != NULL) {
 | 
			
		||||
    // Invoke the option specific callback function to validate and apply the new value.
 | 
			
		||||
    errmsg = did_set_cb(&did_set_cb_args);
 | 
			
		||||
    // Whether option should be processed further or skipped.
 | 
			
		||||
    *doskip = did_set_cb_args.os_doskip;
 | 
			
		||||
    // The 'filetype' and 'syntax' option callback functions may change the os_value_changed field.
 | 
			
		||||
    value_changed = did_set_cb_args.os_value_changed;
 | 
			
		||||
    // The 'keymap', 'filetype' and 'syntax' option callback functions may change the
 | 
			
		||||
    // os_value_checked field.
 | 
			
		||||
    *value_checked = did_set_cb_args.os_value_checked;
 | 
			
		||||
    // The 'isident', 'iskeyword', 'isprint' and 'isfname' options may change the character table.
 | 
			
		||||
    // On failure, this needs to be restored.
 | 
			
		||||
    restore_chartab = did_set_cb_args.os_restore_chartab;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If an error is detected, restore the previous value.
 | 
			
		||||
  if (errmsg != NULL) {
 | 
			
		||||
    set_option_varp(opt_idx, varp, old_value, true);
 | 
			
		||||
    // When resetting some values, need to act on it.
 | 
			
		||||
    if (restore_chartab) {
 | 
			
		||||
      (void)buf_init_chartab(curbuf, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Unset new_value as it is no longer valid.
 | 
			
		||||
    new_value = NIL_OPTVAL;  // NOLINT(clang-analyzer-deadcode.DeadStores)
 | 
			
		||||
  } else {
 | 
			
		||||
    // Re-assign the new value as its value may get freed or modified by the option callback.
 | 
			
		||||
    new_value = optval_from_varp(opt_idx, varp);
 | 
			
		||||
 | 
			
		||||
    // Remember where the option was set.
 | 
			
		||||
    set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
 | 
			
		||||
    // 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;
 | 
			
		||||
 | 
			
		||||
    // Check the bound for num options.
 | 
			
		||||
    if (new_value.type == kOptValTypeNumber) {
 | 
			
		||||
      errmsg = check_num_option_bounds((OptInt *)varp, old_value.data.number, errbuf, errbuflen,
 | 
			
		||||
                                       errmsg);
 | 
			
		||||
      // Re-assign new_value because the new value was modified by the bound check.
 | 
			
		||||
      new_value = optval_from_varp(opt_idx, varp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && (opt->indir & PV_BOTH)) {
 | 
			
		||||
      // 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);
 | 
			
		||||
      OptVal local_unset_value = optval_unset_local(opt_idx, varp_local);
 | 
			
		||||
      set_option_varp(opt_idx, varp_local, local_unset_value, true);
 | 
			
		||||
    } else if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
 | 
			
		||||
      // May set global value for local option.
 | 
			
		||||
      void *varp_global = get_varp_scope(opt, OPT_GLOBAL);
 | 
			
		||||
      set_option_varp(opt_idx, varp_global, optval_copy(new_value), true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Skip processing the option further if asked to do so.
 | 
			
		||||
  if (*doskip) {
 | 
			
		||||
    return errmsg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (errmsg == NULL) {
 | 
			
		||||
    // Trigger the autocommand only after setting the flags.
 | 
			
		||||
    if (varp == &curbuf->b_p_syn) {
 | 
			
		||||
      do_syntax_autocmd(curbuf, value_changed);
 | 
			
		||||
    } else if (varp == &curbuf->b_p_ft) {
 | 
			
		||||
      // 'filetype' is set, trigger the FileType autocommand
 | 
			
		||||
      // Skip this when called from a modeline
 | 
			
		||||
      // Force autocmd when the filetype was changed
 | 
			
		||||
      if (!(opt_flags & OPT_MODELINE) || value_changed) {
 | 
			
		||||
        do_filetype_autocmd(curbuf, value_changed);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (varp == &curwin->w_s->b_p_spl) {
 | 
			
		||||
      do_spelllang_source(curwin);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // In case 'columns' or 'ls' changed.
 | 
			
		||||
  comp_col();
 | 
			
		||||
 | 
			
		||||
  if (varp == &p_mouse) {
 | 
			
		||||
    setmouse();  // in case 'mouse' changed
 | 
			
		||||
  } else if ((varp == &p_flp || varp == &(curbuf->b_p_flp)) && curwin->w_briopt_list) {
 | 
			
		||||
    // Changing Formatlistpattern when briopt includes the list setting:
 | 
			
		||||
    // redraw
 | 
			
		||||
    redraw_all_later(UPD_NOT_VALID);
 | 
			
		||||
  } else if (varp == &p_wbr || varp == &(curwin->w_p_wbr)) {
 | 
			
		||||
    // add / remove window bars for 'winbar'
 | 
			
		||||
    set_winbar(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (curwin->w_curswant != MAXCOL && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
 | 
			
		||||
    curwin->w_set_curswant = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  check_redraw(opt->flags);
 | 
			
		||||
 | 
			
		||||
  if (errmsg == NULL) {
 | 
			
		||||
    uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
 | 
			
		||||
    opt->flags |= P_WAS_SET;
 | 
			
		||||
 | 
			
		||||
    // When an option is set in the sandbox, from a modeline or in secure mode set the P_INSECURE
 | 
			
		||||
    // flag.  Otherwise, if a new value is stored reset the flag.
 | 
			
		||||
    if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) {
 | 
			
		||||
      *p |= P_INSECURE;
 | 
			
		||||
    } else if (value_replaced) {
 | 
			
		||||
      *p &= ~P_INSECURE;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return errmsg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the value of an option using an OptVal.
 | 
			
		||||
///
 | 
			
		||||
/// @param       opt_idx    Option index. Must be >=0.
 | 
			
		||||
/// @param[in]   varp       Option variable pointer, cannot be NULL.
 | 
			
		||||
/// @param       value      New option value.
 | 
			
		||||
/// @param       opt_flags  Option flags.
 | 
			
		||||
/// @param       new_value  Whether value was replaced completely.
 | 
			
		||||
/// @param[out]  errbuf     Buffer for error message.
 | 
			
		||||
/// @param       errbuflen  Length of error buffer.
 | 
			
		||||
/// @param       opt_idx         Index in options[] table. Must be >= 0.
 | 
			
		||||
/// @param[in]   varp            Option variable pointer, cannot be NULL.
 | 
			
		||||
/// @param       value           New option value. Might get freed.
 | 
			
		||||
/// @param       opt_flags       Option flags.
 | 
			
		||||
/// @param       value_replaced  Value was replaced completely.
 | 
			
		||||
/// @param[out]  errbuf          Buffer for error message.
 | 
			
		||||
/// @param       errbuflen       Length of error buffer.
 | 
			
		||||
///
 | 
			
		||||
/// @return  Error message. NULL if there are no errors.
 | 
			
		||||
/// @return  NULL on success, an untranslated error message on error.
 | 
			
		||||
static const char *set_option(const int opt_idx, void *varp, OptVal value, int opt_flags,
 | 
			
		||||
                              const bool new_value, char *errbuf, size_t errbuflen)
 | 
			
		||||
                              const bool value_replaced, char *errbuf, size_t errbuflen)
 | 
			
		||||
{
 | 
			
		||||
  assert(opt_idx >= 0 && varp != NULL);
 | 
			
		||||
 | 
			
		||||
  const char *errmsg = NULL;
 | 
			
		||||
  bool value_checked = false;
 | 
			
		||||
 | 
			
		||||
  vimoption_T *opt = &options[opt_idx];
 | 
			
		||||
 | 
			
		||||
  // TODO(famiu): Unify set_string_option with set_option.
 | 
			
		||||
  if (value.type == kOptValTypeString) {
 | 
			
		||||
    errmsg = set_string_option(opt_idx, varp, value.data.string.data, opt_flags, true,
 | 
			
		||||
                               &value_checked, errbuf, errbuflen);
 | 
			
		||||
    goto end;
 | 
			
		||||
  static const char *optval_type_names[] = {
 | 
			
		||||
    [kOptValTypeNil] = "Nil",
 | 
			
		||||
    [kOptValTypeBoolean] = "Boolean",
 | 
			
		||||
    [kOptValTypeNumber] = "Number",
 | 
			
		||||
    [kOptValTypeString] = "String"
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (value.type == kOptValTypeNil) {
 | 
			
		||||
    // Don't try to unset local value if scope is global.
 | 
			
		||||
    // TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is
 | 
			
		||||
    // fixed.
 | 
			
		||||
    if (opt_flags == OPT_GLOBAL) {
 | 
			
		||||
      errmsg = _("Cannot unset global option value");
 | 
			
		||||
    } else {
 | 
			
		||||
      optval_free(value);
 | 
			
		||||
      value = optval_unset_local(opt_idx, varp);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (!optval_match_type(value, opt_idx)) {
 | 
			
		||||
    char *rep = optval_to_cstr(value);
 | 
			
		||||
    char *valid_types = option_get_valid_types(opt_idx);
 | 
			
		||||
    snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"),
 | 
			
		||||
             opt->fullname, valid_types, optval_type_names[value.type], rep);
 | 
			
		||||
    xfree(rep);
 | 
			
		||||
    xfree(valid_types);
 | 
			
		||||
    errmsg = errbuf;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Disallow changing some options from secure mode.
 | 
			
		||||
  if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) {
 | 
			
		||||
    return e_secure;
 | 
			
		||||
  if (errmsg != NULL) {
 | 
			
		||||
    goto err;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // When using ":set opt=val" for a global option with a local value the local value will be reset,
 | 
			
		||||
  // use the global value here.
 | 
			
		||||
  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && ((int)opt->indir & PV_BOTH)) {
 | 
			
		||||
    varp = opt->var;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  OptVal old_value = optval_from_varp(opt_idx, varp);
 | 
			
		||||
@@ -3527,100 +3781,84 @@ static const char *set_option(const int opt_idx, void *varp, OptVal value, int o
 | 
			
		||||
  // TODO(famiu): This needs to be changed to use the current type of the old value instead of
 | 
			
		||||
  // value.type, when multi-type options are added.
 | 
			
		||||
  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
 | 
			
		||||
    old_local_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_LOCAL));
 | 
			
		||||
    old_global_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL));
 | 
			
		||||
    old_local_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_LOCAL));
 | 
			
		||||
 | 
			
		||||
    // If local value of global-local option is unset, use global value as local value.
 | 
			
		||||
    if (is_option_local_value_unset(opt, curbuf, curwin)) {
 | 
			
		||||
      old_local_value = old_global_value;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Value that's actually being used.
 | 
			
		||||
  // For local scope of a global-local option, it is equal to the global value.
 | 
			
		||||
  // In every other case, it is the same as old_value.
 | 
			
		||||
  const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL);
 | 
			
		||||
  OptVal used_old_value = oldval_is_global ? optval_from_varp(opt_idx, get_varp(opt)) : old_value;
 | 
			
		||||
 | 
			
		||||
  if (value.type == kOptValTypeNumber) {
 | 
			
		||||
    errmsg = validate_num_option((OptInt *)varp, &value.data.number);
 | 
			
		||||
 | 
			
		||||
    // Don't change the value and return early if validation failed.
 | 
			
		||||
    if (errmsg != NULL) {
 | 
			
		||||
      return errmsg;
 | 
			
		||||
      goto err;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Set the new option value.
 | 
			
		||||
  set_option_varp(varp, value);
 | 
			
		||||
  // Remember where the option was set.
 | 
			
		||||
  set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
 | 
			
		||||
  // May set global value for local option.
 | 
			
		||||
  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
 | 
			
		||||
    set_option_varp(get_varp_scope(opt, OPT_GLOBAL), value);
 | 
			
		||||
  set_option_varp(opt_idx, varp, value, false);
 | 
			
		||||
 | 
			
		||||
  OptVal saved_used_value = optval_copy(used_old_value);
 | 
			
		||||
  OptVal saved_old_global_value = optval_copy(old_global_value);
 | 
			
		||||
  OptVal saved_old_local_value = optval_copy(old_local_value);
 | 
			
		||||
  // New value (and varp) may become invalid if the buffer is closed by autocommands.
 | 
			
		||||
  OptVal saved_new_value = optval_copy(value);
 | 
			
		||||
 | 
			
		||||
  uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
 | 
			
		||||
  const int secure_saved = secure;
 | 
			
		||||
 | 
			
		||||
  // When an option is set in the sandbox, from a modeline or in secure mode, then deal with side
 | 
			
		||||
  // effects in secure mode. Also when the value was set with the P_INSECURE flag and is not
 | 
			
		||||
  // completely replaced.
 | 
			
		||||
  if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & P_INSECURE))) {
 | 
			
		||||
    secure = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Invoke the option specific callback function to validate and apply the new value.
 | 
			
		||||
  bool doskip = false;
 | 
			
		||||
  opt_did_set_cb_T did_set_cb;
 | 
			
		||||
 | 
			
		||||
  if ((int *)varp == &p_force_on) {
 | 
			
		||||
    did_set_cb = did_set_force_on;
 | 
			
		||||
  } else if ((int *)varp == &p_force_off) {
 | 
			
		||||
    did_set_cb = did_set_force_off;
 | 
			
		||||
  } else {
 | 
			
		||||
    did_set_cb = opt->opt_did_set_cb;
 | 
			
		||||
  }
 | 
			
		||||
  if (did_set_cb != NULL) {
 | 
			
		||||
    // TODO(famiu): make os_oldval and os_newval use OptVal.
 | 
			
		||||
    optset_T did_set_cb_args = {
 | 
			
		||||
      .os_varp = varp,
 | 
			
		||||
      .os_flags = opt_flags,
 | 
			
		||||
      .os_oldval = old_value.data,
 | 
			
		||||
      .os_newval = value.data,
 | 
			
		||||
      .os_doskip = false,
 | 
			
		||||
      .os_errbuf = NULL,
 | 
			
		||||
      .os_errbuflen = 0,
 | 
			
		||||
      .os_buf = curbuf,
 | 
			
		||||
      .os_win = curwin
 | 
			
		||||
    };
 | 
			
		||||
  errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, &doskip, &value_checked,
 | 
			
		||||
                          value_replaced, errbuf, errbuflen);
 | 
			
		||||
 | 
			
		||||
    errmsg = did_set_cb(&did_set_cb_args);
 | 
			
		||||
    doskip = did_set_cb_args.os_doskip;
 | 
			
		||||
  }
 | 
			
		||||
  secure = secure_saved;
 | 
			
		||||
 | 
			
		||||
  // Stop processing option further if asked to do so.
 | 
			
		||||
  if (doskip) {
 | 
			
		||||
    return errmsg;
 | 
			
		||||
    goto end;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check the bound for num options.
 | 
			
		||||
  if (value.type == kOptValTypeNumber) {
 | 
			
		||||
    errmsg
 | 
			
		||||
      = check_num_option_bounds((OptInt *)varp, old_value.data.number, errbuf, errbuflen, errmsg);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  apply_optionset_autocmd(opt_idx, opt_flags,
 | 
			
		||||
                          old_value,
 | 
			
		||||
                          old_global_value,
 | 
			
		||||
                          old_local_value,
 | 
			
		||||
                          value,
 | 
			
		||||
                          errmsg);
 | 
			
		||||
 | 
			
		||||
  if (opt->flags & P_UI_OPTION) {
 | 
			
		||||
    OptVal value_copy = optval_copy(optval_from_varp(opt_idx, varp));
 | 
			
		||||
    ui_call_option_set(cstr_as_string(opt->fullname),
 | 
			
		||||
                       optval_as_object(value_copy));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comp_col();  // in case 'columns' or 'ls' changed
 | 
			
		||||
  if (curwin->w_curswant != MAXCOL
 | 
			
		||||
      && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
 | 
			
		||||
    curwin->w_set_curswant = true;
 | 
			
		||||
  }
 | 
			
		||||
  check_redraw(opt->flags);
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
  if (errmsg == NULL) {
 | 
			
		||||
    opt->flags |= P_WAS_SET;
 | 
			
		||||
 | 
			
		||||
    // When an option is set in the sandbox, from a modeline or in secure mode set the P_INSECURE
 | 
			
		||||
    // flag.  Otherwise, if a new value is stored reset the flag.
 | 
			
		||||
    uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
 | 
			
		||||
    if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) {
 | 
			
		||||
      *p |= P_INSECURE;
 | 
			
		||||
    } else if (new_value) {
 | 
			
		||||
      *p &= ~P_INSECURE;
 | 
			
		||||
    if (!starting) {
 | 
			
		||||
      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) {
 | 
			
		||||
      // Calculate saved_new_value again as its value might be changed by bound checks.
 | 
			
		||||
      // NOTE: Currently there are no buffer/window local UI options, but if there ever are buffer
 | 
			
		||||
      // or window local UI options added in the future, varp might become invalid if the buffer or
 | 
			
		||||
      // window is closed during an autocommand, and a check would have to be added for it.
 | 
			
		||||
      optval_free(saved_new_value);
 | 
			
		||||
      saved_new_value = optval_copy(optval_from_varp(opt_idx, varp));
 | 
			
		||||
      ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
  // Free copied values as they are not needed anymore
 | 
			
		||||
  optval_free(saved_used_value);
 | 
			
		||||
  optval_free(saved_old_local_value);
 | 
			
		||||
  optval_free(saved_old_global_value);
 | 
			
		||||
  optval_free(saved_new_value);
 | 
			
		||||
  return errmsg;
 | 
			
		||||
err:
 | 
			
		||||
  optval_free(value);
 | 
			
		||||
  return errmsg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -3630,17 +3868,10 @@ end:
 | 
			
		||||
/// @param[in]  value      Option value. If NIL_OPTVAL, the option value is cleared.
 | 
			
		||||
/// @param[in]  opt_flags  Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
 | 
			
		||||
///
 | 
			
		||||
/// @return NULL on success, an untranslated error message on error.
 | 
			
		||||
/// @return  NULL on success, an untranslated error message on error.
 | 
			
		||||
const char *set_option_value(const char *const name, const OptVal value, int opt_flags)
 | 
			
		||||
  FUNC_ATTR_NONNULL_ARG(1)
 | 
			
		||||
{
 | 
			
		||||
  static const char *optval_type_names[] = {
 | 
			
		||||
    [kOptValTypeNil] = "Nil",
 | 
			
		||||
    [kOptValTypeBoolean] = "Boolean",
 | 
			
		||||
    [kOptValTypeNumber] = "Number",
 | 
			
		||||
    [kOptValTypeString] = "String"
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static char errbuf[IOSIZE];
 | 
			
		||||
 | 
			
		||||
  if (is_tty_option(name)) {
 | 
			
		||||
@@ -3666,26 +3897,9 @@ const char *set_option_value(const char *const name, const OptVal value, int opt
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const char *errmsg = NULL;
 | 
			
		||||
  // Copy the value so we can modify the copy.
 | 
			
		||||
  OptVal v = optval_copy(value);
 | 
			
		||||
 | 
			
		||||
  if (v.type == kOptValTypeNil) {
 | 
			
		||||
    v = optval_clear(name, flags, varp, curbuf, curwin);
 | 
			
		||||
  } else if (!optval_match_type(v, opt_idx)) {
 | 
			
		||||
    char *rep = optval_to_cstr(v);
 | 
			
		||||
    char *valid_types = option_get_valid_types(opt_idx);
 | 
			
		||||
    snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"),
 | 
			
		||||
             name, valid_types, optval_type_names[v.type], rep);
 | 
			
		||||
    xfree(rep);
 | 
			
		||||
    xfree(valid_types);
 | 
			
		||||
    errmsg = errbuf;
 | 
			
		||||
    goto end;
 | 
			
		||||
  }
 | 
			
		||||
  errmsg = set_option(opt_idx, varp, optval_copy(value), opt_flags, true, errbuf, sizeof(errbuf));
 | 
			
		||||
 | 
			
		||||
  errmsg = set_option(opt_idx, varp, v, opt_flags, true, errbuf, sizeof(errbuf));
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
  optval_free(v);  // Free the copied OptVal.
 | 
			
		||||
  return errmsg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -4143,115 +4357,6 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
 | 
			
		||||
  return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unset local option value, similar to ":set opt<".
 | 
			
		||||
void unset_global_local_option(char *name, void *from)
 | 
			
		||||
{
 | 
			
		||||
  vimoption_T *p;
 | 
			
		||||
  buf_T *buf = (buf_T *)from;
 | 
			
		||||
 | 
			
		||||
  int opt_idx = findoption(name);
 | 
			
		||||
  if (opt_idx < 0) {
 | 
			
		||||
    semsg(_("E355: Unknown option: %s"), name);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  p = &(options[opt_idx]);
 | 
			
		||||
 | 
			
		||||
  switch ((int)p->indir) {
 | 
			
		||||
  // global option with local value: use local value if it's been set
 | 
			
		||||
  case PV_EP:
 | 
			
		||||
    clear_string_option(&buf->b_p_ep);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_KP:
 | 
			
		||||
    clear_string_option(&buf->b_p_kp);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_PATH:
 | 
			
		||||
    clear_string_option(&buf->b_p_path);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_AR:
 | 
			
		||||
    buf->b_p_ar = -1;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_BKC:
 | 
			
		||||
    clear_string_option(&buf->b_p_bkc);
 | 
			
		||||
    buf->b_bkc_flags = 0;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_TAGS:
 | 
			
		||||
    clear_string_option(&buf->b_p_tags);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_TC:
 | 
			
		||||
    clear_string_option(&buf->b_p_tc);
 | 
			
		||||
    buf->b_tc_flags = 0;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_SISO:
 | 
			
		||||
    curwin->w_p_siso = -1;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_SO:
 | 
			
		||||
    curwin->w_p_so = -1;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_DEF:
 | 
			
		||||
    clear_string_option(&buf->b_p_def);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_INC:
 | 
			
		||||
    clear_string_option(&buf->b_p_inc);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_DICT:
 | 
			
		||||
    clear_string_option(&buf->b_p_dict);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_TSR:
 | 
			
		||||
    clear_string_option(&buf->b_p_tsr);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_TSRFU:
 | 
			
		||||
    clear_string_option(&buf->b_p_tsrfu);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_FP:
 | 
			
		||||
    clear_string_option(&buf->b_p_fp);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_EFM:
 | 
			
		||||
    clear_string_option(&buf->b_p_efm);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_GP:
 | 
			
		||||
    clear_string_option(&buf->b_p_gp);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_MP:
 | 
			
		||||
    clear_string_option(&buf->b_p_mp);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_SBR:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_sbr);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_STL:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_stl);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_WBR:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_wbr);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_UL:
 | 
			
		||||
    buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_LW:
 | 
			
		||||
    clear_string_option(&buf->b_p_lw);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_MENC:
 | 
			
		||||
    clear_string_option(&buf->b_p_menc);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_LCS:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_lcs);
 | 
			
		||||
    set_listchars_option((win_T *)from, ((win_T *)from)->w_p_lcs, true);
 | 
			
		||||
    redraw_later((win_T *)from, UPD_NOT_VALID);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_FCS:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_fcs);
 | 
			
		||||
    set_fillchars_option((win_T *)from, ((win_T *)from)->w_p_fcs, true);
 | 
			
		||||
    redraw_later((win_T *)from, UPD_NOT_VALID);
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_VE:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_ve);
 | 
			
		||||
    ((win_T *)from)->w_ve_flags = 0;
 | 
			
		||||
    break;
 | 
			
		||||
  case PV_STC:
 | 
			
		||||
    clear_string_option(&((win_T *)from)->w_p_stc);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
 | 
			
		||||
{
 | 
			
		||||
  if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) {
 | 
			
		||||
@@ -4634,12 +4739,6 @@ static inline void *get_varp(vimoption_T *p)
 | 
			
		||||
  return get_varp_from(p, curbuf, curwin);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return the did_set callback function for the option at 'opt_idx'
 | 
			
		||||
opt_did_set_cb_T get_option_did_set_cb(int opt_idx)
 | 
			
		||||
{
 | 
			
		||||
  return options[opt_idx].opt_did_set_cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the value of 'equalprg', either the buffer-local one or the global one.
 | 
			
		||||
char *get_equalprg(void)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,7 @@ typedef struct vimoption {
 | 
			
		||||
  /// cmdline. Only useful for string options.
 | 
			
		||||
  opt_expand_cb_T opt_expand_cb;
 | 
			
		||||
 | 
			
		||||
  // TODO(famiu): Use OptVal for def_val.
 | 
			
		||||
  void *def_val;     ///< default values for variable (neovim!!)
 | 
			
		||||
  LastSet last_set;  ///< script in which the option was last set
 | 
			
		||||
} vimoption_T;
 | 
			
		||||
@@ -72,6 +73,7 @@ enum {
 | 
			
		||||
/// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global
 | 
			
		||||
/// values, get local value.
 | 
			
		||||
typedef enum {
 | 
			
		||||
  // TODO(famiu): See if `OPT_FREE` is really necessary and remove it if not.
 | 
			
		||||
  OPT_FREE      = 0x01,   ///< Free old value if it was allocated.
 | 
			
		||||
  OPT_GLOBAL    = 0x02,   ///< Use global value.
 | 
			
		||||
  OPT_LOCAL     = 0x04,   ///< Use local value.
 | 
			
		||||
 
 | 
			
		||||
@@ -173,51 +173,7 @@ void didset_string_options(void)
 | 
			
		||||
  (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Trigger the OptionSet autocommand.
 | 
			
		||||
/// "opt_idx"   is the index of the option being set.
 | 
			
		||||
/// "opt_flags" can be OPT_LOCAL etc.
 | 
			
		||||
/// "oldval"    the old value
 | 
			
		||||
/// "oldval_l"  the old local value (only non-NULL if global and local value are set)
 | 
			
		||||
/// "oldval_g"  the old global value (only non-NULL if global and local value are set)
 | 
			
		||||
/// "newval"    the new value
 | 
			
		||||
void trigger_optionset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l,
 | 
			
		||||
                              char *oldval_g, char *newval)
 | 
			
		||||
{
 | 
			
		||||
  // Don't do this recursively.
 | 
			
		||||
  if (oldval == NULL || newval == NULL
 | 
			
		||||
      || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char buf_type[7];
 | 
			
		||||
 | 
			
		||||
  vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
 | 
			
		||||
               (opt_flags & OPT_LOCAL) ? "local" : "global");
 | 
			
		||||
  set_vim_var_string(VV_OPTION_OLD, oldval, -1);
 | 
			
		||||
  set_vim_var_string(VV_OPTION_NEW, newval, -1);
 | 
			
		||||
  set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
 | 
			
		||||
  if (opt_flags & OPT_LOCAL) {
 | 
			
		||||
    set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
 | 
			
		||||
    set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
 | 
			
		||||
  }
 | 
			
		||||
  if (opt_flags & OPT_GLOBAL) {
 | 
			
		||||
    set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
 | 
			
		||||
    set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1);
 | 
			
		||||
  }
 | 
			
		||||
  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
 | 
			
		||||
    set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
 | 
			
		||||
    set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1);
 | 
			
		||||
    set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1);
 | 
			
		||||
  }
 | 
			
		||||
  if (opt_flags & OPT_MODELINE) {
 | 
			
		||||
    set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
 | 
			
		||||
    set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
 | 
			
		||||
  }
 | 
			
		||||
  apply_autocmds(EVENT_OPTIONSET, get_option(opt_idx)->fullname, NULL, false, NULL);
 | 
			
		||||
  reset_v_option_vars();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *illegal_char(char *errbuf, size_t errbuflen, int c)
 | 
			
		||||
char *illegal_char(char *errbuf, size_t errbuflen, int c)
 | 
			
		||||
{
 | 
			
		||||
  if (errbuf == NULL) {
 | 
			
		||||
    return "";
 | 
			
		||||
@@ -340,7 +296,9 @@ static void set_string_option_global(vimoption_T *opt, char **varp)
 | 
			
		||||
/// "set_sid" is SID_NONE don't set the scriptID.  Otherwise set the scriptID to
 | 
			
		||||
/// "set_sid".
 | 
			
		||||
///
 | 
			
		||||
/// @param opt_flags  OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
 | 
			
		||||
/// @param opt_flags  OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL.
 | 
			
		||||
///
 | 
			
		||||
/// TODO(famiu): Remove this and its win/buf variants.
 | 
			
		||||
void set_string_option_direct(const char *name, int opt_idx, const char *val, int opt_flags,
 | 
			
		||||
                              int set_sid)
 | 
			
		||||
{
 | 
			
		||||
@@ -431,100 +389,6 @@ void set_string_option_direct_in_buf(buf_T *buf, const char *name, int opt_idx,
 | 
			
		||||
  unblock_autocmds();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set a string option to a new value, handling the effects
 | 
			
		||||
/// Must not be called with a hidden option!
 | 
			
		||||
///
 | 
			
		||||
/// @param[in]  opt_idx  Option to set.
 | 
			
		||||
/// @param[in]  value  New value.
 | 
			
		||||
/// @param[in]  opt_flags  Option flags: expected to contain #OPT_LOCAL and/or
 | 
			
		||||
///                        #OPT_GLOBAL.
 | 
			
		||||
///
 | 
			
		||||
/// @return NULL on success, an untranslated error message on error.
 | 
			
		||||
const char *set_string_option(const int opt_idx, void *varp, const char *value, const int opt_flags,
 | 
			
		||||
                              const bool new_value, bool *value_checked, char *const errbuf,
 | 
			
		||||
                              const size_t errbuflen)
 | 
			
		||||
  FUNC_ATTR_WARN_UNUSED_RESULT
 | 
			
		||||
{
 | 
			
		||||
  vimoption_T *opt = get_option(opt_idx);
 | 
			
		||||
 | 
			
		||||
  char *origval_l = NULL;
 | 
			
		||||
  char *origval_g = NULL;
 | 
			
		||||
 | 
			
		||||
  // When using ":set opt=val" for a global option
 | 
			
		||||
  // with a local value the local value will be
 | 
			
		||||
  // reset, use the global value here.
 | 
			
		||||
  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
 | 
			
		||||
      && ((int)opt->indir & PV_BOTH)) {
 | 
			
		||||
    varp = opt->var;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // The old value is kept until we are sure that the new value is valid.
 | 
			
		||||
  char *oldval = *(char **)varp;
 | 
			
		||||
 | 
			
		||||
  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
 | 
			
		||||
    origval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
 | 
			
		||||
    origval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
 | 
			
		||||
 | 
			
		||||
    // A global-local string option might have an empty option as value to
 | 
			
		||||
    // indicate that the global value should be used.
 | 
			
		||||
    if (((int)opt->indir & PV_BOTH) && origval_l == empty_string_option) {
 | 
			
		||||
      origval_l = origval_g;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char *origval;
 | 
			
		||||
  // When setting the local value of a global option, the old value may be
 | 
			
		||||
  // the global value.
 | 
			
		||||
  if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
 | 
			
		||||
    origval = *(char **)get_varp_from(opt, curbuf, curwin);
 | 
			
		||||
  } else {
 | 
			
		||||
    origval = oldval;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *(char **)varp = xstrdup(value != NULL ? value : empty_string_option);
 | 
			
		||||
 | 
			
		||||
  char *const saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
 | 
			
		||||
  char *const saved_oldval_l = (origval_l != NULL) ? xstrdup(origval_l) : 0;
 | 
			
		||||
  char *const saved_oldval_g = (origval_g != NULL) ? xstrdup(origval_g) : 0;
 | 
			
		||||
 | 
			
		||||
  // newval (and varp) may become invalid if the buffer is closed by
 | 
			
		||||
  // autocommands.
 | 
			
		||||
  char *const saved_newval = xstrdup(*(char **)varp);
 | 
			
		||||
 | 
			
		||||
  const int secure_saved = secure;
 | 
			
		||||
  const uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
 | 
			
		||||
 | 
			
		||||
  // When an option is set in the sandbox, from a modeline or in secure mode, then deal with side
 | 
			
		||||
  // effects in secure mode. Also when the value was set with the P_INSECURE flag and is not
 | 
			
		||||
  // completely replaced.
 | 
			
		||||
  if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!new_value && (*p & P_INSECURE))) {
 | 
			
		||||
    secure = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval,
 | 
			
		||||
                                                   errbuf, errbuflen, opt_flags, value_checked);
 | 
			
		||||
 | 
			
		||||
  secure = secure_saved;
 | 
			
		||||
 | 
			
		||||
  // call autocommand after handling side effects
 | 
			
		||||
  if (errmsg == NULL) {
 | 
			
		||||
    if (!starting) {
 | 
			
		||||
      trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_oldval_l,
 | 
			
		||||
                               saved_oldval_g, saved_newval);
 | 
			
		||||
    }
 | 
			
		||||
    if (opt->flags & P_UI_OPTION) {
 | 
			
		||||
      ui_call_option_set(cstr_as_string(opt->fullname),
 | 
			
		||||
                         CSTR_AS_OBJ(saved_newval));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  xfree(saved_origval);
 | 
			
		||||
  xfree(saved_oldval_l);
 | 
			
		||||
  xfree(saved_oldval_g);
 | 
			
		||||
  xfree(saved_newval);
 | 
			
		||||
 | 
			
		||||
  return errmsg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return true if "val" is a valid 'filetype' name.
 | 
			
		||||
/// Also used for 'syntax' and 'keymap'.
 | 
			
		||||
static bool valid_filetype(const char *val)
 | 
			
		||||
@@ -636,7 +500,7 @@ const char *check_stl_option(char *s)
 | 
			
		||||
/// Check for a "normal" directory or file name in some options.  Disallow a
 | 
			
		||||
/// path separator (slash and/or backslash), wildcards and characters that are
 | 
			
		||||
/// often illegal in a file name. Be more permissive if "secure" is off.
 | 
			
		||||
static bool check_illegal_path_names(char *val, uint32_t flags)
 | 
			
		||||
bool check_illegal_path_names(char *val, uint32_t flags)
 | 
			
		||||
{
 | 
			
		||||
  return (((flags & P_NFNAME)
 | 
			
		||||
           && strpbrk(val, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
 | 
			
		||||
@@ -2676,177 +2540,6 @@ int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches)
 | 
			
		||||
  return expand_set_opt_generic(args, get_highlight_name, numMatches, matches);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When 'syntax' is set, load the syntax of that name
 | 
			
		||||
static void do_syntax_autocmd(buf_T *buf, bool value_changed)
 | 
			
		||||
{
 | 
			
		||||
  static int syn_recursive = 0;
 | 
			
		||||
 | 
			
		||||
  syn_recursive++;
 | 
			
		||||
  // Only pass true for "force" when the value changed or not used
 | 
			
		||||
  // recursively, to avoid endless recurrence.
 | 
			
		||||
  apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname,
 | 
			
		||||
                 value_changed || syn_recursive == 1, buf);
 | 
			
		||||
  buf->b_flags |= BF_SYN_SET;
 | 
			
		||||
  syn_recursive--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_spelllang_source(win_T *win)
 | 
			
		||||
{
 | 
			
		||||
  char fname[200];
 | 
			
		||||
  char *q = win->w_s->b_p_spl;
 | 
			
		||||
 | 
			
		||||
  // Skip the first name if it is "cjk".
 | 
			
		||||
  if (strncmp(q, "cjk,", 4) == 0) {
 | 
			
		||||
    q += 4;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Source the spell/LANG.{vim,lua} in 'runtimepath'.
 | 
			
		||||
  // They could set 'spellcapcheck' depending on the language.
 | 
			
		||||
  // Use the first name in 'spelllang' up to '_region' or
 | 
			
		||||
  // '.encoding'.
 | 
			
		||||
  char *p;
 | 
			
		||||
  for (p = q; *p != NUL; p++) {
 | 
			
		||||
    if (!ASCII_ISALNUM(*p) && *p != '-') {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (p > q) {
 | 
			
		||||
    vim_snprintf(fname, sizeof(fname), "spell/%.*s.*", (int)(p - q), q);
 | 
			
		||||
    source_runtime_vim_lua(fname, DIP_ALL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Handle string options that need some action to perform when changed.
 | 
			
		||||
/// The new value must be allocated.
 | 
			
		||||
///
 | 
			
		||||
/// @param opt_idx  index in options[] table
 | 
			
		||||
/// @param varp  pointer to the option variable
 | 
			
		||||
/// @param oldval  previous value of the option
 | 
			
		||||
/// @param errbuf  buffer for errors, or NULL
 | 
			
		||||
/// @param errbuflen  length of errors buffer
 | 
			
		||||
/// @param opt_flags  OPT_LOCAL and/or OPT_GLOBAL
 | 
			
		||||
/// @param op OP_ADDING/OP_PREPENDING/OP_REMOVING
 | 
			
		||||
/// @param value_checked  value was checked to be safe, no need to set P_INSECURE
 | 
			
		||||
///
 | 
			
		||||
/// @return  NULL for success, or an untranslated error message for an error
 | 
			
		||||
const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **varp, char *oldval,
 | 
			
		||||
                                  char *errbuf, size_t errbuflen, int opt_flags,
 | 
			
		||||
                                  bool *value_checked)
 | 
			
		||||
{
 | 
			
		||||
  const char *errmsg = NULL;
 | 
			
		||||
  int restore_chartab = false;
 | 
			
		||||
  vimoption_T *opt = get_option(opt_idx);
 | 
			
		||||
  bool free_oldval = (opt->flags & P_ALLOCED);
 | 
			
		||||
  opt_did_set_cb_T did_set_cb = get_option_did_set_cb(opt_idx);
 | 
			
		||||
  bool value_changed = false;
 | 
			
		||||
 | 
			
		||||
  optset_T args = {
 | 
			
		||||
    .os_varp = varp,
 | 
			
		||||
    .os_idx = opt_idx,
 | 
			
		||||
    .os_flags = opt_flags,
 | 
			
		||||
    .os_oldval.string = cstr_as_string(oldval),
 | 
			
		||||
    .os_newval.string = cstr_as_string(*varp),
 | 
			
		||||
    .os_value_checked = false,
 | 
			
		||||
    .os_value_changed = false,
 | 
			
		||||
    .os_restore_chartab = false,
 | 
			
		||||
    .os_errbuf = errbuf,
 | 
			
		||||
    .os_errbuflen = errbuflen,
 | 
			
		||||
    .os_win = win,
 | 
			
		||||
    .os_buf = buf,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Disallow changing some options from secure mode
 | 
			
		||||
  if ((secure || sandbox != 0) && (opt->flags & P_SECURE)) {
 | 
			
		||||
    errmsg = e_secure;
 | 
			
		||||
    // Check for a "normal" directory or file name in some options.
 | 
			
		||||
  } else if (check_illegal_path_names(*varp, opt->flags)) {
 | 
			
		||||
    errmsg = e_invarg;
 | 
			
		||||
  } else if (did_set_cb != NULL) {
 | 
			
		||||
    // Invoke the option specific callback function to validate and apply
 | 
			
		||||
    // the new option value.
 | 
			
		||||
    errmsg = did_set_cb(&args);
 | 
			
		||||
 | 
			
		||||
    // The 'filetype' and 'syntax' option callback functions may change
 | 
			
		||||
    // the os_value_changed field.
 | 
			
		||||
    value_changed = args.os_value_changed;
 | 
			
		||||
    // The 'keymap', 'filetype' and 'syntax' option callback functions
 | 
			
		||||
    // may change the os_value_checked field.
 | 
			
		||||
    *value_checked = args.os_value_checked;
 | 
			
		||||
    // The 'isident', 'iskeyword', 'isprint' and 'isfname' options may
 | 
			
		||||
    // change the character table.  On failure, this needs to be restored.
 | 
			
		||||
    restore_chartab = args.os_restore_chartab;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If an error is detected, restore the previous value.
 | 
			
		||||
  if (errmsg != NULL) {
 | 
			
		||||
    free_string_option(*varp);
 | 
			
		||||
    *varp = oldval;
 | 
			
		||||
    // When resetting some values, need to act on it.
 | 
			
		||||
    if (restore_chartab) {
 | 
			
		||||
      (void)buf_init_chartab(buf, true);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // Remember where the option was set.
 | 
			
		||||
    set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
 | 
			
		||||
    // Free string options that are in allocated memory.
 | 
			
		||||
    // Use "free_oldval", because recursiveness may change the flags under
 | 
			
		||||
    // our fingers (esp. init_highlight()).
 | 
			
		||||
    if (free_oldval) {
 | 
			
		||||
      free_string_option(oldval);
 | 
			
		||||
    }
 | 
			
		||||
    opt->flags |= P_ALLOCED;
 | 
			
		||||
 | 
			
		||||
    if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
 | 
			
		||||
        && (opt->indir & PV_BOTH)) {
 | 
			
		||||
      // global option with local value set to use global value; free
 | 
			
		||||
      // the local value and make it empty
 | 
			
		||||
      char *p = get_varp_scope(opt, OPT_LOCAL);
 | 
			
		||||
      free_string_option(*(char **)p);
 | 
			
		||||
      *(char **)p = empty_string_option;
 | 
			
		||||
    } else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
 | 
			
		||||
      // May set global value for local option.
 | 
			
		||||
      set_string_option_global(opt, varp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Trigger the autocommand only after setting the flags.
 | 
			
		||||
    if (varp == &buf->b_p_syn) {
 | 
			
		||||
      do_syntax_autocmd(buf, value_changed);
 | 
			
		||||
    } else if (varp == &buf->b_p_ft) {
 | 
			
		||||
      // 'filetype' is set, trigger the FileType autocommand
 | 
			
		||||
      // Skip this when called from a modeline
 | 
			
		||||
      // Force autocmd when the filetype was changed
 | 
			
		||||
      if (!(opt_flags & OPT_MODELINE) || value_changed) {
 | 
			
		||||
        do_filetype_autocmd(buf, value_changed);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (varp == &win->w_s->b_p_spl) {
 | 
			
		||||
      do_spelllang_source(win);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (varp == &p_mouse) {
 | 
			
		||||
    setmouse();  // in case 'mouse' changed
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((varp == &p_flp || varp == &(buf->b_p_flp))
 | 
			
		||||
      && win->w_briopt_list) {
 | 
			
		||||
    // Changing Formatlistpattern when briopt includes the list setting:
 | 
			
		||||
    // redraw
 | 
			
		||||
    redraw_all_later(UPD_NOT_VALID);
 | 
			
		||||
  } else if (varp == &p_wbr || varp == &(win->w_p_wbr)) {
 | 
			
		||||
    // add / remove window bars for 'winbar'
 | 
			
		||||
    set_winbar(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (win->w_curswant != MAXCOL
 | 
			
		||||
      && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
 | 
			
		||||
    win->w_set_curswant = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  check_redraw_for(buf, win, opt->flags);
 | 
			
		||||
 | 
			
		||||
  return errmsg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check an option that can be a range of string values.
 | 
			
		||||
///
 | 
			
		||||
/// @param list  when true: accept a list of values
 | 
			
		||||
 
 | 
			
		||||
@@ -1536,14 +1536,18 @@ describe('API', function()
 | 
			
		||||
 | 
			
		||||
      -- Now try with options with a special "local is unset" value (e.g. 'undolevels')
 | 
			
		||||
      nvim('set_option_value', 'undolevels', 1000, {})
 | 
			
		||||
      eq(1000, nvim('get_option_value', 'undolevels', {scope = 'local'}))
 | 
			
		||||
      nvim('set_option_value', 'undolevels', 1200, {scope = 'local'})
 | 
			
		||||
      eq(1200, nvim('get_option_value', 'undolevels', {scope = 'local'}))
 | 
			
		||||
      nvim('set_option_value', 'undolevels', NIL, {scope = 'local'})
 | 
			
		||||
      eq(-123456, nvim('get_option_value', 'undolevels', {scope = 'local'}))
 | 
			
		||||
      eq(1000, nvim('get_option_value', 'undolevels', {}))
 | 
			
		||||
 | 
			
		||||
      nvim('set_option_value', 'autoread', true, {})
 | 
			
		||||
      eq(true, nvim('get_option_value', 'autoread', {scope = 'local'}))
 | 
			
		||||
      nvim('set_option_value', 'autoread', false, {scope = 'local'})
 | 
			
		||||
      eq(false, nvim('get_option_value', 'autoread', {scope = 'local'}))
 | 
			
		||||
      nvim('set_option_value', 'autoread', NIL, {scope = 'local'})
 | 
			
		||||
      eq(NIL, nvim('get_option_value', 'autoread', {scope = 'local'}))
 | 
			
		||||
      eq(true, nvim('get_option_value', 'autoread', {}))
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('set window options', function()
 | 
			
		||||
 
 | 
			
		||||
@@ -260,7 +260,7 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal tags=tagpath2')
 | 
			
		||||
      expected_combination({'tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local string options
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal tags=tag_global')
 | 
			
		||||
      command('noa setlocal tags=tag_local')
 | 
			
		||||
@@ -269,12 +269,12 @@ describe('au OptionSet', function()
 | 
			
		||||
        'tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set'
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local string options
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa set tags=tag_global')
 | 
			
		||||
      command('noa setlocal tags=')
 | 
			
		||||
      command('set tags=tagpath')
 | 
			
		||||
      expected_combination({'tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set'})
 | 
			
		||||
      expected_combination({'tags', 'tag_global', 'tag_global', 'tag_global', 'tagpath', 'global', 'set'})
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('with string local (to buffer) option', function()
 | 
			
		||||
@@ -295,7 +295,7 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal spelllang=klingon')
 | 
			
		||||
      expected_combination({'spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local string options
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal spelllang=spellglobal')
 | 
			
		||||
      command('noa setlocal spelllang=spelllocal')
 | 
			
		||||
@@ -311,7 +311,7 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('set statusline=foo')
 | 
			
		||||
      expected_combination({'statusline', oldval, oldval, '', 'foo', 'global', 'set'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local string options
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('set statusline&')
 | 
			
		||||
      expected_combination({'statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set'})
 | 
			
		||||
@@ -323,7 +323,7 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal statusline=baz')
 | 
			
		||||
      expected_combination({'statusline', oldval, oldval, '', 'baz', 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local string options
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal statusline=bar')
 | 
			
		||||
      command('noa setlocal statusline=baz')
 | 
			
		||||
@@ -364,11 +364,15 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal cmdheight=2')
 | 
			
		||||
      expected_combination({'cmdheight', 1, 1, '', 2, 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal cmdheight=8')
 | 
			
		||||
      command('noa setlocal cmdheight=1') -- Sets the global(!) value
 | 
			
		||||
      command('set cmdheight=2')
 | 
			
		||||
      expected_combination({'cmdheight', 1, 1, 1, 2, 'global', 'set'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa set cmdheight=8')
 | 
			
		||||
      command('set cmdheight=2')
 | 
			
		||||
      expected_combination({'cmdheight', 8, 8, 8, 2, 'global', 'set'})
 | 
			
		||||
@@ -385,11 +389,15 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal undolevels=2')
 | 
			
		||||
      expected_combination({'undolevels', 1, 1, '', 2, 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal undolevels=8')
 | 
			
		||||
      command('noa setlocal undolevels=1')
 | 
			
		||||
      command('set undolevels=2')
 | 
			
		||||
      expected_combination({'undolevels', 1, 1, 8, 2, 'global', 'set'})
 | 
			
		||||
      expected_combination({'undolevels', 8, 1, 8, 2, 'global', 'set'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa set undolevels=8')
 | 
			
		||||
      command('set undolevels=2')
 | 
			
		||||
      expected_combination({'undolevels', 8, 8, 8, 2, 'global', 'set'})
 | 
			
		||||
@@ -427,11 +435,15 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal scrolloff=2')
 | 
			
		||||
      expected_combination({'scrolloff', 1, 1, '', 2, 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal scrolloff=8')
 | 
			
		||||
      command('noa setlocal scrolloff=1')
 | 
			
		||||
      command('set scrolloff=2')
 | 
			
		||||
      expected_combination({'scrolloff', 1, 1, 8, 2, 'global', 'set'})
 | 
			
		||||
      expected_combination({'scrolloff', 8, 1, 8, 2, 'global', 'set'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa set scrolloff=8')
 | 
			
		||||
      command('set scrolloff=2')
 | 
			
		||||
      expected_combination({'scrolloff', 8, 8, 8, 2, 'global', 'set'})
 | 
			
		||||
@@ -490,11 +502,15 @@ describe('au OptionSet', function()
 | 
			
		||||
      command('setlocal noautoread')
 | 
			
		||||
      expected_combination({'autoread', true, true, '', false, 'local', 'setlocal'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa setglobal noautoread')
 | 
			
		||||
      command('noa setlocal autoread')
 | 
			
		||||
      command('set autoread')
 | 
			
		||||
      expected_combination({'autoread', true, true, false, true, 'global', 'set'})
 | 
			
		||||
      expected_combination({'autoread', false, true, false, true, 'global', 'set'})
 | 
			
		||||
 | 
			
		||||
      -- Note: v:option_old is the old global value for global-local options.
 | 
			
		||||
      -- but the old local value for all other kinds of options.
 | 
			
		||||
      command('noa set noautoread')
 | 
			
		||||
      command('set autoread')
 | 
			
		||||
      expected_combination({'autoread', false, false, false, true, 'global', 'set'})
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user