In Vim, typically, prefixing a command repeats the action multiple times.

This is not the case for gt, which goes to the next tabpage. Whereas gt goes to the next tabpage, 2gt always goes to the second tabpage, 3gt to the third, and so on.

The gt command has a variant that uses termwinkey <C-w>gt, so that it can be invoked from the Vim terminal, with the corresponding counted forms being <C-w>{count}gt.

I'd like to create a mapping on Gt (and correspondingly, <C-w>Gt) that performs the repeated action.

All together, the counted forms would be {count}Gt and <C-w>{count}Gt, e.g. 5gt and <C-w>5gt.

For normal mode, I've no problem with mapping like so:

noremap Gt :<C-u>execute join (repeat (['tabnext'], v:count), ' <Bar> ') <CR>

However, I'm struggling to write the mapping that involves termwinkey, as it goes by the form X{count}Y rather than the usual {count}X, so I'm unable to write a mapping from X utilizing v:count.

  • You could get in the habit of traversing your tabs in reverse order by using gT. If nothing else, it accepts counts as counts, not tab numbers.
    – Friedrich
    Commented Nov 16, 2023 at 14:48
  • I would expect tnoremap or the terminal docs to answer this in some fashion (even if saying that counts are not supported)
    – D. Ben Knoble
    Commented Nov 16, 2023 at 17:12
  • Do you still have something open in your question? How can we help you further? Otherwise maybe could you accept one of the answer using the v button next to the arrow voting buttons. I allow the question to rest :-) Commented Nov 20, 2023 at 13:57
  • @VivianDeSmedt I don't think any of the offered answers address the question as posed, of adding counts in something that resembles the usual form, but they do offer some food for thought. I need to try to come back with some more creativity and do so. Commented Nov 22, 2023 at 15:04
  • Thanks for the feedback. Given the expertise of some of the contributors (@romainl) I'm afraid the answer is somewhere here :-) Commented Nov 23, 2023 at 2:28

5 Answers 5


You can use a mapping to a macro to make an action repeatable. In your case I would

:nnoremap <silent> Gt @='gt'<cr>

With this mapping the count will be applied to repeat the macro and will not be used for gt directly. Alas, this only seems to work in normal mode, but not after coming from terminal mode with termwinkey.


Drawing inspiration from the other answers in performing direct mapping of digits and from the use of getchar, I was able to generalise a technique to provide counts under termwinkey that works for arbitrary counts and is also workable for most mappings.

Consider the mapping specified in the question.

The following procedure works with some manual work and assuming:

  1. The same mapping exists in normal mode
  2. It is acceptable for the relevant mapping to end up in normal mode
" case without counts
map <C-w>Gt Gt
map! <C-w>Gt <Esc>Gt
tmap <C-w>Gt <C-w>NGt

" case with counts
map <expr> <C-w>1 Termcountmap(1, "")
map <expr> <C-w>2 Termcountmap(2, "")
map <expr> <C-w>3 Termcountmap(3, "")
map <expr> <C-w>4 Termcountmap(4, "")
map <expr> <C-w>5 Termcountmap(5, "")
map <expr> <C-w>6 Termcountmap(6, "")
map <expr> <C-w>7 Termcountmap(7, "")
map <expr> <C-w>8 Termcountmap(8, "")
map <expr> <C-w>9 Termcountmap(9, "")
map! <expr> <C-w>1 Termcountmap(1, "<Esc>")
map! <expr> <C-w>2 Termcountmap(2, "<Esc>")
map! <expr> <C-w>3 Termcountmap(3, "<Esc>")
map! <expr> <C-w>4 Termcountmap(4, "<Esc>")
map! <expr> <C-w>5 Termcountmap(5, "<Esc>")
map! <expr> <C-w>6 Termcountmap(6, "<Esc>")
map! <expr> <C-w>7 Termcountmap(7, "<Esc>")
map! <expr> <C-w>8 Termcountmap(8, "<Esc>")
map! <expr> <C-w>9 Termcountmap(9, "<Esc>")
tmap <expr> <C-w>1 Termcountmap(1, "<C-w>N")
tmap <expr> <C-w>2 Termcountmap(2, "<C-w>N")
tmap <expr> <C-w>3 Termcountmap(3, "<C-w>N")
tmap <expr> <C-w>4 Termcountmap(4, "<C-w>N")
tmap <expr> <C-w>5 Termcountmap(5, "<C-w>N")
tmap <expr> <C-w>6 Termcountmap(6, "<C-w>N")
tmap <expr> <C-w>7 Termcountmap(7, "<C-w>N")
tmap <expr> <C-w>8 Termcountmap(8, "<C-w>N")
tmap <expr> <C-w>9 Termcountmap(9, "<C-w>N")

function! Termcountmap(initcount, normalkeys)
        let termcount = a:initcount
        while 1
                        let char = getchar()
                catch /^Vim:Interrupt$/
                        return ""

                if type(char) == 0
                        let char = nr2char(char)

                if char == '0'
                        let termcount = termcount * 10
                elseif char == '1'
                        let termcount = termcount * 10 + 1
                elseif char == '2'
                        let termcount = termcount * 10 + 2
                elseif char == '3'
                        let termcount = termcount * 10 + 3
                elseif char == '4'
                        let termcount = termcount * 10 + 4
                elseif char == '5'
                        let termcount = termcount * 10 + 5
                elseif char == '6'
                        let termcount = termcount * 10 + 6
                elseif char == '7'
                        let termcount = termcount * 10 + 7
                elseif char == '8'
                        let termcount = termcount * 10 + 8
                elseif char == '9'
                        let termcount = termcount * 10 + 9
                elseif char == 'G'
                        return a:normalkeys . termcount . 'G'
                        " return "\<C-w>" . termcount . char
                        " why doesn't the above work???
                        return a:normalkeys . ":\<C-u>" . termcount . ' wincmd ' . char . "\<CR>\<C-l>"

Any other relevant prefixes that would be desirable captured can be added to the elseif cases.

(I have omitted the == '^\d\+$' condition clause guarding nr2char from Vim wiki's sample; I don't see how that clause can ever be true)

  • 1
    Thanks to some implicit conversion rules in vimscript, you should be able to write :let count = count * 10 + char if char =~ '^\d$' and simplify the if-ladder. Commented Apr 29 at 4:42

I would propose:

noremap <C-w>2Gt <cmd>execute join (repeat (['tabnext'], 2), ' <Bar> ')<CR>
noremap <C-w>3Gt <cmd>execute join (repeat (['tabnext'], 3), ' <Bar> ')<CR>
noremap <C-w>4Gt <cmd>execute join (repeat (['tabnext'], 4), ' <Bar> ')<CR>
noremap <C-w>5Gt <cmd>execute join (repeat (['tabnext'], 5), ' <Bar> ')<CR>

You could also generate all these mappings in a loop.

The problem I noticed is that otherwise Vim considers <C-w>2 as a complete keysequence and search based on that input for a mapping if any. This is why I believe we have to instruct Vim to eat the number and the following Gt key before doing the mapping.

I also notice that v:count is not populated in such case.


I have had these two mappings for several years:

nnoremap <expr> gt ":tabnext +" . v:count1 . '<CR>'
nnoremap <expr> gT ":tabnext -" . v:count1 . '<CR>'

but, despite them being theoretically more intuitive than the default, I could never get used to them.

As mentioned in another answer, v:count and v:count1 are surprisingly unusable after :help 'termwinkey' despite commands such as <C-w>3k working perfectly. An easy workaround would be to make explicit terminal-mode mappings:

tnoremap <C-w>gt  <C-w>:tabnext +1<CR>
tnoremap <C-w>2gt <C-w>:tabnext +2<CR>
tnoremap <C-w>3gt <C-w>:tabnext +3<CR>
" etc.

Note regarding the normal version:

  • tabnext +1 doesn't cycle
  • repeated gt will go to each tab until the count is reached

There is a convoluted way that directly goes to the n-th next tabpage (and cycles) thanks to tabpagenr(). Because tabpage numbers are 1-indexed and modulo arithmetic is 0-indexed, we won't win any vimolf with this

:noremap <expr> Gt ':<c-u>exe "normal! ' .. ((tabpagenr() + v:count1 - 1) % tabpagenr('$') + 1) .. 'gt"<cr>'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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