all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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
  0 siblings, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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
  0 siblings, 0 replies; 37+ 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] 37+ messages in thread

end of thread, other threads:[~2024-10-01  9:17 UTC | newest]

Thread overview: 37+ 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-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-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 external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.