From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: sbaugh@catern.com Newsgroups: gmane.emacs.devel Subject: Re: Navigating completions from minibuffer Date: Sat, 18 Nov 2023 20:58:51 +0000 Message-ID: <87jzqen5h0.fsf@catern.com> References: <25929.50004.710119.599023@google.com> <868r7af3v6.fsf@mail.linkov.net> <25930.31126.454503.607723@google.com> <868r78bsnx.fsf@mail.linkov.net> <87y1f569c0.fsf@catern.com> <86fs1c3yml.fsf@mail.linkov.net> <864jhokelp.fsf@mail.linkov.net> <86il62tbfa.fsf@mail.linkov.net> <861qcpu0ft.fsf@mail.linkov.net> <861qcorh4c.fsf@mail.linkov.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="7836"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) To: emacs-devel@gnu.org Cancel-Lock: sha1:cfbKKKsldzfkbTlQmSot9Ys20UM= Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Nov 19 06:35:36 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1r4aT1-0001pk-Bp for ged-emacs-devel@m.gmane-mx.org; Sun, 19 Nov 2023 06:35:35 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r4aSR-0007mh-3k; Sun, 19 Nov 2023 00:34:59 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r4SPH-00074L-Rp for emacs-devel@gnu.org; Sat, 18 Nov 2023 15:59:11 -0500 Original-Received: from ciao.gmane.io ([116.202.254.214]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r4SPF-0001kD-Uj for emacs-devel@gnu.org; Sat, 18 Nov 2023 15:59:11 -0500 Original-Received: from list by ciao.gmane.io with local (Exim 4.92) (envelope-from ) id 1r4SPB-0001GY-RP for emacs-devel@gnu.org; Sat, 18 Nov 2023 21:59:05 +0100 X-Injected-Via-Gmane: http://gmane.org/ Received-SPF: pass client-ip=116.202.254.214; envelope-from=ged-emacs-devel@m.gmane-mx.org; helo=ciao.gmane.io X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Sun, 19 Nov 2023 00:34:55 -0500 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:312943 Archived-At: --=-=-= Content-Type: text/plain Spencer Baugh writes: > Juri Linkov writes: >>> Also... doesn't this mean that with >>> >>> (setq completion-show-help nil >>> completions-header-format nil) >>> >>> *Completions* will start out with the point on the first completion >>> candidate? So M-RET will select it, and with >>> minibuffer-visible-completions=t, even RET will select it? >>> >>> Maybe point should uniformly start at the end of *Completions* instead >>> of at the start? In the one character worth of whitespace we might add? >> >> Starting at the end of *Completions* would work only when >> completion-auto-wrap is t. Another variant is to add a narrow >> character at the beginning of *Completions* like used for >> an empty rectangular region in rectangle-mark-mode. > > Yes, that sounds good. And then we could uniformly move point before > the candidate when deselecting it, which IMO would be a little visually > nicer. OK, how about this? It's an adapted version of my completions-auto-update patch, which provides (for now) only the feature of deselecting the completion when point moves or the minibuffer changes. The infrastructure for doing this turns out to be basically identical, which is interesting. It sets completions-auto-update to 'deselect by default, which I think is reasonable? --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Deselect-the-selected-completion-candidate-when-typi.patch >From beb15769fdacc388a059e6bc79e924bc219bcb98 Mon Sep 17 00:00:00 2001 From: Spencer Baugh Date: Sat, 18 Nov 2023 20:55:18 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-visible-completions makes RET submit the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate. * lisp/minibuffer.el (completion--insert): Add a space before each candidate. (completions-auto-update, completions--deselect) (completions--update-if-displayed, completions--after-change) (minibuffer--old-point, completions--post-command): Add. (minibuffer-completion-help): Add completions--after-change and completions--post-command as hooks. (minibuffer-next-completion): Bind completions-auto-update to nil to avoid immediately deselecting the completion. --- lisp/minibuffer.el | 72 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index e0017d88780..026613c9eb2 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2196,15 +2196,18 @@ completion--insert (equal (or (car-safe str) str) selected)) (setq completion--selected-posn (point))) (if (not (consp str)) - (add-text-properties - (point) - (progn - (insert - (if group-fun - (funcall group-fun str 'transform) - str)) - (point)) - `(mouse-face highlight cursor-face ,completions-highlight-face completion--string ,str)) + (progn + ;; We move point to this character to deselect the completion candidate. + (insert " ") + (add-text-properties + (point) + (progn + (insert + (if group-fun + (funcall group-fun str 'transform) + str)) + (point)) + `(mouse-face highlight cursor-face ,completions-highlight-face completion--string ,str))) ;; If `str' is a list that has 2 elements, ;; then the second element is a suffix annotation. ;; If `str' has 3 elements, then the second element @@ -2391,6 +2394,49 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completions-auto-update 'deselect + "If non-nil, change the *Completions* buffer as you type. + +If `deselect', if a completion candidate in *Completions* is +selected (point is on it), it will be deselected (point will be +moved just before it) when the minibuffer point or contents +change. + +This only affects the *Completions* buffer if it is already +displayed." + :type '(choice (const :tag "*Completions* doesn't change as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" deselect)) + :version "30.1") + +(defun completions--deselect () + "If in a completion candidate, move just before the start of it." + (when (get-text-property (point) 'mouse-face) + (when (and (not (bobp)) (get-text-property (1- (point)) 'mouse-face)) + (goto-char (previous-single-property-change (point) 'mouse-face))) + (unless (bobp) + (backward-char 1)))) + +(defun completions--update-if-displayed () + "Update a displayed *Completions* buffer based on `completions-auto-update'" + (when completions-auto-update + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (when (eq completions-auto-update 'deselect) + (completions--deselect)))))) + +(defun completions--after-change () + "Update displayed *Completions* buffer after change in minibuffer contents." + (when (minibufferp) + (completions--update-if-displayed))) + +(defvar-local minibuffer--old-point nil) + +(defun completions--post-command () + "Update displayed *Completions* buffer after change in minibuffer point." + (when (and (minibufferp) (not (eq minibuffer--old-point (point)))) + (setq minibuffer--old-point (point)) + (completions--update-if-displayed))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2413,6 +2459,8 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'post-command-hook #'completions--post-command t) + (remove-hook 'after-change-hook #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2476,6 +2524,9 @@ minibuffer-completion-help (body-function . ,#'(lambda (window) (with-current-buffer mainbuf + (when completions-auto-update + (add-hook 'post-command-hook #'completions--post-command nil t) + (add-hook 'after-change-hook #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4688,7 +4739,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completions-auto-update nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) -- 2.42.1 --=-=-=--