From 04005b06b07d9415cca3e9407dae06f7b667d144 Mon Sep 17 00:00:00 2001 From: Simon Fowler Date: Thu, 20 Jun 2019 11:19:39 +1000 Subject: [PATCH] Add support for basic auto-indenting of sh code. This attempts to support a simple formatting and intentation style for plain sh syntax (and other sh-compatible code which doesn't stray too far from portable sh). The complexity of sh syntax means that we have to be opinionated - attempting to be more flexible would require extensive context awareness, and would require something more akin to a proper autoformatting tool or a language server. The formatting style used here makes use of vertical whitespace as the primary delimiter, so that code ends up looking like this: if [ $foo = "bar" ]; then thing1 else thing2 fi for i in foo bar baz; do thing1 thing2 done case "$foo" in bar) thing1;; baz) thing1 thing2 ;; esac Since the formatting style used is very opinionated the 'sh_auto_indent' option can be used to disable auto-indentation, with the default set to 'no'. --- rc/filetype/sh.kak | 140 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/rc/filetype/sh.kak b/rc/filetype/sh.kak index ae09183f..0b518fdb 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 maybe-sh-indent-on-new-line + hook -once -always window WinSetOption filetype=.* %{ remove-hooks window sh-.+ } } hook -group sh-highlight global WinSetOption filetype=sh %{ @@ -14,6 +18,9 @@ hook -group sh-highlight global WinSetOption filetype=sh %{ provide-module sh %[ +declare-option -docstring "attempt to automatically indent shell code. Defaults to no." \ + bool sh_auto_indent no + add-highlighter shared/sh regions add-highlighter shared/sh/code default-region group add-highlighter shared/sh/double_string region %{(? 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 = + + ] +] + ]