Spacemacs is a great starter kit. It is the only kit that got my attention. I have been using it for one year or so. I know most of its most useful commands. I like the plug-and-play feeling of its layer system. I like the laziness of its packaging system. I like how the devs build a great terminal experience inside emacs. I like how it abstracts Evil1 from its users.
But I recently grew tired of its clumsiness. It took like seven or eight second to load. I tried to remove some configuration layer. I removed chunks of config I did not need. At its best, Spacemacs took — on my machine2 — seven long seconds to load. I gave me the feeling that I did not need most of the full blown spacemacs distribution. Plus I wanted to understand how to setup my own emacs. I wanted to tweak it. I wanted to get my hands dirty.
During my year in the spacemacs world, I discovered four awesome packages :
use-package
, general.el
, which-key
and ivy
. They are the backbone of my
new setup.
I only need those four packages to build a good spacemacs experience.
use-package
made my emacs fast
use-package
is a package by the current maintainer of emacs. I use it to load
packages lazily. They will not be loaded unless I call them, or one of
my packages call them. It make the startup super fast3.
The following code loads the package which-key, and make sure it is loadable. If
not, it downloads it, thanks to key :ensure
keyword.
(use-package which-key :ensure t
I use the :init
keyword to execute bits of code before the package is loaded.
The following will enable which-key
in each buffer.
:init
(which-key-mode)
I use the :config
keyword to customize the module to my convenience. Those
bits of code are executed after the package is loaded.
:config
(which-key-setup-side-window-right-bottom)
(setq which-key-sort-order 'which-key-key-order-alpha
which-key-side-window-max-width 0.33
which-key-idle-delay 0.05)
)
To avoid a cluttered mode-line, I use the diminish
keyword of use-package
.
:diminish which-key-mode
I could also use the following syntax, to replace the WK
in the mode-line with
Ꙍ
4.
:diminish (which-key-mode . "Ꙍ")
Here I showed you how I use use-package
to load a package that is enabled
globally at startup. But use-package
has many option to make sure the package
is not loaded if nobody needs it. I use the :commands
keyword to load the
package when a command in the list of command is called.
For example, if I want to use the ranger
package, I do not need it until I
call the (ranger)
function. So I put (ranger)
in the list of commands that
will trigger the loading of ranger
.
(use-package ranger :ensure t
:commands (ranger)
:bind (("C-x d" . deer))
:config
(setq ranger-cleanup-eagerly t)
)
use-package
also provide the :bind
keyword. I use it to describe the list of
commands that will trigger the loading of my package, and the keybindings that
I want to associate with the command. In the previous example, I mapped
(deer)
to C-x d
. So, unless I call (ranger)
before typing C-x d
, the
ranger
package will not load.
This is the magic of use-package.
general.el
made my evil shine
General.el is the new evil-leader black. It makes it easy to implement leader
keys, of any length you want. It also has nice integration with use-package
and which-key
. Its primary use is in combination with evil
, but you can also
use it with bare emacs.
(use-package general :ensure t
:config
(general-evil-setup t)
(general-define-key
:states '(normal insert emacs)
:prefix "C-SPC"
:non-normal-prefix "C-SPC"
"l" '(avy-goto-line)
"a" 'align-regexp
)
(general-define-key
:states '(normal motion insert emacs)
:prefix "SPC"
"ar" '(ranger :which-key "call ranger")
"g" '(:ignore t :which-key "Git")
"gs" '(magit-status :which-key "git status")
)
)
The previous chunks load the general
package at startup. This one is not
lazily loaded. It uses (general-define-key)
to define keys that are under the
prefix C-SPC
. When the evil-state is not normal, ie when I am in insert or
visual mode, the prefix is also C-SPC
, but I can set it to something
different. So when I press C-SPC
then l
, it calls avy-goto-line
.
The next chunks is all it takes to setup a Spacemacs-like interface to my
favorite commands using the space bar as a prefix. Like in spacemacs, SPC ar
calls ranger. Notice the :which-key
keyword. Use it to describe your
keybindings. The string I use will be displayed by the which-key
package. Use
the :ignore
keyword when the key-press is only a prefix, and you want to
describe the prefix via which-key
.
which-key
will make your emacs friendly
The obvious benefit of spacemacs to me is its discoverability. Press a key, read the description of the prefix, press another key, etc…
which-key
is a package that prints out a buffer of all the keybindings
currently assigned to the prefix you type. In the previous example, if I type
C-x
, then a buffer prints out:
It is a listing of all the keyboard shortcut starting with C-x
. But what if I
want to know what is behind the RET
keypress ? which-key
provide a way to
describe a prefix.
(which-key-add-key-based-replacements
"C-x RET" "coding system - input"
)
So now when I press C-x
, I see:
Those three packages all belongs in the spacemacs universe, general.el
being
only an upgrade over evil-leader
. The spacemacs dev chose to use helm
as a
default “incremental completion and selection narrowing frameworks”. But
recently, a proficient and prolific emacs package developper5 build an ecosystem
of tools based on its variation of ido
. Those packages are ivy
, counsel
and swiper
. They represent a great alternative to the somewhat clumsy helm
ecosystem.
ivy
extend my mind inside emacs
Just like helm
or ido
, ivy
is a “generic completion framework”. It shines
at being unobtrusive and really fast. I was really surprised by the fact that I
could reimplement most of my most used spacemacs commands on top of ivy
or its
associated package counsel
.
ivy-mode ensures that any Emacs command using completing-read-function uses ivy for completion. Counsel takes this further, providing versions of common Emacs commands that are customised to make the best use of ivy.
Here is my (use-package)
declaration for ivy
and counsel
:
(use-package ivy :ensure t
:diminish (ivy-mode . "") ; does not display ivy in the modeline
:init (ivy-mode 1) ; enable ivy globally at startup
:bind (:map ivy-mode-map ; bind in the ivy buffer
("C-'" . ivy-avy)) ; C-' to ivy-avy
:config
(setq ivy-use-virtual-buffers t) ; extend searching to bookmarks and …
(setq ivy-height 20) ; set height of the ivy window
(setq ivy-count-format "(%d/%d) ") ; count format, from the ivy help page
)
(use-package counsel :ensure t
:bind* ; load counsel when pressed
(("M-x" . counsel-M-x) ; M-x use counsel
("C-x C-f" . counsel-find-file) ; C-x C-f use counsel-find-file
("C-x C-r" . counsel-recentf) ; search recently edited files
("C-c f" . counsel-git) ; search for files in git repo
("C-c s" . counsel-git-grep) ; search for regexp in git repo
("C-c /" . counsel-ag) ; search for regexp in git repo using ag
("C-c l" . counsel-locate)) ; search for files or else using locate
)
swiper
is an isearch
replacement based on ivy. It is really really fast. I
have tried it on huge files6. It still is really really fast. I have the
feeling it is much faster than helm-swoop
, though I did not measured it. I
bind it to C-s
, the default keyboard press to isearch.
Conclusion
Here I showed you how I used those four packages and their ecosystem to build myself a great spacemacs-like experience. In the next post, I want to spend some time on helping you build your own experience of emacs. I will use those packages to describe you how you could build yourself a great emacs environment, using modern tools.
- Evil is The vim emulation inside emacs. It makes emacs behave exactly as vim. Try it if you want to keep your carpal tunnel. [return]
- A 2011 MacBook Pro with 16G of RAM and a Samsung SSD. [return]
- Like one-second fast. [return]
- Yes, you can and should use unicode. [return]
- See its github profile here. [return]
- I have tried it, just like the maintainer, by copy-pasting org.el multiple time in itself. The file was like 1G bytes huge. Swiper did not tremble. [return]