Subject: bug#77649: [PATCH] Add support for updating *Completions* as you type

From: Spencer Baugh <[email-masked]>
Date: Tue, 22 Jul 2025 15:27:53 -0400

Eli Zaretskii <[email-masked]> writes:
>> From: Spencer Baugh <[email-masked]>
>> Cc: [email-masked],  [email-masked],  [email-masked],
>>    [email-masked]
>> Date: Tue, 22 Jul 2025 12:32:18 -0400
>> 
>> Eli Zaretskii <[email-masked]> writes:
>> 
>> >> Cc: [email-masked], [email-masked],
>> >>  Daniel Mendler <[email-masked]>
>> >> Date: Mon, 21 Jul 2025 12:43:56 -0400
>> >> From:  Spencer Baugh via "Bug reports for GNU Emacs,
>> >>  the Swiss army knife of text editors" <[email-masked]>
>> >> 
>> >> +*** New user option 'completion-eager-update.
>> >                                                ^
>> > Closing quote missing there.
>> >
>> >> +This option configures whether typing should update the contents of the
>> >                ^^^^^^^^^^
>> > We normally use "controls".
>> >
>> >> +"*Completions*" buffer, if it is shown.  When the option is set to t,
>> >> +typing will always update the "*Completions*" buffer.  Note that for
>> >> +large or inefficient completion tables, this can slow down typing.  The
>> >> +default setting enables eager updating only if requested by the command.
>> >> +For more fine-grained control you can also toggle this feature by
>> >> +category using the symbol 'eager-update in the user option
>> >> +'completion-category-overrides'.
>> >
>> > IMO, the last two sentences are too vague/mysterious to be useful
>> > (this is a user option, remember?)
>> 
>> These two sentences are just copied (with a minor change) from the
>> announcement for the very similar option completion-eager-display, just
>> above.  Should I reword that one as well in this commit?
>
> If you wish, yes.

Updated and fixed your feedback.

>From 6e6d38974ad142882fd9919f452ae311b418a712 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Thu, 3 Apr 2025 15:38:22 -0400
Subject: [PATCH] Add support for updating *Completions* as you type

Add support for updating the *Completions* buffer as you type,
controlled by a new completion metadata symbol 'eager-update and
new defcustom completion-eager-update.

You can configure a completion category to update *Completions*
as you type by setting completion-category-overrides
appropriately; or set completion-eager-update to t to always
update *Completions* as you type.

This is similar to the recently added completion-eager-display.

* lisp/minibuffer.el (completion-eager-update): Add new
defcustom defaulting to 'auto. (bug#77649)
(completion--eager-update-p, completions--background-update)
(completions--post-command-update): Add.
(completions--after-change): Call
completions--post-command-update via post-command-hook.
(minibuffer-completion-help): Check completion-eager-update and
install completions--after-change.
(completion-help-at-point): Call completion--eager-update-p if
ONLY-IF-EAGER is non-nil.
* etc/NEWS: Announce completion-eager-update.  Reword the
announcement of completion-eager-display for consistency.
---
 etc/NEWS           | 29 +++++++++++++++-------
 lisp/minibuffer.el | 62 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 76 insertions(+), 15 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 2d904614153..f2a08ee3009 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -173,15 +173,26 @@ this behavior is now off by default.  You can re-enable it by setting
 ** Minibuffer and Completions
 
 +++
-*** New user option 'completion-eager-display'.
-This option configures whether completion commands should display the
-"*Completions*" buffer immediately.  When the option is set to t, all
-completion commands show "*Completions*" immediately, respectively nil
-disables the eager display for all commands.  The default setting auto
-enables eager completion only if requested by the command.
-For more fine-grained control you can also toggle this feature by
-category using the symbol 'eager-display' in the user option
-'completion-category-overrides'.
+*** Support for immediate display of the "*Completions*" buffer
+Whenever a minibuffer with completion is opened, the "*Completions*"
+buffer will now be displayed immediately if the completion property
+'eager-display', set by the completion table, is non-nil.  This property
+can be overridden for different completion categories by customizing
+'completion-category-overrides'.  Alternatively, the new user option
+'completion-eager-display' can be set to t to force eager display of
+"*Completions*" for all minibuffers, or nil to suppress this for all
+minibuffers.
+
++++
+*** Support for updating "*Completions*" as you type
+If the "*Completions*" buffer is displayed, it will now be updated as
+you type if the completion property 'eager-update', set by the
+completion table, is non-nil.  This property can be overridden for
+different completion categories by customizing
+'completion-category-overrides'.  Alternatively, the new user option
+'completion-eager-update can be set to t to make "*Completions*" always
+be updated as you type, or nil to suppress this always.  Note that for
+large or inefficient completion tables this can slow down typing.
 
 +++
 *** New user option 'completion-pcm-leading-wildcard'.
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 571c4174537..ea2546f7d2c 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -1072,6 +1072,24 @@ completion-eager-display
                  (const :tag "If requested by the completion command" auto))
   :version "31.1")
 
+(defcustom completion-eager-update 'auto
+  "Whether the *Completions* buffer should update as you type.
+
+If `t', always update as you type.
+
+If `auto', only update if the completion property 'eager-update' is
+non-nil, whether set by the completion table or by
+`completion-category-overrides' or by `completion-category-defaults'.
+
+If nil, never update as you type.
+
+This only affects the *Completions* buffer if it is already
+displayed."
+  :type '(choice (const :tag "Don't update as you type" nil)
+                 (const :tag "Auto-update based on the category" auto)
+                 (const :tag "Always update as you type" t))
+  :version "31.1")
+
 (defcustom completion-auto-help t
   "Non-nil means automatically provide help for invalid completion input.
 If the value is t, the *Completions* buffer is displayed whenever completion
@@ -2648,12 +2666,43 @@ completions--deselect
     (goto-char (or (next-single-property-change (point) 'completion--string)
                    (point-max)))))
 
+(defun completion--eager-update-p (start)
+  "Return non-nil if *Completions* should be automatically updated.
+
+If `completion-eager-update' is the symbol `auto', checks completion
+metadata for the string from START to point."
+  (if (eq completion-eager-update 'auto)
+      (completion-metadata-get (completion--field-metadata start) 'eager-update)
+    completion-eager-update))
+
+(defun completions--background-update ()
+  "Try to update *Completions* without blocking input.
+
+This function uses `while-no-input' and sets `non-essential' to t
+so that the update is less likely to interfere with user typing."
+  (while-no-input
+    (let ((non-essential t))
+      (redisplay)
+      (cond
+       (completion-in-region-mode (completion-help-at-point t))
+       ((completion--eager-update-p (minibuffer-prompt-end))
+        (minibuffer-completion-help))))))
+
+(defun completions--post-command-update ()
+  "Update displayed *Completions* buffer after command, once."
+  (remove-hook 'post-command-hook #'completions--post-command-update)
+  (when (and completion-eager-update (get-buffer-window "*Completions*" 0))
+    (completions--background-update)))
+
 (defun completions--after-change (_start _end _old-len)
   "Update displayed *Completions* buffer after change in buffer contents."
-  (when completion-auto-deselect
+  (when (or completion-auto-deselect completion-eager-update)
     (when-let* ((window (get-buffer-window "*Completions*" 0)))
-      (with-selected-window window
-        (completions--deselect)))))
+      (when completion-auto-deselect
+        (with-selected-window window
+          (completions--deselect)))
+      (when completion-eager-update
+        (add-hook 'post-command-hook #'completions--post-command-update)))))
 
 (defun minibuffer-completion-help (&optional start end)
   "Display a list of possible completions of the current minibuffer contents."
@@ -2744,7 +2793,7 @@ minibuffer-completion-help
             (body-function
              . ,#'(lambda (window)
                     (with-current-buffer mainbuf
-                      (when completion-auto-deselect
+                      (when (or completion-auto-deselect completion-eager-update)
                         (add-hook 'after-change-functions #'completions--after-change nil t))
                       ;; Remove the base-size tail because `sort' requires a properly
                       ;; nil-terminated list.
@@ -3133,7 +3182,7 @@ completion-at-point
                   (car res)))
        (cdr res)))))
 
-(defun completion-help-at-point ()
+(defun completion-help-at-point (&optional only-if-eager)
   "Display the completions on the text around point.
 The completion method is determined by `completion-at-point-functions'."
   (interactive)
@@ -3160,7 +3209,8 @@ completion-help-at-point
                `(,start ,(copy-marker end t) ,collection
                         ,(plist-get plist :predicate)))
          (completion-in-region-mode 1)
-         (minibuffer-completion-help start end)))
+         (when (or (not only-if-eager) (completion--eager-update-p start))
+           (minibuffer-completion-help start end))))
       (`(,hookfun . ,_)
        ;; The hook function already performed completion :-(
        ;; Not much we can do at this point.
-- 
2.39.3