kakoune/rc/filetype/hare.kak
Chris Webb ca50379771 Avoid semantically significant comments in kak files
Kakoune's balanced strings require that delimiter characters nested inside
them are also paired, so for example in %{ }, each nested { must occur
before a corresponding } to balance it out.

In general this will automatically be the case for code in common scripting
languages, but sometimes regular expressions used for syntax highlighting
do end up containing an unbalanced bracket of one type or another.

This problem is easily solved because there is a free choice of balanced
delimiter characters. However, it can also be worked around by adding
a comment which itself contains an unbalanced delimiter character, to
'balance out' the unpaired one in the regular expression.

These unbalanced comments are not ideal as the semantic role they perform
is easy for a casual reader to overlook. A good example is

    catch %{
        # indent after lines with an unclosed { or (
        try %< execute-keys -draft [c[({],[)}] <ret> <a-k> \A[({][^\n]*\n[^\n]*\n?\z <ret> j<a- gt> >
        # indent after a switch's case/default statements
        try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
        # deindent closing brace(s) when after cursor
        try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
    }

in rc/filetype/go/kak. Here, it is not instantly obvious that the comment
containing an unmatched { is required for correctness. If you change the
comment, delete it or rearrange the contents of the catch block, go.kak
will fail to load, and if you cut-and-paste this code as the basis for
a new filetype, it is a loaded gun pointing at your feet.

Luckily, a careful audit of the standard kakoune library turned up only
three such instances, in go.kak, hare.kak and markdown.kak.

The examples in go.kak and hare.kak are easily made robust by replacing
a %{ } with %< > or %[ ] respectively. The example in markdown.kak is
least-intrusively fixed by rewriting the affected regular expression
slightly so it has balanced { and } anyway.
2023-12-13 16:40:48 +00:00

145 lines
6.9 KiB
Plaintext

# detection
hook global BufCreate .*[.]ha %{
set-option buffer filetype hare
}
# initialisation
hook global WinSetOption filetype=hare %{
require-module hare
hook window ModeChange pop:insert:.* -group hare-trim-indent hare-trim-indent
hook window InsertChar \n -group hare-insert hare-insert-on-new-line
hook window InsertChar \n -group hare-indent hare-indent-on-new-line
hook window InsertChar \{ -group hare-indent hare-indent-on-opening-curly-brace
hook window InsertChar \} -group hare-indent hare-indent-on-closing-curly-brace
}
hook -group hare-highlight global WinSetOption filetype=hare %{
add-highlighter window/hare ref hare
hook -once -always window WinSetOption filetype=*. %{ remove-highlighter window/hare }
}
# highlighters
provide-module hare %§
add-highlighter shared/hare regions
add-highlighter shared/hare/code default-region group
add-highlighter shared/hare/comment region // $ fill comment
add-highlighter shared/hare/rawstring region ` ` group
add-highlighter shared/hare/rawstring/ fill string
add-highlighter shared/hare/string region '"' (?<!\\)(\\\\)*" group
add-highlighter shared/hare/string/ fill string
add-highlighter shared/hare/string/ regex '(\\0|\\a|\\b|\\f|\\n|\\r|\\t|\\v|\\\\|\\")' 0:meta
add-highlighter shared/hare/string/ regex "\\'" 0:meta
add-highlighter shared/hare/string/ regex "(\\x[0-9a-fA-F]{2})" 0:meta
add-highlighter shared/hare/string/ regex "(\\u[0-9a-fA-F]{4})" 0:meta
add-highlighter shared/hare/string/ regex "(\\U[0-9a-fA-F]{8})" 0:meta
add-highlighter shared/hare/rune region "'" (?<!\\)(\\\\)*' group
add-highlighter shared/hare/rune/ fill string
add-highlighter shared/hare/rune/ regex "(\\0|\\a|\\b|\\f|\\n|\\r|\\t|\\v|\\\\|\\')" 0:meta
add-highlighter shared/hare/rune/ regex '\\"' 0:meta
add-highlighter shared/hare/rune/ regex "(\\x[0-9a-fA-F]{2})" 0:meta
add-highlighter shared/hare/rune/ regex "(\\u[0-9a-fA-F]{4})" 0:meta
add-highlighter shared/hare/rune/ regex "(\\U[0-9a-fA-F]{8})" 0:meta
# imports
add-highlighter shared/hare/code/ regex "\buse\s.*?(?=;)" 0:module
add-highlighter shared/hare/code/ regex "\buse\b" 0:meta
# functions
add-highlighter shared/hare/code/ regex "\b([0-9a-zA-Z_]*)\h*\(" 1:function
# attributes
add-highlighter shared/hare/code/ regex "@(packed|offset|init|fini|test|noreturn|symbol)\b" 0:attribute
# declarations
add-highlighter shared/hare/code/ regex "\b(let|export|const)\b" 0:meta
add-highlighter shared/hare/code/ regex "\b(fn|type|def)\b" 0:keyword
# builtins
add-highlighter shared/hare/code/ regex "\b(len|offset|free|alloc|assert|append|abort|delete|insert|vastart|vaarg|vaend)\b" 0:builtin
add-highlighter shared/hare/code/ regex "\b(as|is)\b" 0:builtin
# types
add-highlighter shared/hare/code/ regex "\b(struct|union|enum)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(nullable|null|void)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(u8|u16|u32|u64|uint)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(i8|i16|i32|i64|int)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(size|uintptr|char)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(f32|f64)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(str|rune)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(bool)\b" 0:type
add-highlighter shared/hare/code/ regex "\b(valist)\b" 0:type
# literals
add-highlighter shared/hare/code/ regex "\b(true|false)\b" 0:value
add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?(z|(i|u)(8|16|32|64)?)?\b" 0:value
add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?((?=e)|(?=u)|(?=i))" 0:value
add-highlighter shared/hare/code/ regex "\b0b[0-1]+(z|(i|u)(8|16|32|64)?)?\b" 0:value
add-highlighter shared/hare/code/ regex "\b0b[0-1]+((?=u)|(?=i))" 0:value
add-highlighter shared/hare/code/ regex "\b0o[0-7]+(z|(i|u)(8|16|32|64)?)?\b" 0:value
add-highlighter shared/hare/code/ regex "\b0o[0-7]+((?=u)|(?=i))" 0:value
add-highlighter shared/hare/code/ regex "\b0x[0-9a-fA-F]+(z|(i|u)(8|16|32|64)?)?\b" 0:value
add-highlighter shared/hare/code/ regex "\b0x[0-9a-fA-F]+((?=u)|(?=i))" 0:value
# floats
add-highlighter shared/hare/code/ regex "\b[0-9]+\.[0-9]+([eE][-+]?[0-9]+)?(f32|f64)?\b" 0:value
add-highlighter shared/hare/code/ regex "\b[0-9]+\.[0-9]+([eE][-+]?[0-9]+)?((?=e)|(?=f))" 0:value
add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?(f32|f64)\b" 0:value
add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?(?=f)" 0:value
# constants
add-highlighter shared/hare/code/ regex "\b[0-9A-Z_]*\b" 0:value
# control flow
add-highlighter shared/hare/code/ regex "\b(for|if|else|switch|match|return|break|continue|defer|yield|case|static)\b" 0:keyword
# operators
add-highlighter shared/hare/code/ regex "(=|\+|-|\*|/|<|>|!|\?|&|\||\.\.(\.)?)" 0:operator
# commands
define-command -hidden hare-indent-on-new-line %[ evaluate-commands -draft -itersel %[
# preserve indentation on new lines
try %{ execute-keys -draft <semicolon> K <a-&> }
# indent after lines ending with { or (
try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j i<tab> ]
# cleanup trailing white spaces on the previous line
execute-keys -draft k :hare-trim-indent <ret>
# indent after match/switch's case statements
try %[ execute-keys -draft kx <a-k> case\h.*=>\h*$ <ret> j<a-gt> ]
# deindent closing brace(s) when after cursor
try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
] ]
define-command -hidden hare-insert-on-new-line %{ evaluate-commands -draft -itersel %{
try %{ evaluate-commands -draft -save-regs '/"' %{
# copy the comment prefix
execute-keys -save-regs '' k x s ^\h*\K//\h* <ret> y
try %{
# paste the comment prefix
execute-keys x j x s ^\h* <ret>P
}
} }
try %{
# remove trailing whitespace on the above line
execute-keys -draft k :hare-trim-indent <ret>
}
} }
define-command -hidden hare-indent-on-opening-curly-brace %[
# align indent with opening paren when { is entered on a new line after the closing paren
try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
]
define-command -hidden hare-indent-on-closing-curly-brace %[
# align to opening curly brace when alone on a line
try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
]
define-command -hidden hare-trim-indent %{ evaluate-commands -draft -itersel %{
# remove trailing whitespace
try %{ execute-keys -draft x s \h+$ <ret> d }
} }
§