1### public:
2
3declare-option str swatch_face_body 'Aa'
4declare-option str swatch_face_pad ' '
5declare-option str swatch_hex_body '██'
6declare-option str swatch_hex_pad ' '
7
8### private:
9
10declare-option -hidden str-list swatch_strcmp_buffer
11define-command -params 0 -hidden swatch-assert-empty nop
12define-command -params 2 -hidden swatch-streq %{
13 set global swatch_strcmp_buffer %arg{1}
14 set -remove global swatch_strcmp_buffer %arg{2}
15 swatch-assert-empty %opt{swatch_strcmp_buffer}
16}
17
18declare-option -hidden str swatch_face_regex
19evaluate-commands -save-regs 'fdnrcpabioegtsz' %{
20 # Search components.
21 set-register f '(?:\w+)' # A face name.
22 set-register d "(?:(?:set-)?face\h+(?:buffer|global|local|window)\h+%reg:f:\h+)" # The declaration, bar the face itself.
23 set-register n '(?:(?:bright-)?(?:black|red|green|yellow|blue|magenta|cyan|white)|default)' # A named color.
24 set-register r '(?:rgb:[A-Fa-f0-9]{6}|rgba:[A-Fa-f0-9]{6}(?:1[A-Fa-f1-9]|[A-Fa-f2-9][A-Fa-f0-9]))' # A hexadecimal color. If alpha is given, it must be greater than 16.
25 set-register c "(?:%reg{n}|%reg{r})" # A named color or a hexadecimal color.
26 set-register p "(?:,%reg{c})" # A comma-prefixed named color or a hexadecimal color.
27
28 # A set of attributes. The inclusion of `U` can crash Kakoune prior to commit `a0a0009`, so we check `%val{version}`.
29 set-register a '(?:\+[abBcdfFgirsu]+)'
30 try %{
31 swatch-streq %val{version} 'v2025.06.03' # This will be made more robust once a new Kakoune version releases.
32 set-register a '(?:\+[abBcdfFgirsuU]+)'
33 }
34
35 set-register b "(?:@%reg{f})" # A base face.
36
37 # Assembled search components for `<fg>[,<bg>[,<underline>]]`.
38 set-register i "(?:%reg{c}%reg{p}{0,2})" # `<fg>[,<bg>[,<underline>]]`, where no face has been omitted. Note that this requires `<fg>`.
39 set-register o "(?:%reg{c},%reg{c},|%reg{c},,%reg{c}|%reg{c},,|,%reg{c},%reg{c}|,%reg{c},|,%reg{c}|,,%reg{c}|,,)" # `<fg>[,<bg>[,<underline>]]`, where at least one face has been omitted.
40 set-register e "(?:%reg{i}|%reg{o})" # `<fg>[,<bg>[,<underline>]]`.
41
42 # Search cases.
43 set-register g "(?:%reg{e}%reg{a}?%reg{b}?)" # The case in which `<fg>[,<bg>[,<underline>]]` is present.
44 set-register t "(?:%reg{a}%reg{b}?)" # The case in which `<fg>[,<bg>[,<underline>]]` is _not_ present, and `+<attr>` is, i.e., `+<attr>[@base]`.
45 set-register s "(?:%reg{f})" # The case in which `<fg>[,<bg>[,<underline>]]` _nor_ `+<attr>` is present, but `[base]` is, i.e., `[base]`.
46 set-register z "(?:%reg{g}|%reg{t}|%reg{s})" # Any of the prior three cases.
47
48 set-option global swatch_face_regex "%reg{d}(%reg{z})"
49}
50
51declare-option -hidden int swatch_timestamp 0
52declare-option -hidden range-specs swatch_range 0
53
54define-command -docstring 'enable swatch at window scope' swatch-enable-window %{
55 add-highlighter window/swatch replace-ranges swatch_range
56 hook -group swatch window NormalIdle '.*' swatch-highlight
57 hook -group swatch window InsertIdle '.*' swatch-highlight
58}
59
60define-command -docstring 'disable swatch at window scope' swatch-disable-window %{
61 remove-hooks window swatch
62 remove-highlighter window/swatch
63 unset-option window swatch_timestamp
64 unset-option window swatch_range
65}
66
67define-command -docstring 'generate a face & color highlighter for the active buffer' swatch-highlight %{
68 set-option window swatch_range %val{timestamp}
69
70 evaluate-commands -draft %{
71 execute-keys 'gtGbx'
72
73 # Handle forms `[<fg>][,<bg>[,<underline>]][+<attr>][@base]` and `[base]` in face declarations.
74 evaluate-commands -draft -verbatim try %{
75 execute-keys "s%opt{swatch_face_regex}<ret>;<a-b>"
76 evaluate-commands -itersel %{
77 set-option -add window swatch_range "%val{cursor_line}.%val{cursor_column}+0|{%reg{1}}%opt{swatch_face_body}{default,default}%opt{swatch_face_pad}"
78 }
79 }
80
81 # Handle forms `#RGB` and `#RGBA`.
82 evaluate-commands -draft -verbatim try %{
83 execute-keys 's\B#([A-Fa-f0-9])([A-Fa-f0-9])([A-Fa-f0-9])[A-Fa-f0-9]?\b<ret><a-;>'
84 evaluate-commands -itersel %{
85 set-option -add window swatch_range "%val{cursor_line}.%val{cursor_column}+0|{rgb:%reg{1}%reg{1}%reg{2}%reg{2}%reg{3}%reg{3}+fg}%opt{swatch_hex_body}{default,default}%opt{swatch_hex_pad}"
86 }
87 }
88
89 # Handle forms `#RRGGBB` and `#RRGGBBAA`.
90 evaluate-commands -draft -verbatim try %{
91 execute-keys 's\B#([A-Fa-f0-9]{6})(?:[A-Fa-f0-9]{2})?\b<ret><a-;>'
92 evaluate-commands -itersel %{
93 set-option -add window swatch_range "%val{cursor_line}.%val{cursor_column}+0|{rgb:%reg{1}+fg}%opt{swatch_hex_body}{default,default}%opt{swatch_hex_pad}"
94 }
95 }
96 }
97
98 set-option window swatch_timestamp %val{timestamp}
99}