aboutsummaryrefslogtreecommitdiff
path: root/modules/cj-window-geometry-lib.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-25 04:10:38 -0500
committerCraig Jennings <c@cjennings.net>2026-05-25 04:10:38 -0500
commit3f75b39bbbc4e1c136d3f786024c5c1ed19011ce (patch)
treec03cfe18f7a067cae026db400e757cbd5d053d35 /modules/cj-window-geometry-lib.el
parent08014b2f15e099a1c5e662a17a41290f37aeebf4 (diff)
downloaddotemacs-3f75b39bbbc4e1c136d3f786024c5c1ed19011ce.tar.gz
dotemacs-3f75b39bbbc4e1c136d3f786024c5c1ed19011ce.zip
fix(ai-vterm): reuse the frame's half instead of splitting a third
F9 split a third window into a frame that was already divided in two, wedging the agent into the middle or a skinny extra column instead of taking the half it should occupy. The display rule only knew how to reuse a window already showing an agent or to split a fresh one. With a plain two-pane layout it fell through to the split and added a window. I added a display action, cj/--ai-vterm-reuse-edge-window, that reuses the window already forming the target half (the right column on a desktop, the bottom row on a laptop), found by a new cj/window-at-edge helper. It records the displaced buffer with display-buffer-record-window, so toggling off restores that buffer through the native quit-restore-window. The slot's buffer swaps between the agent and whatever it displaced, and no window is created or deleted. The split path still handles a single-window frame or a layout split on the other axis, and the lone fullscreen agent keeps its bury-and-restore-in-place behavior.
Diffstat (limited to 'modules/cj-window-geometry-lib.el')
-rw-r--r--modules/cj-window-geometry-lib.el35
1 files changed, 35 insertions, 0 deletions
diff --git a/modules/cj-window-geometry-lib.el b/modules/cj-window-geometry-lib.el
index a674a072..1c1ab9fd 100644
--- a/modules/cj-window-geometry-lib.el
+++ b/modules/cj-window-geometry-lib.el
@@ -77,5 +77,40 @@ layouts."
('above 'top)
(_ nil)))
+(defun cj/window-at-edge (direction &optional frame)
+ "Return the window forming FRAME's half on the side named by DIRECTION.
+
+DIRECTION is one of right, left, below, above. A window qualifies as
+the half when it sits flush against that frame edge and spans the full
+perpendicular extent: for right or left it is a full-height side column
+(nothing above, below, or further toward the edge, but a sibling on the
+far side); for below or above it is a full-width row. FRAME defaults to
+the selected frame.
+
+Existence is tested with `window-in-direction' rather than raw edge
+arithmetic -- frame-internal geometry makes the root window's edges
+disagree with its children's by a row, so an `=' comparison against the
+root edges never matches. Asking \"is there a window on each side?\"
+sidesteps that.
+
+Returns nil when no window qualifies: a single-window frame (no sibling
+on the far side, so not a distinct half), an axis mismatch (a top/bottom
+split when DIRECTION is right has no full-height right column), or a
+nested edge no one window fills. The caller then falls back to splitting
+a fresh half."
+ (when (memq direction '(right left below above))
+ (let ((far (pcase direction
+ ('right 'left) ('left 'right) ('below 'above) ('above 'below)))
+ (perp (pcase direction
+ ((or 'right 'left) '(above below))
+ ((or 'below 'above) '(left right)))))
+ (seq-find
+ (lambda (w)
+ (and (window-in-direction far w)
+ (not (window-in-direction direction w))
+ (not (window-in-direction (nth 0 perp) w))
+ (not (window-in-direction (nth 1 perp) w))))
+ (window-list (or frame (selected-frame)) 'never)))))
+
(provide 'cj-window-geometry-lib)
;;; cj-window-geometry-lib.el ends here