From 960170e4469b96c5c3d06ba1bb84c9edeaa04b8b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 19:36:33 +0800 Subject: vim-patch:9.0.1366: functions for setting options are in random order (#25440) Problem: Functions for setting options are in random order. Solution: Sort functions alphabetically. (Yegappan Lakshmanan, closes vim/vim#12082) https://github.com/vim/vim/commit/ad60898aa47b44fdece12d28c471fb50df27fb50 Co-authored-by: Yegappan Lakshmanan --- src/nvim/option.c | 1160 +++++++++++++++++----------------- src/nvim/optionstr.c | 1706 +++++++++++++++++++++++++------------------------- 2 files changed, 1436 insertions(+), 1430 deletions(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 7e7e387521..eef5e66aeb 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2037,74 +2037,149 @@ static const char *did_set_force_off(bool *doskip) return NULL; } -/// Process the updated 'langremap' option value. -static const char *did_set_langremap(optset_T *args FUNC_ATTR_UNUSED) +/// Process the updated 'arabic' option value. +static const char *did_set_arabic(optset_T *args) { - // 'langremap' -> !'langnoremap' - p_lnr = !p_lrm; + win_T *win = (win_T *)args->os_win; + const char *errmsg = NULL; + + if (win->w_p_arab) { + // 'arabic' is set, handle various sub-settings. + if (!p_tbidi) { + // set rightleft mode + if (!win->w_p_rl) { + win->w_p_rl = true; + changed_window_setting(); + } + + // Enable Arabic shaping (major part of what Arabic requires) + if (!p_arshape) { + p_arshape = true; + redraw_all_later(UPD_NOT_VALID); + } + } + + // Arabic requires a utf-8 encoding, inform the user if it's not + // set. + if (strcmp(p_enc, "utf-8") != 0) { + static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'"); + + msg_source(HL_ATTR(HLF_W)); + msg(_(w_arabic), HL_ATTR(HLF_W)); + set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1); + } + + // set 'delcombine' + p_deco = true; + + // Force-set the necessary keymap for arabic. + errmsg = set_option_value("keymap", STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); + } else { + // 'arabic' is reset, handle various sub-settings. + if (!p_tbidi) { + // reset rightleft mode + if (win->w_p_rl) { + win->w_p_rl = false; + changed_window_setting(); + } + + // 'arabicshape' isn't reset, it is a global option and + // another window may still need it "on". + } + + // 'delcombine' isn't reset, it is a global option and another + // window may still want it "on". + + // Revert to the default keymap + curbuf->b_p_iminsert = B_IMODE_NONE; + curbuf->b_p_imsearch = B_IMODE_USE_INSERT; + } + + return errmsg; +} + +/// Process the updated 'autochdir' option value. +static const char *did_set_autochdir(optset_T *args FUNC_ATTR_UNUSED) +{ + // Change directories when the 'acd' option is set now. + do_autochdir(); return NULL; } -/// Process the updated 'langnoremap' option value. -static const char *did_set_langnoremap(optset_T *args FUNC_ATTR_UNUSED) +/// Process the updated 'binary' option value. +static const char *did_set_binary(optset_T *args) { - // 'langnoremap' -> !'langremap' - p_lrm = !p_lnr; + 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); + redraw_titles(); + return NULL; } -/// Process the updated 'undofile' option value. -static const char *did_set_undofile(optset_T *args) +/// Called when the 'breakat' option changes value. +static const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED) { - // Only take action when the option was set. - if (!curbuf->b_p_udf && !p_udf) { - return NULL; + for (int i = 0; i < 256; i++) { + breakat_flags[i] = false; } - // When reset we do not delete the undo file, the option may be set again - // without making any changes in between. - uint8_t hash[UNDO_HASH_SIZE]; - - FOR_ALL_BUFFERS(bp) { - // When 'undofile' is set globally: for every buffer, otherwise - // only for the current buffer: Try to read in the undofile, - // if one exists, the buffer wasn't changed and the buffer was - // loaded - if ((curbuf == bp - || (args->os_flags & OPT_GLOBAL) || args->os_flags == 0) - && !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) { - u_compute_hash(bp, hash); - u_read_undo(NULL, hash, bp->b_fname); + if (p_breakat != NULL) { + for (char *p = p_breakat; *p; p++) { + breakat_flags[(uint8_t)(*p)] = true; } } return NULL; } -/// Process the updated 'readonly' option value. -static const char *did_set_readonly(optset_T *args) +/// Process the updated 'buflisted' option value. +static const char *did_set_buflisted(optset_T *args) { - // when 'readonly' is reset globally, also reset readonlymode - if (!curbuf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) { - readonlymode = false; + buf_T *buf = (buf_T *)args->os_buf; + + // when 'buflisted' changes, trigger autocommands + if (args->os_oldval.boolean != buf->b_p_bl) { + apply_autocmds(buf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE, + NULL, NULL, true, buf); } + return NULL; +} - // when 'readonly' is set may give W10 again - if (curbuf->b_p_ro) { - curbuf->b_did_warn = false; +/// Process the new 'cmdheight' option value. +static const char *did_set_cmdheight(optset_T *args) +{ + OptInt old_value = args->os_oldval.number; + + if (ui_has(kUIMessages)) { + p_ch = 0; + } + if (p_ch > Rows - min_rows() + 1) { + p_ch = Rows - min_rows() + 1; } - redraw_titles(); + // if p_ch changed value, change the command line height + // Only compute the new window layout when startup has been + // completed. Otherwise the frame sizes may be wrong. + if ((p_ch != old_value + || tabline_height() + global_stl_height() + topframe->fr_height != Rows - p_ch) + && full_screen) { + command_height(); + } return NULL; } -/// Process the updated 'modifiable' option value. -static const char *did_set_modifiable(optset_T *args FUNC_ATTR_UNUSED) +/// Process the updated 'diff' option value. +static const char *did_set_diff(optset_T *args) { - // when 'modifiable' is changed, redraw the window title - redraw_titles(); - + win_T *win = (win_T *)args->os_win; + // May add or remove the buffer from the list of diff buffers. + diff_buf_adjust(win); + if (foldmethodIsDiff(win)) { + foldUpdateAll(win); + } return NULL; } @@ -2117,76 +2192,205 @@ static const char *did_set_eof_eol_fixeol_bomb(optset_T *args FUNC_ATTR_UNUSED) return NULL; } -/// Process the updated 'binary' option value. -static const char *did_set_binary(optset_T *args) +/// Process the updated 'equalalways' option value. +static const char *did_set_equalalways(optset_T *args) { - buf_T *buf = (buf_T *)args->os_buf; + win_T *win = (win_T *)args->os_win; + if (p_ea && !args->os_oldval.boolean) { + win_equal(win, false, 0); + } - // when 'bin' is set also set some other options - set_options_bin((int)args->os_oldval.boolean, buf->b_p_bin, args->os_flags); - redraw_titles(); + return NULL; +} +/// Process the new 'foldlevel' option value. +static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED) +{ + newFoldLevel(); return NULL; } -/// Process the updated 'buflisted' option value. -static const char *did_set_buflisted(optset_T *args) +/// Process the new 'foldminlines' option value. +static const char *did_set_foldminlines(optset_T *args) { - buf_T *buf = (buf_T *)args->os_buf; + win_T *win = (win_T *)args->os_win; + foldUpdateAll(win); + return NULL; +} - // when 'buflisted' changes, trigger autocommands - if (args->os_oldval.boolean != buf->b_p_bl) { - apply_autocmds(buf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE, - NULL, NULL, true, buf); +/// Process the new 'foldnestmax' option value. +static const char *did_set_foldnestmax(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + if (foldmethodIsSyntax(win) || foldmethodIsIndent(win)) { + foldUpdateAll(win); } return NULL; } -/// Process the updated 'swapfile' option value. -static const char *did_set_swapfile(optset_T *args) +/// Process the new 'helpheight' option value. +static const char *did_set_helpheight(optset_T *args) { - buf_T *buf = (buf_T *)args->os_buf; - // when 'swf' is set, create swapfile, when reset remove swapfile - if (buf->b_p_swf && p_uc) { - ml_open_file(buf); // create the swap file - } else { - // no need to reset curbuf->b_may_swap, ml_open_file() will check - // buf->b_p_swf - mf_close_file(buf, true); // remove the swap file + // Change window height NOW + if (!ONE_WINDOW) { + buf_T *buf = (buf_T *)args->os_buf; + win_T *win = (win_T *)args->os_win; + if (buf->b_help && win->w_height < p_hh) { + win_setheight((int)p_hh); + } } + return NULL; } -/// Process the updated 'paste' option value. -static const char *did_set_paste(optset_T *args FUNC_ATTR_UNUSED) +/// Process the updated 'hlsearch' option value. +static const char *did_set_hlsearch(optset_T *args FUNC_ATTR_UNUSED) { - static int old_p_paste = false; - static int save_sm = 0; - static int save_sta = 0; - static int save_ru = 0; - static int save_ri = 0; + // when 'hlsearch' is set or reset: reset no_hlsearch + set_no_hlsearch(false); + return NULL; +} - if (p_paste) { - // Paste switched from off to on. - // Save the current values, so they can be restored later. - if (!old_p_paste) { - // save options for each buffer - FOR_ALL_BUFFERS(buf) { - buf->b_p_tw_nopaste = buf->b_p_tw; - buf->b_p_wm_nopaste = buf->b_p_wm; - buf->b_p_sts_nopaste = buf->b_p_sts; - buf->b_p_ai_nopaste = buf->b_p_ai; - buf->b_p_et_nopaste = buf->b_p_et; - if (buf->b_p_vsts_nopaste) { - xfree(buf->b_p_vsts_nopaste); - } - buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option - ? xstrdup(buf->b_p_vsts) - : NULL; - } +/// Process the updated 'ignorecase' option value. +static const char *did_set_ignorecase(optset_T *args FUNC_ATTR_UNUSED) +{ + // when 'ignorecase' is set or reset and 'hlsearch' is set, redraw + if (p_hls) { + redraw_all_later(UPD_SOME_VALID); + } + return NULL; +} - // save global options - save_sm = p_sm; +/// Process the new 'iminset' option value. +static const char *did_set_iminsert(optset_T *args FUNC_ATTR_UNUSED) +{ + showmode(); + // Show/unshow value of 'keymap' in status lines. + status_redraw_curbuf(); + + return NULL; +} + +/// Process the updated 'langnoremap' option value. +static const char *did_set_langnoremap(optset_T *args FUNC_ATTR_UNUSED) +{ + // 'langnoremap' -> !'langremap' + p_lrm = !p_lnr; + return NULL; +} + +/// Process the updated 'langremap' option value. +static const char *did_set_langremap(optset_T *args FUNC_ATTR_UNUSED) +{ + // 'langremap' -> !'langnoremap' + p_lnr = !p_lrm; + return NULL; +} + +/// 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; + + // When switching to global statusline, decrease topframe height + // Also clear the cmdline to remove the ruler if there is one + if (value == 3 && old_value != 3) { + frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false); + (void)win_comp_pos(); + clear_cmdline = true; + } + // When switching from global statusline, increase height of topframe by STATUS_HEIGHT + // in order to to re-add the space that was previously taken by the global statusline + if (old_value == 3 && value != 3) { + frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false); + (void)win_comp_pos(); + } + + last_status(false); // (re)set last window status line. + return NULL; +} + +/// Process the updated 'lisp' option value. +static const char *did_set_lisp(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + // When 'lisp' option changes include/exclude '-' in keyword characters. + (void)buf_init_chartab(buf, false); // ignore errors + return NULL; +} + +/// Process the updated 'modifiable' option value. +static const char *did_set_modifiable(optset_T *args FUNC_ATTR_UNUSED) +{ + // when 'modifiable' is changed, redraw the window title + redraw_titles(); + + return NULL; +} + +/// Process the updated 'modified' option value. +static const char *did_set_modified(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + if (!args->os_newval.boolean) { + save_file_ff(buf); // Buffer is unchanged + } + redraw_titles(); + modified_was_set = (int)args->os_newval.boolean; + return NULL; +} + +/// Process the updated 'number' or 'relativenumber' option value. +static const char *did_set_number_relativenumber(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + if (*win->w_p_stc != NUL) { + // When 'relativenumber'/'number' is changed and 'statuscolumn' is set, reset width. + win->w_nrwidth_line_count = 0; + } + return NULL; +} + +/// Process the new 'numberwidth' option value. +static const char *did_set_numberwidth(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + win->w_nrwidth_line_count = 0; // trigger a redraw + + return NULL; +} + +/// Process the updated 'paste' option value. +static const char *did_set_paste(optset_T *args FUNC_ATTR_UNUSED) +{ + static int old_p_paste = false; + static int save_sm = 0; + static int save_sta = 0; + static int save_ru = 0; + static int save_ri = 0; + + if (p_paste) { + // Paste switched from off to on. + // Save the current values, so they can be restored later. + if (!old_p_paste) { + // save options for each buffer + FOR_ALL_BUFFERS(buf) { + buf->b_p_tw_nopaste = buf->b_p_tw; + buf->b_p_wm_nopaste = buf->b_p_wm; + buf->b_p_sts_nopaste = buf->b_p_sts; + buf->b_p_ai_nopaste = buf->b_p_ai; + buf->b_p_et_nopaste = buf->b_p_et; + if (buf->b_p_vsts_nopaste) { + xfree(buf->b_p_vsts_nopaste); + } + buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option + ? xstrdup(buf->b_p_vsts) + : NULL; + } + + // save global options + save_sm = p_sm; save_sta = p_sta; save_ru = p_ru; save_ri = p_ri; @@ -2273,400 +2477,138 @@ static const char *did_set_paste(optset_T *args FUNC_ATTR_UNUSED) p_tw = p_tw_nopaste; p_wm = p_wm_nopaste; if (p_vsts) { - free_string_option(p_vsts); - } - p_vsts = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : empty_option; - } - - old_p_paste = p_paste; - - // Remember where the dependent options were reset - didset_options_sctx((OPT_LOCAL | OPT_GLOBAL), p_paste_dep_opts); - - return NULL; -} - -/// Process the updated 'ignorecase' option value. -static const char *did_set_ignorecase(optset_T *args FUNC_ATTR_UNUSED) -{ - // when 'ignorecase' is set or reset and 'hlsearch' is set, redraw - if (p_hls) { - redraw_all_later(UPD_SOME_VALID); - } - return NULL; -} - -/// Process the updated 'hlsearch' option value. -static const char *did_set_hlsearch(optset_T *args FUNC_ATTR_UNUSED) -{ - // when 'hlsearch' is set or reset: reset no_hlsearch - set_no_hlsearch(false); - return NULL; -} - -/// Process the updated 'scrollbind' option value. -static const char *did_set_scrollbind(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - - // when 'scrollbind' is set: snapshot the current position to avoid a jump - // at the end of normal_cmd() - if (!win->w_p_scb) { - return NULL; - } - do_check_scrollbind(false); - win->w_scbind_pos = win->w_topline; - return NULL; -} - -/// Process the updated 'previewwindow' option value. -static const char *did_set_previewwindow(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - - if (!win->w_p_pvw) { - return NULL; - } - - // There can be only one window with 'previewwindow' set. - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_p_pvw && wp != win) { - win->w_p_pvw = false; - args->os_doskip = true; - return e_preview_window_already_exists; - } - } - - return NULL; -} - -/// Process the updated 'lisp' option value. -static const char *did_set_lisp(optset_T *args) -{ - buf_T *buf = (buf_T *)args->os_buf; - // When 'lisp' option changes include/exclude '-' in keyword characters. - (void)buf_init_chartab(buf, false); // ignore errors - return NULL; -} - -/// Process the updated 'title' or the 'icon' option value. -static const char *did_set_title_icon(optset_T *args FUNC_ATTR_UNUSED) -{ - // when 'title' changed, may need to change the title; same for 'icon' - did_set_title(); - return NULL; -} - -/// Process the updated 'modified' option value. -static const char *did_set_modified(optset_T *args) -{ - buf_T *buf = (buf_T *)args->os_buf; - if (!args->os_newval.boolean) { - save_file_ff(buf); // Buffer is unchanged - } - redraw_titles(); - modified_was_set = (int)args->os_newval.boolean; - return NULL; -} - -#ifdef BACKSLASH_IN_FILENAME -/// Process the updated 'shellslash' option value. -static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED) -{ - if (p_ssl) { - psepc = '/'; - psepcN = '\\'; - pseps[0] = '/'; - } else { - psepc = '\\'; - psepcN = '/'; - pseps[0] = '\\'; - } - - // need to adjust the file name arguments and buffer names. - buflist_slash_adjust(); - alist_slash_adjust(); - scriptnames_slash_adjust(); - return NULL; -} -#endif - -/// Process the updated 'wrap' option value. -static const char *did_set_wrap(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - - // If 'wrap' is set, set w_leftcol to zero. - if (win->w_p_wrap) { - win->w_leftcol = 0; - } - return NULL; -} - -/// Process the updated 'equalalways' option value. -static const char *did_set_equalalways(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - if (p_ea && !args->os_oldval.boolean) { - win_equal(win, false, 0); - } - - return NULL; -} - -/// Process the updated 'autochdir' option value. -static const char *did_set_autochdir(optset_T *args FUNC_ATTR_UNUSED) -{ - // Change directories when the 'acd' option is set now. - do_autochdir(); - return NULL; -} - -/// Process the updated 'diff' option value. -static const char *did_set_diff(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - // May add or remove the buffer from the list of diff buffers. - diff_buf_adjust(win); - if (foldmethodIsDiff(win)) { - foldUpdateAll(win); - } - return NULL; -} - -/// Process the updated 'spell' option value. -static const char *did_set_spell(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - if (win->w_p_spell) { - return parse_spelllang(win); - } - - return NULL; -} - -/// Process the updated 'arabic' option value. -static const char *did_set_arabic(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - const char *errmsg = NULL; - - if (win->w_p_arab) { - // 'arabic' is set, handle various sub-settings. - if (!p_tbidi) { - // set rightleft mode - if (!win->w_p_rl) { - win->w_p_rl = true; - changed_window_setting(); - } - - // Enable Arabic shaping (major part of what Arabic requires) - if (!p_arshape) { - p_arshape = true; - redraw_all_later(UPD_NOT_VALID); - } - } - - // Arabic requires a utf-8 encoding, inform the user if it's not - // set. - if (strcmp(p_enc, "utf-8") != 0) { - static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'"); - - msg_source(HL_ATTR(HLF_W)); - msg(_(w_arabic), HL_ATTR(HLF_W)); - set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1); - } - - // set 'delcombine' - p_deco = true; - - // Force-set the necessary keymap for arabic. - errmsg = set_option_value("keymap", STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); - } else { - // 'arabic' is reset, handle various sub-settings. - if (!p_tbidi) { - // reset rightleft mode - if (win->w_p_rl) { - win->w_p_rl = false; - changed_window_setting(); - } - - // 'arabicshape' isn't reset, it is a global option and - // another window may still need it "on". - } - - // 'delcombine' isn't reset, it is a global option and another - // window may still want it "on". - - // Revert to the default keymap - curbuf->b_p_iminsert = B_IMODE_NONE; - curbuf->b_p_imsearch = B_IMODE_USE_INSERT; - } - - return errmsg; -} - -/// Process the updated 'number' or 'relativenumber' option value. -static const char *did_set_number_relativenumber(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - if (*win->w_p_stc != NUL) { - // When 'relativenumber'/'number' is changed and 'statuscolumn' is set, reset width. - win->w_nrwidth_line_count = 0; - } - return NULL; -} - -/// Set the value of a boolean option, taking care of side effects -/// -/// @param[in] opt_idx Option index in options[] table. -/// @param[out] varp Pointer to the option variable. -/// @param[in] value New value. -/// @param[in] opt_flags OPT_LOCAL and/or OPT_GLOBAL. -/// -/// @return NULL on success, error message on error. -static const char *set_bool_option(const int opt_idx, char *const varp, const int value, - const int opt_flags) -{ - int old_value = *(int *)varp; - int old_global_value = 0; - - // Disallow changing some options from secure mode - if ((secure || sandbox != 0) - && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + free_string_option(p_vsts); + } + p_vsts = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : empty_option; } - // Save the global value 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). - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { - old_global_value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); - } + old_p_paste = p_paste; - *(int *)varp = value; // set the new value - // Remember where the option was set. - set_option_sctx_idx(opt_idx, opt_flags, current_sctx); + // Remember where the dependent options were reset + didset_options_sctx((OPT_LOCAL | OPT_GLOBAL), p_paste_dep_opts); - // May set global value for local option. - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { - *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; - } + return NULL; +} - // Handle side effects for changing a bool option. - const char *errmsg = NULL; - bool doskip = false; - if ((int *)varp == &p_force_on) { - errmsg = did_set_force_on(&doskip); - } else if ((int *)varp == &p_force_off) { - errmsg = did_set_force_off(&doskip); - } else if (options[opt_idx].opt_did_set_cb != NULL) { - optset_T args = { - .os_varp = varp, - .os_flags = opt_flags, - .os_oldval.boolean = old_value, - .os_newval.boolean = value, - .os_doskip = false, - .os_errbuf = NULL, - .os_errbuflen = 0, - .os_buf = curbuf, - .os_win = curwin - }; +/// Process the updated 'previewwindow' option value. +static const char *did_set_previewwindow(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; - errmsg = options[opt_idx].opt_did_set_cb(&args); - doskip = args.os_doskip; - } - if (doskip) { - return errmsg; + if (!win->w_p_pvw) { + return NULL; } - // after handling side effects, call autocommand - - options[opt_idx].flags |= P_WAS_SET; + // There can be only one window with 'previewwindow' set. + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_p_pvw && wp != win) { + win->w_p_pvw = false; + args->os_doskip = true; + return e_preview_window_already_exists; + } + } - apply_optionset_autocmd(opt_idx, opt_flags, - (long)(old_value ? true : false), - (long)(old_global_value ? true : false), - (long)(value ? true : false), NULL); + return NULL; +} - if (options[opt_idx].flags & P_UI_OPTION) { - ui_call_option_set(cstr_as_string(options[opt_idx].fullname), - BOOLEAN_OBJ(*varp)); - } - if ((int *)varp == &p_ru || (int *)varp == &p_sc) { - // in case 'ruler' or 'showcmd' changed - comp_col(); - } - if (curwin->w_curswant != MAXCOL - && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) { - curwin->w_set_curswant = true; +/// Process the new 'pumblend' option value. +static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED) +{ + p_pb = MAX(MIN(p_pb, 100), 0); + hl_invalidate_blends(); + pum_grid.blending = (p_pb > 0); + if (pum_drawn()) { + pum_redraw(); } - check_redraw(options[opt_idx].flags); - return errmsg; + return NULL; } -/// Process the new 'winheight' value. -static const char *did_set_winheight(optset_T *args) +/// Process the updated 'readonly' option value. +static const char *did_set_readonly(optset_T *args) { - // Change window height NOW - if (!ONE_WINDOW) { - win_T *win = (win_T *)args->os_win; - if (win->w_height < p_wh) { - win_setheight((int)p_wh); - } + // when 'readonly' is reset globally, also reset readonlymode + if (!curbuf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) { + readonlymode = false; + } + + // when 'readonly' is set may give W10 again + if (curbuf->b_p_ro) { + curbuf->b_did_warn = false; } + redraw_titles(); + return NULL; } -/// Process the new 'helpheight' option value. -static const char *did_set_helpheight(optset_T *args) +/// Process the new 'scrollback' option value. +static const char *did_set_scrollback(optset_T *args) { - // Change window height NOW - if (!ONE_WINDOW) { - buf_T *buf = (buf_T *)args->os_buf; - win_T *win = (win_T *)args->os_win; - if (buf->b_help && win->w_height < p_hh) { - win_setheight((int)p_hh); - } - } + buf_T *buf = (buf_T *)args->os_buf; + OptInt old_value = args->os_oldval.number; + OptInt value = args->os_newval.number; + if (buf->terminal && value < old_value) { + // Force the scrollback to take immediate effect only when decreasing it. + on_scrollback_option_changed(buf->terminal); + } return NULL; } -/// Process the new 'winwidth' option value. -static const char *did_set_winwidth(optset_T *args) +/// Process the updated 'scrollbind' option value. +static const char *did_set_scrollbind(optset_T *args) { win_T *win = (win_T *)args->os_win; - if (!ONE_WINDOW && win->w_width < p_wiw) { - win_setwidth((int)p_wiw); + // when 'scrollbind' is set: snapshot the current position to avoid a jump + // at the end of normal_cmd() + if (!win->w_p_scb) { + return NULL; } + do_check_scrollbind(false); + win->w_scbind_pos = win->w_topline; return NULL; } -/// Process the new 'laststatus' option value. -static const char *did_set_laststatus(optset_T *args) +#ifdef BACKSLASH_IN_FILENAME +/// Process the updated 'shellslash' option value. +static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED) { - OptInt old_value = args->os_oldval.number; - OptInt value = args->os_newval.number; + if (p_ssl) { + psepc = '/'; + psepcN = '\\'; + pseps[0] = '/'; + } else { + psepc = '\\'; + psepcN = '/'; + pseps[0] = '\\'; + } - // When switching to global statusline, decrease topframe height - // Also clear the cmdline to remove the ruler if there is one - if (value == 3 && old_value != 3) { - frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false); - (void)win_comp_pos(); - clear_cmdline = true; + // need to adjust the file name arguments and buffer names. + buflist_slash_adjust(); + alist_slash_adjust(); + scriptnames_slash_adjust(); + return NULL; +} +#endif + +/// Process the new 'shiftwidth' or the 'tabstop' option value. +static const char *did_set_shiftwidth_tabstop(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + win_T *win = (win_T *)args->os_win; + OptInt *pp = (OptInt *)args->os_varp; + + if (foldmethodIsIndent(win)) { + foldUpdateAll(win); } - // When switching from global statusline, increase height of topframe by STATUS_HEIGHT - // in order to to re-add the space that was previously taken by the global statusline - if (old_value == 3 && value != 3) { - frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false); - (void)win_comp_pos(); + // When 'shiftwidth' changes, or it's zero and 'tabstop' changes: + // parse 'cinoptions'. + if (pp == &buf->b_p_sw || buf->b_p_sw == 0) { + parse_cino(buf); } - last_status(false); // (re)set last window status line. return NULL; } @@ -2691,68 +2633,47 @@ static const char *did_set_smoothscroll(optset_T *args FUNC_ATTR_UNUSED) return NULL; } -/// Process the new 'foldlevel' option value. -static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED) -{ - newFoldLevel(); - return NULL; -} - -/// Process the new 'foldminlines' option value. -static const char *did_set_foldminlines(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - foldUpdateAll(win); - return NULL; -} - -/// Process the new 'foldnestmax' option value. -static const char *did_set_foldnestmax(optset_T *args) +/// Process the updated 'spell' option value. +static const char *did_set_spell(optset_T *args) { win_T *win = (win_T *)args->os_win; - if (foldmethodIsSyntax(win) || foldmethodIsIndent(win)) { - foldUpdateAll(win); + if (win->w_p_spell) { + return parse_spelllang(win); } + return NULL; } -/// Process the new 'shiftwidth' or the 'tabstop' option value. -static const char *did_set_shiftwidth_tabstop(optset_T *args) +/// Process the updated 'swapfile' option value. +static const char *did_set_swapfile(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; - win_T *win = (win_T *)args->os_win; - OptInt *pp = (OptInt *)args->os_varp; - - if (foldmethodIsIndent(win)) { - foldUpdateAll(win); - } - // When 'shiftwidth' changes, or it's zero and 'tabstop' changes: - // parse 'cinoptions'. - if (pp == &buf->b_p_sw || buf->b_p_sw == 0) { - parse_cino(buf); + // when 'swf' is set, create swapfile, when reset remove swapfile + if (buf->b_p_swf && p_uc) { + ml_open_file(buf); // create the swap file + } else { + // no need to reset curbuf->b_may_swap, ml_open_file() will check + // buf->b_p_swf + mf_close_file(buf, true); // remove the swap file } - - return NULL; -} - -/// Process the new 'iminset' option value. -static const char *did_set_iminsert(optset_T *args FUNC_ATTR_UNUSED) -{ - showmode(); - // Show/unshow value of 'keymap' in status lines. - status_redraw_curbuf(); - return NULL; } -/// Process the new 'window' option value. -static const char *did_set_window(optset_T *args FUNC_ATTR_UNUSED) +/// Process the new 'textwidth' option value. +static const char *did_set_textwidth(optset_T *args FUNC_ATTR_UNUSED) { - if (p_window < 1) { - p_window = Rows - 1; - } else if (p_window >= Rows) { - p_window = Rows - 1; + FOR_ALL_TAB_WINDOWS(tp, wp) { + check_colorcolumn(wp); } + + return NULL; +} + +/// Process the updated 'title' or the 'icon' option value. +static const char *did_set_title_icon(optset_T *args FUNC_ATTR_UNUSED) +{ + // when 'title' changed, may need to change the title; same for 'icon' + did_set_title(); return NULL; } @@ -2769,51 +2690,29 @@ static const char *did_set_titlelen(optset_T *args) return NULL; } -/// Process the new 'cmdheight' option value. -static const char *did_set_cmdheight(optset_T *args) -{ - OptInt old_value = args->os_oldval.number; - - if (ui_has(kUIMessages)) { - p_ch = 0; - } - if (p_ch > Rows - min_rows() + 1) { - p_ch = Rows - min_rows() + 1; - } - - // if p_ch changed value, change the command line height - // Only compute the new window layout when startup has been - // completed. Otherwise the frame sizes may be wrong. - if ((p_ch != old_value - || tabline_height() + global_stl_height() + topframe->fr_height != Rows - p_ch) - && full_screen) { - command_height(); - } - - return NULL; -} - -/// Process the new 'updatecount' option value. -static const char *did_set_updatecount(optset_T *args) +/// Process the updated 'undofile' option value. +static const char *did_set_undofile(optset_T *args) { - OptInt old_value = args->os_oldval.number; - - // when 'updatecount' changes from zero to non-zero, open swap files - if (p_uc && !old_value) { - ml_open_files(); + // Only take action when the option was set. + if (!curbuf->b_p_udf && !p_udf) { + return NULL; } - return NULL; -} + // When reset we do not delete the undo file, the option may be set again + // without making any changes in between. + uint8_t hash[UNDO_HASH_SIZE]; -/// Process the new 'pumblend' option value. -static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED) -{ - p_pb = MAX(MIN(p_pb, 100), 0); - hl_invalidate_blends(); - pum_grid.blending = (p_pb > 0); - if (pum_drawn()) { - pum_redraw(); + FOR_ALL_BUFFERS(bp) { + // When 'undofile' is set globally: for every buffer, otherwise + // only for the current buffer: Try to read in the undofile, + // if one exists, the buffer wasn't changed and the buffer was + // loaded + if ((curbuf == bp + || (args->os_flags & OPT_GLOBAL) || args->os_flags == 0) + && !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) { + u_compute_hash(bp, hash); + u_read_undo(NULL, hash, bp->b_fname); + } } return NULL; @@ -2840,34 +2739,29 @@ const char *did_set_buflocal_undolevels(buf_T *buf, OptInt value, OptInt old_val return NULL; } -/// Process the new 'scrollback' option value. -static const char *did_set_scrollback(optset_T *args) +/// Process the new 'undolevels' option value. +static const char *did_set_undolevels(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 *pp = (OptInt *)args->os_varp; - if (buf->terminal && value < old_value) { - // Force the scrollback to take immediate effect only when decreasing it. - on_scrollback_option_changed(buf->terminal); + if (pp == &p_ul) { // global 'undolevels' + did_set_global_undolevels(args->os_newval.number, args->os_oldval.number); + } else if (pp == &curbuf->b_p_ul) { // buffer local 'undolevels' + did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number); } - return NULL; -} - -/// Process the new 'numberwidth' option value. -static const char *did_set_numberwidth(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - win->w_nrwidth_line_count = 0; // trigger a redraw return NULL; } -/// Process the new 'textwidth' option value. -static const char *did_set_textwidth(optset_T *args FUNC_ATTR_UNUSED) +/// Process the new 'updatecount' option value. +static const char *did_set_updatecount(optset_T *args) { - FOR_ALL_TAB_WINDOWS(tp, wp) { - check_colorcolumn(wp); + OptInt old_value = args->os_oldval.number; + + // when 'updatecount' changes from zero to non-zero, open swap files + if (p_uc && !old_value) { + ml_open_files(); } return NULL; @@ -2889,21 +2783,143 @@ static const char *did_set_winblend(optset_T *args) return NULL; } -/// Process the new 'undolevels' option value. -static const char *did_set_undolevels(optset_T *args) +/// Process the new 'window' option value. +static const char *did_set_window(optset_T *args FUNC_ATTR_UNUSED) { - buf_T *buf = (buf_T *)args->os_buf; - OptInt *pp = (OptInt *)args->os_varp; + if (p_window < 1) { + p_window = Rows - 1; + } else if (p_window >= Rows) { + p_window = Rows - 1; + } + return NULL; +} - if (pp == &p_ul) { // global 'undolevels' - did_set_global_undolevels(args->os_newval.number, args->os_oldval.number); - } else if (pp == &curbuf->b_p_ul) { // buffer local 'undolevels' - did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number); +/// Process the new 'winheight' value. +static const char *did_set_winheight(optset_T *args) +{ + // Change window height NOW + if (!ONE_WINDOW) { + win_T *win = (win_T *)args->os_win; + if (win->w_height < p_wh) { + win_setheight((int)p_wh); + } } return NULL; } +/// Process the new 'winwidth' option value. +static const char *did_set_winwidth(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + + if (!ONE_WINDOW && win->w_width < p_wiw) { + win_setwidth((int)p_wiw); + } + return NULL; +} + +/// Process the updated 'wrap' option value. +static const char *did_set_wrap(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + + // If 'wrap' is set, set w_leftcol to zero. + if (win->w_p_wrap) { + win->w_leftcol = 0; + } + return NULL; +} + +/// Set the value of a boolean option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param[in] opt_flags OPT_LOCAL and/or OPT_GLOBAL. +/// +/// @return NULL on success, error message on error. +static const char *set_bool_option(const int opt_idx, char *const varp, const int value, + const int opt_flags) +{ + int old_value = *(int *)varp; + int old_global_value = 0; + + // Disallow changing some options from secure mode + if ((secure || sandbox != 0) + && (options[opt_idx].flags & P_SECURE)) { + return e_secure; + } + + // Save the global value 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). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + + *(int *)varp = value; // set the new value + // Remember where the option was set. + set_option_sctx_idx(opt_idx, opt_flags, current_sctx); + + // May set global value for local option. + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; + } + + // Handle side effects for changing a bool option. + const char *errmsg = NULL; + bool doskip = false; + if ((int *)varp == &p_force_on) { + errmsg = did_set_force_on(&doskip); + } else if ((int *)varp == &p_force_off) { + errmsg = did_set_force_off(&doskip); + } else if (options[opt_idx].opt_did_set_cb != NULL) { + optset_T args = { + .os_varp = varp, + .os_flags = opt_flags, + .os_oldval.boolean = old_value, + .os_newval.boolean = value, + .os_doskip = false, + .os_errbuf = NULL, + .os_errbuflen = 0, + .os_buf = curbuf, + .os_win = curwin + }; + + errmsg = options[opt_idx].opt_did_set_cb(&args); + doskip = args.os_doskip; + } + if (doskip) { + return errmsg; + } + + // after handling side effects, call autocommand + + options[opt_idx].flags |= P_WAS_SET; + + apply_optionset_autocmd(opt_idx, opt_flags, + (long)(old_value ? true : false), + (long)(old_global_value ? true : false), + (long)(value ? true : false), NULL); + + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + BOOLEAN_OBJ(*varp)); + } + if ((int *)varp == &p_ru || (int *)varp == &p_sc) { + // in case 'ruler' or 'showcmd' changed + comp_col(); + } + if (curwin->w_curswant != MAXCOL + && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) { + curwin->w_set_curswant = true; + } + check_redraw(options[opt_idx].flags); + + return errmsg; +} + /// Check the bounds of numeric options. static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long old_Rows, char *errbuf, size_t errbuflen, const char *errmsg) @@ -5744,22 +5760,6 @@ void reset_option_was_set(const char *name) options[idx].flags &= ~P_WAS_SET; } -/// Called when the 'breakat' option changes value. -static const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED) -{ - for (int i = 0; i < 256; i++) { - breakat_flags[i] = false; - } - - if (p_breakat != NULL) { - for (char *p = p_breakat; *p; p++) { - breakat_flags[(uint8_t)(*p)] = true; - } - } - - return NULL; -} - /// fill_culopt_flags() -- called when 'culopt' changes value int fill_culopt_flags(char *val, win_T *wp) { diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 62073a2323..750941da07 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -519,71 +519,6 @@ static bool valid_filetype(const char *val) return valid_name(val, ".-_"); } -/// Handle setting 'mousescroll'. -/// @return error message, NULL if it's OK. -const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) -{ - OptInt vertical = -1; - OptInt horizontal = -1; - - char *string = p_mousescroll; - - while (true) { - char *end = vim_strchr(string, ','); - size_t length = end ? (size_t)(end - string) : strlen(string); - - // Both "ver:" and "hor:" are 4 bytes long. - // They should be followed by at least one digit. - if (length <= 4) { - return e_invarg; - } - - OptInt *direction; - - if (memcmp(string, "ver:", 4) == 0) { - direction = &vertical; - } else if (memcmp(string, "hor:", 4) == 0) { - direction = &horizontal; - } else { - return e_invarg; - } - - // If the direction has already been set, this is a duplicate. - if (*direction != -1) { - return e_invarg; - } - - // Verify that only digits follow the colon. - for (size_t i = 4; i < length; i++) { - if (!ascii_isdigit(string[i])) { - return N_("E5080: Digit expected"); - } - } - - string += 4; - *direction = getdigits_int(&string, false, -1); - - // Num options are generally kept within the signed int range. - // We know this number won't be negative because we've already checked for - // a minus sign. We'll allow 0 as a means of disabling mouse scrolling. - if (*direction == -1) { - return e_invarg; - } - - if (!end) { - break; - } - - string = end + 1; - } - - // If a direction wasn't set, fallback to the default value. - p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical; - p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal; - - return NULL; -} - /// Handle setting 'signcolumn' for value 'val' /// /// @return OK when the value is valid, FAIL otherwise @@ -695,6 +630,81 @@ static bool check_illegal_path_names(char *val, uint32_t flags) && strpbrk(val, "*?[|;&<>\r\n") != NULL)); } +/// An option that accepts a list of flags is changed. +/// e.g. 'viewoptions', 'switchbuf', 'casemap', etc. +static const char *did_set_opt_flags(char *val, char **values, unsigned *flagp, bool list) +{ + if (opt_strings_flags(val, values, flagp, list) != OK) { + return e_invarg; + } + return NULL; +} + +/// An option that accepts a list of string values is changed. +/// e.g. 'nrformats', 'scrollopt', 'wildoptions', etc. +static const char *did_set_opt_strings(char *val, char **values, bool list) +{ + return did_set_opt_flags(val, values, NULL, list); +} + +/// An option which is a list of flags is set. Valid values are in "flags". +static const char *did_set_option_listflag(char *val, char *flags, char *errbuf, size_t errbuflen) +{ + for (char *s = val; *s; s++) { + if (vim_strchr(flags, (uint8_t)(*s)) == NULL) { + return illegal_char(errbuf, errbuflen, (uint8_t)(*s)); + } + } + + return NULL; +} + +/// The 'ambiwidth' option is changed. +const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED) +{ + if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) { + return e_invarg; + } + return check_chars_options(); +} + +/// The 'background' option is changed. +const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) +{ + if (check_opt_strings(p_bg, p_bg_values, false) != OK) { + return e_invarg; + } + + int dark = (*p_bg == 'd'); + + init_highlight(false, false); + + if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) { + // The color scheme must have set 'background' back to another + // value, that's not what we want here. Disable the color + // scheme and set the colors again. + do_unlet(S_LEN("g:colors_name"), true); + free_string_option(p_bg); + p_bg = xstrdup((dark ? "dark" : "light")); + check_string_option(&p_bg); + init_highlight(false, false); + } + return NULL; +} + +/// The 'backspace' option is changed. +const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) +{ + if (ascii_isdigit(*p_bs)) { + if (*p_bs != '2') { + return e_invarg; + } + } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) { + return e_invarg; + } + return NULL; +} + /// The 'backupcopy' option is changed. const char *did_set_backupcopy(optset_T *args) { @@ -746,12 +756,6 @@ const char *did_set_belloff(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true); } -/// The 'termpastefilter' option is changed. -const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED) -{ - return did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true); -} - /// The 'breakindentopt' option is changed. const char *did_set_breakindentopt(optset_T *args) { @@ -767,160 +771,236 @@ const char *did_set_breakindentopt(optset_T *args) return NULL; } -/// The 'isident' or the 'iskeyword' or the 'isprint' or the 'isfname' option is -/// changed. -const char *did_set_isopt(optset_T *args) +/// The 'bufhidden' option is changed. +const char *did_set_bufhidden(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; - // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[] - // If the new option is invalid, use old value. - // 'lisp' option: refill g_chartab[] for '-' char - if (buf_init_chartab(buf, true) == FAIL) { - args->os_restore_chartab = true; // need to restore it below - return e_invarg; // error in value - } - return NULL; + return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false); } -/// The 'helpfile' option is changed. -const char *did_set_helpfile(optset_T *args FUNC_ATTR_UNUSED) +/// The 'buftype' option is changed. +const char *did_set_buftype(optset_T *args) { - // May compute new values for $VIM and $VIMRUNTIME - if (didset_vim) { - vim_unsetenv_ext("VIM"); + buf_T *buf = (buf_T *)args->os_buf; + win_T *win = (win_T *)args->os_win; + // When 'buftype' is set, check for valid value. + if ((buf->terminal && buf->b_p_bt[0] != 't') + || (!buf->terminal && buf->b_p_bt[0] == 't') + || check_opt_strings(buf->b_p_bt, p_buftype_values, false) != OK) { + return e_invarg; } - if (didset_vimruntime) { - vim_unsetenv_ext("VIMRUNTIME"); + if (win->w_status_height || global_stl_height()) { + win->w_redr_status = true; + redraw_later(win, UPD_VALID); } + buf->b_help = (buf->b_p_bt[0] == 'h'); + redraw_titles(); return NULL; } -/// The 'cursorlineopt' option is changed. -const char *did_set_cursorlineopt(optset_T *args) +/// The 'casemap' option is changed. +const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED) { - win_T *win = (win_T *)args->os_win; - char **varp = (char **)args->os_varp; + return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true); +} - if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) { - return e_invarg; +/// The global 'listchars' or 'fillchars' option is changed. +static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags) +{ + const char *errmsg = NULL; + char **local_ptr = opt_lcs ? &win->w_p_lcs : &win->w_p_fcs; + + // only apply the global value to "win" when it does not have a + // local value + if (opt_lcs) { + errmsg = set_listchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL)); + } else { + errmsg = set_fillchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL)); + } + if (errmsg != NULL) { + return errmsg; } - return NULL; -} + // If the current window is set to use the global + // 'listchars'/'fillchars' value, clear the window-local value. + if (!(opt_flags & OPT_GLOBAL)) { + clear_string_option(local_ptr); + } -/// The 'helplang' option is changed. -const char *did_set_helplang(optset_T *args FUNC_ATTR_UNUSED) -{ - // Check for "", "ab", "ab,cd", etc. - for (char *s = p_hlg; *s != NUL; s += 3) { - if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) { - return e_invarg; - } - if (s[2] == NUL) { - break; + FOR_ALL_TAB_WINDOWS(tp, wp) { + // If the current window has a local value need to apply it + // again, it was changed when setting the global value. + // If no error was returned above, we don't expect an error + // here, so ignore the return value. + if (opt_lcs) { + if (*wp->w_p_lcs == NUL) { + (void)set_listchars_option(wp, wp->w_p_lcs, true); + } + } else { + if (*wp->w_p_fcs == NUL) { + (void)set_fillchars_option(wp, wp->w_p_fcs, true); + } } } + + redraw_all_later(UPD_NOT_VALID); + return NULL; } -/// The 'highlight' option is changed. -const char *did_set_highlight(optset_T *args) +/// The 'fillchars' option or the 'listchars' option is changed. +const char *did_set_chars_option(optset_T *args) { + win_T *win = (win_T *)args->os_win; char **varp = (char **)args->os_varp; + const char *errmsg = NULL; - if (strcmp(*varp, HIGHLIGHT_INIT) != 0) { - return e_unsupportedoption; + if (varp == &p_lcs // global 'listchars' + || varp == &p_fcs) { // global 'fillchars' + errmsg = did_set_global_listfillchars(win, *varp, varp == &p_lcs, args->os_flags); + } else if (varp == &win->w_p_lcs) { // local 'listchars' + errmsg = set_listchars_option(win, *varp, true); + } else if (varp == &win->w_p_fcs) { // local 'fillchars' + errmsg = set_fillchars_option(win, *varp, true); } - return NULL; + + return errmsg; } -static const char *did_set_opt_flags(char *val, char **values, unsigned *flagp, bool list) +/// The 'cinoptions' option is changed. +const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED) { - if (opt_strings_flags(val, values, flagp, list) != OK) { - return e_invarg; - } + // TODO(vim): recognize errors + parse_cino(curbuf); + return NULL; } -static const char *did_set_opt_strings(char *val, char **values, bool list) +/// The 'clipboard' option is changed. +const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_flags(val, values, NULL, list); + return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true); } -/// The 'selectmode' option is changed. -const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED) +/// The 'colorcolumn' option is changed. +const char *did_set_colorcolumn(optset_T *args) { - return did_set_opt_strings(p_slm, p_slm_values, true); + win_T *win = (win_T *)args->os_win; + return check_colorcolumn(win); } -/// The 'inccommand' option is changed. -const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED) +/// The 'comments' option is changed. +const char *did_set_comments(optset_T *args) { - return did_set_opt_strings(p_icm, p_icm_values, false); + char **varp = (char **)args->os_varp; + char *errmsg = NULL; + for (char *s = *varp; *s;) { + while (*s && *s != ':') { + if (vim_strchr(COM_ALL, (uint8_t)(*s)) == NULL + && !ascii_isdigit(*s) && *s != '-') { + errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*s)); + break; + } + s++; + } + if (*s++ == NUL) { + errmsg = N_("E524: Missing colon"); + } else if (*s == ',' || *s == NUL) { + errmsg = N_("E525: Zero length string"); + } + if (errmsg != NULL) { + break; + } + while (*s && *s != ',') { + if (*s == '\\' && s[1] != NUL) { + s++; + } + s++; + } + s = skip_to_option_part(s); + } + return errmsg; } -/// The 'sessionoptions' option is changed. -const char *did_set_sessionoptions(optset_T *args) +/// The 'commentstring' option is changed. +const char *did_set_commentstring(optset_T *args) { - if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) { - return e_invarg; - } - if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) { - // Don't allow both "sesdir" and "curdir". - const char *oldval = args->os_oldval.string; - (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true); - return e_invarg; + char **varp = (char **)args->os_varp; + + if (**varp != NUL && strstr(*varp, "%s") == NULL) { + return N_("E537: 'commentstring' must be empty or contain %s"); } return NULL; } -/// The 'ambiwidth' option is changed. -const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED) +/// The 'complete' option is changed. +const char *did_set_complete(optset_T *args) { - if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) { - return e_invarg; + char **varp = (char **)args->os_varp; + + // check if it is a valid value for 'complete' -- Acevedo + for (char *s = *varp; *s;) { + while (*s == ',' || *s == ' ') { + s++; + } + if (!*s) { + break; + } + if (vim_strchr(".wbuksid]tU", (uint8_t)(*s)) == NULL) { + return illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*s)); + } + if (*++s != NUL && *s != ',' && *s != ' ') { + if (s[-1] == 'k' || s[-1] == 's') { + // skip optional filename after 'k' and 's' + while (*s && *s != ',' && *s != ' ') { + if (*s == '\\' && s[1] != NUL) { + s++; + } + s++; + } + } else { + if (args->os_errbuf != NULL) { + vim_snprintf(args->os_errbuf, args->os_errbuflen, + _("E535: Illegal character after <%c>"), + *--s); + return args->os_errbuf; + } + return ""; + } + } } - return check_chars_options(); + return NULL; } -/// The 'background' option is changed. -const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) +/// The 'completeopt' option is changed. +const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) { - if (check_opt_strings(p_bg, p_bg_values, false) != OK) { + if (check_opt_strings(p_cot, p_cot_values, true) != OK) { return e_invarg; } - - int dark = (*p_bg == 'd'); - - init_highlight(false, false); - - if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) { - // The color scheme must have set 'background' back to another - // value, that's not what we want here. Disable the color - // scheme and set the colors again. - do_unlet(S_LEN("g:colors_name"), true); - free_string_option(p_bg); - p_bg = xstrdup((dark ? "dark" : "light")); - check_string_option(&p_bg); - init_highlight(false, false); - } + completeopt_was_set(); return NULL; } -/// The 'whichwrap' option is changed. -const char *did_set_whichwrap(optset_T *args) +#ifdef BACKSLASH_IN_FILENAME +/// The 'completeslash' option is changed. +const char *did_set_completeslash(optset_T *args) { - char **varp = (char **)args->os_varp; - - return did_set_option_listflag(*varp, WW_ALL, args->os_errbuf, args->os_errbuflen); + buf_T *buf = (buf_T *)args->os_buf; + if (check_opt_strings(p_csl, p_csl_values, false) != OK + || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) { + return e_invarg; + } + return NULL; } +#endif -/// The 'shortmess' option is changed. -const char *did_set_shortmess(optset_T *args) +/// The 'concealcursor' option is changed. +const char *did_set_concealcursor(optset_T *args) { char **varp = (char **)args->os_varp; - return did_set_option_listflag(*varp, SHM_ALL, args->os_errbuf, args->os_errbuflen); + return did_set_option_listflag(*varp, COCU_ALL, args->os_errbuf, args->os_errbuflen); } /// The 'cpoptions' option is changed. @@ -931,66 +1011,42 @@ const char *did_set_cpoptions(optset_T *args) return did_set_option_listflag(*varp, CPO_VI, args->os_errbuf, args->os_errbuflen); } -/// The 'clipboard' option is changed. -const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED) -{ - return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true); -} - -/// The 'foldopen' option is changed. -const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED) -{ - return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true); -} - -/// The 'formatoptions' option is changed. -const char *did_set_formatoptions(optset_T *args) -{ - char **varp = (char **)args->os_varp; - - return did_set_option_listflag(*varp, FO_ALL, args->os_errbuf, args->os_errbuflen); -} - -/// The 'concealcursor' option is changed. -const char *did_set_concealcursor(optset_T *args) +/// The 'cursorlineopt' option is changed. +const char *did_set_cursorlineopt(optset_T *args) { + win_T *win = (win_T *)args->os_win; char **varp = (char **)args->os_varp; - return did_set_option_listflag(*varp, COCU_ALL, args->os_errbuf, args->os_errbuflen); -} - -/// The 'mouse' option is changed. -const char *did_set_mouse(optset_T *args) -{ - char **varp = (char **)args->os_varp; + if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) { + return e_invarg; + } - return did_set_option_listflag(*varp, MOUSE_ALL, args->os_errbuf, args->os_errbuflen); + return NULL; } -/// The 'wildmode' option is changed. -const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED) +/// The 'debug' option is changed. +const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED) { - if (check_opt_wim() == FAIL) { - return e_invarg; - } - return NULL; + return did_set_opt_strings(p_debug, p_debug_values, false); } -/// The 'winaltkeys' option is changed. -const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED) +/// The 'diffopt' option is changed. +const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) { - if (*p_wak == NUL || check_opt_strings(p_wak, p_wak_values, false) != OK) { + if (diffopt_changed() == FAIL) { return e_invarg; } return NULL; } -/// The 'eventignore' option is changed. -const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) +/// The 'display' option is changed. +const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) { - if (check_ei() == FAIL) { + if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) { return e_invarg; } + (void)init_chartab(); + msg_grid_validate(); return NULL; } @@ -1042,22 +1098,245 @@ const char *did_set_encoding(optset_T *args) return NULL; } -/// The 'keymap' option has changed. -const char *did_set_keymap(optset_T *args) +/// The 'eventignore' option is changed. +const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) +{ + if (check_ei() == FAIL) { + return e_invarg; + } + return NULL; +} + +/// The 'fileformat' option is changed. +const char *did_set_fileformat(optset_T *args) { buf_T *buf = (buf_T *)args->os_buf; char **varp = (char **)args->os_varp; + const char *oldval = args->os_oldval.string; int opt_flags = args->os_flags; + if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) { + return e_modifiable; + } else if (check_opt_strings(*varp, p_ff_values, false) != OK) { + return e_invarg; + } + redraw_titles(); + // update flag in swap file + ml_setflags(buf); + // Redraw needed when switching to/from "mac": a CR in the text + // will be displayed differently. + if (get_fileformat(buf) == EOL_MAC || *oldval == 'm') { + redraw_buf_later(buf, UPD_NOT_VALID); + } + return NULL; +} + +/// The 'fileformats' option is changed. +const char *did_set_fileformats(optset_T *args) +{ + return did_set_opt_strings(p_ffs, p_ff_values, true); +} + +/// The 'filetype' or the 'syntax' option is changed. +const char *did_set_filetype_or_syntax(optset_T *args) +{ + char **varp = (char **)args->os_varp; if (!valid_filetype(*varp)) { return e_invarg; } - int secure_save = secure; + args->os_value_changed = strcmp(args->os_oldval.string, *varp) != 0; - // Reset the secure flag, since the value of 'keymap' has - // been checked to be safe. - secure = 0; + // Since we check the value, there is no need to set P_INSECURE, + // even when the value comes from a modeline. + args->os_value_checked = true; + + return NULL; +} + +/// The 'foldclose' option is changed. +const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED) +{ + return did_set_opt_strings(p_fcl, p_fcl_values, true); +} + +/// The 'foldcolumn' option is changed. +const char *did_set_foldcolumn(optset_T *args) +{ + char **varp = (char **)args->os_varp; + if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) { + return e_invarg; + } + return NULL; +} + +/// The 'foldexpr' option is changed. +const char *did_set_foldexpr(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + (void)did_set_optexpr(args); + if (foldmethodIsExpr(win)) { + foldUpdateAll(win); + } + return NULL; +} + +/// The 'foldignore' option is changed. +const char *did_set_foldignore(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + if (foldmethodIsIndent(win)) { + foldUpdateAll(win); + } + return NULL; +} + +/// The 'foldmarker' option is changed. +const char *did_set_foldmarker(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + char **varp = (char **)args->os_varp; + char *p = vim_strchr(*varp, ','); + + if (p == NULL) { + return e_comma_required; + } + + if (p == *varp || p[1] == NUL) { + return e_invarg; + } + + if (foldmethodIsMarker(win)) { + foldUpdateAll(win); + } + + return NULL; +} + +/// The 'foldmethod' option is changed. +const char *did_set_foldmethod(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + char **varp = (char **)args->os_varp; + if (check_opt_strings(*varp, p_fdm_values, false) != OK + || *win->w_p_fdm == NUL) { + return e_invarg; + } + foldUpdateAll(win); + if (foldmethodIsDiff(win)) { + newFoldLevel(); + } + return NULL; +} + +/// The 'foldopen' option is changed. +const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED) +{ + return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true); +} + +/// The 'formatoptions' option is changed. +const char *did_set_formatoptions(optset_T *args) +{ + char **varp = (char **)args->os_varp; + + return did_set_option_listflag(*varp, FO_ALL, args->os_errbuf, args->os_errbuflen); +} + +/// The 'guicursor' option is changed. +const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED) +{ + return parse_shape_opt(SHAPE_CURSOR); +} + +/// The 'helpfile' option is changed. +const char *did_set_helpfile(optset_T *args FUNC_ATTR_UNUSED) +{ + // May compute new values for $VIM and $VIMRUNTIME + if (didset_vim) { + vim_unsetenv_ext("VIM"); + } + if (didset_vimruntime) { + vim_unsetenv_ext("VIMRUNTIME"); + } + return NULL; +} + +/// The 'helplang' option is changed. +const char *did_set_helplang(optset_T *args FUNC_ATTR_UNUSED) +{ + // Check for "", "ab", "ab,cd", etc. + for (char *s = p_hlg; *s != NUL; s += 3) { + if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) { + return e_invarg; + } + if (s[2] == NUL) { + break; + } + } + return NULL; +} + +/// The 'highlight' option is changed. +const char *did_set_highlight(optset_T *args) +{ + char **varp = (char **)args->os_varp; + + if (strcmp(*varp, HIGHLIGHT_INIT) != 0) { + return e_unsupportedoption; + } + return NULL; +} + +/// The 'iconstring' option is changed. +const char *did_set_iconstring(optset_T *args) +{ + return did_set_titleiconstring(args, STL_IN_ICON); +} + +/// The 'inccommand' option is changed. +const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED) +{ + return did_set_opt_strings(p_icm, p_icm_values, false); +} + +/// The 'isident' or the 'iskeyword' or the 'isprint' or the 'isfname' option is +/// changed. +const char *did_set_isopt(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[] + // If the new option is invalid, use old value. + // 'lisp' option: refill g_chartab[] for '-' char + if (buf_init_chartab(buf, true) == FAIL) { + args->os_restore_chartab = true; // need to restore it below + return e_invarg; // error in value + } + return NULL; +} + +/// The 'jumpoptions' option is changed. +const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED) +{ + return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true); +} + +/// The 'keymap' option has changed. +const char *did_set_keymap(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + char **varp = (char **)args->os_varp; + int opt_flags = args->os_flags; + + if (!valid_filetype(*varp)) { + return e_invarg; + } + + int secure_save = secure; + + // Reset the secure flag, since the value of 'keymap' has + // been checked to be safe. + secure = 0; // load or unload key mapping tables const char *errmsg = keymap_init(); @@ -1094,33 +1373,26 @@ const char *did_set_keymap(optset_T *args) return errmsg; } -/// The 'fileformat' option is changed. -const char *did_set_fileformat(optset_T *args) +/// The 'keymodel' option is changed. +const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED) { - buf_T *buf = (buf_T *)args->os_buf; - char **varp = (char **)args->os_varp; - const char *oldval = args->os_oldval.string; - int opt_flags = args->os_flags; - if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) { - return e_modifiable; - } else if (check_opt_strings(*varp, p_ff_values, false) != OK) { + if (check_opt_strings(p_km, p_km_values, true) != OK) { return e_invarg; } - redraw_titles(); - // update flag in swap file - ml_setflags(buf); - // Redraw needed when switching to/from "mac": a CR in the text - // will be displayed differently. - if (get_fileformat(buf) == EOL_MAC || *oldval == 'm') { - redraw_buf_later(buf, UPD_NOT_VALID); - } + km_stopsel = (vim_strchr(p_km, 'o') != NULL); + km_startsel = (vim_strchr(p_km, 'a') != NULL); return NULL; } -/// The 'fileformats' option is changed. -const char *did_set_fileformats(optset_T *args) +/// The 'lispoptions' option is changed. +const char *did_set_lispoptions(optset_T *args) { - return did_set_opt_strings(p_ffs, p_ff_values, true); + char **varp = (char **)args->os_varp; + + if (**varp != NUL && strcmp(*varp, "expr:0") != 0 && strcmp(*varp, "expr:1") != 0) { + return e_invarg; + } + return NULL; } /// The 'matchpairs' option is changed. @@ -1150,433 +1422,135 @@ const char *did_set_matchpairs(optset_T *args) return NULL; } -/// The 'cinoptions' option is changed. -const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED) +/// The 'mkspellmem' option is changed. +const char *did_set_mkspellmem(optset_T *args FUNC_ATTR_UNUSED) { - // TODO(vim): recognize errors - parse_cino(curbuf); - + if (spell_check_msm() != OK) { + return e_invarg; + } return NULL; } -/// The 'colorcolumn' option is changed. -const char *did_set_colorcolumn(optset_T *args) +/// The 'mouse' option is changed. +const char *did_set_mouse(optset_T *args) { - win_T *win = (win_T *)args->os_win; - return check_colorcolumn(win); + char **varp = (char **)args->os_varp; + + return did_set_option_listflag(*varp, MOUSE_ALL, args->os_errbuf, args->os_errbuflen); } -const char *did_set_comments(optset_T *args) +/// The 'mousemodel' option is changed. +const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED) { - char **varp = (char **)args->os_varp; - char *errmsg = NULL; - for (char *s = *varp; *s;) { - while (*s && *s != ':') { - if (vim_strchr(COM_ALL, (uint8_t)(*s)) == NULL - && !ascii_isdigit(*s) && *s != '-') { - errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*s)); - break; - } - s++; - } - if (*s++ == NUL) { - errmsg = N_("E524: Missing colon"); - } else if (*s == ',' || *s == NUL) { - errmsg = N_("E525: Zero length string"); - } - if (errmsg != NULL) { - break; - } - while (*s && *s != ',') { - if (*s == '\\' && s[1] != NUL) { - s++; - } - s++; - } - s = skip_to_option_part(s); - } - return errmsg; + return did_set_opt_strings(p_mousem, p_mousem_values, false); } -/// The global 'listchars' or 'fillchars' option is changed. -static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags) +/// Handle setting 'mousescroll'. +/// @return error message, NULL if it's OK. +const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) { - const char *errmsg = NULL; - char **local_ptr = opt_lcs ? &win->w_p_lcs : &win->w_p_fcs; + OptInt vertical = -1; + OptInt horizontal = -1; - // only apply the global value to "win" when it does not have a - // local value - if (opt_lcs) { - errmsg = set_listchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL)); - } else { - errmsg = set_fillchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL)); - } - if (errmsg != NULL) { - return errmsg; - } + char *string = p_mousescroll; - // If the current window is set to use the global - // 'listchars'/'fillchars' value, clear the window-local value. - if (!(opt_flags & OPT_GLOBAL)) { - clear_string_option(local_ptr); - } + while (true) { + char *end = vim_strchr(string, ','); + size_t length = end ? (size_t)(end - string) : strlen(string); - FOR_ALL_TAB_WINDOWS(tp, wp) { - // If the current window has a local value need to apply it - // again, it was changed when setting the global value. - // If no error was returned above, we don't expect an error - // here, so ignore the return value. - if (opt_lcs) { - if (*wp->w_p_lcs == NUL) { - (void)set_listchars_option(wp, wp->w_p_lcs, true); - } - } else { - if (*wp->w_p_fcs == NUL) { - (void)set_fillchars_option(wp, wp->w_p_fcs, true); - } + // Both "ver:" and "hor:" are 4 bytes long. + // They should be followed by at least one digit. + if (length <= 4) { + return e_invarg; } - } - - redraw_all_later(UPD_NOT_VALID); - - return NULL; -} - -/// Handle the new value of 'fillchars'. -const char *set_fillchars_option(win_T *wp, char *val, int apply) -{ - return set_chars_option(wp, val, false, apply); -} - -/// Handle the new value of 'listchars'. -const char *set_listchars_option(win_T *wp, char *val, int apply) -{ - return set_chars_option(wp, val, true, apply); -} - -/// The 'fillchars' option or the 'listchars' option is changed. -const char *did_set_chars_option(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - char **varp = (char **)args->os_varp; - const char *errmsg = NULL; - - if (varp == &p_lcs // global 'listchars' - || varp == &p_fcs) { // global 'fillchars' - errmsg = did_set_global_listfillchars(win, *varp, varp == &p_lcs, args->os_flags); - } else if (varp == &win->w_p_lcs) { // local 'listchars' - errmsg = set_listchars_option(win, *varp, true); - } else if (varp == &win->w_p_fcs) { // local 'fillchars' - errmsg = set_fillchars_option(win, *varp, true); - } - - return errmsg; -} - -/// The 'verbosefile' option is changed. -const char *did_set_verbosefile(optset_T *args) -{ - verbose_stop(); - if (*p_vfile != NUL && verbose_open() == FAIL) { - return (char *)e_invarg; - } - return NULL; -} - -/// The 'viewoptions' option is changed. -const char *did_set_viewoptions(optset_T *args FUNC_ATTR_UNUSED) -{ - return did_set_opt_flags(p_vop, p_ssop_values, &vop_flags, true); -} -static int shada_idx = -1; + OptInt *direction; -static const char *did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf, - size_t errbuflen) -{ - // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo - // option. - *opt_idx = (((*opt)->fullname[0] == 'v') - ? (shada_idx == -1 ? ((shada_idx = findoption("shada"))) : shada_idx) - : *opt_idx); - *opt = get_option(*opt_idx); - // Update free_oldval now that we have the opt_idx for 'shada', otherwise - // there would be a disconnect between the check for P_ALLOCED at the start - // of the function and the set of P_ALLOCED at the end of the function. - *free_oldval = ((*opt)->flags & P_ALLOCED); - for (char *s = p_shada; *s;) { - // Check it's a valid character - if (vim_strchr("!\"%'/:<@cfhnrs", (uint8_t)(*s)) == NULL) { - return illegal_char(errbuf, errbuflen, (uint8_t)(*s)); + if (memcmp(string, "ver:", 4) == 0) { + direction = &vertical; + } else if (memcmp(string, "hor:", 4) == 0) { + direction = &horizontal; + } else { + return e_invarg; } - if (*s == 'n') { // name is always last one - break; - } else if (*s == 'r') { // skip until next ',' - while (*++s && *s != ',') {} - } else if (*s == '%') { - // optional number - while (ascii_isdigit(*++s)) {} - } else if (*s == '!' || *s == 'h' || *s == 'c') { - s++; // no extra chars - } else { // must have a number - while (ascii_isdigit(*++s)) {} - if (!ascii_isdigit(*(s - 1))) { - if (errbuf != NULL) { - vim_snprintf(errbuf, errbuflen, - _("E526: Missing number after <%s>"), - transchar_byte((uint8_t)(*(s - 1)))); - return errbuf; - } else { - return ""; - } - } + // If the direction has already been set, this is a duplicate. + if (*direction != -1) { + return e_invarg; } - if (*s == ',') { - s++; - } else if (*s) { - if (errbuf != NULL) { - return N_("E527: Missing comma"); - } else { - return ""; + + // Verify that only digits follow the colon. + for (size_t i = 4; i < length; i++) { + if (!ascii_isdigit(string[i])) { + return N_("E5080: Digit expected"); } } - } - if (*p_shada && get_shada_parameter('\'') < 0) { - return N_("E528: Must specify a ' value"); - } - return NULL; -} -/// The 'showbreak' option is changed. -const char *did_set_showbreak(optset_T *args) -{ - char **varp = (char **)args->os_varp; + string += 4; + *direction = getdigits_int(&string, false, -1); - for (char *s = *varp; *s;) { - if (ptr2cells(s) != 1) { - return e_showbreak_contains_unprintable_or_wide_character; + // Num options are generally kept within the signed int range. + // We know this number won't be negative because we've already checked for + // a minus sign. We'll allow 0 as a means of disabling mouse scrolling. + if (*direction == -1) { + return e_invarg; } - MB_PTR_ADV(s); - } - return NULL; -} -/// The 'titlestring' or the 'iconstring' option is changed. -static const char *did_set_titleiconstring(optset_T *args, int flagval) -{ - char **varp = (char **)args->os_varp; - - // NULL => statusline syntax - if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) { - stl_syntax |= flagval; - } else { - stl_syntax &= ~flagval; - } - did_set_title(); - - return NULL; -} - -/// The 'titlestring' option is changed. -const char *did_set_titlestring(optset_T *args) -{ - return did_set_titleiconstring(args, STL_IN_TITLE); -} - -/// The 'iconstring' option is changed. -const char *did_set_iconstring(optset_T *args) -{ - return did_set_titleiconstring(args, STL_IN_ICON); -} + if (!end) { + break; + } -/// The 'selection' option is changed. -const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) -{ - if (*p_sel == NUL || check_opt_strings(p_sel, p_sel_values, false) != OK) { - return e_invarg; + string = end + 1; } - return NULL; -} -/// The 'keymodel' option is changed. -const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED) -{ - if (check_opt_strings(p_km, p_km_values, true) != OK) { - return e_invarg; - } - km_stopsel = (vim_strchr(p_km, 'o') != NULL); - km_startsel = (vim_strchr(p_km, 'a') != NULL); - return NULL; -} + // If a direction wasn't set, fallback to the default value. + p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical; + p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal; -/// The 'display' option is changed. -const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) -{ - if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) { - return e_invarg; - } - (void)init_chartab(); - msg_grid_validate(); return NULL; } -/// The 'spellfile' option is changed. -const char *did_set_spellfile(optset_T *args) +/// The 'nrformats' option is changed. +const char *did_set_nrformats(optset_T *args) { char **varp = (char **)args->os_varp; - // When there is a window for this buffer in which 'spell' - // is set load the wordlists. - if ((!valid_spellfile(*varp))) { - return e_invarg; - } - return did_set_spell_option(true); + return did_set_opt_strings(*varp, p_nf_values, true); } -const char *did_set_spelllang(optset_T *args) +/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', +/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. +const char *did_set_optexpr(optset_T *args) { char **varp = (char **)args->os_varp; - // When there is a window for this buffer in which 'spell' - // is set load the wordlists. - if (!valid_spelllang(*varp)) { - return e_invarg; - } - return did_set_spell_option(false); -} - -/// The 'spellcapcheck' option is changed. -const char *did_set_spellcapcheck(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - // When 'spellcapcheck' is set compile the regexp program. - return compile_cap_prog(win->w_s); -} - -/// The 'spelloptions' option is changed. -const char *did_set_spelloptions(optset_T *args) -{ - win_T *win = (win_T *)args->os_win; - if (opt_strings_flags(win->w_s->b_p_spo, p_spo_values, &(win->w_s->b_p_spo_flags), - true) != OK) { - return e_invarg; - } - return NULL; -} - -/// The 'spellsuggest' option is changed. -const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) -{ - if (spell_check_sps() != OK) { - return e_invarg; - } - return NULL; -} - -/// The 'splitkeep' option is changed. -const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED) -{ - return did_set_opt_strings(p_spk, p_spk_values, false); -} - -/// The 'mkspellmem' option is changed. -const char *did_set_mkspellmem(optset_T *args FUNC_ATTR_UNUSED) -{ - if (spell_check_msm() != OK) { - return e_invarg; - } - return NULL; -} - -/// The 'mousemodel' option is changed. -const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED) -{ - return did_set_opt_strings(p_mousem, p_mousem_values, false); -} - -/// The 'bufhidden' option is changed. -const char *did_set_bufhidden(optset_T *args) -{ - buf_T *buf = (buf_T *)args->os_buf; - return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false); -} - -/// The 'buftype' option is changed. -const char *did_set_buftype(optset_T *args) -{ - buf_T *buf = (buf_T *)args->os_buf; - win_T *win = (win_T *)args->os_win; - // When 'buftype' is set, check for valid value. - if ((buf->terminal && buf->b_p_bt[0] != 't') - || (!buf->terminal && buf->b_p_bt[0] == 't') - || check_opt_strings(buf->b_p_bt, p_buftype_values, false) != OK) { - return e_invarg; - } - if (win->w_status_height || global_stl_height()) { - win->w_redr_status = true; - redraw_later(win, UPD_VALID); + // If the option value starts with or s:, then replace that with + // the script identifier. + char *name = get_scriptlocal_funcname(*varp); + if (name != NULL) { + free_string_option(*varp); + *varp = name; } - buf->b_help = (buf->b_p_bt[0] == 'h'); - redraw_titles(); return NULL; } -/// The 'casemap' option is changed. -const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED) +/// The 'redrawdebug' option is changed. +const char *did_set_redrawdebug(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true); + return did_set_opt_flags(p_rdb, p_rdb_values, &rdb_flags, true); } -/// The 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn' option is changed. -/// -/// @param rulerformat true if the 'rulerformat' option is changed -/// @param statuscolumn true if the 'statuscolumn' option is changed -static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerformat, - bool statuscolumn) +/// The 'rightleftcmd' option is changed. +const char *did_set_rightleftcmd(optset_T *args) { - win_T *win = (win_T *)args->os_win; char **varp = (char **)args->os_varp; - if (rulerformat) { // reset ru_wid first - ru_wid = 0; - } else if (statuscolumn) { - // reset 'statuscolumn' width - win->w_nrwidth_line_count = 0; - } - const char *errmsg = NULL; - char *s = *varp; - if (rulerformat && *s == '%') { - // set ru_wid if 'ruf' starts with "%99(" - if (*++s == '-') { // ignore a '-' - s++; - } - int wid = getdigits_int(&s, true, 0); - if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) { - ru_wid = wid; - } else { - errmsg = check_stl_option(p_ruf); - } - } else if (rulerformat || s[0] != '%' || s[1] != '!') { - // check 'statusline', 'winbar', 'tabline' or 'statuscolumn' - // only if it doesn't start with "%!" - errmsg = check_stl_option(s); - } - if (rulerformat && errmsg == NULL) { - comp_col(); - } - return errmsg; -} -/// The 'statusline' option is changed. -const char *did_set_statusline(optset_T *args) -{ - return did_set_statustabline_rulerformat(args, false, false); -} - -/// The 'tabline' option is changed. -const char *did_set_tabline(optset_T *args) -{ - return did_set_statustabline_rulerformat(args, false, false); + // Currently only "search" is a supported value. + if (**varp != NUL && strcmp(*varp, "search") != 0) { + return e_invarg; + } + + return NULL; } /// The 'rulerformat' option is changed. @@ -1585,85 +1559,121 @@ const char *did_set_rulerformat(optset_T *args) return did_set_statustabline_rulerformat(args, true, false); } -/// The 'winbar' option is changed. -const char *did_set_winbar(optset_T *args) +/// The 'scrollopt' option is changed. +const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_statustabline_rulerformat(args, false, false); + return did_set_opt_strings(p_sbo, p_scbopt_values, true); } -/// The 'statuscolumn' option is changed. -const char *did_set_statuscolumn(optset_T *args) +/// The 'selection' option is changed. +const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_statustabline_rulerformat(args, false, true); + if (*p_sel == NUL || check_opt_strings(p_sel, p_sel_values, false) != OK) { + return e_invarg; + } + return NULL; } -/// The 'scrollopt' option is changed. -const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED) +/// The 'selectmode' option is changed. +const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_strings(p_sbo, p_scbopt_values, true); + return did_set_opt_strings(p_slm, p_slm_values, true); } -/// The 'complete' option is changed. -const char *did_set_complete(optset_T *args) +/// The 'sessionoptions' option is changed. +const char *did_set_sessionoptions(optset_T *args) { - char **varp = (char **)args->os_varp; + if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) { + return e_invarg; + } + if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) { + // Don't allow both "sesdir" and "curdir". + const char *oldval = args->os_oldval.string; + (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true); + return e_invarg; + } + return NULL; +} - // check if it is a valid value for 'complete' -- Acevedo - for (char *s = *varp; *s;) { - while (*s == ',' || *s == ' ') { - s++; +static const char *did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf, + size_t errbuflen) +{ + static int shada_idx = -1; + // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo + // option. + *opt_idx = (((*opt)->fullname[0] == 'v') + ? (shada_idx == -1 ? ((shada_idx = findoption("shada"))) : shada_idx) + : *opt_idx); + *opt = get_option(*opt_idx); + // Update free_oldval now that we have the opt_idx for 'shada', otherwise + // there would be a disconnect between the check for P_ALLOCED at the start + // of the function and the set of P_ALLOCED at the end of the function. + *free_oldval = ((*opt)->flags & P_ALLOCED); + for (char *s = p_shada; *s;) { + // Check it's a valid character + if (vim_strchr("!\"%'/:<@cfhnrs", (uint8_t)(*s)) == NULL) { + return illegal_char(errbuf, errbuflen, (uint8_t)(*s)); } - if (!*s) { + if (*s == 'n') { // name is always last one break; - } - if (vim_strchr(".wbuksid]tU", (uint8_t)(*s)) == NULL) { - return illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*s)); - } - if (*++s != NUL && *s != ',' && *s != ' ') { - if (s[-1] == 'k' || s[-1] == 's') { - // skip optional filename after 'k' and 's' - while (*s && *s != ',' && *s != ' ') { - if (*s == '\\' && s[1] != NUL) { - s++; - } - s++; + } else if (*s == 'r') { // skip until next ',' + while (*++s && *s != ',') {} + } else if (*s == '%') { + // optional number + while (ascii_isdigit(*++s)) {} + } else if (*s == '!' || *s == 'h' || *s == 'c') { + s++; // no extra chars + } else { // must have a number + while (ascii_isdigit(*++s)) {} + + if (!ascii_isdigit(*(s - 1))) { + if (errbuf != NULL) { + vim_snprintf(errbuf, errbuflen, + _("E526: Missing number after <%s>"), + transchar_byte((uint8_t)(*(s - 1)))); + return errbuf; + } else { + return ""; } + } + } + if (*s == ',') { + s++; + } else if (*s) { + if (errbuf != NULL) { + return N_("E527: Missing comma"); } else { - if (args->os_errbuf != NULL) { - vim_snprintf(args->os_errbuf, args->os_errbuflen, - _("E535: Illegal character after <%c>"), - *--s); - return args->os_errbuf; - } return ""; } } } + if (*p_shada && get_shada_parameter('\'') < 0) { + return N_("E528: Must specify a ' value"); + } return NULL; } -/// The 'completeopt' option is changed. -const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) +/// The 'shortmess' option is changed. +const char *did_set_shortmess(optset_T *args) { - if (check_opt_strings(p_cot, p_cot_values, true) != OK) { - return e_invarg; - } - completeopt_was_set(); - return NULL; + char **varp = (char **)args->os_varp; + + return did_set_option_listflag(*varp, SHM_ALL, args->os_errbuf, args->os_errbuflen); } -#ifdef BACKSLASH_IN_FILENAME -/// The 'completeslash' option is changed. -const char *did_set_completeslash(optset_T *args) +/// The 'showbreak' option is changed. +const char *did_set_showbreak(optset_T *args) { - buf_T *buf = (buf_T *)args->os_buf; - if (check_opt_strings(p_csl, p_csl_values, false) != OK - || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) { - return e_invarg; + char **varp = (char **)args->os_varp; + + for (char *s = *varp; *s;) { + if (ptr2cells(s) != 1) { + return e_showbreak_contains_unprintable_or_wide_character; + } + MB_PTR_ADV(s); } return NULL; } -#endif /// The 'showcmdloc' option is changed. const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED) @@ -1690,233 +1700,182 @@ const char *did_set_signcolumn(optset_T *args) return NULL; } -/// The 'foldcolumn' option is changed. -const char *did_set_foldcolumn(optset_T *args) -{ - char **varp = (char **)args->os_varp; - if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) { - return e_invarg; - } - return NULL; -} - -/// The 'backspace' option is changed. -const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) -{ - if (ascii_isdigit(*p_bs)) { - if (*p_bs != '2') { - return e_invarg; - } - } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) { - return e_invarg; - } - return NULL; -} - -/// The 'switchbuf' option is changed. -const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED) +/// The 'spellcapcheck' option is changed. +const char *did_set_spellcapcheck(optset_T *args) { - return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true); + win_T *win = (win_T *)args->os_win; + // When 'spellcapcheck' is set compile the regexp program. + return compile_cap_prog(win->w_s); } -/// The 'tagcase' option is changed. -const char *did_set_tagcase(optset_T *args) +/// The 'spellfile' option is changed. +const char *did_set_spellfile(optset_T *args) { - buf_T *buf = (buf_T *)args->os_buf; - int opt_flags = args->os_flags; - - unsigned *flags; - char *p; - - if (opt_flags & OPT_LOCAL) { - p = buf->b_p_tc; - flags = &buf->b_tc_flags; - } else { - p = p_tc; - flags = &tc_flags; - } + char **varp = (char **)args->os_varp; - if ((opt_flags & OPT_LOCAL) && *p == NUL) { - // make the local value empty: use the global value - *flags = 0; - } else if (*p == NUL - || opt_strings_flags(p, p_tc_values, flags, false) != OK) { + // When there is a window for this buffer in which 'spell' + // is set load the wordlists. + if ((!valid_spellfile(*varp))) { return e_invarg; } - return NULL; + return did_set_spell_option(true); } -/// The 'debug' option is changed. -const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED) +/// The 'spelllang' option is changed. +const char *did_set_spelllang(optset_T *args) { - return did_set_opt_strings(p_debug, p_debug_values, false); -} + char **varp = (char **)args->os_varp; -/// The 'diffopt' option is changed. -const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) -{ - if (diffopt_changed() == FAIL) { + // When there is a window for this buffer in which 'spell' + // is set load the wordlists. + if (!valid_spelllang(*varp)) { return e_invarg; } - return NULL; + return did_set_spell_option(false); } -/// The 'foldmethod' option is changed. -const char *did_set_foldmethod(optset_T *args) +/// The 'spelloptions' option is changed. +const char *did_set_spelloptions(optset_T *args) { win_T *win = (win_T *)args->os_win; - char **varp = (char **)args->os_varp; - if (check_opt_strings(*varp, p_fdm_values, false) != OK - || *win->w_p_fdm == NUL) { + if (opt_strings_flags(win->w_s->b_p_spo, p_spo_values, &(win->w_s->b_p_spo_flags), + true) != OK) { return e_invarg; } - foldUpdateAll(win); - if (foldmethodIsDiff(win)) { - newFoldLevel(); - } return NULL; } -/// The 'foldmarker' option is changed. -const char *did_set_foldmarker(optset_T *args) +/// The 'spellsuggest' option is changed. +const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) { - win_T *win = (win_T *)args->os_win; - char **varp = (char **)args->os_varp; - char *p = vim_strchr(*varp, ','); - - if (p == NULL) { - return e_comma_required; - } - - if (p == *varp || p[1] == NUL) { + if (spell_check_sps() != OK) { return e_invarg; } - - if (foldmethodIsMarker(win)) { - foldUpdateAll(win); - } - return NULL; } -/// The 'commentstring' option is changed. -const char *did_set_commentstring(optset_T *args) +/// The 'splitkeep' option is changed. +const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED) { - char **varp = (char **)args->os_varp; + return did_set_opt_strings(p_spk, p_spk_values, false); +} - if (**varp != NUL && strstr(*varp, "%s") == NULL) { - return N_("E537: 'commentstring' must be empty or contain %s"); - } - return NULL; +/// The 'statuscolumn' option is changed. +const char *did_set_statuscolumn(optset_T *args) +{ + return did_set_statustabline_rulerformat(args, false, true); } -/// The 'foldignore' option is changed. -const char *did_set_foldignore(optset_T *args) +/// The 'statusline' option is changed. +const char *did_set_statusline(optset_T *args) { - win_T *win = (win_T *)args->os_win; - if (foldmethodIsIndent(win)) { - foldUpdateAll(win); - } - return NULL; + return did_set_statustabline_rulerformat(args, false, false); } -/// The 'virtualedit' option is changed. -const char *did_set_virtualedit(optset_T *args) +/// The 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn' option is changed. +/// +/// @param rulerformat true if the 'rulerformat' option is changed +/// @param statuscolumn true if the 'statuscolumn' option is changed +static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerformat, + bool statuscolumn) { win_T *win = (win_T *)args->os_win; - - char *ve = p_ve; - unsigned *flags = &ve_flags; - - if (args->os_flags & OPT_LOCAL) { - ve = win->w_p_ve; - flags = &win->w_ve_flags; + char **varp = (char **)args->os_varp; + if (rulerformat) { // reset ru_wid first + ru_wid = 0; + } else if (statuscolumn) { + // reset 'statuscolumn' width + win->w_nrwidth_line_count = 0; } - - if ((args->os_flags & OPT_LOCAL) && *ve == NUL) { - // make the local value empty: use the global value - *flags = 0; - } else { - if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) { - return e_invarg; - } else if (strcmp(ve, args->os_oldval.string) != 0) { - // Recompute cursor position in case the new 've' setting - // changes something. - validate_virtcol_win(win); - // XXX: this only works when win == curwin - coladvance(win->w_virtcol); + const char *errmsg = NULL; + char *s = *varp; + if (rulerformat && *s == '%') { + // set ru_wid if 'ruf' starts with "%99(" + if (*++s == '-') { // ignore a '-' + s++; + } + int wid = getdigits_int(&s, true, 0); + if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) { + ru_wid = wid; + } else { + errmsg = check_stl_option(p_ruf); } + } else if (rulerformat || s[0] != '%' || s[1] != '!') { + // check 'statusline', 'winbar', 'tabline' or 'statuscolumn' + // only if it doesn't start with "%!" + errmsg = check_stl_option(s); } - return NULL; + if (rulerformat && errmsg == NULL) { + comp_col(); + } + return errmsg; } -/// The 'jumpoptions' option is changed. -const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED) +/// The 'switchbuf' option is changed. +const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true); + return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true); } -/// The 'redrawdebug' option is changed. -const char *did_set_redrawdebug(optset_T *args FUNC_ATTR_UNUSED) +/// The 'tabline' option is changed. +const char *did_set_tabline(optset_T *args) { - return did_set_opt_flags(p_rdb, p_rdb_values, &rdb_flags, true); + return did_set_statustabline_rulerformat(args, false, false); } -/// The 'wildoptions' option is changed. -const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED) +/// The 'tagcase' option is changed. +const char *did_set_tagcase(optset_T *args) { - return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true); -} + buf_T *buf = (buf_T *)args->os_buf; + int opt_flags = args->os_flags; -/// The 'lispoptions' option is changed. -const char *did_set_lispoptions(optset_T *args) -{ - char **varp = (char **)args->os_varp; + unsigned *flags; + char *p; - if (**varp != NUL && strcmp(*varp, "expr:0") != 0 && strcmp(*varp, "expr:1") != 0) { + if (opt_flags & OPT_LOCAL) { + p = buf->b_p_tc; + flags = &buf->b_tc_flags; + } else { + p = p_tc; + flags = &tc_flags; + } + + if ((opt_flags & OPT_LOCAL) && *p == NUL) { + // make the local value empty: use the global value + *flags = 0; + } else if (*p == NUL + || opt_strings_flags(p, p_tc_values, flags, false) != OK) { return e_invarg; } return NULL; } -/// The 'rightleftcmd' option is changed. -const char *did_set_rightleftcmd(optset_T *args) +/// The 'termpastefilter' option is changed. +const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED) { - char **varp = (char **)args->os_varp; - - // Currently only "search" is a supported value. - if (**varp != NUL && strcmp(*varp, "search") != 0) { - return e_invarg; - } - - return NULL; + return did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true); } -/// The 'filetype' or the 'syntax' option is changed. -const char *did_set_filetype_or_syntax(optset_T *args) +/// The 'titlestring' or the 'iconstring' option is changed. +static const char *did_set_titleiconstring(optset_T *args, int flagval) { char **varp = (char **)args->os_varp; - if (!valid_filetype(*varp)) { - return e_invarg; + // NULL => statusline syntax + if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) { + stl_syntax |= flagval; + } else { + stl_syntax &= ~flagval; } - - args->os_value_changed = strcmp(args->os_oldval.string, *varp) != 0; - - // Since we check the value, there is no need to set P_INSECURE, - // even when the value comes from a modeline. - args->os_value_checked = true; + did_set_title(); return NULL; } -const char *did_set_winhl(optset_T *args) +/// The 'titlestring' option is changed. +const char *did_set_titlestring(optset_T *args) { - win_T *win = (win_T *)args->os_win; - if (!parse_winhl_opt(win)) { - return e_invarg; - } - return NULL; + return did_set_titleiconstring(args, STL_IN_TITLE); } /// The 'varsofttabstop' option is changed. @@ -1983,62 +1942,97 @@ const char *did_set_vartabstop(optset_T *args) return NULL; } -/// The 'nrformats' option is changed. -const char *did_set_nrformats(optset_T *args) +/// The 'verbosefile' option is changed. +const char *did_set_verbosefile(optset_T *args) { - char **varp = (char **)args->os_varp; + verbose_stop(); + if (*p_vfile != NUL && verbose_open() == FAIL) { + return (char *)e_invarg; + } + return NULL; +} - return did_set_opt_strings(*varp, p_nf_values, true); +/// The 'viewoptions' option is changed. +const char *did_set_viewoptions(optset_T *args FUNC_ATTR_UNUSED) +{ + return did_set_opt_flags(p_vop, p_ssop_values, &vop_flags, true); } -/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', -/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. -const char *did_set_optexpr(optset_T *args) +/// The 'virtualedit' option is changed. +const char *did_set_virtualedit(optset_T *args) { - char **varp = (char **)args->os_varp; + win_T *win = (win_T *)args->os_win; - // If the option value starts with or s:, then replace that with - // the script identifier. - char *name = get_scriptlocal_funcname(*varp); - if (name != NULL) { - free_string_option(*varp); - *varp = name; + char *ve = p_ve; + unsigned *flags = &ve_flags; + + if (args->os_flags & OPT_LOCAL) { + ve = win->w_p_ve; + flags = &win->w_ve_flags; + } + + if ((args->os_flags & OPT_LOCAL) && *ve == NUL) { + // make the local value empty: use the global value + *flags = 0; + } else { + if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) { + return e_invarg; + } else if (strcmp(ve, args->os_oldval.string) != 0) { + // Recompute cursor position in case the new 've' setting + // changes something. + validate_virtcol_win(win); + // XXX: this only works when win == curwin + coladvance(win->w_virtcol); + } } return NULL; } -/// The 'foldexpr' option is changed. -const char *did_set_foldexpr(optset_T *args) +/// The 'whichwrap' option is changed. +const char *did_set_whichwrap(optset_T *args) { - win_T *win = (win_T *)args->os_win; - (void)did_set_optexpr(args); - if (foldmethodIsExpr(win)) { - foldUpdateAll(win); + char **varp = (char **)args->os_varp; + + return did_set_option_listflag(*varp, WW_ALL, args->os_errbuf, args->os_errbuflen); +} + +/// The 'wildmode' option is changed. +const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED) +{ + if (check_opt_wim() == FAIL) { + return e_invarg; } return NULL; } -/// The 'foldclose' option is changed. -const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED) +/// The 'wildoptions' option is changed. +const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_strings(p_fcl, p_fcl_values, true); + return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true); } -/// An option which is a list of flags is set. Valid values are in 'flags'. -static const char *did_set_option_listflag(char *val, char *flags, char *errbuf, size_t errbuflen) +/// The 'winaltkeys' option is changed. +const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED) { - for (char *s = val; *s; s++) { - if (vim_strchr(flags, (uint8_t)(*s)) == NULL) { - return illegal_char(errbuf, errbuflen, (uint8_t)(*s)); - } + if (*p_wak == NUL || check_opt_strings(p_wak, p_wak_values, false) != OK) { + return e_invarg; } - return NULL; } -const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED) +/// The 'winbar' option is changed. +const char *did_set_winbar(optset_T *args) { - return parse_shape_opt(SHAPE_CURSOR); + return did_set_statustabline_rulerformat(args, false, false); +} + +const char *did_set_winhl(optset_T *args) +{ + win_T *win = (win_T *)args->os_win; + if (!parse_winhl_opt(win)) { + return e_invarg; + } + return NULL; } // When 'syntax' is set, load the syntax of that name @@ -2564,3 +2558,15 @@ const char *check_chars_options(void) } return NULL; } + +/// Handle the new value of 'fillchars'. +const char *set_fillchars_option(win_T *wp, char *val, bool apply) +{ + return set_chars_option(wp, val, false, apply); +} + +/// Handle the new value of 'listchars'. +const char *set_listchars_option(win_T *wp, char *val, bool apply) +{ + return set_chars_option(wp, val, true, apply); +} -- cgit