]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow minibuffer to stay in its original frame. Tidy up this area.
authorAlan Mackenzie <acm@muc.de>
Thu, 5 Nov 2020 19:27:43 +0000 (19:27 +0000)
committerAlan Mackenzie <acm@muc.de>
Thu, 5 Nov 2020 19:27:43 +0000 (19:27 +0000)
* doc/emacs/mini.texi (Basic Minibuffer): Add an entry for
minibuffer-follows-selected-frame.

* doc/lispref/minibuf.texi (Minibuffer Misc): Describe the new parameter to
minibufferp, LIVE.

* etc/NEWS: Add an entry describing the new minibuffer strategy.

* lisp/cus-start.el (minibuffer-prompt-properties--setter): Add an entry for
minibuffer-follows-selected-frame.

* lisp/minibuffer.el (minibuffer-message): Check for the current buffer being
an _active_ minibuffer rather than merely a minibuffer.

* src/frame.c (do_switch_frame): Call move_minibuffer_onto_frame.

* src/lisp.h (Top level): Add prototypes for move_minibuffer_onto_frame and
is_minibuffer.

* src/minibuf.c (minibuf_follows_frame): New function which ignores local and
let-bound values of minibuffer-follows-selected-frame.
(choose_minibuf_frame): Reformulate this function to reuse a minibuffer window
where possible, and to ensure no other frame has its minibuffer current, but
only when `minibuffer-follows-selected-frame'.
(move_minibuffer_onto_frame): New function.
(live_minibuffer_p): New function.
(Fminibufferp): Add a new &optional parameter LIVE.  Reformulate, possibly
calling live_minibuffer_p.
(read_minibuf): move the incrementation of minibuf_level to before the call of
choose_minibuf_frame.  Empty the miniwindows of frames without an active
minibuffer, rather than of all but the current frame.
(is_minibuffer): New function.
(read_minibuf_unwind): Note the miniwindow being restored and resize all other
miniwindows to zero size.
(minibuffer-follows-selected-frame): New configuration variable.

* src/window.c (candidate_window_p): In some scenarios, check the miniwindow
holds an active minibuffer.

* src/xdisp.c (get_window_cursor_type): Suppress the cursor for non-active
miniwindows, regardless of minibuf_level.

doc/emacs/mini.texi
doc/lispref/minibuf.texi
etc/NEWS
lisp/cus-start.el
lisp/minibuffer.el
src/frame.c
src/lisp.h
src/minibuf.c
src/window.c
src/xdisp.c

index 54f046a7e05f78645d17bf4c94329a9a2de238f4..ede95a28d4ee13cd480f5baed6f3a48ae64622d9 100644 (file)
@@ -69,6 +69,17 @@ the minibuffer for a few seconds, or until you type something; then
 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
 
index e5a0233b3c71e32b9a8d3be8c1c23623f7a482dc..b6a3434d15e3f60554aeea797a7f36979b813b13 100644 (file)
@@ -2586,10 +2586,12 @@ The minibuffer command @code{next-matching-history-element} (normally
 @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
index d15f3ed1ae325983134bef20e0d32c3b29823b08..7ef832e60330bbb14a51c4a51a9b5922afa3c2c4 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -88,6 +88,17 @@ useful on systems such as FreeBSD which ships only with "etc/termcap".
 ** Minibuffer scrolling is now conservative by default.
 This is controlled by the new variable 'scroll-minibuffer-conservatively'.
 
++++
+** 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 functions.
 This can either be used by saying 'M-x shortdoc-display-group' and
index 6927b6df6b89a96b37c09a40d68ae46f5a797e1a..04fb1dc6d0695b2bff47cb72cf69bc424d82812b 100644 (file)
@@ -394,6 +394,7 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
             ;;                         (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)
index 5a41e2f30bdf6ef43f449962801b71d702f63be5..9d57a817b256fb16b04efb944d7c264206a7e624 100644 (file)
@@ -701,7 +701,7 @@ The text is displayed for `minibuffer-message-timeout' seconds,
 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)
index 7c377da4456122e7ab7a0b276c2b6adabe09d73e..512aaf5f45c0a4facea20ff591b5b620b17d9457 100644 (file)
@@ -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;
 }
 
index a3cfb5044d44047c1228f748948758c69ad35429..cf33031342df64cbb70a137cec20279a70cf9e7c 100644 (file)
@@ -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);
index f957b2ae173d0f6736fff59fd18414e27334d5f1..ebc00ae4e4e7fbf01974d35b6ca2c2ed9c660f23 100644 (file)
@@ -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,
@@ -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,
@@ -433,6 +492,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 +545,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,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.  */
@@ -714,6 +779,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 +850,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 +885,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 +902,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 +1989,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 +2034,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.  */);
index e7433969d293341f23d518a0aec2f6e4d2ef30a5..a6de34f3db69363955faaea6b9c2a660aa89eb71 100644 (file)
@@ -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);
 
index 618ec688e894533661d9dc958344e47d56201cbc..bff14e584de7c8916dfbece0fb7f2602a2f29567 100644 (file)
@@ -31224,7 +31224,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;