Vim close buffer but not split window - vim

If I have 2 buffers split horizontally/vertically and want to close one of them, but i don't want to close a window. I want to keep places of split windows are the same as before closing buffer.
If I press :bd , the window in which was closed buffer also became closed.

Like #RusAlex I don't like plug-ins. I also like to know what code I enter actually does.
nmap ,d :b#<bar>bd#<CR>
In short this adds a key mapping to vim's normal mode waiting for key sequence ,d. When executed this switches to a previously open buffer and attempts to delete the buffer you switched away from.
Deleting an off-screen buffer keeps the screen split as it is.
The command consists of three space-separated parts:
nmap - add/change key mapping for mode normal
,d - key sequence to react to; first , (comma), then d
:b#<bar>bd#<CR> - key sequence to execute
The command to be executed consists of five parts:
: - switch vim to mode command-line
b# - switch window to previously open buffer
<bar> - expect a follow-up command; represents | (pipe character); used for chaining commands
bd# - delete previously open buffer, i.e. the buffer just switched away from
<CR> - execute command(s); represents carriage return, basically the keys Return or Enter
The command is in the format it is used in a configuration file like ~/.vimrc. If you want to add the mapping from within vim you prepend : (colon) - the mapping then will be lost when exiting vim:
:nmap ,d :b#<bar>bd#<CR>
When you open vim it is usually in normal mode as opposed to modes insert (indicated on the bottom of the screen by -- INSERT -- after pressing i), visual and so on. The n in nmap specifies the key mapping to be added to normal mode only. Find more on mappings here
Important notes:
b# will switch to the current buffer if it is the only known buffer.
b# may switch to a hidden/closed buffer, e.g. the one you just closed by pressing ,d.
bd# will close the current buffer if it is the only known buffer unsplitting the screen leaving you with an empty buffer.
bd# will fail if the buffer switched away from is a hidden/closed buffer.
bd# will still unsplit if after switching another window shows the buffer to close.
Additional notes:
:windo b# will switch all windows to the previously open buffer. Not sure how to combine with bd.
<CR> can be left out in which case you have to manually press Return or Enter to execute.
:nmap , displays all normal mode mappings starting with ,.
:ls lists open buffers.

It's impossible to have an empty window in vim, but you can just create a new empty file in the current window using :enew.

I'll have to check on my work computer, but I think the script I'm using for this is BufClose.

You want to delete the buffer but keep the split? You need a new buffer then - :new will do that for you, creating a buffer for a new/empty file, but you'll still need to kill the old buffer/window. In vim a window is a viewport on a buffer, so if you want an empty window you need an empty buffer.

Here's a variation on the answer provided #zenbro that keeps all your (split) windows and tabs open even if the buffer you close is the last one.
The function switches all windows pointing to the current buffer (that you are closing) to the next buffer (or a new buffer if the current buffer is the last one).
function! CloseBuffer()
let curBuf = bufnr('%')
let curTab = tabpagenr()
exe 'bnext'
" If in last buffer, create empty buffer
if curBuf == bufnr('%')
exe 'enew'
endif
" Loop through tabs
for i in range(tabpagenr('$'))
" Go to tab (is there a way with inactive tabs?)
exe 'tabnext ' . (i + 1)
" Store active window nr to restore later
let curWin = winnr()
" Loop through windows pointing to buffer
let winnr = bufwinnr(curBuf)
while (winnr >= 0)
" Go to window and switch to next buffer
exe winnr . 'wincmd w | bnext'
" Restore active window
exe curWin . 'wincmd w'
let winnr = bufwinnr(curBuf)
endwhile
endfor
" Close buffer, restore active tab
exe 'bd' . curBuf
exe 'tabnext ' . curTab
endfunction
To map it:
map <silent> <F4> :call CloseBuffer()<cr>

#RusAlex version + activate current buffer in the end need if delete buffer twice.
nmap ,d :b#<bar>bd#<bar>b<CR>

Here is some workaround:
function! CloseSplitOrDeleteBuffer()
let curNr = winnr()
let curBuf = bufnr('%')
wincmd w " try to move on next split
if winnr() == curNr " there is no split"
exe 'bdelete'
elseif curBuf != bufnr('%') " there is split with another buffer
wincmd W " move back"
exe 'bdelete'
else " there is split with same buffer"
wincmd W
wincmd c
endif
endfunction
nnoremap <silent> Q :call CloseSplitOrDeleteBuffer()<CR>

Related

Copy lines in vim to execute in above window opened by ter command?

I am using the latest vim version.
vim --version | head -1
VIM - Vi IMproved 8.1 (2018 May 18, compiled Aug 12 2019 17:28:55)
Edit a python file with vim.
vim embed.py
x = 3
print(x)
y =4
print(x+y)
Now open a new window with ter command in vim.
The normal way to execute embed.py which is in edit status.
:! python3 %
New window open and execute embed.py.
I have a new idea,how can copy all the lines in embed.py into the above window opened by ter command in vim?Show the expected way as below.
ggyG can't work.
Move cursor in vim window,and press ggyG.
Move cursor in the python3 window.
ctrl + v can't work, <C-\><C-N> can't work too.
It is time to try with gui way,paste nothing also.
Do as Tarun Lalwani say:
step1: copy lines into system clipboard
:%y+
or with other command.
step2: move cursor into the upper window which run python3.
step3: ctrl+v+shift
How can bind all steps with a hot key?
Status 1:
Write the following in my .vimrc.
function! CopyPasteBuffer()
normal gg"+yG
wincmd p
call feedkeys('^W"+')
endfunction
nnoremap <leader>p :call CopyPasteBuffer()<CR>
\p will put ^W"+ on python3's interactive window.
Status 2:
Write the following in my .vimrc.
function! CopyPasteBuffer()
normal gg"+yG
wincmd p
endfunction
nnoremap <leader>p :call CopyPasteBuffer()<CR>
\p will move cursor into upper window,now pressing ctrl+v+sfift can take effect.
Almost done!It remains a issue here.
The last step (step 3) which paste all program's lines into python interactive window haven't been automated into the vimscript,rkta's CopyPasteBuffer() only bind two steps with hot key \p successfully.
Please have a try in bash ,instead of zsh.
Almost same result both for normal gg"+yG and normal gg"*yG,ctrl+v+shift or ctrl+w+ctrl+v or ctrl+v can't paste content in register * if it is normal gg"*yG in CopyPasteBuffer()(verified in my bash).
There is a built-in function named term_sendkeys to send keys to a terminal buffer.
Here is a oneliner to send all lines in the current buffer to the first terminal window using term_sendkeys:
:cal term_sendkeys(term_list()[0], join(map(getbufline(bufnr('.'), 1, '$'), 'v:val . "\n"'), ''))
You can simply define a map to execute the oneliner in your .vimrc like this:
nnoremap <leader>p :<c-u>cal term_sendkeys(term_list()[0], join(map(getbufline(bufnr('.'), 1, '$'), 'v:val . "\n"'), ''))<Cr>
However oneliners are bit hard to understand at glance, so it is better to define this as a function and define a map to call it:
function! s:SendLinesToTerm()
let term_buf = term_list()[0]
let lines = getbufline(bufnr('.'), 1, '$')
let str = join(map(lines, 'v:val . "\n"'), '')
cal term_sendkeys(term_buf, str)
endfunction
nnoremap <leader>p :call <SID>SendLinesToTerm()<Cr>
To copy the current buffer, switch to the terminal running in the only split and paste the buffer contents use this function:
function! CopyPasteBuffer()
normal ggyG
wincmd p
call feedkeys("\<C-W>\"*")
endfunction
(As we are in terminal mode, we need to use Ctrl W " to paste, see :h terminal-typing for other special keys.)
This will paste everything and leave you in the terminal buffer - use Ctrl WW to switch back.
To bind it to a key use
nnoremap <leader>p :call CopyPasteBuffer()<CR>
If you didn't rebind your leader key you can execute the function with \p.
To use the function with the * register just change the function to
function! CopyPasteBuffer()
normal gg"*yG
wincmd p
call feedkeys("\<C-W>\"*")
endfunction
You have to understand that when you yank the lines in vim it is not basically going to system's clipboard. The terminal shown in the upper window can only interact with the system's clipboard
You can see the below thread on how to use system clipboard
https://vi.stackexchange.com/questions/84/how-can-i-copy-text-to-the-system-clipboard-from-vim
I use mac which has pbcopy to copy to the clipboard. So I can execute something like :silent !pbcopy < %. This will copy the file to clipboard. And then a normal CTRL+V or CTRL+SHIFT+V or CMD+V would work based on your OS
For unix you would use something like xclip
How to use the terminal window
According to this SO answer and #Amadan’s comment, in a terminal window, the command ctrl-wN (capital N) allows to exit the “insertion mode” (so that you can copy things from the terminal window); also, ctrl-w" followed by the appropriate register name (* for X primary, + for X clipboard) allows to paste the contents of the said register (that’s what you are interested in). You may also paste the primary register with ctrl-insert, and the clipboard register with the window menu you show on one of your screenshots.
How to use registers
On registers: long story short, Vim stores yanked text in various named registers. If you are running Vim from an X graphical environment, the Vim register * is connected to the X clipboard “primary” (usually the last text selected with the mouse from within a graphical application), and the Vim register + is connected to the X clipboard “clipboard” (usually the last text copied with the shortcut ctrl-v from within a graphical application). By default, Vim puts text yanked with the command yy in the register *, but you can put it in + as well, and you can change the default to + (set clipboard=unnamedplus).
Use whichever register you prefer. Only make sure you use the same when copying and pasting (i.e., by default yy will typically copy to X primary while the window menu will paste from the X clipboard).
Official source: Read :help terminal to learn how to use Vim’s terminal windows, and :help registers for Vim’s registers.
(Credible source: Google PageRank.)
However in your case, aren’t you looking rather at the Python keyword for importing a file into the REPL? Although I don’t know Python very well, it should probably looks like import embed or something.

Vim function to switch between splits

I am trying to find a function which will switch back to the original window in a split view for vim. I know about shortcut and I also know about the function :call cursor. But is there a function which can let me switch back to the previous split window so I could stick it in my vim function?
In a vimscript you can use:
" Save the window where you are currently
let l:currentWindow=winnr()
" Do stuff where you change of window
" Go back to the original window
exe l:currentWindow . "wincmd w"
For more information the doc is always an excellent reading:
:h wincmd
:h winnr()
Edit Another way to do it is to use wincmd p:
wincmd is the vimscript equivalent to Ctrlw in normal mode.
In normal mode when you change of window you can use Ctrlw + p to come back to the previous window. So in vimscript you simply use:
wincmd p
To go back to the previous window.
Of course if the rest of your function use more than 2 splits you will not go back to your initial window but if you have only two splits it can be lighter than using a variable to keep the number of your window.

Merge changes using vimdiff

In my case, I have two files file1 and file2. Using vimdiff, I want to merge the changes as follows:
In first difference, place line from file1 above line from file2. It means difference such as Listing 2 in file2 and List 2 should be List 2 followed by Listing 2 in the merged file.
Reverse case in another change.
Snapshot is shown below.
How can we achieve this using vimdiff?
You can use the following basic commands to merge:
do - Get changes from other window into the current window.
dp - Put the changes from current window into the other window.
]c - Jump to the next change.
[c - Jump to the previous change.
zo - Open folded lines.
zc - Close folded lines.
zr - Unfold both files completely.
zm - Fold both files completely.
Ctrlww - change window.
:only | wq - quit other windows, write and quit.
Quirks to watch for
Both do and dp work if you are on a block of change (or just one line under a single line of change) in Normal mode, but not in Visual mode.
The undo command will only work in the buffer that was changed, so if you use dp and change your mind, you need to switch to the other buffer to undo.
:diffupdate will re-scan the files for changes (Vim can get confused, and show bogus stuff).
Visual mode and finer grained control
When selecting lines of text in Visual mode, you must use the normal commands:
:'<,'>diffget and
:'<,'>diffput.
For example:
Enter Visual mode and mark some text/lines.
Then type :diffput to push the selected lines to the other file or :diffget to get the selected lines from the other file.
To belabor the point: This means that if there is a block of changes consisting of multiple lines, then selecting a subset of lines and issueing :diffput will only apply those changes in the other buffer.
(:diffget and :diffput also accept ranges, see :h copy-diffs for more.)
Compare two buffers inside Vim
If you load up two files in splits (:vs or :sp), you can do :diffthis on each window and achieve a diff of files that were already loaded in buffers.
:diffoff can be used to turn off the diff mode.
This Vimcasts post and video show this in practice.
How to apply all changes between buffers
Make sure that all participating buffers are in diff mode (see :h start-vimdiff)
a. Get changes from a buffer to the current one:
:%diffget <buffer-number>
b. Put all changes from current buffer into another:
:%diffput <buffer-number>
(:% is a range to select the entire file; see :h :%. :ls will show currently opened buffers.)
You can switch back and forth between the two windows with Ctrlww. You can copy from one window do a Ctrlww, and then paste into the other. As you resolve differences, the highlights will change, and disappear.
Take a look at this video.
You can just switch between the windows and copy and paste to resolve the differences, as #David W. suggests in his answer, but Vim also has dedicated :diffput and :diffget commands to simplify this. With these (or the corresponding normal mode do and dp commands), you don't have to switch between windows, and the range defaults to the current change.
If you need to add instead of overwrite with the other buffer's differences (which is a rather unusual case in a classic two-way diff), you still have to yank the original lines and put them after the :diffget.
After you're done in one place, you can use the ]c, [c commands to jump to the next difference.
I am using the following mappings to deal with three-way merges (when conflictstyle=diff3)
nnoremap g1 :<C-U>call MergeKeepLeft()<CR>
nnoremap g2 :<C-U>call MergeKeepBoth()<CR>
nnoremap g3 :<C-U>call MergeKeepRight()<CR>
function! MergeKeepLeft()
let lastsearch = #/
let #/ = '<<<<<<<'
execute "normal! ?\<cr>dd"
let #/ = '|||||||'
execute "normal! /\<cr>V"
let #/ = '>>>>>>>'
execute "normal! /\<cr>d"
let #/ = lastsearch
endfunction
function! MergeKeepBoth()
let lastsearch = #/
let #/ = '<<<<<<<'
execute "normal! ?\<cr>dd"
let #/ = '|||||||'
execute "normal! /\<cr>V"
let #/ = '======='
execute "normal! /\<cr>d"
let #/ = '>>>>>>>'
execute "normal! /\<cr>dd"
let #/ = lastsearch
endfunction
function! MergeKeepRight()
let lastsearch = #/
let #/ = '<<<<<<<'
execute "normal! ?\<cr>V"
let #/ = '======='
execute "normal! /\<cr>d"
let #/ = '>>>>>>>'
execute "normal! /\<cr>dd"
let #/ = lastsearch
endfunction

Making two windows with separate files(?) in gvim

I'm a beginner vi user. I don't know the terminology, but I want to split my gvim terminal(screen?) into 2 windows which each have 5 different files(buffers?). I can open the first 5 files in one window, then split to a second window, but I don't know how to open 5 more different files in the second window. I haven't been able to find this information. Normally I switch between files with :n and :prev.
To say it again: I want files 1-5 on a left window and files 6-10 on a right window. Is this possible?
You can indeed have window-local argument lists:
:arglocal
:args file1 file2 file3 file4 file5
:vsplit
:arglocal
:args file6 file7 file8 file9 file10
This way, you can have one argument list (with files 1-5) for the left window, and another (with files 6-10) on a split right window. Commands like :next and :first in the windows are then independent of each other.
Buffers are global. It means that you can't have, say two vertical windows, housing two exclusive sets of buffers. The same applies to tabs, of course.
So, just use two instances: one on the left with files 1-5 and the other on the left with files 6-10.
Because the two instances are separated, you can safely use :n et :prev without "overflowing".
Tabs are viewports for windows, windows are viewports for buffers. You can view any buffer in any window. I would not call it impossible to create some workaround though: e.g. you can create commands :NEXT and :PREV via :command and make them iterate only over buffers that were opened in this window via :EDIT: like in the code below. But I would highly suggest use some plugin that aids in buffer switching like Command-T (I have nnoremap ,b :CommandTBuffer<CR> for buffer switching) and forget about highly inefficient :next/:previous commands.
function s:Edit(args)
let w:winbuflist=get(w:, 'winbuflist', [bufnr('%')])
execute 'edit' a:args
let buf=bufnr('%')
if index(w:winbuflist, buf) == -1
call add(w:winbuflist, bufnr('%'))
endif
endfunction
function s:Switch(direction)
let buf=bufnr('%')
let w:winbuflist=get(w:, 'winbuflist', [buf])
let idx=index(w:winbuflist, buf)
if idx==-1 || w:winbuflist ==# [buf]
if idx == -1
echohl ErrorMsg
echomsg 'Current buffer was not opened using :E or was opened in another window'
echohl None
endif
execute a:direction
return
elseif a:direction is# 'next'
let idx += 1
if idx == len(w:winbuflist)
let idx=0
endif
elseif a:direction is# 'previous'
let idx -= 1
if idx == -1
let idx=len(w:winbuflist)-1
endif
endif
execute 'buffer' w:winbuflist[idx]
endfunction
function s:RemoveBuf(buf)
for tab in range(1, tabpagenr('$'))
for win in range(1, tabpagewinnr(tab, '$'))
call filter(getwinvar(win, 'winbuflist', []), 'v:val isnot '.a:buf)
endfor
endfor
endfunction
augroup BufWinList
autocmd! BufWipeout * :call s:RemoveBuf(+expand('<abuf>'))
augroup END
" \/\/\/\/\/\/\/ Warning: this is not a completion option. It also
" \/\/\/\/\/\/\/ makes command do the expansion of its arguments.
command -complete=file -nargs=? -bar EDIT :call s:Edit(<q-args>)
command -nargs=0 -bar NEXT :call s:Switch('next')
command -nargs=0 -bar PREV :call s:Switch('previous')
It looks that you just need to perform several splits.
Split to 2 vertical windows: Ctrl-w v
In each window: Ctrl-w s (repeat it 4 times, to get 5 buffers)
You can move between windows with Ctrl-w j / Ctrl-w k
The :split command takes a file name, and opens that in a new, horizontally split window. (But you can also first just :split / <C-W>s / <C-W>v, and then :edit / :next another file.) Prepend :vertical (or shorter :vsplit) for vertical splitting. With this, you can create your desired layout.
To focus a different window, there are many mappings that start with Ctrl + W, e.g. <C-w>j to go to the window below. See :help CTRL-W for the full list.

Vim: Close All Buffers But This One

How can I close all buffers in Vim except the one I am currently editing?
I was able to do this pretty easily like this:
:%bd|e#
Try this
bufdo bd
bufdo runs command for all buffers
http://vim.wikia.com/wiki/Run_a_command_in_multiple_buffers
You could use this script from vim.org:
http://www.vim.org/scripts/script.php?script_id=1071
Just put it to your .vim/plugin directory and then use :BufOnly command to close all buffers but the active one. You could also map it elsewhere you like in your .vimrc.
Source on Github (via vim-scripts mirror): https://github.com/vim-scripts/BufOnly.vim/blob/master/plugin/BufOnly.vim
If you don´t care the current one, is more simple to do something like (no script needing):
1,100bd
I do this
:w | %bd | e#
My favorite if I just want my current buffer open and close all others.
How it works: first write current buffer's changes, then close all open buffers, then reopen the buffer I was currently on. In Vim, the | chains the execution of commands together. If your buffer is up to date the above can be shortened to :%bd | e#
Building on juananruiz's answer.
Make a small change in the buffer you want to keep, then
:1,1000bd
The command bd (buffer delete) will not delete any buffers with unsaved changes. This way you can keep the current (changed) file in the buffer list.
Edit: Please notice that this will also delete your NERDTreeBuffer. You can get it back with :NERDTree
Note: As mentioned in the comments, this closes windows and not buffers.
By using
:on[ly][!]
and
:h only
I put this in my .vimrc file
nnoremap <leader>ca :w <bar> %bd <bar> e# <bar> bd# <CR>
then your leader + ca (close all) close all the buffers except the current one.
What it does is
:w - save current buffer
%bd - close all the buffers
e# - open last edited file
bd# - close the unnamed buffer
Here's what I do. So I like to keep my cursor position after removing all buffers and most of the solutions above just ignores this fact. I also think remapping the command is better than typing it so Here I use <leader>bd to remove all buffers and jump back to my original cursor position.
noremap <leader>bd :%bd\|e#\|bd#<cr>\|'"
%bd = delete all buffers.
e# = open the last buffer for editing (Which Is the buffer I'm working on).
bd# to delete the [No Name] buffer that gets created when you use %bd.
The pipe in between just does one command after another. You've gotta escape it though using \|
'" = keep my cursor position.
Closing all open buffers:
silent! execute "1,".bufnr("$")."bd"
Closing all open buffers except for the current one:
function! CloseAllBuffersButCurrent()
let curr = bufnr("%")
let last = bufnr("$")
if curr > 1 | silent! execute "1,".(curr-1)."bd" | endif
if curr < last | silent! execute (curr+1).",".last."bd" | endif
endfunction
Add this function to .vimrc and call it using :call CloseAllBuffersButCurrent().
Convenience map:
nmap <Leader>\c :call CloseAllBuffersButCurrent()<CR>
There's a plugin that does exactly this and a bit more!
Check out close-buffers.vim
so this is an old question but it helped me get some ideas for my project. in order to close all buffers but the one you are currently using, use;
map <leader>o :execute "%bd\|e#"<CR>
The answer with highest votes will reopen the buffer, it will lose the current line we are working on.
Close then reopen will induce a flush on screen
function! CloseOtherBuffer()
let l:bufnr = bufnr()
execute "only"
for buffer in getbufinfo()
if !buffer.listed
continue
endif
if buffer.bufnr == l:bufnr
continue
else
if buffer.changed
echo buffer.name . " has changed, save first"
continue
endif
let l:cmd = "bdelete " . buffer.bufnr
execute l:cmd
endif
endfor
endfunction
let mapleader = ','
nnoremap <leader>o :call CloseOtherBuffer()<CR>
Previous code will take effect when you are on the target buffer and press , + o. It will close all other buffers except current one.
It iterates all the buffers and close all the buffer number which is not equal to current buffer number.
:%bd then Ctrl-o
:%bd to delete all buffers
Ctrl-o to jump back to the last buffer, which would be the "This One" you were referring to.
I like 1,100bd (suggested by juananruiz) which seems to work for me.
I added a quit! to my mapping to give me
nnoremap <leader>bd :1,100bd<CR>
nnoremap <leader>bdq :1,100bd<CR>:q!<CR>
This kills all the buffers and shuts down Vim, which is what I was looking for mostly.
How about just:
ctrl-w o
(thanks to https://thoughtbot.com/blog/vim-splits-move-faster-and-more-naturally)
I combined Alejandro's comment with badteeth's comment:
command! Bonly silent execute "%bd|norm <C-O>"
The norm <C-O> jumps to the last position in the jump list, which means where the cursor was before the %bd.
I used silent instead of silent!. That way, if any open buffers are modified, Vim prints an error message so I know what happened. The modified buffers stay open in my tests.
Unrelated: this is my 500th answer!
nnoremap <leader>x :execute '%bdelete\|edit #\|normal `"'\|bdelete#<CR>
Close all buffers (side-effect creates new empty buffer)
Open last buffer
Jump to last edit position in buffer
Delete empty buffer

Resources