blob: 462554b1127b760586ca962d49888b834cb9c47c [file] [log] [blame]
Colin Kennedy21570352024-03-03 16:16:47 +01001" Test 'winfixbuf'
2
3source check.vim
4
5" Find the number of open windows in the current tab
6func s:get_windows_count()
7 return tabpagewinnr(tabpagenr(), '$')
8endfunc
9
10" Create some unnamed buffers.
11func s:make_buffers_list()
12 enew
13 file first
14 let l:first = bufnr()
15
16 enew
17 file middle
18 let l:middle = bufnr()
19
20 enew
21 file last
22 let l:last = bufnr()
23
24 set winfixbuf
25
26 return [l:first, l:last]
27endfunc
28
29" Create some unnamed buffers and add them to an args list
30func s:make_args_list()
31 let [l:first, l:last] = s:make_buffers_list()
32
33 args! first middle last
34
35 return [l:first, l:last]
36endfunc
37
38" Create two buffers and then set the window to 'winfixbuf'
39func s:make_buffer_pairs(...)
40 let l:reversed = get(a:, 1, 0)
41
42 if l:reversed == 1
43 enew
44 file original
45
46 set winfixbuf
47
48 enew!
49 file other
50 let l:other = bufnr()
51
52 return l:other
53 endif
54
55 enew
56 file other
57 let l:other = bufnr()
58
59 enew
60 file current
61
62 set winfixbuf
63
64 return l:other
65endfunc
66
67" Create 3 quick buffers and set the window to 'winfixbuf'
68func s:make_buffer_trio()
69 edit first
70 let l:first = bufnr()
71 edit second
72 let l:second = bufnr()
73
74 set winfixbuf
75
76 edit! third
77 let l:third = bufnr()
78
79 execute ":buffer! " . l:second
80
81 return [l:first, l:second, l:third]
82endfunc
83
84" Create a location list with at least 2 entries + a 'winfixbuf' window.
85func s:make_simple_location_list()
86 enew
87 file middle
88 let l:middle = bufnr()
89 call append(0, ["winfix search-term", "another line"])
90
91 enew!
92 file first
93 let l:first = bufnr()
94 call append(0, "first search-term")
95
96 enew!
97 file last
98 let l:last = bufnr()
99 call append(0, "last search-term")
100
101 call setloclist(
102 \ 0,
103 \ [
104 \ {
105 \ "filename": "first",
106 \ "bufnr": l:first,
107 \ "lnum": 1,
108 \ },
109 \ {
110 \ "filename": "middle",
111 \ "bufnr": l:middle,
112 \ "lnum": 1,
113 \ },
114 \ {
115 \ "filename": "middle",
116 \ "bufnr": l:middle,
117 \ "lnum": 2,
118 \ },
119 \ {
120 \ "filename": "last",
121 \ "bufnr": l:last,
122 \ "lnum": 1,
123 \ },
124 \ ]
125 \)
126
127 set winfixbuf
128
129 return [l:first, l:middle, l:last]
130endfunc
131
132" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window.
133func s:make_simple_quickfix()
134 enew
135 file current
136 let l:current = bufnr()
137 call append(0, ["winfix search-term", "another line"])
138
139 enew!
140 file first
141 let l:first = bufnr()
142 call append(0, "first search-term")
143
144 enew!
145 file last
146 let l:last = bufnr()
147 call append(0, "last search-term")
148
149 call setqflist(
150 \ [
151 \ {
152 \ "filename": "first",
153 \ "bufnr": l:first,
154 \ "lnum": 1,
155 \ },
156 \ {
157 \ "filename": "current",
158 \ "bufnr": l:current,
159 \ "lnum": 1,
160 \ },
161 \ {
162 \ "filename": "current",
163 \ "bufnr": l:current,
164 \ "lnum": 2,
165 \ },
166 \ {
167 \ "filename": "last",
168 \ "bufnr": l:last,
169 \ "lnum": 1,
170 \ },
171 \ ]
172 \)
173
174 set winfixbuf
175
176 return [l:current, l:last]
177endfunc
178
179" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window.
180func s:make_quickfix_windows()
181 let [l:current, _] = s:make_simple_quickfix()
182 execute "buffer! " . l:current
183
184 split
185 let l:first_window = win_getid()
186 execute "normal \<C-w>j"
187 let l:winfix_window = win_getid()
188
189 " Open the quickfix in a separate split and go to it
190 copen
191 let l:quickfix_window = win_getid()
192
193 return [l:first_window, l:winfix_window, l:quickfix_window]
194endfunc
195
196" Revert all changes that occurred in any past test
197func s:reset_all_buffers()
198 %bwipeout!
199 set nowinfixbuf
200
201 call setqflist([])
202
203 for l:window_info in getwininfo()
204 call setloclist(l:window_info["winid"], [])
205 endfor
206
207 delmarks A-Z0-9
208endfunc
209
210" Find and set the first quickfix entry that points to `buffer`
211func s:set_quickfix_by_buffer(buffer)
212 let l:index = 1 " quickfix indices start at 1
213 for l:entry in getqflist()
214 if l:entry["bufnr"] == a:buffer
215 execute l:index . "cc"
216
217 return
218 endif
219
220 let l:index += 1
221 endfor
222
223 echoerr 'No quickfix entry matching "' . a:buffer . '" could be found.'
224endfunc
225
226" Fail to call :Next on a 'winfixbuf' window unless :Next! is used.
227func Test_Next()
228 call s:reset_all_buffers()
229
230 let [l:first, _] = s:make_args_list()
231 next!
232
233 call assert_fails("Next", "E1513:")
234 call assert_notequal(l:first, bufnr())
235
236 Next!
237 call assert_equal(l:first, bufnr())
238endfunc
239
240" Call :argdo and choose the next available 'nowinfixbuf' window.
241func Test_argdo_choose_available_window()
242 call s:reset_all_buffers()
243
244 let [_, l:last] = s:make_args_list()
245
246 " Make a split window that is 'nowinfixbuf' but make it the second-to-last
247 " window so that :argdo will first try the 'winfixbuf' window, pass over it,
248 " and prefer the other 'nowinfixbuf' window, instead.
249 "
250 " +-------------------+
251 " | 'nowinfixbuf' |
252 " +-------------------+
253 " | 'winfixbuf' | <-- Cursor is here
254 " +-------------------+
255 split
256 let l:nowinfixbuf_window = win_getid()
257 " Move to the 'winfixbuf' window now
258 execute "normal \<C-w>j"
259 let l:winfixbuf_window = win_getid()
260 let l:expected_windows = s:get_windows_count()
261
262 argdo echo ''
263 call assert_equal(l:nowinfixbuf_window, win_getid())
264 call assert_equal(l:last, bufnr())
265 call assert_equal(l:expected_windows, s:get_windows_count())
266endfunc
267
268" Call :argdo and create a new split window if all available windows are 'winfixbuf'.
269func Test_argdo_make_new_window()
270 call s:reset_all_buffers()
271
272 let [l:first, l:last] = s:make_args_list()
273 let l:current = win_getid()
274 let l:current_windows = s:get_windows_count()
275
276 argdo echo ''
277 call assert_notequal(l:current, win_getid())
278 call assert_equal(l:last, bufnr())
279 execute "normal \<C-w>j"
280 call assert_equal(l:first, bufnr())
281 call assert_equal(l:current_windows + 1, s:get_windows_count())
282endfunc
283
284" Fail :argedit but :argedit! is allowed
285func Test_argedit()
286 call s:reset_all_buffers()
287
288 args! first middle last
289 enew
290 file first
291 let l:first = bufnr()
292
293 enew
294 file middle
295 let l:middle = bufnr()
296
297 enew
298 file last
299 let l:last = bufnr()
300
301 set winfixbuf
302
303 let l:current = bufnr()
304 call assert_fails("argedit first middle last", "E1513:")
305 call assert_equal(l:current, bufnr())
306
307 argedit! first middle last
308 call assert_equal(l:first, bufnr())
309endfunc
310
311" Fail :arglocal but :arglocal! is allowed
312func Test_arglocal()
313 call s:reset_all_buffers()
314
315 let l:other = s:make_buffer_pairs()
316 let l:current = bufnr()
317 argglobal! other
318 execute "buffer! " . l:current
319
320 call assert_fails("arglocal other", "E1513:")
321 call assert_equal(l:current, bufnr())
322
323 arglocal! other
324 call assert_equal(l:other, bufnr())
325endfunc
326
327" Fail :argglobal but :argglobal! is allowed
328func Test_argglobal()
329 call s:reset_all_buffers()
330
331 let l:other = s:make_buffer_pairs()
332 let l:current = bufnr()
333
334 call assert_fails("argglobal other", "E1513:")
335 call assert_equal(l:current, bufnr())
336
337 argglobal! other
338 call assert_equal(l:other, bufnr())
339endfunc
340
341" Fail :args but :args! is allowed
342func Test_args()
343 call s:reset_all_buffers()
344
345 let [l:first, _] = s:make_buffers_list()
346 let l:current = bufnr()
347
348 call assert_fails("args first middle last", "E1513:")
349 call assert_equal(l:current, bufnr())
350
351 args! first middle last
352 call assert_equal(l:first, bufnr())
353endfunc
354
355" Fail :bNext but :bNext! is allowed
356func Test_bNext()
357 call s:reset_all_buffers()
358
359 let l:other = s:make_buffer_pairs()
360 call assert_fails("bNext", "E1513:")
361 let l:current = bufnr()
362
363 call assert_equal(l:current, bufnr())
364
365 bNext!
366 call assert_equal(l:other, bufnr())
367endfunc
368
369" Allow :badd because it doesn't actually change the current window's buffer
370func Test_badd()
371 call s:reset_all_buffers()
372
373 call s:make_buffer_pairs()
374 let l:current = bufnr()
375
376 badd other
377 call assert_equal(l:current, bufnr())
378endfunc
379
380" Allow :balt because it doesn't actually change the current window's buffer
381func Test_balt()
382 call s:reset_all_buffers()
383
384 call s:make_buffer_pairs()
385 let l:current = bufnr()
386
387 balt other
388 call assert_equal(l:current, bufnr())
389endfunc
390
391" Fail :bfirst but :bfirst! is allowed
392func Test_bfirst()
393 call s:reset_all_buffers()
394
395 let l:other = s:make_buffer_pairs()
396 let l:current = bufnr()
397
398 call assert_fails("bfirst", "E1513:")
399 call assert_equal(l:current, bufnr())
400
401 bfirst!
402 call assert_equal(l:other, bufnr())
403endfunc
404
405" Fail :blast but :blast! is allowed
406func Test_blast()
407 call s:reset_all_buffers()
408
409 let l:other = s:make_buffer_pairs(1)
410 bfirst!
411 let l:current = bufnr()
412
413 call assert_fails("blast", "E1513:")
414 call assert_equal(l:current, bufnr())
415
416 blast!
417 call assert_equal(l:other, bufnr())
418endfunc
419
420" Fail :bmodified but :bmodified! is allowed
421func Test_bmodified()
422 call s:reset_all_buffers()
423
424 let l:other = s:make_buffer_pairs()
425 let l:current = bufnr()
426
427 execute "buffer! " . l:other
428 set modified
429 execute "buffer! " . l:current
430
431 call assert_fails("bmodified", "E1513:")
432 call assert_equal(l:current, bufnr())
433
434 bmodified!
435 call assert_equal(l:other, bufnr())
436endfunc
437
438" Fail :bnext but :bnext! is allowed
439func Test_bnext()
440 call s:reset_all_buffers()
441
442 let l:other = s:make_buffer_pairs()
443 let l:current = bufnr()
444
445 call assert_fails("bnext", "E1513:")
446 call assert_equal(l:current, bufnr())
447
448 bnext!
449 call assert_equal(l:other, bufnr())
450endfunc
451
452" Fail :bprevious but :bprevious! is allowed
453func Test_bprevious()
454 call s:reset_all_buffers()
455
456 let l:other = s:make_buffer_pairs()
457 let l:current = bufnr()
458
459 call assert_fails("bprevious", "E1513:")
460 call assert_equal(l:current, bufnr())
461
462 bprevious!
463 call assert_equal(l:other, bufnr())
464endfunc
465
466" Fail :brewind but :brewind! is allowed
467func Test_brewind()
468 call s:reset_all_buffers()
469
470 let l:other = s:make_buffer_pairs()
471 let l:current = bufnr()
472
473 call assert_fails("brewind", "E1513:")
474 call assert_equal(l:current, bufnr())
475
476 brewind!
477 call assert_equal(l:other, bufnr())
478endfunc
479
480" Fail :browse edit but :browse edit! is allowed
481func Test_browse_edit_fail()
Sean Dewar769eb2d2024-03-07 21:37:50 +0100482 " A GUI dialog may stall the test.
483 CheckNotGui
484
Colin Kennedy21570352024-03-03 16:16:47 +0100485 call s:reset_all_buffers()
486
487 let l:other = s:make_buffer_pairs()
488 let l:current = bufnr()
489
490 call assert_fails("browse edit other", "E1513:")
491 call assert_equal(l:current, bufnr())
492
Sean Dewar769eb2d2024-03-07 21:37:50 +0100493 try
494 browse edit! other
495 call assert_equal(l:other, bufnr())
Christian Brabandt0a32b882024-03-13 20:59:27 +0100496 catch /^Vim\%((\a\+)\)\=:E338:/
Sean Dewar769eb2d2024-03-07 21:37:50 +0100497 " Ignore E338, which occurs if console Vim is built with +browse.
498 " Console Vim without +browse will treat this as a regular :edit.
499 endtry
Colin Kennedy21570352024-03-03 16:16:47 +0100500endfunc
501
502" Allow :browse w because it doesn't change the buffer in the current file
503func Test_browse_edit_pass()
Sean Dewar769eb2d2024-03-07 21:37:50 +0100504 " A GUI dialog may stall the test.
505 CheckNotGui
506
Colin Kennedy21570352024-03-03 16:16:47 +0100507 call s:reset_all_buffers()
508
509 let l:other = s:make_buffer_pairs()
510 let l:current = bufnr()
511
Sean Dewar769eb2d2024-03-07 21:37:50 +0100512 try
513 browse write other
Christian Brabandt0a32b882024-03-13 20:59:27 +0100514 catch /^Vim\%((\a\+)\)\=:E338:/
Sean Dewar769eb2d2024-03-07 21:37:50 +0100515 " Ignore E338, which occurs if console Vim is built with +browse.
516 " Console Vim without +browse will treat this as a regular :write.
517 endtry
Colin Kennedy21570352024-03-03 16:16:47 +0100518
519 call delete("other")
520endfunc
521
522" Call :bufdo and choose the next available 'nowinfixbuf' window.
523func Test_bufdo_choose_available_window()
524 call s:reset_all_buffers()
525
526 let l:other = s:make_buffer_pairs()
527
528 " Make a split window that is 'nowinfixbuf' but make it the second-to-last
529 " window so that :bufdo will first try the 'winfixbuf' window, pass over it,
530 " and prefer the other 'nowinfixbuf' window, instead.
531 "
532 " +-------------------+
533 " | 'nowinfixbuf' |
534 " +-------------------+
535 " | 'winfixbuf' | <-- Cursor is here
536 " +-------------------+
537 split
538 let l:nowinfixbuf_window = win_getid()
539 " Move to the 'winfixbuf' window now
540 execute "normal \<C-w>j"
541 let l:winfixbuf_window = win_getid()
542
543 let l:current = bufnr()
544 let l:expected_windows = s:get_windows_count()
545
546 call assert_notequal(l:current, l:other)
547
548 bufdo echo ''
549 call assert_equal(l:nowinfixbuf_window, win_getid())
550 call assert_notequal(l:other, bufnr())
551 call assert_equal(l:expected_windows, s:get_windows_count())
552endfunc
553
554" Call :bufdo and create a new split window if all available windows are 'winfixbuf'.
555func Test_bufdo_make_new_window()
556 call s:reset_all_buffers()
557
558 let [l:first, l:last] = s:make_buffers_list()
559 execute "buffer! " . l:first
560 let l:current = win_getid()
561 let l:current_windows = s:get_windows_count()
562
563 bufdo echo ''
564 call assert_notequal(l:current, win_getid())
565 call assert_equal(l:last, bufnr())
566 execute "normal \<C-w>j"
567 call assert_equal(l:first, bufnr())
568 call assert_equal(l:current_windows + 1, s:get_windows_count())
569endfunc
570
571" Fail :buffer but :buffer! is allowed
572func Test_buffer()
573 call s:reset_all_buffers()
574
575 let l:other = s:make_buffer_pairs()
576 let l:current = bufnr()
577
578 call assert_fails("buffer " . l:other, "E1513:")
579 call assert_equal(l:current, bufnr())
580
581 execute "buffer! " . l:other
582 call assert_equal(l:other, bufnr())
583endfunc
584
585" Allow :buffer on a 'winfixbuf' window if there is no change in buffer
586func Test_buffer_same_buffer()
587 call s:reset_all_buffers()
588
589 call s:make_buffer_pairs()
590 let l:current = bufnr()
591
592 execute "buffer " . l:current
593 call assert_equal(l:current, bufnr())
594
595 execute "buffer! " . l:current
596 call assert_equal(l:current, bufnr())
597endfunc
598
599" Allow :cNext but the 'nowinfixbuf' window is selected, instead
600func Test_cNext()
601 CheckFeature quickfix
602 call s:reset_all_buffers()
603
604 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
605
606 " The call to `:cNext` succeeds but it selects the window with 'nowinfixbuf' instead
607 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
608
609 " Make sure the previous window has 'winfixbuf' so we can test that our
610 " "skip 'winfixbuf' window" logic works.
611 call win_gotoid(l:winfix_window)
612 call win_gotoid(l:quickfix_window)
613
614 cNext
615 call assert_equal(l:first_window, win_getid())
616endfunc
617
618" Allow :cNfile but the 'nowinfixbuf' window is selected, instead
619func Test_cNfile()
620 CheckFeature quickfix
621 call s:reset_all_buffers()
622
623 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
624
625 " The call to `:cNfile` succeeds but it selects the window with 'nowinfixbuf' instead
626 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
627 cnext!
628
629 " Make sure the previous window has 'winfixbuf' so we can test that our
630 " "skip 'winfixbuf' window" logic works.
631 call win_gotoid(l:winfix_window)
632 call win_gotoid(l:quickfix_window)
633
634 cNfile
635 call assert_equal(l:first_window, win_getid())
636endfunc
637
638" Allow :caddexpr because it doesn't change the current buffer
639func Test_caddexpr()
640 CheckFeature quickfix
641 call s:reset_all_buffers()
642
643 let l:file_path = tempname()
644 call writefile(["Error - bad-thing-found"], l:file_path)
645 execute "edit " . l:file_path
646 let l:file_buffer = bufnr()
647 let l:current = bufnr()
648
649 edit first.unittest
650 call append(0, ["some-search-term bad-thing-found"])
651
652 edit! other.unittest
653
654 set winfixbuf
655
656 execute "buffer! " . l:file_buffer
657
658 execute 'caddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")'
659 call assert_equal(l:current, bufnr())
660
661 call delete(l:file_path)
662endfunc
663
664" Fail :cbuffer but :cbuffer! is allowed
665func Test_cbuffer()
666 CheckFeature quickfix
667 call s:reset_all_buffers()
668
669 let l:file_path = tempname()
670 call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path)
671 execute "edit " . l:file_path
672 let l:file_buffer = bufnr()
673 let l:current = bufnr()
674
675 edit first.unittest
676 call append(0, ["some-search-term bad-thing-found"])
677
678 edit! other.unittest
679
680 set winfixbuf
681
682 execute "buffer! " . l:file_buffer
683
684 call assert_fails("cbuffer " . l:file_buffer)
685 call assert_equal(l:current, bufnr())
686
687 execute "cbuffer! " . l:file_buffer
688 call assert_equal("first.unittest", expand("%:t"))
689
690 call delete(l:file_path)
691endfunc
692
693" Allow :cc but the 'nowinfixbuf' window is selected, instead
694func Test_cc()
695 CheckFeature quickfix
696 call s:reset_all_buffers()
697
698 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
699
700 " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead
701 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
702
703 " Make sure the previous window has 'winfixbuf' so we can test that our
704 " "skip 'winfixbuf' window" logic works.
705 call win_gotoid(l:winfix_window)
706 call win_gotoid(l:quickfix_window)
707 " Go up one line in the quickfix window to an quickfix entry that doesn't
708 " point to a winfixbuf buffer
709 normal k
710 " Attempt to make the previous window, winfixbuf buffer, to go to the
711 " non-winfixbuf quickfix entry
712 .cc
713
714 " Confirm that :.cc did not change the winfixbuf-enabled window
715 call assert_equal(l:first_window, win_getid())
716endfunc
717
718" Call :cdo and choose the next available 'nowinfixbuf' window.
719func Test_cdo_choose_available_window()
720 CheckFeature quickfix
721 call s:reset_all_buffers()
722
723 let [l:current, l:last] = s:make_simple_quickfix()
724 execute "buffer! " . l:current
725
726 " Make a split window that is 'nowinfixbuf' but make it the second-to-last
727 " window so that :cdo will first try the 'winfixbuf' window, pass over it,
728 " and prefer the other 'nowinfixbuf' window, instead.
729 "
730 " +-------------------+
731 " | 'nowinfixbuf' |
732 " +-------------------+
733 " | 'winfixbuf' | <-- Cursor is here
734 " +-------------------+
735 split
736 let l:nowinfixbuf_window = win_getid()
737 " Move to the 'winfixbuf' window now
738 execute "normal \<C-w>j"
739 let l:winfixbuf_window = win_getid()
740 let l:expected_windows = s:get_windows_count()
741
742 cdo echo ''
743
744 call assert_equal(l:nowinfixbuf_window, win_getid())
745 call assert_equal(l:last, bufnr())
746 execute "normal \<C-w>j"
747 call assert_equal(l:current, bufnr())
748 call assert_equal(l:expected_windows, s:get_windows_count())
749endfunc
750
751" Call :cdo and create a new split window if all available windows are 'winfixbuf'.
752func Test_cdo_make_new_window()
753 CheckFeature quickfix
754 call s:reset_all_buffers()
755
756 let [l:current_buffer, l:last] = s:make_simple_quickfix()
757 execute "buffer! " . l:current_buffer
758
759 let l:current_window = win_getid()
760 let l:current_windows = s:get_windows_count()
761
762 cdo echo ''
763 call assert_notequal(l:current_window, win_getid())
764 call assert_equal(l:last, bufnr())
765 execute "normal \<C-w>j"
766 call assert_equal(l:current_buffer, bufnr())
767 call assert_equal(l:current_windows + 1, s:get_windows_count())
768endfunc
769
770" Fail :cexpr but :cexpr! is allowed
771func Test_cexpr()
772 CheckFeature quickfix
773 call s:reset_all_buffers()
774
775 let l:file = tempname()
776 let l:entry = '["' . l:file . ':1:bar"]'
777 let l:current = bufnr()
778
779 set winfixbuf
780
781 call assert_fails("cexpr " . l:entry)
782 call assert_equal(l:current, bufnr())
783
784 execute "cexpr! " . l:entry
785 call assert_equal(fnamemodify(l:file, ":t"), expand("%:t"))
786endfunc
787
788" Call :cfdo and choose the next available 'nowinfixbuf' window.
789func Test_cfdo_choose_available_window()
790 CheckFeature quickfix
791 call s:reset_all_buffers()
792
793 let [l:current, l:last] = s:make_simple_quickfix()
794 execute "buffer! " . l:current
795
796 " Make a split window that is 'nowinfixbuf' but make it the second-to-last
797 " window so that :cfdo will first try the 'winfixbuf' window, pass over it,
798 " and prefer the other 'nowinfixbuf' window, instead.
799 "
800 " +-------------------+
801 " | 'nowinfixbuf' |
802 " +-------------------+
803 " | 'winfixbuf' | <-- Cursor is here
804 " +-------------------+
805 split
806 let l:nowinfixbuf_window = win_getid()
807 " Move to the 'winfixbuf' window now
808 execute "normal \<C-w>j"
809 let l:winfixbuf_window = win_getid()
810 let l:expected_windows = s:get_windows_count()
811
812 cfdo echo ''
813
814 call assert_equal(l:nowinfixbuf_window, win_getid())
815 call assert_equal(l:last, bufnr())
816 execute "normal \<C-w>j"
817 call assert_equal(l:current, bufnr())
818 call assert_equal(l:expected_windows, s:get_windows_count())
819endfunc
820
821" Call :cfdo and create a new split window if all available windows are 'winfixbuf'.
822func Test_cfdo_make_new_window()
823 CheckFeature quickfix
824 call s:reset_all_buffers()
825
826 let [l:current_buffer, l:last] = s:make_simple_quickfix()
827 execute "buffer! " . l:current_buffer
828
829 let l:current_window = win_getid()
830 let l:current_windows = s:get_windows_count()
831
832 cfdo echo ''
833 call assert_notequal(l:current_window, win_getid())
834 call assert_equal(l:last, bufnr())
835 execute "normal \<C-w>j"
836 call assert_equal(l:current_buffer, bufnr())
837 call assert_equal(l:current_windows + 1, s:get_windows_count())
838endfunc
839
840" Fail :cfile but :cfile! is allowed
841func Test_cfile()
842 CheckFeature quickfix
843 call s:reset_all_buffers()
844
845 edit first.unittest
846 call append(0, ["some-search-term bad-thing-found"])
847 write
848 let l:first = bufnr()
849
850 edit! second.unittest
851 call append(0, ["some-search-term"])
852 write
853
854 let l:file = tempname()
855 call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file)
856
857 let l:current = bufnr()
858
859 set winfixbuf
860
861 call assert_fails(":cfile " . l:file)
862 call assert_equal(l:current, bufnr())
863
864 execute ":cfile! " . l:file
865 call assert_equal(l:first, bufnr())
866
867 call delete(l:file)
868 call delete("first.unittest")
869 call delete("second.unittest")
870endfunc
871
872" Allow :cfirst but the 'nowinfixbuf' window is selected, instead
873func Test_cfirst()
874 CheckFeature quickfix
875 call s:reset_all_buffers()
876
877 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
878
879 " The call to `:cfirst` succeeds but it selects the window with 'nowinfixbuf' instead
880 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
881
882 " Make sure the previous window has 'winfixbuf' so we can test that our
883 " "skip 'winfixbuf' window" logic works.
884 call win_gotoid(l:winfix_window)
885 call win_gotoid(l:quickfix_window)
886
887 cfirst
888 call assert_equal(l:first_window, win_getid())
889endfunc
890
891" Allow :clast but the 'nowinfixbuf' window is selected, instead
892func Test_clast()
893 CheckFeature quickfix
894 call s:reset_all_buffers()
895
896 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
897
898 " The call to `:clast` succeeds but it selects the window with 'nowinfixbuf' instead
899 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
900
901 " Make sure the previous window has 'winfixbuf' so we can test that our
902 " "skip 'winfixbuf' window" logic works.
903 call win_gotoid(l:winfix_window)
904 call win_gotoid(l:quickfix_window)
905
906 clast
907 call assert_equal(l:first_window, win_getid())
908endfunc
909
910" Allow :cnext but the 'nowinfixbuf' window is selected, instead
911" Make sure no new windows are created and previous windows are reused
912func Test_cnext()
913 CheckFeature quickfix
914 call s:reset_all_buffers()
915
916 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
917 let l:expected = s:get_windows_count()
918
919 " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead
920 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
921
922 cnext!
923 call assert_equal(l:expected, s:get_windows_count())
924
925 " Make sure the previous window has 'winfixbuf' so we can test that our
926 " "skip 'winfixbuf' window" logic works.
927 call win_gotoid(l:winfix_window)
928 call win_gotoid(l:quickfix_window)
929
930 cnext
931 call assert_equal(l:first_window, win_getid())
932 call assert_equal(l:expected, s:get_windows_count())
933endfunc
934
935" Make sure :cnext creates a split window if no previous window exists
936func Test_cnext_no_previous_window()
937 CheckFeature quickfix
938 call s:reset_all_buffers()
939
940 let [l:current, _] = s:make_simple_quickfix()
941 execute "buffer! " . l:current
942
943 let l:expected = s:get_windows_count()
944
945 " Open the quickfix in a separate split and go to it
946 copen
947
948 call assert_equal(l:expected + 1, s:get_windows_count())
949endfunc
950
951" Allow :cnext and create a 'nowinfixbuf' window if none exists
952func Test_cnext_make_new_window()
953 CheckFeature quickfix
954 call s:reset_all_buffers()
955
956 let [l:current, _] = s:make_simple_quickfix()
957 let l:current = win_getid()
958
959 cfirst!
960
961 let l:windows = s:get_windows_count()
962 let l:expected = l:windows + 1 " We're about to create a new split window
963
964 cnext
965 call assert_equal(l:expected, s:get_windows_count())
966
967 cnext!
968 call assert_equal(l:expected, s:get_windows_count())
969endfunc
970
971" Allow :cprevious but the 'nowinfixbuf' window is selected, instead
972func Test_cprevious()
973 CheckFeature quickfix
974 call s:reset_all_buffers()
975
976 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
977
978 " The call to `:cprevious` succeeds but it selects the window with 'nowinfixbuf' instead
979 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
980
981 " Make sure the previous window has 'winfixbuf' so we can test that our
982 " "skip 'winfixbuf' window" logic works.
983 call win_gotoid(l:winfix_window)
984 call win_gotoid(l:quickfix_window)
985
986 cprevious
987 call assert_equal(l:first_window, win_getid())
988endfunc
989
990" Allow :cnfile but the 'nowinfixbuf' window is selected, instead
991func Test_cnfile()
992 CheckFeature quickfix
993 call s:reset_all_buffers()
994
995 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
996
997 " The call to `:cnfile` succeeds but it selects the window with 'nowinfixbuf' instead
998 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
999 cnext!
1000
1001 " Make sure the previous window has 'winfixbuf' so we can test that our
1002 " "skip 'winfixbuf' window" logic works.
1003 call win_gotoid(l:winfix_window)
1004 call win_gotoid(l:quickfix_window)
1005
1006 cnfile
1007 call assert_equal(l:first_window, win_getid())
1008endfunc
1009
1010" Allow :cpfile but the 'nowinfixbuf' window is selected, instead
1011func Test_cpfile()
1012 CheckFeature quickfix
1013 call s:reset_all_buffers()
1014
1015 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
1016
1017 " The call to `:cpfile` succeeds but it selects the window with 'nowinfixbuf' instead
1018 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
1019 cnext!
1020
1021 " Make sure the previous window has 'winfixbuf' so we can test that our
1022 " "skip 'winfixbuf' window" logic works.
1023 call win_gotoid(l:winfix_window)
1024 call win_gotoid(l:quickfix_window)
1025
1026 cpfile
1027 call assert_equal(l:first_window, win_getid())
1028endfunc
1029
1030" Allow :crewind but the 'nowinfixbuf' window is selected, instead
1031func Test_crewind()
1032 CheckFeature quickfix
1033 call s:reset_all_buffers()
1034
1035 let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
1036
1037 " The call to `:crewind` succeeds but it selects the window with 'nowinfixbuf' instead
1038 call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
1039 cnext!
1040
1041 " Make sure the previous window has 'winfixbuf' so we can test that our
1042 " "skip 'winfixbuf' window" logic works.
1043 call win_gotoid(l:winfix_window)
1044 call win_gotoid(l:quickfix_window)
1045
1046 crewind
1047 call assert_equal(l:first_window, win_getid())
1048endfunc
1049
1050" Allow <C-w>f because it opens in a new split
1051func Test_ctrl_w_f()
1052 call s:reset_all_buffers()
1053
1054 enew
1055 let l:file_name = tempname()
1056 call writefile([], l:file_name)
1057 let l:file_buffer = bufnr()
1058
1059 enew
1060 file other
1061 let l:other_buffer = bufnr()
1062
1063 set winfixbuf
1064
1065 call setline(1, l:file_name)
1066 let l:current_windows = s:get_windows_count()
1067 execute "normal \<C-w>f"
1068
1069 call assert_equal(l:current_windows + 1, s:get_windows_count())
1070
1071 call delete(l:file_name)
1072endfunc
1073
1074" Fail :djump but :djump! is allowed
1075func Test_djump()
1076 call s:reset_all_buffers()
1077
1078 let l:include_file = tempname() . ".h"
1079 call writefile(["min(1, 12);",
1080 \ '#include "' . l:include_file . '"'
1081 \ ],
1082 \ "main.c")
1083 call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
1084 edit main.c
1085
1086 set winfixbuf
1087
1088 let l:current = bufnr()
1089
1090 call assert_fails("djump 1 /min/", "E1513:")
1091 call assert_equal(l:current, bufnr())
1092
1093 djump! 1 /min/
1094 call assert_notequal(l:current, bufnr())
1095
1096 call delete("main.c")
1097 call delete(l:include_file)
1098endfunc
1099
1100" Fail :drop but :drop! is allowed
1101func Test_drop()
1102 call s:reset_all_buffers()
1103
1104 let l:other = s:make_buffer_pairs()
1105 let l:current = bufnr()
1106
1107 call assert_fails("drop other", "E1513:")
1108 call assert_equal(l:current, bufnr())
1109
1110 drop! other
1111 call assert_equal(l:other, bufnr())
1112endfunc
1113
1114" Fail :edit but :edit! is allowed
1115func Test_edit()
1116 call s:reset_all_buffers()
1117
1118 let l:other = s:make_buffer_pairs()
1119 let l:current = bufnr()
1120
1121 call assert_fails("edit other", "E1513:")
1122 call assert_equal(l:current, bufnr())
1123
1124 edit! other
1125 call assert_equal(l:other, bufnr())
1126endfunc
1127
Colin Kennedy65e580b2024-03-26 18:29:30 +01001128" Fail :e when selecting a buffer from a relative path if in a different folder
1129"
1130" In this tests there's 2 buffers
1131"
1132" foo - lives on disk, in some folder. e.g. /tmp/foo
1133" foo - an in-memory buffer that has not been saved to disk. If saved, it
1134" would live in a different folder, /other/foo.
1135"
1136" The 'winfixbuf' is looking at the in-memory buffer and trying to switch to
1137" the buffer on-disk (and fails, because it's a different buffer)
1138func Test_edit_different_buffer_on_disk_and_relative_path_to_disk()
1139 call s:reset_all_buffers()
1140
1141 let l:file_on_disk = tempname()
1142 let l:directory_on_disk1 = fnamemodify(l:file_on_disk, ":p:h")
1143 let l:name = fnamemodify(l:file_on_disk, ":t")
1144 execute "edit " . l:file_on_disk
1145 write!
1146
1147 let l:directory_on_disk2 = l:directory_on_disk1 . "_something_else"
1148
1149 if !isdirectory(l:directory_on_disk2)
1150 call mkdir(l:directory_on_disk2)
1151 endif
1152
1153 execute "cd " . l:directory_on_disk2
1154 execute "edit " l:name
1155
1156 let l:current = bufnr()
1157
1158 call assert_equal(l:current, bufnr())
1159 set winfixbuf
1160 call assert_fails("edit " . l:file_on_disk, "E1513:")
1161 call assert_equal(l:current, bufnr())
1162
1163 call delete(l:directory_on_disk1)
1164 call delete(l:directory_on_disk2)
1165endfunc
1166
1167" Fail :e when selecting a buffer from a relative path if in a different folder
1168"
1169" In this tests there's 2 buffers
1170"
1171" foo - lives on disk, in some folder. e.g. /tmp/foo
1172" foo - an in-memory buffer that has not been saved to disk. If saved, it
1173" would live in a different folder, /other/foo.
1174"
1175" The 'winfixbuf' is looking at the on-disk buffer and trying to switch to
1176" the in-memory buffer (and fails, because it's a different buffer)
1177func Test_edit_different_buffer_on_disk_and_relative_path_to_memory()
1178 call s:reset_all_buffers()
1179
1180 let l:file_on_disk = tempname()
1181 let l:directory_on_disk1 = fnamemodify(l:file_on_disk, ":p:h")
1182 let l:name = fnamemodify(l:file_on_disk, ":t")
1183 execute "edit " . l:file_on_disk
1184 write!
1185
1186 let l:directory_on_disk2 = l:directory_on_disk1 . "_something_else"
1187
1188 if !isdirectory(l:directory_on_disk2)
1189 call mkdir(l:directory_on_disk2)
1190 endif
1191
1192 execute "cd " . l:directory_on_disk2
1193 execute "edit " l:name
1194 execute "cd " . l:directory_on_disk1
1195 execute "edit " l:file_on_disk
1196 execute "cd " . l:directory_on_disk2
1197
1198 let l:current = bufnr()
1199
1200 call assert_equal(l:current, bufnr())
1201 set winfixbuf
1202 call assert_fails("edit " . l:name, "E1513:")
1203 call assert_equal(l:current, bufnr())
1204
1205 call delete(l:directory_on_disk1)
1206 call delete(l:directory_on_disk2)
1207endfunc
1208
1209" Fail to call `:e first` if called from a starting, in-memory buffer
1210func Test_edit_first_buffer()
1211 call s:reset_all_buffers()
1212
1213 set winfixbuf
1214 let l:current = bufnr()
1215
1216 call assert_fails("edit first", "E1513:")
1217 call assert_equal(l:current, bufnr())
1218
1219 edit! first
1220 call assert_equal(l:current, bufnr())
1221 edit! somewhere_else
1222 call assert_notequal(l:current, bufnr())
1223endfunc
1224
1225" Allow reloading a buffer using :e
1226func Test_edit_no_arguments()
1227 call s:reset_all_buffers()
1228
1229 let l:current = bufnr()
1230 file some_buffer
1231
1232 call assert_equal(l:current, bufnr())
1233 set winfixbuf
1234 edit
1235 call assert_equal(l:current, bufnr())
1236endfunc
1237
1238" Allow :e selecting the current buffer
1239func Test_edit_same_buffer_in_memory()
1240 call s:reset_all_buffers()
1241
Christian Brabandt79b28672024-03-27 10:44:14 +01001242 let current = bufnr()
Colin Kennedy65e580b2024-03-26 18:29:30 +01001243 file same_buffer
1244
Christian Brabandt79b28672024-03-27 10:44:14 +01001245 call assert_equal(current, bufnr())
Colin Kennedy65e580b2024-03-26 18:29:30 +01001246 set winfixbuf
1247 edit same_buffer
Christian Brabandt79b28672024-03-27 10:44:14 +01001248 call assert_equal(current, bufnr())
1249 set nowinfixbuf
Colin Kennedy65e580b2024-03-26 18:29:30 +01001250endfunc
1251
1252" Allow :e selecting the current buffer as a full path
1253func Test_edit_same_buffer_on_disk_absolute_path()
Christian Brabandt79b28672024-03-27 10:44:14 +01001254 " This fails on CI (Windows builds), why?
1255 CheckNotMSWindows
Colin Kennedy65e580b2024-03-26 18:29:30 +01001256 call s:reset_all_buffers()
1257
Christian Brabandt79b28672024-03-27 10:44:14 +01001258 let file = tempname()
1259 let current = bufnr()
1260 execute "edit " . file
Colin Kennedy65e580b2024-03-26 18:29:30 +01001261 write!
1262
Christian Brabandt79b28672024-03-27 10:44:14 +01001263 call assert_equal(current, bufnr())
Colin Kennedy65e580b2024-03-26 18:29:30 +01001264 set winfixbuf
Christian Brabandt79b28672024-03-27 10:44:14 +01001265 execute "edit " file
1266 call assert_equal(current, bufnr())
Colin Kennedy65e580b2024-03-26 18:29:30 +01001267
Christian Brabandt79b28672024-03-27 10:44:14 +01001268 call delete(file)
1269 set nowinfixbuf
Colin Kennedy65e580b2024-03-26 18:29:30 +01001270endfunc
1271
Colin Kennedy21570352024-03-03 16:16:47 +01001272" Fail :enew but :enew! is allowed
1273func Test_enew()
1274 call s:reset_all_buffers()
1275
1276 let l:other = s:make_buffer_pairs()
1277 let l:current = bufnr()
1278
1279 call assert_fails("enew", "E1513:")
1280 call assert_equal(l:current, bufnr())
1281
1282 enew!
1283 call assert_notequal(l:other, bufnr())
1284 call assert_notequal(3, bufnr())
1285endfunc
1286
1287" Fail :ex but :ex! is allowed
1288func Test_ex()
1289 call s:reset_all_buffers()
1290
1291 let l:other = s:make_buffer_pairs()
1292 let l:current = bufnr()
1293
1294 call assert_fails("ex other", "E1513:")
1295 call assert_equal(l:current, bufnr())
1296
1297 ex! other
1298 call assert_equal(l:other, bufnr())
1299endfunc
1300
1301" Fail :find but :find! is allowed
1302func Test_find()
1303 call s:reset_all_buffers()
1304
1305 let l:current = bufnr()
1306 let l:file = tempname()
1307 call writefile([], l:file)
Sean Dewar769eb2d2024-03-07 21:37:50 +01001308 let l:file = fnamemodify(l:file, ':p') " In case it's Windows 8.3-style.
Colin Kennedy21570352024-03-03 16:16:47 +01001309 let l:directory = fnamemodify(l:file, ":p:h")
1310 let l:name = fnamemodify(l:file, ":p:t")
1311
1312 let l:original_path = &path
1313 execute "set path=" . l:directory
1314
1315 set winfixbuf
1316
1317 call assert_fails("execute 'find " . l:name . "'", "E1513:")
1318 call assert_equal(l:current, bufnr())
1319
1320 execute "find! " . l:name
1321 call assert_equal(l:file, expand("%:p"))
1322
1323 execute "set path=" . l:original_path
1324 call delete(l:file)
1325endfunc
1326
1327" Fail :first but :first! is allowed
1328func Test_first()
1329 call s:reset_all_buffers()
1330
1331 let [l:first, _] = s:make_args_list()
1332 next!
1333
1334 call assert_fails("first", "E1513:")
1335 call assert_notequal(l:first, bufnr())
1336
1337 first!
1338 call assert_equal(l:first, bufnr())
1339endfunc
1340
1341" Fail :grep but :grep! is allowed
1342func Test_grep()
1343 CheckFeature quickfix
1344 call s:reset_all_buffers()
1345
1346 edit first.unittest
1347 call append(0, ["some-search-term"])
1348 write
1349 let l:first = bufnr()
1350
1351 edit current.unittest
1352 call append(0, ["some-search-term"])
1353 write
1354 let l:current = bufnr()
1355
1356 edit! last.unittest
1357 call append(0, ["some-search-term"])
1358 write
1359 let l:last = bufnr()
1360
1361 set winfixbuf
1362
1363 buffer! current.unittest
1364
1365 call assert_fails("silent! grep some-search-term *.unittest", "E1513:")
1366 call assert_equal(l:current, bufnr())
1367 execute "edit! " . l:first
1368
1369 silent! grep! some-search-term *.unittest
1370 call assert_notequal(l:first, bufnr())
1371
1372 call delete("first.unittest")
1373 call delete("current.unittest")
1374 call delete("last.unittest")
1375endfunc
1376
1377" Fail :ijump but :ijump! is allowed
1378func Test_ijump()
1379 call s:reset_all_buffers()
1380
1381 let l:include_file = tempname() . ".h"
1382 call writefile([
1383 \ '#include "' . l:include_file . '"'
1384 \ ],
1385 \ "main.c")
1386 call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
1387 edit main.c
1388
1389 set winfixbuf
1390
1391 let l:current = bufnr()
1392
1393 set define=^\\s*#\\s*define
1394 set include=^\\s*#\\s*include
1395 set path=.,/usr/include,,
1396
1397 call assert_fails("ijump /min/", "E1513:")
1398 call assert_equal(l:current, bufnr())
1399
1400 set nowinfixbuf
1401
1402 ijump! /min/
1403 call assert_notequal(l:current, bufnr())
1404
1405 set define&
1406 set include&
1407 set path&
1408 call delete("main.c")
1409 call delete(l:include_file)
1410endfunc
1411
1412" Fail :lNext but :lNext! is allowed
1413func Test_lNext()
1414 CheckFeature quickfix
1415 call s:reset_all_buffers()
1416
1417 let [l:first, l:middle, _] = s:make_simple_location_list()
Sean Dewar4bb505e2024-03-05 20:39:07 +01001418 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001419
Sean Dewar4bb505e2024-03-05 20:39:07 +01001420 lnext!
1421 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001422 call assert_equal(l:middle, bufnr())
1423
Sean Dewar4bb505e2024-03-05 20:39:07 +01001424 call assert_fails("lNext", "E1513:")
1425 " Ensure the entry didn't change.
1426 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
1427 call assert_equal(l:middle, bufnr())
1428
1429 lnext!
1430 call assert_equal(3, getloclist(0, #{idx: 0}).idx)
1431 call assert_equal(l:middle, bufnr())
Colin Kennedy21570352024-03-03 16:16:47 +01001432
1433 lNext!
Sean Dewar4bb505e2024-03-05 20:39:07 +01001434 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
1435 call assert_equal(l:middle, bufnr())
1436
1437 lNext!
1438 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001439 call assert_equal(l:first, bufnr())
1440endfunc
1441
1442" Fail :lNfile but :lNfile! is allowed
1443func Test_lNfile()
1444 CheckFeature quickfix
1445 call s:reset_all_buffers()
1446
1447 let [l:first, l:current, _] = s:make_simple_location_list()
Sean Dewar4bb505e2024-03-05 20:39:07 +01001448 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001449
Sean Dewar4bb505e2024-03-05 20:39:07 +01001450 lnext!
1451 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001452 call assert_equal(l:current, bufnr())
1453
Sean Dewar4bb505e2024-03-05 20:39:07 +01001454 call assert_fails("lNfile", "E1513:")
1455 " Ensure the entry didn't change.
1456 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
1457 call assert_equal(l:current, bufnr())
1458
1459 lnext!
1460 call assert_equal(3, getloclist(0, #{idx: 0}).idx)
1461 call assert_equal(l:current, bufnr())
Colin Kennedy21570352024-03-03 16:16:47 +01001462
1463 lNfile!
Sean Dewar4bb505e2024-03-05 20:39:07 +01001464 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001465 call assert_equal(l:first, bufnr())
1466endfunc
1467
1468" Allow :laddexpr because it doesn't change the current buffer
1469func Test_laddexpr()
1470 CheckFeature quickfix
1471 call s:reset_all_buffers()
1472
1473 let l:file_path = tempname()
1474 call writefile(["Error - bad-thing-found"], l:file_path)
1475 execute "edit " . l:file_path
1476 let l:file_buffer = bufnr()
1477 let l:current = bufnr()
1478
1479 edit first.unittest
1480 call append(0, ["some-search-term bad-thing-found"])
1481
1482 edit! other.unittest
1483
1484 set winfixbuf
1485
1486 execute "buffer! " . l:file_buffer
1487
1488 execute 'laddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")'
1489 call assert_equal(l:current, bufnr())
1490
1491 call delete(l:file_path)
1492endfunc
1493
1494" Fail :last but :last! is allowed
1495func Test_last()
1496 call s:reset_all_buffers()
1497
1498 let [_, l:last] = s:make_args_list()
1499 next!
1500
1501 call assert_fails("last", "E1513:")
1502 call assert_notequal(l:last, bufnr())
1503
1504 last!
1505 call assert_equal(l:last, bufnr())
1506endfunc
1507
1508" Fail :lbuffer but :lbuffer! is allowed
1509func Test_lbuffer()
1510 CheckFeature quickfix
1511 call s:reset_all_buffers()
1512
1513 let l:file_path = tempname()
1514 call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path)
1515 execute "edit " . l:file_path
1516 let l:file_buffer = bufnr()
1517 let l:current = bufnr()
1518
1519 edit first.unittest
1520 call append(0, ["some-search-term bad-thing-found"])
1521
1522 edit! other.unittest
1523
1524 set winfixbuf
1525
1526 execute "buffer! " . l:file_buffer
1527
1528 call assert_fails("lbuffer " . l:file_buffer)
1529 call assert_equal(l:current, bufnr())
1530
1531 execute "lbuffer! " . l:file_buffer
1532 call assert_equal("first.unittest", expand("%:t"))
1533
1534 call delete(l:file_path)
1535endfunc
1536
1537" Fail :ldo but :ldo! is allowed
1538func Test_ldo()
1539 CheckFeature quickfix
1540 call s:reset_all_buffers()
1541
1542 let [l:first, l:middle, l:last] = s:make_simple_location_list()
1543 lnext!
1544
1545 call assert_fails('execute "ldo buffer ' . l:first . '"', "E1513:")
1546 call assert_equal(l:middle, bufnr())
1547 execute "ldo! buffer " . l:first
1548 call assert_notequal(l:last, bufnr())
1549endfunc
1550
1551" Fail :lfdo but :lfdo! is allowed
1552func Test_lexpr()
1553 CheckFeature quickfix
1554 call s:reset_all_buffers()
1555
1556 let l:file = tempname()
1557 let l:entry = '["' . l:file . ':1:bar"]'
1558 let l:current = bufnr()
1559
1560 set winfixbuf
1561
1562 call assert_fails("lexpr " . l:entry)
1563 call assert_equal(l:current, bufnr())
1564
1565 execute "lexpr! " . l:entry
1566 call assert_equal(fnamemodify(l:file, ":t"), expand("%:t"))
1567endfunc
1568
1569" Fail :lfdo but :lfdo! is allowed
1570func Test_lfdo()
1571 CheckFeature quickfix
1572 call s:reset_all_buffers()
1573
1574 let [l:first, l:middle, l:last] = s:make_simple_location_list()
1575 lnext!
1576
1577 call assert_fails('execute "lfdo buffer ' . l:first . '"', "E1513:")
1578 call assert_equal(l:middle, bufnr())
1579 execute "lfdo! buffer " . l:first
1580 call assert_notequal(l:last, bufnr())
1581endfunc
1582
1583" Fail :lfile but :lfile! is allowed
1584func Test_lfile()
1585 CheckFeature quickfix
1586 call s:reset_all_buffers()
1587
1588 edit first.unittest
1589 call append(0, ["some-search-term bad-thing-found"])
1590 write
1591 let l:first = bufnr()
1592
1593 edit! second.unittest
1594 call append(0, ["some-search-term"])
1595 write
1596
1597 let l:file = tempname()
1598 call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file)
1599
1600 let l:current = bufnr()
1601
1602 set winfixbuf
1603
1604 call assert_fails(":lfile " . l:file)
1605 call assert_equal(l:current, bufnr())
1606
1607 execute ":lfile! " . l:file
1608 call assert_equal(l:first, bufnr())
1609
1610 call delete(l:file)
1611 call delete("first.unittest")
1612 call delete("second.unittest")
1613endfunc
1614
1615" Fail :ll but :ll! is allowed
1616func Test_ll()
1617 CheckFeature quickfix
1618 call s:reset_all_buffers()
1619
1620 let [l:first, l:middle, l:last] = s:make_simple_location_list()
1621 lopen
1622 lfirst!
1623 execute "normal \<C-w>j"
1624 normal j
1625
1626 call assert_fails(".ll", "E1513:")
1627 execute "normal \<C-w>k"
1628 call assert_equal(l:first, bufnr())
1629 execute "normal \<C-w>j"
1630 .ll!
1631 execute "normal \<C-w>k"
1632 call assert_equal(l:middle, bufnr())
1633endfunc
1634
1635" Fail :llast but :llast! is allowed
1636func Test_llast()
1637 CheckFeature quickfix
1638 call s:reset_all_buffers()
1639
1640 let [l:first, _, l:last] = s:make_simple_location_list()
1641 lfirst!
1642
1643 call assert_fails("llast", "E1513:")
1644 call assert_equal(l:first, bufnr())
1645
1646 llast!
1647 call assert_equal(l:last, bufnr())
1648endfunc
1649
1650" Fail :lnext but :lnext! is allowed
1651func Test_lnext()
1652 CheckFeature quickfix
1653 call s:reset_all_buffers()
1654
1655 let [l:first, l:middle, l:last] = s:make_simple_location_list()
1656 ll!
1657
1658 call assert_fails("lnext", "E1513:")
1659 call assert_equal(l:first, bufnr())
1660
1661 lnext!
1662 call assert_equal(l:middle, bufnr())
1663endfunc
1664
1665" Fail :lnfile but :lnfile! is allowed
1666func Test_lnfile()
1667 CheckFeature quickfix
1668 call s:reset_all_buffers()
1669
1670 let [_, l:current, l:last] = s:make_simple_location_list()
Sean Dewar4bb505e2024-03-05 20:39:07 +01001671 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001672
Sean Dewar4bb505e2024-03-05 20:39:07 +01001673 lnext!
1674 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001675 call assert_equal(l:current, bufnr())
1676
Sean Dewar4bb505e2024-03-05 20:39:07 +01001677 call assert_fails("lnfile", "E1513:")
Sean Dewar769eb2d2024-03-07 21:37:50 +01001678 " Ensure the entry didn't change.
Sean Dewar4bb505e2024-03-05 20:39:07 +01001679 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
1680 call assert_equal(l:current, bufnr())
Colin Kennedy21570352024-03-03 16:16:47 +01001681
1682 lnfile!
Sean Dewar4bb505e2024-03-05 20:39:07 +01001683 call assert_equal(4, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001684 call assert_equal(l:last, bufnr())
1685endfunc
1686
1687" Fail :lpfile but :lpfile! is allowed
1688func Test_lpfile()
1689 CheckFeature quickfix
1690 call s:reset_all_buffers()
1691
1692 let [l:first, l:current, _] = s:make_simple_location_list()
1693 lnext!
1694
1695 call assert_fails("lpfile", "E1513:")
1696 call assert_equal(l:current, bufnr())
1697
1698 lnext! " Reset for the next test call
1699
1700 lpfile!
1701 call assert_equal(l:first, bufnr())
1702endfunc
1703
1704" Fail :lprevious but :lprevious! is allowed
1705func Test_lprevious()
1706 CheckFeature quickfix
1707 call s:reset_all_buffers()
1708
1709 let [l:first, l:middle, _] = s:make_simple_location_list()
Sean Dewar4bb505e2024-03-05 20:39:07 +01001710 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001711
Sean Dewar4bb505e2024-03-05 20:39:07 +01001712 lnext!
1713 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001714 call assert_equal(l:middle, bufnr())
1715
Sean Dewar4bb505e2024-03-05 20:39:07 +01001716 call assert_fails("lprevious", "E1513:")
1717 " Ensure the entry didn't change.
1718 call assert_equal(2, getloclist(0, #{idx: 0}).idx)
1719 call assert_equal(l:middle, bufnr())
Colin Kennedy21570352024-03-03 16:16:47 +01001720
1721 lprevious!
Sean Dewar4bb505e2024-03-05 20:39:07 +01001722 call assert_equal(1, getloclist(0, #{idx: 0}).idx)
Colin Kennedy21570352024-03-03 16:16:47 +01001723 call assert_equal(l:first, bufnr())
1724endfunc
1725
1726" Fail :lrewind but :lrewind! is allowed
1727func Test_lrewind()
1728 CheckFeature quickfix
1729 call s:reset_all_buffers()
1730
1731 let [l:first, l:middle, _] = s:make_simple_location_list()
1732 lnext!
1733
1734 call assert_fails("lrewind", "E1513:")
1735 call assert_equal(l:middle, bufnr())
1736
1737 lrewind!
1738 call assert_equal(l:first, bufnr())
1739endfunc
1740
1741" Fail :ltag but :ltag! is allowed
1742func Test_ltag()
1743 call s:reset_all_buffers()
1744
1745 set tags=Xtags
1746 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
1747 \ "one\tXfile\t1",
1748 \ "three\tXfile\t3",
1749 \ "two\tXfile\t2"],
1750 \ "Xtags")
1751 call writefile(["one", "two", "three"], "Xfile")
1752 call writefile(["one"], "Xother")
1753 edit Xother
1754 execute "normal \<C-]>"
1755
1756 set winfixbuf
1757
1758 let l:current = bufnr()
1759
1760 call assert_fails("ltag one", "E1513:")
1761
1762 ltag! one
1763
1764 set tags&
1765 call delete("Xtags")
1766 call delete("Xfile")
1767 call delete("Xother")
1768endfunc
1769
1770" Fail vim.command if we try to change buffers while 'winfixbuf' is set
1771func Test_lua_command()
Sean Dewar5131f222024-03-04 19:09:26 +01001772 CheckFeature lua
Colin Kennedy21570352024-03-03 16:16:47 +01001773 call s:reset_all_buffers()
1774
1775 enew
1776 file first
1777 let l:previous = bufnr()
1778
1779 enew
1780 file second
1781 let l:current = bufnr()
1782
1783 set winfixbuf
1784
1785 call assert_fails('lua vim.command("buffer " .. ' . l:previous . ')')
1786 call assert_equal(l:current, bufnr())
1787
1788 execute 'lua vim.command("buffer! " .. ' . l:previous . ')'
1789 call assert_equal(l:previous, bufnr())
1790endfunc
1791
1792" Fail :lvimgrep but :lvimgrep! is allowed
1793func Test_lvimgrep()
1794 CheckFeature quickfix
1795 call s:reset_all_buffers()
1796
1797 edit first.unittest
1798 call append(0, ["some-search-term"])
1799 write
1800
1801 edit winfix.unittest
1802 call append(0, ["some-search-term"])
1803 write
1804 let l:current = bufnr()
1805
1806 set winfixbuf
1807
1808 edit! last.unittest
1809 call append(0, ["some-search-term"])
1810 write
1811 let l:last = bufnr()
1812
1813 buffer! winfix.unittest
1814
1815 call assert_fails("lvimgrep /some-search-term/ *.unittest", "E1513:")
1816 call assert_equal(l:current, bufnr())
1817
1818 lvimgrep! /some-search-term/ *.unittest
1819 call assert_notequal(l:current, bufnr())
1820
1821 call delete("first.unittest")
1822 call delete("winfix.unittest")
1823 call delete("last.unittest")
1824endfunc
1825
1826" Fail :lvimgrepadd but :lvimgrepadd! is allowed
1827func Test_lvimgrepadd()
1828 CheckFeature quickfix
1829 call s:reset_all_buffers()
1830
1831 edit first.unittest
1832 call append(0, ["some-search-term"])
1833 write
1834
1835 edit winfix.unittest
1836 call append(0, ["some-search-term"])
1837 write
1838 let l:current = bufnr()
1839
1840 set winfixbuf
1841
1842 edit! last.unittest
1843 call append(0, ["some-search-term"])
1844 write
1845 let l:last = bufnr()
1846
1847 buffer! winfix.unittest
1848
1849 call assert_fails("lvimgrepadd /some-search-term/ *.unittest")
1850 call assert_equal(l:current, bufnr())
1851
1852 lvimgrepadd! /some-search-term/ *.unittest
1853 call assert_notequal(l:current, bufnr())
1854
1855 call delete("first.unittest")
1856 call delete("winfix.unittest")
1857 call delete("last.unittest")
1858endfunc
1859
1860" Don't allow global marks to change the current 'winfixbuf' window
1861func Test_marks_mappings_fail()
1862 call s:reset_all_buffers()
1863
1864 let l:other = s:make_buffer_pairs()
1865 let l:current = bufnr()
1866 execute "buffer! " . l:other
1867 normal mA
1868 execute "buffer! " . l:current
1869 normal mB
1870
1871 call assert_fails("normal `A", "E1513:")
1872 call assert_equal(l:current, bufnr())
1873
1874 call assert_fails("normal 'A", "E1513:")
1875 call assert_equal(l:current, bufnr())
1876
1877 set nowinfixbuf
1878
1879 normal `A
1880 call assert_equal(l:other, bufnr())
1881endfunc
1882
1883" Allow global marks in a 'winfixbuf' window if the jump is the same buffer
1884func Test_marks_mappings_pass_intra_move()
1885 call s:reset_all_buffers()
1886
1887 let l:current = bufnr()
1888 call append(0, ["some line", "another line"])
1889 normal mA
1890 normal j
1891 normal mB
1892
1893 set winfixbuf
1894
1895 normal `A
1896 call assert_equal(l:current, bufnr())
1897endfunc
1898
1899" Fail :next but :next! is allowed
1900func Test_next()
1901 call s:reset_all_buffers()
1902
1903 let [l:first, _] = s:make_args_list()
1904 first!
1905
1906 call assert_fails("next", "E1513:")
1907 call assert_equal(l:first, bufnr())
1908
1909 next!
1910 call assert_notequal(l:first, bufnr())
1911endfunc
1912
1913" Ensure :mksession saves 'winfixbuf' details
1914func Test_mksession()
1915 CheckFeature mksession
1916 call s:reset_all_buffers()
1917
1918 set sessionoptions+=options
1919 set winfixbuf
1920
1921 mksession test_winfixbuf_Test_mksession.vim
1922
1923 call s:reset_all_buffers()
1924 let l:winfixbuf = &winfixbuf
1925 call assert_equal(0, l:winfixbuf)
1926
1927 source test_winfixbuf_Test_mksession.vim
1928
1929 let l:winfixbuf = &winfixbuf
1930 call assert_equal(1, l:winfixbuf)
1931
1932 set sessionoptions&
1933 call delete("test_winfixbuf_Test_mksession.vim")
1934endfunc
1935
1936" Allow :next if the next index is the same as the current buffer
1937func Test_next_same_buffer()
1938 call s:reset_all_buffers()
1939
1940 enew
1941 file foo
1942 enew
1943 file bar
1944 enew
1945 file fizz
1946 enew
1947 file buzz
1948 args foo foo bar fizz buzz
1949
1950 edit foo
1951 set winfixbuf
1952 let l:current = bufnr()
1953
1954 " Allow :next because the args list is `[foo] foo bar fizz buzz
1955 next
1956 call assert_equal(l:current, bufnr())
1957
1958 " Fail :next because the args list is `foo [foo] bar fizz buzz
1959 " and the next buffer would be bar, which is a different buffer
1960 call assert_fails("next", "E1513:")
1961 call assert_equal(l:current, bufnr())
1962endfunc
1963
1964" Fail to jump to a tag with g<C-]> if 'winfixbuf' is enabled
1965func Test_normal_g_ctrl_square_bracket_right()
1966 call s:reset_all_buffers()
1967
1968 set tags=Xtags
1969 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
1970 \ "one\tXfile\t1",
1971 \ "three\tXfile\t3",
1972 \ "two\tXfile\t2"],
1973 \ "Xtags")
1974 call writefile(["one", "two", "three"], "Xfile")
1975 call writefile(["one"], "Xother")
1976 edit Xother
1977
1978 set winfixbuf
1979
1980 let l:current = bufnr()
1981
1982 call assert_fails("normal g\<C-]>", "E1513:")
1983 call assert_equal(l:current, bufnr())
1984
1985 set tags&
1986 call delete("Xtags")
1987 call delete("Xfile")
1988 call delete("Xother")
1989endfunc
1990
1991" Fail to jump to a tag with g<RightMouse> if 'winfixbuf' is enabled
1992func Test_normal_g_rightmouse()
1993 call s:reset_all_buffers()
1994 set mouse=n
1995
1996 set tags=Xtags
1997 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
1998 \ "one\tXfile\t1",
1999 \ "three\tXfile\t3",
2000 \ "two\tXfile\t2"],
2001 \ "Xtags")
2002 call writefile(["one", "two", "three"], "Xfile")
2003 call writefile(["one"], "Xother")
2004 edit Xother
2005 execute "normal \<C-]>"
2006
2007 set winfixbuf
2008
2009 let l:current = bufnr()
2010
2011 call assert_fails("normal g\<RightMouse>", "E1513:")
2012 call assert_equal(l:current, bufnr())
2013
2014 set tags&
2015 set mouse&
2016 call delete("Xtags")
2017 call delete("Xfile")
2018 call delete("Xother")
2019endfunc
2020
2021" Fail to jump to a tag with g] if 'winfixbuf' is enabled
2022func Test_normal_g_square_bracket_right()
2023 call s:reset_all_buffers()
2024
2025 set tags=Xtags
2026 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2027 \ "one\tXfile\t1",
2028 \ "three\tXfile\t3",
2029 \ "two\tXfile\t2"],
2030 \ "Xtags")
2031 call writefile(["one", "two", "three"], "Xfile")
2032 call writefile(["one"], "Xother")
2033 edit Xother
2034
2035 set winfixbuf
2036
2037 let l:current = bufnr()
2038
2039 call assert_fails("normal g]", "E1513:")
2040 call assert_equal(l:current, bufnr())
2041
2042 set tags&
2043 call delete("Xtags")
2044 call delete("Xfile")
2045 call delete("Xother")
2046endfunc
2047
2048" Fail to jump to a tag with <C-RightMouse> if 'winfixbuf' is enabled
2049func Test_normal_ctrl_rightmouse()
2050 call s:reset_all_buffers()
2051 set mouse=n
2052
2053 set tags=Xtags
2054 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2055 \ "one\tXfile\t1",
2056 \ "three\tXfile\t3",
2057 \ "two\tXfile\t2"],
2058 \ "Xtags")
2059 call writefile(["one", "two", "three"], "Xfile")
2060 call writefile(["one"], "Xother")
2061 edit Xother
2062 execute "normal \<C-]>"
2063
2064 set winfixbuf
2065
2066 let l:current = bufnr()
2067
2068 call assert_fails("normal \<C-RightMouse>", "E1513:")
2069 call assert_equal(l:current, bufnr())
2070
2071 set tags&
2072 set mouse&
2073 call delete("Xtags")
2074 call delete("Xfile")
2075 call delete("Xother")
2076endfunc
2077
2078" Fail to jump to a tag with <C-t> if 'winfixbuf' is enabled
2079func Test_normal_ctrl_t()
2080 call s:reset_all_buffers()
2081
2082 set tags=Xtags
2083 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2084 \ "one\tXfile\t1",
2085 \ "three\tXfile\t3",
2086 \ "two\tXfile\t2"],
2087 \ "Xtags")
2088 call writefile(["one", "two", "three"], "Xfile")
2089 call writefile(["one"], "Xother")
2090 edit Xother
2091 execute "normal \<C-]>"
2092
2093 set winfixbuf
2094
2095 let l:current = bufnr()
2096
2097 call assert_fails("normal \<C-t>", "E1513:")
2098 call assert_equal(l:current, bufnr())
2099
2100 set tags&
2101 call delete("Xtags")
2102 call delete("Xfile")
2103 call delete("Xother")
2104endfunc
2105
2106" Disallow <C-^> in 'winfixbuf' windows
2107func Test_normal_ctrl_hat()
2108 call s:reset_all_buffers()
2109 clearjumps
2110
2111 enew
2112 file first
2113 let l:first = bufnr()
2114
2115 enew
2116 file current
2117 let l:current = bufnr()
2118
2119 set winfixbuf
2120
2121 call assert_fails("normal \<C-^>", "E1513:")
2122 call assert_equal(l:current, bufnr())
2123endfunc
2124
2125" Allow <C-i> in 'winfixbuf' windows if the movement stays within the buffer
2126func Test_normal_ctrl_i_pass()
2127 call s:reset_all_buffers()
2128 clearjumps
2129
2130 enew
2131 file first
2132 let l:first = bufnr()
2133
2134 enew!
2135 file current
2136 let l:current = bufnr()
2137 " Add some lines so we can populate a jumplist"
2138 call append(0, ["some line", "another line"])
2139 " Add an entry to the jump list
2140 " Go up another line
2141 normal m`
2142 normal k
2143 execute "normal \<C-o>"
2144
2145 set winfixbuf
2146
2147 let l:line = getcurpos()[1]
2148 execute "normal 1\<C-i>"
2149 call assert_notequal(l:line, getcurpos()[1])
2150endfunc
2151
2152" Disallow <C-o> in 'winfixbuf' windows if it would cause the buffer to switch
2153func Test_normal_ctrl_o_fail()
2154 call s:reset_all_buffers()
2155 clearjumps
2156
2157 enew
2158 file first
2159 let l:first = bufnr()
2160
2161 enew
2162 file current
2163 let l:current = bufnr()
2164
2165 set winfixbuf
2166
2167 call assert_fails("normal \<C-o>", "E1513:")
2168 call assert_equal(l:current, bufnr())
2169endfunc
2170
2171" Allow <C-o> in 'winfixbuf' windows if the movement stays within the buffer
2172func Test_normal_ctrl_o_pass()
2173 call s:reset_all_buffers()
2174 clearjumps
2175
2176 enew
2177 file first
2178 let l:first = bufnr()
2179
2180 enew!
2181 file current
2182 let l:current = bufnr()
2183 " Add some lines so we can populate a jumplist
2184 call append(0, ["some line", "another line"])
2185 " Add an entry to the jump list
2186 " Go up another line
2187 normal m`
2188 normal k
2189
2190 set winfixbuf
2191
2192 execute "normal \<C-o>"
2193 call assert_equal(l:current, bufnr())
2194endfunc
2195
2196" Fail to jump to a tag with <C-]> if 'winfixbuf' is enabled
2197func Test_normal_ctrl_square_bracket_right()
2198 call s:reset_all_buffers()
2199
2200 set tags=Xtags
2201 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2202 \ "one\tXfile\t1",
2203 \ "three\tXfile\t3",
2204 \ "two\tXfile\t2"],
2205 \ "Xtags")
2206 call writefile(["one", "two", "three"], "Xfile")
2207 call writefile(["one"], "Xother")
2208 edit Xother
2209
2210 set winfixbuf
2211
2212 let l:current = bufnr()
2213
2214 call assert_fails("normal \<C-]>", "E1513:")
2215 call assert_equal(l:current, bufnr())
2216
2217 set tags&
2218 call delete("Xtags")
2219 call delete("Xfile")
2220 call delete("Xother")
2221endfunc
2222
2223" Allow <C-w><C-]> with 'winfixbuf' enabled because it runs in a new, split window
2224func Test_normal_ctrl_w_ctrl_square_bracket_right()
2225 call s:reset_all_buffers()
2226
2227 set tags=Xtags
2228 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2229 \ "one\tXfile\t1",
2230 \ "three\tXfile\t3",
2231 \ "two\tXfile\t2"],
2232 \ "Xtags")
2233 call writefile(["one", "two", "three"], "Xfile")
2234 call writefile(["one"], "Xother")
2235 edit Xother
2236
2237 set winfixbuf
2238
2239 let l:current_windows = s:get_windows_count()
2240 execute "normal \<C-w>\<C-]>"
2241 call assert_equal(l:current_windows + 1, s:get_windows_count())
2242
2243 set tags&
2244 call delete("Xtags")
2245 call delete("Xfile")
2246 call delete("Xother")
2247endfunc
2248
2249" Allow <C-w>g<C-]> with 'winfixbuf' enabled because it runs in a new, split window
2250func Test_normal_ctrl_w_g_ctrl_square_bracket_right()
2251 call s:reset_all_buffers()
2252
2253 set tags=Xtags
2254 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2255 \ "one\tXfile\t1",
2256 \ "three\tXfile\t3",
2257 \ "two\tXfile\t2"],
2258 \ "Xtags")
2259 call writefile(["one", "two", "three"], "Xfile")
2260 call writefile(["one"], "Xother")
2261 edit Xother
2262
2263 set winfixbuf
2264
2265 let l:current_windows = s:get_windows_count()
2266 execute "normal \<C-w>g\<C-]>"
2267 call assert_equal(l:current_windows + 1, s:get_windows_count())
2268
2269 set tags&
2270 call delete("Xtags")
2271 call delete("Xfile")
2272 call delete("Xother")
2273endfunc
2274
2275" Fail to jump to a tag with <C-]> if 'winfixbuf' is enabled
2276func Test_normal_gt()
2277 call s:reset_all_buffers()
2278
2279 set tags=Xtags
2280 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2281 \ "one\tXfile\t1",
2282 \ "three\tXfile\t3",
2283 \ "two\tXfile\t2"],
2284 \ "Xtags")
2285 call writefile(["one", "two", "three"], "Xfile")
2286 call writefile(["one", "two", "three"], "Xother")
2287 edit Xother
2288
2289 set winfixbuf
2290
2291 let l:current = bufnr()
2292
2293 call assert_fails("normal \<C-]>", "E1513:")
2294 call assert_equal(l:current, bufnr())
2295
2296 set tags&
2297 call delete("Xtags")
2298 call delete("Xfile")
2299 call delete("Xother")
2300endfunc
2301
2302" Prevent gF from switching a 'winfixbuf' window's buffer
2303func Test_normal_gF()
2304 call s:reset_all_buffers()
2305
2306 let l:file = tempname()
2307 call append(0, [l:file])
2308 call writefile([], l:file)
2309 " Place the cursor onto the line that has `l:file`
2310 normal gg
2311 " Prevent Vim from erroring with "No write since last change @ command
2312 " line" when we try to call gF, later.
2313 set hidden
2314
2315 set winfixbuf
2316
2317 let l:buffer = bufnr()
2318
2319 call assert_fails("normal gF", "E1513:")
2320 call assert_equal(l:buffer, bufnr())
2321
2322 set nowinfixbuf
2323
2324 normal gF
2325 call assert_notequal(l:buffer, bufnr())
2326
2327 call delete(l:file)
2328endfunc
2329
2330" Prevent gf from switching a 'winfixbuf' window's buffer
2331func Test_normal_gf()
2332 call s:reset_all_buffers()
2333
2334 let l:file = tempname()
2335 call append(0, [l:file])
2336 call writefile([], l:file)
2337 " Place the cursor onto the line that has `l:file`
2338 normal gg
2339 " Prevent Vim from erroring with "No write since last change @ command
2340 " line" when we try to call gf, later.
2341 set hidden
2342
2343 set winfixbuf
2344
2345 let l:buffer = bufnr()
2346
2347 call assert_fails("normal gf", "E1513:")
2348 call assert_equal(l:buffer, bufnr())
2349
2350 set nowinfixbuf
2351
2352 normal gf
2353 call assert_notequal(l:buffer, bufnr())
2354
2355 call delete(l:file)
2356endfunc
2357
2358" Fail "goto file under the cursor" (using [f, which is the same as `:normal gf`)
2359func Test_normal_square_bracket_left_f()
2360 call s:reset_all_buffers()
2361
2362 let l:file = tempname()
2363 call append(0, [l:file])
2364 call writefile([], l:file)
2365 " Place the cursor onto the line that has `l:file`
2366 normal gg
2367 " Prevent Vim from erroring with "No write since last change @ command
2368 " line" when we try to call gf, later.
2369 set hidden
2370
2371 set winfixbuf
2372
2373 let l:buffer = bufnr()
2374
2375 call assert_fails("normal [f", "E1513:")
2376 call assert_equal(l:buffer, bufnr())
2377
2378 set nowinfixbuf
2379
2380 normal [f
2381 call assert_notequal(l:buffer, bufnr())
2382
2383 call delete(l:file)
2384endfunc
2385
2386" Fail to go to a C macro with [<C-d> if 'winfixbuf' is enabled
2387func Test_normal_square_bracket_left_ctrl_d()
2388 call s:reset_all_buffers()
2389
2390 let l:include_file = tempname() . ".h"
2391 call writefile(["min(1, 12);",
2392 \ '#include "' . l:include_file . '"'
2393 \ ],
2394 \ "main.c")
2395 call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
2396 edit main.c
2397 normal ]\<C-d>
2398
2399 set winfixbuf
2400
2401 let l:current = bufnr()
2402
2403 call assert_fails("normal [\<C-d>", "E1513:")
2404 call assert_equal(l:current, bufnr())
2405
2406 set nowinfixbuf
2407
2408 execute "normal [\<C-d>"
2409 call assert_notequal(l:current, bufnr())
2410
2411 call delete("main.c")
2412 call delete(l:include_file)
2413endfunc
2414
2415" Fail to go to a C macro with ]<C-d> if 'winfixbuf' is enabled
2416func Test_normal_square_bracket_right_ctrl_d()
2417 call s:reset_all_buffers()
2418
2419 let l:include_file = tempname() . ".h"
2420 call writefile(["min(1, 12);",
2421 \ '#include "' . l:include_file . '"'
2422 \ ],
2423 \ "main.c")
2424 call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
2425 edit main.c
2426
2427 set winfixbuf
2428
2429 let l:current = bufnr()
2430
2431 call assert_fails("normal ]\<C-d>", "E1513:")
2432 call assert_equal(l:current, bufnr())
2433
2434 set nowinfixbuf
2435
2436 execute "normal ]\<C-d>"
2437 call assert_notequal(l:current, bufnr())
2438
2439 call delete("main.c")
2440 call delete(l:include_file)
2441endfunc
2442
2443" Fail to go to a C macro with [<C-i> if 'winfixbuf' is enabled
2444func Test_normal_square_bracket_left_ctrl_i()
2445 call s:reset_all_buffers()
2446
2447 let l:include_file = tempname() . ".h"
2448 call writefile(['#include "' . l:include_file . '"',
2449 \ "min(1, 12);",
2450 \ ],
2451 \ "main.c")
2452 call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
2453 edit main.c
2454 " Move to the line with `min(1, 12);` on it"
2455 normal j
2456
2457 set define=^\\s*#\\s*define
2458 set include=^\\s*#\\s*include
2459 set path=.,/usr/include,,
2460
2461 let l:current = bufnr()
2462
2463 set winfixbuf
2464
2465 call assert_fails("normal [\<C-i>", "E1513:")
2466
2467 set nowinfixbuf
2468
2469 execute "normal [\<C-i>"
2470 call assert_notequal(l:current, bufnr())
2471
2472 set define&
2473 set include&
2474 set path&
2475 call delete("main.c")
2476 call delete(l:include_file)
2477endfunc
2478
2479" Fail to go to a C macro with ]<C-i> if 'winfixbuf' is enabled
2480func Test_normal_square_bracket_right_ctrl_i()
2481 call s:reset_all_buffers()
2482
2483 let l:include_file = tempname() . ".h"
2484 call writefile(["min(1, 12);",
2485 \ '#include "' . l:include_file . '"'
2486 \ ],
2487 \ "main.c")
2488 call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
2489 edit main.c
2490
2491 set winfixbuf
2492
2493 set define=^\\s*#\\s*define
2494 set include=^\\s*#\\s*include
2495 set path=.,/usr/include,,
2496
2497 let l:current = bufnr()
2498
2499 call assert_fails("normal ]\<C-i>", "E1513:")
2500 call assert_equal(l:current, bufnr())
2501
2502 set nowinfixbuf
2503
2504 execute "normal ]\<C-i>"
2505 call assert_notequal(l:current, bufnr())
2506
2507 set define&
2508 set include&
2509 set path&
2510 call delete("main.c")
2511 call delete(l:include_file)
2512endfunc
2513
2514" Fail "goto file under the cursor" (using ]f, which is the same as `:normal gf`)
2515func Test_normal_square_bracket_right_f()
2516 call s:reset_all_buffers()
2517
2518 let l:file = tempname()
2519 call append(0, [l:file])
2520 call writefile([], l:file)
2521 " Place the cursor onto the line that has `l:file`
2522 normal gg
2523 " Prevent Vim from erroring with "No write since last change @ command
2524 " line" when we try to call gf, later.
2525 set hidden
2526
2527 set winfixbuf
2528
2529 let l:buffer = bufnr()
2530
2531 call assert_fails("normal ]f", "E1513:")
2532 call assert_equal(l:buffer, bufnr())
2533
2534 set nowinfixbuf
2535
2536 normal ]f
2537 call assert_notequal(l:buffer, bufnr())
2538
2539 call delete(l:file)
2540endfunc
2541
2542" Fail to jump to a tag with v<C-]> if 'winfixbuf' is enabled
2543func Test_normal_v_ctrl_square_bracket_right()
2544 call s:reset_all_buffers()
2545
2546 set tags=Xtags
2547 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2548 \ "one\tXfile\t1",
2549 \ "three\tXfile\t3",
2550 \ "two\tXfile\t2"],
2551 \ "Xtags")
2552 call writefile(["one", "two", "three"], "Xfile")
2553 call writefile(["one"], "Xother")
2554 edit Xother
2555
2556 set winfixbuf
2557
2558 let l:current = bufnr()
2559
2560 call assert_fails("normal v\<C-]>", "E1513:")
2561 call assert_equal(l:current, bufnr())
2562
2563 set tags&
2564 call delete("Xtags")
2565 call delete("Xfile")
2566 call delete("Xother")
2567endfunc
2568
2569" Fail to jump to a tag with vg<C-]> if 'winfixbuf' is enabled
2570func Test_normal_v_g_ctrl_square_bracket_right()
2571 call s:reset_all_buffers()
2572
2573 set tags=Xtags
2574 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2575 \ "one\tXfile\t1",
2576 \ "three\tXfile\t3",
2577 \ "two\tXfile\t2"],
2578 \ "Xtags")
2579 call writefile(["one", "two", "three"], "Xfile")
2580 call writefile(["one"], "Xother")
2581 edit Xother
2582
2583 set winfixbuf
2584
2585 let l:current = bufnr()
2586
2587 call assert_fails("normal vg\<C-]>", "E1513:")
2588 call assert_equal(l:current, bufnr())
2589
2590 set tags&
2591 call delete("Xtags")
2592 call delete("Xfile")
2593 call delete("Xother")
2594endfunc
2595
2596" Allow :pedit because, unlike :edit, it uses a separate window
2597func Test_pedit()
2598 call s:reset_all_buffers()
2599
2600 let l:other = s:make_buffer_pairs()
2601
2602 pedit other
2603
2604 execute "normal \<C-w>w"
2605 call assert_equal(l:other, bufnr())
2606endfunc
2607
2608" Fail :pop but :pop! is allowed
2609func Test_pop()
2610 call s:reset_all_buffers()
2611
2612 set tags=Xtags
2613 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2614 \ "thesame\tXfile\t1;\"\td\tfile:",
2615 \ "thesame\tXfile\t2;\"\td\tfile:",
2616 \ "thesame\tXfile\t3;\"\td\tfile:",
2617 \ ],
2618 \ "Xtags")
2619 call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
2620 call writefile(["thesame one"], "Xother")
2621 edit Xother
2622
2623 tag thesame
2624
2625 set winfixbuf
2626
2627 let l:current = bufnr()
2628
2629 call assert_fails("pop", "E1513:")
2630 call assert_equal(l:current, bufnr())
2631
2632 pop!
2633 call assert_notequal(l:current, bufnr())
2634
2635 set tags&
2636 call delete("Xtags")
2637 call delete("Xfile")
2638 call delete("Xother")
2639endfunc
2640
2641" Fail :previous but :previous! is allowed
2642func Test_previous()
2643 call s:reset_all_buffers()
2644
2645 let [l:first, _] = s:make_args_list()
2646 next!
2647
2648 call assert_fails("previous", "E1513:")
2649 call assert_notequal(l:first, bufnr())
2650
2651 previous!
2652 call assert_equal(l:first, bufnr())
2653endfunc
2654
Sean Dewar769eb2d2024-03-07 21:37:50 +01002655" Fail pyxdo if it changes a window with 'winfixbuf' is set
2656func Test_pythonx_pyxdo()
Colin Kennedy21570352024-03-03 16:16:47 +01002657 CheckFeature pythonx
2658 call s:reset_all_buffers()
2659
2660 enew
2661 file first
2662 let g:_previous_buffer = bufnr()
2663
2664 enew
2665 file second
2666
2667 set winfixbuf
2668
Sean Dewar769eb2d2024-03-07 21:37:50 +01002669 pythonx << EOF
Colin Kennedy21570352024-03-03 16:16:47 +01002670import vim
2671
Sean Dewar769eb2d2024-03-07 21:37:50 +01002672def test_winfixbuf_Test_pythonx_pyxdo_set_buffer():
Colin Kennedy21570352024-03-03 16:16:47 +01002673 buffer = vim.vars['_previous_buffer']
2674 vim.current.buffer = vim.buffers[buffer]
2675EOF
2676
2677 try
Sean Dewar769eb2d2024-03-07 21:37:50 +01002678 pyxdo test_winfixbuf_Test_pythonx_pyxdo_set_buffer()
Christian Brabandt0a32b882024-03-13 20:59:27 +01002679 catch /Vim\%((\a\+)\)\=:E1513:/
Colin Kennedy21570352024-03-03 16:16:47 +01002680 let l:caught = 1
2681 endtry
2682
2683 call assert_equal(1, l:caught)
2684
2685 unlet g:_previous_buffer
2686endfunc
2687
Sean Dewar769eb2d2024-03-07 21:37:50 +01002688" Fail pyxfile if it changes a window with 'winfixbuf' is set
2689func Test_pythonx_pyxfile()
Colin Kennedy21570352024-03-03 16:16:47 +01002690 CheckFeature pythonx
2691 call s:reset_all_buffers()
2692
2693 enew
2694 file first
2695 let g:_previous_buffer = bufnr()
2696
2697 enew
2698 file second
2699
2700 set winfixbuf
2701
2702 call writefile(["import vim",
2703 \ "buffer = vim.vars['_previous_buffer']",
2704 \ "vim.current.buffer = vim.buffers[buffer]",
2705 \ ],
2706 \ "file.py")
2707
2708 try
Sean Dewar769eb2d2024-03-07 21:37:50 +01002709 pyxfile file.py
Christian Brabandt0a32b882024-03-13 20:59:27 +01002710 catch /Vim\%((\a\+)\)\=:E1513:/
Colin Kennedy21570352024-03-03 16:16:47 +01002711 let l:caught = 1
2712 endtry
2713
2714 call assert_equal(1, l:caught)
2715
2716 call delete("file.py")
2717 unlet g:_previous_buffer
2718endfunc
2719
2720" Fail vim.current.buffer if 'winfixbuf' is set
Sean Dewar769eb2d2024-03-07 21:37:50 +01002721func Test_pythonx_vim_current_buffer()
Colin Kennedy21570352024-03-03 16:16:47 +01002722 CheckFeature pythonx
2723 call s:reset_all_buffers()
2724
2725 enew
2726 file first
2727 let g:_previous_buffer = bufnr()
2728
2729 enew
2730 file second
2731
2732 let l:caught = 0
2733
2734 set winfixbuf
2735
2736 try
Sean Dewar769eb2d2024-03-07 21:37:50 +01002737 pythonx << EOF
Colin Kennedy21570352024-03-03 16:16:47 +01002738import vim
2739
2740buffer = vim.vars["_previous_buffer"]
2741vim.current.buffer = vim.buffers[buffer]
2742EOF
Christian Brabandt0a32b882024-03-13 20:59:27 +01002743 catch /Vim\%((\a\+)\)\=:E1513:/
Colin Kennedy21570352024-03-03 16:16:47 +01002744 let l:caught = 1
2745 endtry
2746
2747 call assert_equal(1, l:caught)
2748 unlet g:_previous_buffer
2749endfunc
2750
2751" Ensure remapping to a disabled action still triggers failures
2752func Test_remap_key_fail()
2753 call s:reset_all_buffers()
2754
2755 enew
2756 file first
2757 let l:first = bufnr()
2758
2759 enew
2760 file current
2761 let l:current = bufnr()
2762
2763 set winfixbuf
2764
2765 nnoremap g <C-^>
2766
2767 call assert_fails("normal g", "E1513:")
2768 call assert_equal(l:current, bufnr())
2769
2770 nunmap g
2771endfunc
2772
2773" Ensure remapping a disabled key to something valid does trigger any failures
2774func Test_remap_key_pass()
2775 call s:reset_all_buffers()
2776
2777 enew
2778 file first
2779 let l:first = bufnr()
2780
2781 enew
2782 file current
2783 let l:current = bufnr()
2784
2785 set winfixbuf
2786
2787 call assert_fails("normal \<C-^>", "E1513:")
2788 call assert_equal(l:current, bufnr())
2789
2790 " Disallow <C-^> by default but allow it if the command does something else
2791 nnoremap <C-^> :echo "hello!"
2792
2793 execute "normal \<C-^>"
2794 call assert_equal(l:current, bufnr())
2795
2796 nunmap <C-^>
2797endfunc
2798
2799" Fail :rewind but :rewind! is allowed
2800func Test_rewind()
2801 call s:reset_all_buffers()
2802
2803 let [l:first, _] = s:make_args_list()
2804 next!
2805
2806 call assert_fails("rewind", "E1513:")
2807 call assert_notequal(l:first, bufnr())
2808
2809 rewind!
2810 call assert_equal(l:first, bufnr())
2811endfunc
2812
2813" Allow :sblast because it opens the buffer in a new, split window
2814func Test_sblast()
2815 call s:reset_all_buffers()
2816
2817 let l:other = s:make_buffer_pairs(1)
2818 bfirst!
2819 let l:current = bufnr()
2820
2821 sblast
2822 call assert_equal(l:other, bufnr())
2823endfunc
2824
2825" Fail :sbprevious but :sbprevious! is allowed
2826func Test_sbprevious()
2827 call s:reset_all_buffers()
2828
2829 let l:other = s:make_buffer_pairs()
2830 let l:current = bufnr()
2831
2832 sbprevious
2833 call assert_equal(l:other, bufnr())
2834endfunc
2835
2836" Make sure 'winfixbuf' can be set using 'winfixbuf' or 'wfb'
2837func Test_short_option()
2838 call s:reset_all_buffers()
2839
2840 call s:make_buffer_pairs()
2841
2842 set winfixbuf
2843 call assert_fails("edit something_else", "E1513")
2844
2845 set nowinfixbuf
2846 set wfb
2847 call assert_fails("edit another_place", "E1513")
2848
2849 set nowfb
2850 edit last_place
2851endfunc
2852
2853" Allow :snext because it makes a new window
2854func Test_snext()
2855 call s:reset_all_buffers()
2856
2857 let [l:first, _] = s:make_args_list()
2858 first!
2859
2860 let l:current_window = win_getid()
2861
2862 snext
2863 call assert_notequal(l:current_window, win_getid())
2864 call assert_notequal(l:first, bufnr())
2865endfunc
2866
2867" Ensure the first has 'winfixbuf' and a new split window is 'nowinfixbuf'
2868func Test_split_window()
2869 call s:reset_all_buffers()
2870
2871 split
2872 execute "normal \<C-w>j"
2873
2874 set winfixbuf
2875
2876 let l:winfix_window_1 = win_getid()
2877 vsplit
2878 let l:winfix_window_2 = win_getid()
2879
2880 call assert_equal(1, getwinvar(l:winfix_window_1, "&winfixbuf"))
2881 call assert_equal(0, getwinvar(l:winfix_window_2, "&winfixbuf"))
2882endfunc
2883
2884" Fail :tNext but :tNext! is allowed
2885func Test_tNext()
2886 call s:reset_all_buffers()
2887
2888 set tags=Xtags
2889 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2890 \ "thesame\tXfile\t1;\"\td\tfile:",
2891 \ "thesame\tXfile\t2;\"\td\tfile:",
2892 \ "thesame\tXfile\t3;\"\td\tfile:",
2893 \ ],
2894 \ "Xtags")
2895 call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
2896 call writefile(["thesame one"], "Xother")
2897 edit Xother
2898
2899 tag thesame
2900 execute "normal \<C-^>"
2901 tnext!
2902
2903 set winfixbuf
2904
2905 let l:current = bufnr()
2906
2907 call assert_fails("tNext", "E1513:")
2908 call assert_equal(l:current, bufnr())
2909
2910 tNext!
2911
2912 set tags&
2913 call delete("Xtags")
2914 call delete("Xfile")
2915 call delete("Xother")
2916endfunc
2917
2918" Call :tabdo and choose the next available 'nowinfixbuf' window.
2919func Test_tabdo_choose_available_window()
2920 call s:reset_all_buffers()
2921
2922 let [l:first, _] = s:make_args_list()
2923
2924 " Make a split window that is 'nowinfixbuf' but make it the second-to-last
2925 " window so that :tabdo will first try the 'winfixbuf' window, pass over it,
2926 " and prefer the other 'nowinfixbuf' window, instead.
2927 "
2928 " +-------------------+
2929 " | 'nowinfixbuf' |
2930 " +-------------------+
2931 " | 'winfixbuf' | <-- Cursor is here
2932 " +-------------------+
2933 split
2934 let l:nowinfixbuf_window = win_getid()
2935 " Move to the 'winfixbuf' window now
2936 execute "normal \<C-w>j"
2937 let l:winfixbuf_window = win_getid()
2938
2939 let l:expected_windows = s:get_windows_count()
2940 tabdo echo ''
2941 call assert_equal(l:nowinfixbuf_window, win_getid())
2942 call assert_equal(l:first, bufnr())
2943 call assert_equal(l:expected_windows, s:get_windows_count())
2944endfunc
2945
2946" Call :tabdo and create a new split window if all available windows are 'winfixbuf'.
2947func Test_tabdo_make_new_window()
2948 call s:reset_all_buffers()
2949
2950 let [l:first, _] = s:make_buffers_list()
2951 execute "buffer! " . l:first
2952
2953 let l:current = win_getid()
2954 let l:current_windows = s:get_windows_count()
2955
2956 tabdo echo ''
2957 call assert_notequal(l:current, win_getid())
2958 call assert_equal(l:first, bufnr())
2959 execute "normal \<C-w>j"
2960 call assert_equal(l:first, bufnr())
2961 call assert_equal(l:current_windows + 1, s:get_windows_count())
2962endfunc
2963
2964" Fail :tag but :tag! is allowed
2965func Test_tag()
2966 call s:reset_all_buffers()
2967
2968 set tags=Xtags
2969 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
2970 \ "one\tXfile\t1",
2971 \ "three\tXfile\t3",
2972 \ "two\tXfile\t2"],
2973 \ "Xtags")
2974 call writefile(["one", "two", "three"], "Xfile")
2975 call writefile(["one"], "Xother")
2976 edit Xother
2977
2978 set winfixbuf
2979
2980 let l:current = bufnr()
2981
2982 call assert_fails("tag one", "E1513:")
2983 call assert_equal(l:current, bufnr())
2984
2985 tag! one
2986 call assert_notequal(l:current, bufnr())
2987
2988 set tags&
2989 call delete("Xtags")
2990 call delete("Xfile")
2991 call delete("Xother")
2992endfunc
2993
2994
2995" Fail :tfirst but :tfirst! is allowed
2996func Test_tfirst()
2997 call s:reset_all_buffers()
2998
2999 set tags=Xtags
3000 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
3001 \ "one\tXfile\t1",
3002 \ "three\tXfile\t3",
3003 \ "two\tXfile\t2"],
3004 \ "Xtags")
3005 call writefile(["one", "two", "three"], "Xfile")
3006 call writefile(["one"], "Xother")
3007 edit Xother
3008
3009 set winfixbuf
3010
3011 let l:current = bufnr()
3012
3013 call assert_fails("tfirst", "E1513:")
3014 call assert_equal(l:current, bufnr())
3015
3016 tfirst!
3017 call assert_notequal(l:current, bufnr())
3018
3019 set tags&
3020 call delete("Xtags")
3021 call delete("Xfile")
3022 call delete("Xother")
3023endfunc
3024
3025" Fail :tjump but :tjump! is allowed
3026func Test_tjump()
3027 call s:reset_all_buffers()
3028
3029 set tags=Xtags
3030 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
3031 \ "one\tXfile\t1",
3032 \ "three\tXfile\t3",
3033 \ "two\tXfile\t2"],
3034 \ "Xtags")
3035 call writefile(["one", "two", "three"], "Xfile")
3036 call writefile(["one"], "Xother")
3037 edit Xother
3038
3039 set winfixbuf
3040
3041 let l:current = bufnr()
3042
3043 call assert_fails("tjump one", "E1513:")
3044 call assert_equal(l:current, bufnr())
3045
3046 tjump! one
3047 call assert_notequal(l:current, bufnr())
3048
3049 set tags&
3050 call delete("Xtags")
3051 call delete("Xfile")
3052 call delete("Xother")
3053endfunc
3054
3055" Fail :tlast but :tlast! is allowed
3056func Test_tlast()
3057 call s:reset_all_buffers()
3058
3059 set tags=Xtags
3060 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
3061 \ "one\tXfile\t1",
3062 \ "three\tXfile\t3",
3063 \ "two\tXfile\t2"],
3064 \ "Xtags")
3065 call writefile(["one", "two", "three"], "Xfile")
3066 edit Xfile
3067 tjump one
3068 edit Xfile
3069
3070 set winfixbuf
3071
3072 let l:current = bufnr()
3073
3074 call assert_fails("tlast", "E1513:")
3075 call assert_equal(l:current, bufnr())
3076
3077 tlast!
3078 call assert_equal(l:current, bufnr())
3079
3080 set tags&
3081 call delete("Xtags")
3082 call delete("Xfile")
3083endfunc
3084
3085" Fail :tnext but :tnext! is allowed
3086func Test_tnext()
3087 call s:reset_all_buffers()
3088
3089 set tags=Xtags
3090 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
3091 \ "thesame\tXfile\t1;\"\td\tfile:",
3092 \ "thesame\tXfile\t2;\"\td\tfile:",
3093 \ "thesame\tXfile\t3;\"\td\tfile:",
3094 \ ],
3095 \ "Xtags")
3096 call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
3097 call writefile(["thesame one"], "Xother")
3098 edit Xother
3099
3100 tag thesame
3101 execute "normal \<C-^>"
3102
3103 set winfixbuf
3104
3105 let l:current = bufnr()
3106
3107 call assert_fails("tnext", "E1513:")
3108 call assert_equal(l:current, bufnr())
3109
3110 tnext!
3111 call assert_notequal(l:current, bufnr())
3112
3113 set tags&
3114 call delete("Xtags")
3115 call delete("Xfile")
3116 call delete("Xother")
3117endfunc
3118
3119" Fail :tprevious but :tprevious! is allowed
3120func Test_tprevious()
3121 call s:reset_all_buffers()
3122
3123 set tags=Xtags
3124 call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
3125 \ "thesame\tXfile\t1;\"\td\tfile:",
3126 \ "thesame\tXfile\t2;\"\td\tfile:",
3127 \ "thesame\tXfile\t3;\"\td\tfile:",
3128 \ ],
3129 \ "Xtags")
3130 call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
3131 call writefile(["thesame one"], "Xother")
3132 edit Xother
3133
3134 tag thesame
3135 execute "normal \<C-^>"
3136 tnext!
3137
3138 set winfixbuf
3139
3140 let l:current = bufnr()
3141
3142 call assert_fails("tprevious", "E1513:")
3143 call assert_equal(l:current, bufnr())
3144
3145 tprevious!
3146
3147 set tags&
3148 call delete("Xtags")
3149 call delete("Xfile")
3150 call delete("Xother")
3151endfunc
3152
3153" Fail :view but :view! is allowed
3154func Test_view()
3155 call s:reset_all_buffers()
3156
3157 let l:other = s:make_buffer_pairs()
3158 let l:current = bufnr()
3159
3160 call assert_fails("view other", "E1513:")
3161 call assert_equal(l:current, bufnr())
3162
3163 view! other
3164 call assert_equal(l:other, bufnr())
3165endfunc
3166
3167" Fail :visual but :visual! is allowed
3168func Test_visual()
3169 call s:reset_all_buffers()
3170
3171 let l:other = s:make_buffer_pairs()
3172 let l:current = bufnr()
3173
3174 call assert_fails("visual other", "E1513:")
3175 call assert_equal(l:current, bufnr())
3176
3177 visual! other
3178 call assert_equal(l:other, bufnr())
3179endfunc
3180
3181" Fail :vimgrep but :vimgrep! is allowed
3182func Test_vimgrep()
3183 CheckFeature quickfix
3184 call s:reset_all_buffers()
3185
3186 edit first.unittest
3187 call append(0, ["some-search-term"])
3188 write
3189
3190 edit winfix.unittest
3191 call append(0, ["some-search-term"])
3192 write
3193 let l:current = bufnr()
3194
3195 set winfixbuf
3196
3197 edit! last.unittest
3198 call append(0, ["some-search-term"])
3199 write
3200 let l:last = bufnr()
3201
3202 buffer! winfix.unittest
3203
3204 call assert_fails("vimgrep /some-search-term/ *.unittest")
3205 call assert_equal(l:current, bufnr())
3206
3207 " Don't error and also do swap to the first match because ! was included
3208 vimgrep! /some-search-term/ *.unittest
3209 call assert_notequal(l:current, bufnr())
3210
3211 call delete("first.unittest")
3212 call delete("winfix.unittest")
3213 call delete("last.unittest")
3214endfunc
3215
3216" Fail :vimgrepadd but ::vimgrepadd! is allowed
3217func Test_vimgrepadd()
3218 CheckFeature quickfix
3219 call s:reset_all_buffers()
3220
3221 edit first.unittest
3222 call append(0, ["some-search-term"])
3223 write
3224
3225 edit winfix.unittest
3226 call append(0, ["some-search-term"])
3227 write
3228 let l:current = bufnr()
3229
3230 set winfixbuf
3231
3232 edit! last.unittest
3233 call append(0, ["some-search-term"])
3234 write
3235 let l:last = bufnr()
3236
3237 buffer! winfix.unittest
3238
3239 call assert_fails("vimgrepadd /some-search-term/ *.unittest")
3240 call assert_equal(l:current, bufnr())
3241
3242 vimgrepadd! /some-search-term/ *.unittest
3243 call assert_notequal(l:current, bufnr())
3244 call delete("first.unittest")
3245 call delete("winfix.unittest")
3246 call delete("last.unittest")
3247endfunc
3248
3249" Fail :wNext but :wNext! is allowed
3250func Test_wNext()
3251 call s:reset_all_buffers()
3252
3253 let [l:first, _] = s:make_args_list()
3254 next!
3255
3256 call assert_fails("wNext", "E1513:")
3257 call assert_notequal(l:first, bufnr())
3258
3259 wNext!
3260 call assert_equal(l:first, bufnr())
3261
3262 call delete("first")
3263 call delete("middle")
3264 call delete("last")
3265endfunc
3266
3267" Allow :windo unless `:windo foo` would change a 'winfixbuf' window's buffer
3268func Test_windo()
3269 call s:reset_all_buffers()
3270
3271 let l:current_window = win_getid()
3272 let l:current_buffer = bufnr()
3273 split
3274 enew
3275 file some_other_buffer
3276
3277 set winfixbuf
3278
3279 let l:current = win_getid()
3280
3281 windo echo ''
3282 call assert_equal(l:current_window, win_getid())
3283
3284 call assert_fails('execute "windo buffer ' . l:current_buffer . '"', "E1513:")
3285 call assert_equal(l:current_window, win_getid())
3286
3287 execute "windo buffer! " . l:current_buffer
3288 call assert_equal(l:current_window, win_getid())
3289endfunc
3290
3291" Fail :wnext but :wnext! is allowed
3292func Test_wnext()
3293 call s:reset_all_buffers()
3294
3295 let [_, l:last] = s:make_args_list()
3296 next!
3297
3298 call assert_fails("wnext", "E1513:")
3299 call assert_notequal(l:last, bufnr())
3300
3301 wnext!
3302 call assert_equal(l:last, bufnr())
3303
3304 call delete("first")
3305 call delete("middle")
3306 call delete("last")
3307endfunc
3308
3309" Fail :wprevious but :wprevious! is allowed
3310func Test_wprevious()
3311 call s:reset_all_buffers()
3312
3313 let [l:first, _] = s:make_args_list()
3314 next!
3315
3316 call assert_fails("wprevious", "E1513:")
3317 call assert_notequal(l:first, bufnr())
3318
3319 wprevious!
3320 call assert_equal(l:first, bufnr())
3321
3322 call delete("first")
3323 call delete("middle")
3324 call delete("last")
3325endfunc
Sean Dewar5131f222024-03-04 19:09:26 +01003326
3327func Test_quickfix_switchbuf_invalid_prevwin()
3328 call s:reset_all_buffers()
3329
Sean Dewar4bb505e2024-03-05 20:39:07 +01003330 call s:make_simple_quickfix()
3331 call assert_equal(1, getqflist(#{idx: 0}).idx)
Sean Dewar5131f222024-03-04 19:09:26 +01003332
3333 set switchbuf=uselast
3334 split
3335 copen
3336 execute winnr('#') 'quit'
Sean Dewar4bb505e2024-03-05 20:39:07 +01003337 call assert_equal(2, winnr('$'))
Sean Dewar5131f222024-03-04 19:09:26 +01003338
Sean Dewar4bb505e2024-03-05 20:39:07 +01003339 cnext " Would've triggered a null pointer member access
3340 call assert_equal(2, getqflist(#{idx: 0}).idx)
3341
Sean Dewar5131f222024-03-04 19:09:26 +01003342 set switchbuf&
3343endfunc
3344
Sean Dewar4bb505e2024-03-05 20:39:07 +01003345func Test_listdo_goto_prevwin()
3346 call s:reset_all_buffers()
3347 call s:make_buffers_list()
3348
3349 new
3350 call assert_equal(0, &winfixbuf)
3351 wincmd p
3352 call assert_equal(1, &winfixbuf)
3353 call assert_notequal(bufnr(), bufnr('#'))
3354
3355 augroup ListDoGotoPrevwin
3356 au!
3357 au BufLeave * let s:triggered = 1
3358 \| call assert_equal(bufnr(), winbufnr(winnr()))
3359 augroup END
3360 " Should correctly switch to the window without 'winfixbuf', and curbuf should
3361 " be consistent with curwin->w_buffer for autocommands.
3362 bufdo "
3363 call assert_equal(0, &winfixbuf)
3364 call assert_equal(1, s:triggered)
3365 unlet! s:triggered
3366 au! ListDoGotoPrevwin
3367
3368 set winfixbuf
3369 wincmd p
3370 call assert_equal(2, winnr('$'))
3371 " Both curwin and prevwin have 'winfixbuf' set, so should split a new window
3372 " without it set.
3373 bufdo "
3374 call assert_equal(0, &winfixbuf)
3375 call assert_equal(3, winnr('$'))
3376
3377 quit
3378 call assert_equal(2, winnr('$'))
3379 call assert_equal(1, &winfixbuf)
3380 augroup ListDoGotoPrevwin
3381 au!
3382 au WinEnter * ++once set winfixbuf
3383 augroup END
3384 " Same as before, but naughty autocommands set 'winfixbuf' for the new window.
3385 " :bufdo should give up in this case.
3386 call assert_fails('bufdo "', 'E1513:')
3387
3388 au! ListDoGotoPrevwin
3389 augroup! ListDoGotoPrevwin
3390endfunc
3391
3392func Test_quickfix_changed_split_failed()
3393 call s:reset_all_buffers()
3394
3395 call s:make_simple_quickfix()
3396 call assert_equal(1, winnr('$'))
3397
3398 " Quickfix code will open a split in an attempt to get a 'nowinfixbuf' window
3399 " to switch buffers in. Interfere with things by setting 'winfixbuf' in it.
3400 augroup QfChanged
3401 au!
3402 au WinEnter * ++once call assert_equal(2, winnr('$'))
3403 \| set winfixbuf | call setqflist([], 'f')
3404 augroup END
3405 call assert_fails('cnext', ['E1513:', 'E925:'])
3406 " Check that the split was automatically closed.
3407 call assert_equal(1, winnr('$'))
3408
3409 au! QfChanged
3410 augroup! QfChanged
3411endfunc
3412
Sean Dewar769eb2d2024-03-07 21:37:50 +01003413func Test_bufdo_cnext_splitwin_fails()
Christian Brabandtaf7ae812024-03-06 19:31:39 +01003414 call s:reset_all_buffers()
Sean Dewar769eb2d2024-03-07 21:37:50 +01003415 call s:make_simple_quickfix()
3416 call assert_equal(1, getqflist(#{idx: 0}).idx)
Christian Brabandtaf7ae812024-03-06 19:31:39 +01003417 " Make sure there is not enough room to
3418 " split the winfixedbuf window
3419 let &winheight=&lines
3420 let &winminheight=&lines-2
Sean Dewar769eb2d2024-03-07 21:37:50 +01003421 " Still want E1513, or it may not be clear why a split was attempted and why
3422 " it failing caused the commands to abort.
3423 call assert_fails(':bufdo echo 1', ['E36:', 'E1513:'])
3424 call assert_fails(':cnext', ['E36:', 'E1513:'])
3425 " Ensure the entry didn't change.
3426 call assert_equal(1, getqflist(#{idx: 0}).idx)
Christian Brabandtaf7ae812024-03-06 19:31:39 +01003427 set winminheight&vim winheight&vim
3428endfunc
3429
Sean Dewar5131f222024-03-04 19:09:26 +01003430" vim: shiftwidth=2 sts=2 expandtab