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