diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-24 00:06:01 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-24 00:06:01 -0400 |
| commit | 83f2ac011f1afb085745ea527990eea32019fc00 (patch) | |
| tree | 4916f84bbe9b99d3c3365ff5255ac6c28e36a8d8 /modules/term-config.el | |
| parent | bf947111dd6d894e30191148ee1d0314df56eb25 (diff) | |
| download | dotemacs-83f2ac011f1afb085745ea527990eea32019fc00.tar.gz dotemacs-83f2ac011f1afb085745ea527990eea32019fc00.zip | |
feat(term): modified arrows enter copy-mode and carry direction
C-<arrow> and M-<arrow> in a ghostel buffer now enter copy-mode and move one step in that direction in a single stroke. The tmux path writes the arrow escape sequence into the pty so the copy cursor follows it; without tmux the same keys enter ghostel-copy-mode and move point. All eight keys join ghostel-keymap-exceptions and the semi-char map is rebuilt, so they reach Emacs instead of the terminal program.
Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
Diffstat (limited to 'modules/term-config.el')
| -rw-r--r-- | modules/term-config.el | 91 |
1 files changed, 81 insertions, 10 deletions
diff --git a/modules/term-config.el b/modules/term-config.el index c1c28911d..d8e9c05d0 100644 --- a/modules/term-config.el +++ b/modules/term-config.el @@ -206,6 +206,63 @@ start of the line for the same column-0 reason." (ghostel-copy-mode) (beginning-of-line))) +;; --------------------------- directional copy mode --------------------------- +;; +;; A modified arrow both enters copy-mode and carries its own movement, so one +;; stroke scrolls back in the intended direction. C-<arrow> and M-<arrow> bind +;; to these in ghostel buffers (and join `ghostel-keymap-exceptions' so they +;; reach Emacs instead of the pty). + +(defconst cj/--term-copy-mode-tmux-arrows + '((up . "\e[A") (down . "\e[B") (left . "\e[D") (right . "\e[C")) + "Per-direction arrow escape sequence written into tmux copy-mode. +After `cj/term-copy-mode-dwim' lands in tmux's copy-mode, writing the matching +sequence into the pty moves the copy cursor one step; tmux binds the cursor +keys in both its emacs and vi copy-mode tables.") + +(defun cj/--term-copy-mode-move-step (direction in-tmux) + "Take one copy-mode movement step in DIRECTION. +DIRECTION is one of `up', `down', `left', `right'. When IN-TMUX is non-nil, +write the matching arrow escape sequence into the pty so tmux's copy-mode +cursor follows it; otherwise move point in the `ghostel-copy-mode' buffer. +An unknown DIRECTION is a no-op." + (if in-tmux + (let ((seq (cdr (assq direction cj/--term-copy-mode-tmux-arrows)))) + (when seq (ghostel-send-string seq))) + (pcase direction + ('up (forward-line -1)) + ('down (forward-line 1)) + ('left (backward-char)) + ('right (forward-char))))) + +(defun cj/term-copy-mode-move (direction) + "Enter copy-mode (see `cj/term-copy-mode-dwim') then step one DIRECTION. +DIRECTION is one of `up', `down', `left', `right'. The in-tmux engine is +resolved once up front so the entry and the step pick the same surface." + (let ((in-tmux (cj/term--in-tmux-p))) + (cj/term-copy-mode-dwim) + (cj/--term-copy-mode-move-step direction in-tmux))) + +(defun cj/term-copy-mode-up () + "Enter the terminal's copy-mode and move up one step." + (interactive) + (cj/term-copy-mode-move 'up)) + +(defun cj/term-copy-mode-down () + "Enter the terminal's copy-mode and move down one step." + (interactive) + (cj/term-copy-mode-move 'down)) + +(defun cj/term-copy-mode-left () + "Enter the terminal's copy-mode and move left one step." + (interactive) + (cj/term-copy-mode-move 'left)) + +(defun cj/term-copy-mode-right () + "Enter the terminal's copy-mode and move right one step." + (interactive) + (cj/term-copy-mode-move 'right)) + ;; ----------------------------- ghostel package ------------------------------- (defun cj/turn-off-chrome-for-term () @@ -245,16 +302,20 @@ run its own project-named tmux session instead of a bare, auto-named one. ;; `add-to-list' alone updates the list but not the already-built map, so the ;; rebuild is what actually lets the key through to `ghostel-mode-map' / the ;; global map. C-; and F12 are the prefix + toggle; the modified arrows are - ;; windmove (S-arrows, focus) and buffer-move (C-M-arrows, swap), which the - ;; ai-term workflow expects to work from inside an agent buffer. F8 and F10 - ;; are global bindings (org agenda, music-playlist toggle) that reach Emacs by - ;; falling through to the global map once the semi-char map stops forwarding - ;; them. (Server shutdown moved off C-F10 to C-x C, which is deliberately - ;; left forwarding to the terminal program inside an agent buffer.) + ;; windmove (S-arrows, focus), buffer-move (C-M-arrows, swap), and copy-mode + ;; entry (C-arrows and M-arrows, each entering copy-mode and carrying its + ;; direction via `cj/term-copy-mode-up' & friends), which the ai-term workflow + ;; expects to work from inside an agent buffer. F8 and F10 are global + ;; bindings (org agenda, music-playlist toggle) that reach Emacs by falling + ;; through to the global map once the semi-char map stops forwarding them. + ;; (Server shutdown moved off C-F10 to C-x C, which is deliberately left + ;; forwarding to the terminal program inside an agent buffer.) (with-eval-after-load 'ghostel (dolist (key '("C-;" "<f8>" "<f12>" "<f10>" "S-<up>" "S-<down>" "S-<left>" "S-<right>" - "C-M-<up>" "C-M-<down>" "C-M-<left>" "C-M-<right>")) + "C-M-<up>" "C-M-<down>" "C-M-<left>" "C-M-<right>" + "C-<up>" "C-<down>" "C-<left>" "C-<right>" + "M-<up>" "M-<down>" "M-<left>" "M-<right>")) (add-to-list 'ghostel-keymap-exceptions key)) (ghostel--rebuild-semi-char-keymap)) :hook @@ -452,12 +513,22 @@ Forwarding NUL makes C-Space behave like a terminal key." (defun cj/term-install-keys () "Make `C-;' resolve as the personal keymap inside ghostel buffers, bind the -F12 toggle, and forward C-SPC so it reaches the terminal (see -`cj/term-send-C-SPC')." +F12 toggle, forward C-SPC so it reaches the terminal (see +`cj/term-send-C-SPC'), and bind C-/M-<arrow> to enter copy-mode and step in +that direction." (when (boundp 'ghostel-mode-map) (keymap-set ghostel-mode-map "C-;" cj/custom-keymap) (keymap-set ghostel-mode-map "<f12>" #'cj/term-toggle) - (keymap-set ghostel-mode-map "C-SPC" #'cj/term-send-C-SPC))) + (keymap-set ghostel-mode-map "C-SPC" #'cj/term-send-C-SPC) + (dolist (binding '(("C-<up>" . cj/term-copy-mode-up) + ("M-<up>" . cj/term-copy-mode-up) + ("C-<down>" . cj/term-copy-mode-down) + ("M-<down>" . cj/term-copy-mode-down) + ("C-<left>" . cj/term-copy-mode-left) + ("M-<left>" . cj/term-copy-mode-left) + ("C-<right>" . cj/term-copy-mode-right) + ("M-<right>" . cj/term-copy-mode-right))) + (keymap-set ghostel-mode-map (car binding) (cdr binding))))) (cj/term-install-keys) (with-eval-after-load 'ghostel |
