hook global BufCreate .*\.((z|ba|c|k|mk)?sh(rc|_profile)?|profile) %{ set-option buffer filetype sh } hook global WinSetOption filetype=sh %{ require-module sh set-option window static_words %opt{sh_static_words} hook window ModeChange pop:insert:.* -group sh-trim-indent sh-trim-indent hook window InsertChar \n -group sh-insert sh-insert-on-new-line 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 %{ add-highlighter window/sh ref sh hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/sh } } # using non-ascii characters here so that we can use the '[' command provide-module sh %§ add-highlighter shared/sh regions add-highlighter shared/sh/code default-region group add-highlighter shared/sh/arithmetic region -recurse \(.*?\( (\$|(?<=for)\h*)\(\( \)\) group add-highlighter shared/sh/double_string region %{(? 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-insert-on-new-line %[ evaluate-commands -draft -itersel %[ # copy '#' comment prefix and following white spaces try %{ execute-keys -draft k x s ^\h*\K#\h* y gh j P } ] ] # Use custom object matching to copy indentation for the various logical # blocks. # # Note that we're using a weird non-ascii character instead of [ or { here # because the '[' and '{' characters need to be available for the commands. define-command -hidden sh-indent-on-new-line %¶ evaluate-commands -draft -itersel %@ # 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 x \bdo$ j } # copy the indentation of the matching for/when - matching on the do # statement, so we don't need to duplicate this for the two loop # structures. try %{ execute-keys -draft , k x \bdone$ gh [c\bdo\b,\bdone\b x 1 , 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 x \bthen$ j } # copy the indentation of the matching if try %{ execute-keys -draft , k x \bfi$ gh [c\bif\b,\bfi\b x 1 , j K } # copy the indentation of the matching if, and then re-indent afterwards try %{ execute-keys -draft , k x \belse$ gh [c\bif\b,\bfi\b x 1 , j K 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 x \bin$ j } # copy the indentation of the matching case try %{ execute-keys -draft , k x \besac$ gh [c\bcase\b,\besac\b x 1 , j K } # indent after ) try %{ execute-keys -draft , k x ^\s*\(?[^(]+[^)]\)$ j } # deindent after ;; try %{ execute-keys -draft , k x ^\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 x (\s|^)\{$ j = # deindent closing } try %= execute-keys -draft , k x ^\s*\}$ j K = # deindent closing } when after cursor try %= execute-keys -draft x ^\h*\} gh / \} m 1 = @ ¶ §