Categories
Uncategorized

Getting CTRL+TAB to work in neovim

If you’ve been using a terminal for a while, you’ve surely stumbled across the terminal’s legacy keyboard handling. Terminal escape codes were created in the 1970s and haven’t been updated much.

CTRL + J == newline
CTRL + I == tab

These two examples are some of the least annoying, but the implications aren’t at first obvious. Normal terminal apps can’t tell whether you actually typed “ENTER” on your keyboard or “CTRL” + “I”. From the perspective of the app, it’s the same. Sadly, that’s just the tip of the iceberg for these limitations.

CTRL + … Sequences starting with CTRL+… are basically used up by the way the terminal represents common data (CTRL + D == EOF) with “CTRL CODES”, and CTRL+SHIFT+letter is the same as CTRL+letter

ALT + C might be encoded as 0xc3, which collides with UTF-8 for characters like: é.

SHIFT + … The terminal doesn’t know the difference between “Shift + s” and “S”…

Hyper +… Hardly any apps are cool enough to be able to work with the awesome hyper modifier… Without an enhanced way to process keycodes, this isn’t possible.

Many terminals rely on timing to distinguish codes like Alt+C from Esc + C… This generates lag and errors when typed too slow or too fast. vim-fixkey has a nice list of workarounds to get full access to keypresses in the terminal, though even in the best case scenario, it would be limited by a timeout, and not work for Cmd, Hyper or various CTRL+x keys…


Fortunately Leonerd, the author of libtermkey (now replaced by libtickit), wrote the “fixterms” specification and the author of Kitty has extended the fixterms spec to cover Super, Hyper and other modifier keys. iTerm 2 is also being extended to cover the new Kitty version of the fixterms spec.

Some of this is “worked around” with the XTerm ModifyOtherKeys option that’s also supported in gnome-terminal and Konsole. Unfortunately ModifyOtherKeys is not complete.

There is discussion on the NeoVim github issues about adding fixterms support in neovim, but at this time (Nov 2021) it seems that no support is yet complete. It looks like neovim/src/nvim/keymap.c probably holds most of the code that needs to change.

https://github.com/neovim/neovim/blob/dc4670038e0441dfda7ba11c519b834624a1f6fd/src/nvim/keymap.c#L748-L755

Fix ambigous terminal key strokes? https://github.com/neovim/neovim/issues/176

Someone may have worked on an actual code change for this, tracking at: https://github.com/neovim/neovim/issues/6279


TUI: enable/disable modifyOtherKeys automatically https://github.com/neovim/neovim/issues/15352
TUI: distinguish Tab, CTRL-i (S8C1T mode) https://github.com/neovim/neovim/issues/5916
CTRL-Alt-Space isn’t recognized even though terminal sends ^[^@, works in Vim https://github.com/neovim/neovim/issues/14836

Binding <M-S-Tab> https://github.com/neovim/neovim/issues/2379

TUI: S8C1T (8-bit) mode, v:termresponse https://github.com/neovim/neovim/issues/6279

See: “xterm-8-bit” Nvim does not use 8-bit sequence detection, and always uses 7-bit sequences (for now)

One day, hopefully Neovim will support these sequences by default, but in the mean time it’s possible to map these sequences manually in Kitty and Neovim, it’s possible to manually use a specific mapping by configuring Kitty’s map ... send_text

https://github.com/kovidgoyal/kitty/issues/3248

# In Kitty.conf Example
map ctrl+enter send_text normal,application \x1b[13;5u

# In init.lua
vimp = require('vimp')
vimp.bind('n', '<C-cr>', ':echom "Hello C + R"<CR>')

Remember, the Kitty chart for progressive enhancement is:

BitMeaning
0b1 (1)Disambiguate escape codes
0b10 (2)Report event types
0b100 (4)Report alternate keys
0b1000 (8)Report all keys as escape codes
0b10000 (16)Report associated text

So \x1b = 11011… So it will turn on all options except “report alternate keys”.

Decimal 13 == Carriage Return

; 5u == modifier flags == “Ctrl” + 1.

shift     0b1         (1)
alt       0b10        (2)
ctrl      0b100       (4)
super     0b1000      (8)
hyper     0b10000     (16)
meta      0b100000    (32)
caps_lock 0b1000000   (64)
num_lock  0b10000000  (128)

Another example…

# In Kitty.conf Example
map ctrl+tab     send_text normal,application \x1b[9;5u
map ctrl+shift+tab send_text normal,application \x1b[9;6u


# In init.lua
vimp = require('vimp')
vimp.bind('n', '<C-tab>', ':echom "Hello Tab"<CR>')
vimp.bind('n', '<C-S-tab>', ':echom "Hello Shift Tab"<CR>')

In this example, the relevant parts are:

\x 1b [ 9 ;5u
\x 1b [ 9 ;6u
   ^ 1b = 11011binary == turn on Disambiguate Esc Codes, Report Event Types, Report All Keys as Escaped, Report Associated Text
      [ is just the end of our escape code
         9 is the keycode for Tab
            5u = 1 (constant) + 4 (0100) Control
            6u = 1 (cont) + 5
                   4 = ctrl + 1 shift = 5

Let’s go through a few more examples. Let’s try: CTRL + Super + Tab. Unfortunately, even though Kitty can send this one, until Neovim’s keyboard support is more complete, it doesn’t look like Neovim has any way to receive this keycode.

CTRL     = 4
SUPER    = 8
Constant = 1
Total    =13 
# Remember, Tab = 0x9
\x 1b [ 9; 13u

Sadly, I can’t get Super or Hyper based shortcut keys to work in Neovim or regular Vim yet, but I think it will be here soon…

Leave a Reply

Your email address will not be published. Required fields are marked *