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'.
This commit is contained in:
parent
59e43c8f0c
commit
04005b06b0
|
@ -5,6 +5,10 @@ hook global BufCreate .*\.(z|ba|c|k|mk)?sh(rc|_profile)? %{
|
||||||
hook global WinSetOption filetype=sh %{
|
hook global WinSetOption filetype=sh %{
|
||||||
require-module sh
|
require-module sh
|
||||||
set-option window static_words %opt{sh_static_words}
|
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 %{
|
hook -group sh-highlight global WinSetOption filetype=sh %{
|
||||||
|
@ -14,6 +18,9 @@ hook -group sh-highlight global WinSetOption filetype=sh %{
|
||||||
|
|
||||||
provide-module 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 regions
|
||||||
add-highlighter shared/sh/code default-region group
|
add-highlighter shared/sh/code default-region group
|
||||||
add-highlighter shared/sh/double_string region %{(?<!\\)(?:\\\\)*\K"} %{(?<!\\)(?:\\\\)*"} group
|
add-highlighter shared/sh/double_string region %{(?<!\\)(?:\\\\)*\K"} %{(?<!\\)(?:\\\\)*"} group
|
||||||
|
@ -48,4 +55,137 @@ 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/code/unscoped_expansion regex \$(\w+|#|@|\?|\$|!|-|\*) 0:value
|
||||||
add-highlighter shared/sh/double_string/expansion regex \$(\w+|\{.+?\}) 0:value
|
add-highlighter shared/sh/double_string/expansion regex \$(\w+|\{.+?\}) 0:value
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
# ‾‾‾‾‾‾‾‾
|
||||||
|
|
||||||
|
# Check sh_auto_indent option before attempting to auto-indent anything
|
||||||
|
define-command -hidden maybe-sh-indent-on-new-line %{
|
||||||
|
evaluate-commands %sh{
|
||||||
|
if [ "${kak_opt_sh_auto_indent}" = true ]; then
|
||||||
|
echo "sh-indent-on-new-line"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define-command -hidden sh-trim-indent %{
|
||||||
|
# remove trailing white spaces
|
||||||
|
try %{ execute-keys -draft -itersel <a-x> s \h+$ <ret> 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 <a-x> s ^\h*\K#\h* <ret> y gh j P }
|
||||||
|
# preserve previous line indent
|
||||||
|
try %{ execute-keys -draft \; K <a-&> }
|
||||||
|
# filter previous line
|
||||||
|
try %{ execute-keys -draft k : sh-trim-indent <ret> }
|
||||||
|
|
||||||
|
# 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 <space> k <a-x> <a-k> do$ <ret> j <a-gt> }
|
||||||
|
# deindent after done
|
||||||
|
try %{ execute-keys -draft <space> k <a-x> <a-k> done$ <ret> <a-lt> j K <a-&> }
|
||||||
|
|
||||||
|
# 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 <space> k <a-x> <a-k> then$ <ret> j <a-gt> }
|
||||||
|
# deindent after fi
|
||||||
|
try %{ execute-keys -draft <space> k <a-x> <a-k> fi$ <ret> <a-lt> j K <a-&> }
|
||||||
|
# deindent and reindent after else - deindent the else, then back
|
||||||
|
# down and return to the previous indent level.
|
||||||
|
try %{ execute-keys -draft <space> k <a-x> <a-k> else$ <ret> <a-lt> 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 <space> k <a-x> <a-k> in$ <ret> j <a-gt> }
|
||||||
|
# deindent after esac
|
||||||
|
try %{ execute-keys -draft <space> k <a-x> <a-k> esac$ <ret> <a-lt> j K <a-&> }
|
||||||
|
# indent after )
|
||||||
|
try %{ execute-keys -draft <space> k <a-x> <a-k> ^\s*\(?[^(]+[^)]\)$ <ret> j <a-gt> }
|
||||||
|
# deindent after ;;
|
||||||
|
try %{ execute-keys -draft <space> k <a-x> <a-k> ^\s*\;\;$ <ret> j <a-lt> }
|
||||||
|
|
||||||
|
# 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 <space> k <a-x> <a-k> (\s|^)\{$ <ret> j <a-gt> =
|
||||||
|
# deindent closing }
|
||||||
|
try %= execute-keys -draft <space> k <a-x> <a-k> ^\s*\}$ <ret> <a-lt> j K <a-&> =
|
||||||
|
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user