branch: externals/mct
commit a09951c202de6cf3d24c4eb1d174f0a8b52fc830
Author: Protesilaos Stavrou <i...@protesilaos.com>
Commit: Protesilaos Stavrou <i...@protesilaos.com>

    Define option mct-sort-by-command-or-category and always set 
completions-sort
---
 README.org | 43 ++++++++++++++++++++++++++++++++++++
 mct.el     | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 107 insertions(+), 10 deletions(-)

diff --git a/README.org b/README.org
index 68bcfe94aa..fe28f13ae0 100644
--- a/README.org
+++ b/README.org
@@ -338,6 +338,43 @@ To review echo area messages, use =C-h e= 
(~view-echo-area-messages~).
 
 [[#h:1f42c4e6-53c1-4e8a-81ef-deab70822fa4][Known completion categories]].
 
+** Sort completions based on the command or completion category
+:PROPERTIES:
+:CUSTOM_ID: h:d5cd6e2e-d121-425c-96ec-f8bc9c630dd2
+:END:
+#+vindex: mct-sort-by-command-or-category
+[ This is part of {{{development-version}}}. ]
+
+Brief: Sort completion candidates based on the command or completion
+category.
+
+Symbol: ~mct-sort-by-command-or-category~ (=alist= type)
+
+This is an alist where each element is of the form =(SYMBOLS . SORT-FUNCTION)=.
+
+=SYMBOLS= is either a symbol or a list of symbols. =SYMBOLS= can refer to
+the symbol of a function or completion category. It can also be ~t~,
+which refers to the fallback value.
+
+=SORT-FUNCTION= is a function that takes a list of strings and returns a
+list of strings, sorting them accordingly. Examples of a =SORT-FUNCTION=
+are:
+
+#+findex: mct-sort-by-alpha
+- ~mct-sort-by-alpha~
+
+#+findex: mct-sort-by-alpha-then-by-length
+- ~mct-sort-by-alpha-then-by-length~
+
+#+findex: mct-sort-by-history
+- ~mct-sort-by-history~
+
+#+findex: mct-sort-by-directory-then-by-file
+- ~mct-sort-by-directory-then-by-file~
+
+To not perform any sorting on the completion candidates that match
+=SYMBOLS= set =SORT-FUNCTION= to ~nil~.
+
 ** Size boundaries of the Completions
 :PROPERTIES:
 :CUSTOM_ID: h:2fcf708f-4edf-41f3-9e29-0e750f3a80af
@@ -861,6 +898,12 @@ In this section we cover custom code that builds on what 
MCT offers.
 :END:
 #+cindex: Sorting completions
 
+[ As part of {{{development-version}}}, MCT provides the user option
+  ~mct-sort-by-command-or-category~ and sets the ~completions-sort~
+  accordingly ([[#h:d5cd6e2e-d121-425c-96ec-f8bc9c630dd2][Sort completions 
based on the command or completion category]]).
+  The information herein is thus not needed, but may still be useful
+  in writing a custom sort function. ]
+
 Starting with Emacs 29 (current development target), the user option
 ~completions-sort~ controls the sorting method of candidates in the
 =*Completions*= buffer.  Beside the default of using ~string-lessp~, it
diff --git a/mct.el b/mct.el
index 282f4830c4..6c38bf9424 100644
--- a/mct.el
+++ b/mct.el
@@ -201,6 +201,40 @@ Read the manual for known completion categories."
   :type '(repeat symbol)
   :group 'mct)
 
+(defcustom mct-sort-by-command-or-category
+  '((file . mct-sort-by-directory-then-by-file)
+    ((magit-checkout vc-retrieve-tag) . mct-sort-by-alpha-then-by-length)
+    ((kill-ring imenu consult-location Info-goto-node Info-index Info-menu) . 
nil)
+    (t . mct-sort-by-history))
+  "Sort completion candidates based on the command or completion category.
+This is an alist where each element is of the form (SYMBOLS . SORT-FUNCTION).
+
+SYMBOLS is either a symbol or a list of symbols.  SYMBOLS can refer to
+the symbol of a function or completion category.  It can also be t,
+which refers to the fallback value.
+
+SORT-FUNCTION is a function that takes a list of strings and returns a
+list of strings, sorting them accordingly.  Examples of a SORT-FUNCTION
+are:
+
+- `mct-sort-by-alpha'
+- `mct-sort-by-alpha-then-by-length'
+- `mct-sort-by-history'
+- `mct-sort-by-directory-then-by-file'
+
+To not perform any sorting on the completion candidates that match
+SYMBOLS set SORT-FUNCTION to nil."
+  :type '(alist
+          :key-type (choice symbol (repeat symbol))
+          :value-type (choice
+                       (const :tag "Sort A-Z" mct-sort-by-alpha)
+                       (const :tag "Sort A-Z then short to long" 
mct-sort-by-alpha-then-by-length)
+                       (const :tag "Sort by minibuffer history" 
mct-sort-by-history)
+                       (const :tag "Sort by directory then by file" 
mct-sort-by-directory-then-by-file)
+                       (function :tag "Custom sort function accepting 
COMPLETIONS argument")))
+  :package-version '(mct . "1.1.0")
+  :group 'mct)
+
 (make-obsolete-variable 'mct-display-buffer-action nil "1.0.0")
 (make-obsolete-variable 'mct-completions-format nil "1.0.0")
 (make-obsolete-variable 'mct-persist-dynamic-completion 
'mct-completion-passlist "1.1.0")
@@ -250,7 +284,7 @@ This function can be used as the value of the user option
    (lambda (string1 string2)
      (funcall mct-sort-alpha-function string1 string2))))
 
-(defun mct-sort-by-alpha-length (completions)
+(defun mct-sort-by-alpha-then-by-length (completions)
   "Sort COMPLETIONS first alphabetically, then by length.
 This function can be used as the value of the user option
 `completions-sort'."
@@ -260,6 +294,9 @@ This function can be used as the value of the user option
      (or (funcall mct-sort-alpha-function string1 string2)
          (< (length string1) (length string2))))))
 
+(defalias 'mct-sort-by-alpha-length 'mct-sort-by-alpha-then-by-length
+  "Alias for `mct-sort-by-alpha-then-by-length'.")
+
 ;; Based on `minibuffer-sort-by-history' from Emacs 30.
 (defun mct-sort-by-history (completions)
   "Sort COMPLETIONS by minibuffer history, else return them unsorted.
@@ -274,21 +311,33 @@ This function can be used as the value of the user option
        (minibuffer--sort-preprocess-history minibuffer-completion-base)
        alphabetized))))
 
-(defun mct-sort-directories-then-files (completions)
-  "Sort COMPLETIONS with `mct-sort-by-alpha-length' with directories first."
+(defun mct-sort-by-directory-then-by-file (completions)
+  "Sort COMPLETIONS with `mct-sort-by-alpha-then-by-length' with directories 
first."
   (setq completions (mct-sort-by-alpha completions))
   ;; But then move directories first
   (nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) completions)
          (seq-remove (lambda (x) (string-suffix-p "/" x)) completions)))
 
+(defalias 'mct-sort-directories-then-files 'mct-sort-by-directory-then-by-file
+  "Alias for `mct-sort-by-directory-then-by-file'.")
+
+(defun mct--sort-multi-category-get-function (symbol)
+  "Return sort function for SYMBOL."
+  (catch 'found
+    (pcase-dolist (`(,symbols . ,sort) mct-sort-by-command-or-category)
+      (when (if (listp symbols)
+                (memq symbol symbols)
+              (eq symbol symbols))
+        (throw 'found sort)))))
+
 (defun mct-sort-multi-category (completions)
-  "Sort COMPLETIONS per completion category.
-This function can be used as the value of the user option
-`completions-sort'."
-  (pcase (mct--completion-category)
-    ('kill-ring completions) ; no sorting
-    ('file (mct-sort-directories-then-files completions))
-    (_ (mct-sort-by-history completions))))
+  "Sort COMPLETIONS per command or completion category.
+Do it in accordance with the user option `mct-sort-by-command-or-category'."
+  (if-let* ((symbol (or (mct--this-command) (mct--completion-category) t))
+            (sort-fn (mct--sort-multi-category-get-function symbol))
+            (_ (functionp sort-fn)))
+      (funcall sort-fn completions)
+    completions))
 
 ;;;; Basics of intersection between minibuffer and Completions buffer
 
@@ -1023,6 +1072,9 @@ Do this under any of the following conditions:
 
 (declare-function minibuf-eldef-setup-minibuffer "minibuf-eldef")
 
+(defvar mct-last-completions-sort-value (bound-and-true-p completions-sort)
+  "Last value of `completions-sort'.")
+
 ;;;###autoload
 (define-minor-mode mct-mode
   "Set up opinionated default completion UI."
@@ -1030,6 +1082,7 @@ Do this under any of the following conditions:
   :group 'mct
   (if mct-mode
       (progn
+        (setq completions-sort #'mct-sort-multi-category)
         (add-hook 'completion-list-mode-hook #'mct--setup-completion-list)
         (add-hook 'minibuffer-setup-hook #'mct--setup-passlist)
         (advice-add #'completing-read-default :around 
#'mct--completing-read-advice)
@@ -1039,6 +1092,7 @@ Do this under any of the following conditions:
         (advice-add #'minibuffer-completion-help :around 
#'mct--minibuffer-completion-help-advice)
         (advice-add #'minibuf-eldef-setup-minibuffer :around #'mct--stealthily)
         (advice-add #'completion--insert-strings :after 
#'mct--completion--insert-strings))
+    (setq completions-sort mct-last-completions-sort-value)
     (remove-hook 'completion-list-mode-hook #'mct--setup-completion-list)
     (remove-hook 'minibuffer-setup-hook #'mct--setup-passlist)
     (advice-remove #'completing-read-default #'mct--completing-read-advice)

Reply via email to