branch: externals/vertico
commit ad5069726a6e4d588757f8c952ab9df9eb293f1e
Author: Daniel Mendler <m...@daniel-mendler.de>
Commit: Daniel Mendler <m...@daniel-mendler.de>

    Extract vertico-sort
---
 CHANGELOG.org              |   1 +
 README.org                 |   1 +
 extensions/vertico-sort.el | 104 +++++++++++++++++++++++++++++++++++++++++++++
 vertico.el                 |  72 +++----------------------------
 4 files changed, 113 insertions(+), 65 deletions(-)

diff --git a/CHANGELOG.org b/CHANGELOG.org
index e76c465db4..393e388c78 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -5,6 +5,7 @@
 * Development
 
 - =vertico-flat-format=: Customizable =:spacer= string.
+- =vertico-sort=: Extracted sort functions to separate extension.
 
 * Version 2.0 (2025-03-11)
 
diff --git a/README.org b/README.org
index 4127b526f8..8a95bf8bbc 100644
--- a/README.org
+++ b/README.org
@@ -248,6 +248,7 @@ following extensions come with the Vertico ELPA package:
 - 
[[https://github.com/minad/vertico/blob/main/extensions/vertico-quick.el][vertico-quick]]:
 Commands to select using Avy-style quick keys.
 - 
[[https://github.com/minad/vertico/blob/main/extensions/vertico-repeat.el][vertico-repeat]]:
 The command =vertico-repeat= repeats the last completion session.
 - 
[[https://github.com/minad/vertico/blob/main/extensions/vertico-reverse.el][vertico-reverse]]:
 =vertico-reverse-mode= to reverse the display.
+- 
[[https://github.com/minad/vertico/blob/main/extensions/vertico-sort.el][vertico-sort]]:
 Provides optimized sort functions, by history, by length and lexical.
 - 
[[https://github.com/minad/vertico/blob/main/extensions/vertico-suspend.el][vertico-suspend]]:
 The command =vertico-suspend= suspends and restores the current session.
 - 
[[https://github.com/minad/vertico/blob/main/extensions/vertico-unobtrusive.el][vertico-unobtrusive]]:
 =vertico-unobtrusive-mode= displays only the topmost candidate.
 
diff --git a/extensions/vertico-sort.el b/extensions/vertico-sort.el
new file mode 100644
index 0000000000..d35e253c63
--- /dev/null
+++ b/extensions/vertico-sort.el
@@ -0,0 +1,104 @@
+;;; vertico-sort.el --- Sort functions for Vertico -*- lexical-binding: t -*-
+
+;; Copyright (C) 2021-2025 Free Software Foundation, Inc.
+
+;; Author: Daniel Mendler <m...@daniel-mendler.de>
+;; Maintainer: Daniel Mendler <m...@daniel-mendler.de>
+;; Created: 2021
+;; Version: 2.0
+;; Package-Requires: ((emacs "28.1") (compat "30") (vertico "2.0"))
+;; URL: https://github.com/minad/vertico
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides a set of sort functions for Vertico, which
+;; can be used as `vertico-sort-function'.  By default, Vertico uses
+;; the `vertico-sort-history-length-alpha' function.
+
+;;; Code:
+
+(require 'vertico)
+(eval-when-compile (require 'cl-lib))
+
+(defvar-local vertico-sort--history nil
+  "History hash table and corresponding base string.")
+
+(defun vertico-sort--history ()
+  "Recompute history hash table and return it."
+  (or (and (equal (car vertico-sort--history) vertico--base) (cdr 
vertico-sort--history))
+      (let* ((base vertico--base)
+             (base-len (length base))
+             (hist (and (not (eq minibuffer-history-variable t)) ;; Disabled 
for `t'.
+                        (symbol-value minibuffer-history-variable)))
+             (hash (make-hash-table :test #'equal :size (length hist)))
+             (file-p (and (> base-len 0) ;; Step-wise completion, unlike 
`project-find-file'
+                          (eq minibuffer-history-variable 'file-name-history)))
+             (curr-file (when-let ((win (and file-p 
(minibuffer-selected-window)))
+                                   (file (buffer-file-name (window-buffer 
win))))
+                          (abbreviate-file-name file))))
+        (cl-loop for elem in hist for index from 0 do
+                 (when (and (not (equal curr-file elem)) ;; Deprioritize 
current file
+                            (or (= base-len 0)
+                                (and (>= (length elem) base-len)
+                                     (eq t (compare-strings base 0 base-len 
elem 0 base-len)))))
+                   (let ((file-sep (and file-p (string-search "/" elem 
base-len))))
+                     ;; Drop base string from history elements & special file 
handling.
+                     (when (or (> base-len 0) file-sep)
+                       (setq elem (substring elem base-len (and file-sep (1+ 
file-sep)))))
+                     (unless (gethash elem hash) (puthash elem index hash)))))
+        (cdr (setq vertico-sort--history (cons base hash))))))
+
+(defun vertico-sort--length-string< (x y)
+  "Sorting predicate which compares X and Y first by length then by `string<'."
+  (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y))))
+
+(defun vertico-sort--decorated (list)
+  "Sort decorated LIST and remove decorations."
+  (setq list (sort list #'car-less-than-car))
+  (cl-loop for item on list do (setcar item (cdar item)))
+  list)
+
+(defmacro vertico-sort--define (by bsize bindex bpred pred)
+  "Generate optimized sorting function.
+The function is configured by BY, BSIZE, BINDEX, BPRED and PRED."
+  `(defun ,(intern (mapconcat #'symbol-name `(vertico sort ,@by) "-")) 
(candidates)
+     ,(concat "Sort candidates by " (mapconcat #'symbol-name by ", ") ".")
+     (let* ((buckets (make-vector ,bsize nil))
+            ,@(and (eq (car by) 'history) '((hhash (vertico-sort--history)) 
(hcands))))
+       (dolist (% candidates)
+         ;; Find recent candidate in history or fill bucket
+         (,@(if (not (eq (car by) 'history)) `(progn)
+              `(if-let ((idx (gethash % hhash))) (push (cons idx %) hcands)))
+          (push % (aref buckets (min ,(1- bsize) ,bindex)))))
+       (nconc ,@(and (eq (car by) 'history) '((vertico-sort--decorated 
hcands)))
+              (mapcan (lambda (bucket) (sort bucket #',bpred))
+                      (nbutlast (append buckets nil)))
+              ;; Last bucket needs special treatment
+              (sort (aref buckets ,(1- bsize)) #',pred)))))
+
+;;;###autoload (autoload 'vertico-sort-history-length-alpha "vertico-sort")
+;;;###autoload (autoload 'vertico-sort-history-alpha "vertico-sort")
+;;;###autoload (autoload 'vertico-sort-length-alpha "vertico-sort")
+;;;###autoload (autoload 'vertico-sort-alpha "vertico-sort")
+(vertico-sort--define (history length alpha) 48 (length %) string< 
vertico-sort--length-string<)
+(vertico-sort--define (history alpha) 32 (if (equal % "") 0 (/ (aref % 0) 4)) 
string< string<)
+(vertico-sort--define (length alpha) 48 (length %) string< 
vertico-sort--length-string<)
+(vertico-sort--define (alpha) 32 (if (equal % "") 0 (/ (aref % 0) 4)) string< 
string<)
+
+(provide 'vertico-sort)
+;;; vertico-sort.el ends here
diff --git a/vertico.el b/vertico.el
index 362b6f1fdd..8655774479 100644
--- a/vertico.el
+++ b/vertico.el
@@ -92,14 +92,15 @@ The value should lie between 0 and vertico-count/2."
   "Replacements for multiline strings."
   :type '(cons (string :tag "Newline") (string :tag "Truncation")))
 
-(defcustom vertico-sort-function #'vertico-sort-history-length-alpha
+(defcustom vertico-sort-function
+  (and (fboundp 'vertico-sort-history-length-alpha) 
'vertico-sort-history-length-alpha)
   "Default sorting function, used if no `display-sort-function' is specified."
-  :type `(choice
+  :type '(choice
           (const :tag "No sorting" nil)
-          (const :tag "By history, length and alpha" 
,#'vertico-sort-history-length-alpha)
-          (const :tag "By history and alpha" ,#'vertico-sort-history-alpha)
-          (const :tag "By length and alpha" ,#'vertico-sort-length-alpha)
-          (const :tag "Alphabetically" ,#'vertico-sort-alpha)
+          (const :tag "By history, length and alpha" 
vertico-sort-history-length-alpha)
+          (const :tag "By history and alpha" vertico-sort-history-alpha)
+          (const :tag "By length and alpha" vertico-sort-length-alpha)
+          (const :tag "Alphabetically" vertico-sort-alpha)
           (function :tag "Custom function")))
 
 (defcustom vertico-sort-override-function nil
@@ -146,9 +147,6 @@ The value should lie between 0 and vertico-count/2."
 (defvar-local vertico--hilit #'identity
   "Lazy candidate highlighting function.")
 
-(defvar-local vertico--history-hash nil
-  "History hash table and corresponding base string.")
-
 (defvar-local vertico--candidates-ov nil
   "Overlay showing the candidates.")
 
@@ -191,62 +189,6 @@ The value should lie between 0 and vertico-count/2."
 (defvar-local vertico--allow-prompt nil
   "Prompt selection is allowed.")
 
-(defun vertico--history-hash ()
-  "Recompute history hash table and return it."
-  (or (and (equal (car vertico--history-hash) vertico--base) (cdr 
vertico--history-hash))
-      (let* ((base vertico--base)
-             (base-len (length base))
-             (hist (and (not (eq minibuffer-history-variable t)) ;; Disabled 
for `t'.
-                        (symbol-value minibuffer-history-variable)))
-             (hash (make-hash-table :test #'equal :size (length hist)))
-             (file-p (and (> base-len 0) ;; Step-wise completion, unlike 
`project-find-file'
-                          (eq minibuffer-history-variable 'file-name-history)))
-             (curr-file (when-let ((win (and file-p 
(minibuffer-selected-window)))
-                                   (file (buffer-file-name (window-buffer 
win))))
-                          (abbreviate-file-name file))))
-        (cl-loop for elem in hist for index from 0 do
-                 (when (and (not (equal curr-file elem)) ;; Deprioritize 
current file
-                            (or (= base-len 0)
-                                (and (>= (length elem) base-len)
-                                     (eq t (compare-strings base 0 base-len 
elem 0 base-len)))))
-                   (let ((file-sep (and file-p (string-search "/" elem 
base-len))))
-                     ;; Drop base string from history elements & special file 
handling.
-                     (when (or (> base-len 0) file-sep)
-                       (setq elem (substring elem base-len (and file-sep (1+ 
file-sep)))))
-                     (unless (gethash elem hash) (puthash elem index hash)))))
-        (cdr (setq vertico--history-hash (cons base hash))))))
-
-(defun vertico--length-string< (x y)
-  "Sorting predicate which compares X and Y first by length then by `string<'."
-  (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y))))
-
-(defun vertico--sort-decorated (list)
-  "Sort decorated LIST and remove decorations."
-  (setq list (sort list #'car-less-than-car))
-  (cl-loop for item on list do (setcar item (cdar item)))
-  list)
-
-(defmacro vertico--define-sort (by bsize bindex bpred pred)
-  "Generate optimized sorting function.
-The function is configured by BY, BSIZE, BINDEX, BPRED and PRED."
-  `(defun ,(intern (mapconcat #'symbol-name `(vertico sort ,@by) "-")) 
(candidates)
-     ,(concat "Sort candidates by " (mapconcat #'symbol-name by ", ") ".")
-     (let* ((buckets (make-vector ,bsize nil)) last
-            ,@(and (eq (car by) 'history) '((hhash (vertico--history-hash)) 
hcands)))
-       (dolist (% candidates)
-         ;; Find recent candidate in history or fill bucket
-         (,@(if (not (eq (car by) 'history)) `(progn)
-              `(if-let ((idx (gethash % hhash))) (push (cons idx %) hcands)))
-          (let ((i ,bindex)) (if (< i ,bsize) (push % (aref buckets i)) (push 
% last)))))
-       (nconc ,@(and (eq (car by) 'history) '((vertico--sort-decorated 
hcands)))
-              (mapcan (lambda (bucket) (sort bucket #',bpred)) buckets)
-              (sort last #',pred)))))
-
-(vertico--define-sort (history length alpha) 48 (length %) string< 
vertico--length-string<)
-(vertico--define-sort (history alpha) 32 (if (equal % "") 0 (/ (aref % 0) 4)) 
string< string<)
-(vertico--define-sort (length alpha) 48 (length %) string< 
vertico--length-string<)
-(vertico--define-sort (alpha) 32 (if (equal % "") 0 (/ (aref % 0) 4)) string< 
string<)
-
 (defun vertico--affixate (cands)
   "Annotate CANDS with annotation function."
   (if-let ((aff (vertico--metadata-get 'affixation-function)))

Reply via email to