]> git.eshelyaron.com Git - emacs.git/commitdiff
Support interactively sorting minibuffer completions
authorEshel Yaron <me@eshelyaron.com>
Wed, 27 Dec 2023 09:11:36 +0000 (10:11 +0100)
committerEshel Yaron <me@eshelyaron.com>
Fri, 19 Jan 2024 10:06:00 +0000 (11:06 +0100)
* lisp/minibuffer.el (minibuffer-completions-sort-function): New var.
(minibuffer-read-sort-order-with-completion)
(minibuffer-completions-sort-orders): New user options.
(minibuffer-sort-completions): New command.
(minibuffer-local-completion-map): Bind it to 'C-x C-v'.
(display-completion-list, minibuffer-completion-help): Take
'minibuffer-completions-sort-function' into account.
(completions-header-format): Add '%t' format spec construct,
substituted with a description of the current sort order.
* lisp/menu-bar.el (minibuffer-local-completion-map): Add menu bar
menu entry for sorting completions candidates.
* doc/emacs/mini.texi (Completion Commands): Document new command.
Improve documentation and indexing of 'minibuffer-complete-and-exit'.
(Completion Exit, Completion Options): Update
* doc/lispref/minibuf.texi (Completion Commands): Document new user
options and command.
* etc/NEWS: Announce new feature.

doc/emacs/mini.texi
doc/lispref/minibuf.texi
etc/NEWS
lisp/menu-bar.el
lisp/minibuffer.el

index 72e857a3c0e2387b01750884a7336d6eb01442dd..d8d57c250074efde37bfa2b5c7b85281874f1c0c 100644 (file)
@@ -353,6 +353,9 @@ arguments that often include spaces, such as file names.
 @item @key{RET}
 Submit the text in the minibuffer as the argument, possibly completing
 first (@code{minibuffer-complete-and-exit}).  @xref{Completion Exit}.
+@item C-x C-v
+Change the order of the list of possible completions
+(@code{minibuffer-sort-completions}).
 @item C-x n n
 Narrow (restrict) the list of possible completions according to the
 current minibuffer input
@@ -384,6 +387,22 @@ completion is @samp{auto-fill-mode}, but it only inserts @samp{ill-},
 giving @samp{auto-fill-}.  Another @key{SPC} at this point completes
 all the way to @samp{auto-fill-mode}.
 
+@kindex RET @r{(completion)}
+@findex minibuffer-complete-and-exit
+  @key{RET} (@code{minibuffer-complete-and-exit}) submits the text in
+the minibuffer, or completes it and then submits it, depending on the
+``strictness'' of the completion.  @xref{Completion Exit}.
+
+@kindex C-x C-v @r{(completion)}
+@findex minibuffer-sort-completions
+  @key{C-x C-v} (@code{minibuffer-sort-completions}) changes the order
+of the completions list.  By default, Emacs sorts the list of possible
+completion candidates in the order that you specify in user option
+@code{completions-sort} (@pxref{Completion Options}).  This command
+lets you change the order of the current completions list interactively.
+You can invoke it with a negative prefix argument (@kbd{C-- C-x C-v}) to
+reverse the current order.
+
 @kindex C-x n n @r{(completion)}
 @findex minibuffer-narrow-completions-to-current
   @kbd{C-x n n} (@code{minibuffer-narrow-completions-to-current})
@@ -490,8 +509,8 @@ showing it (@code{kill-current-buffer}).
 @node Completion Exit
 @subsection Completion Exit
 
-@kindex RET @r{(completion in minibuffer)}
-@findex minibuffer-complete-and-exit
+@cindex completion strictness
+@cindex strict completion
   When a command reads an argument using the minibuffer with
 completion, it also controls what happens when you type @key{RET}
 (@code{minibuffer-complete-and-exit}) to submit the argument.  There
@@ -844,15 +863,16 @@ Reference Manual}).
 The variable @code{completions-header-format} is a format spec string
 to control the informative line shown before the completions list of
 candidates, called the @dfn{completions heading line}.  Emacs
-substitutes @samp{%s} and @samp{%r} constructs that occur in this
-string with the number of completion candidates and a description of
-the current completions restriction, respectively.  @xref{Narrow
-Completions}.  To suppress the display of the heading line, customize
-this variable to @code{nil}.  The string that is the value of this
-variable can have text properties to change the visual appearance of
-the heading line; some useful properties are @code{face} or
-@code{cursor-intangible} (@pxref{Special Properties,,Properties with
-Special Meanings, elisp, The Emacs Lisp Reference Manual}).
+substitutes @samp{%s}, @samp{%t} and @samp{%r} constructs that occur
+in this string with the number of completion candidates, the current
+completions sort order, and a description of the current completions
+restriction, respectively.  @xref{Narrow Completions}.  To suppress
+the display of the heading line, customize this variable to
+@code{nil}.  The string that is the value of this variable can have
+text properties to change the visual appearance of the heading line;
+some useful properties are @code{face} or @code{cursor-intangible}
+(@pxref{Special Properties,,Properties with Special Meanings, elisp,
+The Emacs Lisp Reference Manual}).
 
 @vindex completions-highlight-face
 When @code{completions-highlight-face} names a face, the current
index 6178394c50bb4dcbc89f5ba5b8a6841ce0f0bdd4..27ebadc8ee3e70919f33e8829705e53373954078 100644 (file)
@@ -1326,17 +1326,42 @@ The list of completions is displayed as text in a buffer named
 @file{*Completions*}.
 @end deffn
 
+@deffn Command minibuffer-sort-completions
+This function reorders the list of the possible completions.  It
+prompts you for one of the orders in
+@code{minibuffer-completions-sort-orders}, and applies that ordering
+to the current completions list.  When you call this function
+interactively with a negative prefix argument (@kbd{C-- C-x C-v} in
+the minibuffer), it reverses the order of the completion candidates.
+@end deffn
+
+@defopt minibuffer-completions-sort-orders
+This user option specifies which orders the command
+@code{minibuffer-sort-completions} suggests for sorting the
+completions list.  By default, this includes alphabetical sorting,
+sorting by candidate position in the minibuffer history, and no
+sorting at all.  See the documentation string of this user option for
+details about adding new sorting options.
+@end defopt
+
+@deffn Command minibuffer-narrow-completions
+This function narrows (restricts) the list of possible completions.
+It calls the function that the variable
+@code{minibuffer-narrow-completions-function} specifies to get a
+completions predicate, and adds that predicate to
+@code{minibuffer-completion-predicate}.
+@end deffn
+
 @defvar minibuffer-narrow-completions-function
 The value of this variable is a function that command
-@code{minibuffer-narrow-completions} (@kbd{C-x n m} in the minibuffer)
-calls to restrict the list of completion candidates.
-@code{minibuffer-narrow-completions} calls the function that is the
-value of this variable with no arguments, and this function should
-return a cons cell @code{(@var{pred} . @var{desc})} where @var{pred}
-is a function that takes one argument, a completion candidate, and
-returns non-@code{nil} if that candidate should appear in the
-@file{*Completions*} list, and @var{desc} is a string describing
-@var{pred}.
+@code{minibuffer-narrow-completions} calls to restrict the list of
+completion candidates.  @code{minibuffer-narrow-completions} calls the
+function that is the value of this variable with no arguments, and
+this function should return a cons cell @code{(@var{pred}
+. @var{desc})} where @var{pred} is a function that takes one argument,
+a completion candidate, and returns non-@code{nil} if that candidate
+should appear in the @file{*Completions*} list, and @var{desc} is a
+string describing @var{pred}.
 @end defvar
 
 @defun minibuffer-narrow-completions-by-regexp
@@ -1346,14 +1371,6 @@ for a regular expression, and returns a completions predicate that
 only keeps candidates matching that regular expression.
 @end defun
 
-@deffn Command minibuffer-narrow-completions
-This function narrows (restricts) the list of possible completions.
-It calls the function that the variable
-@code{minibuffer-narrow-completions-function} specifies to get a
-completions predicate, and adds that predicate to
-@code{minibuffer-completion-predicate}.
-@end deffn
-
 @deffn Command minibuffer-narrow-completions-to-current
 This is similar to @code{minibuffer-narrow-completions}, except that
 @code{minibuffer-narrow-completions-to-current} restricts the list of
@@ -1404,6 +1421,9 @@ keymap makes the following bindings:
 @item @key{TAB}
 @code{minibuffer-complete}
 
+@item C-x C-v
+@code{minibuffer-sort-completions}
+
 @item C-x n
 @code{minibuffer-narrow-completions-map}
 @end table
index 30bec00ce20671bd7cc878ed2c62358da1463909..7e42ed68ae6fdeaa7ca83f38ce6dad0a939bbf70 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -775,6 +775,13 @@ in the minibuffer to restrict the list of possible completions to only
 include candidates matching the current minibuffer input.  See the
 Info node "(emacs) Narrow Completions" for more information.
 
++++
+*** New command for reordering the minibuffer completions list.
+You can now use 'C-x C-v' ('minibuffer-sort-completions') in the
+minibuffer to change the sort order of the completions list.  If you
+invoke this command with a negative prefix argument ('C-- C-x C-v'),
+it reverses the current order.
+
 *** New minor mode 'completions-auto-update-mode'.
 This global minor mode automatically updates the *Completions* buffer
 as you type in the minibuffer.
index 8826547921e934148007ae45e2a8d8555d060b2b..d8e4497833aa1a372dfbcf4bf4a1857653ad917d 100644 (file)
@@ -2572,6 +2572,11 @@ It must accept a buffer as its only required argument.")
     '(menu-item "Restrict to Current Completions"
                 minibuffer-narrow-completions-to-current
                :help "Restrict completions according to minibuffer input"))
+  (bindings--define-key map
+      [menu-bar minibuf minibuffer-sort-completions]
+    '(menu-item "Sort Completions"
+                minibuffer-sort-completions
+               :help "Sort list of completion candidates"))
   (bindings--define-key map [menu-bar minibuf ?\?]
     '(menu-item "List Completions" minibuffer-completion-help
                :help "Display all possible completions"))
index 4059e81cd6a6943b44988c9d831240fd3f38d98e..0189695463ca5f844c7483c0bb54b57772ca236e 100644 (file)
@@ -2172,14 +2172,15 @@ completions."
   :version "28.1")
 
 (defcustom completions-header-format
-  (propertize "%s possible completions%r:\n" 'face 'shadow)
+  (propertize "%s possible completions%t%r:\n" 'face 'shadow)
   "If non-nil, the format string for completions heading line.
 The heading line is inserted before the completions, and is
 intended to summarize the completions.  The format string may
-contain the sequences \"%s\" and \"%r\", which are substituted
-with the total count of possible completions and the description
-of a description of the current completions restriction,
-respectively.  If this option is nil, no heading line is shown."
+contain the sequences \"%s\", \"%t\" and \"%r\", which are
+substituted with the total count of possible completions, the
+current completions sort order, and a description of the current
+completions restriction.  If this option is nil, no heading line
+is shown."
   :type '(choice (const :tag "No heading line" nil)
                  (string :tag "Format string for heading line"))
   :version "30.1")
@@ -2427,6 +2428,44 @@ and with BASE-SIZE appended as the last element."
           minibuffer-completion-predicate)
          (when descs (mapconcat #'identity descs ", ")))))
 
+(defvar minibuffer-completions-sort-function nil
+  "Function for sorting minibuffer completion candidates, or nil.
+
+When the value of this variable is a function,
+`minibuffer-completion-help' uses that function to sort the
+completions list instead of using the `display-sort-function'
+from the completion table or the value of `completions-sort'.
+
+`minibuffer-sort-completions' sets the value of this variable to
+temporarily override the default completions sorting.")
+
+(defcustom minibuffer-completions-sort-orders
+  '((?a "alphabetical" "Sort alphabetically"
+        minibuffer-sort-alphabetically "sorted alphabetically")
+    (?h "historical" "Sort by position in minibuffer history"
+        minibuffer-sort-by-history "sorted by position in minibuffer history")
+    (?i "identity" "Disable sorting" identity nil)
+    (?d "default" "Default sort order" nil nil))
+  "List of minibuffer completions sort orders.
+Each element is a list of the form (CHAR NAME HELP FUNC DESC),
+where CHAR is a character that you type to select this sort order
+in `minibuffer-sort-completions', NAME is the name of the sort
+order, HELP is a short help string that explains what this sort
+order does, FUNC is the completions sorting function, and DESC is
+a desription that is shown in the *Completions* buffer when the
+sort order is in effect.
+
+FUNC can also be nil, which says to use the default sort order
+when you select this sort order."
+  :version "30.1"
+  :type '(repeat
+          (list character string string
+                (choice function
+                        (const :tag "No sorting" nil)
+                        (const :tag "Use default sort order" identity))
+                (choice string
+                        (const :tag "No description" nil)))))
+
 (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
@@ -2457,12 +2496,29 @@ candidates."
     (let ((pred-desc
            (if-let ((pd (minibuffer--completion-predicate-description)))
                (concat ", " pd)
+             ""))
+          (sort-desc
+           (if minibuffer-completions-sort-function
+               (concat
+                (when-let
+                    ((sd (nth 4 (seq-find
+                                 (lambda (order)
+                                   (eq
+                                    (nth 3 order)
+                                    (advice--cd*r
+                                     minibuffer-completions-sort-function)))
+                                 minibuffer-completions-sort-orders))))
+                  (concat ", " sd))
+                (when (advice-function-member-p
+                       #'reverse minibuffer-completions-sort-function)
+                  ", reversed"))
              "")))
       (with-current-buffer standard-output
         (goto-char (point-max))
         (if completions-header-format
             (insert (format-spec completions-header-format
                                  (list (cons ?s (length completions))
+                                       (cons ?t sort-desc)
                                        (cons ?r pred-desc))))
           (unless completion-show-help
             ;; Ensure beginning-of-buffer isn't a completion.
@@ -2575,6 +2631,54 @@ The candidate will still be chosen by `choose-completion' unless
       (with-selected-window window
         (completions--deselect)))))
 
+(defcustom minibuffer-read-sort-order-with-completion nil
+  "Whether to use completion for reading minibuffer completions sort order.
+If this user options is nil (the default),
+`minibuffer-sort-completions' lets you to select a sort order by
+typing a single key, which is usually the first letter of the
+name of the sort order.  If you set this user option to non-nil,
+`minibuffer-sort-completions' instead reads the sort order name
+in the minibuffer, with completion."
+  :type 'boolean
+  :version "30.1")
+
+(defun minibuffer-sort-completions (arg)
+  "Sort the list of minibuffer completion candidates.
+Prompt for a sort order among
+`minibuffer-completions-sort-orders' and apply it to the current
+completions list.  With negative prefix argument ARG, reverse the
+current order instead."
+  (interactive "p" minibuffer-mode)
+  (if (< arg 0)
+      (if (advice-function-member-p
+           #'reverse minibuffer-completions-sort-function)
+          (remove-function
+           (local 'minibuffer-completions-sort-function) #'reverse)
+        (unless minibuffer-completions-sort-function
+          (setq-local minibuffer-completions-sort-function
+                      (or (completion-metadata-get
+                           (completion--field-metadata
+                            (car (minibuffer--completion-boundaries)))
+                           'display-sort-function)
+                          (pcase completions-sort
+                            ('nil #'identity)
+                            ('alphabetical #'minibuffer-sort-alphabetically)
+                            ('historical #'minibuffer-sort-by-history)
+                            ;; It's already a function, use it.
+                            (_ completions-sort)))))
+        (add-function
+         :filter-return
+         (local 'minibuffer-completions-sort-function) #'reverse))
+    (setq-local
+     minibuffer-completions-sort-function
+     (nth 3 (let ((enable-recursive-minibuffers t))
+              (read-multiple-choice
+               "Sort order" minibuffer-completions-sort-orders
+               nil nil minibuffer-read-sort-order-with-completion)))))
+  (when completion-auto-help
+    (let ((beg-end (minibuffer--completion-boundaries)))
+      (minibuffer-completion-help (car beg-end) (cdr beg-end)))))
+
 (defun minibuffer-completion-help (&optional start end)
   "Display a list of possible completions of the current minibuffer contents."
   (interactive)
@@ -2664,13 +2768,19 @@ The candidate will still be chosen by `choose-completion' unless
                       ;; all-completions, not
                       ;; completion-all-completions.  Often it's the
                       ;; same, but not always.
-                      (setq completions (if sort-fun
-                                            (funcall sort-fun completions)
-                                          (pcase completions-sort
-                                            ('nil completions)
-                                            ('alphabetical (minibuffer-sort-alphabetically completions))
-                                            ('historical (minibuffer-sort-by-history completions))
-                                            (_ (funcall completions-sort completions)))))
+                      (setq completions
+                            (cond
+                             (minibuffer-completions-sort-function
+                              (funcall minibuffer-completions-sort-function
+                                       completions))
+                             (sort-fun
+                              (funcall sort-fun completions))
+                             (t
+                              (pcase completions-sort
+                                ('nil completions)
+                                ('alphabetical (minibuffer-sort-alphabetically completions))
+                                ('historical (minibuffer-sort-by-history completions))
+                                (_ (funcall completions-sort completions))))))
 
                       ;; After sorting, group the candidates using the
                       ;; `group-function'.
@@ -3091,6 +3201,7 @@ The completion method is determined by `completion-at-point-functions'."
   "M-<up>"    #'minibuffer-previous-completion
   "M-<down>"  #'minibuffer-next-completion
   "M-RET"     #'minibuffer-choose-completion
+  "C-x C-v"   #'minibuffer-sort-completions
   "C-x n"     'minibuffer-narrow-completions-map)
 
 (defvar-keymap minibuffer-local-must-match-map