diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-06 18:12:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-06 18:12:34 -0500 |
| commit | 1a097b7e45e958f5849098689c27955ce335a8f1 (patch) | |
| tree | b5b1802487c5884a19a71794c758f436bf627dd0 | |
| parent | 57df2015f93d54db60a8765868ba9c5fc70b4a65 (diff) | |
| download | dotemacs-1a097b7e45e958f5849098689c27955ce335a8f1.tar.gz dotemacs-1a097b7e45e958f5849098689c27955ce335a8f1.zip | |
fix(ai-term): keep the window split when closing an agent
M-F9 close deleted the agent's window, collapsing the layout. Close now swaps that window to the working buffer and kills the agent buffer, so the split stays put. F9 hide still collapses the split. Close no longer does.
| -rw-r--r-- | modules/ai-term.el | 21 | ||||
| -rw-r--r-- | tests/test-ai-term--close.el | 31 |
2 files changed, 45 insertions, 7 deletions
diff --git a/modules/ai-term.el b/modules/ai-term.el index 1384f812..baf752fe 100644 --- a/modules/ai-term.el +++ b/modules/ai-term.el @@ -54,8 +54,10 @@ ;; instead of toggling the current one. ;; - M-F9 `cj/ai-term-close' -- gracefully close an agent: kill its ;; tmux session (stopping the agent process), then its terminal -;; buffer and window. Confirms first. Targets the current -;; agent, the sole live agent, or prompts among several. +;; buffer. Its window stays in the layout (swapped to the +;; working buffer), so closing never collapses a split. Confirms +;; first. Targets the current agent, the sole live agent, or +;; prompts among several. ;; - C-S-F9 `cj/ai-term-close' -- same close command, second binding. ;; (M-F9 is the primary; C-S-F9 may be swallowed by the ;; Wayland/PGTK layer on some machines.) @@ -859,12 +861,14 @@ down." (error nil))) (defun cj/--ai-term-close-buffer (buffer) - "Gracefully tear down AI-term BUFFER: tmux session, window, buffer. + "Gracefully tear down AI-term BUFFER: tmux session, then buffer. Derives the tmux session name from BUFFER's `default-directory' (the project dir the terminal was created in) and kills it so the agent -process stops. Deletes BUFFER's window when it's shown and isn't the -only window in its frame, then kills BUFFER (suppressing the +process stops. When BUFFER is shown, swaps its window to a non-agent +buffer (the working file) rather than deleting the window -- closing an +agent must not collapse the user's window layout; the F9 hide toggle is +what collapses the split. Then kills BUFFER (suppressing the process-still-running prompt -- the session is already down). No-op when BUFFER isn't an AI-term buffer." (when (cj/--ai-term-buffer-p buffer) @@ -872,8 +876,11 @@ when BUFFER isn't an AI-term buffer." (cj/--ai-term-tmux-session-name (buffer-local-value 'default-directory buffer))) (let ((win (get-buffer-window buffer))) - (when (and win (> (length (window-list (window-frame win) 'never)) 1)) - (delete-window win))) + (when (window-live-p win) + (with-selected-window win + (switch-to-buffer + (or (cj/--ai-term-most-recent-non-agent-buffer) + (other-buffer buffer t)))))) (let ((kill-buffer-query-functions nil)) (kill-buffer buffer)))) diff --git a/tests/test-ai-term--close.el b/tests/test-ai-term--close.el index 654e85f0..4098c091 100644 --- a/tests/test-ai-term--close.el +++ b/tests/test-ai-term--close.el @@ -13,7 +13,9 @@ (require 'cl-lib) (add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) (require 'ai-term) +(require 'testutil-ghostel-buffers) (ert-deftest test-ai-term--kill-tmux-session-runs-kill-session () "Normal: invokes `tmux kill-session -t <session>'." @@ -58,6 +60,35 @@ (should (buffer-live-p buf))) (when (buffer-live-p buf) (kill-buffer buf))))) +(ert-deftest test-ai-term--close-buffer-keeps-window-split () + "Regression: closing an agent in a split keeps its window in the layout, +showing a non-agent buffer, instead of deleting the split. Craig's M-F9 +annoyance -- a close must not tear down the window arrangement (the F9 hide +toggle is what collapses the split; close should not)." + (cj/test--kill-agent-buffers) + (let ((work (get-buffer-create "*test-close-keep-work*")) + (agent (get-buffer-create "agent [close-keep]"))) + (with-current-buffer agent (setq-local default-directory "/tmp/close-keep/")) + (unwind-protect + (save-window-excursion + (delete-other-windows) + (set-window-buffer (selected-window) work) + (let ((agent-win (split-window (selected-window) nil 'below))) + (set-window-buffer agent-win agent) + (should-not (one-window-p)) + (cl-letf (((symbol-function 'cj/--ai-term-kill-tmux-session) + (lambda (_s) 0))) + (cj/--ai-term-close-buffer agent)) + ;; The window survives the close ... + (should (window-live-p agent-win)) + (should-not (one-window-p)) + ;; ... now showing a non-agent buffer ... + (should-not (cj/--ai-term-buffer-p (window-buffer agent-win))) + ;; ... and the agent buffer itself is gone. + (should-not (buffer-live-p agent)))) + (when (get-buffer "*test-close-keep-work*") (kill-buffer "*test-close-keep-work*")) + (cj/test--kill-agent-buffers)))) + (ert-deftest test-ai-term--close-target-current-agent-buffer () "Normal: returns the current buffer when it is an agent buffer." (let ((buf (get-buffer-create "agent [cur]"))) |
