unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Functions transpose/rotate/flip windows
@ 2025-01-01  7:09 Pranshu Sharma via Emacs development discussions.
  0 siblings, 0 replies; only message in thread
From: Pranshu Sharma via Emacs development discussions. @ 2025-01-01  7:09 UTC (permalink / raw)
  To: emacs-devel; +Cc: rudalics, eliz, juri

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


I made a new thread, as the final diff is ready that adds the
functionality of https://www.emacswiki.org/emacs/TransposeFrame, and a
lot more (rotating subtrees, 'cycling' windows such as in
https://github.com/daichirata/emacs-rotate).

This diff that adds the required modifications to window.c and window.el
and window.texi done by Martin as well, and adds window-x.el which
contains all the elisp functions which add the interactive functions.

This diff updates NEWS as well, and doesn't add any keybindings for the
functions.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: transpose-windows.diff --]
[-- Type: text/x-diff, Size: 53267 bytes --]

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 3ff78b599de..166d264d02e 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -22,6 +22,7 @@ is displayed in 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.
@@ -1357,7 +1358,7 @@ the sense that these functions may fail to split them as described here.
 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
@@ -1366,7 +1367,7 @@ size.  The space is taken up by the new window, which is returned.
 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).
@@ -1376,13 +1377,13 @@ If @var{size} is @code{nil}, this function obeys the variables
 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
@@ -1401,14 +1402,62 @@ the right of @var{window}.  If @var{side} is @code{left}, the new
 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
@@ -2050,6 +2099,157 @@ distribute its space proportionally among the two remaining live
 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
@@ -7135,13 +7335,16 @@ live window as single, optional argument, defaulting to the selected
 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/etc/NEWS b/etc/NEWS
index 85213cbaa6f..45b7d56a4cd 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -149,6 +149,17 @@ the "*Completions*" buffer is hidden.
 
 ** Windows
 
++++
+*** New functions to modify window layout.
+Several new functions to modify the window layout have been added:
+- rotate-window-layout-anticlockwise
+- rotate-window-layout-clockwise
+- flip-window-layout-horizontally
+- flip-window-layout-vertically
+- transpose-window-layout
+- rotate-windows-back
+- rotate-windows
+
 +++
 *** New hook 'window-deletable-functions'.
 This abnormal hook gives its client a way to save a window from getting
diff --git a/lisp/window-x.el b/lisp/window-x.el
new file mode 100644
index 00000000000..f46d230b26b
--- /dev/null
+++ b/lisp/window-x.el
@@ -0,0 +1,336 @@
+;;; window-x.el --- extended window commands  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Pranshu Sharma <pranshu@bauherren.ovh>
+;;         Martin Rudalics <rudalics@gmx.at>
+;; Maintainer: emacs-devel@gnu.org
+;; Keywords: files
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file defines additional infrequently used window commands that
+;; should not be in window.el to not make the dumped image bigger.
+
+;;; 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.
+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."
+  (let (list)
+    (while window
+      (setq list
+	    (cons
+	     (cond
+	      ((window-top-child window)
+	       (append
+		(list t window
+		      (window-normal-size window nil)
+		      (window-normal-size window t))
+		(window-tree-normal-sizes (window-top-child window) t)))
+	      ((window-left-child window)
+	       (append
+		(list nil window
+			 (window-normal-size window nil)
+			 (window-normal-size window t))
+		(window-tree-normal-sizes (window-left-child window) t)))
+	      (t (list window
+		       (window-normal-size window nil)
+		       (window-normal-size window t))))
+	     list))
+      (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.
+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
+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
+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-window-layout frame-or-window t))
+
+(defun rotate-windows (&optional frame-or-window)
+  "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))))))))
+
+(defun window--transpose (window conf no-resize)
+  "Rearrange windows of 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.")))))
+
+(defun window--transpose-1 (subtree cwin conf no-resize atom-windows)
+  "Subroutine of `window--transpose'.
+SUBTREE must be in the format of the result of
+`window-tree-normal-sizes'.  CWIN is the current window through which
+the window splits are made.  ATOM-WINDOWS is a list of internal atom
+windows.  The CONF and NO-RESIZE arguments are the same as the
+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))
+		  (float (window-pixel-width cwin))
+		(float (window-pixel-height cwin)))))
+    (mapc
+     (pcase-lambda (`(,window . ,size))
+       (prog1
+	   (let* ((split-size (- (round (* flen size))))
+		  (split-type
+		   (funcall (if (car subtree) 'car 'cdr) conf))
+		  (return-win
+		   (if (listp window)
+		       ;; `window' is a window subtree.
+		       ;; `first-child' is a live window that is an descended of window
+		       (let* ((first-child window)
+			      ;; If the window being split is atomic
+			      (is-atom
+			       ;; cadr will return the internal parent window
+			       (memq (cadr first-child) atom-windows)))
+			 ;; (caar (cddddr first-child)) is the first window in the
+			 ;; list if there is a live window.
+			 (while (not (windowp (caar (cddddr first-child))))
+			   (setq first-child (car (cddddr first-child))))
+			 (window--transpose-1
+			  window
+			  (let ((window-combination-limit parent-window-is-set))
+				(split-window
+				 cwin
+				 split-size
+				 split-type
+				 t
+				 (if window-combination-limit
+				     (cons (caar (cddddr first-child)) (cadr subtree))
+				   (caar (cddddr first-child)))))
+			  (if is-atom
+			      '(nil . t)
+			    conf)
+			  no-resize
+			  atom-windows))
+		     ;; `window' is a window.
+		     (split-window
+		      cwin
+		      split-size
+		      split-type t
+		      ;; We need to set parent window if it hasn't been set
+		      ;; already.
+		      (if parent-window-is-set
+			  (cons window (cadr subtree))
+			window)))))
+	     (when (eq window-combination-limit t)
+	       (set-window-combination-limit (cadr subtree) nil))
+	     return-win)
+	 (setq parent-window-is-set nil)))
+     (mapcar
+      (lambda (e)
+	(pcase-let* ((`(,window . ,window-size-info)
+		      (if (windowp (car e))
+			  (cons (car e) e)
+			(cons e (cdr e)))))
+	  (cons window
+		;; The respective size of the window.
+		(if (car subtree)
+		    (cadr window-size-info)
+		  (caddr window-size-info)))))
+      ;; We need to ingore 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)
+			     no-resize atom-windows)))))
+
+;;; window-x.el ends here
diff --git a/lisp/window.el b/lisp/window.el
index cd19fd73849..5318ba299a3 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5511,54 +5511,95 @@ PARENT divided by their number plus 1."
       (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 @@ frame.  The selected window is not changed by this function."
        ((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 @@ frame.  The selected window is not changed by this function."
 		   (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 @@ frame.  The selected window is not changed by this function."
 	   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 7f157911685..d533904a550 100644
--- a/src/window.c
+++ b/src/window.c
@@ -652,15 +652,16 @@ Return nil for an internal window or a deleted window.  */)
 
 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 @@ after that.  */)
 	  : (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 @@ 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 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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
 	   ? 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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
     /* `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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
       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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
     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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
   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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
 	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 @@ set correctly.  See the code of `split-window' for how this is done.  */)
   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 @@ Signal an error when WINDOW is the only window on its frame.  */)
 	}
       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 @@ displayed after a scrolling operation to be somewhat inaccurate.  */);
     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: Type: text/plain, Size: 842 bytes --]


Here is martin's changelog:

Have 'split-window' optionally resurrect deleted windows

* src/window.c (Fwindow_old_buffer): Handle deleted window as
argument.
(make_parent_window): Remove function.
(Fsplit_window_internal): New argument REFER for resurrecting
deleted windows.  Incorporate functionality of defunct
make_parent_window.
(Fdelete_window_internal, delete_all_child_windows): Store any
deleted window's buffer in the window's old_buffer slot.
(window_dead_windows_table): Make it a 'value' type hash table
so the sequence number cannot affect its weakness.
* lisp/window.el (split-window): New argument REFER.
* doc/lispref/windows.texi (Resurrecting Windows): New section.
(Splitting Windows): Explain new argument REFER.
(Window Hooks): Rewrite description of 'window-old-buffer'.

-- 
Pranshu Sharma <https://p.bauherren.ovh>

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2025-01-01  7:09 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-01  7:09 Functions transpose/rotate/flip windows Pranshu Sharma via Emacs development discussions.

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).