aboutsummaryrefslogtreecommitdiff
path: root/modules/term-config.el
diff options
context:
space:
mode:
Diffstat (limited to 'modules/term-config.el')
-rw-r--r--modules/term-config.el147
1 files changed, 113 insertions, 34 deletions
diff --git a/modules/term-config.el b/modules/term-config.el
index c1c28911d..474a85c42 100644
--- a/modules/term-config.el
+++ b/modules/term-config.el
@@ -59,6 +59,14 @@
(defvar ghostel-mode-map)
(defvar ghostel-keymap-exceptions)
(defvar ghostel-buffer-name)
+(defvar ghostel--input-mode)
+
+;; eat backs the F12 toggle (see the eat package + F12 toggle sections below).
+(declare-function eat "eat" (&optional program arg))
+(defvar eat-buffer-name)
+(defvar eat-mode-map)
+(defvar eat-semi-char-mode-map)
+(defvar cj/custom-keymap)
(defvar-keymap cj/term-map
:doc "Personal terminal command map.")
@@ -206,6 +214,44 @@ start of the line for the same column-0 reason."
(ghostel-copy-mode)
(beginning-of-line)))
+;; ----------------------------- copy-mode scroll ------------------------------
+;;
+;; C-<up> both enters copy-mode and scrolls up one line, so a single stroke
+;; lands in the scrollback already moving the right way. It joins
+;; `ghostel-keymap-exceptions' so it reaches Emacs instead of the pty. Only the
+;; up gesture is bound: C-<left>/<right> are readline word-motion at the shell
+;; prompt and must pass through, and the other directions have no copy-mode use.
+;; Pressed again while already in copy-mode it just moves up -- re-entering would
+;; reset the cursor (tmux's prefix-[ + C-a, or ghostel's toggle exiting).
+
+(defun cj/term--tmux-pane-in-copy-mode-p (pane-id)
+ "Return non-nil when tmux PANE-ID is currently displaying a mode.
+tmux's `pane_in_mode' is 1 while a pane is in any mode; copy-mode is the only
+mode this config enters. tmux failures are treated as nil."
+ (condition-case nil
+ (equal "1" (string-trim
+ (cj/term--tmux-output
+ "display-message" "-p" "-t" pane-id "#{pane_in_mode}")))
+ (error nil)))
+
+(defun cj/term-copy-mode-up ()
+ "Enter copy-mode if needed, then scroll up one line.
+A single C-<up> lands in the terminal's copy-mode already moving up. Pressed
+again while already in copy-mode it just moves up another line, so it never
+re-enters and resets the cursor. In tmux, writes the up-arrow escape sequence
+into the pty; without tmux, moves point up in the `ghostel-copy-mode' buffer."
+ (interactive)
+ (let ((pane (ignore-errors (cj/term--current-tmux-pane-id))))
+ (cond
+ (pane
+ (unless (cj/term--tmux-pane-in-copy-mode-p pane)
+ (cj/term-copy-mode-dwim))
+ (ghostel-send-string "\e[A"))
+ (t
+ (unless (eq (bound-and-true-p ghostel--input-mode) 'copy)
+ (cj/term-copy-mode-dwim))
+ (forward-line -1)))))
+
;; ----------------------------- ghostel package -------------------------------
(defun cj/turn-off-chrome-for-term ()
@@ -245,16 +291,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-<up> only, via `cj/term-copy-mode-up'), which the ai-term workflow
+ ;; expects to work from inside an agent buffer. C-<left>/<right> deliberately
+ ;; stay forwarding so readline word-motion works at the shell prompt. 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>"))
(add-to-list 'ghostel-keymap-exceptions key))
(ghostel--rebuild-semi-char-keymap))
:hook
@@ -271,13 +321,35 @@ run its own project-named tmux session instead of a bare, auto-named one.
;; Byte analog of the prior 100000-line vterm setting (~100 bytes/line) -- D7.
(ghostel-max-scrollback (* 10 1024 1024)))
+;; ------------------------------- eat package ---------------------------------
+;; EAT (pure-elisp terminal) backs the F12 toggle: its whole palette is real
+;; Emacs faces, so it themes from the theme. ghostel stays for ai-term (M-SPC).
+;; No tmux here -- F12's EAT runs a plain $SHELL (decision 2026-06-25).
+
+(use-package eat
+ :ensure t
+ :commands (eat)
+ :hook (eat-mode . cj/turn-off-chrome-for-term)
+ :custom
+ ;; Close the EAT buffer when its shell exits (mirrors ghostel-kill-buffer-on-exit).
+ (eat-kill-buffer-on-exit t)
+ :config
+ ;; F12 and C-; must reach Emacs from inside EAT. In semi-char mode (EAT's
+ ;; default) EAT forwards unbound keys to the terminal -- a letter runs
+ ;; `eat-self-input' -- so bind these explicitly or they never reach Emacs:
+ ;; F12 toggles the terminal window, C-; opens the global prefix map.
+ (keymap-set eat-semi-char-mode-map "<f12>" #'cj/term-toggle)
+ (keymap-set eat-semi-char-mode-map "C-;" cj/custom-keymap)
+ (keymap-set eat-mode-map "<f12>" #'cj/term-toggle)
+ (keymap-set eat-mode-map "C-;" cj/custom-keymap))
+
;; ----------------------- F12 toggle (custom) -----------------------
;;
;; Mirrors the geometry-preservation pattern shared with ai-term.el: capture
;; direction + body size at toggle-off, replay them via a custom display action
;; using frame-edge directions and body-relative sizes so the result is
-;; divider-independent and layout-stable. Excludes agent-prefixed buffers,
-;; which ai-term.el owns via F9.
+;; divider-independent and layout-stable. Manages the EAT terminal only;
+;; ai-term.el's ghostel agent buffers are separate (M-SPC).
(defcustom cj/term-toggle-window-height 0.7
"Default fraction of frame height for the F12 terminal window.
@@ -320,19 +392,18 @@ Positive integer: body-cols (right/left) or total-lines (below/above) -- see
nil means fall back to `cj/term-toggle-window-height' as a fraction.")
(defun cj/--term-toggle-buffer-p (buffer)
- "Return non-nil when BUFFER is a terminal buffer F12 should manage.
+ "Return non-nil when BUFFER is the EAT terminal F12 should manage.
-Qualifies when BUFFER is alive and has `ghostel-mode' (or its name starts with
-the ghostel buffer-name prefix), AND its name does NOT start with the agent
-prefix used by ai-term.el."
+Qualifies when BUFFER is alive and has `eat-mode' (or its name starts with the
+EAT buffer-name prefix). ai-term's ghostel agent buffers never match -- they
+are managed separately via M-SPC, not F12."
(and (bufferp buffer)
(buffer-live-p buffer)
(with-current-buffer buffer
- (and (or (eq major-mode 'ghostel-mode)
- (string-prefix-p (or (bound-and-true-p ghostel-buffer-name)
- "*ghostel*")
- (buffer-name buffer)))
- (not (string-prefix-p "agent [" (buffer-name buffer)))))))
+ (or (eq major-mode 'eat-mode)
+ (string-prefix-p (or (bound-and-true-p eat-buffer-name)
+ "*eat*")
+ (buffer-name buffer))))))
(defun cj/--term-toggle-buffers ()
"Return live F12-managed terminal buffers in `buffer-list' (MRU) order."
@@ -396,18 +467,17 @@ Returns one of:
(t '(create-new))))))))
(defun cj/term-toggle ()
- "Toggle a normal (non-agent) ghostel terminal buffer.
-
-- If an F12-managed terminal is displayed in this frame, capture its geometry
- and delete its window (toggle off). Falls back to burying when it is the
- only window in the frame.
-- Otherwise, if any F12-managed terminal buffer is alive, display the most
- recent one via the saved-geometry action.
-- Otherwise, create a new terminal via `(ghostel)' which routes through the
- same display action.
-
-Excludes agent-prefixed buffers; those have their own F9 dispatch via
-`cj/ai-term'."
+ "Toggle the EAT terminal buffer.
+
+- If the EAT terminal is displayed in this frame, capture its geometry and
+ delete its window (toggle off). Falls back to burying when it is the only
+ window in the frame.
+- Otherwise, if the EAT terminal buffer is alive, display it via the
+ saved-geometry action.
+- Otherwise, create a new EAT terminal, displaying it through the same
+ saved-geometry action.
+
+ai-term's ghostel agent buffers are managed separately via M-SPC, not F12."
(interactive)
(pcase (cj/--term-toggle-dispatch)
(`(toggle-off . ,win)
@@ -422,7 +492,15 @@ Excludes agent-prefixed buffers; those have their own F9 dispatch via
(when w (select-window w)))
buf)
(`(create-new)
- (ghostel))))
+ ;; Create the EAT buffer without stealing the layout, then display it
+ ;; through the saved-geometry dock rule (same path as show-recent).
+ (save-window-excursion (eat))
+ (let ((buf (get-buffer (or (bound-and-true-p eat-buffer-name) "*eat*"))))
+ (when buf
+ (display-buffer buf)
+ (let ((w (get-buffer-window buf)))
+ (when w (select-window w))))
+ buf))))
(keymap-global-set "<f12>" #'cj/term-toggle)
@@ -452,12 +530,13 @@ 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-<up> to enter copy-mode and scroll up."
(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)
+ (keymap-set ghostel-mode-map "C-<up>" #'cj/term-copy-mode-up)))
(cj/term-install-keys)
(with-eval-after-load 'ghostel