In the last two parts, we customized some defaults of Emacs. We also installed two packages and customized their default behavior. In this part, we will organize our configuration and make it portable using use-package.
After the first two parts, our
init.el looks like this:
What problems do you see in this configuration? There is one major and one minor problem (and there must be many which I have missed).
- This setup is not portable.
- The structure of code is too flat.
This setup is not portable
We cannot just take our
init.el from one computer to other and then expect to have our Emacs setup portes. What do you think will happen when you copy your
init.el to a new computer?
What is our code assuming?
Is our code telling Emacs to install
We are assuming that the packages -
company will already be there on all computers.
When you open Emacs on a new computer, it will only have the default Emacs packages. So the load-theme call in our
init.el will fail. Enabling company-mode will also fail since there is nothing called
company-mode at this point. So all your configuration code related to
company will either fail or will be ineffective.
An example where it will not fail but will be ineffective is when you set
nil. This call will not fail. It will just create a variable. But it will be ineffective since there is no package which uses that variable.
So the conclusion is - There is no way to port this setup without manually installing all the packages.
The structure of the code is too flat
Currently, our configuration code is not grouped based on packages. You can always group it manually by keeping all configurations for a package together. But that can get hard to maintain. As you go on adding packages and customizing little things here and there, it is easy to spill configuration of a package all over
This might not even feel like a problem at this point in time. But as our setup evolves, this will be a big problem for managing the setup.
There are many different solutions for the above two problems. The natural way of solving the first problem will be to maintain a list of packages that we use and check if those are installed. If some package is not installed, install it. Something like this:
(defvar my-packages '(spacemacs-theme company))(dolist (p my-packages)
(when (not (package-installed-p p))
Note: In Elips any function ending with a
-p is a predicate.
This solution works perfectly well.
But there is a very elegant solution for this (which happens to solve the second problem as well).
It is called use-package.
use-package is an Elisp macro written by John Wiegley. It simplifies and groups together configuration for packages. Install
use-package by pressing:
(By now, you should know how to install a package.)
M-x package-install <RET> use-package <RET>
A common use-package declaration looks like this:
<code to be executed before loading the package> :config
<code to be executed after loading the package> :bind
<key bindings for this package>)
Note: Any word, preceded with a
: is called a
keyword in Elisp. You can consider keywords as being similar to strings, but with a different purpose and presence.
Let’s see how would a use-package declaration look for
company. Our current config for
(global-company-mode t)(define-key company-active-map (kbd "C-n") 'company-select-next)
(define-key company-active-map (kbd "C-p") 'company-select-previous)(setq company-idle-delay 0.0)
With use-package, it will look like this:
:bind (:map company-active-map
("C-n" . company-select-next)
("C-p" . company-select-previous))
(setq company-idle-delay 0.3)
Here we are saying-
- This is a use-package declaration for the package
Notice the minimal syntax here vs the
companyis loaded, set
0.3and enable company mode everywhere.
Now all the configuration related to company is naturally grouped together in the use-package declaration. Next time when we want to make some changes to
company, we will come to this declaration and add our code. This way, our setup will always be well grouped. So use-package has solved our second problem.
But what about the first problem? Will use-package download the missing packages?
Yes, it will. You just have to add
:ensure t to the declaration. For example, look at this:
:bind ("C-x g" . magit-status))
Note: Magit is an super awesome git client for Emacs that you must use.
:ensure t will make sure that
magit is downloaded if it is not there. Also, look at the
bind syntax here. When you want to make a global binding (unlike the
company, which was local to
company-mode-map), the syntax is even more minimal. You just have to specify
(key-binding . command) pairs. :)
use-package has solved both of our problems! :)
Note: use-package has many more useful (and very well thought of) keywords. I recommend going through the README.md.
Note: Even though use-package is a handy macro, we must know what is going on under the hood when we use it. Since it is just a macro, you can expand it yourself and see what is going on. Read about macro expansion here.
So is our setup fully portable now?
Nope! There is still one small problem. use-package is not there in Emacs by default and you cannot use use-package to install itself. :P
So to make our setup fully portable, we need to check if use-package is installed and if it is not, we need to install it.
(when (not (package-installed-p 'use-package))
init.el will look something like this:
Here are a few packages (along with their configurations) that I think every Emacs setup should have:
In this three part series on configuring Emacs from scratch, we have seen,
- The Emacs way of text editing.
- Using the Help system to know what is going on.
- Tweaking the defaults of Emacs.
- Installing packages.
- Organizing your setup.
- Making your setup portable.
While doing this, we have seen how customizable Emacs is! :)
I have not covered even a percent of the capabalities that Emacs has. Org-mode in itself is a topic of another (huge) series of posts. And there are many more packages which make Emacs special and unique.
But I think we have covered enough to be able to proceed on our own now.
The key is to know how to use Emacs’s help system.
(Sorry for repeating this over and over again, but you will realize the power and convenience of Emacs’s help as you use it.)
You can find my Emacs setup here. Feel free to borrow things. If you find better ways, please send pull requests. :)
(It is strongly recommended to have your Emacs configuration under version control.)
Tips on “Evolving your Emacs setup”
The strategy I use to improve my Emacs setup is called UFFL.
(Honestly, only I call it UFFL.)
Use - Find painpoints - Fix - Loop
As you would have guessed, I have totally made this up just to make it analogous to REPL. But it does describe the strategy well. :)
After making an edit to my configuration, I use it for a few days for my day to day tasks. I often find some painpoints (i.e. things that can be improved / automated / achieved in lesser number of key strokes) while using it. Then I find solutions to those problems.
Usually, I try to solve the problems myself by finding the right configuration parameters or by writing some Elisp. Then I check if there are solutions on Emacs Stack Exchange (or anywhere on the Internet). In most of the cases, solutions from the Internet are better and more elegant than my own solutions.
But I still recommend finding/writing your own solution before looking up online, so that you get better at Elisp and at the Emacs way of thinking. :)
And this goes on forever …
The joy is in the journey!