(cosine.blue)

← Back

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.

Gregory Chamberlain

Friday, 06 September 2019

Cover image

1 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.

Skip ahead 700 words to Using Kakoune if you just want to get stuck in right away. Come back if you get totally confused.

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.

2 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(1), grep(1), make(1), and man(1), works with ctags, conforming linters and other things.

Vim is difficult to extend, not least because of its idiosyncratic and unreadable train wreck of a language, VimL. Kakoune, on the other hand, stands by its decision not to complect 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.

2.1 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, WORD1, 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.

In English, the subject-object-verb order can be seen in this anastrophic idiom, “one swallow does not a summer make”.2

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

2.2 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. Since uppercase motion keys tend to extend the selection, Altw, Alte and Altb are used to traverse WORDs; similarly, since a and i have jobs already, Alta and Alti are used to select ‘an object’ or ‘inner object’.

English Vim Kakoune
Delete word d w w d
Delete WORD d W Altw d
Delete inner word d i w Alti w d
Delete back four words d 4 b B B B B d or 4 B d
Change to end of word c e e c
Yank a sentence y a s Alta s y
Change to next occurrence of x c t x t x c
Select to next occurrence of x v t x t x
Indent three lines down 3 > > x X X >
Unindent within braces < i } Alti } <

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.

3 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.3

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 relevadocumentation.4 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 s, z, x, v and m have completely new jobs. Hitting a, i, A, I, o, or O will enter insert mode in much the same way as in vi; Q and q handle macros; pressing u will undo (some will rejoice that it’s U, not Ctrlr, to redo); I could go on. As you’d expect, many motion keys accept counts, as in 4 w to traverse four words.

3.1 Motions

Get the hang of using motions (h, j, k, l, w, e, b, f, and t for starters) and their majuscule equivalents, holding shift to extend the selection you already have, hitting ; 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 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 Alti or Alta 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 g g g g or g k
End of line $ g l
Beginning of line 0 g h
First non-blank character ^ g i
Bottom of buffer G g j
End of buffer G $ g e
Top of window H g t
Bottom of window L g b
Centre of window M g c
Last (alternate) buffer Ctrl^ g a
Path under cursor / in selection g f g f

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

“View …” Vim Kakoune
Centre cursor vertically z z v v or v c
Centre cursor horizontally Nope5 v m
Cursor on top z t v t
Cursor on bottom z b v b
Scroll left z h v h
Scroll down Ctrle v j
Scroll up Ctrly v k
Scroll right z l v l

3.2 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 Alti p and then press Alts. That splits the selection linewise into multiple selections. Another way to accrue selections is using C or AltC. Play around with these yourself. If things get out of hand you can hit Space to reset to one selection.

3.2.1 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.

3.2.2 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 Alti p to select the whole paragraph, then (and this is the good bit)

s s i r Enter

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

c m a t e Esc

to change each into ‘mate’.

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. v i p), 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 % Alts combo is useful for operating linewise on entire files. For example,

% Alts Altk i n c l u d e Enter

will select all lines that contain the word ‘include’; or using AltShiftK we can do

% Alts AltShiftK : $ Return

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

Altk ^ d e f 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.

3.3 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.6 For each selection, Kakoune feeds it as standard input to the program, and replaces it with the standard output.

3.3.1 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.

3.3.2 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.

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.

3.3.3 Inserting Emoji

Use | 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

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

3.3.4 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

4 User configuration

4.1 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 become part of the self-documenting hints that you see throughout the UI.

Classic line-editing shortcuts like Ctrlw and Ctrlu 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.

Update: I have since written a plugin that implements Readline shortcuts reasonably well in Kakoune.

4.2 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.7

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/

4.3 Formatting the entire buffer

The :format command is provided as a shortcut for % | followed by your formatter program, be it fmt(1), par(1) or something else like a style linter. You just need to set the formatcmd option.

set 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.

4.3.1 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 function as a style linter by telling it that the input and output are of the same kind. ReStructuredText for example,

set 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, and some style linting programs will be slower than others. In this document, I’m using Pandoc to reformat around 600 lines of text and it takes a fraction of a second.

4.3.2 SCSS

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

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

4.4 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.

4.4.1 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)' %{
    set buffer indentwidth 2
    set buffer tabstop 2
    set 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:

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

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

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

5 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 Readline8, although not as much as I’d anticipated.

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

6 Further reading


  1. It is by convention that the uppercase ‘WORD’ refers to whitespace-separated groups of characters, whereas the lowercase ‘word’ refers to consecutive runs of alphanumeric characters.↩︎

  2. I suppose, strictly speaking, SOV order would be “one swallow a summer does not make”, but I liked the barn swallow photo too much to change it. (Credit: JJ Cadiz, CC BY 3.0).

    More examples of object-verb inversion in English:

    • “With this ring, I thee wed

    • “Till death us do part

    • Deep into that darkness peering, long I stood there wondering, fearing.”—Edgar Allan Poe, the Raven.

    ↩︎
  3. MacOS users might find a fix here.↩︎

  4. If Clippy is bringing back unpleasant memories, you can change or remove the ASCII art by putting this in your user configuration:
    
    set global ui_options ncurses_assistant=VALUE
    

    where VALUE is clippy, cat, dilbert, none, or off.↩︎

  5. The closest approximations I can find are z H and z L to scroll half a screen width horizontally, alternatively there’s set sidescrolloff=999. Anyway who really needs to centre the view on their cursor horizontally? It seems like it was added to Kakoune just for completeness. Vim beats Kakoune on the scrolling front in other ways like synchronous scrolling, for instance.↩︎

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

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

  8. GNU readline(1) is the line-editing library used by countless terminal-based programs. I now use Emacs bindings in Readline, because they differ enough from Kakoune that I don’t get confused.↩︎