Emacs is a great environment to build a text editor. Being a really old piece of software, searching the internet does not always lead to the best or more recent results. My goal is to help you build an editor inside Emacs using the best tools for the job, with modern tools and well-crafted packages.
In a previous post, I described the tools I needed to build myself a great spacemacs-like1 experience inside emacs. In this post, I want to explain the process of building it in more details. I hope it serves you in building a cool editor the way you like it.
Emacs proficient reader should skip this part and jump directly to the
use-package
part here. Those that already know aboutuse-package
should jump here, where I talk aboutivy
,swiper
,counsel
andgeneral
. Those that do not want to read all my strange french-english prose can and should jump directly to here. I have put up a recipe to a modern Emacs experience.
Emacs is a text editor and a programming environment built on top of a programming language, just like Python or Java, called emacs-lisp. The text editing abilities of a bare Emacs distribution are decent, but today most Emacs users extend it through other pieces of software called modules or packages. They are also written in emacs-lisp, often by user of Emacs that became frustrated by a lack of feature or thought they could do better. The result is a big ecosystem of emacs-lisp packages, of which you can get an idea at melpa.org. Anybody can download it and use it to extend the functionnalities of emacs.
One of the main strength of Emacs is its extensibility and flexibility. As our first step, we are going to learn how to configure it using basic emacs function.
Getting Emacs ready for the job
Once you have installed the most recent version of Emacs using your beloved
package manage and opened it, Emacs will create a directory in your $HOME
directory called ~/.emacs.d
. When Emacs start, it reads a file in this
directory called ~/.emacs.d/init.el
, and execute any emacs-lisp command it
finds in it. We are going to use this file to customize emacs to our liking.
As a version control adept, I have symlinked
init.el
to a dotfile repository under VC.
When inside Emacs, press C-x C-f
(meaning control-f
then control-f
), the
emacs command to open a file. Emacs will prompt you for a file to open, and
type ~/.emacs.d/init.el
. Emacs being really old, some of its default
configuration are somewhat — er — mysterious and mystical. We are going to use
init.el
to introduce some sane defaults. Paste the following chunks using
C-y
.
(setq delete-old-versions -1 ) ; delete excess backup versions silently
(setq version-control t ) ; use version control
(setq vc-make-backup-files t ) ; make backups file even when in version controlled dir
(setq backup-directory-alist `(("." . "~/.emacs.d/backups")) ) ; which directory to put backups file
(setq vc-follow-symlinks t ) ; don't ask for confirmation when opening symlinked file
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)) ) ;transform backups file name
(setq inhibit-startup-screen t ) ; inhibit useless and old-school startup screen
(setq ring-bell-function 'ignore ) ; silent bell when you make a mistake
(setq coding-system-for-read 'utf-8 ) ; use utf-8 by default
(setq coding-system-for-write 'utf-8 )
(setq sentence-end-double-space nil) ; sentence SHOULD end with only a point.
(setq default-fill-column 80) ; toggle wrapping text at the 80th character
(setq initial-scratch-message "Welcome in Emacs") ; print a default message in the empty scratch buffer opened at startup
Place your cursor after the closing paren of the first line and press C-x C-e
.
It will execute the previous bits of code. setq
is an emacs-lisp word meaning
“set the following variable to the following value”. For the first line, we
could translate in plain english to “set the variable delete-old-versions to
-1”.
Now save the init.el
modifications by pressing C-x C-s
, and quit Emacs by
pressing C-x C-c
. Restart it. Now press M-x
(depending on your OS, it should
corresponds to Alt-x
) and describe-variable
, then delete-old-versions
. It
should open a buffer with the documentation of the variable, with a line saying
“Its value is -1”. So basically when Emacs started up, it evaluated the lines of
code in ~/.emacs.d/init.el
. Now press M-x
describe-key
and press
C-e
. It should print a buffer describing the function attached to the
keybindings C-e
In those simple exercises, we covered very important Emacs notion :
- Every command is bound to a function. Those function are, most of the time, defined in emacs-lisp. Some very important function are defined in C for performance.
- You learned that Emacs has a very thorough documentation describing variables, functions, keybindings…
- You learned to modify a variable using emacs-lisp.
- You learned how Emacs execute code at startup, and we can leverage that to customize it to our liking.
Introduce structure in your config : use-package
to the rescue
The emacs-lisp package archive with wich you can interact at melpa.org has, at the time of writing 3 291 packages. The abundance of modules sometimes lead to something that emacs users call Emacs bankruptcy: a state in which the user does not understand its own Emacs configuration.
I give up. During the past 6 years of my emacs career, my emacs configuration file grew to embarrassing levels. As of this morning, it is well over 1000 lines and is a looming burden of disorganization. Startup time is poor, customizations exist for languages that I don’t use anymore (ahem, > csharp-mode), and it has been this way for too long… —— RyanMcGeary, 2007
To prevent this, John Wiegley, the current emacs maintainer put up a great
package called use-package
. So, as our second step, we are going to set up
use-package
and use it for the first time. This “meta”-package is a package to
manage other packages and the way they interact. Put the following lines in your
~/.emacs.d/init.el
.
(require 'package)
This line basically says to Emacs make available any command present in the package module.
(setq package-enable-at-startup nil) ; tells emacs not to load any packages before starting up
;; the following lines tell emacs where on the internet to look up
;; for new packages.
(setq package-archives '(("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")
("marmalade" . "http://marmalade-repo.org/packages/")))
(package-initialize) ; guess what this one does ?
;; Bootstrap `use-package'
(unless (package-installed-p 'use-package) ; unless it is already installed
(package-refresh-contents) ; updage packages archive
(package-install 'use-package)) ; and install the most recent version of use-package
(require 'use-package) ; guess what this one does too ?
Now the best way to learn how it works is by reading its documentation. Nah.
Let’s use it first, read then. Restart emacs and put the following line in your
init.el
, then go to the end of line (C-e
) and press C-x C-e
.
(use-package general :ensure t)
It should check for the general
package and make sure it is accessible. If
not, the :ensure t
part of the previous chunks tells use-package to download
it and place it somewhere accessible, in ~/.emacs.d/elpa/
by default2.
Now every function in the general package is available to you. We will now use
the same use-package
statement to customize the general package. We need the
:config
keyword of the use-package
macro to do just that.
(use-package general :ensure t
:config
(general-define-key "C-'" 'avy-goto-word-1)
)
The third line said “bind to the C-'
press the function avy-goto-word-1
”. It
means we need the avy
package. So let’s use use-package
again to download
and load the avy
package.
(use-package avy :ensure t)
But then we do not need this package right when we start Emacs. It can wait
until we first call avy-goto-word-1
or any other command from the avy
package. So we are going to put this command in the list of command that
triggers the loading of the avy
package. We use the :commands
keyword to do
that.
(use-package avy :ensure t
:commands (avy-goto-word-1))
So now when we start Emacs, the avy
package is not loaded. But when we first
press C-'
, it will call avy-goto-word-1
and trigger the loading of the avy
package.
Use-package has all kinds of tricks to make sure no code is executed or loaded
before it is needed. You really need to check out use-package
on your way
to mold Emacs to your liking.
Move in Emacs : the abo-abo way
In Emacs as in all text editor, you move more than you type. You move between buffers, ie text files loaded into memory. You move between lines. You move between sentences. You move between semantics unit. You move between files. You search for files. You search for text. You search for regexp. You search for projects.
It really helps to have a uniform interface to the most common commands you call.
One of the most prolific Emacs-package developper that I know of is called Oleh
Krehel; abo-abo on Twitter3. During the past
two years, he developped an ecosystem around a completion framework that he
wrote called ivy
. This ecosystem is composed of three packages called ivy
,
counsel
and swiper
.
Counsel allow you to find recently visited files, to switch between buffers, to search for a string in the current git directory, to search for a file in the current git directory, to search for Emacs function, to search for system applications to open, to search for music in rythmbox, to … . Well, you get the idea. Swiper allow you to find text really really quickly inside an Emacs buffer.
Ivy proposes completion candidates to the two other, and to many other Emacs function that needs completion and narrowing. You may have heard of another ecosystem centered around Helm. The spacemacs dev chose to use Helm as a central tool, but I can assure you that you will not regret chosing the Ivy way. It is really really fast, really well thought out and really convenient to use.
So now we have a ton on function to bind, and still no way to make that easy. But do we ?
Bind in Emacs : the general
way
Recently, a growing user base of Emacs user started to use Evil, a vim emulation
built on top of Emacs command. It works just flawlessly. When they did, they
developped some ways to bind keys to functions like in Vim. One of the most
successful solution to this is evil-leader
. And then another Evil user by the
pseudo of noctuid
created a package called general.el
4. Of all the way I
know to define keys in Emacs, this one is the most versatile and the simplest
solution to defining keybindings.
There is many ways to define keybindings in general.el
. Since they are all
constructed around general-define-key
, it is the most flexible. So let’s use
that to bind some keys.
(general-define-key
;; replace default keybindings
"C-s" 'swiper ; search for string in current buffer
"M-x" 'counsel-M-x ; replace default M-x with ivy backend
)
This one is pretty simple. It binds C-s
to swiper and M-x
to counsel-M-x.
Those two keys are probably amongs the two most used keybindings in Emacs. But
General allows more complex solution to keybindings definition. Let’s say we
want all of our personnal keybindings bound to C-c XY
, where XY
is a
combination of our choice. We can define a :prefix
.
(general-define-key
:prefix "C-c"
;; bind to simple key press
"b" 'ivy-switch-buffer ; change buffer, chose using ivy
"/" 'counsel-git-grep ; find string in git project
;; bind to double key press
"ff" 'counsel-find-file ; find file using ivy
"fr" 'counsel-recentf ; find recently edited files
"pf" 'counsel-git ; find file in git project
)
Now C-c b
switches buffer using Ivy. And C-c f
then f
finds files. And
C-c f
then r
find recent files. Now we can see a pattern here: we just
placed all command related to finding files under a C-c f
prefix. It would be
great if we could know when we press C-c
what the f
corresponds to.
But hey it is Emacs. There is a package for it. It is called which-key
. So
guess what ?
(use-package which-key :ensure t)
So now when we press C-c
, a nice buffer shows up that pressing b
will
execute ivy-switch-buffer
. But what does it show for f
? “+prefix
”. We can
do better. We can indicate to Which-key that C-c f
related functions
corresponds to file related operations.
(use-package which-key :ensure t
:config
(which-key-add-key-based-replacement
"C-c f" "file"
"C-c ff" "find file"
"C-c fr" "recently edited"
"C-c p" "project"))
Execute this code with C-x C-e
as usual. Now press C-c
, and see how much
which-key
is awesome. Never forget a keybinding. Very easy on beginners. Very
swift. Very clean. Does it ?
There is some kind of code duplication here. We define it using General, and describe it using Which-key. In fact we can do both in the same statement, using Which-key integration to General.
(general-define-key
:prefix "C-c"
;; bind to simple key press
"b" 'ivy-switch-buffer ; change buffer, chose using ivy
"/" 'counsel-git-grep ; find string in git project
;; bind to double key press
"f" '(:ignore t :which-key "files")
"ff" 'counsel-find-file
"fr" 'counsel-recentf
"p" '(:ignore t :which-key "project")
"pf" '(counsel-git :which-key "find file in git dir")
)
Evaluate this code. Press C-c
. Same output as before. See how General is great
?
Meeting our goal: build yourself a great Spacemacs-like experience
For Evil users out there, General also has nice integration with Evil states. It means that we can easily define keybindings to match Spacemacs design styles. And yet we retain the ability to build the editor we want. That is what we came to Emacs in the beginning.
Check out what this chunk does.
(use-package general :ensure t
:config
(general-define-key
:states '(normal visual insert emacs)
:prefix "SPC"
:non-normal-prefix "C-SPC"
;; simple command
"'" '(iterm-focus :which-key "iterm")
"?" '(iterm-goto-filedir-or-home :which-key "iterm - goto dir")
"/" 'counsel-ag
"TAB" '(switch-to-other-buffer :which-key "prev buffer")
"SPC" '(avy-goto-word-or-subword-1 :which-key "go to char")
;; Applications
"a" '(:ignore t :which-key "Applications")
"ar" 'ranger
"ad" 'dired))
When Evil is in normal or visual state, the prefix is SPC
, as in Spacemacs.
When Evil is in insert or emacs state, the prefix is C-SPC
. We can define some
key under direct access, describe them using Which-key. We can then define
prefix easily, using the :ignore t
keyword. See how close we are from
Spacemacs ?
So then why not just use Spacemacs and be done with it ? Well, I like the SPC
prefix thing too much to let the Spacemacs dev decide for me what keybindings
must refer too. I want to make my own Emacs. I want to be able to define
:prefix
easily. I want to understand what Emacs does, without having to read
the entire Spacemacs code base. I want an editor that I can customize to its
inner deepness, without being hit by design choices I did not make.
And then Spacemacs has one major drawback to me: it is very Qwerty-keyboard oriented. It means that when you use another keyboard layout, you make some weird movement just to accomodate Spacemacs keybindings.
A recipe for a modern Emacs experience
I have put up a recipe to build a Spacemacs-like Emacs. It is based on what I have done systematically, to build a reasonable and very usable emacs to me.
- Spot a package that seems interesting.
- Install it using
use-package
and its:ensure t
keyword. - Use it for an hour or two.
- Determine the functions you need.
- Create autoloading for it using
use-package
abilities to deferred loading. - Create a keybinding for it using
general
, under the:prefix
you like. UseSPC
to look like Spacemacs. - Describe it using
which-key
or using the:which-key
keyword ofgeneral
definition. - There is no other step. Use your package.
I mean, really. You only need those seven steps. You need use-package
,
general
, which-key
. And finally you also need abo-abo packages. They are
just too awesome.