Cleaning up the buffer list

81 views
Skip to first unread message

Chris Jones

unread,
Jul 11, 2012, 7:38:04 PM7/11/12
to vim...@googlegroups.com
Ping's question a few hours ago reminded me of something I keep putting
off.

I usually have a single Vim instance that stays up for weeks if not
months at a time, such that I eventually have a large number of buffers
as listed by the ‘:ls/:buffers’ command.. well over 100 at this point in
time. Even though I usually take good care to exit files once I know
I will no longer need them (via a ‘:q/:wq’).

I took a look at buffer-oriented functions and could no see anything at
a glance that looked like it might help, so I put together the following
‘prototype’ :-) mostly to verify that I understand the problem:

| function! GarCol()
|
| redir @x
| exe ':silent ls'
| redir end
| let mybuffers = @x
| let mbfl = split(mybuffers, '\n')
| for bf in mbfl
| let bfl = split(bf, ' ')
| echo bfl
| if bfl[1] =~ '.a'
| echo 'skipped %a or #a buffer'
| continue
| endif
| if bfl[2] =~ 'a'
| echo 'skipped active buffer'
| continue
| endif
| if bfl[3] =~ '+'
| echo 'skipped modified buffer'
| continue
| endif
| echo bfl[0] ' will be deleted'
| let bfd = bfl[0]
| exe "bw"." ".bfd
| " exe "bun"." ".bfd
| endfor
| return 0
| endfunction

What this does is feed the output of the :ls command to a variable and
filter out buffers that are either ‘active’ (displayed in a window) or
‘modified’. All others get buffer-wiped.

I quickly fired up a Vim session and populated it with test files, and
the above appears to work as I intended, but what bothers me is that
it's based on the output of a command meant for interactive use.

Among other things, the current format is not written in stone & more
subject to change than return values from Vim functions...

Besides, it's based on what I see when I issue the ‘:buffers’ command,
and for all I know, there may be cases where an extra column is added in
the output, (e.g.) so that my list indexing would point to something
else than the flags I was looking for causing unpredictable results.

No big deal of course, since it's only Vim buffers I am throwing away,
not files.. but could be annoying all the same.

Has anyone looked into buffer list manipulation before and could advise
on a different approach & suggest how I might be able to come up with
something a bit less clunky..?

Thanks,

CJ

--
WE GET SIGNAL

ping

unread,
Jul 11, 2012, 7:58:29 PM7/11/12
to vim...@googlegroups.com
On 7/11/2012 7:38 PM, Chris Jones wrote:
> Ping's question a few hours ago reminded me of something I keep putting
> off.
>
> I usually have a single Vim instance that stays up for weeks if not
> months at a time, such that I eventually have a large number of buffers
> as listed by the �:ls/:buffers� command.. well over 100 at this point in
> time. Even though I usually take good care to exit files once I know
> I will no longer need them (via a �:q/:wq�).
>
> I took a look at buffer-oriented functions and could no see anything at
> a glance that looked like it might help, so I put together the following
> �prototype� :-) mostly to verify that I understand the problem:
> filter out buffers that are either �active� (displayed in a window) or
> �modified�. All others get buffer-wiped.
>
> I quickly fired up a Vim session and populated it with test files, and
> the above appears to work as I intended, but what bothers me is that
> it's based on the output of a command meant for interactive use.
>
> Among other things, the current format is not written in stone & more
> subject to change than return values from Vim functions...
>
> Besides, it's based on what I see when I issue the �:buffers� command,
> and for all I know, there may be cases where an extra column is added in
> the output, (e.g.) so that my list indexing would point to something
> else than the flags I was looking for causing unpredictable results.
>
> No big deal of course, since it's only Vim buffers I am throwing away,
> not files.. but could be annoying all the same.
>
> Has anyone looked into buffer list manipulation before and could advise
> on a different approach & suggest how I might be able to come up with
> something a bit less clunky..?
>
> Thanks,
>
> CJ
>

besides other things, b.t.w, the :
:bdelete and :bwipeout
should help cleaning closed buffers (means files closed by :q) out.
just FYI.

Danek Duvall

unread,
Jul 11, 2012, 9:17:52 PM7/11/12
to vim...@googlegroups.com
On Wed, Jul 11, 2012 at 07:38:04PM -0400, Chris Jones wrote:

> Has anyone looked into buffer list manipulation before and could advise
> on a different approach & suggest how I might be able to come up with
> something a bit less clunky..?

You could use :bufdo to execute a function that gathered a list of the
buffer numbers that could be killed, and then call :bwipeout/:bdelete on
them.

Danek

Christian Brabandt

unread,
Jul 12, 2012, 1:58:24 AM7/12/12
to vim...@googlegroups.com
Somethins like this, probably:
fu! s:MyBufferClean()
let _t = tabpagenr()
let blist=range(1,bufnr('$'))
tabdo call filter(blist, 'bufwinnr(v:val)==-1')
for bufnr in blist
exe 'bw' bufnr
endfor
exe _t "tabnext"
endfu
command! MyBufferClean :call <sid>MyBufferClean()

regards,
Christian


geoffr...@thomsonreuters.com

unread,
Jul 12, 2012, 6:34:32 AM7/12/12
to vim...@googlegroups.com
On Thursday, 12 July 2012 00:38:04 UTC+1, Chris Jones wrote:
> I usually have a single Vim instance that stays up for weeks if not
> months at a time, such that I eventually have a large number of buffers
> as listed by the ‘:ls/:buffers’ command.. well over 100 at this point in
> time. Even though I usually take good care to exit files once I know
> I will no longer need them (via a ‘:q/:wq’).

> [I wrote a function to] feed the output of the :ls command to a variable and
> filter out buffers that are either ‘active’ (displayed in a window) or
> ‘modified’. All others get buffer-wiped. [The function
> has potential problems.]

> Has anyone looked into buffer list manipulation before and could advise
> on a different approach &amp; suggest how I might be able to come up with
> something a bit less clunky..?

I have "set hidden" in my vimrc so I end up with a lot of
buffers in the list.

From time to time I use the first example given here to
delete all hidden buffers. Not sure if that's quite what
you want?

http://stackoverflow.com/questions/8450919/how-can-i-delete-all-hidden-buffers

You can also use :bd to unload the buffer and delete it from
the list once you've finished with it, to avoid leaving it
in the buffer list. Possibly a dangerous habit to acquire
though.

regards,
Geoff

Asis Hallab

unread,
Jul 12, 2012, 6:46:07 AM7/12/12
to vim...@googlegroups.com



On Thursday, 12 July 2012 00:38:04 UTC+1, Chris Jones  wrote:
> I usually have a single Vim instance that stays up for weeks if not
> months at a time, such that I eventually have a large number of buffers
> as listed by the ‘:ls/:buffers’ command.. well over 100 at this point in
> time. Even though I usually take good care to exit files once I know
> I will no longer need them (via a ‘:q/:wq’).

> [I wrote a function to] feed the output of the :ls command to a variable and
> filter out buffers that are either ‘active’ (displayed in a window) or
> ‘modified’. All others get buffer-wiped. [The function
> has potential problems.]

> Has anyone looked into buffer list manipulation before and could advise
> on a different approach &amp; suggest how I might be able to come up with
> something a bit less clunky..?

I use the following bufonly plugin written by CJ Robinson, which works perfectly for me.
Originally coming from using IDEs such as Eclipse, 
the provided :BOnly command works as the "close others" on the Tabs of Eclipse.
I use it quite frequently. Also you do not have to worry about changed buffers getting deleted, 
the plugin warns you and does not close them, unless you provide a Bang like this :BOnly! 

Actually, you can check it out here:

or, to install it, put the following vimscript as bonly.vim in your ~/.vim/plugin directory:

" BufOnly.vim  -  Delete all the buffers except the current/named buffer.
"
" Copyright November 2003 by Christian J. Robinson <hep...@gmail.com>
"
" Distributed under the terms of the Vim license.  See ":help license".
"
" Usage:
"
" :Bonly / :BOnly / :Bufonly / :BufOnly [buffer] 
"
" Without any arguments the current buffer is kept.  With an argument the
" buffer name/number supplied is kept.

command! -nargs=? -complete=buffer -bang Bonly
    \ :call BufOnly('<args>', '<bang>')
command! -nargs=? -complete=buffer -bang BOnly
    \ :call BufOnly('<args>', '<bang>')
command! -nargs=? -complete=buffer -bang Bufonly
    \ :call BufOnly('<args>', '<bang>')
command! -nargs=? -complete=buffer -bang BufOnly
    \ :call BufOnly('<args>', '<bang>')

function! BufOnly(buffer, bang)
if a:buffer == ''
" No buffer provided, use the current buffer.
let buffer = bufnr('%')
elseif (a:buffer + 0) > 0
" A buffer number was provided.
let buffer = bufnr(a:buffer + 0)
else
" A buffer name was provided.
let buffer = bufnr(a:buffer)
endif

if buffer == -1
echohl ErrorMsg
echomsg "No matching buffer for" a:buffer
echohl None
return
endif

let last_buffer = bufnr('$')

let delete_count = 0
let n = 1
while n <= last_buffer
if n != buffer && buflisted(n)
if a:bang == '' && getbufvar(n, '&modified')
echohl ErrorMsg
echomsg 'No write since last change for buffer'
\ n '(add ! to override)'
echohl None
else
silent exe 'bdel' . a:bang . ' ' . n
if ! buflisted(n)
let delete_count = delete_count+1
endif
endif
endif
let n = n+1
endwhile

if delete_count == 1
echomsg delete_count "buffer deleted"
elseif delete_count > 1
echomsg delete_count "buffers deleted"
endif

endfunction

Chris Jones

unread,
Jul 12, 2012, 10:22:49 PM7/12/12
to vim...@googlegroups.com
On Thu, Jul 12, 2012 at 06:34:32AM EDT, geoffr...@thomsonreuters.com wrote:

[..]

> I have "set hidden" in my vimrc so I end up with a lot of
> buffers in the list.

Good to know I'm not alone.. :-)

Mind you, some might argue that you're not really supposed to use the
:ls command at all.. so basically you don't get to see it.. You just do
a ‘:b part_of_filename<TAB>’ and Vim elegantly completes.. and display
a list of matches. So maybe it's a non-issue.

> From time to time I use the first example given here to
> delete all hidden buffers. Not sure if that's quite what
> you want?
>
> http://stackoverflow.com/questions/8450919/how-can-i-delete-all-hidden-buffers

Thanks, very useful.. An actually if you page down a bit, someone came
up with something quite similar to my effort..

> You can also use :bd to unload the buffer and delete it from
> the list once you've finished with it, to avoid leaving it
> in the buffer list. Possibly a dangerous habit to acquire
> though.

Very good point.. A lot of ‘problems’ are better addressed by adjusting
one's work habit than scripting contortions.. I had also thought about
this as an option.. e.g. a short user-defined command such as possibly
‘:QQ’ that would quit and unload the buffer in one fell swoop..

I'm not sure I understand what ‘danger’ you are referring to, though..
If the target buffer is modified, Vim will complain (unless you do
a :bd!).. otherwise the only risk is that you'd have to reload the
buffer (:e file).

Thanks,

CJ

--
ALL YOUR BASE ARE BELONG TO US!

Chris Jones

unread,
Jul 12, 2012, 10:54:39 PM7/12/12
to vim...@googlegroups.com
On Thu, Jul 12, 2012 at 01:58:24AM EDT, Christian Brabandt wrote:
> On Thu, July 12, 2012 01:38, Chris Jones wrote:

[..]

> > Has anyone looked into buffer list manipulation before and could
> > advise on a different approach & suggest how I might be able to come
> > up with something a bit less clunky..?

> Somethins like this, probably:
> fu! s:MyBufferClean()
> let _t = tabpagenr()
> let blist=range(1,bufnr('$'))
> tabdo call filter(blist, 'bufwinnr(v:val)==-1')
> for bufnr in blist
> exe 'bw' bufnr
> endfor
> exe _t "tabnext"
> endfu
> command! MyBufferClean :call <sid>MyBufferClean()

Yes, bufwinnr()'s ‘-1’ return value was just about the only thing that
came close. I believe the above works but it's limited to hidden vs.
active.. does not check if a buffer was modified..

I guess I was looking for something that would basically return
something a bit like a C struct, with maybe the buffer number, the file
name and then the indicators.

All the same, I read ‘:h :ls’ and the description of the format of :ls's
output is quite precise.. so I guess I'll just rework my initial effort
and stick with that.

Thanks,

CJ

--
HOW ARE YOU GENTLEMEN?

Vlad Irnov

unread,
Jul 13, 2012, 5:54:07 AM7/13/12
to vim_use


On Jul 12, 10:54 pm, Chris Jones <cjns1...@gmail.com> wrote:
> On Thu, Jul 12, 2012 at 01:58:24AM EDT, Christian Brabandt wrote:
> > On Thu, July 12, 2012 01:38, Chris Jones wrote:
>
> [..]
>
> > > Has anyone looked into buffer list manipulation before and could
> > > advise on a different approach & suggest how I might be able to come
> > > up with something a bit less clunky..?
> > Somethins like this, probably:
> > fu! s:MyBufferClean()
> >     let _t = tabpagenr()
> >     let blist=range(1,bufnr('$'))
> >     tabdo call filter(blist, 'bufwinnr(v:val)==-1')
> >     for bufnr in blist
> >    exe 'bw' bufnr
> >     endfor
> >     exe _t "tabnext"
> > endfu
> > command! MyBufferClean :call <sid>MyBufferClean()
>
> Yes, bufwinnr()'s  ‘-1’ return value was just about the only thing that
> came close. I believe the above works but it's limited to hidden vs.
> active.. does not check if a buffer was modified..
>
> I guess I was looking for something that would basically return
> something a bit like a C struct, with maybe the buffer number, the file
> name and then the indicators.

I would just wipe out all unloaded buffers that are also unlisted:

let bnrs = filter(range(1,bufnr('$')), 'bufexists(v:val)')
for bnr in bnrs
if !bufloaded(bnr) && !buflisted(bnr)
exe 'bw '.bnr
endif
endfor

By definition, if a buffer is unloaded, it is not displayed in any
window and is not modified.
An unloaded buffer can still be listed, which could mean the user
might need it in the future, although this is unlikely and I do not
know any actual examples when this happens.

Also, if the goal to get buffers that are displayed in windows, it's
better to use tabpagebuflist() as shown in the help.

Regards,
Vlad

geoffr...@thomsonreuters.com

unread,
Jul 13, 2012, 6:31:40 AM7/13/12
to vim...@googlegroups.com
On Friday, 13 July 2012 03:22:49 UTC+1, Chris Jones wrote:
> On Thu, Jul 12, 2012 at 06:34:32AM EDT, Geoff wrote:
>
> &gt; You can also use :bd to unload the buffer and delete it from
> &gt; the list once you&#39;ve finished with it, to avoid leaving it
> &gt; in the buffer list. Possibly a dangerous habit to acquire
> &gt; though.
>
> I&#39;m not sure I understand what ‘danger’ you are referring to, though..
> If the target buffer is modified, Vim will complain (unless you do
> a :bd!).. otherwise the only risk is that you&#39;d have to reload the
> buffer (:e file).

Hmm, I'm not sure either. But if you've set hidden and then you use :bd
all the time you are defeating the purpose of having hidden set. Presumably
you had it set for a good reason...

I'm glad you asked actually - I set hidden a couple of years ago and I
definitely had a good reason at the time, but I find I'm no longer sure
what it was. When I wrote the previous email I did vaguely think hidden
was a way of protecting you from losing changes but as you say, :bd
warns about that anyway.

Maybe I'll try turning it off and find out why I set it in the first place :)

regards,
Geoff

Vlad Irnov

unread,
Jul 13, 2012, 7:00:23 AM7/13/12
to vim_use


On Jul 12, 10:54 pm, Chris Jones <cjns1...@gmail.com> wrote:
> On Thu, Jul 12, 2012 at 01:58:24AM EDT, Christian Brabandt wrote:
> > On Thu, July 12, 2012 01:38, Chris Jones wrote:
>
> [..]
>
> > > Has anyone looked into buffer list manipulation before and could
> > > advise on a different approach & suggest how I might be able to come
> > > up with something a bit less clunky..?
> > Somethins like this, probably:
> > fu! s:MyBufferClean()
> >     let _t = tabpagenr()
> >     let blist=range(1,bufnr('$'))
> >     tabdo call filter(blist, 'bufwinnr(v:val)==-1')
> >     for bufnr in blist
> >    exe 'bw' bufnr
> >     endfor
> >     exe _t "tabnext"
> > endfu
> > command! MyBufferClean :call <sid>MyBufferClean()
>
> Yes, bufwinnr()'s  ‘-1’ return value was just about the only thing that
> came close. I believe the above works but it's limited to hidden vs.
> active.. does not check if a buffer was modified..
>
> I guess I was looking for something that would basically return
> something a bit like a C struct, with maybe the buffer number, the file
> name and then the indicators.


I think used filter() the wrong way in my first reply. Better:

let bnrs = range(1,bufnr('$'))
call filter(bnrs, 'bufexists(v:val)')
for bnr in bnrs
if !bufloaded(bnr) && !buflisted(bnr)
exe 'bw '.bnr
endif
endfor

After rereading your first message I realized you probably want to get
rid of 'hidden' buffers. The function that Christian Brabandt
suggested would do it. I would do it like this (untested):

" Wipe out buffers that are not displayed in any window
" (including hidden buffers) and are not modified.
let bnrs = range(1,bufnr('$'))
call filter(bnrs, 'bufexists(v:val)')
let tablist = []
for i in range(1,tabpagenr('$'))
call extend(tablist, tabpagebuflist(i))
endfor
for bnr in bnrs
if index(tablist, bnr) < 0 && !getbufvar(bnr, '&mod')
exe 'bw '.bnr
endif
endfor

You can add check for buflisted() if for some reason you want to keep
unlisted buffers.

HTH,
Vlad

Chris Jones

unread,
Jul 13, 2012, 11:00:24 AM7/13/12
to vim...@googlegroups.com
On Fri, Jul 13, 2012 at 06:31:40AM EDT, geoffr...@thomsonreuters.com wrote:

[..]

> I'm glad you asked actually - I set hidden a couple of years ago and
> I definitely had a good reason at the time, but I find I'm no longer
> sure what it was.

[..]

For instance, I start making changes to a file, then I need to take
a quick look at another file before I commit the changes.. so, I'll do
‘:e file2’.. take a peek and then either ‘:q file2’.. or CTRL-O.. etc.
and get back to the file I was editing.. I ‘nohidden’ is set, Vim will
complain. I prefer this strategy to ‘:vne file2’ especially when
I already have several sub-windows in the current tab. One use-case
among many, I guess.. No wonder I end up with tons of files in my buffer
list..!

You could probably set it to ‘nohidden’ and see if/how it impacts your
workflow?
Reply all
Reply to author
Forward
0 new messages