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/testdir | |
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/testdir')
-rw-r--r-- | src/testdir/Make_all.mak | 1 | ||||
-rw-r--r-- | src/testdir/test_winfixbuf.vim | 3131 |
2 files changed, 3132 insertions, 0 deletions
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") +endfunc + +" Call :bufdo and choose the next available 'nowinfixbuf' window. +func Test_bufdo_choose_available_window() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :bufdo 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:current = bufnr() + let l:expected_windows = s:get_windows_count() + + call assert_notequal(l:current, l:other) + + bufdo echo '' + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_notequal(l:other, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :bufdo and create a new split window if all available windows are 'winfixbuf'. +func Test_bufdo_make_new_window() + call s:reset_all_buffers() + + let [l:first, l:last] = s:make_buffers_list() + execute "buffer! " . l:first + let l:current = win_getid() + let l:current_windows = s:get_windows_count() + + bufdo 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 :buffer but :buffer! is allowed +func Test_buffer() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("buffer " . l:other, "E1513:") + call assert_equal(l:current, bufnr()) + + execute "buffer! " . l:other + call assert_equal(l:other, bufnr()) +endfunc + +" Allow :buffer on a 'winfixbuf' window if there is no change in buffer +func Test_buffer_same_buffer() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + let l:current = bufnr() + + execute "buffer " . l:current + call assert_equal(l:current, bufnr()) + + execute "buffer! " . l:current + call assert_equal(l:current, bufnr()) +endfunc + +" Allow :cNext but the 'nowinfixbuf' window is selected, instead +func Test_cNext() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cNext` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cNext + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cNfile but the 'nowinfixbuf' window is selected, instead +func Test_cNfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cNfile` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cNfile + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :caddexpr because it doesn't change the current buffer +func Test_caddexpr() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file_path = tempname() + call writefile(["Error - bad-thing-found"], l:file_path) + execute "edit " . l:file_path + let l:file_buffer = bufnr() + let l:current = bufnr() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + + edit! other.unittest + + set winfixbuf + + execute "buffer! " . l:file_buffer + + execute 'caddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")' + call assert_equal(l:current, bufnr()) + + call delete(l:file_path) +endfunc + +" Fail :cbuffer but :cbuffer! is allowed +func Test_cbuffer() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file_path = tempname() + call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path) + execute "edit " . l:file_path + let l:file_buffer = bufnr() + let l:current = bufnr() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + + edit! other.unittest + + set winfixbuf + + execute "buffer! " . l:file_buffer + + call assert_fails("cbuffer " . l:file_buffer) + call assert_equal(l:current, bufnr()) + + execute "cbuffer! " . l:file_buffer + call assert_equal("first.unittest", expand("%:t")) + + call delete(l:file_path) +endfunc + +" Allow :cc but the 'nowinfixbuf' window is selected, instead +func Test_cc() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + " Go up one line in the quickfix window to an quickfix entry that doesn't + " point to a winfixbuf buffer + normal k + " Attempt to make the previous window, winfixbuf buffer, to go to the + " non-winfixbuf quickfix entry + .cc + + " Confirm that :.cc did not change the winfixbuf-enabled window + call assert_equal(l:first_window, win_getid()) +endfunc + +" Call :cdo and choose the next available 'nowinfixbuf' window. +func Test_cdo_choose_available_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :cdo 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() + + cdo echo '' + + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :cdo and create a new split window if all available windows are 'winfixbuf'. +func Test_cdo_make_new_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current_buffer, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current_buffer + + let l:current_window = win_getid() + let l:current_windows = s:get_windows_count() + + cdo echo '' + call assert_notequal(l:current_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current_buffer, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :cexpr but :cexpr! is allowed +func Test_cexpr() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file = tempname() + let l:entry = '["' . l:file . ':1:bar"]' + let l:current = bufnr() + + set winfixbuf + + call assert_fails("cexpr " . l:entry) + call assert_equal(l:current, bufnr()) + + execute "cexpr! " . l:entry + call assert_equal(fnamemodify(l:file, ":t"), expand("%:t")) +endfunc + +" Call :cfdo and choose the next available 'nowinfixbuf' window. +func Test_cfdo_choose_available_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :cfdo 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() + + cfdo echo '' + + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :cfdo and create a new split window if all available windows are 'winfixbuf'. +func Test_cfdo_make_new_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current_buffer, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current_buffer + + let l:current_window = win_getid() + let l:current_windows = s:get_windows_count() + + cfdo echo '' + call assert_notequal(l:current_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current_buffer, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :cfile but :cfile! is allowed +func Test_cfile() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + write + let l:first = bufnr() + + edit! second.unittest + call append(0, ["some-search-term"]) + write + + let l:file = tempname() + call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file) + + let l:current = bufnr() + + set winfixbuf + + call assert_fails(":cfile " . l:file) + call assert_equal(l:current, bufnr()) + + execute ":cfile! " . l:file + call assert_equal(l:first, bufnr()) + + call delete(l:file) + call delete("first.unittest") + call delete("second.unittest") +endfunc + +" Allow :cfirst but the 'nowinfixbuf' window is selected, instead +func Test_cfirst() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cfirst` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cfirst + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :clast but the 'nowinfixbuf' window is selected, instead +func Test_clast() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:clast` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + clast + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cnext but the 'nowinfixbuf' window is selected, instead +" Make sure no new windows are created and previous windows are reused +func Test_cnext() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + let l:expected = s:get_windows_count() + + " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + cnext! + call assert_equal(l:expected, s:get_windows_count()) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cnext + call assert_equal(l:first_window, win_getid()) + call assert_equal(l:expected, s:get_windows_count()) +endfunc + +" Make sure :cnext creates a split window if no previous window exists +func Test_cnext_no_previous_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, _] = s:make_simple_quickfix() + execute "buffer! " . l:current + + let l:expected = s:get_windows_count() + + " Open the quickfix in a separate split and go to it + copen + + call assert_equal(l:expected + 1, s:get_windows_count()) +endfunc + +" Allow :cnext and create a 'nowinfixbuf' window if none exists +func Test_cnext_make_new_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, _] = s:make_simple_quickfix() + let l:current = win_getid() + + cfirst! + + let l:windows = s:get_windows_count() + let l:expected = l:windows + 1 " We're about to create a new split window + + cnext + call assert_equal(l:expected, s:get_windows_count()) + + cnext! + call assert_equal(l:expected, s:get_windows_count()) +endfunc + +" Allow :cprevious but the 'nowinfixbuf' window is selected, instead +func Test_cprevious() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cprevious` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cprevious + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cnfile but the 'nowinfixbuf' window is selected, instead +func Test_cnfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cnfile` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cnfile + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cpfile but the 'nowinfixbuf' window is selected, instead +func Test_cpfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cpfile` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cpfile + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :crewind but the 'nowinfixbuf' window is selected, instead +func Test_crewind() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:crewind` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + crewind + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow <C-w>f because it opens in a new split +func Test_ctrl_w_f() + call s:reset_all_buffers() + + enew + let l:file_name = tempname() + call writefile([], l:file_name) + let l:file_buffer = bufnr() + + enew + file other + let l:other_buffer = bufnr() + + set winfixbuf + + call setline(1, l:file_name) + let l:current_windows = s:get_windows_count() + execute "normal \<C-w>f" + + call assert_equal(l:current_windows + 1, s:get_windows_count()) + + call delete(l:file_name) +endfunc + +" Fail :djump but :djump! is allowed +func Test_djump() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile(["min(1, 12);", + \ '#include "' . l:include_file . '"' + \ ], + \ "main.c") + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file) + edit main.c + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("djump 1 /min/", "E1513:") + call assert_equal(l:current, bufnr()) + + djump! 1 /min/ + call assert_notequal(l:current, bufnr()) + + call delete("main.c") + call delete(l:include_file) +endfunc + +" Fail :drop but :drop! is allowed +func Test_drop() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("drop other", "E1513:") + call assert_equal(l:current, bufnr()) + + drop! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :edit but :edit! is allowed +func Test_edit() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("edit other", "E1513:") + call assert_equal(l:current, bufnr()) + + edit! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :enew but :enew! is allowed +func Test_enew() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("enew", "E1513:") + call assert_equal(l:current, bufnr()) + + enew! + call assert_notequal(l:other, bufnr()) + call assert_notequal(3, bufnr()) +endfunc + +" Fail :ex but :ex! is allowed +func Test_ex() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("ex other", "E1513:") + call assert_equal(l:current, bufnr()) + + ex! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :find but :find! is allowed +func Test_find() + call s:reset_all_buffers() + + let l:current = bufnr() + let l:file = tempname() + call writefile([], l:file) + let l:directory = fnamemodify(l:file, ":p:h") + let l:name = fnamemodify(l:file, ":p:t") + + let l:original_path = &path + execute "set path=" . l:directory + + set winfixbuf + + call assert_fails("execute 'find " . l:name . "'", "E1513:") + call assert_equal(l:current, bufnr()) + + execute "find! " . l:name + call assert_equal(l:file, expand("%:p")) + + execute "set path=" . l:original_path + call delete(l:file) +endfunc + +" Fail :first but :first! is allowed +func Test_first() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("first", "E1513:") + call assert_notequal(l:first, bufnr()) + + first! + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :grep but :grep! is allowed +func Test_grep() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term"]) + write |