From 4e2c70272f4fbb686fd9aa4168b058ca1ad6f922 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Tue, 5 Apr 2022 22:20:16 +0300 Subject: [PATCH] New commands for navigating completions from the minibuffer. * lisp/simple.el (minibuffer-local-shell-command-map): * lisp/minibuffer.el (minibuffer-local-completion-map): Bind "M-" to minibuffer-choose-previous-completion, "M-" to minibuffer-choose-next-completion, "M-S-" to minibuffer-previous-completion, "M-S-" to minibuffer-next-completion, "M-RET" to minibuffer-choose-completion. (with-minibuffer-completions-window): New macro. (minibuffer-previous-completion, minibuffer-next-completion) (minibuffer-choose-previous-completion) (minibuffer-choose-next-completion) (minibuffer-choose-completion): New commands. https://lists.gnu.org/archive/html/emacs-devel/2022-03/msg00335.html --- etc/NEWS | 11 ++++++++ lisp/minibuffer.el | 65 +++++++++++++++++++++++++++++++++++++++++++++- lisp/simple.el | 5 ++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index b567caedb35..6b7bb7a18eb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -601,6 +601,17 @@ value. ** Minibuffer and Completions +*** New commands for navigating completions from the minibuffer. +When the minibuffer is the current buffer, typing 'M-' or +'M-' selects a previous/next completion candidate from the +"*Completions*" buffer and inserts it to the minibuffer. +'M-S-' and 'M-S-' do the same, but without inserting +a completion candidate to the minibuffer, then 'M-RET' can be used +to choose the currently active candidate from the "*Completions*" +buffer and exit the minibuffer. With a prefix argument, 'C-u M-RET' +inserts the currently active candidate to the minibuffer, but doesn't +exit the minibuffer. + +++ *** The "*Completions*" buffer can now be automatically selected. To enable this behavior, customize the user option diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 393555fc62f..8a0e15ce05d 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2748,7 +2748,12 @@ The completion method is determined by `completion-at-point-functions'." "?" #'minibuffer-completion-help "" #'switch-to-completions "M-v" #'switch-to-completions - "M-g M-c" #'switch-to-completions) + "M-g M-c" #'switch-to-completions + "M-" #'minibuffer-choose-previous-completion + "M-" #'minibuffer-choose-next-completion + "M-S-" #'minibuffer-previous-completion + "M-S-" #'minibuffer-next-completion + "M-RET" #'minibuffer-choose-completion) (defvar-keymap minibuffer-local-must-match-map :doc "Local keymap for minibuffer input with completion, for exact match." @@ -4338,6 +4343,64 @@ the minibuffer was activated, and execute the forms." (with-minibuffer-selected-window (scroll-other-window-down arg))) +(defmacro with-minibuffer-completions-window (&rest body) + "Execute the forms in BODY from the minibuffer in its completions window. +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) + ;; Make sure we have a completions window. + (progn (minibuffer-completion-help) + (get-buffer-window "*Completions*" 0))))) + (when window + (with-selected-window window + ,@body)))) + +(defun minibuffer-previous-completion (&optional n) + "Run `previous-completion' from the minibuffer in its completions window." + (interactive "p") + (with-minibuffer-completions-window + (let ((completion-wrap-movement nil)) + (when completions-highlight-face + (setq-local cursor-face-highlight-nonselected-window t)) + (previous-completion n)))) + +(defun minibuffer-next-completion (&optional n) + "Run `next-completion' from the minibuffer in its completions window." + (interactive "p") + (with-minibuffer-completions-window + (let ((completion-wrap-movement nil)) + (when completions-highlight-face + (setq-local cursor-face-highlight-nonselected-window t)) + (next-completion n)))) + +(defun minibuffer-choose-previous-completion (&optional n) + "Run `previous-completion' from the minibuffer in its completions window. +Also insert the selected completion to the minibuffer." + (interactive "p") + (minibuffer-previous-completion n) + (minibuffer-choose-completion t t)) + +(defun minibuffer-choose-next-completion (&optional n) + "Run `next-completion' from the minibuffer in its completions window. +Also insert the selected completion to the minibuffer." + (interactive "p") + (minibuffer-next-completion n) + (minibuffer-choose-completion t t)) + +(defun minibuffer-choose-completion (&optional no-exit no-quit) + "Run `choose-completion' from the minibuffer in its completions window. +With prefix argument NO-EXIT, insert the completion at point to the +minibuffer, but don't exit the minibuffer. When the prefix argument +is not provided, then whether to exit the minibuffer depends on the value +of `completion-no-auto-exit'. +If NO-QUIT is non-nil, insert the completion at point to the +minibuffer, but don't quit the completions window." + (interactive "P") + (with-minibuffer-completions-window + (let ((completion-use-base-affixes t)) + (choose-completion nil no-exit no-quit)))) + (defcustom minibuffer-default-prompt-format " (default %s)" "Format string used to output \"default\" values. When prompting for input, there will often be a default value, diff --git a/lisp/simple.el b/lisp/simple.el index 5bf1c32e1db..ef520065011 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -3892,6 +3892,11 @@ to the end of the list of defaults just after the default value." (let ((map (make-sparse-keymap))) (set-keymap-parent map minibuffer-local-map) (define-key map "\t" 'completion-at-point) + (define-key map [M-up] 'minibuffer-choose-previous-completion) + (define-key map [M-down] 'minibuffer-choose-next-completion) + (define-key map [M-S-up] 'minibuffer-previous-completion) + (define-key map [M-S-down] 'minibuffer-next-completion) + (define-key map [?\M-\r] 'minibuffer-choose-completion) map) "Keymap used for completing shell commands in minibuffer.") -- 2.39.5