External program with regex highlighting

I would like to run an external program on a regex highlight to determine the face of the match but could not figure out how to do it. For instance if the file contains dates in a certain format, I would like to run an external command with date +%u $date to determine the day of the week and highlight each of them with different colors.

You should be able to do fine with shell expansion. For example:

:add-highlighter -override buffer/date-highlight regex %sh{date +%u date_value} 0:red

The override option is there so that you can invoke this command again to update which dates are highlighted. For multiple, different highlights you’d give them different name.

The date_value placeholder can either be manually written or you could define a kakoune option where you set it’s value, then you could access it with shell variable $kak_opt_optionName.

Sorry I did not explain clearly the problem I tried to solve. If a text file contains dates
in the following format:

2024-01-01
2024-01-02

I would like to match them with a regex and then run an external command on each of these matches.

date +%u -d 2024-01-01
date +%u -d 2024-01-02

to determine the day of the week from 1 (Monday) to 7 (Sunday) for each date. Then I would
like to use a different color for each day using a list defining 7 colors (red, green, yellow, …). This could be done with a shell script but I do not know how to interface the regex match in the highlighter with an external command in this way.

At a very high level, what you’ll need to do is:

  • create seven “faces” (see :set-face and :doc faces), one for each day of the week, to describe how a date representing that day should be displayed
  • create a range-specs option (see :declare-option and :doc options types), which will associate portions of the buffer with one of those faces
  • add a ranges highlighter (see :add-highlighter and :doc highlighters specs-highlighters) that tells Kakoune to interpret the payload of each item in the range-specs option as a face to be applied to the text in the given range
  • use a NormalIdle hook (or similar) to invoke your code after each time the buffer is modified
  • in that hook:
    • initialise the option to an empty value
    • select all the dates with a regex (probably using :execute-keys to do a %s... operation)
    • for each selection (see :evaluate-commands -itersel), add an item to the option (:set-option -add) that uses the range of the current selection (%val{selection_desc}) and runs the date command on the contents of the selection (%sh{ date +%u -d $kak_selection })

That’s kind of expensive, because it involves shelling out for every regex match. It might be possible to do something more efficient handling all of the dates at once, but it would also be more complex.

Excellent! Thank you very much.