Interfacing Kakoune with external programs
==========================================

In order to interact with the external world, Kakoune uses the shell, mainly
through the +%sh{ ... }+ string type, and it's control socket.

Basic interaction
-----------------

For synchronous operations, +%sh{ ... }+ blocks are easy to use, they behave
similarly to +$( ... )+ shell construct.

For example, one can echo the current time in Kakoune status line using:

[source,bash]
----
:echo %sh{ date }
----

For asynchronous operations, the Kakoune Unix stream socket can be used. This
is the same socket that Kakoune clients connect to. It is available through
+kak_session+ environment variable: the socket is
+/tmp/kakoune/${username}/${kak_session}+

For example, we can echo a message in Kakoune in 10 seconds with:

[source,bash]
----
:nop %sh{ (
    sleep 10
    echo "eval -client '$kak_client' 'echo sleep ended'" |
        kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null & }
----

 * The +nop+ command is used so that any eventual output from the
   +%sh{ ... }+ is not interpreted by Kakoune
 * When writing to the socket, Kakoune has no way to guess in which
   client's context the command should be evaluated. A temporary
   context is used, which does not have any user interface, so if we want
   to interact with the user, we need to use the +eval+ command, with
   it's +-client+ option to send commands to a specific client.
 * For the command to run asynchronously, we wrap it in a sub shell
   with parenthesis, redirect it's +std{in,err,out}+ to +/dev/null+, and
   run it in background with +&+. Using this pattern, the shell does
   not wait for this sub shell to finish before quitting.

Interactive output
------------------

It is a frequent interaction mode to run a program and display it's output
in a Kakoune buffer.

The common pattern to do that is to use a fifo buffer:

[source,bash]
-----
%sh{
     # Create a temporary fifo for communication
     output=$(mktemp -d -t kak-temp-XXXXXXXX)/fifo
     mkfifo ${output}
     # run command detached from the shell
     ( run command here > ${output} ) > /dev/null 2>&1 < /dev/null &
     # Open the file in Kakoune and add a hook to remove the fifo
     echo "edit! -fifo %{output} *buffer-name*
           hook buffer BufClose .* %{ nop %sh{ rm -r $(dirname ${output}} }"
}
-----

This is a very simple example, most of the time, the echo command will as
well contains

-----
set buffer filetype <...>
-----

and some hooks for this filetype will have been written

Completion candidates
---------------------

Filetype specific completion should be provided by external programs.

external completions are provided using an option to store completion, which
have the following format.

----
line.column[+len]@timestamp:candidate1|desc1|menu1:candidate2|desc2|menu2:...
----

the first element of this string list specify where and when this completions
applies, the others are a triplet `<completion text>|<docstring>|<menu text>`

The menu text is a a markup string, so it can contain `{face}` directives.

to effectively use that completion option, it should get added to the completers
option.

---
set buffer completers "option=my_option_name:%opt{completers}"
---

As a completion program may take some time to compute the candidates, it should
run asynchronously. In order to do that, the following pattern may be used:

[source,bash]
-----
# Declare the option which will store the temporary filename
decl str plugin_filename
# Declare the completion option
decl str plugin_completions
# Add plugin_completions to completers for files of good filetype
hook global BufSetOption filetype=my_filetype %{
    set -add buff completers option=plugin_completions
}
%sh{
    # ask Kakoune to write current buffer to temporary file
    filename=$(mktemp -t kak-temp.XXXXXXXX)
    echo "set buffer plugin_filename '$filename'
          write '$filename'"
}
# End the %sh{} so that it's output gets executed by Kakoune.
# Use a nop so that any eventual output of this %sh does not get interpreted.
nop %sh{ ( # launch a detached shell
    buffer="${kak_opt_plugin_filename}"
    line="${kak_cursor_line}"
    column="${kak_cursor_column}"
    # run completer program and put output in colon separated format
    candidates=$(completer $buffer $line $column | completer_filter)
    # remove temporary file
    rm $buffer
    # generate completion option value
    completions="$line.$column@$kak_timestamp:$candidates"
    # write to Kakoune socket for the buffer that triggered the completion
    echo "set buffer=${kak_bufname} plugin_completions '$completions'" |
        kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null & }
-----