diff options
Diffstat (limited to 'config/kak/snippets.kak')
-rw-r--r-- | config/kak/snippets.kak | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/config/kak/snippets.kak b/config/kak/snippets.kak new file mode 100644 index 0000000..6c5020c --- /dev/null +++ b/config/kak/snippets.kak @@ -0,0 +1,295 @@ +provide-module snippets %§ + +declare-option -hidden regex snippets_triggers_regex "\A\z" # doing <a-k>\A\z<ret> 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 <a-k><ret>d + # reg c "snipcommand1" + # } catch %{ + # reg / "\Atrig2\z" + # exec -draft <a-k><ret>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 <a-k><ret>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 '' ';<a-/><ret>' + 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 '<a-s>s\A\t+<ret>s.<ret>R' + > + + # align everything with the current line + eval -draft -itersel -save-regs '"' %< + try %< + exec -draft -save-regs '/' '<a-s>),xs^\s+<ret>y' + exec -draft '<a-s>)<a-,>P' + > + > + + reg s %val{selections_desc} + # process placeholders + try %< + # select all placeholders ${..} and escaped-$ (== $$) + exec 's\$\$|\$\{(\}\}|[^}])*\}<ret>' + # 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 '<a-:><a-;>;d' + # unselect the $ + exec '<a-K>\A\$\z<ret>' + # 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 |