For this one I got inspired by someone else. A little while ago I saw a YouTube stream by System Crafters called Do we really need use-package in Emacs?.
Since starting out with Emacs roughly 2 years ago, I have been using
use-package. Probably because most of the resources that helped me to learn used it. I never questioned it. To be quite frank, I never had a reason to do. For me it does what it promises.
The use-package macro allows you to isolate package configuration in your .emacs file in a way that is both performance-oriented and, well, tidy.
The only reason to even think of an Emacs configuration without
use-package is just the age old question: How hard can it be? In other words, I see an idea and I want to try it out.
Ok, there is an added benefit for me in that I get to learn more about the innards of Emacs. And it is fun.
Let’s get to it then…
The main tool is the function
emacs-lisp-macroexpand. This expands any block with
use-package in my configuration into
emacs-lisp code with Emacs built-in functions, command & co. This is not 100% but with some doc-digging even I managed to do some cleanup.
The effective changes can be found in these commits:
- e477258c - Add custom package installation settings
- 94aeb4eb - Remove ensure keyword from use-package blocks
- 5d130ee2 - Remove use-package blocks
First things first thought. I use the keyword
:ensure quite a lot to make sure, that needed packages are installed. I needed to find a solution for that.
I decided that I wanted to install all the packages at once if these are not already installed. With some search-fu and a little lift-fu I now use the following snippets.
(defvar timu-package-list '(package-1 package-2 ... ...) "List of packages to be installed for the Emacs config to work as configured")
;;; setup package installation ;; credit: https://github.com/bbatsov/prelude (defun timu/packages-installed-p () "Check if all packages in `timu-package-list' are installed." (cl-every #'package-installed-p timu-package-list)) (defun timu/require-package (package) "Install PACKAGE unless already installed." (unless (memq package timu-package-list) (add-to-list 'timu-package-list package)) (unless (package-installed-p package) (package-install package))) (defun timu/require-packages (packages) "Ensure PACKAGES are installed. Missing packages are installed automatically." (mapc #'timu/require-package packages)) (defun timu/install-packages () "Install all packages listed in `timu-package-list'." (unless (timu/packages-installed-p) ;; check for new packages (package versions) (message "%s" "Reloading packages DB...") (package-refresh-contents) (message "%s" " done.") ;; install the missing packages (timu/require-packages timu-package-list))) ;; run package installation (timu/install-packages)
swapping use-package for require
Let us use my Dired config for illustration here. The following block …
(use-package dired :custom (dired-recursive-copies 'always) (dired-isearch-filenames 'dwim) (dired-listing-switches "-Ahlp") (dired-dwim-target t) :hook (dired-mode . hl-line-mode) (dired-mode . dired-hide-details-mode) (dired-mode . diredfl-mode) :init (require 'dired-x))
… got translated into this.
(require 'dired-x) (require 'dired) (setq dired-recursive-copies 'always) (setq dired-isearch-filenames 'dwim) (setq dired-listing-switches "-Ahlp") (setq dired-dwim-target t) (add-hook 'dired-mode-hook 'hl-line-mode) (add-hook 'dired-mode-hook 'dired-hide-details-mode) (add-hook 'dired-mode-hook 'diredfl-mode)
setq instead of the expressions with the
:custom keyword might be not entirely correct here. I just approached it like it was a
:config keyword. It does seem to work for me. Rewriting hooks was reasonably straight forward though. To handle the
:init keyword I just placed a require expression before requiring Dired.
Missing in the above example is the
:after keyword, which I use with
diredfl. This diff shows the changes quite well.
-(use-package diredfl - :after (dired async)) +(with-eval-after-load 'async + (with-eval-after-load 'dired + (require 'diredfl)))
Next was the rewriting of configs with the
:bind keyword. As illustrated in the following diff.
-(use-package flyspell-correct - :after flyspell - :bind - (:map flyspell-mode-map ("C-ƒ" . flyspell-correct-wrapper))) +(with-eval-after-load 'flyspell + (require 'flyspell-correct)) + +(define-key flyspell-mode-map (kbd "C-ƒ") 'flyspell-correct-wrapper)
The next example deals with the translation of blocks with the a
-(use-package csv-mode - :mode - ("\\.csv\\'" . csv-mode)) +(require 'csv-mode) +(add-to-list 'auto-mode-alist '("\\.csv\\'" . csv-mode))
With these examples, I think I have covered how I went about changing my config to remove all the
use-packages blocks. The challenge was really how repetitive it was going through the code. Plus of course making sure that the
parens remained balanced.
To answer my own question from the beginning, not hard at all. Well it was tedious at times, but not really hard. Most of the concepts for replacements were already present in my config. Like the use of the functions
add-to-list or the macro
with-eval-after-load. Plus the documentation – online and inside Emacs itself – is fantastic.
Little bonus, my
emacs-init-time seems to be faster. Take this with a pinch of salt though. My slow init time with
use-package is most likely due to my lack of chops.