diff --git a/rc/filetype/sh.kak b/rc/filetype/sh.kak index ae09183f..a48e6d29 100644 --- a/rc/filetype/sh.kak +++ b/rc/filetype/sh.kak @@ -5,6 +5,10 @@ hook global BufCreate .*\.(z|ba|c|k|mk)?sh(rc|_profile)? %{ hook global WinSetOption filetype=sh %{ require-module sh set-option window static_words %opt{sh_static_words} + + hook window ModeChange insert:.* -group sh-trim-indent sh-trim-indent + hook window InsertChar \n -group sh-indent sh-indent-on-new-line + hook -once -always window WinSetOption filetype=.* %{ remove-hooks window sh-.+ } } hook -group sh-highlight global WinSetOption filetype=sh %{ @@ -48,4 +52,128 @@ add-highlighter shared/sh/code/function regex ^\h*(\w+)\h*\(\) 1:function add-highlighter shared/sh/code/unscoped_expansion regex \$(\w+|#|@|\?|\$|!|-|\*) 0:value add-highlighter shared/sh/double_string/expansion regex \$(\w+|\{.+?\}) 0:value +# Commands +# ‾‾‾‾‾‾‾‾ + +define-command -hidden sh-trim-indent %{ + # remove trailing white spaces + try %{ execute-keys -draft -itersel s \h+$ d } +} + +# This is at best an approximation, since shell syntax is very complex. +# Also note that this targets plain sh syntax, not bash - bash adds a whole +# other level of complexity. If your bash code is fairly portable this will +# probably work. +# +# Of necessity, this is also fairly opinionated about indentation styles. +# Doing it "properly" would require far more context awareness than we can +# bring to this kind of thing. +define-command -hidden sh-indent-on-new-line %[ + evaluate-commands -draft -itersel %[ + # copy '#' comment prefix and following white spaces + try %{ execute-keys -draft k s ^\h*\K#\h* y gh j P } + # preserve previous line indent + try %{ execute-keys -draft \; K } + # filter previous line + try %{ execute-keys -draft k : sh-trim-indent } + + # Indent loop syntax, e.g.: + # for foo in bar; do + # things + # done + # + # or: + # + # while foo; do + # things + # done + # + # or equivalently: + # + # while foo + # do + # things + # done + # + # indent after do + try %{ execute-keys -draft k do$ j } + # deindent after done + try %{ execute-keys -draft k done$ j K } + + # Indent if/then/else syntax, e.g.: + # if [ $foo = $bar ]; then + # things + # else + # other_things + # fi + # + # or equivalently: + # if [ $foo = $bar ] + # then + # things + # else + # other_things + # fi + # + # indent after then + try %{ execute-keys -draft k then$ j } + # deindent after fi + try %{ execute-keys -draft k fi$ j K } + # deindent and reindent after else - deindent the else, then back + # down and return to the previous indent level. + try %{ execute-keys -draft k else$ j } + + # Indent case syntax, e.g.: + # case "$foo" in + # bar) thing1;; + # baz) + # things + # ;; + # *) + # default_things + # ;; + # esac + # + # or equivalently: + # case "$foo" + # in + # bar) thing1;; + # esac + # + # indent after in + try %{ execute-keys -draft k in$ j } + # deindent after esac + try %{ execute-keys -draft k esac$ j K } + # indent after ) + try %{ execute-keys -draft k ^\s*\(?[^(]+[^)]\)$ j } + # deindent after ;; + try %{ execute-keys -draft k ^\s*\;\;$ j } + + # Indent compound commands as logical blocks, e.g.: + # { + # thing1 + # thing2 + # } + # + # or in a function definition: + # foo () { + # thing1 + # thing2 + # } + # + # We don't handle () delimited compond commands - these are technically very + # similar, but the use cases are quite different and much less common. + # + # Note that in this context the '{' and '}' characters are reserved + # words, and hence must be surrounded by a token separator - typically + # white space (including a newline), though technically it can also be + # ';'. Only vertical white space makes sense in this context, though, + # since the syntax denotes a logical block, not a simple compound command. + try %= execute-keys -draft k (\s|^)\{$ j = + # deindent closing } + try %= execute-keys -draft k ^\s*\}$ j K = + + ] +] + ] diff --git a/test/indent/sh/deindent-after-done/cmd b/test/indent/sh/deindent-after-done/cmd new file mode 100644 index 00000000..2bc67f43 --- /dev/null +++ b/test/indent/sh/deindent-after-done/cmd @@ -0,0 +1 @@ +gei diff --git a/test/indent/sh/deindent-after-done/in b/test/indent/sh/deindent-after-done/in new file mode 100644 index 00000000..aba22804 --- /dev/null +++ b/test/indent/sh/deindent-after-done/in @@ -0,0 +1,3 @@ +while true; do + thing1 + done diff --git a/test/indent/sh/deindent-after-done/out b/test/indent/sh/deindent-after-done/out new file mode 100644 index 00000000..029beac9 --- /dev/null +++ b/test/indent/sh/deindent-after-done/out @@ -0,0 +1,4 @@ +while true; do + thing1 +done + diff --git a/test/indent/sh/deindent-after-done/rc b/test/indent/sh/deindent-after-done/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-after-done/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/deindent-after-esac/in b/test/indent/sh/deindent-after-esac/in new file mode 100644 index 00000000..0264f173 --- /dev/null +++ b/test/indent/sh/deindent-after-esac/in @@ -0,0 +1,5 @@ +case $foo in + bar) + thing1 + thing2 + ;; diff --git a/test/indent/sh/deindent-after-esac/out b/test/indent/sh/deindent-after-esac/out new file mode 100644 index 00000000..39120039 --- /dev/null +++ b/test/indent/sh/deindent-after-esac/out @@ -0,0 +1,6 @@ +case $foo in + bar) + thing1 + thing2 + ;; +esac diff --git a/test/indent/sh/deindent-after-esac/rc b/test/indent/sh/deindent-after-esac/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-after-esac/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/deindent-after-fi/cmd b/test/indent/sh/deindent-after-fi/cmd new file mode 100644 index 00000000..edb2652b --- /dev/null +++ b/test/indent/sh/deindent-after-fi/cmd @@ -0,0 +1 @@ +geifi diff --git a/test/indent/sh/deindent-after-fi/in b/test/indent/sh/deindent-after-fi/in new file mode 100644 index 00000000..060389ab --- /dev/null +++ b/test/indent/sh/deindent-after-fi/in @@ -0,0 +1,2 @@ +if [ $foo ]; then + thing1 diff --git a/test/indent/sh/deindent-after-fi/out b/test/indent/sh/deindent-after-fi/out new file mode 100644 index 00000000..6df449ca --- /dev/null +++ b/test/indent/sh/deindent-after-fi/out @@ -0,0 +1,4 @@ +if [ $foo ]; then + thing1 +fi + diff --git a/test/indent/sh/deindent-after-fi/rc b/test/indent/sh/deindent-after-fi/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-after-fi/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/deindent-case-option/cmd b/test/indent/sh/deindent-case-option/cmd new file mode 100644 index 00000000..1a5f223e --- /dev/null +++ b/test/indent/sh/deindent-case-option/cmd @@ -0,0 +1 @@ +gei;;*) diff --git a/test/indent/sh/deindent-case-option/in b/test/indent/sh/deindent-case-option/in new file mode 100644 index 00000000..06c64dac --- /dev/null +++ b/test/indent/sh/deindent-case-option/in @@ -0,0 +1,5 @@ +case $foo in + bar) thing1;; + baz) + thing1 + thing2 diff --git a/test/indent/sh/deindent-case-option/out b/test/indent/sh/deindent-case-option/out new file mode 100644 index 00000000..7d9a6fa6 --- /dev/null +++ b/test/indent/sh/deindent-case-option/out @@ -0,0 +1,7 @@ +case $foo in + bar) thing1;; + baz) + thing1 + thing2 + ;; + *) diff --git a/test/indent/sh/deindent-case-option/rc b/test/indent/sh/deindent-case-option/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-case-option/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/deindent-compound-command/cmd b/test/indent/sh/deindent-compound-command/cmd new file mode 100644 index 00000000..2bc67f43 --- /dev/null +++ b/test/indent/sh/deindent-compound-command/cmd @@ -0,0 +1 @@ +gei diff --git a/test/indent/sh/deindent-compound-command/in b/test/indent/sh/deindent-compound-command/in new file mode 100644 index 00000000..a82d0f49 --- /dev/null +++ b/test/indent/sh/deindent-compound-command/in @@ -0,0 +1,3 @@ +foo () { + thing1 + } diff --git a/test/indent/sh/deindent-compound-command/out b/test/indent/sh/deindent-compound-command/out new file mode 100644 index 00000000..70841a6b --- /dev/null +++ b/test/indent/sh/deindent-compound-command/out @@ -0,0 +1,4 @@ +foo () { + thing1 +} + diff --git a/test/indent/sh/deindent-compound-command/rc b/test/indent/sh/deindent-compound-command/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-compound-command/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/deindent-one-line-case-option/cmd b/test/indent/sh/deindent-one-line-case-option/cmd new file mode 100644 index 00000000..22e45295 --- /dev/null +++ b/test/indent/sh/deindent-one-line-case-option/cmd @@ -0,0 +1 @@ +geibaz) diff --git a/test/indent/sh/deindent-one-line-case-option/in b/test/indent/sh/deindent-one-line-case-option/in new file mode 100644 index 00000000..08e8d55b --- /dev/null +++ b/test/indent/sh/deindent-one-line-case-option/in @@ -0,0 +1,2 @@ +case $foo in + bar) thing1;; diff --git a/test/indent/sh/deindent-one-line-case-option/out b/test/indent/sh/deindent-one-line-case-option/out new file mode 100644 index 00000000..b0d854a5 --- /dev/null +++ b/test/indent/sh/deindent-one-line-case-option/out @@ -0,0 +1,3 @@ +case $foo in + bar) thing1;; + baz) diff --git a/test/indent/sh/deindent-one-line-case-option/rc b/test/indent/sh/deindent-one-line-case-option/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-one-line-case-option/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/deindent-only-else/cmd b/test/indent/sh/deindent-only-else/cmd new file mode 100644 index 00000000..d6c743f9 --- /dev/null +++ b/test/indent/sh/deindent-only-else/cmd @@ -0,0 +1 @@ +geibaz diff --git a/test/indent/sh/deindent-only-else/in b/test/indent/sh/deindent-only-else/in new file mode 100644 index 00000000..8b287fd4 --- /dev/null +++ b/test/indent/sh/deindent-only-else/in @@ -0,0 +1,3 @@ +if [ $foo ]; then + bar + else diff --git a/test/indent/sh/deindent-only-else/out b/test/indent/sh/deindent-only-else/out new file mode 100644 index 00000000..75d8b0fa --- /dev/null +++ b/test/indent/sh/deindent-only-else/out @@ -0,0 +1,4 @@ +if [ $foo ]; then + bar +else + baz diff --git a/test/indent/sh/deindent-only-else/rc b/test/indent/sh/deindent-only-else/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/deindent-only-else/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/indent-after-do/cmd b/test/indent/sh/indent-after-do/cmd new file mode 100644 index 00000000..0bc4727f --- /dev/null +++ b/test/indent/sh/indent-after-do/cmd @@ -0,0 +1 @@ +geithing1 diff --git a/test/indent/sh/indent-after-do/in b/test/indent/sh/indent-after-do/in new file mode 100644 index 00000000..48ec3642 --- /dev/null +++ b/test/indent/sh/indent-after-do/in @@ -0,0 +1 @@ +while true; do diff --git a/test/indent/sh/indent-after-do/out b/test/indent/sh/indent-after-do/out new file mode 100644 index 00000000..210ac409 --- /dev/null +++ b/test/indent/sh/indent-after-do/out @@ -0,0 +1,2 @@ +while true; do + thing1 diff --git a/test/indent/sh/indent-after-do/rc b/test/indent/sh/indent-after-do/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/indent-after-do/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/indent-after-in/cmd b/test/indent/sh/indent-after-in/cmd new file mode 100644 index 00000000..e3f54ff4 --- /dev/null +++ b/test/indent/sh/indent-after-in/cmd @@ -0,0 +1 @@ +geibar) diff --git a/test/indent/sh/indent-after-in/in b/test/indent/sh/indent-after-in/in new file mode 100644 index 00000000..a94d1a3c --- /dev/null +++ b/test/indent/sh/indent-after-in/in @@ -0,0 +1 @@ +case $foo in diff --git a/test/indent/sh/indent-after-in/out b/test/indent/sh/indent-after-in/out new file mode 100644 index 00000000..a3be70a0 --- /dev/null +++ b/test/indent/sh/indent-after-in/out @@ -0,0 +1,2 @@ +case $foo in + bar) diff --git a/test/indent/sh/indent-after-in/rc b/test/indent/sh/indent-after-in/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/indent-after-in/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/indent-after-then/cmd b/test/indent/sh/indent-after-then/cmd new file mode 100644 index 00000000..0bc4727f --- /dev/null +++ b/test/indent/sh/indent-after-then/cmd @@ -0,0 +1 @@ +geithing1 diff --git a/test/indent/sh/indent-after-then/in b/test/indent/sh/indent-after-then/in new file mode 100644 index 00000000..c640fe4f --- /dev/null +++ b/test/indent/sh/indent-after-then/in @@ -0,0 +1 @@ +if [ $foo ]; then diff --git a/test/indent/sh/indent-after-then/out b/test/indent/sh/indent-after-then/out new file mode 100644 index 00000000..060389ab --- /dev/null +++ b/test/indent/sh/indent-after-then/out @@ -0,0 +1,2 @@ +if [ $foo ]; then + thing1 diff --git a/test/indent/sh/indent-after-then/rc b/test/indent/sh/indent-after-then/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/indent-after-then/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/indent-block-case/cmd b/test/indent/sh/indent-block-case/cmd new file mode 100644 index 00000000..3598c36a --- /dev/null +++ b/test/indent/sh/indent-block-case/cmd @@ -0,0 +1 @@ +geithing1thing2 diff --git a/test/indent/sh/indent-block-case/in b/test/indent/sh/indent-block-case/in new file mode 100644 index 00000000..b0d854a5 --- /dev/null +++ b/test/indent/sh/indent-block-case/in @@ -0,0 +1,3 @@ +case $foo in + bar) thing1;; + baz) diff --git a/test/indent/sh/indent-block-case/out b/test/indent/sh/indent-block-case/out new file mode 100644 index 00000000..06c64dac --- /dev/null +++ b/test/indent/sh/indent-block-case/out @@ -0,0 +1,5 @@ +case $foo in + bar) thing1;; + baz) + thing1 + thing2 diff --git a/test/indent/sh/indent-block-case/rc b/test/indent/sh/indent-block-case/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/indent-block-case/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh diff --git a/test/indent/sh/indent-compound-command/cmd b/test/indent/sh/indent-compound-command/cmd new file mode 100644 index 00000000..0bc4727f --- /dev/null +++ b/test/indent/sh/indent-compound-command/cmd @@ -0,0 +1 @@ +geithing1 diff --git a/test/indent/sh/indent-compound-command/in b/test/indent/sh/indent-compound-command/in new file mode 100644 index 00000000..957b235e --- /dev/null +++ b/test/indent/sh/indent-compound-command/in @@ -0,0 +1 @@ +foo () { diff --git a/test/indent/sh/indent-compound-command/out b/test/indent/sh/indent-compound-command/out new file mode 100644 index 00000000..43b16506 --- /dev/null +++ b/test/indent/sh/indent-compound-command/out @@ -0,0 +1,2 @@ +foo () { + thing1 diff --git a/test/indent/sh/indent-compound-command/rc b/test/indent/sh/indent-compound-command/rc new file mode 100644 index 00000000..4795bd80 --- /dev/null +++ b/test/indent/sh/indent-compound-command/rc @@ -0,0 +1,3 @@ +source "%val{runtime}/colors/default.kak" +source "%val{runtime}/rc/filetype/sh.kak" +set buffer filetype sh