diff options
Diffstat (limited to 'src/nvim/option.c')
-rw-r--r-- | src/nvim/option.c | 309 |
1 files changed, 227 insertions, 82 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c index e69c017b08..bec0f12d68 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -394,7 +394,7 @@ void set_init_1(bool clean_arg) // NOTE: mlterm's author is being asked to 'set' a variable // instead of an environment variable due to inheritance. if (os_env_exists("MLTERM")) { - set_option_value_give_err("tbidi", 1L, NULL, 0); + set_option_value_give_err("tbidi", BOOLEAN_OPTVAL(true), 0); } didset_options2(); @@ -2424,7 +2424,7 @@ static const char *did_set_arabic(optset_T *args) p_deco = true; // Force-set the necessary keymap for arabic. - errmsg = set_option_value("keymap", 0L, "arabic", OPT_LOCAL); + errmsg = set_option_value("keymap", STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); } else { // 'arabic' is reset, handle various sub-settings. if (!p_tbidi) { @@ -3339,7 +3339,7 @@ void set_tty_background(const char *value) ? "autocmd VimEnter * ++once ++nested :lua if not vim.api.nvim_get_option_info2('bg', {}).was_set then vim.o.bg = 'light' end" : "autocmd VimEnter * ++once ++nested :lua if not vim.api.nvim_get_option_info2('bg', {}).was_set then vim.o.bg = 'dark' end"); } else { - set_option_value_give_err("bg", 0L, value, 0); + set_option_value_give_err("bg", CSTR_AS_OPTVAL((char *)value), 0); reset_option_was_set("bg"); } } @@ -3355,32 +3355,153 @@ int findoption(const char *const arg) return findoption_len(arg, strlen(arg)); } +void optval_free(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + case kOptValTypeBoolean: + case kOptValTypeNumber: + break; + case kOptValTypeString: + api_free_string(o.data.string); + break; + } +} + +OptVal optval_copy(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + case kOptValTypeBoolean: + case kOptValTypeNumber: + return o; + case kOptValTypeString: + return STRING_OPTVAL(copy_string(o.data.string, NULL)); + default: + abort(); + } +} + +// Match type of OptVal with the type of the target option. Returns true if the types match and +// false otherwise. +static bool optval_match_type(OptVal o, int opt_idx) +{ + assert(opt_idx >= 0); + uint32_t flags = options[opt_idx].flags; + + switch (o.type) { + case kOptValTypeNil: + return false; + case kOptValTypeBoolean: + return flags & P_BOOL; + case kOptValTypeNumber: + return flags & P_NUM; + case kOptValTypeString: + return flags & P_STRING; + default: + abort(); + } +} + +// Return C-string representation of OptVal. Caller must free the returned C-string. +static char *optval_to_cstr(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + return xstrdup(""); + case kOptValTypeBoolean: + return xstrdup(o.data.boolean ? "true" : "false"); + case kOptValTypeNumber: { + char *buf = xmalloc(NUMBUFLEN); + snprintf(buf, NUMBUFLEN, "%" PRId64, o.data.number); + return buf; + } + case kOptValTypeString: { + char *buf = xmalloc(o.data.string.size + 3); + snprintf(buf, o.data.string.size + 3, "\"%s\"", o.data.string.data); + return buf; + } + default: + abort(); + } +} + +// Get an allocated string containing a list of valid types for an option. +// For options with a singular type, it returns the name of the type. For options with multiple +// possible types, it returns a comma separated list of types. For example, if an option can be a +// number, boolean or string, the function returns "Number, Boolean, String" +static char *option_get_valid_types(int opt_idx) +{ + uint32_t flags = options[opt_idx].flags; + uint32_t type_count = 0; + + StringBuilder str = KV_INITIAL_VALUE; + kv_resize(str, 32); + +#define OPTION_ADD_TYPE(typename) \ + do { \ + if (type_count == 0) { \ + kv_concat(str, typename); \ + } else { \ + kv_printf(str, ", %s", typename); \ + } \ + type_count++; \ + } while (0); + + if (flags & P_NUM) { + OPTION_ADD_TYPE("Number"); + } + if (flags & P_BOOL) { + OPTION_ADD_TYPE("Boolean"); + } + if (flags & P_STRING) { + OPTION_ADD_TYPE("String"); + } + + if (type_count == 0) { + abort(); + } + + // Ensure that the string is NUL-terminated. + kv_push(str, NUL); + return str.items; + +#undef OPTION_ADD_TYPE +} + /// Gets the value for an option. /// -/// @param stringval NULL when only checking existence -/// @param flagsp set to the option flags (P_xxxx) (if not NULL) +/// @param[in] name Option name. +/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). +/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). +/// @param[out] hidden Whether option is hidden. /// -/// @returns: -/// Number option: gov_number, *numval gets value. -/// Tottle option: gov_bool, *numval gets value. -/// String option: gov_string, *stringval gets allocated string. -/// Hidden Number option: gov_hidden_number. -/// Hidden Toggle option: gov_hidden_bool. -/// Hidden String option: gov_hidden_string. -/// Unknown option: gov_unknown. -getoption_T get_option_value(const char *name, long *numval, char **stringval, uint32_t *flagsp, - int scope) +/// @return Option value. Returns NIL_OPTVAL for invalid options. Return value must be freed by +/// caller. +OptVal get_option_value(const char *name, uint32_t *flagsp, int scope, bool *hidden) { - if (get_tty_option(name, stringval)) { - return gov_string; + // Make sure that hidden and flagsp are never returned uninitialized + if (hidden != NULL) { + *hidden = false; + } + if (flagsp != NULL) { + *flagsp = 0; + } + + char *str; + if (get_tty_option(name, &str)) { + return CSTR_AS_OPTVAL(str); } int opt_idx = findoption(name); if (opt_idx < 0) { // option not in the table - return gov_unknown; + return NIL_OPTVAL; } char *varp = get_varp_scope(&(options[opt_idx]), scope); + if (hidden != NULL) { + *hidden = varp == NULL; + } if (flagsp != NULL) { // Return the P_xxxx option flags. @@ -3388,30 +3509,23 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, u } if (options[opt_idx].flags & P_STRING) { - if (varp == NULL) { // hidden option - return gov_hidden_string; - } - if (stringval != NULL) { - *stringval = xstrdup(*(char **)(varp)); - } - return gov_string; + return varp == NULL ? STRING_OPTVAL(STRING_INIT) : CSTR_TO_OPTVAL(*(char **)(varp)); } - if (varp == NULL) { // hidden option - return (options[opt_idx].flags & P_NUM) ? gov_hidden_number : gov_hidden_bool; - } if (options[opt_idx].flags & P_NUM) { - *numval = *(long *)varp; + return NUMBER_OPTVAL(varp == NULL ? 0 : (*(long *)varp)); } else { // Special case: 'modified' is b_changed, but we also want to consider // it set when 'ff' or 'fenc' changed. - if ((int *)varp == &curbuf->b_changed) { - *numval = curbufIsChanged(); + if (varp == NULL) { + return BOOLEAN_OPTVAL(false); + } else if ((int *)varp == &curbuf->b_changed) { + return BOOLEAN_OPTVAL(curbufIsChanged()); } else { - *numval = (long)(*(int *)varp); + int n = *(int *)varp; + return BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); } } - return (options[opt_idx].flags & P_NUM) ? gov_number : gov_bool; } // Returns the option attributes and its value. Unlike the above function it @@ -3544,20 +3658,25 @@ vimoption_T *get_option(int opt_idx) /// Set the value of an option /// -/// @param[in] name Option name. -/// @param[in] number New value for the number or boolean option. -/// @param[in] string New value for string option. +/// @param[in] name Option name. +/// @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). /// If OPT_CLEAR is set, the value of the option /// is cleared (the exact semantics of this depend /// on the option). /// /// @return NULL on success, an untranslated error message on error. -const char *set_option_value(const char *const name, const long number, const char *const string, - const int opt_flags) +const char *set_option_value(const char *const name, const OptVal value, int opt_flags) FUNC_ATTR_NONNULL_ARG(1) { - static char errbuf[80]; + static const char *optval_type_names[] = { + [kOptValTypeNil] = "Nil", + [kOptValTypeBoolean] = "Boolean", + [kOptValTypeNumber] = "Number", + [kOptValTypeString] = "String" + }; + + static char errbuf[IOSIZE]; if (is_tty_option(name)) { return NULL; // Fail silently; many old vimrcs set t_xx options. @@ -3565,23 +3684,14 @@ const char *set_option_value(const char *const name, const long number, const ch int opt_idx = findoption(name); if (opt_idx < 0) { - semsg(_("E355: Unknown option: %s"), name); - return NULL; + snprintf(errbuf, IOSIZE, _(e_unknown_option2), name); + return errbuf; } uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { - emsg(_(e_sandbox)); - return NULL; - } - - if (flags & P_STRING) { - const char *s = string; - if (s == NULL || opt_flags & OPT_CLEAR) { - s = ""; - } - return set_string_option(opt_idx, s, opt_flags, errbuf, sizeof(errbuf)); + return _(e_sandbox); } char *varp = get_varp_scope(&(options[opt_idx]), opt_flags); @@ -3590,46 +3700,81 @@ const char *set_option_value(const char *const name, const long number, const ch return NULL; } - if (number == 0 && string != NULL) { - int idx; - - // Either we are given a string or we are setting option - // to zero. - for (idx = 0; string[idx] == '0'; idx++) {} - if (string[idx] != NUL || idx == 0) { - // There's another character after zeros or the string - // is empty. In both cases, we are trying to set a - // num option using a string. - semsg(_("E521: Number required: &%s = '%s'"), - name, string); - return NULL; // do nothing as we hit an error - } - } - long numval = number; - if (opt_flags & OPT_CLEAR) { - if ((int *)varp == &curbuf->b_p_ar) { - numval = -1; - } else if ((long *)varp == &curbuf->b_p_ul) { - numval = NO_LOCAL_UNDOLEVEL; - } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { - numval = -1; - } else { - char *s = NULL; - (void)get_option_value(name, &numval, &s, NULL, OPT_GLOBAL); + const char *errmsg = NULL; + // Copy the value so we can modify the copy. + OptVal v = optval_copy(value); + + if (v.type == kOptValTypeNil) { + opt_flags |= OPT_CLEAR; + + // 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; + } else if (flags & P_NUM) { + v.type = kOptValTypeNumber; + } else if (flags & P_STRING) { + v.type = kOptValTypeString; + } + } 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, _("E5383: Allowed types for option '%s': %s. Got %s value: %s"), + name, valid_types, optval_type_names[v.type], rep); + xfree(rep); + xfree(valid_types); + errmsg = errbuf; + goto end; + } + + switch (v.type) { + case kOptValTypeNil: + abort(); // This will never happen. + case kOptValTypeBoolean: { + if (opt_flags & OPT_CLEAR) { + if ((int *)varp == &curbuf->b_p_ar) { + v.data.boolean = kNone; + } else { + v = get_option_value(name, NULL, OPT_GLOBAL, NULL); + } } + errmsg = set_bool_option(opt_idx, varp, (int)v.data.boolean, opt_flags); + break; + } + case kOptValTypeNumber: { + if (opt_flags & OPT_CLEAR) { + if ((long *)varp == &curbuf->b_p_ul) { + v.data.number = NO_LOCAL_UNDOLEVEL; + } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { + v.data.number = -1; + } else { + v = get_option_value(name, NULL, OPT_GLOBAL, NULL); + } + } + errmsg = set_num_option(opt_idx, varp, (long)v.data.number, errbuf, sizeof(errbuf), opt_flags); + break; + } + case kOptValTypeString: { + const char *s = v.data.string.data; + if (s == NULL || opt_flags & OPT_CLEAR) { + s = ""; + } + errmsg = set_string_option(opt_idx, s, opt_flags, errbuf, sizeof(errbuf)); + break; } - if (flags & P_NUM) { - return set_num_option(opt_idx, varp, numval, errbuf, sizeof(errbuf), opt_flags); } - return set_bool_option(opt_idx, varp, (int)numval, opt_flags); + +end: + optval_free(v); // Free the copied OptVal. + return errmsg; } /// Call set_option_value() and when an error is returned report it. /// /// @param opt_flags OPT_LOCAL or 0 (both) -void set_option_value_give_err(const char *name, long number, const char *string, int opt_flags) +void set_option_value_give_err(const char *name, OptVal value, int opt_flags) { - const char *errmsg = set_option_value(name, number, string, opt_flags); + const char *errmsg = set_option_value(name, value, opt_flags); if (errmsg != NULL) { emsg(_(errmsg)); |