bug#81631: 32.0.50; setting completion-auto-select t when completions-format is 'vertical doesn't returns to the minibuffer when cycling

Stephen Berman via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]>
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
On Sat, 15 Aug 2026 09:10:23 -0400 Lucas Jimenez via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]> wrote:

> Hi,
>
> Setting completions-format 'vertical and completion-auto-select t
> doesn't cycle to the minibuffer when tapping TAB.
>
> This behavior is expected since it is the one 'one-column uses and also
> 'horizontal
>
> Reproduce with:
>
> emacs -Q --eval "(setq completions-format 'vertical
> completion-auto-select t)"

Thanks for this report; I overlooked this behavior of TAB when I made
the changes in 77ca60b48d01.  The attached patch adds this behavior to
the vertical format; can you confirm?

However, while testing I noticed two issues: (1) on entering the
minibuffer from the *Completions* buffer, typing TAB returns to the last
completion candidate rather then going the the first, thus breaking the
cycling behavior (likewise for S-TAB); I will file a separate bug report
about this and propose a fix.  (2) Navigation through the *Completions*
buffer has problems when the completions are annotated (e.g. with key
bindings); I haven't debugged this yet but when I do I'll file a
separate bug.

Steve Berman
(unnamed) (text/x-patch, 10.1 KB)
diff --git a/lisp/simple.el b/lisp/simple.el
index 1f7d57f299a..ac71cc5525b 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10352,111 +10352,122 @@ next-line-completion
 
 Also see the `completion-auto-wrap' variable."
   (interactive "p")
-  (let (line column pos found last first)
-    (when (and (bobp)
-               (> n 0)
-               (get-text-property (point) 'mouse-face)
-               (not (get-text-property (point) 'first-completion)))
-      (let ((inhibit-read-only t))
-        (add-text-properties (point) (1+ (point)) '(first-completion t)))
-      (setq n (1- n)))
+  (let ((tabcommand (member (this-command-keys) '("\t" [backtab])))
+        line column pos found last first)
+    (catch 'bound    
+      (when (and (bobp)
+                 (> n 0)
+                 (get-text-property (point) 'mouse-face)
+                 (not (get-text-property (point) 'first-completion)))
+        (let ((inhibit-read-only t))
+          (add-text-properties (point) (1+ (point)) '(first-completion t)))
+        (setq n (1- n)))
 
-    (if (get-text-property (point) 'mouse-face)
-        ;; If in a completion, move to the start of it.
-        (completion--move-to-candidate-start)
-      ;; Try to move to the previous completion.
-      (setq pos (previous-single-property-change (point) 'mouse-face))
-      (if pos
-          ;; Move to the start of the previous completion.
-          (progn
-            (goto-char pos)
-            (unless (get-text-property (point) 'mouse-face)
-              (goto-char (previous-single-property-change
-                          (point) 'mouse-face nil (point-min)))))
-        (cond ((> n 0) (setq n (1- n)) (first-completion))
-              ((< n 0) (first-completion)))))
+      (if (get-text-property (point) 'mouse-face)
+          ;; If in a completion, move to the start of it.
+          (completion--move-to-candidate-start)
+        ;; Try to move to the previous completion.
+        (setq pos (previous-single-property-change (point) 'mouse-face))
+        (if pos
+            ;; Move to the start of the previous completion.
+            (progn
+              (goto-char pos)
+              (unless (get-text-property (point) 'mouse-face)
+                (goto-char (previous-single-property-change
+                            (point) 'mouse-face nil (point-min)))))
+          (cond ((> n 0) (setq n (1- n)) (first-completion))
+                ((< n 0) (first-completion)))))
 
-    (while (> n 0)
-      (setq found nil pos (point) column (current-column)
-            line (line-number-at-pos)
-            last (= (point) (save-excursion (last-completion) (point))))
-      (if (and (eq completions-format 'vertical)
-               completion-auto-wrap last)
-          (first-completion)            ; Wrap from last to first item.
-        (completion--move-to-candidate-end)
-        (while (and (not found)
-                    (eq (forward-line 1) 0)
-                    (not (eobp))
-                    (move-to-column column))
-          (when (get-text-property (point) 'mouse-face)
-            (setq found t)))
-        (when (not found)
-          (if (and (not completion-auto-wrap)
-                   (if (eq completions-format 'vertical)
-                       (and (or last (get-text-property (point) 'mouse-face))
-                            (last-completion))
-                     (goto-char pos)))
-              t
-            (save-excursion
-              (setq pos nil)
-              (goto-char (point-min))
-              (when (and (eq (move-to-column column) column)
-                         (get-text-property (point) 'mouse-face))
-                (setq pos (point)))
-              (while (and (not pos) (> line (line-number-at-pos)))
-                (forward-line 1)
+      (while (> n 0)
+        (setq found nil pos (point) column (current-column)
+              line (line-number-at-pos)
+              last (= (point) (save-excursion (last-completion) (point))))
+        (if (and (eq completions-format 'vertical)
+                 completion-auto-wrap last)
+            (if (and (eq completion-auto-select t) tabcommand
+                     (minibufferp completion-reference-buffer))
+                (throw 'bound nil)      ; Skip to minibuffer.
+              (first-completion))       ; Wrap from last to first item.
+          (completion--move-to-candidate-end)
+          (while (and (not found)
+                      (eq (forward-line 1) 0)
+                      (not (eobp))
+                      (move-to-column column))
+            (when (get-text-property (point) 'mouse-face)
+              (setq found t)))
+          (when (not found)
+            (if (and (not completion-auto-wrap)
+                     (if (eq completions-format 'vertical)
+                         (and (or last (get-text-property (point) 'mouse-face))
+                              (last-completion))
+                       (goto-char pos)))
+                t
+              (save-excursion
+                (setq pos nil)
+                (goto-char (point-min))
                 (when (and (eq (move-to-column column) column)
                            (get-text-property (point) 'mouse-face))
-                  (setq pos (point)))))
-            (if pos (goto-char pos))
-            (when (eq completions-format 'vertical)
-              (next-column-completion 1)))))   ; Move to next column.
-      (setq n (1- n)))
+                  (setq pos (point)))
+                (while (and (not pos) (> line (line-number-at-pos)))
+                  (forward-line 1)
+                  (when (and (eq (move-to-column column) column)
+                             (get-text-property (point) 'mouse-face))
+                    (setq pos (point)))))
+              (if pos (goto-char pos))
+              (when (eq completions-format 'vertical)
+                (next-column-completion 1)))))   ; Move to next column.
+        (setq n (1- n)))
 
-    (while (< n 0)
-      (setq found nil pos (point) column (current-column)
-            line (line-number-at-pos)
-            first (= (point) (save-excursion (first-completion) (point))))
-      (if (and (eq completions-format 'vertical)
-               completion-auto-wrap first)
-          (last-completion)             ; Wrap from first to last item.
-        (completion--move-to-candidate-start)
-        (while (and (not found)
-                    (eq (forward-line -1) 0)
-                    (move-to-column column))
-          (when (get-text-property (point) 'mouse-face)
-            (setq found t)))
-        (when (not found)
-          (if (and (not completion-auto-wrap)
-                   (if (eq completions-format 'vertical)
-                       (and (or last first
-                                (get-text-property (point) 'mouse-face))
-                            first (first-completion))
-                     (goto-char pos)))
-              t
-            (save-excursion
-              (setq pos nil)
-              (goto-char (point-max))
-              (when (and (eq (move-to-column column) column)
-                         (get-text-property (point) 'mouse-face))
-                (setq pos (point)))
-              (while (and (not pos) (< line (line-number-at-pos)))
-                (forward-line -1)
+      (while (< n 0)
+        (setq found nil pos (point) column (current-column)
+              line (line-number-at-pos)
+              first (= (point) (save-excursion (first-completion) (point))))
+        (if (and (eq completions-format 'vertical)
+                 completion-auto-wrap first)
+            (if (and (eq completion-auto-select t) tabcommand
+                     (minibufferp completion-reference-buffer))
+                (throw 'bound nil)        ; Skip to minibuffer.
+              (last-completion))          ; Wrap from first to last item.
+          (completion--move-to-candidate-start)
+          (while (and (not found)
+                      (eq (forward-line -1) 0)
+                      (move-to-column column))
+            (when (get-text-property (point) 'mouse-face)
+              (setq found t)))
+          (when (not found)
+            (if (and (not completion-auto-wrap)
+                     (if (eq completions-format 'vertical)
+                         (and (or last first
+                                  (get-text-property (point) 'mouse-face))
+                              first (first-completion))
+                       (goto-char pos)))
+                t
+              (save-excursion
+                (setq pos nil)
+                (goto-char (point-max))
                 (when (and (eq (move-to-column column) column)
                            (get-text-property (point) 'mouse-face))
-                  (setq pos (point)))))
-            (if pos (goto-char pos))
-            (when (eq completions-format 'vertical)
-              (previous-column-completion 1)   ; Move to previous column.
-              (setq column (current-column))
-              ;; Move to last item in this column (previous column may
-              ;; have fewer items).
-              (while (not (eobp))
-                (move-to-column column)
-                (setq pos (point))
-                (forward-line))
-              (goto-char pos)))))
-      (setq n (1+ n)))))
+                  (setq pos (point)))
+                (while (and (not pos) (< line (line-number-at-pos)))
+                  (forward-line -1)
+                  (when (and (eq (move-to-column column) column)
+                             (get-text-property (point) 'mouse-face))
+                    (setq pos (point)))))
+              (if pos (goto-char pos))
+              (when (eq completions-format 'vertical)
+                (previous-column-completion 1)   ; Move to previous column.
+                (setq column (current-column))
+                ;; Move to last item in this column (previous column may
+                ;; have fewer items).
+                (while (not (eobp))
+                  (move-to-column column)
+                  (setq pos (point))
+                  (forward-line))
+                (goto-char pos)))))
+        (setq n (1+ n))))
+
+    (when (/= 0 n)
+      (switch-to-minibuffer))))
 
 (defun next-completion (&optional n)
   "Move according to `completions-format' to next completion item.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.