Subject: bug#56791: [PATCH] Add window arg to splitting commands, new commands to split root window

From: Hugo Heagren <[email-masked]>
Date: Wed, 27 Jul 2022 08:58:53 +0100

Some proposed new commands and relevant bindings.

Motivation: sometimes, I have a complex window setup, and I want a
large new window to work in for a while, taking up a high proportion
of the frame. The currently-available interactive commands for
splitting windows only operate on the the selected window. For
example, I might have a frame with windows like this:

,----
| +---------------------+
| |                     |
| |         a           |
| |                     |
| +---------------------+
| |                     |
| |         b           |
| |                     |
| +---------------------+
`----

And want to have this:

#+END_EXAMPLE
+---------+-----------+
|         |           |
|   a     |           |
|         |           |
+---------+     c     |
|         |           |
|   b     |           |
|         |           |
+---------+-----------+
#+END_EXAMPLE

So, I have implemented:
- a new optional argument on `split-window-right' and
  `split-window-below' specifying /which/ window is to be split. This
  defaults to the selected window. It also has no effect on the
  interactive use of these functions, since the prefix argument is
  still used for the SIZE arg.
- new commands (implemented using this arg) which split the root window.
- bound these commands to `C-x 7' and `C-x 9'. This seemed
  appropriate, considering that these numbers were free, but the other
  window-splitting commands are bound to numbers in that map.
- (hopefully?) appropriate documentation changes

Patches are attached. I don't know if Emacs has conventions for how
large commits should be---mine are very atomic. Hope that works, but I
could squash them down further and combine the messages if necessary.

I haven't edited the NEWS file yet. I wanted to see what the devs
think of this and make any necessary changes before doing that.

I haven't contributed to the Emacs core before, but I have assigned
copyright to the FSF. Certificate attached as well. 

Thanks

Hugo
>From bc53825b31eb8130a43a79d2fd7643a187f5e04b Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Tue, 26 Jul 2022 21:15:08 +0100
Subject: [PATCH 3/6] split-window-right: Add WINDOW-TO-SPLIT argument

* lisp/window.el (split-window-right): Add optional argument to
control which window is split (previously, would only split selected
window).  Update docstring.
* doc/lispref/windows.texi (Splitting Windows): Update docs for
`split-window-right'.
---
 doc/lispref/windows.texi |  9 +++++----
 lisp/window.el           | 21 ++++++++++-----------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 51e54ebcd4..3b0c4e4197 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1470,10 +1470,11 @@ Splitting Windows
    For interactive use, Emacs provides two commands which always split
 the selected window.  These call @code{split-window} internally.
 
-@deffn Command split-window-right &optional size
-This function splits the selected window into two side-by-side
-windows, putting the selected window on the left.  If @var{size} is
-positive, the left window gets @var{size} columns; if @var{size} is
+@deffn Command split-window-right &optional size window-to-split
+This function splits the window @var{window-to-split} into two
+side-by-side windows, putting @var{window-to-split} on the left.
+@var{window-to-split} defaults to the selected window.  If @var{size}
+is positive, the left window gets @var{size} columns; if @var{size} is
 negative, the right window gets @minus{}@var{size} columns.
 @end deffn
 
diff --git a/lisp/window.el b/lisp/window.el
index 556fcddf47..9acc393f30 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5734,11 +5734,10 @@ split-root-window-below
   (interactive "P")
   (split-window-below size (frame-root-window)))
 
-(defun split-window-right (&optional size)
-  "Split the selected window into two side-by-side windows.
-The selected window is on the left.  The newly split-off window
-is on the right and displays the same buffer.  Return the new
-window.
+(defun split-window-right (&optional size window-to-split)
+  "Split WINDOW-TO-SPLIT into two side-by-side windows.
+WINDOW-TO-SPLIT is on the left.  The newly split-off window is on
+the right and displays the same buffer.  Return the new window.
 
 If optional argument SIZE is omitted or nil, both windows get the
 same width, or close to it.  If SIZE is positive, the left-hand
@@ -5747,16 +5746,16 @@ split-window-right
 the width of the window's scroll bar; if there are no scroll
 bars, it includes the width of the divider column to the window's
 right, if any."
-  (interactive "P")
-  (let ((old-window (selected-window))
-	(size (and size (prefix-numeric-value size)))
-	new-window)
+  (interactive `(,(when current-prefix-arg
+                    (prefix-numeric-value current-prefix-arg))
+                 ,(selected-window)))
+  (let (new-window)
     (when (and size (< size 0) (< (- size) window-min-width))
       ;; `split-window' would not signal an error here.
       (error "Size of new window too small"))
-    (setq new-window (split-window nil size t))
+    (setq new-window (split-window window-to-split size t))
     ;; Always copy quit-restore parameter in interactive use.
-    (let ((quit-restore (window-parameter old-window 'quit-restore)))
+    (let ((quit-restore (window-parameter window-to-split 'quit-restore)))
       (when quit-restore
 	(set-window-parameter new-window 'quit-restore quit-restore)))
     new-window))
-- 
2.20.1

>From b211505e9ddc4a2685f55f2afb43bcf5c4a7b860 Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Tue, 26 Jul 2022 21:12:03 +0100
Subject: [PATCH 1/6] split-window-below: Add WINDOW-TO-SPLIT argument

* lisp/window.el (split-window-below): Add optional argument to
control which window is split (previously, would only split selected
window).  Update docstring.
* doc/lispref/windows.texi (Splitting Windows): Update docs for
`split-window-below'.
---
 doc/lispref/windows.texi |  9 +++++----
 lisp/window.el           | 26 +++++++++++++-------------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 704ed30366..255e33ad0d 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1477,10 +1477,11 @@ Splitting Windows
 negative, the right window gets @minus{}@var{size} columns.
 @end deffn
 
-@deffn Command split-window-below &optional size
-This function splits the selected window into two windows, one above
-the other, leaving the upper window selected.  If @var{size} is
-positive, the upper window gets @var{size} lines; if @var{size} is
+@deffn Command split-window-below &optional size window-to-split
+This function splits the window @var{window-to-split} into two
+windows, one above the other, leaving the upper window selected.
+@var{window-to-split} defaults to the selected window.  If @var{size}
+is positive, the upper window gets @var{size} lines; if @var{size} is
 negative, the lower window gets @minus{}@var{size} lines.
 @end deffn
 
diff --git a/lisp/window.el b/lisp/window.el
index eba888a89d..69089e63f5 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5672,9 +5672,9 @@ split-window-keep-point
   :type 'boolean
   :group 'windows)
 
-(defun split-window-below (&optional size)
-  "Split the selected window into two windows, one above the other.
-The selected window is above.  The newly split-off window is
+(defun split-window-below (&optional size window-to-split)
+  "Split WINDOW-TO-SPLIT into two windows, one above the other.
+WINDOW-TO-SPLIT is above.  The newly split-off window is
 below and displays the same buffer.  Return the new window.
 
 If optional argument SIZE is omitted or nil, both windows get the
@@ -5683,22 +5683,22 @@ split-window-below
 lower (new) window gets -SIZE lines.
 
 If the variable `split-window-keep-point' is non-nil, both
-windows get the same value of point as the selected window.
+windows get the same value of point as the WINDOW-TO-SPLIT.
 Otherwise, the window starts are chosen so as to minimize the
 amount of redisplay; this is convenient on slow terminals."
-  (interactive "P")
-  (let ((old-window (selected-window))
-	(old-point (window-point))
-	(size (and size (prefix-numeric-value size)))
+  (interactive `(,(when current-prefix-arg
+                    (prefix-numeric-value current-prefix-arg))
+                 ,(selected-window)))
+  (let ((old-point (window-point))
         moved-by-window-height moved new-window bottom)
     (when (and size (< size 0) (< (- size) window-min-height))
       ;; `split-window' would not signal an error here.
       (error "Size of new window too small"))
-    (setq new-window (split-window nil size))
+    (setq new-window (split-window window-to-split size))
     (unless split-window-keep-point
-      (with-current-buffer (window-buffer)
+      (with-current-buffer (window-buffer window-to-split)
 	;; Use `save-excursion' around vertical movements below
-	;; (Bug#10971).  Note: When the selected window's buffer has a
+	;; (Bug#10971).  Note: When WINDOW-TO-SPLIT's buffer has a
 	;; header line, up to two lines of the buffer may not show up
 	;; in the resulting configuration.
 	(save-excursion
@@ -5713,13 +5713,13 @@ split-window-below
 	  (setq bottom (point)))
 	(and moved-by-window-height
 	     (<= bottom (point))
-	     (set-window-point old-window (1- bottom)))
+	     (set-window-point window-to-split (1- bottom)))
 	(and moved-by-window-height
 	     (<= (window-start new-window) old-point)
 	     (set-window-point new-window old-point)
 	     (select-window new-window))))
     ;; Always copy quit-restore parameter in interactive use.
-    (let ((quit-restore (window-parameter old-window 'quit-restore)))
+    (let ((quit-restore (window-parameter window-to-split 'quit-restore)))
       (when quit-restore
 	(set-window-parameter new-window 'quit-restore quit-restore)))
     new-window))
-- 
2.20.1

>From 7e353426525100e0bd60946866809c671fcf2789 Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Tue, 26 Jul 2022 21:26:25 +0100
Subject: [PATCH 6/6] Bind `split-root-window-right' to C-x 9

* lisp/window.el (ctl-x-map): Bind `split-root-window-right' to 9 in
ctl-x-map.  This is consistent with binding other window-splitting
operations to numbers in this map.
---
 lisp/window.el | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lisp/window.el b/lisp/window.el
index 6c5ffe9e29..7ace309906 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -10583,6 +10583,7 @@ ctl-x-map
 (define-key ctl-x-map "-" 'shrink-window-if-larger-than-buffer)
 (define-key ctl-x-map "+" 'balance-windows)
 (define-key ctl-x-map "7" 'split-root-window-below)
+(define-key ctl-x-map "9" 'split-root-window-right)
 (define-key ctl-x-4-map "0" 'kill-buffer-and-window)
 (define-key ctl-x-4-map "1" 'same-window-prefix)
 (define-key ctl-x-4-map "4" 'other-window-prefix)
-- 
2.20.1

>From ddff29ab2fb6b29d16cb0ef37c99ae2fb402f108 Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Tue, 26 Jul 2022 21:25:40 +0100
Subject: [PATCH 5/6] Bind `split-root-window-below' to C-x 7

* lisp/window.el (ctl-x-map): Bind `split-root-window-below' to 7 in
ctl-x-map.  This is consistent with binding other window-splitting
operations to numbers in this map.
---
 lisp/window.el | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lisp/window.el b/lisp/window.el
index c23f3a54f2..6c5ffe9e29 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -10582,6 +10582,7 @@ ctl-x-map
 (define-key ctl-x-map "{" 'shrink-window-horizontally)
 (define-key ctl-x-map "-" 'shrink-window-if-larger-than-buffer)
 (define-key ctl-x-map "+" 'balance-windows)
+(define-key ctl-x-map "7" 'split-root-window-below)
 (define-key ctl-x-4-map "0" 'kill-buffer-and-window)
 (define-key ctl-x-4-map "1" 'same-window-prefix)
 (define-key ctl-x-4-map "4" 'other-window-prefix)
-- 
2.20.1

>From 746ae2161fac4f9a54eac1ec9cdb4a8d3269e9c8 Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Tue, 26 Jul 2022 21:17:46 +0100
Subject: [PATCH 4/6] split-root-window-right: New function

* lisp/window.el (split-root-window-right): New function to split
whole frame.
* doc/lispref/windows.texi (Splitting Windows): Add documentation for
`split-root-window-right'.
---
 doc/lispref/windows.texi | 6 ++++++
 lisp/window.el           | 9 +++++++++
 2 files changed, 15 insertions(+)

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 3b0c4e4197..6734fd3e48 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1491,6 +1491,12 @@ Splitting Windows
 configuration is retained on the top, and a new window is created
 below, taking up the whole width of the frame.  @var{size} is treated
 as by @code{split-window-below}.
+
+@deffn Command split-root-window-right &optional size
+This function splits the whole frame in two.  The current window
+configuration is retained on the left, and a new window is created on
+the right, taking up the whole height of the frame.  @var{size} is treated
+as by @code{split-window-right}.
 @end deffn
 
 @defopt split-window-keep-point
diff --git a/lisp/window.el b/lisp/window.el
index 9acc393f30..c23f3a54f2 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5761,6 +5761,15 @@ split-window-right
     new-window))
 
 (defalias 'split-window-horizontally 'split-window-right)
+
+(defun split-root-window-right (&optional size)
+  "Split root window of current frame into two side-by-side windows.
+The current window configuration is retained within the left
+window, and a new window is created on the right, taking up the
+whole height of the frame.  SIZE is treated as by
+`split-window-right'."
+  (interactive "P")
+  (split-window-right size (frame-root-window)))
 
 ;;; Balancing windows.
 
-- 
2.20.1

>From c1c9f28a03d46595a53561036cf0fb57e340d8e1 Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Tue, 26 Jul 2022 22:37:21 +0100
Subject: [PATCH 2/6] split-root-window-below: New function

* lisp/window.el (split-root-window-below): New function to split
whole frame.
* doc/lispref/windows.texi (Splitting Windows): Add documentation for
`split-root-window-below'.
---
 doc/lispref/windows.texi | 7 +++++++
 lisp/window.el           | 8 ++++++++
 2 files changed, 15 insertions(+)

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 255e33ad0d..51e54ebcd4 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1485,6 +1485,13 @@ Splitting Windows
 negative, the lower window gets @minus{}@var{size} lines.
 @end deffn
 
+@deffn Command split-root-window-below &optional size
+This function splits the whole frame in two.  The current window
+configuration is retained on the top, and a new window is created
+below, taking up the whole width of the frame.  @var{size} is treated
+as by @code{split-window-below}.
+@end deffn
+
 @defopt split-window-keep-point
 If the value of this variable is non-@code{nil} (the default),
 @code{split-window-below} behaves as described above.
diff --git a/lisp/window.el b/lisp/window.el
index 69089e63f5..556fcddf47 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5726,6 +5726,14 @@ split-window-below
 
 (defalias 'split-window-vertically 'split-window-below)
 
+(defun split-root-window-below (&optional size)
+  "Split root window of current frame in two.
+The current window configuration is retained in the top window,
+the lower window takes up the whole width of the frame.  SIZE is
+handled as in `split-window-below'."
+  (interactive "P")
+  (split-window-below size (frame-root-window)))
+
 (defun split-window-right (&optional size)
   "Split the selected window into two side-by-side windows.
 The selected window is on the left.  The newly split-off window
-- 
2.20.1