Making ZZ Behave in Vim
Update Apr 4, 2008: I was recently reminded of this post and thought I should mention that this particular solutions sucks. Although it won't break anything, it doesn't work well. Don't waste your time :) Cheers.
I've gotten quite used to the idea of hitting ZZ
in Vim to kill the
window I'm currently working in. The problem is that I'm so used to the
idea, that I hit it almost automatically when I'm done with a particular
buffer and there is only one window left. The result is that the Vim session
is closed completely whether there is only one listed buffer or not.
My solution to this was not to find the probably existing command or
setting built into Vim that corrects this problem, but rather to write a
function that customizes the behavior of the ZZ
command. This would be the
first time I'd ever written a non-trivial function in pure Vim script, so
I thought I'd give it a shot. The following function is the result. I've put
lots of comments in there so that you can follow if you are new to Vim
scripting like I am.
function! BehaveZZ() " Get the number of *listed* buffers. let highbuf = bufnr("$") let buflist = [] let i = 1 while (i <= highbuf) "Skip unlisted buffers. if (bufexists(i) != 0 && buflisted(i)) call add(buflist, i) endif let i = i + 1 endwhile let bufcount = len(buflist) if (bufcount == 1) if (bufname("%") == "") " This buffer is unnamed (has no associated file). if (&modified) " Give option to save modifications. let choice = input("Lose modifications? [Enter=yes]: ") if (choice == "") set nomodified else echo "ZZ action aborted..." return endif else " The buffer has no modifications. Just do default ZZ. execute "x" endif else " There is only one listed buffer and it is named. In this case, " the standard ZZ works just fine, so do that. execute "x" endif elseif (getbufvar(bufnr("%"), "&buftype") != "") " This buffer is a "special" buffer. execute "bdelete" elseif (bufname("%") == "") " This buffer is unnamed (has no associated file). if (&modified) " Give option to save modifications. let choice = input("Lose modifications? [Enter=yes]: ") if (choice == "") set nomodified else echo "ZZ action aborted..." return endif endif if (winnr("$") > 1) " There are multiple windows open. Just do a normal ZZ. execute "x" else execute "buffer! " . bufnr("#") endif else " This is a named buffer. if (&modified) execute "write" endif if (winnr("$") > 1) " There are multiple windows. Just do normal ZZ. execute "x" else " There is only one window, but multiple listed buffers. let curbuf = bufnr("%") " If we have a 'last visited' buffer, go there. Else bnext. if (bufnr("#") != -1) execute "buffer! " . bufnr("#") else execute "bnext" endif execute "bdelete" . curbuf endif endif endfunction command! ZZ call BehaveZZ()
Now at the time of this writing, the above function has had very limited testing, so if you want to use it, beware. This is what the function does.
- If there are multiple windows open, just close the current
one. (Same as default
ZZ
) - If there is only one window open, but multiple buffers, delete the current buffer and switch to another one.
- If there is only one window, and only one listed buffer, exit
the session. (Same sd default
ZZ
) - If the current buffer is unnamed (ie. has no filename
associated with it), confirm loss of modifications. If user presses
just
Enter
, then discard modifications and delete the buffer. Else abort.
One behaviour of the default Vim that I found odd is a result of
opening multiple files from the command line at once like vim
file1 file2
. When you do this, and try to close one you
get a E173: 1 more file to edit
message, and nothing
happens at all. It however gives no such warnings if you open only
one file, then open a second with the :e file2
command
from inside the editor. This time it just closes the whole session if
there is only one window open. In both of these cases, my function
will close the currently active buffer and switch to one of the other
buffers in the buffer list.
I think I've handled most special cases where a buffer is special like a help buffer, or a scratch buffer too. Initially I made it so that if the current buffer was unnamed and modified then the modifications would just be discarded, but that made me nervous so I added a one key confirmation just to be safe. The whole function turned out to be a lot more complex than I thought it would (not to mention longer). Things like special buffers gave me a lot of grief. Being new to this, the whole snippet probably ended up being more complicated than it needed to be. If you see any bugs or optimizations, I'd be interested in hearing about them. Leave a comment after this post.
To use this function, drop it into your ~/.vimrc
and either
:call
it manually or map a key to it like
:map ZZ :call BehaveZZ()<CR>. That's the mapping I use and it simply replaces
ZZ
's normal
behavior with the new one.
No comments:
Post a Comment