comparison runtime/menu.vim @ 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 ec92ccff5c8b
children d53e8428a79a
comparison
equal deleted inserted replaced
19712:714875349336 19713:8514e8b7e661
1 " Vim support file to define the default menus 1 " Vim support file to define the default menus
2 " You can also use this as a start for your own set of menus. 2 " You can also use this as a start for your own set of menus.
3 " 3 "
4 " Maintainer: Bram Moolenaar <Bram@vim.org> 4 " Maintainer: Bram Moolenaar <Bram@vim.org>
5 " Last Change: 2019 Dec 10 5 " Last Change: 2020 Mar 19
6 6
7 " Note that ":an" (short for ":anoremenu") is often used to make a menu work 7 " Note that ":an" (short for ":anoremenu") is often used to make a menu work
8 " in all modes and avoid side effects from mappings defined by the user. 8 " in all modes and avoid side effects from mappings defined by the user.
9 9
10 " Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise 10 " Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise
137 endif 137 endif
138 an 10.600 &File.-SEP4- <Nop> 138 an 10.600 &File.-SEP4- <Nop>
139 an 10.610 &File.Sa&ve-Exit<Tab>:wqa :confirm wqa<CR> 139 an 10.610 &File.Sa&ve-Exit<Tab>:wqa :confirm wqa<CR>
140 an 10.620 &File.E&xit<Tab>:qa :confirm qa<CR> 140 an 10.620 &File.E&xit<Tab>:qa :confirm qa<CR>
141 141
142 func! <SID>SelectAll() 142 func s:SelectAll()
143 exe "norm! gg" . (&slm == "" ? "VG" : "gH\<C-O>G") 143 exe "norm! gg" . (&slm == "" ? "VG" : "gH\<C-O>G")
144 endfunc 144 endfunc
145 145
146 func! s:FnameEscape(fname) 146 func s:FnameEscape(fname)
147 if exists('*fnameescape') 147 if exists('*fnameescape')
148 return fnameescape(a:fname) 148 return fnameescape(a:fname)
149 endif 149 endif
150 return escape(a:fname, " \t\n*?[{`$\\%#'\"|!<") 150 return escape(a:fname, " \t\n*?[{`$\\%#'\"|!<")
151 endfunc 151 endfunc
354 endfun 354 endfun
355 355
356 let s:did_setup_color_schemes = 0 356 let s:did_setup_color_schemes = 0
357 357
358 " Setup the Edit.Color Scheme submenu 358 " Setup the Edit.Color Scheme submenu
359 func! s:SetupColorSchemes() abort 359 func s:SetupColorSchemes() abort
360 if s:did_setup_color_schemes 360 if s:did_setup_color_schemes
361 return 361 return
362 endif 362 endif
363 let s:did_setup_color_schemes = 1 363 let s:did_setup_color_schemes = 1
364 364
386 386
387 " Setup the Edit.Keymap submenu 387 " Setup the Edit.Keymap submenu
388 if has("keymap") 388 if has("keymap")
389 let s:did_setup_keymaps = 0 389 let s:did_setup_keymaps = 0
390 390
391 func! s:SetupKeymaps() abort 391 func s:SetupKeymaps() abort
392 if s:did_setup_keymaps 392 if s:did_setup_keymaps
393 return 393 return
394 endif 394 endif
395 let s:did_setup_keymaps = 1 395 let s:did_setup_keymaps = 1
396 396
452 an 40.335.250 &Tools.&Spelling.Set\ Language\ to\ "en_nz" :set spl=en_nz spell<CR> 452 an 40.335.250 &Tools.&Spelling.Set\ Language\ to\ "en_nz" :set spl=en_nz spell<CR>
453 an 40.335.260 &Tools.&Spelling.Set\ Language\ to\ "en_us" :set spl=en_us spell<CR> 453 an 40.335.260 &Tools.&Spelling.Set\ Language\ to\ "en_us" :set spl=en_us spell<CR>
454 an <silent> 40.335.270 &Tools.&Spelling.&Find\ More\ Languages :call <SID>SpellLang()<CR> 454 an <silent> 40.335.270 &Tools.&Spelling.&Find\ More\ Languages :call <SID>SpellLang()<CR>
455 455
456 let s:undo_spellang = ['aun &Tools.&Spelling.&Find\ More\ Languages'] 456 let s:undo_spellang = ['aun &Tools.&Spelling.&Find\ More\ Languages']
457 func! s:SpellLang() 457 func s:SpellLang()
458 for cmd in s:undo_spellang 458 for cmd in s:undo_spellang
459 exe "silent! " . cmd 459 exe "silent! " . cmd
460 endfor 460 endfor
461 let s:undo_spellang = [] 461 let s:undo_spellang = []
462 462
564 an <silent> 40.540 &Tools.Conve&rt\ Back<Tab>:%!xxd\ -r 564 an <silent> 40.540 &Tools.Conve&rt\ Back<Tab>:%!xxd\ -r
565 \ :call <SID>XxdBack()<CR> 565 \ :call <SID>XxdBack()<CR>
566 566
567 " Use a function to do the conversion, so that it also works with 'insertmode' 567 " Use a function to do the conversion, so that it also works with 'insertmode'
568 " set. 568 " set.
569 func! s:XxdConv() 569 func s:XxdConv()
570 let mod = &mod 570 let mod = &mod
571 if has("vms") 571 if has("vms")
572 %!mc vim:xxd 572 %!mc vim:xxd
573 else 573 else
574 call s:XxdFind() 574 call s:XxdFind()
578 set ft=xxd 578 set ft=xxd
579 endif 579 endif
580 let &mod = mod 580 let &mod = mod
581 endfun 581 endfun
582 582
583 func! s:XxdBack() 583 func s:XxdBack()
584 let mod = &mod 584 let mod = &mod
585 if has("vms") 585 if has("vms")
586 %!mc vim:xxd -r 586 %!mc vim:xxd -r
587 else 587 else
588 call s:XxdFind() 588 call s:XxdFind()
591 set ft= 591 set ft=
592 doautocmd filetypedetect BufReadPost 592 doautocmd filetypedetect BufReadPost
593 let &mod = mod 593 let &mod = mod
594 endfun 594 endfun
595 595
596 func! s:XxdFind() 596 func s:XxdFind()
597 if !exists("g:xxdprogram") 597 if !exists("g:xxdprogram")
598 " On the PC xxd may not be in the path but in the install directory 598 " On the PC xxd may not be in the path but in the install directory
599 if has("win32") && !executable("xxd") 599 if has("win32") && !executable("xxd")
600 let g:xxdprogram = $VIMRUNTIME . (&shellslash ? '/' : '\') . "xxd.exe" 600 let g:xxdprogram = $VIMRUNTIME . (&shellslash ? '/' : '\') . "xxd.exe"
601 if g:xxdprogram =~ ' ' 601 if g:xxdprogram =~ ' '
608 endfun 608 endfun
609 609
610 let s:did_setup_compilers = 0 610 let s:did_setup_compilers = 0
611 611
612 " Setup the Tools.Compiler submenu 612 " Setup the Tools.Compiler submenu
613 func! s:SetupCompilers() abort 613 func s:SetupCompilers() abort
614 if s:did_setup_compilers 614 if s:did_setup_compilers
615 return 615 return
616 endif 616 endif
617 let s:did_setup_compilers = 1 617 let s:did_setup_compilers = 1
618 618
632 an <silent> 30.440 &Tools.Show\ Compiler\ Se&ttings\ in\ Menu :call <SID>SetupCompilers()<CR> 632 an <silent> 30.440 &Tools.Show\ Compiler\ Se&ttings\ in\ Menu :call <SID>SetupCompilers()<CR>
633 endif 633 endif
634 634
635 " Load ColorScheme, Compiler Setting and Keymap menus when idle. 635 " Load ColorScheme, Compiler Setting and Keymap menus when idle.
636 if !exists("do_no_lazyload_menus") 636 if !exists("do_no_lazyload_menus")
637 func! s:SetupLazyloadMenus() 637 func s:SetupLazyloadMenus()
638 call s:SetupColorSchemes() 638 call s:SetupColorSchemes()
639 call s:SetupCompilers() 639 call s:SetupCompilers()
640 if has("keymap") 640 if has("keymap")
641 call s:SetupKeymaps() 641 call s:SetupKeymaps()
642 endif 642 endif
654 654
655 " wait with building the menu until after loading 'session' files. Makes 655 " wait with building the menu until after loading 'session' files. Makes
656 " startup faster. 656 " startup faster.
657 let s:bmenu_wait = 1 657 let s:bmenu_wait = 1
658 658
659 " Dictionary of buffer number to name. This helps prevent problems where a
660 " buffer as renamed and we didn't keep track of that.
661 let s:bmenu_items = {}
662
659 if !exists("bmenu_priority") 663 if !exists("bmenu_priority")
660 let bmenu_priority = 60 664 let bmenu_priority = 60
661 endif 665 endif
662 666
663 func! s:BMAdd() 667 " invoked from a BufCreate or BufFilePost autocommand
668 func s:BMAdd()
664 if s:bmenu_wait == 0 669 if s:bmenu_wait == 0
665 " when adding too many buffers, redraw in short format 670 " when adding too many buffers, redraw in short format
666 if s:bmenu_count == &menuitems && s:bmenu_short == 0 671 if s:bmenu_count == &menuitems && s:bmenu_short == 0
667 call s:BMShow() 672 call s:BMShow()
668 else 673 else
669 call <SID>BMFilename(expand("<afile>"), expand("<abuf>")) 674 let name = expand("<afile>")
670 let s:bmenu_count = s:bmenu_count + 1 675 let num = expand("<abuf>")
676 if s:BMCanAdd(name, num)
677 call <SID>BMFilename(name, num)
678 let s:bmenu_count += 1
679 endif
671 endif 680 endif
672 endif 681 endif
673 endfunc 682 endfunc
674 683
675 func! s:BMRemove() 684 " invoked from a BufDelete or BufFilePre autocommand
685 func s:BMRemove()
676 if s:bmenu_wait == 0 686 if s:bmenu_wait == 0
677 let name = expand("<afile>") 687 let bufnum = expand("<abuf>")
678 if isdirectory(name) 688 if s:bmenu_items->has_key(bufnum)
679 return 689 let menu_name = s:bmenu_items[bufnum]
680 endif 690 exe 'silent! aun &Buffers.' . menu_name
681 let munge = <SID>BMMunge(name, expand("<abuf>")) 691 let s:bmenu_count = s:bmenu_count - 1
682 692 unlet s:bmenu_items[bufnum]
683 if s:bmenu_short == 0 693 endif
684 exe 'silent! aun &Buffers.' . munge
685 else
686 exe 'silent! aun &Buffers.' . <SID>BMHash2(munge) . munge
687 endif
688 let s:bmenu_count = s:bmenu_count - 1
689 endif 694 endif
690 endfunc 695 endfunc
691 696
697 " Return non-zero if buffer with number "name" / "num" is useful to add in the
698 " buffer menu.
699 func s:BMCanAdd(name, num)
700 " no directory or unlisted buffer
701 if isdirectory(a:name) || !buflisted(a:num)
702 return 0
703 endif
704
705 " no special buffer, such as terminal or popup
706 let buftype = getbufvar(a:num, '&buftype')
707 if buftype != '' && buftype != 'nofile' && buftype != 'nowrite'
708 return 0
709 endif
710
711 " only existing buffers
712 return bufexists(a:num)
713 endfunc
714
692 " Create the buffer menu (delete an existing one first). 715 " Create the buffer menu (delete an existing one first).
693 func! s:BMShow(...) 716 func s:BMShow(...)
694 let s:bmenu_wait = 1 717 let s:bmenu_wait = 1
695 let s:bmenu_short = 1 718 let s:bmenu_short = 1
696 let s:bmenu_count = 0 719 let s:bmenu_count = 0
720 let s:bmenu_items = {}
697 " 721 "
698 " get new priority, if exists 722 " get new priority, if exists
699 if a:0 == 1 723 if a:0 == 1
700 let g:bmenu_priority = a:1 724 let g:bmenu_priority = a:1
701 endif 725 endif
702 726
703 " Remove old menu, if exists; keep one entry to avoid a torn off menu to 727 " Remove old menu, if it exists; keep one entry to avoid a torn off menu to
704 " disappear. Use try/catch to avoid setting v:errmsg 728 " disappear. Use try/catch to avoid setting v:errmsg
705 try | unmenu &Buffers | catch | endtry 729 try | unmenu &Buffers | catch | endtry
706 exe 'noremenu ' . g:bmenu_priority . ".1 &Buffers.Dummy l" 730 exe 'noremenu ' . g:bmenu_priority . ".1 &Buffers.Dummy l"
707 try | unmenu! &Buffers | catch | endtry 731 try | unmenu! &Buffers | catch | endtry
708 732
719 unmenu &Buffers.Dummy 743 unmenu &Buffers.Dummy
720 744
721 " figure out how many buffers there are 745 " figure out how many buffers there are
722 let buf = 1 746 let buf = 1
723 while buf <= bufnr('$') 747 while buf <= bufnr('$')
724 if bufexists(buf) && !isdirectory(bufname(buf)) && buflisted(buf) 748 if s:BMCanAdd(bufname(buf), buf)
725 let s:bmenu_count = s:bmenu_count + 1 749 let s:bmenu_count = s:bmenu_count + 1
726 endif 750 endif
727 let buf = buf + 1 751 let buf = buf + 1
728 endwhile 752 endwhile
729 if s:bmenu_count <= &menuitems 753 if s:bmenu_count <= &menuitems
731 endif 755 endif
732 756
733 " iterate through buffer list, adding each buffer to the menu: 757 " iterate through buffer list, adding each buffer to the menu:
734 let buf = 1 758 let buf = 1
735 while buf <= bufnr('$') 759 while buf <= bufnr('$')
736 if bufexists(buf) && !isdirectory(bufname(buf)) && buflisted(buf) 760 let name = bufname(buf)
737 call <SID>BMFilename(bufname(buf), buf) 761 if s:BMCanAdd(name, buf)
762 call <SID>BMFilename(name, buf)
738 endif 763 endif
739 let buf = buf + 1 764 let buf = buf + 1
740 endwhile 765 endwhile
741 let s:bmenu_wait = 0 766 let s:bmenu_wait = 0
742 aug buffer_list 767 aug buffer_list
744 au BufCreate,BufFilePost * call <SID>BMAdd() 769 au BufCreate,BufFilePost * call <SID>BMAdd()
745 au BufDelete,BufFilePre * call <SID>BMRemove() 770 au BufDelete,BufFilePre * call <SID>BMRemove()
746 aug END 771 aug END
747 endfunc 772 endfunc
748 773
749 func! s:BMHash(name) 774 func s:BMHash(name)
750 " Make name all upper case, so that chars are between 32 and 96 775 " Make name all upper case, so that chars are between 32 and 96
751 let nm = substitute(a:name, ".*", '\U\0', "") 776 let nm = substitute(a:name, ".*", '\U\0', "")
752 if has("ebcdic") 777 if has("ebcdic")
753 " HACK: Replace all non alphabetics with 'Z' 778 " HACK: Replace all non alphabetics with 'Z'
754 " Just to make it work for now. 779 " Just to make it work for now.
759 endif 784 endif
760 " convert first six chars into a number for sorting: 785 " convert first six chars into a number for sorting:
761 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) 786 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)
762 endfunc 787 endfunc
763 788
764 func! s:BMHash2(name) 789 func s:BMHash2(name)
765 let nm = substitute(a:name, ".", '\L\0', "") 790 let nm = substitute(a:name, ".", '\L\0', "")
766 " Not exactly right for EBCDIC... 791 " Not exactly right for EBCDIC...
767 if nm[0] < 'a' || nm[0] > 'z' 792 if nm[0] < 'a' || nm[0] > 'z'
768 return '&others.' 793 return '&others.'
769 elseif nm[0] <= 'd' 794 elseif nm[0] <= 'd'
779 else 804 else
780 return '&u-z.' 805 return '&u-z.'
781 endif 806 endif
782 endfunc 807 endfunc
783 808
784 " insert a buffer name into the buffer menu: 809 " Insert a buffer name into the buffer menu.
785 func! s:BMFilename(name, num) 810 func s:BMFilename(name, num)
786 if isdirectory(a:name)
787 return
788 endif
789 let munge = <SID>BMMunge(a:name, a:num) 811 let munge = <SID>BMMunge(a:name, a:num)
790 let hash = <SID>BMHash(munge) 812 let hash = <SID>BMHash(munge)
791 if s:bmenu_short == 0 813 if s:bmenu_short == 0
792 let name = 'an ' . g:bmenu_priority . '.' . hash . ' &Buffers.' . munge 814 let s:bmenu_items[a:num] = munge
793 else 815 let cmd = 'an ' . g:bmenu_priority . '.' . hash . ' &Buffers.' . munge
794 let name = 'an ' . g:bmenu_priority . '.' . hash . '.' . hash . ' &Buffers.' . <SID>BMHash2(munge) . munge 816 else
817 let menu_name = <SID>BMHash2(munge) . munge
818 let s:bmenu_items[a:num] = menu_name
819 let cmd = 'an ' . g:bmenu_priority . '.' . hash . '.' . hash . ' &Buffers.' . menu_name
795 endif 820 endif
796 " set 'cpo' to include the <CR> 821 " set 'cpo' to include the <CR>
797 let cpo_save = &cpo 822 let cpo_save = &cpo
798 set cpo&vim 823 set cpo&vim
799 exe name . ' :confirm b' . a:num . '<CR>' 824 exe cmd . ' :confirm b' . a:num . '<CR>'
800 let &cpo = cpo_save 825 let &cpo = cpo_save
801 endfunc 826 endfunc
802 827
803 " Truncate a long path to fit it in a menu item. 828 " Truncate a long path to fit it in a menu item.
804 if !exists("g:bmenu_max_pathlen") 829 if !exists("g:bmenu_max_pathlen")
805 let g:bmenu_max_pathlen = 35 830 let g:bmenu_max_pathlen = 35
806 endif 831 endif
807 func! s:BMTruncName(fname) 832 func s:BMTruncName(fname)
808 let name = a:fname 833 let name = a:fname
809 if g:bmenu_max_pathlen < 5 834 if g:bmenu_max_pathlen < 5
810 let name = "" 835 let name = ""
811 else 836 else
812 let len = strlen(name) 837 let len = strlen(name)
822 endif 847 endif
823 endif 848 endif
824 return name 849 return name
825 endfunc 850 endfunc
826 851
827 func! s:BMMunge(fname, bnum) 852 func s:BMMunge(fname, bnum)
828 let name = a:fname 853 let name = a:fname
829 if name == '' 854 if name == ''
830 if !exists("g:menutrans_no_file") 855 if !exists("g:menutrans_no_file")
831 let g:menutrans_no_file = "[No Name]" 856 let g:menutrans_no_file = "[No Name]"
832 endif 857 endif
939 cnoremenu <script> <silent> 1.100 PopUp.Select\ &All <C-U>call <SID>SelectAll()<CR> 964 cnoremenu <script> <silent> 1.100 PopUp.Select\ &All <C-U>call <SID>SelectAll()<CR>
940 965
941 if has("spell") 966 if has("spell")
942 " Spell suggestions in the popup menu. Note that this will slow down the 967 " Spell suggestions in the popup menu. Note that this will slow down the
943 " appearance of the menu! 968 " appearance of the menu!
944 func! <SID>SpellPopup() 969 func s:SpellPopup()
945 if exists("s:changeitem") && s:changeitem != '' 970 if exists("s:changeitem") && s:changeitem != ''
946 call <SID>SpellDel() 971 call <SID>SpellDel()
947 endif 972 endif
948 973
949 " Return quickly if spell checking is not enabled. 974 " Return quickly if spell checking is not enabled.
995 endif 1020 endif
996 endif 1021 endif
997 call cursor(0, curcol) " put the cursor back where it was 1022 call cursor(0, curcol) " put the cursor back where it was
998 endfunc 1023 endfunc
999 1024
1000 func! <SID>SpellReplace(n) 1025 func s:SpellReplace(n)
1001 let l = getline('.') 1026 let l = getline('.')
1002 " Move the cursor to the start of the word. 1027 " Move the cursor to the start of the word.
1003 call spellbadword() 1028 call spellbadword()
1004 call setline('.', strpart(l, 0, col('.') - 1) . s:suglist[a:n - 1] 1029 call setline('.', strpart(l, 0, col('.') - 1) . s:suglist[a:n - 1]
1005 \ . strpart(l, col('.') + len(s:fromword) - 1)) 1030 \ . strpart(l, col('.') + len(s:fromword) - 1))
1006 endfunc 1031 endfunc
1007 1032
1008 func! <SID>SpellDel() 1033 func s:SpellDel()
1009 exe "aunmenu PopUp." . s:changeitem 1034 exe "aunmenu PopUp." . s:changeitem
1010 exe "aunmenu PopUp." . s:additem 1035 exe "aunmenu PopUp." . s:additem
1011 exe "aunmenu PopUp." . s:ignoreitem 1036 exe "aunmenu PopUp." . s:ignoreitem
1012 aunmenu PopUp.-SpellSep- 1037 aunmenu PopUp.-SpellSep-
1013 let s:changeitem = '' 1038 let s:changeitem = ''