summaryrefslogtreecommitdiff
path: root/lisp/minibuffer.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/minibuffer.el')
-rw-r--r--lisp/minibuffer.el737
1 files changed, 505 insertions, 232 deletions
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 03cc70c0d4d..ffcd5d88abe 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -122,10 +122,17 @@ This metadata is an alist. Currently understood keys are:
returns a string to append to STRING.
- `affixation-function': function to prepend/append a prefix/suffix to
entries. Takes one argument (COMPLETIONS) and should return a list
- of completions with a list of either two elements: completion
- and suffix, or three elements: completion, its prefix
- and suffix. This function takes priority over `annotation-function'
- when both are provided, so only this function is used.
+ of annotated completions. The elements of the list must be
+ three-element lists: completion, its prefix and suffix. This
+ function takes priority over `annotation-function' when both are
+ provided, so only this function is used.
+- `group-function': function for grouping the completion candidates.
+ Takes two arguments: a completion candidate (COMPLETION) and a
+ boolean flag (TRANSFORM). If TRANSFORM is nil, the function
+ returns the group title of the group to which the candidate
+ belongs. The returned title may be nil. Otherwise the function
+ returns the transformed candidate. The transformation can remove a
+ redundant prefix, which is displayed in the group title.
- `display-sort-function': function to sort entries in *Completions*.
Takes one argument (COMPLETIONS) and should return a new list
of completions. Can operate destructively.
@@ -271,7 +278,7 @@ the form (concat S2 S)."
(let* ((str (if (string-prefix-p s1 string completion-ignore-case)
(concat s2 (substring string (length s1)))))
(res (if str (complete-with-action action table str pred))))
- (when res
+ (when (or res (eq (car-safe action) 'boundaries))
(cond
((eq (car-safe action) 'boundaries)
(let ((beg (or (and (eq (car-safe res) 'boundaries) (cadr res)) 0)))
@@ -488,8 +495,17 @@ for use at QPOS."
(qsuffix (cdr action))
(ufull (if (zerop (length qsuffix)) ustring
(funcall unquote (concat string qsuffix))))
- (_ (cl-assert (string-prefix-p ustring ufull)))
- (usuffix (substring ufull (length ustring)))
+ ;; If (not (string-prefix-p ustring ufull)) we have a problem:
+ ;; unquoting the qfull gives something "unrelated" to ustring.
+ ;; E.g. "~/" and "/" where "~//" gets unquoted to just "/" (see
+ ;; bug#47678).
+ ;; In that case we can't even tell if we're right before the
+ ;; "/" or right after it (aka if this "/" is from qstring or
+ ;; from qsuffix), thus which usuffix to use is very unclear.
+ (usuffix (if (string-prefix-p ustring ufull)
+ (substring ufull (length ustring))
+ ;; FIXME: Maybe "" is preferable/safer?
+ qsuffix))
(boundaries (completion-boundaries ustring table pred usuffix))
(qlboundary (car (funcall requote (car boundaries) string)))
(qrboundary (if (zerop (cdr boundaries)) 0 ;Common case.
@@ -725,14 +741,16 @@ If ARGS are provided, then pass MESSAGE through `format-message'."
;; Don't overwrite the face properties the caller has set
(text-properties-at 0 message))
(setq message (apply #'propertize message minibuffer-message-properties)))
- (let ((ol (make-overlay (point-max) (point-max) nil t t))
- ;; A quit during sit-for normally only interrupts the sit-for,
- ;; but since minibuffer-message is used at the end of a command,
- ;; at a time when the command has virtually finished already, a C-g
- ;; should really cause an abort-recursive-edit instead (i.e. as if
- ;; the C-g had been typed at top-level). Binding inhibit-quit here
- ;; is an attempt to get that behavior.
- (inhibit-quit t))
+ ;; Put overlay either on `minibuffer-message' property, or at EOB.
+ (let* ((ovpos (minibuffer--message-overlay-pos))
+ (ol (make-overlay ovpos ovpos nil t t))
+ ;; A quit during sit-for normally only interrupts the sit-for,
+ ;; but since minibuffer-message is used at the end of a command,
+ ;; at a time when the command has virtually finished already, a C-g
+ ;; should really cause an abort-recursive-edit instead (i.e. as if
+ ;; the C-g had been typed at top-level). Binding inhibit-quit here
+ ;; is an attempt to get that behavior.
+ (inhibit-quit t))
(unwind-protect
(progn
(unless (zerop (length message))
@@ -741,6 +759,12 @@ If ARGS are provided, then pass MESSAGE through `format-message'."
;; before or after the string, so let's spoon-feed it the pos.
(put-text-property 0 1 'cursor t message))
(overlay-put ol 'after-string message)
+ ;; Make sure the overlay with the message is displayed before
+ ;; any other overlays in that position, in case they have
+ ;; resize-mini-windows set to nil and the other overlay strings
+ ;; are too long for the mini-window width. This makes sure the
+ ;; temporary message will always be visible.
+ (overlay-put ol 'priority 1100)
(sit-for (or minibuffer-message-timeout 1000000)))
(delete-overlay ol)))))
@@ -762,8 +786,10 @@ and `clear-minibuffer-message' called automatically via
(defvar minibuffer-message-overlay nil)
(defun minibuffer--message-overlay-pos ()
- "Return position where `set-minibuffer-message' shall put message overlay."
- ;; Starting from point, look for non-nil 'minibuffer-message'
+ "Return position where minibuffer message functions shall put message overlay.
+The minibuffer message functions include `minibuffer-message' and
+`set-minibuffer-message'."
+ ;; Starting from point, look for non-nil `minibuffer-message'
;; property, and return its position. If none found, return the EOB
;; position.
(let* ((pt (point))
@@ -808,7 +834,7 @@ via `set-message-function'."
;; The current C cursor code doesn't know to use the overlay's
;; marker's stickiness to figure out whether to place the cursor
;; before or after the string, so let's spoon-feed it the pos.
- (put-text-property 0 1 'cursor 1 message))
+ (put-text-property 0 1 'cursor t message))
(overlay-put minibuffer-message-overlay 'after-string message)
;; Make sure the overlay with the message is displayed before
;; any other overlays in that position, in case they have
@@ -856,6 +882,12 @@ If the current buffer is not a minibuffer, erase its entire contents."
;; is on, the field doesn't cover the entire minibuffer contents.
(delete-region (minibuffer-prompt-end) (point-max)))
+(defun minibuffer--completion-prompt-end ()
+ (let ((end (minibuffer-prompt-end)))
+ (if (< (point) end)
+ (user-error "Can't complete in prompt")
+ end)))
+
(defvar completion-show-inline-help t
"If non-nil, print helpful inline messages during completion.")
@@ -1129,6 +1161,44 @@ completion candidates than this number."
:version "24.1"
:type completion--cycling-threshold-type)
+(defcustom completions-group nil
+ "Enable grouping of completion candidates in the *Completions* buffer.
+See also `completions-group-format' and `completions-group-sort'."
+ :type 'boolean
+ :version "28.1")
+
+(defcustom completions-group-sort nil
+ "Sort groups in the *Completions* buffer.
+
+The value can either be nil to disable sorting, `alphabetical' for
+alphabetical sorting or a custom sorting function. The sorting
+function takes and returns an alist of groups, where each element is a
+pair of a group title string and a list of group candidate strings."
+ :type '(choice (const :tag "No sorting" nil)
+ (const :tag "Alphabetical sorting" alphabetical)
+ function)
+ :version "28.1")
+
+(defcustom completions-group-format
+ (concat
+ (propertize " " 'face 'completions-group-separator)
+ (propertize " %s " 'face 'completions-group-title)
+ (propertize " " 'face 'completions-group-separator
+ 'display '(space :align-to right)))
+ "Format string used for the group title."
+ :type 'string
+ :version "28.1")
+
+(defface completions-group-title
+ '((t :inherit shadow :slant italic))
+ "Face used for the title text of the candidate group headlines."
+ :version "28.1")
+
+(defface completions-group-separator
+ '((t :inherit shadow :strike-through t))
+ "Face used for the separator lines between the candidate groups."
+ :version "28.1")
+
(defun completion--cycle-threshold (metadata)
(let* ((cat (completion-metadata-get metadata 'category))
(over (completion--category-override cat 'cycle)))
@@ -1285,10 +1355,9 @@ If no characters can be completed, display a list of possible completions.
If you repeat this command after it displayed such a list,
scroll the window of possible completions."
(interactive)
- (when (<= (minibuffer-prompt-end) (point))
- (completion-in-region (minibuffer-prompt-end) (point-max)
- minibuffer-completion-table
- minibuffer-completion-predicate)))
+ (completion-in-region (minibuffer--completion-prompt-end) (point-max)
+ minibuffer-completion-table
+ minibuffer-completion-predicate))
(defun completion--in-region-1 (beg end)
;; If the previous command was not this,
@@ -1346,6 +1415,68 @@ scroll the window of possible completions."
(if (eq (car bounds) base) md-at-point
(completion-metadata (substring string 0 base) table pred))))
+(defun minibuffer--sort-by-key (elems keyfun)
+ "Return ELEMS sorted by increasing value of their KEYFUN.
+KEYFUN takes an element of ELEMS and should return a numerical value."
+ (mapcar #'cdr
+ (sort (mapcar (lambda (x) (cons (funcall keyfun x) x)) elems)
+ #'car-less-than-car)))
+
+(defun minibuffer--sort-by-position (hist elems)
+ "Sort ELEMS by their position in HIST."
+ (let ((hash (make-hash-table :test #'equal :size (length hist)))
+ (index 0))
+ ;; Record positions in hash
+ (dolist (c hist)
+ (unless (gethash c hash)
+ (puthash c index hash))
+ (cl-incf index))
+ (minibuffer--sort-by-key
+ elems (lambda (x) (gethash x hash most-positive-fixnum)))))
+
+(defun minibuffer--sort-by-length-alpha (elems)
+ "Sort ELEMS first by length, then alphabetically."
+ (sort elems (lambda (c1 c2)
+ (or (< (length c1) (length c2))
+ (and (= (length c1) (length c2))
+ (string< c1 c2))))))
+
+(defun minibuffer--sort-preprocess-history (base)
+ "Preprocess history.
+Remove completion BASE prefix string from history elements."
+ (let* ((def (if (stringp minibuffer-default)
+ minibuffer-default
+ (car-safe minibuffer-default)))
+ (hist (and (not (eq minibuffer-history-variable t))
+ (symbol-value minibuffer-history-variable)))
+ (base-size (length base)))
+ ;; Default comes first.
+ (setq hist (if def (cons def hist) hist))
+ ;; Drop base string from the history elements.
+ (if (= base-size 0)
+ hist
+ (delq nil (mapcar
+ (lambda (c)
+ (when (string-prefix-p base c)
+ (substring c base-size)))
+ hist)))))
+
+(defun minibuffer--group-by (group-fun sort-fun elems)
+ "Group ELEMS by GROUP-FUN and sort groups by SORT-FUN."
+ (let ((groups))
+ (dolist (cand elems)
+ (let* ((key (funcall group-fun cand nil))
+ (group (assoc key groups)))
+ (if group
+ (setcdr group (cons cand (cdr group)))
+ (push (list key cand) groups))))
+ (setq groups (nreverse groups)
+ groups (mapc (lambda (x)
+ (setcdr x (nreverse (cdr x))))
+ groups)
+ groups (funcall sort-fun groups))
+ (mapcan #'cdr groups)))
+
(defun completion-all-sorted-completions (&optional start end)
(or completion-all-sorted-completions
(let* ((start (or start (minibuffer-prompt-end)))
@@ -1375,23 +1506,18 @@ scroll the window of possible completions."
(setq all (delete-dups all))
(setq last (last all))
- (cond
- (sort-fun
- (setq all (funcall sort-fun all)))
- (t
- ;; Prefer shorter completions, by default.
- (setq all (sort all (lambda (c1 c2) (< (length c1) (length c2)))))
- (if (minibufferp)
- ;; Prefer recently used completions and put the default, if
- ;; it exists, on top.
- (let ((hist (symbol-value minibuffer-history-variable)))
- (setq all
- (sort all
- (lambda (c1 c2)
- (cond ((equal c1 minibuffer-default) t)
- ((equal c2 minibuffer-default) nil)
- (t (> (length (member c1 hist))
- (length (member c2 hist))))))))))))
+ (if sort-fun
+ (setq all (funcall sort-fun all))
+ ;; Sort first by length and alphabetically.
+ (setq all (minibuffer--sort-by-length-alpha all))
+ ;; Sort by history position, put the default, if it
+ ;; exists, on top.
+ (when (minibufferp)
+ (setq all (minibuffer--sort-by-position
+ (minibuffer--sort-preprocess-history
+ (substring string 0 base-size))
+ all))))
+
;; Cache the result. This is not just for speed, but also so that
;; repeated calls to minibuffer-force-complete can cycle through
;; all possibilities.
@@ -1409,12 +1535,12 @@ scroll the window of possible completions."
(unless completion-cycling
(minibuffer-force-complete nil nil 'dont-cycle))
(completion--complete-and-exit
- (minibuffer-prompt-end) (point-max) #'exit-minibuffer
+ (minibuffer--completion-prompt-end) (point-max) #'exit-minibuffer
;; If the previous completion completed to an element which fails
;; test-completion, then we shouldn't exit, but that should be rare.
(lambda ()
(if minibuffer--require-match
- (minibuffer-message "Incomplete")
+ (completion--message "Incomplete")
;; If a match is not required, exit after all.
(exit-minibuffer)))))
@@ -1427,7 +1553,7 @@ DONT-CYCLE tells the function not to setup cycling."
;; FIXME: Need to deal with the extra-size issue here as well.
;; FIXME: ~/src/emacs/t<M-TAB>/lisp/minibuffer.el completes to
;; ~/src/emacs/trunk/ and throws away lisp/minibuffer.el.
- (let* ((start (copy-marker (or start (minibuffer-prompt-end))))
+ (let* ((start (copy-marker (or start (minibuffer--completion-prompt-end))))
(end (or end (point-max)))
;; (md (completion--field-metadata start))
(all (completion-all-sorted-completions start end))
@@ -1498,7 +1624,7 @@ If `minibuffer-completion-confirm' is `confirm-after-completion',
`minibuffer-confirm-exit-commands', and accept the input
otherwise."
(interactive)
- (completion-complete-and-exit (minibuffer-prompt-end) (point-max)
+ (completion-complete-and-exit (minibuffer--completion-prompt-end) (point-max)
#'exit-minibuffer))
(defun completion-complete-and-exit (beg end exit-function)
@@ -1664,17 +1790,12 @@ is added, provided that matches some possible completion.
Return nil if there is no valid completion, else t."
(interactive)
(completion-in-region--single-word
- (minibuffer-prompt-end) (point-max)
- minibuffer-completion-table minibuffer-completion-predicate))
-
-(defun completion-in-region--single-word (beg end collection
- &optional predicate)
- (let ((minibuffer-completion-table collection)
- (minibuffer-completion-predicate predicate))
- (pcase (completion--do-completion beg end
- #'completion--try-word-completion)
+ (minibuffer--completion-prompt-end) (point-max)))
+
+(defun completion-in-region--single-word (beg end)
+ (pcase (completion--do-completion beg end #'completion--try-word-completion)
(#b000 nil)
- (_ t))))
+ (_ t)))
(defface completions-annotations '((t :inherit (italic shadow)))
"Face to use for annotations in the *Completions* buffer.")
@@ -1697,15 +1818,17 @@ or appended to completions."
:type 'boolean
:version "28.1")
-(defun completion--insert-strings (strings)
+(defun completion--insert-strings (strings &optional group-fun)
"Insert a list of STRINGS into the current buffer.
-Uses columns to keep the listing readable but compact.
-It also eliminates runs of equal strings."
+The candidate strings are inserted into the buffer depending on the
+completions format as specified by the variable `completions-format'.
+Runs of equal candidate strings are eliminated. GROUP-FUN is a
+`group-function' used for grouping the completion candidates."
(when (consp strings)
(let* ((length (apply #'max
(mapcar (lambda (s)
(if (consp s)
- (apply #'+ (mapcar #'string-width s))
+ (apply #'+ (mapcar #'string-width s))
(string-width s)))
strings)))
(window (get-buffer-window (current-buffer) 0))
@@ -1716,104 +1839,158 @@ It also eliminates runs of equal strings."
;; Don't allocate more columns than we can fill.
;; Windows can't show less than 3 lines anyway.
(max 1 (/ (length strings) 2))))
- (colwidth (/ wwidth columns))
- (column 0)
- (rows (/ (length strings) columns))
- (row 0)
- (first t)
- (laststring nil))
+ (colwidth (/ wwidth columns)))
(unless (or tab-stop-list (null completion-tab-width)
(zerop (mod colwidth completion-tab-width)))
;; Align to tab positions for the case
;; when the caller uses tabs inside prefix.
(setq colwidth (- colwidth (mod colwidth completion-tab-width))))
- ;; The insertion should be "sensible" no matter what choices were made
- ;; for the parameters above.
- (dolist (str strings)
- (unless (equal laststring str) ; Remove (consecutive) duplicates.
- (setq laststring str)
+ (funcall (intern (format "completion--insert-%s" completions-format))
+ strings group-fun length wwidth colwidth columns))))
+
+(defun completion--insert-horizontal (strings group-fun
+ length wwidth
+ colwidth _columns)
+ (let ((column 0)
+ (first t)
+ (last-title nil)
+ (last-string nil))
+ (dolist (str strings)
+ (unless (equal last-string str) ; Remove (consecutive) duplicates.
+ (setq last-string str)
+ (when group-fun
+ (let ((title (funcall group-fun (if (consp str) (car str) str) nil)))
+ (unless (equal title last-title)
+ (setq last-title title)
+ (when title
+ (insert (if first "" "\n") (format completions-group-format title) "\n")
+ (setq column 0
+ first t)))))
+ (unless first
;; FIXME: `string-width' doesn't pay attention to
;; `display' properties.
- (let ((length (if (consp str)
- (apply #'+ (mapcar #'string-width str))
- (string-width str))))
- (cond
- ((eq completions-format 'one-column)
- ;; Nothing special
- )
- ((eq completions-format 'vertical)
- ;; Vertical format
- (when (> row rows)
- (forward-line (- -1 rows))
- (setq row 0 column (+ column colwidth)))
- (when (> column 0)
- (end-of-line)
- (while (> (current-column) column)
- (if (eobp)
- (insert "\n")
- (forward-line 1)
- (end-of-line)))
- (insert " \t")
- (set-text-properties (1- (point)) (point)
- `(display (space :align-to ,column)))))
- (t
- ;; Horizontal format
- (unless first
- (if (< wwidth (+ (max colwidth length) column))
- ;; No space for `str' at point, move to next line.
- (progn (insert "\n") (setq column 0))
- (insert " \t")
- ;; Leave the space unpropertized so that in the case we're
- ;; already past the goal column, there is still
- ;; a space displayed.
- (set-text-properties (1- (point)) (point)
- ;; We can set tab-width using
- ;; completion-tab-width, but
- ;; the caller can prefer using
- ;; \t to align prefixes.
- `(display (space :align-to ,column)))
- nil))))
- (setq first nil)
- (if (not (consp str))
- (put-text-property (point) (progn (insert str) (point))
- 'mouse-face 'highlight)
- ;; If `str' is a list that has 2 elements,
- ;; then the second element is a suffix annotation.
- ;; If `str' has 3 elements, then the second element
- ;; is a prefix, and the third element is a suffix.
- (let* ((prefix (when (nth 2 str) (nth 1 str)))
- (suffix (or (nth 2 str) (nth 1 str))))
- (when prefix
- (let ((beg (point))
- (end (progn (insert prefix) (point))))
- (put-text-property beg end 'mouse-face nil)))
- (put-text-property (point) (progn (insert (car str)) (point))
- 'mouse-face 'highlight)
- (let ((beg (point))
- (end (progn (insert suffix) (point))))
- (put-text-property beg end 'mouse-face nil)
- ;; Put the predefined face only when suffix
- ;; is added via annotation-function without prefix,
- ;; and when the caller doesn't use own face.
- (unless (or prefix (text-property-not-all
- 0 (length suffix) 'face nil suffix))
- (font-lock-prepend-text-property
- beg end 'face 'completions-annotations)))))
- (cond
- ((eq completions-format 'one-column)
- (insert "\n"))
- ((eq completions-format 'vertical)
- ;; Vertical format
- (if (> column 0)
- (forward-line)
- (insert "\n"))
- (setq row (1+ row)))
- (t
- ;; Horizontal format
- ;; Next column to align to.
- (setq column (+ column
- ;; Round up to a whole number of columns.
- (* colwidth (ceiling length colwidth))))))))))))
+ (if (< wwidth (+ column (max colwidth
+ (if (consp str)
+ (apply #'+ (mapcar #'string-width str))
+ (string-width str)))))
+ ;; No space for `str' at point, move to next line.
+ (progn (insert "\n") (setq column 0))
+ (insert " \t")
+ ;; Leave the space unpropertized so that in the case we're
+ ;; already past the goal column, there is still
+ ;; a space displayed.
+ (set-text-properties (1- (point)) (point)
+ ;; We can set tab-width using
+ ;; completion-tab-width, but
+ ;; the caller can prefer using
+ ;; \t to align prefixes.
+ `(display (space :align-to ,column)))
+ nil))
+ (setq first nil)
+ (completion--insert str group-fun)
+ ;; Next column to align to.
+ (setq column (+ column
+ ;; Round up to a whole number of columns.
+ (* colwidth (ceiling length colwidth))))))))
+
+(defun completion--insert-vertical (strings group-fun
+ _length _wwidth
+ colwidth columns)
+ (while strings
+ (let ((group nil)
+ (column 0)
+ (row 0)
+ (rows)
+ (last-string nil))
+ (if group-fun
+ (let* ((str (car strings))
+ (title (funcall group-fun (if (consp str) (car str) str) nil)))
+ (while (and strings
+ (equal title (funcall group-fun
+ (if (consp (car strings))
+ (car (car strings))
+ (car strings))
+ nil)))
+ (push (car strings) group)
+ (pop strings))
+ (setq group (nreverse group)))
+ (setq group strings
+ strings nil))
+ (setq rows (/ (length group) columns))
+ (when group-fun
+ (let* ((str (car group))
+ (title (funcall group-fun (if (consp str) (car str) str) nil)))
+ (when title
+ (goto-char (point-max))
+ (insert (format completions-group-format title) "\n"))))
+ (dolist (str group)
+ (unless (equal last-string str) ; Remove (consecutive) duplicates.
+ (setq last-string str)
+ (when (> row rows)
+ (forward-line (- -1 rows))
+ (setq row 0 column (+ column colwidth)))
+ (when (> column 0)
+ (end-of-line)
+ (while (> (current-column) column)
+ (if (eobp)
+ (insert "\n")
+ (forward-line 1)
+ (end-of-line)))
+ (insert " \t")
+ (set-text-properties (1- (point)) (point)
+ `(display (space :align-to ,column))))
+ (completion--insert str group-fun)
+ (if (> column 0)
+ (forward-line)
+ (insert "\n"))
+ (setq row (1+ row)))))))
+
+(defun completion--insert-one-column (strings group-fun &rest _)
+ (let ((last-title nil) (last-string nil))
+ (dolist (str strings)
+ (unless (equal last-string str) ; Remove (consecutive) duplicates.
+ (setq last-string str)
+ (when group-fun
+ (let ((title (funcall group-fun (if (consp str) (car str) str) nil)))
+ (unless (equal title last-title)
+ (setq last-title title)
+ (when title
+ (insert (format completions-group-format title) "\n")))))
+ (completion--insert str group-fun)
+ (insert "\n")))))
+
+(defun completion--insert (str group-fun)
+ (if (not (consp str))
+ (add-text-properties
+ (point)
+ (progn
+ (insert
+ (if group-fun
+ (funcall group-fun str 'transform)
+ str))
+ (point))
+ `(mouse-face highlight completion--string ,str))
+ ;; If `str' is a list that has 2 elements,
+ ;; then the second element is a suffix annotation.
+ ;; If `str' has 3 elements, then the second element
+ ;; is a prefix, and the third element is a suffix.
+ (let* ((prefix (when (nth 2 str) (nth 1 str)))
+ (suffix (or (nth 2 str) (nth 1 str))))
+ (when prefix
+ (let ((beg (point))
+ (end (progn (insert prefix) (point))))
+ (put-text-property beg end 'mouse-face nil)))
+ (completion--insert (car str) group-fun)
+ (let ((beg (point))
+ (end (progn (insert suffix) (point))))
+ (put-text-property beg end 'mouse-face nil)
+ ;; Put the predefined face only when suffix
+ ;; is added via annotation-function without prefix,
+ ;; and when the caller doesn't use own face.
+ (unless (or prefix (text-property-not-all
+ 0 (length suffix) 'face nil suffix))
+ (font-lock-prepend-text-property
+ beg end 'face 'completions-annotations))))))
(defvar completion-setup-hook nil
"Normal hook run at the end of setting up a completion list buffer.
@@ -1873,7 +2050,7 @@ and with BASE-SIZE appended as the last element."
completions)
base-size))))
-(defun display-completion-list (completions &optional common-substring)
+(defun display-completion-list (completions &optional common-substring group-fun)
"Display the list of completions, COMPLETIONS, using `standard-output'.
Each element may be just a symbol or string
or may be a list of two strings to be printed as if concatenated.
@@ -1883,7 +2060,9 @@ alternative, the second serves as annotation.
The actual completion alternatives, as inserted, are given `mouse-face'
properties of `highlight'.
At the end, this runs the normal hook `completion-setup-hook'.
-It can find the completion buffer in `standard-output'."
+It can find the completion buffer in `standard-output'.
+GROUP-FUN is a `group-function' used for grouping the completion
+candidates."
(declare (advertised-calling-convention (completions) "24.4"))
(if common-substring
(setq completions (completion-hilit-commonality
@@ -1896,7 +2075,7 @@ It can find the completion buffer in `standard-output'."
(let ((standard-output (current-buffer))
(completion-setup-hook nil))
(with-suppressed-warnings ((callargs display-completion-list))
- (display-completion-list completions common-substring)))
+ (display-completion-list completions common-substring group-fun)))
(princ (buffer-string)))
(with-current-buffer standard-output
@@ -1904,7 +2083,7 @@ It can find the completion buffer in `standard-output'."
(if (null completions)
(insert "There are no possible completions of what you have typed.")
(insert "Possible completions are:\n")
- (completion--insert-strings completions))))
+ (completion--insert-strings completions group-fun))))
(run-hooks 'completion-setup-hook)
nil)
@@ -1922,11 +2101,11 @@ These include:
`:affixation-function': Function to prepend/append a prefix/suffix to
completions. The function must accept one argument, a list of
- completions, and return a list where each element is a list of
- either two elements: a completion, and a suffix, or
- three elements: a completion, a prefix and a suffix.
- This function takes priority over `:annotation-function'
- when both are provided, so only this function is used.
+ completions, and return a list of annotated completions. The
+ elements of the list must be three-element lists: completion, its
+ prefix and suffix. This function takes priority over
+ `:annotation-function' when both are provided, so only this
+ function is used.
`:exit-function': Function to run after completion is performed.
@@ -1980,7 +2159,7 @@ variables.")
"Display a list of possible completions of the current minibuffer contents."
(interactive)
(message "Making completion list...")
- (let* ((start (or start (minibuffer-prompt-end)))
+ (let* ((start (or start (minibuffer--completion-prompt-end)))
(end (or end (point-max)))
(string (buffer-substring start end))
(md (completion--field-metadata start))
@@ -1999,7 +2178,7 @@ variables.")
;; the sole completion, then hide (previous&stale) completions.
(minibuffer-hide-completions)
(ding)
- (minibuffer-message
+ (completion--message
(if completions "Sole completion" "No completions")))
(let* ((last (last completions))
@@ -2017,6 +2196,8 @@ variables.")
(aff-fun (or (completion-metadata-get all-md 'affixation-function)
(plist-get completion-extra-properties
:affixation-function)))
+ (sort-fun (completion-metadata-get all-md 'display-sort-function))
+ (group-fun (completion-metadata-get all-md 'group-function))
(mainbuf (current-buffer))
;; If the *Completions* buffer is shown in a new
;; window, mark it as softly-dedicated, so bury-buffer in
@@ -2048,15 +2229,32 @@ variables.")
;; Remove the base-size tail because `sort' requires a properly
;; nil-terminated list.
(when last (setcdr last nil))
- (setq completions
- ;; FIXME: This function is for the output of all-completions,
- ;; not completion-all-completions. Often it's the same, but
- ;; not always.
- (let ((sort-fun (completion-metadata-get
- all-md 'display-sort-function)))
- (if sort-fun
- (funcall sort-fun completions)
- (sort completions 'string-lessp))))
+
+ ;; Sort first using the `display-sort-function'.
+ ;; FIXME: This function is for the output of
+ ;; all-completions, not
+ ;; completion-all-completions. Often it's the
+ ;; same, but not always.
+ (setq completions (if sort-fun
+ (funcall sort-fun completions)
+ (sort completions 'string-lessp)))
+
+ ;; After sorting, group the candidates using the
+ ;; `group-function'.
+ (when group-fun
+ (setq completions
+ (minibuffer--group-by
+ group-fun
+ (pcase completions-group-sort
+ ('nil #'identity)
+ ('alphabetical
+ (lambda (groups)
+ (sort groups
+ (lambda (x y)
+ (string< (car x) (car y))))))
+ (_ completions-group-sort))
+ completions)))
+
(cond
(aff-fun
(setq completions
@@ -2102,7 +2300,7 @@ variables.")
(if (eq (car bounds) (length result))
'exact 'finished)))))))
- (display-completion-list completions)))))
+ (display-completion-list completions nil group-fun)))))
nil)))
nil))
@@ -2116,16 +2314,38 @@ variables.")
(defun exit-minibuffer ()
"Terminate this minibuffer argument."
(interactive)
+ (when (minibufferp)
+ (when (not (minibuffer-innermost-command-loop-p))
+ (error "%s" "Not in most nested command loop"))
+ (when (not (innermost-minibuffer-p))
+ (error "%s" "Not in most nested minibuffer")))
;; If the command that uses this has made modifications in the minibuffer,
;; we don't want them to cause deactivation of the mark in the original
;; buffer.
;; A better solution would be to make deactivate-mark buffer-local
;; (or to turn it into a list of buffers, ...), but in the mean time,
;; this should do the trick in most cases.
- (when (innermost-minibuffer-p)
- (setq deactivate-mark nil)
- (throw 'exit nil))
- (error "%s" "Not in most nested minibuffer"))
+ (setq deactivate-mark nil)
+ (throw 'exit nil))
+
+(defun minibuffer-restore-windows ()
+ "Restore some windows on exit from minibuffer.
+When `read-minibuffer-restore-windows' is nil, then this function
+added to `minibuffer-exit-hook' will remove at least the window
+that displays the \"*Completions*\" buffer."
+ (unless read-minibuffer-restore-windows
+ (minibuffer-hide-completions)))
+
+(add-hook 'minibuffer-exit-hook 'minibuffer-restore-windows)
+
+(defun minibuffer-quit-recursive-edit ()
+ "Quit the command that requested this recursive edit without error.
+Like `abort-recursive-edit' without aborting keyboard macro
+execution."
+ ;; See Info node `(elisp)Recursive Editing' for an explanation of
+ ;; throwing a function to `exit'.
+ (throw 'exit (lambda ()
+ (signal 'minibuffer-quit nil))))
(defun self-insert-and-exit ()
"Terminate minibuffer input."
@@ -2396,8 +2616,10 @@ The completion method is determined by `completion-at-point-functions'."
(define-key map "\C-g" 'abort-minibuffers)
(define-key map "\M-<" 'minibuffer-beginning-of-buffer)
- (define-key map "\r" 'exit-minibuffer)
- (define-key map "\n" 'exit-minibuffer))
+ ;; Put RET last so that it is shown in doc strings in preference to
+ ;; C-j, when using the \\[exit-minibuffer] notation.
+ (define-key map "\n" 'exit-minibuffer)
+ (define-key map "\r" 'exit-minibuffer))
(defvar minibuffer-local-completion-map
(let ((map (make-sparse-keymap)))
@@ -2410,6 +2632,7 @@ The completion method is determined by `completion-at-point-functions'."
(define-key map "?" 'minibuffer-completion-help)
(define-key map [prior] 'switch-to-completions)
(define-key map "\M-v" 'switch-to-completions)
+ (define-key map "\M-g\M-c" 'switch-to-completions)
map)
"Local keymap for minibuffer input with completion.")
@@ -2432,10 +2655,33 @@ with `minibuffer-local-must-match-map'.")
(defvar minibuffer-local-filename-must-match-map (make-sparse-keymap))
(make-obsolete-variable 'minibuffer-local-filename-must-match-map nil "24.1")
-(let ((map minibuffer-local-ns-map))
- (define-key map " " 'exit-minibuffer)
- (define-key map "\t" 'exit-minibuffer)
- (define-key map "?" 'self-insert-and-exit))
+(defvar minibuffer-local-ns-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map minibuffer-local-map)
+ (define-key map " " #'exit-minibuffer)
+ (define-key map "\t" #'exit-minibuffer)
+ (define-key map "?" #'self-insert-and-exit)
+ map)
+ "Local keymap for the minibuffer when spaces are not allowed.")
+
+(defun read-no-blanks-input (prompt &optional initial inherit-input-method)
+ "Read a string from the terminal, not allowing blanks.
+Prompt with PROMPT. Whitespace terminates the input. If INITIAL is
+non-nil, it should be a string, which is used as initial input, with
+point positioned at the end, so that SPACE will accept the input.
+\(Actually, INITIAL can also be a cons of a string and an integer.
+Such values are treated as in `read-from-minibuffer', but are normally
+not useful in this function.)
+
+Third arg INHERIT-INPUT-METHOD, if non-nil, means the minibuffer inherits
+the current input method and the setting of`enable-multibyte-characters'.
+
+If `inhibit-interaction' is non-nil, this function will signal an
+`inhibited-interaction' error."
+ (read-from-minibuffer prompt initial minibuffer-local-ns-map
+ nil minibuffer-history nil inherit-input-method))
+
+;;; Major modes for the minibuffer
(defvar minibuffer-inactive-mode-map
(let ((map (make-keymap)))
@@ -2460,7 +2706,27 @@ not active.")
:abbrev-table nil ;abbrev.el is not loaded yet during dump.
;; Note: this major mode is called from minibuf.c.
"Major mode to use in the minibuffer when it is not active.
-This is only used when the minibuffer area has no active minibuffer.")
+This is only used when the minibuffer area has no active minibuffer.
+
+Note that the minibuffer may change to this mode more often than
+you might expect. For instance, typing `M-x' may change the
+buffer to this mode, then to a different mode, and then back
+again to this mode upon exit. Code running from
+`minibuffer-inactive-mode-hook' has to be prepared to run
+multiple times per minibuffer invocation. Also see
+`minibuffer-exit-hook'.")
+
+(defvaralias 'minibuffer-mode-map 'minibuffer-local-map)
+
+(define-derived-mode minibuffer-mode nil "Minibuffer"
+ "Major mode used for active minibuffers.
+
+For customizing this mode, it is better to use
+`minibuffer-setup-hook' and `minibuffer-exit-hook' rather than
+the mode hook of this mode."
+ :syntax-table nil
+ :abbrev-table nil
+ :interactive nil)
;;; Completion tables.
@@ -2483,7 +2749,7 @@ Useful to give the user default values that won't be substituted."
(defun completion--make-envvar-table ()
(mapcar (lambda (enventry)
- (substring enventry 0 (string-match-p "=" enventry)))
+ (substring enventry 0 (string-search "=" enventry)))
process-environment))
(defconst completion--embedded-envvar-re
@@ -2552,7 +2818,7 @@ same as `substitute-in-file-name'."
pred action))
((eq (car-safe action) 'boundaries)
(let ((start (length (file-name-directory string)))
- (end (string-match-p "/" (cdr action))))
+ (end (string-search "/" (cdr action))))
`(boundaries
;; if `string' is "C:" in w32, (file-name-directory string)
;; returns "C:/", so `start' is 3 rather than 2.
@@ -2839,7 +3105,7 @@ See `read-file-name' for the meaning of the arguments."
(minibuffer-maybe-quote-filename dir)))
(initial (cons (minibuffer-maybe-quote-filename initial) 0)))))
- (let ((completion-ignore-case read-file-name-completion-ignore-case)
+ (let ((ignore-case read-file-name-completion-ignore-case)
(minibuffer-completing-file-name t)
(pred (or predicate 'file-exists-p))
(add-to-history nil))
@@ -2867,6 +3133,7 @@ See `read-file-name' for the meaning of the arguments."
minibuffer-default))
(setq minibuffer-default
(cdr-safe minibuffer-default)))
+ (setq-local completion-ignore-case ignore-case)
;; On the first request on `M-n' fill
;; `minibuffer-default' with a list of defaults
;; relevant for file-name reading.
@@ -2922,6 +3189,7 @@ See `read-file-name' for the meaning of the arguments."
(unless val (error "No file name specified"))
(if (and default-filename
+ (not (file-remote-p dir))
(string-equal val (if (consp insdef) (car insdef) insdef)))
(setq val default-filename))
(setq val (substitute-in-file-name val))
@@ -3159,7 +3427,7 @@ or a symbol, see `completion-pcm--merge-completions'."
(let ((n '()))
(while p
(pcase p
- (`(,(or 'any 'any-delim) ,(or 'any 'point) . ,rest)
+ (`(,(or 'any 'any-delim) ,(or 'any 'point) . ,_)
(setq p (cdr p)))
;; This is not just a performance improvement: it turns a
;; terminating `point' into an implicit `any', which affects
@@ -3255,7 +3523,8 @@ between 0 and 1, and with faces `completions-common-part',
(when completions
(let* ((re (completion-pcm--pattern->regex pattern 'group))
(point-idx (completion-pcm--pattern-point-idx pattern))
- (case-fold-search completion-ignore-case))
+ (case-fold-search completion-ignore-case)
+ last-md)
(mapcar
(lambda (str)
;; Don't modify the string itself.
@@ -3264,7 +3533,7 @@ between 0 and 1, and with faces `completions-common-part',
(error "Internal error: %s does not match %s" re str))
(let* ((pos (if point-idx (match-beginning point-idx) (match-end 0)))
(match-end (match-end 0))
- (md (cddr (match-data)))
+ (md (cddr (setq last-md (match-data t last-md))))
(from 0)
(end (length str))
;; To understand how this works, consider these simple
@@ -3674,39 +3943,38 @@ that is non-nil."
(put 'flex 'completion--adjust-metadata 'completion--flex-adjust-metadata)
(defun completion--flex-adjust-metadata (metadata)
- (cl-flet
- ((compose-flex-sort-fn
- (existing-sort-fn) ; wish `cl-flet' had proper indentation...
- (lambda (completions)
- (let ((pre-sorted
- (if existing-sort-fn
- (funcall existing-sort-fn completions)
- completions)))
- (cond
- ((or (not (window-minibuffer-p))
- ;; JT@2019-12-23: FIXME: this is still wrong. What
- ;; we need to test here is "some input that actually
- ;; leads to flex filtering", not "something after
- ;; the minibuffer prompt". Among other
- ;; inconsistencies, the latter is always true for
- ;; file searches, meaning the next clauses will be
- ;; ignored.
- (> (point-max) (minibuffer-prompt-end)))
- (sort
- pre-sorted
- (lambda (c1 c2)
- (let ((s1 (get-text-property 0 'completion-score c1))
- (s2 (get-text-property 0 'completion-score c2)))
- (> (or s1 0) (or s2 0))))))
- (t pre-sorted))))))
- `(metadata
- (display-sort-function
- . ,(compose-flex-sort-fn
- (completion-metadata-get metadata 'display-sort-function)))
- (cycle-sort-function
- . ,(compose-flex-sort-fn
- (completion-metadata-get metadata 'cycle-sort-function)))
- ,@(cdr metadata))))
+ "If `flex' is actually doing filtering, adjust sorting."
+ (let ((flex-is-filtering-p
+ ;; JT@2019-12-23: FIXME: this is kinda wrong. What we need
+ ;; to test here is "some input that actually leads/led to
+ ;; flex filtering", not "something after the minibuffer
+ ;; prompt". E.g. The latter is always true for file
+ ;; searches, meaning we'll be doing extra work when we
+ ;; needn't.
+ (or (not (window-minibuffer-p))
+ (> (point-max) (minibuffer-prompt-end))))
+ (existing-dsf
+ (completion-metadata-get metadata 'display-sort-function))
+ (existing-csf
+ (completion-metadata-get metadata 'cycle-sort-function)))
+ (cl-flet
+ ((compose-flex-sort-fn
+ (existing-sort-fn) ; wish `cl-flet' had proper indentation...
+ (lambda (completions)
+ (sort
+ (funcall existing-sort-fn completions)
+ (lambda (c1 c2)
+ (let ((s1 (get-text-property 0 'completion-score c1))
+ (s2 (get-text-property 0 'completion-score c2)))
+ (> (or s1 0) (or s2 0))))))))
+ `(metadata
+ ,@(and flex-is-filtering-p
+ `((display-sort-function
+ . ,(compose-flex-sort-fn (or existing-dsf #'identity)))))
+ ,@(and flex-is-filtering-p
+ `((cycle-sort-function
+ . ,(compose-flex-sort-fn (or existing-csf #'identity)))))
+ ,@(cdr metadata)))))
(defun completion-flex--make-flex-pattern (pattern)
"Convert PCM-style PATTERN into PCM-style flex pattern.
@@ -3727,7 +3995,7 @@ which is at the core of flex logic. The extra
(defun completion-flex-try-completion (string table pred point)
"Try to flex-complete STRING in TABLE given PRED and POINT."
- (unless (and completion-flex-nospace (string-match-p " " string))
+ (unless (and completion-flex-nospace (string-search " " string))
(pcase-let ((`(,all ,pattern ,prefix ,suffix ,_carbounds)
(completion-substring--all-completions
string table pred point
@@ -3744,7 +4012,7 @@ which is at the core of flex logic. The extra
(defun completion-flex-all-completions (string table pred point)
"Get flex-completions of STRING in TABLE, given PRED and POINT."
- (unless (and completion-flex-nospace (string-match-p " " string))
+ (unless (and completion-flex-nospace (string-search " " string))
(pcase-let ((`(,all ,pattern ,prefix ,_suffix ,_carbounds)
(completion-substring--all-completions
string table pred point
@@ -3812,13 +4080,7 @@ See `completing-read' for the meaning of the arguments."
;; `read-from-minibuffer' uses 1-based index.
(1+ (cdr initial-input)))))
- (let* ((minibuffer-completion-table collection)
- (minibuffer-completion-predicate predicate)
- ;; FIXME: Remove/rename this var, see the next one.
- (minibuffer-completion-confirm (unless (eq require-match t)
- require-match))
- (minibuffer--require-match require-match)
- (base-keymap (if require-match
+ (let* ((base-keymap (if require-match
minibuffer-local-must-match-map
minibuffer-local-completion-map))
(keymap (if (memq minibuffer-completing-file-name '(nil lambda))
@@ -3831,8 +4093,17 @@ See `completing-read' for the meaning of the arguments."
;; in minibuffer-local-filename-completion-map can
;; override bindings in base-keymap.
base-keymap)))
- (result (read-from-minibuffer prompt initial-input keymap
- nil hist def inherit-input-method)))
+ (result
+ (minibuffer-with-setup-hook
+ (lambda ()
+ (setq-local minibuffer-completion-table collection)
+ (setq-local minibuffer-completion-predicate predicate)
+ ;; FIXME: Remove/rename this var, see the next one.
+ (setq-local minibuffer-completion-confirm
+ (unless (eq require-match t) require-match))
+ (setq-local minibuffer--require-match require-match))
+ (read-from-minibuffer prompt initial-input keymap
+ nil hist def inherit-input-method))))
(when (and (equal result "") def)
(setq result (if (consp def) (car def) def)))
result))
@@ -3938,13 +4209,15 @@ it. See `format' for details.
If DEFAULT is a list, the first element is used as the default.
If not, the element is used as is.
-If DEFAULT is nil, no \"default value\" string is included in the
-return value."
+If DEFAULT is nil or an empty string, no \"default value\" string
+is included in the return value."
(concat
(if (null format-args)
prompt
(apply #'format prompt format-args))
(and default
+ (or (not (stringp default))
+ (length> default 0))
(format minibuffer-default-prompt-format
(if (consp default)
(car default)