21

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.

4
  • It isn't possible to move windows across tabs.
    – lcd047
    May 25 '15 at 5:26
  • Hmm, that's too bad. I could probably create some mapping or command which could accomplish it though. May 25 '15 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.
    – tommcdo
    May 25 '15 at 14:24
  • not quite sure what you mean @Icd047, it totally is possible, see my answer
    – Dale
    May 13 at 6:49
14

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:

  1. Note the buffer number displayed in the current window.
  2. :close! the window.
  3. Switch to the existing target tab page.
  4. :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.

1
  • 2
    I figured that this was the case. I could probably make a small script which does exactly that. May 26 '15 at 12:17
5

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:

  1. We move to the next tab temporarily
  2. Use two variables to reference its tab number and buffers
  3. Return to the current tab
  4. We split the tab and open each buffer
  5. Finally, we close the right hand tab

If our current tab is last, the commands are ignored.

3
  1. go to the existing target tab
  2. split it however you want
  3. :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.

2

If it's a file buffer (won't work for scratch buffers etc.), you can:

  1. :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
  2. Optionally: Ctrl-WCtrl-Q — to close the old window;
  3. gt / gT — navigate to the target tab (and window);
  4. Ctrl-Ws — split the tab any way you like, to open a new window;
  5. :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.

1
  • This is fantastic, I need to set up a mapping for "cut and paste" of Windows into Tabs.
    – dragon788
    May 15 '18 at 17:13
2

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.

3
  • Welcome to Vi and Vim and thank you for the contribution!
    – filbranden
    Jun 4 '20 at 2:30
  • 1
    Important note: scripts must be independent of user settings to become generally useful. In this particular case you really should use hide quit instead of simply q.
    – Matt
    Jun 4 '20 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 that set hidden was not required from the beginning, so I rewrote the document. Anyway I really appreciate your advice, thanks @Matt.
    – usp
    Jun 4 '20 at 14:03
1

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.

2
  • Welcome to Vi and Vim! This is covered in the existing answers, but given the extra detail I’ve upvoted anyway
    – D. Ben Knoble
    May 13 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.
    – Dale
    May 13 at 19:25
0

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>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.