Kakoune: Ability to read and write from and to Kakoune internals

Created on 5 Apr 2019  路  6Comments  路  Source: mawww/kakoune

I had a very simple plug-in to preview colors in the buffer by coloring the background of #rrggbb text.

declare-option -hidden range-specs palette

define-command -hidden palette-update %{
  try %{
    evaluate-commands -draft -save-regs '/DV' %{
      execute-keys '<space>;gtGb'
      set-register / '(?:#|rgb:)([0-9A-Fa-f]{6})'
      execute-keys 's<ret>'
      set-option window palette %val(timestamp)
      evaluate-commands -no-hooks -draft -itersel %{
        # Save description
        set-register D %val(selection_desc)
        # Select value
        execute-keys '1s<ret>'
        # Save value
        set-register V %reg(.)
        set-option -add window palette "%reg(D)|default,rgb:%reg(V)"
      }
    }
  } catch %{
    unset-option window palette
  }
}

define-command palette-enable -docstring 'Enable color preview' %{
  add-highlighter window/palette ranges palette
  hook -group palette window NormalIdle '' palette-update
}

define-command palette-disable -docstring 'Disable color preview' %{
  remove-highlighter window/palette
  remove-hooks window palette
}

We select the view-port and iterate each selection matching #rrggbb or rgb:rrggbb to update the Palette option with the selection description and color value. The all with no shell call.

But yesterday @andreyorst asked and showed me a way to tweak the foreground of #rrggbb text so that the text never gets dimmed by the background.

evaluate-commands -no-hooks -draft -itersel %{
  set-register B %reg(.)
  set-register F %sh(printf '%s' "$kak_main_reg_dot" | tr '0123456789ABCDEFabcdef' 'fedcba9876543210543210')
  set-option -add window palette "%reg(D)|rgb:%reg(F),rgb:%reg(B)"
}

It works by inverting the color of the foreground with the background using the tr program. Which works great. But now a shell is paid for each selection.

So I was tempted to use @SolitudeSF鈥檚 approach to pass the whole buffer to an external program:

define-command colorcol-refresh %{
  evaluate-commands %sh{
    colorcol "$kak_timestamp" "$kak_buffile" "$kak_opt_colorcol_char"
  }
}

But by doing that we lose all the niceties that Kakoune provides for selecting and iterating a selection set. It鈥檚 quite a shame somehow, we can鈥檛 efficiently leverage Kakoune鈥檚 language once a shell is involved.

So what if edit and write could read and write to and from the Kakoune internals?

It reminded me the way [fibers] work on [Crystal]. So I was thinking on a similar feature.

Adapted to Kakoune, it could be:

  • palette-update: write and edit at L18-19
  • palette-enable: evaluate-commands shell block
  • palette-disable: nop shell block
declare-option -hidden range-specs palette_highlighter
declare-option -hidden str palette_messaging

define-command -hidden palette-update %{
  try %{
    evaluate-commands -draft -save-regs '/BDF' %{
      execute-keys '<space>;gtGb'
      set-register / '(?:#|rgb:)([0-9A-Fa-f]{6})'
      execute-keys 's<ret>'
      set-option window palette_highlighter %val(timestamp)
      evaluate-commands -no-hooks -draft -itersel %{
        # Save description
        set-register D %val(selection_desc)
        # Select value
        execute-keys '1s<ret>'
        # Save value
        set-register B %reg(.)
        write "%opt(palette_messaging)/invert-message" -from %val(main_reg_dot)
        edit "%opt(palette_messaging)/invert-response" -to reg F
        set-option -add window palette_highlighter "%reg(D)|rgb:%reg(F),rgb:%reg(B)"
      }
    }
  } catch %{
    unset-option window palette_highlighter
  }
}

define-command palette-enable -docstring 'Enable color preview' %{
  evaluate-commands %sh{
    messaging=$(mktemp -d)
    mkfifo "$messaging/invert-message" "$messaging/invert-response"
    printf 'set-option window palette_messaging %s' "$messaging"
    invert_listen() {
      while test $? = 0; do
        tr '0123456789ABCDEFabcdef' 'fedcba9876543210543210' < "$messaging/invert-message" > "$messaging/invert-response"
      done < /dev/null > /dev/null 2>&1 &
    }
    invert_listen
  }
  add-highlighter window/palette ranges palette
  hook -group palette window NormalIdle '' palette-update
}

define-command palette-disable -docstring 'Disable color preview' %{
  nop %sh{
    rm "$kak_opt_palette_messaging/invert-message" "$kak_opt_palette_messaging/invert-response"
    rmdir "$kak_opt_palette_messaging"
  }
  remove-highlighter window/palette
  remove-hooks window palette
}

Now the shell is invoked once, when enabling the Palette plug-in.

Most helpful comment

Well, we could make %file{...} expand its parameter to alleviate that problem. I think using an expansion makes sense as it means we can use it with existing commands (set-register, set-option, ...) instead of inventing special syntax for that, but making it convenient to use is important as well.

Is it difficult to use in the use case your presented ? Is the evaluate-commands trick not nice enoug ?

I was thinking we would likely fix most of the dynamic paths use by introducing a session tmp directory that could be used, so instead of having to mktmp directories, we could just use $kak_session_tmp_dir/my-well-known-path.

All 6 comments

I think this can be an interesting direction to make some common cases easier to write, however I am not really keen on overloading write/edit, I think I would be more comfortable with the following:

echo -to <filename> ... # same as echo but writes to the given filename instead of to the status line
set global my-option %read{filename} # the new `read` expansion expands to the content of given filename

We might need to make %read expand % strings inside its own parameter so that your example works (that would be %read{%opt{palette_messaging}/invert-response}) or require to use evaluate-commands set-register blah "%%read{%opt{palette_messaging}/invert-response}".

The nice thing is that this could solve the problem with too big env vars, we could just echo -to /tmp/kak-history "%val{history}" before entering the shell scope that would read it.

I find the expansion difficult to use with dynamic paths.

Well, we could make %file{...} expand its parameter to alleviate that problem. I think using an expansion makes sense as it means we can use it with existing commands (set-register, set-option, ...) instead of inventing special syntax for that, but making it convenient to use is important as well.

Is it difficult to use in the use case your presented ? Is the evaluate-commands trick not nice enoug ?

I was thinking we would likely fix most of the dynamic paths use by introducing a session tmp directory that could be used, so instead of having to mktmp directories, we could just use $kak_session_tmp_dir/my-well-known-path.

The syntax is cumbersome but it does the trick yep.

Otherwise I like the idea of a session temp directory.

I added a [comment] for @mawww on [Discuss].

Was this page helpful?
0 / 5 - 0 ratings

Related issues

valerdi picture valerdi  路  4Comments

alexherbo2 picture alexherbo2  路  3Comments

akkartik picture akkartik  路  3Comments

a12l picture a12l  路  3Comments

abitofalchemy picture abitofalchemy  路  3Comments