Subject: [PATCH] Improve sorting in package list

From: Stefan Kangas <[email-masked]>
Date: Mon, 16 Sep 2019 09:36:01 +0200

The attached patch fixes sorting by version or description in the
package list.  It also improves sorting by archive to fall back to
name, and adds doc strings.

Any comments?

Best regards,
Stefan Kangas
From 76d9b28014c8c11159e01d8713e3ce822c8b1244 Mon Sep 17 00:00:00 2001
From: Stefan Kangas <[email-masked]>
Date: Mon, 16 Sep 2019 09:29:48 +0200
Subject: [PATCH] Improve sorting in package list

* lisp/emacs-lisp/package.el (package-menu-mode): Allow sorting by
version or description.
(package-menu--version-predicate): Fix sorting by version.
(package-menu--description-predicate): Fix sorting by description.
(package-menu--archive-predicate): Fall back to sorting by name if
archive is the same.
(package-menu--name-predicate, package-menu--status-predicate): Add
doc string.
* etc/NEWS: Announce the above changes.
---
 etc/NEWS                   |  3 +++
 lisp/emacs-lisp/package.el | 29 +++++++++++++++++++++--------
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 1153daf9ac..98d97eb90a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -975,6 +975,9 @@ early init file.
 ---
 *** Imenu support has been added to 'package-menu-mode'.
 
+---
+*** The package list can now be sorted by version or description.
+
 ** Info
 
 +++
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index ef0c5171de..13361d489d 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -2678,11 +2678,11 @@ package-menu-mode
                              package-menu--transaction-status)))
   (setq tabulated-list-format
         `[("Package" 18 package-menu--name-predicate)
-          ("Version" 13 nil)
+          ("Version" 13 package-menu--version-predicate)
           ("Status"  10 package-menu--status-predicate)
           ,@(if (cdr package-archives)
                 '(("Archive" 10 package-menu--archive-predicate)))
-          ("Description" 0 nil)])
+          ("Description" 0 package-menu--description-predicate)])
   (setq tabulated-list-padding 2)
   (setq tabulated-list-sort-key (cons "Status" nil))
   (add-hook 'tabulated-list-revert-hook #'package-menu--refresh nil t)
@@ -3417,13 +3417,17 @@ package-menu-execute
               (message "Operation %s finished" message-template))))))))
 
 (defun package-menu--version-predicate (A B)
-  (let ((vA (or (aref (cadr A) 1)  '(0)))
-        (vB (or (aref (cadr B) 1) '(0))))
+  "Predicate to sort \"*Packages*\" buffer by the version column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
+  (let ((vA (or (version-to-list (aref (cadr A) 1)) '(0)))
+        (vB (or (version-to-list (aref (cadr B) 1)) '(0))))
     (if (version-list-= vA vB)
         (package-menu--name-predicate A B)
       (version-list-< vA vB))))
 
 (defun package-menu--status-predicate (A B)
+  "Predicate to sort \"*Packages*\" buffer by the status column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
   (let ((sA (aref (cadr A) 2))
         (sB (aref (cadr B) 2)))
     (cond ((string= sA sB)
@@ -3454,19 +3458,28 @@ package-menu--status-predicate
           (t (string< sA sB)))))
 
 (defun package-menu--description-predicate (A B)
-  (let ((dA (aref (cadr A) 3))
-        (dB (aref (cadr B) 3)))
+  "Predicate to sort \"*Packages*\" buffer by the description column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
+  (let ((dA (aref (cadr A) 4))
+        (dB (aref (cadr B) 4)))
     (if (string= dA dB)
         (package-menu--name-predicate A B)
       (string< dA dB))))
 
 (defun package-menu--name-predicate (A B)
+  "Predicate to sort \"*Packages*\" buffer by the name column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
   (string< (symbol-name (package-desc-name (car A)))
            (symbol-name (package-desc-name (car B)))))
 
 (defun package-menu--archive-predicate (A B)
-  (string< (or (package-desc-archive (car A)) "")
-           (or (package-desc-archive (car B)) "")))
+  "Predicate to sort \"*Packages*\" buffer by the archive column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
+  (let ((a (or (package-desc-archive (car A)) ""))
+        (b (or (package-desc-archive (car B)) "")))
+    (if (string= a b)
+        (package-menu--name-predicate A B)
+      (string< a b))))
 
 (defun package-menu--populate-new-package-list ()
   "Decide which packages are new in `package-archives-contents'.
-- 
2.20.1