Subject: bug#77253: 30.1.50; Support RET choosing the selected completion without rebinding arrow keys

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

Juri Linkov <[email-masked]> writes:

>>>>>>> Not sure if it's possible for RET to accept the selected candidate
>>>>>>> by default since users might prefer to accept text in the minibuffer.
>>>>>>
>>>>>> If there's a selected candidate, though, then users have already decided
>>>>>> to use M-<up>/M-<down> to interact with completions.  (Or they've
>>>>>> switched to the completions buffer and selected one)
>>>>>>
>>>>>> If they decide they don't want the candidate they selected, and want to
>>>>>> continue with text in the minibuffer, completion-auto-deselect will
>>>>>> automatically deselect the candidate if they type anything.
>>>>>>
>>>>>> I think these two facts combined make it possible for RET to accept the
>>>>>> selected candidate by default.
>>>>>
>>>>> Makes sense.  So we need to try how well it performs.
>>>>
>>>> How about this?
>>>
>>> Thanks, will try to use for a while.
>>
>> Any feedback on the patch?
>
> Everything looks good.  Could you send the final version of the patch?

Yes, attached.

(Note that this doesn't do the change I mentioned earlier of replacing
(get-buffer-window "*Completions*" 0) everywhere with
minibuffer--completions-visible, which probably should be done in a
separate patch, I can send that if you're up to install it now)

>From df5c9ffe4669bd8417bb5ae3d631723e21e70d68 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Mon, 21 Jul 2025 12:22:19 -0400
Subject: [PATCH] Make RET in minibuffer choose selected completion

Previously, one could select a completion via M-<up>/M-<down>,
but then RET would not actually select the chosen completion.
With the addition of completion-auto-deselect, this is not
actually necessary: we can reasonably assume that when a
completion is selected, the user wants to use that, since their
last action must have been to select it.  So, just choose the
selected completion on RET.

* lisp/minibuffer.el (minibuffer--completions-visible)
(minibuffer-choose-completion-if-selected): Add.
(minibuffer-choose-completion-or-exit): Use the more reliable
minibuffer-choose-completion-if-selected instead of a
condition-case.
(minibuffer-local-must-match-map): Bind RET to
minibuffer-choose-completion-or-exit instead of
minibuffer-complete-and-exit. (bug#77253)
(minibuffer-choose-completion-just-exit): Add.
(minibuffer-local-completion-map): Bind RET to
minibuffer-choose-completion-just-exit instead of
exit-minibuffer.
(minibuffer-visible-completions--filter): Use
minibuffer--completions-visible.
---
 lisp/minibuffer.el | 47 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 34 insertions(+), 13 deletions(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 64e3ad53b42..7a7f6aae44b 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -3232,6 +3232,12 @@ completion-help-at-point
   (define-key map "\n" 'exit-minibuffer)
   (define-key map "\r" 'exit-minibuffer))
 
+(defun minibuffer-choose-completion-just-exit (&optional no-exit)
+  "Choose the selected completion from the minibuffer or call `exit-minibuffer'."
+  (interactive "P")
+  (or (minibuffer-choose-completion-if-selected no-exit)
+      (exit-minibuffer)))
+
 (defvar-keymap minibuffer-local-completion-map
   :doc "Local keymap for minibuffer input with completion."
   :parent minibuffer-local-map
@@ -3241,6 +3247,7 @@ minibuffer-local-completion-map
   ;; another binding for it.
   ;; "M-TAB"  #'minibuffer-force-complete
   "SPC"       #'minibuffer-complete-word
+  "RET"       #'minibuffer-choose-completion-just-exit
   "?"         #'minibuffer-completion-help
   "<prior>"   #'switch-to-completions
   "M-v"       #'switch-to-completions
@@ -3252,7 +3259,7 @@ minibuffer-local-completion-map
 (defvar-keymap minibuffer-local-must-match-map
   :doc "Local keymap for minibuffer input with completion, for exact match."
   :parent minibuffer-local-completion-map
-  "RET" #'minibuffer-complete-and-exit
+  "RET" #'minibuffer-choose-completion-or-exit
   "C-j" #'minibuffer-complete-and-exit)
 
 (defvar-keymap minibuffer-local-filename-completion-map
@@ -3349,18 +3356,34 @@ minibuffer-visible-completions
 (defvar minibuffer-visible-completions--always-bind nil
   "If non-nil, force the `minibuffer-visible-completions' bindings on.")
 
+(defun minibuffer--completions-visible ()
+  "Return the window where the *Completions* buffer for this minibuffer is visible."
+  (when-let ((window (get-buffer-window "*Completions*" 0)))
+    (when (eq (buffer-local-value 'completion-reference-buffer
+                                  (window-buffer window))
+              (window-buffer (active-minibuffer-window)))
+      window)))
+
+(defun minibuffer-choose-completion-if-selected (&optional no-exit no-quit)
+  "Like `minibuffer-choose-completion', but do nothing if no candidate is selected.
+
+Return non-nil if a completion was chosen."
+  (when-let* ((window (minibuffer--completions-visible)))
+    (with-selected-window window
+      ;; Detect selection as if `choose-completion-deselect-if-after' is non-nil.
+      (when (get-text-property (point) 'completion--string)
+        (choose-completion nil no-exit no-quit)
+        t))))
+
 (defun minibuffer-visible-completions--filter (cmd)
   "Return CMD if `minibuffer-visible-completions' bindings should be active."
   (if minibuffer-visible-completions--always-bind
       cmd
-    (when-let ((window (get-buffer-window "*Completions*" 0)))
-      (when (and (eq (buffer-local-value 'completion-reference-buffer
-                                         (window-buffer window))
-                     (window-buffer (active-minibuffer-window)))
-                 (if (eq cmd #'minibuffer-choose-completion-or-exit)
-                     (with-current-buffer (window-buffer window)
-                       (get-text-property (point) 'completion--string))
-                   t))
+    (when-let ((window (minibuffer--completions-visible)))
+      (when (if (eq cmd #'minibuffer-choose-completion-or-exit)
+                (with-current-buffer (window-buffer window)
+                  (get-text-property (point) 'completion--string))
+              t)
         cmd))))
 
 (defun minibuffer-visible-completions--bind (binding)
@@ -5211,10 +5234,8 @@ minibuffer-choose-completion-or-exit
 in the completions window, then exit the minibuffer using its present
 contents."
   (interactive "P")
-  (condition-case nil
-      (let ((choose-completion-deselect-if-after t))
-        (minibuffer-choose-completion no-exit no-quit))
-    (error (minibuffer-complete-and-exit))))
+  (or (minibuffer-choose-completion-if-selected no-exit no-quit)
+      (minibuffer-complete-and-exit)))
 
 (defun minibuffer-complete-history ()
   "Complete as far as possible using the minibuffer history.
-- 
2.39.3