refactor(options): restructure set_option

This commit is contained in:
Famiu Haque 2023-12-24 12:34:58 +06:00
parent 734848dc1a
commit 10ab135adf
No known key found for this signature in database

View File

@ -3589,6 +3589,43 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
return errmsg; return errmsg;
} }
/// 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)
{
const char *errmsg = NULL;
vimoption_T *opt = &options[opt_idx];
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(*newval);
*newval = optval_unset_local(opt_idx, varp);
}
} 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(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. /// Set the value of an option using an OptVal.
/// ///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param opt_idx Index in options[] table. Must not be kOptInvalid.
@ -3602,78 +3639,50 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
/// @return NULL on success, an untranslated error message on error. /// @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, 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) const bool value_replaced, char *errbuf, size_t errbuflen)
FUNC_ATTR_NONNULL_ARG(2)
{ {
assert(opt_idx != kOptInvalid && varp != NULL); assert(opt_idx != kOptInvalid);
const char *errmsg = NULL; const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen);
bool value_checked = false;
vimoption_T *opt = &options[opt_idx];
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 (!option_has_type(opt_idx, value.type)) {
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_get_name(value.type), rep);
xfree(rep);
xfree(valid_types);
errmsg = errbuf;
}
if (errmsg != NULL) { 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, // When using ":set opt=val" for a global option with a local value the local value will be reset,
// use the global value here. // 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; varp = opt->var;
} }
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_value = optval_from_varp(opt_idx, varp);
OptVal old_global_value = NIL_OPTVAL; OptVal old_global_value = optval_from_varp(opt_idx, varp_global);
OptVal old_local_value = NIL_OPTVAL; // If local value of global-local option is unset, use global value as local value.
OptVal old_local_value = is_opt_local_unset
// Save the local and global values before changing anything. This is needed as for a global-only ? old_global_value
// option setting the "local value" in fact sets the global value (since there is only one value). : optval_from_varp(opt_idx, varp_local);
//
// 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;
}
}
// Value that's actually being used. // Value that's actually being used.
// For local scope of a global-local option, it is equal to the global value. // For local scope of a global-local option, it's equal to the global value if the local value is
// In every other case, it is the same as old_value. // unset. 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); // This value is used instead of old_value when triggering the OptionSet autocommand.
OptVal used_old_value = oldval_is_global ? optval_from_varp(opt_idx, get_varp(opt)) : old_value; OptVal used_old_value = (scope_local && is_opt_local_unset)
? optval_from_varp(opt_idx, get_varp(opt))
if (value.type == kOptValTypeNumber) { : old_value;
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);
// Save the old values and the new value in case they get changed.
OptVal saved_used_value = optval_copy(used_old_value); OptVal saved_used_value = optval_copy(used_old_value);
OptVal saved_old_global_value = optval_copy(old_global_value); OptVal saved_old_global_value = optval_copy(old_global_value);
OptVal saved_old_local_value = optval_copy(old_local_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; 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, errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, &value_checked,
value_replaced, errbuf, errbuflen); 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); saved_old_local_value, saved_new_value, errmsg);
} }
if (opt->flags & P_UI_OPTION) { 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)); 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_local_value);
optval_free(saved_old_global_value); optval_free(saved_old_global_value);
optval_free(saved_new_value); optval_free(saved_new_value);
return errmsg;
err:
optval_free(value);
return errmsg; return errmsg;
} }