nelstrom
nelstrom

Reputation: 19572

How can I pare down Vim's buffer list to only include active buffers

How can I pare down my buffer list to only include buffers that are currently open in a window/tab?

When I've been running Vim for a long time, the list of buffers revealed by the :ls command is too large to work with. Ideally, I would like to delete all of the buffers which are not currently visible in a tab or window by running a custom command such as :Only. Can anybody suggest how to achieve this?

It looks like the :bdelete command can accept a list of buffer numbers, but I'm not sure how to translate the output from :ls to a format that can be consumed by the :bdelete command. Any help would be appreciated.

Clarification

Lets say that in my Vim session I have opened 4 files. The :ls command outputs:

:ls
1  a   "abc.c"
2  h   "123.c"
3  h   "xyz.c"
4  a   "abc.h"

Buffer 1 is in the current tab, and and buffer 4 is in a separate tab, but buffers 2 and 3 are both hidden. I would like to run the command :Only, and it would wipe buffers 2 and 3, so the :ls command would output:

:ls
1  a   "abc.c"
4  a   "abc.h"

This example doesn't make the proposed :Only command look very useful, but if you have a list of 40 buffers it would be very welcome.

Upvotes: 11

Views: 3547

Answers (5)

Andrew Sohn
Andrew Sohn

Reputation: 757

" Active Buffers
function ListActive()
    let result = ""
    silent! redir => result
    silent! exe "ls"
    redir END
    let active = ""
    for i in split(result, "\n")
      if matchstr(i, '\m[0-9][0-9]* .a.. ') != ""
        let active .= "\n".i
      endif
    endfor
    echo active
endfunction

E.g.

From shell:

$ vim -p foo bar baz

Inside vim:

:call ListActive()

Output:

1 %a   "foo"                          line 1
2  a   "bar"                          line 0
3  a   "baz"                          line 0
Press ENTER or type command to continue

Close one file:

:q

:call ListActive()

Output:

2 %a   "bar"                          line 1
3  a   "baz"                          line 0
Press ENTER or type command to continue

Open another file:

:split ~/.vimrc

:call ListActive()

Output:

2 #a   "bar"                          line 1
3  a   "baz"                          line 0
4 %a   "~/.vimrc"                     line 244

Upvotes: 1

Drew Stephens
Drew Stephens

Reputation: 17847

I don't use set hidden, so my desire to close buffers that aren't shown in any window is simply to clear up the output of :ls. This function does just that:

function! CloseUnloadedBuffers()
    let lastBuffer = bufnr('$')

    let currentBuffer = 1
    while currentBuffer <= lastBuffer
        " If buffer exists, is shown in :ls output, and isn't loaded
        if bufexists(currentBuffer) && buflisted(currentBuffer) && bufloaded(currentBuffer) == 0
            execute 'bdelete' currentBuffer
        endif

        let currentBuffer = currentBuffer + 1
    endwhile
endfunction

From this Nabble thread

Upvotes: 0

nelstrom
nelstrom

Reputation: 19572

I've adapted Laurence Gonslaves solution.

command! -nargs=* Only call CloseHiddenBuffers()
function! CloseHiddenBuffers()
  " figure out which buffers are visible in any tab
  let visible = {}
  for t in range(1, tabpagenr('$'))
    for b in tabpagebuflist(t)
      let visible[b] = 1
    endfor
  endfor
  " close any buffer that are loaded and not visible
  let l:tally = 0
  for b in range(1, bufnr('$'))
    if bufloaded(b) && !has_key(visible, b)
      let l:tally += 1
      exe 'bw ' . b
    endif
  endfor
  echon "Deleted " . l:tally . " buffers"
endfun

I changed it to use bwipeout instead of bdelete, and added the message to show how many buffers have been removed.

Upvotes: 12

Luc Hermitte
Luc Hermitte

Reputation: 32986

Are you looking for:

:echo map(filter(range(0, bufnr('$')), 'bufwinnr(v:val)>=0'), 'bufname(v:val)')

or more precisely:

exe 'bw '.join(filter(range(0, bufnr('$')), 'bufwinnr(v:val)<0'), ' ')

?


EDIT: The previous answer did not take multiple tabs into account.

It seems I used a complex approach. The list of opened, and displayed, buffers can be obtained thanks to tabpagebuflist() with:

let tabs = range(1, tabpagenr())
echo lh#list#unique_sort(eval(join(map(tabs, 'tabpagebuflist(v:val)'), '+')))

(lh#list#unique_sort() comes from lh-vim-lib, it defines the sort+unique function that vim does not provide)

In order to have the non opened buffers, it becomes a little bit more tricky. Either we use a loop of each tab to obtain the buffers non displayed, or we make a diff between the previous result and the bufexisting buffers:

let tabs = range(1, tabpagenr())
let windowed = lh#list#unique_sort(eval(join(map(tabs, 'tabpagebuflist(v:val)'), '+')))
let existing = filter(range(0,bufnr('$')), 'bufexists(v:val)')
let non_windowed = filter(copy(existing), 'match(windowed, "^".v:val."$")<0')  
echo non_windowed

Upvotes: 7

hacintosh
hacintosh

Reputation: 3862

If you have say 4 buffers open:

:ls
1    abc.c
2    123.c
3    xyz.c
4    abc.h

and you want to "close" buffer 3 "xyz.c":

:bd3

results after deleting buffer 3:

:ls
1    abc.c
2    123.c
4    abc.h

Upvotes: 0

Related Questions