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

From: Spencer Baugh <[email-masked]>
Date: Mon, 21 Jul 2025 12:43:56 -0400

Juri Linkov <[email-masked]> writes:

>>>>> 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.
>>>>
>>>> Thanks, now finally we'll have this long-needed feature.
>>
>> Hi, have you gotten a chance to try this out?
>
> Yes, I tried it out, and everything worked nicely.
>
>> I have been running this for several completion categories at my site
>> and have had no complaints.
>
> Could you send a complete patch with a NEWS entry?

Yes, attached.

>From 5c8290de611710c1859c65c37daf961507b20e62 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.  Slightly reword the
announcement of completion-eager-display for consistency.
---
 etc/NEWS           | 13 +++++++++-
 lisp/minibuffer.el | 59 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b469b22aacf..0c0f333319a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -167,11 +167,22 @@ 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.
+enables eager display 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'.
 
++++
+*** New user option 'completion-eager-update.
+This option configures whether typing should update the contents of the
+"*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'.
+
 +++
 *** New user option 'completion-pcm-leading-wildcard'.
 This option configures how the partial-completion style does completion.
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 571c4174537..12f66f57747 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -1072,6 +1072,21 @@ completion-eager-display
                  (const :tag "If requested by the completion command" auto))
   :version "31.1")
 
+(defcustom completion-eager-update 'auto
+  "Whether typing should update the *Completions* buffer eagerly.
+
+If `t', always update as you type.
+
+If `auto', only update if the completion table has requested it or
+`eager-update' is set in in `completion-category-defaults'.
+
+This only affects the *Completions* buffer if it is already
+displayed."
+  :type '(choice (const :tag "Do nothing when 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 +2663,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 +2790,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 +3179,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 +3206,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