Pykak: Script Kakoune with Python. Over 10x lower latency than using %sh{}

When creating Kakoune plugins, I frequently felt guilty starting an entire process using %sh{} to accomplish a task as simple as multiplying two numbers. This is what inspired me to create pykak, a plugin that allows plugin authors to script Kakoune with Python. The implementation relies on IPC instead of %sh{} (besides the initial call to start the pykak server).

Here’s an example that sorts selections:

def sort-sels %{ python %{
    sels = sorted(valq('selections'))
    keval('reg dquote %s; exec R' % quote(sels))
}}

And here’s an example plugin: GitHub - tomKPZ/counted.kak: Alternative key counts for Kakoune

Please give it a try and let me know what you think. I’d love to get some feedback!

9 Likes

This is pretty amazing, congrats! I am also learning a lot by inspecting the way you implemented things.

One question: Can some your custom fifo logic be replaced by $kak_command_fifo and $kak_response_fifo introduced recently? I can’t say I understand your implementation well enough to say myself but I was curious to see no mention.

1 Like

Hey, you made kakoune-smooth-scroll! I used your socket code in pykak, thanks for the code!

Command/response fifos are only available during %sh evaluations and get cleaned up when %sh ends. Since the python command shouldn’t use %sh, we create our own fifos when the pykak server is started.

1 Like

Impressive. I love the use of (two!) fifos to keep communicating with kakoune synchronously and that arbitrary kakoune data can be queried inside it. I have tried to make something like this so many times and have never been satisfied. I was so inspired by your work that I had to make my own spin on it. I reimplemented it as a library that can be imported in python. This way kakoune commands can be defined using decorated python functions, like this:

import libpykak as k
@k.cmd
def sort_sels(): 
    '''Sort the selection contents.'''
    sels = sorted(k.valq('selections'))
    # Put the selections into the default paste register,
    # then execute `R` to replace the selections.
    k.keval('reg dquote %s; exec R' % k.quote(sels))

When k.cmd is run the connection is initialized using the environment variable kak_session. It can be explicitly initialized by first running k.KakConnection.init("my-session").

The python function can be reimplemented using this:

import libpykak as k 
@k.cmd
def python(*args):
    args = list(args)
    code = args.pop()
    exec(code, globals(), {'args': args})

I have added the other details of exposing the same api and caching the python code objects in my repo:

5 Likes

That looks really cool, I especially like the use of a decorator to define kak functions from python! With that change, I’d be able to write counted.kak in pure python.

Have you considered upstreaming your changes?