10

I frequently want to temporarily maximize a buffer in a tab page and then go back to the old buffer configuration that I was looking at. My current workflow for doing something like this is either to close all the other buffers using <c-w>o or to move the buffer I am currently looking at to its own tab <c-w>T. The problem with the second approach is that it removes the buffer from the original tab page.

I populate three files a.txt, b.txt, and c.txt with a, b, and c respectively.

% echo a > a.txt
% echo b > b.txt
% echo c > c.txt

When I open all three of these files in one tab page, I get the following.

enter image description here

Then I can move a.txt to a tab by itself. When I do, however, a.txt is removed from the original tab page. (So this would be after a <c-w>Tgt)

enter image description here

I'd like the ability to choose at the time that I would have pressed <c-w>T to preserve the contents of the original buffer, create a new tab page containing only the buffer that is currently focused, and then focus the new tab page I've just created. In other words, a command almost exactly like <c-w>T except that the original tab page is preserved and now a.txt is in two tab pages.

Is there a way to do this?

12

I'm not sure but you could try :tab split (or the shorter version :tab sp).

The :split command should duplicate the viewport displaying a.txt, while the :tab modifier should move this viewport into a dedicated tab page.


If you want to change the behavior of C-w T, you could remap it like this:

nnoremap <C-w>T :tab split<CR>

More generally, every time you find a command which splits the window, and you would rather it creates a new tab page instead, you can prefix it with :tab.

It can be used, for example, to read a help buffer in a new tab page:

:tab help {your_topic}

By default, the new tab page will appear after the current one. But if you want to make it appear somewhere else, you can prefix :tab with a count.

For example, to duplicate the current viewport in a tab page after the 3rd one, you could type:

:3tab split

And to make it appear as the first one:

:0tab split

For more information, you can read :h :tab:

:[count]tab {cmd}                   *:tab*
        Execute {cmd} and when it opens a new window open a new tab
        page instead.  Doesn't work for |:diffsplit|, |:diffpatch|,
        |:execute| and |:normal|.
        If [count] is given the new tab page appears after the tab
        page [count] otherwise the new tab page will appear after the
        current one.
        Examples:

            :tab split      " opens current buffer in new tab page
            :tab help gt    " opens tab page with help for "gt"
            :.tab help gt   " as above
            :+tab help      " opens tab page with help after the next
                            " tab page
            :-tab help      " opens tab page with help before the
                            " current one
            :0tab help      " opens tab page with help before the
                            " first one
            :$tab help      " opens tab page with help after the last
                            " one
2

I've mostly been avoiding tabs because of this problem, but now I have a function that has the tab duplication behavior I want. I've tested it, but haven't started using it in earnest. There may be some hidden drawback to this workflow.

One problem with the function is that it doesn't duplicate some of the state from the previous buffer-window pair (like whether set number is on or not). Speculating a bit, c-w T probably does not have this problem because no duplication is performed and the window is probably just reparented.

Vim has a couple of 1-based lists for things like buffers, tabs, and windows. As far as I can tell, they are 1-based because the 0 key is used to move to the beginning of a line and as a result passing zero as a numeric argument is impossible.

We care about three lists for emulating this functionality:

  • The global list of tab pages
  • The per-[tab page] list of windows
  • The global list of buffers

We save all of this values, then create a new tab via "tabnew". New tabs are always created to the right, so none of the indices below the tab that we tabnew'd from are invalidated. (A more robust way to do this would probably be better though).

The tabnew command also moves focus to the new tab and the single window within it. From there we can use the buffer command to create a view onto the buffer that originally had focus.

Then we use the saved index of the original tab to restore focus back to that tab. And then, largely out of paranoia, we set the focus within that tab to the original window. Vim seems to remember which window has focus in non-visible tabs, but I don't like relying on that.

(A few stylistic points: the explicit numeric conversion 0+, global variables, and assertions are all intentional)

function! TabDuplicate()
  " set vars, sanity checking
  let g:tabdup_win      = 0+ winnr()
  let g:tabdup_buf      = 0+ bufnr('%')
  let g:tabdup_tabpage  = 0+ tabpagenr()
  call assert_true(g:tabdup_win > 0)
  call assert_true(g:tabdup_buf > 0)
  call assert_true(g:tabdup_tabpage > 0)
  " make a new tab page,
  " the new tab page will have focus
  " none of the indices, which are all
  " less than the current index, are
  " invalidated by creating a new tab
  execute "tabnew"
  " visit the buffer we saved
  execute "buffer " . g:tabdup_buf
  " return to the original tab page
  execute "tabnext " . g:tabdup_tabpage
  " return focus to original window
  execute g:tabdup_win . " windcmd w"
endfunction

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.