Autoedit

It is a quite common feature for code editor to help the programmer by
automatically inserting some text in certain contexts. This document
goal is to explain how this is done in Kakoune.

There is no special support in Kakoune for this problem, hooks are
expected to be used in order to manage that, and the normal kakoune
editing command are expected to be expressive enough so that relatively
complex indentation can be written concisely.

The main hook is InsertChar, which gets called immediately after a
character has been inserted in insertion mode due to the user pressing
the corresponding key.

Previous line indentation

Let’s see a simple indent hook: preserving the previous line
indentation.

Here is the Kakoune normal mode key list in order to do that:

k<a-x>      # 1. go to previous line and select it
s^\h+<ret>y # 2. select the leading spaces and copy them
j<a-h>P     # 3. go back to next line start and paste the spaces

note that if nothing gets selected on phase 2., an error will be
raised.

We want to do that each time the user jumps a line, just after the new
line is inserted. So the hook would be:

:hook InsertChar \n %{ exec k<a-x> s^\h+<ret>y j<a-h>P }

(exec concatenates the keys for all argument, so the spaces will be
ignored, allowing for clearer separation. either use <space> or
quote the argument to use a space key)

That works, however if the phase 2. raises an error, the :exec
will stop and the user will get its selections on the previous line. The
solution is to use a draft context, instead of the one the user is
interacting with.

:hook InsertChar \n %{ exec -draft k<a-x> s^\h+<ret>y j<a-h>P }

That way, exec is executed in a copy of the user’s context, which
means it manipulates a copy of the user’s selections.

Increasing indentation

A little bit more complicated is to increase indentation whenever we
insert a new line after a { or a (.

The complexity arises from the presence of a condition. We want to
increase the indentation only if the previous line ends with { or
(.

Fortunately, Kakoune provides us with a command for that: the <a-k>
command, which keeps selections where a certain regex can be found.

Here is how we can do that:

k<a-x>             # 1. select the previous line
<a-k>[{(]\h*$<ret> # 2. keep selections that end with { or ( followed by blanks
j<a-gt>            # 3. go back to next line and indent it even if it is empty

Note that if no previous lines end with a { or (, the <a-k>
command will raise an error, and stop the execution. This is what we
want: it is similar to what would happen if we would continue with no
selections; the following commands would have no effects.

However, the error would end up being caught by the hook execution code,
and it will write informations about it in the debug buffer, which we do
not want, as this is actually expected. In order to prevent that, the
exec should be wrapped in a try command. So we would have:

:hook InsertChar \n %[ try %[ exec -draft k<a-x> <a-k>[{(]\h*$<ret> j<a-gt> ] ]
1 Like

Did a refactor of the asciidoc page and what do you think @robertmeta? and improvements?


Auto insertion in Kakoune

A common helpful feature of code editor’s is the automatic inserting of text in
certain contexts. This document explains how Kakoune provides this help.

The management for automatic insertion of text is through hooks. A concisely
written editing command is expressive so relatively complex indentations are
simplified.

While in insertion mode the hook InsertChar gets called immediately after
a character is inserted, due to the user pressing the corresponding key.

Previous line indentation

The example provided below is Kakoune’s normal mode key list for a simple indent
hook: preserving the previous line indentation.

k<a-x>      # 1. go to previous line and select it
s^\h+<ret>y # 2. select the leading spaces and copy them
j<a-h>P     # 3. go back to next line start and paste the spaces

Note 2. raises an error when no selection occurs on that phase.

When the user jumps a line, just after the new line is inserted the hook would be:

:hook InsertChar \n %{ exec k<a-x> s^\h+<ret>y j<a-h>P }

exec command concatenates the keys for all arguments which ignores spaces,
allowing for clearer separation. Either use <space> or ’quote the’ argument to
use a space key.

If phase 2. raises an error, the exec will stop and the user will get its
selections on the previous line. The solution is to use a draft context,
instead of the one the user is interacting with.

:hook InsertChar \n %{ exec -draft k<a-x> s^\h+<ret>y j<a-h>P }

That way, exec is executed in a copy of the user’s context, meaning it
manipulates a copy of the user’s selections.

Increasing indentation

A slightly harder example is increasing indentation whenever we insert a new
line after { or (.

The complexity arises from the presence of a condition. We want to increase
the indentation only if the previous line ends with { or (.

Fortunately, Kakoune provides us with a command for that: the <a-k> command
keeps the selections on the presence of a regular expression.

Here is how we can do that:

k<a-x>             # 1. select the previous line
<a-k>[{(]\h*$<ret> # 2. keep selections that end with { or ( followed by blanks
j<a-gt>            # 3. go back to next line and indent it even if it is empty

If no previous line ends with { or ( the <a-k> command will raise an error
and stop the execution which is what we want. It’s like what would happen if we
continued with no selections.

However, the error is caught by the hook execution code and will write
information to the debug buffer (this is expected), which we don’t want. The
exec should be wrapped in a try command for prevention. So we would have:

:hook InsertChar \n %[ try %[ exec -draft k<a-x> <a-k>[{(]\h*$<ret> j<a-gt> ] ]