diff options
author | zeertzjq <zeertzjq@outlook.com> | 2021-11-22 15:50:14 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2021-11-22 15:50:14 +0800 |
commit | d40db0edbdc7058c6096b8a389bee88d162329a9 (patch) | |
tree | ea3a5af4faa2b7c343cdf41ab4ca83b04102dcca | |
parent | 64abd7be7993ed3fa031b5d71a4482871108093a (diff) | |
download | rneovim-d40db0edbdc7058c6096b8a389bee88d162329a9.tar.gz rneovim-d40db0edbdc7058c6096b8a389bee88d162329a9.tar.bz2 rneovim-d40db0edbdc7058c6096b8a389bee88d162329a9.zip |
vim-patch:8.1.1542: an OptionSet autocommand does not get enough info
Problem: An OptionSet autocommand does not get enough info.
Solution: Add v:option_command, v:option_oldlocal and v:option_oldglobal.
(Latrice Wilgus, closes vim/vim#4118)
https://github.com/vim/vim/commit/d7c968794710f338d491072171df48f96612cf72
-rw-r--r-- | runtime/doc/eval.txt | 21 | ||||
-rw-r--r-- | src/nvim/eval.c | 205 | ||||
-rw-r--r-- | src/nvim/eval.h | 3 | ||||
-rw-r--r-- | src/nvim/option.c | 148 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 522 | ||||
-rw-r--r-- | test/functional/legacy/autocmd_option_spec.lua | 420 |
6 files changed, 1131 insertions, 188 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 58d5d30146..89688b36a7 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2042,10 +2042,29 @@ v:option_new New value of the option. Valid while executing an |OptionSet| autocommand. *v:option_old* v:option_old Old value of the option. Valid while executing an |OptionSet| - autocommand. + autocommand. Depending on the command used for setting and the + kind of option this is either the local old value or the + global old value. + *v:option_oldlocal* +v:option_oldlocal + Old local value of the option. Valid while executing an + |OptionSet| autocommand. + *v:option_oldglobal* +v:option_oldglobal + Old global value of the option. Valid while executing an + |OptionSet| autocommand. *v:option_type* v:option_type Scope of the set command. Valid while executing an |OptionSet| autocommand. Can be either "global" or "local" + *v:option_command* +v:option_command + Command used to set the option. Valid while executing an + |OptionSet| autocommand. + value option was set via ~ + "setlocal" |:setlocal| or ":let l:xxx" + "setglobal" |:setglobal| or ":let g:xxx" + "set" |:set| or |:let| + "modeline" |modeline| *v:operator* *operator-variable* v:operator The last operator given in Normal mode. This is a single character except for commands starting with <g> or <z>, diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d3d0121632..a09ad7924d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -134,13 +134,15 @@ typedef struct { .vv_flags = flags, \ } +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { @@ -150,100 +152,103 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), - VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), - VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), - VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), - VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), - VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), - VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), - VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), - VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), - VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), - VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), - VV(VV_FNAME, "fname", VAR_STRING, VV_RO), - VV(VV_LANG, "lang", VAR_STRING, VV_RO), - VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), - VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), - VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), - VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), - VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), - VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), - VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), - VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), - VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), - VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), - VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), - VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), - VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), - VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), - VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), - VV(VV_REG, "register", VAR_STRING, VV_RO), - VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), - VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), - VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), - VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), - VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), - VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), - VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), - VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), - VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), - VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), - VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), - VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), - VV(VV_CHAR, "char", VAR_STRING, 0), - VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), - VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), - VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), - VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), - VV(VV_OP, "operator", VAR_STRING, VV_RO), - VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), - VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), - VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), - VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), - VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), - VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), - VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), - VV(VV_ERRORS, "errors", VAR_LIST, 0), - VV(VV_FALSE, "false", VAR_BOOL, VV_RO), - VV(VV_TRUE, "true", VAR_BOOL, VV_RO), - VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), - VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), - VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), - VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), - VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), - VV(VV_TESTING, "testing", VAR_NUMBER, 0), - VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), - VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), - VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), - VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), - VV(VV_EVENT, "event", VAR_DICT, VV_RO), - VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), - VV(VV_ARGV, "argv", VAR_LIST, VV_RO), - VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), - VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), // Neovim - VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), - VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), - VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), - VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), - VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), - VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), - VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), }; #undef VV @@ -344,7 +349,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; - assert(STRLEN(p->vv_name) <= 16); + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) { p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -10446,11 +10451,15 @@ void option_last_set_msg(LastSet last_set) } } -// reset v:option_new, v:option_old and v:option_type +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. void reset_v_option_vars(void) { - set_vim_var_string(VV_OPTION_NEW, NULL, -1); - set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } diff --git a/src/nvim/eval.h b/src/nvim/eval.h index d34348a274..986dda2408 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -138,6 +138,9 @@ typedef enum { VV_COMPLETED_ITEM, VV_OPTION_NEW, VV_OPTION_OLD, + VV_OPTION_OLDLOCAL, + VV_OPTION_OLDGLOBAL, + VV_OPTION_COMMAND, VV_OPTION_TYPE, VV_ERRORS, VV_FALSE, diff --git a/src/nvim/option.c b/src/nvim/option.c index 45e2032b35..dbe90920cd 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1301,7 +1301,11 @@ int do_set(char_u *arg, int opt_flags) char_u *oldval = NULL; // previous value if *varp char_u *newval; char_u *origval = NULL; + char_u *origval_l = NULL; + char_u *origval_g = NULL; char *saved_origval = NULL; + char *saved_origval_l = NULL; + char *saved_origval_g = NULL; char *saved_newval = NULL; unsigned newlen; int comma; @@ -1319,10 +1323,21 @@ int do_set(char_u *arg, int opt_flags) // new value is valid. oldval = *(char_u **)varp; + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + + // A global-local string option might have an empty + // option as value to indicate that the global + // value should be used. + if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) { + origval_l = origval_g; + } + } + // When setting the local value of a global // option, the old value may be the global value. - if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags - & OPT_LOCAL)) { + if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) { origval = *(char_u **)get_varp(&options[opt_idx]); } else { origval = oldval; @@ -1388,6 +1403,12 @@ int do_set(char_u *arg, int opt_flags) if (origval == oldval) { origval = *(char_u **)varp; } + if (origval_l == oldval) { + origval_l = *(char_u **)varp; + } + if (origval_g == oldval) { + origval_g = *(char_u **)varp; + } oldval = *(char_u **)varp; } /* @@ -1596,6 +1617,8 @@ int do_set(char_u *arg, int opt_flags) // origval may be freed by // did_set_string_option(), make a copy. saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0; + saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : 0; + saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : 0; // newval (and varp) may become invalid if the // buffer is closed by autocommands. @@ -1630,8 +1653,8 @@ int do_set(char_u *arg, int opt_flags) if (errmsg == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_origval, - saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_origval, saved_origval_l, + saved_origval_g, saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -1639,6 +1662,8 @@ int do_set(char_u *arg, int opt_flags) } } xfree(saved_origval); + xfree(saved_origval_l); + xfree(saved_origval_g); xfree(saved_newval); // If error detected, print the error message. @@ -2233,9 +2258,19 @@ static char *set_string_option(const int opt_idx, const char *const value, const ? OPT_GLOBAL : OPT_LOCAL) : opt_flags)); char *const oldval = *varp; + char *oldval_l = NULL; + char *oldval_g = NULL; + + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + oldval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + oldval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *varp = s; char *const saved_oldval = xstrdup(oldval); + char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0; + char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0; char *const saved_newval = xstrdup(s); int value_checked = false; @@ -2249,7 +2284,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const // call autocommand after handling side effects if (r == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g, + saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -2257,6 +2293,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const } } xfree(saved_oldval); + xfree(saved_oldval_l); + xfree(saved_oldval_g); xfree(saved_newval); return r; @@ -3851,6 +3889,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va 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) @@ -3858,6 +3897,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va return (char *)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); @@ -4134,20 +4180,35 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va // Don't do this while starting up or recursively. if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[2]; + char buf_old_global[2]; char buf_new[2]; char buf_type[7]; - vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", - old_value ? true: false); - vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", - value ? true: false); + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true : false); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%d", old_global_value ? true : false); + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true : false); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4181,7 +4242,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, { char *errmsg = NULL; long old_value = *(long *)varp; - long old_Rows = Rows; // remember old Rows + long old_global_value = 0; // only used when setting a local and global option + long old_Rows = Rows; // remember old Rows long *pp = (long *)varp; // Disallow changing some options from secure mode. @@ -4190,6 +4252,13 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" infact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + // Many number options assume their value is in the signed int range. if (value < INT_MIN || value > INT_MAX) { return e_invarg; @@ -4534,19 +4603,36 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // Don't do this while starting up, failure or recursively. if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[NUMBUFLEN]; + char buf_old_global[NUMBUFLEN]; char buf_new[NUMBUFLEN]; char buf_type[7]; vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%ld", old_global_value); vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4565,7 +4651,15 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return (char *)errmsg; } -static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *newval) +/// Trigger the OptionSet autocommand. +/// "opt_idx" is the index of the option being set. +/// "opt_flags" can be OPT_LOCAL etc. +/// "oldval" the old value +/// "oldval_l" the old local value (only non-NULL if global and local value are set) +/// "oldval_g" the old global value (only non-NULL if global and local value are set) +/// "newval" the new value +static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l, + char *oldval_g, char *newval) { // Don't do this recursively. if (oldval != NULL @@ -4578,8 +4672,24 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, set_vim_var_string(VV_OPTION_OLD, oldval, -1); set_vim_var_string(VV_OPTION_NEW, newval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } } diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 0c8b8a45d9..7835556a47 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -580,9 +580,10 @@ func Test_empty_doau() endfunc func s:AutoCommandOptionSet(match) + let template = "Option: <%s>, OldVal: <%s>, OldValLocal: <%s>, OldValGlobal: <%s>, NewVal: <%s>, Scope: <%s>, Command: <%s>\n" let item = remove(g:options, 0) - let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3]) - let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type) + let expected = printf(template, item[0], item[1], item[2], item[3], item[4], item[5], item[6]) + let actual = printf(template, a:match, v:option_old, v:option_oldlocal, v:option_oldglobal, v:option_new, v:option_type, v:option_command) let g:opt = [expected, actual] "call assert_equal(expected, actual) endfunc @@ -596,130 +597,593 @@ func Test_OptionSet() au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>")) " 1: Setting number option" - let g:options = [['number', 0, 1, 'global']] + let g:options = [['number', 0, 0, 0, 1, 'global', 'set']] set nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 2: Setting local number option" - let g:options = [['number', 1, 0, 'local']] + let g:options = [['number', 1, 1, '', 0, 'local', 'setlocal']] setlocal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 3: Setting global number option" - let g:options = [['number', 1, 0, 'global']] + let g:options = [['number', 1, '', 1, 0, 'global', 'setglobal']] setglobal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 4: Setting local autoindent option" - let g:options = [['autoindent', 0, 1, 'local']] + let g:options = [['autoindent', 0, 0, '', 1, 'local', 'setlocal']] setlocal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 5: Setting global autoindent option" - let g:options = [['autoindent', 0, 1, 'global']] + let g:options = [['autoindent', 0, '', 0, 1, 'global', 'setglobal']] setglobal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 6: Setting global autoindent option" - let g:options = [['autoindent', 1, 0, 'global']] + let g:options = [['autoindent', 1, 1, 1, 0, 'global', 'set']] + set ai! + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 6a: Setting global autoindent option" + let g:options = [['autoindent', 1, 1, 0, 0, 'global', 'set']] + noa setlocal ai + noa setglobal noai set ai! call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " Should not print anything, use :noa " 7: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nonu - call assert_equal([['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 8: Setting several global list and number option" - let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']] + let g:options = [['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']] set list nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 9: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nolist nonu - call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 10: Setting global acd" - let g:options = [['autochdir', 0, 1, 'local']] + let g:options = [['autochdir', 0, 0, '', 1, 'local', 'setlocal']] setlocal acd call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 11: Setting global autoread (also sets local value)" - let g:options = [['autoread', 0, 1, 'global']] + let g:options = [['autoread', 0, 0, 0, 1, 'global', 'set']] set ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 12: Setting local autoread" - let g:options = [['autoread', 1, 1, 'local']] + let g:options = [['autoread', 1, 1, '', 1, 'local', 'setlocal']] setlocal ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 13: Setting global autoread" - let g:options = [['autoread', 1, 0, 'global']] + let g:options = [['autoread', 1, '', 1, 0, 'global', 'setglobal']] setglobal invar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 14: Setting option backspace through :let" - let g:options = [['backspace', '', 'eol,indent,start', 'global']] + let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']] let &bs = "eol,indent,start" call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 15: Setting option backspace through setbufvar()" - let g:options = [['backup', 0, 1, 'local']] + let g:options = [['backup', 0, 0, '', 1, 'local', 'setlocal']] " try twice, first time, shouldn't trigger because option name is invalid, " second time, it should trigger - call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355") + let bnum = bufnr('%') + call assert_fails("call setbufvar(bnum, '&l:bk', 1)", 'E355:') " should trigger, use correct option name - call setbufvar(1, '&backup', 1) + call setbufvar(bnum, '&backup', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 16: Setting number option using setwinvar" - let g:options = [['number', 0, 1, 'local']] + let g:options = [['number', 0, 0, '', 1, 'local', 'setlocal']] call setwinvar(0, '&number', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 17: Setting key option, shouldn't trigger" - let g:options = [['key', 'invalid', 'invalid1', 'invalid']] + let g:options = [['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']] setlocal key=blah setlocal key= - call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) + call assert_equal([['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 18: Setting string option" + + " 18a: Setting string global option" + let oldval = &backupext + let g:options = [['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']] + set backupext=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18b: Resetting string global option" + let g:options = [['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set backupext& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18c: Setting global string global option" + let g:options = [['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal backupext=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18d: Setting local string global option" + " As this is a global option this sets the global value even though + " :setlocal is used! + noa set backupext& " Reset global and local value (without triggering autocmd) + let g:options = [['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal backupext=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18e: Setting again string global option" + noa setglobal backupext=ext_global " Reset global and local value (without triggering autocmd) + noa setlocal backupext=ext_local " Sets the global(!) value! + let g:options = [['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']] + set backupext=fuu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 19a: Setting string local-global (to buffer) option" let oldval = &tags - let g:options = [['tags', oldval, 'tagpath', 'global']] + let g:options = [['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']] set tags=tagpath call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 1l: Resetting string option" - let g:options = [['tags', 'tagpath', oldval, 'global']] + " 19b: Resetting string local-global (to buffer) option" + let g:options = [['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']] set tags& call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) + " 19c: Setting global string local-global (to buffer) option " + let g:options = [['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']] + setglobal tags=tagpath1 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19d: Setting local string local-global (to buffer) option" + let g:options = [['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']] + setlocal tags=tagpath2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19e: Setting again string local-global (to buffer) option" + " Note: v:option_old is the old global value for local-global string options + " but the old local value for all other kinds of options. + noa setglobal tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags=tag_local + let g:options = [['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19f: Setting string local-global (to buffer) option to an empty string" + " Note: v:option_old is the old global value for local-global string options + " but the old local value for all other kinds of options. + noa set tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags= " empty string + let g:options = [['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 20a: Setting string local (to buffer) option" + let oldval = &spelllang + let g:options = [['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']] + set spelllang=elvish,klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20b: Resetting string local (to buffer) option" + let g:options = [['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']] + set spelllang& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20c: Setting global string local (to buffer) option" + let g:options = [['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']] + setglobal spelllang=elvish + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20d: Setting local string local (to buffer) option" + noa set spelllang& " Reset global and local value (without triggering autocmd) + let g:options = [['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']] + setlocal spelllang=klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20e: Setting again string local (to buffer) option" + " Note: v:option_old is the old global value for local-global string options + " but the old local value for all other kinds of options. + noa setglobal spelllang=spellglobal " Reset global and local value (without triggering autocmd) + noa setlocal spelllang=spelllocal + let g:options = [['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']] + set spelllang=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 21a: Setting string local-global (to window) option" + let oldval = &statusline + let g:options = [['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21b: Resetting string local-global (to window) option" + " Note: v:option_old is the old global value for local-global string options + " but the old local value for all other kinds of options. + let g:options = [['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set statusline& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21c: Setting global string local-global (to window) option" + let g:options = [['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal statusline=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21d: Setting local string local-global (to window) option" + noa set statusline& " Reset global and local value (without triggering autocmd) + let g:options = [['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal statusline=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21e: Setting again string local-global (to window) option" + " Note: v:option_old is the old global value for local-global string options + " but the old local value for all other kinds of options. + noa setglobal statusline=bar " Reset global and local value (without triggering autocmd) + noa setlocal statusline=baz + let g:options = [['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 22a: Setting string local (to window) option" + let oldval = &foldignore + let g:options = [['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22b: Resetting string local (to window) option" + let g:options = [['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']] + set foldignore& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22c: Setting global string local (to window) option" + let g:options = [['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal foldignore=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22d: Setting local string local (to window) option" + noa set foldignore& " Reset global and local value (without triggering autocmd) + let g:options = [['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal foldignore=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22e: Setting again string local (to window) option" + noa setglobal foldignore=glob " Reset global and local value (without triggering autocmd) + noa setlocal foldignore=loc + let g:options = [['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 23a: Setting global number local option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '', '1', '2', 'global', 'setglobal']] + setglobal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23b: Setting local number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '', '2', 'local', 'setlocal']] + setlocal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23c: Setting again number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '1', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23d: Setting again number global option" + noa set cmdheight=8 " Reset global and local value (without triggering autocmd) + let g:options = [['cmdheight', '8', '8', '8', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 24a: Setting global number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '8', '', '8', '2', 'global', 'setglobal']] + setglobal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24b: Setting local number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '', '2', 'local', 'setlocal']] + setlocal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24c: Setting again number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24d: Setting again global number global-local (to buffer) option" + noa set undolevels=8 " Reset global and local value (without triggering autocmd) + let g:options = [['undolevels', '8', '8', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 25a: Setting global number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']] + setglobal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25b: Setting local number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']] + setlocal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25c: Setting again number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25d: Setting again global number local (to buffer) option" + noa set wrapmargin=8 " Reset global and local value (without triggering autocmd) + let g:options = [['wrapmargin', '8', '8', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 26: Setting number global-local (to window) option. + " Such option does currently not exist. + + + " 27a: Setting global number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']] + setglobal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27b: Setting local number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']] + setlocal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27c: Setting again number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27d: Ssettin again global number local (to window) option" + noa set foldcolumn=8 " Reset global and local value (without triggering autocmd) + let g:options = [['foldcolumn', '8', '8', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 28a: Setting global boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '', '1', '0', 'global', 'setglobal']] + setglobal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28b: Setting local boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28c: Setting again boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '1', '0', 'global', 'set']] + set nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28d: Setting again global boolean global option" + noa set nowrapscan " Reset global and local value (without triggering autocmd) + let g:options = [['wrapscan', '0', '0', '0', '1', 'global', 'set']] + set wrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 29a: Setting global boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '0', '', '0', '1', 'global', 'setglobal']] + setglobal autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29b: Setting local boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '', '0', 'local', 'setlocal']] + setlocal noautoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29c: Setting again boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29d: Setting again global boolean global-local (to buffer) option" + noa set noautoread " Reset global and local value (without triggering autocmd) + let g:options = [['autoread', '0', '0', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 30a: Setting global boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30b: Setting local boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30c: Setting again boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30d: Setting again global boolean local (to buffer) option" + noa set nocindent " Reset global and local value (without triggering autocmd) + let g:options = [['cindent', '0', '0', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 31: Setting boolean global-local (to window) option + " Currently no such option exists. + + + " 32a: Setting global boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32b: Setting local boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32c: Setting again boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32d: Setting again global boolean local (to window) option" + noa set nocursorcolumn " Reset global and local value (without triggering autocmd) + let g:options = [['cursorcolumn', '0', '0', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 33: Test autocommands when an option value is converted internally. + noa set backspace=1 " Reset global and local value (without triggering autocmd) + let g:options = [['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']] + set backspace=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " Cleanup au! OptionSet " set tags& - for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'tags'] + for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'backupext', 'tags', 'spelllang', 'statusline', 'foldignore', 'cmdheight', 'undolevels', 'wrapmargin', 'foldcolumn', 'wrapscan', 'autoread', 'cindent', 'cursorcolumn'] exe printf(":set %s&vim", opt) endfor call test_override('starting', 0) diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 1914818215..5e586d3a6a 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local nvim = helpers.meths -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq +local clear, eq, neq, eval = helpers.clear, helpers.eq, helpers.neq, helpers.eval local curbuf, buf = helpers.curbuf, helpers.bufmeths local curwin = helpers.curwin local exec_capture = helpers.exec_capture @@ -10,11 +10,14 @@ local function declare_hook_function() source([[ fu! AutoCommand(match, bufnr, winnr) let l:acc = { - \ 'option' : a:match, - \ 'oldval' : v:option_old, - \ 'newval' : v:option_new, - \ 'scope' : v:option_type, - \ 'attr' : { + \ 'option' : a:match, + \ 'oldval' : v:option_old, + \ 'oldval_l' : v:option_oldlocal, + \ 'oldval_g' : v:option_oldglobal, + \ 'newval' : v:option_new, + \ 'scope' : v:option_type, + \ 'cmd' : v:option_command, + \ 'attr' : { \ 'bufnr' : a:bufnr, \ 'winnr' : a:winnr, \ } @@ -42,13 +45,16 @@ local function get_result() return ret end -local function expected_table(option, oldval, newval, scope, attr) +local function expected_table(option, oldval, oldval_l, oldval_g, newval, scope, cmd, attr) return { - option = option, - oldval = tostring(oldval), - newval = tostring(newval), - scope = scope, - attr = attr, + option = option, + oldval = tostring(oldval), + oldval_l = tostring(oldval_l), + oldval_g = tostring(oldval_g), + newval = tostring(newval), + scope = scope, + cmd = cmd, + attr = attr, } end @@ -66,7 +72,7 @@ local function expected_combination(...) end for i, v in ipairs(args) do - local attr = v[5] + local attr = v[8] if not attr then -- remove attr entries ret[i].attr = nil @@ -112,7 +118,7 @@ local function get_new_window_number() end describe('au OptionSet', function() - describe('with any opton (*)', function() + describe('with any option (*)', function() before_each(function() clear() @@ -123,44 +129,44 @@ describe('au OptionSet', function() it('should be called in setting number option', function() command('set nu') - expected_combination({'number', 0, 1, 'global'}) + expected_combination({'number', 0, 0, 0, 1, 'global', 'set'}) command('setlocal nonu') - expected_combination({'number', 1, 0, 'local'}) + expected_combination({'number', 1, 1, '', 0, 'local', 'setlocal'}) command('setglobal nonu') - expected_combination({'number', 1, 0, 'global'}) + expected_combination({'number', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ai') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, '', 0, 1, 'global', 'setglobal'}) command('set noai') - expected_combination({'autoindent', 1, 0, 'global'}) + expected_combination({'autoindent', 1, 1, 1, 0, 'global', 'set'}) end) it('should be called in inverting global autoindent option',function() command('set ai!') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, 0, 0, 1, 'global', 'set'}) end) it('should be called in being unset local autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setlocal ai<') - expected_combination({'autoindent', 1, 0, 'local'}) + expected_combination({'autoindent', 1, 1, '', 0, 'local', 'setlocal'}) end) it('should be called in setting global list and number option at the same time',function() command('set list nu') expected_combination( - {'list', 0, 1, 'global'}, - {'number', 0, 1, 'global'} + {'list', 0, 0, 0, 1, 'global', 'set'}, + {'number', 0, 0, 0, 1, 'global', 'set'} ) end) @@ -171,25 +177,27 @@ describe('au OptionSet', function() it('should be called in setting local acd', function() command('setlocal acd') - expected_combination({'autochdir', 0, 1, 'local'}) + expected_combination({'autochdir', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in setting autoread', function() command('set noar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, 1, 1, 0, 'global', 'set'}) command('setlocal ar') - expected_combination({'autoread', 0, 1, 'local'}) + expected_combination({'autoread', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in inverting global autoread', function() command('setglobal invar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting backspace option through :let', function() + local oldval = eval('&backspace') + command('let &bs=""') - expected_combination({'backspace', 'indent,eol,start', '', 'global'}) + expected_combination({'backspace', oldval, oldval, oldval, '', 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -200,7 +208,7 @@ describe('au OptionSet', function() it('should trigger using correct option name', function() command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -208,9 +216,339 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) + + it('with string global option', function() + local oldval = eval('&backupext') + + command('set backupext=foo') + expected_combination({'backupext', oldval, oldval, oldval, 'foo', 'global', 'set'}) + + command('set backupext&') + expected_combination({'backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal backupext=bar') + expected_combination({'backupext', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set backupext&') + -- As this is a global option this sets the global value even though :setlocal is used! + command('setlocal backupext=baz') + expected_combination({'backupext', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal backupext=ext_global') + command('noa setlocal backupext=ext_local') -- Sets the global(!) value + command('set backupext=foo') + expected_combination({ + 'backupext', 'ext_local', 'ext_local', 'ext_local', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to buffer) option', function() + local oldval = eval('&tags') + + command('set tags=tagpath') + expected_combination({'tags', oldval, oldval, oldval, 'tagpath', 'global', 'set'}) + + command('set tags&') + expected_combination({'tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set'}) + + command('setglobal tags=tagpath1') + expected_combination({'tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal'}) + + command('setlocal tags=tagpath2') + expected_combination({'tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal tags=tag_global') + command('noa setlocal tags=tag_local') + command('set tags=tagpath') + expected_combination({ + 'tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set' + }) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa set tags=tag_global') + command('noa setlocal tags=') + command('set tags=tagpath') + expected_combination({'tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set'}) + end) + + it('with string local (to buffer) option', function() + local oldval = eval('&spelllang') + + command('set spelllang=elvish,klingon') + expected_combination({'spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set'}) + + command('set spelllang&') + expected_combination({ + 'spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set' + }) + + command('setglobal spelllang=elvish') + expected_combination({'spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal'}) + + command('noa set spelllang&') + command('setlocal spelllang=klingon') + expected_combination({'spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal spelllang=spellglobal') + command('noa setlocal spelllang=spelllocal') + command('set spelllang=foo') + expected_combination({ + 'spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to window) option', function() + local oldval = eval('&statusline') + + command('set statusline=foo') + expected_combination({'statusline', oldval, oldval, '', 'foo', 'global', 'set'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('set statusline&') + expected_combination({'statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal statusline=bar') + expected_combination({'statusline', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set statusline&') + command('setlocal statusline=baz') + expected_combination({'statusline', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal statusline=bar') + command('noa setlocal statusline=baz') + command('set statusline=foo') + expected_combination({'statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set'}) + end) + + it('with string local (to window) option', function() + local oldval = eval('&foldignore') + + command('set foldignore=fo') + expected_combination({'foldignore', oldval, oldval, oldval, 'fo', 'global', 'set'}) + + command('set foldignore&') + expected_combination({'foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set'}) + + command('setglobal foldignore=bar') + expected_combination({'foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set foldignore&') + command('setlocal foldignore=baz') + expected_combination({'foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal foldignore=glob') + command('noa setlocal foldignore=loc') + command('set foldignore=fo') + expected_combination({'foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set'}) + end) + + it('with number global option', function() + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setglobal cmdheight=2') + expected_combination({'cmdheight', 1, '', 1, 2, 'global', 'setglobal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setlocal cmdheight=2') + expected_combination({'cmdheight', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('set cmdheight=2') + expected_combination({'cmdheight', 1, 1, 1, 2, 'global', 'set'}) + + command('noa set cmdheight=8') + command('set cmdheight=2') + expected_combination({'cmdheight', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to buffer) option', function() + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setglobal undolevels=2') + expected_combination({'undolevels', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setlocal undolevels=2') + expected_combination({'undolevels', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('set undolevels=2') + expected_combination({'undolevels', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set undolevels=8') + command('set undolevels=2') + expected_combination({'undolevels', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to buffer) option', function() + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setglobal wrapmargin=2') + expected_combination({'wrapmargin', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setlocal wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set wrapmargin=8') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to window) option', function() + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setglobal scrolloff=2') + expected_combination({'scrolloff', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setlocal scrolloff=2') + expected_combination({'scrolloff', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('set scrolloff=2') + expected_combination({'scrolloff', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set scrolloff=8') + command('set scrolloff=2') + expected_combination({'scrolloff', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to window) option', function() + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setglobal foldcolumn=2') + expected_combination({'foldcolumn', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setlocal foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set foldcolumn=8') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with boolean global option', function() + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setglobal nowrapscan') + expected_combination({'wrapscan', 1, '', 1, 0, 'global', 'setglobal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setlocal nowrapscan') + expected_combination({'wrapscan', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('set nowrapscan') + expected_combination({'wrapscan', 1, 1, 1, 0, 'global', 'set'}) + + command('noa set nowrapscan') + command('set wrapscan') + expected_combination({'wrapscan', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean global-local (to buffer) option', function() + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setglobal autoread') + expected_combination({'autoread', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setlocal noautoread') + expected_combination({'autoread', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('set autoread') + expected_combination({'autoread', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set noautoread') + command('set autoread') + expected_combination({'autoread', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to buffer) option', function() + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setglobal cindent') + expected_combination({'cindent', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setlocal nocindent') + expected_combination({'cindent', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('set cindent') + expected_combination({'cindent', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocindent') + command('set cindent') + expected_combination({'cindent', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to window) option', function() + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setglobal cursorcolumn') + expected_combination({'cursorcolumn', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setlocal nocursorcolumn') + expected_combination({'cursorcolumn', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with option value converted internally', function() + command('noa set backspace=1') + command('set backspace=2') + expected_combination(({ + 'backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set' + })) + end) end) describe('with specific option', function() @@ -228,13 +566,13 @@ describe('au OptionSet', function() expected_empty() command('setlocal ro') - expected_combination({'readonly', 0, 1, 'local'}) + expected_combination({'readonly', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ro') - expected_combination({'readonly', 0, 1, 'global'}) + expected_combination({'readonly', 0, '', 0, 1, 'global', 'setglobal'}) command('set noro') - expected_combination({'readonly', 1, 0, 'global'}) + expected_combination({'readonly', 1, 1, 1, 0, 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -249,7 +587,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -259,7 +597,7 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) @@ -275,7 +613,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setwinvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should not trigger if the current window is different from the targetted window', function() @@ -295,7 +633,7 @@ describe('au OptionSet', function() nvim.set_option('autochdir', true) eq(true, nvim.get_option('autochdir')) - expected_combination({'autochdir', '0', '1', 'global'}) + expected_combination({'autochdir', 0, '', 0, 1, 'global', 'setglobal'}) end) it('should trigger if a number option be set globally', function() @@ -303,7 +641,7 @@ describe('au OptionSet', function() nvim.set_option('cmdheight', 5) eq(5, nvim.get_option('cmdheight')) - expected_combination({'cmdheight', 1, 5, 'global'}) + expected_combination({'cmdheight', 1, '', 1, 5, 'global', 'setglobal'}) end) it('should trigger if a string option be set globally', function() @@ -311,7 +649,7 @@ describe('au OptionSet', function() nvim.set_option('ambiwidth', 'double') eq('double', nvim.get_option('ambiwidth')) - expected_combination({'ambiwidth', 'single', 'double', 'global'}) + expected_combination({'ambiwidth', 'single', '', 'single', 'double', 'global', 'setglobal'}) end) end) end) |