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

Lucas Jimenez via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]>
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
This one it cycled to the minibuffer after last candidate, but then it got stucked cycling between the minibuffer and the last candidate not returning to the first cycle to cycle all over again.  On another instance it cycled, but it went like this: last_candidate->minibuffer->closes the minibuffer->opens the minibuffer to select first candidate and cycle again.  The difference is that in the first I had typed a character to complete and in the later it didn’t have anything typed to complete.


> On 16-08-2026, at 12:05, Stephen Berman <[email protected]> wrote:
> 
> 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] <mailto:[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
> 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.