mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #26970 from famiu/refactor/options/set_option_direct
refactor(options): remove `set_string_option_direct()`
This commit is contained in:
commit
dc110cba3c
@ -988,8 +988,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
|
||||
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
|
||||
|
||||
if (scratch) {
|
||||
set_string_option_direct_in_buf(buf, kOptBufhidden, "hide", OPT_LOCAL, 0);
|
||||
set_string_option_direct_in_buf(buf, kOptBuftype, "nofile", OPT_LOCAL, 0);
|
||||
set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, kOptReqBuf,
|
||||
buf);
|
||||
set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, kOptReqBuf,
|
||||
buf);
|
||||
assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
|
||||
buf->b_p_swf = false;
|
||||
buf->b_p_ml = false;
|
||||
|
@ -712,7 +712,7 @@ char *au_event_disable(char *what)
|
||||
} else {
|
||||
STRCAT(new_ei, what);
|
||||
}
|
||||
set_string_option_direct(kOptEventignore, new_ei, 0, SID_NONE);
|
||||
set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(new_ei), 0, SID_NONE);
|
||||
xfree(new_ei);
|
||||
return save_ei;
|
||||
}
|
||||
@ -720,7 +720,7 @@ char *au_event_disable(char *what)
|
||||
void au_event_restore(char *old_ei)
|
||||
{
|
||||
if (old_ei != NULL) {
|
||||
set_string_option_direct(kOptEventignore, old_ei, 0, SID_NONE);
|
||||
set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(old_ei), 0, SID_NONE);
|
||||
xfree(old_ei);
|
||||
}
|
||||
}
|
||||
|
@ -1438,7 +1438,8 @@ void diff_win_options(win_T *wp, bool addbuf)
|
||||
}
|
||||
wp->w_p_fdm_save = xstrdup(wp->w_p_fdm);
|
||||
}
|
||||
set_string_option_direct_in_win(wp, kOptFoldmethod, "diff", OPT_LOCAL, 0);
|
||||
set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, kOptReqWin,
|
||||
wp);
|
||||
|
||||
if (!wp->w_p_diff) {
|
||||
wp->w_p_fen_save = wp->w_p_fen;
|
||||
|
@ -4294,7 +4294,7 @@ skip:
|
||||
// Show 'inccommand' preview if there are matched lines.
|
||||
if (cmdpreview_ns > 0 && !aborting()) {
|
||||
if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
|
||||
set_string_option_direct(kOptInccommand, "", 0, SID_NONE);
|
||||
set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL(""), 0, SID_NONE);
|
||||
} else if (*p_icm != NUL && pat != NULL) {
|
||||
if (pre_hl_id == 0) {
|
||||
pre_hl_id = syn_check_group(S_LEN("Substitute"));
|
||||
@ -4574,7 +4574,7 @@ bool prepare_tagpreview(bool undo_sync)
|
||||
RESET_BINDING(curwin); // don't take over 'scrollbind' and 'cursorbind'
|
||||
curwin->w_p_diff = false; // no 'diff'
|
||||
|
||||
set_string_option_direct(kOptFoldcolumn, "0", 0, SID_NONE); // no 'foldcolumn'
|
||||
set_option_direct(kOptFoldcolumn, STATIC_CSTR_AS_OPTVAL("0"), 0, SID_NONE); // no 'foldcolumn'
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4593,7 +4593,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_string_option_direct(kOptShortmess, "F", 0, SID_NONE);
|
||||
set_option_direct(kOptShortmess, STATIC_CSTR_AS_OPTVAL("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++) {
|
||||
@ -4694,7 +4694,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i
|
||||
|
||||
xfree(str);
|
||||
|
||||
set_string_option_direct(kOptShortmess, save_shm_p, 0, SID_NONE);
|
||||
set_option_direct(kOptShortmess, CSTR_AS_OPTVAL(save_shm_p), 0, SID_NONE);
|
||||
xfree(save_shm_p);
|
||||
|
||||
return preview ? 2 : 1;
|
||||
|
@ -2697,7 +2697,7 @@ static 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_string_option_direct(kOptEventignore, "all", 0, SID_NONE);
|
||||
set_option_direct(kOptEventignore, STATIC_CSTR_AS_OPTVAL("all"), 0, SID_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2717,7 +2717,7 @@ void undo_cmdmod(cmdmod_T *cmod)
|
||||
|
||||
if (cmod->cmod_save_ei != NULL) {
|
||||
// Restore 'eventignore' to the value before ":noautocmd".
|
||||
set_string_option_direct(kOptEventignore, cmod->cmod_save_ei, 0, SID_NONE);
|
||||
set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(cmod->cmod_save_ei), 0, SID_NONE);
|
||||
free_string_option(cmod->cmod_save_ei);
|
||||
cmod->cmod_save_ei = NULL;
|
||||
}
|
||||
|
@ -919,7 +919,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
|
||||
need_wait_return = false;
|
||||
}
|
||||
|
||||
set_string_option_direct(kOptInccommand, s->save_p_icm, 0, SID_NONE);
|
||||
set_option_direct(kOptInccommand, CSTR_AS_OPTVAL(s->save_p_icm), 0, SID_NONE);
|
||||
State = s->save_State;
|
||||
if (cmdpreview != save_cmdpreview) {
|
||||
cmdpreview = save_cmdpreview; // restore preview state
|
||||
@ -2554,7 +2554,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_string_option_direct(kOptInccommand, "nosplit", 0, SID_NONE);
|
||||
set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL("nosplit"), 0, SID_NONE);
|
||||
icm_split = false;
|
||||
}
|
||||
// Setup preview namespace if it's not already set.
|
||||
|
@ -1620,7 +1620,7 @@ failed:
|
||||
save_file_ff(curbuf);
|
||||
// If editing a new file: set 'fenc' for the current buffer.
|
||||
// Also for ":read ++edit file".
|
||||
set_string_option_direct(kOptFileencoding, fenc, OPT_LOCAL, 0);
|
||||
set_option_direct(kOptFileencoding, CSTR_AS_OPTVAL(fenc), OPT_LOCAL, 0);
|
||||
}
|
||||
if (fenc_alloced) {
|
||||
xfree(fenc);
|
||||
@ -1968,7 +1968,7 @@ void set_forced_fenc(exarg_T *eap)
|
||||
}
|
||||
|
||||
char *fenc = enc_canonize(eap->cmd + eap->force_enc);
|
||||
set_string_option_direct(kOptFileencoding, fenc, OPT_LOCAL, 0);
|
||||
set_option_direct(kOptFileencoding, CSTR_AS_OPTVAL(fenc), OPT_LOCAL, 0);
|
||||
xfree(fenc);
|
||||
}
|
||||
|
||||
|
@ -613,7 +613,7 @@ void cleanup_help_tags(int num_file, char **file)
|
||||
void prepare_help_buffer(void)
|
||||
{
|
||||
curbuf->b_help = true;
|
||||
set_string_option_direct(kOptBuftype, "help", OPT_LOCAL, 0);
|
||||
set_option_direct(kOptBuftype, STATIC_CSTR_AS_OPTVAL("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.
|
||||
@ -622,13 +622,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_string_option_direct(kOptIskeyword, p, OPT_LOCAL, 0);
|
||||
set_option_direct(kOptIskeyword, CSTR_AS_OPTVAL(p), OPT_LOCAL, 0);
|
||||
check_buf_options(curbuf);
|
||||
buf_init_chartab(curbuf, false);
|
||||
}
|
||||
|
||||
// Don't use the global foldmethod.
|
||||
set_string_option_direct(kOptFoldmethod, "manual", OPT_LOCAL, 0);
|
||||
set_option_direct(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL, 0);
|
||||
|
||||
curbuf->b_p_ts = 8; // 'tabstop' is 8.
|
||||
curwin->w_p_list = false; // No list mode.
|
||||
|
@ -1105,7 +1105,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_string_option_direct(kOptVartabstop, new_ts_str, OPT_LOCAL, 0);
|
||||
set_option_direct(kOptVartabstop, CSTR_AS_OPTVAL(new_ts_str), OPT_LOCAL, 0);
|
||||
curbuf->b_p_vts_array = new_vts_array;
|
||||
xfree(old_vts_ary);
|
||||
} else {
|
||||
|
@ -1573,7 +1573,7 @@ static void handle_quickfix(mparm_T *paramp)
|
||||
{
|
||||
if (paramp->edit_type == EDIT_QF) {
|
||||
if (paramp->use_ef != NULL) {
|
||||
set_string_option_direct(kOptErrorfile, paramp->use_ef, 0, SID_CARG);
|
||||
set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(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) {
|
||||
|
@ -430,10 +430,9 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags)
|
||||
uint32_t flags = opt->flags;
|
||||
if (varp != NULL) { // skip hidden option, nothing to do for it
|
||||
if (option_has_type(opt_idx, kOptValTypeString)) {
|
||||
// Use set_string_option_direct() for local options to handle freeing and allocating the
|
||||
// value.
|
||||
// Use set_option_direct() for local options to handle freeing and allocating the value.
|
||||
if (opt->indir != PV_NONE) {
|
||||
set_string_option_direct(opt_idx, opt->def_val.string, opt_flags, 0);
|
||||
set_option_direct(opt_idx, CSTR_AS_OPTVAL(opt->def_val.string), opt_flags, 0);
|
||||
} else {
|
||||
if (flags & P_ALLOCED) {
|
||||
free_string_option(*(char **)(varp));
|
||||
@ -765,7 +764,7 @@ static char *stropt_get_default_val(OptIndex opt_idx, uint64_t flags)
|
||||
}
|
||||
|
||||
/// Copy the new string value into allocated memory for the option.
|
||||
/// Can't use set_string_option_direct(), because we need to remove the backslashes.
|
||||
/// Can't use set_option_direct(), because we need to remove the backslashes.
|
||||
static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
|
||||
uint32_t flags FUNC_ATTR_UNUSED)
|
||||
{
|
||||
@ -1398,7 +1397,8 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
|
||||
return;
|
||||
}
|
||||
|
||||
*errmsg = set_option(opt_idx, varp, newval, opt_flags, op == OP_NONE, errbuf, errbuflen);
|
||||
*errmsg = set_option(opt_idx, varp, newval, opt_flags, 0, false, op == OP_NONE, errbuf,
|
||||
errbuflen);
|
||||
}
|
||||
|
||||
/// Parse 'arg' for option settings.
|
||||
@ -3311,38 +3311,6 @@ OptVal object_as_optval(Object o, bool *error)
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
/// Unset the local value of an option. The exact semantics of this depend on the option.
|
||||
/// TODO(famiu): Remove this once we have a dedicated OptVal type for unset local options.
|
||||
///
|
||||
/// @param opt_idx Option index in options[] table.
|
||||
/// @param[in] varp Pointer to option variable.
|
||||
///
|
||||
/// @return [allocated] Option value equal to the unset value for the option.
|
||||
static OptVal optval_unset_local(OptIndex opt_idx, void *varp)
|
||||
{
|
||||
vimoption_T *opt = &options[opt_idx];
|
||||
// For global-local options, use the unset value of the local value.
|
||||
if (opt->indir & PV_BOTH) {
|
||||
// String global-local options always use an empty string for the unset value.
|
||||
if (option_has_type(opt_idx, kOptValTypeString)) {
|
||||
return STATIC_CSTR_TO_OPTVAL("");
|
||||
}
|
||||
|
||||
if ((int *)varp == &curbuf->b_p_ar) {
|
||||
return BOOLEAN_OPTVAL(kNone);
|
||||
} else if ((OptInt *)varp == &curwin->w_p_so || (OptInt *)varp == &curwin->w_p_siso) {
|
||||
return NUMBER_OPTVAL(-1);
|
||||
} else if ((OptInt *)varp == &curbuf->b_p_ul) {
|
||||
return NUMBER_OPTVAL(NO_LOCAL_UNDOLEVEL);
|
||||
} else {
|
||||
// This should never happen.
|
||||
abort();
|
||||
}
|
||||
}
|
||||
// For options that aren't global-local, just set the local value to the global value.
|
||||
return get_option_value(opt_idx, OPT_GLOBAL);
|
||||
}
|
||||
|
||||
/// Get an allocated string containing a list of valid types for an option.
|
||||
/// For options with a singular type, it returns the name of the type. For options with multiple
|
||||
/// possible types, it returns a slash separated list of types. For example, if an option can be a
|
||||
@ -3417,24 +3385,60 @@ vimoption_T *get_option(OptIndex opt_idx)
|
||||
return &options[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.
|
||||
///
|
||||
/// @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)
|
||||
{
|
||||
assert(opt_idx != kOptInvalid);
|
||||
vimoption_T *opt = &options[opt_idx];
|
||||
|
||||
// For global-local options, use the unset value of the local value.
|
||||
if (opt->indir & PV_BOTH) {
|
||||
// String global-local options always use an empty string for the unset value.
|
||||
if (option_has_type(opt_idx, kOptValTypeString)) {
|
||||
return STATIC_CSTR_AS_OPTVAL("");
|
||||
}
|
||||
|
||||
switch (opt_idx) {
|
||||
case kOptAutoread:
|
||||
return BOOLEAN_OPTVAL(kNone);
|
||||
case kOptScrolloff:
|
||||
case kOptSidescrolloff:
|
||||
return NUMBER_OPTVAL(-1);
|
||||
case kOptUndolevels:
|
||||
return NUMBER_OPTVAL(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));
|
||||
}
|
||||
|
||||
/// Check if local value of global-local option is unset for current buffer / window.
|
||||
/// Always returns false for options that aren't global-local.
|
||||
///
|
||||
/// TODO(famiu): Remove this once we have an OptVal type to indicate an unset local value.
|
||||
static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win)
|
||||
static bool is_option_local_value_unset(OptIndex opt_idx)
|
||||
{
|
||||
vimoption_T *opt = get_option(opt_idx);
|
||||
|
||||
// Local value of option that isn't global-local is always considered set.
|
||||
if (!((int)opt->indir & PV_BOTH)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get pointer to local value in varp_local, and a pointer to the currently used value in varp.
|
||||
// If the local value is the one currently being used, that indicates that it's set.
|
||||
// Otherwise it indicates the local value is unset.
|
||||
void *varp = get_varp_from(opt, buf, win);
|
||||
void *varp_local = get_varp_scope_from(opt, OPT_LOCAL, buf, win);
|
||||
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);
|
||||
|
||||
return varp != varp_local;
|
||||
return optval_equal(local_value, unset_local_value);
|
||||
}
|
||||
|
||||
/// Handle side-effects of setting an option.
|
||||
@ -3442,17 +3446,19 @@ static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win
|
||||
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
|
||||
/// @param[in] varp Option variable pointer, cannot be NULL.
|
||||
/// @param old_value Old option value.
|
||||
/// @param new_value New option value.
|
||||
/// @param opt_flags Option flags.
|
||||
/// @param[out] value_checked Value was checked to be safe, no need to set P_INSECURE.
|
||||
/// @param set_sid Script ID. Special values:
|
||||
/// 0: Use current script ID.
|
||||
/// SID_NONE: Don't set script ID.
|
||||
/// @param direct Don't process side-effects.
|
||||
/// @param value_replaced Value was replaced completely.
|
||||
/// @param[out] errbuf Buffer for error message.
|
||||
/// @param errbuflen Length of error buffer.
|
||||
///
|
||||
/// @return NULL on success, an untranslated error message on error.
|
||||
static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value,
|
||||
int opt_flags, bool *value_checked, bool value_replaced,
|
||||
char *errbuf, // NOLINT(readability-non-const-parameter)
|
||||
int opt_flags, scid_T set_sid, const bool direct,
|
||||
const bool value_replaced, char *errbuf, // NOLINT(readability-non-const-parameter)
|
||||
size_t errbuflen)
|
||||
{
|
||||
vimoption_T *opt = &options[opt_idx];
|
||||
@ -3460,6 +3466,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
|
||||
bool restore_chartab = false;
|
||||
bool free_oldval = (opt->flags & P_ALLOCED);
|
||||
bool value_changed = false;
|
||||
bool value_checked = false;
|
||||
|
||||
optset_T did_set_cb_args = {
|
||||
.os_varp = varp,
|
||||
@ -3476,8 +3483,11 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
|
||||
.os_win = curwin
|
||||
};
|
||||
|
||||
if (direct) {
|
||||
// Don't do any extra processing if setting directly.
|
||||
}
|
||||
// Disallow changing immutable options.
|
||||
if (opt->immutable && !optval_equal(old_value, new_value)) {
|
||||
else if (opt->immutable && !optval_equal(old_value, new_value)) {
|
||||
errmsg = e_unsupportedoption;
|
||||
}
|
||||
// Disallow changing some options from secure mode.
|
||||
@ -3495,7 +3505,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
|
||||
value_changed = did_set_cb_args.os_value_changed;
|
||||
// The 'keymap', 'filetype' and 'syntax' option callback functions may change the
|
||||
// os_value_checked field.
|
||||
*value_checked = did_set_cb_args.os_value_checked;
|
||||
value_checked = did_set_cb_args.os_value_checked;
|
||||
// The 'isident', 'iskeyword', 'isprint' and 'isfname' options may change the character table.
|
||||
// On failure, this needs to be restored.
|
||||
restore_chartab = did_set_cb_args.os_restore_chartab;
|
||||
@ -3509,16 +3519,26 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
|
||||
buf_init_chartab(curbuf, true);
|
||||
}
|
||||
|
||||
// Unset new_value as it is no longer valid.
|
||||
new_value = NIL_OPTVAL; // NOLINT(clang-analyzer-deadcode.DeadStores)
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
if (set_sid != SID_NONE) {
|
||||
sctx_T script_ctx;
|
||||
|
||||
if (set_sid == 0) {
|
||||
script_ctx = current_sctx;
|
||||
} else {
|
||||
script_ctx.sc_sid = set_sid;
|
||||
script_ctx.sc_seq = 0;
|
||||
script_ctx.sc_lnum = 0;
|
||||
}
|
||||
// Remember where the option was set.
|
||||
set_option_sctx(opt_idx, opt_flags, current_sctx);
|
||||
set_option_sctx(opt_idx, opt_flags, script_ctx);
|
||||
}
|
||||
|
||||
// Free options that are in allocated memory.
|
||||
// Use "free_oldval", because recursiveness may change the flags (esp. init_highlight()).
|
||||
if (free_oldval) {
|
||||
@ -3526,17 +3546,27 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
|
||||
}
|
||||
opt->flags |= P_ALLOCED;
|
||||
|
||||
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && (opt->indir & PV_BOTH)) {
|
||||
const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
|
||||
const bool opt_is_global_local = opt->indir & PV_BOTH;
|
||||
|
||||
if (scope_both) {
|
||||
if (opt_is_global_local) {
|
||||
// Global option with local value set to use global value.
|
||||
// Free the local value and clear it.
|
||||
void *varp_local = get_varp_scope(opt, OPT_LOCAL);
|
||||
OptVal local_unset_value = optval_unset_local(opt_idx, varp_local);
|
||||
set_option_varp(opt_idx, varp_local, local_unset_value, true);
|
||||
} else if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
|
||||
OptVal local_unset_value = get_option_unset_value(opt_idx);
|
||||
set_option_varp(opt_idx, varp_local, optval_copy(local_unset_value), 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't do anything else if setting the option directly.
|
||||
if (direct) {
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
// Trigger the autocommand only after setting the flags.
|
||||
if (varp == &curbuf->b_p_syn) {
|
||||
@ -3589,91 +3619,105 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
/// Set the value of an option using an OptVal.
|
||||
/// Validate the new value for an option.
|
||||
///
|
||||
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
|
||||
/// @param[in] varp Option variable pointer, cannot be NULL.
|
||||
/// @param value New option value. Might get freed.
|
||||
/// @param opt_flags Option flags.
|
||||
/// @param value_replaced Value was replaced completely.
|
||||
/// @param[out] errbuf Buffer for error message.
|
||||
/// @param errbuflen Length of error buffer.
|
||||
///
|
||||
/// @return NULL on success, an untranslated error message on error.
|
||||
static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags,
|
||||
const bool value_replaced, char *errbuf, size_t errbuflen)
|
||||
/// @param varp Pointer to option variable.
|
||||
/// @param newval[in,out] New option value. Might be modified.
|
||||
static const char *validate_option_value(const OptIndex opt_idx, void *varp, OptVal *newval,
|
||||
int opt_flags, char *errbuf, size_t errbuflen)
|
||||
{
|
||||
assert(opt_idx != kOptInvalid && varp != NULL);
|
||||
|
||||
const char *errmsg = NULL;
|
||||
bool value_checked = false;
|
||||
|
||||
vimoption_T *opt = &options[opt_idx];
|
||||
|
||||
if (value.type == kOptValTypeNil) {
|
||||
if (newval->type == kOptValTypeNil) {
|
||||
// Don't try to unset local value if scope is global.
|
||||
// TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is
|
||||
// fixed.
|
||||
if (opt_flags == OPT_GLOBAL) {
|
||||
errmsg = _("Cannot unset global option value");
|
||||
} else {
|
||||
optval_free(value);
|
||||
value = optval_unset_local(opt_idx, varp);
|
||||
optval_free(*newval);
|
||||
*newval = optval_copy(get_option_unset_value(opt_idx));
|
||||
}
|
||||
} else if (!option_has_type(opt_idx, value.type)) {
|
||||
char *rep = optval_to_cstr(value);
|
||||
} else if (!option_has_type(opt_idx, newval->type)) {
|
||||
char *rep = optval_to_cstr(*newval);
|
||||
char *valid_types = option_get_valid_types(opt_idx);
|
||||
snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"),
|
||||
opt->fullname, valid_types, optval_type_get_name(value.type), rep);
|
||||
opt->fullname, valid_types, optval_type_get_name(newval->type), rep);
|
||||
xfree(rep);
|
||||
xfree(valid_types);
|
||||
errmsg = errbuf;
|
||||
} else if (newval->type == kOptValTypeNumber) {
|
||||
// Validate and bound check num option values.
|
||||
errmsg = validate_num_option(opt_idx, varp, &newval->data.number, errbuf, errbuflen);
|
||||
}
|
||||
|
||||
if (errmsg != NULL) {
|
||||
goto err;
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
/// Set the value of an option using an OptVal.
|
||||
///
|
||||
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
|
||||
/// @param[in] varp Option variable pointer, cannot be NULL.
|
||||
/// @param value New option value. Might get freed.
|
||||
/// @param opt_flags Option flags.
|
||||
/// @param set_sid Script ID. Special values:
|
||||
/// 0: Use current script ID.
|
||||
/// SID_NONE: Don't set script ID.
|
||||
/// @param direct Don't process side-effects.
|
||||
/// @param value_replaced Value was replaced completely.
|
||||
/// @param[out] errbuf Buffer for error message.
|
||||
/// @param errbuflen Length of error buffer.
|
||||
///
|
||||
/// @return NULL on success, an untranslated error message on error.
|
||||
static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags,
|
||||
scid_T set_sid, const bool direct, const bool value_replaced,
|
||||
char *errbuf, size_t errbuflen)
|
||||
FUNC_ATTR_NONNULL_ARG(2)
|
||||
{
|
||||
assert(opt_idx != kOptInvalid);
|
||||
|
||||
const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen);
|
||||
|
||||
if (errmsg != NULL) {
|
||||
optval_free(value);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
vimoption_T *opt = &options[opt_idx];
|
||||
const bool scope_local = opt_flags & OPT_LOCAL;
|
||||
const bool scope_global = opt_flags & OPT_GLOBAL;
|
||||
const bool scope_both = !scope_local && !scope_global;
|
||||
const bool opt_is_global_local = opt->indir & PV_BOTH;
|
||||
// Whether local value of global-local option is unset.
|
||||
// NOTE: When this is true, it also implies that opt_is_global_local is true.
|
||||
const bool is_opt_local_unset = is_option_local_value_unset(opt_idx);
|
||||
|
||||
// When using ":set opt=val" for a global option with a local value the local value will be reset,
|
||||
// use the global value here.
|
||||
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && ((int)opt->indir & PV_BOTH)) {
|
||||
if (scope_both && opt_is_global_local) {
|
||||
varp = opt->var;
|
||||
}
|
||||
|
||||
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 = NIL_OPTVAL;
|
||||
OptVal old_local_value = NIL_OPTVAL;
|
||||
|
||||
// Save the local and global values before changing anything. This is needed as for a global-only
|
||||
// option setting the "local value" in fact sets the global value (since there is only one value).
|
||||
//
|
||||
// TODO(famiu): This needs to be changed to use the current type of the old value instead of
|
||||
// value.type, when multi-type options are added.
|
||||
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
|
||||
old_global_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL));
|
||||
old_local_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_LOCAL));
|
||||
|
||||
OptVal old_global_value = optval_from_varp(opt_idx, varp_global);
|
||||
// If local value of global-local option is unset, use global value as local value.
|
||||
if (is_option_local_value_unset(opt, curbuf, curwin)) {
|
||||
old_local_value = old_global_value;
|
||||
}
|
||||
}
|
||||
|
||||
OptVal old_local_value = is_opt_local_unset
|
||||
? old_global_value
|
||||
: optval_from_varp(opt_idx, varp_local);
|
||||
// Value that's actually being used.
|
||||
// For local scope of a global-local option, it is equal to the global value.
|
||||
// In every other case, it is the same as old_value.
|
||||
const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL);
|
||||
OptVal used_old_value = oldval_is_global ? optval_from_varp(opt_idx, get_varp(opt)) : old_value;
|
||||
|
||||
if (value.type == kOptValTypeNumber) {
|
||||
errmsg = validate_num_option(opt_idx, varp, &value.data.number, errbuf, errbuflen);
|
||||
if (errmsg != NULL) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
set_option_varp(opt_idx, varp, value, false);
|
||||
// For local scope of a global-local option, it's equal to the global value if the local value is
|
||||
// unset. In every other case, it is the same as old_value.
|
||||
// This value is used instead of old_value when triggering the OptionSet autocommand.
|
||||
OptVal used_old_value = (scope_local && is_opt_local_unset)
|
||||
? optval_from_varp(opt_idx, get_varp(opt))
|
||||
: old_value;
|
||||
|
||||
// Save the old values and the new value in case they get changed.
|
||||
OptVal saved_used_value = optval_copy(used_old_value);
|
||||
OptVal saved_old_global_value = optval_copy(old_global_value);
|
||||
OptVal saved_old_local_value = optval_copy(old_local_value);
|
||||
@ -3690,23 +3734,20 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
|
||||
secure = 1;
|
||||
}
|
||||
|
||||
errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, &value_checked,
|
||||
// Set option through its variable pointer.
|
||||
set_option_varp(opt_idx, varp, value, false);
|
||||
// Process any side effects.
|
||||
errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, set_sid, direct,
|
||||
value_replaced, errbuf, errbuflen);
|
||||
|
||||
secure = secure_saved;
|
||||
|
||||
if (errmsg == NULL) {
|
||||
if (errmsg == NULL && !direct) {
|
||||
if (!starting) {
|
||||
apply_optionset_autocmd(opt_idx, opt_flags, saved_used_value, saved_old_global_value,
|
||||
saved_old_local_value, saved_new_value, errmsg);
|
||||
}
|
||||
if (opt->flags & P_UI_OPTION) {
|
||||
// Calculate saved_new_value again as its value might be changed by bound checks.
|
||||
// NOTE: Currently there are no buffer/window local UI options, but if there ever are buffer
|
||||
// or window local UI options added in the future, varp might become invalid if the buffer or
|
||||
// window is closed during an autocommand, and a check would have to be added for it.
|
||||
optval_free(saved_new_value);
|
||||
saved_new_value = optval_copy(optval_from_varp(opt_idx, varp));
|
||||
ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value));
|
||||
}
|
||||
}
|
||||
@ -3716,10 +3757,70 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
|
||||
optval_free(saved_old_local_value);
|
||||
optval_free(saved_old_global_value);
|
||||
optval_free(saved_new_value);
|
||||
|
||||
return errmsg;
|
||||
err:
|
||||
optval_free(value);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
/// Set option value directly, without processing any side effects.
|
||||
///
|
||||
/// @param opt_idx Option index in options[] table.
|
||||
/// @param value Option value.
|
||||
/// @param opt_flags Option flags.
|
||||
/// @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)
|
||||
{
|
||||
static char errbuf[IOSIZE];
|
||||
|
||||
vimoption_T *opt = get_option(opt_idx);
|
||||
|
||||
if (opt->var == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
|
||||
void *varp = get_varp_scope(opt, scope_both ? OPT_LOCAL : opt_flags);
|
||||
|
||||
set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true, errbuf,
|
||||
sizeof(errbuf));
|
||||
}
|
||||
|
||||
/// Set option value directly for buffer / window, without processing any side effects.
|
||||
///
|
||||
/// @param opt_idx Option index in options[] table.
|
||||
/// @param value Option value.
|
||||
/// @param opt_flags Option flags.
|
||||
/// @param set_sid Script ID. Special values:
|
||||
/// 0: Use current script ID.
|
||||
/// SID_NONE: Don't set script ID.
|
||||
/// @param req_scope Requested option scope. See OptReqScope 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,
|
||||
OptReqScope req_scope, void *const from)
|
||||
{
|
||||
buf_T *save_curbuf = curbuf;
|
||||
win_T *save_curwin = curwin;
|
||||
|
||||
// Don't use switch_option_context(), as that calls aucmd_prepbuf(), which may have unintended
|
||||
// side-effects when setting an option directly. Just change the values of curbuf and curwin if
|
||||
// needed, no need to properly switch the window / buffer.
|
||||
switch (req_scope) {
|
||||
case kOptReqGlobal:
|
||||
break;
|
||||
case kOptReqBuf:
|
||||
curbuf = (buf_T *)from;
|
||||
break;
|
||||
case kOptReqWin:
|
||||
curwin = (win_T *)from;
|
||||
curbuf = curwin->w_buffer;
|
||||
break;
|
||||
}
|
||||
|
||||
set_option_direct(opt_idx, value, opt_flags, set_sid);
|
||||
|
||||
curwin = save_curwin;
|
||||
curbuf = save_curbuf;
|
||||
}
|
||||
|
||||
/// Set the value of an option.
|
||||
@ -3747,7 +3848,8 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return set_option(opt_idx, varp, optval_copy(value), opt_flags, true, errbuf, sizeof(errbuf));
|
||||
return set_option(opt_idx, varp, optval_copy(value), opt_flags, 0, false, true, errbuf,
|
||||
sizeof(errbuf));
|
||||
}
|
||||
|
||||
/// Set the value of an option. Supports TTY options, unlike set_option_value().
|
||||
@ -6203,7 +6305,7 @@ void set_fileformat(int eol_style, int opt_flags)
|
||||
|
||||
// p is NULL if "eol_style" is EOL_UNKNOWN.
|
||||
if (p != NULL) {
|
||||
set_string_option_direct(kOptFileformat, p, opt_flags, 0);
|
||||
set_option_direct(kOptFileformat, CSTR_AS_OPTVAL(p), opt_flags, 0);
|
||||
}
|
||||
|
||||
// This may cause the buffer to become (un)modified.
|
||||
|
@ -266,113 +266,6 @@ void check_string_option(char **pp)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set global value for string option when it's a local option.
|
||||
///
|
||||
/// @param opt option
|
||||
/// @param varp pointer to option variable
|
||||
static void set_string_option_global(vimoption_T *opt, char **varp)
|
||||
{
|
||||
char **p;
|
||||
|
||||
// the global value is always allocated
|
||||
if (opt->var == VAR_WIN) {
|
||||
p = (char **)GLOBAL_WO(varp);
|
||||
} else {
|
||||
p = (char **)opt->var;
|
||||
}
|
||||
if (opt->indir != PV_NONE && p != varp) {
|
||||
char *s = xstrdup(*varp);
|
||||
free_string_option(*p);
|
||||
*p = s;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a string option to a new value (without checking the effect).
|
||||
/// The string is copied into allocated memory.
|
||||
/// if ("opt_idx" == kOptInvalid) "name" is used, otherwise "opt_idx" is used.
|
||||
/// When "set_sid" is zero set the scriptID to current_sctx.sc_sid. When
|
||||
/// "set_sid" is SID_NONE don't set the scriptID. Otherwise set the scriptID to
|
||||
/// "set_sid".
|
||||
///
|
||||
/// @param opt_flags Option flags.
|
||||
///
|
||||
/// TODO(famiu): Remove this and its win/buf variants.
|
||||
void set_string_option_direct(OptIndex opt_idx, const char *val, int opt_flags, scid_T set_sid)
|
||||
{
|
||||
vimoption_T *opt = get_option(opt_idx);
|
||||
|
||||
if (opt->var == NULL) { // can't set hidden option
|
||||
return;
|
||||
}
|
||||
|
||||
assert(opt->var != &p_shada);
|
||||
|
||||
bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
|
||||
char *s = xstrdup(val);
|
||||
char **varp = (char **)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags);
|
||||
|
||||
if (opt->flags & P_ALLOCED) {
|
||||
free_string_option(*varp);
|
||||
}
|
||||
*varp = s;
|
||||
|
||||
// For buffer/window local option may also set the global value.
|
||||
if (both) {
|
||||
set_string_option_global(opt, varp);
|
||||
}
|
||||
|
||||
opt->flags |= P_ALLOCED;
|
||||
|
||||
// When setting both values of a global option with a local value,
|
||||
// make the local value empty, so that the global value is used.
|
||||
if ((opt->indir & PV_BOTH) && both) {
|
||||
free_string_option(*varp);
|
||||
*varp = empty_string_option;
|
||||
}
|
||||
if (set_sid != SID_NONE) {
|
||||
sctx_T script_ctx;
|
||||
|
||||
if (set_sid == 0) {
|
||||
script_ctx = current_sctx;
|
||||
} else {
|
||||
script_ctx.sc_sid = set_sid;
|
||||
script_ctx.sc_seq = 0;
|
||||
script_ctx.sc_lnum = 0;
|
||||
}
|
||||
set_option_sctx(opt_idx, opt_flags, script_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Like set_string_option_direct(), but for a window-local option in "wp".
|
||||
/// Blocks autocommands to avoid the old curwin becoming invalid.
|
||||
void set_string_option_direct_in_win(win_T *wp, OptIndex opt_idx, const char *val, int opt_flags,
|
||||
int set_sid)
|
||||
{
|
||||
win_T *save_curwin = curwin;
|
||||
|
||||
block_autocmds();
|
||||
curwin = wp;
|
||||
curbuf = curwin->w_buffer;
|
||||
set_string_option_direct(opt_idx, val, opt_flags, set_sid);
|
||||
curwin = save_curwin;
|
||||
curbuf = curwin->w_buffer;
|
||||
unblock_autocmds();
|
||||
}
|
||||
|
||||
/// Like set_string_option_direct(), but for a buffer-local option in "buf".
|
||||
/// Blocks autocommands to avoid the old curwin becoming invalid.
|
||||
void set_string_option_direct_in_buf(buf_T *buf, OptIndex opt_idx, const char *val, int opt_flags,
|
||||
int set_sid)
|
||||
{
|
||||
buf_T *save_curbuf = curbuf;
|
||||
|
||||
block_autocmds();
|
||||
curbuf = buf;
|
||||
set_string_option_direct(opt_idx, val, opt_flags, set_sid);
|
||||
curbuf = save_curbuf;
|
||||
unblock_autocmds();
|
||||
}
|
||||
|
||||
/// Return true if "val" is a valid 'filetype' name.
|
||||
/// Also used for 'syntax' and 'keymap'.
|
||||
static bool valid_filetype(const char *val)
|
||||
|
@ -704,7 +704,8 @@ static win_T *pum_create_float_preview(bool enter)
|
||||
return NULL;
|
||||
}
|
||||
buf_T *buf = find_buffer_by_handle(b, &err);
|
||||
set_string_option_direct_in_buf(buf, kOptBufhidden, "wipe", OPT_LOCAL, 0);
|
||||
set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, kOptReqBuf,
|
||||
buf);
|
||||
wp->w_float_is_info = true;
|
||||
wp->w_p_diff = false;
|
||||
buf->b_p_bl = false;
|
||||
|
@ -5154,7 +5154,7 @@ void ex_cfile(exarg_T *eap)
|
||||
}
|
||||
}
|
||||
if (*eap->arg != NUL) {
|
||||
set_string_option_direct(kOptErrorfile, eap->arg, 0, 0);
|
||||
set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(eap->arg), 0, 0);
|
||||
}
|
||||
|
||||
char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
|
||||
|
@ -2187,7 +2187,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
// matter?
|
||||
// if (called_emsg > called_emsg_before)
|
||||
if (opt_idx != kOptInvalid && did_emsg > did_emsg_before) {
|
||||
set_string_option_direct(opt_idx, "", opt_scope, SID_ERROR);
|
||||
set_option_direct(opt_idx, STATIC_CSTR_AS_OPTVAL(""), opt_scope, SID_ERROR);
|
||||
}
|
||||
|
||||
// A user function may reset KeyTyped, restore it.
|
||||
|
18
test/functional/options/modified_spec.lua
Normal file
18
test/functional/options/modified_spec.lua
Normal file
@ -0,0 +1,18 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear = helpers.clear
|
||||
local eq = helpers.eq
|
||||
local api = helpers.api
|
||||
|
||||
describe("'modified'", function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
it("can be unset after changing 'fileformat'", function()
|
||||
for _, ff in ipairs({ 'unix', 'dos', 'mac' }) do
|
||||
api.nvim_set_option_value('fileformat', ff, {})
|
||||
api.nvim_set_option_value('modified', false, {})
|
||||
eq(false, api.nvim_get_option_value('modified', {}), 'fileformat=' .. ff)
|
||||
end
|
||||
end)
|
||||
end)
|
Loading…
Reference in New Issue
Block a user