Hyper: Hyperterm doesn't handle Alt key combinations correctly

Created on 9 Jan 2017  ·  15Comments  ·  Source: vercel/hyper

  • [x] I am on the latest Hyper.app version
  • [x] I have searched the issues of this repo and believe that this is not a duplicate
  • OS version and name: macOS 10.12.2
  • Hyper.app version: 1.0.1
  • The issue is reproducible in vanilla Hyper.app: Added modifierKeys: { altIsMeta: true } to otherwise stock .hyper.js

Issue


Issue: when trying to do Alt key combinations (ie. Alt+Left_arrow, Alt+backspace), Hyperterm doesn't interpret this as a command (such as skipping back/forward a word or deleting an entire word). When using Alt+arrow keys, Hyper places [D or [C, etc. and when using Alt+delete, it deletes two words instead of one. Alt+delete also causes multiple lines of text to be erased in emacs.

Relevant info: using zsh shell + ohmyzsh, only modification to .hyper.js is
modifierKeys: { altIsMeta: true } to get Alt to work at all.

Compatibility

Most helpful comment

I've been tearing my hair out trying to figure out the relationship between alt, meta and escape, and why some things seem to work in iTerm but not in Hyper. I believe I've understood what's going on and figured I'd post here for anybody who is as frustrated as I was:

TLDR: iTerm and Hyper mean different things by "treating alt as meta". Hyper also appears to have an issue with feeding escape-prefixed bytes to the shell.

In the past, many keyboards had meta keys. Traditionally, terminals interpreted the meta modifier to set the most significant bit on a byte. ASCII is a 7-bit code in which the most significant bit is always unset. ASCII with meta turns this into a full 8-bit (1 byte) code. Shells and other terminal programs allowed you to bind these metafied bytes. For example, in zsh bindkey "\M-x" kill-word binds the ASCII byte code for x, with its most significant bit set, to the kill-word widget. Let us refer to bytes with their most significant bit set as "metafied".

However, at some point meta keys started disappearing from keyboards. Because it is better for terminals to function consistently across different keyboards, many terminals started to change their interpretation of meta. Now, rather than setting the most significant bit, an additional escape (^[) byte was prepended. Thus pressing meta no longer expressed functionality that was impossible for metaless keyboards, since simultaneously pressing meta and x had the same effect as pressing Esc followed by x. Let us refer to bytes preceded by an Esc as "escape-prefixed".

Unfortunately established software was quite slow to change. Shells and other terminal programs like vim continue to reference meta in their configuration. Some key on the modern keyboard had to be chosen to map to this functionality, and alt became the choice. Thus in macOS Terminal.app there is an option to "Use Option as Meta key". In iTerm2, you can set each option to one of three settings: Normal Meta or Esc+. And in Hyper, you can set altIsMeta: true.

Now, what is extremely confusing about the above is that Meta does not mean the same thing in these three apps. Notice that Terminal and Hyper allow only two options for alt, whereas iTerm allows three. That is because, in both Terminal and Hyper, "use option as meta" means "holding alt generates escape-prepended bytes". You can confirm this by setting this option, going into the respecting terminal, and pressing Ctrl-k Alt-k (Ctrl-k will bypass shell interpretation for the following character, allowing you to insert it directly). In both cases, you should see ^[k, which is an escape character followed by k. Thus, Hyper and Terminal restrict the user to this escape-prefixing behavior and don't seem to expose any way to metafy bytes, which means that you can't target zsh bindings like \M-x in Hyper or Terminal. In contrast, "Meta" in iTerm means "metafy this byte" and "Esc+" means "escape-prefix this byte"

Even more confusingly, many terminal programs treat escape-prefixed and metafied bytes as completely different, whereas others treat them the same. In zsh for instance, bindkey "\M-x" maps to metafied ASCII x, and bindkey "^[x" (or `bindkey "\ex") maps to escape-prefixed x. Meanwhile, other programs contain a layer that automatically translates metafied bytes (most significant bit set) into their escape-prefixed siblings. Thus, some terminal programs will work differently depending on whether the terminal is performing metafication or escape-prefixing, whereas others will work the same.

Finally, Hyper in particular seems to have a problem with feeding escape-prefixed bytes to the shell. I have bindkey -M viins "\en" history-substring-search-up in my zsh config. In Terminal, with alt set to behave as meta (i.e. perform escape-prefixing), this works fine. However, in Hyper, with altIsMeta: true it doesn't work despite the fact that the same escape-prefixing behavior is performed. Instead, alt+n puts me out of viins (vim-insert) mode and into normal mode. What this tells me is that for some reason the leading escape byte is being processed separately from the following n byte, which is triggering the switch out of insert mode. I suspect this is related to #1877 since they both involve Hyper feeding text to the shell.

All 15 comments

I too am having this problem and can reproduce correctly.

Default behaviour of Alt + Delete key combination seems to do nothing.

Setting modifierKeys: { altIsMeta: true } explicitly in the configuration will allow the simultaneous deletion of two words instead of the expected behaviour of only deleting one word.

Nu, has this been fixed _anywhere_?

Alt+f and Alt+b also don't work as expected with altIsMeta: true, you get ∫ and ƒ characters. This has been reported by others in the comments for #330.

I've been tearing my hair out trying to figure out the relationship between alt, meta and escape, and why some things seem to work in iTerm but not in Hyper. I believe I've understood what's going on and figured I'd post here for anybody who is as frustrated as I was:

TLDR: iTerm and Hyper mean different things by "treating alt as meta". Hyper also appears to have an issue with feeding escape-prefixed bytes to the shell.

In the past, many keyboards had meta keys. Traditionally, terminals interpreted the meta modifier to set the most significant bit on a byte. ASCII is a 7-bit code in which the most significant bit is always unset. ASCII with meta turns this into a full 8-bit (1 byte) code. Shells and other terminal programs allowed you to bind these metafied bytes. For example, in zsh bindkey "\M-x" kill-word binds the ASCII byte code for x, with its most significant bit set, to the kill-word widget. Let us refer to bytes with their most significant bit set as "metafied".

However, at some point meta keys started disappearing from keyboards. Because it is better for terminals to function consistently across different keyboards, many terminals started to change their interpretation of meta. Now, rather than setting the most significant bit, an additional escape (^[) byte was prepended. Thus pressing meta no longer expressed functionality that was impossible for metaless keyboards, since simultaneously pressing meta and x had the same effect as pressing Esc followed by x. Let us refer to bytes preceded by an Esc as "escape-prefixed".

Unfortunately established software was quite slow to change. Shells and other terminal programs like vim continue to reference meta in their configuration. Some key on the modern keyboard had to be chosen to map to this functionality, and alt became the choice. Thus in macOS Terminal.app there is an option to "Use Option as Meta key". In iTerm2, you can set each option to one of three settings: Normal Meta or Esc+. And in Hyper, you can set altIsMeta: true.

Now, what is extremely confusing about the above is that Meta does not mean the same thing in these three apps. Notice that Terminal and Hyper allow only two options for alt, whereas iTerm allows three. That is because, in both Terminal and Hyper, "use option as meta" means "holding alt generates escape-prepended bytes". You can confirm this by setting this option, going into the respecting terminal, and pressing Ctrl-k Alt-k (Ctrl-k will bypass shell interpretation for the following character, allowing you to insert it directly). In both cases, you should see ^[k, which is an escape character followed by k. Thus, Hyper and Terminal restrict the user to this escape-prefixing behavior and don't seem to expose any way to metafy bytes, which means that you can't target zsh bindings like \M-x in Hyper or Terminal. In contrast, "Meta" in iTerm means "metafy this byte" and "Esc+" means "escape-prefix this byte"

Even more confusingly, many terminal programs treat escape-prefixed and metafied bytes as completely different, whereas others treat them the same. In zsh for instance, bindkey "\M-x" maps to metafied ASCII x, and bindkey "^[x" (or `bindkey "\ex") maps to escape-prefixed x. Meanwhile, other programs contain a layer that automatically translates metafied bytes (most significant bit set) into their escape-prefixed siblings. Thus, some terminal programs will work differently depending on whether the terminal is performing metafication or escape-prefixing, whereas others will work the same.

Finally, Hyper in particular seems to have a problem with feeding escape-prefixed bytes to the shell. I have bindkey -M viins "\en" history-substring-search-up in my zsh config. In Terminal, with alt set to behave as meta (i.e. perform escape-prefixing), this works fine. However, in Hyper, with altIsMeta: true it doesn't work despite the fact that the same escape-prefixing behavior is performed. Instead, alt+n puts me out of viins (vim-insert) mode and into normal mode. What this tells me is that for some reason the leading escape byte is being processed separately from the following n byte, which is triggering the switch out of insert mode. I suspect this is related to #1877 since they both involve Hyper feeding text to the shell.

Awesome detailed explainations @smackesey! Thank you so much!
We are currently replacing our underlying terminal hterm by xterm.js. We'll pay attention to deal with this meta correctly. And it will be easier with your insights.

@chabou for future reference, I don't think xterm.js has an option for alt as meta atm, here is the relevant function such an option would affect though https://github.com/sourcelair/xterm.js/blob/1c84881d00e381f9f1b4fde235149ffc13296734/src/xterm.js#L1494

Also this is kind of related: https://github.com/sourcelair/xterm.js/issues/487

https://github.com/zeit/hyper/commit/0fbf7cfc97f57bcfaba7868f6939501d59b948a6 seems to fix most of the ALT key combinations form the Readline library.

Alt+b : (backward) moves the cursor backward one word.
Alt+c : Capitalizes the character under the cursor and moves to the end of the word.
Alt+d : Cuts the word after the cursor.
Alt+f : (forward) moves the cursor forward one word.
Alt+l : Lowers the case of every character from the cursor's position to the end of the current word.
Alt+r : Cancels the changes and puts back the line as it was in the history.
Alt+u : Capitalizes every character from the cursor's position to the end of the current word.

ALT+. is still missing though.
Also noticed CTRL+x CTRL+x not there.
ALT+Backspace missing as well

Overall, a great improvement over master.

I fixed Alt+. for my setup by adding a command which is invoked with the Alt+. combination and sends the same escape sequence as xterm.
See: https://github.com/iimog/hyper/commit/b603f13900b79968f5519f1ee9bbab8f69a22611

Only tested for linux (only modified keymaps/linux.json) with Hyper version 2.0.0-canary.9, running zsh.

I assume that this way to fix it is not sufficiently generic for all the other Alt key combinations. I'll try to wrap things into a mini plugin to make it work without re-building hyper.

@smackesey @chabou I've been looking into this stuff for my own purposes with Terminal.app as well (“Use Option as Meta key” is too simple for me, as I'd like different behaviour on left and right alt without switching to iTerm2).

It should be noted that with the proliferation of UTF-8, it's not as simple as just adhering to the pattern of using the eighth bit to signal Meta; I believe this is why iTerm2 recommends using the Esc prefix pattern instead in its FAQ.

ref: #337 for altToEscape feature request

Alt+Enter in Midnight Commander doesen't work without additional patching. But there is no magic required in of vte-based terminals, xterm or urxvt: Alt+Enter in these terminals works out of the box.

alt + arrow keys for tmux shortcuts not working as well

I'm going to make an additional thread of my own, but I wanted to post in here so I can keep tabs.. Even though I can't seem to figure out how to get notifications for issues I posted in working..

Anyway I have been tearing my hair out too. Long story short, I became increasingly annoyed by the fact that for some reason in nano I had to use alt+left/right to jump words, in contrast to the practically ubiquitous ctrl+left/right. I didn't really look too far into it and just assumed it was a weird quick with nano since it has those..

But today I semisortaalmostbutnotquite figured out the problem. Rather than sending the proper code s which would be ^[[1:5D and ^[[1:5C for ctrl+left and ctrl+right respectively, hyper was sending ^[f and ^[b the codes for alt f and alt b.. Not only the key but a different modifier even.

This is due to the _default_ keybindings. Completely ridiculous if you ask me. To make matters worse there is nothing that says "this command editor.moveforwardblabla is actually going to remap a control sequence that may or may not be critical."

Seriously though, why?

So I remove the two key bindings and you know what I discovered?

Ctrl and alt send the same sequences. Alt is meta, true or false, doesn't matter. Same sequence as ctrl..using ESC actuslly produces a proper esc sequence and doubles as a meta key for the bindings I checked, but there's a difference between the cli using zle, and what shows up in the read built the letter doesn't read the combo as a fluid sequence..

I'm running zsh in wsl, and while this problem is bigger than I thought it was going to be. I also set some key bindings a little while ago to get my up/down working correctly and they are the same esc+arrow sequence, but I know I set them with ctrl and alt.. I did recently drop omz so maybe that has something to do with it.. Either way this is one deep ass rabbit hole..

Ibe checked my keyboard input and it hasnt changed, maybe something to do with Logitech software. Either way something changed, but hyper is the only program outputting ctrl sequences in place of alts (checked against win terminal, cmd, command prompt, PowerShell and PowerShell iis).. I suppose I could reinstall conemu to see what it does.. Ugh.

Is there any update on this issue ?
This issue is killing Hyper for any use. Cannot use the ALT key at all, it gets mapped to ctrl sequences in vim and well anything. Tried setting altIsMeta to true and false yet it does nothing.

Looks like this bug relates to general electron-based apps beviour.
In many other e-based apps alt key stolen by e itself. This can be overriden but tooks some efford in reading docs, because it hidden somwhere in e deeps, but it is overridable.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

anthonyettinger picture anthonyettinger  ·  3Comments

ghost picture ghost  ·  3Comments

sarneeh picture sarneeh  ·  3Comments

yvan-sraka picture yvan-sraka  ·  3Comments

rauchg picture rauchg  ·  3Comments