]> git.eshelyaron.com Git - emacs.git/commitdiff
Unify 'minibuffer-hint-mode' and 'completions-auto-update-mode'
authorEshel Yaron <me@eshelyaron.com>
Sun, 16 Jun 2024 07:47:40 +0000 (09:47 +0200)
committerEshel Yaron <me@eshelyaron.com>
Mon, 17 Jun 2024 10:43:48 +0000 (12:43 +0200)
lisp/minibuffer.el

index f42a721887b34607c364ef4e9989793f73bf5520..6cbea950667cfe3788d220427bed9773bcaaaa32 100644 (file)
@@ -1538,7 +1538,7 @@ pair of a group title string and a list of group candidate strings."
 (defface minibuffer-completion-fail
   '((t :inherit isearch-fail))
   "Face for highlighting minibuffer input part that prevents completion."
-  :version "28.1")
+  :version "30.1")
 
 (defcustom minibuffer-pulse-failing-completion
   #'minibuffer-not-completing-remote-file-p
@@ -3469,7 +3469,7 @@ The completion method is determined by `completion-at-point-functions'."
   "C-x j"     #'minibuffer-set-completion-action
   "C-x ~"     #'minibuffer-toggle-exceptional-candidates
   "C-x C-a"   #'minibuffer-toggle-completions-annotations
-  "C-x C-."   #'minibuffer-hint-mode
+  "C-x C-."   #'minibuffer-auto-completion-mode
   "C-j"       #'minibuffer-apply
   "C-p"       #'minibuffer-previous-line-or-completion
   "C-n"       #'minibuffer-next-line-or-completion
@@ -6059,50 +6059,6 @@ interactions is customizable via `minibuffer-regexp-prompts'."
     (remove-hook 'minibuffer-setup-hook #'minibuffer--regexp-setup)
     (remove-hook 'minibuffer-exit-hook #'minibuffer--regexp-exit)))
 
-(defcustom completions-auto-update-idle-time 0.2
-  "Number of seconds of idle to wait for before updating *Completions*.
-This applies to `completions-auto-update-mode', which see."
-  :group 'minibuffer
-  :type 'number)
-
-(defvar-local completions-auto-update-timer nil)
-
-(defun completions-auto-update ()
-  "Update the *Completions* buffer, if it is visible."
-  (when (get-buffer-window completions-buffer-name 0)
-    (if completion-in-region-mode
-        (completion-help-at-point)
-      (minibuffer-completion-help)))
-  (setq completions-auto-update-timer nil))
-
-(defun completions-auto-update-start-timer ()
-  "Start an idle timer for updating *Completions*."
-  (and (null completions-auto-update-timer)
-       (get-buffer-window completions-buffer-name 0)
-       (setq completions-auto-update-timer
-             (run-with-idle-timer completions-auto-update-idle-time
-                                  nil #'completions-auto-update))))
-
-(defun completions-auto-update-setup ()
-  "Prepare for updating *Completions* as you type in the minibuffer."
-  (add-hook 'post-self-insert-hook
-            #'completions-auto-update-start-timer nil t))
-
-(defun completions-auto-update-exit ()
-  "Stop updating *Completions* as you type in the minibuffer."
-  (remove-hook 'post-self-insert-hook
-               #'completions-auto-update-start-timer t))
-
-(define-minor-mode completions-auto-update-mode
-  "Update the *Completions* buffer as you type in the minibuffer."
-  :global t
-  (if completions-auto-update-mode
-      (progn
-        (add-hook 'minibuffer-setup-hook #'completions-auto-update-setup)
-        (add-hook 'minibuffer-exit-hook #'completions-auto-update-exit))
-    (remove-hook 'minibuffer-setup-hook #'completions-auto-update-setup)
-    (remove-hook 'minibuffer-exit-hook #'completions-auto-update-exit)))
-
 (defvar completion-regexp-list nil "Unused obsolete variable.")
 (make-obsolete-variable 'completion-regexp-list nil "30.1")
 
@@ -6112,74 +6068,106 @@ This applies to `completions-auto-update-mode', which see."
 (defvar minibuffer-allow-text-properties nil "Unused obsolete variable.")
 (make-obsolete-variable 'minibuffer-allow-text-properties nil "30.1")
 
-(defvar-local minibuffer-hint-timer nil)
-
-(defcustom minibuffer-hint-idle-time 0.4
-  "Number for seconds to wait before showing a hint in the minibuffer."
-  :type 'float)
-
-(defun minibuffer-hint ()
-  (if-let ((all (let ((completion-lazy-hilit t)
-                      completion-all-sorted-completions)
-                  (completion-all-sorted-completions))))
-      (let ((minibuffer-message-timeout))
-        (minibuffer-message
-         (concat (propertize (car all) 'face 'bold)
-                 (when (consp (cdr all))
-                   (propertize
-                    (concat
-                     "/"
-                     (number-to-string (let ((n 1))
-                                         (while (consp (cdr all))
-                                           (setq n (1+ n)
-                                                 all (cdr all)))
-                                         n)))
-                    'face 'shadow)))))
-    (completion--fail)))
+(defcustom minibuffer-auto-completion-idle-time 0.4
+  "Number for seconds to wait before auto-completion in the minibuffer."
+  :type 'float
+  :version "30.1")
 
-(defun minibuffer-hint-cancel-timer ()
-  (when (timerp minibuffer-hint-timer)
-    (cancel-timer minibuffer-hint-timer)
-    (setq minibuffer-hint-timer nil)))
+(defcustom minibuffer-auto-completion-expand-common nil
+  "Whether to auto-expand minibuffer input based on common part of completions."
+  ;; TODO: Add option to only expand when completion is strict.
+  ;; TODO: Add option to only expand match is exact.
+  :type 'boolean
+  :version "30.1")
 
-(defun minibuffer-hint-fn (minib)
-  "Return a function that shows a hint in minibuffer MINIB."
+(defvar-local minibuffer-auto-completion-timer nil)
+
+(defun minibuffer-auto-completion ()
+  "Try completing minibuffer input and indicate the results."
+  ;; TODO: Optimize.
+  (when minibuffer-auto-completion-expand-common
+    (let* ((cont (minibuffer-contents))
+           (comp (completion-try-completion cont
+                                            minibuffer-completion-table
+                                            minibuffer-completion-predicate
+                                            (- (point)
+                                               (minibuffer-prompt-end)))))
+      (when (consp comp)
+        (let* ((comp-pos (cdr comp))
+               (completion (car comp)))
+          (unless (string= completion cont)
+            (minibuffer-record-completion-input
+              ;; TODO: Pulse changed part.
+              (completion--replace (minibuffer-prompt-end) (point-max)
+                                   completion))
+            (forward-char (- comp-pos (length completion))))))))
+  (if (get-buffer-window completions-buffer-name 0)
+      ;; Completions list is already displayed, refresh it.
+      (minibuffer-completion-help)
+    ;; Otherwise, display a concise summary inline.
+    (if-let ((all (let ((completion-lazy-hilit t)
+                        completion-all-sorted-completions)
+                    (completion-all-sorted-completions))))
+        (let ((minibuffer-message-timeout))
+          (minibuffer-message
+           (concat (if (string= (car all) (minibuffer-contents))
+                       (propertize "Match" 'face 'success)
+                     (propertize (car all) 'face 'bold))
+                   (when (consp (cdr all))
+                     (propertize
+                      (concat
+                       "/"
+                       (number-to-string (let ((n 1))
+                                           (while (consp (cdr all))
+                                             (setq n (1+ n)
+                                                   all (cdr all)))
+                                           n)))
+                      'face 'shadow)))))
+      (completion--fail))))
+
+(defun minibuffer-auto-completion-cancel-timer ()
+  "Cancel `minibuffer-auto-completion-timer'."
+  (when (timerp minibuffer-auto-completion-timer)
+    (cancel-timer minibuffer-auto-completion-timer)
+    (setq minibuffer-auto-completion-timer nil)))
+
+(defun minibuffer-auto-completion-fn (minib)
+  "Return a function that auto-completes in minibuffer MINIB."
   (lambda ()
     (when (equal (current-buffer) minib)
-      (minibuffer-hint))
+      ;; TODO: Consider wrapping this call with `while-no-input'.
+      ;; TODO: Detect slow auto completion and disable/extend delay.
+      (minibuffer-auto-completion))
     (when (buffer-live-p minib)
-      (with-current-buffer minib (minibuffer-hint-cancel-timer)))))
-
-(defun minibuffer-hint-start-timer ()
-  (unless (or (timerp minibuffer-hint-timer)
-              ;; Let `completions-auto-update-mode' do its thing.
-              (and completions-auto-update-mode
-                   (get-buffer-window completions-buffer-name 0)))
-    (setq minibuffer-hint-timer
-          (run-with-idle-timer minibuffer-hint-idle-time
-                               nil (minibuffer-hint-fn (current-buffer))))))
-
-(define-minor-mode minibuffer-hint-mode
-  "Display a hint in the minibuffer after a short delay.
-
-Add this function to `minibuffer-setup-hook' to have Emacs display a
-hint whenever you pause while typing in the minibuffer.  The hint shows
-the top completion candidate that matches your current input, the total
-number of matching candidates in case there is more than one.  To exit
-the minibuffer with the candidate that the hint shows, use \
-\\<minibuffer-local-completion-map>\\[minibuffer-force-complete-and-exit].
-If there are no matching completion candidates, the hint tells you so."
+      (with-current-buffer minib (minibuffer-auto-completion-cancel-timer)))))
+
+(defun minibuffer-auto-completion-start-timer ()
+  "Start minibuffer auto-completion timer."
+  (unless (timerp minibuffer-auto-completion-timer)
+    (setq minibuffer-auto-completion-timer
+          (run-with-idle-timer minibuffer-auto-completion-idle-time
+                               nil (minibuffer-auto-completion-fn (current-buffer))))))
+
+(define-minor-mode minibuffer-auto-completion-mode
+  "Automatically complete minibuffer input and indicate the results."
   :interactive (minibuffer-mode)
-  (if minibuffer-hint-mode
+  (if minibuffer-auto-completion-mode
       (if (not minibuffer-completion-table)
-          (setq minibuffer-hint-mode nil)
-        (add-hook 'post-self-insert-hook #'minibuffer-hint-start-timer nil t)
-        (add-hook 'minibuffer-exit-hook #'minibuffer-hint-cancel-timer nil t)
-        (add-hook 'pre-command-hook #'minibuffer-hint-cancel-timer nil t))
-    (remove-hook 'post-self-insert-hook #'minibuffer-hint-start-timer t)
-    (remove-hook 'minibuffer-exit-hook #'minibuffer-hint-cancel-timer t)
-    (remove-hook 'pre-command-hook #'minibuffer-hint-cancel-timer t)
-    (minibuffer-hint-cancel-timer)))
+          (setq minibuffer-auto-completion-mode nil)
+        (add-hook 'post-self-insert-hook #'minibuffer-auto-completion-start-timer nil t)
+        (add-hook 'minibuffer-exit-hook #'minibuffer-auto-completion-cancel-timer nil t)
+        (add-hook 'pre-command-hook #'minibuffer-auto-completion-cancel-timer nil t))
+    (remove-hook 'post-self-insert-hook #'minibuffer-auto-completion-start-timer t)
+    (remove-hook 'minibuffer-exit-hook #'minibuffer-auto-completion-cancel-timer t)
+    (remove-hook 'pre-command-hook #'minibuffer-auto-completion-cancel-timer t)
+    (minibuffer-auto-completion-cancel-timer)))
+
+(define-minor-mode global-minibuffer-auto-completion-mode
+  "Use `minibuffer-auto-completion-mode' in all minibuffers."
+  :global t
+  (if global-minibuffer-auto-completion-mode
+      (add-hook 'minibuffer-setup-hook #'minibuffer-auto-completion-mode)
+    (remove-hook 'minibuffer-setup-hook #'minibuffer-auto-completion-mode)))
 
 (defun minibuffer-prompt ()
   "Return the current minibuffer prompt."
@@ -6187,7 +6175,7 @@ If there are no matching completion candidates, the hint tells you so."
 
 ;; (defun minibuffer-set-prompt (prompt)
 ;;   "Set the current minibuffer prompt to PROMPT."
-;;   ;; FIXME: Inhibit undo.
+;;   ;; FIXME: Inhibit/adjust undo.
 ;;   (let ((inhibit-read-only t)
 ;;         (buffer-undo-list t)
 ;;         (tniop (- (point-max) (point))))