3

I'm using xfce4-terminal.

I remapped most Alt+key combinations in a file that is loaded in the .virmc. The file (called alt_bindings.vim, only loaded in vim 8 terminal mode) is like this:

set <M-a>=^]a
set <M-A>=^]A
set <M-b>=^]b
set <M-B>=^]B
set <M-c>=^]c
set <M-C>=^]C

etc for most keys. I excluded some combinations that gave me errors right away, such as alt+O and alt+[.

Then I have macros like this one:

"w   0vf'snno^I ^[lywio^[plxxx  :set <80>kD<80>kD^[wwD<80>kd

^[ is Esc key, but the macro seems to trigger my defined keys instead, and it breaks the macro totally. For example, ^[plxxx would be Escplxxx that is: back to normal mode, paste, right, delete 3x. It does something else entirely instead (like inserting xxx and other chars). The problem goes away if I don't load those mappings.

I tried something like:

set <Alt>=^]
set <M-A>=<Alt>A
set <M-b>=<Alt>b
set <M-B>=<Alt>B
set <M-c>=<Alt>c
set <M-C>=<Alt>C

But it doesn't work. I also tried something like:

set <t_F13>=^]
set <M-A>=<t_F13>A
set <M-b>=<t_F13>b
set <M-B>=<t_F13>B
set <M-c>=<t_F13>c
set <M-C>=<t_F13>C

but weird stuff happens and keys don't work either.

I then used (taken from here):

let c='a'
while c <= 'z'
  exec "set <M-".c.">=".c
  exec "nnoremap ".c." <M-".c.">"
  let c = nr2char(1+char2nr(c))
endw

it works, but it exhibits the same problem (broken macros).

Any help? Thanks.

5

The problem is caused by the way that terminals have historically handled the Alt/Meta key.

Back in the mists of history, some computers/terminals had a Meta key. When this was held down while another key was pressed, the 8th bit of the character was set. So e.g. pressing the a key with or without the Meta key depressed would result in the following keycodes:

       Hex  Binary
     p 0x70 01110000 
Meta+p 0xF0 11110000
            ^
            |
            8th bit

However, when sending this information a network, the 8th bit was sometimes used for other purposes (if the protocol/environment was not "8-bit clean") and this method could not be used. An alternative solution was instead to transmit an ESC character followed by the unmodified character. e.g. ^[p. This would then be interpreted by programs that received it as a Meta-P keypress.

This is why Vim is interpreting your Escp in the macro as Meta-P and triggering your mappings.

The reason the problem doesn't occur when you manually type Escp is that when Vim receives an ESC character, it waits for a short period of time to see whether it is followed by another character (See :help 'ttimeout'). If none follows within the time specified in your settings, Vim interprets it as a Esc keypress. If Vim receives another character within that timeframe, it interprets the pair as a Meta-key chord and triggers the mapping.

When you play back the macro, the keypresses are replayed as fast as possible, and so Vim interprets them incorrectly.

Unfortunately, I'm not aware of a great solution for this.

The options I can think of are:

  1. Use GUI Vim: this handles the Alt key itself, and so the mappings will work transparently,
  2. Configure your terminal to send some other keycode for each Alt-chord, and then map these keycodes in Vim,
  3. Use Neovim. I'm led to believe Neovim doesn't have this issue (although I'm not sure how it gets around it).
  4. Alter the way you record your macro so that the key following the Esc isn't one that would trigger a mapping. e.g. while recording, type EscCtrl-Cp instead of Escp,
  5. Don't use Alt-mappings. :(
  6. EDIT: Your comment gave me an idea: instead of manually sourcing files before and after running a macro, you can get Vim to do it for you:

    function! SetKeycodes()
      " N.B. Type ^[ as <Ctrl-V><Esc>
      set <M-o>=^[o
      set <M-p>=^[p
      "etc
    endfunction
    call SetKeycodes()
    
    function! UnsetKeycodes()
      set <M-o>=
      set <M-p>=
      "etc
    endfunction
    
    function! RunMacro()
      let r = getchar()
      echom r
      call UnsetKeycodes()
      execute "normal! @" . nr2char(r)
      call SetKeycodes()
    endfunction
    
    nnoremap @ :call RunMacro()<CR>
    inoremap <M-o> Mapping (o)
    inoremap <M-p> Mapping (p)
    set ttimeoutlen=10
    
  • Thanks, for now I created 2 files (enable and disable), sourced by keybindings, so I can temporarily disable alt-keys before using macros and then re-enable them. At least I know it's not solvable in Vim. In Gvim they work, unfortunately in Neovim I get a different output so I assume they don't work as well(I tried it with no plugins loaded). Even worse in Neovim I can't disable the mappings. – mg979 Feb 15 '18 at 18:21
  • @mg979 Neovim doesn't support Vim's termcap options, which I think might mean your :set <M-... commands are invalid/unnecessary. (Vim documents these under :help :set-termcap, but Neovim's vim_diff documentation doesn't mention them specifically in the entry about termcap options.) – Rich Feb 15 '18 at 20:58
  • @mg979 I've realised I misread your question originally, and you don't actually mention mappings in it at all (plus I misinterpreted what keycodes your terminal is actually sending). What are you actually using your alt keys for, if not for triggering mappings? Nevertheless, see my edit. You can alter the content of the Set/Unset functions to do whatever you need to do before and after running the macro. – Rich Feb 15 '18 at 21:09
  • I think you understood correctly. I was remapping Alt keys in terminal vim 8, not gvim/nvim. The macro I tried in nvim was recorded in vim, so maybe this is the reason it gave a different result, if nvim doesn't use termcap options. I didn't try recording the same macro in nvim, it's probably ok. – mg979 Feb 16 '18 at 8:44
  • Ok I tried your functions (modified a bit to reset timeoutlen and load the sources) and they seem to work ok! – mg979 Feb 16 '18 at 10:24

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.