Don't let package.el pollute the customization

Since Emacs 25.1 package.el tracks which packages I installed manually. The reasons are outlined in a much better way than I could do this in this article.

That’s nice and dandy, but whenever I try out a package my custom file changes, because “packages-selected-packages” changes and is unfortunately written there.

So whenever I install something, even experimental things, this:

’(package-selected-packages (quote (ivy-hydra flx ivy magit font-lock ox-reveal company-quickhelp ox-gfm org-plus-contrib org company-c-headers erc-hl-nicks notmuch company helm-flycheck zenburn-theme eros elfeed flycheck rust-mode helm-flyspell kurecolor toml-mode git-timemachine zeal-at-point rainbow-mode dumb-jump column-marker web-mode pug-mode markdown-mode lua-mode js2-mode go-mode d-mode dtrt-indent clean-aindent-mode pdf-tools htmlize helm-swoop helm-descbinds which-key iflipb avy-zap smartscan avy expand-region undo-tree keyfreq dash paradox use-package))))

changes. And also often in a way that a git merge conflict is almost certain.

Stopping changes to the custom.el file

I already track my packages manually: with “:ensure t” lines in “(use-package” clauses. So certainly I can get rid of the tracking in custom.el?!?!

And it is quite easy, I found the followed tip how to make this:

(defun package--save-selected-packages (&optional VALUE opt)
  nil)

This is the function in package.el that normally saves the value to the customization system. By redefining it to a dummy function, nothing will be saved. My custom.el stays clean.

Telling package.el about manually installed packages

That was easy. But now “package-list-packages” (and also “paradox-list-packages”) list all packages as “dependency”, none as “installed”. Without the saved values from “packages-selected-packages” package.el doesn’t know that what packages I have installed manually and which it installed automatically.

So we must transfer the information from use-packages’s “:ensure t” to package.el !

To do this, simply redirect use-packages’ handler for the “:ensure” clause to your own function. Save those packages that have an ensure clause into “packages-selected-packages” and finally call the original function:

(defun my-use-package-ensure-elpa (name ensure state context &optional no-refresh)
  (let ((package (or (when (eq ensure t) (use-package-as-symbol name))
                     ensure)))
    (when package
      (add-to-list 'package-selected-packages package)))
  (use-package-ensure-elpa name ensure state context no-refresh))

(setq use-package-ensure-function #'my-use-package-ensure-elpa)