emacs-devel
[Top][All Lists]
Advanced

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

Re: Suggestions for improvements to the *Completions* buffer


From: Philip Kaludercic
Subject: Re: Suggestions for improvements to the *Completions* buffer
Date: Sat, 18 Dec 2021 12:22:10 +0000

Philip Kaludercic <philipk@posteo.net> writes:

>>> The question is whether or not there is a need for a separate
>>> switch-to-minibuffer binding (besides M-g M-c)?  Is keeping the
>>> completions buffer active while returning to the minibuffer a real need?
>>
>> Good question.  If four key bindings (q, z, C-g, ESC ESC ESC)
>> all will close the completions (first two using quit-window,
>> and last two using delete-completion-window), then maybe we should have
>> an easy-to-type keybinding that will switch to the minibuffer without
>> closing the completions window?
>>
>> There is an easy-to-type keys <PgUp> and M-v to switch to the
>> completions window, but no an easy-to-type key to switch back.
>>
>> It seems wrong for `q' to switch to the minibuffer without closing
>> the window because `quit-window' implies that window should quit.
>> But what key to use instead, I have no idea.
>
> Or to take inspiration from Protesilaos's MCT package, that switches
> back to the minibuffer once next-completion and previous-completion
> reaches the end/beginning of the buffer, without quitting the window.
> With next-completion bound to TAB while TAB also jumps back to the
> completion buffer, it would behave to just by cycling.

Here are the patches that would implement this behaviour.  I have to use
it for a bit longer before I can say if it is preferable, but I guess if
something along these lines were to be applied, this could be an option.

The mechanism used to make backtab in the minibuffer switch to the last
completion option is not that nice as it uses this-command and
this-command-keys.

>From 0cc155ee3395fab376b06838a79c9a8b6fee6378 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Thu, 9 Dec 2021 17:34:54 +0100
Subject: [PATCH 1/3] Allow for the completion buffer to be automatically
 selected

* lisp/simple.el (completion-auto-select): Add new option.
(completion-setup-function): Respect completion-auto-select.
---
 lisp/simple.el | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lisp/simple.el b/lisp/simple.el
index 84928caa31..213720784c 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -9226,6 +9226,12 @@ completion-show-help
   :version "22.1"
   :group 'completion)
 
+(defcustom completion-auto-select t
+  "Non-nil means to automatically select the completions buffer."
+  :type 'boolean
+  :version "29.1"
+  :group 'completion)
+
 ;; This function goes in completion-setup-hook, so that it is called
 ;; after the text of the completion list buffer is written.
 (defun completion-setup-function ()
@@ -9262,7 +9268,9 @@ completion-setup-function
            (insert "Click on a completion to select it.\n"))
        (insert (substitute-command-keys
                 "In this buffer, type \\[choose-completion] to \
-select the completion near point.\n\n"))))))
+select the completion near point.\n\n")))))
+  (when completion-auto-select
+    (switch-to-completions)))
 
 (add-hook 'completion-setup-hook #'completion-setup-function)
 
-- 
2.30.2

>From 1b53b0c60c830c5e9173f143e4ed7e4719288ec9 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Thu, 9 Dec 2021 17:26:14 +0100
Subject: [PATCH 2/3] Allow for next-completion to wrap around the completion
 buffer

* lisp/simple.el (completion-wrap-movement): Add new option.
(previous-completion): Update docstring.
(next-completion): Respect completion-wrap-movement.
(switch-to-completions): Handle backwards completion by jumping to the
end of the buffer.
* lisp/minibuffer.el: (minibuffer-local-completion-map): Bind
minibuffer-complete to backtab
---
 lisp/minibuffer.el |  1 +
 lisp/simple.el     | 81 ++++++++++++++++++++++++++++++----------------
 2 files changed, 54 insertions(+), 28 deletions(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 28bd1df59a..5f831665eb 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2651,6 +2651,7 @@ minibuffer-local-completion-map
   (let ((map (make-sparse-keymap)))
     (set-keymap-parent map minibuffer-local-map)
     (define-key map "\t" 'minibuffer-complete)
+    (define-key map [backtab] 'minibuffer-complete)
     ;; M-TAB is already abused for many other purposes, so we should find
     ;; another binding for it.
     ;; (define-key map "\e\t" 'minibuffer-force-complete)
diff --git a/lisp/simple.el b/lisp/simple.el
index 213720784c..6a7d7ba918 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -9027,38 +9027,57 @@ delete-completion-window
       (if (get-buffer-window buf)
          (select-window (get-buffer-window buf))))))
 
+(defcustom completion-wrap-movement t
+  "Non-nil means to wrap around when selecting completion options.
+This affects the commands `next-completion' and
+`previous-completion'."
+  :type 'boolean
+  :version "29.1"
+  :group 'completion)
+
 (defun previous-completion (n)
-  "Move to the previous item in the completion list."
+  "Move to the previous item in the completion list.
+With prefix argument N, move back N items (negative N means move
+forward)."
   (interactive "p")
   (next-completion (- n)))
 
 (defun next-completion (n)
   "Move to the next item in the completion list.
-With prefix argument N, move N items (negative N means move backward)."
+With prefix argument N, move N items (negative N means move
+backward)."
   (interactive "p")
   (let ((beg (point-min)) (end (point-max)))
-    (while (and (> n 0) (not (eobp)))
-      ;; If in a completion, move to the end of it.
-      (when (get-text-property (point) 'mouse-face)
-       (goto-char (next-single-property-change (point) 'mouse-face nil end)))
-      ;; Move to start of next one.
-      (unless (get-text-property (point) 'mouse-face)
-       (goto-char (next-single-property-change (point) 'mouse-face nil end)))
-      (setq n (1- n)))
-    (while (and (< n 0) (not (bobp)))
-      (let ((prop (get-text-property (1- (point)) 'mouse-face)))
-       ;; If in a completion, move to the start of it.
-       (when (and prop (eq prop (get-text-property (point) 'mouse-face)))
-         (goto-char (previous-single-property-change
-                     (point) 'mouse-face nil beg)))
-       ;; Move to end of the previous completion.
-       (unless (or (bobp) (get-text-property (1- (point)) 'mouse-face))
-         (goto-char (previous-single-property-change
-                     (point) 'mouse-face nil beg)))
-       ;; Move to the start of that one.
-       (goto-char (previous-single-property-change
-                   (point) 'mouse-face nil beg))
-       (setq n (1+ n))))))
+    (catch 'bound
+      (while (> n 0)
+        ;; If in a completion, move to the end of it.
+        (when (get-text-property (point) 'mouse-face)
+          (goto-char (next-single-property-change (point) 'mouse-face nil 
end)))
+        (when (and completion-wrap-movement (eobp))
+          (throw 'bound nil))
+        ;; Move to start of next one.
+        (unless (get-text-property (point) 'mouse-face)
+          (goto-char (next-single-property-change (point) 'mouse-face nil 
end)))
+        (setq n (1- n)))
+      (while (< n 0)
+        (let ((prop (get-text-property (1- (point)) 'mouse-face)))
+          ;; If in a completion, move to the start of it.
+          (when (and prop (eq prop (get-text-property (point) 'mouse-face)))
+            (goto-char (previous-single-property-change
+                        (point) 'mouse-face nil beg)))
+          ;; Move to end of the previous completion.
+          (unless (or (bobp) (get-text-property (1- (point)) 'mouse-face))
+            (goto-char (previous-single-property-change
+                        (point) 'mouse-face nil beg)))
+          (when (and completion-wrap-movement (bobp))
+            (goto-char (next-single-property-change (point) 'mouse-face nil 
end))
+            (throw 'bound nil))
+          ;; Move to the start of that one.
+          (goto-char (previous-single-property-change
+                      (point) 'mouse-face nil beg))
+          (setq n (1+ n)))))
+    (when (/= 0 n)
+      (switch-to-minibuffer))))
 
 (defun choose-completion (&optional event)
   "Choose the completion at point.
@@ -9283,10 +9302,16 @@ switch-to-completions
                            (get-buffer-window "*Completions*" 0)))))
     (when window
       (select-window window)
-      ;; In the new buffer, go to the first completion.
-      ;; FIXME: Perhaps this should be done in `minibuffer-completion-help'.
-      (when (bobp)
-       (next-completion 1)))))
+      (cond
+       ((and (memq this-command '(completion-at-point minibuffer-complete))
+             (equal (this-command-keys) [backtab])
+             (bobp))
+        (goto-char (point-max))
+        (previous-completion 1))
+       ;; In the new buffer, go to the first completion.
+       ;; FIXME: Perhaps this should be done in `minibuffer-completion-help'.
+       ((bobp)
+        (next-completion 1))))))
 
 (defun read-expression-switch-to-completions ()
   "Select the completion list window while reading an expression."
-- 
2.30.2

>From 1612aa4f9a5c1d4afa9e0d97b959c9623c400ce2 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Thu, 9 Dec 2021 17:36:14 +0100
Subject: [PATCH 3/3] Switch back to minibuffer when quitting completion buffer

* lisp/simple.el (completion-quit): Add new command
(completion-kill-buffer): Add new command
(completion-list-mode-map): Bind completion-quit and
rebind kill-current-buffer to completion-kill-buffer
---
 lisp/simple.el | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/lisp/simple.el b/lisp/simple.el
index 6a7d7ba918..c9ddca7b0a 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -8971,6 +8971,19 @@ set-variable
 
 ;; Define the major mode for lists of completions.
 
+(defun completion-quit ()
+  "Close the completion buffer and return to the minibuffer."
+  (interactive)
+  (quit-window)
+  (switch-to-minibuffer))
+
+(defun completion-kill-buffer ()
+  "Close the completion buffer and return to the minibuffer."
+  (interactive)
+  (let ((win (get-buffer-window "*Completions*")))
+    (when win (quit-window t win)))
+  (switch-to-minibuffer))
+
 (defvar completion-list-mode-map
   (let ((map (make-sparse-keymap)))
     (set-keymap-parent map special-mode-map)
@@ -8984,10 +8997,12 @@ completion-list-mode-map
     (define-key map [right] 'next-completion)
     (define-key map [?\t] 'next-completion)
     (define-key map [backtab] 'previous-completion)
-    (define-key map "z" 'kill-current-buffer)
     (define-key map "n" 'next-completion)
     (define-key map "p" 'previous-completion)
     (define-key map "\M-g\M-c" 'switch-to-minibuffer)
+    (define-key map "z" #'completion-kill-buffer)
+    (define-key map [remap keyboard-quit] #'delete-completion-window)
+    (define-key map [remap quit-window] #'completion-quit)
     map)
   "Local map for completion list buffers.")
 
-- 
2.30.2

-- 
        Philip Kaludercic

reply via email to

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