From Emacs to Kakoune

After using Emacs heavily for ten years and developing proficiency with the modal editing paradigm as popularized by Vim and implemented by Evil-Mode, I decided to give kakoune a try, out of curiosity.

Comparing the list of features supported by kakoune to what Emacs, Vim and VS Code have to offer only leaves the conclusion that kakoune must be strictly inferior, since it has very few features compared to those other editors.

I had no particular frustration with Emacs — it is an excellent editor!

However one thing I noticed in the last few years is that more and more functionality is being implemented outside of editors in external tools instead.

The Language Server Protocol is a prime example of this as are various code formatters like gofmt, prettier and rubocop.

Since both Emacs and Vim are powered by complex programming languages extending them is non-trivial, because it requires in-depth knowledge of the extension language and the domain model exposed by the editor.

When extending Emacs with Elisp, one needs to be aware of at least the following concepts:

  • buffers
  • windows
  • faces
  • major and minor modes
  • Keymaps
  • elisp
  • the package and module system
  • customizable options

And while all of these concepts mostly make sense and have their place, as a power user wanting to extend the editor it is a lot to master.

I could observe this in my usage of the editor: in the beginning I was eager to tinker around with elisp, adding new functionality to the editor.

The longer I used Emacs though, the more I relied on external packages. Usage shifted from creating new functionality to configuring functionality provided by others.

Discovering Kakoune

While trying out Kakoune, I noticed two things:

  • the number of concepts one needs to know is much lower compared to Emacs,
  • kakoune is built around making the integration with external tools easy.

This basically meant that it supported my current usage pattern much better than Emacs: running external commands and doing something with their output is a core part of kakoune.

Most of the interesting functions for working with modern programming languages come in the form of external tools.

Integrating these tools into Kakoune required a much smaller time investment for me than doing the same in Emacs.

Another thing I enjoy a lot about kakoune is that I have the feeling that the core concepts exposed to the user are very simple and that there is a real chance of holding the entirety of them in my head.

In other words it is much more likely to achieve "complete mastery" over a tool like Kakoune than it is to do the same with Emacs.

For now I'm sticking to Kakoune even for professional use, because all challenges I encountered so far (e.g. autoformatting code on save) were easily solvable in a short amount of time.

For reference, this is how my complete configuration file looks like at the moment:

source "%val{config}/plugins/plug.kak/rc/plug.kak"
plug "andreyorst/fzf.kak"

colorscheme base16

provide-module projectile %[
  declare-user-mode user-project

  map global user-project t ':projectile-alternative-file<ret>' -docstring 'alternate file'

  def -override projectile-alternative-file 'echo "No alternative file defined"'
  hook global WinSetOption filetype=ruby %{
    require-module projectile-ruby
    set-option current identwidth 2
  }
  hook global WinSetOption filetype=javascript %{
    set-option current formatcmd %{prettier --stdin --parser=babel}
  }
  hook global WinSetOption filetype=json %{
    set-option current formatcmd %{jq .}
  }
]

provide-module projectile-ruby %[
  def -override projectile-alternative-file 'ruby-alternative-file'
]

map global user d '<esc>:fzf-mode<ret>' -docstring "fzf"
map global user f '<esc>:fzf-mode<ret>v' -docstring "fzf vcs"
map global user b '<esc>:fzf-mode<ret>b' -docstring "fzf buffer"
map global user p ':enter-user-mode user-project<ret>' -docstring "project"

require-module projectile

declare-user-mode sys
define-command sys-mode 'enter-user-mode sys' 
define-command -override sys-eval 'eval %val{selection}'

map global normal <semicolon> <,> -docstring 'user mappings'
map global sys <c-e> ':sys-eval<ret>' -docstring 'eval selection'
map global sys <c-r> ':edit "%val{config}/kakrc"<ret>' -docstring 'edit kakrc' 
map global normal <c-x> ':sys-mode<ret>'
map global insert <c-x> '<esc>:sys-mode<ret>'

set-option global indentwidth 2
hook global BufWritePre .* %{
  evaluate-commands %sh{
    if [ -n "$kak_opt_formatcmd" ]; then
      printf "format-buffer\n"
    else
      printf "\n"
    fi
  }
}

In only 54 lines I get:

  • a shortcut for evaluating selected editor commands in the running editor,
  • a shortcut for editing the main configuration file,
  • jumping between test and implementation files in Ruby,
  • automatic formatting for JavaScript and JSON,
  • lots of shortcuts for various types of fuzzy search.

This is basically 90% of the Emacs features I use the most.

I intend to grow this on an as needed basis, to see where this goes.

Right now I'm quite happy with the simplicity of the setup and that it provides enough of the things I care about.