diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-03-22 09:14:42 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-22 09:14:42 +0800 |
commit | dc110cba3c0d48d7c9dbb91900f8be0cf6cf0c9b (patch) | |
tree | a534155ed127b0c6b4f22ea86150c4e0c045d62a /src | |
parent | 15c6909bb198ca8a1a22405a4a7e96357716e57e (diff) | |
parent | 58cc66ee1f066127c33157b91c64d9b125a96621 (diff) | |
download | rneovim-dc110cba3c0d48d7c9dbb91900f8be0cf6cf0c9b.tar.gz rneovim-dc110cba3c0d48d7c9dbb91900f8be0cf6cf0c9b.tar.bz2 rneovim-dc110cba3c0d48d7c9dbb91900f8be0cf6cf0c9b.zip |
Merge pull request #26970 from famiu/refactor/options/set_option_direct
refactor(options): remove `set_string_option_direct()`
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 6 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 4 | ||||
-rw-r--r-- | src/nvim/diff.c | 3 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 8 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/fileio.c | 4 | ||||
-rw-r--r-- | src/nvim/help.c | 6 | ||||
-rw-r--r-- | src/nvim/indent.c | 2 | ||||
-rw-r--r-- | src/nvim/main.c | 2 | ||||
-rw-r--r-- | src/nvim/option.c | 368 | ||||
-rw-r--r-- | src/nvim/optionstr.c | 107 | ||||
-rw-r--r-- | src/nvim/popupmenu.c | 3 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 2 | ||||
-rw-r--r-- | src/nvim/statusline.c | 2 |
15 files changed, 262 insertions, 263 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 43bf4eaf31..2b3ebb7bfb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -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; diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 285ef538b9..00cfef23c7 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -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); } } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 39d9e9e978..ea846b46ec 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -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; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 7c49189602..dd30cdbba4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -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; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6db72ff2d1..2454c77334 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -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; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e5d82cc126..a4501a31c2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -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. diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index f032604a59..6b50b031a3 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -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); } diff --git a/src/nvim/help.c b/src/nvim/help.c index 779772cedf..e9f67ca3e4 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -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. diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 6cbb86866e..d477b466d7 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -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 { diff --git a/src/nvim/main.c b/src/nvim/main.c index ea189aaa0c..361d43393b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -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) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 3d7fdefdeb..73a5af5520 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -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); - // Remember where the option was set. - set_option_sctx(opt_idx, opt_flags, current_sctx); + 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, 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,16 +3546,26 @@ 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)) { - // 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) { - // 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); + 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 = 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. @@ -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. -/// -/// @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. +/// Validate the new value for an option. /// -/// @return NULL on success, an untranslated error message on error. -static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, - const bool value_replaced, char *errbuf, size_t errbuflen) +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. +/// @param varp Pointer to option variable. +/// @param newval[in,out] New option value. Might be modified. +static const char *validate_option_value(const OptIndex opt_idx, void *varp, OptVal *newval, + int opt_flags, char *errbuf, size_t errbuflen) { - assert(opt_idx != kOptInvalid && varp != NULL); - const char *errmsg = NULL; - bool value_checked = false; - vimoption_T *opt = &options[opt_idx]; - if (value.type == kOptValTypeNil) { + if (newval->type == kOptValTypeNil) { // Don't try to unset local value if scope is global. // TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is // fixed. if (opt_flags == OPT_GLOBAL) { errmsg = _("Cannot unset global option value"); } else { - optval_free(value); - value = optval_unset_local(opt_idx, varp); + optval_free(*newval); + *newval = optval_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); } + 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) { - goto err; + optval_free(value); + return errmsg; } + vimoption_T *opt = &options[opt_idx]; + const bool scope_local = opt_flags & OPT_LOCAL; + const bool scope_global = opt_flags & OPT_GLOBAL; + const bool scope_both = !scope_local && !scope_global; + const bool opt_is_global_local = opt->indir & PV_BOTH; + // Whether local value of global-local option is unset. + // NOTE: When this is true, it also implies that opt_is_global_local is true. + const bool is_opt_local_unset = is_option_local_value_unset(opt_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; } - OptVal old_value = optval_from_varp(opt_idx, varp); - OptVal old_global_value = NIL_OPTVAL; - OptVal old_local_value = NIL_OPTVAL; - - // Save the local and global values before changing anything. This is needed as for a global-only - // option setting the "local value" in fact sets the global value (since there is only one value). - // - // TODO(famiu): This needs to be changed to use the current type of the old value instead of - // value.type, when multi-type options are added. - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { - old_global_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL)); - old_local_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_LOCAL)); - - // If local value of global-local option is unset, use global value as local value. - if (is_option_local_value_unset(opt, curbuf, curwin)) { - old_local_value = old_global_value; - } - } + void *varp_local = get_varp_scope(opt, OPT_LOCAL); + void *varp_global = get_varp_scope(opt, OPT_GLOBAL); + OptVal old_value = optval_from_varp(opt_idx, varp); + OptVal old_global_value = optval_from_varp(opt_idx, varp_global); + // If local value of global-local option is unset, use global value as local value. + OptVal old_local_value = is_opt_local_unset + ? old_global_value + : optval_from_varp(opt_idx, varp_local); // Value that's actually being used. - // For local scope of a global-local option, it is equal to the global value. - // In every other case, it is the same as old_value. - const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL); - OptVal used_old_value = oldval_is_global ? optval_from_varp(opt_idx, get_varp(opt)) : old_value; - - if (value.type == kOptValTypeNumber) { - errmsg = validate_num_option(opt_idx, varp, &value.data.number, errbuf, errbuflen); - if (errmsg != NULL) { - goto err; - } - } - - set_option_varp(opt_idx, varp, value, false); - + // For local scope of a global-local option, it's equal to the global value if the local value is + // unset. In every other case, it is the same as old_value. + // This value is used instead of old_value when triggering the OptionSet autocommand. + OptVal used_old_value = (scope_local && is_opt_local_unset) + ? optval_from_varp(opt_idx, get_varp(opt)) + : old_value; + + // Save the old values and the new value in case they get changed. OptVal saved_used_value = optval_copy(used_old_value); OptVal saved_old_global_value = optval_copy(old_global_value); OptVal saved_old_local_value = optval_copy(old_local_value); @@ -3690,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. diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index f80926726a..29433ddbb5 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -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) diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 5e8fb7f5de..bed0a8df4a 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -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; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 0f639bf86a..9f431556e8 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -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; diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 1d914dd105..ca7083a9e3 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -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. |