Subject: Re: bug#39379: 27.0.60; Fix for #38457 broke ido-vertical-mode

From: Eli Zaretskii <[email-masked]>
Date: Mon, 10 Feb 2020 17:44:08 +0200

> Date: Sun, 02 Feb 2020 20:06:16 +0200
> From: Eli Zaretskii <[email-masked]>
> Cc: [email-masked], [email-masked]
> 
> > Date: Sun, 02 Feb 2020 19:23:46 +0200
> > From: Eli Zaretskii <[email-masked]>
> > Cc: [email-masked], [email-masked]
> > 
> > I think this could be a bug in the display engine, related to resizing
> > the mini-window when an after-string that includes many newlines is
> > put at EOB.  Let me look into it.
> 
> Looks like my guess was correct.  I'll try to devise a solution.

Sorry for a relatively long delay.  In my defense, I first tried a
couple of easy solutions, but they didn't work.  And that got me
thinking, since my experience with solving issues like this due to
overlay strings is that the solutions tend to be less than elegant,
and take several attempts to get them right.

So now, after thinking about this for some time, I think I want to
change my mind and ask why do we need to use an overlay with
after-string in ido.el?  Re-reading related discussions, it seems the
answer is "so that the temporary display of messages is not at the end
of the minibuffer, where it could be invisible due to
resize-mini-windows being nil or restrictions imposed by ido.el via
ido-max-window-height".  Is that the correct conclusion?  If so, then
I think we can solve that problem without overlays.  See the proposed
patch below, which basically reverts ido.el to its previous shape, and
uses a special text property to instruct set-minibuffer-message where
to put the overlay (defaulting to EOB).

WDYT?

diff --git a/lisp/ido.el b/lisp/ido.el
index 6707d81407..edc848f52f 100644
--- a/lisp/ido.el
+++ b/lisp/ido.el
@@ -4728,16 +4728,10 @@ ido-exhibit
 	(let ((inf (ido-completions contents)))
 	  (setq ido-show-confirm-message nil)
 	  (ido-trace "inf" inf)
-          (when ido--overlay
-            (delete-overlay ido--overlay))
-          (let ((o (make-overlay (point-max) (point-max) nil t t)))
-            (when (> (length inf) 0)
-              ;; For hacks that redefine ido-completions function (bug#39379)
-              (when (eq (aref inf 0) ?\n)
-                (setq inf (concat " " inf)))
-              (put-text-property 0 1 'cursor t inf))
-            (overlay-put o 'after-string inf)
-            (setq ido--overlay o)))
+          (let ((pos (point)))
+            (insert inf)
+            (put-text-property pos (1+ pos) 'minibuffer-message t))
+          )
 	))))
 
 (defun ido-completions (name)
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 0589211877..767fc8dff8 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -763,6 +763,16 @@ minibuffer-message-clear-timeout
 (defvar minibuffer-message-timer nil)
 (defvar minibuffer-message-overlay nil)
 
+(defun minibuffer--message-overlay-pos ()
+  "Return position where `set-minibuffer-message' shall put message overlay."
+  ;; Starting from point, look for non-nil 'minibuffer-message'
+  ;; property, and return its position.  If none found, return the EOB
+  ;; position.
+  (let* ((pt (point))
+         (propval (get-text-property pt 'minibuffer-message)))
+    (if propval pt
+      (next-single-property-change pt 'minibuffer-message nil (point-max)))))
+
 (defun set-minibuffer-message (message)
   "Temporarily display MESSAGE at the end of the minibuffer.
 The text is displayed for `minibuffer-message-clear-timeout' seconds
@@ -784,8 +794,9 @@ set-minibuffer-message
 
       (clear-minibuffer-message)
 
-      (setq minibuffer-message-overlay
-            (make-overlay (point-max) (point-max) nil t t))
+      (let ((ovpos (minibuffer--message-overlay-pos)))
+        (setq minibuffer-message-overlay
+              (make-overlay ovpos ovpos nil t t)))
       (unless (zerop (length message))
         ;; The current C cursor code doesn't know to use the overlay's
         ;; marker's stickiness to figure out whether to place the cursor