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.

Spacemacs is a great editor with a great UI.

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.

Diving into Emacs

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 will make your emacs fast

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

Use unicode in your mode-line

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 is a great package to make a short spacemacs-like config

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.

Reading emacs documentation

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:

whichkeybuffer.png

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:

whichkeybuffer-cxret.png

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.


  1. 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]
  2. A 2011 MacBook Pro with 16G of RAM and a Samsung SSD. [return]
  3. Like one-second fast. [return]
  4. Yes, you can and should use unicode. [return]
  5. See its github profile here. [return]
  6. 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]