GNU bug report logs - #79420
30.2; icomplete-vertical-mode flashes horizontal minibuffer prompt before expanding

Previous Next

Package: emacs;

Reported by: Chris Roberts <frayedultrasonicaligator <at> disroot.org>

Date: Wed, 10 Sep 2025 10:52:01 UTC

Severity: normal

Found in version 30.2

To reply to this bug, email your comments to 79420 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#79420; Package emacs. (Wed, 10 Sep 2025 10:52:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Chris Roberts <frayedultrasonicaligator <at> disroot.org>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Wed, 10 Sep 2025 10:52:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Chris Roberts <frayedultrasonicaligator <at> disroot.org>
To: bug-gnu-emacs <at> gnu.org
Subject: 30.2; icomplete-vertical-mode flashes horizontal minibuffer prompt
 before expanding
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




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79420; Package emacs. (Sat, 13 Sep 2025 10:02:02 GMT) Full text and rfc822 format available.

Message #8 received at 79420 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Chris Roberts <frayedultrasonicaligator <at> disroot.org>,
 João Távora <joaotavora <at> gmail.com>
Cc: 79420 <at> debbugs.gnu.org
Subject: Re: bug#79420: 30.2;
 icomplete-vertical-mode flashes horizontal minibuffer prompt before
 expanding
Date: Sat, 13 Sep 2025 13:01:04 +0300
> Date: Wed, 10 Sep 2025 11:48:18 +0200
> From:  Chris Roberts via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
> 
> 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?

Thanks.

João, any comments or suggestions?




This bug report was last modified today.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.