Technology
 

Deleting a buffer without closing the window

From Vim Tips Wiki

(Redirected from VimTip622)

Tip 165 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).

Contents

[edit] Usage

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.
  • Otherwise, show the previous buffer (:bprevious), 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

[edit] Script

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 != w
      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('<bang>', '<args>')
nnoremap <silent> <Leader>bd :Bclose<CR>

[edit] See also

  • MiniBufExplorer provides this capability with a simple user interface
  • bufkill unload/delete/wipe a buffer, keep its window(s), display last accessed buffer(s)
  • BufClose not useful because it doesn't work as expected (if you close a buffer that's open in several windows, those windows close)

[edit] Comments

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 achive 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