cosine.blue

Blog by Gregory Chamberlain.

The Vim-Inspired Editor with a Linguistic Twist

A Vim convert’s commentary on Kakoune, the selection-oriented editor focused on interactivity and incremental results—or mawww’s experiment for a better code editor.

History

What is Kakoune?

Kakoune (/kə'kuːn/) describes itself as a code editor heavily inspired by Vim, and like the venerable vi(1) and its successor, its internal model interprets the user’s keystrokes like utterances of a sort-of text-editing language, but with a certain linguistic twist (more on that later). Available—for reasons that will later become clear—only on Unix-like systems, it was written in C++ by Maxime Coste, has a clean code base and runs like the wind in a terminal. It is released under the Unlicense, which is compatible with the GPL.

Skip ahead to Using Kakoune if you just want to get stuck in right way.

As a long time Vim user, Kakoune quickly piqued my interest and before long I was convinced that it was the editor for me, and not just because it’s incredibly obscure. Its grammatical reshuffle and wise design principles put it above the rest for me, and I’m starting to think it’s more than just a comfy nook in which to hide from the large and looming cold hand of Emacs, the curious GNUisance (sorry) that swings periodically over my shoulder whispering we have Org mode! And Lisp! Maybe next year, Emacs. I really want to like you.

But that’s an essay for another day.

Here I endeavour to introduce the reader to Kakoune and present my thoughts on its merits and misfits in the context of a terminal-based, keyboard-driven workflow. I make frequent comparisons to Vim, and the reader would benefit from some experience with a modal editor such thereas. That said, those uninitiated in modal editors might find that Kakoune’s forgiving mechanics place it among the easier to learn.

Do we really need another Vim clone?

This is neither a fork nor clone.

In an effort to provide incremental results and interactivity (words whose meaning became clear once I met the s key), the selection-oriented code editor takes a bold step away from some of the Vim interactions familiar to many of us, while remaining competitive keystroke-for-keystroke, millisecond-for-precious-millisecond.

Its author cites the Unix philosophy as one of Kakoune’s virtues, and certainly I would agree that it trumps Vim in adherence thereto (examples of this to come); and that is not to say it lacks enough features. Indeed it has built-in wrappers around Git, grep, make, and man, works with ctags, conforming linters and other things.

Screencast: browsing man pages in Kakoune

Vim is difficult to extend, not least because of the idiosyncratic train wreck of a language that is VimL (or Vim Script, or whatever). Conversely, Kakoune stands by its decision not to complicate its language with flow control and whatnot, and instead relies on languages we already know through its %sh{…} blocks. And although Kakoune is very usable out-of-the-box, one look at the plugins page is enough to make any purist cave.

You can even whip some Perl or Ruby or anything you like into an %sh{…} block; in fact—that’s how some of the more complex plugins are built.

So what’s the twist?

Whereas vi’s keystroke language follows verb-object order, Kakoune inverts that by following object-verb order. In real terms, that means you make a selection (object) before deciding what to do (verb) with it. The object might be a character, word, sentence, paragraph, parenthetical, regular expression, you name it; the verb might be delete, yank (copy), change, indent, or even transformative operations like lint, format, uppercase, etc. In Kakoune, it is with this reversed grammar, this postfix notation, that you interactively sweep up a group or groups of characters before acting on them. That way if your object isn’t quite right, you can immediately correct it without having to undo and redo your verb.

I like to think Kakoune’s keystrokes as a text editing language model, in comparison to that of vi, is what linguists would call an anastrophe—specifically an object-verb inversion.

Photo of a barn swallow
Photo by JJ Cadiz, CC BY-SA 3.0. Subject-object-verb order can be seen in this anastrophic idiom, one swallow does not a summer make.

To English speakers, it might seem unnatural to place the verb last, but about 45% of documented natural languages have subject-object-verb order, including Latin, Ancient Greek, Japanese and Korean.

Isn’t that just Vim’s visual mode?

Well, yes, but Vim’s visual mode is a malformed afterthought in comparison, and you have to hit v every time you want to use it. Kakoune is visual mode done right, but not just that; it permits—nay thrives on—multiple selections (thousands if you wish but typically just a few) and provides some really neat tools to narrow them down incrementally, as we’ll see later.

This paradigm shift necessitated a revised set of key mappings, which Kakoune implements rather well if you don’t mind using Alt. Holding Shift means causes the usual motion keys to extend the selection, so unlike in Vim, traversing whitespace-separated words means holding Alt instead. And what about text objects? How do you select in and around words, parenthetical pairs, paragraphs, and so on? In Vim you begin by hitting v to enter visual mode followed by a or i. In Kakoune, however, normal mode is visual mode so instead Alt + a and Alt + i are used to avoid conflict with the normal meanings of a and i (append and insert).

English Vim Kakoune
Delete word dw wd
Delete up to whitespace dW <a-w>d
Delete inner word diw <a-i>wd
Delete back four words d4b BBBBd or4Bd
Change to end of word ce ec
Yank a sentence yas <a-a>sy
Change to next occurrence of ‘x’ ctx txc
Select to next occurrence of ‘x’ vtx tx
Indent three lines down 3>> xXX>
Unindent within braces <i} <a-i>}<
Keystroke idioms in Kakoune and compared to Vim.

Admittedly, from the table above, my new favourite editor looks to be more complicated than Vim. Perhaps that’s true in some sense, but Kakoune’s strengths lie in its forgiving interactivity and incremental results, which are best experienced first hand.

Using Kakoune

At this point, I encourage the reader to play around with Kakoune. Have fun with it! Install it with your package manager or build it from source. Skim over the manual page for kak(1) and open a file in the terminal like so

kak example.cc

Make sure your terminal emulator supports alt key combinations and isn’t overriding them.

Have a look through :doc faq, :doc keys (then :doc keymap for the cheat sheet), and :doc commands, experimenting as you go. That said, the built-in help system is verbose and won’t exactly hold your hand. The Kakoune wiki is more navigable, and this walkthrough is particularly helpful for learning the basics. You can edit files and manipulate buffers with the following commands

Use :delete-buffer to delete a buffer. Write to disk with :w and quit with :q. As you enter commands, you’ll see an ASCII art Clippy show up in a box with the relevant documentation. Commands are self-documenting, and many have a -docstring argument in their definition which enriches these info boxes.

Depending on your terminal colours, things might be unreadable. If so, or otherwise, pick a colour scheme using the :colorscheme <name> command. Try not to spend too many of your precious Earth seconds fussing over which is best, as I did.

Kakoune shares a good deal of elementary keys with Vim, which eases the learning curve for veterans thereof. Of the lowercase letters, only szxv and m have completely new jobs. Hitting the a key, i or o or their shifted equivalents will enter insert mode in much the same way as in vi; Shift + Q and q handle macros; press u to undo (some will rejoice it’s Shift + U, not Ctrl + r, to redo); I could go on. As you’d expect, many motion keys accept counts, as in 4 w to traverse four words.

Motions

Get the hang of using motions—h, j, k, l, w, e, b, f and t for starters—and their majuscule equivalents; hold shift to extend the selection you already have; hit ; to reset the selection range back to the cursor itself. Then of course r, d and c are your destructive verbs, while ~, `, < and > are transformative; i, a, o and Shift + O respect the selection range (try it to see what I mean), as do y (yank/copy) and p (paste). Try combining some of these with Alt and see what happens. Try Alt + i or Alt + a and read the info box to see what you can do with them; same goes for [ and ], and their shifted equivalents { and } which, predictably, extend the selection.

Press g and you’ll see the ‘goto’ menu, which shows you the handful of keys that could complete the key-stroke sequence. Familiarise yourself with some of these.

Go to … Vim Kakoune
Top of buffer gg gg or gk
End of line $ gl
Beginning of line 0 gh
First non-blank character ^ gi
Bottom of buffer G gj
End of buffer G$ ge
Top of window H gt
Bottom of window L gb
Centre of window M gc
Last (alternate) buffer Ctrl + ^ ga
Path under cursor / in selection gf gf

Same goes for v and the ‘view’ menu, which is for adjusting your viewport into the buffer.

View … Vim Kakoune
Centre cursor vertically zz vv or vc
Centre cursor horizontally Nope(*) vm
Cursor on top zt vt
Cursor on bottom zb vb
Scroll left zh vh
Scroll down Ctrl + e vj
Scroll up Ctrl + y vk
Scroll right zl vl
(*) The closest approximations I can find are zH and zL to scroll half a screen width horizontally. Or there’s set sidescrolloff=999.

Multiple selections

Pressing x selects a whole line, x again the next instead; or hold shift and see that X sweeps up multiple lines. Here’s where things get cool: sweep up a few lines with X or Alt + ip and then press Alt + s. That splits the selection linewise into multiple selections. Another way to accrue selections is using Shift + C or Alt + Shift + C. Play around with these yourself. If things get out of hand you can hit Space to reset to one selection.

Aligning columns interactively

This is one case where multiple selections are your best friend. You just make your selections and hit & to align them vertically.

Screencast: aligning columns interactively

Narrowing

Still think multiple selections are a gimmick? So did I. Until I tried some of the narrowing mechanisms. What do I mean by that? Well, let’s say you want to replace all occurrences of ‘sir’ with ‘mate’ inside a particular paragraph. With your cursor situated somewhere within it, you’d press Alt + ip to select the whole paragraph, then (and this is the good bit)

ssirEnter

to select each occurrence of ‘sir’. Now you have multiple selections. Finally you’d type

cmate, Esc

to change each into ‘mate’.

Screencast: Incremental search and replace

To achieve the same in Vim you’d need to specify the line range of the paragraph in an ex command (perhaps with the help of visual mode, i.e. vip), and provide the ‘g’ flag. In Kakoune, everything is achieved with the keyboard-based text-editing language and as a result is more interactive in that it provides incremental results so that you can make corrections on the fly.

You can enter a regular expression (see :doc regex) into the s command, but often simple key-stroke-based interactions thereafter can achieve the same effect while being easier to come up with and adjust as you go. As a contrived example, imagine trying to replace the word that comes immediately after every occurrence of a pattern.

A commonly used technique is to press % which selects the entire buffer. Then you can hit s to select occurrences of some pattern throughout the file. The information in the bottom right keeps count of how many selections you have.

The %Alt + s combo is useful for operating linewise on entire files. For example,

%, Alt + s, Alt + k, in, clude, Enter

will select all lines that contain the word ‘include’; or using Alt + Shift + K we can do

%, Alt + s, Alt + Shift + K, :, $, Return

to select all lines that don’t end with a colon. We could then do

Alt + k, ^, def, Enter

to narrow down further to only those that begin with ‘def’. Follow that up with an S key like so:

S, \, ., Enter

to split each selection at every period. As you can see, the narrowing tools are immensely powerful and the possibilities endless. And if you’re still not satisfied, there’s the $ key which filters your selections by piping each one through any program you like and keeping only those for which the program exists successfully—meaning you could always write a little shell script to be the predicate of that filter.

Changes through external programs

Don’t get too excited about $ before you’ve met |. This key is your program-wielding text-processing glue. The idea is that you make your selection(s) in Kakoune, type something of the form

|program args ...<ret>

and watch the magic happen. For each selection, Kakoune feeds it as standard input to the program, and replaces it with the standard output.

<ret> refers to the Enter or Return key. See :doc mapping for more key names like this.

Word count and formatting

Starting with a simple example, you could select the entire buffer with %, then run it through wc(1) to get a word count of your prose:

%|wc -w<ret>

Or pipe it first through the appropriate pandoc(1) command to convert it to plain text, thereby eliding markup from your word count:

%|pandoc -t plain|wc -w<ret>

Or you could select a paragraph and pipe that through to fmt(1) or par(1), which are programs that nicely reflow paragraphs of text from standard input. This is why Kakoune seems to lack important features such as text reflowing; instead it makes it easy to delegate this sort of processing to other programs, thereby adhering a little closer to the Unix philosophy.

Evaluating code within the buffer

If your buffer contained a Python expression, say

factorial = lambda n: 1 if n<1 else n*factorial(n-1)
print(factorial(13) + factorial(16))

then you could select those lines and pipe it into python(1) by typing |python<ret>, leaving you with 20929016908800 as the selection.

Screencast: Piping the selection through Python

The ! key does the same but without piping anything through to the program: it simply copies the output of the program into the buffer. For instance, !ls<ret> will add a list of the contents of the current directory before the selection. !cat example.sh<ret> will add the lines of the file example.sh before the selection.

Inserting Emoji

Use Shift + | to conveniently pipe words like ‘smile’ 😄 and ‘house’ 🏠 to a program that converts stdin to emoji, like this one liner:

#!/bin/sh
xargs -r printf ':%s:' | pandoc -f gfm -t plain
The -r option is a GNU extension.

Maybe Pandoc is overkill here, but you get the point.

Using the system clipboard

Piping to and from xsel(1) or xclip(1) is your interface to the X clipboard. Now you can copy those snippets from Stack Overflow and feed your cargo cult programming habit.

Primary selection Clipboard
Copy $xsel -i $xsel -bi
Cut |xsel -i |xsel -bi
Paste !xsel !xsel -b

User configuration

Mapping keys

Of course, you can create key mappings for these things to your heart’s content. Nice thing about Kakoune is it has a proper way of keeping user-specific mappings separate from global mappings, thanks to the scope parameter of :map. By default , is the prefix to all user mappings.

As an example, here is a mapping I use all the time:

map global user m ': format;w;make<ret>ga: echo Making<ret>' -docstring 'Format and write the file, then call *make*(1)'

It’s scoped globally because I want this to work everywhere. Now I just hit ,m to

Have a look at :doc mapping for the full story. Note that :map is one of those commands that can take a -docstring argument, which becomes part of the self-documenting hints that you see throughout the UI.

Classic line-editing shortcuts like Ctrl + w and Ctrl + u that predate even Unix are missing from Kakoune’s insert mode; those shortcuts do not fit the paradigm that Kakoune implements says the :doc faq, but for me this is one case where practicality beats purity.

See the kakoune-readline plugin.

Persistent configuration

Let’s say you’ve mapped a few keys and set a few options. How do you make them persistent between kak(1) sessions?

Create the file $HOME/.config/kak/kakrc and place your mapping and options in there. This is the analogue to one’s $HOME/.vimrc.

When you start kak(1), before sourcing your kakrc it first sources all *.kak files within your $HOME/.config/kak/autoload/ directory. That means you can put anything—even Git repositories in there (i.e. plugins)—and all non-Kakoune files don’t cause Kakoune any grief.

See kak(1) (man kak) for details on how exactly Kakoune’s run-time files are sourced.

Once your autoload directory exists, however, Kakoune decides not to source the system run-time files under /usr/share/kak/autoload/, so you’ll want to link that system directory symbolically into yours:

    mkdir -p $HOME/.config/kak/autoload/
    ln -s /usr/share/kak/autoload/ $HOME/.config/kak/autoload/

Formatting the entire buffer

The :format command is like doing %, | and specifying some format-fixing program—maybe fmt(1), par(1) or something else. You just need to set the formatcmd option.

set-option window formatcmd par

The window argument specifies the scope of this particular assignment. See :doc options set-option and :doc scope for more explanation.

Also look at autowrap_fmtcmd and autowrap-enable which wrap lines for you while typing, so it doesn’t feel like you’ve reverted to using Notepad.

Lightweight markup

If you’re writing in a lightweight markup language, be it Markdown, ReStructuredText, AsciiDoc, or what have you, it’s nice to have a program go through your writing and fix little formatting mistakes here and there, remove trailing spaces and unnecessary line breaks and so forth. Kakoune’s :format command lets you do this really smoothly without leaving the editor. Just set formatcmd to some command that calls your style linter with any appropriate arguments.

Pandoc can neatly reformat your lazy keyboard-mash markup by telling it that the input and output are of the same kind. ReStructuredText for example,

set-option buffer formatcmd 'pandoc -f rst -t rst --reference-links'

where passing --reference-links is optional of course, but means you can write an inline link and have it converted automatically to a reference at the bottom of the file, which takes care of the otherwise laborious task of managing references, their names and order by hand.

This could become slow for particularly large documents; here I’m using Pandoc to reformat some 600 lines of text and it takes a fraction of a second.

SCSS

In this screencast you can see me fixing a poorly written stylesheet using prettier(1) with the following formatcmd:

set-option window formatcmd 'prettier --parser=scss'

Hooks

You can use hooks to trigger a command when some event happens, such as opening a new file, resizing the window, pressing a key in insert mode, or the user being idle for some length of time. These also have an associated scope. Read :doc hooks to see how they work.

Filetype-specific run commands

Now, what if you want to set an option differently depending on the kind of file you’re editing? You can do that using the WinSetOption hook:

hook <scope> WinSetOption 'filetype=<filetype>' <command>

This hook is triggered whenever the filetype option is set to <filetype>. The command can be any list of commands. So to set a few options that would be something like this:

hook global WinSetOption filetype=(css|scss) %{
    set-option buffer indentwidth 2
    set-option buffer tabstop 2
    set-option buffer scrolloff 5,0
}

This can get repetitive for lots of different filetypes, so we can factor this out using the function filetype-hook as defined below:

define-command filetype-hook -params 2 %{
    hook global WinSetOption 'filetype=(%arg{1})' %arg{2}
}

filetype-hook css|scss %{
    set-option window indentwidth 2
    set-option window tabstop 2
    set-option window scrolloff 5,0
    set-option window formatcmd 'prettier --parser=scss'
}

filetype-hook ruby|python %{
    set-option window indentwidth 4
    set-option window tabstop 4
    set-option window matching_pairs ( ) [ ] { }
}

Should you use Kakoune?

There’s loads more to Kakoune, not least of which are its plugins. But the potential for it to be configured exactly how you like is huge, and it’s very easy to do so. I haven’t even talked about evaluate-commands and %sh{…} blocks here.

Kakoune is so smooth in the way it uses existing programs; take the clipboard for example: why should Kakoune bother with implementing a feature specific to and dependent on X, when it can very easily be done in run-time configuration by piping to and from a program such as xsel? This is a taste of the Unix philosophy, and it’s what makes Kakoune so wonderfully extensible.

But due to the difference in key mappings, existing Vim users will become frustrated in the transition, and Vim veterans will be leaving their beloved configuration behind, putting them back to square one. I can see many Vim users dismissing Kakoune as an interesting idea, but not worth the effort to learn. I mean, even its creator describes it as an experiment for a better code editor.

I have found that learning to use Kakoune’s key stroke language has messed with my muscle memory somewhat when it comes to Vim and crucially Readline, although not as much as I’d anticipated. Still, I now use Emacs bindings in Readline, because they differ enough that I don’t get confused.

GNU Readline is the line-editing library used by countless terminal-based programs.

Update, August 2020: After much experience with all four, my muscle memory can switch smoothly between Emacs, Readline, vi and Kakoune. But YMMV.

This write-up has been mostly positive, but my experience with Kakoune has not been without issue:

Further reading


To leave a comment, please send a plaintext email to ~chambln/public-inbox@lists.sr.ht and it will show up in my public inbox.