2013-05-29 18:57:46 +02:00
|
|
|
Interfacing Kakoune with external programs
|
|
|
|
==========================================
|
|
|
|
|
|
|
|
In order to interact with the external world, Kakoune uses the shell, mainly
|
2018-08-25 14:11:10 +02:00
|
|
|
through the +%sh{ ... }+ string type, and its control socket.
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
Basic interaction
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
For synchronous operations, +%sh{ ... }+ blocks are easy to use, they behave
|
|
|
|
similarly to +$( ... )+ shell construct.
|
|
|
|
|
2018-08-25 14:11:10 +02:00
|
|
|
For example, one can echo the current time in Kakoune's status line using:
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
[source,bash]
|
|
|
|
----
|
|
|
|
:echo %sh{ date }
|
|
|
|
----
|
|
|
|
|
2013-10-10 19:59:12 +02:00
|
|
|
For asynchronous operations, the Kakoune Unix stream socket can be used. This
|
2018-08-25 14:11:10 +02:00
|
|
|
is the same socket that Kakoune clients connect to. It is available through the
|
2015-11-01 12:11:44 +01:00
|
|
|
+kak_session+ environment variable: the socket is
|
|
|
|
+/tmp/kakoune/${username}/${kak_session}+
|
2013-05-29 18:57:46 +02:00
|
|
|
|
2013-10-10 19:59:12 +02:00
|
|
|
For example, we can echo a message in Kakoune in 10 seconds with:
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
[source,bash]
|
|
|
|
----
|
2018-08-25 14:11:10 +02:00
|
|
|
:nop %sh{ {
|
2013-05-29 18:57:46 +02:00
|
|
|
sleep 10
|
|
|
|
echo "eval -client '$kak_client' 'echo sleep ended'" |
|
2014-03-02 03:08:17 +01:00
|
|
|
kak -p ${kak_session}
|
2018-08-25 14:11:10 +02:00
|
|
|
} > /dev/null 2>&1 < /dev/null & }
|
2013-05-29 18:57:46 +02:00
|
|
|
----
|
|
|
|
|
|
|
|
* The +nop+ command is used so that any eventual output from the
|
2013-10-10 19:59:12 +02:00
|
|
|
+%sh{ ... }+ is not interpreted by Kakoune
|
|
|
|
* When writing to the socket, Kakoune has no way to guess in which
|
2013-05-29 18:57:46 +02:00
|
|
|
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
|
2018-08-25 14:11:10 +02:00
|
|
|
its +-client+ option to send commands to a specific client.
|
2013-10-10 19:59:12 +02:00
|
|
|
* For the command to run asynchronously, we wrap it in a sub shell
|
2018-08-25 14:11:10 +02:00
|
|
|
with braces, redirect its +std{in,err,out}+ to +/dev/null+, and
|
2013-05-29 18:57:46 +02:00
|
|
|
run it in background with +&+. Using this pattern, the shell does
|
2013-10-10 19:59:12 +02:00
|
|
|
not wait for this sub shell to finish before quitting.
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
Interactive output
|
|
|
|
------------------
|
|
|
|
|
2017-04-21 12:32:47 +02:00
|
|
|
It is a frequent interaction mode to run a program and display its output
|
2013-10-10 19:59:12 +02:00
|
|
|
in a Kakoune buffer.
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
The common pattern to do that is to use a fifo buffer:
|
|
|
|
|
|
|
|
[source,bash]
|
|
|
|
-----
|
2018-05-06 23:29:52 +02:00
|
|
|
evaluate-commands %sh{
|
2013-05-29 18:57:46 +02:00
|
|
|
# Create a temporary fifo for communication
|
|
|
|
output=$(mktemp -d -t kak-temp-XXXXXXXX)/fifo
|
|
|
|
mkfifo ${output}
|
|
|
|
# run command detached from the shell
|
2018-08-25 14:11:10 +02:00
|
|
|
{ run command here > ${output} } > /dev/null 2>&1 < /dev/null &
|
2013-10-10 19:59:12 +02:00
|
|
|
# Open the file in Kakoune and add a hook to remove the fifo
|
2018-02-28 14:10:03 +01:00
|
|
|
echo "edit! -fifo ${output} *buffer-name*
|
|
|
|
hook buffer BufClose .* %{ nop %sh{ rm -r $(dirname ${output})} }"
|
2013-05-29 18:57:46 +02:00
|
|
|
}
|
|
|
|
-----
|
|
|
|
|
2013-10-10 19:59:12 +02:00
|
|
|
This is a very simple example, most of the time, the echo command will as
|
2018-08-25 14:11:10 +02:00
|
|
|
well contain
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
-----
|
2013-10-30 10:38:40 +01:00
|
|
|
set buffer filetype <...>
|
2013-05-29 18:57:46 +02:00
|
|
|
-----
|
|
|
|
|
|
|
|
and some hooks for this filetype will have been written
|
|
|
|
|
|
|
|
Completion candidates
|
|
|
|
---------------------
|
|
|
|
|
2015-11-01 12:11:44 +01:00
|
|
|
Filetype specific completion should be provided by external programs.
|
2013-05-29 18:57:46 +02:00
|
|
|
|
2017-04-21 12:32:47 +02:00
|
|
|
External completions are provided using an option to store completion, which
|
2013-05-29 18:57:46 +02:00
|
|
|
have the following format.
|
|
|
|
|
|
|
|
----
|
2018-08-25 14:11:10 +02:00
|
|
|
line.column[+len]@timestamp candidate1|desc1|menu1 candidate2|desc2|menu2 ...
|
2013-05-29 18:57:46 +02:00
|
|
|
----
|
|
|
|
|
2017-04-21 12:32:47 +02:00
|
|
|
the first element of this string list specify where and when this completion
|
2016-08-17 20:52:53 +02:00
|
|
|
applies, the others are a triplet `<completion text>|<docstring>|<menu text>`
|
|
|
|
|
2017-04-21 12:32:47 +02:00
|
|
|
The menu text is a markup string, so it can contain `{face}` directives.
|
2015-05-07 14:42:58 +02:00
|
|
|
|
2017-04-21 12:32:47 +02:00
|
|
|
To effectively use that completion option, it should get added to the completers
|
2015-05-07 14:42:58 +02:00
|
|
|
option.
|
|
|
|
|
|
|
|
---
|
2018-08-25 14:11:10 +02:00
|
|
|
set -add buffer completers option=my_option_name
|
2015-05-07 14:42:58 +02:00
|
|
|
---
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
As a completion program may take some time to compute the candidates, it should
|
2013-10-10 19:59:12 +02:00
|
|
|
run asynchronously. In order to do that, the following pattern may be used:
|
2013-05-29 18:57:46 +02:00
|
|
|
|
|
|
|
[source,bash]
|
|
|
|
-----
|
|
|
|
# Declare the option which will store the temporary filename
|
|
|
|
decl str plugin_filename
|
2015-05-07 14:42:58 +02:00
|
|
|
# Declare the completion option
|
2018-08-25 14:11:10 +02:00
|
|
|
decl completions plugin_completions
|
2015-05-07 14:42:58 +02:00
|
|
|
# Add plugin_completions to completers for files of good filetype
|
|
|
|
hook global BufSetOption filetype=my_filetype %{
|
2018-08-25 14:11:10 +02:00
|
|
|
set -add buffer completers option=plugin_completions
|
2015-05-07 14:42:58 +02:00
|
|
|
}
|
2018-05-06 23:29:52 +02:00
|
|
|
evaluate-commands %sh{
|
2013-10-10 19:59:12 +02:00
|
|
|
# ask Kakoune to write current buffer to temporary file
|
2013-05-29 18:57:46 +02:00
|
|
|
filename=$(mktemp -t kak-temp.XXXXXXXX)
|
2013-10-30 10:38:40 +01:00
|
|
|
echo "set buffer plugin_filename '$filename'
|
2013-05-29 18:57:46 +02:00
|
|
|
write '$filename'"
|
|
|
|
}
|
2018-08-25 14:11:10 +02:00
|
|
|
# End the %sh{} so that its output gets executed by Kakoune.
|
2013-05-29 18:57:46 +02:00
|
|
|
# Use a nop so that any eventual output of this %sh does not get interpreted.
|
2018-08-25 14:11:10 +02:00
|
|
|
nop %sh{ { # launch a detached shell
|
2013-05-29 18:57:46 +02:00
|
|
|
buffer="${kak_opt_plugin_filename}"
|
|
|
|
line="${kak_cursor_line}"
|
|
|
|
column="${kak_cursor_column}"
|
2018-08-25 14:11:10 +02:00
|
|
|
# run completer program and format the output in a list of completions
|
2013-05-29 18:57:46 +02:00
|
|
|
candidates=$(completer $buffer $line $column | completer_filter)
|
|
|
|
# remove temporary file
|
|
|
|
rm $buffer
|
|
|
|
# generate completion option value
|
2018-08-25 14:11:10 +02:00
|
|
|
completions="$line.$column@$kak_timestamp $candidates"
|
2013-10-10 19:59:12 +02:00
|
|
|
# write to Kakoune socket for the buffer that triggered the completion
|
2018-08-25 14:11:10 +02:00
|
|
|
echo "set buffer=${kak_bufname} plugin_completions $completions" |
|
2014-03-02 03:08:17 +01:00
|
|
|
kak -p ${kak_session}
|
2018-08-25 14:11:10 +02:00
|
|
|
} > /dev/null 2>&1 < /dev/null & }
|
2013-05-29 18:57:46 +02:00
|
|
|
-----
|