Tip 165 Printable Monobook Previous Next created 2001 · complexity intermediate · version 7.0

You may use the :split or :vsplit commands to display several windows, with some windows showing different parts of one buffer, while other windows show other buffers.

When finished with a buffer, you can close it with the :bdelete command. However, that command will also close all windows currently showing the buffer.

The script below defines the :Bclose command that deletes a buffer, while keeping the current window layout (no windows are closed).

The :Bclose command deletes a buffer without changing the window layout. For each window where the buffer is currently displayed:

Show the alternate buffer ( :buffer # ), if any.

), if any. Otherwise, show the previous buffer ( :bprevious ), if any.

), if any. Otherwise, show an empty buffer.

:Bclose Close buffer in current window.

:Bclose N Close buffer number N (as shown by :ls ).

:Bclose Name Close buffer named Name (as shown by :ls ).

Assuming the default backslash leader key, you can also press \bd to close (delete) the buffer in the current window (same as :Bclose ).

Like the :bdelete command, :Bclose will fail if the buffer has been modified. You can append ! to discard all changes (for example, :Bclose! will delete the buffer in the current window; any changes to the buffer are lost).

By default, :Bclose will close a buffer even if it is displayed in multiple windows (the windows are not closed). Put the following in your vimrc if you would prefer that a buffer is not closed if it is displayed more than once:

:let bclose_multiple = 0

Create file ~/.vim/plugin/bclose.vim (Unix) or $HOME/vimfiles/plugin/bclose.vim (Windows) containing the script below, then restart Vim.

" Delete buffer while keeping window layout (don't close buffer's windows). " Version 2008-11-18 from http://vim.wikia.com/wiki/VimTip165 if v:version < 700 || exists('loaded_bclose') || &cp finish endif let loaded_bclose = 1 if !exists('bclose_multiple') let bclose_multiple = 1 endif " Display an error message. function! s:Warn(msg) echohl ErrorMsg echomsg a:msg echohl NONE endfunction " Command ':Bclose' executes ':bd' to delete buffer in current window. " The window will show the alternate buffer (Ctrl-^) if it exists, " or the previous buffer (:bp), or a blank buffer if no previous. " Command ':Bclose!' is the same, but executes ':bd!' (discard changes). " An optional argument can specify which buffer to close (name or number). function! s:Bclose(bang, buffer) if empty(a:buffer) let btarget = bufnr('%') elseif a:buffer =~ '^\d\+$' let btarget = bufnr(str2nr(a:buffer)) else let btarget = bufnr(a:buffer) endif if btarget < 0 call s:Warn('No matching buffer for '.a:buffer) return endif if empty(a:bang) && getbufvar(btarget, '&modified') call s:Warn('No write since last change for buffer '.btarget.' (use :Bclose!)') return endif " Numbers of windows that view target buffer which we will delete. let wnums = filter(range(1, winnr('$')), 'winbufnr(v:val) == btarget') if !g:bclose_multiple && len(wnums) > 1 call s:Warn('Buffer is in multiple windows (use ":let bclose_multiple=1")') return endif let wcurrent = winnr() for w in wnums execute w.'wincmd w' let prevbuf = bufnr('#') if prevbuf > 0 && buflisted(prevbuf) && prevbuf != btarget buffer # else bprevious endif if btarget == bufnr('%') " Numbers of listed buffers which are not the target to be deleted. let blisted = filter(range(1, bufnr('$')), 'buflisted(v:val) && v:val != btarget') " Listed, not target, and not displayed. let bhidden = filter(copy(blisted), 'bufwinnr(v:val) < 0') " Take the first buffer, if any (could be more intelligent). let bjump = (bhidden + blisted + [-1])[0] if bjump > 0 execute 'buffer '.bjump else execute 'enew'.a:bang endif endif endfor execute 'bdelete'.a:bang.' '.btarget execute wcurrent.'wincmd w' endfunction command! -bang -complete=buffer -nargs=? Bclose call <SID>Bclose(<q-bang>, <q-args>) nnoremap <silent> <Leader>bd :Bclose<CR>

Alternative Script [ edit | edit source ]

Below, I have a new, more complicated version of above script. The below script will actually create a scratch buffer if there are no listed buffers left. The script, in addition, takes care of a small annoyance. Before, if you 1) open vim, 2) :e a file, 3) :bd, 4) :e the same file, then there will be two buffers listed (that file and a [no name] buffer). The following script ensures this doesn't happen.

Everything in this tutorial assumes the user does "set hidden".

Using this Kwbd command ( :Kwbd ) will make Vim windows behave more like an IDE, or maybe even better. You can also setup a mapping, see the end of the script.

"here is a more exotic version of my original Kwbd script "delete the buffer; keep windows; create a scratch buffer if no buffers left function s:Kwbd(kwbdStage) if(a:kwbdStage == 1) if(&modified) let answer = confirm("This buffer has been modified. Are you sure you want to delete it?", "&Yes

&No", 2) if(answer != 1) return endif endif if(!buflisted(winbufnr(0))) bd! return endif let s:kwbdBufNum = bufnr("%") let s:kwbdWinNum = winnr() windo call s:Kwbd(2) execute s:kwbdWinNum . 'wincmd w' let s:buflistedLeft = 0 let s:bufFinalJump = 0 let l:nBufs = bufnr("$") let l:i = 1 while(l:i <= l:nBufs) if(l:i != s:kwbdBufNum) if(buflisted(l:i)) let s:buflistedLeft = s:buflistedLeft + 1 else if(bufexists(l:i) && !strlen(bufname(l:i)) && !s:bufFinalJump) let s:bufFinalJump = l:i endif endif endif let l:i = l:i + 1 endwhile if(!s:buflistedLeft) if(s:bufFinalJump) windo if(buflisted(winbufnr(0))) | execute "b! " . s:bufFinalJump | endif else enew let l:newBuf = bufnr("%") windo if(buflisted(winbufnr(0))) | execute "b! " . l:newBuf | endif endif execute s:kwbdWinNum . 'wincmd w' endif if(buflisted(s:kwbdBufNum) || s:kwbdBufNum == bufnr("%")) execute "bd! " . s:kwbdBufNum endif if(!s:buflistedLeft) set buflisted set bufhidden=delete set buftype= setlocal noswapfile endif else if(bufnr("%") == s:kwbdBufNum) let prevbufvar = bufnr("#") if(prevbufvar > 0 && buflisted(prevbufvar) && prevbufvar != s:kwbdBufNum) b # else bn endif endif endif endfunction command! Kwbd call s:Kwbd(1) nnoremap <silent> <Plug>Kwbd :<C-u>Kwbd<CR> " Create a mapping (e.g. in your .vimrc) like this: "nmap <C-W>! <Plug>Kwbd

See also [ edit | edit source ]

Bbye Delete buffers and close files in Vim without messing up your window layout. Rewritten Bclose.vim to handle multiple windows. Comes with warranty!

BufClose not useful because it doesn't work as expected (if you close a buffer that's open in several windows, those windows close)

bufkill unload/delete/wipe a buffer, keep its window(s), display last accessed buffer(s)

MiniBufExplorer provides this capability with a simple user interface

For getting asked what should be done, when a file has been modified I use normally :confirm :bd . So I changed the script in 2lines to achieve this:

if empty(a:bang) && getbufvar(btarget, '&modified') call s:Warn('No write since last change for buffer '.btarget.' (use :Bclose!)') " return endif ... endif endfor " execute 'bdelete'.a:bang.' '.btarget execute ':confirm :bdelete '.btarget execute wcurrent.'wincmd w'

Just comment out the return and replace the command. greetings - Joe