Sometimes I need to move windows into an already existing tab, and I was wondering if there was a command/keyboard shortcut that could do this. I know I can close the window, switch to the tab, and then open it in a new split, but I was wondering if there was a shorter way of accomplishing this. Note that I want to move it to an existing tab, not a new tab like <C-w>T
does.
-
It isn't possible to move windows across tabs.– lcd047Commented May 25, 2015 at 5:26
-
Hmm, that's too bad. I could probably create some mapping or command which could accomplish it though.– EvergreenTreeCommented May 25, 2015 at 12:08
-
I think the best you can do is to save all of the window settings and then recreate it on the existing tab.– tommcdoCommented May 25, 2015 at 14:24
-
not quite sure what you mean @Icd047, it totally is possible, see my answer– DaleCommented May 13, 2021 at 6:49
7 Answers
Though there are commands to move existing windows around in the current tab page (i.e. affect the window layout by rotating, resizing, and moving), there are no commands to move a window to another tab.
As a window is just a viewport into a loaded buffer, you have to:
- Note the buffer number displayed in the current window.
:close!
the window.- Switch to the existing target tab page.
:sbuffer
the buffer number to re-open it.
You'll lose any window-specific options by this. You could save and restore those in a script, too.
-
2I figured that this was the case. I could probably make a small script which does exactly that. Commented May 26, 2015 at 12:17
The two commands defined below move all buffers from the right hand tab to the current tab and then close the right hand tab. Mtabsp
moves them by splitting horizontally and Mtabvsp
splits vertically. You need to move the merged tab before using the commands. Use +tabm
, -tabm
or <n>tabm
.
Setup
Add the below vim script to your vimrc
:
function! Mtab(vert)
if tabpagenr() == tabpagenr("$")
return 0
endif
+tabnext
let s:killed_tab = tabpagenr()
let s:buf_list = tabpagebuflist()
-tabnext
for item in s:buf_list
if a:vert
vsp
else
sp
endif
execute "buf " . item
endfor
execute "tabclose " . s:killed_tab
endfunction
command! Mtabsp call Mtab(0)
command! Mtabvsp call Mtab(1)
Technical details
The function Mtab receives one parameter vert
. It determines if the current tab is split horizontally or vertically.
The desired outcome is achieved with the following steps:
- We move to the next tab temporarily
- Use two variables to reference its tab number and buffers
- Return to the current tab
- We split the tab and open each buffer
- Finally, we close the right hand tab
If our current tab is last, the commands are ignored.
- go to the existing target tab
- split it however you want
:b ab
(tab-complete the rest)
ab
are just random letters, replace with the beginning of your desired file name. :b
tab-completion has performed very well for me, it can usually find the right source buffer based on just one letter and 1-2 Tab
keypresses.
The benefit of this approach over Ingo's is the lack of noting or remembering anything, meaning less cognitive load. File name you already know anyway.
If it's a file buffer (won't work for scratch buffers etc.), you can:
:let @f=@%
ENTER — in the source window;- This replaces contents of regular register f with contents of special register %, which contains current file name.
- Note: I chose register f as in file, but you can use any other register you like.
- You can verify that the register contains correct path with:
:echo @f
- Optionally: Ctrl-WCtrl-Q — to close the old window;
- gt / gT — navigate to the target tab (and window);
- Ctrl-Ws — split the tab any way you like, to open a new window;
:e␣
Ctrl-RfENTER- in the newly opened window, loads a file from the path stored in register f;
- Note: the ␣ character here marks a regular space.
Important note: the final step won't work correctly if the path contains spaces or some special characters! :-( As a workaround, the following, slightly longer command should work for paths with spaces (though it may still fail for some other special characters):
:e␣"
Ctrl-Rf"
ENTER
(that is, quoting the result of Ctrl-R, f )
Inspired by answers from lkraav and ingo-karkat.
-
This is fantastic, I need to set up a mapping for "cut and paste" of Windows into Tabs. Commented May 15, 2018 at 17:13
I came across the same issue and decided to write tiny (2 sloc) helper commands. https://github.com/gitusp/yanked-buffer
With this plugin, you can just close the buffer that you want to move, and then reopen it wherever you want, like "yanking" a buffer.
-
Welcome to Vi and Vim and thank you for the contribution! Commented Jun 4, 2020 at 2:30
-
1Important note: scripts must be independent of user settings to become generally useful. In this particular case you really should use
hide quit
instead of simplyq
.– MattCommented Jun 4, 2020 at 4:48 -
You're right,
set hidden
must not be forced to set to use a plugin. Having said that, I happened to realize thatset hidden
was not required from the beginning, so I rewrote the document. Anyway I really appreciate your advice, thanks @Matt.– uspCommented Jun 4, 2020 at 14:03
I have an alternate solution.
:help sb
Basically, this will allow you to split the current buffer with the buffer you pass in. To see the list of buffers do:
:buffers
Here's a working example of my current vim session:
:buffers
1 %a "~/local/git-projects/personal/templategenerator/src/frontend/Makefile" line 5
3 a "package.json" line 0
4 a "notes.txt" line 0
6 a "reference_resources/tpl-gnr-js/webpack.config.js" line 35
7 a "reference_resources/tpl-gnr-js/package.json" line 0
8 a "Makefile" line 1
11 "~/.viminfo" line 1573
I want to split my current buffer (#1 as indicated by the %) and view buffer 8 in my current window:
:vert sb 8
will do what I want. No need for any crazy vimscript hackery.
-
Welcome to Vi and Vim! This is covered in the existing answers, but given the extra detail I’ve upvoted anyway– D. Ben Knoble ♦Commented May 13, 2021 at 11:58
-
So it is... oops. I was scanning through the page for "sb" and totally missed ":sbuffer", that's embarrassing. I'm inclined to remove this myself. I came to this page trying to find the command and ended up scrolling through .viminfo to find it.– DaleCommented May 13, 2021 at 19:25
Providing a script to move the current window to the tab on the left or right. The idea is similar to existing answers: get the buffer number, open the buffer in the new tab, and close the old window.
Put the script in vimrc
and use CTRL+J or CTRL+K to move the window to left or right. I put comments in the script to describe what it's doing.
function! MoveWindow(d)
let bufn = winbufnr(0)
let tabn = tabpagenr()
let pos = getcurpos()
" Switch to the tab on the left or right that we want to move the window to.
if a:d == 'right'
tabnext
" Change 'leftabove' to 'topleft' if you want the window to be moved to the left most of the tab.
let loc = 'leftabove'
else
tabNext
" Change 'rightbelow' to 'botright' if you want the window to be moved to the right most of the tab.
let loc = 'rightbelow'
endif
let newtabn = tabpagenr()
" Open the buffer in the new tab.
execute 'vertical '. loc . ' sbuffer' . bufn
" Switch back to the old tab.
execute 'tabnext' . tabn
" If the window is the only one in the tab, tab number of tabs to the right will be reduced by 1 after closing the window.
if winnr('$') == 1 && tabpagenr() < newtabn
let newtabn = newtabn - 1
endif
q
" Switch to the new tab.
execute 'tabnext' . newtabn
" Reset the cursor position since the cursor position may be different if the buffer was opened in the new tab before.
call setpos('.', pos)
endfunction
map <C-J> :call MoveWindow('left')<CR>
map <C-K> :call MoveWindow('right')<CR>