unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: martin rudalics <rudalics@gmx.at>
To: Pranshu Sharma <pranshusharma366@gmail.com>
Cc: Juri Linkov <juri@linkov.net>, Eli Zaretskii <eliz@gnu.org>,
	emacs-devel@gnu.org
Subject: Re: Add function to rotate/transpose all windows
Date: Tue, 5 Nov 2024 19:01:23 +0100	[thread overview]
Message-ID: <ef202491-5abf-48f4-bce3-5b81998e50c7@gmx.at> (raw)
In-Reply-To: <87ses9k9wo.fsf@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3939 bytes --]

 > This is the top-down way, where say you have the tree
 > (a (b (c d) e))
 > You go like
 > (a) -> (a (b)) -> (a (b c)) -> (a (b (c d)))...
 > the way this one works is bottom up, kind of like:
 > (a) -> (a (c d)) -> (a (b (c d)))...

I don't see that here - I assume that you made the configuration via
something like

C-x 3 C-x o C-x 2 C-x 2 C-x o C-x 3

or its orthogonal variant.  With your approach I do not see (a (c d))
after the second split but (a (b c)) just like with mine.  AFAICT your
algorithm performs exactly the same sequence of splits as mine.  You can
verify my claim by putting a 'y-or-n-p' after each 'split-window' call
(in the version I attached they are commented out in the code), making
two frames on your screen and stepping through your version in one and
mine in the other window.  You will see that they split windows in
exactly the same order.

I also noted that with

C-x 3 C-x o C-x 2 C-x 3 C-x o C-x 2

my and your last 'split-window' call split a parent window - something
you earlier told me that you didn't want to do.

The following is the central idea of my approach.  Suppose F is a first
live window (initially the 'frame-first-window') - a live window which
has no previous sibling in the window tree.  And suppose P is the parent
of F in the window tree that has already been deleted, all relationships
are from the tree before it has been deleted.  'delete-other-windows'
now deletes all windows but F.

   P
  /
F   P1    P2 ... Pn
    /     /      /
   /     /      /
  F1    F2     Fn

Now I take the next sibling P1 of F - I assume it's a parent window
because that's the hard case.  I find the first live descendant of P1
which is F1 (any parent window invariably has such a descendant).  My
first split is now

(split-window F .. (F1 . P))

because I see that F has not yet its valid parent P.  This resurrects F1
and makes F and F1 the children of the resurrected P.  If F already had
its valid parent P, I would have called

(split-window F .. F1)

and P would have got the additional child F1.  Now I recurse with F1
taking over the part of F.  After I'm done with the entire subtree of
P1, I do

(split-window P1 .. F2)

so P gets a new child window F2 and continue with F2 taking over the
part of F1 and F.  After I'm done with the subtree of Pn, I continue
with P's next sibling and go up until I arrive at the root of the window
tree.

Note here: Initially F's parent is nil.  When I split F to resurrect F1
both get the new parent P.  P is already the final parent of F but not
of F1.  When I later split F1 it will get its final parent as well.  In
neither of these splits, 'split-window' has any idea which parent window
to use.  It could try to find one among the dead windows the one whose
first child is F (or F1) but I haven't implemented such a mechanism and
I'm not sure whether it's worth the hassle.  That's why I have the
rotation routine initially remember the old parents of all windows and
pass them to 'split-window' as cdr of the REFER argument.

Note also that I always apply the new size to the window I split.  This
means that when I split F then F1 initially gets the entire remainder of
the size of P.  It also means that when I later split P1, 'split-window'
has to recursively resize the entire tree rooted at P1 and F2 gets the
rest of the size of P - that of P minus that of F and P1.

It might be interesting to try the following variant: Instead of
recursing immediately into F1, first split all siblings of F so that one
has a layout of

F  F1  F2  ... Fn

and only now recursively process F1, F2 ... Fn.  This would have the
advantage that when splitting F1 to fit in the entire subtree rooted at
P1, its size is already the final size.  And we might not have to split
parent windows either.  But such a solution will be less trivial to
code.

Attached find my latest diffs (the documentation has not been completed
yet) and window-rotate.el.

martin

[-- Attachment #2: split-window.diff --]
[-- Type: text/x-patch, Size: 37781 bytes --]

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 0b8d7d3b76d..e67fc256b3d 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -22,6 +22,7 @@ Windows
 * Deleting Windows::        Removing a window from its frame.
 * Recombining Windows::     Preserving the frame layout when splitting and
                               deleting windows.
+* Resurrecting deleted windows:: Restoring indivdual windows.
 * Cyclic Window Ordering::  Moving around the existing windows.
 * Buffers and Windows::     Each window displays the contents of a buffer.
 * Switching Buffers::       Higher-level functions for switching to a buffer.
@@ -1355,7 +1356,7 @@ Splitting Windows
 Examples of such windows are side windows (@pxref{Side Windows}) and
 atomic windows (@pxref{Atomic Windows}).
 
-@defun split-window &optional window size side pixelwise
+@defun split-window &optional window size side pixelwise refer
 This function creates a new live window next to the window
 @var{window}.  If @var{window} is omitted or @code{nil}, it defaults
 to the selected window.  That window is split, and reduced in
@@ -1364,7 +1365,7 @@ Splitting Windows
 The optional second argument @var{size} determines the sizes of
 @var{window} and/or the new window.  If it is omitted or @code{nil},
 both windows are given equal sizes; if there is an odd line, it is
-allocated to the new window.  If @var{size} is a positive number,
+allotted to the new window.  If @var{size} is a positive number,
 @var{window} is given @var{size} lines (or columns, depending on the
 value of @var{side}).  If @var{size} is a negative number, the new
 window is given @minus{}@var{size} lines (or columns).
@@ -1374,13 +1375,13 @@ Splitting Windows
 Sizes}).  Thus, it signals an error if splitting would result in making
 a window smaller than those variables specify.  However, a
 non-@code{nil} value for @var{size} causes those variables to be
-ignored; in that case, the smallest allowable window is considered to be
-one that has space for a text that is one line tall and/or two columns
-wide.
+ignored; in that case, the smallest allowable sizes are determined by
+the values of @code{window-safe-min-height} and
+@code{window-safe-min-width}.
 
 Hence, if @var{size} is specified, it's the caller's responsibility to
-check whether the emanating windows are large enough to encompass all of
-their decorations like a mode line or a scroll bar.  The function
+check whether the emanating windows are large enough to encompass all
+areas like a mode line or a scroll bar.  The function
 @code{window-min-size} (@pxref{Window Sizes}) can be used to determine
 the minimum requirements of @var{window} in this regard.  Since the new
 window usually inherits areas like the mode line or the scroll bar from
@@ -1399,14 +1400,62 @@ Splitting Windows
 window is placed on the left of @var{window}.  In both these cases,
 @var{size} specifies a total window width, in columns.
 
+As a rule, if @var{window} already forms a combination (@pxref{Windows
+and Frames}) that matches @var{side} (a horizontal combination matches
+@var{side} if it is @code{left} or @code{right}, a vertical combination
+matches @var{side} if it is @code{above} or @code{below}) and
+@code{window-combination-limit} (@pxref{Recombining Windows}) is
+@code{nil}, this function reuses @var{window}'s parent in the window
+tree as parent of the new window.
+
+However, if @var{window} is in a combination that does not match
+@var{side} or if @code{window-combination-limit} is non-@code{nil}, this
+function makes a new parent window that replaces @var{window} in the
+window tree and makes @var{window} and the new window its sole child
+windows.  This standard behavior can be overridden via the @var{refer}
+argument.
+
 The optional fourth argument @var{pixelwise}, if non-@code{nil}, means
 to interpret @var{size} in units of pixels, instead of lines and
 columns.
 
-If @var{window} is a live window, the new window inherits various
-properties from it, including margins and scroll bars.  If
-@var{window} is an internal window, the new window inherits the
-properties of the window selected within @var{window}'s frame.
+If the optional fifth argument @var{refer} is non-@code{nil}, it
+specifies a reference window used for setting up properties of the new
+window.  If non-@code{nil}, @var{refer} can be either a window or a cons
+cell of two windows.
+
+If @var{refer} is a cons cell, its @sc{car} has to specify a deleted,
+former live window - a window that has shown a buffer before - on the
+same frame as @var{window}.  That buffer must be still live.  The
+@sc{cdr} has to specify a deleted window that was, before its deletion,
+a parent window on the same frame as @var{window}.  In this case, rather
+then making new windows, this function replaces @var{window} with the
+@sc{cdr} of @var{refer} in the window tree and makes @var{window} and
+@var{refer}'s @sc{car} its new child windows.  Buffer, start and point
+positions of @var{refer}'s @sc{car} are set to the values they had
+immediately before @var{refer}'s @sc{car} was deleted the last time.
+Decorations and parameters remain unaltered from their values before
+@var{refer}'s @sc{car} and @sc{cdr} were deleted.
+
+Alternatively, @var{refer} may specify a deleted, former live window - a
+window that has shown a buffer before - on the same frame as
+@var{window}.  That buffer must be still live.  In this case, this
+function do not make a new window but rather makes @var{refer} live
+again and inserts it into the window tree at the position and with the
+sizes the new window would have been given.  Buffer, start and point
+positions of @var{refer} are set to the values they had immediately
+before @var{refer} was deleted the last time.  Decorations and
+parameters remain unaltered from their values before @var{refer} was
+deleted.  The parent of @var{refer} is then determined as if it were a
+window created anew.
+
+Otherwise, @var{refer} must specify a live window.  In this case, the
+new window will inherit properties like buffer, start and point
+positions and some decorations from @var{refer}.  If @var{refer} is
+@code{nil} or omitted, then if @var{window} is live, any such properties
+are inherited from @var{window}.  If, however, @var{window} is an
+internal window, the new window will inherit these properties from the
+window selected on @var{window}'s frame.
 
 The behavior of this function may be altered by the window parameters
 of @var{window}, so long as the variable
@@ -2048,6 +2097,157 @@ Recombining Windows
 windows.
 
 
+@node Resurrecting deleted windows
+@section Resurrecting deleted windows
+@cindex resurrecting deleted windows
+
+After a window has been deleted (@pxref{Deleting Windows}) it cannot be
+used any more by functions that require a valid window as their argument
+even if some Lisp variable still references that window.  When the last
+reference to a window has ceased to exist, the window's Lisp object will
+be eventually recycled by the garbage collector.
+
+There are two ways to resurrect a deleted window whose object has not
+been yet recycled by the collector: The first is to keep a reference to
+that window in a saved window configuration (@pxref{Window
+Configurations}) and then call @code{set-window-configuration} with that
+configuration as argument.  The second one is to keep a reference to
+that window in a variable or let-bind it and then use that reference as
+@var{refer} argument in @code{split-window} (@pxref{Splitting Windows}).
+
+The major difference between these two is that
+@code{set-window-configuration} restores the frame layout that existed
+before deleting the window.  The @code{split-window} approach, on the
+other hand, allows for arbitrary variations of the layout.
+
+Consider the following example starting with a frame containing a single
+window showing the buffer @file{*scratch*}:
+
+@example
+@group
+(let* ((old (selected-window))
+       (new (split-window old nil 'right))
+       overlay)
+  (with-current-buffer (get-buffer-create "*Messages*")
+    (set-window-buffer new (current-buffer))
+    (setq overlay (make-overlay (point-min) (point-max)))
+    (overlay-put overlay 'face 'highlight)
+    (overlay-put overlay 'window new)
+    (message "new %s parent %s" new (window-parent new))
+    (sit-for 3))
+  (delete-window new)
+  (setq new (split-window old nil 'left))
+  (set-window-buffer new (get-buffer-create "*Messages*"))
+  (format "new %s parent %s" new (window-parent new)))
+@end group
+@end example
+
+When you run that code in @file{*scratch*} it will first split the
+window showing @file{*scratch*} to display @file{*Messages*} in a new
+window on the right.  It also sets up an overlay with a window property
+to highlight the text of @file{*Messages*} in the new window and
+displays a message showing the new window and its parent in the window
+tree.  It then deletes the new window and resurrects it on the left of
+the @file{*scratch*} window again displaying a message showing the new
+window and its parent in the window tree.
+
+Note that both, new window and its parent have changed after moving the
+@file{*Messages*} window to the left.  Also, the highlighting disappears
+because any properties set up for the new window on the right are lost
+when the new window is re-created on the left.
+
+The following code uses the @var{refer} argument of @code{split-window}
+instead.
+
+@example
+@group
+(let* ((old (selected-window))
+       (new (split-window old nil 'right))
+       overlay)
+  (with-current-buffer (get-buffer-create "*Messages*")
+    (set-window-buffer new (current-buffer))
+    (setq overlay (make-overlay (point-min) (point-max)))
+    (overlay-put overlay 'face 'highlight)
+    (overlay-put overlay 'window new)
+    (message "new %s parent %s" new (window-parent new))
+    (sit-for 3))
+  (delete-window new)
+  (split-window old nil 'left nil new)
+  (format "new %s parent %s" new (window-parent new)))
+@end group
+@end example
+
+Note that all properties of the resurrected window like its decorations,
+parameters as well as any overlays with a window property are preserved
+as if that window had never been deleted.  The only things that changed
+are its position in the window tree and consequently the values returned
+by @code{window-left-child} of its parent window as well as the values
+returned by @code{window-prev-sibling} and @code{window-next-sibling} of
+the window and its sibling.
+
+The following code passes both, the new window on the right and its
+parent, via the @var{refer} argument to @code{split-window}: instead.
+
+@example
+@group
+(let* ((old (selected-window))
+       (new (split-window old nil 'right))
+       (parent (window-parent new))
+       overlay)
+  (with-current-buffer (get-buffer-create "*Messages*")
+    (set-window-buffer new (current-buffer))
+    (setq overlay (make-overlay (point-min) (point-max)))
+    (overlay-put overlay 'face 'highlight)
+    (overlay-put overlay 'window new)
+    (message "new %s parent %s" new (window-parent new))
+    (sit-for 3))
+  (delete-window new)
+  (split-window old nil 'left nil (cons new parent))
+  (format "new %s parent %s" new (window-parent new)))
+@end group
+@end example
+
+Note that the parent window has been resurrected along with the new
+window.
+
+Resurrecting dead windows is useful to preserve the identity of windows
+in actions that are supposed to do that like moving windows around on a
+frame or hiding them temporarily.  Any properties of such a window like
+its decorations, the buffer it has shown previously, that buffer's start
+and point position in the window, the window's dedicated status, its
+cursor type are left untouched and there's no need to recreate them from
+scratch.  For internal windows, the value of that window's combination
+limit is preerved which means that the window can be recombined
+(@pxref{Recombining Windows}) as before.
+
+Due to certain limitations in the way windows can be split, making a
+sequence of changes to the window structure can be more tricky.  It's
+still fairly simple to rotate three windows as follows:
+
+@example
+@group
+(let* ((old (selected-window))
+       (new-1 (split-window old nil 'right))
+       (parent-1 (window-parent old))
+       (new-2 (split-window new-1 nil 'below))
+       (parent-2 (window-parent new-2))
+       new)
+  (message "old %s (%s) .. new-1 %s (%s) .. new-2 %s (%s)"
+	   old (window-parent old)
+	   new-1 (window-parent new-1)
+	   new-2 (window-parent new-2))
+  (sit-for 3)
+  (delete-other-windows old)
+  (setq new (split-window old nil 'below nil (cons new-1 parent-1)))
+  (split-window new nil 'right nil (cons new-2 parent-2))
+  (format "old %s (%s) .. new-1 %s (%s) .. new-2 %s (%s)"
+	  old (window-parent old)
+	  new-1 (window-parent new-1)
+	  new-2 (window-parent new-2)))
+@end group
+@end example
+
+
 @node Cyclic Window Ordering
 @section Cyclic Ordering of Windows
 @cindex cyclic ordering of windows
@@ -7126,13 +7326,16 @@ Window Hooks
 window.
 
 @defun window-old-buffer &optional window
-This function returns the buffer shown in @var{window} at the last
-time window change functions were run for @var{window}'s frame.  If it
-returns @code{nil}, @var{window} has been created after that.  If it
-returns @code{t}, @var{window} was not shown at that time but has been
-restored from a previously saved window configuration afterwards.
-Otherwise, the return value is the buffer shown by @var{window} at
-that time.
+This function returns the buffer shown in @var{window} at the last time
+window change functions were run for @var{window}'s frame.  If it
+returns @code{nil}, @var{window} is either an internal window or has
+been created after that.  If it returns @code{t}, @var{window} was not
+shown at that time but has been restored from a previously saved window
+configuration afterwards.  Otherwise, the return value is the buffer
+shown by @var{window} at that time.  As a special case, if @var{window}
+has been deleted, this function returns the last buffer @var{window} had
+shown at that time.  @var{window} can be any window and defaults to the
+selected one.
 @end defun
 
 @defun window-old-pixel-width &optional window
diff --git a/lisp/window.el b/lisp/window.el
index b50770cbd7e..060716df936 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5511,54 +5511,95 @@ window--combination-resizable
       (setq sibling (window-next-sibling sibling)))
     (/ size (1+ number))))
 
-(defun split-window (&optional window size side pixelwise)
+(defun split-window (&optional window size side pixelwise refer)
   "Make a new window adjacent to WINDOW.
 WINDOW must be a valid window and defaults to the selected one.
 Return the new window which is always a live window.
 
-Optional argument SIZE a positive number means make WINDOW SIZE
-lines or columns tall.  If SIZE is negative, make the new window
--SIZE lines or columns tall.  If and only if SIZE is non-nil, its
-absolute value can be less than `window-min-height' or
-`window-min-width'; so this command can make a new window as
-small as one line or two columns.  SIZE defaults to half of
-WINDOW's size.
+If the optional argument SIZE is a positive number, shrink WINDOW
+to SIZE lines or columns in order to accommodate the new window.
+If SIZE is a negative number, make the new window -SIZE lines or
+columns tall.  In both cases, the absolute value of SIZE can be
+less than `window-min-height' or `window-min-width'; so this
+function can make a new window as small as one line or two
+columns.  If SIZE is not a number, make the new window occupy
+half of WINDOW's size.
 
 Optional third argument SIDE nil (or `below') specifies that the
-new window shall be located below WINDOW.  SIDE `above' means the
-new window shall be located above WINDOW.  In both cases SIZE
+new window shall be made below WINDOW.  SIDE `above' means the
+new window shall be made above WINDOW.  In both cases SIZE
 specifies the new number of lines for WINDOW (or the new window
 if SIZE is negative) including space reserved for the mode and/or
-header line.
+header line, scroll bars and window dividers.
 
-SIDE t (or `right') specifies that the new window shall be
-located on the right side of WINDOW.  SIDE `left' means the new
-window shall be located on the left of WINDOW.  In both cases
-SIZE specifies the new number of columns for WINDOW (or the new
-window provided SIZE is negative) including space reserved for
-fringes and the scrollbar or a divider column.
+SIDE t (or `right') specifies that the new window shall be made
+on the right side of WINDOW.  SIDE `left' means the new window
+shall be made on the left of WINDOW.  In both cases, SIZE
+specifies the new number of columns for WINDOW (or the new window
+provided SIZE is negative) including any space reserved for
+fringes, scroll bar and window dividers.
 
 For compatibility reasons, SIDE `up' and `down' are interpreted
 as `above' and `below'.  Any other non-nil value for SIDE is
 currently handled like t (or `right').
 
+As a rule, if WINDOW already forms a combination that matches the SIDE
+parameter and `window-combination-limit' is nil, reuse WINDOW's parent
+in the window tree as parent of the new window.  If WINDOW is in a
+combination that is orthogonal to the SIDE parameter or if
+`window-combination-limit' is non-nil, make a new parent window that
+replaces WINDOW in the window tree and make WINDOW and the new window
+its sole child windows.  This standard behavior can be overridden via
+the REFER argument.
+
 PIXELWISE, if non-nil, means to interpret SIZE pixelwise.
 
+If the optional fifth argument REFER is non-nil, it specifies a
+reference window used for setting up properties of the new window.
+REFER can be either a window or a cons cell of two windows.
+
+If REFER is a cons cell, its car has to specify a deleted, former live
+window - a window that has shown a buffer before - on the same frame as
+WINDOW.  That buffer must be still live.  The cdr has to specify a
+deleted window that was a parent window on the same frame as WINDOW
+before it was deleted.  In this case, rather then making new windows,
+replace WINDOW with the cdr of REFER in the window tree and make WINDOW
+and REFER's car its new child windows.  Buffer, start and point
+positions of REFER's car are set to the values they had immediately
+before REFER's car was deleted the last time.  Decorations and
+parameters remain unaltered from their values before REFER's car and cdr
+were deleted.
+
+Alternatively REFER may specify a deleted, former live window - a window
+that has shown a buffer before - on the same frame as WINDOW.  In this
+case do not make a new window but rather make REFER live again and
+insert it into the window tree at the position and with the sizes the
+new window would have been given.  Buffer, start and point positions of
+REFER are set to the values they had immediately before REFER was
+deleted the last time.  Decorations and parameters remain unaltered from
+their values before REFER was deleted.  Throw an error if REFER's buffer
+has been deleted after REFER itself was deleted.
+
+Otherwise REFER must specify a live window.  In this case, the new
+window will inherit properties like buffer, start and point position and
+some decorations from REFER.  If REFER is nil or omitted, then if WINDOW
+is live, any such properties are inherited from WINDOW.  If, however,
+WINDOW is an internal window, the new window will inherit these
+properties from the window selected on WINDOW's frame.
+
 If the variable `ignore-window-parameters' is non-nil or the
 `split-window' parameter of WINDOW equals t, do not process any
-parameters of WINDOW.  Otherwise, if the `split-window' parameter
-of WINDOW specifies a function, call that function with all three
-arguments and return the value returned by that function.
-
-Otherwise, if WINDOW is part of an atomic window, \"split\" the
-root of that atomic window.  The new window does not become a
-member of that atomic window.
-
-If WINDOW is live, properties of the new window like margins and
-scrollbars are inherited from WINDOW.  If WINDOW is an internal
-window, these properties as well as the buffer displayed in the
-new window are inherited from the window selected on WINDOW's
-frame.  The selected window is not changed by this function."
+parameters of WINDOW.  Otherwise, if the `split-window' parameter of
+WINDOW specifies a function, call that function with the three first
+arguments WINDOW, SIZE and SIDE and return the value returned by that
+function.
+
+Otherwise, if WINDOW is part of an atomic window, \"split\" the root of
+that atomic window.  The new window does not become a member of that
+atomic window.
+
+The selected window and the selected window on WINDOW's frame are not
+changed by this function."
   (setq window (window-normalize-window window))
   (let* ((side (cond
 		((not side) 'below)
@@ -5598,14 +5639,14 @@ split-window
        ((and (window-parameter window 'window-atom)
 	     (setq atom-root (window-atom-root window))
 	     (not (eq atom-root window)))
-	(throw 'done (split-window atom-root size side pixelwise)))
+	(throw 'done (split-window atom-root size side pixelwise refer)))
        ;; If WINDOW's frame has a side window and WINDOW specifies the
        ;; frame's root window, split the frame's main window instead
        ;; (Bug#73627).
        ((and (eq window (frame-root-window frame))
 	     (window-with-parameter 'window-side nil frame))
 	(throw 'done (split-window (window-main-window frame)
-				   size side pixelwise)))
+				   size side pixelwise refer)))
        ;; If WINDOW is a side window or its first or last child is a
        ;; side window, throw an error unless `window-combination-resize'
        ;; equals 'side.
@@ -5644,8 +5685,8 @@ split-window
 		   (window-combined-p window horizontal)))
 	     ;; 'old-pixel-size' is the current pixel size of WINDOW.
 	     (old-pixel-size (window-size window horizontal t))
-	     ;; 'new-size' is the specified or calculated size of the
-	     ;; new window.
+	     ;; 'new-pixel-size' is the specified or calculated size
+	     ;; of the new window.
 	     new-pixel-size new-parent new-normal)
 	(cond
 	 ((not pixel-size)
@@ -5766,8 +5807,9 @@ split-window
 	   window (- (if new-parent 1.0 (window-normal-size window horizontal))
 		     new-normal)))
 
-	(let* ((new (split-window-internal window new-pixel-size side new-normal)))
-	  (window--pixel-to-total frame horizontal)
+	(let ((new (split-window-internal
+		    window new-pixel-size side new-normal refer)))
+          (window--pixel-to-total frame horizontal)
 	  ;; Assign window-side parameters, if any.
 	  (cond
 	   ((eq window-combination-resize 'side)
diff --git a/src/window.c b/src/window.c
index 34968ac824f..6c7749ddae1 100644
--- a/src/window.c
+++ b/src/window.c
@@ -652,15 +652,16 @@ DEFUN ("window-buffer", Fwindow_buffer, Swindow_buffer, 0, 1, 0,
 
 DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0,
        doc: /* Return the old buffer displayed by WINDOW.
-WINDOW must be a live window and defaults to the selected one.
+WINDOW can be any window and defaults to the selected one.
 
 The return value is the buffer shown in WINDOW at the last time window
-change functions were run.  It is nil if WINDOW was created after
-that.  It is t if WINDOW has been restored from a window configuration
-after that.  */)
+change functions were run or WINDOW is a former live window that was
+deleted.  It is nil if WINDOW was created after that.  It is t if WINDOW
+has been restored from a window configuration after that.  It is always
+nil if WINDOW is an internal window.  */)
   (Lisp_Object window)
 {
-  struct window *w = decode_live_window (window);
+  struct window *w = decode_any_window (window);
 
   return (NILP (w->old_buffer)
 	  /* A new window.  */
@@ -668,8 +669,8 @@ DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0,
 	  : (w->change_stamp != WINDOW_XFRAME (w)->change_stamp)
 	  /* A window restored from a configuration.  */
 	  ? Qt
-	  /* A window that was live the last time seen by window
-	     change functions.  */
+	  /* A window that was live the last time seen by window change
+	     functions or was deleted.  */
 	  : w->old_buffer);
 }
 
@@ -4491,45 +4492,6 @@ allocate_window (void)
 				       PVEC_WINDOW);
 }
 
-/* Make new window, have it replace WINDOW in window-tree, and make
-   WINDOW its only vertical child (HORFLAG means make WINDOW its only
-   horizontal child).   */
-static void
-make_parent_window (Lisp_Object window, bool horflag)
-{
-  Lisp_Object parent;
-  register struct window *o, *p;
-
-  o = XWINDOW (window);
-  p = allocate_window ();
-  memcpy ((char *) p + sizeof (union vectorlike_header),
-	  (char *) o + sizeof (union vectorlike_header),
-	  word_size * VECSIZE (struct window));
-  /* P's buffer slot may change from nil to a buffer...  */
-  adjust_window_count (p, 1);
-  XSETWINDOW (parent, p);
-
-  p->sequence_number = ++sequence_number;
-
-  replace_window (window, parent, true);
-
-  wset_next (o, Qnil);
-  wset_prev (o, Qnil);
-  wset_parent (o, parent);
-  /* ...but now P becomes an internal window.  */
-  wset_start (p, Qnil);
-  wset_pointm (p, Qnil);
-  wset_old_pointm (p, Qnil);
-  wset_buffer (p, Qnil);
-  wset_combination (p, horflag, window);
-  wset_combination_limit (p, Qnil);
-  /* Reset any previous and next buffers of p which have been installed
-     by the memcpy above.  */
-  wset_prev_buffers (p, Qnil);
-  wset_next_buffers (p, Qnil);
-  wset_window_parameters (p, Qnil);
-}
-
 /* Make new window from scratch.  */
 Lisp_Object
 make_window (void)
@@ -5073,7 +5035,7 @@ resize_frame_windows (struct frame *f, int size, bool horflag)
 }
 
 
-DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 4, 0,
+DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 5, 0,
        doc: /* Split window OLD.
 Second argument PIXEL-SIZE specifies the number of pixels of the
 new window.  It must be a positive integer.
@@ -5088,32 +5050,33 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
 the right side of WINDOW.  SIDE `left' means the new window shall be
 located on the left of WINDOW.  In both cases PIXEL-SIZE specifies the
 width of the new window including space reserved for fringes and the
-scrollbar or a divider column.
+scroll bar or a divider column.
 
 Fourth argument NORMAL-SIZE specifies the normal size of the new window
-according to the SIDE argument.
+according to the SIDE argument.  Optional fifth argument REFER is as for
+'split-window'.
 
 The new pixel and normal sizes of all involved windows must have been
 set correctly.  See the code of `split-window' for how this is done.  */)
-  (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side, Lisp_Object normal_size)
-{
-  /* OLD (*o) is the window we have to split.  (*p) is either OLD's
-     parent window or an internal window we have to install as OLD's new
-     parent.  REFERENCE (*r) must denote a live window, or is set to OLD
-     provided OLD is a leaf window, or to the frame's selected window.
-     NEW (*n) is the new window created with some parameters taken from
-     REFERENCE (*r).  */
-  Lisp_Object new, frame, reference;
-  struct window *o, *p, *n, *r, *c;
-  struct frame *f;
+  (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side,
+   Lisp_Object normal_size, Lisp_Object refer)
+{
+  /* OLD (*o) is the window to split.  REFER (*r) is a reference window,
+     either an arbitrary live window or a former live, now deleted
+     window on the same frame as OLD.  NEW (*n) is the new window
+     created anew or resurrected from REFER (*r), if specified.  *p
+     refers either to OLD's parent window that will become NEW's parent
+     window too or to a new internal window that becomes OLD's and NEW's
+     new parent.  */
+  struct window *o = decode_valid_window (old);
+  Lisp_Object frame = WINDOW_FRAME (o);
+  struct frame *f = XFRAME (frame);
+  struct window *p, *n, *r, *c;
   bool horflag
     /* HORFLAG is true when we split side-by-side, false otherwise.  */
     = EQ (side, Qt) || EQ (side, Qleft) || EQ (side, Qright);
-
-  CHECK_WINDOW (old);
-  o = XWINDOW (old);
-  frame = WINDOW_FRAME (o);
-  f = XFRAME (frame);
+  Lisp_Object new, parent = Qnil;
+  bool dead = false;
 
   CHECK_FIXNUM (pixel_size);
   EMACS_INT total_size
@@ -5131,14 +5094,74 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
 	   ? WINDOW_VERTICAL_COMBINATION_P (XWINDOW (o->parent))
 	   : WINDOW_HORIZONTAL_COMBINATION_P (XWINDOW (o->parent))));
 
-  /* We need a live reference window to initialize some parameters.  */
-  if (WINDOW_LIVE_P (old))
-    /* OLD is live, use it as reference window.  */
-    reference = old;
+  /* Set up reference window.  */
+  if (NILP (refer))
+    {
+      if (WINDOW_LIVE_P (old))
+	/* OLD is live, use it as reference window.  */
+	refer = old;
+      else
+	/* Use the frame's selected window as reference window.  */
+	refer = FRAME_SELECTED_WINDOW (f);
+
+      r = XWINDOW (refer);
+    }
+  else if (CONSP (refer))
+    {
+      /* If REFER is a cons, then its car must be a deleted, former live
+	 window and its cdr must be a deleted former parent window.  Set
+	 PARENT to the cdr of REFER and REFER to its car.  WINDOW and
+	 REFER end up as the sole children of PARENT which replaces
+	 WINDOW in the window tree.  As a special case, if REFER's cdr
+	 is t, reuse REFER's car's old parent as new parent provided it
+	 is a deleted fromer parent window.  */
+      parent = Fcdr (refer);
+      refer = Fcar (refer);
+      r = decode_any_window (refer);
+
+      if (!NILP (r->contents) || !BUFFERP (r->old_buffer))
+	error ("REFER's car must specify a deleted, former live window");
+      else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
+	error ("The buffer formerly shown by REFER's car has been killed");
+      else if (!EQ (r->frame, frame))
+	error ("REFER's car must specify a window on same frame as WINDOW");
+
+      if (EQ (parent, Qt))
+	/* If REFER's cdr is t, use the old parent of REFER's car as new
+	   parent.  */
+	parent = r->parent;
+
+      p = decode_any_window (parent);
+
+      if (!NILP (p->contents) || BUFFERP (p->old_buffer))
+	error ("REFER's cdr must specify a deleted, former parent window");
+      else if (!EQ (p->frame, frame))
+	error ("REFER's cdr must specify window on same frame as WINDOW");
+
+      dead = true;
+    }
   else
-    /* Use the frame's selected window as reference window.  */
-    reference = FRAME_SELECTED_WINDOW (f);
-  r = XWINDOW (reference);
+    {
+      r = decode_any_window (refer);
+
+      if (NILP (r->contents))
+	/* Presumably a deleted, former live window.  Check whether its
+	   contents can be used.  */
+	{
+	  if (!BUFFERP (r->old_buffer))
+	    error ("REFER must specify a former live window (must have shown a buffer)");
+	  else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
+	    error ("The buffer formerly shown by REFER has been killed");
+	  else if (!EQ (r->frame, frame))
+	    error ("REFER must specify a window on same frame as WINDOW");
+
+	  dead = true;
+	}
+      else if (!NILP (parent))
+	error ("If REFER is a cons, its car must not specify a live window");
+      else if (!WINDOW_LIVE_P (refer))
+	error ("REFER is not a live window (does not show a buffer)");
+    }
 
   /* The following bugs are caught by `split-window'.  */
   if (MINI_WINDOW_P (o))
@@ -5149,16 +5172,18 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
     /* `window-combination-resize' non-nil means try to resize OLD's siblings
        proportionally.  */
     {
-      p = XWINDOW (o->parent);
+      struct window *op = XWINDOW (o->parent);
+
       /* Temporarily pretend we split the parent window.  */
       wset_new_pixel
-	(p, make_fixnum ((horflag ? p->pixel_width : p->pixel_height)
+	(op, make_fixnum ((horflag ? op->pixel_width : op->pixel_height)
 			 - XFIXNUM (pixel_size)));
-      if (!window_resize_check (p, horflag))
+      if (!window_resize_check (op, horflag))
 	error ("Window sizes don't fit");
       else
 	/* Undo the temporary pretension.  */
-	wset_new_pixel (p, make_fixnum (horflag ? p->pixel_width : p->pixel_height));
+	wset_new_pixel
+	  (op, make_fixnum (horflag ? op->pixel_width : op->pixel_height));
     }
   else
     {
@@ -5178,8 +5203,24 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
       Lisp_Object new_normal
 	= horflag ? o->normal_cols : o->normal_lines;
 
-      make_parent_window (old, horflag);
-      p = XWINDOW (o->parent);
+      if (NILP (parent))
+	/* This is the crux of the old make_parent_window.  */
+	{
+	  p = allocate_window ();
+	  XSETWINDOW (parent, p);
+	  p->sequence_number = ++sequence_number;
+	  wset_frame (p, frame);
+	}
+      else
+	/* Pacify GCC.  */
+	p = XWINDOW (parent);
+
+      replace_window (old, parent, true);
+      wset_next (o, Qnil);
+      wset_prev (o, Qnil);
+      wset_parent (o, parent);
+      wset_combination (p, horflag, old);
+
       if (EQ (Vwindow_combination_limit, Qt))
 	/* Store t in the new parent's combination_limit slot to avoid
 	   that its children get merged into another window.  */
@@ -5195,7 +5236,12 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
     p = XWINDOW (o->parent);
 
   fset_redisplay (f);
-  new = make_window ();
+
+  if (dead)
+    new = refer;
+  else
+    new = make_window ();
+
   n = XWINDOW (new);
   wset_frame (n, frame);
   wset_parent (n, o->parent);
@@ -5222,16 +5268,19 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
   n->window_end_valid = false;
   n->last_cursor_vpos = 0;
 
-  /* Get special geometry settings from reference window.  */
-  n->left_margin_cols = r->left_margin_cols;
-  n->right_margin_cols = r->right_margin_cols;
-  n->left_fringe_width = r->left_fringe_width;
-  n->right_fringe_width = r->right_fringe_width;
-  n->fringes_outside_margins = r->fringes_outside_margins;
-  n->scroll_bar_width = r->scroll_bar_width;
-  n->scroll_bar_height = r->scroll_bar_height;
-  wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type);
-  wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type);
+  if (!dead)
+    {
+      /* Get special geometry settings from reference window.  */
+      n->left_margin_cols = r->left_margin_cols;
+      n->right_margin_cols = r->right_margin_cols;
+      n->left_fringe_width = r->left_fringe_width;
+      n->right_fringe_width = r->right_fringe_width;
+      n->fringes_outside_margins = r->fringes_outside_margins;
+      n->scroll_bar_width = r->scroll_bar_width;
+      n->scroll_bar_height = r->scroll_bar_height;
+      wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type);
+      wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type);
+    }
 
   /* Directly assign orthogonal coordinates and sizes.  */
   if (horflag)
@@ -5260,6 +5309,7 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
 	sum = sum + XFIXNUM (c->new_total);
       c = NILP (c->next) ? 0 : XWINDOW (c->next);
     }
+
   wset_new_total (n, make_fixnum ((horflag
 				   ? p->total_cols
 				   : p->total_lines)
@@ -5267,10 +5317,30 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
   wset_new_normal (n, normal_size);
 
   block_input ();
+
+  if (dead)
+    {
+      /* Get dead window back its old buffer and markers.  */
+      wset_buffer (n, n->old_buffer);
+      set_marker_restricted
+	(n->start, make_fixnum (XMARKER (n->start)->charpos), n->contents);
+      set_marker_restricted
+	(n->pointm, make_fixnum (XMARKER (n->pointm)->charpos), n->contents);
+      set_marker_restricted
+	(n->old_pointm, make_fixnum (XMARKER (n->old_pointm)->charpos),
+	 n->contents);
+
+      Vwindow_list = Qnil;
+      /* Remove window from the table of dead windows.  */
+      Fremhash (make_fixnum (n->sequence_number),
+		window_dead_windows_table);
+    }
+
   window_resize_apply (p, horflag);
   adjust_frame_glyphs (f);
-  /* Set buffer of NEW to buffer of reference window.  */
+
   set_window_buffer (new, r->contents, true, true);
+
   FRAME_WINDOW_CHANGE (f) = true;
   unblock_input ();
 
@@ -5368,6 +5438,8 @@ DEFUN ("delete-window-internal", Fdelete_window_internal, Sdelete_window_interna
 	}
       else
 	{
+	  /* Store WINDOW's buffer in old_buffer.  */
+	  wset_old_buffer (w, w->contents);
 	  unshow_buffer (w);
 	  unchain_marker (XMARKER (w->pointm));
 	  unchain_marker (XMARKER (w->old_pointm));
@@ -7712,6 +7784,8 @@ delete_all_child_windows (Lisp_Object window)
     }
   else if (BUFFERP (w->contents))
     {
+      /* Store WINDOW's buffer in old_buffer.  */
+      wset_old_buffer (w, w->contents);
       unshow_buffer (w);
       unchain_marker (XMARKER (w->pointm));
       unchain_marker (XMARKER (w->old_pointm));
@@ -9064,12 +9138,9 @@ syms_of_window (void)
     doc: /* Hash table of dead windows.
 Each entry in this table maps a window number to a window object.
 Entries are added by `delete-window-internal' and are removed by the
-garbage collector.
-
-This table is maintained by code in window.c and is made visible in
-Elisp for testing purposes only.  */);
+garbage collector.  */);
   window_dead_windows_table
-    = CALLN (Fmake_hash_table, QCweakness, Qt);
+    = CALLN (Fmake_hash_table, QCweakness, Qvalue);
 
   defsubr (&Sselected_window);
   defsubr (&Sold_selected_window);

[-- Attachment #3: window-rotate.el --]
[-- Type: text/x-emacs-lisp, Size: 12182 bytes --]

;;; -*- lexical-binding:t -*-

;; Debugging code.
(defvar window-rotate-debug t)

(defun window-rotate-debug (&rest rest)
  "Append REST to debugging buffer *foo*."
  (when window-rotate-debug
    (with-current-buffer (get-buffer-create "*foo*")
      (goto-char (point-max))
      (when rest
	(insert (format "%s" (car rest)))
	(setq rest (cdr rest))
	(while rest
	  (insert (format " .. %s" (car rest)))
	  (setq rest (cdr rest)))
	(insert "\n")))))

(defun window-rotate-debug-side (side)
  (pcase side
    ('left "<")
    ('right ">")
    ('above "^")
    ('below "v")
    (_ "?")))

;; Two variables the code below binds lexically.
(defvar window-alist nil)
(defvar window-rotate-clockwise nil)

(defun window-first-window (window)
  "Return leftmost live descendant of specified WINDOW.
Return nil if WINDOW is not valid.  Otherwise, return the live window in
the window tree rooted at WINDOW such that that window and all ancestors
of that window in that window tree have no previous sibling.  Return
WINDOW if it does not have a child window."
  (when (window-valid-p window)
    (let ((child (or (window-top-child window)
		     (window-left-child window))))
      (if child
	  (window-first-window child)
	window))))

(defun window-alist (&optional frame)
  "Return alist of all windows on FRAME.
FRAME must be a live frame and defaults to the selected one.  Each
element of the return value associates a window on FRAME with its
properties like, for example, its parent in the window tree of FRAME.
Accessor functions like, for example, `window-alist-parent' are provided
to conveniently retrieve the values stored by this function."
  (let (alist)
    (walk-window-tree
     (lambda (window)
       (setq alist
	     (cons
	      (if (window-buffer window)
		  (list window
			(cons 'buffer (window-buffer window))
			(cons 'parent (window-parent window))
			(cons 'prev (window-prev-sibling window))
			(cons 'next (window-next-sibling window))
			(cons 'width (window-normal-size window t))
			(cons 'height (window-normal-size window)))
		(list window
		      (cons 'left (window-left-child window))
		      (cons 'top (window-top-child window))
		      (cons 'parent (window-parent window))
		      (cons 'prev (window-prev-sibling window))
		      (cons 'next (window-next-sibling window))
		      (cons 'first (window-first-window window))
		      (cons 'width (window-normal-size window t))
		      (cons 'height (window-normal-size window))
		      (cons 'combination-limit (window-combination-limit window))
		      (cons 'no-rotate (window-parameter window 'no-rotate))))
	      alist)))
     frame t)

    (when window-rotate-debug
      (with-current-buffer (get-buffer-create "*foo*")
	(erase-buffer)
	(pp alist (current-buffer))
	(insert "\n")))

    alist))

;; Accessor functions.
(defun window-alist-parent (window)
  (cdr (assq 'parent (cdr (assq window window-alist)))))

(defun window-alist-buffer (window)
  (cdr (assq 'buffer (cdr (assq window window-alist)))))

(defun window-alist-next (window)
  (cdr (assq 'next (cdr (assq window window-alist)))))

(defun window-alist-prev (window)
  (cdr (assq 'prev (cdr (assq window window-alist)))))

(defun window-alist-left (window)
  (cdr (assq 'left (cdr (assq window window-alist)))))

(defun window-alist-top (window)
  (cdr (assq 'top (cdr (assq window window-alist)))))

(defun window-alist-first (window)
  (cdr (assq 'first (cdr (assq window window-alist)))))

(defun window-alist-width (window)
  (cdr (assq 'width (cdr (assq window window-alist)))))

(defun window-alist-height (window)
  (cdr (assq 'height (cdr (assq window window-alist)))))

(defun window-alist-no-rotate (window)
  (let ((no-rotate
	 (catch 'no-rotate
	   (while window
	     (when (cdr (assq 'no-rotate (cdr (assq window window-alist))))
	       (throw 'no-rotate t))
	     (setq window (window-alist-parent window))))))
    no-rotate))

(defun window-alist-combination-limit (window)
  (cdr (assq 'combination-limit (cdr (assq window window-alist)))))

(defun window-rotate-clockwise (&optional frame)
  "Rotate root window of specified FRAME clockwise.
FRAME must be a live frame and defaults to the selected one."
  (interactive)
  (let ((window-rotate-clockwise t))
    (window-rotate frame)))

(defun window-rotate-anticlockwise (&optional frame)
  "Rotate root window of specified FRAME anticlockwise.
FRAME must be a live frame and defaults to the selected one."
  (interactive)
  (let (window-rotate-clockwise)
    (window-rotate frame)))

(defun window-alist-check ()
  "Check whether `window-alist' has been preserved. "
  ;; For obvious reasons, never check previous and next siblings here.
  ;; The whole purpose of rotating is to change these.
  (catch 'failed
    (dolist (entry window-alist)
      (let ((window (car entry)))
	(cond
	 ((not (window-valid-p window))
	  (window-rotate-debug
	   (format
	    "Window %s has not been restored" window))
	  (throw 'failed nil))
	 ((not (eq (window-parent window)
		   (window-alist-parent window)))
	  (window-rotate-debug
	   (format
	    "New parent %s of window %s does not match old one %s"
	    (window-parent window) window (window-alist-parent window)))
	  (throw 'failed nil))
	 ((not (eq (window-buffer window)
		   (window-alist-buffer window)))
	  (window-rotate-debug
	   (format
	    "New buffer %s of window %s does not match old one %s"
	    (window-buffer window) window (window-alist-buffer window)))
	  (throw 'failed nil)))))

    t))

(defun window-rotate (&optional frame)
  "Rotate root window of specified FRAME."
  (setq frame (window-normalize-frame frame))
  ;; Construct 'window-alist' which also makes sure that no window
  ;; gets recycled by GC.
  (let ((window-alist (window-alist frame))
	(window (frame-first-window frame))
	(window-min-height 0)
	(window-min-width 0)
	(configuration (current-window-configuration frame)))
    (condition-case var
	(progn
	  ;; Delete all windows but FRAME's first window.
	  (delete-other-windows-internal window)
	  ;; Now rotate starting with the frame's first window as WINDOW
	  ;; argument and nil as ROOT.
	  (window-rotate-debug (format "First %s"window))
	  (window-rotate-1 window nil)
	  (unless (window-alist-check)
	    (user-error "Rotating failed")
	    (set-window-configuration configuration)))
      (error
       (message "Rotating failed as %s" var)
       (set-window-configuration configuration)))))

(defun window-rotate-1 (window root)
  "Rotate window subroutine.
WINDOW must be a leftmost bottommost child window such as produced by
`frame-first-window' or `window-first-window'.  More precisely, WINDOW
must show a buffer and its previous sibling must be nil.

ROOT is either the root window to rotate or a parent window such that
WINDOW is its `frame-first-window'.

Traverse the window structure saved in `window-alist' starting with
WINDOW, continuing with its next siblings.  If a sibling is a parent
window, recurse with WINDOW set to the window returned by
`window-first-window' for that sibling and ROOT set to sibling's parent.
After having traced all siblings continue with WINDOW's parent unless
that parent equals ROOT."
  ;; It's practically always a bug to use a "normal" window accessor
  ;; function (like 'window-parent') in the body of this function.  When
  ;; called for the first time, the frame may have only one live window
  ;; and all other windows are deleted.  Hence most "normal" window
  ;; accessor function will throw an invalid argument error.  So instead
  ;; of 'window-parent' you have to use 'window-alist-parent'.
  (let ((parent (window-alist-parent window))
	next first side size)
    (when (and parent (not (eq parent root)))
      ;; Traverse WINDOW (which is always the first sibling) and its
      ;; next siblings.
      (while (setq next (window-alist-next window))
	;; Calculate SIDE argument for 'split-window' taking into
	;; account 'window-rotate-clockwise' and whether WINDOW is in a
	;; horizontal of vertical combination.  If one of WINDOW's
	;; ancestors has a non-nil 'no-rotate' parameter, don't rotate
	;; its descendants.
	(setq side
	      (cond
	       ((window-alist-no-rotate parent)
		(if (window-alist-left parent) 'right 'below))
	       (window-rotate-clockwise
		(if (window-alist-left parent) 'below 'left))
	       (t
		(if (window-alist-left parent) 'above 'right))))
	(setq size (round
		    (if (or (and (window-alist-no-rotate parent)
				 (window-alist-left parent))
			    (memq side '(left right)))
			(* (if (window-valid-p parent)
			       (window-pixel-width parent)
			     (window-pixel-width window))
			   (window-alist-height window))
		      (* (if (window-valid-p parent)
			     (window-pixel-height parent)
			   (window-pixel-height window))
			 (window-alist-width window)))))
	;; Save current window configuration in case of an error.
	(if (window-alist-buffer next)
	    ;; If the next sibling shows a buffer, split it with
	    ;; WINDOW's old parent as new parent.
	    (let* ((prev (window-alist-prev window))
		   (limit (window-alist-combination-limit parent))
		   ;; Set 'window-combination-limit' to make sure
		   ;; that a new parent is created when WINDOW's
		   ;; previous sibling and WINDOW do not have the
		   ;; same parent.
		   (window-combination-limit
		    (or limit
			(not prev)
			(not (eq (window-alist-parent prev)
				 (window-alist-parent window)))))
		   window-combination-resize)
	      (window-rotate-debug
	       (format "%s %s N %s [%s|%s] S %s %s"
		       (window-rotate-debug-side side) window next
		       (window-parent window) parent size
		       (if window-combination-limit "T" "F")))
	      (split-window
	       window size side t
	       (if (and (window-valid-p parent)
			(eq parent (window-parent window)))
		   next
		 (cons next parent)))
;; 	      (y-or-n-p "N")
	      (set-window-combination-limit parent limit))
	  ;; If the next sibling is a parent window set FIRST to its
	  ;; topmost leftmost live descendant.
	  (setq first (window-alist-first next))
	  (let* ((prev (window-alist-prev window))
		 (limit (window-alist-combination-limit parent))
		 ;; Set 'window-combination-limit' to make sure
		 ;; that a new parent is created when WINDOW's
		 ;; previous sibling and WINDOW do not have the
		 ;; same parent.
		 (window-combination-limit
		  (or limit
		      (not prev)
		      (not (eq (window-alist-parent prev)
			       (window-alist-parent window)))))
		 window-combination-resize)
	    (window-rotate-debug
	     (format "%s %s F %s [%s|%s] S %s %s"
		     (window-rotate-debug-side side) window first
		     (window-parent window) parent size
	     (if window-combination-limit "T" "F")))
	    (split-window
	     window size side t
	     (if (and (window-valid-p parent)
		      (eq parent (window-parent window)))
		 first
	       (cons first parent)))
;; 	    (y-or-n-p "F")
	    (set-window-combination-limit parent limit)
	    ;; Recurse starting with WINDOW's first live descendant.
	    ;; The second argument PARENT should terminate the recursion
	    ;; when going up again.
	    (window-rotate-1 first parent)))
	;; Continue with WINDOW's next sibling.
	(setq window (window-alist-next window)))
      ;; Continue with WINDOW's parent unless we are at the ROOT of the
      ;; current recursion.
      (unless (eq (window-alist-parent window) root)
	(window-rotate-1 (window-alist-parent window) root)))))

(defun window-rotate-setup ()
  (interactive)
  (find-file-noselect "/home/martin/temp/window-rotate.el")
  (eval-buffer (get-buffer "window-rotate.el"))
  (let ((foo (get-buffer-create "*foo*"))
	window)
    (delete-other-windows)
    (switch-to-buffer (get-buffer "window-rotate.el"))
    (goto-char (point-min))
    (re-search-forward "(defun window-rotate-1 (window root)")
;;     (edebug-eval-top-level-form)
    (setq window (split-window nil nil t))
    (set-window-buffer window foo)
    (split-window window)
    (setq window (split-window window nil t))
    (split-window window)
    (select-window window)))

(global-set-key [(super P)] 'window-rotate-setup)
(global-set-key [(super p)] 'window-rotate-clockwise)
(global-set-key [(super q)] 'window-rotate-anticlockwise)

  reply	other threads:[~2024-11-05 18:01 UTC|newest]

Thread overview: 144+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-24 13:45 Add function to rotate/transpose all windows pranshu sharma
2024-09-24 13:53 ` Eli Zaretskii
2024-09-25  8:05   ` martin rudalics
2024-09-25  8:34     ` pranshu sharma
2024-09-25  9:31       ` martin rudalics
2024-09-25 10:50         ` pranshu sharma
2024-09-25 13:53           ` martin rudalics
2024-09-25 15:31             ` pranshu sharma
2024-09-26 14:10       ` martin rudalics
2024-09-26 14:22         ` Eli Zaretskii
2024-09-27 17:29           ` martin rudalics
2024-09-28  7:52             ` pranshu sharma
2024-09-28  9:26               ` martin rudalics
2024-09-28 10:53                 ` pranshu sharma
2024-09-28 14:48                   ` martin rudalics
2024-09-29  7:36                     ` pranshu sharma
2024-09-29  8:40                       ` martin rudalics
2024-09-29  9:23                         ` pranshu sharma
2024-09-29 14:48                           ` martin rudalics
2024-09-30  6:29                             ` pranshu sharma
2024-09-30  8:57                               ` martin rudalics
2024-10-01  9:17                                 ` pranshu sharma
2024-10-02  9:04                                   ` martin rudalics
2024-10-03  7:06                                     ` pranshu sharma
2024-10-03  8:17                                       ` martin rudalics
2024-10-03 10:09                                         ` pranshu sharma
2024-10-03 14:18                                           ` martin rudalics
2024-10-04  5:50                                             ` pranshu sharma
2024-10-04  8:08                                               ` martin rudalics
2024-10-04 15:10                                                 ` pranshu sharma
2024-10-05 14:43                                                   ` martin rudalics
2024-10-06  2:54                                                     ` pranshu sharma
2024-10-06 15:02                                                       ` martin rudalics
2024-10-06 15:52                                                         ` pranshu sharma
2024-10-07  8:33                                                           ` martin rudalics
2024-10-07  9:42                                                             ` pranshu sharma
2024-10-03 15:12                                           ` Eli Zaretskii
2024-10-08 18:35                                       ` Juri Linkov
2024-10-09  6:59                                         ` pranshu sharma
2024-10-09 16:21                                           ` Juri Linkov
2024-10-10 11:49                                             ` pranshu sharma
2024-10-10 16:57                                               ` Juri Linkov
2024-10-13  5:43                                                 ` pranshu sharma
2024-10-13  8:17                                                   ` martin rudalics
2024-10-14 17:36                                                     ` Juri Linkov
2024-10-15  8:34                                                     ` pranshu sharma
2024-10-15 16:16                                                       ` Juri Linkov
2024-10-18 14:52                                                     ` pranshu sharma
2024-10-18 17:48                                                       ` martin rudalics
2024-10-18 18:37                                                         ` Eli Zaretskii
2024-10-19  1:45                                                         ` pranshu sharma
2024-10-19  6:45                                                           ` Eli Zaretskii
2024-10-19 18:19                                                             ` Juri Linkov
2024-10-19  8:33                                                           ` martin rudalics
2024-10-20  8:19                                                       ` martin rudalics
2024-10-20 14:11                                                         ` Pranshu Sharma
2024-10-20 17:37                                                           ` martin rudalics
2024-10-21  5:54                                                             ` Pranshu Sharma
2024-10-21  8:14                                                               ` martin rudalics
2024-10-21  9:23                                                               ` martin rudalics
2024-10-21 13:37                                                             ` Pranshu Sharma
2024-10-22 18:12                                                               ` martin rudalics
2024-10-24 14:38                                                                 ` Pranshu Sharma
2024-10-24 18:39                                                                   ` martin rudalics
2024-10-25 14:24                                                                     ` Pranshu Sharma
2024-10-25 17:09                                                                       ` martin rudalics
2024-10-26  9:14                                                                         ` Pranshu Sharma
2024-10-27  8:23                                                                           ` martin rudalics
2024-11-02 14:06                                                                             ` Pranshu Sharma
2024-11-05 18:01                                                                               ` martin rudalics [this message]
2024-11-08  9:23                                                                                 ` Pranshu Sharma
2024-11-08 10:06                                                                                   ` Pranshu Sharma
2024-11-08 15:52                                                                                     ` martin rudalics
2024-11-09  2:14                                                                                       ` Pranshu Sharma
2024-11-09  8:48                                                                                         ` martin rudalics
2024-11-08 15:52                                                                                   ` martin rudalics
2024-11-09  2:09                                                                                     ` Pranshu Sharma
2024-11-09  8:48                                                                                       ` martin rudalics
2024-11-09 10:55                                                                                         ` Pranshu Sharma
2024-11-09 18:06                                                                                           ` martin rudalics
2024-11-10 10:09                                                                                             ` Pranshu Sharma
2024-11-10 16:36                                                                                               ` martin rudalics
2024-11-11 14:47                                                                                                 ` Pranshu Sharma
2024-11-11 16:55                                                                                                   ` martin rudalics
2024-11-12 13:50                                                                                                     ` Pranshu Sharma
2024-11-12 17:46                                                                                                       ` martin rudalics
2024-11-16 13:36                                                                                                         ` Pranshu Sharma
2024-11-16 16:54                                                                                                           ` martin rudalics
2024-11-17  2:45                                                                                                             ` Pranshu Sharma
2024-11-17 10:22                                                                                                               ` martin rudalics
2024-11-17 15:03                                                                                                                 ` Pranshu Sharma
2024-11-17 16:38                                                                                                                   ` martin rudalics
2024-11-18  0:37                                                                                                                     ` Pranshu Sharma
2024-11-18  8:55                                                                                                                       ` martin rudalics
2024-11-19  9:34                                                                                                                         ` Pranshu Sharma
2024-11-19 17:48                                                                                                                           ` martin rudalics
2024-11-21 14:04                                                                                                                             ` Pranshu Sharma
2024-11-21 17:54                                                                                                                               ` martin rudalics
2024-11-24 13:53                                                                                                                                 ` Pranshu Sharma
2024-11-26  9:49                                                                                                                                   ` martin rudalics
2024-11-26 14:14                                                                                                                                     ` Pranshu Sharma
2024-11-27  8:29                                                                                                                                     ` Pranshu Sharma
2024-11-27  9:18                                                                                                                                       ` martin rudalics
2024-11-27 14:37                                                                                                                                         ` Pranshu Sharma
2024-11-27 17:42                                                                                                                                           ` martin rudalics
2024-11-28  2:43                                                                                                                                             ` Pranshu Sharma
2024-11-28  9:28                                                                                                                                               ` martin rudalics
2024-11-28 15:18                                                                                                                                                 ` Pranshu Sharma
2024-11-30 10:07                                                                                                                                                   ` martin rudalics
2024-11-30 17:41                                                                                                                                                     ` Juri Linkov
2024-11-30 19:01                                                                                                                                                       ` martin rudalics
2024-12-01  4:13                                                                                                                                                         ` Pranshu Sharma
2024-12-03  7:30                                                                                                                                                           ` Juri Linkov
2024-12-03  8:25                                                                                                                                                             ` martin rudalics
2024-12-01  6:41                                                                                                                                                     ` Pranshu Sharma
2024-12-01 17:20                                                                                                                                                       ` martin rudalics
2024-12-02  8:06                                                                                                                                                         ` Pranshu Sharma
2024-12-03  8:23                                                                                                                                                           ` martin rudalics
2024-12-04 15:20                                                                                                                                                             ` Pranshu Sharma
2024-12-04 17:56                                                                                                                                                               ` martin rudalics
2024-12-04 19:12                                                                                                                                                                 ` Juri Linkov
2024-12-05 14:16                                                                                                                                                                   ` Pranshu Sharma
2024-12-05 17:48                                                                                                                                                                     ` Juri Linkov
2024-12-06  4:51                                                                                                                                                                       ` Pranshu Sharma
2024-12-06  7:29                                                                                                                                                                         ` Juri Linkov
2024-12-05 14:23                                                                                                                                                                   ` Pranshu Sharma
2024-12-05 15:17                                                                                                                                                                 ` Pranshu Sharma
2024-10-14 17:32                                                   ` Juri Linkov
2024-09-28  7:58             ` pranshu sharma
2024-09-28  8:18             ` Eli Zaretskii
2024-09-28  9:40               ` martin rudalics
2024-09-28 11:35                 ` Eli Zaretskii
2024-09-28 14:58                   ` martin rudalics
2024-09-28 15:28                     ` Eli Zaretskii
2024-10-07  8:33                       ` martin rudalics
2024-09-28 13:22                 ` pranshu sharma
2024-09-28 14:21                   ` Eli Zaretskii
2024-09-28 14:49                   ` martin rudalics
2024-09-27 10:06         ` pranshu sharma
2024-09-27 17:29           ` martin rudalics
2024-09-24 17:40 ` Petteri Hintsanen
2024-09-24 19:34 ` Charles Choi
2024-09-25  2:00   ` Emanuel Berg
2024-09-25  7:00   ` pranshu sharma

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ef202491-5abf-48f4-bce3-5b81998e50c7@gmx.at \
    --to=rudalics@gmx.at \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    --cc=juri@linkov.net \
    --cc=pranshusharma366@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).