From cbd38d881d723f04aab748740977916707f24034 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 25 Jun 2026 22:45:22 -0400 Subject: refactor(term): F12 opens eshell-through-EAT, retire eshell-toggle and xterm-color Point the F12 dock-and-remember toggle at eshell instead of a standalone EAT zsh shell, so the primary terminal is eshell running through EAT (eat-eshell-mode): elisp functions as commands, TRAMP transparency, and EAT rendering visual commands. Drop the eshell-toggle package and its C- binding, since F12 covers it now. Drop xterm-color from eshell, since EAT handles ANSI color natively and its TERM=xterm-256color fought EAT's own. The toggle's buffer predicate now matches eshell-mode; the toggle tests and fixture are updated. --- modules/eat-config.el | 52 ++++++++++++++++---------------- modules/eshell-config.el | 34 +++------------------ tests/test-term-toggle--buffer-filter.el | 42 +++++++++++++------------- tests/testutil-ghostel-buffers.el | 10 ++++++ 4 files changed, 62 insertions(+), 76 deletions(-) diff --git a/modules/eat-config.el b/modules/eat-config.el index d44fbfba3..7f3eab69f 100644 --- a/modules/eat-config.el +++ b/modules/eat-config.el @@ -1,13 +1,18 @@ -;;; eat-config.el --- EAT terminal and the F12 toggle -*- lexical-binding: t; coding: utf-8; -*- +;;; eat-config.el --- EAT terminal emulator and the F12 eshell toggle -*- lexical-binding: t; coding: utf-8; -*- ;;; Commentary: ;; -;; EAT (Emulate A Terminal, pure elisp) is the F12 terminal. Because EAT renders -;; entirely in elisp, its whole palette is real Emacs faces, so it themes from -;; the theme. This module owns the eat package configuration, the keymap wiring -;; that lets F12 and C-; reach Emacs from inside a terminal, and the F12 +;; EAT (Emulate A Terminal, pure elisp) is the terminal emulator. Because EAT +;; renders entirely in elisp, its whole palette is real Emacs faces, so it themes +;; from the theme. This module owns the eat package configuration, the keymap +;; wiring that lets F12 and C-; reach Emacs from inside a terminal, and the F12 ;; dock-and-remember toggle. ;; +;; F12 opens eshell, which runs through EAT (eat-eshell-mode, set up in +;; eshell-config.el): the shell is eshell -- elisp functions as commands, TRAMP +;; transparency -- and EAT renders its visual commands. eshell-config.el holds +;; the shell itself; this module holds the emulator and the toggle. +;; ;; The toggle reuses the geometry-preservation pattern from cj-window-toggle-lib: ;; capture direction + body size at toggle-off, replay them via a custom display ;; action using frame-edge directions and body-relative sizes, so the docked @@ -19,9 +24,10 @@ (require 'cj-window-toggle-lib) (declare-function eat "eat" (&optional program arg)) -(defvar eat-buffer-name) +(declare-function eshell "eshell" (&optional arg)) (defvar eat-mode-map) (defvar eat-semi-char-mode-map) +(defvar eshell-buffer-name) (defvar cj/custom-keymap) (defun cj/turn-off-chrome-for-term () @@ -114,18 +120,14 @@ 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 the EAT terminal F12 should manage. + "Return non-nil when BUFFER is an eshell terminal F12 should manage. -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." +F12 opens eshell, which runs through EAT via eat-eshell-mode. ai-term's ghostel +agent buffers are managed separately via M-SPC, not F12." (and (bufferp buffer) (buffer-live-p buffer) (with-current-buffer buffer - (or (eq major-mode 'eat-mode) - (string-prefix-p (or (bound-and-true-p eat-buffer-name) - "*eat*") - (buffer-name buffer)))))) + (derived-mode-p 'eshell-mode)))) (defun cj/--term-toggle-buffers () "Return live F12-managed terminal buffers in `buffer-list' (MRU) order." @@ -189,17 +191,15 @@ Returns one of: (t '(create-new)))))))) (defun cj/term-toggle () - "Toggle the EAT terminal buffer. + "Toggle the F12 eshell terminal (the primary `*eshell*', run through EAT). -- 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. +- If it 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 it is alive, display it via the saved-geometry action. +- Otherwise, open eshell, displaying it through the same saved-geometry action. -ai-term's ghostel agent buffers are managed separately via M-SPC, not F12." +eshell runs through EAT via eat-eshell-mode, so visual commands render in a real +terminal. ai-term's ghostel agent buffers are managed separately via M-SPC." (interactive) (pcase (cj/--term-toggle-dispatch) (`(toggle-off . ,win) @@ -214,10 +214,10 @@ ai-term's ghostel agent buffers are managed separately via M-SPC, not F12." (when w (select-window w))) buf) (`(create-new) - ;; Create the EAT buffer without stealing the layout, then display it + ;; Open the primary eshell 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*")))) + (save-window-excursion (eshell)) + (let ((buf (get-buffer (or (bound-and-true-p eshell-buffer-name) "*eshell*")))) (when buf (display-buffer buf) (let ((w (get-buffer-window buf))) diff --git a/modules/eshell-config.el b/modules/eshell-config.el index 33b900af6..ac583cf70 100644 --- a/modules/eshell-config.el +++ b/modules/eshell-config.el @@ -188,35 +188,11 @@ pairs where COMMAND is the `cd' string `eshell/alias' should run." (require 'eat) (eat-eshell-mode 1)) -(use-package eshell-toggle - :custom - (eshell-toggle-size-fraction 2) - (eshell-toggle-run-command nil) - (eshell-toggle-init-function #'eshell-toggle-init-eshell) - :bind - ("C-" . eshell-toggle)) - -(use-package xterm-color - :after eshell - ;; Two hooks. eshell-before-prompt is the real hook name; use-package appends - ;; "-hook", so writing eshell-before-prompt-hook here registered on a - ;; nonexistent eshell-before-prompt-hook-hook and never ran. The eshell-mode - ;; hook scopes TERM=xterm-256color to eshell-spawned processes only (a global - ;; setenv would leak it to every start-process regardless of terminal). - :hook - ((eshell-before-prompt . (lambda () - (setq xterm-color-preserve-properties t))) - (eshell-mode . (lambda () - (setq-local process-environment - (cons "TERM=xterm-256color" - process-environment))))) - :config - ;; Wire xterm-color into eshell's output pipeline (per its README): install - ;; the filter and drop eshell's own ANSI handler. Without this the escapes are - ;; never interpreted and TERM=xterm-256color only leaks raw codes. - (add-to-list 'eshell-preoutput-filter-functions 'xterm-color-filter) - (setq eshell-output-filter-functions - (remove 'eshell-handle-ansi-color eshell-output-filter-functions))) +;; eshell-toggle and xterm-color are retired. F12 opens eshell now (the +;; dock-and-remember toggle in eat-config.el), and eat-eshell-mode renders +;; eshell's output through EAT, which handles ANSI color natively -- so +;; xterm-color's filter and its TERM=xterm-256color override are redundant and +;; would fight EAT's own TERM=eat-truecolor. (use-package eshell-syntax-highlighting :after esh-mode diff --git a/tests/test-term-toggle--buffer-filter.el b/tests/test-term-toggle--buffer-filter.el index 4dae0c8d3..6db2ec65c 100644 --- a/tests/test-term-toggle--buffer-filter.el +++ b/tests/test-term-toggle--buffer-filter.el @@ -4,9 +4,9 @@ ;; Three closely-related helpers determine which terminal buffer F12 ;; manages: the predicate `cj/--term-toggle-buffer-p', the list ;; `cj/--term-toggle-buffers', and the per-frame window finder -;; `cj/--term-toggle-displayed-window'. F12 manages the EAT terminal; -;; ghostel buffers (including ai-term's agent buffers) are NOT F12-managed -- -;; they live on M-SPC. +;; `cj/--term-toggle-displayed-window'. F12 opens eshell (run through EAT via +;; eat-eshell-mode), so it manages eshell-mode buffers. Standalone eat buffers, +;; ghostel buffers, and ai-term's agent buffers are NOT F12-managed. ;;; Code: @@ -22,18 +22,18 @@ (cj/test--kill-agent-buffers) (cj/test--kill-test-term-buffers)) -(ert-deftest test-term-toggle--buffer-p-accepts-eat-mode () - "Normal: an eat-mode buffer qualifies as the F12 terminal." +(ert-deftest test-term-toggle--buffer-p-accepts-eshell-mode () + "Normal: an eshell-mode buffer qualifies as the F12 terminal." (test-term-toggle--cleanup) - (let ((buf (cj/test--make-fake-eat-buffer "*test-term-1*"))) + (let ((buf (cj/test--make-fake-eshell-buffer "*test-term-1*"))) (unwind-protect (should (cj/--term-toggle-buffer-p buf)) (kill-buffer buf)))) -(ert-deftest test-term-toggle--buffer-p-rejects-ghostel () - "Boundary: a ghostel buffer is NOT F12-managed (ghostel is ai-term's, M-SPC)." +(ert-deftest test-term-toggle--buffer-p-rejects-eat () + "Boundary: a standalone eat buffer is NOT F12-managed (F12 opens eshell)." (test-term-toggle--cleanup) - (let ((buf (cj/test--make-fake-ghostel-buffer "*test-term-ghostel*"))) + (let ((buf (cj/test--make-fake-eat-buffer "*test-term-eat*"))) (unwind-protect (should-not (cj/--term-toggle-buffer-p buf)) (kill-buffer buf)))) @@ -47,7 +47,7 @@ (kill-buffer buf)))) (ert-deftest test-term-toggle--buffer-p-rejects-non-terminal () - "Boundary: a regular buffer (not eat-mode, no terminal name prefix) -> nil." + "Boundary: a regular buffer (not eshell-mode) -> nil." (test-term-toggle--cleanup) (let ((buf (get-buffer-create "*test-term-regular*"))) (unwind-protect @@ -57,35 +57,35 @@ (ert-deftest test-term-toggle--buffer-p-rejects-dead-buffer () "Boundary: nil and dead buffers -> nil." (should-not (cj/--term-toggle-buffer-p nil)) - (let ((buf (cj/test--make-fake-eat-buffer "*test-term-dead*"))) + (let ((buf (cj/test--make-fake-eshell-buffer "*test-term-dead*"))) (kill-buffer buf) (should-not (cj/--term-toggle-buffer-p buf)))) -(ert-deftest test-term-toggle--buffers-returns-eat-excludes-others () - "Normal: returns the EAT terminal but not ghostel/agent buffers." +(ert-deftest test-term-toggle--buffers-returns-eshell-excludes-others () + "Normal: returns the eshell terminal but not eat/agent buffers." (test-term-toggle--cleanup) - (let ((eat (cj/test--make-fake-eat-buffer "*test-term-eat*")) + (let ((esh (cj/test--make-fake-eshell-buffer "*test-term-esh*")) (agent (cj/test--make-fake-ghostel-buffer "agent [for-test]"))) (unwind-protect (let ((result (cj/--term-toggle-buffers))) - (should (memq eat result)) + (should (memq esh result)) (should-not (memq agent result))) - (kill-buffer eat) + (kill-buffer esh) (kill-buffer agent)))) (ert-deftest test-term-toggle--displayed-window-finds-terminal () - "Normal: the EAT terminal in a window -> returns that window." + "Normal: the eshell terminal in a window -> returns that window." (test-term-toggle--cleanup) - (let ((eat (cj/test--make-fake-eat-buffer "*test-term-shown*"))) + (let ((esh (cj/test--make-fake-eshell-buffer "*test-term-shown*"))) (unwind-protect (save-window-excursion (delete-other-windows) (let ((win (split-window-right))) - (set-window-buffer win eat) + (set-window-buffer win esh) (let ((result (cj/--term-toggle-displayed-window))) (should (windowp result)) - (should (eq (window-buffer result) eat))))) - (kill-buffer eat)))) + (should (eq (window-buffer result) esh))))) + (kill-buffer esh)))) (ert-deftest test-term-toggle--displayed-window-skips-agent () "Boundary: only an agent terminal is displayed -> nil (agent not F12-managed)." diff --git a/tests/testutil-ghostel-buffers.el b/tests/testutil-ghostel-buffers.el index 3c8d75d00..8e26efec4 100644 --- a/tests/testutil-ghostel-buffers.el +++ b/tests/testutil-ghostel-buffers.el @@ -56,5 +56,15 @@ predicate without the side-effects of `(eat)'." (setq-local major-mode 'eat-mode)) buf)) +(defun cj/test--make-fake-eshell-buffer (name) + "Return a buffer named NAME with `major-mode' set to `eshell-mode'. + +Avoids starting a real eshell by setting the mode buffer-locally. Used by the +F12 toggle tests that need a buffer satisfying the eshell-mode predicate." + (let ((buf (get-buffer-create name))) + (with-current-buffer buf + (setq-local major-mode 'eshell-mode)) + buf)) + (provide 'testutil-ghostel-buffers) ;;; testutil-ghostel-buffers.el ends here -- cgit v1.2.3