diff options
| -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]"))) |
