From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Spencer Baugh Newsgroups: gmane.emacs.devel Subject: Re: Navigating completions from minibuffer Date: Sun, 26 Nov 2023 14:33:34 +0000 (UTC) Message-ID: <87ttp8vb37.fsf@catern.com> References: <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> <87jzqen5h0.fsf@catern.com> <86sf52tf0b.fsf@mail.linkov.net> <878r6tn6uf.fsf@catern.com> <874jhck2rn.fsf@catern.com> <8634wvk2me.fsf@mail.linkov.net> <87sf4tj1xw.fsf@catern.com> <86jzq521qs.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="17884"; mail-complaints-to="usenet@ciao.gmane.io" Cc: emacs-devel@gnu.org To: Juri Linkov Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Nov 26 15:34:34 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 1r7GDQ-0004Nd-Q2 for ged-emacs-devel@m.gmane-mx.org; Sun, 26 Nov 2023 15:34:33 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r7GCZ-0000iy-HL; Sun, 26 Nov 2023 09:33:39 -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 1r7GCY-0000iq-Af for emacs-devel@gnu.org; Sun, 26 Nov 2023 09:33:38 -0500 Original-Received: from s.wrqvwxzv.outbound-mail.sendgrid.net ([149.72.154.232]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1r7GCW-00051v-AG for emacs-devel@gnu.org; Sun, 26 Nov 2023 09:33:38 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=catern.com; h=from:subject:in-reply-to:references:mime-version:to:cc:content-type: cc:content-type:from:subject:to; s=s1; bh=FrV1OiBckW5c96Z37StDWrH7ccTsoXjJlIjMgsGPfdQ=; b=iI4g4V9LBYPh+XIR7PL4+isXAHomUWffisRA9sIMQxL+E26BBAVtwFNJbJQ7VpxT/+rV ZL2C0GE6TUQsuvtgnXwsC9M3S0IzMBphdJOQ9sAX6KDPjBOD4RKBZz1B7PdbAtccmvz5Gm XQMfJxFSJ+ss6thWrDQOueNCGyuBGVGgRc4UKiL8dY+ELempeQGOAti6p7FwAsBonY3htX asjUvTR3cKkt6f8098pOSEwocey2bxvM6HViNkdnSIm4WDIj6QEmZ3wHqytl0RHCPiuBka D36Q5wzQuw/93eHeAIQbO5SHslElPpJ4C4f3ICploJxGTWgMp8sWGJSTnPtkxz+A== Original-Received: by filterdrecv-d585b8d85-jgf7p with SMTP id filterdrecv-d585b8d85-jgf7p-1-6563573E-5 2023-11-26 14:33:34.179097612 +0000 UTC m=+3441220.191215181 Original-Received: from earth.catern.com (unknown) by geopod-ismtpd-44 (SG) with ESMTP id FrYRDoJcRJ-mynRir2SDyQ Sun, 26 Nov 2023 14:33:33.978 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=74.101.51.129; helo=localhost; envelope-from=sbaugh@catern.com; receiver=linkov.net Original-Received: from localhost (unknown [74.101.51.129]) by earth.catern.com (Postfix) with ESMTPSA id 09FD36362A; Sun, 26 Nov 2023 14:33:00 +0000 (UTC) In-Reply-To: <86jzq521qs.fsf@mail.linkov.net> X-SG-EID: =?us-ascii?Q?GW3oCMoYnalRiojMOuLzE6x2H5kORXvlCdz1UwQVRMVT4fbh9ODEfCogOe74cO?= =?us-ascii?Q?rI4e0V+MFZgakz9Re5a6=2FCgtj+HP8WwEiH71+uz?= =?us-ascii?Q?nFj8IbgxvPRZGTQMwrrmK6CLq1DA8Bjb77gTtqk?= =?us-ascii?Q?AicdQ3XsJvulzEJw4OwgsrducDRxLF2WWCj+Ne=2F?= =?us-ascii?Q?bwDXDRfGbm0N=2FrsRTtKKIoDN961FG1r7bEnhaIN?= =?us-ascii?Q?jfpNbd1n6C0eppAeO5PLlsH2wWk2wYN1D+6r0v?= X-Entity-ID: d/0VcHixlS0t7iB1YKCv4Q== Received-SPF: pass client-ip=149.72.154.232; envelope-from=bounces+21787432-489d-emacs-devel=gnu.org@em8926.catern.com; helo=s.wrqvwxzv.outbound-mail.sendgrid.net X-Spam_score_int: -7 X-Spam_score: -0.8 X-Spam_bar: / X-Spam_report: (-0.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_MSPIKE_H2=-0.001, RCVD_IN_VALIDITY_RPBL=1.31, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, UNPARSEABLE_RELAY=0.001 autolearn=no autolearn_force=no X-Spam_action: no action 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:313240 Archived-At: --=-=-= Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Juri Linkov writes: >> minibuffer-choose-completion-or-exit submits 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. > > Thanks, everything works nicely now except the special case > of completion-show-help=nil and completions-header-format=nil. > > Initially the first completion is deselected, and selects it. > But when the first key typed is M- the second completion > is selected instead of the first because M- moves to the > second column when point is between the first and second completions. I was thinking that issue is unrelated to this patch and so it could be fixed separately. But actually I guess this patch makes it more visible indeed, so I guess we need to fix it now. I think the "add a thin newline" approach is totally fine. I did that in my attached patch. > A possible workaround would be to check the special property > 'first-completion'. But then when the logic uses text properties, > why not to use more text properties that indicate that a candidate > is deselected (and remove highlighting in this case) instead of > moving point somewhere outside of the candidate? Even if we work around this with text properties, I think it's nice for deselection to be usually determined by location of point. I think that's easier to understand for the user - they should be able to deselect or reselect something purely by moving point around, it shouldn't require text property manipulation. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Deselect-the-selected-completion-candidate-when-typi.patch >From f4e2bdda0e30af933bee896aa7b5ca2b22684ab5 Mon Sep 17 00:00:00 2001 From: Spencer Baugh Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits 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 so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completion-auto-deselect, completions--deselect) (completions--after-change): Add. (minibuffer-completion-help): Add completions--after-change hook. (minibuffer-next-completion): Bind completion-auto-deselect to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. (display-completion-list): Guarantee a newline at the beginning of *Completions* to avoid ambiguity about candidate selection. * lisp/simple.el (choose-completion-deselect-if-after): Add. (choose-completion): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 43 +++++++++++++++++++++++++++++++++++++++---- lisp/simple.el | 11 ++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..303170125ff 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2310,8 +2310,11 @@ display-completion-list (with-current-buffer standard-output (goto-char (point-max)) - (when completions-header-format - (insert (format completions-header-format (length completions)))) + (if completions-header-format + (insert (format completions-header-format (length completions))) + (unless completion-show-help + ;; Ensure beginning-of-buffer isn't a completion. + (insert (propertize "\n" 'face '(:height 0))))) (completion--insert-strings completions group-fun))) (run-hooks 'completion-setup-hook) @@ -2378,6 +2381,33 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completion-auto-deselect t + "If non-nil, deselect the selected completion candidate when you type. + +A non-nil value means that after typing, point in *Completions* +will be moved off any completion candidates. This means +`minibuffer-choose-completion-or-exit' will exit with the +minibuffer's current contents, instead of a completion candidate." + :type '(choice (const :tag "Candidates in *Completions* stay selected as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" t)) + :version "30.1") + +(defun completions--deselect () + "If point is in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'mouse-face) + (goto-char (or (next-single-property-change (point) 'mouse-face) + (point-max))))) + +(defun completions--after-change (_start _end _old-len) + "Update displayed *Completions* buffer after change in buffer contents." + (when completion-auto-deselect + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (completions--deselect))))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2430,7 @@ 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 'after-change-functions #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2491,8 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completion-auto-deselect + (add-hook 'after-change-functions #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4706,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) + (completion-auto-deselect nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4755,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..bf57b285fca 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10094,6 +10094,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10114,6 +10119,10 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (if-let ((str (get-text-property (posn-point (event-start event)) 'completion--string))) + (substring-no-properties str) + (error "No completion here")) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10129,7 +10138,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 --=-=-=--