From 3f7c16d858e579ed03a195841ba9805fbc2899ba Mon Sep 17 00:00:00 2001 From: Spencer Baugh Date: Wed, 20 Aug 2025 14:27:59 -0400 Subject: [PATCH] Add minibuffer--completions-visible and use it At various places, instead of just checking that there's any window displaying a buffer named *Completions*, we should additionally check that that *Completions* buffer is actually for the current completion session. minibuffer--completions-visible does that. * lisp/comint.el (comint-complete-input-ring) (comint-dynamic-list-completions): Call minibuffer--completions-visible. * lisp/minibuffer.el (minibuffer--completions-visible): Add. (bug#77253) (completion--do-completion, completions--post-command-update) (completions--after-change, minibuffer-hide-completions) (minibuffer-visible-completions) (minibuffer-visible-completions--always-bind) (minibuffer-visible-completions--filter) (with-minibuffer-completions-window, minibuffer-complete-history) (minibuffer-complete-defaults): Call minibuffer--completions-visible. * lisp/pcomplete.el (pcomplete-show-completions): Call minibuffer--completions-visible. * lisp/simple.el (switch-to-completions): Call minibuffer--completions-visible. * test/lisp/minibuffer-tests.el (completion-auto-help-test) (completion-auto-select-test): Call minibuffer--completions-visible. --- lisp/comint.el | 4 ++-- lisp/minibuffer.el | 40 +++++++++++++++++++++-------------- lisp/pcomplete.el | 2 +- lisp/simple.el | 4 ++-- test/lisp/minibuffer-tests.el | 14 ++++++------ 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/lisp/comint.el b/lisp/comint.el index b9c910eff43..bbb9820c16a 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -1197,7 +1197,7 @@ This function makes `comint-dynamic-list-input-ring' obsolete." (ring-elements comint-input-ring) (user-error "No history available"))) (completion-in-region-mode-predicate - (lambda () (get-buffer-window "*Completions*" 0)))) + (lambda () (minibuffer--completions-visible)))) (completion-in-region (comint-line-beginning-position) (point-max) (completion-table-with-metadata @@ -3521,7 +3521,7 @@ The optional argument COMMON-SUBSTRING, if non-nil, should be a string specifying a common substring for adding the faces `completions-first-difference' and `completions-common-part' to the completions." - (let ((window (get-buffer-window "*Completions*" 0))) + (let ((window (minibuffer--completions-visible))) (setq completions (sort completions #'string-lessp)) (if (and (eq last-command this-command) window (window-live-p window) (window-buffer window) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 2dd5e09f8bb..3c80d606cfc 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1616,7 +1616,7 @@ when the buffer's text is already an exact match." (completed (cond ((pcase completion-auto-help - ('visible (get-buffer-window "*Completions*" 0)) + ('visible (minibuffer--completions-visible)) ('always t)) (minibuffer-completion-help beg end)) (t (minibuffer-hide-completions) @@ -2677,13 +2677,13 @@ so that the update is less likely to interfere with user typing." (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)) + (when (and completion-eager-update (minibuffer--completions-visible)) (completions--background-update))) (defun completions--after-change (_start _end _old-len) "Update displayed *Completions* buffer after change in buffer contents." (when (or completion-auto-deselect completion-eager-update) - (when-let* ((window (get-buffer-window "*Completions*" 0))) + (when-let* ((window (minibuffer--completions-visible))) (when completion-auto-deselect (with-selected-window window (completions--deselect))) @@ -2885,7 +2885,7 @@ so that the update is less likely to interfere with user typing." ;; FIXME: We could/should use minibuffer-scroll-window here, but it ;; can also point to the minibuffer-parent-window, so it's a bit tricky. (interactive) - (when-let* ((win (get-buffer-window "*Completions*" 0))) + (when-let* ((win (minibuffer--completions-visible))) (with-selected-window win ;; Move point off any completions, so we don't move point there ;; again the next time `minibuffer-completion-help' is called. @@ -3332,18 +3332,26 @@ and `RET' accepts the input typed into the minibuffer." (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 current *Completions* buffer is visible, if any." + (when-let* ((window (get-buffer-window "*Completions*" 0))) + (when (eq (buffer-local-value 'completion-reference-buffer + (window-buffer window)) + ;; If there's no active minibuffer, we call + ;; `window-buffer' on nil, assuming that completion is + ;; happening in the selected window. + (window-buffer (active-minibuffer-window))) + window))) + (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) @@ -5107,10 +5115,10 @@ the minibuffer was activated, and execute the forms." When used in a minibuffer window, select the window with completions, and execute the forms." (declare (indent 0) (debug t)) - `(let ((window (or (get-buffer-window "*Completions*" 0) + `(let ((window (or (minibuffer--completions-visible) ;; Make sure we have a completions window. (progn (minibuffer-completion-help) - (get-buffer-window "*Completions*" 0))))) + (minibuffer--completions-visible))))) (when window (with-selected-window window (completion--lazy-insert-strings) @@ -5205,7 +5213,7 @@ inputs for the prompting command, instead of the default completion table." (user-error "No history available")))) ;; FIXME: Can we make it work for CRM? (let ((completion-in-region-mode-predicate - (lambda () (get-buffer-window "*Completions*" 0)))) + (lambda () (minibuffer--completions-visible)))) (completion-in-region (minibuffer--completion-prompt-end) (point-max) (completion-table-with-metadata @@ -5223,7 +5231,7 @@ provided by the prompting command, instead of the completion table." minibuffer-default (funcall minibuffer-default-add-function))) (let ((completions (ensure-list minibuffer-default)) (completion-in-region-mode-predicate - (lambda () (get-buffer-window "*Completions*" 0)))) + (lambda () (minibuffer--completions-visible)))) (completion-in-region (minibuffer--completion-prompt-end) (point-max) (completion-table-with-metadata diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el index 43d149d5c90..c3b7f9d52d3 100644 --- a/lisp/pcomplete.el +++ b/lisp/pcomplete.el @@ -1150,7 +1150,7 @@ Typing SPC flushes the help buffer." ((or (eq event 'tab) ;; Needed on a terminal (eq event 9)) - (let ((win (or (get-buffer-window "*Completions*" 0) + (let ((win (or (minibuffer--completions-visible) (display-buffer "*Completions*" 'not-this-window)))) (with-selected-window win diff --git a/lisp/simple.el b/lisp/simple.el index f7f059793ca..b0f6621b37e 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10598,10 +10598,10 @@ to move point between completions.\n\n"))))))) (defun switch-to-completions () "Select the completion list window." (interactive) - (when-let* ((window (or (get-buffer-window "*Completions*" 0) + (when-let* ((window (or (minibuffer--completions-visible) ;; Make sure we have a completions window. (progn (minibuffer-completion-help) - (get-buffer-window "*Completions*" 0))))) + (minibuffer--completions-visible))))) (select-window window) (completion--lazy-insert-strings) (when (bobp) diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el index 59b72899e22..c2c37e63012 100644 --- a/test/lisp/minibuffer-tests.el +++ b/test/lisp/minibuffer-tests.el @@ -454,21 +454,21 @@ '("a" "ab" "ac") (execute-kbd-macro (kbd "a TAB TAB")) (should (equal (car messages) "Complete, but not unique")) - (should-not (get-buffer-window "*Completions*" 0)) + (should-not (minibuffer--completions-visible)) (execute-kbd-macro (kbd "b TAB")) (should (equal (car messages) "Sole completion")))) (let ((completion-auto-help t)) (completing-read-with-minibuffer-setup '("a" "ab" "ac") (execute-kbd-macro (kbd "a TAB TAB")) - (should (get-buffer-window "*Completions*" 0)) + (should (minibuffer--completions-visible)) (execute-kbd-macro (kbd "b TAB")) (should (equal (car messages) "Sole completion")))) (let ((completion-auto-help 'visible)) (completing-read-with-minibuffer-setup '("a" "ab" "ac" "achoo") (execute-kbd-macro (kbd "a TAB TAB")) - (should (get-buffer-window "*Completions*" 0)) + (should (minibuffer--completions-visible)) (execute-kbd-macro (kbd "ch TAB")) (should (equal (car messages) "Sole completion"))))))) @@ -477,19 +477,19 @@ (completing-read-with-minibuffer-setup '("aa" "ab" "ac") (execute-kbd-macro (kbd "a TAB")) - (should (and (get-buffer-window "*Completions*" 0) + (should (and (minibuffer--completions-visible) (eq (current-buffer) (get-buffer "*Completions*")))) (execute-kbd-macro (kbd "TAB TAB TAB")) - (should (and (get-buffer-window "*Completions*" 0) + (should (and (minibuffer--completions-visible) (eq (current-buffer) (get-buffer " *Minibuf-1*")))) (execute-kbd-macro (kbd "S-TAB")) - (should (and (get-buffer-window "*Completions*" 0) + (should (and (minibuffer--completions-visible) (eq (current-buffer) (get-buffer "*Completions*")))))) (let ((completion-auto-select 'second-tab)) (completing-read-with-minibuffer-setup '("aa" "ab" "ac") (execute-kbd-macro (kbd "a TAB")) - (should (and (get-buffer-window "*Completions*" 0) + (should (and (minibuffer--completions-visible) (not (eq (current-buffer) (get-buffer "*Completions*"))))) (execute-kbd-macro (kbd "TAB TAB")) (should (eq (current-buffer) (get-buffer "*Completions*"))))))