(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
"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
(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")
(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."
;; (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))))