Editing git commit messages with a kakoune client

I’m trying to use kak as my default editor. Since it’s too slow to start-up otherwise, I’m running kak -s ses -d, and set EDITOR="kak -c ses". Startup time is ok like this, but, counter-intuitively (to me), leaving a client with :wq doesn’t appear to close the file.

I was a bit confused at first when I came back to the same selection in a file I was editing, but could see that part being useful usually. But for git commit I feel I need a different solution; how do people do this?

$ git commit
... write a commit message in kak, :wq, "hello world"
$ git checkout some-other-branch
$ git show
very important commit
$ git commit --amend
... opens up a buffer with "hello world" and asks me whether to load the
changed file from disk

I can answer that with “yes, always reload”, but that incurs a painful delay. (Also, where is this information stored? Is this “always reload (this file/all files) (in this session/always)”?

Are you using a recent build of Kakoune from git master? A bit over a month ago, PR#2782 landed, which should have made Kakoune start much more quickly.

Yes, when you tell Kakoune to open a file in a particular session, the session gets a new buffer for that file, and that’s independent of whether the buffer has a client attached to it or not. For example, if you said kak -c ses foo.txt bar.txt then there would be two new buffers, but only foo.txt would be visible in a client. There’s probably some set of heuristics that would let Kakoune decide whether a buffer should be kept or discarded when any given client was closed, but it would probably be pretty complex. The current logic is: “buffers are only discarded with :buffer-delete or when the session exits”, which — if annoying — is at least easy to explain.

As far as I know, “yes, always reload” is limited to a specific file in a specific session.

I’m not sure what hooks (if any) are fired when a client exits, and in which order. If you’re lucky, something like this might work:

hook global WinClose .*/COMMIT_EDITMSG %{
    # Delete the Git commit-message buffer when the client exits,
    # since it's effectively a temporary file.
    delete-buffer
}

If not, maybe some permutation might.

1 Like

I know it seems counter-intuitive that :q closes the window but doesn’t delete the buffer. But IMO that’s how it should work.
To re-iterate what @Screwtapello said, ‘:q’ doesn’t delete a buffer, but merely closes the client. In normal operation – starting with kak – the server is tied to the client, and exits when the client does. So, all the buffers in the session get discarded.
When running Kakoune’s server process as a separate daemon – kak -d -s sessname – the server will stay running whether or not a client is connected. This is usually done because you want to keep a persistent session to which clients can connect and disconnect.
That’s how I like to do it, so I can keep a file’s undo history when popping in and out of the editor (also to help speed things up when on Cygwin).

Using hooks, as @Screwtapello said, is a good idea to customize some logic on when buffers are deleted.

Anyway, this is what I’ve been using to help with persistent sessions.
EDITOR='kakd'
alias vi='$EDITOR'

~/bin/kakd

#!/bin/sh
# kakoune wrapper to connect to persistent headless session
# `command` is used to ignore any shell alias to `kak`
startkak(){
    command kak -clear
    command kak -d -s daemon
    command kak -c daemon "$@"
}
command kak -c daemon "$@" || startkak "$@"

And, to help with temporary files like git commits,
in my kakrc

define-command -docstring "Delete current buffer and quit"\
delete-buffer-quit %{
    evaluate-commands "delete-buffer; quit"
}
alias global dq delete-buffer-quit

define-command -docstring "Save current buffer, delete, and quit"\
write-delete-buffer-quit %{
    evaluate-commands "write; delete-buffer; quit"
}
alias global dwq write-delete-buffer-quit
4 Likes

Thanks both for you help.

I’ve compiled kakoune from git now; it does seem it might be fast enough starting up now for me to use it like this – I’ll try it out for a bit and might get back to it.

(I have the slight impression that I’m losing some ESC keyboard inputs since updating, but chances are that’s my awful keyboard degenerating further.)

That dwq binding looks promising, too. I suppose I could even bind it to wq if that’s what I really want.

On the off-chance that this is useful to anyone, here’s a definition to build kakoune on guix from git:

Oh, and you might already know this, but :kill can also be useful, since it terminates the server process and all attached clients.

@Screwtapello I can’t seem to get those hooks working.
I tried:

hook global WinClose '.*/COMMIT_EDITMSG$' %{ delete-buffer }
hook global ClientClose '.*/COMMIT_EDITMSG$' %{ delete-buffer }

Do you know of a way to do this?

I don’t know off the top of my head exactly how to get those hooks working, but I do know that hooks can be a bit surprising, especially around things like buffers and windows being created or destroyed — sometimes things happen in an order you might not expect, or commands are executed in a context that doesn’t have access to some seemingly-relevant piece of information. If you want to know more about how hooks execute, I recommend picking out all the likely-sounding hook names from the documentation, adding a hook for each one that uses echo -debug to log the hook name to the *debug* buffer, then doing the thing you’re interested in, and checking the *debug* buffer to see which hooks were actually triggered, in what order.

About those specific hooks you pasted, the hook pattern needs to match the entire event string as though it were wrapped in ^ and $, so you don’t need to add your own $. Also, the event string for the ClientClose hook is the client name, not the name of the client’s active buffer. It might work with something like:

hook global ClientClose .* %{
    execute-commands %sh{
        case "$kak_buffile" of
            */COMMIT_EDITMSG)
                echo "delete-buffer"
                ;;
        esac
    }
}

However, if ClientClose is designed to execute after the client closes, buffile won’t be available and this example won’t work.

Thanks!
%val(buffile) is still available in the ClientClose hook, so that works.

However, using that hook, resulted in a segfault crash, in some situations.
Filed a bug report