From cb7c0191675454ed81be73aef6c08596b89d9985 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 28 Jul 2026 22:14:21 +0200 Subject: [PATCH 1/2] refactor(options): drop OptVal, use Object Problem: The object subsystem has an intermediate representation for no real reason. Besides the code cost, this also adds an extra (api <=> OptVal) conversion step, which is a (small) perf cost. Solution: We already have `Object`, so use it instead. - drop `OptVal`, `OptValData`, `OptValType`, and related boilerplate. - add `kObjectTypeUnset`. --- src/gen/gen_options.lua | 11 +- src/nvim/api/deprecated.c | 32 +- src/nvim/api/options.c | 46 ++- src/nvim/api/private/converter.c | 1 + src/nvim/api/private/defs.h | 3 + src/nvim/api/private/helpers.c | 4 + src/nvim/api/private/helpers.h | 1 + src/nvim/api/vim.c | 4 +- src/nvim/autocmd.c | 8 +- src/nvim/context.c | 4 +- src/nvim/diff.c | 4 +- src/nvim/eval.c | 8 +- src/nvim/eval/funcs.c | 2 +- src/nvim/eval/vars.c | 87 ++--- src/nvim/ex_cmds.c | 8 +- src/nvim/ex_docmd.c | 6 +- src/nvim/ex_getln.c | 4 +- src/nvim/fileio.c | 4 +- src/nvim/help.c | 6 +- src/nvim/highlight_group.c | 2 +- src/nvim/indent.c | 2 +- src/nvim/insexpand.c | 8 +- src/nvim/lua/converter.c | 1 + src/nvim/main.c | 26 +- src/nvim/memline.c | 2 +- src/nvim/msgpack_rpc/packer.c | 1 + src/nvim/option.c | 575 ++++++++++++++----------------- src/nvim/option.h | 26 +- src/nvim/option_defs.h | 31 +- src/nvim/optionstr.c | 20 +- src/nvim/popupmenu.c | 10 +- src/nvim/quickfix.c | 14 +- src/nvim/runtime.c | 2 +- src/nvim/spell.c | 4 +- src/nvim/spellfile.c | 2 +- src/nvim/tag.c | 4 +- src/nvim/terminal.c | 2 +- src/nvim/ui.c | 2 +- src/nvim/window.c | 4 +- src/nvim/winfloat.c | 2 +- 40 files changed, 463 insertions(+), 520 deletions(-) diff --git a/src/gen/gen_options.lua b/src/gen/gen_options.lua index 0ee9f3d67a..570516f7c7 100644 --- a/src/gen/gen_options.lua +++ b/src/gen/gen_options.lua @@ -144,7 +144,9 @@ end --- @param opt_type vim.option_type --- @return string local function opt_type_enum(opt_type) - return ('kOptValType%s'):format(lowercase_to_titlecase(opt_type)) + return ('kObjectType%s'):format( + ({ boolean = 'Boolean', number = 'Integer', string = 'String' })[opt_type] + ) end --- @param scope vim.option_scope @@ -213,7 +215,10 @@ local function get_opt_val(v) end end - return ('{ .type = %s, .data.%s = %s }'):format(opt_type_enum(v_type), v_type, v) + -- def_val is stored as an API Object. + local obj_type = ({ boolean = 'Boolean', number = 'Integer', string = 'String' })[v_type] + local obj_field = ({ boolean = 'boolean', number = 'integer', string = 'string' })[v_type] + return ('{ .type = kObjectType%s, .data.%s = %s }'):format(obj_type, obj_field, v) end --- @param d vim.option_value|function @@ -281,7 +286,7 @@ local function dump_option(i, o, write) end if not o.defaults then - write(' .def_val=NIL_OPTVAL') + write(' .def_val={ .type = kObjectTypeNil }') elseif o.defaults.condition then write(('#if defined(%s)'):format(o.defaults.condition)) write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name)) diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index d3958aaa8c..d00dde2e1c 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -743,21 +743,19 @@ static Object get_option_from(void *from, OptScope scope, String name, Error *er return (Object)OBJECT_INIT; }); - OptVal value = NIL_OPTVAL; - - if (option_has_scope(opt_idx, scope)) { - value = get_option_value_for(opt_idx, scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL, - scope, from, err); - if (ERROR_SET(err)) { - return (Object)OBJECT_INIT; - } - } - - VALIDATE_S(value.type != kOptValTypeNil, "option name", name.data, { + // Reject a scope the option doesn't support. This must be an explicit check: an unset value is + // itself Nil, so a Nil value can't distinguish "unsupported scope" from "unset value". + VALIDATE_S(option_has_scope(opt_idx, scope), "option name", name.data, { return (Object)OBJECT_INIT; }); - return optval_as_object(value); + Object value = get_option_value_for(opt_idx, scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL, + scope, from, err); + if (ERROR_SET(err)) { + return (Object)OBJECT_INIT; + } + + return value; } /// Sets the value of a global or local (buffer, window) option. @@ -779,16 +777,16 @@ static void set_option_to(uint64_t channel_id, void *to, OptScope scope, String return; }); - bool error = false; - OptVal optval = object_as_optval(value, &error); - - // Handle invalid option value type. + // Only scalar Objects (nil/boolean/number/string) are valid option values. // Don't use `name` in the error message here, because `name` can be any String. // No need to check if value type actually matches the types for the option, as set_option_value() // already handles that. - VALIDATE_EXP(!error, "value", "valid option type", api_typename(value.type), { + const bool valid = value.type == kObjectTypeNil || value.type == kObjectTypeBoolean + || value.type == kObjectTypeInteger || value.type == kObjectTypeString; + VALIDATE_EXP(valid, "value", "valid option type", api_typename(value.type), { return; }); + Object optval = value; // For global-win-local options -> setlocal // For win-local options -> setglobal and setlocal (opt_flags == 0) diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 7fed024d0f..bf47ff4041 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -125,9 +125,8 @@ static int validate_option_value_args(Dict(option) *opts, char *name, bool allow }); } - VALIDATE_CON(*operation == OP_NONE || option_has_type(*opt_idxp, - kOptValTypeString) - || option_has_type(*opt_idxp, kOptValTypeNumber), + VALIDATE_CON(*operation == OP_NONE || option_has_type(*opt_idxp, kObjectTypeString) + || option_has_type(*opt_idxp, kObjectTypeInteger), opts->operation.data, "boolean options", { return FAIL; @@ -188,8 +187,8 @@ static buf_T *do_ft_buf(const char *filetype, CtxSwitch *aco, Error *err) // Set curwin/curbuf to buf and save a few things. ctx_switch(aco, NULL, NULL, ftbuf, 0); - set_option_direct(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, SID_NONE); - set_option_direct(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, SID_NONE); + set_option_direct(kOptBufhidden, STATIC_CSTR_AS_OBJ("hide"), OPT_LOCAL, SID_NONE); + set_option_direct(kOptBuftype, STATIC_CSTR_AS_OBJ("nofile"), OPT_LOCAL, SID_NONE); assert(ftbuf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already ftbuf->b_p_swf = false; ftbuf->b_p_ml = false; @@ -289,7 +288,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) from = ftbuf; } - OptVal value = get_option_value_for(opt_idx, opt_flags, scope, from, err); + Object value = get_option_value_for(opt_idx, opt_flags, scope, from, err); // Restore curwin/curbuf and a few other things. ctx_restore(&aco); @@ -298,17 +297,11 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) } if (ERROR_SET(err)) { - goto err; + api_free_object(value); + return (Object)OBJECT_INIT; } - VALIDATE_S(value.type != kOptValTypeNil, "option", name.data, { - goto err; - }); - - return optval_as_object(value); -err: - optval_free(value); - return (Object)OBJECT_INIT; + return value; } /// Sets the value of an option. The behavior of this function matches that of @@ -361,15 +354,15 @@ Object nvim_set_option_value(uint64_t channel_id, String name, Object value, Dic } } - // Convert the incoming value into an OptVal. + // Convert the incoming value into an Object. bool error = false; - OptVal optval_right = object_as_optval_for(opt_idx, value, operation, &error); + Object optval_right = object_as_optval(opt_idx, value, operation, &error); VALIDATE_EXP(!error, name.data, "a valid type", api_typename(value.type), { return NIL; }); - OptVal merged_val = NIL_OPTVAL; + Object merged_val = NIL; const char *errmsg = NULL; vimoption_T *option = get_option(opt_idx); @@ -381,9 +374,10 @@ Object nvim_set_option_value(uint64_t channel_id, String name, Object value, Dic char *argp = NULL; switch (optval_right.type) { - case kOptValTypeNil: + case kObjectTypeUnset: + case kObjectTypeNil: break; - case kOptValTypeString: { + case kObjectTypeString: { char *optval_escaped = escape_option_str_cmdline(optval_right.data.string.data); // We need a leading equal sign because get_option_newval is used for // cmdline stuff and expects an = @@ -391,18 +385,20 @@ Object nvim_set_option_value(uint64_t channel_id, String name, Object value, Dic XFREE_CLEAR(optval_escaped); break; } - case kOptValTypeNumber: - argp = arena_printf(arena, "=%" PRId64, optval_right.data.number).data; + case kObjectTypeInteger: + argp = arena_printf(arena, "=%" PRId64, optval_right.data.integer).data; break; - case kOptValTypeBoolean: + case kObjectTypeBoolean: merged_val = optval_right; break; + default: + abort(); } optval_free(optval_right); - if (optval_right.type == kOptValTypeNumber || optval_right.type == kOptValTypeString) { - OptVal oldval = optval_from_varp(opt_idx, varp); + if (optval_right.type == kObjectTypeInteger || optval_right.type == kObjectTypeString) { + Object oldval = opt_from_varp(opt_idx, varp); merged_val = get_option_newval(opt_idx, opt_flags, PREFIX_NONE, &argp, 0, operation, option->flags, varp, &oldval, NULL, 0, &errmsg); VALIDATE(errmsg == NULL, "%s", errmsg, { diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 7a1b715fef..dac13273a4 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -278,6 +278,7 @@ void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Erro tv->v_lock = VAR_UNLOCKED; switch (obj->type) { + case kObjectTypeUnset: case kObjectTypeNil: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 4113f2aaae..69cc2c4f6c 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -104,6 +104,9 @@ typedef enum { kObjectTypeArray, kObjectTypeDict, kObjectTypeLuaRef, + /// Internal-only: for unset options (e.g. the local value of a global-local option). + /// Never crosses RPC/Lua boundary (becomes Nil). + kObjectTypeUnset, // EXT types, cannot be split or reordered, see #EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer, kObjectTypeWindow, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9cf2c396c2..321642ec89 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -540,6 +540,7 @@ Array arena_take_arraybuilder(Arena *arena, ArrayBuilder *arr) void api_free_object(Object value) { switch (value.type) { + case kObjectTypeUnset: case kObjectTypeNil: case kObjectTypeBoolean: case kObjectTypeInteger: @@ -659,6 +660,7 @@ Dict copy_dict(Dict dict, Arena *arena) Object copy_object(Object obj, Arena *arena) { switch (obj.type) { + case kObjectTypeUnset: case kObjectTypeBuffer: case kObjectTypeTabpage: case kObjectTypeWindow: @@ -740,6 +742,8 @@ int object_to_hl_id(Object obj, const char *what, Error *err) char *api_typename(ObjectType t) { switch (t) { + case kObjectTypeUnset: + return "unset"; case kObjectTypeNil: return "nil"; case kObjectTypeBoolean: diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 0436b05ce5..33356206df 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -62,6 +62,7 @@ .data.luaref = r }) #define NIL ((Object)OBJECT_INIT) +#define UNSET ((Object) { .type = kObjectTypeUnset }) #define NULL_STRING ((String)STRING_INIT) #define HAS_KEY(d, typ, key) (((d)->is_set__##typ##_ & (1ULL << KEYSET_OPTIDX_##typ##__##key)) != 0) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index c595d47b2e..367f2209a2 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1068,9 +1068,9 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (scratch) { - set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, + set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OBJ("hide"), OPT_LOCAL, 0, kOptScopeBuf, buf); - set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, + set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OBJ("nofile"), OPT_LOCAL, 0, kOptScopeBuf, buf); assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already buf->b_p_swf = false; diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index e2921f6978..6a80162eae 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -703,7 +703,7 @@ char *au_event_disable(char *what) } else { STRCPY(new_ei + p_ei_len, what); } - set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(new_ei), 0, SID_NONE); + set_option_direct(kOptEventignore, CSTR_AS_OBJ(new_ei), 0, SID_NONE); xfree(new_ei); return save_ei; } @@ -711,7 +711,7 @@ char *au_event_disable(char *what) void au_event_restore(char *old_ei) { if (old_ei != NULL) { - set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(old_ei), 0, SID_NONE); + set_option_direct(kOptEventignore, CSTR_AS_OBJ(old_ei), 0, SID_NONE); xfree(old_ei); } } @@ -1316,8 +1316,8 @@ static void deferred_optionset_modified(void **argv) api_clear_error(&err); if (buf) { bool new_val = (bool)(uintptr_t)argv[1]; - OptVal old = BOOLEAN_OPTVAL(!new_val); - OptVal new = BOOLEAN_OPTVAL(new_val); + Object old = BOOLEAN_OBJ(!new_val); + Object new = BOOLEAN_OBJ(new_val); CtxSwitch aco = { 0 }; ctx_switch(&aco, NULL, NULL, buf, 0); apply_optionset_autocmd_now(kOptModified, OPT_LOCAL, old, old, old, new, NULL); diff --git a/src/nvim/context.c b/src/nvim/context.c index d1f97be646..11e15d7466 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -131,8 +131,8 @@ void ctx_save(Context *ctx, const int flags) void ctx_load(Context *ctx, const int flags) FUNC_ATTR_NONNULL_ALL { - OptVal op_shada = get_option_value(kOptShada, OPT_GLOBAL); - set_option_value(kOptShada, STATIC_CSTR_AS_OPTVAL("!,'100,%"), OPT_GLOBAL); + Object op_shada = get_option_value(kOptShada, OPT_GLOBAL); + set_option_value(kOptShada, STATIC_CSTR_AS_OBJ("!,'100,%"), OPT_GLOBAL); if (flags & kCtxRegs) { shada_read_string(ctx->regs, kShaDaWantInfo | kShaDaForceit); diff --git a/src/nvim/diff.c b/src/nvim/diff.c index d0f534d340..f85a1f0824 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1519,7 +1519,7 @@ static void set_diff_option(win_T *wp, bool value) curwin = wp; curbuf = curwin->w_buffer; curbuf->b_ro_locked++; - set_option_value_give_err(kOptDiff, BOOLEAN_OPTVAL(value), OPT_LOCAL); + set_option_value_give_err(kOptDiff, BOOLEAN_OBJ(value), OPT_LOCAL); curbuf->b_ro_locked--; curwin = old_curwin; curbuf = curwin->w_buffer; @@ -1561,7 +1561,7 @@ void diff_win_options(win_T *wp, bool addbuf) } wp->w_p_fdm_save = xstrdup(wp->w_p_fdm); } - set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, + set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OBJ("diff"), OPT_LOCAL, 0, kOptScopeWin, wp); if (!wp->w_p_diff) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cd60391725..7b6d21f9f9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3405,10 +3405,10 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua ret = FAIL; } else if (rettv != NULL) { - OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, opt_flags); - assert(value.type != kOptValTypeNil); + Object value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, opt_flags); + assert(value.type != kObjectTypeNil); - *rettv = optval_as_tv(value, true); + *rettv = opt_to_tv(value, true); } else if (working && !is_tty_opt && is_option_hidden(opt_idx)) { ret = FAIL; } @@ -6459,7 +6459,7 @@ char *do_string_sub(char *str, size_t len, char *pat, char *sub, typval_T *expr, // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OBJ(save_cpo), 0); } free_string_option(save_cpo); } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e4d3a90741..2011fea5fc 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6202,7 +6202,7 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OBJ(save_cpo), 0); } free_string_option(save_cpo); } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index e535d894dc..29844714e5 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1371,35 +1371,35 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, bool is_tty_opt = is_tty_option(arg); bool hidden = is_option_hidden(opt_idx); - OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, opt_flags); - OptVal newval = NIL_OPTVAL; + Object curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, opt_flags); + Object newval = NIL; - if (curval.type == kOptValTypeNil) { + if (curval.type == kObjectTypeNil) { semsg(_(e_unknown_option2), arg); goto theend; } if (op != NULL && *op != '=' - && ((curval.type != kOptValTypeString && *op == '.') - || (curval.type == kOptValTypeString && *op != '.'))) { + && ((curval.type != kObjectTypeString && *op == '.') + || (curval.type == kObjectTypeString && *op != '.'))) { semsg(_(e_letwrong), op); goto theend; } bool error; - newval = tv_to_optval(tv, opt_idx, arg, &error); + newval = opt_from_tv(tv, opt_idx, arg, &error); if (error) { goto theend; } // Current value and new value must have the same type. assert(curval.type == newval.type); - const bool is_num = curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean; - const bool is_string = curval.type == kOptValTypeString; + const bool is_num = curval.type == kObjectTypeInteger || curval.type == kObjectTypeBoolean; + const bool is_string = curval.type == kObjectTypeString; if (op != NULL && *op != '=') { if (!hidden && is_num) { // number or bool - OptInt cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; - OptInt new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; + OptInt cur_n = curval.type == kObjectTypeInteger ? curval.data.integer : curval.data.boolean; + OptInt new_n = newval.type == kObjectTypeInteger ? newval.data.integer : newval.data.boolean; switch (*op) { case '+': @@ -1414,18 +1414,18 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, new_n = num_modulus(cur_n, new_n); break; } - if (curval.type == kOptValTypeNumber) { - newval = NUMBER_OPTVAL(new_n); + if (curval.type == kObjectTypeInteger) { + newval = INTEGER_OBJ(new_n); } else { - newval = BOOLEAN_OPTVAL(TRISTATE_FROM_INT(new_n)); + newval = opt_from_tristate(TRISTATE_FROM_INT(new_n)); } } else if (!hidden && is_string) { // string const char *curval_data = curval.data.string.data; const char *newval_data = newval.data.string.data; if (curval_data != NULL && newval_data != NULL) { - OptVal newval_old = newval; - newval = CSTR_AS_OPTVAL(concat_str(curval_data, newval_data)); + Object newval_old = newval; + newval = CSTR_AS_OBJ(concat_str(curval_data, newval_data)); optval_free(newval_old); } } @@ -3186,24 +3186,23 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL); } -/// Convert typval to option value for a particular option. +/// Converts a typval to a structured option value. /// /// @param[in] tv typval to convert. /// @param[in] option Option name. /// @param[in] flags Option flags. /// @param[out] error Whether an error occurred. /// -/// @return Typval converted to OptVal. Must be freed by caller. -/// Returns NIL_OPTVAL for invalid option name. -static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, bool *error) +/// @return Structured option, or NIL if invalid option name. Must be freed by caller. +static Object opt_from_tv(typval_T *tv, OptIndex opt_idx, const char *option, bool *error) { - OptVal value = NIL_OPTVAL; + Object value = NIL; char nbuf[NUMBUFLEN]; bool err = false; const bool is_tty_opt = is_tty_option(option); - const bool option_has_bool = !is_tty_opt && option_has_type(opt_idx, kOptValTypeBoolean); - const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kOptValTypeNumber); - const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kOptValTypeString); + const bool option_has_bool = !is_tty_opt && option_has_type(opt_idx, kObjectTypeBoolean); + const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kObjectTypeInteger); + const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kObjectTypeString); if (!is_tty_opt && (get_option(opt_idx)->flags & kOptFlagFunc) && tv_is_func(*tv)) { // If the option can be set to a function reference or a lambda @@ -3211,7 +3210,7 @@ static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, b // the name (string) of the function reference. char *strval = encode_tv2string(tv, NULL); err = strval == NULL; - value = CSTR_AS_OPTVAL(strval); + value = CSTR_AS_OBJ(strval); } else if (option_has_bool || option_has_num) { varnumber_T n = option_has_num ? tv_get_number_chk(tv, &err) : tv_get_bool_chk(tv, &err); // This could be either "0" or a string that's not a number. @@ -3227,13 +3226,13 @@ static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, b tv->vval.v_string == NULL ? "" : tv->vval.v_string); } } - value = option_has_num ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); + value = option_has_num ? INTEGER_OBJ((OptInt)n) : opt_from_tristate(TRISTATE_FROM_INT(n)); } else if (option_has_str) { // Avoid setting string option to a boolean or a special value. if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { const char *strval = tv_get_string_buf_chk(tv, nbuf); err = strval == NULL; - value = CSTR_TO_OPTVAL(strval); + value = CSTR_TO_OBJ(strval); } else if (!is_tty_opt) { err = true; emsg(_(e_string_required)); @@ -3248,37 +3247,47 @@ static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, b return value; } -/// Convert an option value to typval. +/// Converts an option value to typval. /// /// @param[in] value Option value to convert. /// @param numbool Whether to convert boolean values to number. /// Used for backwards compatibility. /// -/// @return OptVal converted to typval. -typval_T optval_as_tv(OptVal value, bool numbool) +/// @return Object converted to typval. +typval_T opt_to_tv(Object value, bool numbool) { typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } }; switch (value.type) { - case kOptValTypeNil: + case kObjectTypeUnset: + // Legacy numbool callers (e.g. `&l:autoread` unset global-local boolean) + // expect the kNone; otherwise an unset value is v:null. + if (numbool) { + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = kNone; + } break; - case kOptValTypeBoolean: + case kObjectTypeNil: + break; // Return v:null. + case kObjectTypeBoolean: if (numbool) { rettv.v_type = VAR_NUMBER; rettv.vval.v_number = value.data.boolean; - } else if (value.data.boolean != kNone) { + } else { rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = value.data.boolean == kTrue; + rettv.vval.v_bool = value.data.boolean ? kBoolVarTrue : kBoolVarFalse; } - break; // return v:null for None boolean value. - case kOptValTypeNumber: - rettv.v_type = VAR_NUMBER; - rettv.vval.v_number = value.data.number; break; - case kOptValTypeString: + case kObjectTypeInteger: + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = value.data.integer; + break; + case kObjectTypeString: rettv.v_type = VAR_STRING; rettv.vval.v_string = value.data.string.data; break; + default: + abort(); // Should never happen. } return rettv; @@ -3294,7 +3303,7 @@ static void set_option_from_tv(const char *varname, typval_T *varp) } bool error = false; - OptVal value = tv_to_optval(varp, opt_idx, varname, &error); + Object value = opt_from_tv(varp, opt_idx, varname, &error); if (!error) { const char *errmsg = set_option_value_handle_tty(varname, opt_idx, value, OPT_LOCAL); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 7bbe4f223c..18cb5c534a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4512,7 +4512,7 @@ skip: // Show 'inccommand' preview if there are matched lines. if (cmdpreview_ns > 0 && !aborting()) { if (got_quit || profile_passed_limit(tm)) { // Too slow, disable. - set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL(""), 0, SID_NONE); + set_option_direct(kOptInccommand, STATIC_CSTR_AS_OBJ(""), 0, SID_NONE); } else if (*p_icm != NUL && pat.data != NULL) { if (pre_hl_id == 0) { pre_hl_id = syn_check_group(S_LEN("Substitute")); @@ -4811,7 +4811,7 @@ bool prepare_tagpreview(bool undo_sync, bool use_previewpopup) RESET_BINDING(curwin); // don't take over 'scrollbind' and 'cursorbind' curwin->w_p_diff = false; // no 'diff' - set_option_direct(kOptFoldcolumn, STATIC_CSTR_AS_OPTVAL("0"), 0, SID_NONE); // no 'foldcolumn' + set_option_direct(kOptFoldcolumn, STATIC_CSTR_AS_OBJ("0"), 0, SID_NONE); // no 'foldcolumn' return true; } @@ -4830,7 +4830,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i buf_T *cmdpreview_buf = NULL; // disable file info message - set_option_direct(kOptShortmess, STATIC_CSTR_AS_OPTVAL("F"), 0, SID_NONE); + set_option_direct(kOptShortmess, STATIC_CSTR_AS_OBJ("F"), 0, SID_NONE); // Place cursor on nearest matching line, to undo do_sub() cursor placement. for (size_t i = 0; i < lines.subresults.size; i++) { @@ -4931,7 +4931,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i xfree(str); - set_option_direct(kOptShortmess, CSTR_AS_OPTVAL(save_shm_p), 0, SID_NONE); + set_option_direct(kOptShortmess, CSTR_AS_OBJ(save_shm_p), 0, SID_NONE); xfree(save_shm_p); return preview ? 2 : 1; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index af0cc12ee4..452b31474d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2749,7 +2749,7 @@ void apply_cmdmod(cmdmod_T *cmod) // Set 'eventignore' to "all". // First save the existing option value for restoring it later. cmod->cmod_save_ei = xstrdup(p_ei); - set_option_direct(kOptEventignore, STATIC_CSTR_AS_OPTVAL("all"), 0, SID_NONE); + set_option_direct(kOptEventignore, STATIC_CSTR_AS_OBJ("all"), 0, SID_NONE); } } @@ -2769,7 +2769,7 @@ void undo_cmdmod(cmdmod_T *cmod) if (cmod->cmod_save_ei != NULL) { // Restore 'eventignore' to the value before ":noautocmd". - set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(cmod->cmod_save_ei), 0, SID_NONE); + set_option_direct(kOptEventignore, CSTR_AS_OBJ(cmod->cmod_save_ei), 0, SID_NONE); free_string_option(cmod->cmod_save_ei); cmod->cmod_save_ei = NULL; } @@ -8007,7 +8007,7 @@ static void ex_setfiletype(exarg_T *eap) arg += 9; } - set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL); + set_option_value_give_err(kOptFiletype, CSTR_AS_OBJ(arg), OPT_LOCAL); if (arg != eap->arg) { curbuf->b_did_filetype = false; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index bad0b4b743..a372e264a5 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -982,7 +982,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear need_wait_return = false; } - set_option_direct(kOptInccommand, CSTR_AS_OPTVAL(s->save_p_icm), 0, SID_NONE); + set_option_direct(kOptInccommand, CSTR_AS_OBJ(s->save_p_icm), 0, SID_NONE); State = s->save_State; if (cmdpreview != save_cmdpreview) { cmdpreview = save_cmdpreview; // restore preview state @@ -2712,7 +2712,7 @@ static bool cmdpreview_may_show(CommandLineState *s) // Open preview buffer if inccommand=split. if (icm_split && (cmdpreview_buf = cmdpreview_open_buf()) == NULL) { // Failed to create preview buffer, so disable preview. - set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL("nosplit"), 0, SID_NONE); + set_option_direct(kOptInccommand, STATIC_CSTR_AS_OBJ("nosplit"), 0, SID_NONE); icm_split = false; } // Setup preview namespace if it's not already set. diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 18a727f9c7..993f78315f 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1681,7 +1681,7 @@ failed: save_file_ff(curbuf); // If editing a new file: set 'fenc' for the current buffer. // Also for ":read ++edit file". - set_option_direct(kOptFileencoding, CSTR_AS_OPTVAL(fenc), OPT_LOCAL, 0); + set_option_direct(kOptFileencoding, CSTR_AS_OBJ(fenc), OPT_LOCAL, 0); } if (fenc_alloced) { xfree(fenc); @@ -2023,7 +2023,7 @@ void set_forced_fenc(exarg_T *eap) } char *fenc = enc_canonize(eap->cmd + eap->force_enc); - set_option_direct(kOptFileencoding, CSTR_AS_OPTVAL(fenc), OPT_LOCAL, 0); + set_option_direct(kOptFileencoding, CSTR_AS_OBJ(fenc), OPT_LOCAL, 0); xfree(fenc); } diff --git a/src/nvim/help.c b/src/nvim/help.c index 546591ad13..a6b5f5b5d9 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -423,7 +423,7 @@ void cleanup_help_tags(int num_file, char **file) void prepare_help_buffer(void) { curbuf->b_help = true; - set_option_direct(kOptBuftype, STATIC_CSTR_AS_OPTVAL("help"), OPT_LOCAL, 0); + set_option_direct(kOptBuftype, STATIC_CSTR_AS_OBJ("help"), OPT_LOCAL, 0); // Always set these options after jumping to a help tag, because the // user may have an autocommand that gets in the way. @@ -432,13 +432,13 @@ void prepare_help_buffer(void) // Only set it when needed, buf_init_chartab() is some work. char *p = "!-~,^*,^|,^\",192-255"; if (strcmp(curbuf->b_p_isk, p) != 0) { - set_option_direct(kOptIskeyword, CSTR_AS_OPTVAL(p), OPT_LOCAL, 0); + set_option_direct(kOptIskeyword, CSTR_AS_OBJ(p), OPT_LOCAL, 0); check_buf_options(curbuf); buf_init_chartab(curbuf, false); } // Don't use the global foldmethod. - set_option_direct(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL, 0); + set_option_direct(kOptFoldmethod, STATIC_CSTR_AS_OBJ("manual"), OPT_LOCAL, 0); curbuf->b_p_ts = 8; // 'tabstop' is 8. curwin->w_p_list = false; // No list mode. diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index aa67905870..558a152d39 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -1448,7 +1448,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) && dark != (*p_bg == 'd') && !option_was_set(kOptBackground)) { set_option_value_give_err(kOptBackground, - CSTR_AS_OPTVAL(dark ? "dark" : "light"), 0); + CSTR_AS_OBJ(dark ? "dark" : "light"), 0); reset_option_was_set(kOptBackground); } } diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 6d8f210755..7184debea7 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -1600,7 +1600,7 @@ void ex_retab(exarg_T *eap) colnr_T *old_vts_ary = curbuf->b_p_vts_array; if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) { - set_option_direct(kOptVartabstop, CSTR_AS_OPTVAL(new_ts_str), OPT_LOCAL, 0); + set_option_direct(kOptVartabstop, CSTR_AS_OBJ(new_ts_str), OPT_LOCAL, 0); curbuf->b_p_vts_array = new_vts_array; xfree(old_vts_ary); } else { diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 0fb48f2a8f..bcfe66f08b 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -3036,9 +3036,9 @@ const char *did_set_completefunc(optset_T *args) int retval; if (args->os_flags & OPT_LOCAL) { - retval = option_set_callback_func(args->os_newval.string.data, &buf->b_cfu_cb); + retval = option_set_callback_func(args->os_newval.data.string.data, &buf->b_cfu_cb); } else { - retval = option_set_callback_func(args->os_newval.string.data, &cfu_cb); + retval = option_set_callback_func(args->os_newval.data.string.data, &cfu_cb); if (retval == OK && !(args->os_flags & OPT_GLOBAL)) { set_buflocal_cfu_callback(buf); } @@ -3064,9 +3064,9 @@ const char *did_set_omnifunc(optset_T *args) int retval; if (args->os_flags & OPT_LOCAL) { - retval = option_set_callback_func(args->os_newval.string.data, &buf->b_ofu_cb); + retval = option_set_callback_func(args->os_newval.data.string.data, &buf->b_ofu_cb); } else { - retval = option_set_callback_func(args->os_newval.string.data, &ofu_cb); + retval = option_set_callback_func(args->os_newval.data.string.data, &ofu_cb); if (retval == OK && !(args->os_flags & OPT_GLOBAL)) { set_buflocal_ofu_callback(buf); } diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index de029d4899..d9a4e0a0c9 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -741,6 +741,7 @@ void nlua_push_Object(lua_State *lstate, Object *obj, int flags) FUNC_ATTR_NONNULL_ALL { switch (obj->type) { + case kObjectTypeUnset: case kObjectTypeNil: if (flags & kNluaPushSpecial) { lua_pushnil(lstate); diff --git a/src/nvim/main.c b/src/nvim/main.c index 8ecd860f5d..6d068ffd42 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1189,7 +1189,7 @@ static void command_line_scan(mparm_T *parmp) } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) { parmp->use_vimrc = "NONE"; parmp->clean = true; - set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0); + set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OBJ("NONE"), 0); } else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) { nlua_disable_preload = true; } else { @@ -1203,7 +1203,7 @@ static void command_line_scan(mparm_T *parmp) } break; case 'A': // "-A" start in Arabic mode. - set_option_value_give_err(kOptArabic, BOOLEAN_OPTVAL(true), 0); + set_option_value_give_err(kOptArabic, BOOLEAN_OBJ(true), 0); break; case 'b': // "-b" binary mode. // Needs to be effective before expanding file names, because @@ -1233,8 +1233,8 @@ static void command_line_scan(mparm_T *parmp) usage(); os_exit(0); case 'H': // "-H" start in Hebrew mode: rl + keymap=hebrew set. - set_option_value_give_err(kOptKeymap, STATIC_CSTR_AS_OPTVAL("hebrew"), 0); - set_option_value_give_err(kOptRightleft, BOOLEAN_OPTVAL(true), 0); + set_option_value_give_err(kOptKeymap, STATIC_CSTR_AS_OBJ("hebrew"), 0); + set_option_value_give_err(kOptRightleft, BOOLEAN_OBJ(true), 0); break; case 'M': // "-M" no changes or writing of files reset_modifiable(); @@ -1293,7 +1293,7 @@ static void command_line_scan(mparm_T *parmp) exmode_active = true; parmp->no_swap_file = true; if (p_shadafile == NULL || *p_shadafile == NUL) { - set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0); + set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OBJ("NONE"), 0); } } else { // "-s {scriptin}" read from script file want_argument = true; @@ -1318,7 +1318,7 @@ static void command_line_scan(mparm_T *parmp) // default is 10: a little bit verbose p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value_give_err(kOptVerbosefile, CSTR_AS_OPTVAL(argv[0] + argv_idx), 0); + set_option_value_give_err(kOptVerbosefile, CSTR_AS_OBJ(argv[0] + argv_idx), 0); argv_idx = (int)strlen(argv[0]); } break; @@ -1326,7 +1326,7 @@ static void command_line_scan(mparm_T *parmp) // "-w {scriptout}" write to script if (ascii_isdigit((argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value_give_err(kOptWindow, NUMBER_OPTVAL((OptInt)n), 0); + set_option_value_give_err(kOptWindow, INTEGER_OBJ((OptInt)n), 0); break; } want_argument = true; @@ -1424,7 +1424,7 @@ static void command_line_scan(mparm_T *parmp) break; case 'i': // "-i {shada}" use for shada - set_option_value_give_err(kOptShadafile, CSTR_AS_OPTVAL(argv[0]), 0); + set_option_value_give_err(kOptShadafile, CSTR_AS_OBJ(argv[0]), 0); break; case 'l': // "-l" Lua script: args after "-l". @@ -1434,7 +1434,7 @@ static void command_line_scan(mparm_T *parmp) parmp->no_swap_file = true; parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE"; if (p_shadafile == NULL || *p_shadafile == NUL) { - set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0); + set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OBJ("NONE"), 0); } parmp->luaf = argv[0]; argc--; @@ -1471,7 +1471,7 @@ scripterror: if (ascii_isdigit(*(argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value_give_err(kOptWindow, NUMBER_OPTVAL((OptInt)n), 0); + set_option_value_give_err(kOptWindow, INTEGER_OBJ((OptInt)n), 0); argv_idx = -1; break; } @@ -1658,7 +1658,7 @@ static void handle_quickfix(mparm_T *paramp) { if (paramp->edit_type == EDIT_QF) { if (paramp->use_ef != NULL) { - set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(paramp->use_ef), 0, SID_CARG); + set_option_direct(kOptErrorfile, CSTR_AS_OBJ(paramp->use_ef), 0, SID_CARG); } vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef); if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { @@ -1906,7 +1906,7 @@ static void edit_buffers(mparm_T *parmp) p_shm_save = xstrdup(p_shm); snprintf(buf, sizeof(buf), "F%s", p_shm); - set_option_value_give_err(kOptShortmess, CSTR_AS_OPTVAL(buf), 0); + set_option_value_give_err(kOptShortmess, CSTR_AS_OBJ(buf), 0); } } else { if (curwin->w_next == NULL) { // just checking @@ -1951,7 +1951,7 @@ static void edit_buffers(mparm_T *parmp) } if (p_shm_save != NULL) { - set_option_value_give_err(kOptShortmess, CSTR_AS_OPTVAL(p_shm_save), 0); + set_option_value_give_err(kOptShortmess, CSTR_AS_OBJ(p_shm_save), 0); xfree(p_shm_save); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 6c915473b5..809905c7c4 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -981,7 +981,7 @@ void ml_recover(bool checkext) set_fileformat(b0_ff - 1, OPT_LOCAL); } if (b0_fenc != NULL) { - set_option_value_give_err(kOptFileencoding, CSTR_AS_OPTVAL(b0_fenc), OPT_LOCAL); + set_option_value_give_err(kOptFileencoding, CSTR_AS_OBJ(b0_fenc), OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, true, true); diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c index 6889fd885d..8ef2546487 100644 --- a/src/nvim/msgpack_rpc/packer.c +++ b/src/nvim/msgpack_rpc/packer.c @@ -213,6 +213,7 @@ void mpack_object_inner(Object *current, Object *container, size_t container_idx api_free_luaref(current->data.luaref); current->data.luaref = LUA_NOREF; FALLTHROUGH; + case kObjectTypeUnset: case kObjectTypeNil: mpack_nil(&packer->ptr); break; diff --git a/src/nvim/option.c b/src/nvim/option.c index 96f3fe111a..b15248adee 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -174,7 +174,7 @@ static int p_paste_dep_opts[] = { void set_init_tablocal(void) { // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal! - p_ch = options[kOptCmdheight].def_val.data.number; + p_ch = options[kOptCmdheight].def_val.data.integer; } /// Initialize the 'shell' option to a default value. @@ -290,7 +290,7 @@ static void set_init_default_cdpath(void) } } buf[j] = NUL; - change_option_default(kOptCdpath, CSTR_AS_OPTVAL(buf)); + change_option_default(kOptCdpath, CSTR_AS_OBJ(buf)); xfree(cdpath); } @@ -314,8 +314,8 @@ static void set_init_expand_env(void) p = option_expand(opt_idx, NULL); } if (p != NULL) { - set_option_varp(opt_idx, opt->var, CSTR_TO_OPTVAL(p), true); - change_option_default(opt_idx, CSTR_TO_OPTVAL(p)); + set_option_varp(opt_idx, opt->var, CSTR_TO_OBJ(p), true); + change_option_default(opt_idx, CSTR_TO_OBJ(p)); } } } @@ -406,7 +406,7 @@ void set_init_1(bool clean_arg) // Allow disabling ttyfast during startup to disable features such as // automatic background detection over slow connections. if (os_env_exists("NVIM_NOTTYFAST", false)) { - set_option_value_give_err(kOptTtyfast, BOOLEAN_OPTVAL(false), 0); + set_option_value_give_err(kOptTtyfast, BOOLEAN_OBJ(false), 0); } save_file_ff(curbuf); // Buffer is unchanged @@ -417,7 +417,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", false)) { - set_option_value_give_err(kOptTermbidi, BOOLEAN_OPTVAL(true), 0); + set_option_value_give_err(kOptTermbidi, BOOLEAN_OBJ(true), 0); } didset_options2(); @@ -441,7 +441,7 @@ void set_init_1(bool clean_arg) /// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination). /// /// @return Default value of option for the scope specified in opt_flags. -OptVal get_option_default(const OptIndex opt_idx, int opt_flags) +Object get_option_default(const OptIndex opt_idx, int opt_flags) { vimoption_T *opt = &options[opt_idx]; bool is_global_local_option = option_is_global_local(opt_idx); @@ -449,18 +449,18 @@ OptVal get_option_default(const OptIndex opt_idx, int opt_flags) #ifdef UNIX if (opt_idx == kOptModeline && getuid() == ROOT_UID) { // 'modeline' defaults to off for root. - return BOOLEAN_OPTVAL(false); + return BOOLEAN_OBJ(false); } #endif if ((opt_flags & OPT_LOCAL) && is_global_local_option) { // Use unset local value instead of default value for local scope of global-local options. return get_option_unset_value(opt_idx); - } else if (option_has_type(opt_idx, kOptValTypeString) && !(opt->flags & kOptFlagNoDefExp)) { + } else if (option_has_type(opt_idx, kObjectTypeString) && !(opt->flags & kOptFlagNoDefExp)) { // For string options, expand environment variables and ~ since the default value was already // expanded, only required when an environment variable was set later. char *s = option_expand(opt_idx, opt->def_val.data.string.data); - return s == NULL ? opt->def_val : CSTR_AS_OPTVAL(s); + return s == NULL ? opt->def_val : CSTR_AS_OBJ(s); } else { return opt->def_val; } @@ -471,7 +471,7 @@ OptVal get_option_default(const OptIndex opt_idx, int opt_flags) static void alloc_options_default(void) { for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { - options[opt_idx].def_val = optval_copy(options[opt_idx].def_val); + options[opt_idx].def_val = copy_object(options[opt_idx].def_val, NULL); } } @@ -479,7 +479,7 @@ static void alloc_options_default(void) /// /// @param opt_idx Option index in options[] table. /// @param value New default value. Must be allocated. -static void change_option_default(const OptIndex opt_idx, OptVal value) +static void change_option_default(const OptIndex opt_idx, Object value) { optval_free(options[opt_idx].def_val); options[opt_idx].def_val = value; @@ -493,7 +493,7 @@ static void change_option_default(const OptIndex opt_idx, OptVal value) static void set_option_default(const OptIndex opt_idx, int opt_flags) { bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - OptVal def_val = get_option_default(opt_idx, opt_flags); + Object def_val = get_option_default(opt_idx, opt_flags); set_option_direct(opt_idx, def_val, opt_flags, current_sctx.sc_sid); if (opt_idx == kOptScroll) { @@ -540,7 +540,7 @@ static void set_string_default(OptIndex opt_idx, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { assert(opt_idx != kOptInvalid); - change_option_default(opt_idx, CSTR_AS_OPTVAL(allocated ? val : xstrdup(val))); + change_option_default(opt_idx, CSTR_AS_OBJ(allocated ? val : xstrdup(val))); } /// For an option value that contains comma separated items, find "newval" in @@ -585,11 +585,11 @@ void free_all_options(void) // global option: free value and default value. // hidden option: free default value only. if (!hidden) { - optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); + optval_free(opt_from_varp(opt_idx, options[opt_idx].var)); } } else if (!option_is_window_local(opt_idx)) { // buffer-local option: free global value. - optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); + optval_free(opt_from_varp(opt_idx, options[opt_idx].var)); } optval_free(options[opt_idx].def_val); } @@ -620,7 +620,7 @@ void set_init_2(bool headless) if (!option_was_set(kOptWindow)) { p_window = Rows - 1; } - change_option_default(kOptWindow, NUMBER_OPTVAL(Rows - 1)); + change_option_default(kOptWindow, INTEGER_OBJ(Rows - 1)); } static const struct { @@ -643,9 +643,9 @@ static void change_option_and_default_if_unset(OptIndex idx, const char *val) if (val == NULL || options[idx].flags & kOptFlagWasSet) { return; } - OptVal optval = CSTR_AS_OPTVAL(val); + Object optval = CSTR_AS_OBJ(val); set_option_direct(idx, optval, 0, SID_NONE); - change_option_default(idx, optval_copy(optval)); + change_option_default(idx, copy_object(optval, NULL)); } /// Initialize the options, part three: After reading the .vimrc @@ -668,8 +668,8 @@ void set_init_3(void) #ifdef MSWIN if (i > 0 && !(options[kOptShellslash].flags & kOptFlagWasSet)) { // Use `/` as path separator on Unix-like shells or powershell on Windows - set_option_direct(kOptShellslash, BOOLEAN_OPTVAL(true), 0, SID_NONE); - change_option_default(kOptShellslash, BOOLEAN_OPTVAL(true)); + set_option_direct(kOptShellslash, BOOLEAN_OBJ(true), 0, SID_NONE); + change_option_default(kOptShellslash, BOOLEAN_OBJ(true)); } #endif break; @@ -726,11 +726,11 @@ void set_title_defaults(void) // icon name. Saves a bit of time, because the X11 display server does // not need to be contacted. if (!(options[kOptTitle].flags & kOptFlagWasSet)) { - change_option_default(kOptTitle, BOOLEAN_OPTVAL(false)); + change_option_default(kOptTitle, BOOLEAN_OBJ(false)); p_title = 0; } if (!(options[kOptIcon].flags & kOptFlagWasSet)) { - change_option_default(kOptIcon, BOOLEAN_OPTVAL(false)); + change_option_default(kOptIcon, BOOLEAN_OBJ(false)); p_icon = 0; } } @@ -1207,7 +1207,7 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_ set_prefix_T prefix, const char **errmsg) { // Only bools can have a prefix of 'inv' or 'no' - if (!option_has_type(opt_idx, kOptValTypeBoolean) && prefix != PREFIX_NONE) { + if (!option_has_type(opt_idx, kObjectTypeBoolean) && prefix != PREFIX_NONE) { *errmsg = e_invarg; return FAIL; } @@ -1322,11 +1322,11 @@ const char *find_option_end(const char *arg, OptIndex *opt_idxp) return p; } -/// Get new option value from argp. Allocated OptVal must be freed by caller. +/// Get new option value from argp. Allocated Object must be freed by caller. /// Can unset local value of an option when ":set {option}<" is used. -OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp, +Object get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp, int nextchar, set_op_T op, uint32_t flags, void *varp, - OptVal *oldval_override, char *errbuf, const size_t errbuflen, + Object *oldval_override, char *errbuf, const size_t errbuflen, const char **errmsg) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -1335,7 +1335,7 @@ OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, c vimoption_T *opt = &options[opt_idx]; char *arg = *argp; - OptVal oldval; + Object oldval; if (oldval_override != NULL) { // Allow overriding the oldval. This is needed to handle the case where // options for buffers/windows other than curbuf/curwin are updated. It can @@ -1344,16 +1344,16 @@ OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, c } else { // When setting the local value of a global option, the old value may be the global value. const bool oldval_is_global = option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL); - oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp); + oldval = opt_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp); } - OptVal newval = NIL_OPTVAL; + Object newval = NIL; if (nextchar == '&') { // ":set opt&": Reset to default value. // NOTE: Use OPT_GLOBAL instead of opt_flags to ensure we don't use the unset local value for // global-local options when OPT_LOCAL is used. - return optval_copy(get_option_default(opt_idx, OPT_GLOBAL)); + return copy_object(get_option_default(opt_idx, OPT_GLOBAL), NULL); } else if (nextchar == '<') { // ":set opt<": Reset to global value. // ":setlocal opt<": Copy global value to local value. @@ -1364,24 +1364,16 @@ OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, c } switch (oldval.type) { - case kOptValTypeNil: + case kObjectTypeNil: abort(); - case kOptValTypeBoolean: { + case kObjectTypeUnset: // Unset local value of a global-local boolean. + case kObjectTypeBoolean: { TriState newval_bool; - // ":set opt!": invert + // ":set opt!": invert (an unset local value stays unset) if (nextchar == '!') { - switch (oldval.data.boolean) { - case kNone: - newval_bool = kNone; - break; - case kTrue: - newval_bool = kFalse; - break; - case kFalse: - newval_bool = kTrue; - break; - } + newval_bool = oldval.type == + kObjectTypeUnset ? kNone : (oldval.data.boolean ? kFalse : kTrue); } else { // ":set invopt": invert // ":set opt" or ":set noopt": set or reset @@ -1392,11 +1384,11 @@ OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, c } } - newval = BOOLEAN_OPTVAL(newval_bool); + newval = opt_from_tristate(newval_bool); break; } - case kOptValTypeNumber: { - OptInt oldval_num = oldval.data.number; + case kObjectTypeInteger: { + OptInt oldval_num = oldval.data.integer; OptInt newval_num; // Different ways to set a number option: @@ -1437,18 +1429,20 @@ OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, c newval_num = oldval_num - newval_num; } - newval = NUMBER_OPTVAL(newval_num); + newval = INTEGER_OBJ(newval_num); break; } - case kOptValTypeString: { + case kObjectTypeString: { // A dict option merges here too: its stored value is already a ":set" string, so =/+=/-= apply // as for any string, and set_option() validates and canonicalizes the result. const char *oldval_str = oldval.data.string.data; // Get the new value for the option const char *newval_str = stropt_get_newval(opt_idx, argp, varp, oldval_str, &op); - newval = CSTR_AS_OPTVAL(newval_str); + newval = CSTR_AS_OBJ(newval_str); break; } + default: + abort(); } return newval; @@ -1519,7 +1513,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * // Allow '=' and ':' as MS-DOS command.com allows only one '=' character per "set" command line. if (nextchar == '?' || (prefix == PREFIX_NONE && vim_strchr("=:&<", nextchar) == NULL - && !option_has_type(opt_idx, kOptValTypeBoolean))) { + && !option_has_type(opt_idx, kObjectTypeBoolean))) { // print value if (*did_show) { msg_putchar('\n'); // cursor below last one @@ -1547,7 +1541,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * return; } - if (option_has_type(opt_idx, kOptValTypeBoolean)) { + if (option_has_type(opt_idx, kObjectTypeBoolean)) { if (vim_strchr("=:", nextchar) != NULL) { *errmsg = e_invarg; return; @@ -1564,10 +1558,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * } } - OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp, + Object newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp, NULL, errbuf, errbuflen, errmsg); - if (newval.type == kOptValTypeNil || *errmsg != NULL) { + if (newval.type == kObjectTypeNil || *errmsg != NULL) { return; } @@ -1880,7 +1874,7 @@ static void didset_options_all(void) void check_options(void) { for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { - if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) { + if ((option_has_type(opt_idx, kObjectTypeString)) && options[opt_idx].var != NULL) { check_string_option((char **)get_varp(&(options[opt_idx]))); } } @@ -2080,8 +2074,8 @@ void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx) } /// Execute OptionSet autocmd now (not deferred). -void apply_optionset_autocmd_now(OptIndex opt_idx, int opt_flags, OptVal oldval, OptVal oldval_g, - OptVal oldval_l, OptVal newval, const char *errmsg) +void apply_optionset_autocmd_now(OptIndex opt_idx, int opt_flags, Object oldval, Object oldval_g, + Object oldval_l, Object newval, const char *errmsg) { // Don't do this while starting up, failure or recursively. if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) { @@ -2089,10 +2083,10 @@ void apply_optionset_autocmd_now(OptIndex opt_idx, int opt_flags, OptVal oldval, } char buf_type[7]; - typval_T oldval_tv = optval_as_tv(oldval, false); - typval_T oldval_g_tv = optval_as_tv(oldval_g, false); - typval_T oldval_l_tv = optval_as_tv(oldval_l, false); - typval_T newval_tv = optval_as_tv(newval, false); + typval_T oldval_tv = opt_to_tv(oldval, false); + typval_T oldval_g_tv = opt_to_tv(oldval_g, false); + typval_T oldval_l_tv = opt_to_tv(oldval_l, false); + typval_T newval_tv = opt_to_tv(newval, false); set_vim_var_tv(VV_OPTION_OLD, &oldval_tv); set_vim_var_tv(VV_OPTION_NEW, &newval_tv); @@ -2121,8 +2115,8 @@ void apply_optionset_autocmd_now(OptIndex opt_idx, int opt_flags, OptVal oldval, } /// For 'modified', the event is deferred. -static void apply_optionset_autocmd(OptIndex opt_idx, int opt_flags, OptVal oldval, OptVal oldval_g, - OptVal oldval_l, OptVal newval, const char *errmsg) +static void apply_optionset_autocmd(OptIndex opt_idx, int opt_flags, Object oldval, Object oldval_g, + Object oldval_l, Object newval, const char *errmsg) { if (starting || errmsg != NULL) { return; @@ -2170,7 +2164,7 @@ static const char *did_set_arabic(optset_T *args) p_deco = true; // Force-set the necessary keymap for arabic. - errmsg = set_option_value(kOptKeymap, STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); + errmsg = set_option_value(kOptKeymap, STATIC_CSTR_AS_OBJ("arabic"), OPT_LOCAL); } else { // 'arabic' is reset, handle various sub-settings. if (!p_tbidi) { @@ -2209,7 +2203,7 @@ static const char *did_set_binary(optset_T *args) buf_T *buf = (buf_T *)args->os_buf; // when 'bin' is set also set some other options - set_options_bin((int)args->os_oldval.boolean, buf->b_p_bin, args->os_flags); + set_options_bin((int)args->os_oldval.data.boolean, buf->b_p_bin, args->os_flags); redraw_titles(); return NULL; @@ -2221,7 +2215,7 @@ static const char *did_set_buflisted(optset_T *args) buf_T *buf = (buf_T *)args->os_buf; // when 'buflisted' changes, trigger autocommands - if (args->os_oldval.boolean != buf->b_p_bl) { + if (args->os_oldval.data.boolean != buf->b_p_bl) { apply_autocmds(buf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE, NULL, NULL, true, buf); } @@ -2231,7 +2225,7 @@ static const char *did_set_buflisted(optset_T *args) /// Process the new 'cmdheight' option value. static const char *did_set_cmdheight(optset_T *args) { - OptInt old_value = args->os_oldval.number; + OptInt old_value = args->os_oldval.data.integer; if (p_ch > Rows - min_rows(curtab) + 1) { p_ch = Rows - min_rows(curtab) + 1; @@ -2274,7 +2268,7 @@ static const char *did_set_eof_eol_fixeol_bomb(optset_T *args FUNC_ATTR_UNUSED) static const char *did_set_equalalways(optset_T *args) { win_T *win = (win_T *)args->os_win; - if (p_ea && !args->os_oldval.boolean) { + if (p_ea && !args->os_oldval.data.boolean) { win_equal(win, false, 0); } @@ -2366,8 +2360,8 @@ static const char *did_set_langremap(optset_T *args FUNC_ATTR_UNUSED) /// Process the new 'laststatus' option value. static const char *did_set_laststatus(optset_T *args) { - OptInt old_value = args->os_oldval.number; - OptInt value = args->os_newval.number; + OptInt old_value = args->os_oldval.data.integer; + OptInt value = args->os_newval.data.integer; // When switching to global statusline, decrease topframe height // Also clear the cmdline to remove the ruler if there is one @@ -2397,7 +2391,7 @@ static const char *did_set_lines_or_columns(optset_T *args) if (p_lines != Rows || p_columns != Columns) { // Changing the screen size is not allowed while updating the screen. if (updating_screen) { - OptVal oldval = (OptVal){ .type = kOptValTypeNumber, .data = args->os_oldval }; + Object oldval = INTEGER_OBJ(args->os_oldval.data.integer); set_option_varp(args->os_idx, args->os_varp, oldval, false); } else if (full_screen) { screen_resize((int)p_columns, (int)p_lines); @@ -2449,11 +2443,11 @@ static const char *did_set_modifiable(optset_T *args FUNC_ATTR_UNUSED) static const char *did_set_modified(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; - if (!args->os_newval.boolean) { + if (!args->os_newval.data.boolean) { save_file_ff(buf); // Buffer is unchanged } redraw_titles(); - buf->b_modified_was_set = !!(int)args->os_newval.boolean; + buf->b_modified_was_set = !!(int)args->os_newval.data.boolean; return NULL; } @@ -2662,8 +2656,8 @@ static const char *did_set_readonly(optset_T *args) static const char *did_set_scrollback(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; - OptInt old_value = args->os_oldval.number; - OptInt value = args->os_newval.number; + OptInt old_value = args->os_oldval.data.integer; + OptInt value = args->os_newval.data.integer; if (buf->terminal && value < old_value) { // Force the scrollback to take immediate effect only when decreasing it. @@ -2771,7 +2765,7 @@ static const char *did_set_title_icon(optset_T *args FUNC_ATTR_UNUSED) /// Process the new 'titlelen' option value. static const char *did_set_titlelen(optset_T *args) { - OptInt old_value = args->os_oldval.number; + OptInt old_value = args->os_oldval.data.integer; // if 'titlelen' has changed, redraw the title if (starting != NO_SCREEN && old_value != p_titlelen) { @@ -2839,9 +2833,9 @@ static const char *did_set_undolevels(optset_T *args) OptInt *pp = (OptInt *)args->os_varp; if (pp == &p_ul) { // global 'undolevels' - did_set_global_undolevels(args->os_newval.number, args->os_oldval.number); + did_set_global_undolevels(args->os_newval.data.integer, args->os_oldval.data.integer); } else if (pp == &buf->b_p_ul) { // buffer local 'undolevels' - did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number); + did_set_buflocal_undolevels(buf, args->os_newval.data.integer, args->os_oldval.data.integer); } return NULL; @@ -2850,7 +2844,7 @@ static const char *did_set_undolevels(optset_T *args) /// Process the new 'updatecount' option value. static const char *did_set_updatecount(optset_T *args) { - OptInt old_value = args->os_oldval.number; + OptInt old_value = args->os_oldval.data.integer; // when 'updatecount' changes from zero to non-zero, open swap files if (p_uc && !old_value) { @@ -2877,8 +2871,8 @@ static const char *did_set_wildchar(optset_T *args) static const char *did_set_winblend(optset_T *args) { win_T *win = (win_T *)args->os_win; - OptInt old_value = args->os_oldval.number; - OptInt value = args->os_newval.number; + OptInt old_value = args->os_oldval.data.integer; + OptInt value = args->os_newval.data.integer; if (value != old_value) { win->w_p_winbl = MAX(MIN(win->w_p_winbl, 100), 0); @@ -3269,8 +3263,8 @@ bool is_tty_option(const char *name) /// /// @param name Name of TTY option. /// -/// @return [allocated] TTY option value. Returns NIL_OPTVAL if option isn't a TTY option. -OptVal get_tty_option(const char *name) +/// @return [allocated] TTY option value. Returns NIL if option isn't a TTY option. +Object get_tty_option(const char *name) { char *value = NULL; @@ -3290,7 +3284,7 @@ OptVal get_tty_option(const char *name) value = xstrdup(""); } - return value == NULL ? NIL_OPTVAL : CSTR_AS_OPTVAL(value); + return value == NULL ? NIL : CSTR_AS_OBJ(value); } bool set_tty_option(const char *name, char *value) @@ -3345,178 +3339,159 @@ bool is_dict_option(OptIndex opt_idx) return opt_dict_schema(opt_idx) != NULL; } -/// Free an allocated OptVal. -void optval_free(OptVal o) +/// Free an allocated option value. +void optval_free(Object o) { - switch (o.type) { - case kOptValTypeNil: - case kOptValTypeBoolean: - case kOptValTypeNumber: - break; - case kOptValTypeString: - // Don't free empty string option - if (o.data.string.data != empty_string_option) { - api_free_string(o.data.string); - } - break; + // Only strings own memory; don't free the shared empty-string-option sentinel. + if (o.type == kObjectTypeString && o.data.string.data != empty_string_option) { + api_free_string(o.data.string); } } -/// Copy an OptVal. -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)); - } - UNREACHABLE; -} - /// Check if two option values are equal. -bool optval_equal(OptVal o1, OptVal o2) +bool option_equal(Object o1, Object o2) { if (o1.type != o2.type) { return false; } switch (o1.type) { - case kOptValTypeNil: + case kObjectTypeUnset: + case kObjectTypeNil: return true; - case kOptValTypeBoolean: + case kObjectTypeBoolean: return o1.data.boolean == o2.data.boolean; - case kOptValTypeNumber: - return o1.data.number == o2.data.number; - case kOptValTypeString: + case kObjectTypeInteger: + return o1.data.integer == o2.data.integer; + case kObjectTypeString: return o1.data.string.size == o2.data.string.size && (o1.data.string.data == o2.data.string.data || strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size)); + default: + abort(); // Should not happen. } - UNREACHABLE; } -/// Get type of option. -static OptValType option_get_type(const OptIndex opt_idx) +/// Convert a tri-state boolean option to Object. `kNone` (unset local value) maps to Unset. +Object opt_from_tristate(TriState t) { - return options[opt_idx].type; + return t == kNone ? UNSET : BOOLEAN_OBJ(t == kTrue); } -/// Create OptVal from var pointer. +/// Map an option value Object to its declared option type. Unset maps to boolean, since only +/// global-local booleans have an unset value representation. +static ObjectType optval_type(Object o) +{ + switch (o.type) { + case kObjectTypeNil: + return kObjectTypeNil; + case kObjectTypeUnset: + case kObjectTypeBoolean: + return kObjectTypeBoolean; + case kObjectTypeInteger: + return kObjectTypeInteger; + case kObjectTypeString: + return kObjectTypeString; + default: + abort(); // Should not happen. + } +} + +/// Creates Object from var pointer. /// /// @param opt_idx Option index in options[] table. /// @param[out] varp Pointer to option variable. /// /// @return Option value stored in varp. -OptVal optval_from_varp(OptIndex opt_idx, void *varp) +Object opt_from_varp(OptIndex opt_idx, void *varp) FUNC_ATTR_NONNULL_ARG(2) { // Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc' // changed. if ((int *)varp == &curbuf->b_changed) { - return BOOLEAN_OPTVAL(curbufIsChanged()); + return BOOLEAN_OBJ(curbufIsChanged()); } - OptValType type = option_get_type(opt_idx); - - switch (type) { - case kOptValTypeNil: - return NIL_OPTVAL; - case kOptValTypeBoolean: - return BOOLEAN_OPTVAL(TRISTATE_FROM_INT(*(int *)varp)); - case kOptValTypeNumber: - return NUMBER_OPTVAL(*(OptInt *)varp); - case kOptValTypeString: - return STRING_OPTVAL(cstr_as_string(*(char **)varp)); + switch (options[opt_idx].type) { + case kObjectTypeBoolean: + // Boolean options are tri-states; kNone (an unset local value) maps to Unset. + return opt_from_tristate(TRISTATE_FROM_INT(*(int *)varp)); + case kObjectTypeInteger: + return INTEGER_OBJ(*(OptInt *)varp); + case kObjectTypeString: + return STRING_OBJ(cstr_as_string(*(char **)varp)); + default: + abort(); // Should not happen. } - UNREACHABLE; } -/// Set option var pointer value from OptVal. +/// Sets an option var pointer (the legacy/scalar value) from a structured value. /// /// @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(OptIndex opt_idx, void *varp, OptVal value, bool free_oldval) +static void set_option_varp(OptIndex opt_idx, void *varp, Object value, bool free_oldval) FUNC_ATTR_NONNULL_ARG(2) { - assert(option_has_type(opt_idx, value.type)); - if (free_oldval) { - optval_free(optval_from_varp(opt_idx, varp)); + optval_free(opt_from_varp(opt_idx, varp)); } switch (value.type) { - case kOptValTypeNil: - abort(); - case kOptValTypeBoolean: + case kObjectTypeUnset: + // Unset local value of a global-local boolean: store the kNone sentinel. + assert(option_has_type(opt_idx, kObjectTypeBoolean)); + *(int *)varp = kNone; + return; + case kObjectTypeBoolean: + assert(option_has_type(opt_idx, kObjectTypeBoolean)); *(int *)varp = value.data.boolean; return; - case kOptValTypeNumber: - *(OptInt *)varp = value.data.number; + case kObjectTypeInteger: + assert(option_has_type(opt_idx, kObjectTypeInteger)); + *(OptInt *)varp = value.data.integer; return; - case kOptValTypeString: + case kObjectTypeString: + assert(option_has_type(opt_idx, kObjectTypeString)); *(char **)varp = value.data.string.data; return; + default: + abort(); } - UNREACHABLE; } -/// Return C-string representation of OptVal. Caller must free the returned C-string. -static char *optval_to_cstr(OptVal o) +/// Return C-string representation of an option. Caller must free the returned C-string. +static char *optval_to_cstr(Object o) { switch (o.type) { - case kOptValTypeNil: + case kObjectTypeUnset: + case kObjectTypeNil: return xstrdup(""); - case kOptValTypeBoolean: + case kObjectTypeBoolean: return xstrdup(o.data.boolean ? "true" : "false"); - case kOptValTypeNumber: { + case kObjectTypeInteger: { char *buf = xmalloc(NUMBUFLEN); - snprintf(buf, NUMBUFLEN, "%" PRId64, o.data.number); + snprintf(buf, NUMBUFLEN, "%" PRId64, o.data.integer); return buf; } - case kOptValTypeString: { + case kObjectTypeString: { char *buf = xmalloc(o.data.string.size + 3); snprintf(buf, o.data.string.size + 3, "\"%s\"", o.data.string.data); return buf; } + default: + abort(); // Should not happen. } - UNREACHABLE; -} - -/// Convert an OptVal to an API Object. -Object optval_as_object(OptVal o) -{ - switch (o.type) { - case kOptValTypeNil: - return NIL; - case kOptValTypeBoolean: - switch (o.data.boolean) { - case kFalse: - case kTrue: - return BOOLEAN_OBJ(o.data.boolean); - case kNone: - return NIL; - } - UNREACHABLE; - case kOptValTypeNumber: - return INTEGER_OBJ(o.data.number); - case kOptValTypeString: - return STRING_OBJ(o.data.string); - } - UNREACHABLE; } /// Converts an option value to its structured form. /// /// @return Object allocated in `arena`. -Object optval_to_struct(OptIndex opt_idx, OptVal value, Arena *arena) +Object optval_to_struct(OptIndex opt_idx, Object value, Arena *arena) { - if (value.type != kOptValTypeString) { - return optval_as_object(value); // boolean/number/nil scalar + if (value.type != kObjectTypeString) { + return value; // boolean/number/nil/unset scalar; already an Object. } const uint32_t flags = options[opt_idx].flags; @@ -3585,36 +3560,18 @@ Object optval_to_struct(OptIndex opt_idx, OptVal value, Arena *arena) return rv; } -/// Convert an API Object to an OptVal. -OptVal object_as_optval(Object o, bool *error) -{ - switch (o.type) { - case kObjectTypeNil: - return NIL_OPTVAL; - case kObjectTypeBoolean: - return BOOLEAN_OPTVAL(o.data.boolean); - case kObjectTypeInteger: - return NUMBER_OPTVAL((OptInt)o.data.integer); - case kObjectTypeString: - return STRING_OPTVAL(o.data.string); - default: - *error = true; - return NIL_OPTVAL; - } - UNREACHABLE; -} - -/// Converts a structured option (API Object) to an OptVal (stringly-typed ":set" string). Each +/// Converts a structured option (API Object) to a scalar option value (stringly-typed ":set" +/// string, for string options). Each /// option impl internally expects a ":set" string (unfortunately). /// /// Example: 'listchars' `{ eol = "~" }` => "eol:~". /// /// @param op The :set operation; "key:value" removals are normalized to match by key. -/// @return OptVal (owned; free with optval_free). -OptVal object_as_optval_for(OptIndex opt_idx, Object o, set_op_T op, bool *error) +/// @return Object (owned; free with optval_free). +Object object_as_optval(OptIndex opt_idx, Object o, set_op_T op, bool *error) { if (o.type == kObjectTypeNil) { - return NIL_OPTVAL; + return NIL; } const uint32_t flags = options[opt_idx].flags; @@ -3630,13 +3587,13 @@ OptVal object_as_optval_for(OptIndex opt_idx, Object o, set_op_T op, bool *error bool type_ok; switch (o.type) { case kObjectTypeBoolean: - type_ok = option_has_type(opt_idx, kOptValTypeBoolean); + type_ok = option_has_type(opt_idx, kObjectTypeBoolean); break; case kObjectTypeInteger: - type_ok = option_has_type(opt_idx, kOptValTypeNumber); + type_ok = option_has_type(opt_idx, kObjectTypeInteger); break; case kObjectTypeString: - type_ok = option_has_type(opt_idx, kOptValTypeString) + type_ok = option_has_type(opt_idx, kObjectTypeString) || opt_idx == kOptWildchar || opt_idx == kOptWildcharm; break; case kObjectTypeArray: @@ -3650,13 +3607,13 @@ OptVal object_as_optval_for(OptIndex opt_idx, Object o, set_op_T op, bool *error } if (!type_ok) { *error = true; - return NIL_OPTVAL; + return NIL; } switch (o.type) { case kObjectTypeBoolean: case kObjectTypeInteger: - return object_as_optval(o, error); + return o; // Scalar; already type-checked above. default: break; // String/Array/Dict are serialized below. } @@ -3673,7 +3630,7 @@ OptVal object_as_optval_for(OptIndex opt_idx, Object o, set_op_T op, bool *error if (item.type != kObjectTypeString) { *error = true; GA_DEEP_CLEAR_PTR(&ga); - return NIL_OPTVAL; + return NIL; } bool dup = false; for (int j = 0; !allow_dup && j < ga.ga_len; j++) { @@ -3718,7 +3675,7 @@ OptVal object_as_optval_for(OptIndex opt_idx, Object o, set_op_T op, bool *error } else { *error = true; GA_DEEP_CLEAR_PTR(&ga); - return NIL_OPTVAL; + return NIL; } } // Sort maps and comma-flag lists for a deterministic result (Dict order is unstable); a bare @@ -3738,7 +3695,7 @@ OptVal object_as_optval_for(OptIndex opt_idx, Object o, set_op_T op, bool *error str = with_colon; } - return CSTR_AS_OPTVAL(str); + return CSTR_AS_OBJ(str); } /// Check if option is hidden. @@ -3754,7 +3711,7 @@ bool is_option_hidden(OptIndex opt_idx) } /// Check if option supports a specific type. -bool option_has_type(OptIndex opt_idx, OptValType type) +bool option_has_type(OptIndex opt_idx, ObjectType type) { return opt_idx != kOptInvalid && options[opt_idx].type == type; } @@ -3827,17 +3784,17 @@ uint32_t get_option_flags(OptIndex opt_idx) /// @param opt_idx Option index in options[] table. /// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination). /// -/// @return [allocated] Option value. Returns NIL_OPTVAL for invalid option index. -OptVal get_option_value(OptIndex opt_idx, int opt_flags) +/// @return [allocated] Option value. Returns NIL for invalid option index. +Object get_option_value(OptIndex opt_idx, int opt_flags) { if (opt_idx == kOptInvalid) { // option not in the options[] table. - return NIL_OPTVAL; + return NIL; } vimoption_T *opt = &options[opt_idx]; void *varp = get_varp_scope(opt, opt_flags); - return optval_copy(optval_from_varp(opt_idx, varp)); + return copy_object(opt_from_varp(opt_idx, varp), NULL); } /// Return information for option at 'opt_idx' @@ -3848,13 +3805,13 @@ vimoption_T *get_option(OptIndex opt_idx) } /// Get option value that represents an unset local value for an option. -/// TODO(famiu): Remove this once we have a dedicated OptVal type for unset local options. +/// TODO(famiu): Remove this once we have a dedicated Object type for unset local options. /// /// @param opt_idx Option index in options[] table. /// @param[in] varp Pointer to option variable. /// /// @return Option value equal to the unset value for the option. -static OptVal get_option_unset_value(OptIndex opt_idx) +static Object get_option_unset_value(OptIndex opt_idx) { assert(opt_idx != kOptInvalid); vimoption_T *opt = &options[opt_idx]; @@ -3862,34 +3819,34 @@ static OptVal get_option_unset_value(OptIndex opt_idx) // For global-local options, use the unset value of the local value. if (option_is_global_local(opt_idx)) { // String global-local options always use an empty string for the unset value. - if (option_has_type(opt_idx, kOptValTypeString)) { - return STATIC_CSTR_AS_OPTVAL(""); + if (option_has_type(opt_idx, kObjectTypeString)) { + return STATIC_CSTR_AS_OBJ(""); } switch (opt_idx) { case kOptAutocomplete: case kOptAutoread: case kOptFsync: - return BOOLEAN_OPTVAL(kNone); + return UNSET; case kOptScrolloff: case kOptScrolloffpad: case kOptSidescrolloff: - return NUMBER_OPTVAL(-1); + return INTEGER_OBJ(-1); case kOptUndolevels: - return NUMBER_OPTVAL(NO_LOCAL_UNDOLEVEL); + return INTEGER_OBJ(NO_LOCAL_UNDOLEVEL); default: abort(); } } // For options that aren't global-local, use the global value to represent an unset local value. - return optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL)); + return opt_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL)); } /// 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. +/// TODO(famiu): Remove this once we have an Object type to indicate an unset local value. static bool is_option_local_value_unset(OptIndex opt_idx) { vimoption_T *opt = get_option(opt_idx); @@ -3900,10 +3857,10 @@ static bool is_option_local_value_unset(OptIndex opt_idx) } void *varp_local = get_varp_scope(opt, OPT_LOCAL); - OptVal local_value = optval_from_varp(opt_idx, varp_local); - OptVal unset_local_value = get_option_unset_value(opt_idx); + Object local_value = opt_from_varp(opt_idx, varp_local); + Object unset_local_value = get_option_unset_value(opt_idx); - return optval_equal(local_value, unset_local_value); + return option_equal(local_value, unset_local_value); } /// Handle side-effects of setting an option. @@ -3921,7 +3878,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx) /// @param errbuflen Length of error buffer. /// /// @return NULL on success, an untranslated error message on error. -static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value, +static const char *did_set_option(OptIndex opt_idx, void *varp, Object old_value, Object new_value, int opt_flags, scid_T set_sid, const bool direct, const bool value_replaced, char *errbuf, size_t errbuflen) { @@ -3935,8 +3892,8 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value .os_varp = varp, .os_idx = opt_idx, .os_flags = opt_flags, - .os_oldval = old_value.data, - .os_newval = new_value.data, + .os_oldval = old_value, + .os_newval = new_value, .os_value_checked = false, .os_value_changed = false, .os_restore_chartab = false, @@ -3950,7 +3907,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value // Don't do any extra processing if setting directly. } // Disallow changing immutable options. - else if (opt->immutable && !optval_equal(old_value, new_value)) { + else if (opt->immutable && !option_equal(old_value, new_value)) { errmsg = e_unsupportedoption; } // Disallow changing some options from secure mode. @@ -3958,7 +3915,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value errmsg = e_secure; } // Check for a "normal" directory or file name in some string options. - else if (new_value.type == kOptValTypeString + else if (new_value.type == kObjectTypeString && check_illegal_path_names(*(char **)varp, opt->flags)) { errmsg = e_invarg; } else if (opt->opt_did_set_cb != NULL) { @@ -3987,7 +3944,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value } // 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); + new_value = opt_from_varp(opt_idx, varp); if (set_sid != SID_NONE) { sctx_T script_ctx = set_sid == 0 ? current_sctx : (sctx_T){ .sc_sid = set_sid }; @@ -4004,12 +3961,12 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value // 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 = get_option_unset_value(opt_idx); - set_option_varp(opt_idx, varp_local, optval_copy(local_unset_value), true); + Object local_unset_value = get_option_unset_value(opt_idx); + set_option_varp(opt_idx, varp_local, copy_object(local_unset_value, NULL), true); } else { // 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); + set_option_varp(opt_idx, varp_global, copy_object(new_value, NULL), true); } } @@ -4085,7 +4042,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param newval[in,out] New option value. Might be modified. -static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, int opt_flags, +static const char *validate_option_value(const OptIndex opt_idx, Object *newval, int opt_flags, char *errbuf, size_t errbuflen) { const char *errmsg = NULL; @@ -4093,35 +4050,35 @@ static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, // Always allow unsetting local value of global-local option. if (option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL) - && optval_equal(*newval, get_option_unset_value(opt_idx))) { + && option_equal(*newval, get_option_unset_value(opt_idx))) { return NULL; } - if (newval->type == kOptValTypeNil) { + if (newval->type == kObjectTypeNil) { // 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 { - *newval = optval_copy(get_option_unset_value(opt_idx)); + *newval = copy_object(get_option_unset_value(opt_idx), NULL); } - } else if (!option_has_type(opt_idx, newval->type)) { + } else if (!option_has_type(opt_idx, optval_type(*newval))) { char *rep = optval_to_cstr(*newval); - const char *type_str = optval_type_get_name(opt->type); + const char *type_str = optval_type_name(opt->type); snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"), - opt->fullname, type_str, optval_type_get_name(newval->type), rep); + opt->fullname, type_str, optval_type_name(optval_type(*newval)), rep); xfree(rep); errmsg = errbuf; - } else if (newval->type == kOptValTypeNumber) { + } else if (newval->type == kObjectTypeInteger) { // Validate and bound check num option values. - errmsg = validate_num_option(opt_idx, &newval->data.number, errbuf, errbuflen); + errmsg = validate_num_option(opt_idx, &newval->data.integer, errbuf, errbuflen); } return errmsg; } -/// Set the value of an option using an OptVal. +/// Set the value of an option using an Object. /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param value New option value. Might get freed. @@ -4135,7 +4092,7 @@ static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, /// @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, OptVal value, int opt_flags, scid_T set_sid, +static const char *set_option(const OptIndex opt_idx, Object value, int opt_flags, scid_T set_sid, const bool direct, const bool value_replaced, char *errbuf, size_t errbuflen) { @@ -4145,7 +4102,7 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag // Every set path for a dict option (":set", the API, Vimscript, a merge) funnels through here as a // ":set" string. Validate it once. - if (value.type == kOptValTypeString && is_dict_option(opt_idx)) { + if (value.type == kObjectTypeString && is_dict_option(opt_idx)) { errmsg = opt_strings_check(value.data.string.data, opt_dict_schema(opt_idx)->schema, errbuf, errbuflen); if (errmsg != NULL) { @@ -4201,26 +4158,26 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag 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); + Object old_value = opt_from_varp(opt_idx, varp); + Object old_global_value = opt_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 + Object old_local_value = is_opt_local_unset ? old_global_value - : optval_from_varp(opt_idx, varp_local); + : opt_from_varp(opt_idx, varp_local); // Value that's actually being used. // 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)) + Object used_old_value = (scope_local && is_opt_local_unset) + ? opt_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); + Object saved_used_value = copy_object(used_old_value, NULL); + Object saved_old_global_value = copy_object(old_global_value, NULL); + Object saved_old_local_value = copy_object(old_local_value, NULL); // New value (and varp) may become invalid if the buffer is closed by autocommands. - OptVal saved_new_value = optval_copy(value); + Object saved_new_value = copy_object(value, NULL); uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags); const int secure_saved = secure; @@ -4246,7 +4203,7 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag saved_old_local_value, saved_new_value, errmsg); } if (opt->flags & kOptFlagUIOption) { - ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value)); + ui_call_option_set(cstr_as_string(opt->fullname), saved_new_value); } } @@ -4267,7 +4224,7 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag /// @param set_sid Script ID. Special values: /// 0: Use current script ID. /// SID_NONE: Don't set script ID. -void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid) +void set_option_direct(OptIndex opt_idx, Object value, int opt_flags, scid_T set_sid) { static char errbuf[IOSIZE]; @@ -4275,7 +4232,7 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set return; } - const char *errmsg = set_option(opt_idx, optval_copy(value), opt_flags, set_sid, true, true, + const char *errmsg = set_option(opt_idx, copy_object(value, NULL), opt_flags, set_sid, true, true, errbuf, sizeof(errbuf)); assert(errmsg == NULL); (void)errmsg; // ignore unused warning @@ -4291,7 +4248,7 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set /// SID_NONE: Don't set script ID. /// @param scope Option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. -void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid, +void set_option_direct_for(OptIndex opt_idx, Object value, int opt_flags, scid_T set_sid, OptScope scope, void *const from) { buf_T *save_curbuf = curbuf; @@ -4325,11 +4282,11 @@ void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T /// Sets the value of an (non-tty) option. /// /// @param opt_idx Index in options[] table. Must not be kOptInvalid. -/// @param[in] value Option value. If NIL_OPTVAL, the option value is cleared. +/// @param[in] value Option value. If NIL, 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. -const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt_flags) +const char *set_option_value(const OptIndex opt_idx, const Object value, int opt_flags) { assert(opt_idx != kOptInvalid); @@ -4341,7 +4298,8 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt return _(e_sandbox); } - return set_option(opt_idx, optval_copy(value), opt_flags, 0, false, true, errbuf, sizeof(errbuf)); + return set_option(opt_idx, copy_object(value, NULL), opt_flags, 0, false, true, errbuf, + sizeof(errbuf)); } /// Unset the local value of a global-local option. @@ -4361,11 +4319,11 @@ static inline const char *unset_option_local_value(const OptIndex opt_idx) /// @param opt_idx Option indx in options[] table. If kOptInvalid, `name` is used to /// check if the option is a TTY option, and an error is shown if it's not. /// If the option is a TTY option, the function fails silently. -/// @param value Option value. If NIL_OPTVAL, the option value is cleared. +/// @param value Option value. If NIL, 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. -const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, const OptVal value, +const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, const Object value, int opt_flags) FUNC_ATTR_NONNULL_ARG(1) { @@ -4386,9 +4344,9 @@ const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, cons /// Call set_option_value() and when an error is returned, report it. /// /// @param opt_idx Option index in options[] table. -/// @param value Option value. If NIL_OPTVAL, the option value is cleared. +/// @param value Option value. If NIL, the option value is cleared. /// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination). -void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_flags) +void set_option_value_give_err(const OptIndex opt_idx, Object value, int opt_flags) { const char *errmsg = set_option_value(opt_idx, value, opt_flags); @@ -4489,8 +4447,9 @@ static void restore_option_context(void *const ctx, OptScope scope) /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. /// -/// @return Option value. Must be freed by caller. -OptVal get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scope, void *const from, +/// @return Option value as an owned Object (free with api_free_object). Nil if the option has no +/// value in the requested scope (e.g. an unset global-local value). +Object get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scope, void *const from, Error *err) { CtxSwitch cs = { 0 }; @@ -4499,15 +4458,16 @@ OptVal get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scop bool switched = switch_option_context(ctx, scope, from, err); if (ERROR_SET(err)) { - return NIL_OPTVAL; + return NIL; } - OptVal retv = get_option_value(opt_idx, opt_flags); + Object retv = get_option_value(opt_idx, opt_flags); if (switched) { restore_option_context(ctx, scope); } + // Move ownership of any string from `retv` into the Object; `retv` itself is not freed. return retv; } @@ -4520,7 +4480,7 @@ OptVal get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scop /// @param scope Option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. -void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags, +void set_option_value_for(const char *name, OptIndex opt_idx, Object value, const int opt_flags, const OptScope scope, void *const from, Error *err) FUNC_ATTR_NONNULL_ARG(1) { @@ -4532,11 +4492,11 @@ void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, cons if (scope == kOptScopeTab && (tabpage_T *)from != curtab) { tabpage_T *const tab = (tabpage_T *)from; assert(opt_idx == kOptCmdheight); - if (value.type != kOptValTypeNumber) { + if (value.type != kObjectTypeInteger) { api_set_error(err, kErrorTypeValidation, "'cmdheight' requires a Number"); return; } - tab->tp_ch_used = value.data.number; + tab->tp_ch_used = value.data.integer; return; } @@ -4607,7 +4567,7 @@ static void showoptions(bool all, int opt_flags) int len; if (opt_flags & OPT_ONECOLUMN) { len = Columns; - } else if (option_has_type(opt_idx, kOptValTypeBoolean)) { + } else if (option_has_type(opt_idx, kObjectTypeBoolean)) { len = 1; // a toggle option fits always } else { option_value2string(opt, opt_flags); @@ -4663,10 +4623,10 @@ static int optval_default(OptIndex opt_idx, void *varp) return true; } - OptVal current_val = optval_from_varp(opt_idx, varp); - OptVal default_val = opt->def_val; + Object current_val = opt_from_varp(opt_idx, varp); + Object default_val = opt->def_val; - return optval_equal(current_val, default_val); + return option_equal(current_val, default_val); } /// Send update to UIs with values of UI relevant options @@ -4678,7 +4638,7 @@ void ui_refresh_options(void) continue; } String name = cstr_as_string(options[opt_idx].fullname); - Object value = optval_as_object(optval_from_varp(opt_idx, options[opt_idx].var)); + Object value = opt_from_varp(opt_idx, options[opt_idx].var); ui_call_option_set(name, value); } if (p_mouse != NULL) { @@ -4701,16 +4661,16 @@ static void showoneopt(vimoption_T *opt, int opt_flags) void *varp = get_varp_scope(opt, opt_flags); // for 'modified' we also need to check if 'ff' or 'fenc' changed. - if (option_has_type(opt_idx, kOptValTypeBoolean) + if (option_has_type(opt_idx, kObjectTypeBoolean) && ((int *)varp == &curbuf->b_changed ? !curbufIsChanged() : !*(int *)varp)) { msg_puts("no"); - } else if (option_has_type(opt_idx, kOptValTypeBoolean) && *(int *)varp < 0) { + } else if (option_has_type(opt_idx, kObjectTypeBoolean) && *(int *)varp < 0) { msg_puts("--"); } else { msg_puts(" "); } msg_puts(opt->fullname); - if (!(option_has_type(opt_idx, kOptValTypeBoolean))) { + if (!(option_has_type(opt_idx, kObjectTypeBoolean))) { msg_putchar('='); // put value string in NameBuff option_value2string(opt, opt_flags); @@ -4868,35 +4828,32 @@ int makefoldset(FILE *fd) /// @return FAIL on error, OK otherwise. static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp) { - OptVal value = optval_from_varp(opt_idx, varp); + Object value = opt_from_varp(opt_idx, varp); vimoption_T *opt = &options[opt_idx]; char *name = opt->fullname; uint64_t flags = opt->flags; if (option_is_global_local(opt_idx) && varp != opt->var - && optval_equal(value, get_option_unset_value(opt_idx))) { + && option_equal(value, get_option_unset_value(opt_idx))) { // Processing unset local value of global-local option. Do nothing. return OK; } switch (value.type) { - case kOptValTypeNil: - abort(); - case kOptValTypeBoolean: { - assert(value.data.boolean != kNone); - bool value_bool = TRISTATE_TO_BOOL(value.data.boolean, false); + case kObjectTypeBoolean: { + bool value_bool = value.data.boolean; if (fprintf(fd, "%s %s%s", cmd, value_bool ? "" : "no", name) < 0) { return FAIL; } break; } - case kOptValTypeNumber: { + case kObjectTypeInteger: { if (fprintf(fd, "%s %s=", cmd, name) < 0) { return FAIL; } - OptInt value_num = value.data.number; + OptInt value_num = value.data.integer; OptInt wc; if (wc_use_keyname(varp, &wc)) { @@ -4909,7 +4866,7 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp) } break; } - case kOptValTypeString: { + case kObjectTypeString: { if (fprintf(fd, "%s %s=", cmd, name) < 0) { return FAIL; } @@ -4968,6 +4925,8 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp) xfree(part); return FAIL; } + default: + abort(); } if (put_eol(fd) < 0) { @@ -5898,7 +5857,7 @@ void reset_modifiable(void) { curbuf->b_p_ma = false; p_ma = false; - change_option_default(kOptModifiable, BOOLEAN_OPTVAL(false)); + change_option_default(kOptModifiable, BOOLEAN_OBJ(false)); } /// Set the global value for 'iminsert' to the local value. @@ -6010,7 +5969,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) return; } flags = options[opt_idx].flags; - if (option_has_type(opt_idx, kOptValTypeBoolean)) { + if (option_has_type(opt_idx, kObjectTypeBoolean)) { xp->xp_context = EXPAND_NOTHING; return; } @@ -6075,7 +6034,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) xp->xp_context = EXPAND_NOTHING; } - if (is_term_option || option_has_type(opt_idx, kOptValTypeNumber)) { + if (is_term_option || option_has_type(opt_idx, kObjectTypeInteger)) { return; } @@ -6233,7 +6192,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM continue; } if (xp->xp_context == EXPAND_BOOL_SETTINGS - && !(option_has_type(opt_idx, kOptValTypeBoolean))) { + && !(option_has_type(opt_idx, kObjectTypeBoolean))) { continue; } @@ -6378,7 +6337,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c uint32_t option_flags = options[expand_option_idx].flags; - if (option_has_type(expand_option_idx, kOptValTypeNumber)) { + if (option_has_type(expand_option_idx, kObjectTypeInteger)) { return ExpandOldSetting(numMatches, matches); } else if (option_flags & kOptFlagComma) { // Split the option by comma, then present each option to the user if @@ -6480,7 +6439,7 @@ static void option_value2string(vimoption_T *opt, int opt_flags) void *varp = get_varp_scope(opt, opt_flags); assert(varp != NULL); - if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) { + if (option_has_type(get_opt_idx(opt), kObjectTypeInteger)) { OptInt wc = 0; if (wc_use_keyname(varp, &wc)) { @@ -6813,7 +6772,7 @@ void set_fileformat(int eol_style, int opt_flags) // p is NULL if "eol_style" is EOL_UNKNOWN. if (p != NULL) { - set_option_direct(kOptFileformat, CSTR_AS_OPTVAL(p), opt_flags, 0); + set_option_direct(kOptFileformat, CSTR_AS_OBJ(p), opt_flags, 0); } // This may cause the buffer to become (un)modified. @@ -6965,7 +6924,7 @@ dict_T *get_winbuf_options(const int bufopt) void *varp = get_varp(opt); if (varp != NULL) { - typval_T opt_tv = optval_as_tv(optval_from_varp(opt_idx, varp), true); + typval_T opt_tv = opt_to_tv(opt_from_varp(opt_idx, varp), true); tv_dict_add_tv(d, opt->fullname, strlen(opt->fullname), &opt_tv); } } @@ -7068,8 +7027,8 @@ static Dict vimoption2dict(vimoption_T *opt, int opt_flags, buf_T *buf, win_T *w PUT_C(dict, "last_set_linenr", INTEGER_OBJ(script_ctx.sc_lnum)); PUT_C(dict, "last_set_chan", INTEGER_OBJ((int64_t)script_ctx.sc_chan)); - PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(option_get_type(get_opt_idx(opt))))); - PUT_C(dict, "default", optval_as_object(opt->def_val)); + PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_name(options[get_opt_idx(opt)].type))); + PUT_C(dict, "default", opt->def_val); PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & kOptFlagNoDup))); return dict; diff --git a/src/nvim/option.h b/src/nvim/option.h index 93d7998fd7..c067f5af5c 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -40,31 +40,19 @@ typedef enum { PREFIX_INV, ///< "inv" prefix } set_prefix_T; -/// Get name of OptValType as a string. -static inline const char *optval_type_get_name(const OptValType type) +/// Gets the option-domain name of an option's type. +static inline const char *optval_type_name(const ObjectType type) { switch (type) { - case kOptValTypeNil: - return "nil"; - case kOptValTypeBoolean: + case kObjectTypeBoolean: return "boolean"; - case kOptValTypeNumber: + case kObjectTypeInteger: return "number"; - case kOptValTypeString: + case kObjectTypeString: return "string"; + default: + abort(); } - UNREACHABLE; } -// OptVal helper macros. -#define NIL_OPTVAL ((OptVal) { .type = kOptValTypeNil }) -#define BOOLEAN_OPTVAL(b) ((OptVal) { .type = kOptValTypeBoolean, .data.boolean = b }) -#define NUMBER_OPTVAL(n) ((OptVal) { .type = kOptValTypeNumber, .data.number = n }) -#define STRING_OPTVAL(s) ((OptVal) { .type = kOptValTypeString, .data.string = s }) - -#define CSTR_AS_OPTVAL(s) STRING_OPTVAL(cstr_as_string(s)) -#define CSTR_TO_OPTVAL(s) STRING_OPTVAL(cstr_to_string(s)) -#define STATIC_CSTR_AS_OPTVAL(s) STRING_OPTVAL(STATIC_CSTR_AS_STRING(s)) -#define STATIC_CSTR_TO_OPTVAL(s) STRING_OPTVAL(STATIC_CSTR_TO_STRING(s)) - #include "option.h.generated.h" diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 7d22165c0f..5955e0ae10 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -42,16 +42,6 @@ typedef enum { kOptFlagColon = 1 << 25, ///< Values use colons to create sublists. } OptFlags; -/// Option value type. -/// These types are also used as type flags by using the type value as an index for the type_flags -/// bit field (@see option_has_type()). -typedef enum { - kOptValTypeNil = -1, // Make sure Nil can't be bitshifted and used as an option type flag. - kOptValTypeBoolean, - kOptValTypeNumber, - kOptValTypeString, -} OptValType; - /// Scopes that an option can support. typedef enum { kOptScopeGlobal = 0, ///< Request global option value @@ -63,19 +53,6 @@ typedef enum { #define kOptScopeSize (kOptScopeTab + 1) typedef uint8_t OptScopeFlags; -typedef union { - // boolean options are actually tri-states because they have a third "None" value. - TriState boolean; - OptInt number; - String string; -} OptValData; - -/// Option value -typedef struct { - OptValType type; - OptValData data; -} OptVal; - /// Value kind of one key in a dict option (see "schema" in options.lua). typedef enum { kOptSchemaFlag, ///< bare flag, e.g. 'diffopt' "filler" @@ -118,9 +95,9 @@ typedef struct { int os_flags; /// Old value of the option. - OptValData os_oldval; + Object os_oldval; /// New value of the option. - OptValData os_newval; + Object os_newval; /// Option value was checked to be safe, no need to set kOptFlagInsecure /// Used for the 'keymap', 'filetype' and 'syntax' options. @@ -192,7 +169,7 @@ typedef struct { char *fullname; ///< full option name char *shortname; ///< permissible abbreviation uint32_t flags; ///< see above - OptValType type; ///< option type + ObjectType type; ///< option type OptScopeFlags scope_flags; ///< option scope flags, see OptScope void *var; ///< global option: pointer to variable; ///< window-local option: NULL; @@ -215,6 +192,6 @@ typedef struct { /// cmdline. Only useful for string options. opt_expand_cb_T opt_expand_cb; - OptVal def_val; ///< default value + Object def_val; ///< default value sctx_T script_ctx; ///< script in which the option was last set } vimoption_T; diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 8aaab95932..b657c0d4d9 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -574,7 +574,7 @@ const char *did_set_background(optset_T *args) return errmsg; } - if (args->os_oldval.string.data[0] == *p_bg) { + if (args->os_oldval.data.string.data[0] == *p_bg) { // Value was not changed return NULL; } @@ -621,7 +621,7 @@ const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) const char *did_set_backupcopy(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; - const char *oldval = args->os_oldval.string.data; + const char *oldval = args->os_oldval.data.string.data; int opt_flags = args->os_flags; char *bkc = p_bkc; unsigned *flags = &bkc_flags; @@ -725,7 +725,7 @@ const char *did_set_buftype(optset_T *args) // buftype=prompt: if (buf->b_p_bt[0] == 'p') { // Set default value for 'comments' - set_option_direct(kOptComments, STATIC_CSTR_AS_OPTVAL(""), OPT_LOCAL, SID_NONE); + set_option_direct(kOptComments, STATIC_CSTR_AS_OBJ(""), OPT_LOCAL, SID_NONE); // set the prompt start position to lastline. pos_T next_prompt = { .lnum = buf->b_ml.ml_line_count, .col = buf->b_prompt_start.mark.col, .coladd = 0 }; @@ -1202,7 +1202,7 @@ int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches) const char *did_set_fileformat(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; - const char *oldval = args->os_oldval.string.data; + const char *oldval = args->os_oldval.data.string.data; int opt_flags = args->os_flags; if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) { return e_modifiable; @@ -1244,7 +1244,7 @@ const char *did_set_filetype_or_syntax(optset_T *args) return e_invarg; } - args->os_value_changed = strcmp(args->os_oldval.string.data, *varp) != 0; + args->os_value_changed = strcmp(args->os_oldval.data.string.data, *varp) != 0; // Since we check the value, there is no need to set kOptFlagInsecure, // even when the value comes from a modeline. @@ -1615,7 +1615,7 @@ const char *did_set_sessionoptions(optset_T *args) } if ((ssop_flags & kOptSsopFlagCurdir) && (ssop_flags & kOptSsopFlagSesdir)) { // Don't allow both "sesdir" and "curdir". - const char *oldval = args->os_oldval.string.data; + const char *oldval = args->os_oldval.data.string.data; opt_strings_flags(oldval, opt_ssop_values, &ssop_flags, true, NULL, 0); return e_invarg; } @@ -1676,7 +1676,7 @@ const char *did_set_shellpipe_redir(optset_T *args) { bool seen = false; - for (char *p = args->os_newval.string.data; *p != NUL; p++) { + for (char *p = args->os_newval.data.string.data; *p != NUL; p++) { if (*p != '%') { continue; } @@ -1745,7 +1745,7 @@ const char *did_set_signcolumn(optset_T *args) { win_T *win = (win_T *)args->os_win; char **varp = (char **)args->os_varp; - const char *oldval = args->os_oldval.string.data; + const char *oldval = args->os_oldval.data.string.data; if (check_signcolumn(*varp, varp == &win->w_p_scl ? win : NULL) != OK) { return e_invarg; } @@ -1796,7 +1796,7 @@ const char *did_set_spelloptions(optset_T *args) { win_T *win = (win_T *)args->os_win; int opt_flags = args->os_flags; - const char *val = args->os_newval.string.data; + const char *val = args->os_newval.data.string.data; if (!(opt_flags & OPT_LOCAL)) { const char *errmsg = opt_strings_flags(val, opt_spo_values, &spo_flags, true, args->os_errbuf, @@ -2056,7 +2056,7 @@ const char *did_set_virtualedit(optset_T *args) args->os_errbuflen); if (errmsg != NULL) { return errmsg; - } else if (strcmp(ve, args->os_oldval.string.data) != 0) { + } else if (strcmp(ve, args->os_oldval.data.string.data) != 0) { // Recompute cursor position in case the new 've' setting // changes something. validate_virtcol(win); diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 10f51a201b..8ae0b4f0ce 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -1193,11 +1193,11 @@ static bool pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value_give_err(kOptBuflisted, BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); - set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); - set_option_value_give_err(kOptDiff, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err(kOptSwapfile, BOOLEAN_OBJ(false), OPT_LOCAL); + set_option_value_give_err(kOptBuflisted, BOOLEAN_OBJ(false), OPT_LOCAL); + set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OBJ("nofile"), OPT_LOCAL); + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OBJ("wipe"), OPT_LOCAL); + set_option_value_give_err(kOptDiff, BOOLEAN_OBJ(false), OPT_LOCAL); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 7f2b464b73..b0a7d73ed9 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3896,12 +3896,12 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp static void qf_set_cwindow_options(void) { // switch off 'swapfile' - set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("quickfix"), OPT_LOCAL); - set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value_give_err(kOptSwapfile, BOOLEAN_OBJ(false), OPT_LOCAL); + set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OBJ("quickfix"), OPT_LOCAL); + set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OBJ("hide"), OPT_LOCAL); RESET_BINDING(curwin); curwin->w_p_diff = false; - set_option_value_give_err(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL); + set_option_value_give_err(kOptFoldmethod, STATIC_CSTR_AS_OBJ("manual"), OPT_LOCAL); } // Open a new quickfix or location list window, load the quickfix buffer and @@ -4495,7 +4495,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // resembles reading a file into a buffer, it's more logical when using // autocommands. curbuf->b_ro_locked++; - set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); + set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OBJ("qf"), OPT_LOCAL); curbuf->b_p_ma = false; curbuf->b_keep_filetype = true; // don't detect 'filetype' @@ -5376,7 +5376,7 @@ void ex_cfile(exarg_T *eap) } } if (*eap->arg != NUL) { - set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(eap->arg), 0, 0); + set_option_direct(kOptErrorfile, CSTR_AS_OBJ(eap->arg), 0, 0); } char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; @@ -7656,7 +7656,7 @@ void ex_helpgrep(exarg_T *eap) // Darn, some plugin changed the value. If it's still empty it was // changed and restored, need to restore in the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OBJ(save_cpo), 0); } free_string_option(save_cpo); } diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index db216043f1..9890703b17 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -1183,7 +1183,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack) } bool was_valid = runtime_search_path_valid; - set_option_value_give_err(kOptRuntimepath, STRING_OPTVAL(new_rtp), 0); + set_option_value_give_err(kOptRuntimepath, STRING_OBJ(new_rtp), 0); assert(!runtime_search_path_valid); // If this is the result of "packadd opt_pack", rebuilding runtime_search_pat diff --git a/src/nvim/spell.c b/src/nvim/spell.c index af13135908..4975a331a8 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3220,13 +3220,13 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - OptVal spl = get_option_value(kOptSpelllang, OPT_LOCAL); + Object spl = get_option_value(kOptSpelllang, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value_give_err(kOptSpell, BOOLEAN_OPTVAL(true), OPT_LOCAL); + set_option_value_give_err(kOptSpell, BOOLEAN_OBJ(true), OPT_LOCAL); set_option_value_give_err(kOptSpelllang, spl, OPT_LOCAL); optval_free(spl); diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index d9d35331fe..632b995143 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5686,7 +5686,7 @@ static void init_spellfile(void) (fname != NULL && strstr(path_tail(fname), ".ascii.") != NULL) ? "ascii" : spell_enc(); vim_snprintf(buf + strlen(buf), buf_len - strlen(buf), ".%s.add", enc_suffix); - set_option_value_give_err(kOptSpellfile, CSTR_AS_OPTVAL(buf), OPT_LOCAL); + set_option_value_give_err(kOptSpellfile, CSTR_AS_OBJ(buf), OPT_LOCAL); xfree(buf); } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index fbad68164c..5d7a66973d 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -239,9 +239,9 @@ const char *did_set_tagfunc(optset_T *args) int retval; if (args->os_flags & OPT_LOCAL) { - retval = option_set_callback_func(args->os_newval.string.data, &buf->b_tfu_cb); + retval = option_set_callback_func(args->os_newval.data.string.data, &buf->b_tfu_cb); } else { - retval = option_set_callback_func(args->os_newval.string.data, &tfu_cb); + retval = option_set_callback_func(args->os_newval.data.string.data, &tfu_cb); if (retval == OK && !(args->os_flags & OPT_GLOBAL)) { set_buflocal_tfu_callback(buf); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index cb5a1efba7..52835d360b 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -600,7 +600,7 @@ void terminal_open(Terminal **termpp, buf_T *buf) } refresh_screen(term, buf); buf->b_locked++; - set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); + set_option_value(kOptBuftype, STATIC_CSTR_AS_OBJ("terminal"), OPT_LOCAL); buf->b_locked--; if (buf->b_ffname != NULL) { diff --git a/src/nvim/ui.c b/src/nvim/ui.c index c68a104d81..1db04e2726 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -220,7 +220,7 @@ void ui_refresh(void) // Reset 'cmdheight' for all tabpages when ext_messages toggles. if (had_message != ui_ext[kUIMessages]) { if (ui_refresh_cmdheight) { - set_option_value(kOptCmdheight, NUMBER_OPTVAL(had_message), 0); + set_option_value(kOptCmdheight, INTEGER_OBJ(had_message), 0); FOR_ALL_TABS(tp) { tp->tp_ch_used = had_message; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 531c594633..b2090ea252 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3791,7 +3791,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh, bool OptInt new_ch = MAX(min_set_ch, p_ch + topfrp->fr_height - height); if (new_ch != p_ch) { const OptInt save_ch = min_set_ch; - set_option_value(kOptCmdheight, NUMBER_OPTVAL(new_ch), 0); + set_option_value(kOptCmdheight, INTEGER_OBJ(new_ch), 0); min_set_ch = save_ch; } height = (int)MIN(ROWS_AVAIL, height); @@ -4777,7 +4777,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a OptInt new_ch = p_ch; p_ch = prev_p_ch; command_frame_height = false; - set_option_value(kOptCmdheight, NUMBER_OPTVAL(new_ch), 0); + set_option_value(kOptCmdheight, INTEGER_OBJ(new_ch), 0); command_frame_height = true; } else if (old_curtab != curtab) { tabpage_check_windows(old_curtab); diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index b5b15e1c9b..376af840a6 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -476,7 +476,7 @@ win_T *win_float_special(bool enter, bool new_buf, WinKind kind) return win_float_special_fail(wp, &err); } buf->b_p_bl = false; // unlist - set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, + set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OBJ("wipe"), OPT_LOCAL, 0, kOptScopeBuf, buf); win_set_buf(wp, buf, &err); if (ERROR_SET(&err)) { From 1a755e4890c08542d9a93301b19d317f971f19e2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Jul 2026 15:05:19 +0200 Subject: [PATCH 2/2] fix(options): latent codegen bug Problem: Latent bug from 7f6c14ed54a46e822b80134d39a89f97523a2706 (2015). If an option default is "false" (`o.defaults.if_false = false`, e.g. 'fileignorecase'), codegen does not give it a `.def_val` on the platform where its `#if` condition is undefined; it is zero-initialized. This wasn't noticed until the parent commit, where zero value is kObjectTypeNil, which `set_option_varp()` rejects. Solution: Check `if_false == nil` insteada of "falsey", so the `#else` branch gets generated. --- src/gen/gen_options.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gen/gen_options.lua b/src/gen/gen_options.lua index 570516f7c7..9463edb8d1 100644 --- a/src/gen/gen_options.lua +++ b/src/gen/gen_options.lua @@ -290,7 +290,8 @@ local function dump_option(i, o, write) elseif o.defaults.condition then write(('#if defined(%s)'):format(o.defaults.condition)) write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name)) - if o.defaults.if_false then + -- Check against nil: `if_false=false` is a valid default and must still emit the `#else`. + if o.defaults.if_false ~= nil then write('#else') write(' .def_val=', get_defaults(o.defaults.if_false, o.full_name)) end