From e83faccb913678c81980eff63f78c25d0d0ad298 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sun, 16 Jun 2024 09:47:40 +0200 Subject: [PATCH] Unify 'minibuffer-hint-mode' and 'completions-auto-update-mode' --- lisp/minibuffer.el | 204 +++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 108 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index f42a721887b..6cbea950667 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -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-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)))) -- 2.39.2