Skip to content

Commit 4097805

Browse files
committed
Fixing buffer menu having stale items with terminal / cmdline window
Currently, when using special buffers like terminals / command-line window / quickfix / location list, these buffers will get added to the "Buffers" menu, but they don't get removed when the buffers are gone, leaving stale menu items. Fix buffer menu to be more robust. 1. Currently the buffer menu works by using the buffer name to add/remove the entries, but it's error-prone because the buffer could have been renamed under-the-hood. While it uses BufFilePre/Post autocommands to handle normal buffer renames, it doesn't work for the command-line window (accessible via `q:`) which gets renamed without sending the autocommand. Instead, change the menus to cached a dictionary a bufnum -> menu name, so it will always know how to remove a buffer from itself. 2. Add BufFilePre/Post autocommands to command-line windows when it changes the buffer name to "[Command Line]". 3. Add BufFilePre/Post autocommands to terminal windows when it changes the buffer name to the command, e.g. "!/bin/zsh". Either (1) or (2)+(3) will fix the issue, but just doing all of them as this seems like the right thing to do (2 + 3) also means the menu items show the correct names instead of just saying "[No Name]". This doesn't fix the following which needs to be fixed later: 1. Quickfix and Location List don't send BufDeleted autocmds. This leads to them also leaving stale buffer menu items as there's no way for the script to know that those buffers are gone. Also add unit tests for cmdline-win / terminal buffer menus Note: This fix misc test_cmdline failures in MacVim due to the menu item not being able to be removed. This is a duplicate of vim/vim#5787
1 parent 984a905 commit 4097805

File tree

5 files changed

+59
-15
lines changed

5 files changed

+59
-15
lines changed

runtime/menu.vim

+16-8
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,11 @@ if !exists("no_buffers_menu")
711711
" startup faster.
712712
let s:bmenu_wait = 1
713713

714+
" dictionary of buffer ID to name. This helps prevent bugs where a buffer is
715+
" somehow being renamed and we can't remove it from the menu because we are
716+
" using the wrong menu name.
717+
let s:bmenu_items = {}
718+
714719
if !exists("bmenu_priority")
715720
let bmenu_priority = 60
716721
endif
@@ -733,14 +738,13 @@ func! s:BMRemove()
733738
if isdirectory(name)
734739
return
735740
endif
736-
let munge = <SID>BMMunge(name, expand("<abuf>"))
737-
738-
if s:bmenu_short == 0
739-
exe 'silent! aun &Buffers.' . munge
740-
else
741-
exe 'silent! aun &Buffers.' . <SID>BMHash2(munge) . munge
741+
let bufnum = expand("<abuf>")
742+
if s:bmenu_items->has_key(bufnum)
743+
let menu_name = s:bmenu_items[bufnum]
744+
exe 'silent! aun &Buffers.' . menu_name
745+
let s:bmenu_count = s:bmenu_count - 1
746+
unlet s:bmenu_items[bufnum]
742747
endif
743-
let s:bmenu_count = s:bmenu_count - 1
744748
endif
745749
endfunc
746750

@@ -749,6 +753,7 @@ func! s:BMShow(...)
749753
let s:bmenu_wait = 1
750754
let s:bmenu_short = 1
751755
let s:bmenu_count = 0
756+
let s:bmenu_items = {}
752757
"
753758
" get new priority, if exists
754759
if a:0 == 1
@@ -844,9 +849,12 @@ func! s:BMFilename(name, num)
844849
let munge = <SID>BMMunge(a:name, a:num)
845850
let hash = <SID>BMHash(munge)
846851
if s:bmenu_short == 0
852+
let s:bmenu_items[a:num] = munge
847853
let name = 'an ' . g:bmenu_priority . '.' . hash . ' &Buffers.' . munge
848854
else
849-
let name = 'an ' . g:bmenu_priority . '.' . hash . '.' . hash . ' &Buffers.' . <SID>BMHash2(munge) . munge
855+
let menu_name = <SID>BMHash2(munge) . munge
856+
let s:bmenu_items[a:num] = l:menu_name
857+
let name = 'an ' . g:bmenu_priority . '.' . hash . '.' . hash . ' &Buffers.' . menu_name
850858
endif
851859
" set 'cpo' to include the <CR>
852860
let cpo_save = &cpo

src/ex_getln.c

+2
Original file line numberDiff line numberDiff line change
@@ -4206,7 +4206,9 @@ open_cmdwin(void)
42064206

42074207
// Create the command-line buffer empty.
42084208
(void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
4209+
apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
42094210
(void)setfname(curbuf, (char_u *)"[Command Line]", NULL, TRUE);
4211+
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
42104212
set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL);
42114213
curbuf->b_p_ma = TRUE;
42124214
#ifdef FEAT_FOLDING

src/terminal.c

+4
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ term_start(
523523
term->tl_next = first_term;
524524
first_term = term;
525525

526+
apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
527+
526528
if (opt->jo_term_name != NULL)
527529
curbuf->b_ffname = vim_strsave(opt->jo_term_name);
528530
else if (argv != NULL)
@@ -571,6 +573,8 @@ term_start(
571573
curbuf->b_sfname = vim_strsave(curbuf->b_ffname);
572574
curbuf->b_fname = curbuf->b_ffname;
573575

576+
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
577+
574578
if (opt->jo_term_opencmd != NULL)
575579
term->tl_opencmd = vim_strsave(opt->jo_term_opencmd);
576580

src/testdir/test_cmdline.vim

-7
Original file line numberDiff line numberDiff line change
@@ -1030,13 +1030,6 @@ func Test_cmdline_expand_special()
10301030
endfunc
10311031

10321032
func Test_cmdwin_jump_to_win()
1033-
if has('gui_macvim') && has('gui_running')
1034-
" Due to a mix of MacVim-specific menu behaviors (calling BMShow at start
1035-
" instead of VimEnter), and buffer menu stale item bugs in Vim, this test
1036-
" doesn't work in GUI for now. Will be re-enabled after buffer menu bugs
1037-
" are fixed.
1038-
return
1039-
endif
10401033
call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:')
10411034
new
10421035
set modified

src/testdir/test_menu.vim

+37
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,43 @@ func Test_load_menu()
2323
call assert_equal('', v:errmsg)
2424
endfunc
2525

26+
func Test_buffer_menu_special_buffers()
27+
" Load in runtime menus
28+
try
29+
source $VIMRUNTIME/menu.vim
30+
catch
31+
call assert_report('error while loading menus: ' . v:exception)
32+
endtry
33+
34+
let v:errmsg = ''
35+
if !has("gui_macvim")
36+
" MacVim initializes buffer menus differently (by calling BMShow
37+
" immediately) so this is unnecessary and would break the test.
38+
doautocmd LoadBufferMenu VimEnter
39+
endif
40+
call assert_equal('', v:errmsg)
41+
42+
let orig_buffer_menus = execute("nmenu Buffers")
43+
44+
" Make a new command-line window, test that it creates a new buffer menu,
45+
" and test that when we exits the command-line window, the menu item got removed.
46+
call feedkeys("q::let cmdline_buffer_menus=execute('nmenu Buffers')\<CR>:q\<CR>", 'ntx')
47+
call assert_equal(len(split(orig_buffer_menus, "\n")) + 2, len(split(cmdline_buffer_menus, "\n")))
48+
call assert_equal(orig_buffer_menus, execute("nmenu Buffers"))
49+
50+
" Make a terminal window, and also test that it creates and removes the
51+
" buffer menu item.
52+
terminal
53+
let term_buffer_menus = execute('nmenu Buffers')
54+
call assert_equal(len(split(orig_buffer_menus, "\n")) + 2, len(split(term_buffer_menus, "\n")))
55+
bd!
56+
call assert_equal(orig_buffer_menus, execute("nmenu Buffers"))
57+
58+
" Remove menus to clean up
59+
source $VIMRUNTIME/delmenu.vim
60+
call assert_equal('', v:errmsg)
61+
endfunc
62+
2663
func Test_translate_menu()
2764
if !has('multi_lang')
2865
return

0 commit comments

Comments
 (0)