diff options
author | Colin Kennedy <colinvfx@gmail.com> | 2024-03-03 16:16:47 +0100 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2024-03-03 16:16:47 +0100 |
commit | 215703563757a4464907ead6fb9edaeb7f430bea (patch) | |
tree | 380d7492e2fc174d13a073c6fd4e8dae6714030f /src | |
parent | 353faa373eb132987a1985cf3abe18c006f8cdf0 (diff) |
patch 9.1.0147: Cannot keep a buffer focused in a windowv9.1.0147
Problem: Cannot keep a buffer focused in a window
(Amit Levy)
Solution: Add the 'winfixbuf' window-local option
(Colin Kennedy)
fixes: #6445
closes: #13903
Signed-off-by: Colin Kennedy <colinvfx@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/arglist.c | 8 | ||||
-rw-r--r-- | src/buffer.c | 7 | ||||
-rw-r--r-- | src/errors.h | 2 | ||||
-rw-r--r-- | src/ex_cmds.c | 3 | ||||
-rw-r--r-- | src/ex_cmds.h | 2 | ||||
-rw-r--r-- | src/ex_cmds2.c | 25 | ||||
-rw-r--r-- | src/ex_docmd.c | 15 | ||||
-rw-r--r-- | src/insexpand.c | 2 | ||||
-rw-r--r-- | src/normal.c | 6 | ||||
-rw-r--r-- | src/option.c | 1 | ||||
-rw-r--r-- | src/option.h | 1 | ||||
-rw-r--r-- | src/optiondefs.h | 4 | ||||
-rw-r--r-- | src/proto/search.pro | 2 | ||||
-rw-r--r-- | src/proto/window.pro | 2 | ||||
-rw-r--r-- | src/quickfix.c | 40 | ||||
-rw-r--r-- | src/search.c | 7 | ||||
-rw-r--r-- | src/structs.h | 2 | ||||
-rw-r--r-- | src/tag.c | 6 | ||||
-rw-r--r-- | src/testdir/Make_all.mak | 1 | ||||
-rw-r--r-- | src/testdir/test_winfixbuf.vim | 3131 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/window.c | 33 |
22 files changed, 3287 insertions, 15 deletions
diff --git a/src/arglist.c b/src/arglist.c index 723133254a..187e16e835 100644 --- a/src/arglist.c +++ b/src/arglist.c @@ -682,6 +682,7 @@ do_argfile(exarg_T *eap, int argn) int other; char_u *p; int old_arg_idx = curwin->w_arg_idx; + int is_split_cmd = *eap->cmd == 's'; if (ERROR_IF_ANY_POPUP_WINDOW) return; @@ -697,13 +698,18 @@ do_argfile(exarg_T *eap, int argn) return; } + if (!is_split_cmd + && (&ARGLIST[argn])->ae_fnum != curbuf->b_fnum + && !check_can_set_curbuf_forceit(eap->forceit)) + return; + setpcmark(); #ifdef FEAT_GUI need_mouse_correct = TRUE; #endif // split window or create new tab page first - if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) + if (is_split_cmd || cmdmod.cmod_tab != 0) { if (win_split(0, 0) == FAIL) return; diff --git a/src/buffer.c b/src/buffer.c index 8d62e64368..36396e8d28 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1370,6 +1370,13 @@ do_buffer_ext( if ((flags & DOBUF_NOPOPUP) && bt_popup(buf) && !bt_terminal(buf)) return OK; #endif + if ( + action == DOBUF_GOTO + && buf != curbuf + && !check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) ? TRUE : FALSE)) + // disallow navigating to another buffer when 'winfixbuf' is applied + return FAIL; + if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) { diff --git a/src/errors.h b/src/errors.h index dd2bc95b76..65ee4e826e 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3607,3 +3607,5 @@ EXTERN char e_wrong_number_of_characters_for_field_str[] INIT(= N_("E1511: Wrong number of characters for field \"%s\"")); EXTERN char e_wrong_character_width_for_field_str[] INIT(= N_("E1512: Wrong character width for field \"%s\"")); +EXTERN char e_winfixbuf_cannot_go_to_buffer[] + INIT(= N_("E1513: Cannot edit buffer. 'winfixbuf' is enabled")); diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 720e918bb4..a12d819b3f 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -2428,6 +2428,9 @@ getfile( int retval; char_u *free_me = NULL; + if (!check_can_set_curbuf_forceit(forceit)) + return GETFILE_ERROR; + if (text_locked()) return GETFILE_ERROR; if (curbuf_locked()) diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 4ae6cc2293..bd26e81dcc 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -521,7 +521,7 @@ EXCMD(CMD_doautoall, "doautoall", ex_doautoall, EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_drop, "drop", ex_drop, - EX_FILES|EX_CMDARG|EX_NEEDARG|EX_ARGOPT|EX_TRLBAR, + EX_BANG|EX_FILES|EX_CMDARG|EX_NEEDARG|EX_ARGOPT|EX_TRLBAR, ADDR_NONE), EXCMD(CMD_dsearch, "dsearch", ex_findpat, EX_BANG|EX_RANGE|EX_DFLALL|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK, diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 0bde73070e..c9834d2232 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -457,6 +457,31 @@ ex_listdo(exarg_T *eap) tabpage_T *tp; buf_T *buf = curbuf; int next_fnum = 0; + + if (curwin->w_p_wfb) + { + if ((eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) && !eap->forceit) + { + // Disallow :ldo if 'winfixbuf' is applied + semsg("%s", e_winfixbuf_cannot_go_to_buffer); + return; + } + + if (win_valid(prevwin)) + // Change the current window to another because 'winfixbuf' is enabled + curwin = prevwin; + else + { + // Split the window, which will be 'nowinfixbuf', and set curwin to that + exarg_T new_eap; + CLEAR_FIELD(new_eap); + new_eap.cmdidx = CMD_split; + new_eap.cmd = (char_u *)"split"; + new_eap.arg = (char_u *)""; + ex_splitview(&new_eap); + } + } + #if defined(FEAT_SYN_HL) char_u *save_ei = NULL; #endif diff --git a/src/ex_docmd.c b/src/ex_docmd.c index c18a9107ec..19b1d85c68 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7164,6 +7164,9 @@ ex_resize(exarg_T *eap) static void ex_find(exarg_T *eap) { + if (!check_can_set_curbuf_forceit(eap->forceit)) + return; + char_u *fname; int count; char_u *file_to_find = NULL; @@ -7245,6 +7248,14 @@ ex_open(exarg_T *eap) static void ex_edit(exarg_T *eap) { + // Exclude commands which keep the window's current buffer + if ( + eap->cmdidx != CMD_badd + && eap->cmdidx != CMD_balt + // All other commands must obey 'winfixbuf' / ! rules + && !check_can_set_curbuf_forceit(eap->forceit)) + return; + do_exedit(eap, NULL); } @@ -9031,7 +9042,7 @@ ex_checkpath(exarg_T *eap) { find_pattern_in_path(NULL, 0, 0, FALSE, FALSE, CHECK_PATH, 1L, eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW, - (linenr_T)1, (linenr_T)MAXLNUM); + (linenr_T)1, (linenr_T)MAXLNUM, eap->forceit); } #if defined(FEAT_QUICKFIX) @@ -9101,7 +9112,7 @@ ex_findpat(exarg_T *eap) find_pattern_in_path(eap->arg, 0, (int)STRLEN(eap->arg), whole, !eap->forceit, *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, - n, action, eap->line1, eap->line2); + n, action, eap->line1, eap->line2, eap->forceit); } #endif diff --git a/src/insexpand.c b/src/insexpand.c index 68e970a71a..0847b6c051 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -3407,7 +3407,7 @@ get_next_include_file_completion(int compl_type) (compl_type == CTRL_X_PATH_DEFINES && !(compl_cont_status & CONT_SOL)) ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND, - (linenr_T)1, (linenr_T)MAXLNUM); + (linenr_T)1, (linenr_T)MAXLNUM, FALSE); } #endif diff --git a/src/normal.c b/src/normal.c index 791b02f1cd..5ef3a9277c 100644 --- a/src/normal.c +++ b/src/normal.c @@ -4073,6 +4073,9 @@ nv_gotofile(cmdarg_T *cap) return; #endif + if (!check_can_set_curbuf_disabled()) + return; + ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) @@ -4475,7 +4478,8 @@ nv_brackets(cmdarg_T *cap) SAFE_isupper(cap->nchar) ? ACTION_SHOW_ALL : SAFE_islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO, cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : (linenr_T)1, - (linenr_T)MAXLNUM); + (linenr_T)MAXLNUM, + FALSE); vim_free(ptr); curwin->w_set_curswant = TRUE; } diff --git a/src/option.c b/src/option.c index dd3542f895..8123a2a2c6 100644 --- a/src/option.c +++ b/src/option.c @@ -6420,6 +6420,7 @@ get_varp(struct vimoption *p) #ifdef FEAT_LINEBREAK case PV_NUW: return (char_u *)&(curwin->w_p_nuw); #endif + case PV_WFB: return (char_u *)&(curwin->w_p_wfb); case PV_WFH: return (char_u *)&(curwin->w_p_wfh); case PV_WFW: return (char_u *)&(curwin->w_p_wfw); #if defined(FEAT_QUICKFIX) diff --git a/src/option.h b/src/option.h index 75940cce0a..bf889e47de 100644 --- a/src/option.h +++ b/src/option.h @@ -1309,6 +1309,7 @@ enum #ifdef FEAT_STL_OPT , WV_STL #endif + , WV_WFB , WV_WFH , WV_WFW , WV_WRAP diff --git a/src/optiondefs.h b/src/optiondefs.h index 1a09e1c7fb..4ee2e20de3 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -215,6 +215,7 @@ # define PV_STL OPT_BOTH(OPT_WIN(WV_STL)) #endif #define PV_UL OPT_BOTH(OPT_BUF(BV_UL)) +# define PV_WFB OPT_WIN(WV_WFB) # define PV_WFH OPT_WIN(WV_WFH) # define PV_WFW OPT_WIN(WV_WFW) #define PV_WRAP OPT_WIN(WV_WRAP) @@ -2850,6 +2851,9 @@ static struct vimoption options[] = {"window", "wi", P_NUM|P_VI_DEF, (char_u *)&p_window, PV_NONE, did_set_window, NULL, {(char_u *)0L, (char_u *)0L} SCTX_INIT}, + {"winfixbuf", "wfb", P_BOOL|P_VI_DEF|P_RWIN, + (char_u *)VAR_WIN, PV_WFB, NULL, NULL, + {(char_u *)FALSE, (char_u *)0L} SCTX_INIT}, {"winfixheight", "wfh", P_BOOL|P_VI_DEF|P_RSTAT, (char_u *)VAR_WIN, PV_WFH, NULL, NULL, {(char_u *)FALSE, (char_u *)0L} SCTX_INIT}, diff --git a/src/proto/search.pro b/src/proto/search.pro index 99e279dadf..5b2b889317 100644 --- a/src/proto/search.pro +++ b/src/proto/search.pro @@ -32,7 +32,7 @@ int check_linecomment(char_u *line); void showmatch(int c); int current_search(long count, int forward); int linewhite(linenr_T lnum); -void find_pattern_in_path(char_u *ptr, int dir, int len, int whole, int skip_comments, int type, long count, int action, linenr_T start_lnum, linenr_T end_lnum); +void find_pattern_in_path(char_u *ptr, int dir, int len, int whole, int skip_comments, int type, long count, int action, linenr_T start_lnum, linenr_T end_lnum, int forceit); spat_T *get_spat(int idx); int get_spat_last_idx(void); void f_searchcount(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/window.pro b/src/proto/window.pro index e5c03969fb..9e66db5a7f 100644 --- a/src/proto/window.pro +++ b/src/proto/window.pro @@ -1,4 +1,6 @@ /* window.c */ +int check_can_set_curbuf_disabled(void); +int check_can_set_curbuf_forceit(int forceit); int window_layout_locked(enum CMD_index cmd); win_T *prevwin_curwin(void); win_T *swbuf_goto_win_with_buf(buf_T *buf); diff --git a/src/quickfix.c b/src/quickfix.c index d8bcc1232a..1f4176fe54 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -3146,7 +3146,7 @@ qf_goto_win_with_qfl_file(int qf_fnum) // Didn't find it, go to the window before the quickfix // window, unless 'switchbuf' contains 'uselast': in this case we // try to jump to the previously used window first. - if ((swb_flags & SWB_USELAST) && win_valid(prevwin)) + if ((swb_flags & SWB_USELAST) && !prevwin->w_p_wfb && win_valid(prevwin)) win = prevwin; else if (altwin != NULL) win = altwin; @@ -3158,7 +3158,7 @@ qf_goto_win_with_qfl_file(int qf_fnum) } // Remember a usable window. - if (altwin == NULL && !win->w_p_pvw && bt_normal(win->w_buffer)) + if (altwin == NULL && !win->w_p_pvw && !win->w_p_wfb && bt_normal(win->w_buffer)) altwin = win; } @@ -3261,8 +3261,32 @@ qf_jump_edit_buffer( prev_winid == curwin->w_id ? curwin : NULL); } else + { + if (!forceit && curwin->w_p_wfb) + { + if (qi->qfl_type == QFLT_LOCATION) + { + // Location lists cannot split or reassign their window + // so 'winfixbuf' windows must fail + semsg("%s", e_winfixbuf_cannot_go_to_buffer); + return QF_ABORT; + } + + if (!win_valid(prevwin)) + { + // Split the window, which will be 'nowinfixbuf', and set curwin to that + exarg_T new_eap; + CLEAR_FIELD(new_eap); + new_eap.cmdidx = CMD_split; + new_eap.cmd = (char_u *)"split"; + new_eap.arg = (char_u *)""; + ex_splitview(&new_eap); + } + } + retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); + } // If a location list, check whether the associated window is still // present. @@ -4991,6 +5015,11 @@ qf_jump_first(qf_info_T *qi, int_u save_qfid, int forceit) if (qf_restore_list(qi, save_qfid) == FAIL) return; + + if (!check_can_set_curbuf_forceit(forceit)) + return; + + // Autocommands might have cleared the list, check for that. if (!qf_list_empty(qf_get_curlist(qi))) qf_jump(qi, 0, 0, forceit); @@ -5907,7 +5936,7 @@ ex_cfile(exarg_T *eap) // This function is used by the :cfile, :cgetfile and :caddfile // commands. - // :cfile always creates a new quickfix list and jumps to the + // :cfile always creates a new quickfix list and may jump to the // first error. // :cgetfile creates a new quickfix list but doesn't jump to the // first error. @@ -6497,6 +6526,9 @@ ex_vimgrep(exarg_T *eap) char_u *au_name = NULL; int status; + if (!check_can_set_curbuf_forceit(eap->forceit)) + return; + au_name = vgr_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, TRUE, curbuf)) @@ -6558,7 +6590,7 @@ ex_vimgrep(exarg_T *eap) goto theend; } - // Jump to first match. + // Jump to first match if the current window is not 'winfixbuf' if (!qf_list_empty(qf_get_curlist(qi))) { if ((args.flags & VGR_NOJUMP) == 0) diff --git a/src/search.c b/src/search.c index 1d0542b658..83aaf0ac31 100644 --- a/src/search.c +++ b/src/search.c @@ -3292,7 +3292,8 @@ find_pattern_in_path( long count, int action, // What to do when we find it linenr_T start_lnum, // first line to start searching - linenr_T end_lnum) // last line for searching + linenr_T end_lnum, // last line for searching + int forceit) // If true, always switch to the found path { SearchedFile *files; // Stack of included files SearchedFile *bigger; // When we need more space @@ -3829,7 +3830,7 @@ search_line: break; if (!GETFILE_SUCCESS(getfile( curwin_save->w_buffer->b_fnum, NULL, - NULL, TRUE, lnum, FALSE))) + NULL, TRUE, lnum, forceit))) break; // failed to jump to file } else @@ -3842,7 +3843,7 @@ search_line: { if (!GETFILE_SUCCESS(getfile( 0, files[depth].name, NULL, TRUE, - files[depth].lnum, FALSE))) + files[depth].lnum, forceit))) break; // failed to jump to file // autocommands may have changed the lnum, we don't // want that here diff --git a/src/structs.h b/src/structs.h index 5b88260f35..df2c005e3d 100644 --- a/src/structs.h +++ b/src/structs.h @@ -246,6 +246,8 @@ typedef struct long wo_nuw; # define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth' #endif + int wo_wfb; +#define w_p_wfb w_onebuf_opt.wo_wfb // 'winfixbuf' int wo_wfh; # define w_p_wfh w_onebuf_opt.wo_wfh // 'winfixheight' int wo_wfw; @@ -289,6 +289,9 @@ do_tag( static char_u **matches = NULL; static int flags; + if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) + return FALSE; + #ifdef FEAT_EVAL if (tfu_in_use) { @@ -3705,6 +3708,9 @@ jumpto_tag( size_t len; char_u *lbuf; + if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) + return FAIL; + // Make a copy of the line, it can become invalid when an autocommand calls // back here recursively. len = matching_line_len(lbuf_arg) + 1; diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 8dd04e79e3..d365dfc84f 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -325,6 +325,7 @@ NEW_TESTS = \ test_window_cmd \ test_window_id \ test_windows_home \ + test_winfixbuf \ test_wnext \ test_wordcount \ test_writefile \ diff --git a/src/testdir/test_winfixbuf.vim b/src/testdir/test_winfixbuf.vim new file mode 100644 index 0000000000..0b15983932 --- /dev/null +++ b/src/testdir/test_winfixbuf.vim @@ -0,0 +1,3131 @@ +" Test 'winfixbuf' + +source check.vim + +" Find the number of open windows in the current tab +func s:get_windows_count() + return tabpagewinnr(tabpagenr(), '$') +endfunc + +" Create some unnamed buffers. +func s:make_buffers_list() + enew + file first + let l:first = bufnr() + + enew + file middle + let l:middle = bufnr() + + enew + file last + let l:last = bufnr() + + set winfixbuf + + return [l:first, l:last] +endfunc + +" Create some unnamed buffers and add them to an args list +func s:make_args_list() + let [l:first, l:last] = s:make_buffers_list() + + args! first middle last + + return [l:first, l:last] +endfunc + +" Create two buffers and then set the window to 'winfixbuf' +func s:make_buffer_pairs(...) + let l:reversed = get(a:, 1, 0) + + if l:reversed == 1 + enew + file original + + set winfixbuf + + enew! + file other + let l:other = bufnr() + + return l:other + endif + + enew + file other + let l:other = bufnr() + + enew + file current + + set winfixbuf + + return l:other +endfunc + +" Create 3 quick buffers and set the window to 'winfixbuf' +func s:make_buffer_trio() + edit first + let l:first = bufnr() + edit second + let l:second = bufnr() + + set winfixbuf + + edit! third + let l:third = bufnr() + + execute ":buffer! " . l:second + + return [l:first, l:second, l:third] +endfunc + +" Create a location list with at least 2 entries + a 'winfixbuf' window. +func s:make_simple_location_list() + enew + file middle + let l:middle = bufnr() + call append(0, ["winfix search-term", "another line"]) + + enew! + file first + let l:first = bufnr() + call append(0, "first search-term") + + enew! + file last + let l:last = bufnr() + call append(0, "last search-term") + + call setloclist( + \ 0, + \ [ + \ { + \ "filename": "first", + \ "bufnr": l:first, + \ "lnum": 1, + \ }, + \ { + \ "filename": "middle", + \ "bufnr": l:middle, + \ "lnum": 1, + \ }, + \ { + \ "filename": "middle", + \ "bufnr": l:middle, + \ "lnum": 2, + \ }, + \ { + \ "filename": "last", + \ "bufnr": l:last, + \ "lnum": 1, + \ }, + \ ] + \) + + set winfixbuf + + return [l:first, l:middle, l:last] +endfunc + +" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window. +func s:make_simple_quickfix() + enew + file current + let l:current = bufnr() + call append(0, ["winfix search-term", "another line"]) + + enew! + file first + let l:first = bufnr() + call append(0, "first search-term") + + enew! + file last + let l:last = bufnr() + call append(0, "last search-term") + + call setqflist( + \ [ + \ { + \ "filename": "first", + \ "bufnr": l:first, + \ "lnum": 1, + \ }, + \ { + \ "filename": "current", + \ "bufnr": l:current, + \ "lnum": 1, + \ }, + \ { + \ "filename": "current", + \ "bufnr": l:current, + \ "lnum": 2, + \ }, + \ { + \ "filename": "last", + \ "bufnr": l:last, + \ "lnum": 1, + \ }, + \ ] + \) + + set winfixbuf + + return [l:current, l:last] +endfunc + +" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window. +func s:make_quickfix_windows() + let [l:current, _] = s:make_simple_quickfix() + execute "buffer! " . l:current + + split + let l:first_window = win_getid() + execute "normal \<C-w>j" + let l:winfix_window = win_getid() + + " Open the quickfix in a separate split and go to it + copen + let l:quickfix_window = win_getid() + + return [l:first_window, l:winfix_window, l:quickfix_window] +endfunc + +" Revert all changes that occurred in any past test +func s:reset_all_buffers() + %bwipeout! + set nowinfixbuf + + call setqflist([]) + + for l:window_info in getwininfo() + call setloclist(l:window_info["winid"], []) + endfor + + delmarks A-Z0-9 +endfunc + +" Find and set the first quickfix entry that points to `buffer` +func s:set_quickfix_by_buffer(buffer) + let l:index = 1 " quickfix indices start at 1 + for l:entry in getqflist() + if l:entry["bufnr"] == a:buffer + execute l:index . "cc" + + return + endif + + let l:index += 1 + endfor + + echoerr 'No quickfix entry matching "' . a:buffer . '" could be found.' +endfunc + +" Fail to call :Next on a 'winfixbuf' window unless :Next! is used. +func Test_Next() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("Next", "E1513:") + call assert_notequal(l:first, bufnr()) + + Next! + call assert_equal(l:first, bufnr()) +endfunc + +" Call :argdo and choose the next available 'nowinfixbuf' window. +func Test_argdo_choose_available_window() + call s:reset_all_buffers() + + let [_, l:last] = s:make_args_list() + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :argdo will first try the 'winfixbuf' window, pass over it, + " and prefer the other 'nowinfixbuf' window, instead. + " + " +-------------------+ + " | 'nowinfixbuf' | + " +-------------------+ + " | 'winfixbuf' | <-- Cursor is here + " +-------------------+ + split + let l:nowinfixbuf_window = win_getid() + " Move to the 'winfixbuf' window now + execute "normal \<C-w>j" + let l:winfixbuf_window = win_getid() + let l:expected_windows = s:get_windows_count() + + argdo echo '' + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:last, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :argdo and create a new split window if all available windows are 'winfixbuf'. +func Test_argdo_make_new_window() + call s:reset_all_buffers() + + let [l:first, l:last] = s:make_args_list() + let l:current = win_getid() + let l:current_windows = s:get_windows_count() + + argdo echo '' + call assert_notequal(l:current, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:first, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :argedit but :argedit! is allowed +func Test_argedit() + call s:reset_all_buffers() + + args! first middle last + enew + file first + let l:first = bufnr() + + enew + file middle + let l:middle = bufnr() + + enew + file last + let l:last = bufnr() + + set winfixbuf + + let l:current = bufnr() + call assert_fails("argedit first middle last", "E1513:") + call assert_equal(l:current, bufnr()) + + argedit! first middle last + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :arglocal but :arglocal! is allowed +func Test_arglocal() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + argglobal! other + execute "buffer! " . l:current + + call assert_fails("arglocal other", "E1513:") + call assert_equal(l:current, bufnr()) + + arglocal! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :argglobal but :argglobal! is allowed +func Test_argglobal() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("argglobal other", "E1513:") + call assert_equal(l:current, bufnr()) + + argglobal! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :args but :args! is allowed +func Test_args() + call s:reset_all_buffers() + + let [l:first, _] = s:make_buffers_list() + let l:current = bufnr() + + call assert_fails("args first middle last", "E1513:") + call assert_equal(l:current, bufnr()) + + args! first middle last + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :bNext but :bNext! is allowed +func Test_bNext() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + call assert_fails("bNext", "E1513:") + let l:current = bufnr() + + call assert_equal(l:current, bufnr()) + + bNext! + call assert_equal(l:other, bufnr()) +endfunc + +" Allow :badd because it doesn't actually change the current window's buffer +func Test_badd() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + let l:current = bufnr() + + badd other + call assert_equal(l:current, bufnr()) +endfunc + +" Allow :balt because it doesn't actually change the current window's buffer +func Test_balt() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + let l:current = bufnr() + + balt other + call assert_equal(l:current, bufnr()) +endfunc + +" Fail :bfirst but :bfirst! is allowed +func Test_bfirst() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("bfirst", "E1513:") + call assert_equal(l:current, bufnr()) + + bfirst! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :blast but :blast! is allowed +func Test_blast() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs(1) + bfirst! + let l:current = bufnr() + + call assert_fails("blast", "E1513:") + call assert_equal(l:current, bufnr()) + + blast! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :bmodified but :bmodified! is allowed +func Test_bmodified() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + execute "buffer! " . l:other + set modified + execute "buffer! " . l:current + + call assert_fails("bmodified", "E1513:") + call assert_equal(l:current, bufnr()) + + bmodified! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :bnext but :bnext! is allowed +func Test_bnext() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("bnext", "E1513:") + call assert_equal(l:current, bufnr()) + + bnext! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :bprevious but :bprevious! is allowed +func Test_bprevious() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("bprevious", "E1513:") + call assert_equal(l:current, bufnr()) + + bprevious! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :brewind but :brewind! is allowed +func Test_brewind() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("brewind", "E1513:") + call assert_equal(l:current, bufnr()) + + brewind! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :browse edit but :browse edit! is allowed +func Test_browse_edit_fail() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("browse edit other", "E1513:") + call assert_equal(l:current, bufnr()) + + browse edit! other + call assert_equal(l:other, bufnr()) +endfunc + +" Allow :browse w because it doesn't change the buffer in the current file +func Test_browse_edit_pass() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + browse write other + + call delete("other") |