Vim: Delete Buffer When Quitting Split Window - vim

I have this very useful function in my .vimrc:
function! MyGitDiff()
!git cat-file blob HEAD:% > temp/compare.tmp
diffthis
belowright vertical new
edit temp/compare.tmp
diffthis
endfunction
What it does is basically opening the file I am currently working on from repository in a vertical split window, then compare with it. This is very handy, as I can easily compare changes to the original file.
However, there is a problem. After finishing the compare, I remove the split window by typing :q. This however doesn't remove the buffer from the buffer list and I can still see the compare.tmp file in the buffer list. This is annoying because whenever I make new compare, I get this message:
Warning: File "temp/compare.tmp" has changed since editing started.
Is there anyway to delete the file from buffers as well as closing the vertical split window?

Perhaps you need the bwipe command?
:bw[ipeout][!] N1 N2 ...
Like |:bdelete|, but really delete the buffer. Everything
related to the buffer is lost. All marks in this buffer
become invalid, option settings are lost, etc. Don't use this
unless you know what you are doing.
One option would be to define the following:
function! DelBuf(filename)
let bname = bufname(filename)
if l:bname != ""
let bidx = buffer_number(l:bname)
exec = "bw " . l:bidx
endif
endfunction
and add a call to DelBuf("comapre.tmp") at the beginning of your function.
In theory it should be possible to bind DelBuf to the `bufhidden event like this:
autocmd! bufhidden "compare.tmp" call DelTmp("compare.tmp")
... but for some reason it didn't work for me.

You need to use autocmd winleave bd (buffer delete). Be warned that if you have the buffer open in more than one window they will all be removed.

I usually define the following things for diff-buffers:
setlocal bt=nofile bh=wipe nobl noswf ro
nnoremap <buffer> q :bw<cr>
The first line is what will make the difference in your case (:h 'bh' -> no need for a single execution autcocommand), the second line is just a shortcut.
BTW: use r! git instead of producing a temporary file. This way, you won't have to clear that file either.

Related

In VIM, how do I write all (:wa), but only for non-hidden buffers?

Sometimes I switch Git branches without closing VIM buffers for files that are unique to the current branch. Those buffers will be hidden, and I'll open new buffers in splits and start making changes for the new branch. Rather than :w individually on those new buffers, I'd like to :wa, but that writes to all buffers, including the old ones that are now hidden.
This is frustrating because it writes those old buffers to new files since they don't exist, dirtying my branch.
How do I :wa, but only for the non-hidden buffers that are actively open in my splits?
Since the buffers you want to write are all displayed in windows you can:
:windo w
or the slightly smarter:
:windo update
See :help :windo and :help :update.
There is no native command to do that, but creating your own
is not very difficult. Buffers have attributes, and you are
looking for the ones considered active, which have the
hidden flag set to false.
You can get a list of buffers with getbufinfo(). This
functions returns an array of dictionaries containing buffer
information. Then it is a matter of iterating over these
entries and if it does not represent a hidden buffer,
perform the write (or :update).
As far as I know there is no native way to perform a command
in a different buffer without switching to it. We are thus
forced to switch to a different buffer to perform a command.
This would mess up your current buffer, but can be solved by
saving it before looping and restoring later.
The following function does that, and the accompanying
custom command :Wa just calls it.
function! WriteActiveBuffers()
" Save current buffer number
let current = bufnr('%')
for buffer in getbufinfo({'buflisted':1})
if !buffer["hidden"]
" Found an active buffer
" Switch to it
execute 'buffer' buffer["bufnr"]
" Write if modified
update
endif
endfor
" Restore current buffer
execute 'buffer' current
endfunction
command! Wa call WriteActiveBuffers()
Hmm. Slightly altering from the literal question and taking the use case into account, I'd just advise you to use:
autocmd BufEnter * setlocal bufhidden=delete
in your .vimrc. Maybe restrict * a bit, and then just use pure :wa as you won't have any hidden buffers anymore. :| That might work.

How do I close a buffer while keeping the window open? [duplicate]

Vim's multilayered views (Windows, Buffers and Tabs) left me a little confused. Let's say I split the display (:sp) and then select a different buffer to display in each window. Now I want to close one of the buffers, yet I don't want the window to close (After the closing it can display the next buffer on the list or an empty buffer, it doesn't matter). How can I do this?
I messed with this a bit and finally came up with:
:bp | sp | bn | bd
Here's the copy/paste version for key mapping:
:bp<bar>sp<bar>bn<bar>bd<CR>
I've tested it a fair bit and it works consistently in various conditions. When used on the last buffer it will leave you with a new blank buffer.
Throw this in your .vimrc:
map <leader>q :bp<bar>sp<bar>bn<bar>bd<CR>
Restart Vim, or just :source ~/.vimrc for changes to take effect. Next time you want to close a buffer just type: \q (if \ is your leader key)
I searched for this today and came up with
:b#|bd#
which changes the current window to the previously open buffer and deletes/hides the buffer you just switched away from.
This requires at least two known buffers.
If another window but the current shows the same buffer this will still destroy splitting. You can change all windows to the previously open buffer with
:windo b#
I added more detail about the former command discussing a mapping for it (and some pitfalls) in an answer to a similar question.
There's a script on the Vim wiki to do this. I don't think there is a builtin that does what you want.
The latest version of vim-bufkill is on github.
nmap <leader>d :bprevious<CR>:bdelete #<CR>
Works as it should until one buffer is open in several windows. Good enough unless you want to use the bigger scripts out there.
Edit: this is what i use right now:
function! BufferDelete()
if &modified
echohl ErrorMsg
echomsg "No write since last change. Not closing buffer."
echohl NONE
else
let s:total_nr_buffers = len(filter(range(1, bufnr('$')), 'buflisted(v:val)'))
if s:total_nr_buffers == 1
bdelete
echo "Buffer deleted. Created new buffer."
else
bprevious
bdelete #
echo "Buffer deleted."
endif
endif
endfunction
I think this is what you're looking for
http://www.vim.org/htmldoc/windows.html#window-moving
Try this:
Look ar your buffer id using
:buffers
you will see list of buffers there like
1 a.cpp
2 b.py
3 c.php
if you want to remove b.py from buffer
:2bw
if you want to remove/close all from buffers
:1,3bw
For those who use NERDTree.
I fix this using this plugin https://github.com/jistr/vim-nerdtree-tabs and now I can close the only buff/file/tab without closing the window.
After having the plugin above installed put the following code on my .vimrc:
let g:nerdtree_tabs_autoclose=0
The description for the variable above is: Close current tab if there is only one window in it and it's NERDTree (default 1)
More info here: https://github.com/jistr/vim-nerdtree-tabs#configuration
A simple version I use personally is
:bp|bd#
It goes to the previous buffer and deletes the other buffer (which is actually the original where we jumped from).
This does what you would expect in 99% of cases without any complicated scripts.
As a keyboard shortcut I use the following
nnoremap <silent> <Leader>c :bp<BAR>bd#<CR>
I don't think there is a one shot way to do this, but you could do :enew or :ls to list your buffers and swap to a different one using :b [number].
Once you've got a different buffer in the window :bd # will delete the previous buffer in the window, and since the current buffer still exists the window won't be closed.
I used to use :
:bp<bar>sp<bar>bn<bar>bd<CR>
But I found certain occasions where it closed my window.
Recently I noticed that I always use this when I am working on a project and need to quickly open my .tmux.conf .zshrc before going back to work.
For this usage, I find better to :
switch back to the buffer I was previously working on with C-6
type :bd# to delete the previous buffer (I have mapped it like this : nnoremap <leader>d :bd#<CR>)
It allows me to control the buffer I'm going back to and feels more natural.
Here is a very readable vimscript function, which handles all cases well,
behave similar to built-in:bd (if only one window, just invoke it!),
issue a warning and do nothing if buffer modifed.
if no other buffer, create one, via :enew.
if alternate buffer exist and in buffer-list, switch to it, else go next, via:bn.
more reasonable behavior for multiple-window layout
not closing any window,
always stay on the original window.
for each window that displays current buffer, do as listed above, then delete old current buffer.
nnoremap <Leader>b :call DeleteCurBufferNotCloseWindow()<CR>
func! DeleteCurBufferNotCloseWindow() abort
if &modified
echohl ErrorMsg
echom "E89: no write since last change"
echohl None
elseif winnr('$') == 1
bd
else " multiple window
let oldbuf = bufnr('%')
let oldwin = winnr()
while 1 " all windows that display oldbuf will remain open
if buflisted(bufnr('#'))
b#
else
bn
let curbuf = bufnr('%')
if curbuf == oldbuf
enew " oldbuf is the only buffer, create one
endif
endif
let win = bufwinnr(oldbuf)
if win == -1
break
else " there are other window that display oldbuf
exec win 'wincmd w'
endif
endwhile
" delete oldbuf and restore window to oldwin
exec oldbuf 'bd'
exec oldwin 'wincmd w'
endif
endfunc
Simply do :new|bd# or Paste this into your vimrc
let mapleader = " "
" CLOSE current Buffer without closing window
nnoremap <silent><leader>d :new<BAR>bd#<CR>
" CLOSE current window
noremap <leader>x <c-w>c
Then hit (space + d) or (space + x)
EDIT: even better with
nnoremap <silent><leader>d :new<BAR>bd#<BAR>bp<CR>
Would
:enew
do what you want? it will edit a new, unnamed buffer in the current window leaving the existing file open in any other windows.
My favorite solution for this is the bufkill.vim plugin (GitHub). It provides alternate versions of the various :b-prefix commands that work the same as their counterparts, but leave the window open. They display whatever the last visible buffer contained, or an empty/new buffer if there was no prior buffer.
From the documentation:
When you want to unload/delete/wipe a buffer, use:
:bun/:bd/:bw to close the window as well (vim command), or
:BUN/:BD/:BW to leave the window(s) intact (this script).
To 'close' a view, use :hid[e]. Works if you have managed to split the viewport or opened multiple files. You can't hide the last buffer on display.
1 Further tip that helped me: use :e ./path/to/file.work to open a file in viewport without splitting the window.
P.S. At two days into vim I still have trouble finding the precise help commands. Hopefully this will help someone else keep working until they really get time to understand vim.
If you're like me and you came here trying to do the opposite, close the window without closing the buffer, you can do that via:
:close
I like this answer most, although I prefer
<CTRL-^>:bd#
because it is faster to type and I don't need a keymapping.
The command <CTRL-^> (same as on English keyboard) switches to the alternate file and :bd# deletes the other buffer.
use ":bd" as a command.

Reading the result of an exe file in :vs in vim

Many of my programs are console like applications, which take a data file, and print out the results, either on screen (more often) or in another file.
Since these days I'm doing some analysis which requires a lot of little tempting with one input data file, just to get a few numbers, I usually go: edit data file, start program, get result, edit data file, start program, get result ...
So I was wondering, is there a way in vim, while having open the input file, to define a function which would open a vertical split and load the result of program (pro12.exe) in it ?
How would one go about that ?
The easiest thing to do is probably write a Makefile to run the program (and redirect output to a file), and :set the autoread option on in Vim. Then, you can create a macro r by typing qr:make<Enter>q and run the macro with #r. The macro will cause Vim to invoke make, which will run the program to update the data file. The autoread option will make sure that vim refreshes the updated file without prompting you first.
Assuming pro12.exe is in your %PATH%, this will invoke your helper app and vert-split-open a static output file name. Map to whatever key you like. QND: won't work when relative paths (bufnames) change via cd. YMMV
fun! RunPro12()
bufdo if bufname(bufnr($)) == '/path/to/pro12.exe.output' | close | endif
silent exe '!pro12.exe'
vs /path/to/pro12.exe.output
endfun
map <f3> :call RunPro12()<cr>
I don't like :bufdo solutions as this command always messes up the current windows organisation.
In normal time, I'd use lh#buffer#jump() that jumps to the window where the named buffer is opened if it is already opened, or opens the window otherwise.
function! s:Run0()
call lh#buffer#jump('/path/to/pro12.exe.output', 'vsp')
silent exe '!ls'
endfunction
Given the requested "to define a function which would open a vertical split", we could simply use:
function! s:Run1()
let bn = bufnr('/path/to/pro12.exe.output')
if bn >= 0
exe 'bw '.bn
endif
silent exe '!pro12'
vsp /path/to/pro12.exe.output
endfunction
nnoremap ยต :call <sid>Run1()<cr>
Last thing. If as I suspect pro12 writes to stdout (the standard output, i.e. ~ the console), the last two lines of the function shall become:
vsp /path/to/pro12.exe.output
r!pro12
The "/path/to/pro12.exe.output" may be replaced with anything unique. Typically, I'd use a scratch buffer with a unique name having "pro12 result" in it.
PS: if in the function (this is important) you add a
nnoremap <buffer> q :bw<cr>
you'll be able to simply quit the output window simply by hitting q, from within the output window.

How can I execute a command when a buffer is closed in vim?

Is there something like a close-window-hook in vim/vimscript, so that I can call a function every time a window is closed?
I want to use it for the following scenario:
I use an extra scratch window to display information about the file, and when I close the file I want the scratch window to be closed automatically so that vim exits.
If you have any ideas how to achieve that without a hook that will be just as fine.
edit:
I know about :qa[ll], but I am lazy and only want to type :q or ZZ.
edit2.71828183:
I accepted the autocommand answer as it was closest to the original question, but found another solution in using a preview window instead of a split window. A preview window is automatically closed when the last "normal" window is closed..
autocommands are amazing! I'm pretty sure that in this case, BufLeave will do the job, but you might want BufWinLeave? Have a look at :help autocmd-events for a full list of events.
The other bit you'll care about is: you can have buffer-local autocommands! (:help autocmd-buflocal)
You can define one for the current buffer using au BufLeave <buffer> .... My best guess is that you could run this in whatever command creates the scratch window. You should be able to cache the scratch window's buffer number in a global variable when you open the scratch window, then your autocommand could just delete that buffer (:help :bdelete).
au BufLeave <buffer> bdelete g:scratch_buffer
call CreateScratchWindow()
function CreateScratchWindow() {
...
let g:scratch_buffer = bufnr("")
}
There's also a function winbufnr, for getting buffer numbers by window. You can use either one - just make sure that the scratch window/buffer is current when you use it! (The "" means current window/buffer).
Add a mapping.
:nnoremap :q :qa
then, if you only want to use :q one day, you can just
:qa<backspace>
and normally, you can just
:q<enter>
and get
:qa<enter>
for free.
Ok, not really a new question, but I found it looking for, what I now know, BufWinLeave. However, the scenario the author is describing is something I have in my vimrc with NerdTree, e.g. when the last buffer is closed, I want vim to close the NerdTree "buffer" and then quit.
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif
Replace the NERDTree specific stuff with how the scratch buffer is identified.
You can use
:quitall
or
:qall
to close all windows at once.
add a ! if you want to ignore unsaved changes.
:qall!

How can I close a buffer without closing the window?

Vim's multilayered views (Windows, Buffers and Tabs) left me a little confused. Let's say I split the display (:sp) and then select a different buffer to display in each window. Now I want to close one of the buffers, yet I don't want the window to close (After the closing it can display the next buffer on the list or an empty buffer, it doesn't matter). How can I do this?
I messed with this a bit and finally came up with:
:bp | sp | bn | bd
Here's the copy/paste version for key mapping:
:bp<bar>sp<bar>bn<bar>bd<CR>
I've tested it a fair bit and it works consistently in various conditions. When used on the last buffer it will leave you with a new blank buffer.
Throw this in your .vimrc:
map <leader>q :bp<bar>sp<bar>bn<bar>bd<CR>
Restart Vim, or just :source ~/.vimrc for changes to take effect. Next time you want to close a buffer just type: \q (if \ is your leader key)
I searched for this today and came up with
:b#|bd#
which changes the current window to the previously open buffer and deletes/hides the buffer you just switched away from.
This requires at least two known buffers.
If another window but the current shows the same buffer this will still destroy splitting. You can change all windows to the previously open buffer with
:windo b#
I added more detail about the former command discussing a mapping for it (and some pitfalls) in an answer to a similar question.
There's a script on the Vim wiki to do this. I don't think there is a builtin that does what you want.
The latest version of vim-bufkill is on github.
nmap <leader>d :bprevious<CR>:bdelete #<CR>
Works as it should until one buffer is open in several windows. Good enough unless you want to use the bigger scripts out there.
Edit: this is what i use right now:
function! BufferDelete()
if &modified
echohl ErrorMsg
echomsg "No write since last change. Not closing buffer."
echohl NONE
else
let s:total_nr_buffers = len(filter(range(1, bufnr('$')), 'buflisted(v:val)'))
if s:total_nr_buffers == 1
bdelete
echo "Buffer deleted. Created new buffer."
else
bprevious
bdelete #
echo "Buffer deleted."
endif
endif
endfunction
I think this is what you're looking for
http://www.vim.org/htmldoc/windows.html#window-moving
Try this:
Look ar your buffer id using
:buffers
you will see list of buffers there like
1 a.cpp
2 b.py
3 c.php
if you want to remove b.py from buffer
:2bw
if you want to remove/close all from buffers
:1,3bw
For those who use NERDTree.
I fix this using this plugin https://github.com/jistr/vim-nerdtree-tabs and now I can close the only buff/file/tab without closing the window.
After having the plugin above installed put the following code on my .vimrc:
let g:nerdtree_tabs_autoclose=0
The description for the variable above is: Close current tab if there is only one window in it and it's NERDTree (default 1)
More info here: https://github.com/jistr/vim-nerdtree-tabs#configuration
A simple version I use personally is
:bp|bd#
It goes to the previous buffer and deletes the other buffer (which is actually the original where we jumped from).
This does what you would expect in 99% of cases without any complicated scripts.
As a keyboard shortcut I use the following
nnoremap <silent> <Leader>c :bp<BAR>bd#<CR>
I don't think there is a one shot way to do this, but you could do :enew or :ls to list your buffers and swap to a different one using :b [number].
Once you've got a different buffer in the window :bd # will delete the previous buffer in the window, and since the current buffer still exists the window won't be closed.
I used to use :
:bp<bar>sp<bar>bn<bar>bd<CR>
But I found certain occasions where it closed my window.
Recently I noticed that I always use this when I am working on a project and need to quickly open my .tmux.conf .zshrc before going back to work.
For this usage, I find better to :
switch back to the buffer I was previously working on with C-6
type :bd# to delete the previous buffer (I have mapped it like this : nnoremap <leader>d :bd#<CR>)
It allows me to control the buffer I'm going back to and feels more natural.
Here is a very readable vimscript function, which handles all cases well,
behave similar to built-in:bd (if only one window, just invoke it!),
issue a warning and do nothing if buffer modifed.
if no other buffer, create one, via :enew.
if alternate buffer exist and in buffer-list, switch to it, else go next, via:bn.
more reasonable behavior for multiple-window layout
not closing any window,
always stay on the original window.
for each window that displays current buffer, do as listed above, then delete old current buffer.
nnoremap <Leader>b :call DeleteCurBufferNotCloseWindow()<CR>
func! DeleteCurBufferNotCloseWindow() abort
if &modified
echohl ErrorMsg
echom "E89: no write since last change"
echohl None
elseif winnr('$') == 1
bd
else " multiple window
let oldbuf = bufnr('%')
let oldwin = winnr()
while 1 " all windows that display oldbuf will remain open
if buflisted(bufnr('#'))
b#
else
bn
let curbuf = bufnr('%')
if curbuf == oldbuf
enew " oldbuf is the only buffer, create one
endif
endif
let win = bufwinnr(oldbuf)
if win == -1
break
else " there are other window that display oldbuf
exec win 'wincmd w'
endif
endwhile
" delete oldbuf and restore window to oldwin
exec oldbuf 'bd'
exec oldwin 'wincmd w'
endif
endfunc
Simply do :new|bd# or Paste this into your vimrc
let mapleader = " "
" CLOSE current Buffer without closing window
nnoremap <silent><leader>d :new<BAR>bd#<CR>
" CLOSE current window
noremap <leader>x <c-w>c
Then hit (space + d) or (space + x)
EDIT: even better with
nnoremap <silent><leader>d :new<BAR>bd#<BAR>bp<CR>
Would
:enew
do what you want? it will edit a new, unnamed buffer in the current window leaving the existing file open in any other windows.
My favorite solution for this is the bufkill.vim plugin (GitHub). It provides alternate versions of the various :b-prefix commands that work the same as their counterparts, but leave the window open. They display whatever the last visible buffer contained, or an empty/new buffer if there was no prior buffer.
From the documentation:
When you want to unload/delete/wipe a buffer, use:
:bun/:bd/:bw to close the window as well (vim command), or
:BUN/:BD/:BW to leave the window(s) intact (this script).
To 'close' a view, use :hid[e]. Works if you have managed to split the viewport or opened multiple files. You can't hide the last buffer on display.
1 Further tip that helped me: use :e ./path/to/file.work to open a file in viewport without splitting the window.
P.S. At two days into vim I still have trouble finding the precise help commands. Hopefully this will help someone else keep working until they really get time to understand vim.
If you're like me and you came here trying to do the opposite, close the window without closing the buffer, you can do that via:
:close
I like this answer most, although I prefer
<CTRL-^>:bd#
because it is faster to type and I don't need a keymapping.
The command <CTRL-^> (same as on English keyboard) switches to the alternate file and :bd# deletes the other buffer.
use ":bd" as a command.

Resources