Vim Tips Wiki
(Move categories to tip template)
(preious -> previous)
Line 19: Line 19:
 
* If I close the buffer in a window, it '''must not be closed''' in the other possible windows.
 
* If I close the buffer in a window, it '''must not be closed''' in the other possible windows.
 
* If there is an alternate buffer must be showing that.
 
* If there is an alternate buffer must be showing that.
* If there is not alternate buffer then must be showing the preious buffer.
+
* If there is not alternate buffer then must be showing the previous buffer.
 
* If there is no alternate nor previous buffer (it is the only buffer) must show an empty buffer.
 
* If there is no alternate nor previous buffer (it is the only buffer) must show an empty buffer.
   

Revision as of 17:12, 19 August 2008

Duplicate tip

This tip is very similar to the following:

These tips need to be merged – see the merge guidelines.

Tip 622 Printable Monobook Previous Next

created December 17, 2003 · complexity basic · author Charles E. Campbell, Jr. · version 5.7


In short: if I close a buffer (with :bn for example), I want its window to stay, showing its previous buffer.

The behaviour details:

  • The window layout must be kept in all circumstances.
  • If I close the buffer in a window, it must not be closed in the other possible windows.
  • If there is an alternate buffer must be showing that.
  • If there is not alternate buffer then must be showing the previous buffer.
  • If there is no alternate nor previous buffer (it is the only buffer) must show an empty buffer.

This is how it should work (gnu-emacs kill-buffer does it right):

  • In a multi-window environment:
    • visit a file in window,
    • go to 2nd file (vim gf)
    • go to 3rd file
    • kill-buffer of 3rd file, and you are back to the view of 2nd file, without disturbing windows splits / cursor position
    • kill 2nd file, and voila, you are back to 1st file.

Evolution of solutions

A list of trials and their flaws.

Vim internal

As you can see, the :bn cannot satisfy these requirements at all.

Small command

Here's a small command for your <.vimrc>:

com! Kwbd enew|bw # | bn
map bd :Kwbd<CR>
:com! Kwbd let kwbd_bn= bufnr("%")|enew|exe "bdel ".kwbd_bn|unlet kwbd_bn

To use it, type :Kwbd or bd Kwbd stands for: Keep window (layout) and delete the associated buffer.

Flaw: When a buffer is open in several windows, those other windows still get closed.

Trial two

" Delete the current buffer, issuing bnext in all windows
" where displayed before that
function DeleteBuffer2()
  let bid = bufnr("%")
  let wid = winnr()
  windo if bid == bufnr("%") | bprev | endif
  exe "bdel " . bid
  exe "normal " . wid . "^W^W"
endfunction

" count the number of buffers
function BufferCount()
  " save cur buf number
  let cbuf = bufnr("%")
  let bnum = 0
  bufdo let bnum = bnum + 1
  " return to the buf
  exe "b " . cbuf
  return bnum
endfunction

function DeleteBuffer()
  if BufferCount() > 1
    call DeleteBuffer2()
  else
    exe "bdel"
  endif
endfunction

map <C-K> :call DeleteBuffer()<CR>
imap <C-K> <C-O><C-K>

Flaw: Deletes the buffer from all windows.

Trial three

Here's a command that doesn't implement a buffer kill ring such as the script above implements; but it does preserve window layout. Use

:Kwbd to clear the current window and only do a buffer delete if its the only instance of that buffer being displayed :Kwbd! clears all windows currently displaying the current buffer, and do a buffer delete

Both of these forms of the command preserve window layout.

" Kwbd: keep window layout and do a buffer delete {{{2
" :Kwbd -- clear current window, do buffer delete if its the only one
" :Kwbd! -- clear all windows with current buffer and do buffer delete
com! -bang -nargs=? Kwbd set lz|call <SID>Kwbd(<bang>0)|set nolz
fun! s:Kwbd(really)
  let kwbd = winbufnr(0)
  let winnum = winnr()
  enew
  if a:really
    windo if winbufnr(0) == kwbd | enew | endif
    exe "bd ".kwbd
  else
    let s:cnt= 0
    windo if winbufnr(0) == kwbd | let s:cnt=s:cnt+1 | endif
    if s:cnt == 0
      exe "bd ".kwbd
    endif
  endif
  exe winnum."wincmd W"
endfun

Flaw: It always give an empty buffer in the current window.

Trial four

That's a very simple function I wrote to enable someone to close the current buffer (like :bd) yet not close any associated windows with that buffer. I haven't seen anything easier and/or simpler than this. To use this function, just do :call Kwbd(1)

"delete the buffer; keep windows
function Kwbd(kwbdStage)
  if(a:kwbdStage == 1)
    let g:kwbdBufNum = bufnr("%")
    let g:kwbdWinNum = winnr()
    windo call Kwbd(2)
    execute "bd! " . g:kwbdBufNum
    execute "normal " . g:kwbdWinNum . ""
  else
    if(bufnr("%") == g:kwbdBufNum)
      let prevbufvar = bufnr("#")
      if(prevbufvar > 0 && buflisted(prevbufvar) && prevbufvar != g:kwbdBufNum)
        b #
      else
        bn
      endif
    endif
  endif
endfunction

Flaw: Works almost fine but if only one buffer is opened in two panes then it will close both panes. Anyway, awkward, two function in one.

See also

  • script#1147
  • script#559 Actually that script is not useful because it doesn't work as expected. Try closing a buffer that's open in several windows and see what happens... the windows close.

Comments

Here is a modification to the above script which binds it to the \bd key sequence. You can save it as kwbd.vim and drop it in you ~/.vim/plugin/ folder:

if exists('loaded_kwbd')
  finish
endif
let loaded_kwbd = 1
if !hasmapto('<Plug>Kwbd')
  map <unique> <Leader>bd <Plug>Kwbd
endif
noremap <unique> <script> <Plug>Kwbd  :call <SID>Kwbd(1)<CR>:<BS>
"delete the buffer; keep windows
function <SID>Kwbd(kwbdStage)
  if(a:kwbdStage == 1)
    let g:kwbdBufNum = bufnr("%")
    let g:kwbdWinNum = winnr()
    windo call <SID>Kwbd(2)
    execute "bd! " . g:kwbdBufNum
    execute "normal " . g:kwbdWinNum . ""
  else
    if(bufnr("%") == g:kwbdBufNum)
      let prevbufvar = bufnr("#")
      if(prevbufvar > 0 && buflisted(prevbufvar) && prevbufvar != g:kwbdBufNum)
        b #
      else
        bn
      endif
    endif
  endif
endfunction