summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2013-05-29 18:57:46 +0200
committerMaxime Coste <frrrwww@gmail.com>2013-05-29 18:57:46 +0200
commit0be8566dd7b40ebd8d463e948c920accde174679 (patch)
treeeda5b219ac8a272c8d070ea9c0a3266e653609b5
parent6cb8d69d29c77c01d92f8adb63e574f7f8d512a4 (diff)
Add interfacing.asciidoc describing how to interact with external programs
-rw-r--r--doc/interfacing.asciidoc123
1 files changed, 123 insertions, 0 deletions
diff --git a/doc/interfacing.asciidoc b/doc/interfacing.asciidoc
new file mode 100644
index 00000000..5971582e
--- /dev/null
+++ b/doc/interfacing.asciidoc
@@ -0,0 +1,123 @@
+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 asynchrounous operations, the kakoune unix stream socket can be used. This
+is the same socket that kakoune clients connect to. It is available in the
++kak_socket+ environment variable.
+
+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'" |
+ socat stdin UNIX-CONNECT:${kak_socket}
+) >& /dev/null < /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 asynchrounously, we wrap it in a subshell
+ 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 subshell 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 < /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 exemple, most of the time, the echo command will as
+well contains
+
+-----
+setb filetype <...>
+-----
+
+and some hooks for this filetype will have been written
+
+Completion candidates
+---------------------
+
+Most of the time, filetype specific completion should be provided by
+external programs.
+
+external completions are provided using the +completions+ option, which
+have the following format.
+
+----
+line:column[+len]@timestamp;candidate1;candidate2;...
+----
+
+the first element of this string list specify where and when this completions
+applies, the others are simply completion candidates.
+
+As a completion program may take some time to compute the candidates, it should
+run asynchrounously. 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
+%sh{
+ # ask kakoune to write current buffer to temporary file
+ filename=$(mktemp -t kak-temp.XXXXXXXX)
+ echo "setb 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 an put output in semicolon 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 "setb -buffer '${kak_bufname}' completions '$completions'" |
+ socat stdin UNIX-SOCKET:${kak_socket}
+) >& /dev/null < /dev/null & }
+-----