{"slug": "kakoune-is-a-text-editor", "title": "Kakoune Is a Text Editor", "summary": "Kakoune, a modal screen-based text editor created in 2011 by French programmer Maxime Coste, offers a unique inverted grammar that prioritizes multiple selections and multi-cursor editing, with a design focused on interactivity, orthogonality, and Unix philosophy. The editor's client-server architecture and extensible hook and macro system distinguish it from Vim, and it has inspired but diverged from the Helix editor. The article serves as a comprehensive learning resource for both new and existing users, emphasizing a gradual, long-term approach to mastering its features.", "body_md": "introduction\n\nWelcome!\n\nMy goal with this article is to help you develop a comprehensive understanding\nof the [Kakoune](https://kakoune.org) text editor: *what* it is, *how* it works, and *why* it has\nthat design.\n\nAlthough 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.\n\nThis 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.\n\nI hope this ends up being useful, both to new users and existing Kakoune\nenthusiasts. If you have any questions or comments about this post, please don’t\nhesitate to [contact me](/contact).\n\nThis article was written without AI assistance. If you are interested in a more\nhuman internet, please consider subscribing to my\n[RSS feed](https://ficd.sh/rss.xml) to be notified of future posts.\n\n[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)\n\n[What is kakoune?](#what-is-kakoune)\n\n[Kakoune](https://kakoune.org) (*French: [kakun]*) is a modal screen-based text editor created in\n2011 by French programmer Maxime Coste 1, better known as\n\n[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.\n\nA non-exhaustive overview of Kakoune’s most interesting features 2:\n\n**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\n**Unix philosophy**; Kakoune not only integrates with, but relies on standard Unix tools.\n\nnote on helix\n\n[Kakoune](https://kakoune.org) has served as a major inspiration for the\n[Helix](https://github.com/helix-editor/helix) editor. However, Helix has\ndiverged on some key design points, and the two cannot be discussed\ninterchangeably. Thus, Helix is outside the scope of this discussion. With that\nsaid, although much of this article won’t apply to Helix, some of it might, due\nto its shared DNA with Kakoune. Thus, Helix users may still find this discussion\nvaluable.\n\n[beyond vim](#beyond-vim)\n\nLet’s consider “Vim motions” as an editing language. This “core” doesn’t *need*\nto be surrounded by Vim; there are many editors (VS Code, JetBrains…) that\nsupport Vim motions. Thus, a user that learns Vim motions in one tool can\nquickly adapt to another tool that supports them.\n\nA side effect of this is that, because of Vim’s ubiquity, a lot of people have\ncome to view “Vim-like bindings” as ** the standard** for keyboard-driven,\nmodal editing. In other words, users expect editors to be mostly compatible with\ntheir existing “Vim binding muscle memory”. Some people won’t consider an editor\nthat expects them to change their approach.\n\nI think that’s a shame, because although I’m sure Vim is so popular for a\nreason, *we can certainly do better*. No design is immune to being iterated on\nand improved. Kakoune does this in an innovative way: by **inverting the\nsubject–verb order**.\n\n[What does subject : verb order even mean?](#what-does-subject-verb-order-even-mean)\n\nWhen people say they expect “Vim bindings”, what they really mean isn’t specific\nkeys… sure, we’re used to `hjkl`\n\nfor movement, but we can get used to\nsomething else just as fast. What matters most is Vim’s **modal text editing\ngrammar**.\n\nIt distinguishes between *insert mode* (regular typing) and *normal mode*: in\nwhich *keys* comprise *words* composed into *sentences* that describe text edits\nin the form *verb* followed by *subject*.\n\nThis structure closely mirrors English, so it feels natural for us to reason\nabout. For example, we might express “delete this word” as `dw`\n\n: `d`\n\nfor\n“delete” as the verb, and `w`\n\nfor “until the start of the next word” as the\nsubject.\n\nThere’s [no shortage](https://stackoverflow.com/a/1220118) of\n[excellent writing](https://takac.github.io/2013/01/30/vim-grammar/) on\n[Vim’s text editing language](https://learnvim.irian.to/basics/vim_grammar).\nHowever, let me briefly challenge the intuitiveness of verb : subject for text\nediting.\n\nHow 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?\n\nWell, the optimal (Vim-like) command would be `d<count>w`\n\n, where `<count>`\n\nis a\nnumber key representing how many words you want to delete. However, how do you\nknow that number? It’s trivial for 2-3 words, but how about something like 13?\nSure, you could literally *count* them, but that’s going to take forever and\ntake you out of the groove.\n\nAnother option is to guess. But sadly, your first (and only) visual indicator of\nhow far you went is *after* the edit has been applied. So if you were off by a\nfew words, you’ll have to undo and repeat the entire action with a different\ncount.\n\nThankfully, Vim has an easy solution to the problem: visual mode! Simply press\n`v`\n\n, and now you can use `w`\n\nand other motion keys to create a clearly visible\n*selection* (subject), after which you can press another key to execute the\n*action* (verb). By seeing exactly what you’re going to operate on *before*\ndoing the action, you can reduce misfires greatly.\n\nHowever, Vim’s visual mode isn’t the most ergonomic. There are different\nversions of it (linewise, blockwise, …), and it’s a fully separate mode in\nwhich key behaviors may differ in surprising ways. Furthermore, when you finally\npress an “operator” key, the editor usually returns to normal mode — so if you\nprefer visual editing, be prepared to press `v`\n\n*a lot*.\n\nVim’s visual mode *inverts* the verb order from its normal mode. Subject first,\n*then* verb. The selection *always* comes before the action; as a consequence,\nyou will *see* what you’re acting on before you act.\n\nA friend of mine put it like this: Vim’s inclusion of visual mode is a reluctant\nadmission that the verb-subject order isn’t good enough to cover all cases —\nbut instead of adjusting course and adopting a better grammar, Vim decided to\nbolt support for it on top of the existing model, ballooning complexity to high\nheaven.[3](#fn:jd)\n\n[kakoune’s inverted grammar](#kakounes-inverted-grammar)\n\nLuckily 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.\n\nImagine a normal mode that *is* visual mode. What if, instead of being relegated\nto a finicky sub-mode, visual selection-based editing was not only supported,\nbut had the entire editing experience optimized around it?\n\nWell, you don’t need to imagine it, because Kakoune *is* that subject-verb\nnative editor. For example, `w`\n\ndoesn’t just move the cursor forward to the next\nword, it moves the entire *selection area*. Thus, the meaning of `w`\n\nis “select\nthe next word”. Here’s how that looks:\n\nOf course, where this really shines is *incrementally updating your selection*.\nLet’s return to the previous example of selecting. In Kakoune, the “shift”\nmodifier changes the meaning of many commands: instead of *moving* the\nselection, it will *extend it*. So we can use `W`\n\nto increase our selection, one\nword at a time.\n\nFurthermore, most Kakoune keys support counts too, so you can press `<num>W`\n\nif\nyou know the number already. And here’s the best part: if we overshoot, we don’t\nhave to undo and try again. We can just press `B`\n\nas many times as we need to\ndecrease our selection. Once we’re happy with our selection, we can press any\nkey that *operates* (i.e. a verb), such as `d`\n\nfor “delete”.\n\nwell, actually\n\nIt’s not so much “the selection comes before” as it is “**there’s always a\nselection**”. A key that operates on a selection will *always* work, because\neven your “cursor” is just a single-character selection.\n\nAlthough editing in Kakoune can feel like “make the selection, than do the\naction”, there is no actual requirement to follow up a selection key with an\naction key. You can spend as much time as you want perfecting your selection\nbefore finally pressing a key like `d`\n\nor `y`\n\nto operate on it.\n\nAn 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).\n\n[orthogonality and atomic edits](#orthogonality-and-atomic-edits)\n\nThere’s another interesting consequence of Kakoune’s grammar inversion: **it\ncompletely eliminates the need for operator-pending mode**. Basically, it’s not\nfully accurate to say that `d2w`\n\nis a normal mode command in Vim. The reason:\n`d`\n\non its own can’t actually do anything. You haven’t yet specified *what* to\ndelete!\n\nSo Vim enters a mode called “operator pending”, in which it waits for your\nfollow up: *the subject*! Once you’ve input `2w`\n\n, Vim can complete the sentence\n`d2w`\n\nand execute the edit.\n\nIn Kakoune, the operator (verb) always knows what to operate on: the selections,\nat least one of which **must always exist**. This makes the behavior of normal\nmode keys *atomic*. `d`\n\nis defined as “delete the content of the selection”, and\ngiven the same buffer state (text & selections), it will always behave the same\nway.\n\nThis 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:\n\n- Keys that\n*change what is selected*(move the selection, expand it, shrink it, etc.) - Keys that\n*operate on what is selected*(delete, copy, duplicate, replace, etc.)\n\nThis means edits aren’t grouped into “sentences” like `d2w`\n\nor `cib`\n\n. Since\nthey’re incremental, not only do you get visual feedback for each step, but\n“logical edits” can also be *partially* undone with `u`\n\n.\n\n[A deeper dive into keys & commands](#a-deeper-dive-into-keys-commands)\n\nKakoune has two ways to receive instructions:\n\n- executing keys\n- evaluating commands\n\n[executing keys](#executing-keys)\n\nKeystrokes are simple enough. Not simple as in trivial to learn — there’s a\nlot of them,\n[listed here](https://github.com/mawww/kakoune/blob/master/doc/pages/keys.asciidoc)\n— but simple to express in writing: we simply string keys together. Thus, the\nsequence `hhW`\n\nrepresents pressing `h`\n\ntwice, then `W`\n\n(Shift + `w`\n\n).\n\nFor some keys, we can use a special notation if needed:\n\n- Modifier keys:\n`<a-X>`\n\n:`Alt + X`\n\n`<c-X>`\n\n:`Control + X`\n\n`<c-a-X>`\n\n:`Control + Alt + X`\n\n- etc…\n\n- Special keys:\n`<lt>, <gt>`\n\n: escaped`<`\n\nand`>`\n\n`<esc>`\n\n: escape (as seen above)`<ret>`\n\n: enter`<tab>`\n\n,`<space>`\n\n, etc…\n\nspaces\n\nCertain keys can be represented multiple ways. For example, you can write\n`<space>`\n\nexplicitly, or simply put an actual space in the sequence. In the key\nsequences written under the demo videos, I’m using `<space>`\n\nto denote literal\npresses of the space bar. You can ignore the actual whitespaces, those are just\nthere to make it easier to read and have no further meaning.\n\nWhy is this “key notation” important? Well, it’s **canonical**. Besides being a\nmore pleasant shorthand (`<a-x>`\n\nis easier to write than `Alt + x`\n\nall the\ntime), it gives us all a shared language to discuss and share Kakoune edits.\nAdditionally, Kakoune can actually parse these strings and execute them via the\n`execute-keys`\n\ncommand. Speaking of commands…\n\n[evaluating commands](#evaluating-commands)\n\nKakoune can evaluate a *string* as a set of *commands*. A sequence of such\ncommands is often called **Kakscript**, though it’s not officially a scripting\nlanguage. Much like a shell, its input is split into *words*, which are then\ninterpreted as commands with arguments. There’s shell-like quoting, and commands\ncan be separated by newlines or semicolons.\n\n```\n# comments BTW\nedit -existing src/main.c; set-option buffer indentwidth 4\nwrite src/main.c.bak\n```\n\nLike a shell ~~(wow! who would’ve guessed?)~~ script, Kakoune can even read &\nevaluate commands (Kakscript) from a file (`.kak`\n\nextension by convention).\nThere’s even a command *called* `evaluate-commads`\n\n(`eval`\n\nfor short) which, you\nguessed it, pretty much does the same thing as `eval`\n\nin the shell.\n\nIn case you’re unfamiliar, that means it takes its arguments as strings and\nevaluates them as commands. Like `execute-keys`\n\n, a number of helpful flags are\navailable, such as `-buffer`\n\n, which runs those commands in a different buffer\ncontext. It also allows for nested evaluation, which becomes useful when working\nwith expansions ([discussed later](#command-expansions)).\n\nkakscript quoting\n\nKakscript is mostly shell-like, since it has comments, word splitting, escaping\nnewlines with `\\`\n\n, and so on. However, it has some unorthodox quoting rules,\nbriefly described here.\n\nIn short, Kakoune has two kinds of quote delimiters: *balanced* and *matching*.\nThe usual single `'`\n\nand double `\"`\n\nquotation marks are *matching*:\n\n`\"...\"`\n\n`'...'`\n\nYou can also use a `%`\n\nprefix to pick your own delimiter, which can be **any**\nnon-alphabetic Unicode symbol. For example:\n\n`%|...|`\n\n`%~...~`\n\n`%ε...ε`\n\nAny matching delimiter can be doubled to escape it within the string.\n\nBalanced delimiters are also `%`\n\n-prefixed and follow slightly different rules.\nOnly the four kinds of ASCII brackets are supported:\n\n`%{...}`\n\n`%(...)`\n\n`%[...]`\n\n`%<...>`\n\nThere can’t be escaped, but balanced pairs are allowed to be nested:\n\n`%{{}}`\n\n: contains literal`{}`\n\n`%(())`\n\n: contains literal`()`\n\n`%[[]]`\n\n: contains literal`[]`\n\n`%<<>>`\n\n: contains literal`<>`\n\nAlthough these rules may seem like arbitrary complexity on the surface, they become really helpful when writing scripts with many nested layers of string evaluation.\n\nAs we’ll discover later, Kakscript often embeds POSIX shell, which also uses `'`\n\nand `\"`\n\nfor quoting. Using Kakoune’s unique quoting forms lets us write shell\nscripts without needing to constantly worry about escaping delimiters.\n\nTo read about Kakscript in more detail, see the\n[manual](https://github.com/mawww/kakoune/blob/master/doc/pages/command-parsing.asciidoc).\nFor a more in-depth discussion on its semantics, see my\n[devlog](/blog/kaklang-devlog-1) on writing a Kakscript parser.\n\n[commands are keys](#commands-are-keys)\n\nOf course, commands can be written and evaluated on the fly: pressing `:`\n\nopens\na prompt into which you can type a command and press `<ret>`\n\nto evaluate it. The\nsame rules apply: comments, quoting, word-splitting all work the same.\n\nWhich leads us to an interesting observation…\n\nI argue that commands are just a subset of keys, because any valid command can\nbe typed at the `:`\n\nprompt. Thus, the command `write src/main.c`\n\nis also the key\nsequence `:write src/main.c<ret>`\n\n. These are just two notations to express the\nsame thing.\n\nIn fact, there’s nothing special about even `:`\n\n. It’s just a key whose job is to\nbring up the prompt, wait for input until `<ret>`\n\nis pressed, then evaluate it.\nThe same if true of *any* key that uses a prompt, such as `/`\n\n(search) and\n`<a-k>`\n\n(filter).\n\nSo, why does this matter? It means that commands can be evaluated in *any*\ncontext where keys are valid, because there always exists some sequence of keys\nto produce any command. This is true even in insert mode, wherein `<a-;>`\n\ncan be\nused to escape to normal mode for one command: `<a-;>:write src/main.c<ret>`\n\nLet’s illustrate why the “commands are keys” principle is so important with an\nexample. Consider the common case of binding a key to some action. In Kakoune,\nwe can use the `map`\n\ncommand to ~~map~~ a key to a sequence. For example, let’s\nmake the tab key select the current line, but with whitespace trimmed:\n\n```\nmap global normal <tab> x_\n```\n\nLet’s try a more common one: binding `<c-s>`\n\nsave the file. There’s a small\ndifference, though: there isn’t a key for saving, there’s only the `write`\n\ncommand. Thankfully, that’s no problem for us, because as we observed earlier,\nany valid command can be expressed as a key sequence. Thus, to complete the\nbinding:\n\n```\nmap global normal <c-s> ':w<ret>'\n```\n\nNow, let’s consider the inverse: *executing keys from a command context*. It\nturns out, this is possible too, thanks to the `execute-keys`\n\ncommand (`exec`\n\nfor short). It’s a bit like `map`\n\nin that it executes… well… you know, but\nthe execution happens immediately, and it also exposes some options for us to\ntweak. To understand how this can be useful, let’s create another binding like\nbefore. This time, we’ll make the `=`\n\nkey duplicate the current line:\n\n```\n# x to select the line, y to yank (copy), p to paste below\nmap global normal '=' 'xyp'\n```\n\nThis 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?\n\nThis is where `execute-keys`\n\nbecomes relevant. Beside just executing the key\nsequence we give it (just like `map`\n\n!), this command provides some options that\ncan really help us out.\n\nThe first of these is `-draft`\n\n, which executes keys in a “disposable context”.\nThis basically means that our selection state gets restored after the command\nruns. Consider this example, where the key sequence `glbcFOO<esc>`\n\nselects the\nlast word on the line and replaces it. You can see for yourself how running it\nwith without `-draft`\n\nresults in a different cursor position:\n\nWhile using `-draft`\n\nindeed keeps the original selection:\n\n`-save-regs`\n\ndoes something very similar, except for *registers*, which are like\nKakoune’s clipboards. (Yes, you can use multiple at once, and it’s as powerful\nas you’d expect!) Registers all have names, and it happens that the *default*\nregister (the one stuff gets copied to when you press `y`\n\n) is called `\"`\n\n(aka\n`dquote`\n\n). Thus, we can complete our improved line duplication command like so:\n\n```\n# duplicate the line, preserve register and selections\nexecute-keys -draft -save-regs '\"' 'xyp'\n```\n\nLike before, we just need to write the above command in “key notation” to successfully bind it:\n\n```\nmap global normal '=' ':execute-keys -draft -save-regs dquote xyp<ret>'\n```\n\nAs 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.\n\nisolated mappings\n\nKakoune’s key mappings are *isolated*. If a mapped key refers to itself (e.g.\n`map global normal x <i-p>x`\n\n), the right-hand side will always use the canonical\nmeaning of that key; recursion is never allowed.\n\nIsolation also means that any keys bound by `map`\n\nhave *no* effect on\n`execute-keys`\n\nor subsequent `map`\n\ncommands. (`execute-keys`\n\ndoes have a\n`-with-maps`\n\noption, but its use is generally discouraged).\n\nA 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.\n\nFor example, as a Colemak-DH user, I’ve mapped `mnei`\n\nto `hjkl`\n\nfor navigation.\nHowever, plugins are completely safe to use `hjkl`\n\ninternally; my custom\nmappings won’t interfere. Similarly, when we write our own plugins or custom\nhooks, we are expected to use the canonical keys as well.\n\n[multiple selections](#multiple-selections)\n\nAs I mentioned earlier, multiple selections are a core editing feature in\nKakoune. This is intuitively understood as multi-cursor editing; but since a\ncursor in Kakoune is just a selection of size 1, it’s important to think of it\nas multi-*selection* editing.\n\nCommands that we understand as acting on “the” selection/cursor actually act on\n**all** selections. It just happens that most of the time, we only have one\nselection. But if we have *multiple*, pressing `d`\n\n, for example, deletes *all of\nthem*:\n\nThe concept is simple, but incredibly powerful. Multi-selections don’t\nnecessarily empower us to make edits that are *impossible* in Vim-like\neditors… but they certainly make the whole experience a lot more ergonomic!\nHang tight, you’ll see why in a moment.\n\nSo, how does one create multiple selections in the first place? One way is the\n`s`\n\nkey, which lets us create them by matching a regex. In short, you first\nselect some area; then, you press `s`\n\n, type a regex, and press enter. You’ll end\nup with all non-overlapping matches inside the original selection as new\nselections.\n\n`gjx s\\d+<ret> s2<ret> r7`\n\nThe most common use case for this is the classic “search and replace”. Let’s try\nreplacing every occurrence of “foo” with “bar” in this example. We begin by\npressing `%`\n\n, which selects the entire buffer (think `ggVG`\n\nin Vim, or `<c-a>`\n\nin a “typical” IDE). We need to do this because keys like `s`\n\nalways operate *on\nthe selection*; you must be explicit about saying “please search the whole\nbuffer”.\n\nSo, pressing `s`\n\nbrings up a prompt, we type `foo`\n\n, and after hitting enter, we\nend up with each match selected. That’s the search part down, how about replace?\nWe do it the exact same way we’d replace a single selection: press `c`\n\n(change),\nwhich deletes the selection and enters insert mode. We can type our replacement,\n`bar`\n\n, and press escape to return to normal mode when we’re done.\n\n`% sfoo<ret> cbar<esc>`\n\nYou may be thinking, big whoop, isn’t this basically the same as\n`:s/foo/bar/g<ret>`\n\nin Vim? Yes, but with a big difference: you get visual\nfeedback for **every step** of the edit. Besides helping you catch regex\nmistakes earlier (you’ll clearly see if the wrong thing is selected), the main\nadvantage of Kakoune’s approach is that it lets you branch into a different\noption at almost any step.\n\nYou don’t *have* to replace the matches; you could copy them (`y`\n\n), make all\nletters uppercase (`<a-`>`\n\n), [pipe them to a program](#editing-with-pipes)\n(`|`\n\n)… You can actually do anything, because as we learned earlier, multiple\nselections aren’t anything special; most keys you press just have their effects\nrepeated across selections.\n\nThus, 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.\n\nHere’s me turning each sentence of a passage into a bullet point:\n\n`%<a-I>s a<ret><esc> I-<space> <lt>`\n\nAnd how about swapping the contents of these if/else blocks:\n\n`<a-I>{ <a-)>`\n\nfurther reading\n\nIf you’re interested in more advanced examples,\n[Dmitri Sabadie has an excellent write-up of solving some Vim puzzles in Kakoune.](https://strongly-typed-thoughts.net/blog/vim-kakoune-puzzles-2025)\n\n[registers & marks](#registers-marks)\n\nWe briefly mentioned *registers* in a previous section, but it’s worth\ninvestigating them a little deeper, since they’re a big part of editing with\nKakoune. In case you’re not familiar with the concept, you can think of\nregisters like Kakoune’s internal clipboard.\n\nWhen we copy some text with `y`\n\n, it gets put in the default register. `p`\n\npastes\ntext from that same default register. Like Vim, Kakoune lets us use the ASCII\nletter keys as *named registers*. Double quote `\"`\n\nis the “register key”, so to\ncopy some text into register `a`\n\n, we can press `\"ay`\n\n. To paste text from\nregister `x`\n\n, we type `\"xp`\n\n. This can be done with any key that uses a register.\n\nNote\n\nThis is also why `\"`\n\nor `<dquote>`\n\nis the name of the default yank register!\n\nYou can already imagine how having multiple named clipboards is useful, but\nregisters really shine when we make use of Kakoune’s *special registers*. You\ncan see them all listed in `:doc registers`\n\n, but let’s focus on the most useful\none: the *mark* register, called `^`\n\nor “caret”.\n\nOther editors, like Vim, let you “mark” positions in the buffer. You can then\n“return” to a marked position easily. This feature can be lifesaving when\nnavigating around a complicated codebase. Kakoune has marks too, and of course,\nthey can be used like bookmarks as well. But Kakoune marks have a key property\nthat make them incredibly powerful: **they don’t just mark positions, they save\nselection descriptions to registers**.\n\nIt’s a bit like creating a snapshot of your entire selection state and returning\nto it later. We use `Z`\n\n(uppercase) to save a mark, and `z`\n\n(lowercase) to\nrestore it. Named registers can be used too, like `\"aZ`\n\nand `\"az`\n\n. Consider the\nfollowing example, in which we’re about to do a search and replace but get\ndistracted by something else. Using marks, we’re able to follow distractions to\nour heart’s content, since we can return to the previous editing state easily:\n\n`<a-i>p sfoo<ret> Z /TODO<ret> x2J sbaz<ret> cbar<esc>, 2kxd zcoof<esc>`\n\nWe can also perform *combinations* on marks, which are accessible through\n`<a-z>`\n\nand `<a-Z>`\n\n. The most common is `a`\n\nfor append. `<a-z>a`\n\nselects the\nmark plus our current selection *without* changing the mark, while `<a-Z>a`\n\nappends the current selection *to* the mark.\n\nMark combinations are great for creating super precise selections\n*incrementally*, without needing to conjure up some crazy regex to select it all\nin one go. In this example, I use `<a-Z>a`\n\n(append to register) to select a\nbunch of words, then capitalize all of them with a single press of `~`\n\n:\n\n`wwZ jbb<a-Z>a 5w<a-Z>a 2j3b<a-Z>a glb<a-Z>a z~`\n\nWe can also use the “union” mark combination (`<a-z>u`\n\n) to select an area by\nseparately defining its start and end points. If we save a mark with `Z`\n\n, select\nsomething else, then press `<a-z>u`\n\n, the entire area between those two points is\nselected. This is super handy for selecting a large chunk of text that doesn’t\nall fit on the screen at once:\n\n`xZ 20jvt 20jvt 6j x <a-z>u <a-;> d vv`\n\n[iterative learning](#iterative-learning)\n\nDespite 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.\n\nThe *real* learning experience comes with the gradual process of *refinement* as\nyou discover *more efficient ways* to perform edits you already know. Kakoune is\nvery well suited to being “discovered” in this way because the two main kinds of\nkeys (change the selection, act on the selection) are simple to reason about.\nOnce you understand Kakoune’s conceptual framework, integrating new keys and\ntechniques into your workflow becomes a remarkably *iterative* process.\n\nLet’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”.\n\nMost 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.\n\nLearning 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:\n\n``` python\n# before\ndef foo(first, second):\n  pass\npython\n# after\ndef foo(second, first):\n  pass\n```\n\nLet’s review the keys you’d need to discover the most basic editing path:\n\nsome basic keys\n\n`x`\n\nselects the whole line:\n\n`s`\n\ncreates selections matching a regex:\n\n`<a-)>`\n\nrotates selections content (note the plural):\n\nIf you’ve learned the above keys, you can already form a simple game plan:\nselect as one selection per argument, then rotate. To execute this plan, you’d\npress `x`\n\nto select the line, select the parenthesis with `s\\(.+\\)<ret>`\n\n, then\n“reduce” your selection to just the arguments with `s\\w+<ret>`\n\n, followed by a\nfinal `<a-)>`\n\n.\n\n`x s\\(.+\\)<ret> s\\w+<ret> <a-)>`\n\nThat’s not exactly the most *optimal* way, but my point is, *you can do it\nknowing only three keys*. As you learn more keys, you can make edits like this\nmore convenient.\n\nFor example, you may replace the `s\\w+<ret>`\n\nwith the much simpler\n`S,<space><ret>`\n\n, which *splits* your selection on regex matches (in this case,\njust the literal `,`\n\n):\n\n`x s\\(.+\\)<ret> H<a-;>L S,<space> <a-)>`\n\nThe escaped parenthesis in that regex is kinda ugly, but luckily we can avoid\nthat too. By putting the cursor inside the parenthesis, we can select it all\nwith `<a-i>)`\n\n, thus simplifying the edit:\n\n`www <a-i>) S,<space><ret> <a-)>`\n\nBut what if we want to do this for multiple functions at once? Just select them,\nthen select the parenthesis and the inner words as *nested text objects*:\n\n`% <a-I>) :exec <space> -itersel <space> <a-I>w <a-)>`\n\nNote\n\nWe have to actually type part of this out using `:exec`\n\nbecause we need\n`-itersel`\n\n, which runs a key sequence once per selection. This is how we rotate\nthe words within each line, as opposed to globally!\n\nIn other words, you really don’t need to know that many keys to get started with\nKakoune. All the keys you learn can be composed together in ways that make\nsense, so you can learn new primitives at whatever pace suits you until you’ve\ngot them all. And in the meantime, if you really need something, `:doc keys`\n\nserves as an excellent resource.\n\n[a server-client editor](#a-server-client-editor)\n\nKakoune is designed with the Unix philosophy in mind: *do one thing, and do it\nwell*. Thus, its focus lies entirely with being a competent code editor;\nanything that *can* be outsourced to external tools *should* be.\n\nLike all modern editors, Kakoune can have multiple text buffers open for editing\nat once. However, *unlike* most of its peers, Kakoune does not implement any\nwindow management. A single client has no vertical or horizontal splits; you can\nonly view one buffer at a time.\n\nHowever, Kakoune *does* have a solution to this, and I think it’s pretty\nelegant. Notice how I said “client” earlier? That’s because Kakoune has a\n*server–client architecture*. When you first launch Kakoune, besides starting\nthe interface you see, it also spins up a session with a unique ID… which can\nbe used to attach new clients to this session!\n\nNow, suppose we’ve started Kakoune with `kak -s foobar`\n\n, which just sets a\ncustom session ID. If we want to see & edit two buffers side–by–side, we can\nsimply attach a new client to the session with `kak -c foobar`\n\n, and change its\nactive buffer.\n\nThis 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.\n\nSpeaking of tmux, a strong parallel exists here. It could be helpful to think of\nKakoune like tmux, except it handles text buffers instead of terminal panes.\nTmux allows for detaching from a session, allowing it to exist in the background\nand be reattached to later, even if you’ve closed the terminal window. Kakoune\nalso supports this detach-attach workflow. The `daemonize-session`\n\ncommand\nprevents the session from terminating when the last client is closed, thus\nallowing your editing sessions to persist as long as the daemon isn’t killed.\n\nYou can also control Kakoune sessions from outside using `kak -p <name>`\n\n. This\ncommand reads commands from standard input and evaluates them in the context of\nthe session you named. For example, I can remotely kill the earlier session with\n`echo 'kill' | kak -p foobar`\n\n. This makes it easy to script Kakoune from\nanywhere on your system.\n\n[editing with pipes](#editing-with-pipes)\n\nAny Linux user will tell you that in a shell, the `|`\n\nkey is incredibly\npowerful. It can be used to compose commands together into *pipelines* by\n“piping” the output of one command into the input of another.\n\nAs it turns out, Kakoune lets us integrate this same concept into our text\nediting with the `|`\n\nkey. In short, the content of our selections is piped to\nthe standard input of the command of our choice; the output replaces the\noriginal text.\n\nThis capability has an important consequence: in Kakoune, you can *always*\nexpress a certain text transformation, because even if you can’t (or don’t want\nto) do it with Kakoune keys, you can do it with an external program.\n\nI often select a paragraph and reflow it with `|fmt<ret>`\n\n, or sort a list of\nnumbers with `|sort -n<ret>`\n\n. You could also type some Python code, select it,\nand “evaluate” it inline with `|python<ret>`\n\n:\n\n`4w 2W _ y o<ret>print()<esc> hP x|python<ret>`\n\nBut piping is most interesting when you write your own programs to augment some\naspect of your workflow. For example, I often find myself needing to “increment”\nnumbers and letters, so I wrote a small Python program and added it to my\n`$PATH`\n\nas `char-inc`\n\n:\n\n``` python\n#!/usr/bin/env python3\nimport sys\n\n# helper to safely get i-th arg from argv\ndef arg(i, default=1):\n    return int(sys.argv[i]) if i < len(sys.argv) else default\n\n# read the input text we'll operate on from stdin\ninp = sys.stdin.read()\n# first arg is count\ncount = arg(1)\n# second arg is direction: 1 for up, -1 for down\nfac = arg(2)\n# clamp the count\nif count <= 0:\n    count = 1\n# list of chars\nout = []\nfor c in str(inp):\n    # only increment alphanumerics\n    if c.isalnum():\n        out.append(chr(ord(c) + count * fac))\n    else:\n        # unchanged\n        out.append(c)\nprint(\"\".join(out), end=\"\")\nbash\n$ echo '1. a.' | char-inc\n2. b.\n$ echo '3. c.' | char-inc 2 -1 # optional <count> and <direction>\n1. a.\n```\n\nNow 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:\n\n`xy %<a-s> gh F: |char-inc<ret> ggP bcqud<esc>`\n\n[command expansions](#command-expansions)\n\nKakoune can *expand* certain constructs when evaluating commands. The results of\nthese expansions are treated as normal strings, so they can be passed as\nparameters to other commands, substituted into longer strings, etc. This is\nsimilar to parameter expansion in the shell, but it works a little differently,\nbecause there are multiple *kinds* of expansions. For example, `val`\n\ngives\naccess to read-only internal data, `reg`\n\nlets us read\n[registers](#registers-marks), and `opt`\n\ncan be used to read *options*, which\nare (mostly) like variables. There are actually 7 expansion kinds in total…\nthough I won’t list them all here, you can read more about them in\n`:doc expansions`\n\n. Of all the expansion kinds, `sh`\n\n(shell expansions) are by\nfar the most powerful. We discuss them in a [later section](#shell-expansions).\n\nExpansions can *only* be done using the `%`\n\n-quotes\n[described earlier](#evaluating-commands). The *kind* goes after the `%`\n\nsign,\nand the `name`\n\ngoes between the delimiters: `%kind{name}`\n\n. We can use the `echo`\n\ncommand to experiment with them, since it just prints its arguments to the\nstatus line. For example, we can see the current session ID with\n`echo %val{session}`\n\n, the content of register `a`\n\nwith `echo %reg[a]`\n\n, the\ncurrently detected filetype with `echo %opt|filetype|`\n\n, and so on.\n\n[writing plugins](#writing-plugins)\n\nBesides 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.\n\nEven a task that doesn’t *seem* like it, is just a task of automating actions\nyou can otherwise take interactively. For example, we can directly format a Rust\nfile with just a few keys: first, `%`\n\nto select the entire buffer, then `|`\n\nto\nbring up a prompt for a shell command, typing `rustfmt`\n\nand pressing enter.\n\nThis pipes the entire buffer into `rustfmt`\n\nand replaces it with the output. In\nKakoune key notation, the action is: `%|rustfmt<ret>`\n\n. This would get annoying\nto do each time, though, so let’s use the `map`\n\ncommand to bind this to the\nequal key. We can add it to our `kakrc`\n\n, which runs this command on session\nstartup:\n\n```\n# bind to the equal key in normal mode,\n# across whole session (global scope)\nmap global normal '=' '%|rustfmt<ret>'\n```\n\nSo we’ve finished our first “level” of automation, which is binding a sequence\nof keys (`%|rustfmt<ret>`\n\n) to a single key (`=`\n\n). Think of this binding as a\ndirect translation of keystrokes. However, this feels like a waste of a key; it\nwon’t be helpful in other filetypes, after all! Instead, we can *define a custom\ncommand*.\n\nHow about we create a command called `rustfmt`\n\nwhich executes this key sequence\nfor us? There’s a difference in how `map`\n\nand `define-command`\n\nwork. Where `map`\n\ntranslates a key into a sequence of keystrokes, `define-command`\n\nmaps a *command\nname* to some *Kakscript*.\n\nBecause it expects commands and not keys, this won’t work:\n\n```\ndefine-command rustfmt %|rustfmt<ret>\nError: parse error: 1:26: unterminated string '%|...|'\n```\n\nInstead, we need to run these keystrokes from a command context. The\n`execute-keys`\n\ncommand, which we learned about earlier, is perfect for the job.\n\n```\nexecute-keys -draft '%|rustfmt<ret>'\n```\n\nThe `-draft`\n\nswitch runs it in a “disposable context”, meaning our current\nselection state will be restored, and undo history won’t get clobbered. There’s\nno need to use `-save-regs`\n\nbecause the sequence isn’t doing any yanking. Nice!\nLet’s put this command inside the body of `rustfmt`\n\n:\n\n```\ndefine-command rustfmt %{\n  execute-keys -draft '%|rustfmt<ret>'\n}\n```\n\nOK, so we’ve got a custom command, and we can use it interactively with\n`:rustfmt<ret>`\n\n. We could also bind it to the same key as before, if we want:\n\n```\nmap global normal '=' ':rustfmt<ret>'\n```\n\nThat’s nice, but this isn’t really auto-formatting, is it? Can we get this to\nhappen automatically for Rust code? Yes, with the `hook`\n\ncommand! This command\nlets us register *other* commands that will be evaluated when a certain hook\ntriggers. Available hooks and their parameters are documented in `:doc hooks`\n\n.\n\nIn our case, we can use the `BufWritePre`\n\nhook. This example triggers right\nbefore buffers whose filename ends with `.rs`\n\nare written to the disk; when\nthere’s a match, the `rustfmt`\n\ncommand is executed.\n\n```\nhook global BufWritePre .*\\.rs rustfmt\n```\n\nHopefully 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!\n\n[shell expansions](#shell-expansions)\n\nThere’s one more piece to the Kakoune puzzle: shell expansions. They allow us to\nspawn subshells that control Kakoune over standard output. In other words, we\ncan write shell scripts that “expand” within Kakscript. `%sh{...}`\n\nin Kakoune is\nanalogous to `\"$(...)\"`\n\nin shell.\n\nFor example, the following two blocks are functionally equivalent; it’s just that one of them spawns a subshell to arrive at the same result:\n\n```\necho \"foobar!\"\nevaluate-commands %sh{\n  printf 'echo \"foobar!\"\\n'\n}\n```\n\nKakscript itself is very minimal and barely provides any programming\ncapabilities (there isn’t even an `if/else`\n\n!), but the available commands are\ncapable enough when control flow and logic are handled by the shell, especially\nsince Kakoune’s state can be read from using `$kak_`\n\nprefixed environment\nvariables.\n\nFor example, here’s a small plugin that provides a command called `dyn-theme`\n\n,\nwhich can dynamically set a color scheme based on the time of day. Users can\nalso set the `dyn_theme_enabled`\n\nand `dyn_theme_default`\n\noptions to control its\nbehavior.\n\n```\n# whether theme should be dynamic\ndeclare-option bool dyn_theme_enabled true\n# default to use when dynamic is disabled\ndeclare-option str dyn_theme_default \"ashen\"\n\ndefine-command dyn-theme %{\n  colorscheme %sh{\n    if [ \"$kak_opt_dyn_theme_enabled\" = true ]; then\n      hour=\"$(date +%H\")\n      # if before 6pm, use light theme\n      if [ \"$hour\" -lt 18 ]; then\n        echo \"ashen-light\"\n      # otherwise, use dark theme\n      else\n        echo \"ashen\"\n      fi\n    else\n      echo \"$kak_opt_dyn_theme_default\"\n    fi\n  }\n}\n\n# run it once automatically on startup\nhook global KakBegin .* dyn-theme\n```\n\nIf it’s currently 20:00, then the body of `dyn-theme`\n\nexpands to this:\n\n```\ncolorscheme ashen\n```\n\nSetting the options before calling the command results in a different expansion:\n\n```\nset-option global dyn_theme_enabled false\nset-option global dyn_theme_default gruvbox-dark\n\n# body expands to 'colorscheme gruvbox-dark'\ndyn-theme\n```\n\nActually, there’s no requirement for the core logic to be written in shell. A\n“shell script” can be as simple as calling *another* program, so really, plugins\ncan be written in any language. Although POSIX sh is most common because it’s\nsimple and portable, [Rust](https://github.com/kakoune-lsp/kakoune-lsp),\n[Python](https://git.jdugan6240.dev/jdugan6240/dippin-dotfiles/src/commit/69355d72dc4bf50b0702cebf39768c687d5db68d/.config/kak/utils/indent/indent.py),\n[C](https://github.com/eraserhd/kak-ansi) and\n[Perl](https://github.com/occivink/kakoune-sort-selections) are some other\npopular choices for Kakoune plugins.\n\nKakoune 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.\n\nPersonally, I’ve published over a dozen individual Kakoune plugins, one of which\nhas even been\n[merged into Kakoune itself](https://github.com/mawww/kakoune/blob/master/rc/windowing/niri.kak).\nIt’s also quite common for plugin authors to “publish” plugins simply as part of\ntheir public dotfiles. I do this myself for plugins that don’t really warrant\ntheir own repository.\n\nOverall, 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.\n\n[conclusion](#conclusion)\n\nThank 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.\n\n[acknowledgments](#acknowledgments)\n\nI’m grateful to the Kakoune community for their support and advice. Thanks in\nparticular to [Screwtape](https://zork.net/~st/jottings/),\n[possumvibes](https://possumvibes.com/), [raiguard](https://raiguard.me/),\n[jdugan6240](https://jdugan6240.dev) and\n[axlefublr](https://axlefublr.github.io/) for contributing their time &\nexpertise to review my work.\n\n[further reading](#further-reading)\n\n[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)\n\n[automatically generating demo videos](#automatically-generating-demo-videos)\n\nAll demo videos were generated automatically using\n[VHS](https://github.com/charmbracelet/vhs) and a shell script. It’ll get its\nown blog post eventually; for now, you can see an example below.\n\n## Click to read\n\nFirst, I created a file `demo1/in`\n\n, to which I wrote the input.\n\n```\nThe quick brown fox jumped over the lazy dog.\n```\n\nThen, I created `demo1/tape`\n\n, which tells VHS what keys to press once the input\nbuffer is open. In this case, the instructions are very straightforward.\n\n```\nSleep 500ms\nType@250ms \"wwwwwwwwww\"\nSleep 1s\n```\n\nI complete the process by running `kak-vhs ex15`\n\n, which creates a file\n`ex15.webp`\n\n. The script handles starting Kakoune with the correct configuration,\nopening the input file, executing the tape, and cleaning up after itself.\n`kak-vhs`\n\nis available [here](https://codeberg.org/ficd/kak-vhs).", "url": "https://wpnews.pro/news/kakoune-is-a-text-editor", "canonical_source": "https://ficd.sh/blog/kakoune-is-a-text-editor/", "published_at": "2026-08-18 18:52:02+00:00", "updated_at": "2026-08-18 19:11:17.685956+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Kakoune", "Maxime Coste", "Helix", "Vim", "VS Code", "JetBrains"], "alternates": {"html": "https://wpnews.pro/news/kakoune-is-a-text-editor", "markdown": "https://wpnews.pro/news/kakoune-is-a-text-editor.md", "text": "https://wpnews.pro/news/kakoune-is-a-text-editor.txt", "jsonld": "https://wpnews.pro/news/kakoune-is-a-text-editor.jsonld"}}