Subject: bug#79420: 30.2; icomplete-vertical-mode flashes horizontal minibuffer prompt before expanding

From: Chris Roberts <[email-masked]>
Date: Wed, 10 Sep 2025 11:48:18 +0200

1. emacs -Q
2. Evaluate the following:

(setq icomplete-show-matches-on-no-input t)
(icomplete-vertical-mode +1)

3. M-x

Looking at the minibuffer, you will see the text "M-x" flash
momentarily, before the minibuffer window expands vertically. This can
be made more apparent by setting `icomplete-compute-delay' to a higher
value (0.15 by default.)

This is caused by the call to `sit-for' inside of the
`icomplete-exhibit' function. The wait obviously causes the issue,
because the function pauses the setup, including all of the necessary
vertical code, and waits for `icomplete-compute-delay' seconds. But
even if we set `icomplete-compute-delay' to 0, the prompt will still
flash, because redisplay is triggered. In other words, the only way to
call `sit-for' that doesn't cause an issue is:

(sit-for x t)

where x <= 0.

Following are my thoughts on how to fix this.

The piece of code from `icomplete-exhibit' that we are interested in is
the following:

(and (or icomplete-show-matches-on-no-input
         (not (equal (icomplete--field-string)
                     icomplete--initial-input)))
     (or
      ;; Don't bother with delay after certain number of chars:
      (> (- (point) (icomplete--field-beg))
         icomplete-max-delay-chars)
      ;; Don't delay if the completions are known.
      completion-all-sorted-completions
      ;; Don't delay if alternatives number is small enough:
      (and (sequencep (icomplete--completion-table))
           (< (length (icomplete--completion-table))
              icomplete-delay-completions-threshold))
      ;; Delay - give some grace time for next keystroke, before
      ;; embarking on computing completions:
      (sit-for icomplete-compute-delay)))

We see that `sit-for' will only execute if all the preceding forms
return nil (since it's inside `and'). We also see that setting
`icomplete-delay-completions-threshold' to a high value, like maybe
`most-positive-fixnum' should help us avoid the call to `sit-for' in
most situations. Except it doesn't, because
`(icomplete--completion-table)' does not return `sequencep'! This can
be verified by overriding the function via advice, and capturing the
value returned by `icomplete--completion-table':

;; Don't delay if alternatives number is small enough:
(progn ;; Important that we return nil to avoid exiting early
  (setq reimu (icomplete--completion-table))
  nil)
(and (sequencep (icomplete--completion-table))
     (< (length (icomplete--completion-table))
        icomplete-delay-completions-threshold))

Repeat the reproduction steps including this advice, press C-g after
M-x, and finally C-x C-e `(sequencep reimu)' in the buffer (it's
important that we don't do something that reads from the minibuffer,
to not change the value), and you will get nil. In fact, `(type-of
reimu)' shows that it's a compiled function.

After debugging `icomplete--completion-table', I verified that this
compiled function is in fact the value of the
minibuffer-completion-table at the time. I believe this might be
another bug, because the docstring of `minibuffer-completion-table'
says:

"Alist or obarray used for completion in the minibuffer."

But it's evidently neither alist nor obarray (I assume, since
`(sequencep obarray)' returns t).

Thus, my ideas of fixing the original problem with the flashing prompt
are following, from easiest to hardest:

1. Add a check for `icomplete-compute-delay)' <= 0, before the call to
`sit-for', like so:

;; Don't delay if the wait is <= 0, to avoid redisplay
;; and prompt flashing in vertical mode.
(<= icomplete-compute-delay 0)
;; Delay - give some grace time for next keystroke, before
;; embarking on computing completions:
(sit-for icomplete-compute-delay)))

2. Force `sit-for' to never redisplay. This could also be
conditionalized on `ido-vertical-mode' variable, to not affect the
existing code. I think this is a tricky option, because this line is
really ancient (at least 30 years old) and changing it might have
unintended consequences, perhaps?

3. Fix the presumed bug with minibuffer-completion-table and adjust the
`sequencep' check in `icomplete-exhibit' to compare with
`icomplete-delay-completions-threshold' correctly.

And finally, as mentioned earlier, this line of code is ancient. Maybe
some deliberation is in order on whether it's actually still needed
nowadays?

-----------------------------------------------------------------------

In GNU Emacs 30.2 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.33,
 cairo version 1.16.0) of 2025-09-10 built on cdr
Windowing system distributor 'The X.Org Foundation', version
11.0.12101004
System Description: Linux Mint 21.2

Configured using:
 'configure --with-cairo --with-x-toolkit=gtk3'

Configured features:
CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG LCMS2
LIBSELINUX LIBXML2 MODULES NOTIFY INOTIFY PDUMPER PNG SECCOMP SOUND
SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM
GTK3 ZLIB