5

Very often, after days and days of coding, opening vim -S session everytime, I end up with a fair long buffer list that I want to clean.

Therefore I use :ls to list all buffers, and then :bdelete several times, with a range of buffer numbers and or explicitly specifying buffer names.

The weak points in this approach are two:

  1. if I run :bdelete on a single buffer, the lines N buffers deleted and Press ENTER or type command to continue do not appear, and the buffer list closes, so I have to run :ls again, which is very annoying;
  2. I have to run :bdelete several times, which is less a pain but still annoying.

If there's a cleaner way to list non-contiguous buffer names to :bdelete, that would be great, as it would solve 1 and 2; if not, then a way to prevent the closure of the output of :ls would be good enough, as it'd solve 2.

Important edit

I don't want to delete any buffer which is opened on a window in any tab.

Enlico
  • 2,194
  • 15
  • 31

2 Answers2

6

For my config I wrote this:

~/.vim/autoload/misc.vim

" Wipe all deleted (unloaded & unlisted) or all unloaded buffers
function! misc#bwipeout(listed) abort
    let l:buffers = filter(getbufinfo(), {_, v -> !v.loaded && (!v.listed || a:listed)})
    if !empty(l:buffers)
        execute 'bwipeout' join(map(l:buffers, {_, v -> v.bufnr}))
    endif
endfunction

~/.vim/vimrc

" :Bwipeout[!]
" wipe all deleted/unloaded buffers
command! -bar -bang Bwipeout call misc#bwipeout(<bang>0)

Now do :Bwipeout! to clean the whole mess.

Note: :bwipeout differs from :bdelete in that it actually deletes the buffer, while :bdelete keeps name/number/local bookmarks for later re-use. You can see all "deleted" buffers by the command :ls!.

If you still want to make use of :bdelete you can adapt the code easily:

function s:bdelete() abort
    let l:buffers = filter(getbufinfo(), {_, v -> !v.loaded && v.listed})
    if !empty(l:buffers)
        execute 'bdelete' join(map(l:buffers, {_, v -> v.bufnr}))
    endif
endfunction
command! -bar Bdelete call s:bdelete()
Matt
  • 20,685
  • 1
  • 11
  • 24
  • :bwipeout's doc says Don't use this unless you know what you are doing. Any comments on this, with respect to your solution? – Enlico Sep 02 '20 at 12:31
  • @EnricoMariaDeAngelis If you wonder what's a (minor) difference between :bdelete and :bwipeout you should read both :h :bd and :h :bw and compare. Personally I like :bw more and use it instead of :bd on those rare occasions I have to use one of them. – Matt Sep 02 '20 at 13:08
  • 1
    @EnricoMariaDeAngelis You might be interested in this q&a about :bwipeout – Rich Sep 02 '20 at 14:33
4

For my config I wrote this:

" kill-all but visible buffers
nnoremap <silent> <M-BS> :call Delete_buffers()<CR>:echo "Non-windowed buffers are deleted"<CR>

"" Delete all(saved) but visible buffers func! Delete_buffers() " all visible buffers in all tabs let buflist = [] for i in range(tabpagenr('$')) call extend(buflist, tabpagebuflist(i + 1)) endfor

&quot; all existing buffers
for bnr in range(1, bufnr(&quot;$&quot;))
    if index(buflist, bnr) == -1 &amp;&amp; buflisted(bnr)
        exe 'bd ' . bnr
    endif
endfor

endfunc

Then with Alt+Backspace all but buffers currently visible in split windows are deleted.

Very handy to save/update session without bunch of unneeded buffers.

PS, updated to not delete buffers in other tabs.

PPS, thx @matt, better version:

func! Delete_buffers()
    let l:buffers = filter(getbufinfo(), {_, v -> v.hidden})
    if !empty(l:buffers)
        execute 'bwipeout' join(map(l:buffers, {_, v -> v.bufnr}))
    endif
endfunc

Maxim Kim
  • 13,376
  • 2
  • 18
  • 46
  • let l:buffers = filter(getbufinfo(), {_, v -> v.hidden}) will eventually fail for modified hidden buffers. – Matt Sep 02 '20 at 13:31
  • 1
    @Matt, yes I know. I have set confirm and get confirmation requests to save those buffers. – Maxim Kim Sep 02 '20 at 13:43