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.