Most of the guides on use-package
describe how to start from scratch. If you started your config by copy-pasting various elisp snippets (like I did) you may want to have something like a recipe for how such snippets should be translated into corresponding use-package instructions.
Here are some guidelines from my own experience to “bootstrap” your usage of use-package.
(For the reference – you can find my config here)
- Right now your config is more or less “linear” or imperative so to speak, while use-package will make it more “declarative”.
The simplest would be to wrap all the code related to a specific package into:config
section of use-package declaration.
If there is anything in your config that absolutely must be done before(require '<pkg>)
(this is rare) – you can put that into:init
section. - Use
:demand t
everywhere at first – lazy loading and dependency handling in use-package can be a little bit of a headache (at first).
I advise you to be explicit with:ensure t
in every definition as well (there is an option to enable it globally, but then you may regret later when using something like :quelpa etc.). - So now, this is a wrapper for a configuration for every package:
(use-package <pkg> :demand t :ensure t :config <move all your configs related to <pkg> here> )
- There is one trick (not the only) to configure built-in packages with use-package as well – just omit
:ensure t
and use the name of the built-in package (or feature to be more precise, see below), e.g. this is how you can group yourdesktop
-related code:
(use-package desktop :demand t :custom ((desktop-files-not-to-save "\\(\\=/[^/:]*:\\|(ftp)\\'\\)") (desktop-load-locked-desktop t) (desktop-buffers-not-to-save ".*")))
This is the way to configure
org
andorg-agenda
. There is some confusion in use-package between package-name and feature-name – actually, name that you put right afteruse-package
is the name of thefeature
(most of the time the main feature of the package has the same name), to know exactly the name of the feature you can lookup(provide '<pkg>)
statements in the code of the package. This is sometimes required to know the name of the built-int features (packages) – e.g.org
andorg-agenda
are separate features, you can group configs into different use-package declarations. - Use
pp-macroexpand-last-sexp
– this will help you a lot at times when you’re confused (especially with lazy loading later) – you should understand thatuse-package
just generates plain elisp code, no magic whatsoever. - At this point you are “bootstrapped” so you can do next few steps in any order – this will transition you to doing things more in “use-package”-way.
- Use
:custom
,:custom-face
. This one is easy, most of thesetq
declarations you should probably move into:custom
(just removesetq
part) – see the source code of the variable if it’s actually adefcustom
. - Use
:hook
. Whenever you have(add-hook some-hook (lambda...)
move that into:hook
removing-hook
part of thesome-hook
name – e.g.:hook (some . (lambda))
. - Use :bind. Transform your
global-set-key
into:bind
and yourdefine-key
into:bind (:map ...)
. You will have to keep yourevil-define-key
definitions in:config
though (or use some ‘general’ extension). - Next step – minor transitions to using
:mode
,:magic
, maybebind-keymap
. Use:if
,:unless
if you have something like(when (eq system-type 'windows-nt)...)
. - As a last big move – handle order of loading and execution and migrate your
with-eval-after-load
statements into using:after
(removing:demand t
where appropriate). This is a big step as it will remove dependency on the global order ofuse-package
declarations (and make your config really declarative).
The
:after
might be confusing (look pp-macroexpand-last-sexp if you are confused) but equivalent forwith-eval-after-load <pkg2>
would be combination of(use-package <pkg1> :after <pkg2> :demand t :config ...)
, if you omit:demand
the execution won’t be triggered eagerly after loading<pkg2>
as use-package will wrap configuration into anothereval-after-load <pkg1>
. Anyway, once you get here you will learn how to debug and figure it out.You may want to use
:defer
in some places but I warn you to use:defer
as a dependency mechanism – use it only to reduce init time and defer loading a fraction of seconds later (this will still block UI anyway but feels smoother) for some packages e.g.evil
.Also at this point it will make more sense to move all functions and variables definitions (defun and defvar) into
:init
block.You won’t be able to completely get rid of using
with-eval-after-load
as sometimes it is easier to group some configuration under it, - Return to use-package documentation (C-h f ‘use-package’) from time to time until understanding settles in your brain.
Good luck!