1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
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
|