emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Navigating completions from minibuffer


From: sbaugh
Subject: Re: Navigating completions from minibuffer
Date: Thu, 23 Nov 2023 13:39:36 +0000 (UTC)
User-agent: Gnus/5.13 (Gnus v5.13)

Spencer Baugh <sbaugh@catern.com> writes:
>>> 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.
>>>
>>> * lisp/minibuffer.el (completion--insert): Add a space before each
>>> candidate.
>>
>> I don't think anyone would like such a space shifting the whole layout
>> to the right.  Rather I'd recommend to use a space after each candidate.
>> There is already a space between candidates.  Only at the end a space is
>> missing.
>>
>> Or without adding a space at the end we could change `choose-completion`
>> to not select the candidate when point is at the end
>
> Oh, yes, I definitely like the idea of not submitting the candidate when
> point is at the end, no need for any extra space.  This would work well
> with my thought about having M-RET to submitting even a "deselected"
> candidate: the new behavior of not submitting a candidate when point is
> at the end would only be active for the new command
> minibuffer-choose-completion-or-exit.

Here's an update patch with this approach.

>From 19344d19df371a7432ceb74f66c0f7ece9b4eed0 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <sbaugh@catern.com>
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 (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.
(minibuffer-choose-completion-or-exit): Bind
choose-completion-deselect-if-after so deselection takes effect.
* lisp/simple.el (choose-completion-deselect-if-after): Add.
(completions--get-posn): Check choose-completion-deselect-if-after.
---
 lisp/minibuffer.el | 57 ++++++++++++++++++++++++++++++++++++++++++++--
 lisp/simple.el     | 39 ++++++++++++++++++-------------
 2 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index dd29c155fd3..990a69b35bb 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2440,6 +2440,52 @@ 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 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--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))
+    (unless (and completions-auto-update
+                 (memq this-command '(minibuffer-next-completion 
minibuffer-previous-completion)))
+      (completions--update-if-displayed))))
+
 (defun minibuffer-completion-help (&optional start end)
   "Display a list of possible completions of the current minibuffer contents."
   (interactive)
@@ -2462,6 +2508,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
@@ -2526,6 +2574,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))
@@ -4739,7 +4790,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)
@@ -4787,7 +4839,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 34205a223a7..2043233738a 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10096,24 +10096,31 @@ 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 completions--get-posn (position)
   "Return the completion at POSITION as a string."
-  (save-excursion
-    (goto-char position)
-    (let (beg)
-      (cond
-       ((and (not (eobp))
-             (get-text-property (point) 'completion--string))
-        (setq beg (1+ (point))))
-       ((and (not (bobp))
-             (get-text-property (1- (point)) 'completion--string))
-        (setq beg (point))))
-      (when beg
-        (setq beg (or (previous-single-property-change
-                       beg 'completion--string)
-                      beg))
-        (substring-no-properties
-         (get-text-property beg 'completion--string))))))
+  (if choose-completion-deselect-if-after
+      (substring-no-properties (get-text-property position 
'completion--string))
+    (save-excursion
+      (goto-char position)
+      (let (beg)
+        (cond
+         ((and (not (eobp))
+               (get-text-property (point) 'completion--string))
+          (setq beg (1+ (point))))
+         ((and (not (bobp))
+               (get-text-property (1- (point)) 'completion--string))
+          (setq beg (point))))
+        (when beg
+          (setq beg (or (previous-single-property-change
+                         beg 'completion--string)
+                        beg))
+          (substring-no-properties
+           (get-text-property beg 'completion--string)))))))
 
 (defun choose-completion (&optional event no-exit no-quit)
   "Choose the completion at point.
-- 
2.42.1


reply via email to

[Prev in Thread] Current Thread [Next in Thread]