* Add function to rotate/transpose all windows
@ 2024-09-24 13:45 pranshu sharma
2024-09-24 13:53 ` Eli Zaretskii
` (2 more replies)
0 siblings, 3 replies; 85+ messages in thread
From: pranshu sharma @ 2024-09-24 13:45 UTC (permalink / raw)
To: emacs-devel
An useful for function for C-x w map is to add a function to rotate
windows.
What I mean by this is basiclly rebuilt the window tree, but do vertical
split where a horizonal, and vice versa.
For example if we rotate the the frame with the windows A B C:
- | A | B | | A | |
- |-------| - > |---| C |
- | C | | B | |
I think this is extremly useful, as sometimes for example I open a
window with a horizontal split, and the lines are rather long, then I
have to manually toggle the window split to vertical.
An example of this:
https://www.emacswiki.org/emacs/TransposeFrame
The code in this has the following limitations:
- Does not work in emacs 29.1
- No good way to transpose only specific parts of window tree
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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-24 17:40 ` Petteri Hintsanen
2024-09-24 19:34 ` Charles Choi
2 siblings, 1 reply; 85+ messages in thread
From: Eli Zaretskii @ 2024-09-24 13:53 UTC (permalink / raw)
To: pranshu sharma, martin rudalics; +Cc: emacs-devel
> From: pranshu sharma <pranshusharma366@gmail.com>
> Date: Tue, 24 Sep 2024 23:45:42 +1000
>
>
> An useful for function for C-x w map is to add a function to rotate
> windows.
>
> What I mean by this is basiclly rebuilt the window tree, but do vertical
> split where a horizonal, and vice versa.
>
> For example if we rotate the the frame with the windows A B C:
> - | A | B | | A | |
> - |-------| - > |---| C |
> - | C | | B | |
>
> I think this is extremly useful, as sometimes for example I open a
> window with a horizontal split, and the lines are rather long, then I
> have to manually toggle the window split to vertical.
>
> An example of this:
> https://www.emacswiki.org/emacs/TransposeFrame
> The code in this has the following limitations:
> - Does not work in emacs 29.1
> - No good way to transpose only specific parts of window tree
Thanks.
Martin, any comments or suggestions?
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-24 13:45 Add function to rotate/transpose all windows pranshu sharma
2024-09-24 13:53 ` Eli Zaretskii
@ 2024-09-24 17:40 ` Petteri Hintsanen
2024-09-24 19:34 ` Charles Choi
2 siblings, 0 replies; 85+ messages in thread
From: Petteri Hintsanen @ 2024-09-24 17:40 UTC (permalink / raw)
To: pranshu sharma; +Cc: emacs-devel
pranshu sharma <pranshusharma366@gmail.com> writes:
> An useful for function for C-x w map is to add a function to rotate
> windows.
>
> What I mean by this is basiclly rebuilt the window tree, but do vertical
> split where a horizonal, and vice versa.
>
> For example if we rotate the the frame with the windows A B C:
> - | A | B | | A | |
> - |-------| - > |---| C |
> - | C | | B | |
I agree, this would be a useful feature.
Despite a hint of self-promotion I'd like to point out a minor mode I
wrote a long time ago: http://iki.fi/petterih/cwm-mode.html
It can do something like that, though not exactly.
Having a general facility built into Emacs would be better.
--
Petteri
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-24 13:45 Add function to rotate/transpose all windows pranshu sharma
2024-09-24 13:53 ` Eli Zaretskii
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
2 siblings, 2 replies; 85+ messages in thread
From: Charles Choi @ 2024-09-24 19:34 UTC (permalink / raw)
To: emacs-devel; +Cc: pranshu sharma
Hi folks!
As a side note, I received an issue that a package that I had recently published on MELPA required a dependency on transpose-frame. Investigating further, it seems that transpose-frame was ad-hoc packaged and it is unclear who actually maintains this code.
https://github.com/emacsorphanage/transpose-frame/blob/master/transpose-frame.el
My observation is that what transpose-frame is trying to do is so useful, that it would nice to have such behavior be “built-in” to Emacs core. I am not saying that this package should be included “as-is” though. I leave it to the core team to determine how to best implement this behavior.
All my best -
Charles
—
Charles Y. Choi, Ph.D.
kickingvegas@gmail.com
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-24 19:34 ` Charles Choi
@ 2024-09-25 2:00 ` Emanuel Berg
2024-09-25 7:00 ` pranshu sharma
1 sibling, 0 replies; 85+ messages in thread
From: Emanuel Berg @ 2024-09-25 2:00 UTC (permalink / raw)
To: emacs-devel
Charles Choi wrote:
> As a side note, I received an issue that a package that
> I had recently published on MELPA required a dependency on
> transpose-frame. Investigating further, it seems that
> transpose-frame was ad-hoc packaged and it is unclear who
> actually maintains this code.
>
> https://github.com/emacsorphanage/transpose-frame/blob/master/transpose-frame.el
>
> My observation is that what transpose-frame is trying to do
> is so useful, that it would nice to have such behavior be
> "built-in" to Emacs core. I am not saying that this package
> should be included "as-is" though. I leave it to the core
> team to determine how to best implement this behavior.
There is a lot of transpose already in core Emacs:
transpose-chars
transpose-lines
transpose-paragraphs
transpose-regions
transpose-sentences
transpose-sexps
transpose-sexps-default-function
transpose-subr
transpose-subr-1
transpose-words
Slime has a general list transposer:
(defun slime-transpose-lists (list-of-lists)
(let ((ncols (length (car list-of-lists))))
(cl-loop for col-index below ncols
collect (cl-loop for row in list-of-lists
collect (elt row col-index)))))
And I have a transposer that works for nested elements, here:
https://dataswamp.org/~incal/bad/class/elem/bad-rotate.el
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-24 19:34 ` Charles Choi
2024-09-25 2:00 ` Emanuel Berg
@ 2024-09-25 7:00 ` pranshu sharma
1 sibling, 0 replies; 85+ messages in thread
From: pranshu sharma @ 2024-09-25 7:00 UTC (permalink / raw)
To: Charles Choi; +Cc: emacs-devel
Charles Choi <kickingvegas@gmail.com> writes:
> Hi folks!
>
> As a side note, I received an issue that a package that I had recently
> published on MELPA required a dependency on
> transpose-frame. Investigating further, it seems that transpose-frame
> was ad-hoc packaged and it is unclear who actually maintains this
> code.
>
I managed to get the name of the author, Irie Shinsuke, from:
https://code.launchpad.net/~irie/+junk/transpose-frame.el
However I could not find the email.
The last comit was in early 2011.
> https://github.com/emacsorphanage/transpose-frame/blob/master/transpose-frame.el
>
> My observation is that what transpose-frame is trying to do is so
> useful, that it would nice to have such behavior be “built-in” to
> Emacs core. I am not saying that this package should be included
> “as-is” though. I leave it to the core team to determine how to best
> implement this behavior.
>
>
> All my best -
> Charles
>
> —
> Charles Y. Choi, Ph.D.
> kickingvegas@gmail.com
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-24 13:53 ` Eli Zaretskii
@ 2024-09-25 8:05 ` martin rudalics
2024-09-25 8:34 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-25 8:05 UTC (permalink / raw)
To: Eli Zaretskii, pranshu sharma; +Cc: emacs-devel
>> https://www.emacswiki.org/emacs/TransposeFrame
>> The code in this has the following limitations:
>> - Does not work in emacs 29.1
It works here when I remove the
(window-redisplay-end-trigger tree)
(set-window-redisplay-end-trigger window (pop config))
lines in such versions.
>> - No good way to transpose only specific parts of window tree
Then the FRAME arguments should probably become WINDOW-OR-FRAME and
accept arbitrary internal windows as value.
> Thanks.
>
> Martin, any comments or suggestions?
The problem of any such package is that Emacs never provided a suitable
balance between explicit management of objects like buffers or windows
(via 'kill-buffer' and 'delete-window') and the subsequent internal
handling of these objects.
In particular, the 'delete-window'/'split-window' paradigm may fail when
window objects have been stored in variables. Take this excerpt from
TransposeFrame
(if (eq (overlay-get ol 'window) orig-window)
(overlay-put ol 'window window))))
If this gets called within a 'save-window-excursion', then the 'window'
property of overlays may change from 'orig-window' to 'window'. When
'save-window-excursion' exits and restores 'orig-window' and deletes
'window', these overlays will now reference a dead window. This problem
could be partially fixed by using the 'clone-of' parameter of the
window. But that would fail when windows are rotated a second time.
Such problems could be fixed if we were able to reference windows by
their number (or an arbitrary name) and have Lisp references to a window
use that number instead of the object. It might, however, take
considerable time to have such a concept enter the minds of people used
to storing window identities as Lisp objects.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-25 8:05 ` martin rudalics
@ 2024-09-25 8:34 ` pranshu sharma
2024-09-25 9:31 ` martin rudalics
2024-09-26 14:10 ` martin rudalics
0 siblings, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-09-25 8:34 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>>> https://www.emacswiki.org/emacs/TransposeFrame
>>> The code in this has the following limitations:
>>> - Does not work in emacs 29.1
>
> It works here when I remove the
>
> (window-redisplay-end-trigger tree)
>
> (set-window-redisplay-end-trigger window (pop config))
>
> lines in such versions.
>
>>> - No good way to transpose only specific parts of window tree
>
> Then the FRAME arguments should probably become WINDOW-OR-FRAME and
> accept arbitrary internal windows as value.
>
>> Thanks.
>>
>> Martin, any comments or suggestions?
>
> The problem of any such package is that Emacs never provided a suitable
> balance between explicit management of objects like buffers or windows
> (via 'kill-buffer' and 'delete-window') and the subsequent internal
> handling of these objects.
>
> In particular, the 'delete-window'/'split-window' paradigm may fail when
> window objects have been stored in variables. Take this excerpt from
> TransposeFrame
>
> (if (eq (overlay-get ol 'window) orig-window)
> (overlay-put ol 'window window))))
>
> If this gets called within a 'save-window-excursion', then the 'window'
> property of overlays may change from 'orig-window' to 'window'. When
> 'save-window-excursion' exits and restores 'orig-window' and deletes
> 'window', these overlays will now reference a dead window. This problem
> could be partially fixed by using the 'clone-of' parameter of the
> window. But that would fail when windows are rotated a second time.
From what I understand, the main problems are happening when destroying
and recreating the window arragnment. How about adding a function that
toggles or changes the window arrangment non recursivly, which will deal
with all the backend details, and the main rotate function can just
recursivly call that on all windows.
>
> Such problems could be fixed if we were able to reference windows by
> their number (or an arbitrary name) and have Lisp references to a window
> use that number instead of the object. It might, however, take
> considerable time to have such a concept enter the minds of people used
> to storing window identities as Lisp objects.
>
> martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-25 8:34 ` pranshu sharma
@ 2024-09-25 9:31 ` martin rudalics
2024-09-25 10:50 ` pranshu sharma
2024-09-26 14:10 ` martin rudalics
1 sibling, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-25 9:31 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
>>From what I understand, the main problems are happening when destroying
> and recreating the window arragnment.
Right.
> How about adding a function that
> toggles or changes the window arrangment non recursivly, which will deal
> with all the backend details, and the main rotate function can just
> recursivly call that on all windows.
How would you "deal with all the backend details"? How would you
convince Emacs that the windows B and C in the right configuration
below
- | A | B | | A | |
- |-------| - > |---| C |
- | C | | B | |
are the windows B and C from the left configuration (albeit with
different parents and sizes)?
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-25 9:31 ` martin rudalics
@ 2024-09-25 10:50 ` pranshu sharma
2024-09-25 13:53 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-25 10:50 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>>>From what I understand, the main problems are happening when destroying
>> and recreating the window arragnment.
>
> Right.
>
>> How about adding a function that
>> toggles or changes the window arrangment non recursivly, which will deal
>> with all the backend details, and the main rotate function can just
>> recursivly call that on all windows.
>
> How would you "deal with all the backend details"? How would you
> convince Emacs that the windows B and C in the right configuration
> below
>
> - | A | B | | A | |
> - |-------| - > |---| C |
> - | C | | B | |
>
> are the windows B and C from the left configuration (albeit with
> different parents and sizes)?
I thought you would know.
If it's not possible, then what's wrong with rebuilding the trees from
scratch and clearly documenting that it's being done. I mean users will
still have to do the same thing manually, which will also destroy change
the window tree.
>
> martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-25 10:50 ` pranshu sharma
@ 2024-09-25 13:53 ` martin rudalics
2024-09-25 15:31 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-25 13:53 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> I thought you would know.
In that case I would have told you before.
> If it's not possible, then what's wrong with rebuilding the trees from
> scratch and clearly documenting that it's being done. I mean users will
> still have to do the same thing manually, which will also destroy change
> the window tree.
I don't think that anything would be wrong with such an approach. So if
you intend to rewrite transpose-frame.el, I can tell you what's needed
to get that done properly within the current limitations.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-25 13:53 ` martin rudalics
@ 2024-09-25 15:31 ` pranshu sharma
0 siblings, 0 replies; 85+ messages in thread
From: pranshu sharma @ 2024-09-25 15:31 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
> I don't think that anything would be wrong with such an approach. So if
> you intend to rewrite transpose-frame.el, I can tell you what's needed
> to get that done properly within the current limitations.
Yeah, if you think that this is the only practical way without having to
do some big rewrite, then I'm willing to rewrite tranpose-frame.el.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-25 8:34 ` pranshu sharma
2024-09-25 9:31 ` martin rudalics
@ 2024-09-26 14:10 ` martin rudalics
2024-09-26 14:22 ` Eli Zaretskii
2024-09-27 10:06 ` pranshu sharma
1 sibling, 2 replies; 85+ messages in thread
From: martin rudalics @ 2024-09-26 14:10 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 759 bytes --]
>>From what I understand, the main problems are happening when destroying
> and recreating the window arragnment. How about adding a function that
> toggles or changes the window arrangment non recursivly, which will deal
> with all the backend details, and the main rotate function can just
> recursivly call that on all windows.
I attach a function 'resurrect-window' that rotate/transpose/flip
functions can call to resurrect the previous windows after having
deleted them. Tested with:
(let ((dead (split-window nil nil t)))
(set-window-buffer dead "*Messages*")
(message "%s" (next-window))
(sit-for 2)
(delete-window dead)
(let ((live (split-window)))
(resurrect-window dead live)
(message "%s" (next-window))))
martin
[-- Attachment #2: resurrect.diff --]
[-- Type: text/x-patch, Size: 5812 bytes --]
diff --git a/src/window.c b/src/window.c
index 34968ac824f..312619737f3 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5368,10 +5368,14 @@ DEFUN ("delete-window-internal", Fdelete_window_internal, Sdelete_window_interna
}
else
{
+ w->del_pointm = marker_position (w->pointm);
+ w->del_start = marker_position (w->start);
+
unshow_buffer (w);
unchain_marker (XMARKER (w->pointm));
unchain_marker (XMARKER (w->old_pointm));
unchain_marker (XMARKER (w->start));
+ wset_old_buffer (w, w->contents);
wset_buffer (w, Qnil);
/* Add WINDOW to table of dead windows so when killing a buffer
WINDOW mentions, all references to that buffer can be removed
@@ -5437,6 +5441,97 @@ DEFUN ("delete-window-internal", Fdelete_window_internal, Sdelete_window_interna
return Qnil;
}
+
+
+DEFUN ("resurrect-window", Fresurrect_window, Sresurrect_window, 2, 2, 0,
+ doc: /* Resurrect dead window DEAD in lieu of live window LIVE.
+DEAD must denote a dead window that was a live window before it was
+deleted. LIVE must denote a live window. Put the window object of DEAD
+in the place of LIVE such that the resulting window inherits LIVE's
+geometric properties and its position within the window tree while all
+other properties including the buffer and its positions, decorations and
+parameters are inherited from DEAD.
+
+DEAD must have been on the same frame as LIVE and its old buffer must be
+still live. If LIVE was selected or its frame's selected window, select
+DEAD or make it its frame's selected window instead. */)
+ (Lisp_Object dead, Lisp_Object live)
+{
+ struct window *l = decode_live_window (live);
+ struct window *d = decode_any_window (dead);
+ Lisp_Object frame = WINDOW_FRAME (l);
+ struct frame *f = XFRAME (frame);
+ Lisp_Object tem;
+ bool selected = EQ (live, selected_window);
+ bool frame_selected = EQ (live, f->selected_window);
+
+ if (!NILP (d->contents))
+ error ("Attempt to resurrect undead window");
+ else if (!BUFFERP (d->old_buffer))
+ error ("Dead window has no old buffer");
+ else if (!BUFFER_LIVE_P (XBUFFER (d->old_buffer)))
+ error ("Dead window's old buffer is dead");
+ else if (!EQ (l->frame, d->frame))
+ error ("Live and dead widows must be on same frame");
+
+ block_input ();
+
+ /* Copy links and geometry of LIVE into DEAD. */
+ memcpy (&d->next, &l->next,
+ offsetof (struct window, contents)
+ - offsetof (struct window, next));
+ memcpy (&d->pixel_left, &l->pixel_left,
+ offsetof (struct window, hscroll)
+ - offsetof (struct window, pixel_left));
+
+ /* Replace LIVE in window tree with DEAD. */
+ if (!NILP (d->next))
+ wset_prev (XWINDOW (d->next), dead);
+
+ if (!NILP (d->prev))
+ wset_next (XWINDOW (d->prev), dead);
+
+ tem = d->parent;
+ if (!NILP (tem) && EQ (XWINDOW (tem)->contents, live))
+ wset_combination (XWINDOW (tem), XWINDOW (tem)->horizontal, dead);
+
+ /* Get DEAD back its old buffer and markers. */
+ wset_buffer (d, d->old_buffer);
+ Fset_marker (d->start, make_fixnum (d->del_start), d->contents);
+ Fset_marker (d->pointm, make_fixnum (d->del_pointm), d->contents);
+
+ /* Deal with LIVE. */
+ wset_next (l, Qnil); /* Don't delete l->next too. */
+ free_window_matrices (l);
+ l->del_pointm = marker_position (l->pointm);
+ l->del_start = marker_position (l->start);
+ unshow_buffer (l);
+ unchain_marker (XMARKER (l->pointm));
+ unchain_marker (XMARKER (l->old_pointm));
+ unchain_marker (XMARKER (l->start));
+ wset_old_buffer (l, l->contents);
+ wset_buffer (l, Qnil);
+ /* Add WINDOW to table of dead windows so when killing a buffer WINDOW
+ mentions, all references to that buffer can be removed and the
+ buffer be collected. */
+ Fputhash (make_fixnum (l->sequence_number),
+ live, window_dead_windows_table);
+
+ /* Selection status. */
+ if (selected)
+ Fselect_window (dead, Qt);
+ else if (frame_selected)
+ fset_selected_window (f, dead);
+
+ /* Tell redisplay. */
+ fset_redisplay (f);
+ Vwindow_list = Qnil;
+ adjust_frame_glyphs (f);
+ unblock_input ();
+ FRAME_WINDOW_CHANGE (f) = true;
+
+ return Qnil;
+}
\f
/***********************************************************************
Resizing Mini-Windows
@@ -7712,6 +7807,9 @@ delete_all_child_windows (Lisp_Object window)
}
else if (BUFFERP (w->contents))
{
+ w->del_pointm = marker_position (w->pointm);
+ w->del_start = marker_position (w->start);
+
unshow_buffer (w);
unchain_marker (XMARKER (w->pointm));
unchain_marker (XMARKER (w->old_pointm));
@@ -7720,6 +7818,7 @@ delete_all_child_windows (Lisp_Object window)
only, we use this slot to save the buffer for the sake of
possible resurrection in Fset_window_configuration. */
wset_combination_limit (w, w->contents);
+ wset_old_buffer (w, w->contents);
wset_buffer (w, Qnil);
/* Add WINDOW to table of dead windows so when killing a buffer
WINDOW mentions, all references to that buffer can be removed
@@ -9146,6 +9245,7 @@ syms_of_window (void)
defsubr (&Sget_buffer_window);
defsubr (&Sdelete_other_windows_internal);
defsubr (&Sdelete_window_internal);
+ defsubr (&Sresurrect_window);
defsubr (&Sresize_mini_window_internal);
defsubr (&Sset_window_buffer);
defsubr (&Srun_window_configuration_change_hook);
diff --git a/src/window.h b/src/window.h
index 335e0a3453e..41ee7f6dc18 100644
--- a/src/window.h
+++ b/src/window.h
@@ -264,6 +264,10 @@ #define WINDOW_H_INCLUDED
int total_cols;
int total_lines;
+ /* Positions of pointm and start when window was deleted. */
+ ptrdiff_t del_pointm;
+ ptrdiff_t del_start;
+
/* Number of columns display within the window is scrolled to the left. */
ptrdiff_t hscroll;
^ permalink raw reply related [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-26 14:10 ` martin rudalics
@ 2024-09-26 14:22 ` Eli Zaretskii
2024-09-27 17:29 ` martin rudalics
2024-09-27 10:06 ` pranshu sharma
1 sibling, 1 reply; 85+ messages in thread
From: Eli Zaretskii @ 2024-09-26 14:22 UTC (permalink / raw)
To: martin rudalics; +Cc: pranshusharma366, emacs-devel
> Date: Thu, 26 Sep 2024 16:10:48 +0200
> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
>
> I attach a function 'resurrect-window' that rotate/transpose/flip
> functions can call to resurrect the previous windows after having
> deleted them. Tested with:
>
> (let ((dead (split-window nil nil t)))
> (set-window-buffer dead "*Messages*")
> (message "%s" (next-window))
> (sit-for 2)
> (delete-window dead)
> (let ((live (split-window)))
> (resurrect-window dead live)
> (message "%s" (next-window))))
Thanks.
> +DEFUN ("resurrect-window", Fresurrect_window, Sresurrect_window, 2, 2, 0,
> + doc: /* Resurrect dead window DEAD in lieu of live window LIVE.
> +DEAD must denote a dead window that was a live window before it was
> +deleted. LIVE must denote a live window. Put the window object of DEAD
> +in the place of LIVE such that the resulting window inherits LIVE's
> +geometric properties and its position within the window tree while all
> +other properties including the buffer and its positions, decorations and
> +parameters are inherited from DEAD.
> +
> +DEAD must have been on the same frame as LIVE and its old buffer must be
> +still live. If LIVE was selected or its frame's selected window, select
> +DEAD or make it its frame's selected window instead. */)
This doc string needs to be more detailed in what it means to "put the
window object of DEAD in the place of LIVE". For example, it
currently keeps silent about what happens to LIVE after the call.
> + /* Add WINDOW to table of dead windows so when killing a buffer WINDOW
> + mentions, all references to that buffer can be removed and the
> + buffer be collected. */
There's no WINDOW argument. I think you meant LIVE instead.
Finally, this needs to be documented in the ELisp manual and called
out in NEWS.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-26 14:10 ` martin rudalics
2024-09-26 14:22 ` Eli Zaretskii
@ 2024-09-27 10:06 ` pranshu sharma
2024-09-27 17:29 ` martin rudalics
1 sibling, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-27 10:06 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>>>From what I understand, the main problems are happening when destroying
>> and recreating the window arragnment. How about adding a function that
>> toggles or changes the window arrangment non recursivly, which will deal
>> with all the backend details, and the main rotate function can just
>> recursivly call that on all windows.
>
> I attach a function 'resurrect-window' that rotate/transpose/flip
> functions can call to resurrect the previous windows after having
> deleted them.
Thanks, I tested it and it works.
I'll get back when I've rewrote transpose frame
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-26 14:22 ` Eli Zaretskii
@ 2024-09-27 17:29 ` martin rudalics
2024-09-28 7:52 ` pranshu sharma
` (2 more replies)
0 siblings, 3 replies; 85+ messages in thread
From: martin rudalics @ 2024-09-27 17:29 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: pranshusharma366, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 831 bytes --]
> This doc string needs to be more detailed in what it means to "put the
> window object of DEAD in the place of LIVE". For example, it
> currently keeps silent about what happens to LIVE after the call.
The fact that LIVE existed at all was a misfeature. I now put the dead
window right into the new window created by 'split-window' with the help
of a new argument. Patch attached. Tested with
(let ((dead (split-window nil nil t)))
(set-window-buffer dead "*Messages*")
(message "%s" (next-window))
(sit-for 2)
(delete-window dead)
(let ((live (split-window nil nil nil nil dead)))
(message "%s" (next-window))))
> Finally, this needs to be documented in the ELisp manual and called
> out in NEWS.
Sure. I'll wait until the OP approves the concept and was able to make
use of it.
Thanks, martin
[-- Attachment #2: resurrect.diff --]
[-- Type: text/x-patch, Size: 12834 bytes --]
diff --git a/lisp/window.el b/lisp/window.el
index 07ea9584908..b96a7863a72 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5509,7 +5509,7 @@ 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.
@@ -5552,11 +5552,21 @@ split-window
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."
+If the optional fifth argument REFER is non-nil, it has to denote a
+dead, former live window on the same frame as OLD or an arbitrary live
+window. In the first case, REFER will become the new window with
+properties like buffer, start and point, decorations and parameters as
+to the last time when it was live. In the latter case the new window
+will inherit properties like buffer, start and point, decorations and
+parameters 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.
+
+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)
@@ -5596,7 +5606,7 @@ 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 is a side window or its first or last child is a
;; side window, throw an error unless `window-combination-resize'
;; equals 'side.
@@ -5635,8 +5645,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)
@@ -5757,8 +5767,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..402bb0459e3 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5073,7 +5073,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 +5088,30 @@ 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)
+ (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side, Lisp_Object normal_size,
+ Lisp_Object refer)
{
/* 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;
+ parent. NEW (*n) is the new window created or adopted with
+ properties from REFER (*r), if specified. */
+ 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;
+ bool dead = false;
CHECK_FIXNUM (pixel_size);
EMACS_INT total_size
@@ -5131,14 +5129,38 @@ 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
- /* 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 dead, former live window. Check whether its
+ content can be used. */
+ {
+ if (!BUFFERP (r->old_buffer))
+ error ("Dead reference window was not a live window");
+ else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
+ error ("Dead reference window's old buffer is dead");
+ else if (!EQ (r->frame, frame))
+ error ("Dead referenec window was on other frame");
+
+ dead = true;
+ }
+ else if (!WINDOW_LIVE_P (refer))
+ error ("Reference window must not be internal");
+ }
/* The following bugs are caught by `split-window'. */
if (MINI_WINDOW_P (o))
@@ -5195,7 +5217,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);
@@ -5219,19 +5246,22 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal,
wset_next (o, new);
}
- n->window_end_valid = false;
- n->last_cursor_vpos = 0;
+ if (!dead)
+ {
+ 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);
+ /* 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)
@@ -5267,10 +5297,44 @@ 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);
+ Fset_marker (n->start, make_fixnum (n->del_start), n->contents);
+ Fset_marker (n->pointm, make_fixnum (n->del_pointm), n->contents);
+ Vwindow_list = Qnil;
+ }
+ else
+ {
+ /* Note: n->contents is Qnil throughout this call, so n will be
+ neither considered a leaf nor an internal window. */
+ Lisp_Object buffer = r->contents;
+ struct buffer *b = XBUFFER (buffer);
+
+ /* Provisorially set new's buffer to that of the reference window,
+ resize the parent, reset new's buffer to nil and do the real
+ set_window_buffer. */
+ wset_buffer (n, buffer);
+ set_marker_both (n->pointm, buffer, BUF_PT (b), BUF_PT_BYTE (b));
+ set_marker_restricted
+ (n->start, make_fixnum (b->last_window_start), buffer);
+ }
+
window_resize_apply (p, horflag);
+
+ if (!dead)
+ {
+ /* Set buffer of NEW to buffer of reference window. We have to do it
+ here so the sizes of NEW are in place. But be sure to do it before
+ adjusting the frame glyphs - otherwise Emacs may inexplicably loop
+ forever. */
+ wset_buffer (n, Qnil);
+ set_window_buffer (new, r->contents, true, true);
+ }
+
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,10 +5432,16 @@ DEFUN ("delete-window-internal", Fdelete_window_internal, Sdelete_window_interna
}
else
{
+ if (MARKERP (w->pointm))
+ w->del_pointm = marker_position (w->pointm);
+ if (MARKERP (w->start))
+ w->del_start = marker_position (w->start);
+
unshow_buffer (w);
unchain_marker (XMARKER (w->pointm));
unchain_marker (XMARKER (w->old_pointm));
unchain_marker (XMARKER (w->start));
+ wset_old_buffer (w, w->contents);
wset_buffer (w, Qnil);
/* Add WINDOW to table of dead windows so when killing a buffer
WINDOW mentions, all references to that buffer can be removed
@@ -7712,6 +7782,11 @@ delete_all_child_windows (Lisp_Object window)
}
else if (BUFFERP (w->contents))
{
+ if (MARKERP (w->pointm))
+ w->del_pointm = marker_position (w->pointm);
+ if (MARKERP (w->start))
+ w->del_start = marker_position (w->start);
+
unshow_buffer (w);
unchain_marker (XMARKER (w->pointm));
unchain_marker (XMARKER (w->old_pointm));
@@ -7720,6 +7795,7 @@ delete_all_child_windows (Lisp_Object window)
only, we use this slot to save the buffer for the sake of
possible resurrection in Fset_window_configuration. */
wset_combination_limit (w, w->contents);
+ wset_old_buffer (w, w->contents);
wset_buffer (w, Qnil);
/* Add WINDOW to table of dead windows so when killing a buffer
WINDOW mentions, all references to that buffer can be removed
diff --git a/src/window.h b/src/window.h
index 335e0a3453e..41ee7f6dc18 100644
--- a/src/window.h
+++ b/src/window.h
@@ -264,6 +264,10 @@ #define WINDOW_H_INCLUDED
int total_cols;
int total_lines;
+ /* Positions of pointm and start when window was deleted. */
+ ptrdiff_t del_pointm;
+ ptrdiff_t del_start;
+
/* Number of columns display within the window is scrolled to the left. */
ptrdiff_t hscroll;
^ permalink raw reply related [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-27 10:06 ` pranshu sharma
@ 2024-09-27 17:29 ` martin rudalics
0 siblings, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-09-27 17:29 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> Thanks, I tested it and it works.
I have rewritten this by giving 'split-window' a new argument. See the
patch I attached to my other mail.
> I'll get back when I've rewrote transpose frame
You might want to write your own variant of 'window--subtree' where you
would record the normal sizes of windows instead of their edges. This
would be more accurate and give you immediately a factor to apply when
splitting a window. And for rotating you might want to start with
'frame-first-window' as the sole window and then use 'above' and 'left'
as SIDE arguments of 'split-window' as you see fit.
Good luck, martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-27 17:29 ` martin rudalics
@ 2024-09-28 7:52 ` pranshu sharma
2024-09-28 9:26 ` martin rudalics
2024-09-28 7:58 ` pranshu sharma
2024-09-28 8:18 ` Eli Zaretskii
2 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-28 7:52 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
> Sure. I'll wait until the OP approves the concept and was able to make
> use of it.
Hello Martin,
I managed to make remake the transpose frame function without using
reseruct window, and instead used swap-window-states. I still have to
do some cleaning up and error handling tho, eg is still returns error
when you only have one window.
However one thing I'm stuck with it getting it to work with root C-x 2
C-x 3 kinda splits (basiclly where (listp (caddar (window-tree))) is
true). However if you start with no windows, split window once, go to
split window, they you can go crazy with splitting in any direction
(even the C-x 2 C-x 3) and it will work when you call transpose-frame.
This also different from the transpose-windows.el in that it does not
start on a blank slate(transpose-windows.el calls delete-other-windows
in the transpose-frame-set-arrangement), so transposing partial window
trees much easier but I haven't implimented it yet.
---------------------------
;; is there already another inbuilt funcion like this?
(defun deepmap(func ls)
(if (null ls)
()
(cons (if (listp (car ls))
(deepmap func (car ls))
(funcall func (car ls)))
(deepmap func (cdr ls)))))
(defun transpose-frame ()
(interactive)
(let ((fwin (car (window-tree))))
(toggle-window-split
;; We gotta get sizes now, cuz if not then window split mess em
;; up
(deepmap (lambda (e) (if (windowp e)
(cons
e (window-edges e))
e))
(car (window-tree)))
fwin
t)
))
(defun toggle-window-split (subtree cwin &optional nokill)
(pcase-let* ((`(,eee . ,flen) (if (car subtree)
(cons 1 (window-width cwin))
(cons 0 (window-height cwin))))
(ilen (float (- (nth (+ 2 eee) (cadr subtree))
(nth (+ eee 0) (cadr subtree))))))
(mapcar
(pcase-lambda (`(,win . ,size))
(if (listp win)
(progn
(toggle-window-split win (split-window cwin
(- (round (* flen (/ size ilen))))
(car subtree))))
(progn
(let ((newwin (split-window cwin
(- (round (* flen (/ size ilen))))
(car subtree))))
(window-swap-states newwin win nil)
(delete-window win)))))
(mapcar
(lambda (e)
(pcase-let ((`(,edges ,window?)
(if (windowp (car e))
(list (cdr e) (car e))
(list (cadr e) e))))
(cons window? (- (nth (+ 2 eee) edges)
(nth eee edges)))))
(nreverse (cdddr subtree))))
(unless nokill
(if (windowp (caaddr subtree))
(delete-window (caaddr subtree))
(toggle-window-split (caddr subtree) cwin)))))
---------------------------
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-27 17:29 ` martin rudalics
2024-09-28 7:52 ` pranshu sharma
@ 2024-09-28 7:58 ` pranshu sharma
2024-09-28 8:18 ` Eli Zaretskii
2 siblings, 0 replies; 85+ messages in thread
From: pranshu sharma @ 2024-09-28 7:58 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
Accidently sent wrong definition of transpose-frame. The other
functions still fine.
The correct one is:
--------------------------
(defun transpose-frame ()
(interactive)
(let ((fwin (car (window-tree))))
(while (not (windowp fwin))
(setq fwin (caddr fwin)))
(toggle-window-split
;; We gotta get sizes now, cuz if not then window split mess em
;; up
(deepmap (lambda (e) (if (windowp e)
(cons
e (window-edges e))
e))
(car (window-tree)))
fwin
t)
))
--------------------------
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-27 17:29 ` martin rudalics
2024-09-28 7:52 ` pranshu sharma
2024-09-28 7:58 ` pranshu sharma
@ 2024-09-28 8:18 ` Eli Zaretskii
2024-09-28 9:40 ` martin rudalics
2 siblings, 1 reply; 85+ messages in thread
From: Eli Zaretskii @ 2024-09-28 8:18 UTC (permalink / raw)
To: martin rudalics; +Cc: pranshusharma366, emacs-devel
> Date: Fri, 27 Sep 2024 19:29:21 +0200
> Cc: pranshusharma366@gmail.com, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
>
> > This doc string needs to be more detailed in what it means to "put the
> > window object of DEAD in the place of LIVE". For example, it
> > currently keeps silent about what happens to LIVE after the call.
>
> The fact that LIVE existed at all was a misfeature. I now put the dead
> window right into the new window created by 'split-window' with the help
> of a new argument. Patch attached.
Thanks, but does it really make a lot of sense to make this a
side-effect of splitting a window? (If it makes sense due to
technical reasons, such as commonality of code of the implementation,
we could have a common internal subroutine with 2 separate APIs
exposed to Lisp.)
Or maybe it _will_ make a lot of sense if you reword the doc string so
that it explains why what we do with REFER is a variant of splitting a
window. Something like
Instead of making a new window, this function can reuse an existing
dead window...
Btw, "dead window" is mentioned only once in the ELisp manual, and
even that in passing, so it is not a very clear terminology, and might
need clarifications in this case.
> +If the optional fifth argument REFER is non-nil, it has to denote a
> +dead, former live window on the same frame as OLD or an arbitrary live
> +window. ^^^
What is "OLD" here? More generally, I don't think I understand what
you wanted to say by "or an arbitrary live window".
> In the first case, REFER will become the new window with
> +properties like buffer, start and point, decorations and parameters as
> +to the last time when it was live. In the latter case the new window
> +will inherit properties like buffer, start and point, decorations and
> +parameters from REFER.
I suggest to describe each case separately, i.e. split the previous
sentence ("it has to denote ... or ...") into two, and explain
separately what happens in each case, instead of complicating with the
former and the latter. Especially as there are additional cases (nil
or omitted), which add to the confusion:
> +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 (!BUFFERP (r->old_buffer))
> + error ("Dead reference window was not a live window");
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This seems to imply that the function expects a dead window to be a
live window(??).
> + else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
> + error ("Dead reference window's old buffer is dead");
Will users understand what is "old buffer" of a window?
> + else if (!EQ (r->frame, frame))
> + error ("Dead referenec window was on other frame");
^^^^^^^^^
Typo. Also, I'd say "was on a frame other that that of window being
split" or something like that.
> + dead = true;
> + }
> + else if (!WINDOW_LIVE_P (refer))
> + error ("Reference window must not be internal");
Are we sure "internal" here will be understood? How about using "leaf
window" instead?
More generally, the text of these error messages is not easily
correlated to the problematic argument, because it neither mentions
the argument by its exact name, nor mentions the problematic window by
any other specific reference.
> + /* Provisorially set new's buffer to that of the reference window,
^^^^^^^^^^^^^
Did you mean "provisionally"? or maybe "temporarily"?
Also, "new" should be up-cased.
> + resize the parent, reset new's buffer to nil and do the real
> + set_window_buffer. */
Likewise.
> + /* Set buffer of NEW to buffer of reference window. We have to do it
> + here so the sizes of NEW are in place. But be sure to do it before
> + adjusting the frame glyphs - otherwise Emacs may inexplicably loop
> + forever. */
This should be more explicit what code below adjusts the frame's
glyphs, because without that this very good comment is less useful
than it could be.
> unchain_marker (XMARKER (w->start));
> + wset_old_buffer (w, w->contents);
What is this about?
> @@ -7720,6 +7795,7 @@ delete_all_child_windows (Lisp_Object window)
> only, we use this slot to save the buffer for the sake of
> possible resurrection in Fset_window_configuration. */
> wset_combination_limit (w, w->contents);
> + wset_old_buffer (w, w->contents);
And this?
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 7:52 ` pranshu sharma
@ 2024-09-28 9:26 ` martin rudalics
2024-09-28 10:53 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-28 9:26 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> I managed to make remake the transpose frame function without using
> reseruct window, and instead used swap-window-states.
I don't know 'swap-window-states' but if it uses 'window-state-get' and
'window-state-put' (which I happen to know quite intimately) the result
won't be any better than with the concept you cited earlier.
> However one thing I'm stuck with it getting it to work with root C-x 2
> C-x 3 kinda splits (basiclly where (listp (caddar (window-tree))) is
> true). However if you start with no windows, split window once, go to
> split window, they you can go crazy with splitting in any direction
> (even the C-x 2 C-x 3) and it will work when you call transpose-frame.
I don't understand what you mean here.
> This also different from the transpose-windows.el in that it does not
> start on a blank slate(transpose-windows.el calls delete-other-windows
> in the transpose-frame-set-arrangement), so transposing partial window
> trees much easier but I haven't implimented it yet.
Whatever you do someone has to delete all "other" windows and start with
one specific live window that you subsequently split.
Try with the 'split-window' I posted earlier so you don't have to care
about buffers, decorations and the rest and can concentrate on geometry.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 8:18 ` Eli Zaretskii
@ 2024-09-28 9:40 ` martin rudalics
2024-09-28 11:35 ` Eli Zaretskii
2024-09-28 13:22 ` pranshu sharma
0 siblings, 2 replies; 85+ messages in thread
From: martin rudalics @ 2024-09-28 9:40 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: pranshusharma366, emacs-devel
> Thanks, but does it really make a lot of sense to make this a
> side-effect of splitting a window?
It makes sense because (1) we do not have to make a new window that gets
deleted right away and (2) the geometry of the new window will be
handled by 'split-window' directly and not by some obscure copying
routine (I only later noted that 'resurrect-window' got me warnings
about memcpy combined with offsetof writing beyond the bounds of window
structures).
> (If it makes sense due to
> technical reasons, such as commonality of code of the implementation,
> we could have a common internal subroutine with 2 separate APIs
> exposed to Lisp.)
We can write a separate 'split-window-reuse-existing' function and have
it call a 'split-window-reuse-existing-internal' routine so we don't
compromise the present 'split-window' at all. It's up to you what you
like more.
> Or maybe it _will_ make a lot of sense if you reword the doc string so
> that it explains why what we do with REFER is a variant of splitting a
> window. Something like
>
> Instead of making a new window, this function can reuse an existing
> dead window...
OK.
> Btw, "dead window" is mentioned only once in the ELisp manual, and
> even that in passing, so it is not a very clear terminology, and might
> need clarifications in this case.
The problem is that in Emacs a live window is a window that shows a
buffer. Which implies that internal windows are dead. I once tried to
convince Chong that this terminology is misleading but he didn't allow
me to change it.
>> +If the optional fifth argument REFER is non-nil, it has to denote a
>> +dead, former live window on the same frame as OLD or an arbitrary live
>> +window. ^^^
>
> What is "OLD" here?
It should be WINDOW. OLD is the term used by 'split-window-internal'.
> More generally, I don't think I understand what
> you wanted to say by "or an arbitrary live window".
That's a separate functionality to give users more control of the window
the new window inherits properties from. It's useful when splitting an
internal window and the _new_ window should get its initial buffer and
other properties from any but the frame's selected window.
>> In the first case, REFER will become the new window with
>> +properties like buffer, start and point, decorations and parameters as
>> +to the last time when it was live. In the latter case the new window
>> +will inherit properties like buffer, start and point, decorations and
>> +parameters from REFER.
>
> I suggest to describe each case separately, i.e. split the previous
> sentence ("it has to denote ... or ...") into two, and explain
> separately what happens in each case, instead of complicating with the
> former and the latter. Especially as there are additional cases (nil
> or omitted), which add to the confusion:
I'll try to do that.
>> +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 (!BUFFERP (r->old_buffer))
>> + error ("Dead reference window was not a live window");
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> This seems to imply that the function expects a dead window to be a
> live window(??).
In our misleading terminology a dead window can be either (i) a former
live window or (ii) an internal or former internal window. I have to
exclude (ii) because an internal window should never become a leaf
window and vice-versa.
>> + else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
>> + error ("Dead reference window's old buffer is dead");
>
> Will users understand what is "old buffer" of a window?
It's the buffer returned by 'window-old-buffer'. I will have to explain
this in more detail.
>> + else if (!EQ (r->frame, frame))
>> + error ("Dead referenec window was on other frame");
> ^^^^^^^^^
> Typo. Also, I'd say "was on a frame other that that of window being
> split" or something like that.
OK.
>> + dead = true;
>> + }
>> + else if (!WINDOW_LIVE_P (refer))
>> + error ("Reference window must not be internal");
>
> Are we sure "internal" here will be understood? How about using "leaf
> window" instead?
But it's the opposite - a "non-leaf window". If you (or anyone else)
have any proposals on how to improve the nomenclature in this area, I'll
be all ears.
> More generally, the text of these error messages is not easily
> correlated to the problematic argument, because it neither mentions
> the argument by its exact name, nor mentions the problematic window by
> any other specific reference.
So the error messages should explain in more detail what when wrong.
>> + /* Provisorially set new's buffer to that of the reference window,
> ^^^^^^^^^^^^^
> Did you mean "provisionally"? or maybe "temporarily"?
Both would fit the bill. But this is code that I didn't intend to
submit is part of a completely different changeset where the sizes
of an individual window would be needed to set up the size hints of
frames correctly in set_window_buffer.
>> + /* Set buffer of NEW to buffer of reference window. We have to do it
>> + here so the sizes of NEW are in place. But be sure to do it before
>> + adjusting the frame glyphs - otherwise Emacs may inexplicably loop
>> + forever. */
>
> This should be more explicit what code below adjusts the frame's
> glyphs, because without that this very good comment is less useful
> than it could be.
Again part of that other changeset.
>> unchain_marker (XMARKER (w->start));
>> + wset_old_buffer (w, w->contents);
>
> What is this about?
When we make a new window and delete it before redisplay runs
window_change_functions for the first time, the old buffer of the window
is nil. But that would have caused the "Dead reference window's old
buffer is dead" error with the earlier proposed 'resurrect-window' while
in fact the window (made by 'split-window' right before) did have an old
buffer. It has no significance for the new 'split-window'.
>> @@ -7720,6 +7795,7 @@ delete_all_child_windows (Lisp_Object window)
>> only, we use this slot to save the buffer for the sake of
>> possible resurrection in Fset_window_configuration. */
>> wset_combination_limit (w, w->contents);
>> + wset_old_buffer (w, w->contents);
>
> And this?
Same explanation. The code for Fdelete_window_internal and
delete_all_child_windows should be refactored so that they would call a
common delete_leaf_window.
Thanks for the careful reading, martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 9:26 ` martin rudalics
@ 2024-09-28 10:53 ` pranshu sharma
2024-09-28 14:48 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-28 10:53 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
> I don't know 'swap-window-states' but if it uses 'window-state-get' and
> 'window-state-put' (which I happen to know quite intimately) the result
> won't be any better than with the concept you cited earlier.
>
I meant window-swap-states, and it does use window-state-get and put.
>> However one thing I'm stuck with it getting it to work with root C-x 2
>> C-x 3 kinda splits (basiclly where (listp (caddar (window-tree))) is
>> true). However if you start with no windows, split window once, go to
>> split window, they you can go crazy with splitting in any direction
>> (even the C-x 2 C-x 3) and it will work when you call transpose-frame.
>
> I don't understand what you mean here.
Say you save window layout:
- +----+--+
- | A |C |
- |____|__|
- | B |
- +-------+
(car (window-tree)) returns:
(t (0 0 137 30)
;; Following sexp is returned by (caddar (window-tree))
(nil (0 0 137 15)
#<window A>
#<window C>)
#<window B>)
In a scinario like this, where (caddar (window-tree)) is a list, the
function does not work as it will not have any anchor point.
However transpose-frame will work on the following layout, as it can
anchor of A
- +------+----+--+
- | | B |C |
- | A |____|__|
- | | C |
- +------+-------+
>> This also different from the transpose-windows.el in that it does not
>> start on a blank slate(transpose-windows.el calls delete-other-windows
>> in the transpose-frame-set-arrangement), so transposing partial window
>> trees much easier but I haven't implimented it yet.
>
> Whatever you do someone has to delete all "other" windows and start with
> one specific live window that you subsequently split.
wdym?
> Try with the 'split-window' I posted earlier so you don't have to care
> about buffers, decorations and the rest and can concentrate on geometry.
just compiled emacs an tested it works, I'll do that
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 9:40 ` martin rudalics
@ 2024-09-28 11:35 ` Eli Zaretskii
2024-09-28 14:58 ` martin rudalics
2024-09-28 13:22 ` pranshu sharma
1 sibling, 1 reply; 85+ messages in thread
From: Eli Zaretskii @ 2024-09-28 11:35 UTC (permalink / raw)
To: martin rudalics; +Cc: pranshusharma366, emacs-devel
> Date: Sat, 28 Sep 2024 11:40:27 +0200
> Cc: pranshusharma366@gmail.com, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
>
> >> + if (!BUFFERP (r->old_buffer))
> >> + error ("Dead reference window was not a live window");
> > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > This seems to imply that the function expects a dead window to be a
> > live window(??).
>
> In our misleading terminology a dead window can be either (i) a former
> live window or (ii) an internal or former internal window. I have to
> exclude (ii) because an internal window should never become a leaf
> window and vice-versa.
So maybe this is better:
Dead REFER window was not a leaf window"
> >> + else if (!WINDOW_LIVE_P (refer))
> >> + error ("Reference window must not be internal");
> >
> > Are we sure "internal" here will be understood? How about using "leaf
> > window" instead?
>
> But it's the opposite - a "non-leaf window".
Yes, so I suggested replacing "must not be internal" with "must be a
leaf window". Is that wrong?
> >> unchain_marker (XMARKER (w->start));
> >> + wset_old_buffer (w, w->contents);
> >
> > What is this about?
>
> When we make a new window and delete it before redisplay runs
> window_change_functions for the first time, the old buffer of the window
> is nil. But that would have caused the "Dead reference window's old
> buffer is dead" error with the earlier proposed 'resurrect-window' while
> in fact the window (made by 'split-window' right before) did have an old
> buffer. It has no significance for the new 'split-window'.
So this fixes a related but different bug?
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 9:40 ` martin rudalics
2024-09-28 11:35 ` Eli Zaretskii
@ 2024-09-28 13:22 ` pranshu sharma
2024-09-28 14:21 ` Eli Zaretskii
2024-09-28 14:49 ` martin rudalics
1 sibling, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-09-28 13:22 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>
> It makes sense because (1) we do not have to make a new window that gets
> deleted right away and (2) the geometry of the new window will be
> handled by 'split-window' directly and not by some obscure copying
> routine (I only later noted that 'resurrect-window' got me warnings
> about memcpy combined with offsetof writing beyond the bounds of window
> structures).
Is there a technical reason for the `refer' argument to neccasarily be
_dead_ window, it would be very helpful if you could pass a living
window.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 13:22 ` pranshu sharma
@ 2024-09-28 14:21 ` Eli Zaretskii
2024-09-28 14:49 ` martin rudalics
1 sibling, 0 replies; 85+ messages in thread
From: Eli Zaretskii @ 2024-09-28 14:21 UTC (permalink / raw)
To: pranshu sharma; +Cc: rudalics, emacs-devel
> From: pranshu sharma <pranshusharma366@gmail.com>
> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org
> Date: Sat, 28 Sep 2024 23:22:47 +1000
>
> Is there a technical reason for the `refer' argument to neccasarily be
> _dead_ window, it would be very helpful if you could pass a living
> window.
My reading of the latest version of the patch posted by Martin is that
this is indeed possible.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 10:53 ` pranshu sharma
@ 2024-09-28 14:48 ` martin rudalics
2024-09-29 7:36 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-28 14:48 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> Say you save window layout:
> - +----+--+
> - | A |C |
> - |____|__|
> - | B |
> - +-------+
>
> (car (window-tree)) returns:
>
>
> (t (0 0 137 30)
> ;; Following sexp is returned by (caddar (window-tree))
> (nil (0 0 137 15)
> #<window A>
> #<window C>)
> #<window B>)
>
> In a scinario like this, where (caddar (window-tree)) is a list, the
> function does not work as it will not have any anchor point.
Do you mean that your function doesn't work because A has to be split
twice - once to produce B and then to produce C?
> However transpose-frame will work on the following layout, as it can
> anchor of A
>
> - +------+----+--+
> - | | B |C |
> - | A |____|__|
> - | | C |
> - +------+-------+
What is the anchor point here? Does it work because you have to split A
once only and then deal with the split off window only? I don't know
how to help you but I suppose your function should work by extracting
from the window tree all windows on the same level and recursively apply
itself on the respective tails of the window tree at that level.
>> Whatever you do someone has to delete all "other" windows and start with
>> one specific live window that you subsequently split.
>
> wdym?
The standard case is where you rotate the entire window tree of a frame.
In that case you would call 'delete-other-windows' and start splitting.
But in your initial posting you said that TransposeFrame has "No good
way to transpose only specific parts of window tree". Suppose you have
the configuration you just mentioned
+------+----+--+
| | B |C |
| A |____|__|
| | D |
+------+-------+
and want to leave A in place. So you have to delete the other windows C
and D first and then start splitting B (alternatively, you could
temporarily bind the 'no-delete-other-windows' parameter of A to t and
reset it after calling 'delete-other-windows' on B).
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 13:22 ` pranshu sharma
2024-09-28 14:21 ` Eli Zaretskii
@ 2024-09-28 14:49 ` martin rudalics
1 sibling, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-09-28 14:49 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> Is there a technical reason for the `refer' argument to neccasarily be
> _dead_ window, it would be very helpful if you could pass a living
> window.
You can pass a live window to it. But in that case it will only use
that window's properties for the new window 'split-window' made.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 11:35 ` Eli Zaretskii
@ 2024-09-28 14:58 ` martin rudalics
2024-09-28 15:28 ` Eli Zaretskii
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-28 14:58 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: pranshusharma366, emacs-devel
> So maybe this is better:
>
> Dead REFER window was not a leaf window"
OK.
>> >> + else if (!WINDOW_LIVE_P (refer))
>> >> + error ("Reference window must not be internal");
>> >
>> > Are we sure "internal" here will be understood? How about using "leaf
>> > window" instead?
>>
>> But it's the opposite - a "non-leaf window".
>
> Yes, so I suggested replacing "must not be internal" with "must be a
> leaf window". Is that wrong?
No. We could also say "must display a buffer".
>
>> >> unchain_marker (XMARKER (w->start));
>> >> + wset_old_buffer (w, w->contents);
>> >
>> > What is this about?
>>
>> When we make a new window and delete it before redisplay runs
>> window_change_functions for the first time, the old buffer of the window
>> is nil. But that would have caused the "Dead reference window's old
>> buffer is dead" error with the earlier proposed 'resurrect-window' while
>> in fact the window (made by 'split-window' right before) did have an old
>> buffer. It has no significance for the new 'split-window'.
>
> So this fixes a related but different bug?
It wasn't a bug so far because the "old buffer" was not defined for dead
windows. 'window-old-buffer' is described as
Return the old buffer displayed by WINDOW.
WINDOW must be a live 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.
What the patch does is to now make it work for dead windows in the sense
that its value is that of the buffer shown in the window at the time the
window was deleted. In general, this _is_ the buffer stored in the
old_buffer slot. But if we manage to delete a window whose old buffer
is nil or t _before_ running window change functions on it for the first
time, it isn't.
But I just noticed that even 'window-old-buffer' can be affected by the
patch if we (i) make a window (ii) delete it before it's seen by the
change functions and (iii) resurrect it. In that (unlikely) scenario
the next change functions will consider the window as one seen by the
last change functions although these have never seen it. I have to add
another slot del_buffer to the window structure.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 14:58 ` martin rudalics
@ 2024-09-28 15:28 ` Eli Zaretskii
2024-10-07 8:33 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: Eli Zaretskii @ 2024-09-28 15:28 UTC (permalink / raw)
To: martin rudalics; +Cc: pranshusharma366, emacs-devel
> Date: Sat, 28 Sep 2024 16:58:24 +0200
> Cc: pranshusharma366@gmail.com, emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
>
> >> >> + else if (!WINDOW_LIVE_P (refer))
> >> >> + error ("Reference window must not be internal");
> >> >
> >> > Are we sure "internal" here will be understood? How about using "leaf
> >> > window" instead?
> >>
> >> But it's the opposite - a "non-leaf window".
> >
> > Yes, so I suggested replacing "must not be internal" with "must be a
> > leaf window". Is that wrong?
>
> No. We could also say "must display a buffer".
I think we should say both, to be very clear.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 14:48 ` martin rudalics
@ 2024-09-29 7:36 ` pranshu sharma
2024-09-29 8:40 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-29 7:36 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
>> In a scinario like this, where (caddar (window-tree)) is a list, the
>> function does not work as it will not have any anchor point.
>
> Do you mean that your function doesn't work because A has to be split
> twice - once to produce B and then to produce C?
Something like that, but I fixed now, see code below
> What is the anchor point here? Does it work because you have to split A
> once only and then deal with the split off window only? I don't know
> how to help you but I suppose your function should work by extracting
> from the window tree all windows on the same level and recursively apply
> itself on the respective tails of the window tree at that level.
The problem was that I was splitting off another window as I was not
already desyroying the whole tree.
Thanks for your explenation, I found out I was only over complicating it
by not destroying windows, so I gave up that approach.
This time instead of calling delete-other-windows, I recusivly walked
window subtree and deleted them.
The following code implements transpose-frame and which works fine, I
tested with some random window layouts and it worked for all. It uses
the split-window refer argument.
The code:
-----------------------------------------
(defun transpose-frame (arg)
"Transpose frame, or if arg is non-nil, selected window."
(interactive "P")
(when-let* ((win-tree (car (window--subtree
(if arg
(window-parent (selected-window))
(frame-root-window)))))
(fwin (if arg
(selected-window)
(let ((win (window-child (frame-root-window))))
(while (not (window-live-p win))
(setq win (window-child win)))
win)))
(_ (not (windowp win-tree))))
(toggle-window-split
(let* (delist
(res (deepmap (lambda (e) (if (windowp e)
(prog1 (cons e (window-edges e))
(unless (equal fwin e)
(push e delist)))
e))
win-tree)))
(mapc 'delete-window delist)
res)
fwin)))
;; Is there already another inbuilt funcion like this?
(defun deepmap(func ls)
(if (null ls)
()
(cons (if (listp (car ls))
(deepmap func (car ls))
(funcall func (car ls)))
(deepmap func (cdr ls)))))
(defun toggle-window-split (subtree cwin)
(pcase-let* ((`(,eee . ,flen) (if (car subtree)
(cons 1 (window-width cwin))
(cons 0 (window-height cwin))))
(ilen (float (- (nth (+ 2 eee) (cadr subtree))
(nth (+ eee 0) (cadr subtree))))))
(mapc
(pcase-lambda (`(,win . ,size))
(if (listp win)
(toggle-window-split win (split-window cwin
(- (round (* flen (/ size ilen))))
(car subtree)
nil
(seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win))))
(split-window cwin (- (round (* flen (/ size ilen))))
(car subtree) nil
win)))
(mapcar
(lambda (e)
(pcase-let ((`(,edges ,window?)
(if (windowp (car e))
(list (cdr e) (car e))
(list (cadr e) e))))
(cons window? (- (nth (+ 2 eee) edges)
(nth eee edges)))))
(nreverse (cdddr subtree))))
(unless (windowp (caaddr subtree))
(toggle-window-split (caddr subtree) cwin))))
-----------------------------------------
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-29 7:36 ` pranshu sharma
@ 2024-09-29 8:40 ` martin rudalics
2024-09-29 9:23 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-29 8:40 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> The following code implements transpose-frame and which works fine, I
> tested with some random window layouts and it worked for all. It uses
> the split-window refer argument.
Thank you.
> (defun transpose-frame (arg)
> "Transpose frame, or if arg is non-nil, selected window."
ARG (upper case please) should be renamed to make it more descriptive
and you should explain what transposing the selected window means.
> (res (deepmap (lambda (e) (if (windowp e)
> (prog1 (cons e (window-edges e))
> (unless (equal fwin e)
> (push e delist)))
IIUC here you "flatten" the return value of 'window-tree'. Why do you
do that? Can't 'transpose-frame' work directly on the return value of a
(possibly modified) 'window-tree'?
> (prog1 (cons e (window-edges e))
On my maximized frame calling 'transpose-frame' repeatedly shrinks the
right/bottom windows until they cannot be shrunk any more. I wouldn't
use 'window-edges' here. Make your own version of 'window-tree' (or
give that an optional argument) so it returns the normal sizes of
windows instead of the edges and use the normal sizes as factor for the
SIZE argument of ‘split-window’ and make the PIXELWISE argument non-nil.
Rounding window sizes can be awfully tricky and we should try to avoid
its effects as good as we can. When transposing windows on a GUI you
will always have to take into account that the width of a window
includes things like fringes and scroll bars (where the size of the
latter is toolkit dependent) which often make the width not an integral
multiple of the frame's character width. A similar argument goes for
window heights, for example, if a mode or tab line is displayed with a
different font or some sort of box around it.
Also I expected your functions to run on subwindows of a frame's windows
and permit to rotate configurations clockwise or counter clockwise as
the version from 2011 did? Do you intend to provide those?
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-29 8:40 ` martin rudalics
@ 2024-09-29 9:23 ` pranshu sharma
2024-09-29 14:48 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-29 9:23 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>> The following code implements transpose-frame and which works fine, I
>> tested with some random window layouts and it worked for all. It uses
>> the split-window refer argument.
>
> Thank you.
>
>> (defun transpose-frame (arg)
>> "Transpose frame, or if arg is non-nil, selected window."
>
> ARG (upper case please) should be renamed to make it more descriptive
> and you should explain what transposing the selected window means.
Ok, will do
>
>> (res (deepmap (lambda (e) (if (windowp e)
>> (prog1 (cons e (window-edges e))
>> (unless (equal fwin e)
>> (push e delist)))
>
> IIUC here you "flatten" the return value of 'window-tree'. Why do you
> do that? Can't 'transpose-frame' work directly on the return value of a
> (possibly modified) 'window-tree'?
This is definetly a big hack. The reason why this is needed is
because the parent window might not actually be a window, it might also
be another split, so it would have to recursivly descent find the first
window.
The only real alternative I can see is that if toggle-window-split
returns a list of spltis to do, and then they are mapped over and done
by toggle-window-split one level higher.
I'll look into how transpose-frame.el deals with this.
>> (prog1 (cons e (window-edges e))
>
> On my maximized frame calling 'transpose-frame' repeatedly shrinks the
> right/bottom windows until they cannot be shrunk any more.
Hmm, show your window-tree (or the sequence of splits you did), I tested
with a lot of splits and didn't find this problem.
> I wouldn't use 'window-edges' here. Make your own version of
> 'window-tree' (or give that an optional argument) so it returns the
> normal sizes of windows instead of the edges and use the normal sizes
> as factor for the SIZE argument of ‘split-window’ and make the
> PIXELWISE argument non-nil.
I see, this should definetly make the function better.
> Rounding window sizes can be awfully tricky and we should try to avoid
> its effects as good as we can. When transposing windows on a GUI you
> will always have to take into account that the width of a window
> includes things like fringes and scroll bars (where the size of the
> latter is toolkit dependent) which often make the width not an integral
> multiple of the frame's character width. A similar argument goes for
> window heights, for example, if a mode or tab line is displayed with a
> different font or some sort of box around it.
>
> Also I expected your functions to run on subwindows of a frame's windows
> and permit to rotate configurations clockwise or counter clockwise as
> the version from 2011 did? Do you intend to provide those?
They should be very easy(just changing up the arguments of split-window
to correct combo of above, below, right, left), I'm just
trying to first get the other aspects streamlined, but yes, I do intend
on adding them.
I'll also add flip if you want.
>
> martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-29 9:23 ` pranshu sharma
@ 2024-09-29 14:48 ` martin rudalics
2024-09-30 6:29 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-29 14:48 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 1373 bytes --]
> This is definetly a big hack. The reason why this is needed is
> because the parent window might not actually be a window, it might also
> be another split, so it would have to recursivly descent find the first
> window.
Hmm... I seem to understand. TransposeFrame uses the buffer of the
window it splits so there you have a lot of windows showing the frame's
selected window initially and eventually see them set to their original
buffers. We want to set a window's final buffer immediately at the time
of splitting.
> The only real alternative I can see is that if toggle-window-split
> returns a list of spltis to do, and then they are mapped over and done
> by toggle-window-split one level higher.
>
> I'll look into how transpose-frame.el deals with this.
Note that TransposeFrame also may revert the cdr of the list it deals
with. Back then the 'above and 'left values for SIDE were not known
yet.
>>> (prog1 (cons e (window-edges e))
>>
>> On my maximized frame calling 'transpose-frame' repeatedly shrinks the
>> right/bottom windows until they cannot be shrunk any more.
>
> Hmm, show your window-tree (or the sequence of splits you did), I tested
> with a lot of splits and didn't find this problem.
I'll attach a capture of the "initial" split and the "final" split after
a couple of invocations of 'transpose-frame'.
martin
[-- Attachment #2: initial.png --]
[-- Type: image/png, Size: 84203 bytes --]
[-- Attachment #3: final.png --]
[-- Type: image/png, Size: 85594 bytes --]
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-29 14:48 ` martin rudalics
@ 2024-09-30 6:29 ` pranshu sharma
2024-09-30 8:57 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-09-30 6:29 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
Hello Martin,
After some work, the code is now complete and uses pixels instead colums
of. It can do everything the orignal transpose frame could (flip,
transpose, rotate), and results are the same.
Some function are like opposites, eg flip-frame-horizontally and
flip-frame-verticly, I thought about merging them and using prefix arg,
but then prefix arg already in use for if or not to apply to partial
subtree instead frame.
English is not my first language, so I was unable to make the docstrings
for the more complex function, and I don't know how to explain what
transpose does.
Also I think your error was coming from scroll and tool bars(should be
fixed now cuz pixels), but since terminal does not have them I couldnt
run into too small for splitting error on terminal.
-----------------------------
(defun window--subtree-with-size (window &optional next)
"Like `window--subtree' but each window is replaced with the
list: (WINDOW PIXEL-HEIGHT PIXEL-WIDTH), and window-edges is replaced
with the list: (PIXEL-HEIGHT PIXEL-WIDTH)."
(let (list)
(while window
(setq list
(cons
(cond
((window-top-child window)
(cons t (cons (list (window-pixel-height window) (window-pixel-width window))
(window--subtree-with-size (window-top-child window) t))))
((window-left-child window)
(cons nil (cons (list (window-pixel-height window) (window-pixel-width window))
(window--subtree-with-size (window-left-child window) t))))
(t (list window (window-pixel-height window) (window-pixel-width window))))
list))
(setq window (when next (window-next-sibling window))))
(nreverse list)))
(defun rotate-frame-anticlockwise (subtree-only)
"If SUBTREE-ONLY is nil, rotate the whole frame anti clockwise else only
apply on the subtree of selected window."
(interactive "P")
(apply-rearrange-frame subtree-only '(right . above) nil))
(defun rotate-frame-clockwise (subtree-only)
"Like `rotate-frame-anticlockwise' but rotate clockwise instead."
(interactive "P")
(apply-rearrange-frame subtree-only '(left . below) nil))
(defun flip-frame-horizontally (subtree-only)
"If SUBTREE-ONLY is nil, rotate the whole frame anti clockwise else only
apply on the subtree of selected window."
(interactive "P")
(apply-rearrange-frame subtree-only '(below . left) t))
(defun flip-frame-verticly (subtree-only)
"Like `flip-frame-horizontally' but flip verticlly instead."
(interactive "P")
(apply-rearrange-frame subtree-only '(above . right) t))
(defun transpose-frame (subtree-only)
(interactive "P")
(apply-rearrange-frame subtree-only '(right . below) nil))
(defun apply-rearrange-frame (subtree-only conf norm-size)
"Transpose frame, or if SUBTREE-ONLY is non-nil, selected window."
(if (eq (next-window) (selected-window))
(message "No windows to transpose.")
(let* ((win-tree (car (window--subtree-with-size
(if subtree-only
(window-parent (selected-window))
(frame-root-window)))))
(fwin (if subtree-only
(selected-window)
(let ((win (window-child (frame-root-window))))
(while (not (window-live-p win))
(setq win (window-child win)))
win))))
(mapc (lambda (win)
(when (and (windowp win)
(not (eq win fwin)))
(delete-window win)))
(flatten-list win-tree))
(toggle-window-split win-tree fwin conf norm-size)
(select-window fwin))))
(defun toggle-window-split (subtree cwin conf norm-size)
(pcase-let ((`(,ilen . ,flen) (if (car subtree)
(cons (float (car (cadr subtree)))
(float (window-pixel-width cwin)))
(cons (float (cadr (cadr subtree)))
(float (window-pixel-height cwin))))))
(mapc
(pcase-lambda (`(,win . ,size))
(let ((split-size (- (if norm-size
size
(round (* flen (/ size ilen))))))
(split-type
(funcall (if (car subtree) 'car 'cdr) conf)))
(if (listp win)
(toggle-window-split win (split-window cwin
split-size
split-type
t
(seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win)))
conf norm-size)
(split-window cwin split-size
split-type t
win))))
(mapcar
(lambda (e)
(pcase-let* ((`(,size ,window?)
(if (windowp (car e))
(list (cdr e) (car e))
(list (cadr e) e))))
(cons window? (if (car subtree)
(car size)
(cadr size)))))
(nreverse (cdddr subtree))))
(unless (windowp (caaddr subtree))
(toggle-window-split (caddr subtree) cwin conf norm-size))))
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-30 6:29 ` pranshu sharma
@ 2024-09-30 8:57 ` martin rudalics
2024-10-01 9:17 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-09-30 8:57 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> Hello Martin,
>
> After some work,
... remarkable work ...
> the code is now complete and uses pixels instead colums
> of. It can do everything the orignal transpose frame could (flip,
> transpose, rotate), and results are the same.
>
> Some function are like opposites, eg flip-frame-horizontally and
> flip-frame-verticly, I thought about merging them and using prefix arg,
> but then prefix arg already in use for if or not to apply to partial
> subtree instead frame.
>
> English is not my first language, so I was unable to make the docstrings
> for the more complex function, and I don't know how to explain what
> transpose does.
Let's hope someone will eventually proofread this as soon as we agree on
a final version.
> Also I think your error was coming from scroll and tool bars(should be
> fixed now cuz pixels), but since terminal does not have them I couldnt
> run into too small for splitting error on terminal.
I rotated my windows a number of times and see no shrinking any more.
Even with pixelwise resizing it's impossible to exclude that windows
shrink over time but that's unavoidable unless one uses some heuristics.
So I think that your code handles this well now.
> (defun window--subtree-with-size (window &optional next)
> "Like `window--subtree' but each window is replaced with the
> list: (WINDOW PIXEL-HEIGHT PIXEL-WIDTH), and window-edges is replaced
> with the list: (PIXEL-HEIGHT PIXEL-WIDTH)."
As a rule, the first line of a doc-string should be a complete sentence
and be at most 72 columns wide (see Appendix D.6 of the Elisp manual).
And you should describe all arguments like WINDOW and NEXT.
Alternatively, we could give 'window-tree' an optional PIXELWISE
argument so it returns the pixel edges of windows.
> (let (list)
> (while window
> (setq list
> (cons
> (cond
> ((window-top-child window)
> (cons t (cons (list (window-pixel-height window) (window-pixel-width window))
Lines should not exceed 80 characters and you probably want to use
'cons' instead of 'list' here.
> (window--subtree-with-size (window-top-child window) t))))
> ((window-left-child window)
> (cons nil (cons (list (window-pixel-height window) (window-pixel-width window))
> (window--subtree-with-size (window-left-child window) t))))
> (t (list window (window-pixel-height window) (window-pixel-width window))))
> list))
> (setq window (when next (window-next-sibling window))))
> (nreverse list)))
>
>
> (defun rotate-frame-anticlockwise (subtree-only)
> "If SUBTREE-ONLY is nil, rotate the whole frame anti clockwise else only
> apply on the subtree of selected window."
> (interactive "P")
> (apply-rearrange-frame subtree-only '(right . above) nil))
I think you should rename all these into something like
'rotate-windows-anticlockwise' and 'transpose-windows' (we are already
using plurals in 'transpose-lines' and 'transpose-chars'). And we do
not rotate the entire frame - the minibuffer window stays in place, for
example. So I would try to say
(defun rotate-windows-anticlockwise (&optional frame window)
"Rotate windows of FRAME anticlockwise by 90 degrees.
FRAME must be a live frame and defaults to the selected frame. By
default rotate the root window of FRAME (or its main window if it
differs from the root window). If WINDOW is non-nil ..."
Which means that we need the additional features:
(1) Rotate windows on any live frame.
(2) Run the function on 'window-main-window' of FRAME unless WINDOW is
defined and never rotate a side window. Rotating side windows would
cause completely undefined behavior because their slots are expected to
never change. Hence, if WINDOW is specified and is a side window, say
that you cannot rotate side windows.
(3) "subtree of selected window" is an undefined concept. You should
say something like "to transpose all windows in the same combination as
WINDOW" instead. But note that with a slightly more complex layout, no
average user will know what that combination is (think of C-x 2 C-x 3
C-x o C-x o C-x 3). We could eventually try to flash all windows that
would be affected by the change but that would be non-trivial.
> (defun apply-rearrange-frame (subtree-only conf norm-size)
I'd call this something like 'transpose-windows--rearrange' so you can
change its behavior whenever you want.
> "Transpose frame, or if SUBTREE-ONLY is non-nil, selected window."
> (if (eq (next-window) (selected-window))
> (message "No windows to transpose.")
> (let* ((win-tree (car (window--subtree-with-size
> (if subtree-only
> (window-parent (selected-window))
> (frame-root-window)))))
Is fwin not the same as what 'frame-first-window' returns? If not,
please telly why in a comment.
> (fwin (if subtree-only
> (selected-window)
> (let ((win (window-child (frame-root-window))))
> (while (not (window-live-p win))
> (setq win (window-child win)))
> win))))
> (mapc (lambda (win)
> (when (and (windowp win)
> (not (eq win fwin)))
> (delete-window win)))
Please tell in a comment why you flatten win-tree her.
> (flatten-list win-tree))
> (toggle-window-split win-tree fwin conf norm-size)
> (select-window fwin))))
>
> (defun toggle-window-split (subtree cwin conf norm-size)
Should become window--... and have its arguments described.
> (pcase-let ((`(,ilen . ,flen) (if (car subtree)
> (cons (float (car (cadr subtree)))
> (float (window-pixel-width cwin)))
> (cons (float (cadr (cadr subtree)))
> (float (window-pixel-height cwin))))))
> (mapc
> (pcase-lambda (`(,win . ,size))
> (let ((split-size (- (if norm-size
> size
> (round (* flen (/ size ilen))))))
> (split-type
> (funcall (if (car subtree) 'car 'cdr) conf)))
> (if (listp win)
> (toggle-window-split win (split-window cwin
> split-size
> split-type
> t
> (seq-some
> (lambda (x)
> (and (windowp x) x))
> (flatten-list win)))
> conf norm-size)
Here I'd write
(let ((refer (seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win))))
(toggle-window-split
win (split-window cwin split-size split-type t refer)
conf norm-size))
but I won't argue about styles.
> (nreverse (cdddr subtree))))
> (unless (windowp (caaddr subtree))
> (toggle-window-split (caddr subtree) cwin conf norm-size))))
Whenever using things like cdddr, caaddr or caddr, please say in a
comment what these are supposed to be. You have condensed the original
version of your code considerably so please think about people who
eventually want to understand what the code does.
Many thanks, martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-30 8:57 ` martin rudalics
@ 2024-10-01 9:17 ` pranshu sharma
2024-10-02 9:04 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-01 9:17 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
> Alternatively, we could give 'window-tree' an optional PIXELWISE
> argument so it returns the pixel edges of windows.
You decide
>
> Lines should not exceed 80 characters and you probably want to use
> 'cons' instead of 'list' here.
>
Done
>
> I think you should rename all these into something like
> 'rotate-windows-anticlockwise' and 'transpose-windows' (we are already
> using plurals in 'transpose-lines' and 'transpose-chars'). And we do
> not rotate the entire frame - the minibuffer window stays in place, for
> example. So I would try to say
>
> (defun rotate-windows-anticlockwise (&optional frame window)
> "Rotate windows of FRAME anticlockwise by 90 degrees.
> FRAME must be a live frame and defaults to the selected frame. By
> default rotate the root window of FRAME (or its main window if it
> differs from the root window). If WINDOW is non-nil ..."
>
> Which means that we need the additional features:
>
> (1) Rotate windows on any live frame.
>
> (2) Run the function on 'window-main-window' of FRAME unless WINDOW is
> defined and never rotate a side window. Rotating side windows would
> cause completely undefined behavior because their slots are expected to
> never change. Hence, if WINDOW is specified and is a side window, say
> that you cannot rotate side windows.
>
> (3) "subtree of selected window" is an undefined concept. You should
> say something like "to transpose all windows in the same combination as
> WINDOW" instead. But note that with a slightly more complex layout, no
> average user will know what that combination is (think of C-x 2 C-x 3
> C-x o C-x o C-x 3). We could eventually try to flash all windows that
> would be affected by the change but that would be non-trivial.
>
Done, I just said all 'windows of' instead.
Also instead of seperate window and frame args, I just added added one
window-or-frame arg.
> I'd call this something like 'transpose-windows--rearrange' so you can
> change its behavior whenever you want.
>
Yes, good idea
> Is fwin not the same as what 'frame-first-window' returns? If not,
> please telly why in a comment.
It was, I just found out the function exists, thx
> Please tell in a comment why you flatten win-tree her.
>
>> (flatten-list win-tree))
>> (toggle-window-split win-tree fwin conf norm-size)
>> (select-window fwin))))
>>
>> (defun toggle-window-split (subtree cwin conf norm-size)
>
> Should become window--... and have its arguments described.
I just did as subroutine style, same as something like
`window--atom-check-1'.
> Here I'd write
>
> (let ((refer (seq-some
> (lambda (x)
> (and (windowp x) x))
> (flatten-list win))))
> (toggle-window-split
> win (split-window cwin split-size split-type t refer)
> conf norm-size))
>
> but I won't argue about styles.
Although this would be cleaner, but it means the instance of refer would
be stored in memory unnecessarily while the old toggle-window-split
would be going recursivly.
> Whenever using things like cdddr, caaddr or caddr, please say in a
> comment what these are supposed to be. You have condensed the original
> version of your code considerably so please think about people who
> eventually want to understand what the code does.
>
Yeah I did that, after all, I found out while doing this orignally how
hard the code in in trasnpose-frame.el was to read. Well even without
the comments(which I added now), the code is more readable now thanks
you adding refer argument to split-window.
---------------------------------------------
(defun window--subtree-with-size (window &optional next)
"Like `window--subtree' but each window is replaced with the
list: (WINDOW (PIXEL-HEIGHT . PIXEL-WIDTH)), and window-edges is replaced
with the cons cell: (PIXEL-HEIGHT . PIXEL-WIDTH)."
(let (list)
(while window
(setq list
(cons
(cond
((window-top-child window)
(cons t (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window--subtree-with-size
(window-top-child window) t))))
((window-left-child window)
(cons nil (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window--subtree-with-size
(window-left-child window) t))))
(t (list window (cons (window-pixel-height window)
(window-pixel-width window)))))
list))
(setq window (when next (window-next-sibling window))))
(nreverse list)))
(defun rotate-windows-anticlockwise (&optional frame-or-window)
"Rotate windows of FRAME-OR-WINDOW 90 degrees anticlockwise.
See `rotate-windows-clockwise' for more."
(interactive `(,(and prefix-arg (window-parent (selected-window)))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(frame-root-window frame-or-window))))
(transpose-windows--rearrange window '(right . above) nil)))
(defun rotate-windows-clockwise (&optional frame-or-window)
"Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate from the root
window of the frame, otherwise rotate from FRAME-OR-WINDOW."
(interactive `(,(and prefix-arg (window-parent (selected-window)))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(frame-root-window frame-or-window))))
(transpose-windows--rearrange window '(left . below) nil)))
(defun flip-windows-horizontally (&optional frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, flip from the root
window of the frame, otherwise flip from FRAME-OR-WINDOW."
(interactive `(,(and prefix-arg (window-parent (selected-window)))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(frame-root-window frame-or-window))))
(transpose-windows--rearrange window '(below . left) t)))
(defun flip-windows-vertically (&optional frame-or-window)
"Vertically flip windows of FRAME-OR-WINDOW.
See `flip-windows-horizontally' for more."
(interactive `(,(and prefix-arg (window-parent (selected-window)))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(frame-root-window frame-or-window))))
(transpose-windows--rearrange window '(above . right) t)))
(defun transpose-windows (&optional frame-or-window)
"Transpose windows of FRAME-OR-WINDOW.
Windows are rearanged such that where an horizontal split was used, an
vertical one is instead, and vice versa. FRAME-OR-WINDOW must be a live
frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
is a frame, transpose from the root window of the frame, otherwise
transpose from FRAME-OR-WINDOW."
(interactive `(,(and prefix-arg (window-parent (selected-window)))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(frame-root-window frame-or-window))))
(transpose-windows--rearrange window '(right . below) nil)))
(defun transpose-windows--rearrange (frame-or-window conf norm-size)
"Rearrange windows of FRAME-OR-WINDOW recursively.
CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where
horizontal split is called when splitting a window that was previously
horizontally split, and VERTICAL-SPLIT for a window that was previously
vertically split. When is NORM-SIZE non-nil, the size argument of the
window-split is converted from vertical to horizontal or vice versa,
with the same proportion of the total split."
(let ((rwin (if (framep frame-or-window)
(frame-root-window frame-root-window)
frame-or-window)))
(if (or (not rwin)
(zerop (window-child-count rwin)))
(message "Not enough windows")
(let* ((fwin (frame-first-window rwin))
(selwin (frame-selected-window frame-or-window))
(win-tree (car (window--subtree-with-size rwin))))
;; All child windows need to be recursively deleted.
(mapc (lambda (win)
(when (and (windowp win)
(not (eq win fwin)))
(delete-window win)))
;; We know for sure that first 2 in the list are not
;; windows.
(cddr (flatten-list win-tree)))
(transpose-windows--rearrange-1 win-tree fwin conf norm-size)
;; Go back to previously selected window.
(select-window selwin)))))
(defun transpose-windows--rearrange-1 (subtree cwin conf norm-size)
"Subroutine of `transpose-windows--rearrange'."
;; `ilen' is the max size a window could be of given the split type.
;; `flen' is max size the window could be converted to the opposite
;; of the given split type.
(pcase-let ((`(,ilen . ,flen) (if (car subtree)
(cons (float (car (cadr subtree)))
(float (window-pixel-width cwin)))
(cons (float (cdr (cadr subtree)))
(float (window-pixel-height cwin))))))
(mapc
(pcase-lambda (`(,win . ,size))
(let ((split-size (- (if norm-size
size
(round (* flen (/ size ilen))))))
(split-type
(funcall (if (car subtree) 'car 'cdr) conf)))
(if (listp win)
;; `win' is a window subtree.
(transpose-windows--rearrange-1 win (split-window cwin
split-size
split-type
t
(seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win)))
conf norm-size)
;; `win' is a window.
(split-window cwin split-size
split-type t
win))))
(mapcar
(lambda (e)
(let ((window? (if (windowp (car e)) (car e) e)))
(cons window?
;; The relevent size of the window.
(if (car subtree)
(car (cadr e))
(cdr (cadr e))))))
;; By using cdddr, we ignore over window split type, sizes and
;; the first window (it's implicitly created).
(nreverse (cdddr subtree))))
;; (caaddr subtree) is the first window.
(unless (windowp (caaddr subtree))
(transpose-windows--rearrange-1 (caddr subtree) cwin conf norm-size))))
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-01 9:17 ` pranshu sharma
@ 2024-10-02 9:04 ` martin rudalics
2024-10-03 7:06 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-02 9:04 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
>> Alternatively, we could give 'window-tree' an optional PIXELWISE
>> argument so it returns the pixel edges of windows.
>
> You decide
Better not - the fact that 'window-tree' does not report the edges of
live windows constitutes bad design. Let's not support it. Rather
let's make it a normal function called 'window-tree-pixel-sizes'. I
would write its doc string as
(defun window-tree-pixel-sizes (window &optional next)
"Return pixel sizes of all windows rooted at WINDOW.
The return value is a list where each window is represented either by a
triple whose first element is either t for an internal window that is a
horizontal combination, nil for an internal window that is a vertical
combination, or the window itself for a live window. The second element
is a cons of the pixel height and pixel width of the window. The third
element is specified for internal windows only and recursively lists
that window's child windows using the same triple structure."
but you would have to check whether it really does what I wrote.
> (defun rotate-windows-anticlockwise (&optional frame-or-window)
> "Rotate windows of FRAME-OR-WINDOW 90 degrees anticlockwise.
> See `rotate-windows-clockwise' for more."
I think it's better to write out the full doc-string here first and
in a final sentence say
See `rotate-windows-clockwise' for how to rotate windows in the
opposite direction.
> (defun rotate-windows-clockwise (&optional frame-or-window)
> "Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
> FRAME-OR-WINDOW must be a live frame or window and defaults to the
> selected frame. If FRAME-OR-WINDOW is a frame, rotate from the root
> window of the frame, otherwise rotate from FRAME-OR-WINDOW."
Please add two spaces after the end of each sentence like
selected frame. If FRAME-OR-WINDOW is a frame, ...
Also I would omit the "from" in "rotate from". And I would add a cross
reference to 'rotate-windows-anticlockwise' at the end.
> (defun flip-windows-horizontally (&optional frame-or-window)
> "Horizontally flip windows of FRAME-OR-WINDOW.
> FRAME-OR-WINDOW must be a live frame or window and defaults to the
> selected frame. If FRAME-OR-WINDOW is a frame, flip from the root
> window of the frame, otherwise flip from FRAME-OR-WINDOW."
Here you should probably first say what "flip" means. It's obvious when
you have a frame with two side-by-side live windows. It's already less
obvious with three side-by-side windows and even less so with more
complex layouts.
> (defun transpose-windows (&optional frame-or-window)
> "Transpose windows of FRAME-OR-WINDOW.
> Windows are rearanged such that where an horizontal split was used, an
> vertical one is instead, and vice versa. FRAME-OR-WINDOW must be a live
> frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
> is a frame, transpose from the root window of the frame, otherwise
> transpose from FRAME-OR-WINDOW."
Please us active voice like
Rearrange windows such that where a horizontal split was used a
vertical one is used instead, and vice versa.
and again remove the "from" in "transpose from".
> (defun transpose-windows--rearrange (frame-or-window conf norm-size)
I'd call this just 'window--transpose'.
> "Rearrange windows of FRAME-OR-WINDOW recursively.
> CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where
> horizontal split is called when splitting a window that was previously
> horizontally split, and VERTICAL-SPLIT for a window that was previously
> vertically split.
CONF is confusing. Why is it a cons cell in the first place? Wouldn't
a simple boolean - t for horizontal and nil for vertical - suffice? If
not, please explain why.
> When is NORM-SIZE non-nil, the size argument of the
I think it's just the pixel size and not the normal size, right? Also
you should write it as "When NORM-SIZE is non-nil ..."
> (frame-root-window frame-root-window)
This doesn't look right.
> (message "Not enough windows")
Better is "No windows to transpose".
> (let* ((fwin (frame-first-window rwin))
Note that 'frame-first-window' returns the first live window on its
frame. What if you want to flip some child windows only?
> (select-window selwin)))))
should become (set-frame-selected-window selwin) for the case that you
rotate windows on a non-selected frame.
> (defun transpose-windows--rearrange-1 (subtree cwin conf norm-size)
And this I'd call 'window--transpose-1'
> "Subroutine of `transpose-windows--rearrange'."
Again please describe all arguments in the doc-string.
And please handle the case where a frame contains side windows. Just
replace 'frame-root-window' with 'window--main-window' everywhere and
talk about "main window" instead of "root window".
Thanks, martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-02 9:04 ` martin rudalics
@ 2024-10-03 7:06 ` pranshu sharma
2024-10-03 8:17 ` martin rudalics
2024-10-08 18:35 ` Juri Linkov
0 siblings, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-10-03 7:06 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>>> Alternatively, we could give 'window-tree' an optional PIXELWISE
>>> argument so it returns the pixel edges of windows.
>>
>> You decide
>
> Better not - the fact that 'window-tree' does not report the edges of
> live windows constitutes bad design. Let's not support it. Rather
> let's make it a normal function called 'window-tree-pixel-sizes'. I
> would write its doc string as
>
> (defun window-tree-pixel-sizes (window &optional next)
> "Return pixel sizes of all windows rooted at WINDOW.
> The return value is a list where each window is represented either by a
> triple whose first element is either t for an internal window that is a
> horizontal combination, nil for an internal window that is a vertical
> combination, or the window itself for a live window. The second element
> is a cons of the pixel height and pixel width of the window. The third
> element is specified for internal windows only and recursively lists
> that window's child windows using the same triple structure."
>
> but you would have to check whether it really does what I wrote.
yes this does exactly that, thanks
>> (defun rotate-windows-anticlockwise (&optional frame-or-window)
>> "Rotate windows of FRAME-OR-WINDOW 90 degrees anticlockwise.
>> See `rotate-windows-clockwise' for more."
>
> I think it's better to write out the full doc-string here first and
> in a final sentence say
>
> See `rotate-windows-clockwise' for how to rotate windows in the
> opposite direction.
k
>
>> (defun flip-windows-horizontally (&optional frame-or-window)
>> "Horizontally flip windows of FRAME-OR-WINDOW.
>> FRAME-OR-WINDOW must be a live frame or window and defaults to the
>> selected frame. If FRAME-OR-WINDOW is a frame, flip from the root
>> window of the frame, otherwise flip from FRAME-OR-WINDOW."
>
> Here you should probably first say what "flip" means. It's obvious when
> you have a frame with two side-by-side live windows. It's already less
> obvious with three side-by-side windows and even less so with more
> complex layouts.
I explained it just using the metaphor of a reflection
>> (defun transpose-windows--rearrange (frame-or-window conf norm-size)
>
> I'd call this just 'window--transpose'.
done
>
>> "Rearrange windows of FRAME-OR-WINDOW recursively.
>> CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where
>> horizontal split is called when splitting a window that was previously
>> horizontally split, and VERTICAL-SPLIT for a window that was previously
>> vertically split.
>
> CONF is confusing. Why is it a cons cell in the first place? Wouldn't
> a simple boolean - t for horizontal and nil for vertical - suffice? If
> not, please explain why.
You can also have below split and abvoe split, not just nil and t
>
>> When is NORM-SIZE non-nil, the size argument of the
>
> I think it's just the pixel size and not the normal size, right? Also
> you should write it as "When NORM-SIZE is non-nil ..."
It's a bolean that states if or not the height or width should be
converted to width or height respecitlcly. I changed the name.
> Note that 'frame-first-window' returns the first live window on its
> frame. What if you want to flip some child windows only?
I didnt read docstring properly when I rewrote function, mb, played
cardi b bit too loud. I kind of assumed since it accepts
frame-or-window it would return first child. But fixed that now.
>> (select-window selwin)))))
>
> should become (set-frame-selected-window selwin) for the case that you
> rotate windows on a non-selected frame.
Thanks, I was looking for this function
>
>> (defun transpose-windows--rearrange-1 (subtree cwin conf norm-size)
>
> And this I'd call 'window--transpose-1'
>
> Again please describe all arguments in the doc-string.
I already explained the arguments in transpose-windows--rearrange. As
far as I can tell most other -1 postfix don't explain their arguments eg
window--resize-reset-1.
btw, what are the plans for the keybindings? imo they should be added
top level in C-x w map, as there are lots of spots avaiable, and there
are only 5 total new functions.
(defun window-tree-pixel-sizes (window &optional next)
"Return pixel sizes of all windows rooted at WINDOW.
The return value is a list where each window is represented either by a
triple whose first element is either t for an internal window that is a
horizontal combination, nil for an internal window that is a vertical
combination, or the window itself for a live window. The second element
is a cons of the pixel height and pixel width of the window. The third
element is specified for internal windows only and recursively lists
that window's child windows using the same triple structure."
(let (list)
(while window
(setq list
(cons
(cond
((window-top-child window)
(cons t (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(window-top-child window) t))))
((window-left-child window)
(cons nil (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(window-left-child window) t))))
(t (list window (cons (window-pixel-height window)
(window-pixel-width window)))))
list))
(setq window (when next (window-next-sibling window))))
(nreverse list)))
(defun rotate-windows-anticlockwise (&optional frame-or-window)
"Rotate windows of FRAME-OR-WINDOW anti-clockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
of the frame, otherwise rotate FRAME-OR-WINDOW. See
`rotate-windows-clockwise' for how to rotate windows in the opposite
direction"
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(transpose-windows--rearrange window '(right . above) nil)))
(defun rotate-windows-clockwise (&optional frame-or-window)
"Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
of the frame, otherwise rotate FRAME-OR-WINDOW. See
`rotate-windows-anticlockwise' for how to rotate windows in the opposite
direction"
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(transpose-windows--rearrange window '(left . below) nil)))
(defun flip-windows-horizontally (&optional frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW. When the windows are
flipped horzontally, the window layout is made to it's reflection from
the side edge. FRAME-OR-WINDOW must be a live frame or window and
defaults to the selected frame. If FRAME-OR-WINDOW is a frame, flip from
the main window of the frame, otherwise flip from FRAME-OR-WINDOW. See
`flip-windows-vertically' for how to flip windows vertically."
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(transpose-windows--rearrange window '(below . left) t)))
(defun flip-windows-vertically (&optional frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW. When the windows are
flipped vertically, the window layout is made to it's reflection from
the top edge. FRAME-OR-WINDOW must be a live frame or window and
defaults to the selected frame. If FRAME-OR-WINDOW is a frame, flip from
the main window of the frame, otherwise flip from FRAME-OR-WINDOW. See
`flip-windows-horizontally' for how to flip windows horizontally."
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(transpose-windows--rearrange window '(above . right) t)))
(defun transpose-windows (&optional frame-or-window)
"Transpose windows of FRAME-OR-WINDOW.
Rearrange windows such that where a horizontal split was used a vertical
one is used instead, and vice versa. FRAME-OR-WINDOW must be a live
frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
is a frame, transpose the main window of the frame, otherwise
transpose FRAME-OR-WINDOW."
(interactive `(,(and current-prefix-arg (window-parent (selected-window)))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(transpose-windows--rearrange window '(right . below) nil)))
(defun transpose-windows--rearrange (frame-or-window conf do-not-convert-size)
"Rearrange windows of FRAME-OR-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 `split-window' for a window that was
previously vertically split. When is DO-NOT-CONVERT-SIZE non-nil, the
size argument of the window-split is converted from vertical to
horizontal or vice versa, with the same proportion of the total split."
(pcase-let ((`(,rwin . ,frame)
(if (framep frame-or-window)
(cons (window-main-window frame-or-window) frame-or-window)
(cons frame-or-window (window-frame frame-or-window)))))
(if (or (not rwin)
(zerop (window-child-count rwin)))
(message "No windows to transpose")
(let* ((fwin rwin)
(selwin (frame-selected-window frame-or-window))
(win-tree (car (window-tree-pixel-sizes rwin))))
(while (not (window-live-p fwin))
(setq fwin (window-child fwin)))
;; All child windows need to be recursively deleted.
(mapc (lambda (win)
(when (and (windowp win)
(not (eq win fwin)))
(delete-window win)))
;; We know for sure that first 2 in the list are not
;; windows.
(cddr (flatten-list win-tree)))
(window--transpose-1 win-tree fwin conf do-not-convert-size)
;; Go back to previously selected window.
(set-frame-selected-window frame selwin)))))
(defun window--transpose-1 (subtree cwin conf do-not-convert-size)
"Subroutine of `transpose-windows--rearrange'."
;; `ilen' is the max size a window could be of given the split type.
;; `flen' is max size the window could be converted to the opposite
;; of the given split type.
(pcase-let ((`(,ilen . ,flen) (if (car subtree)
(cons (float (car (cadr subtree)))
(float (window-pixel-width cwin)))
(cons (float (cdr (cadr subtree)))
(float (window-pixel-height cwin))))))
(mapc
(pcase-lambda (`(,win . ,size))
(let ((split-size (- (if do-not-convert-size
size
(round (* flen (/ size ilen))))))
(split-type
(funcall (if (car subtree) 'car 'cdr) conf)))
(if (listp win)
;; `win' is a window subtree.
(window--transpose-1 win (split-window cwin
split-size
split-type
t
(seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win)))
conf do-not-convert-size)
;; `win' is a window.
(split-window cwin split-size
split-type t
win))))
(mapcar
(lambda (e)
(let ((window? (if (windowp (car e)) (car e) e)))
(cons window?
;; The relevent size of the window.
(if (car subtree)
(car (cadr e))
(cdr (cadr e))))))
;; By using cdddr, we ignore over window split type, sizes and
;; the first window (it's implicitly created).
(nreverse (cdddr subtree))))
;; (caaddr subtree) is the first window.
(unless (windowp (caaddr subtree))
(window--transpose-1 (caddr subtree) cwin conf do-not-convert-size))))
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-03 7:06 ` pranshu sharma
@ 2024-10-03 8:17 ` martin rudalics
2024-10-03 10:09 ` pranshu sharma
2024-10-08 18:35 ` Juri Linkov
1 sibling, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-03 8:17 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
>>> (defun transpose-windows--rearrange (frame-or-window conf norm-size)
>>
>> I'd call this just 'window--transpose'.
>
> done
Not really IIUC.
>> CONF is confusing. Why is it a cons cell in the first place? Wouldn't
>> a simple boolean - t for horizontal and nil for vertical - suffice? If
>> not, please explain why.
>
> You can also have below split and abvoe split, not just nil and t
Aha... So the values are the possible values of the SIDE argument of
'split-window'.
> I already explained the arguments in transpose-windows--rearrange. As
> far as I can tell most other -1 postfix don't explain their arguments eg
> window--resize-reset-1.
Then please say "the arguments are the same as for `window--transpose'".
> btw, what are the plans for the keybindings? imo they should be added
> top level in C-x w map, as there are lots of spots avaiable, and there
> are only 5 total new functions.
Once we have installed it, we'll consult Juri Linkov. He knows more
about keybindings in this area. BTW, you could have a look at the
function 'window-swap-states'. IIUC it should then be rewritten in
terms of the 'flip-windows-...' functions to get rid of window states
and the overlay rigmarole.
> (defun rotate-windows-anticlockwise (&optional frame-or-window)
> "Rotate windows of FRAME-OR-WINDOW anti-clockwise by 90 degrees.
> FRAME-OR-WINDOW must be a live frame or window and defaults to the
> selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
> of the frame, otherwise rotate FRAME-OR-WINDOW. See
> `rotate-windows-clockwise' for how to rotate windows in the opposite
> direction"
> (interactive `(,(and current-prefix-arg (window-parent))))
Please test what happens if you have a keybinding for this and you hit
that key in the minibuffer window, for example, after typing C-h f.
> (defun flip-windows-horizontally (&optional frame-or-window)
> "Horizontally flip windows of FRAME-OR-WINDOW. When the windows are
> flipped horzontally, the window layout is made to it's reflection from
> the side edge. FRAME-OR-WINDOW must be a live frame or window and
> defaults to the selected frame. If FRAME-OR-WINDOW is a frame, flip from
Still a space missing after the "."
> (defun flip-windows-vertically (&optional frame-or-window)
> "Horizontally flip windows of FRAME-OR-WINDOW. When the windows are
Newline missing after ".".
> flipped vertically, the window layout is made to it's reflection from
> the top edge. FRAME-OR-WINDOW must be a live frame or window and
> defaults to the selected frame. If FRAME-OR-WINDOW is a frame, flip from
Space missing after ".".
> (defun transpose-windows--rearrange (frame-or-window conf do-not-convert-size)
Rename to 'window--transpose' as you claimed above.
> "Rearrange windows of FRAME-OR-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 `split-window' for a window that was
... third argument _of_ ...
> previously vertically split. When is DO-NOT-CONVERT-SIZE non-nil, the
Should become "If DO-NOT-CONVERT-SIZE is non-nil, ..."
> size argument of the window-split is converted from vertical to
> horizontal or vice versa, with the same proportion of the total split."
> (pcase-let ((`(,rwin . ,frame)
> (if (framep frame-or-window)
> (cons (window-main-window frame-or-window) frame-or-window)
> (cons frame-or-window (window-frame frame-or-window)))))
> (if (or (not rwin)
> (zerop (window-child-count rwin)))
Actually
(zerop (window-child-count rwin))
is equivalent to
(window-live-p rwin)
and the latter should be cheaper.
> ;; The relevent size of the window.
I think "respective size" is what you mean.
> ;; By using cdddr, we ignore over window split type, sizes and
Rather "ignore window split type" without the "over".
> ;; the first window (it's implicitly created).
> (nreverse (cdddr subtree))))
> ;; (caaddr subtree) is the first window.
> (unless (windowp (caaddr subtree))
> (window--transpose-1 (caddr subtree) cwin conf do-not-convert-size))))
What I forgot to ask so far: Have you completed copyright assignment
for Emacs? I can't look into this myself.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-03 8:17 ` martin rudalics
@ 2024-10-03 10:09 ` pranshu sharma
2024-10-03 14:18 ` martin rudalics
2024-10-03 15:12 ` Eli Zaretskii
0 siblings, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-10-03 10:09 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>>> I'd call this just 'window--transpose'.
>>
>> done
>
> Not really IIUC.
sorry, it is done now
> Then please say "the arguments are the same as for
> `window--transpose'".
yes, done
> Once we have installed it, we'll consult Juri Linkov. He knows more
> about keybindings in this area. BTW, you could have a look at the
> function 'window-swap-states'. IIUC it should then be rewritten in
> terms of the 'flip-windows-...' functions to get rid of window states
> and the overlay rigmarole.
Hmm, the only way I can imagine using the flip thing is modifying window
tree to swap 2 windows in the subtree, and then set conf to (below
. left) and do-not-convert-size to nil. This will just rebuild the
whole window tree.
>
> Please test what happens if you have a keybinding for this and you hit
> that key in the minibuffer window, for example, after typing C-h f.
>
It just acts as if you are calling it without prefix arg, I think that
is expected behaviour imo. It just goes back to orignal if
read-minibuffer-restore-windows is non nil.
> Still a space missing after the "."
>
>> (defun flip-windows-vertically (&optional frame-or-window)
>> "Horizontally flip windows of FRAME-OR-WINDOW. When the windows are
>
> Newline missing after ".".
>
>> flipped vertically, the window layout is made to it's reflection from
>> the top edge. FRAME-OR-WINDOW must be a live frame or window and
>> defaults to the selected frame. If FRAME-OR-WINDOW is a frame, flip from
>
> Space missing after ".".
>> (defun transpose-windows--rearrange (frame-or-window conf do-not-convert-size)
> ... third argument _of_ ...
>
>> previously vertically split. When is DO-NOT-CONVERT-SIZE non-nil, the
>
ah puncutation comments, I just failed my english writing exam on
wednesday :(
> Actually
>
> (zerop (window-child-count rwin))
>
> is equivalent to
>
> (window-live-p rwin)
>
> and the latter should be cheaper.
Thanks, I applied that change
> What I forgot to ask so far: Have you completed copyright assignment
> for Emacs? I can't look into this myself.
I sent the form to assign@gnu.org, 15 days ago, still waiting for reply.
Does it normally take this long?
Code:
----------------------------------------
(defun window-tree-pixel-sizes (window &optional next)
"Return pixel sizes of all windows rooted at WINDOW.
The return value is a list where each window is represented either by a
triple whose first element is either t for an internal window that is a
horizontal combination, nil for an internal window that is a vertical
combination, or the window itself for a live window. The second element
is a cons of the pixel height and pixel width of the window. The third
element is specified for internal windows only and recursively lists
that window's child windows using the same triple structure."
(let (list)
(while window
(setq list
(cons
(cond
((window-top-child window)
(cons t (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(window-top-child window) t))))
((window-left-child window)
(cons nil (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(window-left-child window) t))))
(t (list window (cons (window-pixel-height window)
(window-pixel-width window)))))
list))
(setq window (when next (window-next-sibling window))))
(nreverse list)))
(defun rotate-windows-anticlockwise (&optional frame-or-window)
"Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
of the frame, otherwise rotate FRAME-OR-WINDOW. See
`rotate-windows-clockwise' for how to rotate windows in the opposite
direction"
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(window--transpose window '(right . above) nil)))
(defun rotate-windows-clockwise (&optional frame-or-window)
"Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
of the frame, otherwise rotate FRAME-OR-WINDOW. See
`rotate-windows-anticlockwise' for how to rotate windows in the opposite
direction"
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(window--transpose window '(left . below) nil)))
(defun flip-windows-horizontally (&optional frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW.
When the windows are flipped horzontally, the window layout is made to
it's reflection from the side edge. FRAME-OR-WINDOW must be a live
frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
is a frame, flip from the main window of the frame, otherwise flip from
FRAME-OR-WINDOW. See `flip-windows-vertically' for how to flip windows
vertically."
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(window--transpose window '(below . left) t)))
(defun flip-windows-vertically (&optional frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW.
When the windows are flipped vertically, the window layout is made to
it's reflection from the top edge. FRAME-OR-WINDOW must be a live frame
or window and defaults to the selected frame. If FRAME-OR-WINDOW is a
frame, flip from the main window of the frame, otherwise flip from
FRAME-OR-WINDOW. See `flip-windows-horizontally' for how to flip
windows horizontally."
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(window--transpose window '(above . right) t)))
(defun transpose-windows (&optional frame-or-window)
"Transpose windows of FRAME-OR-WINDOW.
Rearrange windows such that where a horizontal split was used a vertical
one is used instead, and vice versa. FRAME-OR-WINDOW must be a live
frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
is a frame, transpose the main window of the frame, otherwise
transpose FRAME-OR-WINDOW."
(interactive `(,(and current-prefix-arg (window-parent))))
(let ((window (if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window))))
(window--transpose window '(right . below) nil)))
(defun window--transpose (frame-or-window conf do-not-convert-size)
"Rearrange windows of FRAME-OR-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 DO-NOT-CONVERT-SIZE non-nil, the size
argument of the window-split is converted from vertical to horizontal or
vice versa, with the same proportion of the total split."
(pcase-let ((`(,rwin . ,frame)
(if (framep frame-or-window)
(cons (window-main-window frame-or-window) frame-or-window)
(cons frame-or-window (window-frame frame-or-window)))))
(if (or (not rwin)
(window-live-p rwin))
(message "No windows to transpose")
(let* ((fwin rwin)
(selwin (frame-selected-window frame-or-window))
(win-tree (car (window-tree-pixel-sizes rwin))))
(while (not (window-live-p fwin))
(setq fwin (window-child fwin)))
;; All child windows need to be recursively deleted.
(mapc (lambda (win)
(when (and (windowp win)
(not (eq win fwin)))
(delete-window win)))
;; We know for sure that first 2 in the list are not
;; windows.
(cddr (flatten-list win-tree)))
(window--transpose-1 win-tree fwin conf do-not-convert-size)
;; Go back to previously selected window.
(set-frame-selected-window frame selwin)))))
(defun window--transpose-1 (subtree cwin conf do-not-convert-size)
"Subroutine of `window--transpose'.
SUBTREE must be in the format of the result of
`window-tree-pixel-sizes'. CWIN is the current window through which the
window splits are made. The CONF and DO-NOT-CONVERT-SIZE arguments are
the same as the ones in `window--transpose'."
;; `ilen' is the max size a window could be of given the split type.
;; `flen' is max size the window could be converted to the opposite
;; of the given split type.
(pcase-let ((`(,ilen . ,flen) (if (car subtree)
(cons (float (car (cadr subtree)))
(float (window-pixel-width cwin)))
(cons (float (cdr (cadr subtree)))
(float (window-pixel-height cwin))))))
(mapc
(pcase-lambda (`(,win . ,size))
(let ((split-size (- (if do-not-convert-size
size
(round (* flen (/ size ilen))))))
(split-type
(funcall (if (car subtree) 'car 'cdr) conf)))
(if (listp win)
;; `win' is a window subtree.
(window--transpose-1 win (split-window cwin
split-size
split-type
t
(seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win)))
conf do-not-convert-size)
;; `win' is a window.
(split-window cwin split-size
split-type t
win))))
(mapcar
(lambda (e)
(let ((window? (if (windowp (car e)) (car e) e)))
(cons window?
;; The respective size of the window.
(if (car subtree)
(car (cadr e))
(cdr (cadr e))))))
;; By using cdddr, we ignore window split type, sizes and the
;; first window (it's implicitly created).
(nreverse (cdddr subtree))))
;; (caaddr subtree) is the first window.
(unless (windowp (caaddr subtree))
(window--transpose-1 (caddr subtree) cwin conf do-not-convert-size))))
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-03 10:09 ` pranshu sharma
@ 2024-10-03 14:18 ` martin rudalics
2024-10-04 5:50 ` pranshu sharma
2024-10-03 15:12 ` Eli Zaretskii
1 sibling, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-03 14:18 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
>> BTW, you could have a look at the
>> function 'window-swap-states'. IIUC it should then be rewritten in
>> terms of the 'flip-windows-...' functions to get rid of window states
>> and the overlay rigmarole.
>
> Hmm, the only way I can imagine using the flip thing is modifying window
> tree to swap 2 windows in the subtree, and then set conf to (below
> . left) and do-not-convert-size to nil. This will just rebuild the
> whole window tree.
I thought when the two windows are neighbors (which they often enough
are) we could flip them. But I think the solution is to (1) add for
each live window its number in 'window-state-get' (we need a function
'window-number' for that) and (2) in 'window-state-put', whenever we
split a window, try to find out whether a window with the respective
number is in 'window-dead-windows-table' and reuse it in that case.
> I sent the form to assign@gnu.org, 15 days ago, still waiting for reply.
> Does it normally take this long?
It can take longer. Don't worry. They'll reply before Emacs 31 gets
out.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-03 10:09 ` pranshu sharma
2024-10-03 14:18 ` martin rudalics
@ 2024-10-03 15:12 ` Eli Zaretskii
1 sibling, 0 replies; 85+ messages in thread
From: Eli Zaretskii @ 2024-10-03 15:12 UTC (permalink / raw)
To: pranshu sharma; +Cc: rudalics, emacs-devel
> From: pranshu sharma <pranshusharma366@gmail.com>
> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org
> Date: Thu, 03 Oct 2024 20:09:43 +1000
>
> > What I forgot to ask so far: Have you completed copyright assignment
> > for Emacs? I can't look into this myself.
>
> I sent the form to assign@gnu.org, 15 days ago, still waiting for reply.
Please ping them and CC me.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-03 14:18 ` martin rudalics
@ 2024-10-04 5:50 ` pranshu sharma
2024-10-04 8:08 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-04 5:50 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>> Hmm, the only way I can imagine using the flip thing is modifying window
>> tree to swap 2 windows in the subtree, and then set conf to (below
>> . left) and do-not-convert-size to nil. This will just rebuild the
>> whole window tree.
>
> I thought when the two windows are neighbors (which they often enough
> are) we could flip them.
Flip windows would work to swap when they are sibling windows, and
neigbor windows can sometimes not be siblings.
> But I think the solution is to (1) add for each live window its number
> in 'window-state-get' (we need a function 'window-number' for that)
> and (2) in 'window-state-put', whenever we split a window, try to find
> out whether a window with the respective number is in
> 'window-dead-windows-table' and reuse it in that case.
I can't comment much cuz I'm not really familiar with these functions,
but one thing that pops out is would maintaining a dead windows table
take up lot of resources?
Also do you think a function like 'window-rebuild-tree' would be useful,
and it accepts the a tree like the output of window-tree-pixel-sizes.
rn this is possible by using the following arguments for
window--transpose-1:
(window--transpose-1 tree-you-want (below . left) t)
However this is not really obvious.
I think it could be useful to add a wrapper with more obvious name
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-04 5:50 ` pranshu sharma
@ 2024-10-04 8:08 ` martin rudalics
2024-10-04 15:10 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-04 8:08 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
>> I thought when the two windows are neighbors (which they often enough
>> are) we could flip them.
>
> Flip windows would work to swap when they are sibling windows, and
> neigbor windows can sometimes not be siblings.
Right. I meant siblings (it was the typical two windows frame I had in
mind).
> I can't comment much cuz I'm not really familiar with these functions,
> but one thing that pops out is would maintaining a dead windows table
> take up lot of resources?
No. It's already here via the variable 'window-dead-windows-table'.
> Also do you think a function like 'window-rebuild-tree' would be useful,
> and it accepts the a tree like the output of window-tree-pixel-sizes.
> rn this is possible by using the following arguments for
> window--transpose-1:
> (window--transpose-1 tree-you-want (below . left) t)
> However this is not really obvious.
>
> I think it could be useful to add a wrapper with more obvious name
You would have to tell me in more detail what 'window-rebuild-tree'
would do. One thing 'window-tree-pixel-sizes' should then possibly do
is to include the identity of internal windows so 'window-rebuild-tree'
could resurrect them as well.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-04 8:08 ` martin rudalics
@ 2024-10-04 15:10 ` pranshu sharma
2024-10-05 14:43 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-04 15:10 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>> I can't comment much cuz I'm not really familiar with these functions,
>> but one thing that pops out is would maintaining a dead windows table
>> take up lot of resources?
>
> No. It's already here via the variable 'window-dead-windows-table'.
I see
>> Also do you think a function like 'window-rebuild-tree' would be useful,
>> and it accepts the a tree like the output of window-tree-pixel-sizes.
>> rn this is possible by using the following arguments for
>> window--transpose-1:
>> (window--transpose-1 tree-you-want (below . left) t)
>> However this is not really obvious.
>>
>> I think it could be useful to add a wrapper with more obvious name
>
> You would have to tell me in more detail what 'window-rebuild-tree'
> would do.
Say window window-tree-pixel-sizes retruns:
((t (918 . 562) (#<window 7 on *scratch*> (442 . 562)) (#<window 23 on
*scratch*> (476 . 562))))
Then if you C-x 1, and then:
(window-rebuild-tree
'((t (918 . 562) (#<window 7 on *scratch*> (442 . 562)) (#<window 23 on
*scratch*> (476 . 562)))))
It should rebuild the whole thing to the orignal
> One thing 'window-tree-pixel-sizes' should then possibly do is to
> include the identity of internal windows so 'window-rebuild-tree'
> could resurrect them as well.
idk what you mean by this, is the identity not included in window object
itself, as in does refer argument not work with it?
also I found that when using tabs from tab-bar-mode, calling functions
like flip windows 2 times in a row will fuck up the tabs. I tested it
with orignal transpose-frame.el and it does not have this problem, mayeb
it is something to do with the C new patch refer argument code?
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-04 15:10 ` pranshu sharma
@ 2024-10-05 14:43 ` martin rudalics
2024-10-06 2:54 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-05 14:43 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
>> You would have to tell me in more detail what 'window-rebuild-tree'
>> would do.
>
> Say window window-tree-pixel-sizes retruns:
>
> ((t (918 . 562) (#<window 7 on *scratch*> (442 . 562)) (#<window 23 on
> *scratch*> (476 . 562))))
>
> Then if you C-x 1, and then:
> (window-rebuild-tree
> '((t (918 . 562) (#<window 7 on *scratch*> (442 . 562)) (#<window 23 on
> *scratch*> (476 . 562)))))
>
> It should rebuild the whole thing to the orignal
Yes. That would be useful to (1) make a window state and store it
somewhere (2) arbitrarily alter the frame's configuration and (3) put
the state back into its frame. It's obviously the same thing
'save-window-excursion' does but there's a twist: When a frame gets
deleted, you cannot restore the window configuration in it.
'window-build-tree' could do it and 'undelete-frame' could use it. It
has one drawback: We can't collect the dead windows as long as the saved
state needs them. BTW I have no idea why 'undelete-frame-mode' does not
try to resurrect a deleted frame as we do with windows.
>> One thing 'window-tree-pixel-sizes' should then possibly do is to
>> include the identity of internal windows so 'window-rebuild-tree'
>> could resurrect them as well.
>
> idk what you mean by this, is the identity not included in window object
> itself, as in does refer argument not work with it?
No. Look at your your example above: All we know about the parent
window is what type of combination it is and what sizes it has.
> also I found that when using tabs from tab-bar-mode, calling functions
> like flip windows 2 times in a row will fuck up the tabs.
In what sense does it "fuck up the tabs"?
> I tested it
> with orignal transpose-frame.el and it does not have this problem, mayeb
> it is something to do with the C new patch refer argument code?
'tab-bar-mode' uses window configurations. Maybe that's the cause.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-05 14:43 ` martin rudalics
@ 2024-10-06 2:54 ` pranshu sharma
2024-10-06 15:02 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-06 2:54 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>> Say window window-tree-pixel-sizes retruns:
>>
>> ((t (918 . 562) (#<window 7 on *scratch*> (442 . 562)) (#<window 23 on
>> *scratch*> (476 . 562))))
>>
>> Then if you C-x 1, and then:
>> (window-rebuild-tree
>> '((t (918 . 562) (#<window 7 on *scratch*> (442 . 562)) (#<window 23 on
>> *scratch*> (476 . 562)))))
>>
>> It should rebuild the whole thing to the orignal
>
> Yes. That would be useful to (1) make a window state and store it
> somewhere (2) arbitrarily alter the frame's configuration and (3) put
> the state back into its frame. It's obviously the same thing
> 'save-window-excursion' does but there's a twist: When a frame gets
> deleted, you cannot restore the window configuration in it.
> 'window-build-tree' could do it and 'undelete-frame' could use it. It
> has one drawback: We can't collect the dead windows as long as the saved
> state needs them. BTW I have no idea why 'undelete-frame-mode' does not
> try to resurrect a deleted frame as we do with windows.
I was thinking more about making it easier to programticly change the
layout, as window-configurations are in C source, which is far from
userland. This would make it possible to do stuff like recursivly go
through window tree and do whatver you want, eg make vertical split
windows a bit larger.
>>> One thing 'window-tree-pixel-sizes' should then possibly do is to
>>> include the identity of internal windows so 'window-rebuild-tree'
>>> could resurrect them as well.
>>
>> idk what you mean by this, is the identity not included in window object
>> itself, as in does refer argument not work with it?
>
> No. Look at your your example above: All we know about the parent
> window is what type of combination it is and what sizes it has.
Do you mean the windows lile #<window nnn>, that are not included in
(window-tree) and return nil in window-live-p?
If so, what do we need to know about them apart from sizes,
transpose-windows and that know nothing about them and still work.
>> also I found that when using tabs from tab-bar-mode, calling functions
>> like flip windows 2 times in a row will fuck up the tabs.
>
> In what sense does it "fuck up the tabs"?
>
It just makes them dissapear in gui, and in terminal it makes them blank.
Try this:
C-x t n
C-x 2
C-x 3
M-x flip-windows
C-x z
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-06 2:54 ` pranshu sharma
@ 2024-10-06 15:02 ` martin rudalics
2024-10-06 15:52 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-06 15:02 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> I was thinking more about making it easier to programticly change the
> layout, as window-configurations are in C source, which is far from
> userland. This would make it possible to do stuff like recursivly go
> through window tree and do whatver you want, eg make vertical split
> windows a bit larger.
Hmm.. You can't do that by simply resizing windows?
>> No. Look at your your example above: All we know about the parent
>> window is what type of combination it is and what sizes it has.
>
> Do you mean the windows lile #<window nnn>, that are not included in
> (window-tree) and return nil in window-live-p?
Yes.
> If so, what do we need to know about them apart from sizes,
> transpose-windows and that know nothing about them and still work.
Because internal windows usually don't have properties. The only
exception are window parameters. With emacs -Q do C-x 2, then do
(set-window-parameter (window-parent) 'foo 3)
M-x transpose-windows
(window-parameter (window-parent) 'foo)
Gets me nil here so the parameter is lost.
> It just makes them dissapear in gui, and in terminal it makes them blank.
>
> Try this:
> C-x t n
> C-x 2
> C-x 3
> M-x flip-windows
> C-x z
Works here (with 'flip-windows-horizontally') as expected. At the end
there are two tabs, one for a one window configuration and one for a
three windows one.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-06 15:02 ` martin rudalics
@ 2024-10-06 15:52 ` pranshu sharma
2024-10-07 8:33 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-06 15:52 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 1756 bytes --]
martin rudalics <rudalics@gmx.at> writes:
>> I was thinking more about making it easier to programticly change the
>> layout, as window-configurations are in C source, which is far from
>> userland. This would make it possible to do stuff like recursivly go
>> through window tree and do whatver you want, eg make vertical split
>> windows a bit larger.
>
> Hmm.. You can't do that by simply resizing windows?
>
>>> No. Look at your your example above: All we know about the parent
>>> window is what type of combination it is and what sizes it has.
>>
>> Do you mean the windows lile #<window nnn>, that are not included in
>> (window-tree) and return nil in window-live-p?
>
> Yes.
>
>> If so, what do we need to know about them apart from sizes,
>> transpose-windows and that know nothing about them and still work.
>
> Because internal windows usually don't have properties. The only
> exception are window parameters. With emacs -Q do C-x 2, then do
>
> (set-window-parameter (window-parent) 'foo 3)
>
> M-x transpose-windows
>
> (window-parameter (window-parent) 'foo)
>
> Gets me nil here so the parameter is lost.
I see, seems like this is low level, complex and will require lot of
work to do right. I'm out. Let just focus on getting the orignal
functions added.
>> It just makes them dissapear in gui, and in terminal it makes them blank.
>>
>> Try this:
>> C-x t n
>> C-x 2
>> C-x 3
>> M-x flip-windows
>> C-x z
>
> Works here (with 'flip-windows-horizontally') as expected. At the end
> there are two tabs, one for a one window configuration and one for a
> three windows one.
I'm on master branch if that changes anything. Also you have to call
flip-windows-horizontally at least twice for it to work.
I attached demo of this.
[-- Attachment #2: out.mp4 --]
[-- Type: video/mp4, Size: 738207 bytes --]
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-09-28 15:28 ` Eli Zaretskii
@ 2024-10-07 8:33 ` martin rudalics
0 siblings, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-07 8:33 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: pranshusharma366, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 1321 bytes --]
I now rewrote 'split-window' and 'split-window-internal' and attach the
new versions. I don't mention "dead windows" in docs and error messages
any more. I can't use "leaf windows" because that is nowhere specified
(with one exception in E.9.2 the Elisp manual talks about "leaf nodes"
only). Internal windows OTOH are mentioned because they are defined and
frequently used in the manual. Please have a look.
ChangeLog is below, a NEWS entry will be added later.
martin
Let 'split-window' optionally reuse dead window instead of making new one
* lisp/window.el (split-window): New argument REFER to reuse dead
window instead of making a new one.
* src/window.c (Fwindow_old_buffer): Make it work for any window
including deleted ones. In doc-string say that for a deleted
window it returns the buffer the window had at the time it was
deleted.
(make_parent_window): Reset old_buffer slot installed by memcpy.
(Fsplit_window_internal): Handle new argument REFER inherited
from 'split-window'.
(Fdelete_window_internal, delete_all_child_windows): Set
old_buffer slot of the window to delete to the window's buffer.
* doc/lispref/windows.texi (Splitting Windows): Describe new
argument REFER.
(Window Hooks): Mention that for a dead window 'window-old-buffer'
returns the buffer the window had when it was deleted.
[-- Attachment #2: split-window.diff --]
[-- Type: text/x-patch, Size: 17820 bytes --]
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 541c91ddae2..149cc47de62 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1354,7 +1354,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
@@ -1402,10 +1402,24 @@ Splitting Windows
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 has to
+denote either a deleted, former live window on the same frame as
+@var{window} or an arbitrary live window. If @var{refer} is a deleted
+window, this function does not make a new window but rather resurrects
+@var{refer} 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 restored 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. An error is thrown if @var{refer}'s buffer has been deleted
+after @var{refer} itself was deleted.
+
+If @var{refer} is a live window, the new window will inherit properties
+like buffer, start and point 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
@@ -7103,13 +7117,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 07ea9584908..52191cff1dd 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5509,7 +5509,7 @@ 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.
@@ -5552,11 +5552,26 @@ split-window
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."
+If the optional fifth argument REFER is non-nil, it has to denote either
+a deleted, former live window on the same frame as WINDOW or an
+arbitrary live window. If REFER is a deleted window, 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.
+
+If REFER is a live window, the new window will inherit properties like
+buffer, start and point 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.
+
+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)
@@ -5596,7 +5611,7 @@ 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 is a side window or its first or last child is a
;; side window, throw an error unless `window-combination-resize'
;; equals 'side.
@@ -5635,8 +5650,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)
@@ -5757,8 +5772,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..94ea3491a11 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);
}
@@ -4521,6 +4522,7 @@ make_parent_window (Lisp_Object window, bool horflag)
wset_pointm (p, Qnil);
wset_old_pointm (p, Qnil);
wset_buffer (p, Qnil);
+ wset_old_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
@@ -5073,7 +5075,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 +5090,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;
+ bool dead = false;
CHECK_FIXNUM (pixel_size);
EMACS_INT total_size
@@ -5131,14 +5134,38 @@ 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
- /* 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 ("Deleted window REFER was not a live window (did not show a buffer)");
+ else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
+ error ("The buffer formerly shown by deleted window REFER has been deleted");
+ else if (!EQ (r->frame, frame))
+ error ("Deleted window REFER was not on the same frame as the window to split");
+
+ dead = true;
+ }
+ 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))
@@ -5195,7 +5222,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 +5254,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 +5295,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 +5303,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 +5424,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 +7770,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 +9124,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);
^ permalink raw reply related [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-06 15:52 ` pranshu sharma
@ 2024-10-07 8:33 ` martin rudalics
2024-10-07 9:42 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-07 8:33 UTC (permalink / raw)
To: pranshu sharma; +Cc: Eli Zaretskii, emacs-devel
> I see, seems like this is low level, complex and will require lot of
> work to do right. I'm out. Let just focus on getting the orignal
> functions added.
No sweat. In another mail I attached a new version of 'split-window'.
Please use that from now on.
>>> It just makes them dissapear in gui, and in terminal it makes them blank.
>>>
>>> Try this:
>>> C-x t n
>>> C-x 2
>>> C-x 3
>>> M-x flip-windows
>>> C-x z
>>
>> Works here (with 'flip-windows-horizontally') as expected. At the end
>> there are two tabs, one for a one window configuration and one for a
>> three windows one.
>
> I'm on master branch if that changes anything. Also you have to call
> flip-windows-horizontally at least twice for it to work.
>
> I attached demo of this.
In the demo you use C-x 3 C-x 2 C-x 3 so you make one split more.
However, even with that additional split I see no problem here. Unless
you mean that the final C-x 1 is part of the scenario. But even with
C-x t n C-x 3 C-x 2 C-x 3 C-x w f C-x z C-x 1
I still see two tabs here. I'm not familiar with 'tab-bar-mode' so you
will probably have to debug it. Otherwise, we can ask Juri whether he
sees any problems with it.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-07 8:33 ` martin rudalics
@ 2024-10-07 9:42 ` pranshu sharma
0 siblings, 0 replies; 85+ messages in thread
From: pranshu sharma @ 2024-10-07 9:42 UTC (permalink / raw)
To: martin rudalics; +Cc: Eli Zaretskii, emacs-devel
> No sweat. In another mail I attached a new version of 'split-window'.
> Please use that from now on.
Thanks, compiling emacs with the patch applied fixed the tab bug I was
having.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-03 7:06 ` pranshu sharma
2024-10-03 8:17 ` martin rudalics
@ 2024-10-08 18:35 ` Juri Linkov
2024-10-09 6:59 ` pranshu sharma
1 sibling, 1 reply; 85+ messages in thread
From: Juri Linkov @ 2024-10-08 18:35 UTC (permalink / raw)
To: pranshu sharma; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
> btw, what are the plans for the keybindings? imo they should be added
> top level in C-x w map, as there are lots of spots avaiable, and there
> are only 5 total new functions.
I recommend to put all these related transform commands on the dedicated
prefix map 'C-x w t', e.g. 'C-x w t t' for transpose-windows, etc.
because for example recently we discussed addition of new maps
'C-x w d <arrow>' to delete a window in direction, and
'C-x w s <arrow>' to split a window in direction.
But all these keys are already taken:
'C-x w d' for 'toggle-window-dedicated' and
'C-x w s' for 'window-toggle-side-windows'.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-08 18:35 ` Juri Linkov
@ 2024-10-09 6:59 ` pranshu sharma
2024-10-09 16:21 ` Juri Linkov
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-09 6:59 UTC (permalink / raw)
To: Juri Linkov; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
Juri Linkov <juri@linkov.net> writes:
> I recommend to put all these related transform commands on the dedicated
> prefix map 'C-x w t', e.g. 'C-x w t t' for transpose-windows, etc.
I see, then imo 'C-x w r' would probably be better, nemonic for
rerarrange. I don't see any connection between rotating and flipping
windows under transposing catagory.
> because for example recently we discussed addition of new maps
> 'C-x w d <arrow>' to delete a window in direction, and
> 'C-x w s <arrow>' to split a window in direction.
> But all these keys are already taken:
> 'C-x w d' for 'toggle-window-dedicated' and
> 'C-x w s' for 'window-toggle-side-windows'.
Is there really need to be so strict, I mean, having semi-nonsencial but
useful keybindings is better than not having them in the first place.
For example the split window in direction could be 'C-x w n <arrow>',
'n' for new window in direction. I would rather this than not having
it. (tbh having four arrows not very useful, as C-x 2 and C-x 3 already
deal with 2 of them).
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-09 6:59 ` pranshu sharma
@ 2024-10-09 16:21 ` Juri Linkov
2024-10-10 11:49 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: Juri Linkov @ 2024-10-09 16:21 UTC (permalink / raw)
To: pranshu sharma; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
>> I recommend to put all these related transform commands on the dedicated
>> prefix map 'C-x w t', e.g. 'C-x w t t' for transpose-windows, etc.
>
> I see, then imo 'C-x w r' would probably be better, nemonic for
> rerarrange. I don't see any connection between rotating and flipping
> windows under transposing catagory.
Here is an existing example to get inspiration.
There are two keymaps on images:
image-map:
i r image-rotate
i h image-flip-horizontally
i v image-flip-vertically
image-mode-map:
s 0 image-transform-reset-to-initial
s b image-transform-fit-both
s f image-mode-fit-frame
s h image-transform-fit-to-height
s i image-transform-fit-to-width
s m image-transform-set-smoothing
s o image-transform-reset-to-original
s p image-transform-set-percent
s r image-transform-set-rotation
s s image-transform-set-scale
s w image-transform-fit-to-window
>> because for example recently we discussed addition of new maps
>> 'C-x w d <arrow>' to delete a window in direction, and
>> 'C-x w s <arrow>' to split a window in direction.
>> But all these keys are already taken:
>> 'C-x w d' for 'toggle-window-dedicated' and
>> 'C-x w s' for 'window-toggle-side-windows'.
>
> Is there really need to be so strict, I mean, having semi-nonsencial but
> useful keybindings is better than not having them in the first place.
>
> For example the split window in direction could be 'C-x w n <arrow>',
> 'n' for new window in direction. I would rather this than not having
> it. (tbh having four arrows not very useful, as C-x 2 and C-x 3 already
> deal with 2 of them).
'n' for new is a nice mnemonics indeed.
Or maybe better to move the toggle commands to the prefix 'C-x w t':
'C-x w t d' for 'toggle-window-dedicated' and
'C-x w t s' for 'window-toggle-side-windows'.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-09 16:21 ` Juri Linkov
@ 2024-10-10 11:49 ` pranshu sharma
2024-10-10 16:57 ` Juri Linkov
0 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-10 11:49 UTC (permalink / raw)
To: Juri Linkov; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
Juri Linkov <juri@linkov.net> writes:
>>> I recommend to put all these related transform commands on the dedicated
>>> prefix map 'C-x w t', e.g. 'C-x w t t' for transpose-windows, etc.
>>
>> I see, then imo 'C-x w r' would probably be better, nemonic for
>> rerarrange. I don't see any connection between rotating and flipping
>> windows under transposing catagory.
>
> Here is an existing example to get inspiration.
> There are two keymaps on images:
>
> image-map:
> i r image-rotate
> i h image-flip-horizontally
> i v image-flip-vertically
>
Whole i map:
i + image-increase-size
i - image-decrease-size
i c image-crop
i h image-flip-horizontally
i o image-save
i r image-rotate
i v image-flip-vertically
i x image-cut
seems like the pattern here is changing how image is displayed.
What do you think about rotate/transpose/flip keybindings based of this?
>>> because for example recently we discussed addition of new maps
>>> 'C-x w d <arrow>' to delete a window in direction, and
>>> 'C-x w s <arrow>' to split a window in direction.
>>> But all these keys are already taken:
>>> 'C-x w d' for 'toggle-window-dedicated' and
>>> 'C-x w s' for 'window-toggle-side-windows'.
>>
>> Is there really need to be so strict, I mean, having semi-nonsencial but
>> useful keybindings is better than not having them in the first place.
>>
>> For example the split window in direction could be 'C-x w n <arrow>',
>> 'n' for new window in direction. I would rather this than not having
>> it. (tbh having four arrows not very useful, as C-x 2 and C-x 3 already
>> deal with 2 of them).
>
> 'n' for new is a nice mnemonics indeed.
Now I think about it, n is better than s, as when you are splitting
something, with an argument, it implies splitting direction (eg angle)
more than new window. 'n' would somewhat imply that a new window is
beign spawned.
> Or maybe better to move the toggle commands to the prefix 'C-x w t':
>
> 'C-x w t d' for 'toggle-window-dedicated' and
> 'C-x w t s' for 'window-toggle-side-windows'.
maybe, but my rsi says no
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-10 11:49 ` pranshu sharma
@ 2024-10-10 16:57 ` Juri Linkov
2024-10-13 5:43 ` pranshu sharma
0 siblings, 1 reply; 85+ messages in thread
From: Juri Linkov @ 2024-10-10 16:57 UTC (permalink / raw)
To: pranshu sharma; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
>>>> I recommend to put all these related transform commands on the dedicated
>>>> prefix map 'C-x w t', e.g. 'C-x w t t' for transpose-windows, etc.
>>>
>>> I see, then imo 'C-x w r' would probably be better, nemonic for
>>> rerarrange. I don't see any connection between rotating and flipping
>>> windows under transposing catagory.
>>
>> Here is an existing example to get inspiration.
>> There are two keymaps on images:
>>
>> image-map:
>> i r image-rotate
>> i h image-flip-horizontally
>> i v image-flip-vertically
>
> Whole i map:
> i + image-increase-size
> i - image-decrease-size
> i c image-crop
> i h image-flip-horizontally
> i o image-save
> i r image-rotate
> i v image-flip-vertically
> i x image-cut
>
> seems like the pattern here is changing how image is displayed.
>
> What do you think about rotate/transpose/flip keybindings based of this?
Then keybindings could be like these:
C-x w r a rotate-windows-anticlockwise
C-x w r c rotate-windows-clockwise
C-x w r h flip-windows-horizontally
C-x w r v flip-windows-vertically
C-x w r t transpose-windows
>>>> because for example recently we discussed addition of new maps
>>>> 'C-x w d <arrow>' to delete a window in direction, and
>>>> 'C-x w s <arrow>' to split a window in direction.
>>>> But all these keys are already taken:
>>>> 'C-x w d' for 'toggle-window-dedicated' and
>>>> 'C-x w s' for 'window-toggle-side-windows'.
>>>
>>> Is there really need to be so strict, I mean, having semi-nonsencial but
>>> useful keybindings is better than not having them in the first place.
>>>
>>> For example the split window in direction could be 'C-x w n <arrow>',
>>> 'n' for new window in direction. I would rather this than not having
>>> it. (tbh having four arrows not very useful, as C-x 2 and C-x 3 already
>>> deal with 2 of them).
>>
>> 'n' for new is a nice mnemonics indeed.
>
> Now I think about it, n is better than s, as when you are splitting
> something, with an argument, it implies splitting direction (eg angle)
> more than new window. 'n' would somewhat imply that a new window is
> beign spawned.
Agreed.
Also there is 'C-x w d' bound to toggle-window-dedicated,
so the key [D] can't be used to [D]elete a window
neither to [D]isplay a window in the specified direction.
>> Or maybe better to move the toggle commands to the prefix 'C-x w t':
>>
>> 'C-x w t d' for 'toggle-window-dedicated' and
>> 'C-x w t s' for 'window-toggle-side-windows'.
>
> maybe, but my rsi says no
Depends on how often these key sequences are used.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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:32 ` Juri Linkov
0 siblings, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-10-13 5:43 UTC (permalink / raw)
To: Juri Linkov; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
Juri Linkov <juri@linkov.net> writes:
> Then keybindings could be like these:
>
> C-x w r a rotate-windows-anticlockwise
> C-x w r c rotate-windows-clockwise
> C-x w r h flip-windows-horizontally
> C-x w r v flip-windows-vertically
> C-x w r t transpose-windows
Yeah this seems good.
Btw Martin, did you have look at the code for these functions, and think
it is ready? I am still waiting on assign@gnu.org(I sent them signed
form) but then we can add it.
>> Now I think about it, n is better than s, as when you are splitting
>> something, with an argument, it implies splitting direction (eg angle)
>> more than new window. 'n' would somewhat imply that a new window is
>> beign spawned.
>
> Agreed.
>
> Also there is 'C-x w d' bound to toggle-window-dedicated,
> so the key [D] can't be used to [D]elete a window
> neither to [D]isplay a window in the specified direction.
toggle window dedicated seems like an extremely useless command tbh,
have you should about binding it to C-x w D or C-x w C-d. I prefer the
former, but capital keys are not really used in C-x maps, but lots of
major modes(gnus,org) use them.
Also for the delete-window, you could maybe bind it to C-x w k, for kill
window, or C-x w <DEL>, not perfect but better than nothing. Also is
windmove not responiseble for these bindings?
>>> Or maybe better to move the toggle commands to the prefix 'C-x w t':
>>>
>>> 'C-x w t d' for 'toggle-window-dedicated' and
>>> 'C-x w t s' for 'window-toggle-side-windows'.
>>
>> maybe, but my rsi says no
>
> Depends on how often these key sequences are used.
If we are also coutning that in, do you think C-x w ^ map should be
moved to C-x w t (for tear-off) instead? I mean those keys are
frequently used if you have multitab/frame workflow, and pressing 'C-x w
^' a lot is perfect RSI fuel.
Also C-x w o for open in new frame/tab could be used.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-13 5:43 ` pranshu sharma
@ 2024-10-13 8:17 ` martin rudalics
2024-10-14 17:36 ` Juri Linkov
` (2 more replies)
2024-10-14 17:32 ` Juri Linkov
1 sibling, 3 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-13 8:17 UTC (permalink / raw)
To: pranshu sharma, Juri Linkov; +Cc: Eli Zaretskii, emacs-devel
>> Then keybindings could be like these:
>>
>> C-x w r a rotate-windows-anticlockwise
>> C-x w r c rotate-windows-clockwise
>> C-x w r h flip-windows-horizontally
>> C-x w r v flip-windows-vertically
>> C-x w r t transpose-windows
>
> Yeah this seems good.
I'm not sure whether "horizontally" and "vertically" are good terms. In
the past, people complained that for example 'split-window-horizontally'
could mean side-by-side or at some horizontal axis. Maybe
'flip-windows-left-right' and 'flip-windows-above-below' would be better
and should be reflected in the key mnemonics somehow.
> Btw Martin, did you have look at the code for these functions, and think
> it is ready? I am still waiting on assign@gnu.org(I sent them signed
> form) but then we can add it.
Yes. Also people might still want to comment on my latest patch to
'split-window'.
> toggle window dedicated seems like an extremely useless command tbh,
> have you should about binding it to C-x w D or C-x w C-d. I prefer the
> former, but capital keys are not really used in C-x maps, but lots of
> major modes(gnus,org) use them.
You should hurry here: IIUC 'toggle-window-dedicated' is new in the
release version and if you want to change its default binding, you'd
better do that right away there.
> Also C-x w o for open in new frame/tab could be used.
"o" could be also used for deleting an "old" window in some direction
but I think that it has been already taken to mean "other" for too long.
But whatever you do my opinion should hardly count - I'm using my own
sets of key bindings ever since.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-13 5:43 ` pranshu sharma
2024-10-13 8:17 ` martin rudalics
@ 2024-10-14 17:32 ` Juri Linkov
1 sibling, 0 replies; 85+ messages in thread
From: Juri Linkov @ 2024-10-14 17:32 UTC (permalink / raw)
To: pranshu sharma; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
>> Also there is 'C-x w d' bound to toggle-window-dedicated,
>> so the key [D] can't be used to [D]elete a window
>> neither to [D]isplay a window in the specified direction.
>
> toggle window dedicated seems like an extremely useless command tbh,
> have you should about binding it to C-x w D or C-x w C-d. I prefer the
> former, but capital keys are not really used in C-x maps, but lots of
> major modes(gnus,org) use them.
'C-x w D' is fine since "D" is displayed on the mode-line
for strongly dedicated windows.
> Also for the delete-window, you could maybe bind it to C-x w k, for kill
> window, or C-x w <DEL>, not perfect but better than nothing.
'C-x w k' would be more suitable for 'quit-window'
with its KILL argument set to t.
> Also is windmove not responiseble for these bindings?
windmove commands are exactly what should be bound to these keys
because there are no default short keybindings for
windmove-display-default-keybindings, windmove-delete-default-keybindings,
etc.
>>>> Or maybe better to move the toggle commands to the prefix 'C-x w t':
>>>>
>>>> 'C-x w t d' for 'toggle-window-dedicated' and
>>>> 'C-x w t s' for 'window-toggle-side-windows'.
>>>
>>> maybe, but my rsi says no
>>
>> Depends on how often these key sequences are used.
>
> If we are also coutning that in, do you think C-x w ^ map should be
> moved to C-x w t (for tear-off) instead? I mean those keys are
> frequently used if you have multitab/frame workflow, and pressing
> 'C-x w ^' a lot is perfect RSI fuel.
'^' is not easy to type indeed, so any suggestions are welcome.
Then with the prefix 'C-x w t' we could replace
C-x w ^ f tear-off-window
with
C-x w t f tear-off-window
with mnemonic 'w t f'.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-13 8:17 ` martin rudalics
@ 2024-10-14 17:36 ` Juri Linkov
2024-10-15 8:34 ` pranshu sharma
2024-10-18 14:52 ` pranshu sharma
2 siblings, 0 replies; 85+ messages in thread
From: Juri Linkov @ 2024-10-14 17:36 UTC (permalink / raw)
To: martin rudalics; +Cc: pranshu sharma, Eli Zaretskii, emacs-devel
>> Btw Martin, did you have look at the code for these functions, and think
>> it is ready? I am still waiting on assign@gnu.org(I sent them signed
>> form) but then we can add it.
>
> Yes. Also people might still want to comment on my latest patch to
> 'split-window'.
I already tested your latest patch together with Pranshu's implementation
of new commands, and everything works nicely.
>> toggle window dedicated seems like an extremely useless command tbh,
>> have you should about binding it to C-x w D or C-x w C-d. I prefer the
>> former, but capital keys are not really used in C-x maps, but lots of
>> major modes(gnus,org) use them.
>
> You should hurry here: IIUC 'toggle-window-dedicated' is new in the
> release version and if you want to change its default binding, you'd
> better do that right away there.
Agreed.
>> Also C-x w o for open in new frame/tab could be used.
>
> "o" could be also used for deleting an "old" window in some direction
> but I think that it has been already taken to mean "other" for too long.
Or 'C-x w C-o' like 'C-o' in 'open-line'. Or 'C-x w c' ([c]reate).
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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
2 siblings, 1 reply; 85+ messages in thread
From: pranshu sharma @ 2024-10-15 8:34 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>>> Then keybindings could be like these:
>>>
>>> C-x w r a rotate-windows-anticlockwise
>>> C-x w r c rotate-windows-clockwise
>>> C-x w r h flip-windows-horizontally
>>> C-x w r v flip-windows-vertically
>>> C-x w r t transpose-windows
>>
>> Yeah this seems good.
>
> I'm not sure whether "horizontally" and "vertically" are good terms. In
> the past, people complained that for example 'split-window-horizontally'
> could mean side-by-side or at some horizontal axis. Maybe
> 'flip-windows-left-right' and 'flip-windows-above-below' would be better
> and should be reflected in the key mnemonics somehow.
I think in terms of flipping the window, horizontal/vertical don't
really leave that much room for interpretatin as
split-window-horizontally. flip-windows-left-right above-below seem
kind of clunky tbh, imo we should just go with horizontal/vertical, I
mean, worst case users will learn it by using it, or reading the
doctring. 99% of users should already know from
split-window-horizotnally and it's counter part what horizontal and
vertical means in the context of window. But I'm also fine with
left-right, only problem is that it will make searching through C-h f
and 'C-h m' a little bit weird
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-15 8:34 ` pranshu sharma
@ 2024-10-15 16:16 ` Juri Linkov
0 siblings, 0 replies; 85+ messages in thread
From: Juri Linkov @ 2024-10-15 16:16 UTC (permalink / raw)
To: pranshu sharma; +Cc: martin rudalics, Eli Zaretskii, emacs-devel
>>>> Then keybindings could be like these:
>>>>
>>>> C-x w r a rotate-windows-anticlockwise
>>>> C-x w r c rotate-windows-clockwise
>>>> C-x w r h flip-windows-horizontally
>>>> C-x w r v flip-windows-vertically
>>>> C-x w r t transpose-windows
>>>
>>> Yeah this seems good.
>>
>> I'm not sure whether "horizontally" and "vertically" are good terms. In
>> the past, people complained that for example 'split-window-horizontally'
>> could mean side-by-side or at some horizontal axis. Maybe
>> 'flip-windows-left-right' and 'flip-windows-above-below' would be better
>> and should be reflected in the key mnemonics somehow.
>
> I think in terms of flipping the window, horizontal/vertical don't
> really leave that much room for interpretatin as
> split-window-horizontally. flip-windows-left-right above-below seem
> kind of clunky tbh, imo we should just go with horizontal/vertical, I
> mean, worst case users will learn it by using it, or reading the
> doctring. 99% of users should already know from
> split-window-horizotnally and it's counter part what horizontal and
> vertical means in the context of window. But I'm also fine with
> left-right, only problem is that it will make searching through C-h f
> and 'C-h m' a little bit weird
Agreed, let's stick to the established terminology.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-13 8:17 ` martin rudalics
2024-10-14 17:36 ` Juri Linkov
2024-10-15 8:34 ` pranshu sharma
@ 2024-10-18 14:52 ` pranshu sharma
2024-10-18 17:48 ` martin rudalics
2024-10-20 8:19 ` martin rudalics
2 siblings, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-10-18 14:52 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 1226 bytes --]
martin rudalics <rudalics@gmx.at> writes:
>> Btw Martin, did you have look at the code for these functions, and think
>> it is ready? I am still waiting on assign@gnu.org(I sent them signed
>> form) but then we can add it.
>
> Yes. Also people might still want to comment on my latest patch to
> 'split-window'.
>
Ok, so today I made major changes to the interactive side of the
functions(new file attached), mostly because the function would error if
you call iti with prefix arg, and the press C-x z, as it would use the
last used argument of frame-or-window which is now dead.
The problem was that C-x z sends with same arguments, so using code in
the (interactive (HERE)) would be repeated, which means same argument
would be used in `repeat'.
I added subtree speicfic options to the docstring(before they were not
there), and made changes to window--transpose. However it is weird if
you C-u M-x transpose-windows C-x z multiple times, you get weired
result since the parent window changes, nothing I can do about that,
just how windows work.
Also I'm finally done with the bureaucracy, I got the copyright
assignment done.
I'm ready to send final patch now, do you want it to be added to
window.el or new file?
[-- Attachment #2: new file --]
[-- Type: application/emacs-lisp, Size: 8509 bytes --]
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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-20 8:19 ` martin rudalics
1 sibling, 2 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-18 17:48 UTC (permalink / raw)
To: pranshu sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
> Ok, so today I made major changes to the interactive side of the
> functions(new file attached), mostly because the function would error if
> you call iti with prefix arg, and the press C-x z, as it would use the
> last used argument of frame-or-window which is now dead.
If we did reuse parent windows too as I once suggested, it would be
still valid and in place.
Note that 'flip-windows-horizontally' binds "windows" instead of
"window". And please don't cascade ifs - the appropriate Lisp idiom
here is 'cond' as in
(let ((window (cond
(current-prefix-arg
(window-parent))
((windowp frame-or-window)
frame-or-window)
(t
(window-main-window frame-or-window)))))
BTW does it work if FRAME-OR-WINDOW is an internal window? In the doc
string you say that it must be a "live frame or window" but 'windowp'
returns t for internal windows too.
> The problem was that C-x z sends with same arguments, so using code in
> the (interactive (HERE)) would be repeated, which means same argument
> would be used in `repeat'.
I never use C-x z so I have no opinion here.
> I added subtree speicfic options to the docstring(before they were not
> there), and made changes to window--transpose. However it is weird if
> you C-u M-x transpose-windows C-x z multiple times, you get weired
> result since the parent window changes, nothing I can do about that,
> just how windows work.
If you optionally can supply REFER as a cons where the car is the
previous (currently deleted) parent and the cdr the previous (currently
deleted) leaf window (you have to keep pointers to all of them just in
case the collector runs in between), we can easily fix that. Just that
'window-tree-pixel-sizes' would have to report the old parent window too
(in front of the flag telling whether it constitutes a horizontal or
vertical split, probably). It's easy to do, believe me.
> Also I'm finally done with the bureaucracy, I got the copyright
> assignment done.
Fine.
> I'm ready to send final patch now, do you want it to be added to
> window.el or new file?
What about adding it to windmove.el? Eli or Juri will decide.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-18 17:48 ` martin rudalics
@ 2024-10-18 18:37 ` Eli Zaretskii
2024-10-19 1:45 ` pranshu sharma
1 sibling, 0 replies; 85+ messages in thread
From: Eli Zaretskii @ 2024-10-18 18:37 UTC (permalink / raw)
To: martin rudalics; +Cc: pranshusharma366, juri, emacs-devel
> Date: Fri, 18 Oct 2024 19:48:41 +0200
> Cc: Juri Linkov <juri@linkov.net>, Eli Zaretskii <eliz@gnu.org>,
> emacs-devel@gnu.org
> From: martin rudalics <rudalics@gmx.at>
>
> > I'm ready to send final patch now, do you want it to be added to
> > window.el or new file?
>
> What about adding it to windmove.el? Eli or Juri will decide.
I don't think we need it preloaded? If so, windmove.el is indeed a
better place, IMO. But I'd like to hear from Juri as well.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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 8:33 ` martin rudalics
1 sibling, 2 replies; 85+ messages in thread
From: pranshu sharma @ 2024-10-19 1:45 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
> Note that 'flip-windows-horizontally' binds "windows" instead of
> "window". And please don't cascade ifs - the appropriate Lisp idiom
> here is 'cond' as in
>
Ah, I see
>
> BTW does it work if FRAME-OR-WINDOW is an internal window? In the doc
> string you say that it must be a "live frame or window" but 'windowp'
> returns t for internal windows too.
>
It works if frame-or-window is an internal window, I changed 'live' with
'valid'
>> I added subtree speicfic options to the docstring(before they were not
>> there), and made changes to window--transpose. However it is weird if
>> you C-u M-x transpose-windows C-x z multiple times, you get weired
>> result since the parent window changes, nothing I can do about that,
>> just how windows work.
>
> If you optionally can supply REFER as a cons where the car is the
> previous (currently deleted) parent and the cdr the previous (currently
> deleted) leaf window (you have to keep pointers to all of them just in
> case the collector runs in between), we can easily fix that. Just that
> 'window-tree-pixel-sizes' would have to report the old parent window too
> (in front of the flag telling whether it constitutes a horizontal or
> vertical split, probably). It's easy to do, believe me.
>
I don't understand
>
> What about adding it to windmove.el? Eli or Juri will decide.
>
I agree with Eli it should autoloaded, but every windmove function seems
to be focused on something to do with directional windows, these
functions would be a bit out of place.
How does a new autoloaded window-util.el sound? if you think its a good
idea I can also spend some time making an ace-window like window jump
function to make window-util a bit more complete. Which instead of
acewin puts a letter or number in the window's modeline instead, and if
you enter that letter/number you go to that window (the idea is to save
letter in a window-parameter, and use that in modeline)
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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
1 sibling, 1 reply; 85+ messages in thread
From: Eli Zaretskii @ 2024-10-19 6:45 UTC (permalink / raw)
To: pranshu sharma; +Cc: rudalics, juri, emacs-devel
> From: pranshu sharma <pranshusharma366@gmail.com>
> Cc: Juri Linkov <juri@linkov.net>, Eli Zaretskii <eliz@gnu.org>,
> emacs-devel@gnu.org
> Date: Sat, 19 Oct 2024 11:45:02 +1000
>
> > What about adding it to windmove.el? Eli or Juri will decide.
> >
>
> I agree with Eli it should autoloaded, but every windmove function seems
> to be focused on something to do with directional windows, these
> functions would be a bit out of place.
>
> How does a new autoloaded window-util.el sound?
We could have a new file window-x.el, perhaps. "-util" sounds like
it's for utility subroutines, which these functions aren't, AFAIU.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-19 1:45 ` pranshu sharma
2024-10-19 6:45 ` Eli Zaretskii
@ 2024-10-19 8:33 ` martin rudalics
1 sibling, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-19 8:33 UTC (permalink / raw)
To: pranshu sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
>> If you optionally can supply REFER as a cons where the car is the
>> previous (currently deleted) parent and the cdr the previous (currently
>> deleted) leaf window (you have to keep pointers to all of them just in
>> case the collector runs in between), we can easily fix that. Just that
>> 'window-tree-pixel-sizes' would have to report the old parent window too
>> (in front of the flag telling whether it constitutes a horizontal or
>> vertical split, probably). It's easy to do, believe me.
>>
>
> I don't understand
Suppose I had defined 'window-tree-pixel-sizes' as
(defun window-tree-pixel-sizes (window &optional next)
"Return pixel sizes of all windows rooted at WINDOW.
The return value is a list where each internal window is represented by
a list of four elements: The window object, an indicator that is t if
the window is a horizontal combination and nil otherwise, the size of
the window as a cons of its pixel height and pixel width and a recursive
list of the window's child windows using the same list structure. A
live window is represented by a cons consisting of the window object and
a cons of its pixel height and pixel width."
(let (list child)
(while window
(setq list
(cons
(if (window-live-p window)
(cons window (cons (window-pixel-height window)
(window-pixel-width window)))
(list window
(not (window-top-child window))
(cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(or (window-top-child window)
(window-left-child window))
t)))
list))
(setq window (when next (window-next-sibling window))))
(nreverse list)))
Then
(pp (window-tree-pixel-sizes (frame-root-window)))
gets me here with a three live windows frame instead of your
((nil (979 . 1680) (#<window 15 on window-transpose.el> (979 . 840))
(t (979 . 840) (#<window 89 on *Messages*> (490 . 840))
(#<window 95 on *scratch*> (489 . 840)))))
something like
((#<window 56> t (979 . 1680)
((#<window 15 on window-transpose.el> 979 . 840)
(#<window 60> nil (979 . 840)
((#<window 57 on *Messages*> 490 . 840)
(#<window 61 on *scratch*> 489 . 840))))))
The first element for each window would be its object: So #<window 56>
is a horizontal combination with the live #<window 15> as left and the
internal window #<window 60> as its right child. Now when you want to
recreate #<window #15> you would have to pass 'split-window' a cons cell
referencing #<window 56> and #<window #15> and 'split-window-internal'
would resurrect the first as the parent and the second as its child.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-19 6:45 ` Eli Zaretskii
@ 2024-10-19 18:19 ` Juri Linkov
0 siblings, 0 replies; 85+ messages in thread
From: Juri Linkov @ 2024-10-19 18:19 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: pranshu sharma, rudalics, emacs-devel
>> > What about adding it to windmove.el? Eli or Juri will decide.
>>
>> I agree with Eli it should autoloaded, but every windmove function seems
>> to be focused on something to do with directional windows, these
>> functions would be a bit out of place.
>>
>> How does a new autoloaded window-util.el sound?
>
> We could have a new file window-x.el, perhaps. "-util" sounds like
> it's for utility subroutines, which these functions aren't, AFAIU.
window-x.el looks right following the established file naming scheme.
Then some new functions from the end of window.el could be moved to
window-x.el as well.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-18 14:52 ` pranshu sharma
2024-10-18 17:48 ` martin rudalics
@ 2024-10-20 8:19 ` martin rudalics
2024-10-20 14:11 ` Pranshu Sharma
1 sibling, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-20 8:19 UTC (permalink / raw)
To: pranshu sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 923 bytes --]
> Ok, so today I made major changes to the interactive side of the
> functions(new file attached), mostly because the function would error if
> you call iti with prefix arg, and the press C-x z, as it would use the
> last used argument of frame-or-window which is now dead.
>
> The problem was that C-x z sends with same arguments, so using code in
> the (interactive (HERE)) would be repeated, which means same argument
> would be used in `repeat'.
>
> I added subtree speicfic options to the docstring(before they were not
> there), and made changes to window--transpose. However it is weird if
> you C-u M-x transpose-windows C-x z multiple times, you get weired
> result since the parent window changes, nothing I can do about that,
> just how windows work.
I now tried to make 'split-window' reuse a dead parent window as well so
you shouldn't have to care about this any more. Patch attached.
martin
[-- Attachment #2: split-window.diff --]
[-- Type: text/x-patch, Size: 19302 bytes --]
git diff | tee
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 0b8d7d3b76d..e34fc990734 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1355,7 +1355,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
@@ -1403,10 +1403,24 @@ Splitting Windows
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 has to
+denote either a deleted, former live window on the same frame as
+@var{window} or an arbitrary live window. If @var{refer} is a deleted
+window, this function does not make a new window but rather resurrects
+@var{refer} 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 restored 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. An error is thrown if @var{refer}'s buffer has been deleted
+after @var{refer} itself was deleted.
+
+If @var{refer} is a live window, the new window will inherit properties
+like buffer, start and point 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
@@ -7126,13 +7140,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..2cf2c288452 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5511,7 +5511,7 @@ 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.
@@ -5554,11 +5554,30 @@ split-window
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."
+If the optional fifth argument REFER is non-nil, it has to denote either
+a deleted, former live window on the same frame as WINDOW or an
+arbitrary live window. If REFER is a deleted window, 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.
+
+If and only if REFER is a deleted window and its previous parent window
+is now a deleted window too, do not make a new parent window but reuse
+the old aprent window, making it a valid window again.
+
+If REFER is a live window, the new window will inherit properties like
+buffer, start and point 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.
+
+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 +5617,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 +5663,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 +5785,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..ca5d8dea8aa 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);
}
@@ -4521,6 +4522,7 @@ make_parent_window (Lisp_Object window, bool horflag)
wset_pointm (p, Qnil);
wset_old_pointm (p, Qnil);
wset_buffer (p, Qnil);
+ wset_old_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
@@ -5073,7 +5075,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 +5090,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;
+ bool dead = false;
CHECK_FIXNUM (pixel_size);
EMACS_INT total_size
@@ -5131,14 +5134,38 @@ 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
- /* 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 ("Deleted window REFER was not a live window (did not show a buffer)");
+ else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
+ error ("The buffer formerly shown by deleted window REFER has been deleted");
+ else if (!EQ (r->frame, frame))
+ error ("Deleted window REFER was not on the same frame as the window to split");
+
+ dead = true;
+ }
+ 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))
@@ -5178,8 +5205,23 @@ 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 (dead && WINDOWP (r->parent) && !WINDOW_VALID_P (r->parent))
+ /* Reuse REFER's old parent too. */
+ {
+ p = XWINDOW (r->parent);
+ replace_window (old, r->parent, true);
+ wset_next (o, Qnil);
+ wset_prev (o, Qnil);
+ wset_parent (o, r->parent);
+ wset_combination (p, horflag, old);
+ }
+ else
+ /* Make a new parent window. */
+ {
+ make_parent_window (old, horflag);
+ p = XWINDOW (o->parent);
+ }
+
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 +5237,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 +5269,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 +5310,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 +5318,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 +5439,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 +7785,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 +9139,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);
^ permalink raw reply related [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-20 8:19 ` martin rudalics
@ 2024-10-20 14:11 ` Pranshu Sharma
2024-10-20 17:37 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: Pranshu Sharma @ 2024-10-20 14:11 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>
> I now tried to make 'split-window' reuse a dead parent window as well so
> you shouldn't have to care about this any more. Patch attached.
Thanks!
I can't compile emacs rn(will do tommorow), but does this
respwawn the child windows or only keep the parameters.
Either way, looks like rewrite of window--transpose-1 is coming.
Or do you mean it automaticlly makes the window-parameters the same?, as
in I won't have to modify anything?
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-20 14:11 ` Pranshu Sharma
@ 2024-10-20 17:37 ` martin rudalics
2024-10-21 5:54 ` Pranshu Sharma
2024-10-21 13:37 ` Pranshu Sharma
0 siblings, 2 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-20 17:37 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
> Or do you mean it automaticlly makes the window-parameters the same?, as
> in I won't have to modify anything?
It should work automatically in the sense that the parent windows get
reused too. But it doesn't work reliably for a couple of reasons:
With emacs -Q do
(let ((window (split-window)))
(set-window-buffer window (get-buffer-create "*foo*"))
(setq window (split-window nil nil t))
(set-window-buffer window "*Messages*"))
so you have an arrangement with *scratch* and *Messages* at the top and
*foo* at the bottom. Now rotate windows clockwise with the *Messages*
window selected. Here this first deletes the remaining windows and
starts with *scratch* as the root window's buffer. Now the very first
you ask is to split the *scratch* window with REFER set to the *foo*
window.
This is a bad idea because *foo* is _not_ *scratch*'s sibling in the
initial configuration - *Messages* is. It can break my approach which
reuses the old parent of REFER which, however, is _not_ the old parent
of the *scratch* window. In the next call to 'split-window' you again
ask to split the *scratch* window but set REFER to the *Messages* window.
All goes miraculously well.
Repeat the call. Now *foo* with REFER *scratch* reuses the parent of
*scratch*. Next *scratch* with REFER *Messages* reuses the parent of
*Messages*. Probably due to the fact in which order you earlier deleted
windows, this changes the parent windows from (the car of each cons is
the buffer window the cdr the parent window)
((#<window 9 on *Messages*> . #<window 8>)
(#<window 7 on *foo*> . #<window 6>)
(#<window 3 on *scratch*> . #<window 8>))
before the call to
((#<window 9 on *Messages*> . #<window 6>)
(#<window 7 on *foo*> . #<window 8>)
(#<window 3 on *scratch*> . #<window 6>))
after the call. So the parent windows were interchanged. Looks like a
minor annoyance. But with a more complex layout things fail. Try with
(let ((window (split-window)))
(set-window-buffer window (get-buffer-create "*foo1*"))
(setq window (split-window nil nil t))
(set-window-buffer window (get-buffer-create "*foo2*"))
(setq window (split-window window))
(set-window-buffer window (get-buffer-create "*foo3*")))
and repeat the scenario. Here, in the second call, when it tries to
split the window on *foo3* with REFER set to the window of *foo2*,
'split-window' finds out that the parent of *foo2* has been already
reused and allocates a new parent window.
I think you have to change two things:
(1) Instead of
;; All child windows need to be recursively deleted.
(mapc (lambda (win)
(when (and (windowp win)
(not (eq win fwin)))
(delete-window win)))
;; We know for sure that first 2 in the list are not
;; windows.
(cddr (flatten-list win-tree)))
use
(delete-other-windows fwin)
Your strategy of deleting windows piecemeal destroys the original window
structure in a quite chaotic way.
(2) Whenever you split a leaf window (like *scratch* in the first
scenario), make sure that REFER gets always set to the window's
'window-next-sibling' (*Messages* in the first scenario) and never to
some window further up or down in the hierarchy (like *foo* in the first
scenario). Which means that in the first scenario you have two possible
strategies:
- Split *scratch* first with REFER *Messages* and then its parent with
REFER *foo*.
- Split *foo* first with REFER *scratch* and then *scratch* with REFER
*Messages*.
You should try both in case we later find out that the one we did choose
fails.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
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
1 sibling, 2 replies; 85+ messages in thread
From: Pranshu Sharma @ 2024-10-21 5:54 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>> Or do you mean it automaticlly makes the window-parameters the same?, as
>> in I won't have to modify anything?
>
> It should work automatically in the sense that the parent windows get
> reused too. But it doesn't work reliably for a couple of reasons:
I compiled an it works
> With emacs -Q do
>
> (let ((window (split-window)))
> (set-window-buffer window (get-buffer-create "*foo*"))
> (setq window (split-window nil nil t))
> (set-window-buffer window "*Messages*"))
>
> so you have an arrangement with *scratch* and *Messages* at the top and
> *foo* at the bottom. Now rotate windows clockwise with the *Messages*
> window selected. Here this first deletes the remaining windows and
> starts with *scratch* as the root window's buffer. Now the very first
> you ask is to split the *scratch* window with REFER set to the *foo*
> window.
>
> This is a bad idea because *foo* is _not_ *scratch*'s sibling in the
> initial configuration - *Messages* is. It can break my approach which
> reuses the old parent of REFER which, however, is _not_ the old parent
> of the *scratch* window. In the next call to 'split-window' you again
> ask to split the *scratch* window but set REFER to the *Messages* window.
> All goes miraculously well.
Ohh, I finally get what you mean.
I thought that for each layout, there is only one possible window tree
that will match it.
> Repeat the call. Now *foo* with REFER *scratch* reuses the parent of
> *scratch*. Next *scratch* with REFER *Messages* reuses the parent of
> *Messages*. Probably due to the fact in which order you earlier deleted
> windows, this changes the parent windows from (the car of each cons is
> the buffer window the cdr the parent window)
>
> ((#<window 9 on *Messages*> . #<window 8>)
> (#<window 7 on *foo*> . #<window 6>)
> (#<window 3 on *scratch*> . #<window 8>))
>
> before the call to
>
> ((#<window 9 on *Messages*> . #<window 6>)
> (#<window 7 on *foo*> . #<window 8>)
> (#<window 3 on *scratch*> . #<window 6>))
>
> after the call. So the parent windows were interchanged. Looks like a
> minor annoyance. But with a more complex layout things fail. Try with
>
> (let ((window (split-window)))
> (set-window-buffer window (get-buffer-create "*foo1*"))
> (setq window (split-window nil nil t))
> (set-window-buffer window (get-buffer-create "*foo2*"))
> (setq window (split-window window))
> (set-window-buffer window (get-buffer-create "*foo3*")))
>
> and repeat the scenario. Here, in the second call, when it tries to
> split the window on *foo3* with REFER set to the window of *foo2*,
> 'split-window' finds out that the parent of *foo2* has been already
> reused and allocates a new parent window.
>
> I think you have to change two things:
>
> (1) Instead of
>
> ;; All child windows need to be recursively deleted.
> (mapc (lambda (win)
> (when (and (windowp win)
> (not (eq win fwin)))
> (delete-window win)))
> ;; We know for sure that first 2 in the list are not
> ;; windows.
> (cddr (flatten-list win-tree)))
>
> use
>
> (delete-other-windows fwin)
>
> Your strategy of deleting windows piecemeal destroys the original window
> structure in a quite chaotic way.
The problem is that it will fail when only acting upon a partial subtree.
> (2) Whenever you split a leaf window (like *scratch* in the first
> scenario), make sure that REFER gets always set to the window's
> 'window-next-sibling' (*Messages* in the first scenario) and never to
> some window further up or down in the hierarchy (like *foo* in the first
> scenario). Which means that in the first scenario you have two possible
> strategies:
>
> - Split *scratch* first with REFER *Messages* and then its parent with
> REFER *foo*.
>
> - Split *foo* first with REFER *scratch* and then *scratch* with REFER
> *Messages*.
I see what you mean here, I knew I should done something like this from
the start when I had to flatten the window subtree.
Seems like this will require a rewrite of the windows--transpose-1
(Yay!), but it should be doable, I have an idea of what I could do.
btw, is there a major perfomance/memory footprint of the patches you are
adding
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-21 5:54 ` Pranshu Sharma
@ 2024-10-21 8:14 ` martin rudalics
2024-10-21 9:23 ` martin rudalics
1 sibling, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-21 8:14 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
> I thought that for each layout, there is only one possible window tree
> that will match it.
I think so too. But there are infinite many ways to obtain that layout.
>> Your strategy of deleting windows piecemeal destroys the original window
>> structure in a quite chaotic way.
>
> The problem is that it will fail when only acting upon a partial subtree.
I see. I think I have to give 'delete-other-windows' an additional
argument say MAIN so it checks whether WINDOW is in MAIN's subtree and
puts WINDOW in the place of MAIN in that subtree. You would call it
with MAIN set to the root of the subtree and WINDOW to fwin.
Note again: When you delete windows one by one you may change the
parents of the windows that remain but you want to delete afterwards.
But 'split-window' should work with the parent windows that existed
_before_ you started deleting. So maybe you should first try to
experiment with 'delete-other-windows' as it is now and then we can try
to fix 'delete-other-windows' the way I sketched above.
> I see what you mean here, I knew I should done something like this from
> the start when I had to flatten the window subtree.
>
> Seems like this will require a rewrite of the windows--transpose-1
> (Yay!), but it should be doable, I have an idea of what I could do.
I cannot exclude that one can also get good results by using your
current approach. As I said above, there are many ways to obtain a
specific layout.
> btw, is there a major perfomance/memory footprint of the patches you are
> adding
In which sense? Reusing windows should reduce GC overhead since you
create less new windows and produce less obsolete ones. Everything else
is negligible.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-21 5:54 ` Pranshu Sharma
2024-10-21 8:14 ` martin rudalics
@ 2024-10-21 9:23 ` martin rudalics
1 sibling, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-21 9:23 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
>> Your strategy of deleting windows piecemeal destroys the original window
>> structure in a quite chaotic way.
>
> The problem is that it will fail when only acting upon a partial subtree.
I completely forgot that 'delete-other-windows-internal' should already
do everything you need: Call it with WINDOW set to fwin and ROOT set to
the root window of the "partial subtree".
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-20 17:37 ` martin rudalics
2024-10-21 5:54 ` Pranshu Sharma
@ 2024-10-21 13:37 ` Pranshu Sharma
2024-10-22 18:12 ` martin rudalics
1 sibling, 1 reply; 85+ messages in thread
From: Pranshu Sharma @ 2024-10-21 13:37 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
martin rudalics <rudalics@gmx.at> writes:
>
> - Split *scratch* first with REFER *Messages* and then its parent with
> REFER *foo*.
>
> - Split *foo* first with REFER *scratch* and then *scratch* with REFER
> *Messages*.
>
Here, the second option is non feesible as it relies on #<window 222 on
*foo*> being an actual window, not a window split. Althouht it is
possible with workarounds and I'll end up implimenting the first option
trying to get the second to work.
(t (0 1 148 31)
(nil (0 1 148 16) #<window 182 on *scratch*> #<window 224 on
*Messages*>)
#<window 222 on *foo*>)
When I orignally wrote the transpose-windows-1, I didn't know about
internal windows.
Also I looked at your other message about window split internal, that
should help a lot.
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-21 13:37 ` Pranshu Sharma
@ 2024-10-22 18:12 ` martin rudalics
2024-10-24 14:38 ` Pranshu Sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-22 18:12 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 2100 bytes --]
>> - Split *scratch* first with REFER *Messages* and then its parent with
>> REFER *foo*.
>>
>> - Split *foo* first with REFER *scratch* and then *scratch* with REFER
>> *Messages*.
>>
>
> Here, the second option is non feesible as it relies on #<window 222 on
> *foo*> being an actual window, not a window split. Althouht it is
> possible with workarounds and I'll end up implimenting the first option
> trying to get the second to work.
>
> (t (0 1 148 31)
> (nil (0 1 148 16) #<window 182 on *scratch*> #<window 224 on
> *Messages*>)
> #<window 222 on *foo*>)
>
> When I orignally wrote the transpose-windows-1, I didn't know about
> internal windows.
>
> Also I looked at your other message about window split internal, that
> should help a lot.
OK. I looked into this again and maybe I can help you. Attached find a
new version of 'split-window' that accepts also a (WINDOW . PARENT)
cons as REFER where WINDOW is the buffer window to use and PARENT its
parent before WINDOW was deleted. How to use that is documented in the
version of window-transpose.el I also attach. It's full of debugging
code so you have to figure out yourself how to make use of it.
The three crucial aspects are:
(1) It has a function 'window-alist' that establishes before transposing
anything an association list of all live windows with their parents and
stores it in the variable 'window-alist'.
(2) It deletes windows via 'delete-other-windows-internal'.
(3) When it calls 'split-window' then in the (listp win) case I set the
cdr of the cons to the old parent of the _window to split_ (found via
'window-alist'). In the win is a window case, I set the cdr of the cons
to the old parent of the _window to make_ (again found via
'window-alist').
This survives quite a number of 'rotate-windows-clockwise' on the three
windows structure I create on the bottom of window-transpose.el.
Try it and then maybe try it with your remaining functions in an
analogous manner. The tricky part is certainly (3) which might just
work with my simple rotation scenario.
martin
[-- Attachment #2: split-window-diff --]
[-- Type: text/plain, Size: 21794 bytes --]
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 0b8d7d3b76d..8bf6ebc828b 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1355,7 +1355,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
@@ -1403,10 +1403,35 @@ Splitting Windows
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 has to
+denote either a deleted, former live window on the same frame as
+@var{window} or an arbitrary live window. If @var{refer} is a deleted
+window, this function does not make a new window but rather resurrects
+@var{refer} 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 restored 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. An error is thrown if @var{refer}'s buffer has been deleted
+after @var{refer} itself was deleted.
+
+If and only if @var{refer} is a deleted window and its former parent
+window (@pxref{Windows and Frames}) is a deleted window too, this
+function will also resurrect the old parent window and insert it into
+the appropriate place in the window tree.
+
+Note that in order to resurrect a deleted window, that window must not
+have been recycled by the garbage collector (@pxref{Garbage Collection})
+yet. Hence to make sure that a deleted window can get resurrected, you
+should keep a reference to any such window in a variable until this
+function returns.
+
+If @var{refer} is a live window, the new window will inherit properties
+like buffer, start and point 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
@@ -7126,13 +7151,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..2cf2c288452 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5511,7 +5511,7 @@ 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.
@@ -5554,11 +5554,30 @@ split-window
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."
+If the optional fifth argument REFER is non-nil, it has to denote either
+a deleted, former live window on the same frame as WINDOW or an
+arbitrary live window. If REFER is a deleted window, 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.
+
+If and only if REFER is a deleted window and its previous parent window
+is now a deleted window too, do not make a new parent window but reuse
+the old aprent window, making it a valid window again.
+
+If REFER is a live window, the new window will inherit properties like
+buffer, start and point 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.
+
+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 +5617,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 +5663,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 +5785,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..5254f96ba89 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,54 @@ 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
- /* Use the frame's selected window as reference window. */
- reference = FRAME_SELECTED_WINDOW (f);
- r = XWINDOW (reference);
+ {
+ if (CONSP (refer))
+ {
+ parent = Fcdr (refer);
+ p = decode_any_window (parent);
+ refer = Fcar (refer);
+
+ /* Note: PARENT is not necessarily REFER's parent at the time
+ REFER was deleted but maybe some earlier parent of REFER. */
+ if (BUFFERP (p->old_buffer))
+ error ("Deleted window in REFER was a live window (did show a buffer)");
+ else if (!EQ (p->frame, frame))
+ error ("Deleted window in REFER was not on same frame as the window to split");
+ }
+
+ 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 ("Deleted window in REFER was not a live window (did not show a buffer)");
+ else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
+ error ("The buffer formerly shown by deleted window in REFER has been deleted");
+ else if (!EQ (r->frame, frame))
+ error ("Deleted window in REFER was not on same frame as the window to split");
+
+ dead = true;
+ }
+ else if (!NILP (parent))
+ error ("REFER must not contain live and dead windows");
+ 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))
@@ -5178,8 +5181,31 @@ 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);
+ /* This is the crux of the old make_parent_window. */
+ if (dead && WINDOWP (parent) && !WINDOW_VALID_P (parent))
+ /* Reuse REFER's cdr (provided by caller). */
+ p = XWINDOW (parent);
+ else if (dead && WINDOWP (r->parent) && !WINDOW_VALID_P (r->parent))
+ /* Reuse REFER's old parent (potentially unsafe). */
+ {
+ parent = r->parent;
+ p = XWINDOW (parent);
+ }
+ else
+ /* Allocate new parent (classic style). */
+ {
+ p = allocate_window ();
+ XSETWINDOW (parent, p);
+ p->sequence_number = ++sequence_number;
+ wset_frame (p, frame);
+ }
+
+ 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 +5221,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 +5253,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 +5294,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 +5302,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 +5423,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 +7769,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 +9123,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-transpose.el --]
[-- Type: text/x-emacs-lisp, Size: 9966 bytes --]
;;; -*- lexical-binding:t -*-
(setq debug-on-error t)
(defvar old-window-alist nil)
(defvar old-window-list nil)
(defvar new-window-alist nil)
(defvar window-alist nil)
(defun window-alist (&optional window)
(setq window-alist nil)
(walk-window-subtree
(lambda (w)
(setq window-alist
(cons (cons w (window-parent w))
window-alist)))
(or window (frame-root-window))))
(defun old-window-alist ()
(setq old-window-list (window-list))
(setq old-window-alist nil)
(dolist (window old-window-list)
(setq old-window-alist
(cons
(cons window (window-parent window))
old-window-alist)))
(setq old-window-alist (nreverse old-window-alist)))
(defun new-window-alist ()
(setq new-window-alist nil)
(dolist (window old-window-list)
(setq new-window-alist
(cons
(cons window (window-parent window))
new-window-alist)))
(setq new-window-alist (nreverse new-window-alist)))
(defun window-tree-pixel-sizes (window &optional next)
"Return pixel sizes of all windows rooted at WINDOW.
The return value is a list where each window is represented either by a
triple whose first element is either t for an internal window that is a
horizontal combination, nil for an internal window that is a vertical
combination, or the window itself for a live window. The second element
is a cons of the pixel height and pixel width of the window. The third
element is specified for internal windows only and recursively lists
that window's child windows using the same triple structure."
(let (list)
(while window
(setq list
(cons
(cond
((window-top-child window)
(cons t (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(window-top-child window) t))))
((window-left-child window)
(cons nil (cons (cons (window-pixel-height window)
(window-pixel-width window))
(window-tree-pixel-sizes
(window-left-child window) t))))
(t (list window (cons (window-pixel-height window)
(window-pixel-width window)))))
list))
(setq window (when next (window-next-sibling window))))
(nreverse list)))
(defun rotate-windows-anticlockwise (&optional subtree frame-or-window)
"Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
of the frame, otherwise rotate FRAME-OR-WINDOW. See
`rotate-windows-clockwise' for how to rotate windows in the opposite
direction. If SUBTREE is non-nil, the function will act as if
FRAME-OR-WINDOW is the parent window of the selected window."
(interactive "P")
(let ((window (if subtree
(window-parent)
(if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window)))))
(window--transpose window '(right . above) nil)))
(defun rotate-windows-clockwise (&optional subtree frame-or-window)
"Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
FRAME-OR-WINDOW must be a live frame or window and defaults to the
selected frame. If FRAME-OR-WINDOW is a frame, rotate the main window
of the frame, otherwise rotate FRAME-OR-WINDOW. See
`rotate-windows-anticlockwise' for how to rotate windows in the opposite
direction. If SUBTREE is non-nil, the function will act as if
FRAME-OR-WINDOW is the parent window of the selected window."
(interactive "P")
(let ((window (cond
(subtree
(window-parent))
((windowp frame-or-window)
frame-or-window)
(t
(window-main-window frame-or-window)))))
(old-window-alist)
(window-alist window)
;; (message "%s" window-alist) (sit-for 3)
(window--transpose window '(left . below) nil)
(new-window-alist)
(with-current-buffer (get-buffer-create "*foo*")
(erase-buffer)
(pp old-window-alist (current-buffer))
(insert "\n")
(pp new-window-alist (current-buffer)))))
(defun flip-windows-horizontally (&optional subtree frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW.
When the windows are flipped horzontally, the window layout is made to
it's reflection from the side edge. FRAME-OR-WINDOW must be a live
frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
is a frame, flip from the main window of the frame, otherwise flip from
FRAME-OR-WINDOW. See `flip-windows-vertically' for how to flip windows
vertically. If SUBTREE is non-nil, the function will act as if
FRAME-OR-WINDOW is the parent window of the selected window."
(interactive "P")
(let ((window (cond
(subtree
(window-parent))
((windowp frame-or-window)
frame-or-window)
(t
(window-main-window frame-or-window)))))
(window--transpose window '(below . left) t)))
(defun flip-windows-vertically (&optional subtree frame-or-window)
"Horizontally flip windows of FRAME-OR-WINDOW.
When the windows are flipped vertically, the window layout is made to
it's reflection from the top edge. FRAME-OR-WINDOW must be a live frame
or window and defaults to the selected frame. If FRAME-OR-WINDOW is a
frame, flip from the main window of the frame, otherwise flip from
FRAME-OR-WINDOW. See `flip-windows-horizontally' for how to flip
windows horizontally."
(interactive "P")
(let ((window (if subtree
(window-parent)
(if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window)))))
(window--transpose window '(above . right) t)))
(defun transpose-windows (&optional subtree frame-or-window)
"Transpose windows of FRAME-OR-WINDOW.
Rearrange windows such that where a horizontal split was used a vertical
one is used instead, and vice versa. FRAME-OR-WINDOW must be a live
frame or window and defaults to the selected frame. If FRAME-OR-WINDOW
is a frame, transpose the main window of the frame, otherwise transpose
FRAME-OR-WINDOW. If SUBTREE is non-nil, the function will act as if
FRAME-OR-WINDOW is the parent window of the selected window."
(interactive "P")
(let ((window (if subtree
(window-parent)
(if (windowp frame-or-window)
frame-or-window
(window-main-window frame-or-window)))))
(window--transpose window '(right . below) nil)))
(defun window--transpose (window conf do-not-convert-size)
"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 DO-NOT-CONVERT-SIZE non-nil, the size
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))
(fwin window)
(selwin (frame-selected-window window))
(win-tree (car (window-tree-pixel-sizes window))))
(while (not (window-live-p fwin))
(setq fwin (window-child fwin)))
(delete-other-windows-internal
fwin (and (window-valid-p (car win-tree)) (car win-tree)))
(window--transpose-1 win-tree fwin conf do-not-convert-size)
;; Go back to previously selected window.
(set-frame-selected-window frame selwin))))
(defun window--transpose-1 (subtree cwin conf do-not-convert-size)
"Subroutine of `window--transpose'.
SUBTREE must be in the format of the result of
`window-tree-pixel-sizes'. CWIN is the current window through which the
window splits are made. The CONF and DO-NOT-CONVERT-SIZE arguments are
the same as the ones in `window--transpose'."
;; `ilen' is the max size a window could be of given the split type.
;; `flen' is max size the window could be converted to the opposite
;; of the given split type.
(pcase-let ((`(,ilen . ,flen) (if (car subtree)
(cons (float (car (cadr subtree)))
(float (window-pixel-width cwin)))
(cons (float (cdr (cadr subtree)))
(float (window-pixel-height cwin))))))
(mapc
(pcase-lambda (`(,win . ,size))
(let ((split-size (- (if do-not-convert-size
size
(round (* flen (/ size ilen))))))
(split-type
(funcall (if (car subtree) 'car 'cdr) conf)))
(if (listp win)
(let* ((refer-car (seq-some
(lambda (x)
(and (windowp x) x))
(flatten-list win)))
(refer (cons refer-car
(cdr (assq cwin window-alist)))))
;; (message "1 .. %s" (cons refer (cdr (assq refer-car window-alist)))) (sit-for 3)
;; `win' is a window subtree.
(window--transpose-1 win (split-window cwin
split-size
split-type
t
refer)
conf do-not-convert-size))
;; `win' is a window.
;; (message "2 .. %s" (cons win (cdr (assq win window-alist)))) (sit-for 3)
(split-window cwin split-size
split-type t
(cons win (cdr (assq win window-alist)))))))
(mapcar
(lambda (e)
(let ((window? (if (windowp (car e)) (car e) e)))
(cons window?
;; The respective size of the window.
(if (car subtree)
(car (cadr e))
(cdr (cadr e))))))
;; By using cdddr, we ignore window split type, sizes and the
;; first window (it's implicitly created).
(nreverse (cdddr subtree))))
;; (caaddr subtree) is the first window.
(unless (windowp (caaddr subtree))
(window--transpose-1 (caddr subtree) cwin conf do-not-convert-size))))
(global-set-key [(super p)] 'transpose-windows)
(global-set-key [(super q)] 'rotate-windows-clockwise)
(let ((window (split-window)))
(set-window-buffer window (get-buffer-create "*foo*"))
(setq window (split-window nil nil t))
(set-window-buffer window (get-buffer-create "*Messages*"))
(select-window window))
^ permalink raw reply related [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-22 18:12 ` martin rudalics
@ 2024-10-24 14:38 ` Pranshu Sharma
2024-10-24 18:39 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: Pranshu Sharma @ 2024-10-24 14:38 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 2495 bytes --]
martin rudalics <rudalics@gmx.at> writes:
> OK. I looked into this again and maybe I can help you. Attached find a
> new version of 'split-window' that accepts also a (WINDOW . PARENT)
> cons as REFER where WINDOW is the buffer window to use and PARENT its
> parent before WINDOW was deleted. How to use that is documented in the
> version of window-transpose.el I also attach. It's full of debugging
> code so you have to figure out yourself how to make use of it.
>
> The three crucial aspects are:
>
> (1) It has a function 'window-alist' that establishes before transposing
> anything an association list of all live windows with their parents and
> stores it in the variable 'window-alist'.
>
> (2) It deletes windows via 'delete-other-windows-internal'.
>
> (3) When it calls 'split-window' then in the (listp win) case I set the
> cdr of the cons to the old parent of the _window to split_ (found via
> 'window-alist'). In the win is a window case, I set the cdr of the cons
> to the old parent of the _window to make_ (again found via
> 'window-alist').
>
> This survives quite a number of 'rotate-windows-clockwise' on the three
> windows structure I create on the bottom of window-transpose.el.
>
> Try it and then maybe try it with your remaining functions in an
> analogous manner. The tricky part is certainly (3) which might just
> work with my simple rotation scenario.
Sorry for late reply, been caught up with rl shit.
I had a skim look at this, will have a proper one tommorow or saturday,
and I think the problem of using flatten-list still exists.
I've made a new protoptype (buggy) of window--transpose-1 that does not
call flatten.
Right now, it works with this kind of split:
(let ((win (split-window (split-window nil nil t))))
(dotimes (_ 4)
(setq win (split-window win 10 t))
))
and:
(let ((win (split-window (split-window nil nil t))))
(dotimes (_ 4)
(setq win (split-window win 10 t))
))
However, not on the one achived by:
C-x 3
C-x o
C-x 2
C-x 3
I did make some progress on this, but the problem was there was no way I know
to achive this, which makes it a bit harder:
|-------------| |-------------|
| A | | A |
|-------------| |-------------|
| B | -> | B | |
|-------------| |------| D |
| C | | C | |
|-------------| |-------------|
I'm sure there is way to make it work without this, I'll probably come
up with way in shower.
[-- Attachment #2: isolate.el --]
[-- Type: application/emacs-lisp, Size: 2205 bytes --]
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-24 14:38 ` Pranshu Sharma
@ 2024-10-24 18:39 ` martin rudalics
2024-10-25 14:24 ` Pranshu Sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-24 18:39 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 1905 bytes --]
> However, not on the one achived by:
> C-x 3
> C-x o
> C-x 2
> C-x 3
Yes, this one is a bit nasty.
> I did make some progress on this, but the problem was there was no way I know
> to achive this, which makes it a bit harder:
>
> |-------------| |-------------|
> | A | | A |
> |-------------| |-------------|
> | B | -> | B | |
> |-------------| |------| D |
> | C | | C | |
> |-------------| |-------------|
>
> I'm sure there is way to make it work without this, I'll probably come
> up with way in shower.
Maybe you should have done
|-------------| |-------------|
| A | | A |
|-------------| |-------------|
| | | -> | B | |
| B | D | |------| D |
| | | | C | |
|-------------| |-------------|
since you don't want to split parent windows.
I attach a file called window-rotate.el which handles the above
scenario. In contrast with a window tree it simply operates on the old
window structure of the frame directly which I store as an association
list. Note two aspects:
(1) It always tries to find a leftmost live window to start operating
on. I think you do the same but I've never been able to understand how.
(2) It binds 'window-combination-limit' to make a parent window within
something like the first combination you sketched above so this becomes
|-------------|
| A |
|-------------|
| ----------- |
|| B ||
||-----------||
|| C ||
| ----------- |
|-------------|
Half of the code is debugging code that is not commented. Note that the
code handles rotations only, rotates only the entire frame, does not set
sizes and probably misses other things. But it seems to work so please
test it and maybe try to use as much of it as possible.
martin
[-- Attachment #2: window-rotate.el --]
[-- Type: text/x-emacs-lisp, Size: 8543 bytes --]
;;; -*- lexical-binding:t -*-
;; Debugging code.
(defvar window-number 0)
(defvar old-window-alist nil)
(defvar old-window-list nil)
(defvar new-window-alist nil)
(defun old-window-alist ()
(setq old-window-list (window-list))
(setq old-window-alist nil)
(dolist (window old-window-list)
(setq old-window-alist
(cons
(cons window (window-parent window))
old-window-alist)))
(setq old-window-alist (nreverse old-window-alist)))
(defun new-window-alist ()
(setq new-window-alist nil)
(dolist (window old-window-list)
(setq new-window-alist
(cons
(cons window (window-parent window))
new-window-alist)))
(setq new-window-alist (nreverse new-window-alist)))
;; Two variables the code below binds lexically.
(defvar window-alist nil)
(defvar window-clockwise)
(defun window-first-window (window)
"Return topmost leftmost live descendant of WINDOW."
(let ((child (or (window-top-child window)
(window-left-child window))))
(if child
(window-first-window child)
window)))
(defun window-alist ()
"Return alist of all windows on selected frame."
(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 'next (window-next-sibling window))
(cons 'prev (window-prev-sibling window))
(cons 'width (window-pixel-width window))
(cons 'height (window-pixel-height window)))
(list window
(cons 'left (window-left-child window))
(cons 'top (window-top-child window))
(cons 'parent (window-parent window))
(cons 'next (window-next-sibling window))
(cons 'prev (window-prev-sibling window))
(cons 'first (window-first-window window))
(cons 'width (window-pixel-width window))
(cons 'height (window-pixel-height window))))
alist)))
nil t)
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-rotate-clockwise ()
"Rotate root window of selected frame clockwise."
(interactive)
(let ((window-clockwise t)
;; Construct 'window-alist' which also makes sure that no window
;; gets recycled by GC.
(window-alist (window-alist))
(window (frame-first-window)))
(setq window-number 0)
(old-window-alist)
;; Delete all windows but the frame's first window.
(delete-other-windows-internal window)
(with-current-buffer (get-buffer-create "*foo*")
(erase-buffer)
(pp window-alist (current-buffer))
(insert "\n"))
;; Now rotate starting with the frame's first window.
(window-rotate window nil)
(new-window-alist)
(with-current-buffer (get-buffer-create "*foo*")
(pp old-window-alist (current-buffer))
(insert "\n")
(pp new-window-alist (current-buffer)))))
(defun window-rotate-anti-clockwise ()
"Rotate root window of selected frame anti-clockwise."
(interactive)
(let (window-clockwise
;; Construct 'window-alist' which also makes sure that no window
;; gets recycled by GC.
(window-alist (window-alist))
(window (frame-first-window)))
(setq window-number 0)
(old-window-alist)
;; Delete all windows but the frame's first window.
(delete-other-windows-internal window)
(with-current-buffer (get-buffer-create "*foo*")
(erase-buffer)
(pp window-alist (current-buffer))
(insert "\n"))
;; Now rotate starting with the frame's first window.
(window-rotate window nil)
(new-window-alist)
(with-current-buffer (get-buffer-create "*foo*")
(pp old-window-alist (current-buffer))
(insert "\n")
(pp new-window-alist (current-buffer)))))
(defun window-rotate (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."
(let ((parent (window-alist-parent window))
next first side)
(when parent
;; 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-clockwise' and whether WINDOW is in a
;; horizontal of vertical combination.
(setq side (if window-clockwise
(if (window-alist-left parent) 'below 'left)
(if (window-alist-left parent) 'above 'right)))
;; Save current window configuration in case of an error.
(let ((config (current-window-configuration)))
(if (window-alist-buffer next)
;; If the next sibling shows a buffer, split it with
;; WINDOW's old parent as new parent.
(condition-case var
(let* ((prev (window-alist-prev window))
;; 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 (not prev)
(and (not (eq (window-alist-parent prev)
(window-alist-parent window)))
t))))
(with-current-buffer (get-buffer-create "*foo*")
(insert (format "1 .. w %s .. n %s .. p %s\n" window next parent)))
;; Split WINDOW with REFER set to a cons of WINDOW's
;; saved next sibling and WINDOW's saved parent.
(split-window window nil side nil (cons next parent)))
(error
(with-current-buffer (get-buffer-create "*foo*")
(pp window-alist (current-buffer))
(insert "\n")
(insert (format "1 .. w %s .. s %s .. r %s\n" window side (cons next parent)))
(insert (format "%s\n" var)))
(set-window-configuration config)))
;; If the next sibling is a parent window set FIRST to its
;; topmost leftmost live descendant.
(setq first (window-alist-first next))
(condition-case var
(let* ((prev (window-alist-prev window))
;; 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 (not prev)
(and (not (eq (window-alist-parent prev)
(window-alist-parent window)))
t))))
(with-current-buffer (get-buffer-create "*foo*")
(insert (format "2 .. w %s .. f %s .. p %s\n" window first parent)))
;; Split WINDOW with REFER set to a cons of WINDOW's
;; first live descendant and WINDOW's saved parent.
(split-window window nil side nil (cons first parent)))
(error
(with-current-buffer (get-buffer-create "*foo*")
(pp window-alist (current-buffer))
(insert "\n")
(insert (format "2 .. w %s .. s %s .. r %s\n" window side (cons next parent)))
(insert (format "%s\n" var)))
(set-window-configuration config)))
;; Recurse starting with WINDOW's first live descendant.
;; The second argument PARENT should terminate the recursion
;; when going up again.
(window-rotate first parent)))
;; Continue with WINDOW's next sibling.
(setq window next))
;; Continue with WINDOW's parent unless we are at the ROOT of the
;; current recursion.
(unless (eq (window-alist-parent window) root)
(window-rotate (window-alist-parent window) root)))))
(global-set-key [(super p)] 'window-rotate-clockwise)
(global-set-key [(super q)] 'window-rotate-anti-clockwise)
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-24 18:39 ` martin rudalics
@ 2024-10-25 14:24 ` Pranshu Sharma
2024-10-25 17:09 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: Pranshu Sharma @ 2024-10-25 14:24 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 2283 bytes --]
martin rudalics <rudalics@gmx.at> writes:
>
> since you don't want to split parent windows.
I decided to do this, since I also don't want my brain to start bleeding
> I attach a file called window-rotate.el which handles the above
> scenario. In contrast with a window tree it simply operates on the old
> window structure of the frame directly which I store as an association
> list. Note two aspects:
>
> (1) It always tries to find a leftmost live window to start operating
> on. I think you do the same but I've never been able to understand how.
From looking at the code, same reason as you. The leftmost top window
is first window in a flattened window tree, then the recurisve descent
started from there.
The new function also does ame, but perfoms recursive ascent asc
> (2) It binds 'window-combination-limit' to make a parent window within
> something like the first combination you sketched above so this becomes
>
> |-------------|
> | A |
> |-------------|
> | ----------- |
> || B ||
> ||-----------||
> || C ||
> | ----------- |
> |-------------|
Wow! this is best thing since flush toilets, I was able to make the
function work with this.
Also, I plan on working on a cycle-windows function which will basiclly
change window-tree so that that the layout isn't changed, but the
windows are thing is. This will be able to be properly done using
window--transpose-1, instead of window swap state which you said didn't
work.
> Half of the code is debugging code that is not commented. Note that the
> code handles rotations only, rotates only the entire frame, does not set
> sizes and probably misses other things. But it seems to work so please
> test it and maybe try to use as much of it as possible.
I looked at it, and could not really understnad it fully due to some low
level stuff, but I got the idea and incoroperated some logic of
windows-rotate.
It seems like window-configurations are kind of being reinvented except
in elisp, which is good.
The function I made, I feel like my approach is higher level then yours
cause I can't quite fully wrap my head around window management in
emacs. So a lot of stuff, I don't know you can do. But on other hand,
I feel the logic is also quite simpler, you can judge that.
[-- Attachment #2: window-transpose.el --]
[-- Type: application/emacs-lisp, Size: 9149 bytes --]
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-25 14:24 ` Pranshu Sharma
@ 2024-10-25 17:09 ` martin rudalics
2024-10-26 9:14 ` Pranshu Sharma
0 siblings, 1 reply; 85+ messages in thread
From: martin rudalics @ 2024-10-25 17:09 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
>> (2) It binds 'window-combination-limit' to make a parent window within
>> something like the first combination you sketched above so this becomes
[...]
> Wow! this is best thing since flush toilets, I was able to make the
> function work with this.
I don't see it in the code you sent, though. Also that code has one bug
and produces a couple of warnings:
In rotate-windows-anticlockwise:
window-transpose.el:59:48: Warning: Unused lexical argument ‘subtree’
In rotate-windows-clockwise:
window-transpose.el:84:44: Warning: Unused lexical argument ‘subtree’
In flip-windows-horizontally:
window-transpose.el:109:45: Warning: Unused lexical argument ‘subtree’
In flip-windows-vertically:
window-transpose.el:128:43: Warning: Unused lexical argument ‘subtree’
In transpose-windows:
window-transpose.el:146:37: Warning: Unused lexical argument ‘subtree’
In window--transpose-1:
window-transpose.el:209:28: Warning: reference to free variable ‘fwin’
The last one keeps it from running.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-25 17:09 ` martin rudalics
@ 2024-10-26 9:14 ` Pranshu Sharma
2024-10-27 8:23 ` martin rudalics
0 siblings, 1 reply; 85+ messages in thread
From: Pranshu Sharma @ 2024-10-26 9:14 UTC (permalink / raw)
To: martin rudalics; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 659 bytes --]
martin rudalics <rudalics@gmx.at> writes:
> I don't see it in the code you sent, though.
Look in window--transpose
> In rotate-windows-anticlockwise:
> window-transpose.el:59:48: Warning: Unused lexical argument ‘subtree’
Oh, I just realised these ones also existed in before code
> In window--transpose-1:
> window-transpose.el:209:28: Warning: reference to free variable ‘fwin’
>
> The last one keeps it from running.
This one happend becuase you turned on lexical scoping.
I changed the code so it works with lexical
Also the
C-x 3
C-x 2
C-x 3
split has some size messups (still works tho), that will need fixing
[-- Attachment #2: window-transpose.el --]
[-- Type: application/emacs-lisp, Size: 9066 bytes --]
^ permalink raw reply [flat|nested] 85+ messages in thread
* Re: Add function to rotate/transpose all windows
2024-10-26 9:14 ` Pranshu Sharma
@ 2024-10-27 8:23 ` martin rudalics
0 siblings, 0 replies; 85+ messages in thread
From: martin rudalics @ 2024-10-27 8:23 UTC (permalink / raw)
To: Pranshu Sharma; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel
> Look in window--transpose
I see it now. You should not bind 'window-combination-limit' there.
Always bind it around a single 'split-window' call only. If you bind it
for all 'split-window' operations run by transposing, you get a binary
window tree which is certainly not what a user wants.
When with emacs -Q I set 'debug-on-error' t, do C-x 2 C-x 2 and then
call 'rotate-windows-clockwise' twice I get
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
-(916.0 nil)
(if (eq o-split split-type) (- fflen o-size) fflen)
(let* ((split-type (funcall (if (car subtree) 'car 'cdr) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'(lambda (parent-win arg0) (progn (ignore ...) (let* ... ...))) (let ((ls (mapcar ... ...))) (seq-mapn 'cons (mapcar 'car ls) (cons nil (mapcar ... ls)))) cwin))) (progn (let ((upper-bound (- (length subtree) 3)) (counter 0)) (while (< counter upper-bound) (let ((_ counter)) (setq deepest-window (window-parent deepest-window))) (setq counter (1+ counter)))) deepest-window))
(let ((ilen x0) (fflen x1)) (let* ((split-type (funcall (if (car subtree) 'car 'cdr) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'(lambda (parent-win arg0) (progn ... ...)) (let ((ls ...)) (seq-mapn 'cons (mapcar ... ls) (cons nil ...))) cwin))) (progn (let ((upper-bound (- (length subtree) 3)) (counter 0)) (while (< counter upper-bound) (let ((_ counter)) (setq deepest-window (window-parent deepest-window))) (setq counter (1+ counter)))) deepest-window)))
(let* ((x0 (car-safe val)) (x1 (cdr-safe val))) (let ((ilen x0) (fflen x1)) (let* ((split-type (funcall (if (car subtree) 'car 'cdr) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'(lambda ... ...) (let (...) (seq-mapn ... ... ...)) cwin))) (progn (let ((upper-bound (- ... 3)) (counter 0)) (while (< counter upper-bound) (let (...) (setq deepest-window ...)) (setq counter (1+ counter)))) deepest-window))))
(progn (ignore (consp val)) (let* ((x0 (car-safe val)) (x1 (cdr-safe val))) (let ((ilen x0) (fflen x1)) (let* ((split-type (funcall (if ... ... ...) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'... (let ... ...) cwin))) (progn (let ((upper-bound ...) (counter 0)) (while (< counter upper-bound) (let ... ...) (setq counter ...))) deepest-window)))))
(let* ((val (if (car subtree) (cons (float (car (car ...))) (float (window-pixel-width cwin))) (cons (float (cdr (car ...))) (float (window-pixel-height cwin)))))) (progn (ignore (consp val)) (let* ((x0 (car-safe val)) (x1 (cdr-safe val))) (let ((ilen x0) (fflen x1)) (let* ((split-type (funcall ... conf)) (flen (if ... ... fflen)) (deepest-window (seq-reduce ... ... cwin))) (progn (let (... ...) (while ... ... ...)) deepest-window))))))
window--transpose-1((nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405))) #<window 7 on *scratch*> (left . below) nil #<window 7 on *scratch*> nil below)
(if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type))
(if (listp win) (if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and (not size) o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win)))
(let ((split-size (and size (if do-not-convert-size size (round (* flen (/ size ilen))))))) (if (listp win) (if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and (not size) o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win))))
(if (eq fwin win) parent-win (let ((split-size (and size (if do-not-convert-size size (round (* flen ...)))))) (if (listp win) (if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and (not size) o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win)))))
(let ((win x2) (size x3)) (if (eq fwin win) parent-win (let ((split-size (and size (if do-not-convert-size size (round ...))))) (if (listp win) (if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and (not size) o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win))))))
(let* ((x2 (car-safe arg0)) (x3 (cdr-safe arg0))) (let ((win x2) (size x3)) (if (eq fwin win) parent-win (let ((split-size (and size (if do-not-convert-size size ...)))) (if (listp win) (if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and (not size) o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win)))))))
(progn (ignore (consp arg0)) (let* ((x2 (car-safe arg0)) (x3 (cdr-safe arg0))) (let ((win x2) (size x3)) (if (eq fwin win) parent-win (let ((split-size (and size ...))) (if (listp win) (if (and ... o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and ... o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win))))))))
#f(lambda (parent-win arg0) [(flen 916.0) (split-type below) (ilen 1680.0) (o-split nil) (o-size nil) (fwin #<window 7 on *scratch*>) (do-not-convert-size nil) (conf (left . below))] (progn (ignore (consp arg0)) (let* ((x2 (car-safe arg0)) (x3 (cdr-safe arg0))) (let ((win x2) (size x3)) (if (eq fwin win) parent-win (let ((split-size (and size (if do-not-convert-size size (round (* flen (/ size ilen))))))) (if (listp win) (if (and (not size) o-size) (window--transpose-1 win parent-win conf do-not-convert-size fwin o-size o-split) (window--transpose-1 win parent-win conf do-not-convert-size fwin split-size split-type)) (if (and (not size) o-size) (split-window parent-win o-size o-split t win) (split-window parent-win split-size split-type t win)))))))))(#<window 7 on *scratch*> ((nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405)))))
#f(compiled-function (elt) #<bytecode 0x1f3b072de5930c3f>)(((nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405)))))
mapc(#f(compiled-function (elt) #<bytecode 0x1f3b072de5930c3f>) (((nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405)))) (#<window 3> . 1248)))
seq-do(#f(compiled-function (elt) #<bytecode 0x1f3b072de5930c3f>) (((nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405)))) (#<window 3> . 1248)))
seq-reduce(#f(lambda (parent-win arg0) [(flen 916.0) (split-type below) (ilen 1680.0) (o-split nil) (o-size nil) (fwin #<window 7 on *scratch*>) (do-not-convert-size nil) (conf (left . below))] (progn (ignore (consp arg0)) (let* ((x2 (car-safe arg0)) (x3 (cdr-safe arg0))) (let ((win x2) (size x3)) (if (eq fwin win) parent-win (let (...) (if ... ... ...))))))) (((nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405)))) (#<window 3> . 1248)) #<window 7 on *scratch*>)
(let* ((split-type (funcall (if (car subtree) 'car 'cdr) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'(lambda (parent-win arg0) (progn (ignore ...) (let* ... ...))) (let ((ls (mapcar ... ...))) (seq-mapn 'cons (mapcar 'car ls) (cons nil (mapcar ... ls)))) cwin))) (progn (let ((upper-bound (- (length subtree) 3)) (counter 0)) (while (< counter upper-bound) (let ((_ counter)) (setq deepest-window (window-parent deepest-window))) (setq counter (1+ counter)))) deepest-window))
(let ((ilen x0) (fflen x1)) (let* ((split-type (funcall (if (car subtree) 'car 'cdr) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'(lambda (parent-win arg0) (progn ... ...)) (let ((ls ...)) (seq-mapn 'cons (mapcar ... ls) (cons nil ...))) cwin))) (progn (let ((upper-bound (- (length subtree) 3)) (counter 0)) (while (< counter upper-bound) (let ((_ counter)) (setq deepest-window (window-parent deepest-window))) (setq counter (1+ counter)))) deepest-window)))
(let* ((x0 (car-safe val)) (x1 (cdr-safe val))) (let ((ilen x0) (fflen x1)) (let* ((split-type (funcall (if (car subtree) 'car 'cdr) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'(lambda ... ...) (let (...) (seq-mapn ... ... ...)) cwin))) (progn (let ((upper-bound (- ... 3)) (counter 0)) (while (< counter upper-bound) (let (...) (setq deepest-window ...)) (setq counter (1+ counter)))) deepest-window))))
(progn (ignore (consp val)) (let* ((x0 (car-safe val)) (x1 (cdr-safe val))) (let ((ilen x0) (fflen x1)) (let* ((split-type (funcall (if ... ... ...) conf)) (flen (if (eq o-split split-type) (- fflen o-size) fflen)) (deepest-window (seq-reduce #'... (let ... ...) cwin))) (progn (let ((upper-bound ...) (counter 0)) (while (< counter upper-bound) (let ... ...) (setq counter ...))) deepest-window)))))
(let* ((val (if (car subtree) (cons (float (car (car ...))) (float (window-pixel-width cwin))) (cons (float (cdr (car ...))) (float (window-pixel-height cwin)))))) (progn (ignore (consp val)) (let* ((x0 (car-safe val)) (x1 (cdr-safe val))) (let ((ilen x0) (fflen x1)) (let* ((split-type (funcall ... conf)) (flen (if ... ... fflen)) (deepest-window (seq-reduce ... ... cwin))) (progn (let (... ...) (while ... ... ...)) deepest-window))))))
window--transpose-1((nil (916 . 1680) (nil (916 . 1248) (#<window 7 on *scratch*> (916 . 843)) (#<window 8> (916 . 405))) (#<window 3> (916 . 432))) #<window 7 on *scratch*> (left . below) nil #<window 7 on *scratch*> nil)
(let* ((frame (window-frame window)) (fwin window) (selwin (frame-selected-window window)) (window-combination-limit t) (win-tree (car (window-tree-pixel-sizes window)))) (while (not (window-live-p fwin)) (setq fwin (window-child fwin))) (delete-other-windows-internal fwin) (window--transpose-1 win-tree fwin conf do-not-convert-size fwin nil) (set-frame-selected-window frame selwin))
(if (or (not window) (window-live-p window)) (message "No windows to transpose") (let* ((frame (window-frame window)) (fwin window) (selwin (frame-selected-window window)) (window-combination-limit t) (win-tree (car (window-tree-pixel-sizes window)))) (while (not (window-live-p fwin)) (setq fwin (window-child fwin))) (delete-other-windows-internal fwin) (window--transpose-1 win-tree fwin conf do-not-convert-size fwin nil) (set-frame-selected-window frame selwin)))
window--transpose(#<window 6> (left . below) nil)
(let ((window (cond (subtree (window-parent)) ((windowp frame-or-window) frame-or-window) (t (window-main-window frame-or-window))))) (old-window-alist) (window--transpose window '(left . below) nil) (new-window-alist) (save-current-buffer (set-buffer (get-buffer-create "*foo*")) (pp old-window-alist (current-buffer)) (insert "\n") (pp new-window-alist (current-buffer))))
rotate-windows-clockwise(nil)
funcall-interactively(rotate-windows-clockwise nil)
call-interactively(rotate-windows-clockwise nil nil)
command-execute(rotate-windows-clockwise)
I have no idea whether it's related. For me that backtrace is
impossible to trace. Please have a look. Once it works, I'll try to
come up with an example why what you do is wrong.
Note the following paragraph in the Elisp manual on
'window-combination-limit'.
If, as a consequence of this variable’s setting, ‘split-window’
makes a new parent window, it also calls
‘set-window-combination-limit’ (see below) on the newly-created
internal window. This affects how the window tree is rearranged
when the child windows are deleted (see below).
So when you bind 'window-combination-limit' globally, deleting windows
after transposing may behave differently from before transposing them.
The correct approach is to save the value of 'window-combination-limit'
for a parent window before splitting, split the window, and then restore
the previous value of the parent.
The problem, however, is also with finding the right parent window and
I'm afraid your algorithm doesn't get that right yet. IIUC when finding
a next sibling as the new window in a split and that sibling is an
internal window, you take that window's first live descendant - let's
call it FIRST - and pass it to 'split-window' as REFER argument. Right?
But 'split-window' has no idea of FIRST's former parent and, when it now
needs a parent because of 'window-combination-limit' being non-nil, it
will not reuse the previous parent of FIRST (some internal window down
the old window tree) but simply make a new one.
Note that if you had not set 'window-combination-limit', 'split-window'
would have set the parent to the parent of the window to split, which is
equally wrong as you probably noticed earlier. So what you have to do
here is to pass a cons as REFER to 'split-window' with the car set to
FIRST and the cdr set to the parent of FIRST it had in the window tree
_before_ you deleted the other windows. This will instruct
'split-window' to reinstall that old parent in the window tree and
things should work out as intended.
Note in this context that preserving the identities of live windows
across transpositions via the REFER argument is important to keep all
those things like window positions, margins, scroll bars, fringes,
overlays with a window property, dedicated status and parameters like
'no-other-window' or 'quit-restore' in place. Without the REFER
argument, you would have to painstakingly restore all these things
manually for each new live window from the old one it substitutes.
Preserving the identity of internal windows on the other hand is
important to keep their combination limits and any parameters stored
for them. Even here, it's much easier to reuse the old parent windows
instead of saving all these properties before deleting windows and
trying to find the identity of the parent windows that substituted the
old ones and store the appropriate properties in the new windows.
martin
^ permalink raw reply [flat|nested] 85+ messages in thread
end of thread, other threads:[~2024-10-27 8:23 UTC | newest]
Thread overview: 85+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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-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
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).