diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/option.c | 136 |
1 files changed, 71 insertions, 65 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c index 3d7fdefdeb..bfd628f9aa 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3589,91 +3589,100 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value return errmsg; } -/// Set the value of an option using an OptVal. +/// Validate the new value for an option. /// -/// @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 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 *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, - const bool value_replaced, char *errbuf, size_t errbuflen) +/// @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) { - assert(opt_idx != kOptInvalid && varp != NULL); - const char *errmsg = NULL; - bool value_checked = false; - vimoption_T *opt = &options[opt_idx]; - if (value.type == kOptValTypeNil) { + 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 // fixed. if (opt_flags == OPT_GLOBAL) { errmsg = _("Cannot unset global option value"); } else { - optval_free(value); - value = optval_unset_local(opt_idx, varp); + optval_free(*newval); + *newval = optval_unset_local(opt_idx, varp); } - } else if (!option_has_type(opt_idx, value.type)) { - char *rep = optval_to_cstr(value); + } else if (!option_has_type(opt_idx, newval->type)) { + char *rep = optval_to_cstr(*newval); 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_get_name(value.type), rep); + opt->fullname, valid_types, optval_type_get_name(newval->type), rep); xfree(rep); xfree(valid_types); 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); } + return errmsg; +} + +/// 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 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 *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, + const bool value_replaced, char *errbuf, size_t errbuflen) + FUNC_ATTR_NONNULL_ARG(2) +{ + assert(opt_idx != kOptInvalid); + + const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen); + if (errmsg != NULL) { - goto err; + 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. + const bool is_opt_local_unset = is_option_local_value_unset(opt, curbuf, curwin); + // 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)) { + if (scope_both && opt_is_global_local) { varp = opt->var; } - OptVal old_value = optval_from_varp(opt_idx, varp); - OptVal old_global_value = NIL_OPTVAL; - OptVal old_local_value = NIL_OPTVAL; - - // Save the local and global values before changing anything. This is needed as for a global-only - // option setting the "local value" in fact sets the global value (since there is only one value). - // - // 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_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; - } - } + void *varp_local = get_varp_scope(opt, OPT_LOCAL); + void *varp_global = get_varp_scope(opt, OPT_GLOBAL); + OptVal old_value = optval_from_varp(opt_idx, varp); + OptVal old_global_value = optval_from_varp(opt_idx, varp_global); + // If local value of global-local option is unset, use global value as local value. + OptVal old_local_value = is_opt_local_unset + ? old_global_value + : optval_from_varp(opt_idx, varp_local); // 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(opt_idx, varp, &value.data.number, errbuf, errbuflen); - if (errmsg != NULL) { - goto err; - } - } - - set_option_varp(opt_idx, varp, value, false); - + // For local scope of a global-local option, it's equal to the global value if the local value is + // unset. In every other case, it is the same as old_value. + // This value is used instead of old_value when triggering the OptionSet autocommand. + OptVal used_old_value = (scope_local && is_opt_local_unset) + ? optval_from_varp(opt_idx, get_varp(opt)) + : old_value; + + // Save the old values and the new value in case they get changed. 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); @@ -3690,6 +3699,11 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, secure = 1; } + bool value_checked = false; + + // Set option through its variable pointer. + set_option_varp(opt_idx, varp, value, false); + // Process any side effects. errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, &value_checked, value_replaced, errbuf, errbuflen); @@ -3701,12 +3715,6 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal 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)); } } @@ -3716,9 +3724,7 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal 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; } |