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)