Saving selection state across sessions

A while ago I created the state-save plugin which automatically saves the selection state as you edit a file and restores it the next time you open that file. I recently updated it and figured I should advertise it here!

  • no longer tries to save the state of scratch buffers, since they don’t have the same content from session to session and the saved state would be invalid anyway
  • added an option that lets you disable state-saving for certain files. Now git commit won’t start with your cursor at the end of the buffer, just because your previous commit had a lot of text in it!

I called this plugin “state-save” because I hoped one day I could extend it to save command-history and undo-history, like Vim’s viminfo file. However, at the time Kakoune only provided selection information.

A few months ago Kakoune implemented the features required to save and restore command-history, and now I’ve finally updated kakoune-state-save to support it!

Full instructions are in the repo, but basically, once you update to the latest version of the plugin, stick a snippet like this in your kakrc:

hook global KakBegin .* %{
    state-save-reg-sync colon
    state-save-reg-sync pipe
    state-save-reg-sync slash

hook global KakEnd .* %{
    state-save-reg-sync colon
    state-save-reg-sync pipe
    state-save-reg-sync slash

Now every time you start a Kakoune session, you’ll inherit the command-history (the colon register), pipe-command history (the pipe register) and search history (the slash register) from the previous Kakoune session. When you exit a session, that session’s history will be merged into the history on disk, ready for next time.

WARNING: Because of the way echo -to-file works, reading saved registers requires use of eval, so anyone who has write access to the state-save_path directory can trick your Kakoune into executing arbitrary code. If anybody knows a way to avoid this, I’d love to hear it.

Of course, you can save and restore registers other than the history-like ones, but Kakoune won’t automatically merge and de-duplicate register values. For example, here’s a way to copy/paste between Kakoune sessions:

hook global FocusOut .* %{ state-save-reg-save dquote }
hook global FocusIn  .* %{ state-save-reg-load dquote }

Nicely done, and nice documentation.

After some day using it, I like how it stays out of the way.

1 Like

… trick your Kakoune into executing arbitrary code

Is this any different from a hook to load a local.kakrc in projects? Or the user’s autoload folder?

Anyway, I guess it’s not as obvious that this plugin would do that, as the others. So thanks for the heads up.

Yeah, there’s lots of ways of getting Kakoune to execute arbitrary code, but I can imagine somebody protecting their autoload folder and forgetting about this plugin.

I just pushed a change to kakoune-state-save that removes the state-save-reg-sync command.

Certain Kakoune registers — the ones that represent prompt history, like colon for the normal command prompt — will merge new values into the existing register contents, instead of just replacing them. So if you run:

reg a foo
reg a bar
echo %reg{a}

…then you will see “bar”, but if you run:

reg colon foo
reg colon bar
echo %reg{colon}

…you will see “reg colon foo foo reg colon bar bar echo %reg{colon}” because it contains the commands you typed as well as the values you manually inserted with :reg colon.

So when I made kakoune-state-save, I created state-save-reg-sync that would load a history register from disk (automatically merging it into the current session’s history) and then immediately save the result back out. The idea was, if you had multiple Kakoune sessions open, one session’s history wouldn’t clobber the other.

Unfortunately, it turns out there’s an additional wrinkle to register merging - history registers are limited to 100 items, so if you load 100 items from disk, then whatever was previously in the register gets crowded out. As a result, instead of merging the current session’s history with the history on disk, state-save-reg-sync would replace the current session’s history. Once you’d written 100 commands, no newer command could ever be stored.

Instead of state-save-reg-sync, I now recommend using state-save-reg-load in a KakBegin hook, and state-save-reg-save in a KakEnd hook. This runs the risk of clobbering data if you have multiple sessions running at the same time, but better to risk losing some data than to guarantee losing all of it.

I also filed #3354 for a smarter way to merge history registers. If it gets fixed someday, I’ll add state-save-reg-sync back in.

1 Like