# Kakoune Is a Text Editor

> Source: <https://ficd.sh/blog/kakoune-is-a-text-editor/>
> Published: 2026-08-18 18:52:02+00:00

introduction

Welcome!

My goal with this article is to help you develop a comprehensive understanding
of the [Kakoune](https://kakoune.org) text editor: *what* it is, *how* it works, and *why* it has
that design.

Although this is not exactly a tutorial, we build up these concepts gradually, and plenty concrete usage examples and demo videos are provided to supplement the discussion.

This piece is quite long, and densely loaded with information. I suggest treating it like a long-term learning resource you can chip away at, rather than trying to tackle it all in one session.

I hope this ends up being useful, both to new users and existing Kakoune
enthusiasts. If you have any questions or comments about this post, please don’t
hesitate to [contact me](/contact).

This article was written without AI assistance. If you are interested in a more
human internet, please consider subscribing to my
[RSS feed](https://ficd.sh/rss.xml) to be notified of future posts.

[What is kakoune?](#what-is-kakoune)[beyond vim](#beyond-vim)[What does subject : verb order even mean?](#what-does-subject-verb-order-even-mean)[kakoune’s inverted grammar](#kakounes-inverted-grammar)[orthogonality and atomic edits](#orthogonality-and-atomic-edits)[A deeper dive into keys & commands](#a-deeper-dive-into-keys-commands)[multiple selections](#multiple-selections)[registers & marks](#registers-marks)[iterative learning](#iterative-learning)[a server-client editor](#a-server-client-editor)[editing with pipes](#editing-with-pipes)[command expansions](#command-expansions)[writing plugins](#writing-plugins)[shell expansions](#shell-expansions)[conclusion](#conclusion)

[What is kakoune?](#what-is-kakoune)

[Kakoune](https://kakoune.org) (*French: [kakun]*) is a modal screen-based text editor created in
2011 by French programmer Maxime Coste 1, better known as

[mawww](https://github.com/mawww). Having begun its life as a C++ rewrite of Vim, it has since evolved into a project with a strong identity and unique design goals.

A non-exhaustive overview of Kakoune’s most interesting features 2:

**Multiple selections and multi-cursor editing are first-class**(regex filtering, splitting, aligning, etc.).** Interactivity and experimentation are core design principles**.** Orthogonal design with powerful editing primitives**(selection rotation, indentation leveling, case manipulation…)** Client/Server architecture in which the windows of your session are managed by your terminal or window manager**(tmux, i3, niri…).** Highly extendable with an easy-to-learn hook & macro system**(unofficially called “Kakscript”).- Large focus on
**Unix philosophy**; Kakoune not only integrates with, but relies on standard Unix tools.

note on helix

[Kakoune](https://kakoune.org) has served as a major inspiration for the
[Helix](https://github.com/helix-editor/helix) editor. However, Helix has
diverged on some key design points, and the two cannot be discussed
interchangeably. Thus, Helix is outside the scope of this discussion. With that
said, although much of this article won’t apply to Helix, some of it might, due
to its shared DNA with Kakoune. Thus, Helix users may still find this discussion
valuable.

[beyond vim](#beyond-vim)

Let’s consider “Vim motions” as an editing language. This “core” doesn’t *need*
to be surrounded by Vim; there are many editors (VS Code, JetBrains…) that
support Vim motions. Thus, a user that learns Vim motions in one tool can
quickly adapt to another tool that supports them.

A side effect of this is that, because of Vim’s ubiquity, a lot of people have
come to view “Vim-like bindings” as ** the standard** for keyboard-driven,
modal editing. In other words, users expect editors to be mostly compatible with
their existing “Vim binding muscle memory”. Some people won’t consider an editor
that expects them to change their approach.

I think that’s a shame, because although I’m sure Vim is so popular for a
reason, *we can certainly do better*. No design is immune to being iterated on
and improved. Kakoune does this in an innovative way: by **inverting the
subject–verb order**.

[What does subject : verb order even mean?](#what-does-subject-verb-order-even-mean)

When people say they expect “Vim bindings”, what they really mean isn’t specific
keys… sure, we’re used to `hjkl`

for movement, but we can get used to
something else just as fast. What matters most is Vim’s **modal text editing
grammar**.

It distinguishes between *insert mode* (regular typing) and *normal mode*: in
which *keys* comprise *words* composed into *sentences* that describe text edits
in the form *verb* followed by *subject*.

This structure closely mirrors English, so it feels natural for us to reason
about. For example, we might express “delete this word” as `dw`

: `d`

for
“delete” as the verb, and `w`

for “until the start of the next word” as the
subject.

There’s [no shortage](https://stackoverflow.com/a/1220118) of
[excellent writing](https://takac.github.io/2013/01/30/vim-grammar/) on
[Vim’s text editing language](https://learnvim.irian.to/basics/vim_grammar).
However, let me briefly challenge the intuitiveness of verb : subject for text
editing.

How often have you found yourself in the following situation? You’re editing the middle of a sentence, and now you want to delete from the cursor up to a certain word. You know exactly how far to go; since you can clearly see the target with your eyes. The challenge: how to share this information with the editor?

Well, the optimal (Vim-like) command would be `d<count>w`

, where `<count>`

is a
number key representing how many words you want to delete. However, how do you
know that number? It’s trivial for 2-3 words, but how about something like 13?
Sure, you could literally *count* them, but that’s going to take forever and
take you out of the groove.

Another option is to guess. But sadly, your first (and only) visual indicator of
how far you went is *after* the edit has been applied. So if you were off by a
few words, you’ll have to undo and repeat the entire action with a different
count.

Thankfully, Vim has an easy solution to the problem: visual mode! Simply press
`v`

, and now you can use `w`

and other motion keys to create a clearly visible
*selection* (subject), after which you can press another key to execute the
*action* (verb). By seeing exactly what you’re going to operate on *before*
doing the action, you can reduce misfires greatly.

However, Vim’s visual mode isn’t the most ergonomic. There are different
versions of it (linewise, blockwise, …), and it’s a fully separate mode in
which key behaviors may differ in surprising ways. Furthermore, when you finally
press an “operator” key, the editor usually returns to normal mode — so if you
prefer visual editing, be prepared to press `v`

*a lot*.

Vim’s visual mode *inverts* the verb order from its normal mode. Subject first,
*then* verb. The selection *always* comes before the action; as a consequence,
you will *see* what you’re acting on before you act.

A friend of mine put it like this: Vim’s inclusion of visual mode is a reluctant
admission that the verb-subject order isn’t good enough to cover all cases —
but instead of adjusting course and adopting a better grammar, Vim decided to
bolt support for it on top of the existing model, ballooning complexity to high
heaven.[3](#fn:jd)

[kakoune’s inverted grammar](#kakounes-inverted-grammar)

Luckily for the visual mode enjoyers of the world, there is a better solution out there. This is the exact situation where Kakoune’s editing language gets it chance to shine.

Imagine a normal mode that *is* visual mode. What if, instead of being relegated
to a finicky sub-mode, visual selection-based editing was not only supported,
but had the entire editing experience optimized around it?

Well, you don’t need to imagine it, because Kakoune *is* that subject-verb
native editor. For example, `w`

doesn’t just move the cursor forward to the next
word, it moves the entire *selection area*. Thus, the meaning of `w`

is “select
the next word”. Here’s how that looks:

Of course, where this really shines is *incrementally updating your selection*.
Let’s return to the previous example of selecting. In Kakoune, the “shift”
modifier changes the meaning of many commands: instead of *moving* the
selection, it will *extend it*. So we can use `W`

to increase our selection, one
word at a time.

Furthermore, most Kakoune keys support counts too, so you can press `<num>W`

if
you know the number already. And here’s the best part: if we overshoot, we don’t
have to undo and try again. We can just press `B`

as many times as we need to
decrease our selection. Once we’re happy with our selection, we can press any
key that *operates* (i.e. a verb), such as `d`

for “delete”.

well, actually

It’s not so much “the selection comes before” as it is “**there’s always a
selection**”. A key that operates on a selection will *always* work, because
even your “cursor” is just a single-character selection.

Although editing in Kakoune can feel like “make the selection, than do the
action”, there is no actual requirement to follow up a selection key with an
action key. You can spend as much time as you want perfecting your selection
before finally pressing a key like `d`

or `y`

to operate on it.

An often overlooked benefit of Kakoune’s verb-order-inversion is that it reduces the total number of modes by essentially merging Vim’s visual mode into normal mode. This eliminates some mental overhead and reduces total keystrokes (no more pressing stuff to always hop in and out of visual mode).

[orthogonality and atomic edits](#orthogonality-and-atomic-edits)

There’s another interesting consequence of Kakoune’s grammar inversion: **it
completely eliminates the need for operator-pending mode**. Basically, it’s not
fully accurate to say that `d2w`

is a normal mode command in Vim. The reason:
`d`

on its own can’t actually do anything. You haven’t yet specified *what* to
delete!

So Vim enters a mode called “operator pending”, in which it waits for your
follow up: *the subject*! Once you’ve input `2w`

, Vim can complete the sentence
`d2w`

and execute the edit.

In Kakoune, the operator (verb) always knows what to operate on: the selections,
at least one of which **must always exist**. This makes the behavior of normal
mode keys *atomic*. `d`

is defined as “delete the content of the selection”, and
given the same buffer state (text & selections), it will always behave the same
way.

This key difference from Vim makes composing actions into edits remarkably easy to improvise, because any key can follow any key. In essence, there are only two kinds of keys in Kakoune’s normal mode:

- Keys that
*change what is selected*(move the selection, expand it, shrink it, etc.) - Keys that
*operate on what is selected*(delete, copy, duplicate, replace, etc.)

This means edits aren’t grouped into “sentences” like `d2w`

or `cib`

. Since
they’re incremental, not only do you get visual feedback for each step, but
“logical edits” can also be *partially* undone with `u`

.

[A deeper dive into keys & commands](#a-deeper-dive-into-keys-commands)

Kakoune has two ways to receive instructions:

- executing keys
- evaluating commands

[executing keys](#executing-keys)

Keystrokes are simple enough. Not simple as in trivial to learn — there’s a
lot of them,
[listed here](https://github.com/mawww/kakoune/blob/master/doc/pages/keys.asciidoc)
— but simple to express in writing: we simply string keys together. Thus, the
sequence `hhW`

represents pressing `h`

twice, then `W`

(Shift + `w`

).

For some keys, we can use a special notation if needed:

- Modifier keys:
`<a-X>`

:`Alt + X`

`<c-X>`

:`Control + X`

`<c-a-X>`

:`Control + Alt + X`

- etc…

- Special keys:
`<lt>, <gt>`

: escaped`<`

and`>`

`<esc>`

: escape (as seen above)`<ret>`

: enter`<tab>`

,`<space>`

, etc…

spaces

Certain keys can be represented multiple ways. For example, you can write
`<space>`

explicitly, or simply put an actual space in the sequence. In the key
sequences written under the demo videos, I’m using `<space>`

to denote literal
presses of the space bar. You can ignore the actual whitespaces, those are just
there to make it easier to read and have no further meaning.

Why is this “key notation” important? Well, it’s **canonical**. Besides being a
more pleasant shorthand (`<a-x>`

is easier to write than `Alt + x`

all the
time), it gives us all a shared language to discuss and share Kakoune edits.
Additionally, Kakoune can actually parse these strings and execute them via the
`execute-keys`

command. Speaking of commands…

[evaluating commands](#evaluating-commands)

Kakoune can evaluate a *string* as a set of *commands*. A sequence of such
commands is often called **Kakscript**, though it’s not officially a scripting
language. Much like a shell, its input is split into *words*, which are then
interpreted as commands with arguments. There’s shell-like quoting, and commands
can be separated by newlines or semicolons.

```
# comments BTW
edit -existing src/main.c; set-option buffer indentwidth 4
write src/main.c.bak
```

Like a shell ~~(wow! who would’ve guessed?)~~ script, Kakoune can even read &
evaluate commands (Kakscript) from a file (`.kak`

extension by convention).
There’s even a command *called* `evaluate-commads`

(`eval`

for short) which, you
guessed it, pretty much does the same thing as `eval`

in the shell.

In case you’re unfamiliar, that means it takes its arguments as strings and
evaluates them as commands. Like `execute-keys`

, a number of helpful flags are
available, such as `-buffer`

, which runs those commands in a different buffer
context. It also allows for nested evaluation, which becomes useful when working
with expansions ([discussed later](#command-expansions)).

kakscript quoting

Kakscript is mostly shell-like, since it has comments, word splitting, escaping
newlines with `\`

, and so on. However, it has some unorthodox quoting rules,
briefly described here.

In short, Kakoune has two kinds of quote delimiters: *balanced* and *matching*.
The usual single `'`

and double `"`

quotation marks are *matching*:

`"..."`

`'...'`

You can also use a `%`

prefix to pick your own delimiter, which can be **any**
non-alphabetic Unicode symbol. For example:

`%|...|`

`%~...~`

`%ε...ε`

Any matching delimiter can be doubled to escape it within the string.

Balanced delimiters are also `%`

-prefixed and follow slightly different rules.
Only the four kinds of ASCII brackets are supported:

`%{...}`

`%(...)`

`%[...]`

`%<...>`

There can’t be escaped, but balanced pairs are allowed to be nested:

`%{{}}`

: contains literal`{}`

`%(())`

: contains literal`()`

`%[[]]`

: contains literal`[]`

`%<<>>`

: contains literal`<>`

Although these rules may seem like arbitrary complexity on the surface, they become really helpful when writing scripts with many nested layers of string evaluation.

As we’ll discover later, Kakscript often embeds POSIX shell, which also uses `'`

and `"`

for quoting. Using Kakoune’s unique quoting forms lets us write shell
scripts without needing to constantly worry about escaping delimiters.

To read about Kakscript in more detail, see the
[manual](https://github.com/mawww/kakoune/blob/master/doc/pages/command-parsing.asciidoc).
For a more in-depth discussion on its semantics, see my
[devlog](/blog/kaklang-devlog-1) on writing a Kakscript parser.

[commands are keys](#commands-are-keys)

Of course, commands can be written and evaluated on the fly: pressing `:`

opens
a prompt into which you can type a command and press `<ret>`

to evaluate it. The
same rules apply: comments, quoting, word-splitting all work the same.

Which leads us to an interesting observation…

I argue that commands are just a subset of keys, because any valid command can
be typed at the `:`

prompt. Thus, the command `write src/main.c`

is also the key
sequence `:write src/main.c<ret>`

. These are just two notations to express the
same thing.

In fact, there’s nothing special about even `:`

. It’s just a key whose job is to
bring up the prompt, wait for input until `<ret>`

is pressed, then evaluate it.
The same if true of *any* key that uses a prompt, such as `/`

(search) and
`<a-k>`

(filter).

So, why does this matter? It means that commands can be evaluated in *any*
context where keys are valid, because there always exists some sequence of keys
to produce any command. This is true even in insert mode, wherein `<a-;>`

can be
used to escape to normal mode for one command: `<a-;>:write src/main.c<ret>`

Let’s illustrate why the “commands are keys” principle is so important with an
example. Consider the common case of binding a key to some action. In Kakoune,
we can use the `map`

command to ~~map~~ a key to a sequence. For example, let’s
make the tab key select the current line, but with whitespace trimmed:

```
map global normal <tab> x_
```

Let’s try a more common one: binding `<c-s>`

save the file. There’s a small
difference, though: there isn’t a key for saving, there’s only the `write`

command. Thankfully, that’s no problem for us, because as we observed earlier,
any valid command can be expressed as a key sequence. Thus, to complete the
binding:

```
map global normal <c-s> ':w<ret>'
```

Now, let’s consider the inverse: *executing keys from a command context*. It
turns out, this is possible too, thanks to the `execute-keys`

command (`exec`

for short). It’s a bit like `map`

in that it executes… well… you know, but
the execution happens immediately, and it also exposes some options for us to
tweak. To understand how this can be useful, let’s create another binding like
before. This time, we’ll make the `=`

key duplicate the current line:

```
# x to select the line, y to yank (copy), p to paste below
map global normal '=' 'xyp'
```

This works, but there’s two problems: because those keys are being executed literally as if you had pressed them, we end up with side effects: our previous selection is lost, and our clipboard gets overwritten. What if we wanted to keep working with either of those after duplicating the line?

This is where `execute-keys`

becomes relevant. Beside just executing the key
sequence we give it (just like `map`

!), this command provides some options that
can really help us out.

The first of these is `-draft`

, which executes keys in a “disposable context”.
This basically means that our selection state gets restored after the command
runs. Consider this example, where the key sequence `glbcFOO<esc>`

selects the
last word on the line and replaces it. You can see for yourself how running it
with without `-draft`

results in a different cursor position:

While using `-draft`

indeed keeps the original selection:

`-save-regs`

does something very similar, except for *registers*, which are like
Kakoune’s clipboards. (Yes, you can use multiple at once, and it’s as powerful
as you’d expect!) Registers all have names, and it happens that the *default*
register (the one stuff gets copied to when you press `y`

) is called `"`

(aka
`dquote`

). Thus, we can complete our improved line duplication command like so:

```
# duplicate the line, preserve register and selections
execute-keys -draft -save-regs '"' 'xyp'
```

Like before, we just need to write the above command in “key notation” to successfully bind it:

```
map global normal '=' ':execute-keys -draft -save-regs dquote xyp<ret>'
```

As you can see, the interplay between commands and keys has its uses for both interactive editing as well as scripting & macros. Even if you don’t plan to write any plugins, these tricks are bound to come in handy in your own configurations.

isolated mappings

Kakoune’s key mappings are *isolated*. If a mapped key refers to itself (e.g.
`map global normal x <i-p>x`

), the right-hand side will always use the canonical
meaning of that key; recursion is never allowed.

Isolation also means that any keys bound by `map`

have *no* effect on
`execute-keys`

or subsequent `map`

commands. (`execute-keys`

does have a
`-with-maps`

option, but its use is generally discouraged).

A positive effect of this isolation is that key sequences are fully portable across Kakoune configurations, which enables the easy sharing of macros, custom commands, plugin implementations, etc.

For example, as a Colemak-DH user, I’ve mapped `mnei`

to `hjkl`

for navigation.
However, plugins are completely safe to use `hjkl`

internally; my custom
mappings won’t interfere. Similarly, when we write our own plugins or custom
hooks, we are expected to use the canonical keys as well.

[multiple selections](#multiple-selections)

As I mentioned earlier, multiple selections are a core editing feature in
Kakoune. This is intuitively understood as multi-cursor editing; but since a
cursor in Kakoune is just a selection of size 1, it’s important to think of it
as multi-*selection* editing.

Commands that we understand as acting on “the” selection/cursor actually act on
**all** selections. It just happens that most of the time, we only have one
selection. But if we have *multiple*, pressing `d`

, for example, deletes *all of
them*:

The concept is simple, but incredibly powerful. Multi-selections don’t
necessarily empower us to make edits that are *impossible* in Vim-like
editors… but they certainly make the whole experience a lot more ergonomic!
Hang tight, you’ll see why in a moment.

So, how does one create multiple selections in the first place? One way is the
`s`

key, which lets us create them by matching a regex. In short, you first
select some area; then, you press `s`

, type a regex, and press enter. You’ll end
up with all non-overlapping matches inside the original selection as new
selections.

`gjx s\d+<ret> s2<ret> r7`

The most common use case for this is the classic “search and replace”. Let’s try
replacing every occurrence of “foo” with “bar” in this example. We begin by
pressing `%`

, which selects the entire buffer (think `ggVG`

in Vim, or `<c-a>`

in a “typical” IDE). We need to do this because keys like `s`

always operate *on
the selection*; you must be explicit about saying “please search the whole
buffer”.

So, pressing `s`

brings up a prompt, we type `foo`

, and after hitting enter, we
end up with each match selected. That’s the search part down, how about replace?
We do it the exact same way we’d replace a single selection: press `c`

(change),
which deletes the selection and enters insert mode. We can type our replacement,
`bar`

, and press escape to return to normal mode when we’re done.

`% sfoo<ret> cbar<esc>`

You may be thinking, big whoop, isn’t this basically the same as
`:s/foo/bar/g<ret>`

in Vim? Yes, but with a big difference: you get visual
feedback for **every step** of the edit. Besides helping you catch regex
mistakes earlier (you’ll clearly see if the wrong thing is selected), the main
advantage of Kakoune’s approach is that it lets you branch into a different
option at almost any step.

You don’t *have* to replace the matches; you could copy them (`y`

), make all
letters uppercase (`<a-`>`

), [pipe them to a program](#editing-with-pipes)
(`|`

)… You can actually do anything, because as we learned earlier, multiple
selections aren’t anything special; most keys you press just have their effects
repeated across selections.

Thus, the power of multi-selection shines when making more complex edits. Let’s demonstrate that with some brief examples. Don’t worry about understanding each step, these are just to give you an idea of what’s possible. I’ll share the key sequences so you can try them out yourselves.

Here’s me turning each sentence of a passage into a bullet point:

`%<a-I>s a<ret><esc> I-<space> <lt>`

And how about swapping the contents of these if/else blocks:

`<a-I>{ <a-)>`

further reading

If you’re interested in more advanced examples,
[Dmitri Sabadie has an excellent write-up of solving some Vim puzzles in Kakoune.](https://strongly-typed-thoughts.net/blog/vim-kakoune-puzzles-2025)

[registers & marks](#registers-marks)

We briefly mentioned *registers* in a previous section, but it’s worth
investigating them a little deeper, since they’re a big part of editing with
Kakoune. In case you’re not familiar with the concept, you can think of
registers like Kakoune’s internal clipboard.

When we copy some text with `y`

, it gets put in the default register. `p`

pastes
text from that same default register. Like Vim, Kakoune lets us use the ASCII
letter keys as *named registers*. Double quote `"`

is the “register key”, so to
copy some text into register `a`

, we can press `"ay`

. To paste text from
register `x`

, we type `"xp`

. This can be done with any key that uses a register.

Note

This is also why `"`

or `<dquote>`

is the name of the default yank register!

You can already imagine how having multiple named clipboards is useful, but
registers really shine when we make use of Kakoune’s *special registers*. You
can see them all listed in `:doc registers`

, but let’s focus on the most useful
one: the *mark* register, called `^`

or “caret”.

Other editors, like Vim, let you “mark” positions in the buffer. You can then
“return” to a marked position easily. This feature can be lifesaving when
navigating around a complicated codebase. Kakoune has marks too, and of course,
they can be used like bookmarks as well. But Kakoune marks have a key property
that make them incredibly powerful: **they don’t just mark positions, they save
selection descriptions to registers**.

It’s a bit like creating a snapshot of your entire selection state and returning
to it later. We use `Z`

(uppercase) to save a mark, and `z`

(lowercase) to
restore it. Named registers can be used too, like `"aZ`

and `"az`

. Consider the
following example, in which we’re about to do a search and replace but get
distracted by something else. Using marks, we’re able to follow distractions to
our heart’s content, since we can return to the previous editing state easily:

`<a-i>p sfoo<ret> Z /TODO<ret> x2J sbaz<ret> cbar<esc>, 2kxd zcoof<esc>`

We can also perform *combinations* on marks, which are accessible through
`<a-z>`

and `<a-Z>`

. The most common is `a`

for append. `<a-z>a`

selects the
mark plus our current selection *without* changing the mark, while `<a-Z>a`

appends the current selection *to* the mark.

Mark combinations are great for creating super precise selections
*incrementally*, without needing to conjure up some crazy regex to select it all
in one go. In this example, I use `<a-Z>a`

(append to register) to select a
bunch of words, then capitalize all of them with a single press of `~`

:

`wwZ jbb<a-Z>a 5w<a-Z>a 2j3b<a-Z>a glb<a-Z>a z~`

We can also use the “union” mark combination (`<a-z>u`

) to select an area by
separately defining its start and end points. If we save a mark with `Z`

, select
something else, then press `<a-z>u`

, the entire area between those two points is
selected. This is super handy for selecting a large chunk of text that doesn’t
all fit on the screen at once:

`xZ 20jvt 20jvt 6j x <a-z>u <a-;> d vv`

[iterative learning](#iterative-learning)

Despite Kakoune’s high complexity, you can actually get up to speed with it pretty quickly. The truth is, most edits you’ll want to do can be expressed with a smaller number of basic keys that you can learn in a day. At this point, you’ll likely be somewhat inefficient, but nothing is stopping you from actually using Kakoune to complete your daily work at a reasonable pace.

The *real* learning experience comes with the gradual process of *refinement* as
you discover *more efficient ways* to perform edits you already know. Kakoune is
very well suited to being “discovered” in this way because the two main kinds of
keys (change the selection, act on the selection) are simple to reason about.
Once you understand Kakoune’s conceptual framework, integrating new keys and
techniques into your workflow becomes a remarkably *iterative* process.

Let’s being with something called an “editing goal”. This is basically a description of the end result you want to arrive at. For example, “change this string’s quote delimiters”, or “extract these lines of code into a separate function”.

Most goals have multiple, equally valid ways to be achieved. When you are tasked with an editing goal you haven’t encountered before, a set of unknown paths become available to you. Your job is to “discover” one of those paths, based on the keys and techniques you already know. If you’ve got the basics down, a basic, but inefficient path should always be available to you.

Learning Kakoune is really about slowly integrating new techniques into your existing workflows to make them more efficient. Let’s illustrate this principle with an example. Suppose your goal is to swap the arguments in a Python function signature:

``` python
# before
def foo(first, second):
  pass
python
# after
def foo(second, first):
  pass
```

Let’s review the keys you’d need to discover the most basic editing path:

some basic keys

`x`

selects the whole line:

`s`

creates selections matching a regex:

`<a-)>`

rotates selections content (note the plural):

If you’ve learned the above keys, you can already form a simple game plan:
select as one selection per argument, then rotate. To execute this plan, you’d
press `x`

to select the line, select the parenthesis with `s\(.+\)<ret>`

, then
“reduce” your selection to just the arguments with `s\w+<ret>`

, followed by a
final `<a-)>`

.

`x s\(.+\)<ret> s\w+<ret> <a-)>`

That’s not exactly the most *optimal* way, but my point is, *you can do it
knowing only three keys*. As you learn more keys, you can make edits like this
more convenient.

For example, you may replace the `s\w+<ret>`

with the much simpler
`S,<space><ret>`

, which *splits* your selection on regex matches (in this case,
just the literal `,`

):

`x s\(.+\)<ret> H<a-;>L S,<space> <a-)>`

The escaped parenthesis in that regex is kinda ugly, but luckily we can avoid
that too. By putting the cursor inside the parenthesis, we can select it all
with `<a-i>)`

, thus simplifying the edit:

`www <a-i>) S,<space><ret> <a-)>`

But what if we want to do this for multiple functions at once? Just select them,
then select the parenthesis and the inner words as *nested text objects*:

`% <a-I>) :exec <space> -itersel <space> <a-I>w <a-)>`

Note

We have to actually type part of this out using `:exec`

because we need
`-itersel`

, which runs a key sequence once per selection. This is how we rotate
the words within each line, as opposed to globally!

In other words, you really don’t need to know that many keys to get started with
Kakoune. All the keys you learn can be composed together in ways that make
sense, so you can learn new primitives at whatever pace suits you until you’ve
got them all. And in the meantime, if you really need something, `:doc keys`

serves as an excellent resource.

[a server-client editor](#a-server-client-editor)

Kakoune is designed with the Unix philosophy in mind: *do one thing, and do it
well*. Thus, its focus lies entirely with being a competent code editor;
anything that *can* be outsourced to external tools *should* be.

Like all modern editors, Kakoune can have multiple text buffers open for editing
at once. However, *unlike* most of its peers, Kakoune does not implement any
window management. A single client has no vertical or horizontal splits; you can
only view one buffer at a time.

However, Kakoune *does* have a solution to this, and I think it’s pretty
elegant. Notice how I said “client” earlier? That’s because Kakoune has a
*server–client architecture*. When you first launch Kakoune, besides starting
the interface you see, it also spins up a session with a unique ID… which can
be used to attach new clients to this session!

Now, suppose we’ve started Kakoune with `kak -s foobar`

, which just sets a
custom session ID. If we want to see & edit two buffers side–by–side, we can
simply attach a new client to the session with `kak -c foobar`

, and change its
active buffer.

This is what lets us outsource managing buffer windows to an actual window manager. A Kakoune client is a separate process. Thus, you can open new clients as tmux planes, separate terminal windows managed by your operating system, or however else you prefer to manage terminal applications.

Speaking of tmux, a strong parallel exists here. It could be helpful to think of
Kakoune like tmux, except it handles text buffers instead of terminal panes.
Tmux allows for detaching from a session, allowing it to exist in the background
and be reattached to later, even if you’ve closed the terminal window. Kakoune
also supports this detach-attach workflow. The `daemonize-session`

command
prevents the session from terminating when the last client is closed, thus
allowing your editing sessions to persist as long as the daemon isn’t killed.

You can also control Kakoune sessions from outside using `kak -p <name>`

. This
command reads commands from standard input and evaluates them in the context of
the session you named. For example, I can remotely kill the earlier session with
`echo 'kill' | kak -p foobar`

. This makes it easy to script Kakoune from
anywhere on your system.

[editing with pipes](#editing-with-pipes)

Any Linux user will tell you that in a shell, the `|`

key is incredibly
powerful. It can be used to compose commands together into *pipelines* by
“piping” the output of one command into the input of another.

As it turns out, Kakoune lets us integrate this same concept into our text
editing with the `|`

key. In short, the content of our selections is piped to
the standard input of the command of our choice; the output replaces the
original text.

This capability has an important consequence: in Kakoune, you can *always*
express a certain text transformation, because even if you can’t (or don’t want
to) do it with Kakoune keys, you can do it with an external program.

I often select a paragraph and reflow it with `|fmt<ret>`

, or sort a list of
numbers with `|sort -n<ret>`

. You could also type some Python code, select it,
and “evaluate” it inline with `|python<ret>`

:

`4w 2W _ y o<ret>print()<esc> hP x|python<ret>`

But piping is most interesting when you write your own programs to augment some
aspect of your workflow. For example, I often find myself needing to “increment”
numbers and letters, so I wrote a small Python program and added it to my
`$PATH`

as `char-inc`

:

``` python
#!/usr/bin/env python3
import sys

# helper to safely get i-th arg from argv
def arg(i, default=1):
    return int(sys.argv[i]) if i < len(sys.argv) else default

# read the input text we'll operate on from stdin
inp = sys.stdin.read()
# first arg is count
count = arg(1)
# second arg is direction: 1 for up, -1 for down
fac = arg(2)
# clamp the count
if count <= 0:
    count = 1
# list of chars
out = []
for c in str(inp):
    # only increment alphanumerics
    if c.isalnum():
        out.append(chr(ord(c) + count * fac))
    else:
        # unchanged
        out.append(c)
print("".join(out), end="")
bash
$ echo '1. a.' | char-inc
2. b.
$ echo '3. c.' | char-inc 2 -1 # optional <count> and <direction>
1. a.
```

Now I can refine my selection with regular Kakoune keys, then use my custom program to easily increment the selections. Here’s an example of me updating an ordered list:

`xy %<a-s> gh F: |char-inc<ret> ggP bcqud<esc>`

[command expansions](#command-expansions)

Kakoune can *expand* certain constructs when evaluating commands. The results of
these expansions are treated as normal strings, so they can be passed as
parameters to other commands, substituted into longer strings, etc. This is
similar to parameter expansion in the shell, but it works a little differently,
because there are multiple *kinds* of expansions. For example, `val`

gives
access to read-only internal data, `reg`

lets us read
[registers](#registers-marks), and `opt`

can be used to read *options*, which
are (mostly) like variables. There are actually 7 expansion kinds in total…
though I won’t list them all here, you can read more about them in
`:doc expansions`

. Of all the expansion kinds, `sh`

(shell expansions) are by
far the most powerful. We discuss them in a [later section](#shell-expansions).

Expansions can *only* be done using the `%`

-quotes
[described earlier](#evaluating-commands). The *kind* goes after the `%`

sign,
and the `name`

goes between the delimiters: `%kind{name}`

. We can use the `echo`

command to experiment with them, since it just prints its arguments to the
status line. For example, we can see the current session ID with
`echo %val{session}`

, the content of register `a`

with `echo %reg[a]`

, the
currently detected filetype with `echo %opt|filetype|`

, and so on.

[writing plugins](#writing-plugins)

Besides being a joy to use interactively, Kakoune makes it easy to automate & script its behavior with macros, custom commands, hooks, key mapping, and more. Let’s learn about it by writing a simple plugin: an auto-formatter for Rust code.

Even a task that doesn’t *seem* like it, is just a task of automating actions
you can otherwise take interactively. For example, we can directly format a Rust
file with just a few keys: first, `%`

to select the entire buffer, then `|`

to
bring up a prompt for a shell command, typing `rustfmt`

and pressing enter.

This pipes the entire buffer into `rustfmt`

and replaces it with the output. In
Kakoune key notation, the action is: `%|rustfmt<ret>`

. This would get annoying
to do each time, though, so let’s use the `map`

command to bind this to the
equal key. We can add it to our `kakrc`

, which runs this command on session
startup:

```
# bind to the equal key in normal mode,
# across whole session (global scope)
map global normal '=' '%|rustfmt<ret>'
```

So we’ve finished our first “level” of automation, which is binding a sequence
of keys (`%|rustfmt<ret>`

) to a single key (`=`

). Think of this binding as a
direct translation of keystrokes. However, this feels like a waste of a key; it
won’t be helpful in other filetypes, after all! Instead, we can *define a custom
command*.

How about we create a command called `rustfmt`

which executes this key sequence
for us? There’s a difference in how `map`

and `define-command`

work. Where `map`

translates a key into a sequence of keystrokes, `define-command`

maps a *command
name* to some *Kakscript*.

Because it expects commands and not keys, this won’t work:

```
define-command rustfmt %|rustfmt<ret>
Error: parse error: 1:26: unterminated string '%|...|'
```

Instead, we need to run these keystrokes from a command context. The
`execute-keys`

command, which we learned about earlier, is perfect for the job.

```
execute-keys -draft '%|rustfmt<ret>'
```

The `-draft`

switch runs it in a “disposable context”, meaning our current
selection state will be restored, and undo history won’t get clobbered. There’s
no need to use `-save-regs`

because the sequence isn’t doing any yanking. Nice!
Let’s put this command inside the body of `rustfmt`

:

```
define-command rustfmt %{
  execute-keys -draft '%|rustfmt<ret>'
}
```

OK, so we’ve got a custom command, and we can use it interactively with
`:rustfmt<ret>`

. We could also bind it to the same key as before, if we want:

```
map global normal '=' ':rustfmt<ret>'
```

That’s nice, but this isn’t really auto-formatting, is it? Can we get this to
happen automatically for Rust code? Yes, with the `hook`

command! This command
lets us register *other* commands that will be evaluated when a certain hook
triggers. Available hooks and their parameters are documented in `:doc hooks`

.

In our case, we can use the `BufWritePre`

hook. This example triggers right
before buffers whose filename ends with `.rs`

are written to the disk; when
there’s a match, the `rustfmt`

command is executed.

```
hook global BufWritePre .*\.rs rustfmt
```

Hopefully this gave you an idea of how Kakoune’s hook & macro system can be used to automate behavior or implement certain functionality through some pretty simple components!

[shell expansions](#shell-expansions)

There’s one more piece to the Kakoune puzzle: shell expansions. They allow us to
spawn subshells that control Kakoune over standard output. In other words, we
can write shell scripts that “expand” within Kakscript. `%sh{...}`

in Kakoune is
analogous to `"$(...)"`

in shell.

For example, the following two blocks are functionally equivalent; it’s just that one of them spawns a subshell to arrive at the same result:

```
echo "foobar!"
evaluate-commands %sh{
  printf 'echo "foobar!"\n'
}
```

Kakscript itself is very minimal and barely provides any programming
capabilities (there isn’t even an `if/else`

!), but the available commands are
capable enough when control flow and logic are handled by the shell, especially
since Kakoune’s state can be read from using `$kak_`

prefixed environment
variables.

For example, here’s a small plugin that provides a command called `dyn-theme`

,
which can dynamically set a color scheme based on the time of day. Users can
also set the `dyn_theme_enabled`

and `dyn_theme_default`

options to control its
behavior.

```
# whether theme should be dynamic
declare-option bool dyn_theme_enabled true
# default to use when dynamic is disabled
declare-option str dyn_theme_default "ashen"

define-command dyn-theme %{
  colorscheme %sh{
    if [ "$kak_opt_dyn_theme_enabled" = true ]; then
      hour="$(date +%H")
      # if before 6pm, use light theme
      if [ "$hour" -lt 18 ]; then
        echo "ashen-light"
      # otherwise, use dark theme
      else
        echo "ashen"
      fi
    else
      echo "$kak_opt_dyn_theme_default"
    fi
  }
}

# run it once automatically on startup
hook global KakBegin .* dyn-theme
```

If it’s currently 20:00, then the body of `dyn-theme`

expands to this:

```
colorscheme ashen
```

Setting the options before calling the command results in a different expansion:

```
set-option global dyn_theme_enabled false
set-option global dyn_theme_default gruvbox-dark

# body expands to 'colorscheme gruvbox-dark'
dyn-theme
```

Actually, there’s no requirement for the core logic to be written in shell. A
“shell script” can be as simple as calling *another* program, so really, plugins
can be written in any language. Although POSIX sh is most common because it’s
simple and portable, [Rust](https://github.com/kakoune-lsp/kakoune-lsp),
[Python](https://git.jdugan6240.dev/jdugan6240/dippin-dotfiles/src/commit/69355d72dc4bf50b0702cebf39768c687d5db68d/.config/kak/utils/indent/indent.py),
[C](https://github.com/eraserhd/kak-ansi) and
[Perl](https://github.com/occivink/kakoune-sort-selections) are some other
popular choices for Kakoune plugins.

Kakoune plugins are nothing more than configuration & commands that you share with others. As a consequence, you end up learning how to write plugins simply by customizing your own configuration. You’ll define lots of custom commands, use the hook system, and automate common actions with custom keybindings. These are all the same skills needed to write plugins.

Personally, I’ve published over a dozen individual Kakoune plugins, one of which
has even been
[merged into Kakoune itself](https://github.com/mawww/kakoune/blob/master/rc/windowing/niri.kak).
It’s also quite common for plugin authors to “publish” plugins simply as part of
their public dotfiles. I do this myself for plugins that don’t really warrant
their own repository.

Overall, the topic of shell expansions & plugin development is an entirely different can of worms. I haven’t even touched on scopes, highlighters, and FIFOs. Delving further into it is out of scope for this article. My aim here is to share how this extension model works, and why it’s a good choice for Kakoune.

[conclusion](#conclusion)

Thank you for reading all the way until the end. Becoming a Kakoune “power user” is a challenging but incredibly rewarding path. I hope I was able to make it just a little bit easier for newcomers and experienced users alike. Writing this took considerable time and effort, and it’s by no means exhaustive — there’s still much left to discuss. You can expect much more Kakoune content in future posts.

[acknowledgments](#acknowledgments)

I’m grateful to the Kakoune community for their support and advice. Thanks in
particular to [Screwtape](https://zork.net/~st/jottings/),
[possumvibes](https://possumvibes.com/), [raiguard](https://raiguard.me/),
[jdugan6240](https://jdugan6240.dev) and
[axlefublr](https://axlefublr.github.io/) for contributing their time &
expertise to review my work.

[further reading](#further-reading)

[Why Kakoune - The quest for a better code editor](https://kakoune.org/why-kakoune/why-kakoune.html)[The Vim-Inspired Editor with a Linguistic Twist](https://cosine.blue/2019-09-06-kakoune.html)[Kakoune, a Punk Rock Text Editor](https://zork.net/~st/jottings/kakoune-a-punk-rock-text-editor.html)[Intro to Kakoune highlighters](https://zork.net/~st/jottings/Intro_to_Kakoune_highlighters.html)[Intro to Kakoune completions](https://zork.net/~st/jottings/Intro_to_Kakoune_completions.html)[Understanding Kakoune’s Command Language](https://gist.github.com/lobre/4838c6859ff7d89e43447f07432cba7d)[The Kakoune Philosophy](https://strongly-typed-thoughts.net/blog/kakoune-philosophy)

[automatically generating demo videos](#automatically-generating-demo-videos)

All demo videos were generated automatically using
[VHS](https://github.com/charmbracelet/vhs) and a shell script. It’ll get its
own blog post eventually; for now, you can see an example below.

## Click to read

First, I created a file `demo1/in`

, to which I wrote the input.

```
The quick brown fox jumped over the lazy dog.
```

Then, I created `demo1/tape`

, which tells VHS what keys to press once the input
buffer is open. In this case, the instructions are very straightforward.

```
Sleep 500ms
Type@250ms "wwwwwwwwww"
Sleep 1s
```

I complete the process by running `kak-vhs ex15`

, which creates a file
`ex15.webp`

. The script handles starting Kakoune with the correct configuration,
opening the input file, executing the tape, and cleaning up after itself.
`kak-vhs`

is available [here](https://codeberg.org/ficd/kak-vhs).
