I don’t know about you, but I like to monitor my Emacs configuration to see if
it is growing out of control due to my penchant for experimenting with ELisp and
new packages. This led me to a simple question: why not have a command that can
produce a summary of the installed packages? Specifically, I want to know the
total number of packages installed and I’d like to list the packages for every
archive set up in
(defun mu-package-report () "Report total package counts grouped by archive." (interactive) (package-refresh-contents) (mu--display-package-report (let* ((arch-pkgs (mu--archive-packages)) (counts (seq-sort-by #'cdr #'> (mu--archive-counts arch-pkgs))) (by-arch (seq-group-by #'car arch-pkgs))) (concat (format "Total packages: %s\n\n" (apply #'+ (mapcar #'cdr counts))) (mapconcat (lambda (archive) (concat "• " (format "%s (%s)" (car archive) (cdr archive)) ": " (mapconcat (lambda (ap-pair) (cdr ap-pair)) (alist-get (car archive) by-arch) ", "))) counts "\n\n")))))
Now, let’s unpack it.
I want my report buffer to be displayed in a new window, so this is what
(defun mu--display-package-report (output) "Display OUTPUT in a popup buffer." (let ((buffer-name "*package-report*")) (with-help-window buffer-name (with-current-buffer buffer-name (visual-line-mode 1) (erase-buffer) (insert output) (goto-char (point-min))))))
Next, I want to know from which archive (e.g., GNU ELPA) a package is coming to my system:
(defun mu--archive-packages () "Return a list of (archive . package) cons cells." (seq-reduce (lambda (res package) (let ((archive (package-desc-archive (cadr (assq package package-archive-contents)))) (pkg (symbol-name package))) (push (cons archive pkg) res))) (mapcar #'car package-alist) nil))
This is nice because now I can also have the number of packages for every archive.
(defun mu--archive-counts (arch-pkgs) "Return a list of cons cells from alist ARCH-PKGS. The cars are package archives, the cdrs are the number of packages installed from each archive." (seq-reduce (lambda (counts key) (cons (cons key (+ 1 (or (cdr (assoc key counts)) 0))) (assoc-delete-all key counts))) (mapcar #'car arch-pkgs) nil))
The rest of the code in
mu-package-report deals with formatting the output.
For example, I am using
seq-sort-by to have the archive with the highest
number of packages on top. As for the
mapconcat producing the actual bullet
points, the Emacs community came to the rescue on Emacs Stack
Finally, this is how M-x mu-package-report RET looks like: