How I build my own spacemacs
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 Evil 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 machine — 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 fast.
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.
I use the :config keyword to customize the module to my convenience. Those bits of code are executed after the package is loaded.
(setq which-key-sort-order 'which-key-key-order-alpha
To avoid a cluttered mode-line, I use the :diminish keyword of use-package.
I could also use the following syntax, to replace the WK in the mode-line with Ꙍ.
: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
: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
:states '(normal insert emacs)
:states '(normal motion insert emacs)
"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.
"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 developper build an ecosystem of tools based on his variation of ido. Those packages are ivy, counsel and swiper. They represent a great alternative to the somewhat clumsy helm ecosystem.
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. Here is what the ivy-dev has to tell about it :
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
(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 files. 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.
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.
Originally published at sam217pa.github.io on August 30, 2016.