diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-18 17:00:08 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-18 17:00:08 -0400 |
| commit | 55fb9102c70dc272d4267aec30eed4860f3abdf5 (patch) | |
| tree | df2f80a85eb42b17d8770545440523e331266a20 | |
| parent | a537ef1155bff9aa269a6e7d6d30aa9a378f552b (diff) | |
| download | dotemacs-55fb9102c70dc272d4267aec30eed4860f3abdf5.tar.gz dotemacs-55fb9102c70dc272d4267aec30eed4860f3abdf5.zip | |
feat(vterm): forward <escape> to the pty in vterm-mode
`<escape>' is bound globally to `keyboard-escape-quit' in
modules/keybindings.el, so Emacs swallows the key before it can reach the
pty. Bind it in vterm-mode-map to cj/vterm-send-escape, which writes a
literal ESC byte via vterm-send-string. tmux's copy-mode `cancel' binding
then fires; vi-mode exits, fzf cancel, etc., also work as expected.
| -rw-r--r-- | modules/vterm-config.el | 14 | ||||
| -rw-r--r-- | tests/test-vterm-tmux-history.el | 15 |
2 files changed, 28 insertions, 1 deletions
diff --git a/modules/vterm-config.el b/modules/vterm-config.el index 0817c3d9..70f5d60a 100644 --- a/modules/vterm-config.el +++ b/modules/vterm-config.el @@ -215,6 +215,17 @@ because tmux never sees the events." (interactive) (cj/vterm--send-mouse-wheel 65)) +(defun cj/vterm-send-escape () + "Send the ESC byte to the program running in this vterm. + +`<escape>' is bound globally to `keyboard-escape-quit' (see +`modules/keybindings.el'), so without this override Emacs swallows +the key before it can reach the pty. Forwarding it here lets tmux +copy-mode cancel, vi-mode exits, and any other in-terminal program +that relies on Escape see the key." + (interactive) + (vterm-send-string "\e")) + (use-package vterm :defer .5 :commands (vterm vterm-other-window) @@ -255,7 +266,8 @@ ai-vterm.el is loaded." ("<wheel-up>" . cj/vterm-mouse-wheel-up) ("<wheel-down>" . cj/vterm-mouse-wheel-down) ("<mouse-4>" . cj/vterm-mouse-wheel-up) - ("<mouse-5>" . cj/vterm-mouse-wheel-down)) + ("<mouse-5>" . cj/vterm-mouse-wheel-down) + ("<escape>" . cj/vterm-send-escape)) :custom (vterm-kill-buffer-on-exit t) (vterm-max-scrollback 100000) diff --git a/tests/test-vterm-tmux-history.el b/tests/test-vterm-tmux-history.el index be654905..88bd5593 100644 --- a/tests/test-vterm-tmux-history.el +++ b/tests/test-vterm-tmux-history.el @@ -352,6 +352,21 @@ its own copy-mode against the full pane history." (cj/vterm-mouse-wheel-down) (should (equal sent '("\e[<65;1;1M")))))) +(ert-deftest test-vterm-send-escape-writes-esc-byte () + "Normal: `cj/vterm-send-escape' forwards a literal ESC byte to the pty so +tmux copy-mode, vi-mode exits, etc., can see the key past Emacs's global +`<escape>' → `keyboard-escape-quit' binding." + (let ((sent nil)) + (cl-letf (((symbol-function 'vterm-send-string) + (lambda (s &optional _paste-p) (push s sent)))) + (cj/vterm-send-escape) + (should (equal sent '("\e")))))) + +(ert-deftest test-vterm-escape-binding-installed-on-vterm-mode-map () + "Normal: `<escape>' in `vterm-mode-map' routes through `cj/vterm-send-escape'." + (should (eq (keymap-lookup vterm-mode-map "<escape>") + #'cj/vterm-send-escape))) + (ert-deftest test-vterm-wheel-bindings-installed-on-vterm-mode-map () "Normal: wheel-up / wheel-down (and X11 mouse-4 / mouse-5) route to the forwarding commands so tmux can see them via `set -g mouse on'." |
