Configuring Emacs from Scratch — use-package

Suvrat Apte
Dec 5, 2019 · 6 min read

This is the third part of a series on “Configuring Emacs from Scratch”.
You can read the first part
here and the second part here.

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).

  1. This setup is not portable.
  2. The structure of code is too flat.

This setup is not portable

What is our code assuming?
Is our code telling Emacs to install spacemacs-theme and company? No!
We are assuming that the packages - spacemacs-theme and 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 spacemacs-theme and company will either fail or will be ineffective.
An example where it will not fail but will be ineffective is when you set spacemacs-theme-comment-bg to 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

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))
(package-install 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

M-x package-install <RET> use-package <RET>

A common use-package declaration looks like this:

(use-package <package-name>
:init
<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 company is:

(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:

(use-package company
:bind (:map company-active-map
("C-n" . company-select-next)
("C-p" . company-select-previous))
:config
(setq company-idle-delay 0.3)
(global-company-mode t))

Here we are saying-

  1. This is a use-package declaration for the package company.
  2. In company-active-map, bind C-n to company-select-next and C-p to company-select-previous.
    Notice the minimal syntax here vs the define-key call.
  3. After company is loaded, set company-idle-delay to 0.3 and 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:

(use-package magit
:ensure t
: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 bind in 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))
(package-refresh-contents)
(package-install 'use-package))

Now our init.el will look something like this:

Here are a few packages (along with their configurations) that I think every Emacs setup should have:

Conclusion

  • 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.)

I must mention and thank Narendra Joshi and Vedang Manerikar as I’ve always stolen (and look forward to stealing) things from their Emacs configurations.

Tips on “Evolving your Emacs setup”

Use - Find painpoints - Fix - Loop

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!

helpshift-engineering

Engineering blog for Helpshift

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store