diff options
Diffstat (limited to 'lisp/icomplete.el')
-rw-r--r-- | lisp/icomplete.el | 100 |
1 files changed, 78 insertions, 22 deletions
diff --git a/lisp/icomplete.el b/lisp/icomplete.el index 3747ae3d281..4e546807b7f 100644 --- a/lisp/icomplete.el +++ b/lisp/icomplete.el @@ -75,7 +75,11 @@ everything preceding the ~/ is discarded so the interactive selection process starts again from the user's $HOME.") (defcustom icomplete-show-matches-on-no-input nil - "When non-nil, show completions when first prompting for input." + "When non-nil, show completions when first prompting for input. +This also means that if you traverse the list of completions with +commands like `C-.' and just hit RET without typing any +characters, the match under point will be chosen instead of the +default." :type 'boolean :version "24.4") @@ -153,12 +157,22 @@ icompletion is occurring." (defvar icomplete-minibuffer-map (let ((map (make-sparse-keymap))) (define-key map [?\M-\t] 'icomplete-force-complete) + (define-key map [remap minibuffer-complete-and-exit] 'icomplete-ret) (define-key map [?\C-j] 'icomplete-force-complete-and-exit) (define-key map [?\C-.] 'icomplete-forward-completions) (define-key map [?\C-,] 'icomplete-backward-completions) map) "Keymap used by `icomplete-mode' in the minibuffer.") +(defun icomplete-ret () + "Exit minibuffer for icomplete." + (interactive) + (if (and icomplete-show-matches-on-no-input + (car completion-all-sorted-completions) + (eql (icomplete--field-end) (icomplete--field-beg))) + (icomplete-force-complete-and-exit) + (minibuffer-complete-and-exit))) + (defun icomplete-force-complete-and-exit () "Complete the minibuffer with the longest possible match and exit. Use the first of the matches if there are any displayed, and use @@ -465,38 +479,80 @@ Usually run by inclusion in `minibuffer-setup-hook'." with beg = (icomplete--field-beg) with end = (icomplete--field-end) with all = (completion-all-sorted-completions beg end) + ;; Icomplete mode re-sorts candidates, bubbling the default to + ;; top if it's found somewhere down the list. This loop's + ;; iteration variable, `fn' iterates through these "bubble up + ;; predicates" which may vary depending on specific + ;; `completing-read' invocations, described below: for fn in (cond ((and minibuffer-default (stringp minibuffer-default) ; bug#38992 (= (icomplete--field-end) (icomplete--field-beg))) - ;; When we have a non-nil string default and - ;; no input whatsoever: we want to make sure - ;; that default is bubbled to the top so that - ;; `icomplete-force-complete-and-exit' will - ;; select it (do that even if the match - ;; doesn't match the completion perfectly. - `(,(lambda (comp) + ;; Here, we have a non-nil string default and + ;; no input whatsoever. We want to make sure + ;; that the default is bubbled to the top so + ;; that `icomplete-force-complete-and-exit' + ;; will select it. We want to do that even if + ;; the match doesn't match the completion + ;; perfectly. + ;; + `(;; The first predicate ensures that: + ;; + ;; (completing-read "thing? " '("foo" "bar") + ;; nil nil nil nil "bar") + ;; + ;; Has "bar" at the top, so RET will select + ;; it, as desired. + ,(lambda (comp) (equal minibuffer-default comp)) + ;; Why do we need this second predicate? + ;; Because that'll make things like M-x man + ;; RET RET, when invoked with point on the + ;; "bar" word, behave correctly. There, the + ;; default doesn't quite match any + ;; candidate. So: + ;; + ;; (completing-read "Man entry? " '("foo(1)" "bar(1)") + ;; nil nil nil nil "bar") + ;; + ;; Will place "bar(1)" on top, and RET will + ;; select it -- again, as desired. + ;; + ;; FIXME: it's arguable that this second + ;; behaviour should be a property of the + ;; completion table and not the completion + ;; frontend such as we have done + ;; here. However, it seems generically + ;; useful for a very broad spectrum of + ;; cases. ,(lambda (comp) (string-prefix-p minibuffer-default comp)))) ((and fido-mode (not minibuffer-default) (eq (icomplete--category) 'file)) - ;; `fido-mode' has some extra file-sorting - ;; semantics even if there isn't a default, - ;; which is to bubble "./" to the top if it - ;; exists. This makes M-x dired RET RET go to - ;; the directory of current file, which is - ;; what vanilla Emacs and `ido-mode' both do. + ;; When there isn't a default, `fido-mode' + ;; specifically also has some extra + ;; file-sorting semantics inherited from Ido. + ;; Those make the directory "./" bubble to the + ;; top (if it exists). This makes M-x dired + ;; RET RET go to the directory of current + ;; file, which is non-Icomplete vanilla Emacs + ;; and `ido-mode' both do. `(,(lambda (comp) (string= "./" comp))))) - thereis (cl-loop - for l on all - while (consp (cdr l)) - for comp = (cadr l) - when (funcall fn comp) - do (setf (cdr l) (cddr l)) - and return - (completion--cache-all-sorted-completions beg end (cons comp all))) + ;; After we have setup the predicates, look for a completion + ;; matching one of them and bubble up it, destructively on + ;; `completion-all-sorted-completions' (unless that completion + ;; happens to be already on top). + thereis (or + (and (funcall fn (car all)) all) + (cl-loop + for l on all + while (consp (cdr l)) + for comp = (cadr l) + when (funcall fn comp) + do (setf (cdr l) (cddr l)) + and return + (completion--cache-all-sorted-completions beg end (cons comp all)))) finally return all))) |