Subject: bug#44933: 27.1; Ephemeral frame selection shrinks minibuffer

From: Sean Devlin <[email-masked]>
Date: Mon, 30 Nov 2020 14:32:27 -0500

Hi Martin,

> On Nov 30, 2020, at 4:04 AM, martin rudalics <[email-masked]> wrote:
> 
> > In my reproduction, the minibuffer window is grown manually via
> > `window-resize`.
> 
> With 'resize-mini-windows' non-nil this usually has no visible effect
> because any such manual resizing is immediately undone by the automatic
> resizing scheme.

I’m not sure I understand. Are you saying a program should not be able to grow the minibuffer window this way? Or just that it will be undone by the next resizing event? (Of course, this is a toy example, so I have no opinion on what the correct behavior should be. I’m just wondering.)

> 
> > In the case of selectrum, the completing read
> > candidates are displayed via overlay. I think this means that a
> > resizing function that considers the buffer text only will resize
> > these back down to a single line.
> 
> The routine that determines the window size (resize_mini_window in
> xdisp.c) should consider overlays as well (if they are in the right
> buffer).  Anything else would be a bug.

I’ve been playing with this a bit (on a recent Emacs 28.0.50), and I’m not sure I understand the current behavior.

For example, I have three frames open and I ran this code in the scratch buffer:

(with-current-buffer (window-buffer (minibuffer-window))
  (remove-overlays)
  (erase-buffer)
  (insert "this is some text")
  (let ((ov (make-overlay (point) (point) nil t t)))
    (overlay-put ov 'after-string "\none\ntwo\nthree")))

After moving the point, all I can see is the first line in the minibuffer: “this is some text”. This is true across all three frames.

If I select the other frames by clicking on them, the frame that just lost focus will now show all four lines (i.e. including the overlay). The minibuffer windows on the other frames will stay at one line until I select them by clicking and then select some other frame.

On the other hand, if I select frames by calling `other-frame` via a key binding, the behavior is slightly different: the minibuffer window on a frame expands to four lines as that frame loses focus, and the minibuffer window on the newly selected frame contracts back down to one line.

Are all of these behaviors expected and correct? (Again, I have no opinion; I’m just trying to understand how things are meant to work.)

> 
> > I just tested, and binding `resize-mini-windows` to nil around the
> > window selection in `term-emulate-terminal` does solve my
> > problem. Does this seem like the right fix? I think it is surprising
> > that output from a term process coming in the background should resize
> > a minibuffer window (and especially an active one).
> 
> If you look into the code of do_switch_frame (in frame.c) you will be
> able to spot
> 
>  if (!for_deletion && FRAME_HAS_MINIBUF_P (sf))
>    resize_mini_window (XWINDOW (FRAME_MINIBUF_WINDOW (sf)), 1);
> 
> which means that currently even redisplay itself may resize the mini
> window every time it constructs a mode line or frame title.  I can only
> offer two advices: (1) Avoid 'with-selected-window/frame' in timers and
> (2) make sure no redisplay happen _within_ such a form.

Interesting. I think this partially explains the behaviors I’m seeing above.

To be clear, `term-emulate-terminal` is a process filter. I’m not sure if that’s exactly like a timer under the hood, but it does run without user interaction.

> 
> > Also, I see that `term-emulate-terminal` is calling `select-window` to
> > perform its window selections. From my reading of the docs, I think it
> > might make sense for it to pass `mark-for-redisplay` as the `norecord`
> > argument. It doesn't seem like we should be modifying the buffer list
> > or most recently selected window in this case, but we do want to
> > redisplay the new output.
> 
> I would have to understand the semantics of 'term-emulate-terminal' to
> answer that.  Hopefully, someone else can chime in here.

I can give a little information here, though someone else will surely know more.

This function is the process filter for a term process, so it handles new output from the process. After doing so, it iterates over all the windows to see if any contain the process buffer. For any that are, it selects those windows and scrolls those windows appropriately.

> 
> > I think the documentation should clarify that window selection can
> > change window sizes as a side effect. The docs for selecting windows
> > (https://www.gnu.org/software/emacs/manual/html_node/elisp/Selecting-Windows.html)
> > do not mention that `select-window` can change minibuffer sizes. The
> > docs for minibuffer windows
> > (https://www.gnu.org/software/emacs/manual/html_node/elisp/Minibuffer-Windows.html)
> > do mention these variables to control how minibuffer windows can be
> > resized automatically, but they do not say what functions might try to
> > do this automatic resizing.
> 
> With the recently added 'minibuffer-follows-selected-frame' we now have
> an additional source of complications to consider.  Maybe you could, as
> soon as the implementation of the latter has consolidated, play with the
> various values of 'resize-mini-windows' and suggest suitable fixes for
> the documentations of 'select-window' and 'select-frame'.

Sure, I can do that. Is there a timeline for this or some place I can follow development progress?

From testing the current implementation, it seems selectrum has a similar issue here: when I switch to a new frame, the minibuffer does follow, but the list of candidates is hidden until I enter a new input.

> 
> Alternatively, we could consider skipping the
> 
>    resize_mini_window (XWINDOW (FRAME_MINIBUF_WINDOW (sf)), 1);
> 
> above for temporary window/frame selections (and at least for redisplay
> as I currently do here) but the consequences of such a step are not
> easily fathomable.
> 
> > Finally, I wonder if `with-selected-window` (which
> > `term-emulate-terminal` does not currently use) should bind
> > `resize-mini-windows`.
> 
> ... with the consequences I mentioned in the sentence before ...
> 
> > The docs say it is "the preferred way to
> > temporarily work with" a selected window, so it does seem like
> > automatic resizing is not in the spirit of the function. On the other
> > hand, I don't know all the existing use cases; maybe this would break
> > things.
> 
> Right.
> 
> martin