Mercurial > vim
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 = '' |