provide-module snippets %§ declare-option -hidden regex snippets_triggers_regex "\A\z" # doing \A\z will always fail hook global WinSetOption 'snippets=$' %{ set window snippets_triggers_regex "\A\z" } hook global WinSetOption 'snippets=.+$' %{ set window snippets_triggers_regex %sh{ eval set -- "$kak_quoted_opt_snippets" if [ $(($#%3)) -ne 0 ]; then printf '\A\z'; exit; fi res="" while [ $# -ne 0 ]; do if [ -n "$2" ]; then if [ -z "$res" ]; then res="$2" else res="$res|$2" fi fi shift 3 done if [ -z "$res" ]; then printf '\A\z' else printf '(?:%s)' "$res" fi } } define-command snippets-expand-trigger -params ..1 %{ eval -save-regs '/snc' %{ # -draft so that we don't modify anything in case of failure eval -draft %{ # ideally early out in here to avoid going to the (expensive) shell scope eval %arg{1} # this shell scope generates a block that looks like this # except with single quotes instead of %{..} # # try %{ # reg / "\Atrig1\z" # exec -draft d # reg c "snipcommand1" # } catch %{ # reg / "\Atrig2\z" # exec -draft d # reg c "snipcommand2" # } catch %{ # .. # } eval %sh{ quadrupleupsinglequotes() { rest="$1" while :; do beforequote="${rest%%"'"*}" if [ "$rest" = "$beforequote" ]; then printf %s "$rest" break fi printf "%s''''" "$beforequote" rest="${rest#*"'"}" done } eval set -- "$kak_quoted_opt_snippets" if [ $(($#%3)) -ne 0 ]; then exit; fi first=0 while [ $# -ne 0 ]; do if [ -z "$2" ]; then shift 3 continue fi if [ $first -eq 0 ]; then printf "try '\n" first=1 else printf "' catch '\n" fi # put the trigger into %reg{/} as \Atrig\z printf "reg / ''\\\A" # we're at two levels of nested single quotes (one for try ".." catch "..", one for reg "..") # in the arbitrary user input (snippet trigger and snippet name) quadrupleupsinglequotes "$2" printf "\\\z''\n" printf "exec -draft d\n" printf "reg n ''" quadrupleupsinglequotes "$1" printf "''\n" printf "reg c ''" quadrupleupsinglequotes "$3" printf "''\n" shift 3 done printf "'" } # preserve the selections generated by the snippet, since -draft will discard them eval %reg{c} reg s %val{selections_desc} } eval select %reg{s} echo "Snippet '%reg{n}' expanded" } } hook global WinSetOption 'snippets_auto_expand=false$' %{ rmhooks window snippets-auto-expand } hook global WinSetOption 'snippets_auto_expand=true$' %{ rmhooks window snippets-auto-expand hook -group snippets-auto-expand window InsertChar .* %{ try %{ snippets-expand-trigger %{ reg / "(%opt{snippets_triggers_regex})|." exec -save-regs '' ';' eval -draft -verbatim menu "%reg{1}" '' } } } } declare-option str-list snippets # this one must be declared after the hook, otherwise it might not be enabled right away declare-option bool snippets_auto_expand true define-command snippets-impl -hidden -params 1.. %{ eval %sh{ use=$1 shift 1 index=4 while [ $# -ne 0 ]; do if [ "$1" = "$use" ]; then printf "eval %%arg{%s}" "$index" exit fi index=$((index + 3)) shift 3 done printf "fail 'Snippet not found'" } } define-command snippets -params 1 -shell-script-candidates %{ eval set -- "$kak_quoted_opt_snippets" if [ $(($#%3)) -ne 0 ]; then exit; fi while [ $# -ne 0 ]; do printf '%s\n' "$1" shift 3 done } %{ snippets-impl %arg{1} %opt{snippets} } define-command snippets-menu-impl -hidden -params .. %{ eval %sh{ if [ $(($#%3)) -ne 0 ]; then exit; fi printf 'menu' i=1 while [ $# -ne 0 ]; do printf " %%arg{%s}" $i printf " 'snippets %%arg{%s}'" $i i=$((i+3)) shift 3 done } } define-command snippets-menu %{ snippets-menu-impl %opt{snippets} } define-command snippets-info %{ info -title Snippets %sh{ eval set -- "$kak_quoted_opt_snippets" if [ $(($#%3)) -ne 0 ]; then printf "Invalid 'snippets' value"; exit; fi if [ $# -eq 0 ]; then printf 'No snippets defined'; exit; fi maxtriglen=0 while [ $# -ne 0 ]; do if [ ${#2} -gt $maxtriglen ]; then maxtriglen=${#2} fi shift 3 done eval set -- "$kak_quoted_opt_snippets" while [ $# -ne 0 ]; do if [ $maxtriglen -eq 0 ]; then printf '%s\n' "$1" else if [ "$2" = "" ]; then printf "%${maxtriglen}s %s\n" "" "$1" else printf "%${maxtriglen}s ➡ %s\n" "$2" "$1" fi fi shift 3 done } } define-command snippets-insert -hidden -params 1 %< eval -save-regs 's' %< eval -draft -save-regs '"' %< # paste the snippet reg dquote %arg{1} exec P # replace leading tabs with the appropriate indent try %< reg dquote %sh< if [ $kak_opt_indentwidth -eq 0 ]; then printf '\t' else printf "%${kak_opt_indentwidth}s" fi > exec -draft 's\A\t+s.R' > # align everything with the current line eval -draft -itersel -save-regs '"' %< try %< exec -draft -save-regs '/' '),xs^\s+y' exec -draft ')P' > > reg s %val{selections_desc} # process placeholders try %< # select all placeholders ${..} and escaped-$ (== $$) exec 's\$\$|\$\{(\}\}|[^}])*\}' # nonsense test text to check the regex # qwldqwld {qldwlqwld} qlwdl$qwld {qwdlqwld}}qwdlqwldl} # lqlwdl$qwldlqwdl$qwdlqwld {qwd$$lqwld} $qwdlqwld$ # ${asd.as.d.} lqwdlqwld $$${as.dqdqw} # remove one $ from all $$, and leading $ from ${..} exec -draft ';d' # unselect the $ exec '\A\$\z' # we're left with only {..} placeholders, process them... eval reg dquote %sh< eval set -- "$kak_quoted_selections" for sel do # remove trailing } sel="${sel%\}}" # and leading { sel="${sel#{}" # de-double }} tmp="$sel" sel="" while true; do case "$tmp" in *}}*) sel="${sel}${tmp%%\}\}*}}" tmp=${tmp#*\}\}} ;; *) sel="${sel}${tmp}" break ;; esac done # and quote the result in '..', with escaping (== doubling of ') tmp="$sel" sel="" while true; do case "$tmp" in *\'*) sel="${sel}${tmp%%\'*}''" tmp=${tmp#*\'} ;; *) sel="${sel}${tmp}" break ;; esac done # all done, print it # nothing like some good old posix-shell text processing printf "'%s' " "$sel" done > exec R reg s %val{selections_desc} > > try %{ select %reg{s} } > > § require-module snippets