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.
This commit is contained in:
Chris Webb 2023-12-13 16:40:48 +00:00
parent 83fb65aef5
commit ca50379771
3 changed files with 6 additions and 6 deletions

View File

@ -105,20 +105,20 @@ define-command -hidden go-indent-on-new-line %~
try %{ execute-keys -draft <semicolon>K<a-&> } try %{ execute-keys -draft <semicolon>K<a-&> }
# cleanup trailing white spaces on the previous line # cleanup trailing white spaces on the previous line
try %{ execute-keys -draft kx s \h+$ <ret>d } try %{ execute-keys -draft kx s \h+$ <ret>d }
try %{ try %<
try %{ # line comment try %{ # line comment
execute-keys -draft kx s ^\h*// <ret> execute-keys -draft kx s ^\h*// <ret>
} catch %{ # block comment } catch %{ # block comment
execute-keys -draft <a-?> /\* <ret> <a-K>\*/<ret> execute-keys -draft <a-?> /\* <ret> <a-K>\*/<ret>
} }
} catch %{ > catch %<
# indent after lines with an unclosed { or ( # 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> > 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 # indent after a switch's case/default statements
try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ] try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
# deindent closing brace(s) when after cursor # deindent closing brace(s) when after cursor
try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ] try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
} >
= =
~ ~

View File

@ -99,7 +99,7 @@ provide-module hare %§
add-highlighter shared/hare/code/ regex "(=|\+|-|\*|/|<|>|!|\?|&|\||\.\.(\.)?)" 0:operator add-highlighter shared/hare/code/ regex "(=|\+|-|\*|/|<|>|!|\?|&|\||\.\.(\.)?)" 0:operator
# commands # commands
define-command -hidden hare-indent-on-new-line %{ evaluate-commands -draft -itersel %{ define-command -hidden hare-indent-on-new-line %[ evaluate-commands -draft -itersel %[
# preserve indentation on new lines # preserve indentation on new lines
try %{ execute-keys -draft <semicolon> K <a-&> } try %{ execute-keys -draft <semicolon> K <a-&> }
# indent after lines ending with { or ( # indent after lines ending with { or (
@ -110,7 +110,7 @@ provide-module hare %§
try %[ execute-keys -draft kx <a-k> case\h.*=>\h*$ <ret> j<a-gt> ] try %[ execute-keys -draft kx <a-k> case\h.*=>\h*$ <ret> j<a-gt> ]
# deindent closing brace(s) when after cursor # deindent closing brace(s) when after cursor
try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ] 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 %{ define-command -hidden hare-insert-on-new-line %{ evaluate-commands -draft -itersel %{
try %{ evaluate-commands -draft -save-regs '/"' %{ try %{ evaluate-commands -draft -save-regs '/"' %{

View File

@ -93,7 +93,7 @@ add-highlighter shared/markdown/inline/text/ regex "\H( {2,})$" 1:+r@meta
define-command markdown-load-languages -params 1 %{ define-command markdown-load-languages -params 1 %{
evaluate-commands -draft %{ try %{ evaluate-commands -draft %{ try %{
execute-keys "%arg{1}s```\h*\{?[.=]?\K\w+<ret>" # } execute-keys "%arg{1}1s```\h*\{?[.=]?(\w+)\}?<ret>"
evaluate-commands -itersel %{ try %{ evaluate-commands -itersel %{ try %{
require-module %val{selection} require-module %val{selection}
add-highlighter "shared/markdown/codeblock/%val{selection}" region -match-capture "^(\h*)```\h*(%val{selection}\b|\{[.=]?%val{selection}\})" ^(\h*)``` regions add-highlighter "shared/markdown/codeblock/%val{selection}" region -match-capture "^(\h*)```\h*(%val{selection}\b|\{[.=]?%val{selection}\})" ^(\h*)``` regions