[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Stop frames stealing eachothers' minibuffers!
From: |
Alan Mackenzie |
Subject: |
Re: Stop frames stealing eachothers' minibuffers! |
Date: |
Wed, 21 Oct 2020 20:04:38 +0000 |
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".
* 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;
+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).
- Re: Stop frames stealing eachothers' minibuffers!, (continued)
- Re: Stop frames stealing eachothers' minibuffers!, Eli Zaretskii, 2020/10/14
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/14
- Re: Stop frames stealing eachothers' minibuffers!, Eli Zaretskii, 2020/10/15
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/15
- Re: Stop frames stealing eachothers' minibuffers!, Eli Zaretskii, 2020/10/15
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/21
- RE: Stop frames stealing eachothers' minibuffers!, Drew Adams, 2020/10/21
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/21
- Re: Stop frames stealing eachothers' minibuffers!, Stefan Monnier, 2020/10/21
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/21
- Re: Stop frames stealing eachothers' minibuffers!,
Alan Mackenzie <=
- Re: Stop frames stealing eachothers' minibuffers!, Eli Zaretskii, 2020/10/22
- C-x o is moving between frames. [Was: Stop frames stealing eachothers' minibuffers!], Alan Mackenzie, 2020/10/23
- Re: C-x o is moving between frames. [Was: Stop frames stealing eachothers' minibuffers!], Stefan Monnier, 2020/10/23
- Re: C-x o is moving between frames. [Was: Stop frames stealing eachothers' minibuffers!], Eli Zaretskii, 2020/10/24
- Re: C-x o is moving between frames. [Was: Stop frames stealing eachothers' minibuffers!], Alan Mackenzie, 2020/10/24
- Re: C-x o is moving between frames. [Was: Stop frames stealing eachothers' minibuffers!], Stefan Monnier, 2020/10/24
- Re: C-x o is moving between frames. [Was: Stop frames stealing eachothers' minibuffers!], Alan Mackenzie, 2020/10/24
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/30
- Re: Stop frames stealing eachothers' minibuffers!, Eli Zaretskii, 2020/10/31
- Re: Stop frames stealing eachothers' minibuffers!, Alan Mackenzie, 2020/10/31