changeset 19713:8514e8b7e661 v8.2.0413

patch 8.2.0413: buffer menu does not handle special buffers properly Commit: https://github.com/vim/vim/commit/5e94a29ebbde10dd973d58f1adba9a2fc83877d1 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Mar 19 18:46:57 2020 +0100 patch 8.2.0413: buffer menu does not handle special buffers properly Problem: Buffer menu does not handle special buffers properly. Solution: Keep a dictionary with buffer names to reliably keep track of entries. Also trigger BufFilePre and BufFilePost for command-line and terminal buffers when the name changes.
author Bram Moolenaar <Bram@vim.org>
date Thu, 19 Mar 2020 19:00:04 +0100
parents 714875349336
children 0e0ef0f4009f
files runtime/menu.vim src/ex_getln.c src/terminal.c src/testdir/Make_all.mak src/testdir/test_alot.vim src/testdir/test_menu.vim src/version.c
diffstat 7 files changed, 120 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/menu.vim
+++ b/runtime/menu.vim
@@ -2,7 +2,7 @@
 " You can also use this as a start for your own set of menus.
 "
 " Maintainer:	Bram Moolenaar <Bram@vim.org>
-" Last Change:	2019 Dec 10
+" Last Change:	2020 Mar 19
 
 " Note that ":an" (short for ":anoremenu") is often used to make a menu work
 " in all modes and avoid side effects from mappings defined by the user.
@@ -139,11 +139,11 @@ an 10.600 &File.-SEP4-				<Nop>
 an 10.610 &File.Sa&ve-Exit<Tab>:wqa		:confirm wqa<CR>
 an 10.620 &File.E&xit<Tab>:qa			:confirm qa<CR>
 
-func! <SID>SelectAll()
+func s:SelectAll()
   exe "norm! gg" . (&slm == "" ? "VG" : "gH\<C-O>G")
 endfunc
 
-func! s:FnameEscape(fname)
+func s:FnameEscape(fname)
   if exists('*fnameescape')
     return fnameescape(a:fname)
   endif
@@ -356,7 +356,7 @@ endfun
 let s:did_setup_color_schemes = 0
 
 " Setup the Edit.Color Scheme submenu
-func! s:SetupColorSchemes() abort
+func s:SetupColorSchemes() abort
   if s:did_setup_color_schemes
     return
   endif
@@ -388,7 +388,7 @@ endif
 if has("keymap")
   let s:did_setup_keymaps = 0
 
-  func! s:SetupKeymaps() abort
+  func s:SetupKeymaps() abort
     if s:did_setup_keymaps
       return
     endif
@@ -454,7 +454,7 @@ if has("spell")
   an <silent> 40.335.270 &Tools.&Spelling.&Find\ More\ Languages	:call <SID>SpellLang()<CR>
 
   let s:undo_spellang = ['aun &Tools.&Spelling.&Find\ More\ Languages']
-  func! s:SpellLang()
+  func s:SpellLang()
     for cmd in s:undo_spellang
       exe "silent! " . cmd
     endfor
@@ -566,7 +566,7 @@ an <silent> 40.540 &Tools.Conve&rt\ Back
 
 " Use a function to do the conversion, so that it also works with 'insertmode'
 " set.
-func! s:XxdConv()
+func s:XxdConv()
   let mod = &mod
   if has("vms")
     %!mc vim:xxd
@@ -580,7 +580,7 @@ func! s:XxdConv()
   let &mod = mod
 endfun
 
-func! s:XxdBack()
+func s:XxdBack()
   let mod = &mod
   if has("vms")
     %!mc vim:xxd -r
@@ -593,7 +593,7 @@ func! s:XxdBack()
   let &mod = mod
 endfun
 
-func! s:XxdFind()
+func s:XxdFind()
   if !exists("g:xxdprogram")
     " On the PC xxd may not be in the path but in the install directory
     if has("win32") && !executable("xxd")
@@ -610,7 +610,7 @@ endfun
 let s:did_setup_compilers = 0
 
 " Setup the Tools.Compiler submenu
-func! s:SetupCompilers() abort
+func s:SetupCompilers() abort
   if s:did_setup_compilers
     return
   endif
@@ -634,7 +634,7 @@ endif
 
 " Load ColorScheme, Compiler Setting and Keymap menus when idle.
 if !exists("do_no_lazyload_menus")
-  func! s:SetupLazyloadMenus()
+  func s:SetupLazyloadMenus()
     call s:SetupColorSchemes()
     call s:SetupCompilers()
     if has("keymap")
@@ -656,51 +656,75 @@ if !exists("no_buffers_menu")
 " startup faster.
 let s:bmenu_wait = 1
 
+" Dictionary of buffer number to name. This helps prevent problems where a
+" buffer as renamed and we didn't keep track of that.
+let s:bmenu_items = {}
+
 if !exists("bmenu_priority")
   let bmenu_priority = 60
 endif
 
-func! s:BMAdd()
+" invoked from a BufCreate or BufFilePost autocommand
+func s:BMAdd()
   if s:bmenu_wait == 0
     " when adding too many buffers, redraw in short format
     if s:bmenu_count == &menuitems && s:bmenu_short == 0
       call s:BMShow()
     else
-      call <SID>BMFilename(expand("<afile>"), expand("<abuf>"))
-      let s:bmenu_count = s:bmenu_count + 1
+      let name = expand("<afile>")
+      let num = expand("<abuf>")
+      if s:BMCanAdd(name, num)
+	call <SID>BMFilename(name, num)
+	let s:bmenu_count += 1
+      endif
+    endif
+  endif
+endfunc
+
+" invoked from a BufDelete or BufFilePre autocommand
+func s:BMRemove()
+  if s:bmenu_wait == 0
+    let bufnum = expand("<abuf>")
+    if s:bmenu_items->has_key(bufnum)
+      let menu_name = s:bmenu_items[bufnum]
+      exe 'silent! aun &Buffers.' . menu_name
+      let s:bmenu_count = s:bmenu_count - 1
+      unlet s:bmenu_items[bufnum]
     endif
   endif
 endfunc
 
-func! s:BMRemove()
-  if s:bmenu_wait == 0
-    let name = expand("<afile>")
-    if isdirectory(name)
-      return
-    endif
-    let munge = <SID>BMMunge(name, expand("<abuf>"))
+" Return non-zero if buffer with number "name" / "num" is useful to add in the
+" buffer menu.
+func s:BMCanAdd(name, num)
+  " no directory or unlisted buffer
+  if isdirectory(a:name) || !buflisted(a:num)
+    return 0
+  endif
 
-    if s:bmenu_short == 0
-      exe 'silent! aun &Buffers.' . munge
-    else
-      exe 'silent! aun &Buffers.' . <SID>BMHash2(munge) . munge
-    endif
-    let s:bmenu_count = s:bmenu_count - 1
+  " no special buffer, such as terminal or popup
+  let buftype = getbufvar(a:num, '&buftype')
+  if buftype != '' && buftype != 'nofile' && buftype != 'nowrite'
+    return 0
   endif
+
+  " only existing buffers
+  return bufexists(a:num)
 endfunc
 
 " Create the buffer menu (delete an existing one first).
-func! s:BMShow(...)
+func s:BMShow(...)
   let s:bmenu_wait = 1
   let s:bmenu_short = 1
   let s:bmenu_count = 0
+  let s:bmenu_items = {}
   "
   " get new priority, if exists
   if a:0 == 1
     let g:bmenu_priority = a:1
   endif
 
-  " Remove old menu, if exists; keep one entry to avoid a torn off menu to
+  " Remove old menu, if it exists; keep one entry to avoid a torn off menu to
   " disappear.  Use try/catch to avoid setting v:errmsg
   try | unmenu &Buffers | catch | endtry
   exe 'noremenu ' . g:bmenu_priority . ".1 &Buffers.Dummy l"
@@ -721,7 +745,7 @@ func! s:BMShow(...)
   " figure out how many buffers there are
   let buf = 1
   while buf <= bufnr('$')
-    if bufexists(buf) && !isdirectory(bufname(buf)) && buflisted(buf)
+    if s:BMCanAdd(bufname(buf), buf)
       let s:bmenu_count = s:bmenu_count + 1
     endif
     let buf = buf + 1
@@ -733,8 +757,9 @@ func! s:BMShow(...)
   " iterate through buffer list, adding each buffer to the menu:
   let buf = 1
   while buf <= bufnr('$')
-    if bufexists(buf) && !isdirectory(bufname(buf)) && buflisted(buf)
-      call <SID>BMFilename(bufname(buf), buf)
+    let name = bufname(buf)
+    if s:BMCanAdd(name, buf)
+      call <SID>BMFilename(name, buf)
     endif
     let buf = buf + 1
   endwhile
@@ -746,7 +771,7 @@ func! s:BMShow(...)
   aug END
 endfunc
 
-func! s:BMHash(name)
+func s:BMHash(name)
   " Make name all upper case, so that chars are between 32 and 96
   let nm = substitute(a:name, ".*", '\U\0', "")
   if has("ebcdic")
@@ -761,7 +786,7 @@ func! s:BMHash(name)
   return (char2nr(nm[0]) - sp) * 0x800000 + (char2nr(nm[1]) - sp) * 0x20000 + (char2nr(nm[2]) - sp) * 0x1000 + (char2nr(nm[3]) - sp) * 0x80 + (char2nr(nm[4]) - sp) * 0x20 + (char2nr(nm[5]) - sp)
 endfunc
 
-func! s:BMHash2(name)
+func s:BMHash2(name)
   let nm = substitute(a:name, ".", '\L\0', "")
   " Not exactly right for EBCDIC...
   if nm[0] < 'a' || nm[0] > 'z'
@@ -781,22 +806,22 @@ func! s:BMHash2(name)
   endif
 endfunc
 
-" insert a buffer name into the buffer menu:
-func! s:BMFilename(name, num)
-  if isdirectory(a:name)
-    return
-  endif
+" Insert a buffer name into the buffer menu.
+func s:BMFilename(name, num)
   let munge = <SID>BMMunge(a:name, a:num)
   let hash = <SID>BMHash(munge)
   if s:bmenu_short == 0
-    let name = 'an ' . g:bmenu_priority . '.' . hash . ' &Buffers.' . munge
+    let s:bmenu_items[a:num] = munge
+    let cmd = 'an ' . g:bmenu_priority . '.' . hash . ' &Buffers.' . munge
   else
-    let name = 'an ' . g:bmenu_priority . '.' . hash . '.' . hash . ' &Buffers.' . <SID>BMHash2(munge) . munge
+    let menu_name = <SID>BMHash2(munge) . munge
+    let s:bmenu_items[a:num] = menu_name
+    let cmd = 'an ' . g:bmenu_priority . '.' . hash . '.' . hash . ' &Buffers.' . menu_name
   endif
   " set 'cpo' to include the <CR>
   let cpo_save = &cpo
   set cpo&vim
-  exe name . ' :confirm b' . a:num . '<CR>'
+  exe cmd . ' :confirm b' . a:num . '<CR>'
   let &cpo = cpo_save
 endfunc
 
@@ -804,7 +829,7 @@ endfunc
 if !exists("g:bmenu_max_pathlen")
   let g:bmenu_max_pathlen = 35
 endif
-func! s:BMTruncName(fname)
+func s:BMTruncName(fname)
   let name = a:fname
   if g:bmenu_max_pathlen < 5
     let name = ""
@@ -824,7 +849,7 @@ func! s:BMTruncName(fname)
   return name
 endfunc
 
-func! s:BMMunge(fname, bnum)
+func s:BMMunge(fname, bnum)
   let name = a:fname
   if name == ''
     if !exists("g:menutrans_no_file")
@@ -941,7 +966,7 @@ cnoremenu <script> <silent> 1.100 PopUp.
 if has("spell")
   " Spell suggestions in the popup menu.  Note that this will slow down the
   " appearance of the menu!
-  func! <SID>SpellPopup()
+  func s:SpellPopup()
     if exists("s:changeitem") && s:changeitem != ''
       call <SID>SpellDel()
     endif
@@ -997,7 +1022,7 @@ if has("spell")
     call cursor(0, curcol)	" put the cursor back where it was
   endfunc
 
-  func! <SID>SpellReplace(n)
+  func s:SpellReplace(n)
     let l = getline('.')
     " Move the cursor to the start of the word.
     call spellbadword()
@@ -1005,7 +1030,7 @@ if has("spell")
 	  \ . strpart(l, col('.') + len(s:fromword) - 1))
   endfunc
 
-  func! <SID>SpellDel()
+  func s:SpellDel()
     exe "aunmenu PopUp." . s:changeitem
     exe "aunmenu PopUp." . s:additem
     exe "aunmenu PopUp." . s:ignoreitem
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -4195,7 +4195,9 @@ open_cmdwin(void)
 
     // Create the command-line buffer empty.
     (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
+    apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
     (void)setfname(curbuf, (char_u *)"[Command Line]", NULL, TRUE);
+    apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
     set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL);
     curbuf->b_p_ma = TRUE;
 #ifdef FEAT_FOLDING
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -523,6 +523,8 @@ term_start(
     term->tl_next = first_term;
     first_term = term;
 
+    apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
+
     if (opt->jo_term_name != NULL)
 	curbuf->b_ffname = vim_strsave(opt->jo_term_name);
     else if (argv != NULL)
@@ -571,6 +573,8 @@ term_start(
     curbuf->b_sfname = vim_strsave(curbuf->b_ffname);
     curbuf->b_fname = curbuf->b_ffname;
 
+    apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
+
     if (opt->jo_term_opencmd != NULL)
 	term->tl_opencmd = vim_strsave(opt->jo_term_opencmd);
 
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -175,9 +175,9 @@ NEW_TESTS = \
 	test_matchadd_conceal \
 	test_matchadd_conceal_utf8 \
 	test_memory_usage \
-	test_method \
 	test_menu \
 	test_messages \
+	test_method \
 	test_mksession \
 	test_mksession_utf8 \
 	test_modeline \
@@ -402,6 +402,7 @@ NEW_TESTS_RES = \
 	test_matchadd_conceal.res \
 	test_matchadd_conceal_utf8.res \
 	test_memory_usage.res \
+	test_menu.res \
 	test_messages.res \
 	test_method.res \
 	test_mksession.res \
--- a/src/testdir/test_alot.vim
+++ b/src/testdir/test_alot.vim
@@ -19,7 +19,6 @@ source test_glob2regpat.vim
 source test_global.vim
 source test_jumps.vim
 source test_lispwords.vim
-source test_menu.vim
 source test_move.vim
 source test_put.vim
 source test_recover.vim
--- a/src/testdir/test_menu.vim
+++ b/src/testdir/test_menu.vim
@@ -19,6 +19,41 @@ func Test_load_menu()
   call assert_equal('', v:errmsg)
 endfunc
 
+func Test_buffer_menu_special_buffers()
+  " Load in runtime menus
+  try
+    source $VIMRUNTIME/menu.vim
+  catch
+    call assert_report('error while loading menus: ' . v:exception)
+  endtry
+
+  let v:errmsg = ''
+  doautocmd LoadBufferMenu VimEnter
+  call assert_equal('', v:errmsg)
+
+  let orig_buffer_menus = execute("nmenu Buffers")
+
+  " Make a new command-line window, test that it does not create a new buffer
+  " menu.
+  call feedkeys("q::let cmdline_buffer_menus=execute('nmenu Buffers')\<CR>:q\<CR>", 'ntx')
+  call assert_equal(len(split(orig_buffer_menus, "\n")), len(split(cmdline_buffer_menus, "\n")))
+  call assert_equal(orig_buffer_menus, execute("nmenu Buffers"))
+
+  if has('terminal')
+    " Open a terminal window and test that it does not create a buffer menu
+    " item.
+    terminal
+    let term_buffer_menus = execute('nmenu Buffers')
+    call assert_equal(len(split(orig_buffer_menus, "\n")), len(split(term_buffer_menus, "\n")))
+    bwipe!
+    call assert_equal(orig_buffer_menus, execute("nmenu Buffers"))
+  endif
+
+  " Remove menus to clean up
+  source $VIMRUNTIME/delmenu.vim
+  call assert_equal('', v:errmsg)
+endfunc
+
 func Test_translate_menu()
   if !has('multi_lang')
     return
@@ -121,6 +156,7 @@ endfunc
 " Test for menu item completion in command line
 func Test_menu_expand()
   " Create the menu itmes for test
+  menu Dummy.Nothing lll
   for i in range(1, 4)
     let m = 'menu Xmenu.A' .. i .. '.A' .. i
     for j in range(1, 4)
@@ -146,7 +182,7 @@ func Test_menu_expand()
   " Test for <Up> to go up a menu
   call feedkeys(":emenu Xmenu.A\<Tab>\<Down>\<Up>\<Up>\<Up>" ..
         \ "\<C-A>\<C-B>\"\<CR>", 'xt')
-  call assert_equal('"emenu Buffers. Xmenu.', @:)
+  call assert_equal('"emenu Dummy. Xmenu.', @:)
 
   " Test for expanding only submenus
   call feedkeys(":popup Xmenu.\<C-A>\<C-B>\"\<CR>", 'xt')
@@ -166,6 +202,7 @@ func Test_menu_expand()
 
   set wildmenu&
   unmenu Xmenu
+  unmenu Dummy
 
   " Test for expanding popup menus with some hidden items
   menu Xmenu.foo.A1 a1
@@ -175,7 +212,6 @@ func Test_menu_expand()
   call feedkeys(":popup Xmenu.\<C-A>\<C-B>\"\<CR>", 'xt')
   call assert_equal('"popup Xmenu.foo', @:)
   unmenu Xmenu
-
 endfunc
 
 " Test for the menu_info() function
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    413,
+/**/
     412,
 /**/
     411,