* RE: Stop frames stealing eachothers' minibuffers! [not found] ` <<83imad0yb3.fsf@gnu.org> @ 2020-11-10 20:17 ` Drew Adams 0 siblings, 0 replies; 252+ messages in thread From: Drew Adams @ 2020-11-10 20:17 UTC (permalink / raw) To: Eli Zaretskii, Stefan Monnier Cc: enometh, drew.adams, andreyk.mad, emacs-devel > > Do you have a bug#nb for it? > > Nice try. Always with the snarky sarcasm, hostility, and ad hominem, eh Eli? It's really not necessary - or helpful, you know. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers!
@ 2021-02-06 23:25 jakanakaevangeli
2021-02-07 12:55 ` Alan Mackenzie
0 siblings, 1 reply; 252+ messages in thread
From: jakanakaevangeli @ 2021-02-06 23:25 UTC (permalink / raw)
To: acm; +Cc: emacs-devel
Thanks for your hard work.
Your patch fixes the mentioned problems, save for the following
regression, which isn't related to minibuffers: upon
entering two or more M-x recursive-edit, pressing C-]
(abort-recursive-edit) will take you to top-level rather than quit just
one recursive edit and produce an error "No catch for tag: exit, t".
> The handling you're proposing for exit-read-minibuffer is no less
> complicated than what's already there for exit.
Actually with function minibuf_c_loop_level, introduced in the patch,
you could probably do something similar to my proposed idea by adjusting
the catch for Qexit (in command_loop in src/keyboard.c) rather than
introducing a new catch for a new Qexit_read_minibuffer.
If you have the time and energy, you can read more about this below,
where I brainstorm two more ideas.
The way I see it, there is, in essence, one problem: we want to quit out
of multiple recursive-edit levels. And for this, there are three
solutions:
1) internal_catch shall detect if we are in a non-inner minibuffer and
re-throw Qexit until necessary. This is the current solution.
Pros:
- The simplest solution and easiest to maintain
Cons:
- internal_catch special cases Qexit and is dependent on
Fcurrent_buffer
(Note that my experience in lisp programming is limited and my gut
feeling that this "isn't clean" may be completely misplaced.)
2) command_loop (in src/keyboard.c) shall detect if we are in a
non-inner minibuffer and re-throw Qexit until necessary. So
basically, move the code from internal_catch to command_loop or
recursive_edit_1.
Cons:
- A bit harder to implement and maintain
3) The catcher for Qexit in command_loop shall be generalized to accept
an integer value N, which would mean to re-throw to Qexit with N-1
(or quit if N<=1) and quit-minibuffers shall be adjusted to calculate
how many minibuffers to quit with the help of your new
minibuf_c_loop_level function.
Cons:
- The most complicated solution
- The new (throw 'exit N) API will have to be documented and
maintained
- Will probably not be all that useful for users
- In could even cause a lot more problems that its worth
- Hard to deprecate (perhaps its use could be discouraged or the API
considered not supported and only to be used by internal Emacs
commands like quit-minibuffers)
Pros:
- does not rely on static variables
- isolates multi-level quitting only to a single command
(quit-minibuffers)
- Users might get a new possibly useful API for quitting multiple
levels
^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-06 23:25 jakanakaevangeli @ 2021-02-07 12:55 ` Alan Mackenzie 2021-02-07 16:44 ` jakanakaevangeli 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-02-07 12:55 UTC (permalink / raw) To: jakanakaevangeli; +Cc: emacs-devel Hello again, jakanakaevangeli. On Sun, Feb 07, 2021 at 00:25:56 +0100, jakanakaevangeli wrote: > Thanks for your hard work. Just as a matter of interest, your email host rejected my post yesterday, saying that the interchange had to start with a STARTTLS command. > Your patch fixes the mentioned problems, save for the following > regression, which isn't related to minibuffers: upon entering two or > more M-x recursive-edit, pressing C-] (abort-recursive-edit) will take > you to top-level rather than quit just one recursive edit and produce > an error "No catch for tag: exit, t". Yes. That happens even on C-] when there's just a single recursive edit in progress. > > The handling you're proposing for exit-read-minibuffer is no less > > complicated than what's already there for exit. > Actually with function minibuf_c_loop_level, introduced in the patch, > you could probably do something similar to my proposed idea by adjusting > the catch for Qexit (in command_loop in src/keyboard.c) rather than > introducing a new catch for a new Qexit_read_minibuffer. > If you have the time and energy, you can read more about this below, > where I brainstorm two more ideas. > The way I see it, there is, in essence, one problem: we want to quit out > of multiple recursive-edit levels. And for this, there are three > solutions: [ .... ] It turns out the tools in place were adequate to fix the problem without too much trouble. The critical thing is in internal_catch to distinguish between minibuffer throws and recursive edit throws, and only throw again for the minibuffer case. Additionally, minibuf_c_loop_level should now be made a static function, but I can do that later. Could I ask you please to try out the following patch, which should be applied on top of yesterday's patch: --- src/eval.c~ 2021-02-06 12:44:35.798096526 +0000 +++ src/eval.c 2021-02-07 12:27:09.927308860 +0000 @@ -1199,9 +1199,10 @@ { if (minibuffer_quit_level == -1) minibuffer_quit_level = this_minibuffer_depth (Qnil); - if (minibuf_level > minibuffer_quit_level - || command_loop_level - > minibuf_c_loop_level (minibuffer_quit_level)) + /* MINIBUFFER_QUIT_LEVEL == 0 -> not in minibuffer. */ + if (minibuffer_quit_level + && minibuf_level > minibuffer_quit_level + && !NILP (Fminibuffer_innermost_command_loop_p (Qnil))) Fthrow (Qexit, Qt); else minibuffer_quit_level = -1; --- src/minibuf.c~ 2021-02-06 15:04:58.878632379 +0000 +++ src/minibuf.c 2021-02-07 12:02:54.913389037 +0000 @@ -437,6 +437,8 @@ if (!minibuf_depth) error ("Not in a minibuffer"); + if (NILP (Fminibuffer_innermost_command_loop_p (Qnil))) + error ("Not in most nested command loop"); if (minibuf_depth < minibuf_level) { array[0] = fmt; -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-07 12:55 ` Alan Mackenzie @ 2021-02-07 16:44 ` jakanakaevangeli 2021-02-07 20:26 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: jakanakaevangeli @ 2021-02-07 16:44 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Just as a matter of interest, your email host rejected my post > yesterday, saying that the interchange had to start with a STARTTLS > command. Thank you for reporting, I will try to get in touch with my email host. > Could I ask you please to try out the following patch, which should be > applied on top of yesterday's patch: I tried it and everything works as expected, if we use C-g. The only minor thing I noticed is with abort-recursive-edit (C-]). When executed from a normal buffer, it does what its doc-string suggests. But when executed from a non-innermost minibuffer, it exits multiple recursive edits, just like abort-minibuffers. (This isn't surprising since it calls Fthrow (Qexit, Qt) just like abort-minibuffers, only without asking yes_or_no_p first.) This might actually be intended behaviour, depending on how you interpret "this minibuffer input" from abort-recursive-edit's doc-string. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-07 16:44 ` jakanakaevangeli @ 2021-02-07 20:26 ` Alan Mackenzie 2021-02-08 12:53 ` jakanakaevangeli 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-02-07 20:26 UTC (permalink / raw) To: jakanakaevangeli; +Cc: emacs-devel Hello jakanakaevangeli. Thank you indeed for all the testing you are doing. It is saving me a lot of hassle in the future. On Sun, Feb 07, 2021 at 17:44:17 +0100, jakanakaevangeli wrote: [ .... ] > > Could I ask you please to try out the following patch, which should be > > applied on top of yesterday's patch: > I tried it and everything works as expected, if we use C-g. The only > minor thing I noticed is with abort-recursive-edit (C-]). When executed > from a normal buffer, it does what its doc-string suggests. But when > executed from a non-innermost minibuffer, it exits multiple recursive > edits, just like abort-minibuffers. (This isn't surprising since it > calls Fthrow (Qexit, Qt) just like abort-minibuffers, only without > asking yes_or_no_p first.) This is not good. > This might actually be intended behaviour, depending on how you > interpret "this minibuffer input" from abort-recursive-edit's > doc-string. I spent some time boring into the doc string and the description in the Elisp manual. The only conclusion I could come to was that C-] is meant to abort EXACTLY ONE level. So I have tweaked the C sources once more and come up with the following patch. It should apply to the Emacs state after yesterday's patch. Please remove the patch from earlier today before you try to apply the new patch. Thanks! --- src/eval.c~ 2021-02-06 12:44:35.798096526 +0000 +++ src/eval.c 2021-02-07 20:07:13.793787769 +0000 @@ -1163,16 +1163,17 @@ FUNC should return a Lisp_Object. This is how catches are done from within C code. */ +/* MINIBUFFER_QUIT_LEVEL is to handle quitting from nested minibuffers by + throwing t to tag `exit'. + Value -1 means there is no (throw 'exit t) in progress; + 0 means the `throw' wasn't done from an active minibuffer; + N > 0 means the `throw' was done from the minibuffer at level N. */ +EMACS_INT minibuffer_quit_level = -1; + Lisp_Object internal_catch (Lisp_Object tag, Lisp_Object (*func) (Lisp_Object), Lisp_Object arg) { - /* MINIBUFFER_QUIT_LEVEL is to handle quitting from nested minibuffers by - throwing t to tag `exit'. - Value -1 means there is no (throw 'exit t) in progress; - 0 means the `throw' wasn't done from an active minibuffer; - N > 0 means the `throw' was done from the minibuffer at level N. */ - static EMACS_INT minibuffer_quit_level = -1; /* This structure is made part of the chain `catchlist'. */ struct handler *c = push_handler (tag, CATCHER); @@ -1192,16 +1193,14 @@ Lisp_Object val = handlerlist->val; clobbered_eassert (handlerlist == c); handlerlist = handlerlist->next; - if (EQ (tag, Qexit) && EQ (val, Qt)) + if (EQ (tag, Qexit) && EQ (val, Qt) && minibuffer_quit_level > 0) /* If we've thrown t to tag `exit' from within a minibuffer, we exit all minibuffers more deeply nested than the current one. */ { - if (minibuffer_quit_level == -1) - minibuffer_quit_level = this_minibuffer_depth (Qnil); - if (minibuf_level > minibuffer_quit_level - || command_loop_level - > minibuf_c_loop_level (minibuffer_quit_level)) + /* MINIBUFFER_QUIT_LEVEL == 0 -> not in minibuffer. */ + if (minibuf_level > minibuffer_quit_level + && !NILP (Fminibuffer_innermost_command_loop_p (Qnil))) Fthrow (Qexit, Qt); else minibuffer_quit_level = -1; --- src/minibuf.c~ 2021-02-06 15:04:58.878632379 +0000 +++ src/minibuf.c 2021-02-07 20:06:34.249789948 +0000 @@ -437,12 +437,17 @@ if (!minibuf_depth) error ("Not in a minibuffer"); + if (NILP (Fminibuffer_innermost_command_loop_p (Qnil))) + error ("Not in most nested command loop"); if (minibuf_depth < minibuf_level) { array[0] = fmt; array[1] = make_fixnum (minibuf_level - minibuf_depth + 1); if (!NILP (Fyes_or_no_p (Fformat (2, array)))) - Fthrow (Qexit, Qt); + { + minibuffer_quit_level = minibuf_depth; + Fthrow (Qexit, Qt); + } } else Fthrow (Qexit, Qt); --- src/lisp.h~ 2021-02-06 12:45:15.205094355 +0000 +++ src/lisp.h 2021-02-07 20:07:45.816786005 +0000 @@ -4090,6 +4090,7 @@ } /* Defined in eval.c. */ +extern EMACS_INT minibuffer_quit_level; extern Lisp_Object Vautoload_queue; extern Lisp_Object Vrun_hooks; extern Lisp_Object Vsignaling_function; -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-07 20:26 ` Alan Mackenzie @ 2021-02-08 12:53 ` jakanakaevangeli 2021-02-11 11:44 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: jakanakaevangeli @ 2021-02-08 12:53 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel Alan Mackenzie <acm@muc.de> writes: > Hello jakanakaevangeli. Hello > I spent some time boring into the doc string and the description in the > Elisp manual. The only conclusion I could come to was that C-] is meant > to abort EXACTLY ONE level. > > So I have tweaked the C sources once more and come up with the following > patch. It should apply to the Emacs state after yesterday's patch. > Please remove the patch from earlier today before you try to apply the > new patch. Thanks! During testing, I didn't encounter any problems related to your latest patch so you hard work paid off. Sadly, I did encounter 2 additional minibuffer issues which aren't related to your latest two patches, that is, they are present regardless if these patches are applied or not. I'm posting them here since they are still possibly related to the new minibuffer frame-following functionality. 1) With minibuffer-follows-selected-frame set to t (the default value): - press M-x on frame A - select frame B (the minibuffer will move to this frame) - C-x o, to select the minibuffer - C-g to quit it Miniwindow stays selected and its buffer is *Minibuf-1* instead of *Minibuf-0*. You can check this by evaluating (minibuffer-window). 2) With minibuffer-follows-selected-frame set to nil: - (setq set enable-recursive-minibuffers t) - (minibuffer-depth-indicate-mode 1) - select frame A and press M-x - select frame B and press M-x - select frame A and close it - select frame B and quit its minibuffer with C-g. This doesn't quit the outer minibuffer, as expected, but this minibuffer isn't shown anywhere and the only reasonable way to quit it is with C-], which, I believe, a lot of users don't know about (at least I personally didn't until quite recently). You can check this with (minibuffer-depth). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-08 12:53 ` jakanakaevangeli @ 2021-02-11 11:44 ` Alan Mackenzie 2021-02-11 14:29 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-02-11 11:44 UTC (permalink / raw) To: jakanakaevangeli; +Cc: emacs-devel Hello, jakanakaevangeli. On Mon, Feb 08, 2021 at 13:53:43 +0100, jakanakaevangeli wrote: > Alan Mackenzie <acm@muc.de> writes: > Hello > > I spent some time boring into the doc string and the description in the > > Elisp manual. The only conclusion I could come to was that C-] is meant > > to abort EXACTLY ONE level. > > So I have tweaked the C sources once more and come up with the following > > patch. It should apply to the Emacs state after yesterday's patch. > > Please remove the patch from earlier today before you try to apply the > > new patch. Thanks! > During testing, I didn't encounter any problems related to your latest > patch so you hard work paid off. > Sadly, I did encounter 2 additional minibuffer issues which aren't > related to your latest two patches, that is, they are present regardless > if these patches are applied or not. I'm posting them here since they > are still possibly related to the new minibuffer frame-following > functionality. Thanks! > 1) With minibuffer-follows-selected-frame set to t (the default value): > - press M-x on frame A > - select frame B (the minibuffer will move to this frame) > - C-x o, to select the minibuffer > - C-g to quit it > Miniwindow stays selected and its buffer is *Minibuf-1* instead of > *Minibuf-0*. You can check this by evaluating (minibuffer-window). This one was somewhat tricky to resolve, my fixes uncovering several other inconsistencies in the minibuffer handling, which I hope I have now fixed. > 2) With minibuffer-follows-selected-frame set to nil: > - (setq set enable-recursive-minibuffers t) > - (minibuffer-depth-indicate-mode 1) > - select frame A and press M-x > - select frame B and press M-x > - select frame A and close it > - select frame B and quit its minibuffer with C-g. > This doesn't quit the outer minibuffer, as expected, but this minibuffer > isn't shown anywhere and the only reasonable way to quit it is with C-], > which, I believe, a lot of users don't know about (at least I personally > didn't until quite recently). You can check this with > (minibuffer-depth). This is more involved. What do we want to happen when a frame with open minibuffers is deleted? I would say that these minibuffers should, except in the (eq minibuffer-follows-selected-frame t) case, be aborted, together with any others in other frames whose nesting level makes this necessary. Then there're complications with recursive edits. But I haven't looked at implementing this, yet. Alternatively, maybe open minibuffers should be moved to the "previous" frame. Perhaps I should open another thread on emacs-devel for this problem. Anyhow, I have a patch for bug 1. It is a full diff from master, rather than a difference from "yesterday". I would be very grateful indeed if you could try it out, and let me know again how well it's working. I would really like to commit this to master. Thanks again! diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index a899a943d4..aacb8ab00b 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2116,18 +2116,19 @@ minibuffer-hide-completions (defun exit-minibuffer () "Terminate this minibuffer argument." (interactive) - (when (or - (innermost-minibuffer-p) - (not (minibufferp))) + (when (minibufferp) + (when (not (minibuffer-innermost-command-loop-p)) + (error "%s" "Not in most nested command loop")) + (when (not (innermost-minibuffer-p)) + (error "%s" "Not in most nested minibuffer"))) ;; If the command that uses this has made modifications in the minibuffer, ;; we don't want them to cause deactivation of the mark in the original ;; buffer. ;; A better solution would be to make deactivate-mark buffer-local ;; (or to turn it into a list of buffers, ...), but in the mean time, ;; this should do the trick in most cases. - (setq deactivate-mark nil) - (throw 'exit nil)) - (error "%s" "Not in most nested minibuffer")) + (setq deactivate-mark nil) + (throw 'exit nil)) (defun self-insert-and-exit () "Terminate minibuffer input." diff --git a/src/eval.c b/src/eval.c index 3aff3b56d5..91fc4e6837 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1165,21 +1165,23 @@ usage: (catch TAG BODY...) */) FUNC should return a Lisp_Object. This is how catches are done from within C code. */ +/* MINIBUFFER_QUIT_LEVEL is to handle quitting from nested minibuffers by + throwing t to tag `exit'. + 0 means there is no (throw 'exit t) in progress, or it wasn't from + a minibuffer which isn't the most nested; + N > 0 means the `throw' was done from the minibuffer at level N which + wasn't the most nested. */ +EMACS_INT minibuffer_quit_level = 0; + Lisp_Object internal_catch (Lisp_Object tag, Lisp_Object (*func) (Lisp_Object), Lisp_Object arg) { - /* MINIBUFFER_QUIT_LEVEL is to handle quitting from nested minibuffers by - throwing t to tag `exit'. - Value -1 means there is no (throw 'exit t) in progress; - 0 means the `throw' wasn't done from an active minibuffer; - N > 0 means the `throw' was done from the minibuffer at level N. */ - static EMACS_INT minibuffer_quit_level = -1; /* This structure is made part of the chain `catchlist'. */ struct handler *c = push_handler (tag, CATCHER); if (EQ (tag, Qexit)) - minibuffer_quit_level = -1; + minibuffer_quit_level = 0; /* Call FUNC. */ if (! sys_setjmp (c->jmp)) @@ -1194,22 +1196,16 @@ internal_catch (Lisp_Object tag, Lisp_Object val = handlerlist->val; clobbered_eassert (handlerlist == c); handlerlist = handlerlist->next; - if (EQ (tag, Qexit) && EQ (val, Qt)) + if (EQ (tag, Qexit) && EQ (val, Qt) && minibuffer_quit_level > 0) /* If we've thrown t to tag `exit' from within a minibuffer, we exit all minibuffers more deeply nested than the current one. */ { - EMACS_INT mini_depth = this_minibuffer_depth (Qnil); - if (mini_depth && mini_depth != minibuffer_quit_level) - { - if (minibuffer_quit_level == -1) - minibuffer_quit_level = mini_depth; - if (minibuffer_quit_level - && (minibuf_level > minibuffer_quit_level)) - Fthrow (Qexit, Qt); - } + if (minibuf_level > minibuffer_quit_level + && !NILP (Fminibuffer_innermost_command_loop_p (Qnil))) + Fthrow (Qexit, Qt); else - minibuffer_quit_level = -1; + minibuffer_quit_level = 0; } return val; } diff --git a/src/lisp.h b/src/lisp.h index 409a1e7060..0847324d1f 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4091,6 +4091,7 @@ intern_c_string (const char *str) } /* Defined in eval.c. */ +extern EMACS_INT minibuffer_quit_level; extern Lisp_Object Vautoload_queue; extern Lisp_Object Vrun_hooks; extern Lisp_Object Vsignaling_function; @@ -4369,6 +4370,7 @@ extern void syms_of_casetab (void); /* Defined in keyboard.c. */ +extern EMACS_INT command_loop_level; extern Lisp_Object echo_message_buffer; extern struct kboard *echo_kboard; extern void cancel_echoing (void); diff --git a/src/minibuf.c b/src/minibuf.c index 949c3d989d..4b1f4b1ff7 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -41,6 +41,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ minibuffer recursions are encountered. */ Lisp_Object Vminibuffer_list; +Lisp_Object Vcommand_loop_level_list; /* Data to remember during recursive minibuffer invocations. */ @@ -64,6 +65,8 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; static Lisp_Object nth_minibuffer (EMACS_INT depth); +static EMACS_INT minibuf_c_loop_level (EMACS_INT depth); +static void set_minibuffer_mode (Lisp_Object buf, EMACS_INT depth); \f /* Return TRUE when a frame switch causes a minibuffer on the old @@ -181,7 +184,12 @@ void move_minibuffer_onto_frame (void) set_window_buffer (sf->minibuffer_window, nth_minibuffer (i), 0, 0); minibuf_window = sf->minibuffer_window; if (of != sf) - set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); + { + Lisp_Object temp = get_minibuffer (0); + + set_window_buffer (of->minibuffer_window, temp, 0, 0); + set_minibuffer_mode (temp, 0); + } } } @@ -389,6 +397,21 @@ No argument or nil as argument means use the current buffer as BUFFER. */) : Qnil; } +DEFUN ("minibuffer-innermost-command-loop-p", Fminibuffer_innermost_command_loop_p, + Sminibuffer_innermost_command_loop_p, 0, 1, 0, + doc: /* Return t if BUFFER is a minibuffer at the current command loop level. +No argument or nil as argument means use the current buffer as BUFFER. */) + (Lisp_Object buffer) +{ + EMACS_INT depth; + if (NILP (buffer)) + buffer = Fcurrent_buffer (); + depth = this_minibuffer_depth (buffer); + return depth && minibuf_c_loop_level (depth) == command_loop_level + ? Qt + : Qnil; +} + /* Return the nesting depth of the active minibuffer BUFFER, or 0 if BUFFER isn't such a thing. If BUFFER is nil, this means use the current buffer. */ @@ -420,12 +443,17 @@ confirm the aborting of the current minibuffer and all contained ones. */) if (!minibuf_depth) error ("Not in a minibuffer"); + if (NILP (Fminibuffer_innermost_command_loop_p (Qnil))) + error ("Not in most nested command loop"); if (minibuf_depth < minibuf_level) { array[0] = fmt; array[1] = make_fixnum (minibuf_level - minibuf_depth + 1); if (!NILP (Fyes_or_no_p (Fformat (2, array)))) - Fthrow (Qexit, Qt); + { + minibuffer_quit_level = minibuf_depth; + Fthrow (Qexit, Qt); + } } else Fthrow (Qexit, Qt); @@ -508,6 +536,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; Lisp_Object calling_frame = selected_frame; + Lisp_Object calling_window = selected_window; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -598,7 +627,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level > 1 && minibuf_moves_frame_when_opened () - && !minibuf_follows_frame ()) + && (!minibuf_follows_frame () + || (!EQ (mini_frame, selected_frame)))) { EMACS_INT i; @@ -607,8 +637,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, set_window_buffer (minibuf_window, nth_minibuffer (i), 0, 0); } - record_unwind_protect_void (choose_minibuf_frame); - record_unwind_protect (restore_window_configuration, Fcons (Qt, Fcurrent_window_configuration (Qnil))); @@ -640,7 +668,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (calling_frame, + Fcons (calling_window, + minibuf_save_list)))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -694,6 +724,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Switch to the minibuffer. */ minibuffer = get_minibuffer (minibuf_level); + set_minibuffer_mode (minibuffer, minibuf_level); Fset_buffer (minibuffer); /* Defeat (setq-default truncate-lines t), since truncated lines do @@ -738,6 +769,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, where there is an active minibuffer. Set them to point to ` *Minibuf-0*', which is always empty. */ empty_minibuf = get_minibuffer (0); + set_minibuffer_mode (empty_minibuf, 0); FOR_EACH_FRAME (dummy, frame) { @@ -837,20 +869,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, recursive_edit_1 (); - /* We've exited the recursive edit without an error, so switch the - current window away from the expired minibuffer window. */ - { - Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); - /* PREV can be on a different frame when we have a minibuffer only - frame, the other frame's minibuffer window is MINIBUF_WINDOW, - and its "focus window" is also MINIBUF_WINDOW. */ - while (!EQ (prev, minibuf_window) - && !EQ (selected_frame, WINDOW_FRAME (XWINDOW (prev)))) - prev = Fprevious_window (prev, Qnil, Qnil); - if (!EQ (prev, minibuf_window)) - Fset_frame_selected_window (selected_frame, prev, Qnil); - } - /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -959,11 +977,16 @@ Lisp_Object get_minibuffer (EMACS_INT depth) { Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + Lisp_Object cll_tail = Fnthcdr (make_fixnum (depth), + Vcommand_loop_level_list); if (NILP (tail)) { tail = list1 (Qnil); Vminibuffer_list = nconc2 (Vminibuffer_list, tail); + cll_tail = list1 (Qnil); + Vcommand_loop_level_list = nconc2 (Vcommand_loop_level_list, cll_tail); } + XSETCAR (cll_tail, make_fixnum (depth ? command_loop_level : 0)); Lisp_Object buf = Fcar (tail); if (NILP (buf) || !BUFFER_LIVE_P (XBUFFER (buf))) { @@ -973,7 +996,6 @@ get_minibuffer (EMACS_INT depth) buf = Fget_buffer_create (lname, Qnil); /* Do this before set_minibuffer_mode. */ XSETCAR (tail, buf); - set_minibuffer_mode (buf, depth); /* Although the buffer's name starts with a space, undo should be enabled in it. */ Fbuffer_enable_undo (buf); @@ -985,12 +1007,19 @@ get_minibuffer (EMACS_INT depth) while the buffer doesn't know about them any more. */ delete_all_overlays (XBUFFER (buf)); reset_buffer (XBUFFER (buf)); - set_minibuffer_mode (buf, depth); } return buf; } +static EMACS_INT minibuf_c_loop_level (EMACS_INT depth) +{ + Lisp_Object cll = Fnth (make_fixnum (depth), Vcommand_loop_level_list); + if (FIXNUMP (cll)) + return XFIXNUM (cll); + return 0; +} + static void run_exit_minibuf_hook (void) { @@ -1004,17 +1033,16 @@ static void read_minibuf_unwind (void) { Lisp_Object old_deactivate_mark; - Lisp_Object window; + Lisp_Object calling_frame; + Lisp_Object calling_window; Lisp_Object future_mini_window; - /* If this was a recursive minibuffer, - tie the minibuffer window back to the outer level minibuffer buffer. */ - minibuf_level--; - - window = minibuf_window; /* To keep things predictable, in case it matters, let's be in the - minibuffer when we reset the relevant variables. */ - Fset_buffer (XWINDOW (window)->contents); + minibuffer when we reset the relevant variables. Don't depend on + `minibuf_window' here. This could by now be the mini-window of any + frame. */ + Fset_buffer (nth_minibuffer (minibuf_level)); + minibuf_level--; /* Restore prompt, etc, from outer minibuffer level. */ Lisp_Object key_vec = Fcar (minibuf_save_list); @@ -1042,6 +1070,10 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + calling_frame = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); + calling_window = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -1059,7 +1091,7 @@ read_minibuf_unwind (void) mini-window back to its normal size. */ if (minibuf_level == 0 || !EQ (selected_frame, WINDOW_FRAME (XWINDOW (future_mini_window)))) - resize_mini_window (XWINDOW (window), 0); + resize_mini_window (XWINDOW (minibuf_window), 0); /* Deal with frames that should be removed when exiting the minibuffer. */ @@ -1090,6 +1122,24 @@ read_minibuf_unwind (void) to make sure we don't leave around bindings and stuff which only made sense during the read_minibuf invocation. */ call0 (intern ("minibuffer-inactive-mode")); + + /* We've exited the recursive edit, so switch the current windows + away from the expired minibuffer window, both in the current + minibuffer's frame and the original calling frame. */ + choose_minibuf_frame (); + if (!EQ (WINDOW_FRAME (XWINDOW (minibuf_window)), calling_frame)) + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + if (!EQ (prev, minibuf_window) + && EQ (WINDOW_FRAME (XWINDOW (prev)), + WINDOW_FRAME (XWINDOW (minibuf_window)))) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + else + Fset_frame_selected_window (calling_frame, calling_window, Qnil); } \f @@ -2137,6 +2187,7 @@ void init_minibuf_once (void) { staticpro (&Vminibuffer_list); + staticpro (&Vcommand_loop_level_list); pdumper_do_now_and_after_load (init_minibuf_once_for_pdumper); } @@ -2150,6 +2201,7 @@ init_minibuf_once_for_pdumper (void) restore from a dump file. pdumper doesn't try to preserve frames, windows, and so on, so reset everything related here. */ Vminibuffer_list = Qnil; + Vcommand_loop_level_list = Qnil; minibuf_level = 0; minibuf_prompt = Qnil; minibuf_save_list = Qnil; @@ -2380,6 +2432,7 @@ instead. */); defsubr (&Sminibufferp); defsubr (&Sinnermost_minibuffer_p); + defsubr (&Sminibuffer_innermost_command_loop_p); defsubr (&Sabort_minibuffers); defsubr (&Sminibuffer_prompt_end); defsubr (&Sminibuffer_contents); diff --git a/src/window.h b/src/window.h index 79eb44e7a3..b6f88e8f55 100644 --- a/src/window.h +++ b/src/window.h @@ -1120,10 +1120,6 @@ void set_window_buffer (Lisp_Object window, Lisp_Object buffer, extern Lisp_Object echo_area_window; -/* Depth in recursive edits. */ - -extern EMACS_INT command_loop_level; - /* Non-zero if we should redraw the mode lines on the next redisplay. Usually set to a unique small integer so we can track the main causes of full redisplays in `redisplay--mode-lines-cause'. */ -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-11 11:44 ` Alan Mackenzie @ 2021-02-11 14:29 ` Stefan Monnier 2021-02-12 9:48 ` Alan Mackenzie 2021-03-13 18:23 ` Alan Mackenzie 0 siblings, 2 replies; 252+ messages in thread From: Stefan Monnier @ 2021-02-11 14:29 UTC (permalink / raw) To: Alan Mackenzie; +Cc: jakanakaevangeli, emacs-devel > This is more involved. What do we want to happen when a frame with open > minibuffers is deleted? I would say that these minibuffers should, > except in the (eq minibuffer-follows-selected-frame t) case, be aborted, > together with any others in other frames whose nesting level makes this > necessary. I vote for moving those minibuffers elsewhere (anywhere else is fine by me, really). I assume it's no more complicated code-wise, and it should suffer less from the risk of losing information. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-11 14:29 ` Stefan Monnier @ 2021-02-12 9:48 ` Alan Mackenzie 2021-03-13 18:23 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-02-12 9:48 UTC (permalink / raw) To: Stefan Monnier; +Cc: jakanakaevangeli, emacs-devel Hello, Stefan. Thanks for still reading this old thread! On Thu, Feb 11, 2021 at 09:29:44 -0500, Stefan Monnier wrote: > > This is more involved. What do we want to happen when a frame with open > > minibuffers is deleted? I would say that these minibuffers should, > > except in the (eq minibuffer-follows-selected-frame t) case, be aborted, > > together with any others in other frames whose nesting level makes this > > necessary. > I vote for moving those minibuffers elsewhere (anywhere else is fine by > me, really). I assume it's no more complicated code-wise, and it should > suffer less from the risk of losing information. Thinking about it, you are right. Deleting frames doesn't abort any other buffers, so why should minibuffers suffer? The code is going to be tedious and moderately difficult to write either way. So I think on deleting a frame, any minibuffers in it should get pushed onto the previous frame. > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-11 14:29 ` Stefan Monnier 2021-02-12 9:48 ` Alan Mackenzie @ 2021-03-13 18:23 ` Alan Mackenzie 2021-03-13 19:39 ` Stefan Monnier 2021-03-13 20:53 ` jakanakaevangeli 1 sibling, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-13 18:23 UTC (permalink / raw) To: Stefan Monnier, jakanakaevangeli; +Cc: emacs-devel Hello, Stefan and jakanakaevangeli. On Thu, Feb 11, 2021 at 09:29:44 -0500, Stefan Monnier wrote: > > This is more involved. What do we want to happen when a frame with open > > minibuffers is deleted? I would say that these minibuffers should, > > except in the (eq minibuffer-follows-selected-frame t) case, be aborted, > > together with any others in other frames whose nesting level makes this > > necessary. > I vote for moving those minibuffers elsewhere (anywhere else is fine by > me, really). I assume it's no more complicated code-wise, and it should > suffer less from the risk of losing information. Just a quick recapitulation of the problem: when minibuffer-follows-selected-frame is nil, and enable-recursive-minibuffers t, it is possible to have several open minibuffers on several frames. If one of these frames is deleted, its minibuffers continue to exist, but are wholly inaccessible - the only thing to do with them is to abort them, e.g. with C-]. This is suboptimal. The patch below tries to solve this by, when such a frame gets deleted, "zipping" its minibuffers into those of another frame. The idea behind the patch is to use the mini-window's w->prev_buffers component to hold the list of its minibuffers. This mini-window component was unused by the rest of Emacs, apart from accidentally by window--before-delete-windows, which I have now amended. I would be grateful indeed if either of you (or indeed, anybody else), would apply the patch and try it out, or indeed comment on it. Thanks! diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index f81e64bdf9..7da0a48b7c 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -82,7 +82,9 @@ Basic Minibuffer (@pxref{Recursive Mini,,, elisp}). This option is mainly to retain (approximately) the behavior prior to Emacs 28.1. Note that the effect of the command, when you finally finish using the minibuffer, -always takes place in the frame where you first opened it. +always takes place in the frame where you first opened it. The sole +exception is that when that frame no longer exists, the action takes +place in the currently selected frame. @node Minibuffer File @section Minibuffers for File Names diff --git a/lisp/window.el b/lisp/window.el index cfd9876ed0..fb2ea4a985 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4158,7 +4158,7 @@ window--before-delete-windows This function is called only if `switch-to-buffer-preserve-window-point' evaluates non-nil." - (dolist (win (window-list)) + (dolist (win (window-list nil 'no-minibuf)) (let* ((buf (window-buffer (or window win))) (start (window-start win)) (pos (window-point win)) @@ -4416,7 +4416,8 @@ record-window-buffer window (assq-delete-all buffer (window-prev-buffers window)))) ;; Don't record insignificant buffers. - (unless (eq (aref (buffer-name buffer) 0) ?\s) + (when (or (not (eq (aref (buffer-name buffer) 0) ?\s)) + (string-match "^ \\*Minibuf" (buffer-name buffer))) ;; Add an entry for buffer to WINDOW's previous buffers. (with-current-buffer buffer (let ((start (window-start window)) diff --git a/src/frame.c b/src/frame.c index a62347c1fb..b9df5739dd 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1487,7 +1487,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor #endif internal_last_event_frame = Qnil; - move_minibuffer_onto_frame (); + move_minibuffers_onto_frame (sf, for_deletion); return frame; } diff --git a/src/lisp.h b/src/lisp.h index b95f389b89..2f4e6377cb 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4348,7 +4348,7 @@ extern void clear_regexp_cache (void); extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; -extern void move_minibuffer_onto_frame (void); +extern void move_minibuffers_onto_frame (struct frame *, int); extern bool is_minibuffer (EMACS_INT, Lisp_Object); extern EMACS_INT this_minibuffer_depth (Lisp_Object); extern EMACS_INT minibuf_level; diff --git a/src/minibuf.c b/src/minibuf.c index 4b1f4b1ff7..53ed8d5ef8 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -67,6 +67,7 @@ static ptrdiff_t minibuf_prompt_width; static Lisp_Object nth_minibuffer (EMACS_INT depth); static EMACS_INT minibuf_c_loop_level (EMACS_INT depth); static void set_minibuffer_mode (Lisp_Object buf, EMACS_INT depth); +static bool live_minibuffer_p (Lisp_Object); \f /* Return TRUE when a frame switch causes a minibuffer on the old @@ -78,6 +79,7 @@ minibuf_follows_frame (void) Qt); } +#if 0 /* Return TRUE when a minibuffer always remains on the frame where it was first invoked. */ static bool @@ -85,6 +87,7 @@ minibuf_stays_put (void) { return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } +#endif /* Return TRUE when opening a (recursive) minibuffer causes minibuffers on other frames to move to the selected frame. */ @@ -112,83 +115,137 @@ choose_minibuf_frame (void) emacs_abort (); minibuf_window = sf->minibuffer_window; - /* If we've still got another minibuffer open, use its mini-window - instead. */ - if (minibuf_level > 1 && minibuf_stays_put ()) - { - Lisp_Object buffer = get_minibuffer (minibuf_level); - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, - buffer)) - { - minibuf_window = XFRAME (frame)->minibuffer_window; - break; - } - } } +} - if (minibuf_moves_frame_when_opened () - && FRAMEP (selected_frame) - && FRAME_LIVE_P (XFRAME (selected_frame))) - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - struct frame *of; - - FOR_EACH_FRAME (tail, frame) - if (!EQ (frame, selected_frame) - && minibuf_level > 1 - /* The frame's minibuffer can be on a different frame. */ - && ! EQ (XWINDOW ((of = XFRAME (frame))->minibuffer_window)->frame, - selected_frame)) - { - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - - if (!EQ (XWINDOW (of->minibuffer_window)->contents, - nth_minibuffer (0))) - set_window_buffer (of->minibuffer_window, - nth_minibuffer (0), 0, 0); - } - } +/* Get the next live buffer entry from a w->prev_buffer, setting the pertinent + variables of `zip_minibuffer_stacks'. The parameter P is either "d" for + the destination structures, or "s" for the source structures. When the + ->prev_buffers list is exhausted, set di/si to -1. */ +#define NEXT_BUFFER_ENTRY(p) \ + do \ + { \ + if (NILP (p##_bufs)) \ + { \ + p##b = Qnil; \ + p##_ent = Qnil; \ + } \ + else \ + { \ + p##_ent = Fcar (p##_bufs); \ + p##b = Fcar (p##_ent); \ + p##_bufs = Fcdr (p##_bufs); \ + } \ + if (!NILP (p##b)) \ + p##i = this_minibuffer_depth (p##b); \ + else \ + p##i = -1; \ + } while (p##i == 0) + +/* Move the ordered "stack" of minibuffers from SOURCE_WINDOW to + DEST_WINDOW, interleaving those minibuffers with any in DEST_WINDOW + to produce an ordered combination. The ordering is by minibuffer + depth. A stack of minibuffers consists of the minibuffer currently + in DEST/SOURCE_WINDOW together with any recorded in the + ->prev_buffers field of the struct window. */ +static void +zip_minibuffer_stacks (Lisp_Object dest_window, Lisp_Object source_window) +{ + struct window *dw = XWINDOW (dest_window); + struct window *sw = XWINDOW (source_window); + Lisp_Object d_bufs, s_bufs; /* Lists of minibuffer entries */ + Lisp_Object acc = Qnil; + Lisp_Object d_ent, s_ent; /* Entries from dw/sw->prev_buffers */ + Lisp_Object db, sb; /* (Mini)buffers from the above */ + EMACS_INT di, si; /* Indices in the minibuffer list of db and sb */ + + if (!live_minibuffer_p (dw->contents) + && NILP (dw->prev_buffers)) + { + set_window_buffer (dest_window, sw->contents, 0, 0); + Fset_window_start (dest_window, Fwindow_start (source_window), Qnil); + Fset_window_point (dest_window, Fwindow_point (source_window)); + dw->prev_buffers = sw->prev_buffers; + set_window_buffer (source_window, get_minibuffer (0), 0, 0); + sw->prev_buffers = Qnil; + return; + } + + if (live_minibuffer_p (dw->contents)) + call1 (Qrecord_window_buffer, dest_window); + if (live_minibuffer_p (sw->contents)) + call1 (Qrecord_window_buffer, source_window); + + d_bufs = Fnreverse (dw->prev_buffers); + s_bufs = Fnreverse (sw->prev_buffers); + + NEXT_BUFFER_ENTRY (d); + NEXT_BUFFER_ENTRY (s); + + while (di != -1 && si != -1) + if (di < si) + { + acc = Fcons (d_ent, acc); + NEXT_BUFFER_ENTRY (d); + } + else + { + acc = Fcons (s_ent, acc); + NEXT_BUFFER_ENTRY (s); + } + while (di != -1) + { + acc = Fcons (d_ent, acc); + NEXT_BUFFER_ENTRY (d); + } + while (si != -1) + { + acc = Fcons (s_ent, acc); + NEXT_BUFFER_ENTRY (s); + } + if (!NILP (acc)) + { + d_ent = Fcar (acc); + acc = Fcdr (acc); + set_window_buffer (dest_window, Fcar (d_ent), 0, 0); + Fset_window_start (dest_window, Fcar (Fcdr (d_ent)), Qnil); + Fset_window_point (dest_window, Fcar (Fcdr (Fcdr (d_ent)))); + } + dw->prev_buffers = acc; + sw->prev_buffers = Qnil; + set_window_buffer (source_window, get_minibuffer (0), 0, 0); } +#undef NEXT_BUFFER_ENTRY -/* If `minibuffer_follows_selected_frame' is t and we have a - minibuffer, move it from its current frame to the selected frame. - This function is intended to be called from `do_switch_frame' in - frame.c. */ -void move_minibuffer_onto_frame (void) +/* If `minibuffer_follows_selected_frame' is t, or we're about to + delete a frame which potentially "contains" minibuffers, move them + from the old frame to the selected frame. This function is + intended to be called from `do_switch_frame' in frame.c. OF is the + old frame, FOR_DELETION is self explanatory. */ +void +move_minibuffers_onto_frame (struct frame *of, int for_deletion) { - if (!minibuf_level) - return; - if (!minibuf_follows_frame ()) + if (!for_deletion && (!minibuf_level || !minibuf_follows_frame ())) return; if (FRAMEP (selected_frame) && FRAME_LIVE_P (XFRAME (selected_frame)) - && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) + && (for_deletion + || !EQ (minibuf_window, + XFRAME (selected_frame)->minibuffer_window))) { - EMACS_INT i; struct frame *sf = XFRAME (selected_frame); - Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; - struct frame *of = XFRAME (old_frame); - - /* Stack up all the (recursively) open minibuffers on the selected - mini_window. */ - for (i = 1; i <= minibuf_level; i++) - set_window_buffer (sf->minibuffer_window, nth_minibuffer (i), 0, 0); - minibuf_window = sf->minibuffer_window; - if (of != sf) + Lisp_Object mini_frame = XWINDOW (minibuf_window)->frame; + struct frame *mf = XFRAME (mini_frame); + if (minibuf_follows_frame () || for_deletion) + zip_minibuffer_stacks (sf->minibuffer_window, + of->minibuffer_window); + if (minibuf_window != sf->minibuffer_window) { Lisp_Object temp = get_minibuffer (0); - set_window_buffer (of->minibuffer_window, temp, 0, 0); + set_window_buffer (mf->minibuffer_window, temp, 0, 0); set_minibuffer_mode (temp, 0); + minibuf_window = sf->minibuffer_window; } } } @@ -221,6 +278,7 @@ without invoking the usual minibuffer commands. */) /* Actual minibuffer invocation. */ static void read_minibuf_unwind (void); +static void minibuffer_unwind (void); static void run_exit_minibuf_hook (void); @@ -544,7 +602,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histval; Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; + Lisp_Object old_minibuf_window = minibuf_window; specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -626,17 +684,23 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); if (minibuf_level > 1 + && (!EQ (minibuf_window, old_minibuf_window)) && minibuf_moves_frame_when_opened () - && (!minibuf_follows_frame () - || (!EQ (mini_frame, selected_frame)))) + && (!minibuf_follows_frame ())) { - EMACS_INT i; + Lisp_Object old_frame = XWINDOW (old_minibuf_window)->frame; + struct frame *of = XFRAME (old_frame); - /* Stack up the existing minibuffers on the current mini-window */ - for (i = 1; i < minibuf_level; i++) - set_window_buffer (minibuf_window, nth_minibuffer (i), 0, 0); + zip_minibuffer_stacks (minibuf_window, old_minibuf_window); + /* The frame's minibuffer can be on a different frame. */ + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (old_frame, + Fframe_first_window (old_frame), Qnil); } + if (live_minibuffer_p (XWINDOW (minibuf_window)->contents)) + call1 (Qrecord_window_buffer, minibuf_window); + record_unwind_protect_void (minibuffer_unwind); record_unwind_protect (restore_window_configuration, Fcons (Qt, Fcurrent_window_configuration (Qnil))); @@ -771,23 +835,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, empty_minibuf = get_minibuffer (0); set_minibuffer_mode (empty_minibuf, 0); - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - Lisp_Object buffer; - - if (!NILP (mini_window) && !EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - { - buffer = XWINDOW (mini_window)->contents; - if (!live_minibuffer_p (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -908,7 +955,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, unbind_to (count, Qnil); /* Switch the frame back to the calling frame. */ - if (!EQ (selected_frame, calling_frame) + if ((!EQ (selected_frame, calling_frame) + || !EQ (XWINDOW (XFRAME (calling_frame)->minibuffer_window)->frame, + calling_frame)) && FRAMEP (calling_frame) && FRAME_LIVE_P (XFRAME (calling_frame))) call2 (intern ("select-frame-set-input-focus"), calling_frame, Qnil); @@ -1127,20 +1176,57 @@ read_minibuf_unwind (void) away from the expired minibuffer window, both in the current minibuffer's frame and the original calling frame. */ choose_minibuf_frame (); - if (!EQ (WINDOW_FRAME (XWINDOW (minibuf_window)), calling_frame)) - { - Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); - /* PREV can be on a different frame when we have a minibuffer only - frame, the other frame's minibuffer window is MINIBUF_WINDOW, - and its "focus window" is also MINIBUF_WINDOW. */ - if (!EQ (prev, minibuf_window) - && EQ (WINDOW_FRAME (XWINDOW (prev)), - WINDOW_FRAME (XWINDOW (minibuf_window)))) - Fset_frame_selected_window (selected_frame, prev, Qnil); - } - else - Fset_frame_selected_window (calling_frame, calling_window, Qnil); + if (NILP (XWINDOW (minibuf_window)->prev_buffers)) + { + if (!EQ (WINDOW_FRAME (XWINDOW (minibuf_window)), calling_frame)) + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + if (!EQ (prev, minibuf_window) + && EQ (WINDOW_FRAME (XWINDOW (prev)), + WINDOW_FRAME (XWINDOW (minibuf_window)))) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + else + Fset_frame_selected_window (calling_frame, calling_window, Qnil); + } } + +/* Replace the expired minibuffer in the selected frame with the next + less nested minibuffer, if any, stored in the minibuffer window. + Otherwise, replace it with the null minibuffer. MINIBUF_WINDOW + gets set to the selected frame's minibuffer. */ +static void +minibuffer_unwind (void) +{ + struct frame *sf = XFRAME (selected_frame); + struct window *w; + Lisp_Object entry; + + if (FRAMEP (selected_frame) + && FRAME_LIVE_P (sf)) + { + minibuf_window = sf->minibuffer_window; + w = XWINDOW (minibuf_window); + if (!NILP (w->prev_buffers)) + { + entry = Fcar (w->prev_buffers); + w->prev_buffers = Fcdr (w->prev_buffers); + set_window_buffer (minibuf_window, Fcar (entry), 0, 0); + Fset_window_start (minibuf_window, Fcar (Fcdr (entry)), Qnil); + Fset_window_point (minibuf_window, Fcar (Fcdr (Fcdr (entry)))); + /* set-window-configuration may/will have unselected the + mini-window as the selected window. Restore it. */ + if (EQ (w->frame, selected_frame)) + Fset_frame_selected_window (selected_frame, minibuf_window, Qnil); + } + else + set_window_buffer (minibuf_window, nth_minibuffer (0), 0, 0); + } +} + \f void diff --git a/src/window.c b/src/window.c index eb16e2a433..cde53e8059 100644 --- a/src/window.c +++ b/src/window.c @@ -6958,7 +6960,8 @@ the return value is nil. Otherwise the value is t. */) if (BUFFERP (w->contents) && !EQ (w->contents, p->buffer) - && BUFFER_LIVE_P (XBUFFER (p->buffer))) + && BUFFER_LIVE_P (XBUFFER (p->buffer)) + && (NILP (Fminibufferp (p->buffer, Qnil)))) /* If a window we restore gets another buffer, record the window's old buffer. */ call1 (Qrecord_window_buffer, window); diff --git a/src/xdisp.c b/src/xdisp.c index cc0a689ba3..a405d51f80 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -12650,9 +12650,8 @@ gui_consider_frame_title (Lisp_Object frame) mode_line_noprop_buf; then display the title. */ record_unwind_protect (unwind_format_mode_line, format_mode_line_unwind_data - (f, current_buffer, selected_window, false)); + (NULL, current_buffer, Qnil, false)); - Fselect_window (f->selected_window, Qt); set_buffer_internal_1 (XBUFFER (XWINDOW (f->selected_window)->contents)); fmt = FRAME_ICONIFIED_P (f) ? Vicon_title_format : Vframe_title_format; > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-13 18:23 ` Alan Mackenzie @ 2021-03-13 19:39 ` Stefan Monnier 2021-03-13 20:24 ` Alan Mackenzie 2021-03-13 20:53 ` jakanakaevangeli 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2021-03-13 19:39 UTC (permalink / raw) To: Alan Mackenzie; +Cc: jakanakaevangeli, emacs-devel Hi Alan, > I would be grateful indeed if either of you (or indeed, anybody else), > would apply the patch and try it out, or indeed comment on it. Thanks! I'm not very familiar with this part of Emacs's code, but here are some things I noticed: > @@ -4416,7 +4416,8 @@ record-window-buffer > window (assq-delete-all buffer (window-prev-buffers window)))) > > ;; Don't record insignificant buffers. > - (unless (eq (aref (buffer-name buffer) 0) ?\s) > + (when (or (not (eq (aref (buffer-name buffer) 0) ?\s)) > + (string-match "^ \\*Minibuf" (buffer-name buffer))) I think you want to use `minibufferp` here (and if not, then please add a comment explaining why). > @@ -4348,7 +4348,7 @@ extern void clear_regexp_cache (void); > > extern Lisp_Object Vminibuffer_list; > extern Lisp_Object last_minibuf_string; > -extern void move_minibuffer_onto_frame (void); > +extern void move_minibuffers_onto_frame (struct frame *, int); Please use the `bool` type for booleans. > +/* Get the next live buffer entry from a w->prev_buffer, setting the pertinent > + variables of `zip_minibuffer_stacks'. The parameter P is either "d" for > + the destination structures, or "s" for the source structures. When the > + ->prev_buffers list is exhausted, set di/si to -1. */ > +#define NEXT_BUFFER_ENTRY(p) \ > + do \ > + { \ > + if (NILP (p##_bufs)) \ > + { \ > + p##b = Qnil; \ > + p##_ent = Qnil; \ > + } \ > + else \ > + { \ > + p##_ent = Fcar (p##_bufs); \ > + p##b = Fcar (p##_ent); \ > + p##_bufs = Fcdr (p##_bufs); \ > + } \ > + if (!NILP (p##b)) \ > + p##i = this_minibuffer_depth (p##b); \ > + else \ > + p##i = -1; \ > + } while (p##i == 0) Any chance we could make that into a function taking an argument `p` that's a reference to a struct { Lisp_Object bufs; Lisp_Object b; Lisp_Object ent; int depth; } ? I know we already use such longish macros in various places, but I'd rather we use fewer of them rather than adding more of them, because they're a pain to debug (and I find they make the code harder to read; e.g. here we see a call to `NEXT_BUFFER_ENTRY` we can't know which variables will be accessed/modified without looking at the definition of the macro). > +/* Move the ordered "stack" of minibuffers from SOURCE_WINDOW to > + DEST_WINDOW, interleaving those minibuffers with any in DEST_WINDOW > + to produce an ordered combination. The ordering is by minibuffer > + depth. A stack of minibuffers consists of the minibuffer currently > + in DEST/SOURCE_WINDOW together with any recorded in the > + ->prev_buffers field of the struct window. */ This is actually the "merge" part of a merge sort, so we could use `sort_list` or even just the `merge` function used by `sort_list`. [ Tho maybe it is simpler and more robust (in case ELisp code can access (and hence modify) those lists) to leave those lists not sorted, and instead look for the deepest buffer in `minibuffer_unwind` rather than just picking the next. ] > +/* If `minibuffer_follows_selected_frame' is t, or we're about to > + delete a frame which potentially "contains" minibuffers, move them > + from the old frame to the selected frame. This function is > + intended to be called from `do_switch_frame' in frame.c. OF is the > + old frame, FOR_DELETION is self explanatory. */ Actually, I suspect that the "self explanatory" part will not be true when my daughter ends up having to fix that code 10 years from now. [ Tho, it currently looks quite unlikely :-( ] Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-13 19:39 ` Stefan Monnier @ 2021-03-13 20:24 ` Alan Mackenzie 2021-03-13 20:52 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-13 20:24 UTC (permalink / raw) To: Stefan Monnier; +Cc: jakanakaevangeli, emacs-devel Hello, Stefan. Thanks for such a rapid and useful reply. I'll put most of your suggestions (actually, probably all of them) into my code this evening. I didn't actually know there was a `sort_list' function, or a merge part of it, so that should save quite a lot of code if I can use it (which I probably can). On Sat, Mar 13, 2021 at 14:39:28 -0500, Stefan Monnier wrote: > Hi Alan, > > I would be grateful indeed if either of you (or indeed, anybody else), > > would apply the patch and try it out, or indeed comment on it. Thanks! > I'm not very familiar with this part of Emacs's code, but here are some > things I noticed: [ .... ] > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-13 20:24 ` Alan Mackenzie @ 2021-03-13 20:52 ` Stefan Monnier 2021-03-14 18:26 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2021-03-13 20:52 UTC (permalink / raw) To: Alan Mackenzie; +Cc: jakanakaevangeli, emacs-devel > I didn't actually know there was a `sort_list' function, or a merge part > of it, so that should save quite a lot of code if I can use it (which I > probably can). I didn't either, really, I found them by following the code from `Fsort`. Of course, this is meant for ELisp so the predicate has to be an ELisp value rather than a C function, so it will probably force you to define the predicate with DEFUN, which is not completely satisfactory (tho not terribly harmful either, I guess). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-13 20:52 ` Stefan Monnier @ 2021-03-14 18:26 ` Alan Mackenzie 2021-03-14 18:48 ` Eli Zaretskii 2021-03-14 20:32 ` Stefan Monnier 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-14 18:26 UTC (permalink / raw) To: Stefan Monnier; +Cc: jakanakaevangeli, emacs-devel Hello, Stefan. On Sat, Mar 13, 2021 at 15:52:51 -0500, Stefan Monnier wrote: > > I didn't actually know there was a `sort_list' function, or a merge part > > of it, so that should save quite a lot of code if I can use it (which I > > probably can). > I didn't either, really, I found them by following the code from > `Fsort`. Of course, this is meant for ELisp so the predicate has to be > an ELisp value rather than a C function, so it will probably force you > to define the predicate with DEFUN, which is not completely > satisfactory (tho not terribly harmful either, I guess). No, not completely satisfactory. So, after spending an hour or so trying to work out how to define a Lisp function in C without it being visible in the Lisp world, I hit on a simpler solution - copy `merge' to a new function `merge_c', replacing the Lisp predicate with a boolean C function. This works fine, at the small cost of a little code duplication. I think I've followed all your other suggestions from yesterday, now. Anyhow, I think I've fixed the bugs that jakanakaevangeli pointed out yesterday, so I'll post my updated patch in my reply to her/him. > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-14 18:26 ` Alan Mackenzie @ 2021-03-14 18:48 ` Eli Zaretskii 2021-03-14 20:32 ` Stefan Monnier 1 sibling, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2021-03-14 18:48 UTC (permalink / raw) To: Alan Mackenzie; +Cc: monnier, jakanakaevangeli, emacs-devel > Date: Sun, 14 Mar 2021 18:26:18 +0000 > From: Alan Mackenzie <acm@muc.de> > Cc: jakanakaevangeli <jakanakaevangeli@chiru.no>, emacs-devel@gnu.org > > after spending an hour or so trying to work out how to define a Lisp > function in C without it being visible in the Lisp world Isn't defsubr sup[posed to do that? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-14 18:26 ` Alan Mackenzie 2021-03-14 18:48 ` Eli Zaretskii @ 2021-03-14 20:32 ` Stefan Monnier 1 sibling, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2021-03-14 20:32 UTC (permalink / raw) To: Alan Mackenzie; +Cc: jakanakaevangeli, emacs-devel > No, not completely satisfactory. So, after spending an hour or so > trying to work out how to define a Lisp function in C without it being > visible in the Lisp world, Oh, I wasn't even thinking of trying to hide it from ELisp (AFAICT it's not dangerous to expose it to ELisp). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-13 18:23 ` Alan Mackenzie 2021-03-13 19:39 ` Stefan Monnier @ 2021-03-13 20:53 ` jakanakaevangeli 2021-03-14 19:17 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: jakanakaevangeli @ 2021-03-13 20:53 UTC (permalink / raw) To: Alan Mackenzie, Stefan Monnier; +Cc: emacs-devel Alan Mackenzie <acm@muc.de> writes: > Hello, Stefan and jakanakaevangeli. > > On Thu, Feb 11, 2021 at 09:29:44 -0500, Stefan Monnier wrote: >> > This is more involved. What do we want to happen when a frame with open >> > minibuffers is deleted? I would say that these minibuffers should, >> > except in the (eq minibuffer-follows-selected-frame t) case, be aborted, >> > together with any others in other frames whose nesting level makes this >> > necessary. > >> I vote for moving those minibuffers elsewhere (anywhere else is fine by >> me, really). I assume it's no more complicated code-wise, and it should >> suffer less from the risk of losing information. > > Just a quick recapitulation of the problem: when > minibuffer-follows-selected-frame is nil, and > enable-recursive-minibuffers t, it is possible to have several open > minibuffers on several frames. If one of these frames is deleted, its > minibuffers continue to exist, but are wholly inaccessible - the only > thing to do with them is to abort them, e.g. with C-]. This is > suboptimal. > > The patch below tries to solve this by, when such a frame gets deleted, > "zipping" its minibuffers into those of another frame. The idea behind > the patch is to use the mini-window's w->prev_buffers component to hold > the list of its minibuffers. This mini-window component was unused by > the rest of Emacs, apart from accidentally by > window--before-delete-windows, which I have now amended. > > I would be grateful indeed if either of you (or indeed, anybody else), > would apply the patch and try it out, or indeed comment on it. Thanks! Great! I tested it and there still seem to be a few ways one can end up with invisible active minibuffers, all of them with minibuffer-follows-selected-frame set to nil: 1) C-x C-f on frame A C-x C-f on frame B select frame A and press C-] (abort-recursive-edit) This does quit only the inner minibuffer as expected but it hides the outer one for some reason. This also seems to be a regression of this patch, the minibuffer isn't hidden without this patch applied. 2) (Might depend on your window manager, I'm not sure) Open frames A, B, and C C-x C-f on frame A C-x b on frame B Close frame A Both of the minibuffers are now moved to frame C and the outer C-x C-f is shown in the mini-window, which is kind of annoying. If we now select frame C and press C-], the same problem as in 1) occurs. 3) In emacs daemon, evaluate (run-at-time 5 nil #'make-frame '((window-system . x))) and then open a minibuffer and close the last frame. When a new frame appears, the same problem as in 1) occurs. Hope this helps. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-13 20:53 ` jakanakaevangeli @ 2021-03-14 19:17 ` Alan Mackenzie 2021-03-14 21:23 ` Miha Rihtaršič 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-14 19:17 UTC (permalink / raw) To: jakanakaevangeli; +Cc: Stefan Monnier, emacs-devel Hello, Jakanakaevangeli, On Sat, Mar 13, 2021 at 21:53:05 +0100, jakanakaevangeli wrote: > Alan Mackenzie <acm@muc.de> writes: [ .... ] > > I would be grateful indeed if either of you (or indeed, anybody else), > > would apply the patch and try it out, or indeed comment on it. Thanks! > Great! I tested it and there still seem to be a few ways one can end up > with invisible active minibuffers, all of them with > minibuffer-follows-selected-frame set to nil: > 1) > C-x C-f on frame A > C-x C-f on frame B > select frame A and press C-] (abort-recursive-edit) > This does quit only the inner minibuffer as expected but it hides the > outer one for some reason. This also seems to be a regression of this > patch, the minibuffer isn't hidden without this patch applied. I think I've fixed that, now. There was a call to set-window-configuration which was replacing the wanted minibuffer with a different one. That's now fixed. > 2) (Might depend on your window manager, I'm not sure) > Open frames A, B, and C > C-x C-f on frame A > C-x b on frame B > Close frame A > Both of the minibuffers are now moved to frame C and the outer C-x C-f > is shown in the mini-window, which is kind of annoying. If we now select > frame C and press C-], the same problem as in 1) occurs. This was a bit of dangling code from an earlier version which should have been removed. It was, again, overwriting "the" minibuffer (which was still on frame B) with the null minibuffer, thus causing the C-x b to be lost. > 3) > In emacs daemon, evaluate > (run-at-time 5 nil #'make-frame '((window-system . x))) > and then open a minibuffer and close the last frame. When a new frame > appears, the same problem as in 1) occurs. I think one of the two previous changes fixed this. > Hope this helps. It's been an enormous help, thanks! Testing this is difficult because there are so many different things which can interact with eachother in so many unwanted ways. Anyway, here's an updated patch, which should apply to master, incorporating Stefan's suggestions from last night, and fixes to the above bugs: diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index f81e64bdf9..7da0a48b7c 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -82,7 +82,9 @@ Basic Minibuffer (@pxref{Recursive Mini,,, elisp}). This option is mainly to retain (approximately) the behavior prior to Emacs 28.1. Note that the effect of the command, when you finally finish using the minibuffer, -always takes place in the frame where you first opened it. +always takes place in the frame where you first opened it. The sole +exception is that when that frame no longer exists, the action takes +place in the currently selected frame. @node Minibuffer File @section Minibuffers for File Names diff --git a/lisp/window.el b/lisp/window.el index cfd9876ed0..f27631bb86 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4158,7 +4158,7 @@ window--before-delete-windows This function is called only if `switch-to-buffer-preserve-window-point' evaluates non-nil." - (dolist (win (window-list)) + (dolist (win (window-list nil 'no-minibuf)) (let* ((buf (window-buffer (or window win))) (start (window-start win)) (pos (window-point win)) @@ -4416,7 +4416,8 @@ record-window-buffer window (assq-delete-all buffer (window-prev-buffers window)))) ;; Don't record insignificant buffers. - (unless (eq (aref (buffer-name buffer) 0) ?\s) + (when (or (not (eq (aref (buffer-name buffer) 0) ?\s)) + (minibufferp buffer)) ;; Add an entry for buffer to WINDOW's previous buffers. (with-current-buffer buffer (let ((start (window-start window)) diff --git a/src/fns.c b/src/fns.c index 7914bd4779..f9bd3913bb 100644 --- a/src/fns.c +++ b/src/fns.c @@ -2227,6 +2227,52 @@ merge (Lisp_Object org_l1, Lisp_Object org_l2, Lisp_Object pred) } } +Lisp_Object +merge_c (Lisp_Object org_l1, Lisp_Object org_l2, bool (*less) (Lisp_Object, Lisp_Object)) +{ + Lisp_Object l1 = org_l1; + Lisp_Object l2 = org_l2; + Lisp_Object tail = Qnil; + Lisp_Object value = Qnil; + + while (1) + { + if (NILP (l1)) + { + if (NILP (tail)) + return l2; + Fsetcdr (tail, l2); + return value; + } + if (NILP (l2)) + { + if (NILP (tail)) + return l1; + Fsetcdr (tail, l1); + return value; + } + + Lisp_Object tem; + if (less (Fcar (l1), Fcar (l2))) + { + tem = l1; + l1 = Fcdr (l1); + org_l1 = l1; + } + else + { + tem = l2; + l2 = Fcdr (l2); + org_l2 = l2; + } + if (NILP (tail)) + value = tem; + else + Fsetcdr (tail, tem); + tail = tem; + } +} + \f /* This does not check for quits. That is safe since it must terminate. */ diff --git a/src/frame.c b/src/frame.c index a62347c1fb..b9df5739dd 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1487,7 +1487,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor #endif internal_last_event_frame = Qnil; - move_minibuffer_onto_frame (); + move_minibuffers_onto_frame (sf, for_deletion); return frame; } diff --git a/src/lisp.h b/src/lisp.h index b95f389b89..c67c8b0857 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3610,6 +3610,7 @@ extern void validate_subarray (Lisp_Object, Lisp_Object, Lisp_Object, extern Lisp_Object substring_both (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t); extern Lisp_Object merge (Lisp_Object, Lisp_Object, Lisp_Object); +extern Lisp_Object merge_c (Lisp_Object, Lisp_Object, bool (*) (Lisp_Object, Lisp_Object)); extern Lisp_Object do_yes_or_no_p (Lisp_Object); extern int string_version_cmp (Lisp_Object, Lisp_Object); extern Lisp_Object concat2 (Lisp_Object, Lisp_Object); @@ -4348,7 +4349,7 @@ extern void clear_regexp_cache (void); extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; -extern void move_minibuffer_onto_frame (void); +extern void move_minibuffers_onto_frame (struct frame *, bool); extern bool is_minibuffer (EMACS_INT, Lisp_Object); extern EMACS_INT this_minibuffer_depth (Lisp_Object); extern EMACS_INT minibuf_level; diff --git a/src/minibuf.c b/src/minibuf.c index 4b1f4b1ff7..279dc67c33 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -67,6 +67,7 @@ static ptrdiff_t minibuf_prompt_width; static Lisp_Object nth_minibuffer (EMACS_INT depth); static EMACS_INT minibuf_c_loop_level (EMACS_INT depth); static void set_minibuffer_mode (Lisp_Object buf, EMACS_INT depth); +static bool live_minibuffer_p (Lisp_Object); \f /* Return TRUE when a frame switch causes a minibuffer on the old @@ -78,6 +79,7 @@ minibuf_follows_frame (void) Qt); } +#if 0 /* Return TRUE when a minibuffer always remains on the frame where it was first invoked. */ static bool @@ -85,6 +87,7 @@ minibuf_stays_put (void) { return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } +#endif /* Return TRUE when opening a (recursive) minibuffer causes minibuffers on other frames to move to the selected frame. */ @@ -112,84 +115,86 @@ choose_minibuf_frame (void) emacs_abort (); minibuf_window = sf->minibuffer_window; - /* If we've still got another minibuffer open, use its mini-window - instead. */ - if (minibuf_level > 1 && minibuf_stays_put ()) - { - Lisp_Object buffer = get_minibuffer (minibuf_level); - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, - buffer)) - { - minibuf_window = XFRAME (frame)->minibuffer_window; - break; - } - } } +} - if (minibuf_moves_frame_when_opened () - && FRAMEP (selected_frame) - && FRAME_LIVE_P (XFRAME (selected_frame))) - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - struct frame *of; - - FOR_EACH_FRAME (tail, frame) - if (!EQ (frame, selected_frame) - && minibuf_level > 1 - /* The frame's minibuffer can be on a different frame. */ - && ! EQ (XWINDOW ((of = XFRAME (frame))->minibuffer_window)->frame, - selected_frame)) - { - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - - if (!EQ (XWINDOW (of->minibuffer_window)->contents, - nth_minibuffer (0))) - set_window_buffer (of->minibuffer_window, - nth_minibuffer (0), 0, 0); - } - } +/* If ENT1 has a higher minibuffer index than ENT2, return true. More +precisely, compare the buffer components of each window->prev_buffers +entry. */ +static bool +minibuffer_ent_greater (Lisp_Object ent1, Lisp_Object ent2) +{ + return this_minibuffer_depth (Fcar (ent1)) + > this_minibuffer_depth (Fcar (ent2)) ; } -/* If `minibuffer_follows_selected_frame' is t and we have a - minibuffer, move it from its current frame to the selected frame. - This function is intended to be called from `do_switch_frame' in - frame.c. */ -void move_minibuffer_onto_frame (void) +/* Move the ordered "stack" of minibuffers from SOURCE_WINDOW to + DEST_WINDOW, interleaving those minibuffers with any in DEST_WINDOW + to produce an ordered combination. The ordering is by minibuffer + depth. A stack of minibuffers consists of the minibuffer currently + in DEST/SOURCE_WINDOW together with any recorded in the + ->prev_buffers field of the struct window. */ +static void +zip_minibuffer_stacks (Lisp_Object dest_window, Lisp_Object source_window) { - if (!minibuf_level) - return; - if (!minibuf_follows_frame ()) + struct window *dw = XWINDOW (dest_window); + struct window *sw = XWINDOW (source_window); + Lisp_Object acc; + Lisp_Object d_ent; /* Entry from dw->prev_buffers */ + + if (!live_minibuffer_p (dw->contents) + && NILP (dw->prev_buffers)) + { + set_window_buffer (dest_window, sw->contents, 0, 0); + Fset_window_start (dest_window, Fwindow_start (source_window), Qnil); + Fset_window_point (dest_window, Fwindow_point (source_window)); + dw->prev_buffers = sw->prev_buffers; + set_window_buffer (source_window, get_minibuffer (0), 0, 0); + sw->prev_buffers = Qnil; + return; + } + + if (live_minibuffer_p (dw->contents)) + call1 (Qrecord_window_buffer, dest_window); + if (live_minibuffer_p (sw->contents)) + call1 (Qrecord_window_buffer, source_window); + + acc = merge_c (dw->prev_buffers, sw->prev_buffers, minibuffer_ent_greater); + + if (!NILP (acc)) + { + d_ent = Fcar (acc); + acc = Fcdr (acc); + set_window_buffer (dest_window, Fcar (d_ent), 0, 0); + Fset_window_start (dest_window, Fcar (Fcdr (d_ent)), Qnil); + Fset_window_point (dest_window, Fcar (Fcdr (Fcdr (d_ent)))); + } + dw->prev_buffers = acc; + sw->prev_buffers = Qnil; + set_window_buffer (source_window, get_minibuffer (0), 0, 0); +} + +/* If `minibuffer_follows_selected_frame' is t, or we're about to + delete a frame which potentially "contains" minibuffers, move them + from the old frame to the selected frame. This function is + intended to be called from `do_switch_frame' in frame.c. OF is the + old frame, FOR_DELETION is true if OF is about to be deleted. */ +void +move_minibuffers_onto_frame (struct frame *of, bool for_deletion) +{ + if (!for_deletion && (!minibuf_level || !minibuf_follows_frame ())) return; if (FRAMEP (selected_frame) && FRAME_LIVE_P (XFRAME (selected_frame)) - && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) + && (for_deletion + || !EQ (minibuf_window, + XFRAME (selected_frame)->minibuffer_window))) { - EMACS_INT i; struct frame *sf = XFRAME (selected_frame); - Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; - struct frame *of = XFRAME (old_frame); - /* Stack up all the (recursively) open minibuffers on the selected - mini_window. */ - for (i = 1; i <= minibuf_level; i++) - set_window_buffer (sf->minibuffer_window, nth_minibuffer (i), 0, 0); - minibuf_window = sf->minibuffer_window; - if (of != sf) - { - Lisp_Object temp = get_minibuffer (0); - - set_window_buffer (of->minibuffer_window, temp, 0, 0); - set_minibuffer_mode (temp, 0); - } + if (minibuf_follows_frame () || for_deletion) + zip_minibuffer_stacks (sf->minibuffer_window, + of->minibuffer_window); } } @@ -221,6 +226,7 @@ without invoking the usual minibuffer commands. */) /* Actual minibuffer invocation. */ static void read_minibuf_unwind (void); +static void minibuffer_unwind (void); static void run_exit_minibuf_hook (void); @@ -544,7 +550,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histval; Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; + Lisp_Object old_minibuf_window = minibuf_window; specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -626,17 +632,23 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); if (minibuf_level > 1 + && (!EQ (minibuf_window, old_minibuf_window)) && minibuf_moves_frame_when_opened () - && (!minibuf_follows_frame () - || (!EQ (mini_frame, selected_frame)))) + && (!minibuf_follows_frame ())) { - EMACS_INT i; + Lisp_Object old_frame = XWINDOW (old_minibuf_window)->frame; + struct frame *of = XFRAME (old_frame); - /* Stack up the existing minibuffers on the current mini-window */ - for (i = 1; i < minibuf_level; i++) - set_window_buffer (minibuf_window, nth_minibuffer (i), 0, 0); + zip_minibuffer_stacks (minibuf_window, old_minibuf_window); + /* The frame's minibuffer can be on a different frame. */ + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (old_frame, + Fframe_first_window (old_frame), Qnil); } + if (live_minibuffer_p (XWINDOW (minibuf_window)->contents)) + call1 (Qrecord_window_buffer, minibuf_window); + record_unwind_protect_void (minibuffer_unwind); record_unwind_protect (restore_window_configuration, Fcons (Qt, Fcurrent_window_configuration (Qnil))); @@ -771,23 +783,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, empty_minibuf = get_minibuffer (0); set_minibuffer_mode (empty_minibuf, 0); - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - Lisp_Object buffer; - - if (!NILP (mini_window) && !EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - { - buffer = XWINDOW (mini_window)->contents; - if (!live_minibuffer_p (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -908,7 +903,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, unbind_to (count, Qnil); /* Switch the frame back to the calling frame. */ - if (!EQ (selected_frame, calling_frame) + if ((!EQ (selected_frame, calling_frame) + || !EQ (XWINDOW (XFRAME (calling_frame)->minibuffer_window)->frame, + calling_frame)) && FRAMEP (calling_frame) && FRAME_LIVE_P (XFRAME (calling_frame))) call2 (intern ("select-frame-set-input-focus"), calling_frame, Qnil); @@ -1026,6 +1023,11 @@ run_exit_minibuf_hook (void) safe_run_hooks (Qminibuffer_exit_hook); } +/* This variable preserves the minibuffer in the selected frame across + the call of restore_window_configuration. It should be used only + by `read_minibuf_unwind' and `minibuffer_unwind'. */ +static Lisp_Object selected_frame_MB; + /* This function is called on exiting minibuffer, whether normally or not, and it restores the current window, buffer, etc. */ @@ -1127,20 +1129,72 @@ read_minibuf_unwind (void) away from the expired minibuffer window, both in the current minibuffer's frame and the original calling frame. */ choose_minibuf_frame (); - if (!EQ (WINDOW_FRAME (XWINDOW (minibuf_window)), calling_frame)) - { - Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); - /* PREV can be on a different frame when we have a minibuffer only - frame, the other frame's minibuffer window is MINIBUF_WINDOW, - and its "focus window" is also MINIBUF_WINDOW. */ - if (!EQ (prev, minibuf_window) - && EQ (WINDOW_FRAME (XWINDOW (prev)), - WINDOW_FRAME (XWINDOW (minibuf_window)))) - Fset_frame_selected_window (selected_frame, prev, Qnil); - } - else - Fset_frame_selected_window (calling_frame, calling_window, Qnil); + selected_frame_MB = XWINDOW (minibuf_window)->contents; + if (NILP (XWINDOW (minibuf_window)->prev_buffers)) + { + if (!EQ (WINDOW_FRAME (XWINDOW (minibuf_window)), calling_frame)) + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + if (!EQ (prev, minibuf_window) + && EQ (WINDOW_FRAME (XWINDOW (prev)), + WINDOW_FRAME (XWINDOW (minibuf_window)))) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + else + Fset_frame_selected_window (calling_frame, calling_window, Qnil); + } } + +/* Replace the expired minibuffer in whatever frame it is now in with + the next less nested minibuffer in that frame, if any. Otherwise, + replace it with the null minibuffer. MINIBUF_WINDOW is not + changed. */ +static void +minibuffer_unwind (void) +{ + struct frame *f; + struct window *w; + Lisp_Object window, frame, frames; + Lisp_Object entry; + + /* Locate the expired minibuffer. */ + FOR_EACH_FRAME (frames, frame) + { + f = XFRAME (frame); + window = f->minibuffer_window; + w = XWINDOW (window); + if (EQ (w->frame, frame) + && EQ (EQ (frame, selected_frame) + ? selected_frame_MB + : w->contents, + nth_minibuffer (minibuf_level + 1))) + goto found; + } + return; /* expired minibuffer not found */ + + found: + if (FRAME_LIVE_P (f)) + { + /* minibuf_window = sf->minibuffer_window; */ + if (!NILP (w->prev_buffers)) + { + entry = Fcar (w->prev_buffers); + w->prev_buffers = Fcdr (w->prev_buffers); + set_window_buffer (window, Fcar (entry), 0, 0); + Fset_window_start (window, Fcar (Fcdr (entry)), Qnil); + Fset_window_point (window, Fcar (Fcdr (Fcdr (entry)))); + /* set-window-configuration may/will have unselected the + mini-window as the selected window. Restore it. */ + Fset_frame_selected_window (frame, window, Qnil); + } + else + set_window_buffer (window, nth_minibuffer (0), 0, 0); + } +} + \f void @@ -2213,6 +2267,7 @@ syms_of_minibuf (void) { staticpro (&minibuf_prompt); staticpro (&minibuf_save_list); + staticpro (&selected_frame_MB); DEFSYM (Qminibuffer_follows_selected_frame, "minibuffer-follows-selected-frame"); diff --git a/src/window.c b/src/window.c index eb16e2a433..cde53e8059 100644 --- a/src/window.c +++ b/src/window.c @@ -6958,7 +6960,8 @@ the return value is nil. Otherwise the value is t. */) if (BUFFERP (w->contents) && !EQ (w->contents, p->buffer) - && BUFFER_LIVE_P (XBUFFER (p->buffer))) + && BUFFER_LIVE_P (XBUFFER (p->buffer)) + && (NILP (Fminibufferp (p->buffer, Qnil)))) /* If a window we restore gets another buffer, record the window's old buffer. */ call1 (Qrecord_window_buffer, window); diff --git a/src/xdisp.c b/src/xdisp.c index cc0a689ba3..a405d51f80 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -12650,9 +12650,8 @@ gui_consider_frame_title (Lisp_Object frame) mode_line_noprop_buf; then display the title. */ record_unwind_protect (unwind_format_mode_line, format_mode_line_unwind_data - (f, current_buffer, selected_window, false)); + (NULL, current_buffer, Qnil, false)); - Fselect_window (f->selected_window, Qt); set_buffer_internal_1 (XBUFFER (XWINDOW (f->selected_window)->contents)); fmt = FRAME_ICONIFIED_P (f) ? Vicon_title_format : Vframe_title_format; -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-14 19:17 ` Alan Mackenzie @ 2021-03-14 21:23 ` Miha Rihtaršič 2021-03-17 19:32 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Miha Rihtaršič @ 2021-03-14 21:23 UTC (permalink / raw) To: Alan Mackenzie; +Cc: Stefan Monnier, emacs-devel Alan Mackenzie <acm@muc.de> writes: > Hello, Jakanakaevangeli, > > On Sat, Mar 13, 2021 at 21:53:05 +0100, jakanakaevangeli wrote: > > [ .... ] > >> 3) >> In emacs daemon, evaluate >> (run-at-time 5 nil #'make-frame '((window-system . x))) >> and then open a minibuffer and close the last frame. When a new frame >> appears, the same problem as in 1) occurs. > > I think one of the two previous changes fixed this. Sorry, this still doesn't seem to be fixed. Just in case I wasn't clear enough, I'll try to reiterate. When a frame is closed, it's active minibuffer should now be moved to another frame. What I wanted to test then was, what happens if we have only a single frame with a minibuffer and we decide to close it, which is possible with emacs daemon. Immediately, there will be no frames left for the minibuffer to take refuge in and this seems to cause some problems. And two new regressions. These require minibuffer-follows-selected-frame to be set to t. 1) C-x C-f on frame A select frame B select frame A Minibuffer is moved to B, but not back to A. 2) Have two frames open open a minibuffer on a frame close this frame The other frame does have the miniwindow selected, but the minibuffer isn't shown in it. Best. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-14 21:23 ` Miha Rihtaršič @ 2021-03-17 19:32 ` Alan Mackenzie 2021-03-17 19:55 ` Eli Zaretskii 2021-03-21 15:44 ` Miha Rihtaršič 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-17 19:32 UTC (permalink / raw) To: Miha Rihtaršič; +Cc: Stefan Monnier, emacs-devel Hello, Miha. On Sun, Mar 14, 2021 at 22:23:02 +0100, Miha Rihtaršič wrote: > Alan Mackenzie <acm@muc.de> writes: > > Hello, Jakanakaevangeli, > > On Sat, Mar 13, 2021 at 21:53:05 +0100, jakanakaevangeli wrote: > > [ .... ] > >> 3) > >> In emacs daemon, evaluate > >> (run-at-time 5 nil #'make-frame '((window-system . x))) > >> and then open a minibuffer and close the last frame. When a new frame > >> appears, the same problem as in 1) occurs. > > I think one of the two previous changes fixed this. > Sorry, this still doesn't seem to be fixed. Just in case I wasn't clear > enough, I'll try to reiterate. > When a frame is closed, it's active minibuffer should now be moved to > another frame. What I wanted to test then was, what happens if we have > only a single frame with a minibuffer and we decide to close it, which > is possible with emacs daemon. Immediately, there will be no frames left > for the minibuffer to take refuge in and this seems to cause some > problems. My apologies. I hadn't understood what the bug is, and can confirm it's still there. My patch below doesn't address it. This could be difficult to fix. I don't think that clicking on the last frame's close button goes through `delete-frame' - it just closes the program, whether that's emacs or emacsclient. Maybe there's some "close program" hook that could be used to save any stack of open minibuffers. I just don't know the code in this area. At least, not yet. ;-) > And two new regressions. These require minibuffer-follows-selected-frame > to be set to t. Yes. Sorry about these. This time around, I've tried to be more careful and done more testing myself - hence me taking a few days longer than I have up till now. I've found and corrected another ~two bugs myself. > 1) > C-x C-f on frame A > select frame B > select frame A > Minibuffer is moved to B, but not back to A. > 2) > Have two frames open > open a minibuffer on a frame > close this frame > The other frame does have the miniwindow selected, but the > minibuffer isn't shown in it. I think I've corrected these two, now. Here's another patch, this time to be applied on top of the last patch. --- minibuf.20210315.see 2021-03-16 10:36:40.058109894 +0000 +++ minibuf.c 2021-03-17 19:15:33.586176135 +0000 @@ -59,6 +59,12 @@ static Lisp_Object minibuf_prompt; +/* The frame containinug the most recently opened Minibuffer. This is + used only when `minibuffer-follows-selected-frame' is neither nil + nor t. */ + +static Lisp_Object MB_frame; + /* Width of current mini-buffer prompt. Only set after display_line of the line that contains the prompt. */ @@ -182,19 +188,17 @@ void move_minibuffers_onto_frame (struct frame *of, bool for_deletion) { + struct frame *f = XFRAME (selected_frame); + + minibuf_window = f->minibuffer_window; if (!for_deletion && (!minibuf_level || !minibuf_follows_frame ())) return; - if (FRAMEP (selected_frame) - && FRAME_LIVE_P (XFRAME (selected_frame)) - && (for_deletion - || !EQ (minibuf_window, - XFRAME (selected_frame)->minibuffer_window))) + if (FRAME_LIVE_P (f) + && !EQ (f->minibuffer_window, of->minibuffer_window)) { - struct frame *sf = XFRAME (selected_frame); - - if (minibuf_follows_frame () || for_deletion) - zip_minibuffer_stacks (sf->minibuffer_window, - of->minibuffer_window); + zip_minibuffer_stacks (f->minibuffer_window, of->minibuffer_window); + if (for_deletion && EQ (XFRAME (MB_frame), of)) + MB_frame = selected_frame; } } @@ -550,7 +554,6 @@ Lisp_Object histval; Lisp_Object empty_minibuf; - Lisp_Object old_minibuf_window = minibuf_window; specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -632,19 +635,20 @@ mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); if (minibuf_level > 1 - && (!EQ (minibuf_window, old_minibuf_window)) + && !EQ (XWINDOW (XFRAME (selected_frame)->minibuffer_window)->frame, + MB_frame) && minibuf_moves_frame_when_opened () && (!minibuf_follows_frame ())) { - Lisp_Object old_frame = XWINDOW (old_minibuf_window)->frame; - struct frame *of = XFRAME (old_frame); + struct frame *of = XFRAME (MB_frame); - zip_minibuffer_stacks (minibuf_window, old_minibuf_window); - /* The frame's minibuffer can be on a different frame. */ + zip_minibuffer_stacks (minibuf_window, of->minibuffer_window); + /* MB_frame's minibuffer can be on a different frame. */ if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) - Fset_frame_selected_window (old_frame, - Fframe_first_window (old_frame), Qnil); + Fset_frame_selected_window (MB_frame, + Fframe_first_window (MB_frame), Qnil); } + MB_frame = XWINDOW (XFRAME (selected_frame)->minibuffer_window)->frame; if (live_minibuffer_p (XWINDOW (minibuf_window)->contents)) call1 (Qrecord_window_buffer, minibuf_window); @@ -1023,10 +1027,13 @@ safe_run_hooks (Qminibuffer_exit_hook); } -/* This variable preserves the minibuffer in the selected frame across - the call of restore_window_configuration. It should be used only - by `read_minibuf_unwind' and `minibuffer_unwind'. */ -static Lisp_Object selected_frame_MB; +/* This variable records the expired minibuffer's frame between the + calls of `read_minibuf_unwind' and `minibuffer_unwind'. It should + be used only by these two functions. Note that the same search + method for the MB's frame won't always work in `minibuffer_unwind' + because the intervening `restore-window-configuration' will have + changed the buffer in the mini-window. */ +static Lisp_Object exp_MB_frame; /* This function is called on exiting minibuffer, whether normally or not, and it restores the current window, buffer, etc. */ @@ -1038,6 +1045,28 @@ Lisp_Object calling_frame; Lisp_Object calling_window; Lisp_Object future_mini_window; + Lisp_Object saved_selected_frame = selected_frame; + Lisp_Object window, frames; + struct window *w; + struct frame *f; + + /* Locate the expired minibuffer. */ + FOR_EACH_FRAME (frames, exp_MB_frame) + { + f = XFRAME (exp_MB_frame); + window = f->minibuffer_window; + w = XWINDOW (window); + if (EQ (w->frame, exp_MB_frame) + && EQ (w->contents, nth_minibuffer (minibuf_level))) + goto found; + } + return; /* expired minibuffer not found. Maybe we should output an + error, here. */ + + found: + if (!EQ (exp_MB_frame, saved_selected_frame)) + do_switch_frame (exp_MB_frame, 0, 0, Qt); /* This also sets + minibuff_window */ /* To keep things predictable, in case it matters, let's be in the minibuffer when we reset the relevant variables. Don't depend on @@ -1129,7 +1158,6 @@ away from the expired minibuffer window, both in the current minibuffer's frame and the original calling frame. */ choose_minibuf_frame (); - selected_frame_MB = XWINDOW (minibuf_window)->contents; if (NILP (XWINDOW (minibuf_window)->prev_buffers)) { if (!EQ (WINDOW_FRAME (XWINDOW (minibuf_window)), calling_frame)) @@ -1146,36 +1174,26 @@ else Fset_frame_selected_window (calling_frame, calling_window, Qnil); } + + /* Restore the selected frame. */ + if (!EQ (exp_MB_frame, saved_selected_frame)) + do_switch_frame (saved_selected_frame, 0, 0, Qt); } -/* Replace the expired minibuffer in whatever frame it is now in with - the next less nested minibuffer in that frame, if any. Otherwise, - replace it with the null minibuffer. MINIBUF_WINDOW is not - changed. */ +/* Replace the expired minibuffer in frame exp_MB_frame with the next less + nested minibuffer in that frame, if any. Otherwise, replace it + with the null minibuffer. MINIBUF_WINDOW is not changed. */ static void minibuffer_unwind (void) { struct frame *f; struct window *w; - Lisp_Object window, frame, frames; + Lisp_Object window; Lisp_Object entry; - /* Locate the expired minibuffer. */ - FOR_EACH_FRAME (frames, frame) - { - f = XFRAME (frame); - window = f->minibuffer_window; - w = XWINDOW (window); - if (EQ (w->frame, frame) - && EQ (EQ (frame, selected_frame) - ? selected_frame_MB - : w->contents, - nth_minibuffer (minibuf_level + 1))) - goto found; - } - return; /* expired minibuffer not found */ - - found: + f = XFRAME (exp_MB_frame); + window = f->minibuffer_window; + w = XWINDOW (window); if (FRAME_LIVE_P (f)) { /* minibuf_window = sf->minibuffer_window; */ @@ -1188,7 +1206,7 @@ Fset_window_point (window, Fcar (Fcdr (Fcdr (entry)))); /* set-window-configuration may/will have unselected the mini-window as the selected window. Restore it. */ - Fset_frame_selected_window (frame, window, Qnil); + Fset_frame_selected_window (exp_MB_frame, window, Qnil); } else set_window_buffer (window, nth_minibuffer (0), 0, 0); @@ -2267,7 +2285,9 @@ { staticpro (&minibuf_prompt); staticpro (&minibuf_save_list); - staticpro (&selected_frame_MB); + staticpro (&MB_frame); + MB_frame = Qnil; + staticpro (&exp_MB_frame); DEFSYM (Qminibuffer_follows_selected_frame, "minibuffer-follows-selected-frame"); > Best. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-17 19:32 ` Alan Mackenzie @ 2021-03-17 19:55 ` Eli Zaretskii 2021-03-17 20:19 ` Eli Zaretskii 2021-03-21 15:44 ` Miha Rihtaršič 1 sibling, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-17 19:55 UTC (permalink / raw) To: Alan Mackenzie; +Cc: monnier, jakanakaevangeli, emacs-devel > Date: Wed, 17 Mar 2021 19:32:37 +0000 > From: Alan Mackenzie <acm@muc.de> > Cc: Stefan Monnier <monnier@iro.umontreal.ca>, emacs-devel@gnu.org > > > When a frame is closed, it's active minibuffer should now be moved to > > another frame. What I wanted to test then was, what happens if we have > > only a single frame with a minibuffer and we decide to close it, which > > is possible with emacs daemon. Immediately, there will be no frames left > > for the minibuffer to take refuge in and this seems to cause some > > problems. > > My apologies. I hadn't understood what the bug is, and can confirm it's > still there. My patch below doesn't address it. > > This could be difficult to fix. I don't think that clicking on the last > frame's close button goes through `delete-frame' - it just closes the > program, whether that's emacs or emacsclient. I'm not sure I understand completely what you say here, but clicking on the frame's close button does invoke delete-frame, see the handling of DELETE_FRAME_EVENT in keyboard.c. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-17 19:55 ` Eli Zaretskii @ 2021-03-17 20:19 ` Eli Zaretskii 2021-03-18 11:27 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-17 20:19 UTC (permalink / raw) To: acm; +Cc: monnier, jakanakaevangeli, emacs-devel > Date: Wed, 17 Mar 2021 21:55:38 +0200 > From: Eli Zaretskii <eliz@gnu.org> > Cc: monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, emacs-devel@gnu.org > > > This could be difficult to fix. I don't think that clicking on the last > > frame's close button goes through `delete-frame' - it just closes the > > program, whether that's emacs or emacsclient. > > I'm not sure I understand completely what you say here, but clicking > on the frame's close button does invoke delete-frame, see the handling > of DELETE_FRAME_EVENT in keyboard.c. And if you are talking about the last live frame of an Emacs session, then look at handle-delete-frame, which is called when you click that close button: it will call save-buffers-kill-emacs (which has a hook) and then kill-emacs (which also has a hook). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-17 20:19 ` Eli Zaretskii @ 2021-03-18 11:27 ` Alan Mackenzie 2021-03-18 11:46 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-18 11:27 UTC (permalink / raw) To: Eli Zaretskii; +Cc: monnier, jakanakaevangeli, emacs-devel Hello, Eli. On Wed, Mar 17, 2021 at 22:19:10 +0200, Eli Zaretskii wrote: > > Date: Wed, 17 Mar 2021 21:55:38 +0200 > > From: Eli Zaretskii <eliz@gnu.org> > > Cc: monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, emacs-devel@gnu.org > > > This could be difficult to fix. I don't think that clicking on the last > > > frame's close button goes through `delete-frame' - it just closes the > > > program, whether that's emacs or emacsclient. > > I'm not sure I understand completely what you say here, but clicking > > on the frame's close button does invoke delete-frame, see the handling > > of DELETE_FRAME_EVENT in keyboard.c. > And if you are talking about the last live frame of an Emacs session, > then look at handle-delete-frame, which is called when you click that > close button: it will call save-buffers-kill-emacs (which has a hook) > and then kill-emacs (which also has a hook). Thank you, that is very helpful. I was indeed talking about the last live frame, specifically in an emacsclient session when the Emacs daemon is running. The problem that Miha highlighted is that when there is an open minibuffer when that last frame is deleted, that open minibuffer remains in existence, fouling up the next emacsclient session. Maybe such open minibuffers should just be aborted (along with any other recursive edits) when the last frame gets deleted. This would be simpler to code than preserving those minibuffers somewhere, and restoring them at the next emacsclient session. Aborting them also seems more natural, since their contents are unlikely to have any relevance to the next emacsclient session. Incidentally, there doesn't seem to be anything in the Elisp manual about the handling of Emacs daemon sessions. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-18 11:27 ` Alan Mackenzie @ 2021-03-18 11:46 ` Eli Zaretskii 2021-03-18 15:51 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-18 11:46 UTC (permalink / raw) To: Alan Mackenzie, martin rudalics; +Cc: monnier, jakanakaevangeli, emacs-devel > Date: Thu, 18 Mar 2021 11:27:18 +0000 > Cc: monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > The problem that Miha highlighted is that when there is an open > minibuffer when that last frame is deleted, that open minibuffer remains > in existence, fouling up the next emacsclient session. > > Maybe such open minibuffers should just be aborted (along with any other > recursive edits) when the last frame gets deleted. This would be > simpler to code than preserving those minibuffers somewhere, and > restoring them at the next emacsclient session. Aborting them also > seems more natural, since their contents are unlikely to have any > relevance to the next emacsclient session. Martin, can you comment on this, please? > Incidentally, there doesn't seem to be anything in the Elisp manual > about the handling of Emacs daemon sessions. Please be more specific: which aspects of the daemon sessions would you like to be described that aren't already described? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-18 11:46 ` Eli Zaretskii @ 2021-03-18 15:51 ` martin rudalics 2021-03-18 16:58 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-03-18 15:51 UTC (permalink / raw) To: Eli Zaretskii, Alan Mackenzie; +Cc: monnier, jakanakaevangeli, emacs-devel >> The problem that Miha highlighted is that when there is an open >> minibuffer when that last frame is deleted, that open minibuffer remains >> in existence, fouling up the next emacsclient session. >> >> Maybe such open minibuffers should just be aborted (along with any other >> recursive edits) when the last frame gets deleted. This would be >> simpler to code than preserving those minibuffers somewhere, and >> restoring them at the next emacsclient session. Aborting them also >> seems more natural, since their contents are unlikely to have any >> relevance to the next emacsclient session. > > Martin, can you comment on this, please? What about answering questions about unsaved buffers, running processes ... in such a situation? I never use emacsclient so I have no idea how this should behave in practice. But any non-crucial dialog should be aborted IMHO. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-18 15:51 ` martin rudalics @ 2021-03-18 16:58 ` Alan Mackenzie 2021-03-18 18:44 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-18 16:58 UTC (permalink / raw) To: martin rudalics; +Cc: Eli Zaretskii, monnier, jakanakaevangeli, emacs-devel Hello, Martin and Eli. On Thu, Mar 18, 2021 at 16:51:32 +0100, martin rudalics wrote: > >> The problem that Miha highlighted is that when there is an open > >> minibuffer when that last frame is deleted, that open minibuffer remains > >> in existence, fouling up the next emacsclient session. > >> > >> Maybe such open minibuffers should just be aborted (along with any other > >> recursive edits) when the last frame gets deleted. This would be > >> simpler to code than preserving those minibuffers somewhere, and > >> restoring them at the next emacsclient session. Aborting them also > >> seems more natural, since their contents are unlikely to have any > >> relevance to the next emacsclient session. > > > > Martin, can you comment on this, please? > What about answering questions about unsaved buffers, running processes > ... in such a situation? I never use emacsclient so I have no idea how > this should behave in practice. I don't use emacsclient either. But questions about unsaved buffers seem to prevent Emacs terminating until they get answered. The same for running processes (at least, for gdb). > But any non-crucial dialog should be aborted IMHO. Are the any uses of the minibuffer which count as crucial, in this sense? > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-18 16:58 ` Alan Mackenzie @ 2021-03-18 18:44 ` Eli Zaretskii 2021-03-19 11:40 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-18 18:44 UTC (permalink / raw) To: Alan Mackenzie; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel > Date: Thu, 18 Mar 2021 16:58:25 +0000 > Cc: Eli Zaretskii <eliz@gnu.org>, monnier@iro.umontreal.ca, > jakanakaevangeli@chiru.no, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > >> Maybe such open minibuffers should just be aborted (along with any other > > >> recursive edits) when the last frame gets deleted. This would be > > >> simpler to code than preserving those minibuffers somewhere, and > > >> restoring them at the next emacsclient session. Aborting them also > > >> seems more natural, since their contents are unlikely to have any > > >> relevance to the next emacsclient session. > > > > > > Martin, can you comment on this, please? > > > What about answering questions about unsaved buffers, running processes > > ... in such a situation? I never use emacsclient so I have no idea how > > this should behave in practice. > > I don't use emacsclient either. But questions about unsaved buffers > seem to prevent Emacs terminating until they get answered. The same for > running processes (at least, for gdb). Are we shutting down Emacs, or are we returning to a headless daemon state? If the former, we need to ask all those questions before we shut down Emacs. Why is it a problem to ask them? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-18 18:44 ` Eli Zaretskii @ 2021-03-19 11:40 ` Alan Mackenzie 2021-03-19 12:33 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-19 11:40 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel Hello, Eli. On Thu, Mar 18, 2021 at 20:44:12 +0200, Eli Zaretskii wrote: > > Date: Thu, 18 Mar 2021 16:58:25 +0000 > > Cc: Eli Zaretskii <eliz@gnu.org>, monnier@iro.umontreal.ca, > > jakanakaevangeli@chiru.no, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > >> Maybe such open minibuffers should just be aborted (along with any other > > > >> recursive edits) when the last frame gets deleted. This would be > > > >> simpler to code than preserving those minibuffers somewhere, and > > > >> restoring them at the next emacsclient session. Aborting them also > > > >> seems more natural, since their contents are unlikely to have any > > > >> relevance to the next emacsclient session. > > > > Martin, can you comment on this, please? > > > What about answering questions about unsaved buffers, running processes > > > ... in such a situation? I never use emacsclient so I have no idea how > > > this should behave in practice. > > I don't use emacsclient either. But questions about unsaved buffers > > seem to prevent Emacs terminating until they get answered. The same for > > running processes (at least, for gdb). > Are we shutting down Emacs, or are we returning to a headless daemon > state? Returning to a headless daemon state. > If the former, we need to ask all those questions before we shut down > Emacs. Why is it a problem to ask them? Sorry, I didn't mean to imply these things were problems; it was just an observation, comparing the question about unsaved buffers with the lack of a question on open minibuffers. In the following, I'm using my not yet committed version of minibuf.c, etc.: When I now try Miha's recipe: (In last frame of emacsclient:) M-: (run-at-time 10 nil #'make-frame '((window-system . x))) C-x C-f foo Close the frame by clicking on its close button <Wait the remainder of the 10 seconds> , the new frame comes up _with_ the minibuffer visible. I don't understand how the mini window survived with its contents, and it worries me. The minibuffers are stored in the frames' ->minibuffer_window components, as well as existing as buffers in their own right. When the last frame is deleted, I would expect the MB to survive, but NOT to be put back into the mini window of the newly created frame. Could it be that the last frame isn't actually deleted from the Emacs daemon when clicking on its close button? Merely that the emacsclient program is closed, leaving its window inactive/invisible/whatever? In fact running M-: (frame-list) shows two frames, one of which is called " *Minibuf-1*", though it isn't displayed in X-Windows. This is the sort of thing I would like to be able to read about in the Elisp manual, along with basic questions like "How can one test whether one is in an emacsclient session rather than an ordinary Emacs?". -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-19 11:40 ` Alan Mackenzie @ 2021-03-19 12:33 ` Eli Zaretskii 2021-03-19 15:35 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-19 12:33 UTC (permalink / raw) To: Alan Mackenzie; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel > Date: Fri, 19 Mar 2021 11:40:52 +0000 > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > > What about answering questions about unsaved buffers, running processes > > > > ... in such a situation? I never use emacsclient so I have no idea how > > > > this should behave in practice. > > > > I don't use emacsclient either. But questions about unsaved buffers > > > seem to prevent Emacs terminating until they get answered. The same for > > > running processes (at least, for gdb). > > > Are we shutting down Emacs, or are we returning to a headless daemon > > state? > > Returning to a headless daemon state. Then I think there's no need to ask those questions at all. Emacs is not going away, so it's okay to leave unsaved edits in the session that continues to run. > When I now try Miha's recipe: > (In last frame of emacsclient:) > M-: (run-at-time 10 nil #'make-frame '((window-system . x))) > C-x C-f foo > Close the frame by clicking on its close button > <Wait the remainder of the 10 seconds> > > , the new frame comes up _with_ the minibuffer visible. I don't > understand how the mini window survived with its contents, and it > worries me. Why does it worry you? You've asked Emacs to create a frame, and a frame by default has a mini-window. Right? > Could it be that the last frame isn't actually deleted from the Emacs > daemon when clicking on its close button? I think indeed that's what happens, because otherwise clicking there would have shut down Emacs -- which it doesn't in the daemon case. > This is the sort of thing I would like to be able to read about in the > Elisp manual You expect the ELisp manual to describe the internals to this detail? That's stuff for comments in the code (adding which will be very welcome), not for the ELisp reference. > along with basic questions like "How can one test whether one is in > an emacsclient session rather than an ordinary Emacs?". This seems to be a matter of improving indexing: see the variable mode-line-client and how it is computed. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-19 12:33 ` Eli Zaretskii @ 2021-03-19 15:35 ` Alan Mackenzie 2021-03-19 15:59 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-19 15:35 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel Hello, Eli. On Fri, Mar 19, 2021 at 14:33:20 +0200, Eli Zaretskii wrote: > > Date: Fri, 19 Mar 2021 11:40:52 +0000 > > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > > emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > > What about answering questions about unsaved buffers, running processes > > > > > ... in such a situation? I never use emacsclient so I have no idea how > > > > > this should behave in practice. > > > > I don't use emacsclient either. But questions about unsaved buffers > > > > seem to prevent Emacs terminating until they get answered. The same for > > > > running processes (at least, for gdb). > > > Are we shutting down Emacs, or are we returning to a headless daemon > > > state? > > Returning to a headless daemon state. > Then I think there's no need to ask those questions at all. Emacs is > not going away, so it's okay to leave unsaved edits in the session > that continues to run. Yes, I think that's right. > > When I now try Miha's recipe: > > (In last frame of emacsclient:) > > M-: (run-at-time 10 nil #'make-frame '((window-system . x))) > > C-x C-f foo > > Close the frame by clicking on its close button > > <Wait the remainder of the 10 seconds> > > , the new frame comes up _with_ the minibuffer visible. I don't > > understand how the mini window survived with its contents, and it > > worries me. > Why does it worry you? You've asked Emacs to create a frame, and a > frame by default has a mini-window. Right? I understand it a lot better now. The new mini-window gets the minibuffers only because I had minibuffer-follows-selected-frame set to t. With either of the other values, the minibuffers remain on the old inaccessible frame. This is not good. > > Could it be that the last frame isn't actually deleted from the Emacs > > daemon when clicking on its close button? > I think indeed that's what happens, because otherwise clicking there > would have shut down Emacs -- which it doesn't in the daemon case. Yes, this is the case. I would dearly like to find a way of determining if this last frame is still visible, or iconified, or whatever, as opposed to inaccessible. The old frame in the above recipe was a -nw frame, effectively a TTY, and `frame-visible-p' returns t for it, even though it is not even displayable (it's containing shell has gone). > > This is the sort of thing I would like to be able to read about in the > > Elisp manual > You expect the ELisp manual to describe the internals to this detail? > That's stuff for comments in the code (adding which will be very > welcome), not for the ELisp reference. Hmm. There is effectively nothing about the effect of Emacs as a daemon in Elisp. > > along with basic questions like "How can one test whether one is in > > an emacsclient session rather than an ordinary Emacs?". > This seems to be a matter of improving indexing: see the variable > mode-line-client and how it is computed. OK, thanks. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-19 15:35 ` Alan Mackenzie @ 2021-03-19 15:59 ` Eli Zaretskii 2021-03-20 10:28 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-19 15:59 UTC (permalink / raw) To: Alan Mackenzie; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel > Date: Fri, 19 Mar 2021 15:35:13 +0000 > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > , the new frame comes up _with_ the minibuffer visible. I don't > > > understand how the mini window survived with its contents, and it > > > worries me. > > > Why does it worry you? You've asked Emacs to create a frame, and a > > frame by default has a mini-window. Right? > > I understand it a lot better now. The new mini-window gets the > minibuffers only because I had minibuffer-follows-selected-frame set to > t. With either of the other values, the minibuffers remain on the old > inaccessible frame. This is not good. Can't you move them from those inaccessible frames to this one? > > > Could it be that the last frame isn't actually deleted from the Emacs > > > daemon when clicking on its close button? > > > I think indeed that's what happens, because otherwise clicking there > > would have shut down Emacs -- which it doesn't in the daemon case. > > Yes, this is the case. I would dearly like to find a way of determining > if this last frame is still visible, or iconified, or whatever, as > opposed to inaccessible. I think this is the initial frame that exists in every Emacs session when it starts, except that in the daemon we don't delete it. It's a non-GUI frame which exists just to keep code that expects some frame to exist happy. > The old frame in the above recipe was a -nw frame, effectively a > TTY, and `frame-visible-p' returns t for it, even though it is not > even displayable (it's containing shell has gone). We don't have a means of knowing whether a TTY frame is displayed, it conceptually always is. > Hmm. There is effectively nothing about the effect of Emacs as a daemon > in Elisp. There's nothing special about that, just some implementation details. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-19 15:59 ` Eli Zaretskii @ 2021-03-20 10:28 ` Alan Mackenzie 2021-03-20 10:49 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-20 10:28 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel Hello, Eli. On Fri, Mar 19, 2021 at 17:59:45 +0200, Eli Zaretskii wrote: > > Date: Fri, 19 Mar 2021 15:35:13 +0000 > > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > > emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > , the new frame comes up _with_ the minibuffer visible. I don't > > > > understand how the mini window survived with its contents, and it > > > > worries me. > > > Why does it worry you? You've asked Emacs to create a frame, and a > > > frame by default has a mini-window. Right? > > I understand it a lot better now. The new mini-window gets the > > minibuffers only because I had minibuffer-follows-selected-frame set to > > t. With either of the other values, the minibuffers remain on the old > > inaccessible frame. This is not good. > Can't you move them from those inaccessible frames to this one? The actual moving of the minibuffers isn't the problem .... > > > > Could it be that the last frame isn't actually deleted from the Emacs > > > > daemon when clicking on its close button? > > > I think indeed that's what happens, because otherwise clicking there > > > would have shut down Emacs -- which it doesn't in the daemon case. > > Yes, this is the case. I would dearly like to find a way of determining > > if this last frame is still visible, or iconified, or whatever, as > > opposed to inaccessible. > I think this is the initial frame that exists in every Emacs session > when it starts, except that in the daemon we don't delete it. It's a > non-GUI frame which exists just to keep code that expects some frame > to exist happy. This initial frame gets used as a normal frame when one calls $ emacsclient foo for the first time. It stays in use until one clicks on its close button (I think this is actually the close button of the containing xterm). When a new GUI frame is brought up by M-: (run-at-time 10 nil #'make-frame '((window-system . x))) , the initial frame may or may not have been "closed" by clicking its close button. I think you're telling me that it's not possible to distinguish these two cases. If so, that's surely a defect in Emacs. > > The old frame in the above recipe was a -nw frame, effectively a > > TTY, and `frame-visible-p' returns t for it, even though it is not > > even displayable (it's containing shell has gone). > We don't have a means of knowing whether a TTY frame is displayed, it > conceptually always is. Perhaps we should create some means of knowing this? Maybe in handle-delete-frame, where Emacs has determined there is only one frame left, before calling save-buffers-kill-emacs we could mark the last frame's f->visible to "not visible". Or maybe set up a special non-zero dummy value for f->terminal. Or something like that. > > Hmm. There is effectively nothing about the effect of Emacs as a daemon > > in Elisp. > There's nothing special about that, just some implementation details. OK. I'm not sure I agree, but it's not really a big point. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 10:28 ` Alan Mackenzie @ 2021-03-20 10:49 ` Eli Zaretskii 2021-03-20 12:24 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-20 10:49 UTC (permalink / raw) To: Alan Mackenzie; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel > Date: Sat, 20 Mar 2021 10:28:26 +0000 > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > I think this is the initial frame that exists in every Emacs session > > when it starts, except that in the daemon we don't delete it. It's a > > non-GUI frame which exists just to keep code that expects some frame > > to exist happy. > > This initial frame gets used as a normal frame when one calls > > $ emacsclient foo > > for the first time. Are you sure? ISTR that we create a new frame on the terminal from which emacsclient was invoked. The initial frame is never used except at startup and when the daemon should do something while it has no clients. > It stays in use until one clicks on its close > button (I think this is actually the close button of the containing > xterm). When a new GUI frame is brought up by > > M-: (run-at-time 10 nil #'make-frame '((window-system . x))) > > , the initial frame may or may not have been "closed" by clicking its > close button. I think you're telling me that it's not possible to > distinguish these two cases. If so, that's surely a defect in Emacs. No, I'm saying that the initial frame is never deleted. At least that's my recollection. > > We don't have a means of knowing whether a TTY frame is displayed, it > > conceptually always is. > > Perhaps we should create some means of knowing this? Maybe in > handle-delete-frame, where Emacs has determined there is only one frame > left, before calling save-buffers-kill-emacs we could mark the last > frame's f->visible to "not visible". You mean, for a frame that is about to be deleted anyway? what purpose would that serve? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 10:49 ` Eli Zaretskii @ 2021-03-20 12:24 ` Alan Mackenzie 2021-03-20 12:49 ` Miha Rihtaršič ` (2 more replies) 0 siblings, 3 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-20 12:24 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel Hello, Eli. On Sat, Mar 20, 2021 at 12:49:05 +0200, Eli Zaretskii wrote: > > Date: Sat, 20 Mar 2021 10:28:26 +0000 > > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > > emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > I think this is the initial frame that exists in every Emacs session > > > when it starts, except that in the daemon we don't delete it. It's a > > > non-GUI frame which exists just to keep code that expects some frame > > > to exist happy. > > This initial frame gets used as a normal frame when one calls > > $ emacsclient foo > > for the first time. > Are you sure? ISTR that we create a new frame on the terminal from > which emacsclient was invoked. The initial frame is never used except > at startup and when the daemon should do something while it has no > clients. Apologies, I was wrong. On the first emacsclient call, a second frame gets created. The initial frame is marked in some way so that, e.g., C-x 5 o doesn't see it. > > It stays in use until one clicks on its close > > button (I think this is actually the close button of the containing > > xterm). When a new GUI frame is brought up by > > M-: (run-at-time 10 nil #'make-frame '((window-system . x))) > > , the initial frame may or may not have been "closed" by clicking its > > close button. I think you're telling me that it's not possible to > > distinguish these two cases. If so, that's surely a defect in Emacs. > No, I'm saying that the initial frame is never deleted. At least > that's my recollection. So, when a new frame is created (possibly with emacsclient), and there are now exactly two frames, we want to copy any minibuffers from the other frame to the new one when that other frame is the initial frame. I still can't find a way of identifying the initial frame for sure - it lacks a 'display frame parameter, but so do ordinary frames on a tty. It lacks a 'client frame parameter, but so do ordinary frames when Emacs is started normally and M-x server-start invoked. Would it, perhaps, be useful to add another frame parameter 'initial-frame to identify this frame? [ .... ] -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 12:24 ` Alan Mackenzie @ 2021-03-20 12:49 ` Miha Rihtaršič 2021-03-20 13:59 ` Stefan Monnier 2021-03-21 10:30 ` Alan Mackenzie 2021-03-20 12:50 ` Eli Zaretskii 2021-03-20 13:55 ` Stefan Monnier 2 siblings, 2 replies; 252+ messages in thread From: Miha Rihtaršič @ 2021-03-20 12:49 UTC (permalink / raw) To: Alan Mackenzie, Eli Zaretskii; +Cc: emacs-devel Alan Mackenzie <acm@muc.de> writes: >> No, I'm saying that the initial frame is never deleted. At least >> that's my recollection. > > So, when a new frame is created (possibly with emacsclient), and there > are now exactly two frames, we want to copy any minibuffers from the > other frame to the new one when that other frame is the initial frame. Just giving a heads up that trouble may arise when moving a minibuffer from one terminal to another (from tty to X for example). This is judging from the following comment that I stumbled upon when reading function `server-goto-toplevel': ;; We're inside a minibuffer already, so if the emacs-client is trying ;; to open a frame on a new display, we might end up with an unusable ;; frame because input from that display will be blocked (until exiting ;; the minibuffer). Better exit this minibuffer right away. `emacsclient ~/foo' causes a throw to top-level in most cases before spawning a new frame to avoid some trouble, but I'm not sure if this trouble applies for our case. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 12:49 ` Miha Rihtaršič @ 2021-03-20 13:59 ` Stefan Monnier 2021-03-21 10:30 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2021-03-20 13:59 UTC (permalink / raw) To: Miha Rihtaršič; +Cc: Alan Mackenzie, Eli Zaretskii, emacs-devel > Just giving a heads up that trouble may arise when moving a minibuffer > from one terminal to another (from tty to X for example). Actually that comment from server.el is not about the difficulty of moving minibuffers but about the problem caused by the lack of minibuffer movement: if a minibuffer is active in terminal 1, then Emacs is in a special mode where input from other terminals is ignored, so an emacsclient opening a frame on a new terminal will display a "dead" frame (in the sense that it doesn't respond to user input until the minibuffer in terminal 1 is exited). Alan's recent changes could actually be used to improve that behavior of server.el by moving the active minibuffer(s) to the new frame instead of aborting those minibuffers. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 12:49 ` Miha Rihtaršič 2021-03-20 13:59 ` Stefan Monnier @ 2021-03-21 10:30 ` Alan Mackenzie 2021-03-21 10:38 ` Eli Zaretskii 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-03-21 10:30 UTC (permalink / raw) To: Miha Rihtaršič; +Cc: Eli Zaretskii, emacs-devel Hello, Miha. On Sat, Mar 20, 2021 at 13:49:19 +0100, Miha Rihtaršič wrote: > Alan Mackenzie <acm@muc.de> writes: > >> No, I'm saying that the initial frame is never deleted. At least > >> that's my recollection. > > So, when a new frame is created (possibly with emacsclient), and there > > are now exactly two frames, we want to copy any minibuffers from the > > other frame to the new one when that other frame is the initial frame. > Just giving a heads up that trouble may arise when moving a minibuffer > from one terminal to another (from tty to X for example). > This is judging from the following comment that I stumbled upon when > reading function `server-goto-toplevel': Thanks. > ;; We're inside a minibuffer already, so if the emacs-client is trying > ;; to open a frame on a new display, we might end up with an unusable > ;; frame because input from that display will be blocked (until exiting > ;; the minibuffer). Better exit this minibuffer right away. That looks like a bug which ought to be fixed, but I've no idea where to start looking for the problem. > `emacsclient ~/foo' causes a throw to top-level in most cases before > spawning a new frame to avoid some trouble, but I'm not sure if this > trouble applies for our case. When starting emacsclient, I've seen minibuffers being preserved, but I think I've also seen them being aborted. By trouble, I think you mean that described in the comment above. On a slightly different note, do you see any reason not to commit the latest state of src/minibuf.c etc., as amended by my patch from Wednesday? -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 10:30 ` Alan Mackenzie @ 2021-03-21 10:38 ` Eli Zaretskii 2021-03-21 10:40 ` Eli Zaretskii 2021-03-21 14:49 ` Alan Mackenzie 0 siblings, 2 replies; 252+ messages in thread From: Eli Zaretskii @ 2021-03-21 10:38 UTC (permalink / raw) To: Alan Mackenzie; +Cc: jakanakaevangeli, emacs-devel > Date: Sun, 21 Mar 2021 10:30:39 +0000 > From: Alan Mackenzie <acm@muc.de> > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > > > ;; We're inside a minibuffer already, so if the emacs-client is trying > > ;; to open a frame on a new display, we might end up with an unusable > > ;; frame because input from that display will be blocked (until exiting > > ;; the minibuffer). Better exit this minibuffer right away. > > That looks like a bug which ought to be fixed, but I've no idea where to > start looking for the problem. It's a "feature" we only allow a single frame to be in the "input" state at any given time. > > `emacsclient ~/foo' causes a throw to top-level in most cases before > > spawning a new frame to avoid some trouble, but I'm not sure if this > > trouble applies for our case. > > When starting emacsclient, I've seen minibuffers being preserved, but I > think I've also seen them being aborted. By trouble, I think you mean > that described in the comment above. I confess that I don't have a clear idea of what is the problem you are trying to solve in the context of this discussion. Why do you want to "move" the minibuffer when emacsclient starts a new frame (or is it when it closes a frame?), and what does "move" mean in this context? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 10:38 ` Eli Zaretskii @ 2021-03-21 10:40 ` Eli Zaretskii 2021-03-21 14:49 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2021-03-21 10:40 UTC (permalink / raw) To: acm; +Cc: jakanakaevangeli, emacs-devel > Date: Sun, 21 Mar 2021 12:38:34 +0200 > From: Eli Zaretskii <eliz@gnu.org> > Cc: jakanakaevangeli@chiru.no, emacs-devel@gnu.org > > > Date: Sun, 21 Mar 2021 10:30:39 +0000 > > From: Alan Mackenzie <acm@muc.de> > > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > > > > > ;; We're inside a minibuffer already, so if the emacs-client is trying > > > ;; to open a frame on a new display, we might end up with an unusable > > > ;; frame because input from that display will be blocked (until exiting > > > ;; the minibuffer). Better exit this minibuffer right away. > > > > That looks like a bug which ought to be fixed, but I've no idea where to > > start looking for the problem. > > It's a "feature" we only allow a single frame to be in the "input" > state at any given time. Sorry, my sloppy wording made this misleading. Instead of "frame" please read "display" or "terminal". ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 10:38 ` Eli Zaretskii 2021-03-21 10:40 ` Eli Zaretskii @ 2021-03-21 14:49 ` Alan Mackenzie 2021-03-21 15:00 ` Stefan Monnier 2021-03-21 15:43 ` Eli Zaretskii 1 sibling, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-21 14:49 UTC (permalink / raw) To: Eli Zaretskii; +Cc: jakanakaevangeli, emacs-devel [ "frame" replaced by "terminal" where appropriate ] Hello, Eli. On Sun, Mar 21, 2021 at 12:38:34 +0200, Eli Zaretskii wrote: > > Date: Sun, 21 Mar 2021 10:30:39 +0000 > > From: Alan Mackenzie <acm@muc.de> > > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > > > ;; We're inside a minibuffer already, so if the emacs-client is trying > > > ;; to open a frame on a new display, we might end up with an unusable > > > ;; frame because input from that display will be blocked (until exiting > > > ;; the minibuffer). Better exit this minibuffer right away. > > That looks like a bug which ought to be fixed, but I've no idea where to > > start looking for the problem. > It's a "feature" we only allow a single terminal to be in the "input" > state at any given time. Er, OK. There surely must be good reasons for this. > > > `emacsclient ~/foo' causes a throw to top-level in most cases before > > > spawning a new frame to avoid some trouble, but I'm not sure if this > > > trouble applies for our case. > > When starting emacsclient, I've seen minibuffers being preserved, but I > > think I've also seen them being aborted. By trouble, I think you mean > > that described in the comment above. > I confess that I don't have a clear idea of what is the problem you > are trying to solve in the context of this discussion. Why do you > want to "move" the minibuffer when emacsclient starts a new frame (or > is it when it closes a frame?), and what does "move" mean in this > context? The following description includes things which aren't yet committed. "Move" means to move the minibuffer from frame F1 to frame F2, as for example, C-x 5 o requires when minibuffer-follows-selected-frame is t (the default). When enable-recursive-minibuffers is t, there can be a "stack" of minibuffers on a frame. The entire stack gets moved. The top minibuffer in the stack is at f->minibuffer_window, any lower elements are stored in w->prev_buffers, where w is XWINDOW (f->minibuffer_window). It is also required to move minibuffers when the frame containing them gets deleted. Otherwise the minibuffers would continue to be active, yet be inaccessible from any frame - this would be undesirable. When the (second) last frame in a --daemon Emacs gets deleted, any minibuffers it contains get moved to the initial frame. When calling emacsclient opens a new frame, these minibuffers need to be moved onto that new frame (or else be aborted, somehow). The problem I'm trying to solve here is to understand what happens when emacsclient opens a frame on a different terminal from where emacs --daemon was started, when there are active minibuffers on the original terminal. What would be nice would be for these minibuffers to be moved onto the new frame (when minibuffer-follows-selected-frame is t) or left on the other non-initial frame (otherwise). It appears, from Miha's observation yesterday, that any active minibuffers would get aborted in this case, to prevent the old frame blocking the new one. It may well be that further work in this area isn't worthwhile - just how often is somebody going to create a frame in a different terminal whilst a minibuffer is active, anyway? -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 14:49 ` Alan Mackenzie @ 2021-03-21 15:00 ` Stefan Monnier 2021-03-21 15:43 ` Eli Zaretskii 1 sibling, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2021-03-21 15:00 UTC (permalink / raw) To: Alan Mackenzie; +Cc: Eli Zaretskii, jakanakaevangeli, emacs-devel > The problem I'm trying to solve here is to understand what happens when > emacsclient opens a frame on a different terminal from where emacs > --daemon was started, when there are active minibuffers on the original > terminal. What would be nice would be for these minibuffers to be moved > onto the new frame (when minibuffer-follows-selected-frame is t) or left > on the other non-initial frame (otherwise). Leaving them on the other non-initial frame is OK if the new frame is on the same display. If it is on another display it can be a problem because that other display may be inaccessible to the user at that moment. That's why I added `server-goto-toplevel`. > It appears, from Miha's observation yesterday, that any active > minibuffers would get aborted in this case, to prevent the old frame > blocking the new one. Of course, if you could move the active minibuffers to the new frame (when minibuffer-follows-selected-frame is t), then we could change `server-goto-toplevel` so as not to abort them. > It may well be that further work in this area isn't worthwhile - just how > often is somebody going to create a frame in a different terminal whilst > a minibuffer is active, anyway? I implemented `server-goto-toplevel` because it happened to me quite a few times ;-) Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 14:49 ` Alan Mackenzie 2021-03-21 15:00 ` Stefan Monnier @ 2021-03-21 15:43 ` Eli Zaretskii 2021-03-21 16:17 ` Michael Welsh Duggan 2021-03-21 16:37 ` Alan Mackenzie 1 sibling, 2 replies; 252+ messages in thread From: Eli Zaretskii @ 2021-03-21 15:43 UTC (permalink / raw) To: Alan Mackenzie; +Cc: jakanakaevangeli, emacs-devel > Date: Sun, 21 Mar 2021 14:49:56 +0000 > Cc: jakanakaevangeli@chiru.no, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > It's a "feature" we only allow a single terminal to be in the "input" > > state at any given time. > > Er, OK. There surely must be good reasons for this. The "good reasons" are that we are unable to handle input from two or more keyboards: they will mix up into a single garbled stream of input events. That's because there's only one channel through which Emacs reads input -- the single event queue we have in keyboard.c. > The following description includes things which aren't yet committed. > > "Move" means to move the minibuffer from frame F1 to frame F2, as for > example, C-x 5 o requires when minibuffer-follows-selected-frame is t > (the default). When enable-recursive-minibuffers is t, there can be a > "stack" of minibuffers on a frame. The entire stack gets moved. The top > minibuffer in the stack is at f->minibuffer_window, any lower elements > are stored in w->prev_buffers, where w is XWINDOW (f->minibuffer_window). > > It is also required to move minibuffers when the frame containing them > gets deleted. Otherwise the minibuffers would continue to be active, yet > be inaccessible from any frame - this would be undesirable. > > When the (second) last frame in a --daemon Emacs gets deleted, any > minibuffers it contains get moved to the initial frame. When calling > emacsclient opens a new frame, these minibuffers need to be moved onto > that new frame (or else be aborted, somehow). AFAIU, this just means copying the data that describes the stack of the minibuffers, is that right? If so, what is left on the frame that is the source of that: a single empty minibuffer? or the entire original stack? or something else? > The problem I'm trying to solve here is to understand what happens when > emacsclient opens a frame on a different terminal from where emacs > --daemon was started, when there are active minibuffers on the original > terminal. What would be nice would be for these minibuffers to be moved > onto the new frame (when minibuffer-follows-selected-frame is t) or left > on the other non-initial frame (otherwise). It appears, from Miha's > observation yesterday, that any active minibuffers would get aborted in > this case, to prevent the old frame blocking the new one. And what's wrong with aborting the active minibuffer in this case? isn't that what the user wants? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 15:43 ` Eli Zaretskii @ 2021-03-21 16:17 ` Michael Welsh Duggan 2021-03-21 16:37 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Michael Welsh Duggan @ 2021-03-21 16:17 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Alan Mackenzie, jakanakaevangeli, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> The problem I'm trying to solve here is to understand what happens when >> emacsclient opens a frame on a different terminal from where emacs >> --daemon was started, when there are active minibuffers on the original >> terminal. What would be nice would be for these minibuffers to be moved >> onto the new frame (when minibuffer-follows-selected-frame is t) or left >> on the other non-initial frame (otherwise). It appears, from Miha's >> observation yesterday, that any active minibuffers would get aborted in >> this case, to prevent the old frame blocking the new one. > > And what's wrong with aborting the active minibuffer in this case? > isn't that what the user wants? I see two use cases here. 1) I've purposefully killed that last active frame. In this case any remaining open minibuffers are probably a mistake. 2) My connection to a remote machine went down, taking my last frame with it. I reconnect and I might want my minibuffer state back. I'm personally of the opinion that just aborting the minibuffers is good enough. Minibuffers should not generally be long-lived with state anyway. Under almost all circumstances, I believe any lost minibuffer state should be recoverable manually. -- Michael Welsh Duggan (md5i@md5i.com) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 15:43 ` Eli Zaretskii 2021-03-21 16:17 ` Michael Welsh Duggan @ 2021-03-21 16:37 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-21 16:37 UTC (permalink / raw) To: Eli Zaretskii; +Cc: jakanakaevangeli, emacs-devel Hello, Eli. On Sun, Mar 21, 2021 at 17:43:02 +0200, Eli Zaretskii wrote: > > Date: Sun, 21 Mar 2021 14:49:56 +0000 > > Cc: jakanakaevangeli@chiru.no, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > It's a "feature" we only allow a single terminal to be in the "input" > > > state at any given time. > > Er, OK. There surely must be good reasons for this. > The "good reasons" are that we are unable to handle input from two or > more keyboards: they will mix up into a single garbled stream of input > events. That's because there's only one channel through which Emacs > reads input -- the single event queue we have in keyboard.c. Thanks, that's very clear. > > The following description includes things which aren't yet committed. > > "Move" means to move the minibuffer from frame F1 to frame F2, as for > > example, C-x 5 o requires when minibuffer-follows-selected-frame is t > > (the default). When enable-recursive-minibuffers is t, there can be a > > "stack" of minibuffers on a frame. The entire stack gets moved. The top > > minibuffer in the stack is at f->minibuffer_window, any lower elements > > are stored in w->prev_buffers, where w is XWINDOW (f->minibuffer_window). > > It is also required to move minibuffers when the frame containing them > > gets deleted. Otherwise the minibuffers would continue to be active, yet > > be inaccessible from any frame - this would be undesirable. > > When the (second) last frame in a --daemon Emacs gets deleted, any > > minibuffers it contains get moved to the initial frame. When calling > > emacsclient opens a new frame, these minibuffers need to be moved onto > > that new frame (or else be aborted, somehow). > AFAIU, this just means copying the data that describes the stack of > the minibuffers, is that right? Not quite: if there're also minibuffers in the destination frame (with minibuffer-follows-selected-frame set to nil), the two minibuffer stacks get combined in the destination. > If so, what is left on the frame that is the source of that: a single > empty minibuffer? or the entire original stack? or something else? What is left on the source frame is a single minibuffer, " *Minibuf-0*", which functions as the null minibuffer. > > The problem I'm trying to solve here is to understand what happens when > > emacsclient opens a frame on a different terminal from where emacs > > --daemon was started, when there are active minibuffers on the original > > terminal. What would be nice would be for these minibuffers to be moved > > onto the new frame (when minibuffer-follows-selected-frame is t) or left > > on the other non-initial frame (otherwise). It appears, from Miha's > > observation yesterday, that any active minibuffers would get aborted in > > this case, to prevent the old frame blocking the new one. > And what's wrong with aborting the active minibuffer in this case? I don't think there's all that much wrong with aborting it. It is a little inconsistent with creating a new emacsclient frame on the same terminal - here, any existing minibuffers survive. > isn't that what the user wants? I honestly don't know. But as long as the code ends up doing something consistent and reasonable (and this aborting the minibuffers _is_ reasonable), I don't think it's worth spending too much time on. Given how there's just one keyboard input stream, it would be a LOT of work to make that one input stream per terminal. We surely don't really need this. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 12:24 ` Alan Mackenzie 2021-03-20 12:49 ` Miha Rihtaršič @ 2021-03-20 12:50 ` Eli Zaretskii 2021-03-20 13:51 ` Alan Mackenzie 2021-03-20 13:55 ` Stefan Monnier 2 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2021-03-20 12:50 UTC (permalink / raw) To: Alan Mackenzie; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel > Date: Sat, 20 Mar 2021 12:24:55 +0000 > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > So, when a new frame is created (possibly with emacsclient), and there > are now exactly two frames, we want to copy any minibuffers from the > other frame to the new one when that other frame is the initial frame. > > I still can't find a way of identifying the initial frame for sure - it > lacks a 'display frame parameter, but so do ordinary frames on a tty. It > lacks a 'client frame parameter, but so do ordinary frames when Emacs is > started normally and M-x server-start invoked. > > Would it, perhaps, be useful to add another frame parameter > 'initial-frame to identify this frame? Can you tell how you are trying to "identify this frame"? I'm afraid I don't see what is your difficulties in this regard. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 12:50 ` Eli Zaretskii @ 2021-03-20 13:51 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-20 13:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, monnier, jakanakaevangeli, emacs-devel Hello, Eli. On Sat, Mar 20, 2021 at 14:50:19 +0200, Eli Zaretskii wrote: > > Date: Sat, 20 Mar 2021 12:24:55 +0000 > > Cc: rudalics@gmx.at, monnier@iro.umontreal.ca, jakanakaevangeli@chiru.no, > > emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > So, when a new frame is created (possibly with emacsclient), and there > > are now exactly two frames, we want to copy any minibuffers from the > > other frame to the new one when that other frame is the initial frame. > > I still can't find a way of identifying the initial frame for sure - it > > lacks a 'display frame parameter, but so do ordinary frames on a tty. It > > lacks a 'client frame parameter, but so do ordinary frames when Emacs is > > started normally and M-x server-start invoked. > > Would it, perhaps, be useful to add another frame parameter > > 'initial-frame to identify this frame? > Can you tell how you are trying to "identify this frame"? I'm afraid > I don't see what is your difficulties in this regard. The problem is that I can't come up with any way to identify the initial frame reliably. I can't see how to write bool initial_frame_p (struct frame *f) { .... } That's why I suggested a new frame property. Do you know of any means to identify this frame? If so, please tell me about it! Thanks! -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 12:24 ` Alan Mackenzie 2021-03-20 12:49 ` Miha Rihtaršič 2021-03-20 12:50 ` Eli Zaretskii @ 2021-03-20 13:55 ` Stefan Monnier 2021-03-20 14:01 ` Eli Zaretskii 2021-03-20 14:12 ` Alan Mackenzie 2 siblings, 2 replies; 252+ messages in thread From: Stefan Monnier @ 2021-03-20 13:55 UTC (permalink / raw) To: Alan Mackenzie; +Cc: rudalics, Eli Zaretskii, jakanakaevangeli, emacs-devel > I still can't find a way of identifying the initial frame for sure - it > lacks a 'display frame parameter, but so do ordinary frames on a tty. It > lacks a 'client frame parameter, but so do ordinary frames when Emacs is > started normally and M-x server-start invoked. xdisp.c uses FRAME_INITIAL_P (SELECTED_FRAME ()) -- Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 13:55 ` Stefan Monnier @ 2021-03-20 14:01 ` Eli Zaretskii 2021-03-20 14:12 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2021-03-20 14:01 UTC (permalink / raw) To: Stefan Monnier; +Cc: acm, emacs-devel, jakanakaevangeli, rudalics > From: Stefan Monnier <monnier@iro.umontreal.ca> > Date: Sat, 20 Mar 2021 09:55:02 -0400 > Cc: rudalics@gmx.at, Eli Zaretskii <eliz@gnu.org>, jakanakaevangeli@chiru.no, > emacs-devel@gnu.org > > > I still can't find a way of identifying the initial frame for sure - it > > lacks a 'display frame parameter, but so do ordinary frames on a tty. It > > lacks a 'client frame parameter, but so do ordinary frames when Emacs is > > started normally and M-x server-start invoked. > > xdisp.c uses > > FRAME_INITIAL_P (SELECTED_FRAME ()) Right. Just use a frame pointer argument instead of SELECTED_FRAME(), i.e. struct frame *f; ... if (FRAME_INITIAL_P (f)) ... ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-20 13:55 ` Stefan Monnier 2021-03-20 14:01 ` Eli Zaretskii @ 2021-03-20 14:12 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-20 14:12 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, Eli Zaretskii, jakanakaevangeli, emacs-devel Hello, Stefan. On Sat, Mar 20, 2021 at 09:55:02 -0400, Stefan Monnier wrote: > > I still can't find a way of identifying the initial frame for sure - it > > lacks a 'display frame parameter, but so do ordinary frames on a tty. It > > lacks a 'client frame parameter, but so do ordinary frames when Emacs is > > started normally and M-x server-start invoked. > xdisp.c uses > FRAME_INITIAL_P (SELECTED_FRAME ()) Thanks, that's exactly what I need. > -- Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-17 19:32 ` Alan Mackenzie 2021-03-17 19:55 ` Eli Zaretskii @ 2021-03-21 15:44 ` Miha Rihtaršič 2021-03-21 17:03 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: Miha Rihtaršič @ 2021-03-21 15:44 UTC (permalink / raw) To: Alan Mackenzie; +Cc: Stefan Monnier, emacs-devel Alan Mackenzie <acm@muc.de> writes: > Hello, Miha. Hello > Yes. Sorry about these. This time around, I've tried to be more > careful and done more testing myself - hence me taking a few days longer > than I have up till now. I've found and corrected another ~two bugs > myself. > >> 1) >> C-x C-f on frame A >> select frame B >> select frame A >> Minibuffer is moved to B, but not back to A. > >> 2) >> Have two frames open >> open a minibuffer on a frame >> close this frame >> The other frame does have the miniwindow selected, but the >> minibuffer isn't shown in it. > > I think I've corrected these two, now. > > Here's another patch, this time to be applied on top of the last patch. > [...] > do you see any reason not to commit this? Sorry for the late reply. During my testing I didn't encounter any new problems. Thanks. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-03-21 15:44 ` Miha Rihtaršič @ 2021-03-21 17:03 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-03-21 17:03 UTC (permalink / raw) To: Miha Rihtaršič; +Cc: Stefan Monnier, emacs-devel Hello, Miha. On Sun, Mar 21, 2021 at 16:44:53 +0100, Miha Rihtaršič wrote: > Alan Mackenzie <acm@muc.de> writes: [ .... ] > > Here's another patch, this time to be applied on top of the last patch. > > [...] > > do you see any reason not to commit this? > Sorry for the late reply. During my testing I didn't encounter any new > problems. Thanks. Many thanks for the testing! I've now committed the patches to savannah. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! @ 2021-02-03 15:20 jakanakaevangeli 2021-02-06 15:52 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: jakanakaevangeli @ 2021-02-03 15:20 UTC (permalink / raw) To: acm; +Cc: emacs-devel I am reporting three technical problems with the current minibuffer quitting behaviour (on commit 20e48b6fd6cade60e468140a66127d326abfb8ff, after your patch was applied): 1) If you enter a (non-minibuffer) M-x recursive-edit inside a minibuffer, abort-minibuffers (bound to C-g) will quit the inner recursive edit but not the minibuffer, which is what the command's doc-string suggests. This is only a minor inconvenience. In fact, this behaviour is consistent with previous Emacs versions. 2) If you - set minibuffer-follows-selected-frame to nil, - open a minibuffer on frame A, - open a new inner minibuffer on frame B, - enter a M-x recursive-edit in this minibuffer, - select frame A's outer minibuffer, press C-g and answer yes, abort-minibuffers will fail to quit the outer minibuffer, it will not be visible in the mini-window, but you can check by evaluating (minibuffer-depth). 3) This one is a bit opinionated: internal_catch now has undocumented special handling for Qexit. I have come up with an idea to fix all of the above problems: - revert the special handling of Qexit in internal_catch - in read_minibuf, wrap the call to recursive_edit_1 with a catch for symbol exit-read-minibuffer - throwing to this symbol with nil or t shall have the same effect as throwing nil or t to 'exit - throwing to this symbol with a natural number N shall re-throw to this same symbol with N-1 (or quit if N<=1), effectively quitting out of N minibuffers - make exit-minibuffer throw to 'exit-read-minibuffer - make quit-minibuffers throw to 'exit-read-minibuffer with minibuf_level - this_minibuf_depth () + 1 Please let me know if you find this idea worthy of implementing and if you want me to try implementing it and posting a patch. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-02-03 15:20 jakanakaevangeli @ 2021-02-06 15:52 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-02-06 15:52 UTC (permalink / raw) To: jakanakaevangeli; +Cc: emacs-devel Hello, jakanakaevangeli. Thank you very much indeed for your post, which I have treated as a bug report. On Wed, Feb 03, 2021 at 16:20:50 +0100, jakanakaevangeli wrote: > I am reporting three technical problems with the current minibuffer > quitting behaviour (on commit 20e48b6fd6cade60e468140a66127d326abfb8ff, > after your patch was applied): > 1) If you enter a (non-minibuffer) M-x recursive-edit inside a > minibuffer, abort-minibuffers (bound to C-g) will quit the inner > recursive edit but not the minibuffer, which is what the command's > doc-string suggests. This is only a minor inconvenience. In fact, > this behaviour is consistent with previous Emacs versions. Indeed, in my patch I failed completely to account for recursive edits. The behaviour you report is indeed a bug. > 2) If you > - set minibuffer-follows-selected-frame to nil, > - open a minibuffer on frame A, > - open a new inner minibuffer on frame B, > - enter a M-x recursive-edit in this minibuffer, > - select frame A's outer minibuffer, press C-g and answer yes, > abort-minibuffers will fail to quit the outer minibuffer, it will not > be visible in the mini-window, but you can check by evaluating > (minibuffer-depth). Yes. This is a bug closely related to the above. > 3) This one is a bit opinionated: internal_catch now has undocumented > special handling for Qexit. Yes, also. I'll be thinking about this in the coming days. It would probably be a good idea to put a comment describing the special handling for (throw 'exit t) before the function. I notice also, on the page "Text from Minibuffer" in the Elisp manual, I neglected to change the description of the binding of C-g from the former `abort-recursive-edit' to the now current `abort-minibuffers'. > I have come up with an idea to fix all of the above problems: > - revert the special handling of Qexit in internal_catch > - in read_minibuf, wrap the call to recursive_edit_1 with a catch for > symbol exit-read-minibuffer > - throwing to this symbol with nil or t shall have the same effect as > throwing nil or t to 'exit > - throwing to this symbol with a natural number N shall re-throw to this > same symbol with N-1 (or quit if N<=1), effectively quitting out of N > minibuffers > - make exit-minibuffer throw to 'exit-read-minibuffer > - make quit-minibuffers throw to 'exit-read-minibuffer with > minibuf_level - this_minibuf_depth () + 1 > Please let me know if you find this idea worthy of implementing and if > you want me to try implementing it and posting a patch. It's an ingenious idea, but to be honest I don't think it's the best way to solve the problems. The handling you're proposing for exit-read-minibuffer is no less complicated than what's already there for exit. I can't help feeling that there would be nasty interactions between the two catch tags exit and exit-read-minibuffer. I think it's documented in the Elisp manual that (throw 'exit t) is the way to abort a minibuffer and its calling function. Also, this part of the Emacs C code is horrendous to adapt and maintain, as I've discovered since November. ;-) It is full of special cases that one is scarcely aware of (a minibuffer in its own frame, for example). To be honest, I've become tired of trying to make this section of the code work properly, but having started on it, I've got to finish it. Anyhow, I've attempted to solve the problems you reported, and come up with the following patch, which should apply cleanly to the savannah master branch. I'd be grateful if you could try it out, and report back as to whether the bugs you noticed are actually fixed by it, or what is still not quite right. diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 03cc70c0d4..f8143feedd 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2116,16 +2116,18 @@ minibuffer-hide-completions (defun exit-minibuffer () "Terminate this minibuffer argument." (interactive) + (when (not (minibuffer-innermost-command-loop-p)) + (error "%s" "Not in most nested command loop")) + (when (not (innermost-minibuffer-p)) + (error "%s" "Not in most nested minibuffer")) ;; If the command that uses this has made modifications in the minibuffer, ;; we don't want them to cause deactivation of the mark in the original ;; buffer. ;; A better solution would be to make deactivate-mark buffer-local ;; (or to turn it into a list of buffers, ...), but in the mean time, ;; this should do the trick in most cases. - (when (innermost-minibuffer-p) - (setq deactivate-mark nil) - (throw 'exit nil)) - (error "%s" "Not in most nested minibuffer")) + (setq deactivate-mark nil) + (throw 'exit nil)) (defun self-insert-and-exit () "Terminate minibuffer input." diff --git a/src/eval.c b/src/eval.c index 5bf3faebc8..d4456d8b46 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1197,15 +1197,12 @@ internal_catch (Lisp_Object tag, exit all minibuffers more deeply nested than the current one. */ { - EMACS_INT mini_depth = this_minibuffer_depth (Qnil); - if (mini_depth && mini_depth != minibuffer_quit_level) - { - if (minibuffer_quit_level == -1) - minibuffer_quit_level = mini_depth; - if (minibuffer_quit_level - && (minibuf_level > minibuffer_quit_level)) - Fthrow (Qexit, Qt); - } + if (minibuffer_quit_level == -1) + minibuffer_quit_level = this_minibuffer_depth (Qnil); + if (minibuf_level > minibuffer_quit_level + || command_loop_level + > minibuf_c_loop_level (minibuffer_quit_level)) + Fthrow (Qexit, Qt); else minibuffer_quit_level = -1; } diff --git a/src/lisp.h b/src/lisp.h index f658868544..035d7b37fc 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4349,6 +4349,7 @@ extern bool is_minibuffer (EMACS_INT, Lisp_Object); extern EMACS_INT this_minibuffer_depth (Lisp_Object); extern EMACS_INT minibuf_level; extern Lisp_Object get_minibuffer (EMACS_INT); +extern EMACS_INT minibuf_c_loop_level (EMACS_INT depth); extern void init_minibuf_once (void); extern void syms_of_minibuf (void); extern void barf_if_interaction_inhibited (void); @@ -4368,6 +4369,7 @@ extern void syms_of_casetab (void); /* Defined in keyboard.c. */ +extern EMACS_INT command_loop_level; extern Lisp_Object echo_message_buffer; extern struct kboard *echo_kboard; extern void cancel_echoing (void); diff --git a/src/minibuf.c b/src/minibuf.c index 949c3d989d..0745c095bb 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -41,6 +41,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ minibuffer recursions are encountered. */ Lisp_Object Vminibuffer_list; +Lisp_Object Vcommand_loop_level_list; /* Data to remember during recursive minibuffer invocations. */ @@ -64,6 +65,7 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; static Lisp_Object nth_minibuffer (EMACS_INT depth); +static void restore_minibuf_window (Lisp_Object mw); \f /* Return TRUE when a frame switch causes a minibuffer on the old @@ -389,6 +391,21 @@ No argument or nil as argument means use the current buffer as BUFFER. */) : Qnil; } +DEFUN ("minibuffer-innermost-command-loop-p", Fminibuffer_innermost_command_loop_p, + Sminibuffer_innermost_command_loop_p, 0, 1, 0, + doc: /* Return t if BUFFER is a minibuffer at the current command loop level. +No argument or nil as argument means use the current buffer as BUFFER. */) + (Lisp_Object buffer) +{ + EMACS_INT depth; + if (NILP (buffer)) + buffer = Fcurrent_buffer (); + depth = this_minibuffer_depth (buffer); + return depth && minibuf_c_loop_level (depth) == command_loop_level + ? Qt + : Qnil; +} + /* Return the nesting depth of the active minibuffer BUFFER, or 0 if BUFFER isn't such a thing. If BUFFER is nil, this means use the current buffer. */ @@ -598,7 +615,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level > 1 && minibuf_moves_frame_when_opened () - && !minibuf_follows_frame ()) + && (!minibuf_follows_frame () + || (!EQ (mini_frame, selected_frame)))) { EMACS_INT i; @@ -835,7 +853,18 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); - recursive_edit_1 (); + { + /* Note that here it doesn't suffice to record MINIBUF_WINDOW in a local + variable. It must be correct in `read_minibuf_unwind' in the event of + a non-local exit. */ + int count2 = SPECPDL_INDEX (); + + record_unwind_protect (restore_minibuf_window, minibuf_window); + + recursive_edit_1 (); + + unbind_to (count2, Qnil); + } /* We've exited the recursive edit without an error, so switch the current window away from the expired minibuffer window. */ @@ -908,6 +937,11 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return val; } +static void restore_minibuf_window (Lisp_Object mw) +{ + minibuf_window = mw; +} + /* Return true if BUF is a particular existing minibuffer. */ bool is_minibuffer (EMACS_INT depth, Lisp_Object buf) @@ -959,11 +993,16 @@ Lisp_Object get_minibuffer (EMACS_INT depth) { Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + Lisp_Object cll_tail = Fnthcdr (make_fixnum (depth), + Vcommand_loop_level_list); if (NILP (tail)) { tail = list1 (Qnil); Vminibuffer_list = nconc2 (Vminibuffer_list, tail); + cll_tail = list1 (Qnil); + Vcommand_loop_level_list = nconc2 (Vcommand_loop_level_list, cll_tail); } + XSETCAR (cll_tail, make_fixnum (depth ? command_loop_level : 0)); Lisp_Object buf = Fcar (tail); if (NILP (buf) || !BUFFER_LIVE_P (XBUFFER (buf))) { @@ -991,6 +1030,14 @@ get_minibuffer (EMACS_INT depth) return buf; } +EMACS_INT minibuf_c_loop_level (EMACS_INT depth) +{ + Lisp_Object cll = Fnth (make_fixnum (depth), Vcommand_loop_level_list); + if (FIXNUMP (cll)) + return XFIXNUM (cll); + return 0; +} + static void run_exit_minibuf_hook (void) { @@ -2137,6 +2184,7 @@ void init_minibuf_once (void) { staticpro (&Vminibuffer_list); + staticpro (&Vcommand_loop_level_list); pdumper_do_now_and_after_load (init_minibuf_once_for_pdumper); } @@ -2150,6 +2198,7 @@ init_minibuf_once_for_pdumper (void) restore from a dump file. pdumper doesn't try to preserve frames, windows, and so on, so reset everything related here. */ Vminibuffer_list = Qnil; + Vcommand_loop_level_list = Qnil; minibuf_level = 0; minibuf_prompt = Qnil; minibuf_save_list = Qnil; @@ -2380,6 +2429,7 @@ instead. */); defsubr (&Sminibufferp); defsubr (&Sinnermost_minibuffer_p); + defsubr (&Sminibuffer_innermost_command_loop_p); defsubr (&Sabort_minibuffers); defsubr (&Sminibuffer_prompt_end); defsubr (&Sminibuffer_contents); diff --git a/src/window.h b/src/window.h index 79eb44e7a3..b6f88e8f55 100644 --- a/src/window.h +++ b/src/window.h @@ -1120,10 +1120,6 @@ void set_window_buffer (Lisp_Object window, Lisp_Object buffer, extern Lisp_Object echo_area_window; -/* Depth in recursive edits. */ - -extern EMACS_INT command_loop_level; - /* Non-zero if we should redraw the mode lines on the next redisplay. Usually set to a unique small integer so we can track the main causes of full redisplays in `redisplay--mode-lines-cause'. */ -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Stop frames stealing eachothers' minibuffers! @ 2020-10-13 19:02 Alan Mackenzie 2020-10-13 19:20 ` Eli Zaretskii ` (2 more replies) 0 siblings, 3 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-10-13 19:02 UTC (permalink / raw) To: emacs-devel Hello, Emacs. In recent versions of master (and, I believe the emacs-27 branch) frames steal eachothers' minibuffers. By this I mean: (i) Have two frames open displaying buffers. (ii) On frame F1 do C-x b. This leaves a minibuffer open there. (iii) Move to F2. (iv) Do C-x 8 RET <enter some character>. F1's minibuffer is now on F2. This is bad. Seeing as how a minibuffer often has a strong association with its frame (e.g., C-x C-f opens a buffer in the same frame it was invoked from), this shifting of minibuffers from one frame to another is confusing. The following patch is a first attempt at fixing this. Note that it adds no code, it merely takes away code which was hindering the desired operation, and simplifies minibuf.c appreciably. I might well have missed subtle points in this proposed change. It has also changed (?improved) the error handling behaviour somewhat. In the above scenario, after applying the patch: (v) in F2 do M-x. This errors with "Command attempted to use minibuffer while in minibuffer", yet no longers aborts the command which has opened the (first) minibuffer. Personally, I think this is less confusing and potentially less annoying than the current behaviour. Comments? diff --git a/src/minibuf.c b/src/minibuf.c index f957b2ae17..10e58cc86b 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -76,37 +76,13 @@ choose_minibuf_frame (void) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { struct frame *sf = XFRAME (selected_frame); - Lisp_Object buffer; - /* I don't think that any frames may validly have a null minibuffer window anymore. */ if (NILP (sf->minibuffer_window)) emacs_abort (); - /* Under X, we come here with minibuf_window being the - minibuffer window of the unused termcap window created in - init_window_once. That window doesn't have a buffer. */ - buffer = XWINDOW (minibuf_window)->contents; - if (BUFFERP (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); minibuf_window = sf->minibuffer_window; } - - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); - } } DEFUN ("active-minibuffer-window", Factive_minibuffer_window, @@ -362,9 +338,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histstring; Lisp_Object histval; - Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; - specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -416,11 +389,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, { Lisp_Object str = build_string ("Command attempted to use minibuffer while in minibuffer"); - if (EQ (selected_window, minibuf_window)) - Fsignal (Quser_error, (list1 (str))); - else - /* If we're in another window, cancel the minibuffer that's active. */ - Fthrow (Qexit, str); + Fsignal (Quser_error, (list1 (str))); } if ((noninteractive @@ -566,23 +535,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level == 1 || !EQ (minibuf_window, selected_window)) minibuf_selected_window = selected_window; - /* Empty out the minibuffers of all frames other than the one - where we are going to display one now. - Set them to point to ` *Minibuf-0*', which is always empty. */ - empty_minibuf = get_minibuffer (0); - - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 19:02 Alan Mackenzie @ 2020-10-13 19:20 ` Eli Zaretskii 2020-10-13 19:51 ` Alan Mackenzie 2020-10-13 22:28 ` Stefan Monnier 2020-10-13 19:22 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:25 ` Stefan Monnier 2 siblings, 2 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-10-13 19:20 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Tue, 13 Oct 2020 19:02:55 +0000 > From: Alan Mackenzie <acm@muc.de> > > Seeing as how a minibuffer often has a strong association with its frame > (e.g., C-x C-f opens a buffer in the same frame it was invoked from), > this shifting of minibuffers from one frame to another is confusing. Is it? It makes sure the minibuffer is on the selected frame, which is natural in many/most use cases. Forcing the user to go back to a non-selected frame _is_ IMO confusing and inconvenient, at least in the usual cases. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 19:20 ` Eli Zaretskii @ 2020-10-13 19:51 ` Alan Mackenzie 2020-10-13 20:25 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:28 ` Stefan Monnier 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-13 19:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Tue, Oct 13, 2020 at 22:20:15 +0300, Eli Zaretskii wrote: > > Date: Tue, 13 Oct 2020 19:02:55 +0000 > > From: Alan Mackenzie <acm@muc.de> > > Seeing as how a minibuffer often has a strong association with its frame > > (e.g., C-x C-f opens a buffer in the same frame it was invoked from), > > this shifting of minibuffers from one frame to another is confusing. > Is it? It is (or was) to me. Seeing it for the first time, a user wouldn't know whether the C-x C-f would open the buffer in the current frame or the one she issued the command from. > It makes sure the minibuffer is on the selected frame, which is > natural in many/most use cases. It only does this sometimes. If the command using the minibuffer is given on frame F1, and the selected frame becomes F2, the minibuffer sometimes moves, sometimes doesn't, depending on what the user does. For example, C-s in F2 doesn't usually move the minibuffer, but it will if you use C-x 8 RET. This is inconsistent. > Forcing the user to go back to a non-selected frame _is_ IMO confusing > and inconvenient, at least in the usual cases. I don't find it so. The frame you complete a minibuffer command in should be the one you started it from. IMNSHO. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 19:51 ` Alan Mackenzie @ 2020-10-13 20:25 ` Gregory Heytings via Emacs development discussions. 2020-10-13 20:44 ` Alan Mackenzie 2020-10-13 20:51 ` Andreas Schwab 0 siblings, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-10-13 20:25 UTC (permalink / raw) To: Alan Mackenzie; +Cc: Eli Zaretskii, emacs-devel > > If the command using the minibuffer is given on frame F1, and the > selected frame becomes F2, the minibuffer sometimes moves, sometimes > doesn't, depending on what the user does. For example, C-s in F2 doesn't > usually move the minibuffer, but it will if you use C-x 8 RET. This is > inconsistent. > You are confusing two things here: the minibuffer (for interactive use) and the echo area (for messages). C-s uses the echo area, C-x C-f and C-x 8 RET use the minibuffer. I agree with you on one thing: C-x 8 RET should raise a "Command attempted to use minibuffer while in minibuffer" error in this case (when enable-recursive-minibuffers is nil, which is its default value). I'm not sure why it doesn't. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 20:25 ` Gregory Heytings via Emacs development discussions. @ 2020-10-13 20:44 ` Alan Mackenzie 2020-10-13 21:02 ` Drew Adams 2020-10-14 14:34 ` Eli Zaretskii 2020-10-13 20:51 ` Andreas Schwab 1 sibling, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-10-13 20:44 UTC (permalink / raw) To: Gregory Heytings; +Cc: Eli Zaretskii, emacs-devel Hello, Gregory. On Tue, Oct 13, 2020 at 20:25:04 +0000, Gregory Heytings wrote: > > If the command using the minibuffer is given on frame F1, and the > > selected frame becomes F2, the minibuffer sometimes moves, sometimes > > doesn't, depending on what the user does. For example, C-s in F2 > > doesn't usually move the minibuffer, but it will if you use C-x 8 > > RET. This is inconsistent. > You are confusing two things here: the minibuffer (for interactive use) > and the echo area (for messages). C-s uses the echo area, C-x C-f and > C-x 8 RET use the minibuffer. Sorry, I meant the use of C-x 8 RET from within isearch. In that sense, usually C-s will not suck in an active minibuffer, but it will if you have to type foreign characters into your search string. This is inconsistent. > I agree with you on one thing: C-x 8 RET should raise a "Command attempted > to use minibuffer while in minibuffer" error in this case ..... You do not agree with me. ;-) C-x 8 RET is essentially a command using recursive minibuffers. Otherwise, how could you type in a foreign character while using C-x C-f? To make C-x 8 RET sometimes recursive, other times not, would be too complicated. > .... (when enable-recursive-minibuffers is nil, which is its default > value). I'm not sure why it doesn't. See above. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* RE: Stop frames stealing eachothers' minibuffers! 2020-10-13 20:44 ` Alan Mackenzie @ 2020-10-13 21:02 ` Drew Adams 2020-10-14 14:34 ` Eli Zaretskii 1 sibling, 0 replies; 252+ messages in thread From: Drew Adams @ 2020-10-13 21:02 UTC (permalink / raw) To: Alan Mackenzie, Gregory Heytings; +Cc: Eli Zaretskii, emacs-devel > You do not agree with me. ;-) C-x 8 RET is essentially a command using > recursive minibuffers. Otherwise, how could you type in a foreign > character while using C-x C-f? To make C-x 8 RET sometimes recursive, > other times not, would be too complicated. Yes, `C-x 8 RET' is bound in `isearch-mode-map' to `isearch-char-by-name', which, like `M-e', uses `with-isearch-suspended' to temporarily read a character with the minibuffer. That's done with `read-char-by-name', which, yes, binds `enable-recursive-minibuffers' to t. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 20:44 ` Alan Mackenzie 2020-10-13 21:02 ` Drew Adams @ 2020-10-14 14:34 ` Eli Zaretskii 2020-10-14 16:02 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 14:34 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, emacs-devel > Date: Tue, 13 Oct 2020 20:44:08 +0000 > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > If the command using the minibuffer is given on frame F1, and the > > > selected frame becomes F2, the minibuffer sometimes moves, sometimes > > > doesn't, depending on what the user does. For example, C-s in F2 > > > doesn't usually move the minibuffer, but it will if you use C-x 8 > > > RET. This is inconsistent. > > > You are confusing two things here: the minibuffer (for interactive use) > > and the echo area (for messages). C-s uses the echo area, C-x C-f and > > C-x 8 RET use the minibuffer. > > Sorry, I meant the use of C-x 8 RET from within isearch. In that sense, > usually C-s will not suck in an active minibuffer, but it will if you > have to type foreign characters into your search string. This is > inconsistent. So maybe we should fix this inconsistency, not disable the switch to the selected frame where that is useful and expected? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 14:34 ` Eli Zaretskii @ 2020-10-14 16:02 ` Alan Mackenzie 2020-10-14 16:14 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-14 16:02 UTC (permalink / raw) To: Eli Zaretskii; +Cc: ghe, emacs-devel Hello, Eli. On Wed, Oct 14, 2020 at 17:34:56 +0300, Eli Zaretskii wrote: > > Date: Tue, 13 Oct 2020 20:44:08 +0000 > > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > If the command using the minibuffer is given on frame F1, and the > > > > selected frame becomes F2, the minibuffer sometimes moves, sometimes > > > > doesn't, depending on what the user does. For example, C-s in F2 > > > > doesn't usually move the minibuffer, but it will if you use C-x 8 > > > > RET. This is inconsistent. > > > You are confusing two things here: the minibuffer (for interactive use) > > > and the echo area (for messages). C-s uses the echo area, C-x C-f and > > > C-x 8 RET use the minibuffer. > > Sorry, I meant the use of C-x 8 RET from within isearch. In that sense, > > usually C-s will not suck in an active minibuffer, but it will if you > > have to type foreign characters into your search string. This is > > inconsistent. > So maybe we should fix this inconsistency, not disable the switch to > the selected frame where that is useful and expected? Well, my patch _does_ fix the inconsistency, by tying each minibuffer absolutely to the frame it acts on. It never occurred to me, until a few days ago, that anybody might find that strategy strange or awkward. Otherwise, to fix this inconsistency in Isearch (when there's a minibuffer open in another frame), we must either always pull the minibuffer into the Isearch frame, or never. I favour the "never" option of course, but going with "always", the logical endpoint is surely that an open minibuffer will always be displayed on the currently selected frame. It is only slightly more extreme to display that minibuffer on _every_ frame. That would be consistent, but I don't think I would like it very much. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 16:02 ` Alan Mackenzie @ 2020-10-14 16:14 ` Eli Zaretskii 2020-10-14 16:35 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 16:14 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, emacs-devel > Date: Wed, 14 Oct 2020 16:02:40 +0000 > Cc: ghe@sdf.org, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > Sorry, I meant the use of C-x 8 RET from within isearch. In that sense, > > > usually C-s will not suck in an active minibuffer, but it will if you > > > have to type foreign characters into your search string. This is > > > inconsistent. > > > So maybe we should fix this inconsistency, not disable the switch to > > the selected frame where that is useful and expected? > > Well, my patch _does_ fix the inconsistency, by tying each minibuffer > absolutely to the frame it acts on. It never occurred to me, until a > few days ago, that anybody might find that strategy strange or awkward. To me, consistent behavior would be to switch to the mini-window of the selected frame. So if we cannot reconcile our preferences, maybe we should have a user option to decide which behavior to choose. > Otherwise, to fix this inconsistency in Isearch (when there's a > minibuffer open in another frame), we must either always pull the > minibuffer into the Isearch frame, or never. I don't think I follow: Isearch doesn't use the minibuffer. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 16:14 ` Eli Zaretskii @ 2020-10-14 16:35 ` Alan Mackenzie 2020-10-14 17:05 ` Eli Zaretskii 2020-10-14 17:07 ` Gregory Heytings via Emacs development discussions. 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-10-14 16:35 UTC (permalink / raw) To: Eli Zaretskii; +Cc: ghe, emacs-devel Hello, Eli. On Wed, Oct 14, 2020 at 19:14:35 +0300, Eli Zaretskii wrote: > > Date: Wed, 14 Oct 2020 16:02:40 +0000 > > Cc: ghe@sdf.org, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > Sorry, I meant the use of C-x 8 RET from within isearch. In that sense, > > > > usually C-s will not suck in an active minibuffer, but it will if you > > > > have to type foreign characters into your search string. This is > > > > inconsistent. > > > So maybe we should fix this inconsistency, not disable the switch to > > > the selected frame where that is useful and expected? > > Well, my patch _does_ fix the inconsistency, by tying each minibuffer > > absolutely to the frame it acts on. It never occurred to me, until a > > few days ago, that anybody might find that strategy strange or awkward. > To me, consistent behavior would be to switch to the mini-window of > the selected frame. I'm not quite sure what a mini-window is. Does it mean the window within which the minibuffer is displayed? As in max-mini-window-height? > So if we cannot reconcile our preferences, maybe we should have a user > option to decide which behavior to choose. Perhaps. If we can formulate the two (or several) options in a non-confusing way. This is a fairly arcane matter. > > Otherwise, to fix this inconsistency in Isearch (when there's a > > minibuffer open in another frame), we must either always pull the > > minibuffer into the Isearch frame, or never. > I don't think I follow: Isearch doesn't use the minibuffer. My apologies for being unclear. I was thinking about what happens after the Isearch is over. Currently the minibuffer is pulled in if the Isearch has used the minibuffer for any reason. With my patch, this would never happen after an Isearch. What I meant was that with the "always" variation, after an Isearch, an open minibuffer would always be pulled over from another frame. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 16:35 ` Alan Mackenzie @ 2020-10-14 17:05 ` Eli Zaretskii 2020-10-14 18:45 ` Alan Mackenzie 2020-10-14 17:07 ` Gregory Heytings via Emacs development discussions. 1 sibling, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 17:05 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, emacs-devel > Date: Wed, 14 Oct 2020 16:35:34 +0000 > Cc: ghe@sdf.org, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > To me, consistent behavior would be to switch to the mini-window of > > the selected frame. > > I'm not quite sure what a mini-window is. Does it mean the window within > which the minibuffer is displayed? As in max-mini-window-height? The mini-window is the small window at the bottom of the frame which is used to display the echo area and the minibuffer. > My apologies for being unclear. I was thinking about what happens after > the Isearch is over. Currently the minibuffer is pulled in if the > Isearch has used the minibuffer for any reason. With my patch, this > would never happen after an Isearch. > > What I meant was that with the "always" variation, after an Isearch, an > open minibuffer would always be pulled over from another frame. Would you please describe the recipe to try, and then describe how you would like to change the current behavior in that recipe? I feel that I don't really understand what you are trying to say (e.g., what does "minibuffer is pulled in" mean?). Thanks. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 17:05 ` Eli Zaretskii @ 2020-10-14 18:45 ` Alan Mackenzie 2020-10-14 18:58 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-14 18:45 UTC (permalink / raw) To: Eli Zaretskii; +Cc: ghe, emacs-devel Hello, Eli. On Wed, Oct 14, 2020 at 20:05:36 +0300, Eli Zaretskii wrote: > > Date: Wed, 14 Oct 2020 16:35:34 +0000 > > Cc: ghe@sdf.org, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > To me, consistent behavior would be to switch to the mini-window of > > > the selected frame. > > I'm not quite sure what a mini-window is. Does it mean the window within > > which the minibuffer is displayed? As in max-mini-window-height? > The mini-window is the small window at the bottom of the frame which > is used to display the echo area and the minibuffer. Thanks. > > My apologies for being unclear. I was thinking about what happens after > > the Isearch is over. Currently the minibuffer is pulled in if the > > Isearch has used the minibuffer for any reason. With my patch, this > > would never happen after an Isearch. > > What I meant was that with the "always" variation, after an Isearch, an > > open minibuffer would always be pulled over from another frame. > Would you please describe the recipe to try, and then describe how you > would like to change the current behavior in that recipe? I feel that > I don't really understand what you are trying to say (e.g., what does > "minibuffer is pulled in" mean?). Calling the frames F1 and F2: (i) On F1, C-x b ; Leaves a minibuffer open. (ii) Move to F2. (iii) C-s foo RET On the current master, and with my patch, the minibuffer is still on F1. With my "always" variation, the minibuffer would now be on F2 (or, possibly on all frames). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Starting again from a vanilla state: (iv) On F1, C-x b ; Leaves a minibuffer open. (v) Move to F2. (vi) C-s foo ; Leaves an Isearch active. (vii) C-x 8 RET <Some character> RET ; Inserts a foreign character into the search string. (viii) RET ; Terminates Isearch. On the current master, the minibuffer has been moved to F2. With my patch, it would still be on F1. With the "always" variation it would be on F2 (or, possibly on all frames). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; The current master seems to me to be inconsistent, in that whether the minibuffer moves from F1 to F2 depends on whether the Isearch used a (recursive) minibuffer. With my patch, a minibuffer would remain on the frame it was opened on, no matter what. With the "always" variation, a minibuffer would always be on the currently selected frame; selecting a frame, no matter how, would cause any current minibuffer to move to that frame. Thus, I would suggest the following new option (written here as defvar rather than defcustom for ease of writing): ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar minibuffer-follows-frame 'hybrid "How a minibuffer moves on selecting a different frame. It takes one of the following values: nil: Minibuffers remain on the frame they were opened on. t: A minibuffers is moved onto the newly selected frame. The symbol `hybrid': A minibuffer is moved on onto the current frame when a recursive minibuffer opened on this frame terminates. `hybrid' corresponds with the standard behavior from Emacs 27 and earlier." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I think you are proposing the equivalent of (eq minibuffer-follows-frame t), but I'm not sure. > Thanks. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 18:45 ` Alan Mackenzie @ 2020-10-14 18:58 ` Eli Zaretskii 2020-10-14 19:49 ` Alan Mackenzie 2020-10-14 20:17 ` Gregory Heytings via Emacs development discussions. 0 siblings, 2 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 18:58 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, emacs-devel > Date: Wed, 14 Oct 2020 18:45:23 +0000 > Cc: ghe@sdf.org, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > Calling the frames F1 and F2: > > (i) On F1, C-x b ; Leaves a minibuffer open. > (ii) Move to F2. > (iii) C-s foo RET > > On the current master, and with my patch, the minibuffer is still on F1. > With my "always" variation, the minibuffer would now be on F2 (or, > possibly on all frames). > > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > > Starting again from a vanilla state: > (iv) On F1, C-x b ; Leaves a minibuffer open. > (v) Move to F2. > (vi) C-s foo ; Leaves an Isearch active. > (vii) C-x 8 RET <Some character> RET ; Inserts a foreign character into > the search string. > (viii) RET ; Terminates Isearch. > > On the current master, the minibuffer has been moved to F2. With my > patch, it would still be on F1. With the "always" variation it would be > on F2 (or, possibly on all frames). You should try this with the emacs-27 branch, because Gregory's patch installed there (and will be soon merged to master) changes the behavior quite a bit. > The current master seems to me to be inconsistent, in that whether the > minibuffer moves from F1 to F2 depends on whether the Isearch used a > (recursive) minibuffer. AFAICT, this no longer happens. > With my patch, a minibuffer would remain on the frame it was opened on, > no matter what. That's a separate issue, I believe. I'm not sure I like the behavior you suggest. If the user switched to a different frame, why should the minibuffer prompt stay on the non-selected frame? > (defvar minibuffer-follows-frame 'hybrid > "How a minibuffer moves on selecting a different frame. > It takes one of the following values: > nil: Minibuffers remain on the frame they were opened on. > t: A minibuffers is moved onto the newly selected frame. > The symbol `hybrid': A minibuffer is moved on onto the current frame when > a recursive minibuffer opened on this frame terminates. > > `hybrid' corresponds with the standard behavior from Emacs 27 and earlier." I think 'hybrid' is no longer needed (and makes little sense as useful behavior to me). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 18:58 ` Eli Zaretskii @ 2020-10-14 19:49 ` Alan Mackenzie 2020-10-15 13:44 ` Eli Zaretskii 2020-10-14 20:17 ` Gregory Heytings via Emacs development discussions. 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-14 19:49 UTC (permalink / raw) To: Eli Zaretskii; +Cc: ghe, emacs-devel Hello, Eli. On Wed, Oct 14, 2020 at 21:58:35 +0300, Eli Zaretskii wrote: > > Date: Wed, 14 Oct 2020 18:45:23 +0000 > > Cc: ghe@sdf.org, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > Calling the frames F1 and F2: [ .... ] > > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > > Starting again from a vanilla state: > > (iv) On F1, C-x b ; Leaves a minibuffer open. > > (v) Move to F2. > > (vi) C-s foo ; Leaves an Isearch active. > > (vii) C-x 8 RET <Some character> RET ; Inserts a foreign character into > > the search string. > > (viii) RET ; Terminates Isearch. > > On the current master, the minibuffer has been moved to F2. With my > > patch, it would still be on F1. With the "always" variation it would be > > on F2 (or, possibly on all frames). > You should try this with the emacs-27 branch, because Gregory's patch > installed there (and will be soon merged to master) changes the > behavior quite a bit. I've just tried it. The behaviour is indeed that which I noted above - the minibuffer moves to F2 if and only if a minibuffer has been used in Isearch. > > The current master seems to me to be inconsistent, in that whether the > > minibuffer moves from F1 to F2 depends on whether the Isearch used a > > (recursive) minibuffer. > AFAICT, this no longer happens. Oh, but it does. > > With my patch, a minibuffer would remain on the frame it was opened on, > > no matter what. > That's a separate issue, I believe. I'm not sure I like the behavior > you suggest. If the user switched to a different frame, why should > the minibuffer prompt stay on the non-selected frame? Because the action which the minibuffer will invoke usually takes place in that now non-selected frame. I feel a bit of a jolt when I hit RET in F2, but the effect (of switch-to-buffer) takes place in F1. This applies to C-x C-f, C-x C-w, C-x b, M-x imenu, ..... > > (defvar minibuffer-follows-frame 'hybrid > > "How a minibuffer moves on selecting a different frame. > > It takes one of the following values: > > nil: Minibuffers remain on the frame they were opened on. > > t: A minibuffers is moved onto the newly selected frame. > > The symbol `hybrid': A minibuffer is moved on onto the current frame when > > a recursive minibuffer opened on this frame terminates. > > `hybrid' corresponds with the standard behavior from Emacs 27 and earlier." > I think 'hybrid' is no longer needed (and makes little sense as useful > behavior to me). I agree, but for backward compatibility.... ;-) -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 19:49 ` Alan Mackenzie @ 2020-10-15 13:44 ` Eli Zaretskii 2020-10-15 18:01 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-15 13:44 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, emacs-devel > Date: Wed, 14 Oct 2020 19:49:04 +0000 > Cc: ghe@sdf.org, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > Starting again from a vanilla state: > > > (iv) On F1, C-x b ; Leaves a minibuffer open. > > > (v) Move to F2. > > > (vi) C-s foo ; Leaves an Isearch active. > > > (vii) C-x 8 RET <Some character> RET ; Inserts a foreign character into > > > the search string. > > > (viii) RET ; Terminates Isearch. > > > > On the current master, the minibuffer has been moved to F2. With my > > > patch, it would still be on F1. With the "always" variation it would be > > > on F2 (or, possibly on all frames). > > > You should try this with the emacs-27 branch, because Gregory's patch > > installed there (and will be soon merged to master) changes the > > behavior quite a bit. > > I've just tried it. The behaviour is indeed that which I noted above - > the minibuffer moves to F2 if and only if a minibuffer has been used in > Isearch. > > > > The current master seems to me to be inconsistent, in that whether the > > > minibuffer moves from F1 to F2 depends on whether the Isearch used a > > > (recursive) minibuffer. > > > AFAICT, this no longer happens. > > Oh, but it does. We are talking past each other. This behavior of the current emacs-27 branchlooks correct to me: On F1, C-x b Move to F2 C-s foo ; Isearch prompt appears in F2's echo area C-x 8 RET <some character> RET; editing is in F2's minibuffer RET ; terminates Isearch and leaves the active minibuffer on F2 This behavior is wrong: On F1, C-x b Move to F2 C-s foo ; Isearch prompt appears in F2's echo area RET ; terminates Isearch and leaves the active minibuffer on F1 The latter is correct, except for the last step: the active minibuffer should have switched to F2, which is now the selected frame. > > > With my patch, a minibuffer would remain on the frame it was opened on, > > > no matter what. > > > That's a separate issue, I believe. I'm not sure I like the behavior > > you suggest. If the user switched to a different frame, why should > > the minibuffer prompt stay on the non-selected frame? > > Because the action which the minibuffer will invoke usually takes place > in that now non-selected frame. There's no guarantee of that. Moreover, a non-selected frame could have been iconified or even deleted. The only sane place to continue interaction is on the selected frame (and let's leave the use case of separate minibuffer-only frame aside, okay?). > I feel a bit of a jolt when I hit RET in F2, but the effect (of > switch-to-buffer) takes place in F1. This applies to C-x C-f, C-x > C-w, C-x b, M-x imenu, ..... Not clear why: you switched to another frame, so continue using that. If you want to continue using the original frame, switch back there. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-15 13:44 ` Eli Zaretskii @ 2020-10-15 18:01 ` Alan Mackenzie 2020-10-15 18:18 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-15 18:01 UTC (permalink / raw) To: Eli Zaretskii; +Cc: ghe, emacs-devel Hello, Eli. On Thu, Oct 15, 2020 at 16:44:42 +0300, Eli Zaretskii wrote: > > Date: Wed, 14 Oct 2020 19:49:04 +0000 > > Cc: ghe@sdf.org, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > Starting again from a vanilla state: > > > > (iv) On F1, C-x b ; Leaves a minibuffer open. > > > > (v) Move to F2. > > > > (vi) C-s foo ; Leaves an Isearch active. > > > > (vii) C-x 8 RET <Some character> RET ; Inserts a foreign character into > > > > the search string. > > > > (viii) RET ; Terminates Isearch. > > > > On the current master, the minibuffer has been moved to F2. With my > > > > patch, it would still be on F1. With the "always" variation it would be > > > > on F2 (or, possibly on all frames). > > > You should try this with the emacs-27 branch, because Gregory's patch > > > installed there (and will be soon merged to master) changes the > > > behavior quite a bit. > > I've just tried it. The behaviour is indeed that which I noted above - > > the minibuffer moves to F2 if and only if a minibuffer has been used in > > Isearch. > > > > The current master seems to me to be inconsistent, in that whether the > > > > minibuffer moves from F1 to F2 depends on whether the Isearch used a > > > > (recursive) minibuffer. > > > AFAICT, this no longer happens. > > Oh, but it does. > We are talking past each other. This behavior of the current emacs-27 > branch looks correct to me: > On F1, C-x b > Move to F2 > C-s foo ; Isearch prompt appears in F2's echo area > C-x 8 RET <some character> RET; editing is in F2's minibuffer > RET ; terminates Isearch and leaves the active minibuffer on F2 > This behavior is wrong: > On F1, C-x b > Move to F2 > C-s foo ; Isearch prompt appears in F2's echo area > RET ; terminates Isearch and leaves the active minibuffer on F1 > The latter is correct, except for the last step: the active minibuffer > should have switched to F2, which is now the selected frame. OK, but I disagree. We seem to have different mental models of the minibuffer. For you, the MB, I think, is what demands immediate attention, therefore should be on the selected frame. For me, the MB is an integral part of the frame on which it was opened. Is there any chance we could implement an option for this (which has been mentioned already)? > > > > With my patch, a minibuffer would remain on the frame it was > > > > opened on, no matter what. > > > That's a separate issue, I believe. I'm not sure I like the > > > behavior you suggest. If the user switched to a different frame, > > > why should the minibuffer prompt stay on the non-selected frame? > > Because the action which the minibuffer will invoke usually takes place > > in that now non-selected frame. > There's no guarantee of that. Moreover, a non-selected frame could > have been iconified or even deleted. Yes, that needs consideration. An iconified frame could be restored, but I'm not sure about a deleted frame. Maybe deleting a frame with an open MB on it should kill the MB together with the command which is using it. Or something like that. > The only sane place to continue interaction is on the selected frame > ... I don't think I agree with "only". We have problems if, say, the frame is an ediff frame detached from what it's working on. We have problems if the frame is too small to hold the MB's prompt. > .... (and let's leave the use case of separate minibuffer-only frame > aside, okay?). Well, we'll need to deal with it at some stage. > > I feel a bit of a jolt when I hit RET in F2, but the effect (of > > switch-to-buffer) takes place in F1. This applies to C-x C-f, C-x > > C-w, C-x b, M-x imenu, ..... > Not clear why: you switched to another frame, so continue using that. > If you want to continue using the original frame, switch back there. The problem happens to me when I've forgotten I've still got an open minibuffer on the other frame. This happens, for example, when I need to search a buffer to find out what to type into, say, M-x imenu, and then get distracted by whatever. Anyhow, back to practicalities. I think we agreed last night (talking about the "hybrid" option) that the current way of doing things isn't very good. I think, but I'm not sure, that you're saying the MB, if open, should always be present on the currently selected frame and nowhere else. If I'm wrong here, could you possibly give a precise description of when you say the MB should be moved to a different frame. From a C coding point of view, if nothing special is done, the MB remains on the frame it was opened on. (My patch was nothing but the removal of code which moved the MB.) The code I removed was somewhat tangled. If we're going to implement "MB follows selected frame", we may well want to add a call to minibuf.c from frame.c. This will need doing with care. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-15 18:01 ` Alan Mackenzie @ 2020-10-15 18:18 ` Eli Zaretskii 2020-10-21 15:19 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-15 18:18 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, emacs-devel > Date: Thu, 15 Oct 2020 18:01:43 +0000 > Cc: ghe@sdf.org, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > The latter is correct, except for the last step: the active minibuffer > > should have switched to F2, which is now the selected frame. > > OK, but I disagree. We seem to have different mental models of the > minibuffer. For you, the MB, I think, is what demands immediate > attention, therefore should be on the selected frame. For me, the MB is > an integral part of the frame on which it was opened. > > Is there any chance we could implement an option for this (which has been > mentioned already)? I'm okay with such an option. I think I already said that. > > > I feel a bit of a jolt when I hit RET in F2, but the effect (of > > > switch-to-buffer) takes place in F1. This applies to C-x C-f, C-x > > > C-w, C-x b, M-x imenu, ..... > > > Not clear why: you switched to another frame, so continue using that. > > If you want to continue using the original frame, switch back there. > > The problem happens to me when I've forgotten I've still got an open > minibuffer on the other frame. The frame's title will help, as it shows "*Minibuf-1*" or somesuch. > Anyhow, back to practicalities. I think we agreed last night (talking > about the "hybrid" option) that the current way of doing things isn't > very good. I think, but I'm not sure, that you're saying the MB, if > open, should always be present on the currently selected frame and > nowhere else. If I'm wrong here, could you possibly give a precise > description of when you say the MB should be moved to a different frame. IMO, the minibuffer should _always_ be on the selected frame, unless it has its own minibuffer-only frame (in which case it is always there). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-15 18:18 ` Eli Zaretskii @ 2020-10-21 15:19 ` Alan Mackenzie 2020-10-21 16:49 ` Drew Adams ` (2 more replies) 0 siblings, 3 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-10-21 15:19 UTC (permalink / raw) To: Eli Zaretskii; +Cc: ghe, emacs-devel Hello, Eli. On Thu, Oct 15, 2020 at 21:18:10 +0300, Eli Zaretskii wrote: > > Date: Thu, 15 Oct 2020 18:01:43 +0000 > > Cc: ghe@sdf.org, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > The latter is correct, except for the last step: the active minibuffer > > > should have switched to F2, which is now the selected frame. > > OK, but I disagree. We seem to have different mental models of the > > minibuffer. For you, the MB, I think, is what demands immediate > > attention, therefore should be on the selected frame. For me, the MB is > > an integral part of the frame on which it was opened. > > Is there any chance we could implement an option for this (which has been > > mentioned already)? > I'm okay with such an option. I think I already said that. I too think you did. There's a proposed patch below. [ .... ] > > Anyhow, back to practicalities. I think we agreed last night (talking > > about the "hybrid" option) that the current way of doing things isn't > > very good. I think, but I'm not sure, that you're saying the MB, if > > open, should always be present on the currently selected frame and > > nowhere else. If I'm wrong here, could you possibly give a precise > > description of when you say the MB should be moved to a different frame. > IMO, the minibuffer should _always_ be on the selected frame, unless > it has its own minibuffer-only frame (in which case it is always > there). OK. I've implemented a new variable `minibuffer-follows-frame' which is t by default. For safety's sake, only the default-toplevel-value of this variable is consulted, to stop funny things happening should it be dynamically bound or if there might be a buffer local value. For minibuffer-follows-frame nil, the error "Command attempted to use minibuffer while in minibuffer" no longer aborts an enclosing minibuffer command. There doesn't seem any need or benefit - the enclosing command can carry on later. Here's my patch (including amendments to the Emacs manual and a NEWS entry). Comments would be most appreciated. diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index 54f046a7e0..0e6d86f3b8 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -69,6 +69,16 @@ Basic Minibuffer the minibuffer comes back. While the minibuffer is in use, Emacs does not echo keystrokes. + Whilst using the minibuffer, you can switch to a different frame, +perhaps to note text you need to enter (@pxref{Frame Commands}). By +default, the minibuffer moves to this new frame. If you set the user +option @code{minibuffer-follows-frame} to @code{nil}, then the +minibuffer stays in the frame where it was opened, and you must switch +back to that frame in order to complete (or abort) the current +command. Note that the effect of the command, when you finally finish +using the minibuffer, always takes place in the frame where you first +opened it. + @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index f3e3d9a1b6..95b5d7ffd0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -86,6 +86,19 @@ useful on systems such as FreeBSD which ships only with "etc/termcap". * Changes in Emacs 28.1 +++ +** Switching frames when a minibuffer is active has been rationalized. +By default, the minibuffer is moved to the newly selected frame. When +the current command is continued (by completing the minibuffer +action), it takes effect in the frame the minibuffer was first opened +in. An alternative behavior is available by customizing +'minibuffer-follows-frame' to nil; here, the minibuffer stays on the +frame it was first opened on, and you must switch back to this frame +to continue or abort the current command. The old (pre 28.1), +somewhat chaotic behavior is no longer available. + ++++ +*** A new system for displaying documentation for groups of function is added. + ** New system for displaying documentation for groups of function. This can either be used by saying 'M-x shortdoc-display-group' and choosing a group, or clicking a button in the *Help* buffers when diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 3fd6ac031c..2120f5a627 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,6 +394,7 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c + (minibuffer-follows-frame minibuffer boolean "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/src/frame.c b/src/frame.c index 0b707c2af8..361a7119c5 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1482,6 +1482,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor #endif internal_last_event_frame = Qnil; + move_minibuffer_onto_frame (); return frame; } diff --git a/src/lisp.h b/src/lisp.h index 45353fbef3..5b00252f24 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4336,6 +4336,7 @@ extern void clear_regexp_cache (void); extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; +extern void move_minibuffer_onto_frame (void); extern Lisp_Object get_minibuffer (EMACS_INT); extern void init_minibuf_once (void); extern void syms_of_minibuf (void); diff --git a/src/minibuf.c b/src/minibuf.c index f957b2ae17..61863af538 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -64,6 +64,12 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; \f +static bool +minibuf_follows_frame (void) +{ + return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_frame)); +} + /* Put minibuf on currently selected frame's minibuffer. We do this whenever the user starts a new minibuffer or when a minibuffer exits. */ @@ -76,37 +82,74 @@ choose_minibuf_frame (void) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { struct frame *sf = XFRAME (selected_frame); - Lisp_Object buffer; - /* I don't think that any frames may validly have a null minibuffer window anymore. */ if (NILP (sf->minibuffer_window)) emacs_abort (); - /* Under X, we come here with minibuf_window being the - minibuffer window of the unused termcap window created in - init_window_once. That window doesn't have a buffer. */ - buffer = XWINDOW (minibuf_window)->contents; - if (BUFFERP (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); - minibuf_window = sf->minibuffer_window; + if (minibuf_follows_frame ()) + minibuf_window = sf->minibuffer_window; + else if (minibuf_level) + { + Lisp_Object buffer = get_minibuffer (minibuf_level); + Lisp_Object tail, frame; + + FOR_EACH_FRAME (tail, frame) + { + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, + buffer)) + { + minibuf_window = XFRAME (frame)->minibuffer_window; + goto after_set; + } + minibuf_window = sf->minibuffer_window; + after_set: ; + } + } + else + minibuf_window = Qnil; } - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; + if (minibuf_follows_frame ()) + /* Make sure no other frame has a minibuffer as its selected window, + because the text would not be displayed in it, and that would be + confusing. Only allow the selected frame to do this, + and that only if the minibuffer is active. */ + { + Lisp_Object tail, frame; - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); - } + FOR_EACH_FRAME (tail, frame) + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) + && !(EQ (frame, selected_frame) + && minibuf_level > 0)) + Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); + } +} + +/* If `minibuffer_follows_frame' and we have a minibuffer, move it + from its current frame to the selected frame. This function is + intended to be called from `do_switch_frame' in frame.c. */ +void move_minibuffer_onto_frame (void) +{ + if (!minibuf_level) + return; + if (!minibuf_follows_frame ()) + return; + if (FRAMEP (selected_frame) + && FRAME_LIVE_P (XFRAME (selected_frame)) + && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) + { + struct frame *sf = XFRAME (selected_frame); + Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; + struct frame *of = XFRAME (old_frame); + Lisp_Object buffer = XWINDOW (minibuf_window)->contents; + + set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + minibuf_window = sf->minibuffer_window; + Fset_frame_selected_window (selected_frame, sf->minibuffer_window, + Qnil); + set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); + } } DEFUN ("active-minibuffer-window", Factive_minibuffer_window, @@ -362,9 +405,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histstring; Lisp_Object histval; - Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; - specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -416,11 +456,15 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, { Lisp_Object str = build_string ("Command attempted to use minibuffer while in minibuffer"); - if (EQ (selected_window, minibuf_window)) - Fsignal (Quser_error, (list1 (str))); + /* OLD STOUGH, 2020-10-21 */ + if (!minibuf_follows_frame () + || EQ (selected_window, minibuf_window)) + Fsignal (Quser_error, (list1 (str))); else - /* If we're in another window, cancel the minibuffer that's active. */ - Fthrow (Qexit, str); + /* If we're in another window, cancel the minibuffer that's active. */ + Fthrow (Qexit, str); + /* END OF OLD STOUGH */ + Fsignal (Quser_error, (list1 (str))); } if ((noninteractive @@ -433,6 +477,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return unbind_to (count, val); } + minibuf_level++; /* Before calling choose_minibuf_frame. */ + /* Choose the minibuffer window and frame, and take action on them. */ /* Prepare for restoring the current buffer since choose_minibuf_frame @@ -484,7 +530,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, = Fcons (Fthis_command_keys_vector (), minibuf_save_list); record_unwind_protect_void (read_minibuf_unwind); - minibuf_level++; /* We are exiting the minibuffer one way or the other, so run the hook. It should be run before unwinding the minibuf settings. Do it separately from read_minibuf_unwind because we need to make sure that @@ -566,23 +611,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level == 1 || !EQ (minibuf_window, selected_window)) minibuf_selected_window = selected_window; - /* Empty out the minibuffers of all frames other than the one - where we are going to display one now. - Set them to point to ` *Minibuf-0*', which is always empty. */ - empty_minibuf = get_minibuffer (0); - - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -1911,6 +1939,8 @@ syms_of_minibuf (void) staticpro (&minibuf_prompt); staticpro (&minibuf_save_list); + DEFSYM (Qminibuffer_follows_frame, + "minibuffer-follows-frame"); DEFSYM (Qcompletion_ignore_case, "completion-ignore-case"); DEFSYM (Qminibuffer_default, "minibuffer-default"); Fset (Qminibuffer_default, Qnil); @@ -1954,6 +1984,14 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; + DEFVAR_BOOL ("minibuffer-follows-frame", minibuffer_follows_frame, + doc: /* Non-nil means an open minibuffer will move to a newly selected frame. +Nil means that a minibuffer will appear only in the frame which created it. + +Any buffer local or dynamic binding of this variable is ignored. Only the +default top level value is used. */); + minibuffer_follows_frame = 1; + DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, doc: /* Non-nil means completion ignores case when reading a buffer name. */); -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* RE: Stop frames stealing eachothers' minibuffers! 2020-10-21 15:19 ` Alan Mackenzie @ 2020-10-21 16:49 ` Drew Adams 2020-10-21 19:13 ` Alan Mackenzie 2020-10-21 18:32 ` Stefan Monnier 2020-10-21 20:04 ` Alan Mackenzie 2 siblings, 1 reply; 252+ messages in thread From: Drew Adams @ 2020-10-21 16:49 UTC (permalink / raw) To: Alan Mackenzie, Eli Zaretskii; +Cc: ghe, emacs-devel (Minor) "Whilst" -> "While" (Amerenglish) "the minibuffer moves" -> "the active minibuffer moves" "in the frame the minibuffer was first opened in" -> "in the frame where the minibuffer was first opened" "on the frame it was first opened on" -> "in the frame where it was first opened" (And decide whether a minibuffer - and a window in general, is in or on a frame. I don't know what the convention might be.) Consider splitting this sentence: "An alternative behavior is available by customizing 'minibuffer-follows-frame' to nil; here, the minibuffer stays on the frame it was first opened on, and you must switch back to this frame to continue or abort the current command." Consider removing this gratuitous bit: "somewhat chaotic". ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-21 16:49 ` Drew Adams @ 2020-10-21 19:13 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-10-21 19:13 UTC (permalink / raw) To: Drew Adams; +Cc: ghe, Eli Zaretskii, emacs-devel Hello, Drew, thanks very much for eyeballing my English text. On Wed, Oct 21, 2020 at 09:49:36 -0700, Drew Adams wrote: > (Minor) > "Whilst" -> > "While" (Amerenglish) > "the minibuffer moves" -> > "the active minibuffer moves" > "in the frame the minibuffer was first opened in" -> > "in the frame where the minibuffer was first opened" > "on the frame it was first opened on" -> > "in the frame where it was first opened" > (And decide whether a minibuffer - and a window in > general, is in or on a frame. I don't know what the > convention might be.) > Consider splitting this sentence: > "An alternative behavior is available by customizing > 'minibuffer-follows-frame' to nil; here, the > minibuffer stays on the frame it was first opened on, > and you must switch back to this frame to continue or > abort the current command." I've made all of these adjustments now, thanks, with a window being "in" a frame. > Consider removing this gratuitous bit: "somewhat chaotic". OK, with the benefit of somebody else looking at it, that's too pejorative. I've changed it to "somewhat unsystematic", which I think is gentler, yet still gives a reason why the former behaviour is no longer available. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-21 15:19 ` Alan Mackenzie 2020-10-21 16:49 ` Drew Adams @ 2020-10-21 18:32 ` Stefan Monnier 2020-10-21 19:38 ` Alan Mackenzie 2020-10-21 20:04 ` Alan Mackenzie 2 siblings, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-10-21 18:32 UTC (permalink / raw) To: Alan Mackenzie; +Cc: ghe, Eli Zaretskii, emacs-devel Thanks Alan, This looks pretty good to me (haven't tried it yet). Some comments/questions below, Stefan > @@ -86,6 +86,19 @@ useful on systems such as FreeBSD which ships only with "etc/termcap". > * Changes in Emacs 28.1 > > +++ > +** Switching frames when a minibuffer is active has been rationalized. > +By default, the minibuffer is moved to the newly selected frame. When > +the current command is continued (by completing the minibuffer > +action), it takes effect in the frame the minibuffer was first opened > +in. An alternative behavior is available by customizing > +'minibuffer-follows-frame' to nil; here, the minibuffer stays on the > +frame it was first opened on, and you must switch back to this frame > +to continue or abort the current command. The old (pre 28.1), > +somewhat chaotic behavior is no longer available. > + > ++++ > +*** A new system for displaying documentation for groups of function is added. > + > ** New system for displaying documentation for groups of function. > This can either be used by saying 'M-x shortdoc-display-group' and > choosing a group, or clicking a button in the *Help* buffers when Looks like a chunk of Lars's shortdoc got brought along. > +static bool > +minibuf_follows_frame (void) > +{ > + return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_frame)); > +} I can't think of any reason why we'd need to bother with `Fdefault_toplevel_value` here. I think the only justification would be if using some other value could result in a crash or in garbled display, but AFAICT it could only result (in the worst case) in a vaguely unexpected behavior where the mininbuffer follows the frame when it shouldn't or vice versa. > + FOR_EACH_FRAME (tail, frame) > + { > + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, > + buffer)) > + { > + minibuf_window = XFRAME (frame)->minibuffer_window; > + goto after_set; > + } > + minibuf_window = sf->minibuffer_window; > + after_set: ; > + } I don't understand the: minibuf_window = sf->minibuffer_window Won't this undo the minibuf_window = XFRAME (frame)->minibuffer_window; executed in a previous iteration? Should this be moved to just before the loop maybe? > + /* OLD STOUGH, 2020-10-21 */ Not sure how useful this is. I'd either remove this comment or replace it with an actual explanation of what's going on and/or how the code used to work. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-21 18:32 ` Stefan Monnier @ 2020-10-21 19:38 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-10-21 19:38 UTC (permalink / raw) To: Stefan Monnier; +Cc: ghe, Eli Zaretskii, emacs-devel Hello, Stefan, thanks for the review. In detail: On Wed, Oct 21, 2020 at 14:32:21 -0400, Stefan Monnier wrote: > Thanks Alan, > This looks pretty good to me (haven't tried it yet). > Some comments/questions below, > Stefan > > @@ -86,6 +86,19 @@ useful on systems such as FreeBSD which ships only with "etc/termcap". > > * Changes in Emacs 28.1 > > > > +++ > > +** Switching frames when a minibuffer is active has been rationalized. > > +By default, the minibuffer is moved to the newly selected frame. When > > +the current command is continued (by completing the minibuffer > > +action), it takes effect in the frame the minibuffer was first opened > > +in. An alternative behavior is available by customizing > > +'minibuffer-follows-frame' to nil; here, the minibuffer stays on the > > +frame it was first opened on, and you must switch back to this frame > > +to continue or abort the current command. The old (pre 28.1), > > +somewhat chaotic behavior is no longer available. > > + > > ++++ > > +*** A new system for displaying documentation for groups of function is added. > > + > > ** New system for displaying documentation for groups of function. > > This can either be used by saying 'M-x shortdoc-display-group' and > > choosing a group, or clicking a button in the *Help* buffers when > Looks like a chunk of Lars's shortdoc got brought along. Yes, sorry. I made a mess of hand editing the result of the merge. Now fixed. > > +static bool > > +minibuf_follows_frame (void) > > +{ > > + return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_frame)); > > +} > I can't think of any reason why we'd need to bother with > `Fdefault_toplevel_value` here. I think the only justification would be > if using some other value could result in a crash or in garbled display, > but AFAICT it could only result (in the worst case) in a vaguely > unexpected behavior where the mininbuffer follows the frame when it > shouldn't or vice versa. Maybe I'm being a bit too careful, but I've got bad feelings about what might happen if a buffer local value was different from the canonical value. I simply can't picture what might happen, but like you say, it could well end up with a mini-window being displayed on two frames, and there being no way to fix that situation. > > + FOR_EACH_FRAME (tail, frame) > > + { > > + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, > > + buffer)) > > + { > > + minibuf_window = XFRAME (frame)->minibuffer_window; > > + goto after_set; > > + } > > + minibuf_window = sf->minibuffer_window; > > + after_set: ; > > + } > I don't understand the: > minibuf_window = sf->minibuffer_window > Won't this undo the > minibuf_window = XFRAME (frame)->minibuffer_window; > executed in a previous iteration? > Should this be moved to just before the loop maybe? Yes, you're right. I put the "catch-all" setting of minibuf_window wrongly inside the FOR_EACH_FRAME loop. Like you say, a neater way of expressing this is to put the "catch-all" setting before the loop, and allow it to get overwritten by anything found in the loop. > > + /* OLD STOUGH, 2020-10-21 */ > Not sure how useful this is. I'd either remove this comment or replace > it with an actual explanation of what's going on and/or how the code > used to work. It's my own personal comment for restoring changes, and it should not have found it's way into the patch. Sorry. I'll repost the patch, with your and Drew's corrections separately. > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-21 15:19 ` Alan Mackenzie 2020-10-21 16:49 ` Drew Adams 2020-10-21 18:32 ` Stefan Monnier @ 2020-10-21 20:04 ` Alan Mackenzie 2020-10-22 16:14 ` Eli Zaretskii 2 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-21 20:04 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Wed, Oct 21, 2020 at 15:19:45 +0000, Alan Mackenzie wrote: > On Thu, Oct 15, 2020 at 21:18:10 +0300, Eli Zaretskii wrote: > > > Date: Thu, 15 Oct 2020 18:01:43 +0000 > > > Cc: ghe@sdf.org, emacs-devel@gnu.org > > > From: Alan Mackenzie <acm@muc.de> [ .... ] > OK. I've implemented a new variable `minibuffer-follows-frame' which is > t by default. For safety's sake, only the default-toplevel-value of this > variable is consulted, to stop funny things happening should it be > dynamically bound or if there might be a buffer local value. > For minibuffer-follows-frame nil, the error "Command attempted to use > minibuffer while in minibuffer" no longer aborts an enclosing minibuffer > command. There doesn't seem any need or benefit - the enclosing command > can carry on later. > Here's my patch (including amendments to the Emacs manual and a NEWS > entry). Comments would be most appreciated. Here's a corrected version of the patch, incorporating fixes and suggestions from Stefan and Drew: diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index 54f046a7e0..176d0df14e 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -69,6 +69,16 @@ Basic Minibuffer the minibuffer comes back. While the minibuffer is in use, Emacs does not echo keystrokes. + While using the minibuffer, you can switch to a different frame, +perhaps to note text you need to enter (@pxref{Frame Commands}). By +default, the active minibuffer moves to this new frame. If you set +the user option @code{minibuffer-follows-frame} to @code{nil}, then +the minibuffer stays in the frame where it was opened, and you must +switch back to that frame in order to complete (or abort) the current +command. Note that the effect of the command, when you finally finish +using the minibuffer, always takes place in the frame where you first +opened it. + @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index f3e3d9a1b6..0aef5ec2f6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -85,6 +85,17 @@ useful on systems such as FreeBSD which ships only with "etc/termcap". \f * Changes in Emacs 28.1 ++++ +** Switching frames when a minibuffer is active has been rationalized. +By default, the active minibuffer is moved to the newly selected +frame. When the current command is continued (by completing the +minibuffer action), it takes effect in the frame where the minibuffer +was first opened. An alternative behavior is available by customizing +'minibuffer-follows-frame' to nil. Here, the minibuffer stays in the +frame where it was first opened, and you must switch back to this +frame to continue or abort the current command. The old (pre Emacs +28.1) somewhat unsystematic behavior is no longer available. + +++ ** New system for displaying documentation for groups of function. This can either be used by saying 'M-x shortdoc-display-group' and diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 3fd6ac031c..2120f5a627 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,6 +394,7 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c + (minibuffer-follows-frame minibuffer boolean "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/src/frame.c b/src/frame.c index 0b707c2af8..361a7119c5 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1482,6 +1482,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor #endif internal_last_event_frame = Qnil; + move_minibuffer_onto_frame (); return frame; } diff --git a/src/lisp.h b/src/lisp.h index 45353fbef3..5b00252f24 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4336,6 +4336,7 @@ extern void clear_regexp_cache (void); extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; +extern void move_minibuffer_onto_frame (void); extern Lisp_Object get_minibuffer (EMACS_INT); extern void init_minibuf_once (void); extern void syms_of_minibuf (void); diff --git a/src/minibuf.c b/src/minibuf.c index f957b2ae17..8269e251ae 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -64,6 +64,12 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; \f +static bool +minibuf_follows_frame (void) +{ + return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_frame)); +} + /* Put minibuf on currently selected frame's minibuffer. We do this whenever the user starts a new minibuffer or when a minibuffer exits. */ @@ -76,37 +82,71 @@ choose_minibuf_frame (void) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { struct frame *sf = XFRAME (selected_frame); - Lisp_Object buffer; - /* I don't think that any frames may validly have a null minibuffer window anymore. */ if (NILP (sf->minibuffer_window)) emacs_abort (); - /* Under X, we come here with minibuf_window being the - minibuffer window of the unused termcap window created in - init_window_once. That window doesn't have a buffer. */ - buffer = XWINDOW (minibuf_window)->contents; - if (BUFFERP (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); - minibuf_window = sf->minibuffer_window; + if (minibuf_follows_frame ()) + minibuf_window = sf->minibuffer_window; + else if (minibuf_level) + { + Lisp_Object buffer = get_minibuffer (minibuf_level); + Lisp_Object tail, frame; + + minibuf_window = sf->minibuffer_window; + FOR_EACH_FRAME (tail, frame) + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, + buffer)) + { + minibuf_window = XFRAME (frame)->minibuffer_window; + break; + } + } + else + minibuf_window = Qnil; } - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; + if (minibuf_follows_frame ()) + /* Make sure no other frame has a minibuffer as its selected window, + because the text would not be displayed in it, and that would be + confusing. Only allow the selected frame to do this, + and that only if the minibuffer is active. */ + { + Lisp_Object tail, frame; - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); - } + FOR_EACH_FRAME (tail, frame) + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) + && !(EQ (frame, selected_frame) + && minibuf_level > 0)) + Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); + } +} + +/* If `minibuffer_follows_frame' and we have a minibuffer, move it + from its current frame to the selected frame. This function is + intended to be called from `do_switch_frame' in frame.c. */ +void move_minibuffer_onto_frame (void) +{ + if (!minibuf_level) + return; + if (!minibuf_follows_frame ()) + return; + if (FRAMEP (selected_frame) + && FRAME_LIVE_P (XFRAME (selected_frame)) + && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) + { + struct frame *sf = XFRAME (selected_frame); + Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; + struct frame *of = XFRAME (old_frame); + Lisp_Object buffer = XWINDOW (minibuf_window)->contents; + + set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + minibuf_window = sf->minibuffer_window; + Fset_frame_selected_window (selected_frame, sf->minibuffer_window, + Qnil); + set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); + } } DEFUN ("active-minibuffer-window", Factive_minibuffer_window, @@ -362,9 +402,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histstring; Lisp_Object histval; - Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; - specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -416,11 +453,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, { Lisp_Object str = build_string ("Command attempted to use minibuffer while in minibuffer"); - if (EQ (selected_window, minibuf_window)) - Fsignal (Quser_error, (list1 (str))); + if (!minibuf_follows_frame () + || EQ (selected_window, minibuf_window)) + Fsignal (Quser_error, (list1 (str))); else - /* If we're in another window, cancel the minibuffer that's active. */ - Fthrow (Qexit, str); + /* If we're in another window, cancel the minibuffer that's active. */ + Fthrow (Qexit, str); } if ((noninteractive @@ -433,6 +471,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return unbind_to (count, val); } + minibuf_level++; /* Before calling choose_minibuf_frame. */ + /* Choose the minibuffer window and frame, and take action on them. */ /* Prepare for restoring the current buffer since choose_minibuf_frame @@ -484,7 +524,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, = Fcons (Fthis_command_keys_vector (), minibuf_save_list); record_unwind_protect_void (read_minibuf_unwind); - minibuf_level++; /* We are exiting the minibuffer one way or the other, so run the hook. It should be run before unwinding the minibuf settings. Do it separately from read_minibuf_unwind because we need to make sure that @@ -566,23 +605,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level == 1 || !EQ (minibuf_window, selected_window)) minibuf_selected_window = selected_window; - /* Empty out the minibuffers of all frames other than the one - where we are going to display one now. - Set them to point to ` *Minibuf-0*', which is always empty. */ - empty_minibuf = get_minibuffer (0); - - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -1911,6 +1933,8 @@ syms_of_minibuf (void) staticpro (&minibuf_prompt); staticpro (&minibuf_save_list); + DEFSYM (Qminibuffer_follows_frame, + "minibuffer-follows-frame"); DEFSYM (Qcompletion_ignore_case, "completion-ignore-case"); DEFSYM (Qminibuffer_default, "minibuffer-default"); Fset (Qminibuffer_default, Qnil); @@ -1954,6 +1978,14 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; + DEFVAR_BOOL ("minibuffer-follows-frame", minibuffer_follows_frame, + doc: /* Non-nil means an open minibuffer will move to a newly selected frame. +Nil means that a minibuffer will appear only in the frame which created it. + +Any buffer local or dynamic binding of this variable is ignored. Only the +default top level value is used. */); + minibuffer_follows_frame = 1; + DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, doc: /* Non-nil means completion ignores case when reading a buffer name. */); -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-21 20:04 ` Alan Mackenzie @ 2020-10-22 16:14 ` Eli Zaretskii 2020-10-30 22:09 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-22 16:14 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Wed, 21 Oct 2020 20:04:38 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > Here's a corrected version of the patch, incorporating fixes and > suggestions from Stefan and Drew: Thanks, I have a few minor comments: > +the user option @code{minibuffer-follows-frame} to @code{nil}, then I'd prefer to name the option minibuffer-follows-selected-frame. > ++++ > +** Switching frames when a minibuffer is active has been rationalized. "Rationalized"? How about Minibuffer behavior when selected frame changes can now be controlled. ? > +By default, the active minibuffer is moved to the newly selected > +frame. When the current command is continued (by completing the > +minibuffer action), it takes effect in the frame where the minibuffer > +was first opened. An alternative behavior is available by customizing > +'minibuffer-follows-frame' to nil. Here, the minibuffer stays in the > +frame where it was first opened, and you must switch back to this > +frame to continue or abort the current command. The old (pre Emacs > +28.1) somewhat unsystematic behavior is no longer available. Can you reword to use less of passive tense here? I may be missing something: where does this code handles the case of minibuffer-only frames? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-22 16:14 ` Eli Zaretskii @ 2020-10-30 22:09 ` Alan Mackenzie 2020-10-31 7:25 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-30 22:09 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Thu, Oct 22, 2020 at 19:14:09 +0300, Eli Zaretskii wrote: > > Date: Wed, 21 Oct 2020 20:04:38 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > Here's a corrected version of the patch, incorporating fixes and > > suggestions from Stefan and Drew: And below is a further amended patch. > Thanks, I have a few minor comments: > > +the user option @code{minibuffer-follows-frame} to @code{nil}, then > I'd prefer to name the option minibuffer-follows-selected-frame. OK. I've changed it. > > ++++ > > +** Switching frames when a minibuffer is active has been rationalized. > "Rationalized"? How about > Minibuffer behavior when selected frame changes can now be controlled. > ? What I've put in is ** Improved handling of minibuffers on switching frames. . How about that? > > +By default, the active minibuffer is moved to the newly selected > > +frame. When the current command is continued (by completing the > > +minibuffer action), it takes effect in the frame where the minibuffer > > +was first opened. An alternative behavior is available by customizing > > +'minibuffer-follows-frame' to nil. Here, the minibuffer stays in the > > +frame where it was first opened, and you must switch back to this > > +frame to continue or abort the current command. The old (pre Emacs > > +28.1) somewhat unsystematic behavior is no longer available. > Can you reword to use less of passive tense here? DONE. > I may be missing something: where does this code handles the case of > minibuffer-only frames? The previous version didn't, really, very much. The current version of the patch contains quite a few changes, for example to suppress the display of a ghost cursor in what had been a miniwindow. I've tested a little bit with an unusual configuration, 4 frames, of which 2 are without minibuffers, one is a MB, and the last is a "normal" frame. I don't see anything untoward except, perhaps, the action of C-x o when there are minibuffers open. But that wasn't entirely predictable on the unchanged master branch either. Here's the latest version of the patch. Comments would be appreciated: diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index 54f046a7e0..ede95a28d4 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -69,6 +69,17 @@ Basic Minibuffer the minibuffer comes back. While the minibuffer is in use, Emacs does not echo keystrokes. +@vindex minibuffer-follows-selected-frame + While using the minibuffer, you can switch to a different frame, +perhaps to note text you need to enter (@pxref{Frame Commands}). By +default, the active minibuffer moves to this new frame. If you set +the user option @code{minibuffer-follows-selected-frame} to +@code{nil}, then the minibuffer stays in the frame where you opened +it, and you must switch back to that frame in order to complete (or +abort) the current command. Note that the effect of the command, when +you finally finish using the minibuffer, always takes place in the +frame where you first opened it. + @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index 4cc66aef6b..2489706d89 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -85,6 +85,17 @@ useful on systems such as FreeBSD which ships only with "etc/termcap". \f * Changes in Emacs 28.1 ++++ +** Improved handling of minibuffers on switching frames. +By default, an active minibuffer now moves to a newly selected frame. +When continuing the current command (by completing the minibuffer +action), the effect happens in the frame where the minibuffer was +first opened. An alternative behavior is available by customizing +'minibuffer-follows-selected-frame' to nil. Here, the minibuffer +stays in the frame where you first opened it, and you must switch back +to this frame to continue or abort its command. The old, somewhat +unsystematic behavior is no longer available. + +++ ** New system for displaying documentation for groups of function. This can either be used by saying 'M-x shortdoc-display-group' and diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 6927b6df6b..04fb1dc6d0 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,6 +394,7 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c + (minibuffer-follows-selected-frame minibuffer boolean "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5a41e2f30b..9d57a817b2 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -701,7 +701,7 @@ minibuffer-message or until the next input event arrives, whichever comes first. Enclose MESSAGE in [...] if this is not yet the case. If ARGS are provided, then pass MESSAGE through `format-message'." - (if (not (minibufferp (current-buffer))) + (if (not (minibufferp (current-buffer) t)) (progn (if args (apply #'message message args) diff --git a/src/frame.c b/src/frame.c index 7c377da445..512aaf5f45 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1482,6 +1482,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor #endif internal_last_event_frame = Qnil; + move_minibuffer_onto_frame (); return frame; } diff --git a/src/lisp.h b/src/lisp.h index 45353fbef3..548eebc9c7 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4336,6 +4336,8 @@ extern void clear_regexp_cache (void); extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; +extern void move_minibuffer_onto_frame (void); +extern bool is_minibuffer (EMACS_INT, Lisp_Object); extern Lisp_Object get_minibuffer (EMACS_INT); extern void init_minibuf_once (void); extern void syms_of_minibuf (void); diff --git a/src/minibuf.c b/src/minibuf.c index f957b2ae17..11b5eec236 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -64,6 +64,12 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; \f +static bool +minibuf_follows_frame (void) +{ + return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + /* Put minibuf on currently selected frame's minibuffer. We do this whenever the user starts a new minibuffer or when a minibuffer exits. */ @@ -76,37 +82,73 @@ choose_minibuf_frame (void) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { struct frame *sf = XFRAME (selected_frame); - Lisp_Object buffer; - /* I don't think that any frames may validly have a null minibuffer window anymore. */ if (NILP (sf->minibuffer_window)) emacs_abort (); - /* Under X, we come here with minibuf_window being the - minibuffer window of the unused termcap window created in - init_window_once. That window doesn't have a buffer. */ - buffer = XWINDOW (minibuf_window)->contents; - if (BUFFERP (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); - minibuf_window = sf->minibuffer_window; + if (minibuf_follows_frame ()) + minibuf_window = sf->minibuffer_window; + else if (minibuf_level) + { + Lisp_Object buffer = get_minibuffer (minibuf_level); + Lisp_Object tail, frame; + + minibuf_window = sf->minibuffer_window; + FOR_EACH_FRAME (tail, frame) + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, + buffer)) + { + minibuf_window = XFRAME (frame)->minibuffer_window; + break; + } + } + else + minibuf_window = Qnil; } - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; + if (minibuf_follows_frame ()) + /* Make sure no other frame has a minibuffer as its selected window, + because the text would not be displayed in it, and that would be + confusing. Only allow the selected frame to do this, + and that only if the minibuffer is active. */ + { + Lisp_Object tail, frame; - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); - } + FOR_EACH_FRAME (tail, frame) + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) + && !(EQ (frame, selected_frame) + && minibuf_level > 0)) + Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); + } +} + +/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it + from its current frame to the selected frame. This function is + intended to be called from `do_switch_frame' in frame.c. */ +void move_minibuffer_onto_frame (void) +{ + if (!minibuf_level) + return; + if (!minibuf_follows_frame ()) + return; + if (FRAMEP (selected_frame) + && FRAME_LIVE_P (XFRAME (selected_frame)) + && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) + { + struct frame *sf = XFRAME (selected_frame); + Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; + struct frame *of = XFRAME (old_frame); + Lisp_Object buffer = XWINDOW (minibuf_window)->contents; + + set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + minibuf_window = sf->minibuffer_window; + if (XWINDOW (minibuf_window)->frame == selected_frame) + /* The minibuffer might be on another frame. */ + Fset_frame_selected_window (selected_frame, sf->minibuffer_window, + Qnil); + set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); + } } DEFUN ("active-minibuffer-window", Factive_minibuffer_window, @@ -262,13 +304,15 @@ read_minibuf_noninteractive (Lisp_Object prompt, bool expflag, } \f DEFUN ("minibufferp", Fminibufferp, - Sminibufferp, 0, 1, 0, + Sminibufferp, 0, 2, 0, doc: /* Return t if BUFFER is a minibuffer. No argument or nil as argument means use current buffer as BUFFER. -BUFFER can be a buffer or a buffer name. */) - (Lisp_Object buffer) +BUFFER can be a buffer or a buffer name. If LIVE is non-nil, then +t will be returned only if BUFFER is an active minibuffer. */) + (Lisp_Object buffer, Lisp_Object live) { Lisp_Object tem; + EMACS_INT i; if (NILP (buffer)) buffer = Fcurrent_buffer (); @@ -277,8 +321,22 @@ BUFFER can be a buffer or a buffer name. */) else CHECK_BUFFER (buffer); - tem = Fmemq (buffer, Vminibuffer_list); - return ! NILP (tem) ? Qt : Qnil; + /* tem = Fmemq (buffer, Vminibuffer_list); */ + /* return (!NILP (tem) && !EQ (tem, Vminibuffer_list)) ? Qt : Qnil; */ + if (!NILP (live)) + return !NILP (Fmemq (buffer, Vminibuffer_list)) ? Qt : Qnil; + if (EQ (buffer, Fcar (Vminibuffer_list))) + /* *Minibuf-0* is never active. */ + return Qnil; + tem = Fcdr (Vminibuffer_list); + for (i = 1; i <= minibuf_level; i++) + { + if (NILP (tem)) + return Qnil; + if (EQ (Fcar (tem), buffer)) + return Qt; + } + return Qnil; } DEFUN ("minibuffer-prompt-end", Fminibuffer_prompt_end, @@ -362,9 +420,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histstring; Lisp_Object histval; - Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; - specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -416,11 +471,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, { Lisp_Object str = build_string ("Command attempted to use minibuffer while in minibuffer"); - if (EQ (selected_window, minibuf_window)) - Fsignal (Quser_error, (list1 (str))); + if (!minibuf_follows_frame () + || EQ (selected_window, minibuf_window)) + Fsignal (Quser_error, (list1 (str))); else - /* If we're in another window, cancel the minibuffer that's active. */ - Fthrow (Qexit, str); + /* If we're in another window, cancel the minibuffer that's active. */ + Fthrow (Qexit, str); } if ((noninteractive @@ -433,6 +489,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return unbind_to (count, val); } + minibuf_level++; /* Before calling choose_minibuf_frame. */ + /* Choose the minibuffer window and frame, and take action on them. */ /* Prepare for restoring the current buffer since choose_minibuf_frame @@ -484,7 +542,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, = Fcons (Fthis_command_keys_vector (), minibuf_save_list); record_unwind_protect_void (read_minibuf_unwind); - minibuf_level++; /* We are exiting the minibuffer one way or the other, so run the hook. It should be run before unwinding the minibuf settings. Do it separately from read_minibuf_unwind because we need to make sure that @@ -566,23 +623,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level == 1 || !EQ (minibuf_window, selected_window)) minibuf_selected_window = selected_window; - /* Empty out the minibuffers of all frames other than the one - where we are going to display one now. - Set them to point to ` *Minibuf-0*', which is always empty. */ - empty_minibuf = get_minibuffer (0); - - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -714,6 +754,16 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return val; } +/* Returns whether a Lisp_Object is a particular existing minibuffer. */ +bool +is_minibuffer (EMACS_INT depth, Lisp_Object buf) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return + !NILP (tail) + && EQ (Fcar (tail), buf); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -775,6 +825,7 @@ read_minibuf_unwind (void) { Lisp_Object old_deactivate_mark; Lisp_Object window; + Lisp_Object future_mini_window; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -809,6 +860,7 @@ read_minibuf_unwind (void) if (FRAME_LIVE_P (XFRAME (WINDOW_FRAME (XWINDOW (temp))))) minibuf_window = temp; #endif + future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ @@ -825,7 +877,8 @@ read_minibuf_unwind (void) /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ - if (minibuf_level == 0) + if (minibuf_level == 0 + || !EQ (selected_frame, WINDOW_FRAME (XWINDOW (future_mini_window)))) resize_mini_window (XWINDOW (window), 0); /* Deal with frames that should be removed when exiting the @@ -1911,6 +1964,8 @@ syms_of_minibuf (void) staticpro (&minibuf_prompt); staticpro (&minibuf_save_list); + DEFSYM (Qminibuffer_follows_selected_frame, + "minibuffer-follows-selected-frame"); DEFSYM (Qcompletion_ignore_case, "completion-ignore-case"); DEFSYM (Qminibuffer_default, "minibuffer-default"); Fset (Qminibuffer_default, Qnil); @@ -1954,6 +2009,14 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; + DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* Non-nil means an open minibuffer will move to a newly selected frame. +Nil means that a minibuffer will appear only in the frame which created it. + +Any buffer local or dynamic binding of this variable is ignored. Only the +default top level value is used. */); + minibuffer_follows_selected_frame = 1; + DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, doc: /* Non-nil means completion ignores case when reading a buffer name. */); diff --git a/src/window.c b/src/window.c index e7433969d2..2eba509727 100644 --- a/src/window.c +++ b/src/window.c @@ -2643,8 +2643,10 @@ candidate_window_p (Lisp_Object window, Lisp_Object owindow, /* To qualify as candidate, it's not sufficient for WINDOW's frame to just share the minibuffer window - it must be active as well (see Bug#24500). */ - candidate_p = (EQ (XWINDOW (all_frames)->frame, w->frame) - || EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f))); + candidate_p = ((EQ (XWINDOW (all_frames)->frame, w->frame) + || (EQ (f->minibuffer_window, all_frames) + && EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f)))) + && !is_minibuffer (0, XWINDOW (all_frames)->contents)); else if (FRAMEP (all_frames)) candidate_p = EQ (all_frames, w->frame); diff --git a/src/xdisp.c b/src/xdisp.c index 0e5dffbe00..0dfe34a011 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -31220,7 +31220,9 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width, { *active_cursor = false; - if (MINI_WINDOW_P (w) && minibuf_level == 0) + if (MINI_WINDOW_P (w) && + (minibuf_level == 0 + || is_minibuffer (0, w->contents))) return NO_CURSOR; non_selected = true; -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-30 22:09 ` Alan Mackenzie @ 2020-10-31 7:25 ` Eli Zaretskii 2020-10-31 16:14 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-31 7:25 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Fri, 30 Oct 2020 22:09:17 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > DEFUN ("minibufferp", Fminibufferp, > - Sminibufferp, 0, 1, 0, > + Sminibufferp, 0, 2, 0, > doc: /* Return t if BUFFER is a minibuffer. > No argument or nil as argument means use current buffer as BUFFER. > -BUFFER can be a buffer or a buffer name. */) > - (Lisp_Object buffer) > +BUFFER can be a buffer or a buffer name. If LIVE is non-nil, then > +t will be returned only if BUFFER is an active minibuffer. */) > + (Lisp_Object buffer, Lisp_Object live) > { This uses passive tense in the last sentence. > + /* tem = Fmemq (buffer, Vminibuffer_list); */ > + /* return (!NILP (tem) && !EQ (tem, Vminibuffer_list)) ? Qt : Qnil; */ This seems to be a leftover from developing the new code > + if (!NILP (live)) > + return !NILP (Fmemq (buffer, Vminibuffer_list)) ? Qt : Qnil; > + if (EQ (buffer, Fcar (Vminibuffer_list))) > + /* *Minibuf-0* is never active. */ > + return Qnil; > + tem = Fcdr (Vminibuffer_list); > + for (i = 1; i <= minibuf_level; i++) > + { > + if (NILP (tem)) > + return Qnil; Why is this test inside the loop? > @@ -416,11 +471,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, > { > Lisp_Object str > = build_string ("Command attempted to use minibuffer while in minibuffer"); > - if (EQ (selected_window, minibuf_window)) > - Fsignal (Quser_error, (list1 (str))); > + if (!minibuf_follows_frame () > + || EQ (selected_window, minibuf_window)) > + Fsignal (Quser_error, (list1 (str))); I don't think I understand this change: what does minibuffer-follows-selected-frame have to do with recursive minibuffer usage? They are two independent features. > - /* Empty out the minibuffers of all frames other than the one > - where we are going to display one now. > - Set them to point to ` *Minibuf-0*', which is always empty. */ > - empty_minibuf = get_minibuffer (0); > - > - FOR_EACH_FRAME (dummy, frame) > - { > - Lisp_Object root_window = Fframe_root_window (frame); > - Lisp_Object mini_window = XWINDOW (root_window)->next; > - > - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) > - && !NILP (Fwindow_minibuffer_p (mini_window))) > - /* Use set_window_buffer instead of Fset_window_buffer (see > - discussion of bug#11984, bug#12025, bug#12026). */ > - set_window_buffer (mini_window, empty_minibuf, 0, 0); > - } Does this mean the minibuffers on other frames will now not be emptied? > +/* Returns whether a Lisp_Object is a particular existing minibuffer. */ Our style of comments in these cases is like this: "Return non-zero if BUF is a particular existing minibuffer." > + DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, > + doc: /* Non-nil means an open minibuffer will move to a newly selected frame. Talking about "moving" and "newly selected" here might come as a surprise, because the context was not described. How about Non-nil means active minibuffer always displays on the selected frame. Thanks. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-31 7:25 ` Eli Zaretskii @ 2020-10-31 16:14 ` Alan Mackenzie 2020-10-31 16:45 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-31 16:14 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. Thanks for such a quick reply to my last post. On Sat, Oct 31, 2020 at 09:25:00 +0200, Eli Zaretskii wrote: > > Date: Fri, 30 Oct 2020 22:09:17 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > DEFUN ("minibufferp", Fminibufferp, > > - Sminibufferp, 0, 1, 0, > > + Sminibufferp, 0, 2, 0, > > doc: /* Return t if BUFFER is a minibuffer. > > No argument or nil as argument means use current buffer as BUFFER. > > -BUFFER can be a buffer or a buffer name. */) > > - (Lisp_Object buffer) > > +BUFFER can be a buffer or a buffer name. If LIVE is non-nil, then > > +t will be returned only if BUFFER is an active minibuffer. */) > > + (Lisp_Object buffer, Lisp_Object live) > > { > This uses passive tense in the last sentence. Fixed. I've also added a bit to lispref/minibuf.texi for the new argument. (See amended patch below). > > + /* tem = Fmemq (buffer, Vminibuffer_list); */ > > + /* return (!NILP (tem) && !EQ (tem, Vminibuffer_list)) ? Qt : Qnil; */ > This seems to be a leftover from developing the new code Yes, sorry about that. I've now removed it. > > + if (!NILP (live)) > > + return !NILP (Fmemq (buffer, Vminibuffer_list)) ? Qt : Qnil; > > + if (EQ (buffer, Fcar (Vminibuffer_list))) > > + /* *Minibuf-0* is never active. */ > > + return Qnil; > > + tem = Fcdr (Vminibuffer_list); > > + for (i = 1; i <= minibuf_level; i++) > > + { > > + if (NILP (tem)) > > + return Qnil; > Why is this test inside the loop? What was wrong here was forgetting to (cdr tem) each time through the loop. I've fixed that. I've also removed the test, since it really isn't needed. Also in that snippet, the (!NILP (live)) was the wrong way round, and I've corrected it to (NILP (live)). > > @@ -416,11 +471,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, > > { > > Lisp_Object str > > = build_string ("Command attempted to use minibuffer while in minibuffer"); > > - if (EQ (selected_window, minibuf_window)) > > - Fsignal (Quser_error, (list1 (str))); > > + if (!minibuf_follows_frame () > > + || EQ (selected_window, minibuf_window)) > > + Fsignal (Quser_error, (list1 (str))); > I don't think I understand this change: what does > minibuffer-follows-selected-frame have to do with recursive minibuffer > usage? They are two independent features. They're not as independent as all that. The existing logic appeared to say "if we're in the miniwindow, just abort the current command, otherwise abort all nested commands.". Extending that logic to when we have several miniwindows, we'd (perhaps) get "if we're in _A_ miniwindow just abort the current command.". Which, further extended, goes "if we're in ANY window, just abort the current command.". I agree, this is an independent feature from the main one. But it brings consistency (and, possibly, usability) to this abort facility. I can put this back to more or less what it was. But why do we abort the whole command stack when there's just a single error? > > - /* Empty out the minibuffers of all frames other than the one > > - where we are going to display one now. > > - Set them to point to ` *Minibuf-0*', which is always empty. */ > > - empty_minibuf = get_minibuffer (0); > > - > > - FOR_EACH_FRAME (dummy, frame) > > - { > > - Lisp_Object root_window = Fframe_root_window (frame); > > - Lisp_Object mini_window = XWINDOW (root_window)->next; > > - > > - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) > > - && !NILP (Fwindow_minibuffer_p (mini_window))) > > - /* Use set_window_buffer instead of Fset_window_buffer (see > > - discussion of bug#11984, bug#12025, bug#12026). */ > > - set_window_buffer (mini_window, empty_minibuf, 0, 0); > > - } > Does this mean the minibuffers on other frames will now not be emptied? Yes, indeed. If minibuffer-follows-selected-frame, there'll only be a single mini-window, which we're about to write into, so there's no point emptying out a null set of other windows. If !minibuffer-follows-selected-frame, we don't want to empty these other mini-windows. They're there for when the user eventually comes back to them. > > +/* Returns whether a Lisp_Object is a particular existing minibuffer. */ > Our style of comments in these cases is like this: > "Return non-zero if BUF is a particular existing minibuffer." Fixed. > > + DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, > > + doc: /* Non-nil means an open minibuffer will move to a newly selected frame. > Talking about "moving" and "newly selected" here might come as a > surprise, because the context was not described. How about > Non-nil means active minibuffer always displays on the selected frame. OK, I've put that in with a "the" before "active minibuffer", to be definite we're only talking about the innermost minibuffer. > Thanks. OK, here's the amended patch: diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index 54f046a7e0..ede95a28d4 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -69,6 +69,17 @@ Basic Minibuffer the minibuffer comes back. While the minibuffer is in use, Emacs does not echo keystrokes. +@vindex minibuffer-follows-selected-frame + While using the minibuffer, you can switch to a different frame, +perhaps to note text you need to enter (@pxref{Frame Commands}). By +default, the active minibuffer moves to this new frame. If you set +the user option @code{minibuffer-follows-selected-frame} to +@code{nil}, then the minibuffer stays in the frame where you opened +it, and you must switch back to that frame in order to complete (or +abort) the current command. Note that the effect of the command, when +you finally finish using the minibuffer, always takes place in the +frame where you first opened it. + @node Minibuffer File @section Minibuffers for File Names diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index e5a0233b3c..b6a3434d15 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -2586,10 +2586,12 @@ Recursive Mini @node Minibuffer Misc @section Minibuffer Miscellany -@defun minibufferp &optional buffer-or-name +@defun minibufferp &optional buffer-or-name live This function returns non-@code{nil} if @var{buffer-or-name} is a -minibuffer. If @var{buffer-or-name} is omitted, it tests the current -buffer. +minibuffer. If @var{buffer-or-name} is omitted or @code{nil}, it +tests the current buffer. When @var{live} is non-@code{nil}, the +function returns non-@code{nil} only when @var{buffer-or-name} is an +active minibuffer. @end defun @defvar minibuffer-setup-hook diff --git a/etc/NEWS b/etc/NEWS index 4cc66aef6b..2489706d89 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -85,6 +85,17 @@ useful on systems such as FreeBSD which ships only with "etc/termcap". \f * Changes in Emacs 28.1 ++++ +** Improved handling of minibuffers on switching frames. +By default, an active minibuffer now moves to a newly selected frame. +When continuing the current command (by completing the minibuffer +action), the effect happens in the frame where the minibuffer was +first opened. An alternative behavior is available by customizing +'minibuffer-follows-selected-frame' to nil. Here, the minibuffer +stays in the frame where you first opened it, and you must switch back +to this frame to continue or abort its command. The old, somewhat +unsystematic behavior is no longer available. + +++ ** New system for displaying documentation for groups of function. This can either be used by saying 'M-x shortdoc-display-group' and diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 6927b6df6b..04fb1dc6d0 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,6 +394,7 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c + (minibuffer-follows-selected-frame minibuffer boolean "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5a41e2f30b..9d57a817b2 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -701,7 +701,7 @@ minibuffer-message or until the next input event arrives, whichever comes first. Enclose MESSAGE in [...] if this is not yet the case. If ARGS are provided, then pass MESSAGE through `format-message'." - (if (not (minibufferp (current-buffer))) + (if (not (minibufferp (current-buffer) t)) (progn (if args (apply #'message message args) diff --git a/src/frame.c b/src/frame.c index 7c377da445..512aaf5f45 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1482,6 +1482,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor #endif internal_last_event_frame = Qnil; + move_minibuffer_onto_frame (); return frame; } diff --git a/src/lisp.h b/src/lisp.h index 45353fbef3..548eebc9c7 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4336,6 +4336,8 @@ extern void clear_regexp_cache (void); extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; +extern void move_minibuffer_onto_frame (void); +extern bool is_minibuffer (EMACS_INT, Lisp_Object); extern Lisp_Object get_minibuffer (EMACS_INT); extern void init_minibuf_once (void); extern void syms_of_minibuf (void); diff --git a/src/minibuf.c b/src/minibuf.c index f957b2ae17..ff3201b4ae 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -64,6 +64,12 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; \f +static bool +minibuf_follows_frame (void) +{ + return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + /* Put minibuf on currently selected frame's minibuffer. We do this whenever the user starts a new minibuffer or when a minibuffer exits. */ @@ -76,37 +82,72 @@ choose_minibuf_frame (void) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { struct frame *sf = XFRAME (selected_frame); - Lisp_Object buffer; - /* I don't think that any frames may validly have a null minibuffer window anymore. */ if (NILP (sf->minibuffer_window)) emacs_abort (); - /* Under X, we come here with minibuf_window being the - minibuffer window of the unused termcap window created in - init_window_once. That window doesn't have a buffer. */ - buffer = XWINDOW (minibuf_window)->contents; - if (BUFFERP (buffer)) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); minibuf_window = sf->minibuffer_window; + /* If we've still got another minibuffer open, use its mini-window + instead. */ + if (minibuf_level && !minibuf_follows_frame ()) + { + Lisp_Object buffer = get_minibuffer (minibuf_level); + Lisp_Object tail, frame; + + FOR_EACH_FRAME (tail, frame) + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, + buffer)) + { + minibuf_window = XFRAME (frame)->minibuffer_window; + break; + } + } } - /* Make sure no other frame has a minibuffer as its selected window, - because the text would not be displayed in it, and that would be - confusing. Only allow the selected frame to do this, - and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; + if (minibuf_follows_frame ()) + /* Make sure no other frame has a minibuffer as its selected window, + because the text would not be displayed in it, and that would be + confusing. Only allow the selected frame to do this, + and that only if the minibuffer is active. */ + { + Lisp_Object tail, frame; + + FOR_EACH_FRAME (tail, frame) + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) + && !(EQ (frame, selected_frame) + && minibuf_level > 0)) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + } +} - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil); - } +/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it + from its current frame to the selected frame. This function is + intended to be called from `do_switch_frame' in frame.c. */ +void move_minibuffer_onto_frame (void) +{ + if (!minibuf_level) + return; + if (!minibuf_follows_frame ()) + return; + if (FRAMEP (selected_frame) + && FRAME_LIVE_P (XFRAME (selected_frame)) + && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) + { + struct frame *sf = XFRAME (selected_frame); + Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; + struct frame *of = XFRAME (old_frame); + Lisp_Object buffer = XWINDOW (minibuf_window)->contents; + + set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + minibuf_window = sf->minibuffer_window; + if (XWINDOW (minibuf_window)->frame == selected_frame) + /* The minibuffer might be on another frame. */ + Fset_frame_selected_window (selected_frame, sf->minibuffer_window, + Qnil); + set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); + } } DEFUN ("active-minibuffer-window", Factive_minibuffer_window, @@ -262,13 +303,15 @@ read_minibuf_noninteractive (Lisp_Object prompt, bool expflag, } \f DEFUN ("minibufferp", Fminibufferp, - Sminibufferp, 0, 1, 0, + Sminibufferp, 0, 2, 0, doc: /* Return t if BUFFER is a minibuffer. No argument or nil as argument means use current buffer as BUFFER. -BUFFER can be a buffer or a buffer name. */) - (Lisp_Object buffer) +BUFFER can be a buffer or a buffer name. If LIVE is non-nil, then +return t only if BUFFER is an active minibuffer. */) + (Lisp_Object buffer, Lisp_Object live) { Lisp_Object tem; + EMACS_INT i; if (NILP (buffer)) buffer = Fcurrent_buffer (); @@ -277,8 +320,16 @@ BUFFER can be a buffer or a buffer name. */) else CHECK_BUFFER (buffer); - tem = Fmemq (buffer, Vminibuffer_list); - return ! NILP (tem) ? Qt : Qnil; + if (NILP (live)) + return !NILP (Fmemq (buffer, Vminibuffer_list)) ? Qt : Qnil; + if (EQ (buffer, Fcar (Vminibuffer_list))) + /* *Minibuf-0* is never active. */ + return Qnil; + tem = Fcdr (Vminibuffer_list); + for (i = 1; i <= minibuf_level; i++, tem = Fcdr (tem)) + if (EQ (Fcar (tem), buffer)) + return Qt; + return Qnil; } DEFUN ("minibuffer-prompt-end", Fminibuffer_prompt_end, @@ -362,9 +413,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object histstring; Lisp_Object histval; - Lisp_Object empty_minibuf; - Lisp_Object dummy, frame; - specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); @@ -416,11 +464,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, { Lisp_Object str = build_string ("Command attempted to use minibuffer while in minibuffer"); - if (EQ (selected_window, minibuf_window)) - Fsignal (Quser_error, (list1 (str))); + if (!minibuf_follows_frame () + || EQ (selected_window, minibuf_window)) + Fsignal (Quser_error, (list1 (str))); else - /* If we're in another window, cancel the minibuffer that's active. */ - Fthrow (Qexit, str); + /* If we're in another window, cancel the minibuffer that's active. */ + Fthrow (Qexit, str); } if ((noninteractive @@ -433,6 +482,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return unbind_to (count, val); } + minibuf_level++; /* Before calling choose_minibuf_frame. */ + /* Choose the minibuffer window and frame, and take action on them. */ /* Prepare for restoring the current buffer since choose_minibuf_frame @@ -484,7 +535,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, = Fcons (Fthis_command_keys_vector (), minibuf_save_list); record_unwind_protect_void (read_minibuf_unwind); - minibuf_level++; /* We are exiting the minibuffer one way or the other, so run the hook. It should be run before unwinding the minibuf settings. Do it separately from read_minibuf_unwind because we need to make sure that @@ -566,23 +616,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level == 1 || !EQ (minibuf_window, selected_window)) minibuf_selected_window = selected_window; - /* Empty out the minibuffers of all frames other than the one - where we are going to display one now. - Set them to point to ` *Minibuf-0*', which is always empty. */ - empty_minibuf = get_minibuffer (0); - - FOR_EACH_FRAME (dummy, frame) - { - Lisp_Object root_window = Fframe_root_window (frame); - Lisp_Object mini_window = XWINDOW (root_window)->next; - - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); - } - /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -714,6 +747,16 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, return val; } +/* Return true if BUF is a particular existing minibuffer. */ +bool +is_minibuffer (EMACS_INT depth, Lisp_Object buf) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return + !NILP (tail) + && EQ (Fcar (tail), buf); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -775,6 +818,7 @@ read_minibuf_unwind (void) { Lisp_Object old_deactivate_mark; Lisp_Object window; + Lisp_Object future_mini_window; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -809,6 +853,7 @@ read_minibuf_unwind (void) if (FRAME_LIVE_P (XFRAME (WINDOW_FRAME (XWINDOW (temp))))) minibuf_window = temp; #endif + future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ @@ -825,7 +870,8 @@ read_minibuf_unwind (void) /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ - if (minibuf_level == 0) + if (minibuf_level == 0 + || !EQ (selected_frame, WINDOW_FRAME (XWINDOW (future_mini_window)))) resize_mini_window (XWINDOW (window), 0); /* Deal with frames that should be removed when exiting the @@ -1911,6 +1957,8 @@ syms_of_minibuf (void) staticpro (&minibuf_prompt); staticpro (&minibuf_save_list); + DEFSYM (Qminibuffer_follows_selected_frame, + "minibuffer-follows-selected-frame"); DEFSYM (Qcompletion_ignore_case, "completion-ignore-case"); DEFSYM (Qminibuffer_default, "minibuffer-default"); Fset (Qminibuffer_default, Qnil); @@ -1954,6 +2002,14 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; + DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* Non-nil means the active minibuffer always displays on the selected frame. +Nil means that a minibuffer will appear only in the frame which created it. + +Any buffer local or dynamic binding of this variable is ignored. Only the +default top level value is used. */); + minibuffer_follows_selected_frame = 1; + DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, doc: /* Non-nil means completion ignores case when reading a buffer name. */); diff --git a/src/window.c b/src/window.c index e7433969d2..2eba509727 100644 --- a/src/window.c +++ b/src/window.c @@ -2643,8 +2643,10 @@ candidate_window_p (Lisp_Object window, Lisp_Object owindow, /* To qualify as candidate, it's not sufficient for WINDOW's frame to just share the minibuffer window - it must be active as well (see Bug#24500). */ - candidate_p = (EQ (XWINDOW (all_frames)->frame, w->frame) - || EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f))); + candidate_p = ((EQ (XWINDOW (all_frames)->frame, w->frame) + || (EQ (f->minibuffer_window, all_frames) + && EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f)))) + && !is_minibuffer (0, XWINDOW (all_frames)->contents)); else if (FRAMEP (all_frames)) candidate_p = EQ (all_frames, w->frame); diff --git a/src/xdisp.c b/src/xdisp.c index 0e5dffbe00..0dfe34a011 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -31220,7 +31220,9 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width, { *active_cursor = false; - if (MINI_WINDOW_P (w) && minibuf_level == 0) + if (MINI_WINDOW_P (w) && + (minibuf_level == 0 + || is_minibuffer (0, w->contents))) return NO_CURSOR; non_selected = true; -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-31 16:14 ` Alan Mackenzie @ 2020-10-31 16:45 ` Eli Zaretskii 2020-10-31 19:44 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-31 16:45 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Sat, 31 Oct 2020 16:14:22 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > @@ -416,11 +471,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, > > > { > > > Lisp_Object str > > > = build_string ("Command attempted to use minibuffer while in minibuffer"); > > > - if (EQ (selected_window, minibuf_window)) > > > - Fsignal (Quser_error, (list1 (str))); > > > + if (!minibuf_follows_frame () > > > + || EQ (selected_window, minibuf_window)) > > > + Fsignal (Quser_error, (list1 (str))); > > > I don't think I understand this change: what does > > minibuffer-follows-selected-frame have to do with recursive minibuffer > > usage? They are two independent features. > > They're not as independent as all that. The existing logic appeared to > say "if we're in the miniwindow, just abort the current command, > otherwise abort all nested commands.". Extending that logic to when we > have several miniwindows, we'd (perhaps) get "if we're in _A_ miniwindow > just abort the current command.". > > Which, further extended, goes "if we're in ANY window, just abort the > current command.". I agree, this is an independent feature from the > main one. But it brings consistency (and, possibly, usability) to this > abort facility. I can put this back to more or less what it was. But > why do we abort the whole command stack when there's just a single > error? Sorry, you've lost me here. The existing logic is: if we are in a minibuffer with minibuf_level > 1, then we throw to top-level, either by signaling a user-error or silently. Your change introduced the call to minibuf_follows_frame into this equation, and I just cannot understand what business does it have here? Recursive minibuffers should be "verboten" regardless of whether the minibuffer follows the selected frame or not. What am I missing here? > > > - /* Empty out the minibuffers of all frames other than the one > > > - where we are going to display one now. > > > - Set them to point to ` *Minibuf-0*', which is always empty. */ > > > - empty_minibuf = get_minibuffer (0); > > > - > > > - FOR_EACH_FRAME (dummy, frame) > > > - { > > > - Lisp_Object root_window = Fframe_root_window (frame); > > > - Lisp_Object mini_window = XWINDOW (root_window)->next; > > > - > > > - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) > > > - && !NILP (Fwindow_minibuffer_p (mini_window))) > > > - /* Use set_window_buffer instead of Fset_window_buffer (see > > > - discussion of bug#11984, bug#12025, bug#12026). */ > > > - set_window_buffer (mini_window, empty_minibuf, 0, 0); > > > - } > > > Does this mean the minibuffers on other frames will now not be emptied? > > Yes, indeed. If minibuffer-follows-selected-frame, there'll only be a > single mini-window, which we're about to write into, so there's no point > emptying out a null set of other windows. That's the intention, perhaps, but are we really 110% sure this will happen? And where's the alternative code which will make sure the other minibuffers are cleared in this case? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-31 16:45 ` Eli Zaretskii @ 2020-10-31 19:44 ` Alan Mackenzie 2020-10-31 20:00 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-31 19:44 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello again, Eli. On Sat, Oct 31, 2020 at 18:45:19 +0200, Eli Zaretskii wrote: > > Date: Sat, 31 Oct 2020 16:14:22 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > @@ -416,11 +471,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, > > > > { > > > > Lisp_Object str > > > > = build_string ("Command attempted to use minibuffer while in minibuffer"); > > > > - if (EQ (selected_window, minibuf_window)) > > > > - Fsignal (Quser_error, (list1 (str))); > > > > + if (!minibuf_follows_frame () > > > > + || EQ (selected_window, minibuf_window)) > > > > + Fsignal (Quser_error, (list1 (str))); > > > I don't think I understand this change: what does > > > minibuffer-follows-selected-frame have to do with recursive minibuffer > > > usage? They are two independent features. > > They're not as independent as all that. The existing logic appeared to > > say "if we're in the miniwindow, just abort the current command, > > otherwise abort all nested commands.". Extending that logic to when we > > have several miniwindows, we'd (perhaps) get "if we're in _A_ miniwindow > > just abort the current command.". > > Which, further extended, goes "if we're in ANY window, just abort the > > current command.". I agree, this is an independent feature from the > > main one. But it brings consistency (and, possibly, usability) to this > > abort facility. I can put this back to more or less what it was. But > > why do we abort the whole command stack when there's just a single > > error? > Sorry, you've lost me here. The existing logic is: if we are in a > minibuffer with minibuf_level > 1, then we throw to top-level, either > by signaling a user-error or silently. Your change introduced the > call to minibuf_follows_frame into this equation, and I just cannot > understand what business does it have here? Recursive minibuffers > should be "verboten" regardless of whether the minibuffer follows the > selected frame or not. What am I missing here? Maybe I'm not being coherent. I think it would be better not to abort the first command when a user accidentally tries to invoke a recursive minibuffer. But it's not a big point. I can't remember very clearly, but I think I made this change early on in the project because the Fthrow (Qexit, str); left some mini-windows in a messy state; or something like that. That doesn't happen any more. So, maybe I should just remove this hunk from the proposed patch. It doesn't seem that important any more. > > > > - /* Empty out the minibuffers of all frames other than the one > > > > - where we are going to display one now. > > > > - Set them to point to ` *Minibuf-0*', which is always empty. */ > > > > - empty_minibuf = get_minibuffer (0); > > > > - > > > > - FOR_EACH_FRAME (dummy, frame) > > > > - { > > > > - Lisp_Object root_window = Fframe_root_window (frame); > > > > - Lisp_Object mini_window = XWINDOW (root_window)->next; > > > > - > > > > - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) > > > > - && !NILP (Fwindow_minibuffer_p (mini_window))) > > > > - /* Use set_window_buffer instead of Fset_window_buffer (see > > > > - discussion of bug#11984, bug#12025, bug#12026). */ > > > > - set_window_buffer (mini_window, empty_minibuf, 0, 0); > > > > - } > > > Does this mean the minibuffers on other frames will now not be emptied? > > Yes, indeed. If minibuffer-follows-selected-frame, there'll only be a > > single mini-window, which we're about to write into, so there's no point > > emptying out a null set of other windows. > That's the intention, perhaps, but are we really 110% sure this will > happen? And where's the alternative code which will make sure the > other minibuffers are cleared in this case? Maybe something like edebug invoked from a recursive edit when there's a minibuffer live. That could be problematic, perhaps. How about emptying mini-windows which don't have live minibuffers on them? This could be tested by Fminibufferp (b, Qt). In practice, when minibuffer-follows-selected-frame this would empty all mini-windows but the current one, and when !m-f-s-f it would leave intact the mini-windows we want to be left intact. To be honest, I think I've been seeing stale messages hanging around in echo areas, and this emptying might clear them out. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-31 19:44 ` Alan Mackenzie @ 2020-10-31 20:00 ` Eli Zaretskii 2020-10-31 20:39 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-31 20:00 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Sat, 31 Oct 2020 19:44:19 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > I can't remember very clearly, but I think I made this change early on in > the project because the Fthrow (Qexit, str); left some mini-windows in a > messy state; or something like that. That doesn't happen any more. > > So, maybe I should just remove this hunk from the proposed patch. It > doesn't seem that important any more. Fine with me. > How about emptying mini-windows which don't have live minibuffers on > them? This could be tested by Fminibufferp (b, Qt). In practice, when > minibuffer-follows-selected-frame this would empty all mini-windows but > the current one, and when !m-f-s-f it would leave intact the mini-windows > we want to be left intact. let me turn the table and ask why this hunk is needed? What doesn't work right if this code is left in place? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-31 20:00 ` Eli Zaretskii @ 2020-10-31 20:39 ` Alan Mackenzie 2020-11-01 18:35 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-10-31 20:39 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Sat, Oct 31, 2020 at 22:00:09 +0200, Eli Zaretskii wrote: > > Date: Sat, 31 Oct 2020 19:44:19 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > I can't remember very clearly, but I think I made this change early on in > > the project because the Fthrow (Qexit, str); left some mini-windows in a > > messy state; or something like that. That doesn't happen any more. > > So, maybe I should just remove this hunk from the proposed patch. It > > doesn't seem that important any more. > Fine with me. OK, that's done. > > How about emptying mini-windows which don't have live minibuffers on > > them? This could be tested by Fminibufferp (b, Qt). In practice, when > > minibuffer-follows-selected-frame this would empty all mini-windows but > > the current one, and when !m-f-s-f it would leave intact the mini-windows > > we want to be left intact. > let me turn the table and ask why this hunk is needed? What doesn't > work right if this code is left in place? Without that hunk (i.e. with the emptying-out code): (i) emacs -Q (ii) M-: (setq minibuffer-follows-selected-frame nil) (iii) C-x 5 2 ; giving two frames. (iv) C-x b ; leaving a minibuffer open. (v) C-x 5 o ; move to other frame. (vi) C-r in ; start an isearch. (vii) C-x 8 RET ; intending on inserting a non-keyboard character. At this point, the mini-window in Frame 1 is emptied. This is bad. There appears to be no way to get its minibuffer back again. By contrast, the same sequence of operations without (ii) (with an extra step: (v).5 C-x o ; move to the ordinary window. ), the first minibuffer is "protected" on Frame 2 underneath the C-x 8 RET minibuffer. So, when the isearch is finished, the "Switch to buffer" minibuffer appears again, and is active. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-31 20:39 ` Alan Mackenzie @ 2020-11-01 18:35 ` Eli Zaretskii 2020-11-01 19:53 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-01 18:35 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Sat, 31 Oct 2020 20:39:14 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > How about emptying mini-windows which don't have live minibuffers on > > > them? This could be tested by Fminibufferp (b, Qt). In practice, when > > > minibuffer-follows-selected-frame this would empty all mini-windows but > > > the current one, and when !m-f-s-f it would leave intact the mini-windows > > > we want to be left intact. > > > let me turn the table and ask why this hunk is needed? What doesn't > > work right if this code is left in place? > > Without that hunk (i.e. with the emptying-out code): > (i) emacs -Q > (ii) M-: (setq minibuffer-follows-selected-frame nil) > (iii) C-x 5 2 ; giving two frames. > (iv) C-x b ; leaving a minibuffer open. > (v) C-x 5 o ; move to other frame. > > (vi) C-r in ; start an isearch. > (vii) C-x 8 RET ; intending on inserting a non-keyboard character. > > At this point, the mini-window in Frame 1 is emptied. This is bad. But the same happens with the current master. So this is no worse than what we have today. > By contrast, the same sequence of operations without (ii) (with an extra > step: > > (v).5 C-x o ; move to the ordinary window. > > ), the first minibuffer is "protected" on Frame 2 underneath the C-x 8 > RET minibuffer. So, when the isearch is finished, the "Switch to > buffer" minibuffer appears again, and is active. Is this with or without removing the code which empties all the other minibuffers? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-01 18:35 ` Eli Zaretskii @ 2020-11-01 19:53 ` Alan Mackenzie 2020-11-02 17:19 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-01 19:53 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Sun, Nov 01, 2020 at 20:35:54 +0200, Eli Zaretskii wrote: > > Date: Sat, 31 Oct 2020 20:39:14 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > How about emptying mini-windows which don't have live minibuffers > > > > on them? This could be tested by Fminibufferp (b, Qt). In > > > > practice, when minibuffer-follows-selected-frame this would empty > > > > all mini-windows but the current one, and when !m-f-s-f it would > > > > leave intact the mini-windows we want to be left intact. > > > let me turn the table and ask why this hunk is needed? What > > > doesn't work right if this code is left in place? > > Without that hunk (i.e. with the emptying-out code): > > (i) emacs -Q > > (ii) M-: (setq minibuffer-follows-selected-frame nil) > > (iii) C-x 5 2 ; giving two frames. > > (iv) C-x b ; leaving a minibuffer open. > > (v) C-x 5 o ; move to other frame. > > (vi) C-r in ; start an isearch. > > (vii) C-x 8 RET ; intending on inserting a non-keyboard character. > > At this point, the mini-window in Frame 1 is emptied. This is bad. > But the same happens with the current master. So this is no worse than > what we have today. It might be no worse (the situations aren't exactly parallel), but I think the !minibuffer_f_s_frame case should be no worse than the m_f_s_f case. We should be aiming to improve things. The mental model for !m_f_s_f is that a minibuffer is attached to a frame. So actions in a different frame shouldn't affect it. > > By contrast, the same sequence of operations without (ii) (with an extra > > step: > > (v).5 C-x o ; move to the ordinary window. > > ), the first minibuffer is "protected" on Frame 2 underneath the C-x 8 > > RET minibuffer. So, when the isearch is finished, the "Switch to > > buffer" minibuffer appears again, and is active. > Is this with or without removing the code which empties all the other > minibuffers? This is with the emptying code present, exactly as in the previous case. I propose amending the emptying out code to look like this: /* Empty out the minibuffers of all frames, except those frames where there is an active minibuffer. Set them to point to ` *Minibuf-0*', which is always empty. */ empty_minibuf = get_minibuffer (0); FOR_EACH_FRAME (dummy, frame) { Lisp_Object root_window = Fframe_root_window (frame); Lisp_Object mini_window = XWINDOW (root_window)->next; Lisp_Object buffer; if (!NILP (mini_window) && !EQ (mini_window, minibuf_window) && !NILP (Fwindow_minibuffer_p (mini_window))) { buffer = XWINDOW (mini_window)->contents; if (NILP (Fminibufferp (buffer, Qt))) <======================== /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ set_window_buffer (mini_window, empty_minibuf, 0, 0); } } Here Fminibufferp (with the new argument Qt) tests for an active minibuffer. This code appears to work well. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-01 19:53 ` Alan Mackenzie @ 2020-11-02 17:19 ` Eli Zaretskii 2020-11-02 18:51 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-02 17:19 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Sun, 1 Nov 2020 19:53:13 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > /* Empty out the minibuffers of all frames, except those frames > where there is an active minibuffer. > Set them to point to ` *Minibuf-0*', which is always empty. */ > empty_minibuf = get_minibuffer (0); > > FOR_EACH_FRAME (dummy, frame) > { > Lisp_Object root_window = Fframe_root_window (frame); > Lisp_Object mini_window = XWINDOW (root_window)->next; > Lisp_Object buffer; > > if (!NILP (mini_window) && !EQ (mini_window, minibuf_window) > && !NILP (Fwindow_minibuffer_p (mini_window))) > { > buffer = XWINDOW (mini_window)->contents; > if (NILP (Fminibufferp (buffer, Qt))) <======================== > /* Use set_window_buffer instead of Fset_window_buffer (see > discussion of bug#11984, bug#12025, bug#12026). */ > set_window_buffer (mini_window, empty_minibuf, 0, 0); > } > } > > Here Fminibufferp (with the new argument Qt) tests for an active > minibuffer. This code appears to work well. Please just use the guts of Fminibufferp; the tests it does on its argument are not needed here. Also, won't this condition catch *Minibuf-0* as well? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-02 17:19 ` Eli Zaretskii @ 2020-11-02 18:51 ` Alan Mackenzie 2020-11-02 19:19 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-02 18:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Mon, Nov 02, 2020 at 19:19:18 +0200, Eli Zaretskii wrote: > > Date: Sun, 1 Nov 2020 19:53:13 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > /* Empty out the minibuffers of all frames, except those frames > > where there is an active minibuffer. > > Set them to point to ` *Minibuf-0*', which is always empty. */ > > empty_minibuf = get_minibuffer (0); > > > > FOR_EACH_FRAME (dummy, frame) > > { > > Lisp_Object root_window = Fframe_root_window (frame); > > Lisp_Object mini_window = XWINDOW (root_window)->next; > > Lisp_Object buffer; > > > > if (!NILP (mini_window) && !EQ (mini_window, minibuf_window) > > && !NILP (Fwindow_minibuffer_p (mini_window))) > > { > > buffer = XWINDOW (mini_window)->contents; > > if (NILP (Fminibufferp (buffer, Qt))) <======================== > > /* Use set_window_buffer instead of Fset_window_buffer (see > > discussion of bug#11984, bug#12025, bug#12026). */ > > set_window_buffer (mini_window, empty_minibuf, 0, 0); > > } > > } > > Here Fminibufferp (with the new argument Qt) tests for an active > > minibuffer. This code appears to work well. > Please just use the guts of Fminibufferp; the tests it does on its > argument are not needed here. Sorry, I can't follow you, here. What do you mean by "the guts" of Fminibufferp? What things do you mean that the word "just" should exclude? > Also, won't this condition catch *Minibuf-0* as well? The Qt argument will cause Fminibufferp to return Qnil for all buffers except *Minibuf-1*, *Minibuf-2*, ..., *Minibuf-n*, where n is minibuf-level. In particular, Qnil is returned for *Minibuf-0*, so any mini-window showing *Minibuf-0* would get emptied. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-02 18:51 ` Alan Mackenzie @ 2020-11-02 19:19 ` Eli Zaretskii 2020-11-03 21:08 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-02 19:19 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Mon, 2 Nov 2020 18:51:47 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > Please just use the guts of Fminibufferp; the tests it does on its > > argument are not needed here. > > Sorry, I can't follow you, here. What do you mean by "the guts" of > Fminibufferp? What things do you mean that the word "just" should > exclude? I meant to exclude these tests that Fminibufferp does: if (NILP (buffer)) buffer = Fcurrent_buffer (); else if (STRINGP (buffer)) buffer = Fget_buffer (buffer); else CHECK_BUFFER (buffer); ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-02 19:19 ` Eli Zaretskii @ 2020-11-03 21:08 ` Alan Mackenzie 2020-11-04 16:47 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-03 21:08 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Mon, Nov 02, 2020 at 21:19:40 +0200, Eli Zaretskii wrote: > > Date: Mon, 2 Nov 2020 18:51:47 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de > > > Please just use the guts of Fminibufferp; the tests it does on its > > > argument are not needed here. > > Sorry, I can't follow you, here. What do you mean by "the guts" of > > Fminibufferp? What things do you mean that the word "just" should > > exclude? > I meant to exclude these tests that Fminibufferp does: > if (NILP (buffer)) > buffer = Fcurrent_buffer (); > else if (STRINGP (buffer)) > buffer = Fget_buffer (buffer); > else > CHECK_BUFFER (buffer); OK, I've extracted a new function `live_minibuffer_p' from Fminibufferp, and call that directly from the mini-window emptying code. (The new second parameter in Fminibufferp is still needed, since it is used in minibuffer.el.) Here's a patch of the subset of changes to minibuf.c which are relevant to our discussions of the last two or three days. Perhaps we are close to the stage when this could be committed to master. diff --git a/src/minibuf.c b/src/minibuf.c index f957b2ae17..ebc00ae4e4 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -261,15 +302,31 @@ read_minibuf_noninteractive (Lisp_Object prompt, bool expflag, return val; } \f +/* Return true when BUFFER is an active minibuffer. */ +static bool +live_minibuffer_p (Lisp_Object buffer) +{ + Lisp_Object tem; + EMACS_INT i; + + if (EQ (buffer, Fcar (Vminibuffer_list))) + /* *Minibuf-0* is never active. */ + return false; + tem = Fcdr (Vminibuffer_list); + for (i = 1; i <= minibuf_level; i++, tem = Fcdr (tem)) + if (EQ (Fcar (tem), buffer)) + return true; + return false; +} + DEFUN ("minibufferp", Fminibufferp, - Sminibufferp, 0, 1, 0, + Sminibufferp, 0, 2, 0, doc: /* Return t if BUFFER is a minibuffer. No argument or nil as argument means use current buffer as BUFFER. -BUFFER can be a buffer or a buffer name. */) - (Lisp_Object buffer) +BUFFER can be a buffer or a buffer name. If LIVE is non-nil, then +return t only if BUFFER is an active minibuffer. */) + (Lisp_Object buffer, Lisp_Object live) { - Lisp_Object tem; - if (NILP (buffer)) buffer = Fcurrent_buffer (); else if (STRINGP (buffer)) @@ -277,8 +334,10 @@ BUFFER can be a buffer or a buffer name. */) else CHECK_BUFFER (buffer); - tem = Fmemq (buffer, Vminibuffer_list); - return ! NILP (tem) ? Qt : Qnil; + return (NILP (live) + ? !NILP (Fmemq (buffer, Vminibuffer_list)) + : live_minibuffer_p (buffer)) + ? Qt : Qnil; } DEFUN ("minibuffer-prompt-end", Fminibuffer_prompt_end, @@ -566,8 +626,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if (minibuf_level == 1 || !EQ (minibuf_window, selected_window)) minibuf_selected_window = selected_window; - /* Empty out the minibuffers of all frames other than the one - where we are going to display one now. + /* Empty out the minibuffers of all frames, except those frames + where there is an active minibuffer. Set them to point to ` *Minibuf-0*', which is always empty. */ empty_minibuf = get_minibuffer (0); @@ -575,12 +635,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, { Lisp_Object root_window = Fframe_root_window (frame); Lisp_Object mini_window = XWINDOW (root_window)->next; + Lisp_Object buffer; - if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window) - && !NILP (Fwindow_minibuffer_p (mini_window))) - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (mini_window, empty_minibuf, 0, 0); + if (!NILP (mini_window) && !EQ (mini_window, minibuf_window) + && !NILP (Fwindow_minibuffer_p (mini_window))) + { + buffer = XWINDOW (mini_window)->contents; + if (!live_minibuffer_p (buffer)) + /* Use set_window_buffer instead of Fset_window_buffer (see + discussion of bug#11984, bug#12025, bug#12026). */ + set_window_buffer (mini_window, empty_minibuf, 0, 0); + } } /* Display this minibuffer in the proper window. */ -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-03 21:08 ` Alan Mackenzie @ 2020-11-04 16:47 ` Eli Zaretskii 2020-11-04 17:39 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-04 16:47 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Date: Tue, 3 Nov 2020 21:08:53 +0000 > Cc: emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > OK, I've extracted a new function `live_minibuffer_p' from Fminibufferp, > and call that directly from the mini-window emptying code. (The new > second parameter in Fminibufferp is still needed, since it is used in > minibuffer.el.) > > Here's a patch of the subset of changes to minibuf.c which are relevant > to our discussions of the last two or three days. Perhaps we are close > to the stage when this could be committed to master. Yes, I think so. > + if (EQ (buffer, Fcar (Vminibuffer_list))) > + /* *Minibuf-0* is never active. */ > + return false; > + tem = Fcdr (Vminibuffer_list); > + for (i = 1; i <= minibuf_level; i++, tem = Fcdr (tem)) > + if (EQ (Fcar (tem), buffer)) > + return true; > + return false; I'm curious: why a loop instead of a call to Fmemq? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-04 16:47 ` Eli Zaretskii @ 2020-11-04 17:39 ` Alan Mackenzie 2020-11-09 15:09 ` Madhu 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-04 17:39 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Hello, Eli. On Wed, Nov 04, 2020 at 18:47:10 +0200, Eli Zaretskii wrote: > > Date: Tue, 3 Nov 2020 21:08:53 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > OK, I've extracted a new function `live_minibuffer_p' from > > Fminibufferp, and call that directly from the mini-window emptying > > code. (The new second parameter in Fminibufferp is still needed, > > since it is used in minibuffer.el.) > > Here's a patch of the subset of changes to minibuf.c which are > > relevant to our discussions of the last two or three days. Perhaps > > we are close to the stage when this could be committed to master. > Yes, I think so. Maybe I should do that this evening. WDYT? > > + if (EQ (buffer, Fcar (Vminibuffer_list))) > > + /* *Minibuf-0* is never active. */ > > + return false; > > + tem = Fcdr (Vminibuffer_list); > > + for (i = 1; i <= minibuf_level; i++, tem = Fcdr (tem)) > > + if (EQ (Fcar (tem), buffer)) > > + return true; > > + return false; > I'm curious: why a loop instead of a call to Fmemq? Because Vminibuffer_list is a list of all minibuffers which have ever been active. When the use of a MB is terminated, the MB stays on the list (for reuse), and minibuf_level is decremented. So an Fmemq would need somehow to ignore the end of the Vminibuffer_list. Just doing a loop in C seems simpler. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-04 17:39 ` Alan Mackenzie @ 2020-11-09 15:09 ` Madhu 2020-11-09 20:34 ` Andrii Kolomoiets 0 siblings, 1 reply; 252+ messages in thread From: Madhu @ 2020-11-09 15:09 UTC (permalink / raw) To: emacs-devel * Alan Mackenzie <20201104173954.GA14535@ACM> : Wrote on Wed, 4 Nov 2020 17:39:54 +0000: > On Wed, Nov 04, 2020 at 18:47:10 +0200, Eli Zaretskii wrote: >> > Date: Tue, 3 Nov 2020 21:08:53 +0000 >> > From: Alan Mackenzie <acm@muc.de> > >> > OK, I've extracted a new function `live_minibuffer_p' from >> > Fminibufferp, and call that directly from the mini-window emptying >> > code. (The new second parameter in Fminibufferp is still needed, >> > since it is used in minibuffer.el.) [snip] These patches introduce a regression on "graphical" emacs - 1. emacs -Q 2. M-: (setq pop-up-frames 'graphic-only) 3. M-! g <TAB> This should pop up a *Completions* buffer in a new frame. On choosing the completion (via a button1 or by navigating to the desired point and typing RET) - the frame should be automatically hidden[1] This doesn't happen anymore and the completion buffer and frame remain there taking up focus. [1] default value for frame-auto-hide-function is #'iconify-frame, but if your window manager cannot iconify it, set (setq frame-auto-hide-function #'delete-frame) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-09 15:09 ` Madhu @ 2020-11-09 20:34 ` Andrii Kolomoiets 2020-11-10 3:25 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-09 20:34 UTC (permalink / raw) To: Madhu; +Cc: emacs-devel Madhu <enometh@meer.net> writes: > * Alan Mackenzie <20201104173954.GA14535@ACM> : > Wrote on Wed, 4 Nov 2020 17:39:54 +0000: >> On Wed, Nov 04, 2020 at 18:47:10 +0200, Eli Zaretskii wrote: >>> > Date: Tue, 3 Nov 2020 21:08:53 +0000 >>> > From: Alan Mackenzie <acm@muc.de> >> >>> > OK, I've extracted a new function `live_minibuffer_p' from >>> > Fminibufferp, and call that directly from the mini-window emptying >>> > code. (The new second parameter in Fminibufferp is still needed, >>> > since it is used in minibuffer.el.) > [snip] > > On choosing the completion (via a button1 or by navigating to the > desired point and typing RET) - the frame should be automatically > hidden[1] > > This doesn't happen anymore and the completion buffer and frame remain > there taking up focus. Take a look at the 'minibuffer-follows-selected-frame' variable. Setting it to 'nil' will solve your issue. Alan, is it possible to make 'minibuffer-follows-selected-frame' nil by default? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-09 20:34 ` Andrii Kolomoiets @ 2020-11-10 3:25 ` Eli Zaretskii 2020-11-10 8:08 ` Andrii Kolomoiets 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-10 3:25 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: enometh, emacs-devel > From: Andrii Kolomoiets <andreyk.mad@gmail.com> > Date: Mon, 09 Nov 2020 22:34:31 +0200 > Cc: emacs-devel@gnu.org > > Alan, is it possible to make 'minibuffer-follows-selected-frame' nil by > default? If this is because the other value produces bugs, IMO we should fix those bugs rather than make them less frequent (and thus harder to detect) by flipping the default value. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 3:25 ` Eli Zaretskii @ 2020-11-10 8:08 ` Andrii Kolomoiets 2020-11-10 8:52 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-10 8:08 UTC (permalink / raw) To: Eli Zaretskii; +Cc: enometh, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Andrii Kolomoiets <andreyk.mad@gmail.com> >> Date: Mon, 09 Nov 2020 22:34:31 +0200 >> Cc: emacs-devel@gnu.org >> >> Alan, is it possible to make 'minibuffer-follows-selected-frame' nil by >> default? > > If this is because the other value produces bugs, IMO we should fix > those bugs rather than make them less frequent (and thus harder to > detect) by flipping the default value. It is not producing bugs for me, but changes behavior. E.g. in emacs -Q: 1. Evaluate (select-frame-set-input-focus (make-frame '((minibuffer . only) (left . 1.0)))) 2. M-x 3. C-x 5 o Before minibuffer-follows-selected-frame, the prompt stays in the minibuffer-only frame. On recent master, the prompt is moved to other frame leaving minibuffer-only frame empty. I can't report this as a bug. Just wondering why minibuffer-follows-selected-frame is set to t by default, potentially changing someone's expected behavior. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 8:08 ` Andrii Kolomoiets @ 2020-11-10 8:52 ` Eli Zaretskii 2020-11-10 13:21 ` Stefan Monnier 2020-11-10 16:45 ` Drew Adams 0 siblings, 2 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-10 8:52 UTC (permalink / raw) To: emacs-devel, Andrii Kolomoiets; +Cc: enometh On November 10, 2020 10:08:46 AM GMT+02:00, Andrii Kolomoiets <andreyk.mad@gmail.com> wrote: > Eli Zaretskii <eliz@gnu.org> writes: > > >> From: Andrii Kolomoiets <andreyk.mad@gmail.com> > >> Date: Mon, 09 Nov 2020 22:34:31 +0200 > >> Cc: emacs-devel@gnu.org > >> > >> Alan, is it possible to make 'minibuffer-follows-selected-frame' > nil by > >> default? > > > > If this is because the other value produces bugs, IMO we should fix > > those bugs rather than make them less frequent (and thus harder to > > detect) by flipping the default value. > > It is not producing bugs for me, but changes behavior. > > E.g. in emacs -Q: > > 1. Evaluate > (select-frame-set-input-focus > (make-frame '((minibuffer . only) > (left . 1.0)))) > 2. M-x > 3. C-x 5 o > > Before minibuffer-follows-selected-frame, the prompt stays in the > minibuffer-only frame. > On recent master, the prompt is moved to other frame leaving > minibuffer-only frame empty. I can't report this as a bug. Just > wondering why minibuffer-follows-selected-frame is set to t by > default, > potentially changing someone's expected behavior. The defaults are selected for the common usage patterns. It is not clear to me that the test case you presented is common. But if it is, perhaps we do need to consider changing the default. Does anyone else think this is common usage, to have a minibuffer-only frame while other frames also have minibuffers? Alternatively, perhaps minibuffers activated in minibuffer-only frames should behave specially in this regard? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 8:52 ` Eli Zaretskii @ 2020-11-10 13:21 ` Stefan Monnier 2020-11-10 17:27 ` Andrii Kolomoiets 2020-11-10 16:45 ` Drew Adams 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-11-10 13:21 UTC (permalink / raw) To: Eli Zaretskii; +Cc: enometh, Andrii Kolomoiets, emacs-devel > Does anyone else think this is common usage, to have a minibuffer-only frame > while other frames also have minibuffers? FWIW, I've never seen it in the wild (I've seen mixes of frames with and without minibuffers, but when there is a minibuffer-only frame never seen it accompanied with other frames-with-minibuffer, except for frames on other terminals). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 13:21 ` Stefan Monnier @ 2020-11-10 17:27 ` Andrii Kolomoiets 2020-11-10 18:26 ` Eli Zaretskii 2020-11-10 19:57 ` Stefan Monnier 0 siblings, 2 replies; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-10 17:27 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, enometh, emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >> Does anyone else think this is common usage, to have a minibuffer-only frame >> while other frames also have minibuffers? > > FWIW, I've never seen it in the wild (I've seen mixes of frames with > and without minibuffers, but when there is a minibuffer-only frame > never seen it accompanied with other frames-with-minibuffer, except for > frames on other terminals). I don't sure what the "in the wild" means, but I know at least two packages that shows minibuffer-only child frame on reading user input: https://raw.githubusercontent.com/honmaple/emacs-maple-minibuffer/master/maple-minibuffer.el https://raw.githubusercontent.com/muffinmad/emacs-mini-frame/master/mini-frame.el ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 17:27 ` Andrii Kolomoiets @ 2020-11-10 18:26 ` Eli Zaretskii 2020-11-10 22:43 ` Andrii Kolomoiets 2020-11-10 19:57 ` Stefan Monnier 1 sibling, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-10 18:26 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: enometh, monnier, emacs-devel > From: Andrii Kolomoiets <andreyk.mad@gmail.com> > Date: Tue, 10 Nov 2020 19:27:45 +0200 > Cc: Eli Zaretskii <eliz@gnu.org>, enometh@meer.net, emacs-devel@gnu.org > > Stefan Monnier <monnier@iro.umontreal.ca> writes: > > >> Does anyone else think this is common usage, to have a minibuffer-only frame > >> while other frames also have minibuffers? > > > > FWIW, I've never seen it in the wild (I've seen mixes of frames with > > and without minibuffers, but when there is a minibuffer-only frame > > never seen it accompanied with other frames-with-minibuffer, except for > > frames on other terminals). > > I don't sure what the "in the wild" means, but I know at least two > packages that shows minibuffer-only child frame on reading user input: I didn't ask if this was possible, or used, I asked if such usage is common. It is clear that we cannot find a default that will fit all the uses, but we should have the default that works well in common use cases. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 18:26 ` Eli Zaretskii @ 2020-11-10 22:43 ` Andrii Kolomoiets 2020-11-11 15:38 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-10 22:43 UTC (permalink / raw) To: Eli Zaretskii; +Cc: enometh, monnier, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >>>> Does anyone else think this is common usage, to have a minibuffer-only frame >>>> while other frames also have minibuffers? >>> >>> FWIW, I've never seen it in the wild (I've seen mixes of frames with >>> and without minibuffers, but when there is a minibuffer-only frame >>> never seen it accompanied with other frames-with-minibuffer, except for >>> frames on other terminals). >> >> I don't sure what the "in the wild" means, but I know at least two >> packages that shows minibuffer-only child frame on reading user input: > > I didn't ask if this was possible, or used, I asked if such usage is > common. I don't think such usage is common nowadays. Maybe someday it would sound like "minibuffer-only frame while other frames have echo areas" and then it would be more commonly used IMO. > It is clear that we cannot find a default that will fit all > the uses, but we should have the default that works well in common use > cases. Totally agree. Just thought that the existing behavior is common enough. Don't you the think new behavior may be confusing? The old behavior was like "Oh, I left the minibuffer in that frame; OK, I need to switch to that frame and complete the task". And the new one is like "Oh, the minibuffer is on active frame, cool! But wait, where is the results of my eval?" because in 'emacs -Q': C-x 5 b foo RET bar M-: (buffer-string) C-x 5 o C-x o RET Seems like the 'buffer-string' is evaluated in the *scratch* buffer, but it is not. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 22:43 ` Andrii Kolomoiets @ 2020-11-11 15:38 ` Eli Zaretskii 0 siblings, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-11 15:38 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: enometh, monnier, emacs-devel > From: Andrii Kolomoiets <andreyk.mad@gmail.com> > Cc: monnier@iro.umontreal.ca, enometh@meer.net, emacs-devel@gnu.org > Date: Wed, 11 Nov 2020 00:43:13 +0200 > > Don't you the think new behavior may be confusing? The old behavior was > like "Oh, I left the minibuffer in that frame; OK, I need to switch to > that frame and complete the task". Actually, the old behavior was more like "Oh, I left the minibuffer in that frame, but sometimes -- whoops! -- it jumps to the new frame, and sometimes it doesn't." > And the new one is like "Oh, the minibuffer is on active frame, > cool! But wait, where is the results of my eval?" because in 'emacs > -Q': > > C-x 5 b foo RET bar M-: (buffer-string) C-x 5 o C-x o RET > > Seems like the 'buffer-string' is evaluated in the *scratch* buffer, but > it is not. Exactly as documented, btw. But if we think this is a bug, we should fix it regardless of the default value of the new option. Once again, let's distinguish between what we think are bugs and what we think is valid behavior that someone might not like as the default. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 17:27 ` Andrii Kolomoiets 2020-11-10 18:26 ` Eli Zaretskii @ 2020-11-10 19:57 ` Stefan Monnier 2020-11-10 22:54 ` Andrii Kolomoiets 2020-11-11 8:28 ` martin rudalics 1 sibling, 2 replies; 252+ messages in thread From: Stefan Monnier @ 2020-11-10 19:57 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: Eli Zaretskii, enometh, emacs-devel > I don't sure what the "in the wild" means, but I know at least two > packages that shows minibuffer-only child frame on reading user input: > > https://raw.githubusercontent.com/honmaple/emacs-maple-minibuffer/master/maple-minibuffer.el > > https://raw.githubusercontent.com/muffinmad/emacs-mini-frame/master/mini-frame.el Indeed, great examples, thanks. > I can't report this as a bug. Just wondering why > minibuffer-follows-selected-frame is set to t by default, potentially > changing someone's expected behavior. I think the reason is that it more closely mimics what happens with the echo area and it arguably (philosophically) better matches the default setting of `enable-recursive-minibuffers`. But if it results in regressions with packages to maple-minibuffer or mini-frame, we should of course try and address that (either by refining the behavior or by changing the default). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 19:57 ` Stefan Monnier @ 2020-11-10 22:54 ` Andrii Kolomoiets 2020-11-10 23:18 ` Stefan Monnier 2020-11-11 8:28 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-10 22:54 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, enometh, emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >> I don't sure what the "in the wild" means, but I know at least two >> packages that shows minibuffer-only child frame on reading user input: >> >> https://raw.githubusercontent.com/honmaple/emacs-maple-minibuffer/master/maple-minibuffer.el >> >> https://raw.githubusercontent.com/muffinmad/emacs-mini-frame/master/mini-frame.el > > Indeed, great examples, thanks. > >> I can't report this as a bug. Just wondering why >> minibuffer-follows-selected-frame is set to t by default, potentially >> changing someone's expected behavior. > > I think the reason is that it more closely mimics what happens with the > echo area and it arguably (philosophically) better matches the default > setting of `enable-recursive-minibuffers`. Maybe I'm missing something, but the message "Command attempted to use minibuffer while in minibuffer" is displayed in other frame, just like in Emacs 27. > But if it results in regressions with packages to maple-minibuffer or > mini-frame, we should of course try and address that (either by refining > the behavior or by changing the default). Well, mini-frame-mode already has a (not so pretty) workaround for the new behavior: set the global option on mode activation. Probably the better solution will be to not take the minibuffer away from minibuffer-only frames. After all, it's all they got :) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 22:54 ` Andrii Kolomoiets @ 2020-11-10 23:18 ` Stefan Monnier 2020-11-11 7:47 ` Andrii Kolomoiets 0 siblings, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-11-10 23:18 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: Eli Zaretskii, enometh, emacs-devel >> I think the reason is that it more closely mimics what happens with the >> echo area and it arguably (philosophically) better matches the default >> setting of `enable-recursive-minibuffers`. > Maybe I'm missing something, but the message "Command attempted to use > minibuffer while in minibuffer" is displayed in other frame, just like > in Emacs 27. I'm not sure which scenario you're referring to. What I was referring to is the fact that if you do `M-: (message "hello") RET`, the "hello" message subsequently follows the selected frame (until it gets replaced by another message or erased by command that doesn't emit any message). > Probably the better solution will be to not take the minibuffer away > from minibuffer-only frames. After all, it's all they got :) Personally I philosophically like the (non-default) nil setting for the new variable, so that minibuffers stick to the frame where they presumably operate. But since my Emacs only has a single mini window anyway, I'm never affected by this setting at all, so I don't have any real experience either way. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 23:18 ` Stefan Monnier @ 2020-11-11 7:47 ` Andrii Kolomoiets 2020-11-11 16:07 ` Eli Zaretskii 2020-11-19 10:40 ` Alan Mackenzie 0 siblings, 2 replies; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-11 7:47 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, enometh, emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >>> it arguably (philosophically) better matches the default setting of >>> `enable-recursive-minibuffers`. >> Maybe I'm missing something, but the message "Command attempted to use >> minibuffer while in minibuffer" is displayed in other frame, just like >> in Emacs 27. > > I'm not sure which scenario you're referring to. What I was referring > to is the fact that if you do `M-: (message "hello") RET`, the "hello" > message subsequently follows the selected frame (until it gets replaced > by another message or erased by command that doesn't emit any message). Sorry for not quoting the part of the message I was talking about. I'm referring to this scenario: C-x 5 2 M-x C-x 5 o M-x With the default setting of `enable-recursive-minibuffers`, the minibuffer is moved to active frame, but the error message is displayed in the other frame. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-11 7:47 ` Andrii Kolomoiets @ 2020-11-11 16:07 ` Eli Zaretskii 2020-11-11 20:37 ` Alan Mackenzie 2020-11-19 10:40 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-11 16:07 UTC (permalink / raw) To: Andrii Kolomoiets, Alan Mackenzie; +Cc: enometh, monnier, emacs-devel > From: Andrii Kolomoiets <andreyk.mad@gmail.com> > Date: Wed, 11 Nov 2020 09:47:39 +0200 > Cc: Eli Zaretskii <eliz@gnu.org>, enometh@meer.net, emacs-devel@gnu.org > > C-x 5 2 > M-x > C-x 5 o > M-x > > With the default setting of `enable-recursive-minibuffers`, the > minibuffer is moved to active frame, but the error message is displayed > in the other frame. Arguably a bug. Alan, could you please look into this? Thanks. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-11 16:07 ` Eli Zaretskii @ 2020-11-11 20:37 ` Alan Mackenzie 2020-11-14 13:36 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-11 20:37 UTC (permalink / raw) To: Eli Zaretskii; +Cc: enometh, monnier, Andrii Kolomoiets, emacs-devel Hello, Eli. On Wed, Nov 11, 2020 at 18:07:49 +0200, Eli Zaretskii wrote: > > From: Andrii Kolomoiets <andreyk.mad@gmail.com> > > Date: Wed, 11 Nov 2020 09:47:39 +0200 > > Cc: Eli Zaretskii <eliz@gnu.org>, enometh@meer.net, emacs-devel@gnu.org > > C-x 5 2 > > M-x > > C-x 5 o > > M-x > > With the default setting of `enable-recursive-minibuffers`, the > > minibuffer is moved to active frame, but the error message is displayed > > in the other frame. > Arguably a bug. Alan, could you please look into this? I think this is a bug, too. I've been looking at the C sources for some while but haven't found anything useful. I've tried setting echo_area_window during the frame change, but this doesn't have any visible effect. I'll carry on with the diagnosis. Any quick tips would be welcome. Thanks! > Thanks. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-11 20:37 ` Alan Mackenzie @ 2020-11-14 13:36 ` Eli Zaretskii 2020-11-14 17:12 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-14 13:36 UTC (permalink / raw) To: Alan Mackenzie; +Cc: enometh, monnier, andreyk.mad, emacs-devel > Date: Wed, 11 Nov 2020 20:37:16 +0000 > Cc: Andrii Kolomoiets <andreyk.mad@gmail.com>, monnier@iro.umontreal.ca, > enometh@meer.net, emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > > > C-x 5 2 > > > M-x > > > C-x 5 o > > > M-x > > > > With the default setting of `enable-recursive-minibuffers`, the > > > minibuffer is moved to active frame, but the error message is displayed > > > in the other frame. > > > Arguably a bug. Alan, could you please look into this? > > I think this is a bug, too. > > I've been looking at the C sources for some while but haven't found > anything useful. I've tried setting echo_area_window during the frame > change, but this doesn't have any visible effect. > > I'll carry on with the diagnosis. Any quick tips would be welcome. My guess is that this is somehow related to the fact that error messages are displayed as part of handling an error signal, which causes a throw to top level. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-14 13:36 ` Eli Zaretskii @ 2020-11-14 17:12 ` Eli Zaretskii 2020-11-14 18:48 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-11-14 17:12 UTC (permalink / raw) To: acm; +Cc: enometh, monnier, andreyk.mad, emacs-devel > Date: Sat, 14 Nov 2020 15:36:43 +0200 > From: Eli Zaretskii <eliz@gnu.org> > Cc: enometh@meer.net, monnier@iro.umontreal.ca, andreyk.mad@gmail.com, > emacs-devel@gnu.org > > > Date: Wed, 11 Nov 2020 20:37:16 +0000 > > Cc: Andrii Kolomoiets <andreyk.mad@gmail.com>, monnier@iro.umontreal.ca, > > enometh@meer.net, emacs-devel@gnu.org > > From: Alan Mackenzie <acm@muc.de> > > > > > > C-x 5 2 > > > > M-x > > > > C-x 5 o > > > > M-x > > > > > > With the default setting of `enable-recursive-minibuffers`, the > > > > minibuffer is moved to active frame, but the error message is displayed > > > > in the other frame. > > > > > Arguably a bug. Alan, could you please look into this? > > > > I think this is a bug, too. > > > > I've been looking at the C sources for some while but haven't found > > anything useful. I've tried setting echo_area_window during the frame > > change, but this doesn't have any visible effect. > > > > I'll carry on with the diagnosis. Any quick tips would be welcome. > > My guess is that this is somehow related to the fact that error > messages are displayed as part of handling an error signal, which > causes a throw to top level. Specifically, read-from-minibuffer (called when we type the first M-x) binds some variables, then enters recursive-edit. When the error unwinds the stack, it restores the original window configuration, which includes the frame which was selected back then. And that undoes the effect of "C-x 5 o", so the error message is displayed on the original frame. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-14 17:12 ` Eli Zaretskii @ 2020-11-14 18:48 ` Alan Mackenzie 2020-11-14 19:11 ` Eli Zaretskii 2020-11-14 19:24 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-14 18:48 UTC (permalink / raw) To: Eli Zaretskii; +Cc: enometh, monnier, andreyk.mad, emacs-devel Hello, Eli. On Sat, Nov 14, 2020 at 19:12:24 +0200, Eli Zaretskii wrote: > > Date: Sat, 14 Nov 2020 15:36:43 +0200 > > From: Eli Zaretskii <eliz@gnu.org> > > Cc: enometh@meer.net, monnier@iro.umontreal.ca, andreyk.mad@gmail.com, > > emacs-devel@gnu.org > > > Date: Wed, 11 Nov 2020 20:37:16 +0000 > > > Cc: Andrii Kolomoiets <andreyk.mad@gmail.com>, monnier@iro.umontreal.ca, > > > enometh@meer.net, emacs-devel@gnu.org > > > From: Alan Mackenzie <acm@muc.de> > > > > > C-x 5 2 > > > > > M-x > > > > > C-x 5 o > > > > > M-x > > > > > With the default setting of `enable-recursive-minibuffers`, the > > > > > minibuffer is moved to active frame, but the error message is displayed > > > > > in the other frame. > > > > Arguably a bug. Alan, could you please look into this? > > > I think this is a bug, too. > > > I've been looking at the C sources for some while but haven't found > > > anything useful. I've tried setting echo_area_window during the frame > > > change, but this doesn't have any visible effect. > > > I'll carry on with the diagnosis. Any quick tips would be welcome. > > My guess is that this is somehow related to the fact that error > > messages are displayed as part of handling an error signal, which > > causes a throw to top level. > Specifically, read-from-minibuffer (called when we type the first M-x) > binds some variables, then enters recursive-edit. When the error > unwinds the stack, it restores the original window configuration, > which includes the frame which was selected back then. And that > undoes the effect of "C-x 5 o", so the error message is displayed on > the original frame. Thanks! I was approaching the same conclusion myself, but much more slowly than you. ;-) Arguably, Fset_window_configuration selecting the pertinent frame is a bug in the specification of that function - one should be able to restore a frame's configuration without selecting the frame as well. However, backwards compatibility, and all that.... I propose fixing the bug at the place where read_minibuf saves the frame configuration. As well as saving that frame configuration, it should additionally save another Lisp_Object for which nil means "restore the selected frame", non-nil means "don't restore it". Fset_window_configuration would acquire an extra &optional parameter with the same meaning. The function restore_window_configuration, frustratingly, is used in only one place apart from read_minibuf, and that is in an obsolete byte code in bytecode.c. What do you think? -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-14 18:48 ` Alan Mackenzie @ 2020-11-14 19:11 ` Eli Zaretskii 2020-11-14 19:24 ` martin rudalics 1 sibling, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-14 19:11 UTC (permalink / raw) To: Alan Mackenzie, martin rudalics Cc: enometh, monnier, andreyk.mad, emacs-devel > Date: Sat, 14 Nov 2020 18:48:58 +0000 > Cc: enometh@meer.net, monnier@iro.umontreal.ca, andreyk.mad@gmail.com, > emacs-devel@gnu.org > From: Alan Mackenzie <acm@muc.de> > > I propose fixing the bug at the place where read_minibuf saves the frame > configuration. As well as saving that frame configuration, it should > additionally save another Lisp_Object for which nil means "restore the > selected frame", non-nil means "don't restore it". > Fset_window_configuration would acquire an extra &optional parameter with > the same meaning. Sounds fine, but I'd like to hear Martin's comments on this idea. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-14 18:48 ` Alan Mackenzie 2020-11-14 19:11 ` Eli Zaretskii @ 2020-11-14 19:24 ` martin rudalics 2020-11-14 21:37 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-14 19:24 UTC (permalink / raw) To: Alan Mackenzie, Eli Zaretskii; +Cc: enometh, monnier, andreyk.mad, emacs-devel > Arguably, Fset_window_configuration selecting the pertinent frame is a > bug in the specification of that function - one should be able to restore > a frame's configuration without selecting the frame as well. However, > backwards compatibility, and all that.... You'd first have to get rid of the two select_window calls which select the frame whose configuration gets restored. Fset_window_configuration is a PITA. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-14 19:24 ` martin rudalics @ 2020-11-14 21:37 ` Alan Mackenzie 2020-11-15 8:48 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-14 21:37 UTC (permalink / raw) To: martin rudalics, Eli Zaretskii; +Cc: enometh, monnier, andreyk.mad, emacs-devel Hello, Martin and Eli. On Sat, Nov 14, 2020 at 20:24:32 +0100, martin rudalics wrote: > > Arguably, Fset_window_configuration selecting the pertinent frame is a > > bug in the specification of that function - one should be able to restore > > a frame's configuration without selecting the frame as well. However, > > backwards compatibility, and all that.... > You'd first have to get rid of the two select_window calls which select > the frame whose configuration gets restored. Fset_window_configuration > is a PITA. Actually, I just put in a do_switch_frame back to the frame which was the selected one at the beginning of the function. But I agree with you about Fset_window_configuration. Anyway, here's a provisional patch. I realise that my amendment to the doc string of Fset_window_configuration is poor, but it's too late to fix it tonight. ;-( I also realise I'll need to amend the Elisp manual and NEWS, but also not tonight. The patch seems to fix the bug (the one about a "recursive" C-x b displaying its error message in the wrong frame). Comments are welcome. diff --git a/src/keyboard.c b/src/keyboard.c index 49a0a8bd23..1579c007ec 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -2122,7 +2122,7 @@ read_char_help_form_unwind (void) Lisp_Object window_config = XCAR (help_form_saved_window_configs); help_form_saved_window_configs = XCDR (help_form_saved_window_configs); if (!NILP (window_config)) - Fset_window_configuration (window_config); + Fset_window_configuration (window_config, Qnil); } #define STOP_POLLING \ diff --git a/src/minibuf.c b/src/minibuf.c index 8c19559b08..acb633c583 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -501,14 +501,15 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, record_unwind_protect_void (choose_minibuf_frame); record_unwind_protect (restore_window_configuration, - Fcurrent_window_configuration (Qnil)); + Fcons (Qt, Fcurrent_window_configuration (Qnil))); /* If the minibuffer window is on a different frame, save that frame's configuration too. */ mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); if (!EQ (mini_frame, selected_frame)) record_unwind_protect (restore_window_configuration, - Fcurrent_window_configuration (mini_frame)); + Fcons (Qt, + Fcurrent_window_configuration (mini_frame))); /* If the minibuffer is on an iconified or invisible frame, make it visible now. */ diff --git a/src/window.c b/src/window.c index a6de34f3db..3187720f89 100644 --- a/src/window.c +++ b/src/window.c @@ -6824,19 +6826,24 @@ DEFUN ("window-configuration-frame", Fwindow_configuration_frame, Swindow_config } DEFUN ("set-window-configuration", Fset_window_configuration, - Sset_window_configuration, 1, 1, 0, + Sset_window_configuration, 1, 2, 0, doc: /* Set the configuration of windows and buffers as specified by CONFIGURATION. CONFIGURATION must be a value previously returned by `current-window-configuration' (which see). + +Normally, the original frame of CONFIGURATION gets selected, but if DONT-SET-FRAME is +non-nil, the frame selected at the beginning of this function remains selected. + If CONFIGURATION was made from a frame that is now deleted, only frame-independent values can be restored. In this case, the return value is nil. Otherwise the value is t. */) - (Lisp_Object configuration) + (Lisp_Object configuration, Lisp_Object dont_set_frame) { register struct save_window_data *data; struct Lisp_Vector *saved_windows; Lisp_Object new_current_buffer; Lisp_Object frame; + Lisp_Object old_frame = selected_frame; struct frame *f; ptrdiff_t old_point = -1; USE_SAFE_ALLOCA; @@ -7153,7 +7160,10 @@ the return value is nil. Otherwise the value is t. */) select_window above totally superfluous; it still sets f's selected window. */ if (FRAME_LIVE_P (XFRAME (data->selected_frame))) - do_switch_frame (data->selected_frame, 0, 0, Qnil); + do_switch_frame (NILP (dont_set_frame) + ? data->selected_frame + : old_frame + , 0, 0, Qnil); } FRAME_WINDOW_CHANGE (f) = true; @@ -7191,7 +7201,10 @@ the return value is nil. Otherwise the value is t. */) void restore_window_configuration (Lisp_Object configuration) { - Fset_window_configuration (configuration); + if (CONSP (configuration)) + Fset_window_configuration (XCDR (configuration), XCAR (configuration)); + else + Fset_window_configuration (configuration, Qnil); } > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-14 21:37 ` Alan Mackenzie @ 2020-11-15 8:48 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-15 8:48 UTC (permalink / raw) To: Alan Mackenzie, Eli Zaretskii; +Cc: enometh, monnier, andreyk.mad, emacs-devel > The patch seems to fix the bug (the one about a "recursive" C-x b > displaying its error message in the wrong frame). > > Comments are welcome. IIRC restoring the window configuration here is a bad idea because any change of the window configuration you do in a nested invocation is immediately undone when the outer invocation gets in control again. So 'read_minibuf' is just as poor as 'set-window-configuration' and you can't harm either of them much by doing such changes. Both should be rewritten from scratch with a clear concept of what they are supposed to accomplish in mind. And obviously all those select_window/Fselect_window calls outside the scope of 'select-window' proper must get removed. We hardly can get them right within window.c itself so what do we expect from the others? martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-11 7:47 ` Andrii Kolomoiets 2020-11-11 16:07 ` Eli Zaretskii @ 2020-11-19 10:40 ` Alan Mackenzie 2020-11-19 11:40 ` Andrii Kolomoiets 2020-11-20 18:47 ` martin rudalics 1 sibling, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-19 10:40 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: Eli Zaretskii, emacs-devel, Stefan Monnier, enometh Hello, Andrii. On Wed, Nov 11, 2020 at 09:47:39 +0200, Andrii Kolomoiets wrote: > Stefan Monnier <monnier@iro.umontreal.ca> writes: > >>> it arguably (philosophically) better matches the default setting of > >>> `enable-recursive-minibuffers`. > >> Maybe I'm missing something, but the message "Command attempted to use > >> minibuffer while in minibuffer" is displayed in other frame, just like > >> in Emacs 27. > > I'm not sure which scenario you're referring to. What I was referring > > to is the fact that if you do `M-: (message "hello") RET`, the "hello" > > message subsequently follows the selected frame (until it gets replaced > > by another message or erased by command that doesn't emit any message). > Sorry for not quoting the part of the message I was talking about. I'm > referring to this scenario: > C-x 5 2 > M-x > C-x 5 o > M-x > With the default setting of `enable-recursive-minibuffers`, the > minibuffer is moved to active frame, but the error message is displayed > in the other frame. I've just committed a fix to the Emacs master branch. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-19 10:40 ` Alan Mackenzie @ 2020-11-19 11:40 ` Andrii Kolomoiets 2020-11-19 13:30 ` Alan Mackenzie 2020-11-20 18:47 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-19 11:40 UTC (permalink / raw) To: Alan Mackenzie; +Cc: enometh, Eli Zaretskii, Stefan Monnier, emacs-devel Alan Mackenzie <acm@muc.de> writes: >> With the default setting of `enable-recursive-minibuffers`, the >> minibuffer is moved to active frame, but the error message is displayed >> in the other frame. > > I've just committed a fix to the Emacs master branch. Some NEWS entries are deleted with your commit. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-19 11:40 ` Andrii Kolomoiets @ 2020-11-19 13:30 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-19 13:30 UTC (permalink / raw) To: Andrii Kolomoiets; +Cc: enometh, Eli Zaretskii, Stefan Monnier, emacs-devel Hello, Andrii. On Thu, Nov 19, 2020 at 13:40:19 +0200, Andrii Kolomoiets wrote: > Alan Mackenzie <acm@muc.de> writes: > >> With the default setting of `enable-recursive-minibuffers`, the > >> minibuffer is moved to active frame, but the error message is displayed > >> in the other frame. > > I've just committed a fix to the Emacs master branch. > Some NEWS entries are deleted with your commit. Sorry about that, and thanks for noticing. That problem should now be fixed. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-19 10:40 ` Alan Mackenzie 2020-11-19 11:40 ` Andrii Kolomoiets @ 2020-11-20 18:47 ` martin rudalics 2020-11-20 21:00 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-20 18:47 UTC (permalink / raw) To: Alan Mackenzie, Andrii Kolomoiets Cc: enometh, Eli Zaretskii, Stefan Monnier, emacs-devel > I've just committed a fix to the Emacs master branch. Sorry, but this cannot be possibly right. With emacs -Q load a file with (setq default-frame-alist '((minibuffer . nil))) (defun foo () (interactive) (read-from-minibuffer "...?") (insert (format "%s" (selected-frame)))) insert (foo) into *scratch* and type C-x C-e. After answering the prompt this gets me #<frame *Minibuf-1* 0x36132d8>. Do the same with Emacs 27 and it will get you the *scratch* frame. As soon as 'read-from-minibuffer' has finished its job, it is as if it never happened and the old configuration must have been restored. We might be able to stay on old_frame when minibuf_level > 0 but in the scenario above the frame that was selected when 'read-from-minibuffer' was called must be re-selected. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-20 18:47 ` martin rudalics @ 2020-11-20 21:00 ` Alan Mackenzie 2020-11-20 21:36 ` Stefan Monnier 2020-11-21 9:02 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-20 21:00 UTC (permalink / raw) To: martin rudalics Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel Hello, Martin. On Fri, Nov 20, 2020 at 19:47:28 +0100, martin rudalics wrote: > > I've just committed a fix to the Emacs master branch. > Sorry, but this cannot be possibly right. With emacs -Q load a file > with > (setq default-frame-alist '((minibuffer . nil))) > (defun foo () > (interactive) > (read-from-minibuffer "...?") > (insert (format "%s" (selected-frame)))) > insert (foo) into *scratch* and type C-x C-e. After answering the > prompt this gets me #<frame *Minibuf-1* 0x36132d8>. Do the same with > Emacs 27 and it will get you the *scratch* frame. Yes, I see this (except for the Emacs 27 bit which I haven't tried, yet). However, if I type (frame-list) into *scratch* and do C-x C-e I get only one frame in the list, and it has the same address in the #<frame ...> output as the " *Minibuf-1* 0xxxx" output. In other words, I think your (insert (format "%s" (selected-frame))) is getting the correct frame, but the current buffer within it is still the minibuffer. Might you possibly have simplified the recipe so much that the problem is no longer shown by the recipe? Removing the setting of default-frame-alist from the file doesn't seem to make any difference. > As soon as 'read-from-minibuffer' has finished its job, it is as if it > never happened and the old configuration must have been restored. We > might be able to stay on old_frame when minibuf_level > 0 but in the > scenario above the frame that was selected when 'read-from-minibuffer' > was called must be re-selected. Why were we selecting a frame as an "incidental" side effect of restoring a window configuration? Surely the frame to be selected should be selected deliberately and explicitly. I'm thinking that what needs restoring is the frame's current buffer, and I'm wondering why that wasn't done by the (set-window-configuration foo t) which happened near the end of read_minibuf. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-20 21:00 ` Alan Mackenzie @ 2020-11-20 21:36 ` Stefan Monnier 2020-11-21 9:02 ` martin rudalics 1 sibling, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-11-20 21:36 UTC (permalink / raw) To: Alan Mackenzie Cc: martin rudalics, Eli Zaretskii, enometh, Andrii Kolomoiets, emacs-devel >> (setq default-frame-alist '((minibuffer . nil))) >> >> (defun foo () >> (interactive) >> (read-from-minibuffer "...?") >> (insert (format "%s" (selected-frame)))) [...] > However, if I type (frame-list) into *scratch* and do C-x C-e I get only > one frame in the list, and it has the same address in the #<frame ...> > output as the " *Minibuf-1* 0xxxx" output. Hmm... any chance you've missed the (setq default-frame-alist '((minibuffer . nil))) or you executed it too late? There *should* be 2 frames in the above recipe, one being a minibuffer-only frame. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-20 21:00 ` Alan Mackenzie 2020-11-20 21:36 ` Stefan Monnier @ 2020-11-21 9:02 ` martin rudalics 2020-11-21 10:27 ` Alan Mackenzie 2020-11-21 17:19 ` Stefan Monnier 1 sibling, 2 replies; 252+ messages in thread From: martin rudalics @ 2020-11-21 9:02 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier >> (setq default-frame-alist '((minibuffer . nil))) > >> (defun foo () >> (interactive) >> (read-from-minibuffer "...?") >> (insert (format "%s" (selected-frame)))) > >> insert (foo) into *scratch* and type C-x C-e. After answering the >> prompt this gets me #<frame *Minibuf-1* 0x36132d8>. Do the same with >> Emacs 27 and it will get you the *scratch* frame. > > Yes, I see this (except for the Emacs 27 bit which I haven't tried, > yet). > > However, if I type (frame-list) into *scratch* and do C-x C-e I get only > one frame in the list, and it has the same address in the #<frame ...> > output as the " *Minibuf-1* 0xxxx" output. You have to _load_ the (setq default-frame-alist '((minibuffer . nil))) part, it won't work after your default initial frame has been already set up. Run Emacs via something like emacs -Q --load ~/foo.el to see the effect (and please try to get used to test any changes in this area with such a setup as well). But indeed, with Emacs -Q executing 'foo' defined as (defun foo () (interactive) (read-from-minibuffer "...?") (insert (format "window: %s .. frame: %s" (selected-window) (selected-frame)))) reveals another aspect broken by your change. The values reported by 'selected-window' and 'select-frame' do not match up any more (unless our masochistic way or printing frame names hides an important detail). > In other words, I think your (insert (format "%s" (selected-frame))) is > getting the correct frame, but the current buffer within it is still the > minibuffer. The frame is _not_ correct - try with a separate minibuffer frame. > Might you possibly have simplified the recipe so much that the problem > is no longer shown by the recipe? Removing the setting of > default-frame-alist from the file doesn't seem to make any difference. See above. > Why were we selecting a frame as an "incidental" side effect of restoring > a window configuration? Surely the frame to be selected should be > selected deliberately and explicitly. Agreed. But I suppose the current behavior was chosen precisely to support the use case we're discussing here: With a separate minibuffer frame it has to restore the frame that was selected before the minibuffer interaction started. All the remaining stuff done by restore_window_configuration is rubbish IMHO. Basically, minibuffer only frames could work seamlessly were it not for the complications that people added over the years: 'restore_window_configuration': Anything the minibuffer dialogue changes in the window configuration of a normal frame (like popping up completions) should be undone immediately by the associated code. Any changes to the configuration done explicitly by the user in between (like, for example, splitting a window to run some other application in it) should not be undone. 'redirect-frame-focus': There's should be no need for that - when the minibuffer becomes active, raise its frame to send keystrokes to it. With the above minibuffer nil setting here I get a minibuffer frame that is hidden by the non-minibuffer frame when it shows the prompt - no wonder that people immediately refrain from using such a set up. The prompt must be initially visible - always and everywhere. Emacs should be no hide-and-seek game. 'frame-auto-hide-function': No need for that either. When a minibuffer frame becomes active, it should become visible and be displayed on top of the frame from where the dialogue originated. When the dialogue terminates, the minibuffer frame should return to its prior state. Obviously, people might dislike a minibuffer-only frame getting focus every time something is displayed in the echo area. But that's just a consequence of Emacs habit to conflate minibuffer and echo area in one and the same part of a user's display. > I'm thinking that what needs restoring is the frame's current buffer, and > I'm wondering why that wasn't done by the (set-window-configuration foo > t) which happened near the end of read_minibuf. A frame does not have a current buffer. The "current buffer" is a completely different concept. The only real connection is that in keyboard.c the set_buffer_internal (XBUFFER (XWINDOW (selected_window)->contents)); resets the current buffer to that of the selected window which also should be the selected frame's selected window. But your change apparently broke that. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 9:02 ` martin rudalics @ 2020-11-21 10:27 ` Alan Mackenzie 2020-11-21 11:55 ` martin rudalics 2020-11-21 17:19 ` Stefan Monnier 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-21 10:27 UTC (permalink / raw) To: martin rudalics Cc: enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier Hello, Martin. On Sat, Nov 21, 2020 at 10:02:15 +0100, martin rudalics wrote: > >> (setq default-frame-alist '((minibuffer . nil))) > >> (defun foo () > >> (interactive) > >> (read-from-minibuffer "...?") > >> (insert (format "%s" (selected-frame)))) > >> insert (foo) into *scratch* and type C-x C-e. After answering the > >> prompt this gets me #<frame *Minibuf-1* 0x36132d8>. Do the same with > >> Emacs 27 and it will get you the *scratch* frame. > > Yes, I see this (except for the Emacs 27 bit which I haven't tried, > > yet). > > However, if I type (frame-list) into *scratch* and do C-x C-e I get only > > one frame in the list, and it has the same address in the #<frame ...> > > output as the " *Minibuf-1* 0xxxx" output. > You have to _load_ the > (setq default-frame-alist '((minibuffer . nil))) > part, it won't work after your default initial frame has been already > set up. Run Emacs via something like > emacs -Q --load ~/foo.el > to see the effect .... OK, I've got it now. [ .... ] > But indeed, with Emacs -Q executing 'foo' defined as > (defun foo () > (interactive) > (read-from-minibuffer "...?") > (insert (format "window: %s .. frame: %s" (selected-window) (selected-frame)))) > reveals another aspect broken by your change. The values reported by > 'selected-window' and 'select-frame' do not match up any more (unless > our masochistic way of printing frame names hides an important detail). I'll look at that later. I'll look at the other things below [snipped] later, too. I'm a bit short of time today. [ .... ] Here's a quick and dirty patch to minibuf.c which appears to fix the bug, but I haven't given it much testing yet: diff --git a/src/minibuf.c b/src/minibuf.c index 464e3018f7..5312dc9805 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -508,7 +508,14 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); if (!EQ (mini_frame, selected_frame)) record_unwind_protect (restore_window_configuration, - Fcons (Qt, + Fcons ( + /* If we're at top minibuffer level in a + minibuffer-only frame, arrange for the + frame later to be switched back. */ + (FRAME_MINIBUF_ONLY_P (XFRAME (mini_frame)) + && minibuf_level <= 1) + ? Qnil + : Qt, Fcurrent_window_configuration (mini_frame))); /* If the minibuffer is on an iconified or invisible frame, > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 10:27 ` Alan Mackenzie @ 2020-11-21 11:55 ` martin rudalics 2020-11-21 12:45 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-21 11:55 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, Stefan Monnier, Eli Zaretskii, Andrii Kolomoiets, emacs-devel > Here's a quick and dirty patch to minibuf.c which appears to fix the bug, > but I haven't given it much testing yet: This will still fail when read_minibuf, triggered from any frame but the minibuffer frame, exits with minibuf_level > 0. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 11:55 ` martin rudalics @ 2020-11-21 12:45 ` Alan Mackenzie 2020-11-21 15:53 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-21 12:45 UTC (permalink / raw) To: martin rudalics Cc: enometh, Stefan Monnier, Eli Zaretskii, Andrii Kolomoiets, emacs-devel Hello, Martin. On Sat, Nov 21, 2020 at 12:55:41 +0100, martin rudalics wrote: > > Here's a quick and dirty patch to minibuf.c which appears to fix the bug, > > but I haven't given it much testing yet: > This will still fail when read_minibuf, triggered from any frame but the > minibuffer frame, exits with minibuf_level > 0. (i) I start emacs -Q --load=foo.el (ii) C-x 5 2 ; this leaves three frames, one being the minibuffer frame. (iii) C-x b ; leaves point in the minibuffer. (iv) C-x 5 o ; moves to the other normal frame. (v) C-r C-x 8 RET SPACE RET ; invokes a recursive minibuffer At this stage, point is in the minibuffer, exactly as after (iii). I don't see this as a failure. Where do you see the failure happening with that quick and dirty patch? > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 12:45 ` Alan Mackenzie @ 2020-11-21 15:53 ` martin rudalics 2020-11-22 10:59 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-21 15:53 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel > Where do you see the failure happening with that quick and dirty patch? Load (via --load) a file with the following contents: (setq default-frame-alist '((minibuffer . nil))) (setq enable-recursive-minibuffers t) (defun foo () (interactive) (read-from-minibuffer "...?") (insert (format "%s" (selected-frame)))) (global-set-key [(control meta +)] 'foo) Now type C-x 5 2 followed by C-M-+ in one of the normal frames. Then type C-M-+ in the other normal frame and type RET in the minibuffer frame. Here I get in *scratch* #<frame *Minibuf-2* 0x313ab10> and only answering the second prompt gets me a normal frame inserted. Emacs 27 after any of these prompts inserts the frame selected at the time I typed C-M-+ which is TRT. As I mentioned earlier in this thread, such tribulations are the price we have to pay for providing non-modal dialogues. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 15:53 ` martin rudalics @ 2020-11-22 10:59 ` Alan Mackenzie 2020-11-22 15:13 ` Stefan Monnier 2020-11-22 17:57 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-22 10:59 UTC (permalink / raw) To: martin rudalics Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel Hello, Martin. On Sat, Nov 21, 2020 at 16:53:11 +0100, martin rudalics wrote: > > Where do you see the failure happening with that quick and dirty patch? > Load (via --load) a file with the following contents: > (setq default-frame-alist '((minibuffer . nil))) > (setq enable-recursive-minibuffers t) > (defun foo () > (interactive) > (read-from-minibuffer "...?") > (insert (format "%s" (selected-frame)))) > (global-set-key [(control meta +)] 'foo) > Now type C-x 5 2 followed by C-M-+ in one of the normal frames. Then > type C-M-+ in the other normal frame and type RET in the minibuffer > frame. Here I get in *scratch* > #<frame *Minibuf-2* 0x313ab10> > and only answering the second prompt gets me a normal frame inserted. Yes. It seems my patch from yesterday was too complicated. At the record_unwind_protect for the window configuration of the calling frame (when that is different from the minibuffer frame) in read_minibuf, we can set it up later to select that calling frame unconditionally. Basically, if the MB is on a different frame, we want to return to the calling frame always. So, instead of yesterday's patch, I think this patch works: diff --git a/src/minibuf.c b/src/minibuf.c index 464e3018f7..fc3fd92a88 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -508,7 +508,10 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); if (!EQ (mini_frame, selected_frame)) record_unwind_protect (restore_window_configuration, - Fcons (Qt, + Fcons (/* Arrange for the frame later to be + switched back to the calling + frame. */ + Qnil, Fcurrent_window_configuration (mini_frame))); /* If the minibuffer is on an iconified or invisible frame, > Emacs 27 after any of these prompts inserts the frame selected at the > time I typed C-M-+ which is TRT. There seem to be quite a few things wrong, even on Emacs 27. On starting it with loading your initialisation file from the command line, the initial selected frame is the minibuffer frame, which is surely suboptimal. On M-: followed by C-x 5 o (moving to the normal frame), the unfinished command in the minibuffer frame cannot now be cancelled, and C-x 5 o doesn't move back into the minibuffer. I'm coming round to your way of thinking, that this whole area is a mess, and needs redesigning. > As I mentioned earlier in this thread, such tribulations are the price > we have to pay for providing non-modal dialogues. Yes, maybe, but modal dialogues are a right pain for the user, as can be seen by using virtually any commericial software on non-free systems. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-22 10:59 ` Alan Mackenzie @ 2020-11-22 15:13 ` Stefan Monnier 2020-11-22 17:11 ` Alan Mackenzie 2020-11-22 17:57 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-11-22 15:13 UTC (permalink / raw) To: Alan Mackenzie Cc: martin rudalics, enometh, Eli Zaretskii, Andrii Kolomoiets, emacs-devel > Yes. It seems my patch from yesterday was too complicated. At the Agreed. I think we should more narrowly taylor-make it for the problem at hand: i.e. when signaling an error, remember the selected-frame somewhere before doing the longjmp, and then when emitting the error message make use of that remembered selected-frame to choose which miniwindow to use for the message. Basically, the problem at hand is that we want to emit the error message "as if" we did it before the longjmp, but for technical reasons we need to do it after the longjmp. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-22 15:13 ` Stefan Monnier @ 2020-11-22 17:11 ` Alan Mackenzie 2020-11-22 19:58 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-22 17:11 UTC (permalink / raw) To: Stefan Monnier Cc: martin rudalics, enometh, Eli Zaretskii, Andrii Kolomoiets, emacs-devel Hello, Stefan. On Sun, Nov 22, 2020 at 10:13:35 -0500, Stefan Monnier wrote: > > Yes. It seems my patch from yesterday was too complicated. At the > Agreed. I think we should more narrowly taylor-make it for the problem > at hand: i.e. when signaling an error, remember the selected-frame > somewhere before doing the longjmp, and then when emitting the error > message make use of that remembered selected-frame to choose which > miniwindow to use for the message. Er, I think you're talking about the patch I committed on Thursday, not the proposed one I sent to Martin. > Basically, the problem at hand is that we want to emit the error message > "as if" we did it before the longjmp, but for technical reasons we need > to do it after the longjmp. > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-22 17:11 ` Alan Mackenzie @ 2020-11-22 19:58 ` Stefan Monnier 0 siblings, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-11-22 19:58 UTC (permalink / raw) To: Alan Mackenzie Cc: martin rudalics, enometh, Eli Zaretskii, Andrii Kolomoiets, emacs-devel >> Agreed. I think we should more narrowly taylor-make it for the problem >> at hand: i.e. when signaling an error, remember the selected-frame >> somewhere before doing the longjmp, and then when emitting the error >> message make use of that remembered selected-frame to choose which >> miniwindow to use for the message. > > Er, I think you're talking about the patch I committed on Thursday, not > the proposed one I sent to Martin. Hmm... like King Crimson said: confusion will be my epitaph, Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-22 10:59 ` Alan Mackenzie 2020-11-22 15:13 ` Stefan Monnier @ 2020-11-22 17:57 ` martin rudalics 2020-11-22 18:38 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-22 17:57 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier > diff --git a/src/minibuf.c b/src/minibuf.c > index 464e3018f7..fc3fd92a88 100644 > --- a/src/minibuf.c > +++ b/src/minibuf.c > @@ -508,7 +508,10 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, > mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); > if (!EQ (mini_frame, selected_frame)) > record_unwind_protect (restore_window_configuration, > - Fcons (Qt, > + Fcons (/* Arrange for the frame later to be > + switched back to the calling > + frame. */ > + Qnil, > Fcurrent_window_configuration (mini_frame))); > > /* If the minibuffer is on an iconified or invisible frame, This one immediately chokes when I run emacs -Q --eval "(setq default-frame-alist '((minibuffer . nil)))" and type C-x 5 2. Here the cursor disappears entirely although when I do some typing now the text shows up in the minibuffer window. TRT as with Emacs 27 is to select and focus the new frame. > There seem to be quite a few things wrong, even on Emacs 27. On > starting it with loading your initialisation file from the command line, > the initial selected frame is the minibuffer frame, which is surely > suboptimal. Here the minibuffer-only frame is selected but partially hidden by the normal frame so that I don't see no cursor initially. I don't know why people like it that way. A minibuffer child frame is explicitly not selected. Note that the way Emacs creates the two frames layout is atrocious - we first make a normal frame to do our usual initialization stuff and then we create the minibuffer-only and the minibuffer-less frames and delete the initial one (compare 'frame-notice-user-settings'). It's for years that I want to rewrite that ... > On M-: followed by C-x 5 o (moving to the normal frame), > the unfinished command in the minibuffer frame cannot now be cancelled, > and C-x 5 o doesn't move back into the minibuffer. 'other-frame' never selects a minibuffer-only frame. It probably should. And the behavior of C-g in a separate minibuffer frame IME usually varies with the time of the day. > Yes, maybe, but modal dialogues are a right pain for the user, as can be > seen by using virtually any commericial software on non-free systems. Modal dialogues are supported by the windowing subsystem, regardless of whether it's free or not. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-22 17:57 ` martin rudalics @ 2020-11-22 18:38 ` Alan Mackenzie 2020-11-23 9:10 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-22 18:38 UTC (permalink / raw) To: martin rudalics Cc: enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier Hello, Martin. On Sun, Nov 22, 2020 at 18:57:30 +0100, martin rudalics wrote: > > diff --git a/src/minibuf.c b/src/minibuf.c > > index 464e3018f7..fc3fd92a88 100644 > > --- a/src/minibuf.c > > +++ b/src/minibuf.c > > @@ -508,7 +508,10 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, > > mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window)); > > if (!EQ (mini_frame, selected_frame)) > > record_unwind_protect (restore_window_configuration, > > - Fcons (Qt, > > + Fcons (/* Arrange for the frame later to be > > + switched back to the calling > > + frame. */ > > + Qnil, > > Fcurrent_window_configuration (mini_frame))); > > > > /* If the minibuffer is on an iconified or invisible frame, > This one immediately chokes when I run > emacs -Q --eval "(setq default-frame-alist '((minibuffer . nil)))" > and type C-x 5 2. Here the cursor disappears entirely although when I > do some typing now the text shows up in the minibuffer window. TRT as > with Emacs 27 is to select and focus the new frame. On my machine (XFCE on X-Windows on GNU) I see TRT when I do this. Could it be something to do with your window manager? > > There seem to be quite a few things wrong, even on Emacs 27. On > > starting it with loading your initialisation file from the command > > line, the initial selected frame is the minibuffer frame, which is > > surely suboptimal. > Here the minibuffer-only frame is selected but partially hidden by the > normal frame so that I don't see no cursor initially. I don't know why > people like it that way. A minibuffer child frame is explicitly not > selected. Do people like it, or is it just not a big enough annoyance for anybody to complain? If I were a minibuffer-only frame user, I suspect it would drive me up the wall. > Note that the way Emacs creates the two frames layout is atrocious - we > first make a normal frame to do our usual initialization stuff and then > we create the minibuffer-only and the minibuffer-less frames and delete > the initial one (compare 'frame-notice-user-settings'). It's for years > that I want to rewrite that ... Yes, I can understand that. But you will be aware of all the nasty little things which will leap out in front of you and force you to solve along the way. > > On M-: followed by C-x 5 o (moving to the normal frame), > > the unfinished command in the minibuffer frame cannot now be cancelled, > > and C-x 5 o doesn't move back into the minibuffer. > 'other-frame' never selects a minibuffer-only frame. It probably should. I'm more of the view that a minibuffer-only frame should never be selected other than by activating a minibuffer. > And the behavior of C-g in a separate minibuffer frame IME usually > varies with the time of the day. > > Yes, maybe, but modal dialogues are a right pain for the user, as can be > > seen by using virtually any commericial software on non-free systems. > Modal dialogues are supported by the windowing subsystem, regardless of > whether it's free or not. Yes. I hate these dialogues, which force you to cancel them and start again, should you need some information from another part of the program to be able to fill them in. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-22 18:38 ` Alan Mackenzie @ 2020-11-23 9:10 ` martin rudalics 2020-11-23 13:36 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-23 9:10 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, Stefan Monnier, Eli Zaretskii, Andrii Kolomoiets, emacs-devel > On my machine (XFCE on X-Windows on GNU) I see TRT when I do this. Could > it be something to do with your window manager? Here it's xfce 4.12 with xfwm4 so probably something very similar to yours. But since the behavior does not depend on your patches as I just verified, something else must be causing it. >> Here the minibuffer-only frame is selected but partially hidden by the >> normal frame so that I don't see no cursor initially. I don't know why >> people like it that way. A minibuffer child frame is explicitly not >> selected. > > Do people like it, or is it just not a big enough annoyance for anybody > to complain? If I were a minibuffer-only frame user, I suspect it would > drive me up the wall. I suppose we only have two such users - Stefan and Drew - and they seem to like it (or work around it). >> 'other-frame' never selects a minibuffer-only frame. It probably should. > > I'm more of the view that a minibuffer-only frame should never be > selected other than by activating a minibuffer. Then what did you mean with the last line of >> On M-: followed by C-x 5 o (moving to the normal frame), >> the unfinished command in the minibuffer frame cannot now be cancelled, >> and C-x 5 o doesn't move back into the minibuffer. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 9:10 ` martin rudalics @ 2020-11-23 13:36 ` Alan Mackenzie 2020-11-23 14:22 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-23 13:36 UTC (permalink / raw) To: martin rudalics Cc: enometh, Stefan Monnier, Eli Zaretskii, Andrii Kolomoiets, emacs-devel Hello, Martin. On Mon, Nov 23, 2020 at 10:10:36 +0100, martin rudalics wrote: > > On my machine (XFCE on X-Windows on GNU) I see TRT when I do this. Could > > it be something to do with your window manager? > Here it's xfce 4.12 with xfwm4 so probably something very similar to > yours. But since the behavior does not depend on your patches as I just > verified, something else must be causing it. OK. > >> Here the minibuffer-only frame is selected but partially hidden by the > >> normal frame so that I don't see no cursor initially. I don't know why > >> people like it that way. A minibuffer child frame is explicitly not > >> selected. > > Do people like it, or is it just not a big enough annoyance for anybody > > to complain? If I were a minibuffer-only frame user, I suspect it would > > drive me up the wall. > I suppose we only have two such users - Stefan and Drew - and they seem > to like it (or work around it). Maybe one or other of them might answer this point. > >> 'other-frame' never selects a minibuffer-only frame. It probably should. > > I'm more of the view that a minibuffer-only frame should never be > > selected other than by activating a minibuffer. > Then what did you mean with the last line of > >> On M-: followed by C-x 5 o (moving to the normal frame), > >> the unfinished command in the minibuffer frame cannot now be cancelled, > >> and C-x 5 o doesn't move back into the minibuffer. It was a complaint about not being able to cancel the unfinished command in the minibuffer. One should be able to cancel such a command. Eventually it occurred to me that I could click on the minibuffer frame with the mouse, which I did, and then C-g worked. If you have nothing against it, I'll commit the fix from yesterday (the one supplying a Qnil to the future set-window-configuration) to master. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 13:36 ` Alan Mackenzie @ 2020-11-23 14:22 ` martin rudalics 2020-11-23 16:07 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-23 14:22 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel >> Then what did you mean with the last line of > >> >> On M-: followed by C-x 5 o (moving to the normal frame), >> >> the unfinished command in the minibuffer frame cannot now be cancelled, >> >> and C-x 5 o doesn't move back into the minibuffer. > > It was a complaint about not being able to cancel the unfinished command > in the minibuffer. One should be able to cancel such a command. > Eventually it occurred to me that I could click on the minibuffer frame > with the mouse, C-x o should have worked too. > which I did, and then C-g worked. > > If you have nothing against it, I'll commit the fix from yesterday (the > one supplying a Qnil to the future set-window-configuration) to master. Commit it. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 14:22 ` martin rudalics @ 2020-11-23 16:07 ` Alan Mackenzie 2020-11-23 18:08 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-23 16:07 UTC (permalink / raw) To: martin rudalics Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel Hello, Martin. On Mon, Nov 23, 2020 at 15:22:08 +0100, martin rudalics wrote: > >> Then what did you mean with the last line of > >> >> On M-: followed by C-x 5 o (moving to the normal frame), > >> >> the unfinished command in the minibuffer frame cannot now be cancelled, > >> >> and C-x 5 o doesn't move back into the minibuffer. > > It was a complaint about not being able to cancel the unfinished command > > in the minibuffer. One should be able to cancel such a command. > > Eventually it occurred to me that I could click on the minibuffer frame > > with the mouse, > C-x o should have worked too. > > which I did, and then C-g worked. > > If you have nothing against it, I'll commit the fix from yesterday (the > > one supplying a Qnil to the future set-window-configuration) to master. > Commit it. DONE. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 16:07 ` Alan Mackenzie @ 2020-11-23 18:08 ` martin rudalics 2020-11-23 20:16 ` Andrii Kolomoiets 2020-11-23 20:22 ` Gregory Heytings via Emacs development discussions. 0 siblings, 2 replies; 252+ messages in thread From: martin rudalics @ 2020-11-23 18:08 UTC (permalink / raw) To: Alan Mackenzie Cc: enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier > DONE. Thanks. To elaborate on an earlier problem I mentioned which, as I found out, happens already with Emacs 27 at the least. I'd be just interested if you can reproduce it on your system. Start with emacs -Q --eval "(setq default-frame-alist '((minibuffer . nil)))" Now in the minibuffer window type C-h f and at the prompt type setq RET. This pops up a help window on the normal frame explaining 'setq' and 'Type "q" in help window to delete it.' appears in the minibuffer window. Here, sometimes the normal frame is selected, sometimes the minibuffer frame remains selected. It might be a timing issue since sometimes I see the cursor flicker at the end of the 'Type "q" ...' text before it moves to the normal frame, provided it moves there. The strange thing that happens is when I now type "q" in the help window. Here the minibuffer window gets selected and ready to receive input but no cursor is shown in it. Do you see that? Anyone else? Thanks, martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 18:08 ` martin rudalics @ 2020-11-23 20:16 ` Andrii Kolomoiets 2020-11-24 8:46 ` martin rudalics 2020-11-23 20:22 ` Gregory Heytings via Emacs development discussions. 1 sibling, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-23 20:16 UTC (permalink / raw) To: martin rudalics Cc: Alan Mackenzie, enometh, emacs-devel, Eli Zaretskii, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 1452 bytes --] In GNU Emacs 28.0.50 (build 24, x86_64-apple-darwin19.6.0, NS appkit-2022.10 Version 11.0.1 (Build 20B29)) Windowing system distributor 'Apple', version 10.3.2022 System Description: macOS 11.0.1 > Thanks. To elaborate on an earlier problem I mentioned which, as I > found out, happens already with Emacs 27 at the least. I'd be just > interested if you can reproduce it on your system. Start with > > emacs -Q --eval "(setq default-frame-alist '((minibuffer . nil)))" To make both frames visible I use the following command: emacs -Q --eval "(setq initial-frame-alist '((minibuffer) (top . 86)))" > Now in the minibuffer window type C-h f and at the prompt type setq RET. > This pops up a help window on the normal frame explaining 'setq' and > 'Type "q" in help window to delete it.' appears in the minibuffer > window. Here, sometimes the normal frame is selected, sometimes the > minibuffer frame remains selected. It might be a timing issue since > sometimes I see the cursor flicker at the end of the 'Type "q" ...' > text before it moves to the normal frame, provided it moves there. Here the normal frame is visually selected, but the minibuffer frame is ready to receive input. See attached screenshot. > The strange thing that happens is when I now type "q" in the help > window. Here the minibuffer window gets selected and ready to receive > input but no cursor is shown in it. Do you see that? Anyone else? Can confirm. [-- Attachment #2: chf.png --] [-- Type: image/png, Size: 114767 bytes --] ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 20:16 ` Andrii Kolomoiets @ 2020-11-24 8:46 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-24 8:46 UTC (permalink / raw) To: Andrii Kolomoiets Cc: Alan Mackenzie, enometh, Eli Zaretskii, Stefan Monnier, emacs-devel > To make both frames visible I use the following command: > > emacs -Q --eval "(setq initial-frame-alist '((minibuffer) (top . 86)))" Yes. I'm using (top . -1). > Here the normal frame is visually selected, but the minibuffer frame is > ready to receive input. See attached screenshot. That should be focus redirection at work. 'Type "q" in help window to delete it.' is a hint that the normal frame is not selected internally so C-x 1 would not work. I see no logical flaws till here. >> The strange thing that happens is when I now type "q" in the help >> window. Here the minibuffer window gets selected and ready to receive >> input but no cursor is shown in it. Do you see that? Anyone else? > > Can confirm. Thanks. I have no idea why this happens - window point and start are both at 1 and the buffer is empty. Either redisplay gets confused or this is by design ... martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 18:08 ` martin rudalics 2020-11-23 20:16 ` Andrii Kolomoiets @ 2020-11-23 20:22 ` Gregory Heytings via Emacs development discussions. 2020-11-23 20:26 ` Andrii Kolomoiets 2020-11-24 8:46 ` martin rudalics 1 sibling, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-23 20:22 UTC (permalink / raw) To: martin rudalics Cc: Alan Mackenzie, enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier > > Thanks. To elaborate on an earlier problem I mentioned which, as I > found out, happens already with Emacs 27 at the least. I'd be just > interested if you can reproduce it on your system. Start with > > emacs -Q --eval "(setq default-frame-alist '((minibuffer . nil)))" > > Now in the minibuffer window type C-h f and at the prompt type setq RET. > This pops up a help window on the normal frame explaining 'setq' and > 'Type "q" in help window to delete it.' appears in the minibuffer > window. Here, sometimes the normal frame is selected, sometimes the > minibuffer frame remains selected. It might be a timing issue since > sometimes I see the cursor flicker at the end of the 'Type "q" ...' text > before it moves to the normal frame, provided it moves there. > > The strange thing that happens is when I now type "q" in the help > window. Here the minibuffer window gets selected and ready to receive > input but no cursor is shown in it. Do you see that? Anyone else? > I can confirm this behavior. It does not happen with Emacs 23 to 25, and happens with Emacs 26 to 28. Except that: "Now in the minibuffer window type C-h f and..." is (in my case at least) "Select the miniwindow frame, type C-h f, and..." I did not see the "sometimes the normal frame is selected, sometimes the minibuffer frame remains selected", in my case at least the miniwindow frame remains selected in all cases. Which means that I need to click in the *Help* buffer before typing "q". "Here the minibuffer window gets selected and ready to receive input but no cursor is shown in it." is (in my case at least) "The minibuffer gets selected and ready to receive input but no cursor is shown in it AND the miniwindow frame is not raised (put above the main frame by the window manager)". Another strange thing is that at that point a number of keys are bound to something, as if something like "C-x 5" was always pressed: "b" says "Switch to buffer in other frame" "f" says "Find file in other frame:" "m" opens "mail" "i" opens "info" "n" creates a new frame But it is not the "C-x 5" keymap, because "d" or "r" for example are not bound, and "n" is not bound in "C-x 5". ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 20:22 ` Gregory Heytings via Emacs development discussions. @ 2020-11-23 20:26 ` Andrii Kolomoiets 2020-11-24 8:47 ` martin rudalics 2020-11-24 8:46 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-23 20:26 UTC (permalink / raw) To: Gregory Heytings Cc: emacs-devel, martin rudalics, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii Gregory Heytings <ghe@sdf.org> writes: > Another strange thing is that at that point a number of keys are bound > to something, as if something like "C-x 5" was always pressed: > > "b" says "Switch to buffer in other frame" > "f" says "Find file in other frame:" > "m" opens "mail" > "i" opens "info" > "n" creates a new frame > > But it is not the "C-x 5" keymap, because "d" or "r" for example are > not bound, and "n" is not bound in "C-x 5". Those keys are from the 'minibuffer-inactive-mode-map' variable: Keymap for use in the minibuffer when it is not active. The non-mouse bindings in this keymap can only be used in minibuffer-only frames, since the minibuffer can normally not be selected when it is not active. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 20:26 ` Andrii Kolomoiets @ 2020-11-24 8:47 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-24 8:47 UTC (permalink / raw) To: Andrii Kolomoiets, Gregory Heytings Cc: Alan Mackenzie, enometh, Eli Zaretskii, Stefan Monnier, emacs-devel >> Another strange thing is that at that point a number of keys are bound >> to something, as if something like "C-x 5" was always pressed: >> >> "b" says "Switch to buffer in other frame" >> "f" says "Find file in other frame:" >> "m" opens "mail" >> "i" opens "info" >> "n" creates a new frame >> >> But it is not the "C-x 5" keymap, because "d" or "r" for example are >> not bound, and "n" is not bound in "C-x 5". > > Those keys are from the 'minibuffer-inactive-mode-map' variable: > > Keymap for use in the minibuffer when it is not active. > The non-mouse bindings in this keymap can only be used in minibuffer-only > frames, since the minibuffer can normally not be selected when it is > not active. Thanks for the explanation. Maybe this is supposed to handle the limbo state where the WM focuses the minibuffer-onlly frame but Emacs doesn't want or need that. It might explain then that the minibuffer-only frame gets focus initially. The rationale behind this is nowhere described. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-23 20:22 ` Gregory Heytings via Emacs development discussions. 2020-11-23 20:26 ` Andrii Kolomoiets @ 2020-11-24 8:46 ` martin rudalics 2020-11-24 10:25 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-24 8:46 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > I can confirm this behavior. Which OS? > It does not happen with Emacs 23 to 25, and happens with Emacs 26 to 28. That's interesting. I'll have to take a look. > Except that: > > "Now in the minibuffer window type C-h f and..." is (in my case at least) "Select the miniwindow frame, type C-h f, and..." > > I did not see the "sometimes the normal frame is selected, sometimes > the minibuffer frame remains selected", in my case at least the > miniwindow frame remains selected in all cases. Which means that I > need to click in the *Help* buffer before typing "q". Same here, usually. But I managed a few times to have the normal frame selected. In that case, as I mentioned, I quickly saw the cursor flicker in the minibuffer window before it showed up on the normal frame. Also, instead of using the mouse, C-x 5 o followed by C-x o works too, but C-x o used directly in the minibuffer window does not work. > "Here the minibuffer window gets selected and ready to receive input > but no cursor is shown in it." is (in my case at least) "The > minibuffer gets selected and ready to receive input but no cursor is > shown in it AND the miniwindow frame is not raised (put above the main > frame by the window manager)". One can use (setq minibuffer-auto-raise t) If I do that, then typing something into the minibuffer window in the state where no cursor is seen, automatically switches to the normal frame. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-24 8:46 ` martin rudalics @ 2020-11-24 10:25 ` martin rudalics 2020-11-24 11:37 ` Gregory Heytings via Emacs development discussions. 2020-11-24 12:59 ` Andrii Kolomoiets 0 siblings, 2 replies; 252+ messages in thread From: martin rudalics @ 2020-11-24 10:25 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 319 bytes --] > > It does not happen with Emacs 23 to 25, and happens with Emacs 26 to 28. > > That's interesting. I'll have to take a look. OK. It was me who introduced that bug in Emacs 26. Many thanks for spotting it. Can both of you confirm that reverting that change fixes the behavior? Patch attached. Thanks, martin [-- Attachment #2: frame-redirect.diff --] [-- Type: text/x-patch, Size: 686 bytes --] diff --git a/src/frame.c b/src/frame.c index 512aaf5f45..f56176910e 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1426,11 +1426,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor if (FRAMEP (gfocus)) { focus = FRAME_FOCUS_FRAME (XFRAME (gfocus)); - if ((FRAMEP (focus) && XFRAME (focus) == SELECTED_FRAME ()) - /* Redirect frame focus also when FRAME has its minibuffer - window on the selected frame (see Bug#24500). */ - || (NILP (focus) - && EQ (FRAME_MINIBUF_WINDOW (f), sf->selected_window))) + if (FRAMEP (focus) && XFRAME (focus) == SELECTED_FRAME ()) Fredirect_frame_focus (gfocus, frame); } } ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-24 10:25 ` martin rudalics @ 2020-11-24 11:37 ` Gregory Heytings via Emacs development discussions. 2020-11-24 19:24 ` martin rudalics 2020-11-24 12:59 ` Andrii Kolomoiets 1 sibling, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-24 11:37 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >>> It does not happen with Emacs 23 to 25, and happens with Emacs 26 to >>> 28. >> >> That's interesting. I'll have to take a look. > > OK. It was me who introduced that bug in Emacs 26. Many thanks for > spotting it. > > Can both of you confirm that reverting that change fixes the behavior? > Patch attached. > Yes, it does. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-24 11:37 ` Gregory Heytings via Emacs development discussions. @ 2020-11-24 19:24 ` martin rudalics 2020-11-25 9:25 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-24 19:24 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> Can both of you confirm that reverting that change fixes the behavior? Patch attached. >> > > Yes, it does. Fine. I will push this to Emacs 27.2. Thanks, martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-24 19:24 ` martin rudalics @ 2020-11-25 9:25 ` martin rudalics 2020-11-25 21:09 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-25 9:25 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > Fine. I will push this to Emacs 27.2. Done. Alan, the following two concerns are still awaiting a fix: Madhu's: These patches introduce a regression on "graphical" emacs - 1. emacs -Q 2. M-: (setq pop-up-frames 'graphic-only) 3. M-! g <TAB> This should pop up a *Completions* buffer in a new frame. On choosing the completion (via a button1 or by navigating to the desired point and typing RET) - the frame should be automatically hidden[1] This doesn't happen anymore and the completion buffer and frame remain there taking up focus. [1] default value for frame-auto-hide-function is #'iconify-frame, but if your window manager cannot iconify it, set (setq frame-auto-hide-function #'delete-frame) Andrii's: It is not producing bugs for me, but changes behavior. E.g. in emacs -Q: 1. Evaluate (select-frame-set-input-focus (make-frame '((minibuffer . only) (left . 1.0)))) 2. M-x 3. C-x 5 o Before minibuffer-follows-selected-frame, the prompt stays in the minibuffer-only frame. On recent master, the prompt is moved to other frame leaving minibuffer-only frame empty. I can't report this as a bug. Just wondering why minibuffer-follows-selected-frame is set to t by default, potentially changing someone's expected behavior. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 9:25 ` martin rudalics @ 2020-11-25 21:09 ` Alan Mackenzie 2020-11-25 21:31 ` Gregory Heytings via Emacs development discussions. 2020-11-26 15:43 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-25 21:09 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin. On Wed, Nov 25, 2020 at 10:25:46 +0100, martin rudalics wrote: > > Fine. I will push this to Emacs 27.2. > Done. Alan, the following two concerns are still awaiting a fix: OK. > Madhu's: > These patches introduce a regression on "graphical" emacs - > 1. emacs -Q > 2. M-: (setq pop-up-frames 'graphic-only) > 3. M-! g <TAB> > This should pop up a *Completions* buffer in a new frame. > On choosing the completion (via a button1 or by navigating to the > desired point and typing RET) - the frame should be automatically > hidden[1] > This doesn't happen anymore and the completion buffer and frame remain > there taking up focus. I've started looking at this. It could take some time to resolve. > [1] default value for frame-auto-hide-function is #'iconify-frame, but > if your window manager cannot iconify it, set > (setq frame-auto-hide-function #'delete-frame) > Andrii's: > It is not producing bugs for me, but changes behavior. This is inevitable, even if regrettable. > E.g. in emacs -Q: > 1. Evaluate > (select-frame-set-input-focus > (make-frame '((minibuffer . only) > (left . 1.0)))) > 2. M-x > 3. C-x 5 o > Before minibuffer-follows-selected-frame, the prompt stays in the > minibuffer-only frame. > On recent master, the prompt is moved to other frame leaving > minibuffer-only frame empty. I can't report this as a bug. Just > wondering why minibuffer-follows-selected-frame is set to t by default, > potentially changing someone's expected behavior. The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves with a frame switch, sometimes it doesn't. The change in master is intended to make the behaviour logical and systematic. As to why minibuffer-follows-selected-frame is t by default, there is no particular reason. It transpired that Eli and I had different mental models of the connection between minibuffers and frames. The setting t represents Eli's model rather than mine (?ours). Setting it to nil by default would also cause annoyance, in different ways. Also, how often do people actually select minibuffer-only frames? Unless I'm missing something, it seems a rather strange thing to want to do. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 21:09 ` Alan Mackenzie @ 2020-11-25 21:31 ` Gregory Heytings via Emacs development discussions. 2020-11-25 21:54 ` Alan Mackenzie 2020-11-26 15:44 ` martin rudalics 2020-11-26 15:43 ` martin rudalics 1 sibling, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-25 21:31 UTC (permalink / raw) To: Alan Mackenzie Cc: martin rudalics, Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Eli Zaretskii > > The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves with > a frame switch, sometimes it doesn't. > I wouldn't write it is "chaotic". The behavior you consider "chaotic" is well-defined, and has been there since Emacs 21 at least: the minibuffer moves from frame F1 to frame F2 if and only if the minibuffer is active on frame F1 and a recursive minibuffer is entered on frame F2. There are other possible behaviors of course, but IMO the current one is a reasonable one. > > Also, how often do people actually select minibuffer-only frames? Unless > I'm missing something, it seems a rather strange thing to want to do. > There are at least two Emacs users on this list who use minibuffer-only frames: Stefan and Drew. I'm also curious why they do this, and would be interested if they could explain what the benefit of doing this is. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 21:31 ` Gregory Heytings via Emacs development discussions. @ 2020-11-25 21:54 ` Alan Mackenzie 2020-11-25 22:23 ` Gregory Heytings via Emacs development discussions. 2020-11-26 15:44 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-25 21:54 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hello, Gregory. On Wed, Nov 25, 2020 at 21:31:13 +0000, Gregory Heytings wrote: > > The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves with > > a frame switch, sometimes it doesn't. > I wouldn't write it is "chaotic". The behavior you consider "chaotic" is > well-defined, and has been there since Emacs 21 at least: the minibuffer > moves from frame F1 to frame F2 if and only if the minibuffer is active on > frame F1 and a recursive minibuffer is entered on frame F2. I'm not sure what you mean by "is" in that sentence. > There are other possible behaviors of course, but IMO the current one > is a reasonable one. If a recursive minibuffer operation has been carried out, then the minibuffer moves, if it hasn't it doesn't. That means Emacs has some invisible internal state, something which doesn't seem desirable. > > Also, how often do people actually select minibuffer-only frames? Unless > > I'm missing something, it seems a rather strange thing to want to do. > There are at least two Emacs users on this list who use minibuffer-only > frames: Stefan and Drew. Sorry, I don't think I was clear. By "select .... frames" I meant the operation of making the minibuffer frame the current frame, not the chosing of an Emacs setup which includes minibuffer-only frames. Such a minibuffer-only frame comes into operation whenever a minibuffer action is invoked in another frame, but actually selecting it independently of such a minibuffer action? > I'm also curious why they do this, and would be interested if they > could explain what the benefit of doing this is. It's also not a setup I would want to use. By preference, I use the Linux tty and only ever have one frame on the screen at a time. But I can imagine people wanting their interactive minibuffer always to be in the same place on a GUI screen. Or something like that. Maybe Stefan or Drew will answer this. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 21:54 ` Alan Mackenzie @ 2020-11-25 22:23 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:02 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-25 22:23 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii >>> The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves >>> with a frame switch, sometimes it doesn't. >> >> I wouldn't write it is "chaotic". The behavior you consider "chaotic" >> is well-defined, and has been there since Emacs 21 at least: the >> minibuffer moves from frame F1 to frame F2 if and only if the >> minibuffer is active on frame F1 and a recursive minibuffer is entered >> on frame F2. > > I'm not sure what you mean by "is" in that sentence. > I've reread what I wrote five times, and I don't understand the question ;-) >> There are other possible behaviors of course, but IMO the current one >> is a reasonable one. > > If a recursive minibuffer operation has been carried out, then the > minibuffer moves, if it hasn't it doesn't. That means Emacs has some > invisible internal state, something which doesn't seem desirable. > What do you mean? By definition a recursive minibuffer is entered when a minibuffer has been entered and not yet left, that is, when the operation has not yet been completed. After typing C-x C-f in frame F1 and M-: in frame F2, the two minibuffers have been moved to frame F2. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 22:23 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:02 ` Alan Mackenzie 2020-11-27 10:36 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 10:02 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hello, Gregory. On Wed, Nov 25, 2020 at 22:23:17 +0000, Gregory Heytings wrote: > >>> The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves > >>> with a frame switch, sometimes it doesn't. > >> I wouldn't write it is "chaotic". The behavior you consider "chaotic" > >> is well-defined, and has been there since Emacs 21 at least: the > >> minibuffer moves from frame F1 to frame F2 if and only if the > >> minibuffer is active on frame F1 and a recursive minibuffer is entered > >> on frame F2. > > I'm not sure what you mean by "is" in that sentence. > I've reread what I wrote five times, and I don't understand the question > ;-) It seems somewhat indefinite _when_ the recursive minibuffer "is" entered on frame F2, relative to the other operations. > >> There are other possible behaviors of course, but IMO the current one > >> is a reasonable one. > > If a recursive minibuffer operation has been carried out, then the > > minibuffer moves, if it hasn't it doesn't. That means Emacs has some > > invisible internal state, something which doesn't seem desirable. > What do you mean? In Emacs 27, emacs -Q, C-x 5 2, giving a two-frame setup. In F1, C-x b. C-x 5 o, moving to F2. C-r SPACE. Note that the open minibuffer remains on F1. Now type C-x 8 RET SPACE RET. This sucks the open minibuffer over to F2, despite the C-r operation having nothing to do with the suspended operation in the minibuffer. This is not good and has been/is in the process of being fixed for Emacs 28. > By definition a recursive minibuffer is entered when a minibuffer has > been entered and not yet left, that is, when the operation has not yet > been completed. After typing C-x C-f in frame F1 and M-: in frame F2, > the two minibuffers have been moved to frame F2. More precisely, if we're talking about Emacs 28 with minibuffer-follows-selected-frame at its default of t, the C-x C-f minibuffer moves to F2 when F2 gets selected. The M-: then gives an error message in F2, or (when enable-recursive-minibuffers is t) opens a recursive minibuffer in F2. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:02 ` Alan Mackenzie @ 2020-11-27 10:36 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:47 ` Gregory Heytings via Emacs development discussions. 2020-11-27 11:14 ` Alan Mackenzie 0 siblings, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:36 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hi Alan, >>>>> The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves >>>>> with a frame switch, sometimes it doesn't. >>>> >>>> I wouldn't write it is "chaotic". The behavior you consider >>>> "chaotic" is well-defined, and has been there since Emacs 21 at >>>> least: the minibuffer moves from frame F1 to frame F2 if and only if >>>> the minibuffer is active on frame F1 and a recursive minibuffer is >>>> entered on frame F2. >>> >>> I'm not sure what you mean by "is" in that sentence. >> >> I've reread what I wrote five times, and I don't understand the >> question ;-) > > It seems somewhat indefinite _when_ the recursive minibuffer "is" > entered on frame F2, relative to the other operations. > No it is not. Another, perhaps more precise definition would be: in Emacs 21 to 27, when minibuffers MB1 to MBn are active on a frame F1, and an operation which activates the minibuffer is used on a frame F2, the minibuffers MB1 to MBn are moved to frame F2, and the minibuffer MBn+1 is created. >>>> There are other possible behaviors of course, but IMO the current one >>>> is a reasonable one. >>> >>> If a recursive minibuffer operation has been carried out, then the >>> minibuffer moves, if it hasn't it doesn't. That means Emacs has some >>> invisible internal state, something which doesn't seem desirable. >> >> What do you mean? > > In Emacs 27, emacs -Q, C-x 5 2, giving a two-frame setup. In F1, C-x b. > C-x 5 o, moving to F2. C-r SPACE. Note that the open minibuffer > remains on F1. > Yes, this is normal, C-r does not use the minibuffer, but the echo area (you can see this because the cursor does not move leave the buffer in which you are). The minibuffers and echo area are both displayed in the miniwindow, but they are different. > > Now type C-x 8 RET SPACE RET. This sucks the open minibuffer over to > F2, despite the C-r operation having nothing to do with the suspended > operation in the minibuffer. > This is normal again, C-x 8 RET starts an operation which uses the minibuffer, and the cursor leaves the buffer in which you are and moves to the miniwindow. As explained above, when an operation which activates a minibuffer is used on a frame while other minibuffers are active on another frame, they are moved to that frame. I honestly can't see what's wrong with this. IMO the only other reasonable behavior is to make sure that _all_ minibuffers are moved from frame F1 to frame F2 whenever one switches from a frame F1 to a frame F2. This is not feasible with Emacs 21-27, and still not feasible with Emacs 28. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:36 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:47 ` Gregory Heytings via Emacs development discussions. 2020-11-27 11:20 ` Alan Mackenzie 2020-11-27 11:14 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:47 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii >> Now type C-x 8 RET SPACE RET. This sucks the open minibuffer over to >> F2, despite the C-r operation having nothing to do with the suspended >> operation in the minibuffer. > > This is normal again, C-x 8 RET starts an operation which uses the > minibuffer, and the cursor leaves the buffer in which you are and moves > to the miniwindow. As explained above, when an operation which > activates a minibuffer is used on a frame while other minibuffers are > active on another frame, they are moved to that frame. I honestly can't > see what's wrong with this. > > IMO the only other reasonable behavior is to make sure that _all_ > minibuffers are moved from frame F1 to frame F2 whenever one switches > from a frame F1 to a frame F2. This is not feasible with Emacs 21-27, > and still not feasible with Emacs 28. > I wrote too fast here: this is feasible with Emacs 28 (by setting minibuffer-follows-selected-frame to t, which is its default value), but there is no way to get the previous behavior of Emacs 21-27. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:47 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 11:20 ` Alan Mackenzie 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 11:20 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hello, Gregory. On Fri, Nov 27, 2020 at 10:47:49 +0000, Gregory Heytings wrote: [ .... ] > > IMO the only other reasonable behavior is to make sure that _all_ > > minibuffers are moved from frame F1 to frame F2 whenever one switches > > from a frame F1 to a frame F2. This is not feasible with Emacs > > 21-27, and still not feasible with Emacs 28. > I wrote too fast here: this is feasible with Emacs 28 (by setting > minibuffer-follows-selected-frame to t, which is its default value), but > there is no way to get the previous behavior of Emacs 21-27. The behaviour in Emacs 21-27 was unsatisfactory. Eli and I agreed this earlier on in this thread, and explicitly agreed the new behaviour would be different. That earlier behaviour was agreed to be chaotic, and would be very difficult to restore without breaking the recent changes in this area. What, in particular, do you miss from the old behaviour? If it's something specific, maybe it would be possible to add it in, somehow. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 11:20 ` Alan Mackenzie @ 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. 0 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 12:03 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hi Alan, > > The behaviour in Emacs 21-27 was unsatisfactory. Eli and I agreed this > earlier on in this thread, and explicitly agreed the new behaviour would > be different. > > That earlier behaviour was agreed to be chaotic, and would be very > difficult to restore without breaking the recent changes in this area. > > What, in particular, do you miss from the old behaviour? If it's > something specific, maybe it would be possible to add it in, somehow. > Simply: the old behavior. I agree that a new option "minibuffer-follows-selected-frame" would be welcome, and I also agree that it can be set to t by default in Emacs 28, but setting it to nil should restore the behavior of Emacs 21-27. Changing the longstanding behavior of something as central to Emacs as the minibuffer just because two people agree that it is "chaotic", while many have been using it for decades without complaining, and without giving those who for one reason or another like/rely on the old behavior a way to restore the old behavior, is just wrong IMO. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:36 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:47 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 11:14 ` Alan Mackenzie 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 11:14 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hello, Gregory. On Fri, Nov 27, 2020 at 10:36:35 +0000, Gregory Heytings wrote: > Hi Alan, > >>>>> The behaviour in Emacs 27 is chaotic. Sometimes a minibuffer moves > >>>>> with a frame switch, sometimes it doesn't. [ .... ] > >>> If a recursive minibuffer operation has been carried out, then the > >>> minibuffer moves, if it hasn't it doesn't. That means Emacs has some > >>> invisible internal state, something which doesn't seem desirable. > >> What do you mean? > > In Emacs 27, emacs -Q, C-x 5 2, giving a two-frame setup. In F1, C-x b. > > C-x 5 o, moving to F2. C-r SPACE. Note that the open minibuffer > > remains on F1. > Yes, this is normal, C-r does not use the minibuffer, but the echo area > (you can see this because the cursor does not move leave the buffer in > which you are). The minibuffers and echo area are both displayed in the > miniwindow, but they are different. > > Now type C-x 8 RET SPACE RET. This sucks the open minibuffer over to > > F2, despite the C-r operation having nothing to do with the suspended > > operation in the minibuffer. > This is normal again, C-x 8 RET starts an operation which uses the > minibuffer, and the cursor leaves the buffer in which you are and moves to > the miniwindow. As explained above, when an operation which activates a > minibuffer is used on a frame while other minibuffers are active on > another frame, they are moved to that frame. I honestly can't see what's > wrong with this. I can see a lot wrong with it, from a UI point of view. Whether the MB gets moved depends on _how_ a user enters characters into C-r. This is bad. The MB should either move when you type C-r or it shouldn't. Emacs 28 fixes this. Also we shouldn't force users to have to understand the difference between the echo area and the minibuffer. Some of them will understand, many will not. That most of isearch uses the echo area, but some of it uses the minibuffer, is an arcane implementation issue which users shouldn't have to worry about. We should leave them in peace. > IMO the only other reasonable behavior is to make sure that _all_ > minibuffers are moved from frame F1 to frame F2 whenever one switches from > a frame F1 to a frame F2. This is not feasible with Emacs 21-27, and > still not feasible with Emacs 28. As you note in your next post, it is feasible with Emacs 28, or at least will be when the bugs are fixed. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 11:14 ` Alan Mackenzie @ 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. 2020-11-27 15:42 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 12:03 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Stefan Monnier, Eli Zaretskii Hi Alan, >> This is normal again, C-x 8 RET starts an operation which uses the >> minibuffer, and the cursor leaves the buffer in which you are and moves >> to the miniwindow. As explained above, when an operation which >> activates a minibuffer is used on a frame while other minibuffers are >> active on another frame, they are moved to that frame. I honestly >> can't see what's wrong with this. > > I can see a lot wrong with it, from a UI point of view. Whether the MB > gets moved depends on _how_ a user enters characters into C-r. This is > bad. The MB should either move when you type C-r or it shouldn't. > I disagree with that explanation. You do not "enter characters into C-r". When you type characters after typing C-r, they are displayed in the echo area, but that doesn't mean you enter them there: the cursor does not move there. When you type C-x 8 RET, the cursor moves into the miniwindow, and at that point you type characters in a minibuffer. > > Emacs 28 fixes this. > At the cost of breaking a longstanding behavior... > > Also we shouldn't force users to have to understand the difference > between the echo area and the minibuffer. Some of them will understand, > many will not. > That's a basic thing to learn when you use Emacs. And it's easy to understand: the echo area is for "status messages" with which you do not interact (and this is visible because the point does not leave the buffer in which you are), minibuffers are for "commands" with which you interact (and this is visible because the point moves from the buffer in which you are to the miniwindow). > > That most of isearch uses the echo area, but some of it uses the > minibuffer, is an arcane implementation issue which users shouldn't have > to worry about. We should leave them in peace. > I doubt a user who does not understand the difference between the echo area and the minibuffer would use C-x 8 RET during an isearch. And even then, with the above explanation that difference is easy to understand. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 15:42 ` martin rudalics 2020-11-27 15:54 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 15:42 UTC (permalink / raw) To: Gregory Heytings, Alan Mackenzie Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel > That's a basic thing to learn when you use Emacs. And it's easy to > understand: the echo area is for "status messages" with which you do > not interact (and this is visible because the point does not leave the > buffer in which you are), minibuffers are for "commands" with which > you interact (and this is visible because the point moves from the > buffer in which you are to the miniwindow). It's not so easy. With Emacs 26, 'y-or-n-p' still used the echo area for answering questions. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 15:42 ` martin rudalics @ 2020-11-27 15:54 ` Gregory Heytings via Emacs development discussions. 2020-11-27 17:14 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 15:54 UTC (permalink / raw) To: martin rudalics Cc: Alan Mackenzie, Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Eli Zaretskii >> That's a basic thing to learn when you use Emacs. And it's easy to >> understand: the echo area is for "status messages" with which you do >> not interact (and this is visible because the point does not leave the >> buffer in which you are), minibuffers are for "commands" with which you >> interact (and this is visible because the point moves from the buffer >> in which you are to the miniwindow). > > It's not so easy. With Emacs 26, 'y-or-n-p' still used the echo area > for answering questions. > Okay, there are (or rather, IIUC, there were) exceptions to that general rule ;-) But I think you meant Emacs 25 and earlier, with my Emacs 26 y-or-n-p does use the minibuffer. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 15:54 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 17:14 ` martin rudalics 2020-11-27 17:43 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 17:14 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > But I think you meant Emacs 25 and earlier, with my Emacs 26 y-or-n-p > does use the minibuffer. This time I did read the NEWS entry. For Emacs 27.1 it says *** 'y-or-n-p' now uses the minibuffer to read 'y' or 'n' answer. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 17:14 ` martin rudalics @ 2020-11-27 17:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:08 ` martin rudalics 2020-11-27 18:50 ` Eli Zaretskii 0 siblings, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 17:43 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> But I think you meant Emacs 25 and earlier, with my Emacs 26 y-or-n-p >> does use the minibuffer. > > This time I did read the NEWS entry. For Emacs 27.1 it says > > *** 'y-or-n-p' now uses the minibuffer to read 'y' or 'n' answer. > Never trust the documentation ;-) (progn (setq frame-title-format "%b") (y-or-n-p "*Minibuf-1*?")) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 17:43 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 18:08 ` martin rudalics 2020-11-27 20:02 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:50 ` Eli Zaretskii 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 18:08 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > Never trust the documentation ;-) > > (progn (setq frame-title-format "%b") (y-or-n-p "*Minibuf-1*?")) Never trust 'frame-title-format' ;-) (progn (display-buffer (get-buffer-create " *Echo Area 1*")) (y-or-n-p "......")) martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 18:08 ` martin rudalics @ 2020-11-27 20:02 ` Gregory Heytings via Emacs development discussions. 0 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 20:02 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> Never trust the documentation ;-) >> >> (progn (setq frame-title-format "%b") (y-or-n-p "*Minibuf-1*?")) > > Never trust 'frame-title-format' ;-) > > (progn > (display-buffer (get-buffer-create " *Echo Area 1*")) > (y-or-n-p "......")) > Whoops, indeed, you are correct. I accidentally changed a link on my computer a few days ago, and what I thought was Emacs 26 was in fact Emacs 27. Sorry for the noise! ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 17:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:08 ` martin rudalics @ 2020-11-27 18:50 ` Eli Zaretskii 1 sibling, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-27 18:50 UTC (permalink / raw) To: Gregory Heytings Cc: andreyk.mad, emacs-devel, rudalics, enometh, monnier, acm > Date: Fri, 27 Nov 2020 17:43:14 +0000 > From: Gregory Heytings <ghe@sdf.org> > cc: Andrii Kolomoiets <andreyk.mad@gmail.com>, emacs-devel@gnu.org, > enometh@meer.net, Stefan Monnier <monnier@iro.umontreal.ca>, > Alan Mackenzie <acm@muc.de>, Eli Zaretskii <eliz@gnu.org> > > > This time I did read the NEWS entry. For Emacs 27.1 it says > > > > *** 'y-or-n-p' now uses the minibuffer to read 'y' or 'n' answer. > > > > Never trust the documentation ;-) > > (progn (setq frame-title-format "%b") (y-or-n-p "*Minibuf-1*?")) This tells me Emacs 26 didn't use the minibuffer. How did you evaluate this? You should do it with "C-x C-e", not with "M-:". ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 21:31 ` Gregory Heytings via Emacs development discussions. 2020-11-25 21:54 ` Alan Mackenzie @ 2020-11-26 15:44 ` martin rudalics 2020-11-26 20:32 ` Gregory Heytings via Emacs development discussions. ` (2 more replies) 1 sibling, 3 replies; 252+ messages in thread From: martin rudalics @ 2020-11-26 15:44 UTC (permalink / raw) To: Gregory Heytings, Alan Mackenzie Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel > I wouldn't write it is "chaotic". The behavior you consider "chaotic" > is well-defined, and has been there since Emacs 21 at least: the > minibuffer moves from frame F1 to frame F2 if and only if the > minibuffer is active on frame F1 and a recursive minibuffer is entered > on frame F2. There are other possible behaviors of course, but IMO > the current one is a reasonable one. The basic behavioral change I see is with 'enable-recursive-minibuffers' non-nil and two frames: When I type C-h f setq in the first frame and C-h f cons in the second frame, hit RET, reselect the minibuffer window and hit RET again, with Emacs 27 a help window pops up in the first frame while Emacs 28 reuses the help window of the second frame. In both cases the second RET goes to the second frame and both behaviors seem reasonable to me. If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, the behavior does not entirely match that of Emacs 27 because the second RET must be typed in the first frame. So if some application relies on the exact replication of the behavior of Emacs 27, we have a regression. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-26 15:44 ` martin rudalics @ 2020-11-26 20:32 ` Gregory Heytings via Emacs development discussions. 2020-11-27 7:33 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:13 ` Alan Mackenzie 2 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-26 20:32 UTC (permalink / raw) To: martin rudalics Cc: Alan Mackenzie, enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel >> I wouldn't write it is "chaotic". The behavior you consider "chaotic" >> is well-defined, and has been there since Emacs 21 at least: the >> minibuffer moves from frame F1 to frame F2 if and only if the >> minibuffer is active on frame F1 and a recursive minibuffer is entered >> on frame F2. There are other possible behaviors of course, but IMO the >> current one is a reasonable one. > > The basic behavioral change I see is with 'enable-recursive-minibuffers' > non-nil and two frames: When I type C-h f setq in the first frame and > C-h f cons in the second frame, hit RET, reselect the minibuffer window > and hit RET again, with Emacs 27 a help window pops up in the first > frame while Emacs 28 reuses the help window of the second frame. In > both cases the second RET goes to the second frame and both behaviors > seem reasonable to me. > > If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, > the behavior does not entirely match that of Emacs 27 because the second > RET must be typed in the first frame. So if some application relies on > the exact replication of the behavior of Emacs 27, we have a regression. > Note that this patch and discussion started with the following observation (on Oct 13): > (i) Have two frames open displaying buffers. > (ii) On frame F1 do C-x b. This leaves a minibuffer open there. > (iii) Move to F2. > (iv) Do C-x 8 RET <enter some character>. > F1's minibuffer is now on F2. This is bad. It is indeed not possible to replicate the behavior of Emacs 27 and earlier. What we have is, for example: | Emacs 21-27 | Emacs 28 with (setq m-f-s-f t) | Emacs 28 with (setq m-f-s-f nil) A | MB1 on F1 | MB1 on F2 | MB1 on F1 B | MB1+2 on F2 | MB1+2 on F2 | MB1 on F1, MB2 on F2 [1] A: type C-x C-f on frame F1, switch to frame F2 B: type C-x C-f on frame F1, switch to frame F2, type M-: [1] There is also a severe regression in this case. Type C-x C-f on frame F1, switch to frame F2, type M-:. "Find file" is still visible in the miniwindow on frame F1; switch to frame F1. Experiment 1: Type the name of a file and RET. You'll get the error message "End of file during parsing", and MB2 on frame F2 will be left. MB1 is now unuseable, and impossible to leave, it will stay on F1 whatever you do. Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" will stay in MB1 on frame F1. However you cannot use it anymore, the keymap of MB1 is now minibuffer-inactive-mode-map. And like in experiment 1, you cannot leave it. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-26 15:44 ` martin rudalics 2020-11-26 20:32 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 7:33 ` Gregory Heytings via Emacs development discussions. 2020-11-27 9:34 ` martin rudalics 2020-11-28 10:45 ` Alan Mackenzie 2020-11-27 10:13 ` Alan Mackenzie 2 siblings, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 7:33 UTC (permalink / raw) To: martin rudalics Cc: Alan Mackenzie, Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Eli Zaretskii [Apparently this email I sent a few hours ago did not reach the mailing list. I apologize if you receive it twice.] >> I wouldn't write it is "chaotic". The behavior you consider "chaotic" >> is well-defined, and has been there since Emacs 21 at least: the >> minibuffer moves from frame F1 to frame F2 if and only if the >> minibuffer is active on frame F1 and a recursive minibuffer is entered >> on frame F2. There are other possible behaviors of course, but IMO the >> current one is a reasonable one. > > The basic behavioral change I see is with 'enable-recursive-minibuffers' > non-nil and two frames: When I type C-h f setq in the first frame and > C-h f cons in the second frame, hit RET, reselect the minibuffer window > and hit RET again, with Emacs 27 a help window pops up in the first > frame while Emacs 28 reuses the help window of the second frame. In > both cases the second RET goes to the second frame and both behaviors > seem reasonable to me. > > If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, > the behavior does not entirely match that of Emacs 27 because the second > RET must be typed in the first frame. So if some application relies on > the exact replication of the behavior of Emacs 27, we have a regression. > Note that this patch and discussion started with the following observation (on Oct 13): > (i) Have two frames open displaying buffers. > (ii) On frame F1 do C-x b. This leaves a minibuffer open there. > (iii) Move to F2. > (iv) Do C-x 8 RET <enter some character>. > F1's minibuffer is now on F2. This is bad. It is indeed not possible to replicate the behavior of Emacs 27 and earlier. What we have is, for example: | Emacs 21-27 | Emacs 28 with (setq m-f-s-f t) | Emacs 28 with (setq m-f-s-f nil) A | MB1 on F1 | MB1 on F2 | MB1 on F1 B | MB1+2 on F2 | MB1+2 on F2 | MB1 on F1, MB2 on F2 [1] A: type C-x C-f on frame F1, switch to frame F2 B: type C-x C-f on frame F1, switch to frame F2, type M-: [1] There is also a severe regression in this case. Type C-x C-f on frame F1, switch to frame F2, type M-:. "Find file" is still visible in the miniwindow on frame F1; switch to frame F1. Experiment 1: Type the name of a file and RET. You'll get the error message "End of file during parsing", and MB2 on frame F2 will be left. MB1 is now unuseable, and impossible to leave, it will stay on F1 whatever you do. Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" will stay in MB1 on frame F1. However you cannot use it anymore, the keymap of MB1 is now minibuffer-inactive-mode-map. And like in experiment 1, you cannot leave it. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 7:33 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 9:34 ` martin rudalics 2020-11-27 10:06 ` Gregory Heytings via Emacs development discussions. 2020-11-28 10:45 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 9:34 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > Note that this patch and discussion started with the following observation (on Oct 13): > >> (i) Have two frames open displaying buffers. >> (ii) On frame F1 do C-x b. This leaves a minibuffer open there. >> (iii) Move to F2. >> (iv) Do C-x 8 RET <enter some character>. >> F1's minibuffer is now on F2. This is bad. But I see the same with Emacs 27. After moving to F2, the minibuffer of F1 appears there. It's just that when I now confirm the prompt to switch to *Messages*, that buffer appears in F2 with Emacs 28 whereas with Emacs 27 it appeared in F1 (with 'minibuffer-follows-selected-frame' at its default t). > It is indeed not possible to replicate the behavior of Emacs 27 and earlier. What we have is, for example: > > | Emacs 21-27 | Emacs 28 with (setq m-f-s-f t) | Emacs 28 with (setq m-f-s-f nil) > A | MB1 on F1 | MB1 on F2 | MB1 on F1 > B | MB1+2 on F2 | MB1+2 on F2 | MB1 on F1, MB2 on F2 [1] > > A: type C-x C-f on frame F1, switch to frame F2 > B: type C-x C-f on frame F1, switch to frame F2, type M-: Right. Needs 'enable-recursive-minibuffers' non-nil to replicate. > [1] There is also a severe regression in this case. Type C-x C-f on > frame F1, switch to frame F2, type M-:. "Find file" is still visible > in the miniwindow on frame F1; switch to frame F1. > > Experiment 1: Type the name of a file and RET. You'll get the error > message "End of file during parsing", and MB2 on frame F2 will be > left. MB1 is now unuseable, and impossible to leave, it will stay on > F1 whatever you do. 'keyboard-escape-quit' gets me out. While this should be the last resort only, it happened to me occasionally before Alan's changes. Yet, your scenario must be fixed. > Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" > will stay in MB1 on frame F1. However you cannot use it anymore, the > keymap of MB1 is now minibuffer-inactive-mode-map. And like in > experiment 1, you cannot leave it. Bad indeed. Must be fixed too. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 9:34 ` martin rudalics @ 2020-11-27 10:06 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:36 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:06 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> Note that this patch and discussion started with the following >> observation (on Oct 13): >> >>> (i) Have two frames open displaying buffers. >>> (ii) On frame F1 do C-x b. This leaves a minibuffer open there. >>> (iii) Move to F2. >>> (iv) Do C-x 8 RET <enter some character>. >>> F1's minibuffer is now on F2. This is bad. > > But I see the same with Emacs 27. After moving to F2, the minibuffer of > F1 appears there. > Are you sure? You see the minibuffer moving from frame F1 to frame F2, without doing anything? With Emacs 27? I have Emacs 27.1, and do not see this, neither with graphical nor with terminal Emacs. >> It is indeed not possible to replicate the behavior of Emacs 27 and >> earlier. What we have is, for example: >> >> | Emacs 21-27 | Emacs 28 with (setq m-f-s-f t) | Emacs 28 with (setq m-f-s-f nil) >> A | MB1 on F1 | MB1 on F2 | MB1 on F1 >> B | MB1+2 on F2 | MB1+2 on F2 | MB1 on F1, MB2 on F2 [1] >> >> A: type C-x C-f on frame F1, switch to frame F2 >> B: type C-x C-f on frame F1, switch to frame F2, type M-: > > Right. Needs 'enable-recursive-minibuffers' non-nil to replicate. > Or using a command which temporarily sets enable-recursive-minibuffers, like C-x 8 RET or C-h f or C-h w or... But without enable-recursive-minibuffers this patch and discussion would not take place, you simply get a "Command attempted to use minibuffer while in minibuffer". In any case, it is not possible to replicate the behavior of Emacs 21-27, which is IMO not a good thing, especially for something as central to Emacs as the minibuffer. >> [1] There is also a severe regression in this case. Type C-x C-f on >> frame F1, switch to frame F2, type M-:. "Find file" is still visible >> in the miniwindow on frame F1; switch to frame F1. >> >> Experiment 1: Type the name of a file and RET. You'll get the error >> message "End of file during parsing", and MB2 on frame F2 will be left. >> MB1 is now unuseable, and impossible to leave, it will stay on F1 >> whatever you do. > > 'keyboard-escape-quit' gets me out. While this should be the last > resort only, it happened to me occasionally before Alan's changes. Yet, > your scenario must be fixed. > Indeed, 'abort-recursive-edit' also works. But as you write these commands should be used in last resort, especially as they are not known to newcomers. >> Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" >> will stay in MB1 on frame F1. However you cannot use it anymore, the >> keymap of MB1 is now minibuffer-inactive-mode-map. And like in >> experiment 1, you cannot leave it. > > Bad indeed. Must be fixed too. > IMO "fixing" in this case should be going back to the earlier state (the Emacs 21-27 behavior), and first trying to _precisely_ define the desired new behavior. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:06 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:36 ` martin rudalics 2020-11-27 10:43 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 10:36 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >>>> (i) Have two frames open displaying buffers. >>>> (ii) On frame F1 do C-x b. This leaves a minibuffer open there. >>>> (iii) Move to F2. >>>> (iv) Do C-x 8 RET <enter some character>. >>>> F1's minibuffer is now on F2. This is bad. >> >> But I see the same with Emacs 27. After moving to F2, the minibuffer of F1 appears there. >> > > Are you sure? You see the minibuffer moving from frame F1 to frame > F2, without doing anything? With Emacs 27? I have Emacs 27.1, and do > not see this, neither with graphical nor with terminal Emacs. Why "moving"? The active minibuffer is already on F2 because after (iii) I am on F2 and answered the C-x 8 prompt there. With Emacs 27 built two days ago. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:36 ` martin rudalics @ 2020-11-27 10:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 15:41 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:43 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >>>>> (i) Have two frames open displaying buffers. >>>>> (ii) On frame F1 do C-x b. This leaves a minibuffer open there. >>>>> (iii) Move to F2. >>>>> (iv) Do C-x 8 RET <enter some character>. >>>>> F1's minibuffer is now on F2. This is bad. >>> >>> But I see the same with Emacs 27. After moving to F2, the minibuffer >>> of F1 appears there. >> >> Are you sure? You see the minibuffer moving from frame F1 to frame F2, >> without doing anything? With Emacs 27? I have Emacs 27.1, and do not >> see this, neither with graphical nor with terminal Emacs. > > Why "moving"? The active minibuffer is already on F2 because after > (iii) I am on F2 and answered the C-x 8 prompt there. With Emacs 27 > built two days ago. > Yes, and this is precisely the "bad" behavior Alan would like to fix. With Emacs 28 (-Q), the active minibuffer is moved from frame F1 to frame F2 at step (iii), _before_ you answer the C-x 8 RET. That is, the active minibuffer is moved from frame F1 to frame F2 whenever you switch from frame F1 to frame F2. With Emacs 21-27, this happens only at step (iv), _after_ you hit C-x 8 RET. That is, the active minibuffer is moved from frame F1 to frame F2 only when you activate a new minibuffer on frame F2. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:43 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 15:41 ` martin rudalics 2020-11-27 16:19 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 15:41 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > With Emacs 28 (-Q), the active minibuffer is moved from frame F1 to > frame F2 at step (iii), _before_ you answer the C-x 8 RET. That is, > the active minibuffer is moved from frame F1 to frame F2 whenever you > switch from frame F1 to frame F2. > > With Emacs 21-27, this happens only at step (iv), _after_ you hit C-x > 8 RET. That is, the active minibuffer is moved from frame F1 to frame > F2 only when you activate a new minibuffer on frame F2. I understand you now. With Emacs 27 the minibuffer window moves lazily - that is, only when it's needed on some other frame - with Emacs 28 it moves eagerly. That doesn't look like a change for the better. Alan, couldn't we try to do that by giving 'minibuffer-follows-selected-frame' a third value like 'on-demand and move only when the user really wants to interact with the minibuffer on another frame? martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 15:41 ` martin rudalics @ 2020-11-27 16:19 ` Gregory Heytings via Emacs development discussions. 2020-11-27 17:14 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 16:19 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> With Emacs 28 (-Q), the active minibuffer is moved from frame F1 to >> frame F2 at step (iii), _before_ you answer the C-x 8 RET. That is, the >> active minibuffer is moved from frame F1 to frame F2 whenever you >> switch from frame F1 to frame F2. >> >> With Emacs 21-27, this happens only at step (iv), _after_ you hit C-x 8 >> RET. That is, the active minibuffer is moved from frame F1 to frame F2 >> only when you activate a new minibuffer on frame F2. > > I understand you now. With Emacs 27 the minibuffer window moves lazily > - that is, only when it's needed on some other frame - with Emacs 28 it > moves eagerly. That doesn't look like a change for the better. Alan, > couldn't we try to do that by giving 'minibuffer-follows-selected-frame' > a third value like 'on-demand and move only when the user really wants > to interact with the minibuffer on another frame? > Yes, AFAIU doing this would amount to make it possible to restore the Emacs 21-27 behavior, which would be a good thing. BTW, this (to have three possible behaviors) is what I suggested as early as Oct 14. This suggestion was totally ignored: 1. the Emacs 21-27 behavior, with which all recursive minibuffers are moved from one frame to another when one or more minibuffers are active in one frame, and a new recursive activation happens in another frame: that's what we lost 2. move all recursive minibuffers from one frame to the other when switching to another frame: that's what we now have with minibuffer-follows-selected-frame set to t, except that it doesn't work as it should (the result of the commands do not take place in the frame in which they were initiated) 3. tie each one of the recursive minibuffers to the frame in which it was activated: that's what we now have with minibuffer-follows-selected-frame set to nil, except that it doesn't work as it should (interacting with the minibuffers that are not the most recently entered one break things badly) My feeling (I did not look at the code) is that too many things were changed at once. Perhaps this should be done / have been done in two steps, first implement the additional behavior 2, then behavior 3. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 16:19 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 17:14 ` martin rudalics 2020-11-27 18:01 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 17:14 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii > My feeling (I did not look at the code) is that too many things were > changed at once. Perhaps this should be done / have been done in two > steps, first implement the additional behavior 2, then behavior 3. There are two things we could consider with 'minibuffer-follows-selected-frame' non-nil: - Optionally, don't move the minibuffer window too eagerly. Moving the prompt to my separate *Info* frame that I just want to consult for the interaction I'm about to perform might look gratuitous. - Optionally, tie the frame where a minibuffer interaction was initiated to that minibuffer and when the ensuing action is performed, make that frame the selected one. But I think the main task for the moment is to fix the 'minibuffer-follows-selected-frame' nil behavior. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 17:14 ` martin rudalics @ 2020-11-27 18:01 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:35 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 18:01 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> My feeling (I did not look at the code) is that too many things were >> changed at once. Perhaps this should be done / have been done in two >> steps, first implement the additional behavior 2, then behavior 3. > > There are two things we could consider with > 'minibuffer-follows-selected-frame' non-nil: > > - Optionally, don't move the minibuffer window too eagerly. Moving the > prompt to my separate *Info* frame that I just want to consult for the > interaction I'm about to perform might look gratuitous. > It does, definitely. What you describe here is the behavior of Emacs 21-27, IIUC. > > - Optionally, tie the frame where a minibuffer interaction was initiated > to that minibuffer and when the ensuing action is performed, make that > frame the selected one. > Isn't this what minibuffer-follows-selected-frame t is supposed to do? (Except that the frame is not automatically selected.) > > But I think the main task for the moment is to fix the > 'minibuffer-follows-selected-frame' nil behavior. > The minibuffer-follows-selected-frame t behavior is also broken, alas, at least it doesn't do what the NEWS item says it should do. My feeling is also that "minibuffer-follows-selected-frame" is not a good generic name for all these behaviors. Perhaps one should have something like "recursive-minibuffer-behavior" with several possible values: move-to-selected-frame-on-activation always-on-selected-frame always-on-selected-frame-and-raise-activation-frame tie-to-activation-frame (This is just a draft. Perhaps these options should in fact be split in two separate options.) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 18:01 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 18:35 ` martin rudalics 2020-11-27 20:05 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-27 18:35 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >> - Optionally, tie the frame where a minibuffer interaction was initiated to that minibuffer and when the ensuing action is performed, make that frame the selected one. >> > > Isn't this what minibuffer-follows-selected-frame t is supposed to do? (Except that the frame is not automatically selected.) With 'minibuffer-follows-selected-frame' non-nil, the remainder of 'switch-to-buffer' simply runs on the frame where the prompt is answered which is not necessarily the frame where C-x b was issued initially. > My feeling is also that "minibuffer-follows-selected-frame" is not a > good generic name for all these behaviors. We have worse ones. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 18:35 ` martin rudalics @ 2020-11-27 20:05 ` Gregory Heytings via Emacs development discussions. 0 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 20:05 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Alan Mackenzie, Eli Zaretskii >>> - Optionally, tie the frame where a minibuffer interaction was >>> initiated to that minibuffer and when the ensuing action is performed, >>> make that frame the selected one. >> >> Isn't this what minibuffer-follows-selected-frame t is supposed to do? >> (Except that the frame is not automatically selected.) > > With 'minibuffer-follows-selected-frame' non-nil, the remainder of > 'switch-to-buffer' simply runs on the frame where the prompt is answered > which is not necessarily the frame where C-x b was issued initially. > Yes, and this is not what it is supposed to do. The NEWS item says "the effect of what you type in the minibuffer happens in the frame where the minibuffer was first activated, even if it moved to another frame." ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 7:33 ` Gregory Heytings via Emacs development discussions. 2020-11-27 9:34 ` martin rudalics @ 2020-11-28 10:45 ` Alan Mackenzie 2020-11-28 15:35 ` Alan Mackenzie 2020-11-28 17:02 ` Stefan Monnier 1 sibling, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-28 10:45 UTC (permalink / raw) To: Gregory Heytings, martin rudalics Cc: enometh, Eli Zaretskii, Stefan Monnier, Andrii Kolomoiets, emacs-devel Hello, Gregory and Martin. On Fri, Nov 27, 2020 at 07:33:04 +0000, Gregory Heytings wrote: [ .... ] > | Emacs 21-27 | Emacs 28 with (setq m-f-s-f t) | Emacs 28 with (setq m-f-s-f nil) > A | MB1 on F1 | MB1 on F2 | MB1 on F1 > B | MB1+2 on F2 | MB1+2 on F2 | MB1 on F1, MB2 on F2 [1] > A: type C-x C-f on frame F1, switch to frame F2 > B: type C-x C-f on frame F1, switch to frame F2, type M-: > [1] There is also a severe regression in this case. Type C-x C-f on frame > F1, switch to frame F2, type M-:. "Find file" is still visible in the > miniwindow on frame F1; switch to frame F1. > Experiment 1: Type the name of a file and RET. You'll get the error > message "End of file during parsing", and MB2 on frame F2 will be left. > MB1 is now unuseable, and impossible to leave, it will stay on F1 whatever > you do. > Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" > will stay in MB1 on frame F1. However you cannot use it anymore, the > keymap of MB1 is now minibuffer-inactive-mode-map. And like in experiment > 1, you cannot leave it. The abstract cause of this situation would appear to be using F1's minibuffer while a more deeply nested minibuffer is still active. It is a violation of the "recursive" nature of these buffers. I think a solution would be to put F1's minibuffer into minibuffer-inactive-mode until the recursive MB in F2 has terminated. What do you think of this? -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 10:45 ` Alan Mackenzie @ 2020-11-28 15:35 ` Alan Mackenzie 2020-11-28 17:02 ` Stefan Monnier 1 sibling, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-28 15:35 UTC (permalink / raw) To: Gregory Heytings, martin rudalics Cc: enometh, emacs-devel, Eli Zaretskii, Andrii Kolomoiets, Stefan Monnier > Hello, Gregory and Martin. On Sat, Nov 28, 2020 at 10:45:34 +0000, Alan Mackenzie wrote: > On Fri, Nov 27, 2020 at 07:33:04 +0000, Gregory Heytings wrote: > [ .... ] > > | Emacs 21-27 | Emacs 28 with (setq m-f-s-f t) | Emacs 28 with (setq m-f-s-f nil) > > A | MB1 on F1 | MB1 on F2 | MB1 on F1 > > B | MB1+2 on F2 | MB1+2 on F2 | MB1 on F1, MB2 on F2 [1] > > A: type C-x C-f on frame F1, switch to frame F2 > > B: type C-x C-f on frame F1, switch to frame F2, type M-: > > [1] There is also a severe regression in this case. Type C-x C-f on frame > > F1, switch to frame F2, type M-:. "Find file" is still visible in the > > miniwindow on frame F1; switch to frame F1. > > Experiment 1: Type the name of a file and RET. You'll get the error > > message "End of file during parsing", and MB2 on frame F2 will be left. > > MB1 is now unuseable, and impossible to leave, it will stay on F1 whatever > > you do. > > Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" > > will stay in MB1 on frame F1. However you cannot use it anymore, the > > keymap of MB1 is now minibuffer-inactive-mode-map. And like in experiment > > 1, you cannot leave it. > The abstract cause of this situation would appear to be using F1's > minibuffer while a more deeply nested minibuffer is still active. It is > a violation of the "recursive" nature of these buffers. > I think a solution would be to put F1's minibuffer into > minibuffer-inactive-mode until the recursive MB in F2 has terminated. > What do you think of this? Here's a trial implementation, which doesn't quite put F1's MB into minibuffer-inactive-mode, but sets its local keymap to the mode's keymap. diff --git a/src/minibuf.c b/src/minibuf.c index fc3fd92a88..58e72bc4a4 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,6 +63,8 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f static bool minibuf_follows_frame (void) @@ -90,7 +92,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && !minibuf_follows_frame ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -411,6 +413,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -532,7 +535,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (BVAR (XBUFFER (nth_minibuffer (minibuf_level - 1)), + keymap), + minibuf_save_list))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -727,8 +732,26 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); + /* Prevent the user manipulating outer levels of recursive minibuffers. */ + if (minibuf_level > 1) + { + Lisp_Object inactive_map; + if ((inactive_map = + find_symbol_value (intern ("minibuffer-inactive-mode-map"))) + != Qunbound) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level - 1)), + inactive_map); + } + recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the frame + back to the calling frame. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + do_switch_frame (calling_frame, 1, 0, Qnil); + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -790,6 +813,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -852,6 +883,7 @@ read_minibuf_unwind (void) Lisp_Object old_deactivate_mark; Lisp_Object window; Lisp_Object future_mini_window; + Lisp_Object map; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -888,6 +920,8 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + map = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -901,6 +935,10 @@ read_minibuf_unwind (void) unbind_to (count, Qnil); } + /* Restore the keymap of any outer level recursive minibuffer. */ + if (minibuf_level > 0) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level)), map); + /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ if (minibuf_level == 0 -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 10:45 ` Alan Mackenzie 2020-11-28 15:35 ` Alan Mackenzie @ 2020-11-28 17:02 ` Stefan Monnier 2020-11-28 20:59 ` Gregory Heytings via Emacs development discussions. 2020-11-29 18:15 ` Alan Mackenzie 1 sibling, 2 replies; 252+ messages in thread From: Stefan Monnier @ 2020-11-28 17:02 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Gregory Heytings, Eli Zaretskii >> A: type C-x C-f on frame F1, switch to frame F2 >> B: type C-x C-f on frame F1, switch to frame F2, type M-: > >> [1] There is also a severe regression in this case. Type C-x C-f on frame >> F1, switch to frame F2, type M-:. "Find file" is still visible in the >> miniwindow on frame F1; switch to frame F1. > >> Experiment 1: Type the name of a file and RET. You'll get the error >> message "End of file during parsing", and MB2 on frame F2 will be left. >> MB1 is now unuseable, and impossible to leave, it will stay on F1 whatever >> you do. > >> Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" >> will stay in MB1 on frame F1. However you cannot use it anymore, the >> keymap of MB1 is now minibuffer-inactive-mode-map. And like in experiment >> 1, you cannot leave it. > > The abstract cause of this situation would appear to be using F1's > minibuffer while a more deeply nested minibuffer is still active. More specifically, it's the act of leaving MB1 when there's a deeper MB2 active: the code for leaving a minibuffer (e.g. `exit-minibuffer` or `abort-recursive-edit`) doesn't actually pay attention to which minibuffer is currently being used: while it's run from MB1 it actually exits MB2. I'm not completely sure why we end up with a broken state, but I guess it's because some of the code that "deactivates" the minibuffer upon exit in run in the minibuffer that the users thought they were about to exit rather than in the one that is actually exited. I expect that this is the core origin of the problem. One way to address it might be to make every minibuffer use a different exit tag (instead of the constant `exit` symbol), so that the `throw` will not be caught by some unrelated `catch`. Additionally, we may want to tweak `exit-minibuffer` and `abort-recursive-edit` so that the user is warned/prompted before "silently" canceling that other (deeper) minibuffer. [ Another way to attack the problem would be to arrange it so that every minibuffer runs in its own thread, so you can exit one without affecting the other. I think it might be an interesting direction, but it's probably not trivial. In any case, cmpletely out of scope of the present problem. ] Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 17:02 ` Stefan Monnier @ 2020-11-28 20:59 ` Gregory Heytings via Emacs development discussions. 2020-11-28 21:10 ` Stefan Monnier 2020-11-29 18:15 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-28 20:59 UTC (permalink / raw) To: Stefan Monnier Cc: Alan Mackenzie, Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Eli Zaretskii > > More specifically, it's the act of leaving MB1 when there's a deeper MB2 > active: the code for leaving a minibuffer (e.g. `exit-minibuffer` or > `abort-recursive-edit`) doesn't actually pay attention to which > minibuffer is currently being used: while it's run from MB1 it actually > exits MB2. I'm not completely sure why we end up with a broken state, > but I guess it's because some of the code that "deactivates" the > minibuffer upon exit in run in the minibuffer that the users thought > they were about to exit rather than in the one that is actually exited. > Isn't the main reason for this that it has never been possible to interact with a MBn when a MBm, with m > n, was active? IOW, that exit-minibuffer was so far only meant to be used for the most recent minibuffer? BTW, I played a bit with this, and it seems that recursive minibuffers on more than two frames do not work correctly, since Emacs 21 at least. Here is a recipe you can try with Emacs 21-28: emacs -Q M-: (setq enable-recursive-minibuffers t) RET C-x C-f ; create MB1 on frame F1 C-x 5 2 C-x C-f ; move MB1 to frame F2 and create MB2 C-x 5 2 C-x C-f .emacs RET ; create MB3 on frame F3, open file in frame F3 C-x o .emacs RET ; activate MB2 on frame F3, open file in frame F2 At that point MB2 is moved to frame F2, and is now unuseable (in minibuffer-inactive-mode). You can either abort-recursive-edit (which works), or exit-recursive-edit (which, strangely, opens Dired on frame F1). You can add more frames to the recipe, only the two last ones will work as expected. What should have happened is that, at the end of the recipe, MB2 should have been exited and MB1 should be activated on frame F3, so that C-x o .emacs RET would open the file in frame F1. > > One way to address it might be to make every minibuffer use a different > exit tag (instead of the constant `exit` symbol), so that the `throw` > will not be caught by some unrelated `catch`. Additionally, we may want > to tweak `exit-minibuffer` and `abort-recursive-edit` so that the user > is warned/prompted before "silently" canceling that other (deeper) > minibuffer. > Is such an added complexity really worth the price? IMO the old behavior (with the bug above fixed) and a new behavior "minibuffer-follows-selected-frame" which would give a similar experience to that of using a minibuffer-only window, should be enough. What is the added value of tying minibuffers to the frames in which they were created, compared to the price of implementing that feature? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 20:59 ` Gregory Heytings via Emacs development discussions. @ 2020-11-28 21:10 ` Stefan Monnier 2020-11-28 22:01 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-11-28 21:10 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Alan Mackenzie, Eli Zaretskii >> More specifically, it's the act of leaving MB1 when there's a deeper MB2 >> active: the code for leaving a minibuffer (e.g. `exit-minibuffer` or >> `abort-recursive-edit`) doesn't actually pay attention to which minibuffer >> is currently being used: while it's run from MB1 it actually exits MB2. >> I'm not completely sure why we end up with a broken state, but I guess >> it's because some of the code that "deactivates" the minibuffer upon exit >> in run in the minibuffer that the users thought they were about to exit >> rather than in the one that is actually exited. > Isn't the main reason for this that it has never been possible to interact > with a MBn when a MBm, with m > n, was active? Well, it's at best an indirect cause of the bug, but yes, it's the reason why this bug wasn't visible until now. >> One way to address it might be to make every minibuffer use a different >> exit tag (instead of the constant `exit` symbol), so that the `throw` will >> not be caught by some unrelated `catch`. Additionally, we may want to >> tweak `exit-minibuffer` and `abort-recursive-edit` so that the user is >> warned/prompted before "silently" canceling that other >> (deeper) minibuffer. > Is such an added complexity really worth the price? I don't see much complexity here. The main issue is not complexity but the fact that it's a change, so there can be backward compatibility issues (most likely the catcher will have catch both the old `exit` tag as well as the new tag, in case some old code throws the `exit` tag rather than going through `exit-minibuffer`). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 21:10 ` Stefan Monnier @ 2020-11-28 22:01 ` Gregory Heytings via Emacs development discussions. 2020-11-28 22:10 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-28 22:01 UTC (permalink / raw) To: Stefan Monnier Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Alan Mackenzie, Eli Zaretskii >> Isn't the main reason for this that it has never been possible to >> interact with a MBn when a MBm, with m > n, was active? > > Well, it's at best an indirect cause of the bug, but yes, it's the > reason why this bug wasn't visible until now. > Put another way, is it not problematic to interact with and/or terminate a recursive edit while one or more higher level recursive edits are underway? Do you think the recipe with more than two frames I just sent demonstrates a bug? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 22:01 ` Gregory Heytings via Emacs development discussions. @ 2020-11-28 22:10 ` Stefan Monnier 2020-11-28 22:38 ` Gregory Heytings via Emacs development discussions. 0 siblings, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-11-28 22:10 UTC (permalink / raw) To: Gregory Heytings Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Alan Mackenzie, Eli Zaretskii >>> Isn't the main reason for this that it has never been possible to >>> interact with a MBn when a MBm, with m > n, was active? >> Well, it's at best an indirect cause of the bug, but yes, it's the reason >> why this bug wasn't visible until now. > Put another way, is it not problematic to interact with and/or terminate > a recursive edit while one or more higher level recursive edits > are underway? I think "interact with" should be OK (I can see situations where you'd use two minibuffers, where you copy text from one to the other), but since the invocations have to obey the nesting, exiting from the non-deepest minibuffer is indeed a problem. We could start by signaling an error when trying to exit the non-deepest minibuffer? > Do you think the recipe with more than two frames I just sent > demonstrates a bug? If you're referring to the recipe to which I responded, then yes. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 22:10 ` Stefan Monnier @ 2020-11-28 22:38 ` Gregory Heytings via Emacs development discussions. 0 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-28 22:38 UTC (permalink / raw) To: Stefan Monnier Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Alan Mackenzie, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 1501 bytes --] >>>> Isn't the main reason for this that it has never been possible to >>>> interact with a MBn when a MBm, with m > n, was active? >>> >>> Well, it's at best an indirect cause of the bug, but yes, it's the >>> reason why this bug wasn't visible until now. >> >> Put another way, is it not problematic to interact with and/or >> terminate a recursive edit while one or more higher level recursive >> edits are underway? > > I think "interact with" should be OK (I can see situations where you'd > use two minibuffers, where you copy text from one to the other), but > since the invocations have to obey the nesting, exiting from the > non-deepest minibuffer is indeed a problem. > Okay, so I wasn't completely wrong when I said that tying minibuffers to the frames in which they are created is complex ;-) If it becomes possible to interact with lower level minibuffers, typing ".emacs RET" in a lower level minibuffer that was previously created with "C-x C-f" would open the file. What would happen with the minibuffer at that point? What should happen if it is entered again for example with C-x o? Will it become a Schrödinger minibuffer that is neither alive nor dead? >> Do you think the recipe with more than two frames I just sent >> demonstrates a bug? > > If you're referring to the recipe to which I responded, then yes. > I'm not sure what you mean by "the recipe to which I responded". I meant the recipe I sent about an hour ago. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-28 17:02 ` Stefan Monnier 2020-11-28 20:59 ` Gregory Heytings via Emacs development discussions. @ 2020-11-29 18:15 ` Alan Mackenzie 1 sibling, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-29 18:15 UTC (permalink / raw) To: Stefan Monnier, Gregory Heytins, martin rudalics Cc: enometh, Eli Zaretskii, Andrii Kolomoiets, emacs-devel Hello, Stefan, Gregory, and Martin. On Sat, Nov 28, 2020 at 12:02:34 -0500, Stefan Monnier wrote: > >> A: type C-x C-f on frame F1, switch to frame F2 > >> B: type C-x C-f on frame F1, switch to frame F2, type M-: > >> [1] There is also a severe regression in this case. Type C-x C-f on frame > >> F1, switch to frame F2, type M-:. "Find file" is still visible in the > >> miniwindow on frame F1; switch to frame F1. > >> Experiment 1: Type the name of a file and RET. You'll get the error > >> message "End of file during parsing", and MB2 on frame F2 will be left. > >> MB1 is now unuseable, and impossible to leave, it will stay on F1 whatever > >> you do. > >> Experiment 2: Type C-g. MB2 on frame F2 will be left, and "Find file" > >> will stay in MB1 on frame F1. However you cannot use it anymore, the > >> keymap of MB1 is now minibuffer-inactive-mode-map. And like in experiment > >> 1, you cannot leave it. > > The abstract cause of this situation would appear to be using F1's > > minibuffer while a more deeply nested minibuffer is still active. > More specifically, it's the act of leaving MB1 when there's a deeper MB2 > active: the code for leaving a minibuffer (e.g. `exit-minibuffer` or > `abort-recursive-edit`) doesn't actually pay attention to which > minibuffer is currently being used: while it's run from MB1 it actually > exits MB2. I'm not completely sure why we end up with a broken state, > but I guess it's because some of the code that "deactivates" the > minibuffer upon exit in run in the minibuffer that the users thought they > were about to exit rather than in the one that is actually exited. > I expect that this is the core origin of the problem. > One way to address it might be to make every minibuffer use a different exit > tag (instead of the constant `exit` symbol), so that the `throw` will > not be caught by some unrelated `catch`. Additionally, we may want to > tweak `exit-minibuffer` and `abort-recursive-edit` so that the user is > warned/prompted before "silently" canceling that other (deeper) minibuffer. > [ Another way to attack the problem would be to arrange it so that every > minibuffer runs in its own thread, so you can exit one without > affecting the other. I think it might be an interesting direction, > but it's probably not trivial. In any case, cmpletely out of scope > of the present problem. ] I haven't paid too much attention to the above. But I do have a patch which addresses the problem by removing the minibuffer key map from outer level minibuffers, and giving them minibuffer-inactive-mode-map instead. Gregory, the patch is intended to restore at least part of the former behaviour. Set minibuffer-follows-selected-frame to a non-nil, not-t value (such as 'hybrid), and minibuffers will move onto a selected frame when the user invokes a minibuffer on that frame. I think that's what you wanted. The patch might also fix your (Gregory's) problem with three frames and three minibuffers. I'm not sure. Also, the patch is incomplete - the customsation entry in cus-start.el needs amending, as do the Emacs manual and (probably) the NEWS entry. Any testing you could do would be most appreciated. Thanks! diff --git a/src/minibuf.c b/src/minibuf.c index fc3fd92a88..9db95c8381 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,9 +63,30 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f +/* Return TRUE when a frame switch causes a minibuffer on the old + frame to move onto the new one. */ static bool minibuf_follows_frame (void) +{ + return EQ (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame), + Qt); +} + +/* Return TRUE when a minibuffer always remains on the frame where it + was first invoked. */ +static bool +minibuf_stays_put (void) +{ + return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + +/* Return TRUE when opening a (recursive) minibuffer causes + minibuffers on other frames to move to the selected frame. */ +static bool +minibuf_moves_frame_when_opened (void) { return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } @@ -90,7 +111,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && minibuf_stays_put ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -105,26 +126,38 @@ choose_minibuf_frame (void) } } - if (minibuf_follows_frame ()) + if (minibuf_moves_frame_when_opened ()) /* Make sure no other frame has a minibuffer as its selected window, because the text would not be displayed in it, and that would be confusing. Only allow the selected frame to do this, and that only if the minibuffer is active. */ { Lisp_Object tail, frame; + Lisp_Object buffer = nth_minibuffer (minibuf_level - 1); + struct frame *sf = XFRAME (selected_frame); FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); + if (!EQ (frame, selected_frame) + && minibuf_level > 1) + { + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame))))) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents, + buffer)) + { + set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + set_window_buffer (XFRAME (frame)->minibuffer_window, + get_minibuffer (0), 0, 0); + } + } } } -/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it - from its current frame to the selected frame. This function is - intended to be called from `do_switch_frame' in frame.c. */ +/* If `minibuffer_follows_selected_frame' is t and we have a + minibuffer, move it from its current frame to the selected frame. + This function is intended to be called from `do_switch_frame' in + frame.c. */ void move_minibuffer_onto_frame (void) { if (!minibuf_level) @@ -411,6 +444,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -532,7 +566,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (BVAR (XBUFFER (nth_minibuffer (minibuf_level - 1)), + keymap), + minibuf_save_list))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -727,8 +763,33 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); + /* Prevent the user manipulating outer levels of recursive minibuffers. */ + if (minibuf_level > 1) + { + Lisp_Object inactive_map; + if ((inactive_map = + find_symbol_value (intern ("minibuffer-inactive-mode-map"))) + != Qunbound) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level - 1)), + inactive_map); + } + recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the + frame back to the calling frame. Also switch the current window + away from the expired minibuffer window. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + { + Fset_frame_selected_window (selected_frame, + Fprevious_window (minibuf_window, + Qnil, Qnil), + Qnil); + do_switch_frame (calling_frame, 1, 0, Qnil); + } + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -790,6 +851,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -852,6 +921,7 @@ read_minibuf_unwind (void) Lisp_Object old_deactivate_mark; Lisp_Object window; Lisp_Object future_mini_window; + Lisp_Object map; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -888,6 +958,8 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + map = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -901,6 +973,10 @@ read_minibuf_unwind (void) unbind_to (count, Qnil); } + /* Restore the keymap of any outer level recursive minibuffer. */ + if (minibuf_level > 0) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level)), map); + /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ if (minibuf_level == 0 @@ -2035,13 +2111,15 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; - DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, - doc: /* Non-nil means the active minibuffer always displays on the selected frame. + DEFVAR_LISP ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* t means the active minibuffer always displays on the selected frame. Nil means that a minibuffer will appear only in the frame which created it. +Any other value means the minibuffer will move onto another frame, but +only when the user starts using a minniffer. Any buffer local or dynamic binding of this variable is ignored. Only the default top level value is used. */); - minibuffer_follows_selected_frame = 1; + minibuffer_follows_selected_frame = Qt; DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-26 15:44 ` martin rudalics 2020-11-26 20:32 ` Gregory Heytings via Emacs development discussions. 2020-11-27 7:33 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 10:13 ` Alan Mackenzie 2020-11-27 10:36 ` martin rudalics 2 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 10:13 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin. On Thu, Nov 26, 2020 at 16:44:11 +0100, martin rudalics wrote: > > I wouldn't write it is "chaotic". The behavior you consider "chaotic" > > is well-defined, and has been there since Emacs 21 at least: the > > minibuffer moves from frame F1 to frame F2 if and only if the > > minibuffer is active on frame F1 and a recursive minibuffer is entered > > on frame F2. There are other possible behaviors of course, but IMO > > the current one is a reasonable one. > The basic behavioral change I see is with > 'enable-recursive-minibuffers' non-nil and two frames: When I type C-h > f setq in the first frame and C-h f cons in the second frame, hit RET, > reselect the minibuffer window and hit RET again, with Emacs 27 a help > window pops up in the first frame while Emacs 28 reuses the help window > of the second frame. In both cases the second RET goes to the second > frame and both behaviors seem reasonable to me. > If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, Do you mean "to nil", here? That variable is non-nil by default. > the behavior does not entirely match that of Emacs 27 because the second > RET must be typed in the first frame. So if some application relies on > the exact replication of the behavior of Emacs 27, we have a regression. Well the new behaviour is explicitly not wholly compatible with the old. I'm not sure that counts as a regression. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:13 ` Alan Mackenzie @ 2020-11-27 10:36 ` martin rudalics 2020-11-27 11:30 ` Alan Mackenzie ` (2 more replies) 0 siblings, 3 replies; 252+ messages in thread From: martin rudalics @ 2020-11-27 10:36 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii >> If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, > > Do you mean "to nil", here? That variable is non-nil by default. Right. I meant "to nil" here. >> the behavior does not entirely match that of Emacs 27 because the second >> RET must be typed in the first frame. So if some application relies on >> the exact replication of the behavior of Emacs 27, we have a regression. > > Well the new behaviour is explicitly not wholly compatible with the old. > I'm not sure that counts as a regression. Having a customizable variable like 'minibuffer-follows-selected-frame' whose purpose is to get back the old behavior, should also provide that old behavior as faithfully as possible IMHO. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:36 ` martin rudalics @ 2020-11-27 11:30 ` Alan Mackenzie 2020-11-27 12:29 ` Eli Zaretskii 2021-01-03 18:10 ` Alan Mackenzie 2 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 11:30 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin. On Fri, Nov 27, 2020 at 11:36:47 +0100, martin rudalics wrote: > >> If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, > > Do you mean "to nil", here? That variable is non-nil by default. > Right. I meant "to nil" here. > >> the behavior does not entirely match that of Emacs 27 because the second > >> RET must be typed in the first frame. So if some application relies on > >> the exact replication of the behavior of Emacs 27, we have a regression. > > Well the new behaviour is explicitly not wholly compatible with the old. > > I'm not sure that counts as a regression. > Having a customizable variable like 'minibuffer-follows-selected-frame' > whose purpose is to get back the old behavior, should also provide that > old behavior as faithfully as possible IMHO. That is not the purpose of the variable. The purpose is to be able to chose between mental models of a minibuffer in a frame. Eli's mental model is that the MB represents the action which should be performed next. Mine is that a MB is part of the frame it is opened in. So Eli gets to leave m-f-s-f at t, I get to set it to nil. The old behaviour was chaotic and unsystematic, and Eli and I agreed this earlier on in the thread. The recent changes were an attempt to bring the behaviour back, at least partially, to something systematic. The new variable m-f-s-f should enable a user to set the behaviour she wants. Maybe there's something missing (aside from bugs which still need fixing). > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:36 ` martin rudalics 2020-11-27 11:30 ` Alan Mackenzie @ 2020-11-27 12:29 ` Eli Zaretskii 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 15:42 ` martin rudalics 2021-01-03 18:10 ` Alan Mackenzie 2 siblings, 2 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-27 12:29 UTC (permalink / raw) To: martin rudalics; +Cc: andreyk.mad, emacs-devel, enometh, monnier, ghe, acm > Cc: Gregory Heytings <ghe@sdf.org>, Andrii Kolomoiets > <andreyk.mad@gmail.com>, emacs-devel@gnu.org, enometh@meer.net, > Stefan Monnier <monnier@iro.umontreal.ca>, Eli Zaretskii <eliz@gnu.org> > From: martin rudalics <rudalics@gmx.at> > Date: Fri, 27 Nov 2020 11:36:47 +0100 > > Having a customizable variable like 'minibuffer-follows-selected-frame' > whose purpose is to get back the old behavior, should also provide that > old behavior as faithfully as possible IMHO. The NEWS entry clearly says that the old behavior is no longer available, so getting back the old behavior is not the purpose of that variable. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 12:29 ` Eli Zaretskii @ 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 14:09 ` Stefan Monnier ` (2 more replies) 2020-11-27 15:42 ` martin rudalics 1 sibling, 3 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-11-27 13:43 UTC (permalink / raw) To: Eli Zaretskii Cc: martin rudalics, andreyk.mad, emacs-devel, enometh, monnier, acm >> Having a customizable variable like 'minibuffer-follows-selected-frame' >> whose purpose is to get back the old behavior, should also provide that >> old behavior as faithfully as possible IMHO. > > The NEWS entry clearly says that the old behavior is no longer > available, so getting back the old behavior is not the purpose of that > variable. > I hope that does not mean "end of discussion". I sent two recipes to Martin a few hours ago, which demonstrate that the behavior with that variable set to nil is broken. Again it is surprising that such a radical change was accepted without testing these cases, which are obvious cases to test. The NEWS entry says "Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer was first activated, even if it moved to another frame." This is not correct. Three recipes: emacs -Q C-x 5 2 C-x C-f C-x 5 o C-x o .emacs RET The file is opened in the frame in which you are, not in the frame in which C-x C-f was entered. Another recipe: emacs -Q C-x 5 2 C-h f setq C-x 5 o C-x o RET The *Help* buffer is displayed in the frame in which you are, not in the frame in which C-h f setq was entered. Yet another recipe, similar to the one with which this discussion started: emacs -Q C-x 5 2 C-x b C-x 5 o C-x o RET The buffer is displayed in the frame in which you are, not in the frame in which C-x b was entered. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 14:09 ` Stefan Monnier 2020-11-27 15:03 ` Eli Zaretskii 2020-11-27 22:00 ` Alan Mackenzie 2 siblings, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-11-27 14:09 UTC (permalink / raw) To: Gregory Heytings Cc: andreyk.mad, emacs-devel, martin rudalics, enometh, acm, Eli Zaretskii > emacs -Q > C-x 5 2 > C-x C-f > C-x 5 o > C-x o > .emacs RET > > The file is opened in the frame in which you are, not in the frame in which > C-x C-f was entered. Looks like a bug, indeed. I recommend filing at via `M-x report-emacs-bug` so it's not forgotten. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 14:09 ` Stefan Monnier @ 2020-11-27 15:03 ` Eli Zaretskii 2020-11-27 22:00 ` Alan Mackenzie 2 siblings, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-27 15:03 UTC (permalink / raw) To: Gregory Heytings Cc: andreyk.mad, emacs-devel, rudalics, enometh, monnier, acm > Date: Fri, 27 Nov 2020 13:43:27 +0000 > From: Gregory Heytings <ghe@sdf.org> > cc: martin rudalics <rudalics@gmx.at>, andreyk.mad@gmail.com, > emacs-devel@gnu.org, enometh@meer.net, monnier@iro.umontreal.ca, > acm@muc.de > > >> Having a customizable variable like 'minibuffer-follows-selected-frame' > >> whose purpose is to get back the old behavior, should also provide that > >> old behavior as faithfully as possible IMHO. > > > > The NEWS entry clearly says that the old behavior is no longer > > available, so getting back the old behavior is not the purpose of that > > variable. > > I hope that does not mean "end of discussion". No, it's just to point out that Martin was expecting from that variable something it didn't intend to provide. > it is surprising that such a radical change was accepted without > testing these cases, which are obvious cases to test. The master branch _is_ for testing things. If a change has adverse effects, those adverse effects should be fixed. If it turns out there are too many adverse effects that cannot be reasonably fixed, the change will be reverted. > The NEWS entry says "Nevertheless, the effect of what you type in the > minibuffer happens in the frame where the minibuffer was first activated, > even if it moved to another frame." This is not correct. Three recipes: Please report bugs about these recipes, they need to be investigated, and either they or the documentation needs to be fixed. TIA ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 14:09 ` Stefan Monnier 2020-11-27 15:03 ` Eli Zaretskii @ 2020-11-27 22:00 ` Alan Mackenzie 2 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 22:00 UTC (permalink / raw) To: Gregory Heytings Cc: andreyk.mad, emacs-devel, martin rudalics, enometh, monnier, Eli Zaretskii Hello, Gregory. On Fri, Nov 27, 2020 at 13:43:27 +0000, Gregory Heytings wrote: > >> Having a customizable variable like 'minibuffer-follows-selected-frame' > >> whose purpose is to get back the old behavior, should also provide that > >> old behavior as faithfully as possible IMHO. > > The NEWS entry clearly says that the old behavior is no longer > > available, so getting back the old behavior is not the purpose of that > > variable. > I hope that does not mean "end of discussion". > I sent two recipes to Martin a few hours ago, which demonstrate that the > behavior with that variable set to nil is broken. Again it is surprising > that such a radical change was accepted without testing these cases, which > are obvious cases to test. > The NEWS entry says "Nevertheless, the effect of what you type in the > minibuffer happens in the frame where the minibuffer was first activated, > even if it moved to another frame." This is not correct. Three recipes: > emacs -Q > C-x 5 2 > C-x C-f > C-x 5 o > C-x o > .emacs RET > The file is opened in the frame in which you are, not in the frame in > which C-x C-f was entered. > Another recipe: > emacs -Q > C-x 5 2 > C-h f setq > C-x 5 o > C-x o > RET > The *Help* buffer is displayed in the frame in which you are, not in the > frame in which C-h f setq was entered. > Yet another recipe, similar to the one with which this discussion started: > emacs -Q > C-x 5 2 > C-x b > C-x 5 o > C-x o > RET > The buffer is displayed in the frame in which you are, not in the frame in > which C-x b was entered. Thanks for drawing this to our attention. This bug slipped in unnoticed in my commit 6e469709c550ba18d9d5a34f6bb89908472f0eb2 from Thu Nov 19 10:31:50 2020 +0000, "In attempted recursive minibuffer use, display error message in correct frame". You are right, more systematic testing would have caught this bug before it got committed. I urge you to try out the following fix, and let us all know whether you find further problems with it. Thanks! diff --git a/src/minibuf.c b/src/minibuf.c index fc3fd92a88..7009579763 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -411,6 +411,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -729,6 +730,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, recursive_edit_1 (); + if (!EQ (selected_frame, calling_frame)) + do_switch_frame (calling_frame, 1, 0, Qnil); + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 12:29 ` Eli Zaretskii 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. @ 2020-11-27 15:42 ` martin rudalics 1 sibling, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-27 15:42 UTC (permalink / raw) To: Eli Zaretskii; +Cc: andreyk.mad, emacs-devel, enometh, monnier, ghe, acm >> Having a customizable variable like 'minibuffer-follows-selected-frame' >> whose purpose is to get back the old behavior, should also provide that >> old behavior as faithfully as possible IMHO. > > The NEWS entry clearly says that the old behavior is no longer > available, so getting back the old behavior is not the purpose of that > variable. Thanks for the clarification. I clearly didn't bother to read the NEWS entry. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-27 10:36 ` martin rudalics 2020-11-27 11:30 ` Alan Mackenzie 2020-11-27 12:29 ` Eli Zaretskii @ 2021-01-03 18:10 ` Alan Mackenzie 2021-01-03 18:24 ` martin rudalics 2021-01-04 9:20 ` martin rudalics 2 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-03 18:10 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin, Andrii, Gregory, and everybody else. I have been working on this topic over the last few days, and think the time is almost ripe for another commit to master. In particular, several bugs have been fixed. I would be grateful to anybody who tests out the patch below. Otherwise I think the patch is pretty much ready for committing. On Fri, Nov 27, 2020 at 11:36:47 +0100, martin rudalics wrote: > >> If, with Emacs 28, I set 'minibuffer-follows-selected-frame' to non-nil, > > > > Do you mean "to nil", here? That variable is non-nil by default. > Right. I meant "to nil" here. > >> the behavior does not entirely match that of Emacs 27 because the second > >> RET must be typed in the first frame. So if some application relies on > >> the exact replication of the behavior of Emacs 27, we have a regression. > > > > Well the new behaviour is explicitly not wholly compatible with the old. > > I'm not sure that counts as a regression. > Having a customizable variable like 'minibuffer-follows-selected-frame' > whose purpose is to get back the old behavior, should also provide that > old behavior as faithfully as possible IMHO. Due to protests, minibuffer-follows-selected-frame can now also take a non-nil, non-t value which provides an approximation of the old behaviour. The bugs which have been fixed are these: 1/- "Madhu's bug". Set pop-up-frames to 'graphic-only, do M-!, type g TAB, which opens *Completions* in a new frame. Select something. The *Completions* frame should be deleted, but isn't. This was a problem in window-deleteable-p (window.el), where if an active minibuffer is in a frame, that frame is regarded as not to be deleted. The new minibuffer mechanisms move minibuffers to the new frame on a frame change, so this obviously(?) clashed. I have amended window-deleteable-p to take account of minibuffer-follows-selected-frame and the new mechanisms. 2/- From Martin: Start emacs -Q -l foo.el, where foo.el is: (setq default-frame-alist '((minibuffer . nil))) (defun foo () (interactive) (read-from-minibuffer "...?") (insert (format "%s" (selected-frame)))) (global-set-key [(control meta +)] 'foo) (setq enable-recursive-minibuffers t) Do C-M-+, type something into the minibuffer, and either selected-frame announced *Minibuffer-1*, or there was an error about "Window not being in Frame". Both of these problems are now fixed. 3/- From Gregory: Start emacs -Q, and set enable-recursive-minibuffers to t. Do C-x C-f C-x 5 o twice, then C-x C-f a third time. It was possible to enter filenames for and visit files for the innermost two minibuffers, but not the outermost one. This has (I believe) been fixed. 4/- With minibuffer-follows-selected-frame nil, and enable-recursive-minibuffers t, there were problems caused by editing outer level minibuffers whilst an inner level buffer was still active. I've tried to fix this by giving outer level MBs the keymap minibuffer-inactive-mode-map temporariliy whilst a recursive MB is active. One bug which I haven't fixed, and doesn't appear to be to do with these changes, is: 5/- emacs -Q, enter the following into *Scratch*: (defun bar () (interactive) (read-from-minibuffer "...?") (insert "window: %s ... frame: %s" (selected-window) (selected-frame))) , evaluate it, then evaluate (bar). The window announced under "window:" is *Scratch*, that under "frame:" is *Minibuf-1*. It seems they should match. I think the problem is that frame->name doesn't get appear to get set on a set-frame-selected-window call. I think the command loop sets frame->name, and the recursive command loop in read_minibuf sets it to *Minibuf-1*, and nothing else changes it until the next iteration of the main command loop. Also, this bug was in the version of master just before I made my first commit in this area. OK, that's enough talking. Here's the current version of the patch, which might well be ready to commit. As already said, I'd be grateful for anybody who tests it. Thanks! diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index c7c8fb30ac..f81e64bdf9 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -76,9 +76,13 @@ Basic Minibuffer the user option @code{minibuffer-follows-selected-frame} to @code{nil}, then the minibuffer stays in the frame where you opened it, and you must switch back to that frame in order to complete (or -abort) the current command. Note that the effect of the command, when -you finally finish using the minibuffer, always takes place in the -frame where you first opened it. +abort) the current command. If you set that option to a value which +is neither @code{nil} nor @code{t}, the minibuffer moves frame only +after a recursive minibuffer has been opened in the current command +(@pxref{Recursive Mini,,, elisp}). This option is mainly to retain +(approximately) the behavior prior to Emacs 28.1. Note that the +effect of the command, when you finally finish using the minibuffer, +always takes place in the frame where you first opened it. @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index b294ff1d23..bd707cb047 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,12 +102,13 @@ effect should be negligible in the vast majority of cases anyway. By default, when you switch to another frame, an active minibuffer now moves to the newly selected frame. Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer -was first activated, even if it moved to another frame. An -alternative behavior is available by customizing -'minibuffer-follows-selected-frame' to nil. Here, the minibuffer -stays in the frame where you first opened it, and you must switch back -to this frame to continue or abort its command. The old, somewhat -unsystematic behavior, which mixed these two is no longer available. +was first activated. An alternative behavior is available by +customizing 'minibuffer-follows-selected-frame' to nil. Here, the +minibuffer stays in the frame where you first opened it, and you must +switch back to this frame to continue or abort its command. The old +behavior, which mixed these two, can be approximated by customizing +'minibuffer-follows-selected-frame' to a value which is neither nil +nor t. +++ ** New system for displaying documentation for groups of functions. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 85dd14f628..0293d34d1c 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,7 +394,11 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c - (minibuffer-follows-selected-frame minibuffer boolean "28.1") + (minibuffer-follows-selected-frame + minibuffer (choice (const :tag "Always" t) + (const :tag "When used" hybrid) + (const :tag "Never" nil)) + "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/window.el b/lisp/window.el index cd13e6603a..4b7d2c4677 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4116,7 +4116,10 @@ window-deletable-p frame)) (throw 'other t)))) (let ((minibuf (active-minibuffer-window))) - (and minibuf (eq frame (window-frame minibuf))))) + (and minibuf (eq frame (window-frame minibuf)) + (not (eq (default-toplevel-value + minibuffer-follows-selected-frame) + t))))) 'frame)) ((window-minibuffer-p window) ;; If WINDOW is the minibuffer window of a non-minibuffer-only diff --git a/src/minibuf.c b/src/minibuf.c index 8b23569019..be4ce9d321 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,9 +63,30 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f +/* Return TRUE when a frame switch causes a minibuffer on the old + frame to move onto the new one. */ static bool minibuf_follows_frame (void) +{ + return EQ (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame), + Qt); +} + +/* Return TRUE when a minibuffer always remains on the frame where it + was first invoked. */ +static bool +minibuf_stays_put (void) +{ + return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + +/* Return TRUE when opening a (recursive) minibuffer causes + minibuffers on other frames to move to the selected frame. */ +static bool +minibuf_moves_frame_when_opened (void) { return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } @@ -90,7 +111,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && minibuf_stays_put ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -105,26 +126,37 @@ choose_minibuf_frame (void) } } - if (minibuf_follows_frame ()) + if (minibuf_moves_frame_when_opened ()) /* Make sure no other frame has a minibuffer as its selected window, because the text would not be displayed in it, and that would be confusing. Only allow the selected frame to do this, and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - } + { + Lisp_Object tail, frame; + struct frame *sf = XFRAME (selected_frame); + struct frame *of; + + FOR_EACH_FRAME (tail, frame) + if (!EQ (frame, selected_frame) + && minibuf_level > 1) + { + of = XFRAME (frame); + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + + if (!EQ (XWINDOW (of->minibuffer_window)->contents, + nth_minibuffer (0))) + set_window_buffer (of->minibuffer_window, + nth_minibuffer (0), 0, 0); + } + } } -/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it - from its current frame to the selected frame. This function is - intended to be called from `do_switch_frame' in frame.c. */ +/* If `minibuffer_follows_selected_frame' is t and we have a + minibuffer, move it from its current frame to the selected frame. + This function is intended to be called from `do_switch_frame' in + frame.c. */ void move_minibuffer_onto_frame (void) { if (!minibuf_level) @@ -411,6 +443,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -532,7 +565,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (BVAR (XBUFFER (nth_minibuffer (minibuf_level - 1)), + keymap), + minibuf_save_list))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -648,6 +683,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, } } + if (minibuf_moves_frame_when_opened ()) + { + EMACS_INT i; + + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i < minibuf_level; i++) + set_window_buffer (XFRAME (mini_frame)->minibuffer_window, + nth_minibuffer (i), 0, 0); + } + /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -727,8 +773,39 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); + /* Prevent the user manipulating outer levels of recursive minibuffers. */ + if (minibuf_level > 1) + { + Lisp_Object inactive_map; + if ((inactive_map = + find_symbol_value (intern ("minibuffer-inactive-mode-map"))) + != Qunbound) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level - 1)), + inactive_map); + } + recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the + current window away from the expired minibuffer window. */ + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + while (!EQ (prev, minibuf_window) + && !EQ (selected_frame, WINDOW_FRAME (XWINDOW (prev)))) + prev = Fprevious_window (prev, Qnil, Qnil); + if (!EQ (prev, minibuf_window)) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + + /* Switch the frame back to the calling frame. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + do_switch_frame (calling_frame, 1, 0, Qnil); + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -790,6 +867,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -852,6 +937,7 @@ read_minibuf_unwind (void) Lisp_Object old_deactivate_mark; Lisp_Object window; Lisp_Object future_mini_window; + Lisp_Object map; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -888,6 +974,8 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + map = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -901,6 +989,10 @@ read_minibuf_unwind (void) unbind_to (count, Qnil); } + /* Restore the keymap of any outer level recursive minibuffer. */ + if (minibuf_level > 0) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level)), map); + /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ if (minibuf_level == 0 @@ -2035,13 +2127,15 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; - DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, - doc: /* Non-nil means the active minibuffer always displays on the selected frame. + DEFVAR_LISP ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* t means the active minibuffer always displays on the selected frame. Nil means that a minibuffer will appear only in the frame which created it. +Any other value means the minibuffer will move onto another frame, but +only when the user starts using a minniffer there. Any buffer local or dynamic binding of this variable is ignored. Only the default top level value is used. */); - minibuffer_follows_selected_frame = 1; + minibuffer_follows_selected_frame = Qt; DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-03 18:10 ` Alan Mackenzie @ 2021-01-03 18:24 ` martin rudalics 2021-01-03 18:42 ` Alan Mackenzie 2021-01-04 9:20 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-03 18:24 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > Here's the current version of the patch, > which might well be ready to commit. As already said, I'd be grateful > for anybody who tests it. Thanks! Please send it as attachment, here the linefeeds got lost and it doesn't apply. Thanks, martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-03 18:24 ` martin rudalics @ 2021-01-03 18:42 ` Alan Mackenzie 2021-01-03 20:08 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-01-03 18:42 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 404 bytes --] Hello, Martin. On Sun, Jan 03, 2021 at 19:24:03 +0100, martin rudalics wrote: > > Here's the current version of the patch, > > which might well be ready to commit. As already said, I'd be grateful > > for anybody who tests it. Thanks! > Please send it as attachment, here the linefeeds got lost and it doesn't > apply. OK, here it is! > Thanks, martin -- Alan Mackenzie (Nuremberg, Germany). [-- Attachment #2: diff.2021-01-03.diff --] [-- Type: text/plain, Size: 12872 bytes --] diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index c7c8fb30ac..f81e64bdf9 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -76,9 +76,13 @@ Basic Minibuffer the user option @code{minibuffer-follows-selected-frame} to @code{nil}, then the minibuffer stays in the frame where you opened it, and you must switch back to that frame in order to complete (or -abort) the current command. Note that the effect of the command, when -you finally finish using the minibuffer, always takes place in the -frame where you first opened it. +abort) the current command. If you set that option to a value which +is neither @code{nil} nor @code{t}, the minibuffer moves frame only +after a recursive minibuffer has been opened in the current command +(@pxref{Recursive Mini,,, elisp}). This option is mainly to retain +(approximately) the behavior prior to Emacs 28.1. Note that the +effect of the command, when you finally finish using the minibuffer, +always takes place in the frame where you first opened it. @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index b294ff1d23..bd707cb047 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,12 +102,13 @@ effect should be negligible in the vast majority of cases anyway. By default, when you switch to another frame, an active minibuffer now moves to the newly selected frame. Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer -was first activated, even if it moved to another frame. An -alternative behavior is available by customizing -'minibuffer-follows-selected-frame' to nil. Here, the minibuffer -stays in the frame where you first opened it, and you must switch back -to this frame to continue or abort its command. The old, somewhat -unsystematic behavior, which mixed these two is no longer available. +was first activated. An alternative behavior is available by +customizing 'minibuffer-follows-selected-frame' to nil. Here, the +minibuffer stays in the frame where you first opened it, and you must +switch back to this frame to continue or abort its command. The old +behavior, which mixed these two, can be approximated by customizing +'minibuffer-follows-selected-frame' to a value which is neither nil +nor t. +++ ** New system for displaying documentation for groups of functions. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 85dd14f628..0293d34d1c 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,7 +394,11 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c - (minibuffer-follows-selected-frame minibuffer boolean "28.1") + (minibuffer-follows-selected-frame + minibuffer (choice (const :tag "Always" t) + (const :tag "When used" hybrid) + (const :tag "Never" nil)) + "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/window.el b/lisp/window.el index cd13e6603a..4b7d2c4677 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4116,7 +4116,10 @@ window-deletable-p frame)) (throw 'other t)))) (let ((minibuf (active-minibuffer-window))) - (and minibuf (eq frame (window-frame minibuf))))) + (and minibuf (eq frame (window-frame minibuf)) + (not (eq (default-toplevel-value + minibuffer-follows-selected-frame) + t))))) 'frame)) ((window-minibuffer-p window) ;; If WINDOW is the minibuffer window of a non-minibuffer-only diff --git a/src/minibuf.c b/src/minibuf.c index 8b23569019..be4ce9d321 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,9 +63,30 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f +/* Return TRUE when a frame switch causes a minibuffer on the old + frame to move onto the new one. */ static bool minibuf_follows_frame (void) +{ + return EQ (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame), + Qt); +} + +/* Return TRUE when a minibuffer always remains on the frame where it + was first invoked. */ +static bool +minibuf_stays_put (void) +{ + return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + +/* Return TRUE when opening a (recursive) minibuffer causes + minibuffers on other frames to move to the selected frame. */ +static bool +minibuf_moves_frame_when_opened (void) { return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } @@ -90,7 +111,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && minibuf_stays_put ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -105,26 +126,37 @@ choose_minibuf_frame (void) } } - if (minibuf_follows_frame ()) + if (minibuf_moves_frame_when_opened ()) /* Make sure no other frame has a minibuffer as its selected window, because the text would not be displayed in it, and that would be confusing. Only allow the selected frame to do this, and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - } + { + Lisp_Object tail, frame; + struct frame *sf = XFRAME (selected_frame); + struct frame *of; + + FOR_EACH_FRAME (tail, frame) + if (!EQ (frame, selected_frame) + && minibuf_level > 1) + { + of = XFRAME (frame); + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + + if (!EQ (XWINDOW (of->minibuffer_window)->contents, + nth_minibuffer (0))) + set_window_buffer (of->minibuffer_window, + nth_minibuffer (0), 0, 0); + } + } } -/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it - from its current frame to the selected frame. This function is - intended to be called from `do_switch_frame' in frame.c. */ +/* If `minibuffer_follows_selected_frame' is t and we have a + minibuffer, move it from its current frame to the selected frame. + This function is intended to be called from `do_switch_frame' in + frame.c. */ void move_minibuffer_onto_frame (void) { if (!minibuf_level) @@ -411,6 +443,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -532,7 +565,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (BVAR (XBUFFER (nth_minibuffer (minibuf_level - 1)), + keymap), + minibuf_save_list))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -648,6 +683,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, } } + if (minibuf_moves_frame_when_opened ()) + { + EMACS_INT i; + + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i < minibuf_level; i++) + set_window_buffer (XFRAME (mini_frame)->minibuffer_window, + nth_minibuffer (i), 0, 0); + } + /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -727,8 +773,39 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); + /* Prevent the user manipulating outer levels of recursive minibuffers. */ + if (minibuf_level > 1) + { + Lisp_Object inactive_map; + if ((inactive_map = + find_symbol_value (intern ("minibuffer-inactive-mode-map"))) + != Qunbound) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level - 1)), + inactive_map); + } + recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the + current window away from the expired minibuffer window. */ + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + while (!EQ (prev, minibuf_window) + && !EQ (selected_frame, WINDOW_FRAME (XWINDOW (prev)))) + prev = Fprevious_window (prev, Qnil, Qnil); + if (!EQ (prev, minibuf_window)) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + + /* Switch the frame back to the calling frame. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + do_switch_frame (calling_frame, 1, 0, Qnil); + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -790,6 +867,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -852,6 +937,7 @@ read_minibuf_unwind (void) Lisp_Object old_deactivate_mark; Lisp_Object window; Lisp_Object future_mini_window; + Lisp_Object map; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -888,6 +974,8 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + map = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -901,6 +989,10 @@ read_minibuf_unwind (void) unbind_to (count, Qnil); } + /* Restore the keymap of any outer level recursive minibuffer. */ + if (minibuf_level > 0) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level)), map); + /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ if (minibuf_level == 0 @@ -2035,13 +2127,15 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; - DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, - doc: /* Non-nil means the active minibuffer always displays on the selected frame. + DEFVAR_LISP ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* t means the active minibuffer always displays on the selected frame. Nil means that a minibuffer will appear only in the frame which created it. +Any other value means the minibuffer will move onto another frame, but +only when the user starts using a minniffer there. Any buffer local or dynamic binding of this variable is ignored. Only the default top level value is used. */); - minibuffer_follows_selected_frame = 1; + minibuffer_follows_selected_frame = Qt; DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-03 18:42 ` Alan Mackenzie @ 2021-01-03 20:08 ` martin rudalics 2021-01-03 20:43 ` Alan Mackenzie 0 siblings, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-03 20:08 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > OK, here it is! Thanks. It fails to build here as: CC cmds.o ../../src/minibuf.c: In function ‘choose_minibuf_frame’: ../../src/minibuf.c:136:19: warning: unused variable ‘sf’ [-Wunused-variable] struct frame *sf = XFRAME (selected_frame); ^~ ../../src/minibuf.c: In function ‘read_minibuf’: ../../src/minibuf.c:784:11: error: invalid operands to binary != (have ‘Lisp_Object’ {aka ‘struct Lisp_Object’} and ‘Lisp_Object’ {aka ‘struct Lisp_Object’}) if ((inactive_map = ~~~~~~~~~~~~~~~ find_symbol_value (intern ("minibuffer-inactive-mode-map"))) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ != Qunbound) ^~ CC casetab.o make[1]: *** [Makefile:405: minibuf.o] Fehler 1 make[1]: *** Es wird auf noch nicht beendete Prozesse gewartet.... make[1]: Verzeichnis „/home/martin/emacs-git/trunk/obj-gtk/src“ wird verlassen make: *** [Makefile:424: src] Fehler 2 You have to use !EQ when testing Lisp Objects for inequality. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-03 20:08 ` martin rudalics @ 2021-01-03 20:43 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-03 20:43 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 1437 bytes --] Hello, Martin. On Sun, Jan 03, 2021 at 21:08:21 +0100, martin rudalics wrote: > > OK, here it is! > Thanks. It fails to build here as: > CC cmds.o > ../../src/minibuf.c: In function ‘choose_minibuf_frame’: > ../../src/minibuf.c:136:19: warning: unused variable ‘sf’ [-Wunused-variable] > struct frame *sf = XFRAME (selected_frame); > ^~ > ../../src/minibuf.c: In function ‘read_minibuf’: > ../../src/minibuf.c:784:11: error: invalid operands to binary != (have ‘Lisp_Object’ {aka ‘struct Lisp_Object’} and ‘Lisp_Object’ {aka ‘struct Lisp_Object’}) > if ((inactive_map = > ~~~~~~~~~~~~~~~ > find_symbol_value (intern ("minibuffer-inactive-mode-map"))) > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > != Qunbound) > ^~ > CC casetab.o > make[1]: *** [Makefile:405: minibuf.o] Fehler 1 > make[1]: *** Es wird auf noch nicht beendete Prozesse gewartet.... > make[1]: Verzeichnis „/home/martin/emacs-git/trunk/obj-gtk/src“ wird verlassen > make: *** [Makefile:424: src] Fehler 2 Thanks! My compiler (GCC 9.3.0) didn't even raise a warning for that. I've corrected it, and also got rid of the unused variable. > You have to use !EQ when testing Lisp Objects for inequality. Yes, indeed. The corrected patch is attached. > martin -- Alan Mackenzie (Nuremberg, Germany). [-- Attachment #2: diff.20210103b.diff --] [-- Type: text/plain, Size: 12820 bytes --] diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index c7c8fb30ac..f81e64bdf9 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -76,9 +76,13 @@ Basic Minibuffer the user option @code{minibuffer-follows-selected-frame} to @code{nil}, then the minibuffer stays in the frame where you opened it, and you must switch back to that frame in order to complete (or -abort) the current command. Note that the effect of the command, when -you finally finish using the minibuffer, always takes place in the -frame where you first opened it. +abort) the current command. If you set that option to a value which +is neither @code{nil} nor @code{t}, the minibuffer moves frame only +after a recursive minibuffer has been opened in the current command +(@pxref{Recursive Mini,,, elisp}). This option is mainly to retain +(approximately) the behavior prior to Emacs 28.1. Note that the +effect of the command, when you finally finish using the minibuffer, +always takes place in the frame where you first opened it. @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index b294ff1d23..bd707cb047 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,12 +102,13 @@ effect should be negligible in the vast majority of cases anyway. By default, when you switch to another frame, an active minibuffer now moves to the newly selected frame. Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer -was first activated, even if it moved to another frame. An -alternative behavior is available by customizing -'minibuffer-follows-selected-frame' to nil. Here, the minibuffer -stays in the frame where you first opened it, and you must switch back -to this frame to continue or abort its command. The old, somewhat -unsystematic behavior, which mixed these two is no longer available. +was first activated. An alternative behavior is available by +customizing 'minibuffer-follows-selected-frame' to nil. Here, the +minibuffer stays in the frame where you first opened it, and you must +switch back to this frame to continue or abort its command. The old +behavior, which mixed these two, can be approximated by customizing +'minibuffer-follows-selected-frame' to a value which is neither nil +nor t. +++ ** New system for displaying documentation for groups of functions. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 85dd14f628..0293d34d1c 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,7 +394,11 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c - (minibuffer-follows-selected-frame minibuffer boolean "28.1") + (minibuffer-follows-selected-frame + minibuffer (choice (const :tag "Always" t) + (const :tag "When used" hybrid) + (const :tag "Never" nil)) + "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/window.el b/lisp/window.el index cd13e6603a..4b7d2c4677 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4116,7 +4116,10 @@ window-deletable-p frame)) (throw 'other t)))) (let ((minibuf (active-minibuffer-window))) - (and minibuf (eq frame (window-frame minibuf))))) + (and minibuf (eq frame (window-frame minibuf)) + (not (eq (default-toplevel-value + minibuffer-follows-selected-frame) + t))))) 'frame)) ((window-minibuffer-p window) ;; If WINDOW is the minibuffer window of a non-minibuffer-only diff --git a/src/minibuf.c b/src/minibuf.c index 8b23569019..55445f103d 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,9 +63,30 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f +/* Return TRUE when a frame switch causes a minibuffer on the old + frame to move onto the new one. */ static bool minibuf_follows_frame (void) +{ + return EQ (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame), + Qt); +} + +/* Return TRUE when a minibuffer always remains on the frame where it + was first invoked. */ +static bool +minibuf_stays_put (void) +{ + return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + +/* Return TRUE when opening a (recursive) minibuffer causes + minibuffers on other frames to move to the selected frame. */ +static bool +minibuf_moves_frame_when_opened (void) { return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } @@ -90,7 +111,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && minibuf_stays_put ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -105,26 +126,36 @@ choose_minibuf_frame (void) } } - if (minibuf_follows_frame ()) + if (minibuf_moves_frame_when_opened ()) /* Make sure no other frame has a minibuffer as its selected window, because the text would not be displayed in it, and that would be confusing. Only allow the selected frame to do this, and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - } + { + Lisp_Object tail, frame; + struct frame *of; + + FOR_EACH_FRAME (tail, frame) + if (!EQ (frame, selected_frame) + && minibuf_level > 1) + { + of = XFRAME (frame); + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + + if (!EQ (XWINDOW (of->minibuffer_window)->contents, + nth_minibuffer (0))) + set_window_buffer (of->minibuffer_window, + nth_minibuffer (0), 0, 0); + } + } } -/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it - from its current frame to the selected frame. This function is - intended to be called from `do_switch_frame' in frame.c. */ +/* If `minibuffer_follows_selected_frame' is t and we have a + minibuffer, move it from its current frame to the selected frame. + This function is intended to be called from `do_switch_frame' in + frame.c. */ void move_minibuffer_onto_frame (void) { if (!minibuf_level) @@ -411,6 +442,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -532,7 +564,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (BVAR (XBUFFER (nth_minibuffer (minibuf_level - 1)), + keymap), + minibuf_save_list))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -648,6 +682,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, } } + if (minibuf_moves_frame_when_opened ()) + { + EMACS_INT i; + + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i < minibuf_level; i++) + set_window_buffer (XFRAME (mini_frame)->minibuffer_window, + nth_minibuffer (i), 0, 0); + } + /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -727,8 +772,39 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); + /* Prevent the user manipulating outer levels of recursive minibuffers. */ + if (minibuf_level > 1) + { + Lisp_Object inactive_map; + if (!EQ (inactive_map = + find_symbol_value (intern ("minibuffer-inactive-mode-map")), + Qunbound)) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level - 1)), + inactive_map); + } + recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the + current window away from the expired minibuffer window. */ + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + while (!EQ (prev, minibuf_window) + && !EQ (selected_frame, WINDOW_FRAME (XWINDOW (prev)))) + prev = Fprevious_window (prev, Qnil, Qnil); + if (!EQ (prev, minibuf_window)) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + + /* Switch the frame back to the calling frame. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + do_switch_frame (calling_frame, 1, 0, Qnil); + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -790,6 +866,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -852,6 +936,7 @@ read_minibuf_unwind (void) Lisp_Object old_deactivate_mark; Lisp_Object window; Lisp_Object future_mini_window; + Lisp_Object map; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -888,6 +973,8 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + map = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -901,6 +988,10 @@ read_minibuf_unwind (void) unbind_to (count, Qnil); } + /* Restore the keymap of any outer level recursive minibuffer. */ + if (minibuf_level > 0) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level)), map); + /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ if (minibuf_level == 0 @@ -2035,13 +2126,15 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; - DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, - doc: /* Non-nil means the active minibuffer always displays on the selected frame. + DEFVAR_LISP ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* t means the active minibuffer always displays on the selected frame. Nil means that a minibuffer will appear only in the frame which created it. +Any other value means the minibuffer will move onto another frame, but +only when the user starts using a minniffer there. Any buffer local or dynamic binding of this variable is ignored. Only the default top level value is used. */); - minibuffer_follows_selected_frame = 1; + minibuffer_follows_selected_frame = Qt; DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-03 18:10 ` Alan Mackenzie 2021-01-03 18:24 ` martin rudalics @ 2021-01-04 9:20 ` martin rudalics 2021-01-05 18:07 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-04 9:20 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii It builds now so let's get back to your initial request. > 1/- "Madhu's bug". Set pop-up-frames to 'graphic-only, do M-!, type > g TAB, which opens *Completions* in a new frame. Select something. The > *Completions* frame should be deleted, but isn't. > > This was a problem in window-deleteable-p (window.el), where if an > active minibuffer is in a frame, that frame is regarded as not to be > deleted. The new minibuffer mechanisms move minibuffers to the new > frame on a frame change, so this obviously(?) clashed. I have amended > window-deleteable-p to take account of > minibuffer-follows-selected-frame and the new mechanisms. I leave this to Madhu. Here the minibuffer frame is not deleted but gets iconified which is probably due to the default of 'auto-hide-function', something I'm not going to dispute here. > 2/- From Martin: Start emacs -Q -l foo.el, where foo.el is: > > (setq default-frame-alist '((minibuffer . nil))) > > (defun foo () > (interactive) > (read-from-minibuffer "...?") > (insert (format "%s" (selected-frame)))) > > (global-set-key [(control meta +)] 'foo) > > (setq enable-recursive-minibuffers t) > > Do C-M-+, type something into the minibuffer, and either selected-frame > announced *Minibuffer-1*, or there was an error about "Window not being > in Frame". Both of these problems are now fixed. Confirmed. At least I can't reproduce them any more. > > 3/- From Gregory: Start emacs -Q, and set enable-recursive-minibuffers > to t. Do C-x C-f C-x 5 o twice, then C-x C-f a third time. It was > possible to enter filenames for and visit files for the innermost two > minibuffers, but not the outermost one. This has (I believe) been > fixed. I'm not sure what's missing here, probably a C-x 5 2 to get the frame C-x 5 o can act upon. So I let Gregory tell whether this has been fixed. What I see is that with 'enable-recursive-minibuffers' t C-x 5 2 followed by two C-x C-f C-x 5 o - doesn't get me _always_ into the minibuffer window of the frame switched too (I'm not sure whether it should and under which circumstances - this should be clarified), and - typing C-g to quit an inner invocation of C-x C-f sometimes gets me a catatonic minibuffer window where either nothing is displayed or just a simple "Quit" appears - I have to get out of it via C-x o. > 4/- With minibuffer-follows-selected-frame nil, and > enable-recursive-minibuffers t, there were problems caused by editing > outer level minibuffers whilst an inner level buffer was still active. > I've tried to fix this by giving outer level MBs the keymap > minibuffer-inactive-mode-map temporariliy whilst a recursive MB is > active. How do I edit an outer level minibuffer whilst an inner level buffer is active? > One bug which I haven't fixed, and doesn't appear to be to do with these > changes, is: > > > 5/- emacs -Q, enter the following into *Scratch*: > > (defun bar () > (interactive) > (read-from-minibuffer "...?") > (insert "window: %s ... frame: %s" ... which is probably missing a "format" here ... > (selected-window) (selected-frame))) > > , evaluate it, then evaluate (bar). The window announced under "window:" > is *Scratch*, that under "frame:" is *Minibuf-1*. It seems they should > match. > > I think the problem is that frame->name doesn't get appear to get set on > a set-frame-selected-window call. I think the command loop sets > frame->name, and the recursive command loop in read_minibuf sets it to > *Minibuf-1*, and nothing else changes it until the next iteration of the > main command loop. > > Also, this bug was in the version of master just before I made my first > commit in this area. That's probably part of the select window/frame mystery. You just can't have them both - 'select-window' and 'select-frame' - and hope that they will live happily ever after. Maybe a wset_redisplay (XWINDOW (window)); after the fset_selected_window (XFRAME (frame), window); is due so that the redisplay mechanism knows what title to draw in that frame but I don't think we should care much. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-04 9:20 ` martin rudalics @ 2021-01-05 18:07 ` Alan Mackenzie 2021-01-05 18:53 ` martin rudalics 2021-01-06 0:14 ` Gregory Heytings via Emacs development discussions. 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-05 18:07 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 3282 bytes --] Hello, Martin and everybody else. On Mon, Jan 04, 2021 at 10:20:11 +0100, martin rudalics wrote: [ .... ] > > 3/- From Gregory: Start emacs -Q, and set enable-recursive-minibuffers > > to t. Do C-x C-f C-x 5 o twice, then C-x C-f a third time. It was > > possible to enter filenames for and visit files for the innermost two > > minibuffers, but not the outermost one. This has (I believe) been > > fixed. > I'm not sure what's missing here, probably a C-x 5 2 to get the frame > C-x 5 o can act upon. So I let Gregory tell whether this has been > fixed. What I see is that with 'enable-recursive-minibuffers' t C-x 5 2 > followed by two C-x C-f C-x 5 o > - doesn't get me _always_ into the minibuffer window of the frame > switched to (I'm not sure whether it should and under which > circumstances - this should be clarified), I don't think it should, in general, unless the miniwindow is (still) the frame's current window. I've found out why, in Gregory's scenario, after the "middle" RET to visit a file, point was not moving back to the "middle frame": it's because select_frame is insufficient of itself to move X-Window's focus, which stays in the "old" frame. Any command now causes a "switch-frame" event which moves the minibuffers back into the "old" frame, which isn't what we want. The solution (a bit ugly) is to call the lisp function select-frame-set-input-focus rather than just do_switch_frame near the end of read_minibuf. > and > - typing C-g to quit an inner invocation of C-x C-f sometimes gets me a > catatonic minibuffer window where either nothing is displayed or just > a simple "Quit" appears - I have to get out of it via C-x o. Yes. I still have some work to do in this area. > > 4/- With minibuffer-follows-selected-frame nil, and > > enable-recursive-minibuffers t, there were problems caused by editing > > outer level minibuffers whilst an inner level buffer was still active. > > I've tried to fix this by giving outer level MBs the keymap > > minibuffer-inactive-mode-map temporariliy whilst a recursive MB is > > active. > How do I edit an outer level minibuffer whilst an inner level buffer is > active? At the moment, you can't. This isn't good. What do you think of the following solution? Instead of setting outer minibuffers' maps temporarily to minibuffer-inactive-mode-map, I could amend exit-minibuffer so that it would throw an error when there was a more nested active minibuffer, but leave the current minibuffers untouched. Also, C-g should then abort the current minibuffer's caller, together with those of any more nested MBs. [ .... ] The change in the current version of the patch (attached) is that, as already mentioned, select-frame-set-input-focus is called rather than just do_select_frame from near the end of read_minibuf. Also, that call has been moved from just after the recursive edit to the very end of read_minibuf. Also, move_minibuffer_onto_frame now moves the entire stack of minibuffers, not just the current one (it is not called when minibuffers-follows-selected-frame is nil). How would you feel about committing this patch? It is an improvement over the current state, even though not yet finished. > martin -- Alan Mackenzie (Nuremberg, Germany). [-- Attachment #2: diff.20210105.diff --] [-- Type: text/plain, Size: 14612 bytes --] diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index c7c8fb30ac..f81e64bdf9 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -76,9 +76,13 @@ Basic Minibuffer the user option @code{minibuffer-follows-selected-frame} to @code{nil}, then the minibuffer stays in the frame where you opened it, and you must switch back to that frame in order to complete (or -abort) the current command. Note that the effect of the command, when -you finally finish using the minibuffer, always takes place in the -frame where you first opened it. +abort) the current command. If you set that option to a value which +is neither @code{nil} nor @code{t}, the minibuffer moves frame only +after a recursive minibuffer has been opened in the current command +(@pxref{Recursive Mini,,, elisp}). This option is mainly to retain +(approximately) the behavior prior to Emacs 28.1. Note that the +effect of the command, when you finally finish using the minibuffer, +always takes place in the frame where you first opened it. @node Minibuffer File @section Minibuffers for File Names diff --git a/etc/NEWS b/etc/NEWS index b294ff1d23..bd707cb047 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,12 +102,13 @@ effect should be negligible in the vast majority of cases anyway. By default, when you switch to another frame, an active minibuffer now moves to the newly selected frame. Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer -was first activated, even if it moved to another frame. An -alternative behavior is available by customizing -'minibuffer-follows-selected-frame' to nil. Here, the minibuffer -stays in the frame where you first opened it, and you must switch back -to this frame to continue or abort its command. The old, somewhat -unsystematic behavior, which mixed these two is no longer available. +was first activated. An alternative behavior is available by +customizing 'minibuffer-follows-selected-frame' to nil. Here, the +minibuffer stays in the frame where you first opened it, and you must +switch back to this frame to continue or abort its command. The old +behavior, which mixed these two, can be approximated by customizing +'minibuffer-follows-selected-frame' to a value which is neither nil +nor t. +++ ** New system for displaying documentation for groups of functions. @@ -2046,6 +2047,12 @@ and display the result. When non-nil, then functions 'read-char-choice' and 'y-or-n-p' (respectively) use the function 'read-key' to read a character instead of using the minibuffer. ++++ +** New syntax flag 'e'. +This indicates that one or two (or more) escape characters escape a +comment ender with this flag, causing the comment to be continued past +that comment ender (typically onto the next line). + +++ ** 'set-window-configuration' now takes an optional 'dont-set-frame' parameter which, when non-nil, instructs the function not to select diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 85dd14f628..0293d34d1c 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,7 +394,11 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c - (minibuffer-follows-selected-frame minibuffer boolean "28.1") + (minibuffer-follows-selected-frame + minibuffer (choice (const :tag "Always" t) + (const :tag "When used" hybrid) + (const :tag "Never" nil)) + "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/window.el b/lisp/window.el index cd13e6603a..4b7d2c4677 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4116,7 +4116,10 @@ window-deletable-p frame)) (throw 'other t)))) (let ((minibuf (active-minibuffer-window))) - (and minibuf (eq frame (window-frame minibuf))))) + (and minibuf (eq frame (window-frame minibuf)) + (not (eq (default-toplevel-value + minibuffer-follows-selected-frame) + t))))) 'frame)) ((window-minibuffer-p window) ;; If WINDOW is the minibuffer window of a non-minibuffer-only diff --git a/src/minibuf.c b/src/minibuf.c index 8b23569019..a99a208c2b 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,9 +63,30 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f +/* Return TRUE when a frame switch causes a minibuffer on the old + frame to move onto the new one. */ static bool minibuf_follows_frame (void) +{ + return EQ (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame), + Qt); +} + +/* Return TRUE when a minibuffer always remains on the frame where it + was first invoked. */ +static bool +minibuf_stays_put (void) +{ + return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + +/* Return TRUE when opening a (recursive) minibuffer causes + minibuffers on other frames to move to the selected frame. */ +static bool +minibuf_moves_frame_when_opened (void) { return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } @@ -90,7 +111,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && minibuf_stays_put ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -105,26 +126,36 @@ choose_minibuf_frame (void) } } - if (minibuf_follows_frame ()) + if (minibuf_moves_frame_when_opened ()) /* Make sure no other frame has a minibuffer as its selected window, because the text would not be displayed in it, and that would be confusing. Only allow the selected frame to do this, and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - } + { + Lisp_Object tail, frame; + struct frame *of; + + FOR_EACH_FRAME (tail, frame) + if (!EQ (frame, selected_frame) + && minibuf_level > 1) + { + of = XFRAME (frame); + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + + if (!EQ (XWINDOW (of->minibuffer_window)->contents, + nth_minibuffer (0))) + set_window_buffer (of->minibuffer_window, + nth_minibuffer (0), 0, 0); + } + } } -/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it - from its current frame to the selected frame. This function is - intended to be called from `do_switch_frame' in frame.c. */ +/* If `minibuffer_follows_selected_frame' is t and we have a + minibuffer, move it from its current frame to the selected frame. + This function is intended to be called from `do_switch_frame' in + frame.c. */ void move_minibuffer_onto_frame (void) { if (!minibuf_level) @@ -135,12 +166,15 @@ void move_minibuffer_onto_frame (void) && FRAME_LIVE_P (XFRAME (selected_frame)) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { + EMACS_INT i; struct frame *sf = XFRAME (selected_frame); Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; struct frame *of = XFRAME (old_frame); - Lisp_Object buffer = XWINDOW (minibuf_window)->contents; - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i <= minibuf_level; i++) + set_window_buffer (sf->minibuffer_window, nth_minibuffer (i), 0, 0); minibuf_window = sf->minibuffer_window; set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); } @@ -411,6 +445,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -532,7 +567,9 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, minibuf_save_list = Fcons (Voverriding_local_map, Fcons (minibuf_window, - minibuf_save_list)); + Fcons (BVAR (XBUFFER (nth_minibuffer (minibuf_level - 1)), + keymap), + minibuf_save_list))); minibuf_save_list = Fcons (minibuf_prompt, Fcons (make_fixnum (minibuf_prompt_width), @@ -648,6 +685,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, } } + if (minibuf_moves_frame_when_opened ()) + { + EMACS_INT i; + + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i < minibuf_level; i++) + set_window_buffer (XFRAME (mini_frame)->minibuffer_window, + nth_minibuffer (i), 0, 0); + } + /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -727,8 +775,32 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, /* Don't allow the user to undo past this point. */ bset_undo_list (current_buffer, Qnil); + /* Prevent the user manipulating outer levels of recursive minibuffers. */ + if (minibuf_level > 1) + { + Lisp_Object inactive_map = + find_symbol_value (intern ("minibuffer-inactive-mode-map")); + if (!EQ (inactive_map, Qunbound)) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level - 1)), + inactive_map); + } + recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the + current window away from the expired minibuffer window. */ + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + while (!EQ (prev, minibuf_window) + && !EQ (selected_frame, WINDOW_FRAME (XWINDOW (prev)))) + prev = Fprevious_window (prev, Qnil, Qnil); + if (!EQ (prev, minibuf_window)) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -767,6 +839,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, in set-window-configuration. */ unbind_to (count, Qnil); + /* Switch the frame back to the calling frame. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + call2 (intern ("select-frame-set-input-focus"), calling_frame, Qnil); + /* Add the value to the appropriate history list, if any. This is done after the previous buffer has been made current again, in case the history variable is buffer-local. */ @@ -790,6 +868,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -852,6 +938,7 @@ read_minibuf_unwind (void) Lisp_Object old_deactivate_mark; Lisp_Object window; Lisp_Object future_mini_window; + Lisp_Object map; /* If this was a recursive minibuffer, tie the minibuffer window back to the outer level minibuffer buffer. */ @@ -888,6 +975,8 @@ read_minibuf_unwind (void) #endif future_mini_window = Fcar (minibuf_save_list); minibuf_save_list = Fcdr (minibuf_save_list); + map = Fcar (minibuf_save_list); + minibuf_save_list = Fcdr (minibuf_save_list); /* Erase the minibuffer we were using at this level. */ { @@ -901,6 +990,10 @@ read_minibuf_unwind (void) unbind_to (count, Qnil); } + /* Restore the keymap of any outer level recursive minibuffer. */ + if (minibuf_level > 0) + bset_keymap (XBUFFER (nth_minibuffer (minibuf_level)), map); + /* When we get to the outmost level, make sure we resize the mini-window back to its normal size. */ if (minibuf_level == 0 @@ -2035,13 +2128,15 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; - DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, - doc: /* Non-nil means the active minibuffer always displays on the selected frame. + DEFVAR_LISP ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* t means the active minibuffer always displays on the selected frame. Nil means that a minibuffer will appear only in the frame which created it. +Any other value means the minibuffer will move onto another frame, but +only when the user starts using a minniffer there. Any buffer local or dynamic binding of this variable is ignored. Only the default top level value is used. */); - minibuffer_follows_selected_frame = 1; + minibuffer_follows_selected_frame = Qt; DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-05 18:07 ` Alan Mackenzie @ 2021-01-05 18:53 ` martin rudalics 2021-01-07 17:36 ` Alan Mackenzie 2021-01-06 0:14 ` Gregory Heytings via Emacs development discussions. 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-05 18:53 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii >> - doesn't get me _always_ into the minibuffer window of the frame >> switched to (I'm not sure whether it should and under which >> circumstances - this should be clarified), > > I don't think it should, in general, unless the miniwindow is (still) the > frame's current window. OK. Let's stick to this rule. > I've found out why, in Gregory's scenario, after the "middle" RET to > visit a file, point was not moving back to the "middle frame": it's > because select_frame is insufficient of itself to move X-Window's focus, > which stays in the "old" frame. Any command now causes a "switch-frame" > event which moves the minibuffers back into the "old" frame, which isn't > what we want. The solution (a bit ugly) is to call the lisp function > select-frame-set-input-focus rather than just do_switch_frame near the > end of read_minibuf. This is indeed ugly and might harm 'redirect-frame-focus'. >> How do I edit an outer level minibuffer whilst an inner level buffer is >> active? > > At the moment, you can't. This isn't good. I think it is good (enough). Do we want spaghetti minibuffers? > What do you think of the > following solution? > > Instead of setting outer minibuffers' maps temporarily to > minibuffer-inactive-mode-map, I could amend exit-minibuffer so that it > would throw an error when there was a more nested active minibuffer, but > leave the current minibuffers untouched. Also, C-g should then abort the > current minibuffer's caller, together with those of any more nested MBs. We're right in hell's kitchen here. > The change in the current version of the patch (attached) is that, as > already mentioned, select-frame-set-input-focus is called rather than > just do_select_frame from near the end of read_minibuf. Also, that call > has been moved from just after the recursive edit to the very end of > read_minibuf. Also, move_minibuffer_onto_frame now moves the entire > stack of minibuffers, not just the current one (it is not called when > minibuffers-follows-selected-frame is nil). > > How would you feel about committing this patch? It is an improvement > over the current state, even though not yet finished. Let's wait for a week at least. I'd really want to hear the comments from the rest of the bunch. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-05 18:53 ` martin rudalics @ 2021-01-07 17:36 ` Alan Mackenzie 2021-01-07 18:08 ` Drew Adams 2021-01-07 18:26 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-07 17:36 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello again, Martin. On Tue, Jan 05, 2021 at 19:53:23 +0100, martin rudalics wrote: > >> - doesn't get me _always_ into the minibuffer window of the frame > >> switched to (I'm not sure whether it should and under which > >> circumstances - this should be clarified), > > I don't think it should, in general, unless the miniwindow is (still) the > > frame's current window. > OK. Let's stick to this rule. :-) > > I've found out why, in Gregory's scenario, after the "middle" RET to > > visit a file, point was not moving back to the "middle frame": it's > > because select_frame is insufficient of itself to move X-Window's focus, > > which stays in the "old" frame. Any command now causes a "switch-frame" > > event which moves the minibuffers back into the "old" frame, which isn't > > what we want. The solution (a bit ugly) is to call the lisp function > > select-frame-set-input-focus rather than just do_switch_frame near the > > end of read_minibuf. > This is indeed ugly and might harm 'redirect-frame-focus'. Actualy, I don't think it's as ugly as all that. select-frame-set-input-focus is an extremely coherent function; it does one thing and does it well, i.e. switch frames. Maybe it's a pity that it's a Lisp function rather than a C function, but it is as it is. I've been trying to think through the bit about redirect-frame-focus. It's difficult. Are you aware of any particular scenarios where it might go wrong with select-frame-set-input-focus? Or is it more a general concern, possibly from past experience? For example if the source frame has focus redirected to a MB-only frame, and s-f-s-i-focus is called, should that redirected focus be undone? I'm a little confused about the difference between a MB-only frame being the selected frame, and it being the focus frame of a different "normal" frame. > >> How do I edit an outer level minibuffer whilst an inner level buffer is > >> active? > > At the moment, you can't. This isn't good. > I think it is good (enough). Do we want spaghetti minibuffers? I've changed the handling of outer level minibuffers so that they can be freely edited (but not exited, see below). > > What do you think of the > > following solution? > > Instead of setting outer minibuffers' maps temporarily to > > minibuffer-inactive-mode-map, I could amend exit-minibuffer so that > > it would throw an error when there was a more nested active > > minibuffer, but leave the current minibuffers untouched. Also, C-g > > should then abort the current minibuffer's caller, together with > > those of any more nested MBs. > We're right in hell's kitchen here. I've actually implemented the above, on a trial basis. It was actually surprisingly easy. The C-g bit was just 9 extra lines (plus comments) in internal_catch (eval.c), and another small function in minibuf.c. You say "hell's kitchen". Again, is this on general principles, or can you see some specific problems which might crop up? My feeling is that this way of dealing with "not the innermost minibuffer" is simple, and possibly as good as we're going to get. Another possibility, is that rather than a stack of minibuffers, we have a collection of minibuffers, any one of which (which is accessible) can be used and exited at any time. But I can foreseen lots of unforeseen problems happening with this, and I don't want to try to implement it at the moment. > > The change in the current version of the patch (attached) is that, > > as already mentioned, select-frame-set-input-focus is called rather > > than just do_select_frame from near the end of read_minibuf. Also, > > that call has been moved from just after the recursive edit to the > > very end of read_minibuf. Also, move_minibuffer_onto_frame now > > moves the entire stack of minibuffers, not just the current one (it > > is not called when minibuffers-follows-selected-frame is nil). > > How would you feel about committing this patch? It is an improvement > > over the current state, even though not yet finished. > Let's wait for a week at least. I'd really want to hear the comments > from the rest of the bunch. OK. My feeling at the moment is the patch is now finished (apart from some documentation changes). Maybe I should push out another patch, though I'm not doing that right at the moment. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* RE: Stop frames stealing eachothers' minibuffers! 2021-01-07 17:36 ` Alan Mackenzie @ 2021-01-07 18:08 ` Drew Adams 2021-01-07 18:26 ` martin rudalics 1 sibling, 0 replies; 252+ messages in thread From: Drew Adams @ 2021-01-07 18:08 UTC (permalink / raw) To: Alan Mackenzie, martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > I've been trying to think through the bit about redirect-frame-focus. > It's difficult. Are you aware of any particular scenarios where it might > go wrong with select-frame-set-input-focus? Apologies for chiming in here. I'm not very knowledgeable about this, and I can't speak directly to the current context. I can say that I use `redirect-frame-focus' to redirect the focus for a standalone `*Completions*' frame to a standalone minibuffer frame. And I have problems with Emacs 27, including bad changes in frame focus. (Dunno whether my problems with Emacs 27 are related to this thread.) I also call `select-frame-set-input-focus' a fair amount in my code. And users can, e.g., mouse-click another frame during minibuffer input to select that other frame (or select it in another way), do something with that frame selected, then select the minibuffer frame again, etc. Again, no idea whether what I'm saying here is relevant to the question at hand. If not, please ignore. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-07 17:36 ` Alan Mackenzie 2021-01-07 18:08 ` Drew Adams @ 2021-01-07 18:26 ` martin rudalics 2021-01-10 0:53 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-07 18:26 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > Actualy, I don't think it's as ugly as all that. > select-frame-set-input-focus is an extremely coherent function; it does > one thing and does it well, i.e. switch frames. Maybe it's a pity that > it's a Lisp function rather than a C function, but it is as it is. It does call 'raise-frame' something people with 'focus-follows-mouse' t (not 'auto-raise') might not want here. And it may have to move the mouse cursor according to the 'mouse-autoselect-window' and 'focus-follows-mouse' policies. Most window managers don't handle the latter well (IIRC only the otherwise abominable mutter and KDE could do it approximately). And the former wants the selected window to be set up well before calling 'select-frame-set-input-focus'. > I've been trying to think through the bit about redirect-frame-focus. > It's difficult. Are you aware of any particular scenarios where it might > go wrong with select-frame-set-input-focus? Or is it more a general > concern, possibly from past experience? For example if the source frame > has focus redirected to a MB-only frame, and s-f-s-i-focus is called, > should that redirected focus be undone? I'm a little confused about the > difference between a MB-only frame being the selected frame, and it being > the focus frame of a different "normal" frame. I already had to revert one change because I got that wrong. It's again mostly about using 'focus-follows-mouse' t with a standalone minibuffer frame and I've never been able to understand how people manage that when the normal frame practically completely covers the minibuffer frame. But sooner or later this point will get moot because with Wayland people will be no more able to put a standalone minibuffer frame wherever they want to. >> We're right in hell's kitchen here. > > I've actually implemented the above, on a trial basis. It was actually > surprisingly easy. The C-g bit was just 9 extra lines (plus comments) in > internal_catch (eval.c), and another small function in minibuf.c. > > You say "hell's kitchen". Again, is this on general principles, Just gut feeling. > or can > you see some specific problems which might crop up? My feeling is that > this way of dealing with "not the innermost minibuffer" is simple, and > possibly as good as we're going to get. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-07 18:26 ` martin rudalics @ 2021-01-10 0:53 ` Alan Mackenzie 2021-01-10 1:34 ` Stefan Monnier 2021-01-10 16:04 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-10 0:53 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 5982 bytes --] Hello, Martin. On Thu, Jan 07, 2021 at 19:26:02 +0100, martin rudalics wrote: > > Actualy, I don't think it's as ugly as all that. > > select-frame-set-input-focus is an extremely coherent function; it does > > one thing and does it well, i.e. switch frames. Maybe it's a pity that > > it's a Lisp function rather than a C function, but it is as it is. > It does call 'raise-frame' something people with 'focus-follows-mouse' t > (not 'auto-raise') might not want here. And it may have to move the > mouse cursor according to the 'mouse-autoselect-window' and > 'focus-follows-mouse' policies. Most window managers don't handle the > latter well (IIRC only the otherwise abominable mutter and KDE could do > it approximately). And the former wants the selected window to be set > up well before calling 'select-frame-set-input-focus'. It is nevertheless the standard function to call to move focus to a different frame. If there are problems with focus-follows-mouse, they should surely be fixed in frame.el as a separate problem. > > I've been trying to think through the bit about redirect-frame-focus. > > It's difficult. Are you aware of any particular scenarios where it might > > go wrong with select-frame-set-input-focus? Or is it more a general > > concern, possibly from past experience? For example if the source frame > > has focus redirected to a MB-only frame, and s-f-s-i-focus is called, > > should that redirected focus be undone? I'm a little confused about the > > difference between a MB-only frame being the selected frame, and it being > > the focus frame of a different "normal" frame. > I already had to revert one change because I got that wrong. It's again > mostly about using 'focus-follows-mouse' t with a standalone minibuffer > frame and I've never been able to understand how people manage that when > the normal frame practically completely covers the minibuffer frame. > But sooner or later this point will get moot because with Wayland people > will be no more able to put a standalone minibuffer frame wherever they > want to. OK. My gut feeling is just to ignore this potential problem at the moment. > >> We're right in hell's kitchen here. > > I've actually implemented the above, on a trial basis. It was actually > > surprisingly easy. The C-g bit was just 9 extra lines (plus comments) in > > internal_catch (eval.c), and another small function in minibuf.c. > > You say "hell's kitchen". Again, is this on general principles, > Just gut feeling. OK. I can understand that. ;-) > > or can > > you see some specific problems which might crop up? My feeling is that > > this way of dealing with "not the innermost minibuffer" is simple, and > > possibly as good as we're going to get. My intention at the moment is to commit my current state of work. I'm attaching the patch. Here's a commit message to go with it: Fix incompleteness in the implementation of minibuffer-follows-selected-frame In particular, add a new value to the variable, and fix several bugs apparent with the implementation up till now. * doc/emacs/mini.texi (Basic Minibuffer): Add a description of the new non-nil, non-t value of minibuffer-follows-selected-frame. * doc/emacs/trouble.texi (Quitting): Add a description of how C-g handles recursive minibuffers when typed in one which isn't the most nested. * doc/lispref/minibuf.texi (Intro to Minibuffers): Add an @dfn for "active minibuffer". (Minibuffer Commands): Document that exit-minibuffer throws an error when not invoked from the innermost Minibuffer. (Recursive Mini): Amend the description of the visibility of outer level minibuffers. (Minibuffer Misc): In the description of the minibuffer hooks, replace "the minibuffer" with "a minibuffer". * etc/NEWS (Entry announcing minibuffer-follows-selected-frame): Add a description of the new non-nil, non-t value. * lisp/cus-start.el (top level): make the customize entry for minibuffer-follows-selected-frame a choice between three entries. * lisp/minibuffer.el (exit-minibuffer): throw an error when we're not in the most nested minibuffer. * lisp/window.el (window-deletable-p): return the symbol `frame' when (amongst other things) minibuffer-follows-selected-frame is t. * src/eval.c (internal_catch): Add a mechanism to (throw 'exit t) repeatedly when the throw currently being processed doesn't terminate the current minibuffer. * src/lisp.h (this_minibuffer_depth): New extern declaration (minibuf_level): extern declaration moved here from window.h. * src/minibuf.c (minibuffer_follows_frame, minibuf_stays_put) (minibuf_moves_frame_when_opened): New and amended functions to query the value of minibuffer-follows-selected-frame. (choose_minibuf_frame): check (minibuf > 1) in place of (minibufer > 0) at a particular place. At another place, check that an alleged frame is so and is live. Before selecting a non-miniwindow on a different frame, ensure it really is a different frame. (move_minibuffer_onto_frame): Stack up all recursive minibuffers on the target frame. Check the minibuf_window isn't in the old frame before setting that frame's miniwindow to an inactive minibuffer. (Finnermost_minibuffer_p): New primitive. (this_minibuffer_depth): New function. (read_minibuf): Record the calling frame in a variable, and switch back to it after the recursive edit has terminated normally, using select-frame-set-input-focus. Stack up all the recursive minibuffers on the miniwindow where a new minibuffer is being opened. After the recursive edit, switch the selected window away from the expired minibuffer's window. (nth_minibuffer): New function. (minibuffer-follows-selected-frame): Change from a DEFVAR_BOOL to a DEFVAR_LISP. * src/window.c (decode_next_window_args): Set *minibuf to w's mini-window's content when that content is a minibuffer. * src/window.h (minibuf_level) Declaration moved from here to lisp.h. > martin -- Alan Mackenzie (Nuremberg, Germany). [-- Attachment #2: diff.20210109c.diff --] [-- Type: text/plain, Size: 21714 bytes --] diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index c7c8fb30ac..f81e64bdf9 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -76,9 +76,13 @@ Basic Minibuffer the user option @code{minibuffer-follows-selected-frame} to @code{nil}, then the minibuffer stays in the frame where you opened it, and you must switch back to that frame in order to complete (or -abort) the current command. Note that the effect of the command, when -you finally finish using the minibuffer, always takes place in the -frame where you first opened it. +abort) the current command. If you set that option to a value which +is neither @code{nil} nor @code{t}, the minibuffer moves frame only +after a recursive minibuffer has been opened in the current command +(@pxref{Recursive Mini,,, elisp}). This option is mainly to retain +(approximately) the behavior prior to Emacs 28.1. Note that the +effect of the command, when you finally finish using the minibuffer, +always takes place in the frame where you first opened it. @node Minibuffer File @section Minibuffers for File Names diff --git a/doc/emacs/trouble.texi b/doc/emacs/trouble.texi index 4da3d4a3e8..13f03814fd 100644 --- a/doc/emacs/trouble.texi +++ b/doc/emacs/trouble.texi @@ -57,6 +57,13 @@ Quitting successive @kbd{C-g} characters to get out of a search. @xref{Incremental Search}, for details. + If you type @kbd{C-g} in a minibuffer, this quits the command that +opened that minibuffer, closing it. If that minibuffer is not the +most recently opened one (which can happen when +@code{minibuffer-follows-selected-frame} is @code{nil} (@pxref{Basic +Minibuffer})), @kbd{C-g} closes the more recently opened ones, +quitting their associated commands. + On MS-DOS, the character @kbd{C-@key{Break}} serves as a quit character like @kbd{C-g}. The reason is that it is not feasible, on MS-DOS, to recognize @kbd{C-g} while a command is running, between interactions diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index f0036f0ccf..da7c22eb9c 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -82,10 +82,12 @@ Intro to Minibuffers incrementing the number at the end of the name. (The names begin with a space so that they won't show up in normal buffer lists.) Of several recursive minibuffers, the innermost (or most recently -entered) is the active minibuffer. We usually call this @emph{the} -minibuffer. You can permit or forbid recursive minibuffers by setting -the variable @code{enable-recursive-minibuffers}, or by putting -properties of that name on command symbols (@xref{Recursive Mini}.) +entered) is the @dfn{active minibuffer}--it is the one you can +terminate by typing @key{RET} (@code{exit-minibuffer}) in. We usually +call this @emph{the} minibuffer. You can permit or forbid recursive +minibuffers by setting the variable +@code{enable-recursive-minibuffers}, or by putting properties of that +name on command symbols (@xref{Recursive Mini}.) Like other buffers, a minibuffer uses a local keymap (@pxref{Keymaps}) to specify special key bindings. The function that @@ -348,7 +350,7 @@ Text from Minibuffer @item @key{RET} @code{exit-minibuffer} -@item @kbd{M-<} +@item @key{M-<} @code{minibuffer-beginning-of-buffer} @item @kbd{C-g} @@ -2380,7 +2382,8 @@ Minibuffer Commands @deffn Command exit-minibuffer This command exits the active minibuffer. It is normally bound to -keys in minibuffer local keymaps. +keys in minibuffer local keymaps. The command throws an error if the +current buffer is not the active minibuffer. @end deffn @deffn Command self-insert-and-exit @@ -2594,8 +2597,11 @@ Recursive Mini If this variable is non-@code{nil}, you can invoke commands (such as @code{find-file}) that use minibuffers even while the minibuffer is active. Such invocation produces a recursive editing level for a new -minibuffer. The outer-level minibuffer is invisible while you are -editing the inner one. +minibuffer. By default, the outer-level minibuffer is invisible while +you are editing the inner one. If you have +@code{minibuffer-follows-selected-frame} set to @code{nil}, you can +have minibuffers visible on several frames at the same time. +@xref{Basic Minibuffer,,, emacs}. If this variable is @code{nil}, you cannot invoke minibuffer commands when the minibuffer is active, not even if you switch to another window @@ -2623,7 +2629,7 @@ Minibuffer Misc @end defun @defvar minibuffer-setup-hook -This is a normal hook that is run whenever the minibuffer is entered. +This is a normal hook that is run whenever a minibuffer is entered. @xref{Hooks}. @end defvar @@ -2641,7 +2647,7 @@ Minibuffer Misc @end defmac @defvar minibuffer-exit-hook -This is a normal hook that is run whenever the minibuffer is exited. +This is a normal hook that is run whenever a minibuffer is exited. @xref{Hooks}. @end defvar diff --git a/etc/NEWS b/etc/NEWS index eaaf9bfb0e..8c0c809231 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,12 +102,13 @@ effect should be negligible in the vast majority of cases anyway. By default, when you switch to another frame, an active minibuffer now moves to the newly selected frame. Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer -was first activated, even if it moved to another frame. An -alternative behavior is available by customizing -'minibuffer-follows-selected-frame' to nil. Here, the minibuffer -stays in the frame where you first opened it, and you must switch back -to this frame to continue or abort its command. The old, somewhat -unsystematic behavior, which mixed these two is no longer available. +was first activated. An alternative behavior is available by +customizing 'minibuffer-follows-selected-frame' to nil. Here, the +minibuffer stays in the frame where you first opened it, and you must +switch back to this frame to continue or abort its command. The old +behavior, which mixed these two, can be approximated by customizing +'minibuffer-follows-selected-frame' to a value which is neither nil +nor t. +++ ** New system for displaying documentation for groups of functions. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 85dd14f628..0293d34d1c 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -394,7 +394,11 @@ minibuffer-prompt-properties--setter ;; (directory :format "%v")))) (load-prefer-newer lisp boolean "24.4") ;; minibuf.c - (minibuffer-follows-selected-frame minibuffer boolean "28.1") + (minibuffer-follows-selected-frame + minibuffer (choice (const :tag "Always" t) + (const :tag "When used" hybrid) + (const :tag "Never" nil)) + "28.1") (enable-recursive-minibuffers minibuffer boolean) (history-length minibuffer (choice (const :tag "Infinite" t) integer) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 556f5d3a56..cbdd4a90f1 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2125,8 +2125,10 @@ exit-minibuffer ;; A better solution would be to make deactivate-mark buffer-local ;; (or to turn it into a list of buffers, ...), but in the mean time, ;; this should do the trick in most cases. - (setq deactivate-mark nil) - (throw 'exit nil)) + (when (innermost-minibuffer-p) + (setq deactivate-mark nil) + (throw 'exit nil)) + (error "%s" "Not in most nested minibuffer")) (defun self-insert-and-exit () "Terminate minibuffer input." diff --git a/lisp/window.el b/lisp/window.el index 38be778906..a6cdd4dec2 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4116,7 +4116,10 @@ window-deletable-p frame)) (throw 'other t)))) (let ((minibuf (active-minibuffer-window))) - (and minibuf (eq frame (window-frame minibuf))))) + (and minibuf (eq frame (window-frame minibuf)) + (not (eq (default-toplevel-value + minibuffer-follows-selected-frame) + t))))) 'frame)) ((window-minibuffer-p window) ;; If WINDOW is the minibuffer window of a non-minibuffer-only diff --git a/src/eval.c b/src/eval.c index 706aafdf50..5bf3faebc8 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1167,9 +1167,18 @@ Lisp_Object internal_catch (Lisp_Object tag, Lisp_Object (*func) (Lisp_Object), Lisp_Object arg) { + /* MINIBUFFER_QUIT_LEVEL is to handle quitting from nested minibuffers by + throwing t to tag `exit'. + Value -1 means there is no (throw 'exit t) in progress; + 0 means the `throw' wasn't done from an active minibuffer; + N > 0 means the `throw' was done from the minibuffer at level N. */ + static EMACS_INT minibuffer_quit_level = -1; /* This structure is made part of the chain `catchlist'. */ struct handler *c = push_handler (tag, CATCHER); + if (EQ (tag, Qexit)) + minibuffer_quit_level = -1; + /* Call FUNC. */ if (! sys_setjmp (c->jmp)) { @@ -1183,6 +1192,23 @@ internal_catch (Lisp_Object tag, Lisp_Object val = handlerlist->val; clobbered_eassert (handlerlist == c); handlerlist = handlerlist->next; + if (EQ (tag, Qexit) && EQ (val, Qt)) + /* If we've thrown t to tag `exit' from within a minibuffer, we + exit all minibuffers more deeply nested than the current + one. */ + { + EMACS_INT mini_depth = this_minibuffer_depth (Qnil); + if (mini_depth && mini_depth != minibuffer_quit_level) + { + if (minibuffer_quit_level == -1) + minibuffer_quit_level = mini_depth; + if (minibuffer_quit_level + && (minibuf_level > minibuffer_quit_level)) + Fthrow (Qexit, Qt); + } + else + minibuffer_quit_level = -1; + } return val; } } diff --git a/src/lisp.h b/src/lisp.h index d139df9342..86be25852a 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4346,6 +4346,8 @@ extern Lisp_Object Vminibuffer_list; extern Lisp_Object last_minibuf_string; extern void move_minibuffer_onto_frame (void); extern bool is_minibuffer (EMACS_INT, Lisp_Object); +extern EMACS_INT this_minibuffer_depth (Lisp_Object); +extern EMACS_INT minibuf_level; extern Lisp_Object get_minibuffer (EMACS_INT); extern void init_minibuf_once (void); extern void syms_of_minibuf (void); diff --git a/src/minibuf.c b/src/minibuf.c index 5ee440f662..758377532e 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -63,9 +63,30 @@ static Lisp_Object minibuf_prompt; static ptrdiff_t minibuf_prompt_width; +static Lisp_Object nth_minibuffer (EMACS_INT depth); + \f +/* Return TRUE when a frame switch causes a minibuffer on the old + frame to move onto the new one. */ static bool minibuf_follows_frame (void) +{ + return EQ (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame), + Qt); +} + +/* Return TRUE when a minibuffer always remains on the frame where it + was first invoked. */ +static bool +minibuf_stays_put (void) +{ + return NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); +} + +/* Return TRUE when opening a (recursive) minibuffer causes + minibuffers on other frames to move to the selected frame. */ +static bool +minibuf_moves_frame_when_opened (void) { return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame)); } @@ -90,7 +111,7 @@ choose_minibuf_frame (void) minibuf_window = sf->minibuffer_window; /* If we've still got another minibuffer open, use its mini-window instead. */ - if (minibuf_level && !minibuf_follows_frame ()) + if (minibuf_level > 1 && minibuf_stays_put ()) { Lisp_Object buffer = get_minibuffer (minibuf_level); Lisp_Object tail, frame; @@ -105,26 +126,40 @@ choose_minibuf_frame (void) } } - if (minibuf_follows_frame ()) + if (minibuf_moves_frame_when_opened () + && FRAMEP (selected_frame) + && FRAME_LIVE_P (XFRAME (selected_frame))) /* Make sure no other frame has a minibuffer as its selected window, because the text would not be displayed in it, and that would be confusing. Only allow the selected frame to do this, and that only if the minibuffer is active. */ - { - Lisp_Object tail, frame; - - FOR_EACH_FRAME (tail, frame) - if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame)))) - && !(EQ (frame, selected_frame) - && minibuf_level > 0)) - Fset_frame_selected_window (frame, Fframe_first_window (frame), - Qnil); - } + { + Lisp_Object tail, frame; + struct frame *of; + + FOR_EACH_FRAME (tail, frame) + if (!EQ (frame, selected_frame) + && minibuf_level > 1 + /* The frame's minibuffer can be on a different frame. */ + && XWINDOW ((of = XFRAME (frame))->minibuffer_window)->frame + != selected_frame) + { + if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (of)))) + Fset_frame_selected_window (frame, Fframe_first_window (frame), + Qnil); + + if (!EQ (XWINDOW (of->minibuffer_window)->contents, + nth_minibuffer (0))) + set_window_buffer (of->minibuffer_window, + nth_minibuffer (0), 0, 0); + } + } } -/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it - from its current frame to the selected frame. This function is - intended to be called from `do_switch_frame' in frame.c. */ +/* If `minibuffer_follows_selected_frame' is t and we have a + minibuffer, move it from its current frame to the selected frame. + This function is intended to be called from `do_switch_frame' in + frame.c. */ void move_minibuffer_onto_frame (void) { if (!minibuf_level) @@ -135,14 +170,18 @@ void move_minibuffer_onto_frame (void) && FRAME_LIVE_P (XFRAME (selected_frame)) && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window)) { + EMACS_INT i; struct frame *sf = XFRAME (selected_frame); Lisp_Object old_frame = XWINDOW (minibuf_window)->frame; struct frame *of = XFRAME (old_frame); - Lisp_Object buffer = XWINDOW (minibuf_window)->contents; - set_window_buffer (sf->minibuffer_window, buffer, 0, 0); + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i <= minibuf_level; i++) + set_window_buffer (sf->minibuffer_window, nth_minibuffer (i), 0, 0); minibuf_window = sf->minibuffer_window; - set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); + if (of != sf) + set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0); } } @@ -336,6 +375,39 @@ return t only if BUFFER is an active minibuffer. */) ? Qt : Qnil; } +DEFUN ("innermost-minibuffer-p", Finnermost_minibuffer_p, + Sinnermost_minibuffer_p, 0, 1, 0, + doc: /* Return t if BUFFER is the most nested active minibuffer. +No argument or nil as argument means use the current buffer as BUFFER. */) + (Lisp_Object buffer) +{ + if (NILP (buffer)) + buffer = Fcurrent_buffer (); + return EQ (buffer, (Fcar (Fnthcdr (make_fixnum (minibuf_level), + Vminibuffer_list)))) + ? Qt + : Qnil; +} + +/* Return the nesting depth of the active minibuffer BUFFER, or 0 if + BUFFER isn't such a thing. If BUFFER is nil, this means use the current + buffer. */ +EMACS_INT +this_minibuffer_depth (Lisp_Object buffer) +{ + EMACS_INT i; + Lisp_Object bufs; + + if (NILP (buffer)) + buffer = Fcurrent_buffer (); + for (i = 1, bufs = Fcdr (Vminibuffer_list); + i <= minibuf_level; + i++, bufs = Fcdr (bufs)) + if (EQ (Fcar (bufs), buffer)) + return i; + return 0; +} + DEFUN ("minibuffer-prompt-end", Fminibuffer_prompt_end, Sminibuffer_prompt_end, 0, 0, 0, doc: /* Return the buffer position of the end of the minibuffer prompt. @@ -411,6 +483,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, Lisp_Object val; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object mini_frame, ambient_dir, minibuffer, input_method; + Lisp_Object calling_frame = selected_frame; Lisp_Object enable_multibyte; EMACS_INT pos = 0; /* String to add to the history. */ @@ -648,6 +721,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, } } + if (minibuf_moves_frame_when_opened ()) + { + EMACS_INT i; + + /* Stack up all the (recursively) open minibuffers on the selected + mini_window. */ + for (i = 1; i < minibuf_level; i++) + set_window_buffer (XFRAME (mini_frame)->minibuffer_window, + nth_minibuffer (i), 0, 0); + } + /* Display this minibuffer in the proper window. */ /* Use set_window_buffer instead of Fset_window_buffer (see discussion of bug#11984, bug#12025, bug#12026). */ @@ -729,6 +813,20 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, recursive_edit_1 (); + /* We've exited the recursive edit without an error, so switch the + current window away from the expired minibuffer window. */ + { + Lisp_Object prev = Fprevious_window (minibuf_window, Qnil, Qnil); + /* PREV can be on a different frame when we have a minibuffer only + frame, the other frame's minibuffer window is MINIBUF_WINDOW, + and its "focus window" is also MINIBUF_WINDOW. */ + while (!EQ (prev, minibuf_window) + && !EQ (selected_frame, WINDOW_FRAME (XWINDOW (prev)))) + prev = Fprevious_window (prev, Qnil, Qnil); + if (!EQ (prev, minibuf_window)) + Fset_frame_selected_window (selected_frame, prev, Qnil); + } + /* If cursor is on the minibuffer line, show the user we have exited by putting it in column 0. */ if (XWINDOW (minibuf_window)->cursor.vpos >= 0 @@ -767,6 +865,12 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, in set-window-configuration. */ unbind_to (count, Qnil); + /* Switch the frame back to the calling frame. */ + if (!EQ (selected_frame, calling_frame) + && FRAMEP (calling_frame) + && FRAME_LIVE_P (XFRAME (calling_frame))) + call2 (intern ("select-frame-set-input-focus"), calling_frame, Qnil); + /* Add the value to the appropriate history list, if any. This is done after the previous buffer has been made current again, in case the history variable is buffer-local. */ @@ -790,6 +894,14 @@ is_minibuffer (EMACS_INT depth, Lisp_Object buf) && EQ (Fcar (tail), buf); } +/* Return the DEPTHth minibuffer, or nil if such does not yet exist. */ +static Lisp_Object +nth_minibuffer (EMACS_INT depth) +{ + Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list); + return XCAR (tail); +} + /* Return a buffer to be used as the minibuffer at depth `depth'. depth = 0 is the lowest allowed argument, and that is the value used for nonrecursive minibuffer invocations. */ @@ -2032,13 +2144,15 @@ For example, `eval-expression' uses this. */); The function is called with the arguments passed to `read-buffer'. */); Vread_buffer_function = Qnil; - DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, - doc: /* Non-nil means the active minibuffer always displays on the selected frame. + DEFVAR_LISP ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame, + doc: /* t means the active minibuffer always displays on the selected frame. Nil means that a minibuffer will appear only in the frame which created it. +Any other value means the minibuffer will move onto another frame, but +only when the user starts using a minibuffer there. Any buffer local or dynamic binding of this variable is ignored. Only the default top level value is used. */); - minibuffer_follows_selected_frame = 1; + minibuffer_follows_selected_frame = Qt; DEFVAR_BOOL ("read-buffer-completion-ignore-case", read_buffer_completion_ignore_case, @@ -2196,6 +2310,7 @@ uses to hide passwords. */); defsubr (&Sminibuffer_prompt); defsubr (&Sminibufferp); + defsubr (&Sinnermost_minibuffer_p); defsubr (&Sminibuffer_prompt_end); defsubr (&Sminibuffer_contents); defsubr (&Sminibuffer_contents_no_properties); diff --git a/src/window.c b/src/window.c index 5e78aa400b..fe3a93f7ac 100644 --- a/src/window.c +++ b/src/window.c @@ -2663,12 +2663,15 @@ static void decode_next_window_args (Lisp_Object *window, Lisp_Object *minibuf, Lisp_Object *all_frames) { struct window *w = decode_live_window (*window); + Lisp_Object miniwin = XFRAME (w->frame)->minibuffer_window; XSETWINDOW (*window, w); /* MINIBUF nil may or may not include minibuffers. Decide if it does. */ if (NILP (*minibuf)) - *minibuf = minibuf_level ? minibuf_window : Qlambda; + *minibuf = this_minibuffer_depth (XWINDOW (miniwin)->contents) + ? miniwin + : Qlambda; else if (!EQ (*minibuf, Qt)) *minibuf = Qlambda; diff --git a/src/window.h b/src/window.h index 332cb3091f..79eb44e7a3 100644 --- a/src/window.h +++ b/src/window.h @@ -1124,10 +1124,6 @@ extern Lisp_Object echo_area_window; extern EMACS_INT command_loop_level; -/* Depth in minibuffer invocations. */ - -extern EMACS_INT minibuf_level; - /* Non-zero if we should redraw the mode lines on the next redisplay. Usually set to a unique small integer so we can track the main causes of full redisplays in `redisplay--mode-lines-cause'. */ ^ permalink raw reply related [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 0:53 ` Alan Mackenzie @ 2021-01-10 1:34 ` Stefan Monnier 2021-01-10 16:03 ` Alan Mackenzie 2021-01-10 16:04 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2021-01-10 1:34 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Gregory Heytings, Eli Zaretskii > It is nevertheless the standard function to call to move focus to a > different frame. Yes, but since setting focus is problematic in general (because of the many different ways it can be handled depending on the GUI and window manager and age of the captain), we usually try to refrain from doing it at all: it's usually a source of problems even if it looks like a solution in the short term. IIRC `raise-frame` is often a good middle ground which gets most of the benefit of `select-frame-set-input-focus` with much fewer problems. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 1:34 ` Stefan Monnier @ 2021-01-10 16:03 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-10 16:03 UTC (permalink / raw) To: Stefan Monnier Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Gregory Heytings, Eli Zaretskii Hello, Stefan. On Sat, Jan 09, 2021 at 20:34:12 -0500, Stefan Monnier wrote: > > It is nevertheless the standard function to call to move focus to a > > different frame. > Yes, but since setting focus is problematic in general (because of the > many different ways it can be handled depending on the GUI and window > manager and age of the captain), we usually try to refrain from doing it > at all: it's usually a source of problems even if it looks like > a solution in the short term. In this case, selecting the frame is the essential part of the process. It happens after a minibuffer is exited, and selects the frame in which the minibuffer was opened, the one in which the coming action is to take place. > IIRC `raise-frame` is often a good middle ground which gets most of the > benefit of `select-frame-set-input-focus` with much fewer problems. > Stefan -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 0:53 ` Alan Mackenzie 2021-01-10 1:34 ` Stefan Monnier @ 2021-01-10 16:04 ` martin rudalics 2021-01-10 17:18 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-10 16:04 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii >> It does call 'raise-frame' something people with 'focus-follows-mouse' t >> (not 'auto-raise') might not want here. And it may have to move the >> mouse cursor according to the 'mouse-autoselect-window' and >> 'focus-follows-mouse' policies. Most window managers don't handle the >> latter well (IIRC only the otherwise abominable mutter and KDE could do >> it approximately). And the former wants the selected window to be set >> up well before calling 'select-frame-set-input-focus'. > > It is nevertheless the standard function to call to move focus to a > different frame. If there are problems with focus-follows-mouse, they > should surely be fixed in frame.el as a separate problem. The 'focus-follows-mouse' problem is a window manager problem and cannot be fixed in frame.el. It typically happens when he mouse pointer is (i) over frame-1, one (ii) gives focus to frame-2 and (iii) moves the mouse which causes the WM to refocus frame-1. Some WMs here move the mouse pointer in (ii) to frame-2 (and forcefully do that for pop up windows) which can be however a pain for Emacs because the WM doesn't know where in frame-2 to move it to and so with 'mouse-autoselect-window' a wrong window may get auto-selected. And please remain aware of the fact that focus stealing is, in general, considered bad practice. It shouldn't happen in the case at hand because _some_ Emacs frame should already have focus when invoking 'select-frame-set-input-focus'. Still, a window manager's local policy settings might make it refrain from giving focus to some frame on behalf of our request. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 16:04 ` martin rudalics @ 2021-01-10 17:18 ` Alan Mackenzie 2021-01-10 17:30 ` Stefan Monnier 2021-01-10 17:49 ` martin rudalics 0 siblings, 2 replies; 252+ messages in thread From: Alan Mackenzie @ 2021-01-10 17:18 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin. On Sun, Jan 10, 2021 at 17:04:57 +0100, martin rudalics wrote: > >> It does call 'raise-frame' something people with 'focus-follows-mouse' t > >> (not 'auto-raise') might not want here. And it may have to move the > >> mouse cursor according to the 'mouse-autoselect-window' and > >> 'focus-follows-mouse' policies. Most window managers don't handle the > >> latter well (IIRC only the otherwise abominable mutter and KDE could do > >> it approximately). And the former wants the selected window to be set > >> up well before calling 'select-frame-set-input-focus'. > > It is nevertheless the standard function to call to move focus to a > > different frame. If there are problems with focus-follows-mouse, they > > should surely be fixed in frame.el as a separate problem. > The 'focus-follows-mouse' problem is a window manager problem and cannot > be fixed in frame.el. It typically happens when he mouse pointer is (i) > over frame-1, one (ii) gives focus to frame-2 and (iii) moves the mouse > which causes the WM to refocus frame-1. Some WMs here move the mouse > pointer in (ii) to frame-2 (and forcefully do that for pop up windows) > which can be however a pain for Emacs because the WM doesn't know where > in frame-2 to move it to and so with 'mouse-autoselect-window' a wrong > window may get auto-selected. > And please remain aware of the fact that focus stealing is, in general, > considered bad practice. It shouldn't happen in the case at hand > because _some_ Emacs frame should already have focus when invoking > 'select-frame-set-input-focus'. Still, a window manager's local policy > settings might make it refrain from giving focus to some frame on behalf > of our request. The need to set the focus is as follows. Suppose we replace the select-frame-set-input-focus with the select-frame (actually, do_switch_frame) that was previously there. With minibuffer-follows-selected-frame t (the default), enable-recursive-minibuffers t: Three frames F1, F2, F3: On F1, C-x C-f foo.el. Move to F2: C-x C-f bar.el. Move to F3 C-x C-f baz.el RET. baz.el is visited in F3. The minibuffers remain in F3. Move point to *Minibuf-2* in F3, and type RET. bar.el is visited in F2, BUT THE FOCUS REMAINS IN F3. This is suboptimal. If we visit a file in a frame, the focus should be on that frame, not somewhere else. As an analogy, C-x 5 f foo.el visits foo.el on another (typically new) frame and MOVES THE FOCUS TO THAT FRAME. Why shouldn't that be done in the current scenario? > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 17:18 ` Alan Mackenzie @ 2021-01-10 17:30 ` Stefan Monnier 2021-01-10 17:49 ` martin rudalics 1 sibling, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2021-01-10 17:30 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, martin rudalics, enometh, Gregory Heytings, Eli Zaretskii > On F1, C-x C-f foo.el. Move to F2: C-x C-f bar.el. Move to F3 C-x C-f > baz.el RET. > > baz.el is visited in F3. The minibuffers remain in F3. Move point to > *Minibuf-2* in F3, and type RET. bar.el is visited in F2, BUT THE > FOCUS REMAINS IN F3. This is suboptimal. It depends on many aspects. I don't expect `C-x C-f` to affect focus, so this "suboptimal" looks OK to me (with "me" wearing my cap of Emacs user using a WM where the focus follows the mouse). It would also be OK (and probably better) to raise the frame F2, since it might be (partly) hidden by F3. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 17:18 ` Alan Mackenzie 2021-01-10 17:30 ` Stefan Monnier @ 2021-01-10 17:49 ` martin rudalics 2021-01-10 18:25 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2021-01-10 17:49 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > Three frames F1, F2, F3: > > On F1, C-x C-f foo.el. Move to F2: C-x C-f bar.el. Move to F3 C-x C-f > baz.el RET. > > baz.el is visited in F3. The minibuffers remain in F3. Move point to > *Minibuf-2* in F3, and type RET. bar.el is visited in F2, BUT THE > FOCUS REMAINS IN F3. This is suboptimal. If we visit a file in a > frame, the focus should be on that frame, not somewhere else. As an > analogy, C-x 5 f foo.el visits foo.el on another (typically new) frame > and MOVES THE FOCUS TO THAT FRAME. This "MOVES THE FOCUS TO THAT FRAME" is done by the WM. Emacs doesn't care normally because it usually can't fight it anyway (you can try by setting the ‘no-focus-on-map’ frame parameter but it's no general cure). With practically all WMs, a new window always gets focus automatically. > Why shouldn't that be done in the > current scenario? Because we are talking about a frame (F2) that already exists in your scenario. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 17:49 ` martin rudalics @ 2021-01-10 18:25 ` Alan Mackenzie 2021-01-10 19:05 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-01-10 18:25 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin. On Sun, Jan 10, 2021 at 18:49:03 +0100, martin rudalics wrote: > > Three frames F1, F2, F3: > > On F1, C-x C-f foo.el. Move to F2: C-x C-f bar.el. Move to F3 C-x C-f > > baz.el RET. > > baz.el is visited in F3. The minibuffers remain in F3. Move point to > > *Minibuf-2* in F3, and type RET. bar.el is visited in F2, BUT THE > > FOCUS REMAINS IN F3. This is suboptimal. If we visit a file in a > > frame, the focus should be on that frame, not somewhere else. As an > > analogy, C-x 5 f foo.el visits foo.el on another (typically new) frame > > and MOVES THE FOCUS TO THAT FRAME. > This "MOVES THE FOCUS TO THAT FRAME" is done by the WM. Emacs doesn't > care normally because it usually can't fight it anyway (you can try by > setting the ‘no-focus-on-map’ frame parameter but it's no general cure). > With practically all WMs, a new window always gets focus automatically. > > Why shouldn't that be done in the current scenario? > Because we are talking about a frame (F2) that already exists in your > scenario. Sorry, that answer doesn't fit the question I though I was asking. What I meant was that it is surely a good thing that after visiting a file with C-x C-f, the focus ends up in the frame where the file's buffer is. Why does this cease to be a good thing if the frame already exists? Note other scenarios where the frame already exists, such as C-x 5 b foo.el RET when foo.el is already displayed in another frame. The focus is moved to that other frame. Whether that is done by Emacs or the WM is immaterial, the focus MUST be moved, otherwise there'd be no point to doing the C-x 5 b. Similarly with a "deferred" C-x C-f, the focus must also be moved. Surely? I'll need to go back a bit to try and understand the arguments against shifting the focus. Right at the moment, I'm not seeing them. > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-10 18:25 ` Alan Mackenzie @ 2021-01-10 19:05 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2021-01-10 19:05 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > What I meant was that it is surely a good thing that after visiting a > file with C-x C-f, the focus ends up in the frame where the file's > buffer is. Why does this cease to be a good thing if the frame already > exists? We do call `select-frame-set-input-focus' in `pop-to-buffer' when the buffer is on another frame and obviously in C-x 5 o. These are probably its most prominent clients. > Note other scenarios where the frame already exists, such as C-x 5 b > foo.el RET when foo.el is already displayed in another frame. The focus > is moved to that other frame. Whether that is done by Emacs or the WM > is immaterial, the focus MUST be moved, otherwise there'd be no point to > doing the C-x 5 b. I talked about the WM because you mentioned showing a buffer in a "typically new" frame. > Similarly with a "deferred" C-x C-f, the focus must also be moved. > Surely? > > I'll need to go back a bit to try and understand the arguments against > shifting the focus. Right at the moment, I'm not seeing them. It's completely up to you whether to use it or not. But in my experience any code is better without dealing with frame focus. In particular when there's still a hanging visit for foo.el in F1. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-05 18:07 ` Alan Mackenzie 2021-01-05 18:53 ` martin rudalics @ 2021-01-06 0:14 ` Gregory Heytings via Emacs development discussions. 2021-01-06 0:48 ` Stefan Monnier 2021-01-07 13:27 ` Alan Mackenzie 1 sibling, 2 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2021-01-06 0:14 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > > I do still find his manner of expression difficult to deal with. > I apologized once, I will not do this again. I've read my previous mails to you again, and don't see anything wrong in what I said. >> It did not "turn out", I explained in detail that the behavior that >> Alan considered buggy was not at all buggy before he started working on >> this. > > I don't think you "explained" at all, and certainly not before I started > working on it - I initiated the discussion with a proposed patch so as > to minimise the risk of just wasting people's time with bikeshedding. > I did tell you that the behavior you found incoherent was not, and that this behavior was an old one dating back to Emacs 21 at least, three days before you initiated the discussion. That happened in the "New multi-command facility displays in the wrong echo area" thread. > > You keep referring to an "old behaviour" that I removed, as though there > were something coherent, something valuable, something worth keeping. I > don't think there's anything of the kind. I think the former behaviour > just happened by accident as a result of people working on other things, > and nobody consciously made it happen. I am now consciously trying to > fix it. If you've argued for an old behaviour on its merits, possibly > in the thread "stealing eachother's minibuffers", could you perhaps > point out the place in that thread, so that I can read it again. > The old behavior is indeed valuable, if only because it is an old behavior. Emacs' stability is important. I don't see why the burden of proof that a behavior about which virtually no Emacs user in the last twenty years complained is not a bad one would be on me. You believe that the old behavior is chaotic and happened by accident, but it is also possible that your belief is wrong. The old behavior is, at the very least, not chaotic, it is well defined: from a user's point of view, when a recursive minibuffer is entered in a frame F2 while one or more recursive minibuffers are active on a frame F1, these minibuffers are moved from frame F1 to frame F2 before the new minibuffer is created. Saying that this is not an "ad hoc unsystematic mess" is not expressing an opinion among other opinions. >> What could have been done instead is to add some new code next to the >> existing one to conditionally provide a new behavior, > > That could not have been done, due to the state of the code in > minibuf.c, in particular, due to the lack of any coherent "existing > behaviour". I looked at your 2ecbf4cfae7, c3edaa55249 and 6e469709c55 commits, and at your patch, and I don't see why the old code could not continue to exist next to the new one. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-06 0:14 ` Gregory Heytings via Emacs development discussions. @ 2021-01-06 0:48 ` Stefan Monnier 2021-01-06 9:40 ` Gregory Heytings via Emacs development discussions. 2021-01-07 13:27 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2021-01-06 0:48 UTC (permalink / raw) To: Gregory Heytings via Emacs development discussions. Cc: Gregory Heytings, Alan Mackenzie > The old behavior is indeed valuable, if only because it is an old behavior. > Emacs' stability is important. I don't see why the burden of proof that > a behavior about which virtually no Emacs user in the last twenty years > complained is not a bad one would be on me. M-x lisp-date-mode RET signaled an error in all Emacsen until now. Yet no user complained about it over all these years. Should we add a config var so users can get back the old behavior? Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-06 0:48 ` Stefan Monnier @ 2021-01-06 9:40 ` Gregory Heytings via Emacs development discussions. 2021-01-06 15:52 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2021-01-06 9:40 UTC (permalink / raw) To: Stefan Monnier; +Cc: Alan Mackenzie, emacs-devel >> The old behavior is indeed valuable, if only because it is an old >> behavior. Emacs' stability is important. I don't see why the burden of >> proof that a behavior about which virtually no Emacs user in the last >> twenty years complained is not a bad one would be on me. > > M-x lisp-date-mode RET > > signaled an error in all Emacsen until now. Yet no user complained about > it over all these years. Should we add a config var so users can get > back the old behavior? > I think you meant "lisp-data-mode". I'm not sure you are teasing me, so: (1) The "[No match]" error in earlier Emacsen is clear, there is no reason a user would have complained about it. (2) The likelihood that a user tries to type M-x lisp-data-mode RET when no such mode exists is very, very low compared to the likelihood that a user interacts with the minibuffer. Admittedly the scenario that is being discussed here is more precise than "interacting with the minibuffer", but I did experience the old behavior several times without ever thinking it was a bug, and found it perfectly coherent. And I never tried to type M-x lisp-data-mode RET. (3) The old behavior is in this case very easy to get back AFAICS: (fset 'lisp-data-mode nil). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-06 9:40 ` Gregory Heytings via Emacs development discussions. @ 2021-01-06 15:52 ` Stefan Monnier 2021-01-07 7:52 ` Richard Stallman 0 siblings, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2021-01-06 15:52 UTC (permalink / raw) To: Gregory Heytings; +Cc: Alan Mackenzie, emacs-devel > I think you meant "lisp-data-mode". Indeed. > I'm not sure you are teasing me, so: No, I'm just pointing out that even such an "obviously nothing but a new feature" implies a change in old behavior. The same applies to the most obvious "bug fixes" and to basically every single commit we ever apply. Even adding a config var to recover the old behavior is itself a change in old behavior. So your rule is simply inapplicable. You seem to think it's obvious to distinguish between a change in behavior and a bug fix or a new feature or other categories, and while in many cases it is, there are also many cases where it's not and *that* is the reason why we sometimes introduce a change without a clean way to recover the old behavior: we failed to recognize that it wasn't just a bug fix or a plain improvement. New rules won't help. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-06 15:52 ` Stefan Monnier @ 2021-01-07 7:52 ` Richard Stallman 2021-01-07 14:33 ` Eli Zaretskii 0 siblings, 1 reply; 252+ messages in thread From: Richard Stallman @ 2021-01-07 7:52 UTC (permalink / raw) To: Stefan Monnier; +Cc: ghe, acm, emacs-devel [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] > So your rule is simply inapplicable. You seem to think it's obvious to > distinguish between a change in behavior and a bug fix or a new feature > or other categories, and while in many cases it is, there are also many > cases where it's not and *that* is the reason why we sometimes introduce > a change without a clean way to recover the old behavior: we failed to > recognize that it wasn't just a bug fix or a plain improvement. Yes, that does happen. We can't avoid that. But, as the y-or-n-p problem shows, there are times when people make a change that is obviously intended to change the UI, and isn't fixing a bug. And yet we changed the default and did not add a variable to control the change. If we adopt a rule about these situations, we will do better. -- Dr Richard Stallman Chief GNUisance of the GNU Project (https://gnu.org) Founder, Free Software Foundation (https://fsf.org) Internet Hall-of-Famer (https://internethalloffame.org) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-07 7:52 ` Richard Stallman @ 2021-01-07 14:33 ` Eli Zaretskii 0 siblings, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2021-01-07 14:33 UTC (permalink / raw) To: rms; +Cc: ghe, acm, monnier, emacs-devel > From: Richard Stallman <rms@gnu.org> > Date: Thu, 07 Jan 2021 02:52:33 -0500 > Cc: ghe@sdf.org, acm@muc.de, emacs-devel@gnu.org > > But, as the y-or-n-p problem shows, there are times when people make a > change that is obviously intended to change the UI, and isn't fixing a > bug. That is incorrect: the change was made to fix annoying (a.k.a. "buggy") behavior, whereby some async message displayed while y-or-n-p was prompting the user would completely hide the prompt, and Emacs would be left in a state where it expects the user to type something, but doesn't say so. > If we adopt a rule about these situations, we will do better. It depends on the rules. The rules proposed until now will definitely cause us to do worse, which is unjustified given how rare these cases happen. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-06 0:14 ` Gregory Heytings via Emacs development discussions. 2021-01-06 0:48 ` Stefan Monnier @ 2021-01-07 13:27 ` Alan Mackenzie 2021-01-07 23:34 ` Gregory Heytings via Emacs development discussions. 1 sibling, 1 reply; 252+ messages in thread From: Alan Mackenzie @ 2021-01-07 13:27 UTC (permalink / raw) To: Gregory Heytings; +Cc: emacs-devel Hello, Gregory. On Wed, Jan 06, 2021 at 00:14:45 +0000, Gregory Heytings wrote: > > I do still find his manner of expression difficult to deal with. > I apologized once, I will not do this again. I've read my previous > mails to you again, and don't see anything wrong in what I said. Let me repeat, it is not the content of your posts I find difficult to deal with. It's their rudeness and aggressiveness. If you'd said what you had to say in a gentle and co-operative manner, as for example Martin does when disagreeing with me, I wouldn't now be asking myself if responding to your posts is worth it at all. > >> It did not "turn out", I explained in detail that the behavior that > >> Alan considered buggy was not at all buggy before he started working > >> on this. > > I don't think you "explained" at all, and certainly not before I started > > working on it - I initiated the discussion with a proposed patch so as > > to minimise the risk of just wasting people's time with bikeshedding. > I did tell you that the behavior you found incoherent was not, and that > this behavior was an old one dating back to Emacs 21 at least, three days > before you initiated the discussion. That happened in the "New > multi-command facility displays in the wrong echo area" thread. You "told" me, and seem to expect that I accept what you say without question, as though you were some sort of guru. On this particular point, you seem to be mistaken, or at the very least in a minority of one. > > You keep referring to an "old behaviour" that I removed, as though there > > were something coherent, something valuable, something worth keeping. I > > don't think there's anything of the kind. I think the former behaviour > > just happened by accident as a result of people working on other things, > > and nobody consciously made it happen. I am now consciously trying to > > fix it. If you've argued for an old behaviour on its merits, possibly > > in the thread "stealing eachother's minibuffers", could you perhaps > > point out the place in that thread, so that I can read it again. > The old behavior is indeed valuable, if only because it is an old > behavior. So you're defending old bugs as valuable, simply because they're old. You decline to defend this behaviour on its merits. Nobody else has done so either, as far as I'm aware. > Emacs' stability is important. I don't see why the burden of proof > that a behavior about which virtually no Emacs user in the last twenty > years complained is not a bad one would be on me. Ratchet your level of abstraction down a notch or two, please. We're talking about a particular bit of behaviour, which if somebody proposed as new on emacs-devel would get rejected out of hand, and indeed ridiculed. I was asking you to provide me with missing information, something which might persuade me that this behaviour was less chaotic and more systematic than it outwardly appears. Emacs's "stability", in the way you seem to mean it would prevent new developments and old bug fixing from happening. > You believe that the old behavior is chaotic and happened by accident, but > it is also possible that your belief is wrong. Not really. You're not telling me somebody sat down at his computer one day, and said "hey, it'd be a great idea if the minibuffer changed frames when and only when a recursive minibuffer were opened on another frame"? Be serious, such an abstruse "design" can only have arisen by accident. > The old behavior is, at the very least, not chaotic, it is well defined: > from a user's point of view, when a recursive minibuffer is entered in a > frame F2 while one or more recursive minibuffers are active on a frame F1, > these minibuffers are moved from frame F1 to frame F2 before the new > minibuffer is created. Saying that this is not an "ad hoc unsystematic > mess" is not expressing an opinion among other opinions. > >> What could have been done instead is to add some new code next to the > >> existing one to conditionally provide a new behavior, > > That could not have been done, due to the state of the code in > > minibuf.c, in particular, due to the lack of any coherent "existing > > behaviour". > I looked at your 2ecbf4cfae7, c3edaa55249 and 6e469709c55 commits, and > at your patch, and I don't see why the old code could not continue to > exist next to the new one. I am the person doing the work, not you. -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2021-01-07 13:27 ` Alan Mackenzie @ 2021-01-07 23:34 ` Gregory Heytings via Emacs development discussions. 0 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2021-01-07 23:34 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > > Let me repeat, it is not the content of your posts I find difficult to > deal with. It's their rudeness and aggressiveness. > I think we are miscommunicating. I honestly don't understand where you perceive "rudeness and aggressiveness" in my posts. I can only say again that this was not at all my intention. > >> The old behavior is indeed valuable, if only because it is an old >> behavior. > > So you're defending old bugs as valuable, simply because they're old. > You decline to defend this behaviour on its merits. Nobody else has > done so either, as far as I'm aware. > I did defend it on its merits, multiple times. What I said did not convince you; I should perhaps have written something more detailed, as I do below. I experienced the behavior which surprised you from time to time, and found it coherent with the general Emacs behavior. Let's first define that behavior again: from a user's point of view, in Emacs 21 (and possibly earlier) to 27, when minibuffers MB1 to MBn are active on a frame F1, and a command that activates the minibuffer is started on a frame F2, the minibuffers MB1 to MBn are moved to frame F2, and the minibuffer MBn+1 is created. This behavior happens with enable-recursive-minibuffers set to t, either globally or temporarily. The other possible behaviors are, AFAICS, the ones you try to implement: 1. Either tying each minibuffer to the frame in which it is created. If you tie each minibuffer to the frame in which it was created, there are two options: 1.1. Either it becomes possible to interact with them. From an UI point of view, the problem of that behavior is that it is not coherent with recursive edits in Emacs, of which recursive minibuffers are an instance. When you enter a recursive edit you cannot act on the non-recursive edit level or on the previous recursive edit levels anymore, you first have to leave the current recursive edit. As the Emacs manual explains, recursive edits "constrain you to go back in a particular order---from the innermost level toward the top level." IOW, that behavior would violate the general recursive editing constraint. 1.2. Or you make sure in one way or another that it is not possible to interact with them, which means that the user sees a minibuffer that it cannot use (for instance by clicking on it). From an UI point of view, this would be counter-intuitive; I don't know any other UI element that is displayed by Emacs and that you cannot interact with. 2. Or always moving the minibuffers to the currently active frame. From an UI point of view, the problem is that such a movement is, in most cases, gratuitious: when you start a command in a frame and switch to another frame, in most cases this switch is a temporary one (for example to check the documentation of the command you just started), and what you intend to do is to switch back to the frame in which you started the command to complete it. The old behavior, which you despise, is not "chaotic" (see the definition of that word in Wordnet: "completely unordered and unpredictable and confusing", or in Wiktionary: "extremely disorganized"), it is well-defined, and it is a reasonable solution which avoids (at least) these three problems. And it has worked well for virtually all Emacs users in the last twenty years. There is, in fact, a fourth possible behavior, which AFAICT has not been discussed in this thread: 1.3. Tying each minibuffer to the frame in which it is created, but make it invisible when a recursive minibuffer is entered in another frame, and visible again when that recursive minibuffer is left. The problem I see in this potential behavior is that, upon leaving the recursive minibuffer, the user would have no indication that another minibuffer is still (or rather, again) open in another frame, waiting for their input. This is yet another problem that the old behavior avoids. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-25 21:09 ` Alan Mackenzie 2020-11-25 21:31 ` Gregory Heytings via Emacs development discussions. @ 2020-11-26 15:43 ` martin rudalics 2020-11-27 11:53 ` Alan Mackenzie 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-26 15:43 UTC (permalink / raw) To: Alan Mackenzie Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii > Also, how often do people actually select minibuffer-only frames? > Unless I'm missing something, it seems a rather strange thing to want to > do. It's an integral part of the minibuffer-only frame setup: When you start a non-modal dialogue and want to do something else in between, then you eventually want to select the minibuffer-only frame in order to continue that dialogue. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-26 15:43 ` martin rudalics @ 2020-11-27 11:53 ` Alan Mackenzie 0 siblings, 0 replies; 252+ messages in thread From: Alan Mackenzie @ 2020-11-27 11:53 UTC (permalink / raw) To: martin rudalics Cc: Andrii Kolomoiets, emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Eli Zaretskii Hello, Martin. On Thu, Nov 26, 2020 at 16:43:13 +0100, martin rudalics wrote: > > Also, how often do people actually select minibuffer-only frames? > > Unless I'm missing something, it seems a rather strange thing to want to > > do. > It's an integral part of the minibuffer-only frame setup: When you start > a non-modal dialogue and want to do something else in between, then you > eventually want to select the minibuffer-only frame in order to continue > that dialogue. OK. Thanks! > martin -- Alan Mackenzie (Nuremberg, Germany). ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-24 10:25 ` martin rudalics 2020-11-24 11:37 ` Gregory Heytings via Emacs development discussions. @ 2020-11-24 12:59 ` Andrii Kolomoiets 2020-11-24 19:24 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Andrii Kolomoiets @ 2020-11-24 12:59 UTC (permalink / raw) To: martin rudalics Cc: emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Alan Mackenzie, Eli Zaretskii martin rudalics <rudalics@gmx.at> writes: > Can both of you confirm that reverting that change fixes the behavior? > Patch attached. With the patch applied, normal frame became selected and accept input after 'C-h f setq RET' and remains the same after typing 'q' in the *Help* buffer. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-24 12:59 ` Andrii Kolomoiets @ 2020-11-24 19:24 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-24 19:24 UTC (permalink / raw) To: Andrii Kolomoiets Cc: emacs-devel, enometh, Stefan Monnier, Gregory Heytings, Alan Mackenzie, Eli Zaretskii >> Can both of you confirm that reverting that change fixes the behavior? >> Patch attached. > > With the patch applied, normal frame became selected and accept input > after 'C-h f setq RET' and remains the same after typing 'q' in the > *Help* buffer. The 'Type C-x 1 ...' text would be more appropriate but apparently the 'quit-restore parameter doesn't get set up accordingly. I won't change that. Thank you, martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 9:02 ` martin rudalics 2020-11-21 10:27 ` Alan Mackenzie @ 2020-11-21 17:19 ` Stefan Monnier 2020-11-21 18:08 ` martin rudalics 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-11-21 17:19 UTC (permalink / raw) To: martin rudalics Cc: Alan Mackenzie, enometh, Eli Zaretskii, Andrii Kolomoiets, emacs-devel > reveals another aspect broken by your change. The values reported by > 'selected-window' and 'select-frame' do not match up any more (unless > our masochistic way or printing frame names hides an important detail). You mean that (selected-frame) != (window-frame)? That's an important invariant to preserve. We've had problems with this for years and I've worked pretty hard to fix them over the years (I've been tempted to remove the `selected_frame` variable and define SELECTED_FRAME on top of `selected_window` instead, but that's proved difficult in the `select-frame` and `select-window` code). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-21 17:19 ` Stefan Monnier @ 2020-11-21 18:08 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-21 18:08 UTC (permalink / raw) To: Stefan Monnier Cc: Alan Mackenzie, enometh, Eli Zaretskii, Andrii Kolomoiets, emacs-devel > You mean that (selected-frame) != (window-frame)? Yes. But Alan's last fix might have fixed this part now. > That's an important invariant to preserve. We've had problems with this > for years and I've worked pretty hard to fix them over the years (I've > been tempted to remove the `selected_frame` variable and define > SELECTED_FRAME on top of `selected_window` instead, but that's proved > difficult in the `select-frame` and `select-window` code). Maybe we should try again. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 19:57 ` Stefan Monnier 2020-11-10 22:54 ` Andrii Kolomoiets @ 2020-11-11 8:28 ` martin rudalics 2020-11-11 18:47 ` Drew Adams 1 sibling, 1 reply; 252+ messages in thread From: martin rudalics @ 2020-11-11 8:28 UTC (permalink / raw) To: Stefan Monnier, Andrii Kolomoiets; +Cc: Eli Zaretskii, enometh, emacs-devel > But if it results in regressions with packages to maple-minibuffer or > mini-frame, we should of course try and address that (either by refining > the behavior or by changing the default). Both, Andrii's and Madhu's scenarios, have to be fixed. Then we might be able to get rid of other oddities like 'redirect-frame-focus' and frame auto hiding as well. What we want to do here is to track non-modal user dialogues in our own way. In particular, we want to handle the case that a user wants to suspend such a dialogue and do something else in between, either in Emacs or in another application. The major issues then are how to handle the following actions wrt the Emacs windows participating in that dialogue: mouse clicks and hovering, alt-tabbing and altering the window configuration via C-x o, C-x 5 o, window creation and deletion. Before and after Alan's changes all of these do not work satisfactorily. Most applications I know of do not allow such non-modal dialogues. And I know of no other application that may display messages in the same area where a prompt appears which further complicates things with Emacs. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* RE: Stop frames stealing eachothers' minibuffers! 2020-11-11 8:28 ` martin rudalics @ 2020-11-11 18:47 ` Drew Adams 2020-11-11 19:10 ` martin rudalics 0 siblings, 1 reply; 252+ messages in thread From: Drew Adams @ 2020-11-11 18:47 UTC (permalink / raw) To: martin rudalics, Stefan Monnier, Andrii Kolomoiets Cc: Eli Zaretskii, enometh, emacs-devel > Both, Andrii's and Madhu's scenarios, have to be fixed. Then we might > be able to get rid of other oddities like 'redirect-frame-focus' What do you mean by that? I certainly don't want Emacs to get rid of being able to redirect a frame's focus to a different frame. This is an important (even necessary) feature, IMO. > Most applications I know of do not allow such non-modal dialogues. I don't know what this means, e.g. what you mean by "application" here. Emacs has always let you use other windows and frames while a minibuffer remains active, and it should continue to allow that. But I'm probably misunderstanding you, here. At least I hope I am. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-11 18:47 ` Drew Adams @ 2020-11-11 19:10 ` martin rudalics 0 siblings, 0 replies; 252+ messages in thread From: martin rudalics @ 2020-11-11 19:10 UTC (permalink / raw) To: Drew Adams, Stefan Monnier, Andrii Kolomoiets Cc: Eli Zaretskii, enometh, emacs-devel > What do you mean by that? I certainly don't want Emacs > to get rid of being able to redirect a frame's focus to > a different frame. This is an important (even necessary) > feature, IMO. Not IMO. >> Most applications I know of do not allow such non-modal dialogues. > > I don't know what this means, e.g. what you mean by > "application" here. Emacs has always let you use other > windows and frames while a minibuffer remains active, > and it should continue to allow that. > > But I'm probably misunderstanding you, here. At least > I hope I am. You've been misunderstanding me here. martin ^ permalink raw reply [flat|nested] 252+ messages in thread
* RE: Stop frames stealing eachothers' minibuffers! 2020-11-10 8:52 ` Eli Zaretskii 2020-11-10 13:21 ` Stefan Monnier @ 2020-11-10 16:45 ` Drew Adams 2020-11-10 19:51 ` Stefan Monnier 1 sibling, 1 reply; 252+ messages in thread From: Drew Adams @ 2020-11-10 16:45 UTC (permalink / raw) To: Eli Zaretskii, emacs-devel, Andrii Kolomoiets; +Cc: enometh > The defaults are selected for the common usage patterns. It is not > clear to me that the test case you presented is common. But if it is, > perhaps we do need to consider changing the default. > > Does anyone else think this is common usage, to have a minibuffer-only > frame while other frames also have minibuffers? > > Alternatively, perhaps minibuffers activated in minibuffer-only frames > should behave specially in this regard? FWIW (not much, I'm afraid): I can't speak to this, as Emacs 27 has totally broken Emacs for me. And specifically wrt changing frame focus while using the minibuffer (which in my case is in a standalone frame). (I've mentioned this before. And yes, it's too bad that I have no specifics that might help track down the problem. I have to continue to use Emacs 26, to be able to use Emacs at all.) Something - dunno what, and dunno whether I'll ever have the time to somehow track it down - makes it impossible to use Emacs 27 with my setup, which has a dedicated frame for *Completions* whose focus is automatically redirected to the standalone minibuffer frame. Just sayin', to mention that at least one user has been bit badly by _something_ that changed from Emacs 26 to 27 - something in the area of frame focus and the minibuffer. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 16:45 ` Drew Adams @ 2020-11-10 19:51 ` Stefan Monnier 2020-11-10 20:08 ` Eli Zaretskii 2020-11-10 20:12 ` Drew Adams 0 siblings, 2 replies; 252+ messages in thread From: Stefan Monnier @ 2020-11-10 19:51 UTC (permalink / raw) To: Drew Adams; +Cc: Eli Zaretskii, enometh, Andrii Kolomoiets, emacs-devel > I can't speak to this, as Emacs 27 has totally broken > Emacs for me. And specifically wrt changing frame > focus while using the minibuffer (which in my case is > in a standalone frame). Do you have a bug#nb for it? Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-11-10 19:51 ` Stefan Monnier @ 2020-11-10 20:08 ` Eli Zaretskii 2020-11-10 20:12 ` Drew Adams 1 sibling, 0 replies; 252+ messages in thread From: Eli Zaretskii @ 2020-11-10 20:08 UTC (permalink / raw) To: Stefan Monnier; +Cc: enometh, andreyk.mad, drew.adams, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org, Andrii Kolomoiets > <andreyk.mad@gmail.com>, enometh@meer.net > Date: Tue, 10 Nov 2020 14:51:23 -0500 > > > I can't speak to this, as Emacs 27 has totally broken > > Emacs for me. And specifically wrt changing frame > > focus while using the minibuffer (which in my case is > > in a standalone frame). > > Do you have a bug#nb for it? Nice try. ^ permalink raw reply [flat|nested] 252+ messages in thread
* RE: Stop frames stealing eachothers' minibuffers! 2020-11-10 19:51 ` Stefan Monnier 2020-11-10 20:08 ` Eli Zaretskii @ 2020-11-10 20:12 ` Drew Adams 1 sibling, 0 replies; 252+ messages in thread From: Drew Adams @ 2020-11-10 20:12 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, enometh, Andrii Kolomoiets, emacs-devel > > I can't speak to this, as Emacs 27 has totally broken > > Emacs for me. And specifically wrt changing frame > > focus while using the minibuffer (which in my case is > > in a standalone frame). > > Do you have a bug#nb for it? I filed bug #41087 for it, but as I said (there & here), I haven't been able to come up with operational info about it. Maybe at some point I'll have some time to dig into it. Otherwise, I'm vaguely hoping that someone will run into a related problem that will eventually lead to fixing or reverting whatever (also) caused my problems in this regard. That's perhaps unlikely, if it inherently involves a *Completions* frame whose input is redirected to a standalone minibuffer. There are no doubt few, if any, others in a similar boat. I don't know that those things are inherent to the problems, however, so I still have a glimmer of hope. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 18:58 ` Eli Zaretskii 2020-10-14 19:49 ` Alan Mackenzie @ 2020-10-14 20:17 ` Gregory Heytings via Emacs development discussions. 1 sibling, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-10-14 20:17 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Alan Mackenzie, emacs-devel Hi Eli, > > You should try this with the emacs-27 branch, because Gregory's patch > installed there (and will be soon merged to master) changes the behavior > quite a bit. > It only changes the behavior for the echo area, not for minibuffers. For minibuffers the behavior has not changed since Emacs 24: when a recursive minibuffer is entered in a frame, all other recursive minibuffers (if any) are moved from the frame in which they were to that frame. >> The current master seems to me to be inconsistent, in that whether the >> minibuffer moves from F1 to F2 depends on whether the Isearch used a >> (recursive) minibuffer. > > AFAICT, this no longer happens. > It does. As I said a few hours ago, there are three possible behaviors when each frame has its miniwindow: A. when a (recursive) minibuffer is entered in a frame, all other minibuffers (if any) are moved from the frame in which they were to that frame B. if one or more (recursive) minibuffers are active when switching to another frame (for example with C-x 5 o) these minibuffers are moved to that other frame C. each (recursive) minibuffer is tied to the frame in which it was activated, that is, minibuffers are never moved from one frame to another Behavior A is the current one. Behavior B is what Stefan seems to favor. Behavior C is what Alan seems to favor. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 16:35 ` Alan Mackenzie 2020-10-14 17:05 ` Eli Zaretskii @ 2020-10-14 17:07 ` Gregory Heytings via Emacs development discussions. 1 sibling, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-10-14 17:07 UTC (permalink / raw) To: Alan Mackenzie; +Cc: Eli Zaretskii, emacs-devel > > I'm not quite sure what a mini-window is. Does it mean the window > within which the minibuffer is displayed? As in max-mini-window-height? > The mini-window is usually at the bottom of the Emacs frame and is usually only one line tall. It does not have a mode-line. It displays the minibuffer (for interactive use, with C-x C-f, M-x, ...) and the echo area (for messages). >> So if we cannot reconcile our preferences, maybe we should have a user >> option to decide which behavior to choose. > > Perhaps. If we can formulate the two (or several) options in a > non-confusing way. This is a fairly arcane matter. > AFAIU, the possible options include at least: - keep the current behavior, with which all recursive minibuffers are moved from one frame to another when one or more minibuffers are active in one frame, and a new recursive activation happens in another frame (which happens only when enable-recursive-minibuffers is t) - move all recursive minibuffers from one frame to the other when switching to another frame - tie each one of the recursive minibuffers to the frame in which it was activated (Of course the above options only make sense when there is one miniwindow in each frame.) ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 20:25 ` Gregory Heytings via Emacs development discussions. 2020-10-13 20:44 ` Alan Mackenzie @ 2020-10-13 20:51 ` Andreas Schwab 2020-10-13 21:02 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:22 ` Stefan Monnier 1 sibling, 2 replies; 252+ messages in thread From: Andreas Schwab @ 2020-10-13 20:51 UTC (permalink / raw) To: Gregory Heytings via Emacs development discussions. Cc: Gregory Heytings, Alan Mackenzie, Eli Zaretskii On Okt 13 2020, Gregory Heytings via "Emacs development discussions." wrote: > I agree with you on one thing: C-x 8 RET should raise a "Command attempted > to use minibuffer while in minibuffer" error in this case (when > enable-recursive-minibuffers is nil, which is its default value). I'm not > sure why it doesn't. Because read-char-by-name binds enable-recursive-minibuffers to t. Andreas. -- Andreas Schwab, schwab@linux-m68k.org GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1 "And now for something completely different." ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 20:51 ` Andreas Schwab @ 2020-10-13 21:02 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:22 ` Stefan Monnier 1 sibling, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-10-13 21:02 UTC (permalink / raw) To: Andreas Schwab; +Cc: emacs-devel, Alan Mackenzie, Eli Zaretskii >> I agree with you on one thing: C-x 8 RET should raise a "Command >> attempted to use minibuffer while in minibuffer" error in this case >> (when enable-recursive-minibuffers is nil, which is its default value). >> I'm not sure why it doesn't. > > Because read-char-by-name binds enable-recursive-minibuffers to t. > That's it. I should have checked. Thank you! ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 20:51 ` Andreas Schwab 2020-10-13 21:02 ` Gregory Heytings via Emacs development discussions. @ 2020-10-13 22:22 ` Stefan Monnier 1 sibling, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-10-13 22:22 UTC (permalink / raw) To: Andreas Schwab Cc: Gregory Heytings, Alan Mackenzie, Eli Zaretskii, Gregory Heytings via Emacs development discussions. >> I agree with you on one thing: C-x 8 RET should raise a "Command attempted >> to use minibuffer while in minibuffer" error in this case (when >> enable-recursive-minibuffers is nil, which is its default value). I'm not >> sure why it doesn't. > Because read-char-by-name binds enable-recursive-minibuffers to t. Which it does to allow you to use `C-x 8 RET` to insert unicode chars into minibuffers. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 19:20 ` Eli Zaretskii 2020-10-13 19:51 ` Alan Mackenzie @ 2020-10-13 22:28 ` Stefan Monnier 2020-10-14 14:47 ` Eli Zaretskii 1 sibling, 1 reply; 252+ messages in thread From: Stefan Monnier @ 2020-10-13 22:28 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Alan Mackenzie, emacs-devel >> Seeing as how a minibuffer often has a strong association with its frame >> (e.g., C-x C-f opens a buffer in the same frame it was invoked from), >> this shifting of minibuffers from one frame to another is confusing. > Is it? It makes sure the minibuffer is on the selected frame, which > is natural in many/most use cases. But it only makes sure after you used another minibuffer, so in the remaining 90% of the cases you're still left with an active minibuffer that is not on the selected frame. If that 90% is not considered a problem, then I wonder what's different after using a nested minibuffer to justify the current behavior? Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 22:28 ` Stefan Monnier @ 2020-10-14 14:47 ` Eli Zaretskii 2020-10-14 17:22 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 14:47 UTC (permalink / raw) To: Stefan Monnier; +Cc: acm, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: Alan Mackenzie <acm@muc.de>, emacs-devel@gnu.org > Date: Tue, 13 Oct 2020 18:28:39 -0400 > > >> Seeing as how a minibuffer often has a strong association with its frame > >> (e.g., C-x C-f opens a buffer in the same frame it was invoked from), > >> this shifting of minibuffers from one frame to another is confusing. > > Is it? It makes sure the minibuffer is on the selected frame, which > > is natural in many/most use cases. > > But it only makes sure after you used another minibuffer Maybe that's the bug we should fix, then? Not "play along" with it by making the exceptions be the rule? ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 14:47 ` Eli Zaretskii @ 2020-10-14 17:22 ` Stefan Monnier 2020-10-14 17:32 ` Gregory Heytings via Emacs development discussions. 2020-10-14 17:43 ` Eli Zaretskii 0 siblings, 2 replies; 252+ messages in thread From: Stefan Monnier @ 2020-10-14 17:22 UTC (permalink / raw) To: Eli Zaretskii; +Cc: acm, emacs-devel >> >> Seeing as how a minibuffer often has a strong association with its frame >> >> (e.g., C-x C-f opens a buffer in the same frame it was invoked from), >> >> this shifting of minibuffers from one frame to another is confusing. >> > Is it? It makes sure the minibuffer is on the selected frame, which >> > is natural in many/most use cases. >> But it only makes sure after you used another minibuffer > Maybe that's the bug we should fix, then? You mean, we'd make the active minibuffer follow along with changes to the selected frame? Yes, that would be more consistent. I think that's what we do with the echo area already, so there's precedent for it. I can't tell if it would be an improvement or a regression (I think it wouldn't affect my use cases either way). Stefan PS: Just trying out now the echo-area case to check I remembered it right, I see we have a bug there (that dates back to Emacs-25 at least): % emacs -Q src/emacs.c C-x 5 b RET M-: (message "hello") RET ... use your window manager to select the other frame ... we now see "hello" in both miniwindows, whereas I expected it to be seen only in the selected frame (i.e. to be erased from the previously selected frame). C-g we now see "Quit" in one of the miniwindows and "hello" in the other. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 17:22 ` Stefan Monnier @ 2020-10-14 17:32 ` Gregory Heytings via Emacs development discussions. 2020-10-14 17:47 ` Eli Zaretskii 2020-10-14 17:43 ` Eli Zaretskii 1 sibling, 1 reply; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-10-14 17:32 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, acm, emacs-devel > > You mean, we'd make the active minibuffer follow along with changes to > the selected frame? Yes, that would be more consistent. I think that's > what we do with the echo area already, so there's precedent for it. > Why not, but it's not what Alan would have wanted. IIUC, what he wants is to tie each one of the recursive minibuffers to the frame in which it was activated. > > % emacs -Q src/emacs.c > C-x 5 b RET > M-: (message "hello") RET > ... use your window manager to select the other frame ... > > we now see "hello" in both miniwindows, whereas I expected it to be seen > only in the selected frame (i.e. to be erased from the previously > selected frame). > > C-g > > we now see "Quit" in one of the miniwindows and "hello" in the other. > I just checked. This behavior is not present in Emacs 21-23. It is present since Emacs 24. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 17:32 ` Gregory Heytings via Emacs development discussions. @ 2020-10-14 17:47 ` Eli Zaretskii 2020-10-15 1:43 ` Stefan Monnier 0 siblings, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 17:47 UTC (permalink / raw) To: Gregory Heytings; +Cc: acm, monnier, emacs-devel > Date: Wed, 14 Oct 2020 17:32:56 +0000 > From: Gregory Heytings <ghe@sdf.org> > cc: Eli Zaretskii <eliz@gnu.org>, acm@muc.de, emacs-devel@gnu.org > > > % emacs -Q src/emacs.c > > C-x 5 b RET > > M-: (message "hello") RET > > ... use your window manager to select the other frame ... > > > > we now see "hello" in both miniwindows, whereas I expected it to be seen > > only in the selected frame (i.e. to be erased from the previously > > selected frame). > > > > C-g > > > > we now see "Quit" in one of the miniwindows and "hello" in the other. > > > > I just checked. This behavior is not present in Emacs 21-23. It is > present since Emacs 24. Crystal ball says that this is one consequence of not redisplaying non-selected frames as much as Emacs 21-23 did. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 17:47 ` Eli Zaretskii @ 2020-10-15 1:43 ` Stefan Monnier 0 siblings, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-10-15 1:43 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Gregory Heytings, acm, emacs-devel > Crystal ball says that this is one consequence of not redisplaying > non-selected frames as much as Emacs 21-23 did. Indeed, it sounds like a result of my changes in this area. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 17:22 ` Stefan Monnier 2020-10-14 17:32 ` Gregory Heytings via Emacs development discussions. @ 2020-10-14 17:43 ` Eli Zaretskii 2020-10-15 1:42 ` Stefan Monnier 1 sibling, 1 reply; 252+ messages in thread From: Eli Zaretskii @ 2020-10-14 17:43 UTC (permalink / raw) To: Stefan Monnier; +Cc: acm, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: acm@muc.de, emacs-devel@gnu.org > Date: Wed, 14 Oct 2020 13:22:28 -0400 > > >> >> Seeing as how a minibuffer often has a strong association with its frame > >> >> (e.g., C-x C-f opens a buffer in the same frame it was invoked from), > >> >> this shifting of minibuffers from one frame to another is confusing. > >> > Is it? It makes sure the minibuffer is on the selected frame, which > >> > is natural in many/most use cases. > >> But it only makes sure after you used another minibuffer > > Maybe that's the bug we should fix, then? > > You mean, we'd make the active minibuffer follow along with changes to > the selected frame? Yes, that would be more consistent. > I think that's what we do with the echo area already, so there's > precedent for it. Yes. > I can't tell if it would be an improvement or a regression (I think it > wouldn't affect my use cases either way). > > > Stefan > > > PS: Just trying out now the echo-area case to check I remembered it > right, I see we have a bug there (that dates back to Emacs-25 at least): > > % emacs -Q src/emacs.c > C-x 5 b RET > M-: (message "hello") RET > ... use your window manager to select the other frame ... > > we now see "hello" in both miniwindows, whereas I expected it to be seen > only in the selected frame (i.e. to be erased from the previously > selected frame). > > C-g > > we now see "Quit" in one of the miniwindows and "hello" in the other. We are slowly eradicating problems like this one. Alan's proposal suggests that we move in the opposite direction, which I think would be a mistake. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-14 17:43 ` Eli Zaretskii @ 2020-10-15 1:42 ` Stefan Monnier 0 siblings, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-10-15 1:42 UTC (permalink / raw) To: Eli Zaretskii; +Cc: acm, emacs-devel > We are slowly eradicating problems like this one. Alan's proposal > suggests that we move in the opposite direction, which I think would > be a mistake. I think both behaviors make sense: keep the echo-areas and minibuffers in the mini-window to which they "belong" (e.g. the mini-window associated with the `minibuffer-selected-window`), or make them follow the `selected-frame`. Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 19:02 Alan Mackenzie 2020-10-13 19:20 ` Eli Zaretskii @ 2020-10-13 19:22 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:25 ` Stefan Monnier 2 siblings, 0 replies; 252+ messages in thread From: Gregory Heytings via Emacs development discussions. @ 2020-10-13 19:22 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel Hi Alan, > > In recent versions of master (and, I believe the emacs-27 branch) frames > steal eachothers' minibuffers. > > By this I mean: > (i) Have two frames open displaying buffers. > (ii) On frame F1 do C-x b. This leaves a minibuffer open there. > (iii) Move to F2. > (iv) Do C-x 8 RET <enter some character>. > > F1's minibuffer is now on F2. This is bad. > As I told you a few days ago, this is not a new Emacs behavior, it dates back to at least Emacs 24 (and probably earlier, I did not check). IMO this behavior is not problematic: the minibuffer has moved to F2, but when you press RET after choosing the buffer you want, the buffer switch happens in F1, where it should happen. And on a TTY, Emacs even goes back to F1. ^ permalink raw reply [flat|nested] 252+ messages in thread
* Re: Stop frames stealing eachothers' minibuffers! 2020-10-13 19:02 Alan Mackenzie 2020-10-13 19:20 ` Eli Zaretskii 2020-10-13 19:22 ` Gregory Heytings via Emacs development discussions. @ 2020-10-13 22:25 ` Stefan Monnier 2 siblings, 0 replies; 252+ messages in thread From: Stefan Monnier @ 2020-10-13 22:25 UTC (permalink / raw) To: Alan Mackenzie; +Cc: emacs-devel > Comments? FWIW, I really like this patch. I haven't tried it to see how it behaves, but from the look of it, it seems very unlikely to affect my usage pattern (I only have a single mini window shared by all my frames, anyway). Stefan ^ permalink raw reply [flat|nested] 252+ messages in thread
end of thread, other threads:[~2021-03-21 17:03 UTC | newest] Thread overview: 252+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <<20201031194419.GC5887@ACM> [not found] ` <<834kmago8m.fsf@gnu.org> [not found] ` <<20201031203914.GD5887@ACM> [not found] ` <<835z6ogc1h.fsf@gnu.org> [not found] ` <<20201101195313.GA6190@ACM> [not found] ` <<83sg9rd6cp.fsf@gnu.org> [not found] ` <<20201102185147.GC7297@ACM> [not found] ` <<83mtzzd0s3.fsf@gnu.org> [not found] ` <<20201103210853.GA21923@ACM> [not found] ` <<83ft5pax2p.fsf@gnu.org> [not found] ` <<20201104173954.GA14535@ACM> [not found] ` <<m31rh2pnws.fsf@leonis4.robolove.meer.net> [not found] ` <<m28sbas208.fsf@gmail.com> [not found] ` <<83v9ed3nbw.fsf@gnu.org> [not found] ` <<m21rh1prap.fsf@gmail.com> [not found] ` <<CF5D4DFC-5288-4D2C-AF4A-A7D1B267CAFF@gnu.org> [not found] ` <<44261efc-da8d-44f2-9a9a-200d1683b313@default> [not found] ` <<jwvzh3pvvmi.fsf-monnier+emacs@gnu.org> [not found] ` <<83imad0yb3.fsf@gnu.org> 2020-11-10 20:17 ` Stop frames stealing eachothers' minibuffers! Drew Adams 2021-02-06 23:25 jakanakaevangeli 2021-02-07 12:55 ` Alan Mackenzie 2021-02-07 16:44 ` jakanakaevangeli 2021-02-07 20:26 ` Alan Mackenzie 2021-02-08 12:53 ` jakanakaevangeli 2021-02-11 11:44 ` Alan Mackenzie 2021-02-11 14:29 ` Stefan Monnier 2021-02-12 9:48 ` Alan Mackenzie 2021-03-13 18:23 ` Alan Mackenzie 2021-03-13 19:39 ` Stefan Monnier 2021-03-13 20:24 ` Alan Mackenzie 2021-03-13 20:52 ` Stefan Monnier 2021-03-14 18:26 ` Alan Mackenzie 2021-03-14 18:48 ` Eli Zaretskii 2021-03-14 20:32 ` Stefan Monnier 2021-03-13 20:53 ` jakanakaevangeli 2021-03-14 19:17 ` Alan Mackenzie 2021-03-14 21:23 ` Miha Rihtaršič 2021-03-17 19:32 ` Alan Mackenzie 2021-03-17 19:55 ` Eli Zaretskii 2021-03-17 20:19 ` Eli Zaretskii 2021-03-18 11:27 ` Alan Mackenzie 2021-03-18 11:46 ` Eli Zaretskii 2021-03-18 15:51 ` martin rudalics 2021-03-18 16:58 ` Alan Mackenzie 2021-03-18 18:44 ` Eli Zaretskii 2021-03-19 11:40 ` Alan Mackenzie 2021-03-19 12:33 ` Eli Zaretskii 2021-03-19 15:35 ` Alan Mackenzie 2021-03-19 15:59 ` Eli Zaretskii 2021-03-20 10:28 ` Alan Mackenzie 2021-03-20 10:49 ` Eli Zaretskii 2021-03-20 12:24 ` Alan Mackenzie 2021-03-20 12:49 ` Miha Rihtaršič 2021-03-20 13:59 ` Stefan Monnier 2021-03-21 10:30 ` Alan Mackenzie 2021-03-21 10:38 ` Eli Zaretskii 2021-03-21 10:40 ` Eli Zaretskii 2021-03-21 14:49 ` Alan Mackenzie 2021-03-21 15:00 ` Stefan Monnier 2021-03-21 15:43 ` Eli Zaretskii 2021-03-21 16:17 ` Michael Welsh Duggan 2021-03-21 16:37 ` Alan Mackenzie 2021-03-20 12:50 ` Eli Zaretskii 2021-03-20 13:51 ` Alan Mackenzie 2021-03-20 13:55 ` Stefan Monnier 2021-03-20 14:01 ` Eli Zaretskii 2021-03-20 14:12 ` Alan Mackenzie 2021-03-21 15:44 ` Miha Rihtaršič 2021-03-21 17:03 ` Alan Mackenzie -- strict thread matches above, loose matches on Subject: below -- 2021-02-03 15:20 jakanakaevangeli 2021-02-06 15:52 ` Alan Mackenzie 2020-10-13 19:02 Alan Mackenzie 2020-10-13 19:20 ` Eli Zaretskii 2020-10-13 19:51 ` Alan Mackenzie 2020-10-13 20:25 ` Gregory Heytings via Emacs development discussions. 2020-10-13 20:44 ` Alan Mackenzie 2020-10-13 21:02 ` Drew Adams 2020-10-14 14:34 ` Eli Zaretskii 2020-10-14 16:02 ` Alan Mackenzie 2020-10-14 16:14 ` Eli Zaretskii 2020-10-14 16:35 ` Alan Mackenzie 2020-10-14 17:05 ` Eli Zaretskii 2020-10-14 18:45 ` Alan Mackenzie 2020-10-14 18:58 ` Eli Zaretskii 2020-10-14 19:49 ` Alan Mackenzie 2020-10-15 13:44 ` Eli Zaretskii 2020-10-15 18:01 ` Alan Mackenzie 2020-10-15 18:18 ` Eli Zaretskii 2020-10-21 15:19 ` Alan Mackenzie 2020-10-21 16:49 ` Drew Adams 2020-10-21 19:13 ` Alan Mackenzie 2020-10-21 18:32 ` Stefan Monnier 2020-10-21 19:38 ` Alan Mackenzie 2020-10-21 20:04 ` Alan Mackenzie 2020-10-22 16:14 ` Eli Zaretskii 2020-10-30 22:09 ` Alan Mackenzie 2020-10-31 7:25 ` Eli Zaretskii 2020-10-31 16:14 ` Alan Mackenzie 2020-10-31 16:45 ` Eli Zaretskii 2020-10-31 19:44 ` Alan Mackenzie 2020-10-31 20:00 ` Eli Zaretskii 2020-10-31 20:39 ` Alan Mackenzie 2020-11-01 18:35 ` Eli Zaretskii 2020-11-01 19:53 ` Alan Mackenzie 2020-11-02 17:19 ` Eli Zaretskii 2020-11-02 18:51 ` Alan Mackenzie 2020-11-02 19:19 ` Eli Zaretskii 2020-11-03 21:08 ` Alan Mackenzie 2020-11-04 16:47 ` Eli Zaretskii 2020-11-04 17:39 ` Alan Mackenzie 2020-11-09 15:09 ` Madhu 2020-11-09 20:34 ` Andrii Kolomoiets 2020-11-10 3:25 ` Eli Zaretskii 2020-11-10 8:08 ` Andrii Kolomoiets 2020-11-10 8:52 ` Eli Zaretskii 2020-11-10 13:21 ` Stefan Monnier 2020-11-10 17:27 ` Andrii Kolomoiets 2020-11-10 18:26 ` Eli Zaretskii 2020-11-10 22:43 ` Andrii Kolomoiets 2020-11-11 15:38 ` Eli Zaretskii 2020-11-10 19:57 ` Stefan Monnier 2020-11-10 22:54 ` Andrii Kolomoiets 2020-11-10 23:18 ` Stefan Monnier 2020-11-11 7:47 ` Andrii Kolomoiets 2020-11-11 16:07 ` Eli Zaretskii 2020-11-11 20:37 ` Alan Mackenzie 2020-11-14 13:36 ` Eli Zaretskii 2020-11-14 17:12 ` Eli Zaretskii 2020-11-14 18:48 ` Alan Mackenzie 2020-11-14 19:11 ` Eli Zaretskii 2020-11-14 19:24 ` martin rudalics 2020-11-14 21:37 ` Alan Mackenzie 2020-11-15 8:48 ` martin rudalics 2020-11-19 10:40 ` Alan Mackenzie 2020-11-19 11:40 ` Andrii Kolomoiets 2020-11-19 13:30 ` Alan Mackenzie 2020-11-20 18:47 ` martin rudalics 2020-11-20 21:00 ` Alan Mackenzie 2020-11-20 21:36 ` Stefan Monnier 2020-11-21 9:02 ` martin rudalics 2020-11-21 10:27 ` Alan Mackenzie 2020-11-21 11:55 ` martin rudalics 2020-11-21 12:45 ` Alan Mackenzie 2020-11-21 15:53 ` martin rudalics 2020-11-22 10:59 ` Alan Mackenzie 2020-11-22 15:13 ` Stefan Monnier 2020-11-22 17:11 ` Alan Mackenzie 2020-11-22 19:58 ` Stefan Monnier 2020-11-22 17:57 ` martin rudalics 2020-11-22 18:38 ` Alan Mackenzie 2020-11-23 9:10 ` martin rudalics 2020-11-23 13:36 ` Alan Mackenzie 2020-11-23 14:22 ` martin rudalics 2020-11-23 16:07 ` Alan Mackenzie 2020-11-23 18:08 ` martin rudalics 2020-11-23 20:16 ` Andrii Kolomoiets 2020-11-24 8:46 ` martin rudalics 2020-11-23 20:22 ` Gregory Heytings via Emacs development discussions. 2020-11-23 20:26 ` Andrii Kolomoiets 2020-11-24 8:47 ` martin rudalics 2020-11-24 8:46 ` martin rudalics 2020-11-24 10:25 ` martin rudalics 2020-11-24 11:37 ` Gregory Heytings via Emacs development discussions. 2020-11-24 19:24 ` martin rudalics 2020-11-25 9:25 ` martin rudalics 2020-11-25 21:09 ` Alan Mackenzie 2020-11-25 21:31 ` Gregory Heytings via Emacs development discussions. 2020-11-25 21:54 ` Alan Mackenzie 2020-11-25 22:23 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:02 ` Alan Mackenzie 2020-11-27 10:36 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:47 ` Gregory Heytings via Emacs development discussions. 2020-11-27 11:20 ` Alan Mackenzie 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. 2020-11-27 11:14 ` Alan Mackenzie 2020-11-27 12:03 ` Gregory Heytings via Emacs development discussions. 2020-11-27 15:42 ` martin rudalics 2020-11-27 15:54 ` Gregory Heytings via Emacs development discussions. 2020-11-27 17:14 ` martin rudalics 2020-11-27 17:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:08 ` martin rudalics 2020-11-27 20:02 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:50 ` Eli Zaretskii 2020-11-26 15:44 ` martin rudalics 2020-11-26 20:32 ` Gregory Heytings via Emacs development discussions. 2020-11-27 7:33 ` Gregory Heytings via Emacs development discussions. 2020-11-27 9:34 ` martin rudalics 2020-11-27 10:06 ` Gregory Heytings via Emacs development discussions. 2020-11-27 10:36 ` martin rudalics 2020-11-27 10:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 15:41 ` martin rudalics 2020-11-27 16:19 ` Gregory Heytings via Emacs development discussions. 2020-11-27 17:14 ` martin rudalics 2020-11-27 18:01 ` Gregory Heytings via Emacs development discussions. 2020-11-27 18:35 ` martin rudalics 2020-11-27 20:05 ` Gregory Heytings via Emacs development discussions. 2020-11-28 10:45 ` Alan Mackenzie 2020-11-28 15:35 ` Alan Mackenzie 2020-11-28 17:02 ` Stefan Monnier 2020-11-28 20:59 ` Gregory Heytings via Emacs development discussions. 2020-11-28 21:10 ` Stefan Monnier 2020-11-28 22:01 ` Gregory Heytings via Emacs development discussions. 2020-11-28 22:10 ` Stefan Monnier 2020-11-28 22:38 ` Gregory Heytings via Emacs development discussions. 2020-11-29 18:15 ` Alan Mackenzie 2020-11-27 10:13 ` Alan Mackenzie 2020-11-27 10:36 ` martin rudalics 2020-11-27 11:30 ` Alan Mackenzie 2020-11-27 12:29 ` Eli Zaretskii 2020-11-27 13:43 ` Gregory Heytings via Emacs development discussions. 2020-11-27 14:09 ` Stefan Monnier 2020-11-27 15:03 ` Eli Zaretskii 2020-11-27 22:00 ` Alan Mackenzie 2020-11-27 15:42 ` martin rudalics 2021-01-03 18:10 ` Alan Mackenzie 2021-01-03 18:24 ` martin rudalics 2021-01-03 18:42 ` Alan Mackenzie 2021-01-03 20:08 ` martin rudalics 2021-01-03 20:43 ` Alan Mackenzie 2021-01-04 9:20 ` martin rudalics 2021-01-05 18:07 ` Alan Mackenzie 2021-01-05 18:53 ` martin rudalics 2021-01-07 17:36 ` Alan Mackenzie 2021-01-07 18:08 ` Drew Adams 2021-01-07 18:26 ` martin rudalics 2021-01-10 0:53 ` Alan Mackenzie 2021-01-10 1:34 ` Stefan Monnier 2021-01-10 16:03 ` Alan Mackenzie 2021-01-10 16:04 ` martin rudalics 2021-01-10 17:18 ` Alan Mackenzie 2021-01-10 17:30 ` Stefan Monnier 2021-01-10 17:49 ` martin rudalics 2021-01-10 18:25 ` Alan Mackenzie 2021-01-10 19:05 ` martin rudalics 2021-01-06 0:14 ` Gregory Heytings via Emacs development discussions. 2021-01-06 0:48 ` Stefan Monnier 2021-01-06 9:40 ` Gregory Heytings via Emacs development discussions. 2021-01-06 15:52 ` Stefan Monnier 2021-01-07 7:52 ` Richard Stallman 2021-01-07 14:33 ` Eli Zaretskii 2021-01-07 13:27 ` Alan Mackenzie 2021-01-07 23:34 ` Gregory Heytings via Emacs development discussions. 2020-11-26 15:43 ` martin rudalics 2020-11-27 11:53 ` Alan Mackenzie 2020-11-24 12:59 ` Andrii Kolomoiets 2020-11-24 19:24 ` martin rudalics 2020-11-21 17:19 ` Stefan Monnier 2020-11-21 18:08 ` martin rudalics 2020-11-11 8:28 ` martin rudalics 2020-11-11 18:47 ` Drew Adams 2020-11-11 19:10 ` martin rudalics 2020-11-10 16:45 ` Drew Adams 2020-11-10 19:51 ` Stefan Monnier 2020-11-10 20:08 ` Eli Zaretskii 2020-11-10 20:12 ` Drew Adams 2020-10-14 20:17 ` Gregory Heytings via Emacs development discussions. 2020-10-14 17:07 ` Gregory Heytings via Emacs development discussions. 2020-10-13 20:51 ` Andreas Schwab 2020-10-13 21:02 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:22 ` Stefan Monnier 2020-10-13 22:28 ` Stefan Monnier 2020-10-14 14:47 ` Eli Zaretskii 2020-10-14 17:22 ` Stefan Monnier 2020-10-14 17:32 ` Gregory Heytings via Emacs development discussions. 2020-10-14 17:47 ` Eli Zaretskii 2020-10-15 1:43 ` Stefan Monnier 2020-10-14 17:43 ` Eli Zaretskii 2020-10-15 1:42 ` Stefan Monnier 2020-10-13 19:22 ` Gregory Heytings via Emacs development discussions. 2020-10-13 22:25 ` Stefan Monnier
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).