aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/ai-term.el45
-rw-r--r--tests/test-ai-term--reuse-edge-window.el41
2 files changed, 77 insertions, 9 deletions
diff --git a/modules/ai-term.el b/modules/ai-term.el
index 55728a3c4..25e56c508 100644
--- a/modules/ai-term.el
+++ b/modules/ai-term.el
@@ -433,6 +433,18 @@ without deleting), nil when the window was deleted. Consumed by
buried agent in the current window (the only one) or splitting per
the saved direction.")
+(defvar cj/--ai-term-last-toggle-deleted-split nil
+ "Non-nil when the last F9 toggle-off deleted the agent's own split window.
+
+Set t by `cj/--ai-term-toggle-off' only when it actually `delete-window's
+the agent (a multi-window layout where the agent had its own window);
+nil for a bury or a degenerate swap. Consumed by
+`cj/--ai-term-reuse-edge-window': when set, the next toggle-on re-splits a
+fresh agent window instead of reusing a window at the edge. Without this,
+toggling the agent off and on in a 3+ window layout would reuse the user's
+working window at the edge, displacing its buffer and collapsing the layout
+-- the toggle must be reversible (off then on returns the same windows).")
+
(defvar cj/--ai-term-last-hidden-buffer nil
"The agent buffer hidden by the most recent F9 toggle-off.
@@ -534,14 +546,22 @@ displaced buffer and the agent, never changing the window count.
Runs after `cj/--ai-term-reuse-existing-agent', so an agent already on
screen has been handled already; the window reused here always holds a
-non-agent buffer, which is replaced (it stays alive, just unshown)."
- (let* ((direction (or cj/--ai-term-last-direction
- (cj/--ai-term-default-direction)))
- (win (cj/window-at-edge direction)))
- (when (and win (not (window-dedicated-p win)))
- (display-buffer-record-window 'reuse win buffer)
- (set-window-buffer win buffer)
- win)))
+non-agent buffer, which is replaced (it stays alive, just unshown).
+
+Skipped entirely when the prior toggle-off deleted the agent's own split
+window (`cj/--ai-term-last-toggle-deleted-split'): re-showing then reuses a
+working window at the edge and collapses the layout. Consume the flag and
+return nil so `cj/--ai-term-display-saved' re-splits a fresh agent window,
+keeping the toggle reversible."
+ (if cj/--ai-term-last-toggle-deleted-split
+ (progn (setq cj/--ai-term-last-toggle-deleted-split nil) nil)
+ (let* ((direction (or cj/--ai-term-last-direction
+ (cj/--ai-term-default-direction)))
+ (win (cj/window-at-edge direction)))
+ (when (and win (not (window-dedicated-p win)))
+ (display-buffer-record-window 'reuse win buffer)
+ (set-window-buffer win buffer)
+ win))))
(defun cj/--ai-term-display-saved (buffer alist)
"Display-buffer action: split per saved direction and size.
@@ -824,6 +844,7 @@ Two cases, by window count:
((one-window-p)
(cj/--ai-term-capture-state win)
(setq cj/--ai-term-last-was-bury t)
+ (setq cj/--ai-term-last-toggle-deleted-split nil)
(bury-buffer (window-buffer win))
(when (and (window-live-p win)
(cj/--ai-term-buffer-p (window-buffer win)))
@@ -833,9 +854,15 @@ Two cases, by window count:
(setq cj/--ai-term-last-was-bury nil)
(if (and (window-live-p win)
(> (length (window-list (window-frame win) 'never)) 1))
- (delete-window win)
+ (progn
+ (delete-window win)
+ ;; The agent had its own window in a multi-window layout, now gone:
+ ;; the next toggle-on must re-split it rather than reuse a working
+ ;; window at the edge (see `cj/--ai-term-reuse-edge-window').
+ (setq cj/--ai-term-last-toggle-deleted-split t))
;; Degenerate fallback (window became sole between dispatch and
;; here): swap to a non-agent buffer rather than leave the agent up.
+ (setq cj/--ai-term-last-toggle-deleted-split nil)
(when (window-live-p win)
(cj/--ai-term-swap-to-working-buffer win)))))
nil)
diff --git a/tests/test-ai-term--reuse-edge-window.el b/tests/test-ai-term--reuse-edge-window.el
index f6259ae50..a9a0529e8 100644
--- a/tests/test-ai-term--reuse-edge-window.el
+++ b/tests/test-ai-term--reuse-edge-window.el
@@ -269,5 +269,46 @@ most-recent agent, which would now be the other one."
(when (get-buffer right-name) (kill-buffer right-name))
(cj/test--kill-agent-buffers))))
+(ert-deftest test-ai-term--reuse-edge-window-3win-toggle-restores-own-window ()
+ "Regression: in a 3-window layout the agent has its own split, so toggling it
+off then on restores it as its own window without displacing a working window.
+Before the fix, toggle-on reused the bottom edge (the user's main window),
+collapsing three windows to two and hiding the main buffer. A toggle must be
+reversible: off then on returns to the same layout."
+ (cj/test--kill-agent-buffers)
+ (let ((agent-name "agent [3win-toggle]")
+ (code-name "*test-3win-code*")
+ (main-name "*test-3win-main*")
+ (cj/--ai-term-last-direction nil)
+ (cj/--ai-term-last-size nil)
+ (cj/--ai-term-last-was-bury nil))
+ (unwind-protect
+ (save-window-excursion
+ (delete-other-windows)
+ (cl-letf (((symbol-function 'cj/--ai-term-default-direction) (lambda (&rest _) 'below)))
+ (let ((code-buf (get-buffer-create code-name))
+ (main-buf (get-buffer-create main-name))
+ (agent-buf (get-buffer-create agent-name)))
+ (set-window-buffer (selected-window) code-buf)
+ (let* ((main-win (split-window (selected-window) nil 'below))
+ (agent-win (split-window main-win nil 'below)))
+ (set-window-buffer main-win main-buf)
+ (set-window-buffer agent-win agent-buf)
+ (should (= (count-windows) 3))
+ (let ((display-buffer-alist (cj/--ai-term-display-rule-list)))
+ (select-window agent-win)
+ (cj/test--call-as-gui #'cj/ai-term) ; off -> code | main
+ (should (= (count-windows) 2))
+ (should-not (member agent-name (cj/test--displayed-buffer-names)))
+ (cj/test--call-as-gui #'cj/ai-term) ; on -> back to 3 windows
+ (should (= (count-windows) 3))
+ (let ((bufs (cj/test--displayed-buffer-names)))
+ (should (member agent-name bufs))
+ (should (member code-name bufs))
+ (should (member main-name bufs))))))))
+ (when (get-buffer code-name) (kill-buffer code-name))
+ (when (get-buffer main-name) (kill-buffer main-name))
+ (cj/test--kill-agent-buffers))))
+
(provide 'test-ai-term--reuse-edge-window)
;;; test-ai-term--reuse-edge-window.el ends here