]> git.eshelyaron.com Git - emacs.git/commitdiff
; Touch-ups for new window-x.el
authorEshel Yaron <me@eshelyaron.com>
Sat, 11 Jan 2025 12:57:26 +0000 (13:57 +0100)
committerEshel Yaron <me@eshelyaron.com>
Sun, 12 Jan 2025 10:57:51 +0000 (11:57 +0100)
* lisp/window-x.el: Autoload commands, provide feature.
(window-tree-normal-sizes): Improve docstring.
(window--window-to-transpose, window--depmap): Remove.
(window--rotate-interactive-arg): New function.
(rotate-window-layout-anticlockwise): Rename to...
(rotate-window-layout-counterclockwise): ...this.
(rotate-window-layout-clockwise)
(flip-window-layout-horizontally)
(flip-window-layout-vertically, transpose-window-layout)
(rotate-windows-back, rotate-windows, window--transpose)
(window--transpose-1): Cosmetics.

lisp/window-x.el

index d6c1851ddeb1b5e2bf0177a8fcc2a472d3117d7f..82271aa1d4c2289c73e47cd34551b6917c006f25 100644 (file)
@@ -1,4 +1,4 @@
-;;; window-x.el --- extended window commands  -*- lexical-binding: t; -*-
+;;; window-x.el --- Extra window organization commands  -*- lexical-binding: t; -*-
 
 ;; Copyright (C) 2025 Free Software Foundation, Inc.
 
 
 ;;; Commentary:
 
-;; This file defines additional infrequently used window commands that
-;; should not be in window.el to not make the dumped image bigger.
+;; This file defines less frequently used window organization commands.
 
 ;;; Code:
 
 (defun window-tree-normal-sizes (window &optional next)
   "Return normal sizes of all windows rooted at WINDOW.
-A list of the form (SPLIT-TYPE PARENT-WIN PARENT-WIN-HEIGHT
-PARENT-WIN-WIDTH W1 W2 ...) is returned.  SPLIT-TYPE is non-nil if
-PARENT-WIN is split horizontally.  PARENT-WIN is the internal window.
+
+The return value is a list of the form (SPLIT-TYPE PARENT-WIN
+PARENT-WIN-HEIGHT PARENT-WIN-WIDTH . WS), where SPLIT-TYPE is non-nil if
+PARENT-WIN is split horizontally; PARENT-WIN is the internal window;
 PARENT-WIN-HEIGHT and PARENT-WIN-WIDTH are the normal heights of
-PARENT-WIN.  Wn is a list of the form (WINDOW HEIGHT WIDTH) where HEIGHT
-and WIDTH are the normal height and width of the window."
+PARENT-WIN; and WS is a list of lists the form (WINDOW HEIGHT WIDTH)
+where HEIGHT and WIDTH are the normal height and width of the window.
+
+(fn WINDOW)"
   (let (list)
     (while window
       (setq list
@@ -62,192 +64,159 @@ and WIDTH are the normal height and width of the window."
       (setq window (when next (window-next-sibling window))))
     (nreverse list)))
 
-(defun window--window-to-transpose (frame-or-window)
-  "Return the window to be acted upon by `window--transpose'.
-If FRAME-OR-WINDOW is a window return FRAME-OR-WINDOW.  If
-FRAME-OR-WINDOW is a frame, return FRAME-OR-WINDOW's main window.  If
-FRAME-OR-WINDOW is nil, than the frames main window wil be returned.  If
-FRAME-OR-WINDOW is non-nil, and not a frame or a window or a number,
-than the return value will be the parent window of the selected window."
-  (cond
-   ((windowp frame-or-window)
-    frame-or-window)
-   ((or (framep frame-or-window) (not frame-or-window))
-    (window-main-window frame-or-window))
-   (frame-or-window
-    (window-parent))))
-
-(defun rotate-window-layout-anticlockwise (&optional frame-or-window)
-  "Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees.
-Transform the layout of windows such that a window on top becomes a
-window on the right, a window on the right moves to the bottom, a window
-on the bottom moves to the left and a window on the left becomes one on
-the top.
-
-If FRAME-OR-WINDOW is nil, rotate the main window of the selected
-frame.  If FRAME-OR-WINDOW specifies a live frame, rotate the main
-window of that frame.  If FRAME-OR-WINDOW specifies a parent window,
-rotate that window.  In any other case and interactively with a prefix
-argument rotate the parent window of the selected window."
-  (interactive "P")
-  (let ((window (window--window-to-transpose frame-or-window)))
-    (window--transpose window '(right . above) nil)))
-
-(defun rotate-window-layout-clockwise (&optional frame-or-window)
-  "Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
-Transform the layout of windows such that a window on top becomes a
-window on the right, a window on the right moves to the bottom, a
-window on the bottom moves to the left and a window on the left becomes
-one on the top.
-
-If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame.
-If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
-that frame.  If FRAME-OR-WINDOW specifies a parent window, rotate that
-window.  In any other case and interactively with a prefix argument
-rotate the parent window of the selected window."
-  (interactive "P")
-  (let ((window (window--window-to-transpose frame-or-window)))
-    (window--transpose window '(left . below) nil)))
-
-(defun flip-window-layout-horizontally (&optional frame-or-window)
-  "Horizontally flip windows of FRAME-OR-WINDOW.
+(defsubst window--rotate-interactive-arg ()
+  "Return interative window argument for window rotation commands."
+  (if current-prefix-arg (window-parent) (window-main-window)))
+
+;;;###autoload
+(defun rotate-window-layout-counterclockwise (&optional window)
+  "Rotate windows under WINDOW counterclockwise by 90 degrees.
+
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to rotate the parent window of the
+selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (window--transpose window '(right . above) nil))
+
+;;;###autoload
+(defun rotate-window-layout-clockwise (&optional window)
+  "Rotate windows under WINDOW clockwise by 90 degrees.
+
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to rotate the parent window of the
+selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (window--transpose window '(left . below) nil))
+
+;;;###autoload
+(defun flip-window-layout-horizontally (&optional window)
+  "Horizontally flip windows under WINDOW.
+
 Flip the window layout so that the window on the right becomes the
 window on the left, and vice-versa.
 
-If FRAME-OR-WINDOW is nil, flip the main window of the selected frame.
-If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
-that frame.  If FRAME-OR-WINDOW specifies a parent window, rotate that
-window.  In any other case and interactively with a prefix argument
-rotate the parent window of the selected window."
-  (interactive "P")
-  (let ((window (window--window-to-transpose frame-or-window)))
-    (window--transpose window '(below . left) t)))
-
-(defun flip-window-layout-vertically (&optional frame-or-window)
-  "Verticlly flip windows of FRAME-OR-WINDOW.
-Flip the window layout so that the top window becomes the bottom window
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to flip the parent window of the
+selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (window--transpose window '(below . left) t))
+
+;;;###autoload
+(defun flip-window-layout-vertically (&optional window)
+  "Vertically flip windows under WINDOW.
+
+Flip the window layout so that the top window becomes the bottom window,
 and vice-versa.
 
-If FRAME-OR-WINDOW is nil, flip the main window of the selected frame.
-If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
-that frame.  If FRAME-OR-WINDOW specifies a parent window, rotate that
-window.  In any other case and interactively with a prefix argument
-rotate the parent window of the selected window."
-  (interactive "P")
-  (let ((window (window--window-to-transpose frame-or-window)))
-    (window--transpose window '(above . right) t)))
-
-(defun transpose-window-layout (&optional frame-or-window)
-  "Transpose windows of FRAME-OR-WINDOW.
-Make the windows on FRAME-OR-WINDOW so that every horizontal split
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to flip the parent window of the
+selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (window--transpose window '(above . right) t))
+
+;;;###autoload
+(defun transpose-window-layout (&optional window)
+  "Transpose windows under WINDOW.
+
+Reorganize the windows under WINDOW so that every horizontal split
 becomes a vertical split, and vice versa.  This is equivalent to
 diagonally flipping.
 
-If FRAME-OR-WINDOW is nil, transpose the main window of the selected frame.
-If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
-that frame.  If FRAME-OR-WINDOW specifies a parent window, rotate that
-window.  In any other case and interactively with a prefix argument
-rotate the parent window of the selected window."
-  (interactive "P")
-  (let ((window (window--window-to-transpose frame-or-window)))
-    (window--transpose window '(right . below) nil)))
-
-(defun window--depmap(fun ls)
-  "Map FUN across all nodes of list LS."
-  (if (consp ls)
-      (cons
-       (if (consp (car ls))
-         (window--depmap fun (car ls))
-        (funcall fun (car ls)))
-       (window--depmap fun (cdr ls)))
-    (funcall fun ls)))
-
-(defun rotate-windows-back(&optional frame-or-window)
-  "Move windows into locations of their predecessors in cyclic ordering.
-
-If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame.
-If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
-that frame.  If FRAME-OR-WINDOW specifies a parent window, rotate that
-window.  In any other case and interactively with a prefix argument
-rotate the parent window of the selected window."
-  (interactive "P")
-  (rotate-windows frame-or-window t))
-
-(defun rotate-windows (&optional frame-or-window reverse)
-  "Move windows into locations of their forerunners in cyclic ordering.
-
-Else if FRAME-OR-WINDOW is nil, rotate the main window of the
-selected frame.  If FRAME-OR-WINDOW specifies a live frame, rotate the
-main window of that frame.  If FRAME-OR-WINDOW specifies a parent
-window, rotate that window.  In any other case and interactively with a
-prefix argument rotate the parent window of the selected window."
-  (interactive "P")
-  (let ((window (window--window-to-transpose frame-or-window)))
-    (if (or (not window)
-           (window-live-p window))
-       (message "No windows to transpose")
-      (let* ((frame (window-frame window))
-            (selected-window (frame-selected-window window))
-            (win-tree (car (window-tree-normal-sizes window)))
-            (winls (seq-filter 'window-live-p (flatten-list win-tree)))
-            (rotated-ls (if reverse
-                            (append (cdr winls) (list (car winls)))
-                          (append (last winls) winls)))
-            (other-window-arg (if reverse 1 -1))
-            (first-window (car rotated-ls))
-            (new-win-tree (window--depmap
-                           (lambda (x)
-                             (if (window-live-p x)
-                                 (pop rotated-ls)
-                               x))
-                           win-tree)))
-       (if (or (seq-some 'window-atom-root winls)
-               (seq-some 'window-fixed-size-p winls))
-           (message "This does not work with fixed size or atom windows.")
-           (progn
-             ;; All child windows need to be recursively deleted.
-             (delete-other-windows-internal first-window window)
-             ;; (delete-dups atom-windows)
-             (window--transpose-1 new-win-tree first-window '(below . right) t nil)
-             (set-frame-selected-window frame selected-window)
-             (other-window other-window-arg)
-             (while (not (memq (selected-window) winls))
-               (other-window other-window-arg))))))))
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to transpose the parent window of
+the selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (window--transpose window '(right . below) nil))
+
+;;;###autoload
+(defun rotate-windows-back (&optional window)
+  "Rotate windows under WINDOW backward in cyclic ordering.
+
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to rotate the parent window of the
+selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (rotate-windows window t))
+
+;;;###autoload
+(defun rotate-windows (&optional window reverse)
+  "Rotate windows under WINDOW in cyclic ordering.
+
+Optional argument REVERSE says to rotate windows backward, in reverse
+cyclic order.
+
+If WINDOW is nil, it defaults to the root window of the selected frame.
+
+Interactively, a prefix argument says to rotate the parent window of the
+selected window."
+  (interactive (list (window--rotate-interactive-arg)))
+  (when (or (not window) (window-live-p window))
+    (user-error "No windows to transpose"))
+  (let* ((frame (window-frame window))
+        (selected-window (frame-selected-window window))
+        (win-tree (car (window-tree-normal-sizes window)))
+        (winls (seq-filter #'window-live-p (flatten-list win-tree)))
+        (rotated-ls (if reverse
+                        (append (cdr winls) (list (car winls)))
+                      (append (last winls) winls)))
+        (other-window-arg (if reverse 1 -1))
+        (first-window (car rotated-ls))
+        (new-win-tree
+          ;; Recursively process `win-tree' and construct a new tree
+          ;; with the same shape and rotated windows at the leaves.
+          (named-let rec ((tree win-tree))
+            (cond
+             ((consp tree) (cons (rec (car tree)) (rec (cdr tree))))
+             ((window-live-p tree) (pop rotated-ls))
+             (t tree)))))
+    (when (or (seq-some #'window-atom-root winls)
+             (seq-some #'window-fixed-size-p winls))
+      (user-error "Cannot rotate windows due to fixed size or atom windows"))
+    ;; All child windows need to be recursively deleted.
+    (delete-other-windows-internal first-window window)
+    ;; (delete-dups atom-windows)
+    (window--transpose-1 new-win-tree first-window '(below . right) t nil)
+    (set-frame-selected-window frame selected-window)
+    (other-window other-window-arg)
+    (while (not (memq (selected-window) winls))
+      (other-window other-window-arg))))
 
 (defun window--transpose (window conf no-resize)
-  "Rearrange windows of WINDOW recursively.
-CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where
+  "Rearrange windows under WINDOW recursively.
+CONF should be a cons cell (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where
 HORIZONTAL-SPLIT will be used as the third argument of `split-window'
 when splitting a window that was previously horizontally split, and
 VERTICAL-SPLIT as third argument of `split-window' for a window that was
 previously vertically split.  If NO-RESIZE is nil, the SIDE argument of
 the window-split is converted from vertical to horizontal or vice versa,
 with the same proportion of the total split."
-  (if (or (not window)
-         (window-live-p window))
-      (message "No windows to transpose")
-    (let* ((frame (window-frame window))
-          (first-window window)
-          (selected-window (frame-selected-window window))
-          (win-tree (car (window-tree-normal-sizes window)))
-          (win-list (seq-filter 'window-live-p (flatten-list win-tree)))
-          (atom-windows
-           (remq nil (mapcar 'window-atom-root
-                             win-list))))
-      (if (and (not (eq (car atom-windows) window))
-              (or no-resize
-                  (and (not atom-windows)
-                       (not (seq-some 'window-fixed-size-p win-list)))))
-         (progn
-           (delete-dups atom-windows)
-           (while (not (window-live-p first-window))
-             (setq first-window (window-child first-window)))
-           (delete-other-windows-internal first-window window)
-           (window--transpose-1 win-tree first-window conf no-resize atom-windows)
-           ;; Go back to previously selected window.
-           (set-frame-selected-window frame selected-window)
-           (mapc 'window-make-atom atom-windows))
-       (message "This does not work with fixed size or atom windows.")))))
+  (when (or (not window) (window-live-p window))
+    (user-error "No windows to transpose"))
+  (let* ((frame (window-frame window))
+        (first-window window)
+        (selected-window (frame-selected-window window))
+        (win-tree (car (window-tree-normal-sizes window)))
+        (win-list (seq-filter #'window-live-p (flatten-list win-tree)))
+        (atom-windows (seq-keep #'window-atom-root win-list)))
+    (unless (and (not (eq (car atom-windows) window))
+                (or no-resize
+                    (and (not atom-windows)
+                         (not (seq-some #'window-fixed-size-p win-list)))))
+      (user-error "Cannot rotate windows due to fixed size or atom windows"))
+    (delete-dups atom-windows)
+    (while (not (window-live-p first-window))
+      (setq first-window (window-child first-window)))
+    (delete-other-windows-internal first-window window)
+    (window--transpose-1 win-tree first-window conf no-resize atom-windows)
+    ;; Go back to previously selected window.
+    (set-frame-selected-window frame selected-window)
+    (mapc #'window-make-atom atom-windows)))
 
 (defun window--transpose-1 (subtree cwin conf no-resize atom-windows)
   "Subroutine of `window--transpose'.
@@ -259,8 +228,7 @@ ones in `window--transpose'."
   ;; `flen' is max size the window could be converted to the opposite
   ;; of the given split type.
   (let ((parent-window-is-set t)
-       (flen (if (funcall (if no-resize 'not 'identity)
-                          (car subtree))
+       (flen (if (xor no-resize (car subtree))
                  (float (window-pixel-width cwin))
                (float (window-pixel-height cwin)))))
     (mapc
@@ -268,7 +236,7 @@ ones in `window--transpose'."
        (prog1
           (let* ((split-size (- (round (* flen size))))
                  (split-type
-                  (funcall (if (car subtree) 'car 'cdr) conf))
+                  (funcall (if (car subtree) #'car #'cdr) conf))
                  (return-win
                   (if (listp window)
                       ;; `window' is a window subtree.
@@ -293,9 +261,7 @@ ones in `window--transpose'."
                             (if window-combination-limit
                                 (cons (caar (cddddr first-child)) (cadr subtree))
                               (caar (cddddr first-child)))))
-                         (if is-atom
-                             '(nil . t)
-                           conf)
+                         (if is-atom '(nil . t) conf)
                          no-resize
                          atom-windows))
                     ;; `window' is a window.
@@ -323,14 +289,16 @@ ones in `window--transpose'."
                (if (car subtree)
                    (cadr window-size-info)
                  (caddr window-size-info)))))
-      ;; We need to ingore first 5 elements of window list, we ignore
+      ;; We need to ignore first 5 elements of window list, we ignore
       ;; window split type, sizes and the first window (it's
       ;; implicitly created).  We just have a list of windows.
       (nreverse (cdr (cddddr subtree)))))
     ;; (caar (cddddr subtree)) is the first child window of subtree.
     (unless (windowp (caar (cddddr subtree)))
       (let ((is-atom (memq (cadr (cadr (cddddr subtree))) atom-windows)))
-       (window--transpose-1 (car (cddddr subtree)) cwin (if is-atom '(nil . t) conf)
+       (window--transpose-1 (car (cddddr subtree)) cwin
+                             (if is-atom '(nil . t) conf)
                             no-resize atom-windows)))))
 
+(provide 'window-x)
 ;;; window-x.el ends here