1# https://cuelang.org
2# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
3
4# Detection
5# ‾‾‾‾‾‾‾‾‾
6
7hook global BufCreate .*[.](cue) %{
8    set-option buffer filetype cue
9}
10
11# Initialization
12# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
13
14hook global WinSetOption filetype=cue %[
15    require-module cue
16
17    # cleanup trailing whitespaces when exiting insert mode
18    hook window ModeChange pop:insert:.* -group cue-trim-indent cue-trim-indent
19    hook window InsertChar \n -group cue-insert cue-insert-on-new-line
20    hook window InsertChar \n -group cue-indent cue-indent-on-new-line
21    hook window InsertChar \{ -group cue-indent cue-indent-on-opening-curly-brace
22    hook window InsertChar [)}] -group cue-indent cue-indent-on-closing
23
24    hook -once -always window WinSetOption filetype=.* %{ remove-hooks window cue-.+ }
25]
26
27hook -group cue-highlight global WinSetOption filetype=cue %{
28    add-highlighter window/cue ref cue
29    hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/cue }
30}
31
32provide-module cue %§
33
34# Highlighters
35# ‾‾‾‾‾‾‾‾‾‾‾‾
36
37# https://cuelang.org/docs/references/spec/
38
39add-highlighter shared/cue regions
40add-highlighter shared/cue/code default-region group
41add-highlighter shared/cue/simple_string    region '"'   (?<!\\)(\\\\)*" regions
42add-highlighter shared/cue/simple_bytes     region "'"   (?<!\\)(\\\\)*' fill string
43add-highlighter shared/cue/multiline_string region '"""' '"""'           ref shared/cue/simple_string
44add-highlighter shared/cue/multiline_bytes  region "'''" "'''"           ref shared/cue/simple_bytes
45add-highlighter shared/cue/line_comment     region "//"  "$"             fill comment
46
47add-highlighter shared/cue/simple_string/base default-region fill string
48add-highlighter shared/cue/simple_string/interpolation region -recurse \( \\\( \) fill meta
49
50evaluate-commands %sh{
51    # Grammar
52    binary_digit="[01]"
53    decimal_digit="[0-9]"
54    hex_digit="[0-9a-fA-F]"
55    octal_digit="[0-7]"
56
57    decimal_lit="[1-9](_?${decimal_digit})*"
58    binary_lit="0b${binary_digit}(_?${binary_digit})*"
59    hex_lit="0[xX]${hex_digit}(_?${hex_digit})*"
60    octal_lit="0o${octal_digit}(_?${octal_digit})*"
61
62    decimals="${decimal_digit}(_?${decimal_digit})*"
63    multiplier="([KMGTP]i?)?"
64    exponent="([eE][+-]?${decimals})"
65    si_lit="(${decimals}(\.${decimals})?${multiplier}|\.${decimals}${multiplier})"
66
67    int_lit="\b(${decimal_lit}|${si_lit}|${octal_lit}|${binary_lit}|${hex_lit})\b"
68    float_lit="\b${decimals}\.(${decimals})?${exponent}?|\b${decimals}${exponent}\b|\.${decimals}${exponent}?\b"
69
70    operator_chars="(\+|-|\*|/|&|&&|\||\|\||=|==|=~|!|!=|!~|<|>|<=|>=)"
71    punctuation="(_\|_|:|::|,|\.|\.\.\.|\(|\{|\[|\)|\}|\])"
72
73    function_calls="\w+(?=\()"
74    identifier="(?!\d)[\w_$]+"
75    reserved="\b__${identifier}"
76
77    preamble="^(package|import)\b"
78
79    functions="len close and or"
80    keywords="for in if let"
81    operators="div mod quo rem"
82    types="
83        bool string bytes rune number
84        uint uint8 uint16 uint32 uint64 uint128
85        int int8 int16 int32 int64 int128
86        float float32 float64
87    "
88    values="null true false"
89
90    join() { sep=$2; set -- $1; IFS="$sep"; echo "$*"; }
91
92    static_words="$(join "package import ${functions} ${keywords} ${operators} ${types} ${values}" ' ')"
93
94    # Add the language's grammar to the static completion list
95    printf %s\\n "declare-option str-list cue_static_words ${static_words}"
96
97    functions="$(join "${functions}" '|')"
98    keywords="$(join "${keywords}" '|')"
99    operators="$(join "${operators}" '|')"
100    types="$(join "${types}" '|')"
101    values="$(join "${values}" '|')"
102
103    # Highlight keywords
104    printf %s "
105        add-highlighter shared/cue/code/ regex ${float_lit} 0:value
106        add-highlighter shared/cue/code/ regex ${function_calls} 0:function
107        add-highlighter shared/cue/code/ regex ${int_lit} 0:value
108        add-highlighter shared/cue/code/ regex ${operator_chars} 0:operator
109        add-highlighter shared/cue/code/ regex ${preamble} 0:keyword
110        add-highlighter shared/cue/code/ regex ${punctuation} 0:operator
111        add-highlighter shared/cue/code/ regex ${reserved} 0:keyword
112        add-highlighter shared/cue/code/ regex \b(${functions})\b 0:builtin
113        add-highlighter shared/cue/code/ regex \b(${keywords})\b 0:keyword
114        add-highlighter shared/cue/code/ regex \b(${operators})\b 0:operator
115        add-highlighter shared/cue/code/ regex \b(${types})\b 0:type
116        add-highlighter shared/cue/code/ regex \b(${values})\b 0:value
117    "
118}
119
120# Commands
121# ‾‾‾‾‾‾‾‾
122
123define-command -hidden cue-trim-indent %{
124    # remove trailing white spaces
125    try %{ execute-keys -draft -itersel <a-x> s \h+$ <ret> d }
126}
127
128define-command -hidden cue-insert-on-new-line %~
129    evaluate-commands -draft -itersel %<
130        # copy // comments prefix and following white spaces
131        try %{ execute-keys -draft <semicolon><c-s>k<a-x> s ^\h*\K//[!/]?\h* <ret> y<c-o>P<esc> }
132    >
133~
134
135define-command -hidden cue-indent-on-new-line %~
136    evaluate-commands -draft -itersel %<
137        # preserve previous line indent
138        try %{ execute-keys -draft <semicolon> K <a-&> }
139        try %<
140            # only if we didn't copy a comment
141            execute-keys -draft <a-x> <a-K> ^\h*// <ret>
142            # indent after lines ending with { or (
143            try %[ execute-keys -draft k <a-x> <a-k> [{(]\h*$ <ret> j <a-gt> ]
144            # indent after lines ending with [{(].+ and move first parameter to own line
145            try %< execute-keys -draft [c[({],[)}] <ret> <a-k> \A[({][^\n]+\n[^\n]*\n?\z <ret> L i<ret><esc> <gt> <a-S> <a-&> >
146            # deindent closing brace(s) when after cursor
147            try %< execute-keys -draft <a-x> <a-k> ^\h*[})] <ret> gh / [})] <ret>  m <a-S> 1<a-&> >
148        >
149        # filter previous line
150        try %{ execute-keys -draft k : cue-trim-indent <ret> }
151    >
152~
153
154define-command -hidden cue-indent-on-opening-curly-brace %[
155    evaluate-commands -draft -itersel %_
156        # align indent with opening paren when { is entered on a new line after the closing paren
157        try %[ execute-keys -draft h <a-F> ) M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
158    _
159]
160
161define-command -hidden cue-indent-on-closing %[
162    evaluate-commands -draft -itersel %_
163        # align to opening curly brace or paren when alone on a line
164        try %< execute-keys -draft <a-h> <a-k> ^\h*[)}]$ <ret> h m <a-S> 1<a-&> >
165    _
166]
167
168§
169