Route ESC to terminal in alt-screen apps under evil-ghostel (#215)#216
Conversation
Pressing ESC in evil insert state inside ghostel ran `evil-normal-state' even when the inner app (vim, less, htop, …) needed the keystroke. Add a buffer-local routing mode `auto'/`terminal'/`evil' controlled by `evil-ghostel-escape' and a toggle command with numeric prefix support. `auto' uses DECSET 1049 to decide. Terminal-bound ESC snaps to the live viewport like every other typed key; the evil-bound fallback lands on `evil-force-normal-state' when the user's `<escape>' binding is missing or a chord prefix. Fixes #215
| (setq evil-ghostel--escape-mode target) | ||
| (message "evil-ghostel ESC mode: %s" target))) | ||
|
|
||
| (evil-define-key* 'insert evil-ghostel-mode-map |
There was a problem hiding this comment.
I'd imagine there's a scenario where you're in normal state in emacs but want ESC to be sent to the insert state in the nested emacs/vim or maybe vice versa. Not sure because we're dealing with 2 modal states that could be not synchronized.
Or maybe in a non normal/insert state (like visual/motion/etc?) state but wanting ESC to pass through.
There was a problem hiding this comment.
Since I'm not an evil user I'm not 100% I understand what you mean, can you rephrase or make a suggestion for a fix?
btw (maybe it helps), you can always C-c C-q <esc> to send the next key to the terminal whatever it is ( in this case).
There was a problem hiding this comment.
e.g. if you're in emacs (outer) and vim (inner) [start emacs -> start ghostel -> start vim inside ghostel
maybe using 'insert state is ok e.g. for the user to interact with ghostel, they need to be in insert state, which then triggers your function above
the cycling might be strange though
e.g. emacs+evil [normal state] -> go to insert state -> press i in vim to go to insert state -> press ESC. this cycles the ESC to the next thing.
But you want this instead:
emacs+evil [normal state] -> go to insert state in emacs -> press i in vim to go to insert state -> press ESC -> (normal state in vim) -> press i again to go to insert state in vim -> press ESC to go to normal state in vim -> press i again to go to insert state in vim -> and so on.
^ I think this case is more likely to happen.
If you do that though, you still need a way to get back to the outer emacs (e.g. to get back to normal state in emacs) but once the inner vim is taking ESC inputs, it really won't know which ESC you want to send to so you can't do it automagically.
suggestion for a fix?
No suggestion, the way we do it in vterm is kind of a "this is too complicated, just let the user decide when they want what". If there's a more clever way to handle this, I'm all for it.
There was a problem hiding this comment.
emacs+evil [normal state] -> go to insert state in emacs -> press i in vim to go to insert state -> press ESC -> (normal state in vim) -> press i again to go to insert state in vim -> press ESC to go to normal state in vim -> press i again to go to insert state in vim -> and so on.
^ I think this case is more likely to happen.
If you do that though, you still need a way to get back to the outer emacs (e.g. to get back to normal state in emacs) but once the inner vim is taking ESC inputs, it really won't know which ESC you want to send to so you can't do it automagically.
But doesn't the auto-mode kinda address that in that is sends the ESC so long to vim as long as vim is open. You close it to type something in the shell, ESC gets routed to evil?!
And if you want your first case or not have ESC forwardet to the terminal when an alt-screen app is running, you have to manually change the routing (like in your vterm solution right now)?!
There was a problem hiding this comment.
You might want to leave the window/leave vim running but still be able to get back to Normal state in emacs. I think this is a relatively common use case so won't describe specific examples. :)
And if you want your first case or not have ESC forwardet to the terminal when an alt-screen app is running, you have to manually change the routing (like in your vterm solution right now)?!
Yeah, I think it's a more manual/intentional step.
There was a problem hiding this comment.
You might want to leave the window/leave vim running but still be able to get back to Normal state in emacs. I think this is a relatively common use case so won't describe specific examples. :)
ok.
But anyway, should we merge this PR as it provides at least a workaround (and with the auto-mode hopefully even a slightly better hack than vterm)?
(and this might be me not using it, but if you just want to get into normal state emacs, you
can of course just bind evil-force-normal-state so something like C-c esc or whatever.)
And if you or some other evil user has a better idea how that "workflow" could be implemented I'm open for issues or PRs.
There was a problem hiding this comment.
Great, thanks.
And happy to discuss any ideas/PRs, especially evil related as I don't test this integration very much.
Summary
Fixes #215. Pressing ESC in evil insert state inside a ghostel buffer ran
evil-normal-stateeven when the inner app (vim, less, htop, nvim, etc.) needed the keystroke.evil-ghostel-escape:auto(default) |terminal|evil.autochecks DECSET 1049 (alt-screen) to decide.evil-ghostel-toggle-send-escape: no-arg cyclesauto → terminal → evil → auto; numeric prefix1/2/3sets directly. Out-of-range numeric prefix signalsuser-error.ghostel--snap-to-inputto match the invariant every other key inghostel-mode-mapholds.evil-force-normal-statewhen the user's<escape>binding is missing or a chord prefix (e.g.evil-escape'sjk), so the keystroke is never silently dropped.Test plan
make -j4 all— 218 ERT tests pass, 11 new intest/evil-ghostel-test.el:lookup-keyreturns nil (chord/rebound case)user-errorterminalin buffer A,evilin buffer B, re-enters A)evil-ghostel-escapedefcustomM-x ghostel:viminsert mode → ESC leaves insert (auto path).evilmid-vim, ESC switches evil state instead.opencode(alt-screen but user wants evil ESC) → toggle toevilworks.M-2 evil-ghostel-toggle-send-escapedirectly forcesterminal.