Reputation: 19572
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.
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
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
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
Upvotes: 0
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
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
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