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