Subject: bug#17272: bug#19064: bug#17272: bug#19064: 25.0.50; `message' overwrites `y-or-n-p' prompt, so user misses it

From: Juri Linkov <[email-masked]>
Date: Mon, 18 Nov 2019 23:10:30 +0200

>>> But before I had a chance to answer the prompt, compilation finished
>>> and obscured the prompt with this message permanently:
>>>
>>>   Compilation finished
>>>
>>> So I forgot about what was in the prompt :-(
>>
>> Yeah, it's a problem all over the place.
>>
>>> Since Drew doesn't want to improve safety to cover all such cases,
>>> we need to address these issues one by one, so here is the next patch:
>>
>> Drew doesn't have veto powers.  I say go ahead and make the change in
>> `message' (with a variable that can be used to force `message' to not
>> behave like `minibuffer-message').
>
> Yes, I believe a new variable would make Drew happy.

The variable name is ‘message-in-echo-area’.  After a little testing,
it seems to handle all such cases well:

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 6e72eb73f9..7e74fa1ffb 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -712,16 +712,16 @@ minibuffer-message
       (progn
         (if args
             (apply #'message message args)
-          (message "%s" message))
+          (message-in-echo-area "%s" message))
         (prog1 (sit-for (or minibuffer-message-timeout 1000000))
-          (message nil)))
+          (message-in-echo-area nil)))
     ;; Record message in the *Messages* buffer
     (let ((inhibit-message t))
       (if args
           (apply #'message message args)
-        (message "%s" message)))
+        (message-in-echo-area "%s" message)))
     ;; Clear out any old echo-area message to make way for our new thing.
-    (message nil)
+    (message-in-echo-area nil)
     (setq message (if (and (null args)
                            (string-match-p "\\` *\\[.+\\]\\'" message))
                       ;; Make sure we can put-text-property.
diff --git a/src/editfns.c b/src/editfns.c
index 8fc866d391..a1e3fb1fa5 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -2877,6 +2877,49 @@ Fmessage
 
 usage: (message FORMAT-STRING &rest ARGS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
+{
+  if (NILP (Vmessage_in_echo_area)
+      && !(NILP (args[0]) || (STRINGP (args[0]) && SBYTES (args[0]) == 0))
+      && WINDOW_LIVE_P (Fold_selected_window ())
+      && BUFFERP (Fwindow_buffer (Fold_selected_window ()))
+      && !NILP (Fminibufferp (Fwindow_buffer (Fold_selected_window ()))))
+    {
+      ptrdiff_t count = SPECPDL_INDEX ();
+
+      /* Avoid possible recursion.  */
+      specbind (Qmessage_in_echo_area, Qt);
+
+      record_unwind_current_buffer ();
+      Fset_buffer (Fwindow_buffer (Fold_selected_window ()));
+
+      return unbind_to (count, CALLN (Fapply, intern ("minibuffer-message"),
+                                      Flist (nargs, args)));
+    }
+  else
+    return Fmessage_in_echo_area (nargs, args);
+}
+
+DEFUN ("message-in-echo-area", Fmessage_in_echo_area, Smessage_in_echo_area, 1, MANY, 0,
+       doc: /* Display a message at the bottom of the screen.
+The message also goes into the `*Messages*' buffer, if `message-log-max'
+is non-nil.  (In keyboard macros, that's all it does.)
+Return the message.
+
+In batch mode, the message is printed to the standard error stream,
+followed by a newline.
+
+The first argument is a format control string, and the rest are data
+to be formatted under control of the string.  Percent sign (%), grave
+accent (\\=`) and apostrophe (\\=') are special in the format; see
+`format-message' for details.  To display STRING without special
+treatment, use (message-in-echo-area "%s" STRING).
+
+If the first argument is nil or the empty string, the function clears
+any existing message; this lets the minibuffer contents show.  See
+also `current-message'.
+
+usage: (message-in-echo-area FORMAT-STRING &rest ARGS)  */)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   if (NILP (args[0])
       || (STRINGP (args[0])
@@ -4520,6 +4563,11 @@ syms_of_editfns (void)
 it to be non-nil.  */);
   binary_as_unsigned = false;
 
+  DEFVAR_LISP ("message-in-echo-area", Vmessage_in_echo_area,
+	       doc: /* Non-nil means overwrite the minibuffer with a message in the echo area.  */);
+  Vmessage_in_echo_area = Qnil;
+  DEFSYM (Qmessage_in_echo_area, "message-in-echo-area");
+
   defsubr (&Spropertize);
   defsubr (&Schar_equal);
   defsubr (&Sgoto_char);
@@ -4594,6 +4642,7 @@ syms_of_editfns (void)
   defsubr (&Semacs_pid);
   defsubr (&Ssystem_name);
   defsubr (&Smessage);
+  defsubr (&Smessage_in_echo_area);
   defsubr (&Smessage_box);
   defsubr (&Smessage_or_box);
   defsubr (&Scurrent_message);