From c3362aefa2e762211409923cfff065541bebf9e7 Mon Sep 17 00:00:00 2001 From: Saumit Dinesan Date: Sun, 22 May 2022 00:37:40 +0530 Subject: ble.sh & z4h addition --- .local/src/blesh/lib/core-complete.sh | 6327 +++++++++++++++++++++++++++++++++ 1 file changed, 6327 insertions(+) create mode 100644 .local/src/blesh/lib/core-complete.sh (limited to '.local/src/blesh/lib/core-complete.sh') diff --git a/.local/src/blesh/lib/core-complete.sh b/.local/src/blesh/lib/core-complete.sh new file mode 100644 index 0000000..9d7c4c1 --- /dev/null +++ b/.local/src/blesh/lib/core-complete.sh @@ -0,0 +1,6327 @@ +# this script is a part of blesh (https://github.com/akinomyoga/ble.sh) under BSD-3-Clause license +ble/util/import "$_ble_base/lib/core-syntax.sh" +function ble/complete/string#search-longest-suffix-in { + local needle=$1 haystack=$2 + local l=0 u=${#needle} + while ((l0;j--)); do + ret=${rhs::j} + [[ $lhs == *"$ret" ]] && return 0 + done + ret= + fi +} +function ble/complete/get-wordbreaks { + wordbreaks=$_ble_term_IFS$COMP_WORDBREAKS + [[ $wordbreaks == *'('* ]] && wordbreaks=${wordbreaks//['()']}'()' + [[ $wordbreaks == *']'* ]] && wordbreaks=']'${wordbreaks//']'} + [[ $wordbreaks == *'-'* ]] && wordbreaks=${wordbreaks//'-'}'-' +} +_ble_complete_menu_items=() +_ble_complete_menu_class= +_ble_complete_menu_param= +_ble_complete_menu_version= +_ble_complete_menu_page_style= +_ble_complete_menu_ipage= +_ble_complete_menu_offset= +_ble_complete_menu_icons=() +_ble_complete_menu_info_data=() +_ble_complete_menu_selected=-1 +function ble/complete/menu#check-cancel { + ((menu_iloop++%menu_interval==0)) && + [[ :$comp_type: != *:sync:* ]] && + ble/decode/has-input +} +_ble_complete_menu_style_measure=() +_ble_complete_menu_style_icons=() +_ble_complete_menu_style_pages=() +function ble/complete/menu#render-item { + if ble/is-function "$menu_class"/render-item; then + "$menu_class"/render-item "$@" + return "$?" + fi + local item=$1 opts=$2 + local sgr0=$_ble_term_sgr0 sgr1=$_ble_term_rev + [[ :$opts: == *:selected:* ]] && local sgr0=$sgr1 sgr1=$sgr0 + ble/canvas/trace-text "$item" nonewline:external-sgr + ret=$sgr0$ret$_ble_term_sgr0 +} +function ble/complete/menu-style:align/construct/.measure-candidates-in-page { + local max_wcell=$bleopt_menu_align_max; ((max_wcell>cols&&(max_wcell=cols))) + ((wcell=bleopt_menu_align_min,wcell<2&&(wcell=2))) + local ncell=0 index=$begin + local item ret esc1 w + for item in "${menu_items[@]:begin}"; do + ble/complete/menu#check-cancel && return 148 + local wcell_old=$wcell + local w=${_ble_complete_menu_style_measure[index]%%:*} + if [[ ! $w ]]; then + local x=0 y=0 + ble/complete/menu#render-item "$item"; esc1=$ret + local w=$((y*cols+x)) + _ble_complete_menu_style_measure[index]=$w:${#item}:$item$esc1 + fi + local wcell_request=$((w++,w<=max_wcell?w:max_wcell)) + ((wcell0&&x1+w>=cols)); then + ((ncell=ncell_eol+cand_ncell)) + elif ((x1+wncell_eol&&(ncell=ncell_eol))) + else + ((ncell+=cand_ncell)) + fi + else + ((ncell+=cand_ncell)) + fi + local max_ncell=$((line_ncell*lines)) + ((index&&ncell>max_ncell)) && { wcell=$wcell_old; break; } + ((index++)) + done + end=$index +} +function ble/complete/menu-style:align/construct-page { + x=0 y=0 esc= + local wcell=2 + ble/complete/menu-style:align/construct/.measure-candidates-in-page + (($?==148)) && return 148 + local ncell=$((cols/wcell)) + local index=$begin entry + for entry in "${_ble_complete_menu_style_measure[@]:begin:end-begin}"; do + ble/complete/menu#check-cancel && return 148 + local w=${entry%%:*}; entry=${entry#*:} + local s=${entry%%:*}; entry=${entry#*:} + local item=${entry::s} esc1=${entry:s} + local x0=$x y0=$y + if ((x==0||x+w=lines&&(x=x0,y=y0,1))) && break + else + if [[ $menu_style == align-nowrap ]]; then + ((y+1>=lines)) && break + esc=$esc$'\n' + ((x0=x=0,y0=++y)) + ((x=w%cols,y+=w/cols)) + ((y>=lines&&(x=x0,y=y0,1))) && break + else + ble/complete/menu#render-item "$item" || + ((begin==index)) || # [Note: 少なくとも1個ははみ出ても表示する] + { x=$x0 y=$y0; break; }; esc1=$ret + fi + fi + _ble_complete_menu_style_icons[index]=$x0,$y0,$x,$y,${#item},${#esc1}:$item$esc1 + esc=$esc$esc1 + if ((++index=lines)) && break + esc=$esc$'\n' + ((x=0,++y)) + fi + fi + done + end=$index +} +function ble/complete/menu-style:align-nowrap/construct-page { + ble/complete/menu-style:align/construct-page "$@" +} +function ble/complete/menu-style:dense/construct-page { + x=0 y=0 esc= + local item index=$begin N=${#menu_items[@]} + for item in "${menu_items[@]:begin}"; do + ble/complete/menu#check-cancel && return 148 + local x0=$x y0=$y esc1 + ble/complete/menu#render-item "$item" || + ((index==begin)) || + { x=$x0 y=$y0; break; }; esc1=$ret + if [[ $menu_style == dense-nowrap ]]; then + if ((y>y0&&x>0||y>y0+1)); then + ((++y0>=lines)) && break + esc=$esc$'\n' + ((y=y0,x=x0=0)) + ble/complete/menu#render-item "$item" || + ((begin==index)) || + { x=$x0 y=$y0; break; }; esc1=$ret + fi + fi + _ble_complete_menu_style_icons[index]=$x0,$y0,$x,$y,${#item},${#esc1}:$item$esc1 + esc=$esc$esc1 + if ((++index=lines)) && break + esc=$esc$'\n' + ((x=0,++y)) + fi + fi + done + end=$index +} +function ble/complete/menu-style:dense-nowrap/construct-page { + ble/complete/menu-style:dense/construct-page "$@" +} +function ble/complete/menu-style:linewise/construct-page { + local opts=$1 ret + local max_icon_width=$((cols-1)) + local prefix_format=$bleopt_menu_linewise_prefix prefix_width=0 + if [[ $prefix_format ]]; then + local prefix1 + ble/util/sprintf prefix1 "$prefix_format" ${#menu_items[@]} + local x1 y1 x2 y2 + LINES=1 COLUMNS=$max_icon_width x=0 y=0 ble/canvas/trace "$prefix1" truncate:measure-bbox + if ((x2<=max_icon_width/2)); then + prefix_width=$x2 + ble/string#reserve-prototype "$prefix_width" + fi + fi + local item x0 y0 esc1 index=$begin + end=$begin x=0 y=0 esc= + for item in "${menu_items[@]:begin:lines}"; do + ble/complete/menu#check-cancel && return 148 + if ((prefix_width)); then + local prefix1; ble/util/sprintf prefix1 "$prefix_format" $((index+1)) + LINES=1 COLUMNS=$max_icon_width y=0 ble/canvas/trace "$prefix1" truncate:relative:measure-bbox; esc1=$ret + if ((x=lines)) && break + ((x=0,++y)) + esc=$esc$'\n' + done + end=$index +} +function ble/complete/menu-style:linewise/guess { + ((ipage=scroll/lines, + begin=ipage*lines, + end=begin)) +} +_ble_complete_menu_desc_pageheight=() +function ble/complete/menu-style:desc/construct-page { + local opts=$1 ret + local opt_raw=; [[ $menu_style != desc-text ]] && opt_raw=1 + end=$begin esc= x=0 y=0 + local colsep=' | ' + local desc_sgr0=$'\e[m' + ble/color/face2sgr-ansi syntax_quoted; local desc_sgrq=$ret + ble/color/face2sgr-ansi syntax_delimiter; local desc_sgrt=$ret + local ncolumn=1 nline=$lines + local nrest_item=$((${#menu_items[@]}-begin)) + if [[ $bleopt_menu_desc_multicolumn_width ]]; then + ncolumn=$((cols/bleopt_menu_desc_multicolumn_width)) + if ((ncolumn<1)); then + ncolumn=1 + elif ((ncolumn>nrest_item)); then + ncolumn=$nrest_item + fi + fi + ((nline=(${#menu_items[@]}-begin+ncolumn-1)/ncolumn, + nline>lines&&(nline=lines))) + local ncolumn_max=$(((nrest_item+nline-1)/nline)) + ((ncolumn>ncolumn_max&&(ncolumn=ncolumn_max))) + local available_width=$cols + case $_ble_term_TERM in + (screen:*|tmux:*|kitty:*|contra:*) ;; + (*) ((available_width--)) ;; + esac + local wcolumn=$(((available_width-${#colsep}*(ncolumn-1))/ncolumn)) + local wcand_limit=$(((wcolumn+1)*2/3)) + ((wcand_limit<10&&(wcand_limit=wcolumn))) + local -a DRAW_BUFF=() + local index=$begin icolumn ymax=0 + for ((icolumn=0;icolumnmax_width&&(max_width=w))) + ble/array#push measure "$w:${#pack}:$pack$esc1" + done + local cand_width=$max_width + local desc_x=$((cand_width+1)); ((desc_x>wcolumn&&(desc_x=wcolumn))) + local desc_prefix=; ((wcolumn-desc_x>30)) && desc_prefix=': ' + local xcolumn=$((icolumn*(wcolumn+${#colsep}))) + x=0 y=0 + local entry w s pack esc1 x0 y0 pad + for entry in "${measure[@]}"; do + ble/complete/menu#check-cancel && return 148 + w=${entry%%:*} entry=${entry#*:} + s=${entry%%:*} entry=${entry#*:} + pack=${entry::s} esc1=${entry:s} + ((x0=x,y0=y,x+=w)) + _ble_complete_menu_style_icons[index]=$((xcolumn+x0)),$y0,$((xcolumn+x)),$y,${#pack},${#esc1},"0 0 $wcand_limit 1":$pack$esc1 + ((index++)) + ble/canvas/put.draw "$esc1" + ble/canvas/put-spaces.draw $((pad=desc_x-x)) + ble/canvas/put.draw "$desc_prefix" + ((x+=pad+${#desc_prefix})) + local desc=$desc_sgrt'(no description)'$desc_sgr0 + ble/function#try "$menu_class"/get-desc "$pack" + if [[ $opt_raw ]]; then + y=0 g=0 lc=0 lg=0 LINES=1 COLUMNS=$wcolumn ble/canvas/trace.draw "$desc" truncate:relative:ellipsis + else + y=0 lines=1 cols=$wcolumn ble/canvas/trace-text "$desc" nonewline + ble/canvas/put.draw "$ret" + fi + ble/canvas/put.draw "$_ble_term_sgr0" + ((y+1>=nline)) && break + ble/canvas/put-move.draw $((-x)) 1 + ((x=0,++y)) + done + ((y>ymax)) && ymax=$y + if ((icolumn+1end)); then + ((ret=end)) + fi + return 0 +} +function ble/complete/menu-style:desc-text/construct-page { ble/complete/menu-style:desc/construct-page "$@"; } +function ble/complete/menu-style:desc-text/guess { ble/complete/menu-style:desc/guess; } +function ble/complete/menu-style:desc-text/locate { ble/complete/menu-style:desc/locate "$@"; } +function ble/complete/menu-style:desc-raw/construct-page { ble/complete/menu-style:desc/construct-page "$@"; } +function ble/complete/menu-style:desc-raw/guess { ble/complete/menu-style:desc/guess; } +function ble/complete/menu-style:desc-raw/locate { ble/complete/menu-style:desc/locate "$@"; } +function ble/complete/menu#construct/.initialize-size { + ble/edit/info/.initialize-size + local maxlines=$((bleopt_complete_menu_maxlines)) + ((maxlines>0&&lines>maxlines)) && lines=$maxlines +} +function ble/complete/menu#construct { + local menu_opts=$1 + local menu_iloop=0 + local menu_interval=$bleopt_complete_polling_cycle + local cols lines + ble/complete/menu#construct/.initialize-size + local nitem=${#menu_items[@]} + local version=$nitem:$lines:$cols + if ((nitem==0)); then + _ble_complete_menu_version=$version + _ble_complete_menu_items=() + _ble_complete_menu_page_style= + _ble_complete_menu_ipage=0 + _ble_complete_menu_offset=0 + _ble_complete_menu_icons=() + _ble_complete_menu_info_data=(ansi $'\e[38;5;242m(no items)\e[m') + _ble_complete_menu_selected=-1 + return 0 + fi + local scroll=0 rex=':scroll=([0-9]+):' use_cache= + if [[ :$menu_opts: =~ $rex ]]; then + scroll=${BASH_REMATCH[1]} + ((nitem&&(scroll%=nitem))) + [[ $_ble_complete_menu_version == $version ]] && use_cache=1 + fi + if [[ ! $use_cache ]]; then + _ble_complete_menu_style_measure=() + _ble_complete_menu_style_icons=() + _ble_complete_menu_style_pages=() + fi + local begin=0 end=0 ipage=0 x y esc + ble/function#try ble/complete/menu-style:"$menu_style"/guess + while ((end=0&&!(visible_beg<=nsel&&nsel=0)); then + local entry=${_ble_complete_menu_icons[osel-visible_beg]} + local fields text=${entry#*:} + ble/string#split fields , "${entry%%:*}" + if ((fields[3]<_ble_canvas_panel_height[_ble_edit_info_panel])); then + ble/canvas/panel#goto.draw "$_ble_edit_info_panel" "${fields[@]::2}" + ble/canvas/put.draw "${text:fields[4]}" + _ble_canvas_x=${fields[2]} _ble_canvas_y=$((infoy+fields[3])) + fi + fi + local value= + if ((nsel>=0)); then + [[ :$opts: == *:goto-page-top:* ]] && nsel=$visible_beg + local entry=${_ble_complete_menu_icons[nsel-visible_beg]} + local fields text=${entry#*:} + ble/string#split fields , "${entry%%:*}" + local x=${fields[0]} y=${fields[1]} + local item=${text::fields[4]} + local ret + if [[ ${fields[6]} ]]; then + local box cols lines + ble/string#split-words box "${fields[6]}" + x=${box[0]} y=${box[1]} cols=${box[2]} lines=${box[3]} + ble/complete/menu#render-item "$item" selected + ((x+=fields[0]-box[0])) + ((y+=fields[1]-box[1])) + else + local cols lines + ble/complete/menu#construct/.initialize-size + ble/complete/menu#render-item "$item" selected + fi + if ((y<_ble_canvas_panel_height[_ble_edit_info_panel])); then + ble/canvas/panel#goto.draw "$_ble_edit_info_panel" "${fields[@]::2}" + ble/canvas/put.draw "$ret" + _ble_canvas_x=$x _ble_canvas_y=$((infoy+y)) + fi + _ble_complete_menu_selected=$nsel + else + _ble_complete_menu_selected=-1 + value=$_ble_complete_menu_original + fi + ble/canvas/panel/load-position.draw "$pos0" + ble/canvas/bflush.draw + ble/function#try "$menu_class"/onselect "$nsel" "$osel" + return 0 +} +function ble/widget/menu/forward { + local opts=$1 + local nsel=$((_ble_complete_menu_selected+1)) + local ncand=${#_ble_complete_menu_items[@]} + if ((nsel>=ncand)); then + if [[ :$opts: == *:cyclic:* ]] && ((ncand>=2)); then + nsel=0 + else + ble/widget/.bell "menu: no more candidates" + return 1 + fi + fi + ble/complete/menu#select "$nsel" +} +function ble/widget/menu/backward { + local opts=$1 + local nsel=$((_ble_complete_menu_selected-1)) + if ((nsel<0)); then + local ncand=${#_ble_complete_menu_items[@]} + if [[ :$opts: == *:cyclic:* ]] && ((ncand>=2)); then + ((nsel=ncand-1)) + else + ble/widget/.bell "menu: no more candidates" + return 1 + fi + fi + ble/complete/menu#select "$nsel" +} +function ble/widget/menu/forward-column { + local osel=$((_ble_complete_menu_selected)) + if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate right "$osel"; then + local nsel=$ret ncand=${#_ble_complete_menu_items[@]} + if ((0<=nsel&&nsel=0)) || return 1 + local entry=${_ble_complete_menu_icons[osel-offset]} + local fields; ble/string#split fields , "${entry%%:*}" + local ox=${fields[0]} oy=${fields[1]} + local nsel=-1 + if ((oxcolumn)); then + local i=$osel + while ((--i>=offset)); do + entry=${_ble_complete_menu_icons[i-offset]} + ble/string#split fields , "${entry%%:*}" + local x=${fields[0]} y=${fields[1]} + ((y=0&&nsel!=osel)) && + ble/complete/menu#select "$nsel" +} +function ble/widget/menu/forward-line { + local offset=$_ble_complete_menu_offset + local osel=$_ble_complete_menu_selected + ((osel>=0)) || return 1 + local nsel=-1 goto_column= + if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate down "$osel"; then + nsel=$ret + else + local entry=${_ble_complete_menu_icons[osel-offset]} + local fields; ble/string#split fields , "${entry%%:*}" + local ox=${fields[0]} oy=${fields[1]} + ble/widget/menu/.check-last-column + local i=$osel nsel=-1 is_next_page= + for entry in "${_ble_complete_menu_icons[@]:osel+1-offset}"; do + ble/string#split fields , "${entry%%:*}" + local x=${fields[0]} y=${fields[1]} + ((y<=oy||y==oy+1&&x<=ox||nsel<0)) || break + ((++i,y>oy&&(nsel=i))) + done + ((nsel<0&&(is_next_page=1,nsel=offset+${#_ble_complete_menu_icons[@]}))) + ((is_next_page)) && goto_column=$ox + fi + local ncand=${#_ble_complete_menu_items[@]} + if ((0<=nsel&&nsel=0)) || return 1 + local nsel=-1 goto_column= + if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate up "$osel"; then + nsel=$ret + else + local entry=${_ble_complete_menu_icons[osel-offset]} + local fields; ble/string#split fields , "${entry%%:*}" + local ox=${fields[0]} oy=${fields[1]} + ble/widget/menu/.check-last-column + local nsel=$osel + while ((--nsel>=offset)); do + entry=${_ble_complete_menu_icons[nsel-offset]} + ble/string#split fields , "${entry%%:*}" + local x=${fields[0]} y=${fields[1]} + ((y0)); then + ble/complete/menu#select $((_ble_complete_menu_offset-1)) goto-page-top + else + ble/widget/.bell "menu: this is the first page." + return 1 + fi +} +function ble/widget/menu/forward-page { + local next=$((_ble_complete_menu_offset+${#_ble_complete_menu_icons[@]})) + if ((next<${#_ble_complete_menu_items[@]})); then + ble/complete/menu#select "$next" + else + ble/widget/.bell "menu: this is the last page." + return 1 + fi +} +function ble/widget/menu/beginning-of-page { + ble/complete/menu#select "$_ble_complete_menu_offset" +} +function ble/widget/menu/end-of-page { + local nicon=${#_ble_complete_menu_icons[@]} + ((nicon)) && ble/complete/menu#select $((_ble_complete_menu_offset+nicon-1)) +} +function ble/widget/menu/cancel { + ble/decode/keymap/pop + ble/complete/menu#clear + "$_ble_complete_menu_class"/oncancel +} +function ble/widget/menu/accept { + ble/decode/keymap/pop + ble/complete/menu#clear + local nsel=$_ble_complete_menu_selected + local hook=$_ble_complete_menu_accept_hook + _ble_complete_menu_accept_hook= + if ((nsel>=0)); then + "$_ble_complete_menu_class"/onaccept "$nsel" "${_ble_complete_menu_items[nsel]}" + else + "$_ble_complete_menu_class"/onaccept "$nsel" + fi +} +function ble-decode/keymap:menu/define { + ble-bind -f __default__ 'bell' + ble-bind -f __line_limit__ nop + ble-bind -f C-m 'menu/accept' + ble-bind -f RET 'menu/accept' + ble-bind -f C-g 'menu/cancel' + ble-bind -f 'C-x C-g' 'menu/cancel' + ble-bind -f 'C-M-g' 'menu/cancel' + ble-bind -f C-f 'menu/forward-column' + ble-bind -f right 'menu/forward-column' + ble-bind -f C-i 'menu/forward cyclic' + ble-bind -f TAB 'menu/forward cyclic' + ble-bind -f C-b 'menu/backward-column' + ble-bind -f left 'menu/backward-column' + ble-bind -f C-S-i 'menu/backward cyclic' + ble-bind -f S-TAB 'menu/backward cyclic' + ble-bind -f C-n 'menu/forward-line' + ble-bind -f down 'menu/forward-line' + ble-bind -f C-p 'menu/backward-line' + ble-bind -f up 'menu/backward-line' + ble-bind -f prior 'menu/backward-page' + ble-bind -f next 'menu/forward-page' + ble-bind -f home 'menu/beginning-of-page' + ble-bind -f end 'menu/end-of-page' +} +function ble/complete/menu.class/onaccept { + local hook=$_ble_complete_menu_accept_hook + _ble_complete_menu_accept_hook= + "$hook" "$@" +} +function ble/complete/menu.class/oncancel { + local hook=$_ble_complete_menu_cancel_hook + _ble_complete_menu_cancel_hook= + "$hook" "$@" +} +function ble/complete/menu#start { + _ble_complete_menu_accept_hook=$1; shift + _ble_complete_menu_cancel_hook= + local menu_style=linewise + local menu_items; menu_items=("$@") + local menu_class=ble/complete/menu.class menu_param= + ble/complete/menu#construct sync || return "$?" + ble/complete/menu#show + ble/complete/menu#select 0 + ble/decode/keymap/push menu + return 147 +} +function ble/complete/check-cancel { + [[ :$comp_type: != *:sync:* ]] && ble/decode/has-input +} +function ble/complete/string#escape-for-completion-context { + local str=$1 escape_flags=$2 + case $comps_flags in + (*S*) ble/string#escape-for-bash-single-quote "$str" ;; + (*E*) ble/string#escape-for-bash-escape-string "$str" ;; + (*[DI]*) ble/string#escape-for-bash-double-quote "$str" ;; + (*) + if [[ $comps_fixed ]]; then + ble/string#escape-for-bash-specialchars "$str" "b$escape_flags" + else + ble/string#escape-for-bash-specialchars "$str" "$escape_flags" + fi ;; + esac +} +function ble/complete/action/complete.addtail { + suffix=$suffix$1 +} +function ble/complete/action/complete.mark-directory { + [[ :$comp_type: == *:markdir:* && $CAND != */ ]] && + [[ ! -h $CAND || ( $insert == "$COMPS" || :$comp_type: == *:marksymdir:* ) ]] && + ble/complete/action/complete.addtail / +} +function ble/complete/action/complete.close-quotation { + case $comps_flags in + (*[SE]*) ble/complete/action/complete.addtail \' ;; + (*[DI]*) ble/complete/action/complete.addtail \" ;; + esac +} +_ble_complete_quote_insert_varnames=( + quote_action + quote_escape_flags + quote_cont_cutbackslash + quote_paramx_comps + quote_trav_prefix + quote_fixed_comps + quote_fixed_compv + quote_fixed_comps_len + quote_fixed_compv_len) +function ble/complete/action/quote-insert.initialize { + quote_action=$1 + quote_escape_flags=c + if [[ $quote_action == command ]]; then + quote_escape_flags= + elif [[ $quote_action == progcomp ]]; then + [[ $comp_opts != *:filenames:* ]] && + quote_escape_flags=${quote_escape_flags//c} + fi + [[ $comps_fixed ]] && quote_escape_flags=b$quote_escape_flags + quote_cont_cutbackslash= + [[ $comps_flags == *B* && $COMPS == *'\' ]] && + quote_cont_cutbackslash=1 + quote_paramx_comps=$COMPS + if [[ $comps_flags == *p* ]]; then + [[ $comps_flags == *B* && $quote_paramx_comps == *'\' ]] && + quote_paramx_comps=${quote_paramx_comps%'\'} + case $comps_flags in + (*[DI]*) + if [[ $COMPS =~ $rex_raw_paramx ]]; then + local rematch1=${BASH_REMATCH[1]} + quote_paramx_comps=$rematch1'${'${COMPS:${#rematch1}+1}'}' + else + quote_paramx_comps=$quote_paramx_comps'""' + fi ;; + (*) + quote_paramx_comps=$quote_paramx_comps'\' ;; + esac + fi + quote_trav_prefix= + case $comps_flags in + (*S*) quote_trav_prefix=\' ;; + (*E*) quote_trav_prefix=\$\' ;; + (*D*) quote_trav_prefix=\" ;; + (*I*) quote_trav_prefix=\$\" ;; + esac + quote_fixed_comps= + quote_fixed_compv= + quote_fixed_comps_len= + quote_fixed_compv_len= + if [[ $comps_fixed ]]; then + quote_fixed_compv=${comps_fixed#*:} + quote_fixed_compv_len=${#quote_fixed_compv} + quote_fixed_comps_len=${comps_fixed%%:*} + quote_fixed_comps=${COMPS::quote_fixed_comps_len} + fi +} +function ble/complete/action/quote-insert { + if [[ ! $quote_action ]]; then + local "${_ble_complete_quote_insert_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/action/quote-insert.initialize "${1:-plain}" + fi + local escape_flags=$quote_escape_flags + if [[ $quote_action == command ]]; then + [[ $DATA == *:noquote:* || $COMPS == "$COMPV" && ( $CAND == '[[' || $CAND == '!' ) ]] && return 0 + elif [[ $quote_action == progcomp ]]; then + [[ $comp_opts == *:noquote:* ]] && return 0 + [[ $comp_opts == *:nospace:* && $CAND == *' ' && ! -f $CAND ]] && return 0 + [[ $CAND == '~'* && ! ( $comp_opts == *:filenames:* && -e $CAND ) ]] && + escape_flags=T$escape_flags + fi + if [[ $comps_flags == *v* && $CAND == "$COMPV"* ]]; then + local ins ret + ble/complete/string#escape-for-completion-context "${CAND:${#COMPV}}" "$escape_flags"; ins=$ret + if [[ $comps_flags == *p* && $ins == [_a-zA-Z0-9]* ]]; then + INSERT=$quote_paramx_comps$ins + else + [[ $quote_cont_cutbackslash ]] && ins=${ins#'\'} + INSERT=$COMPS$ins; + fi + elif [[ $quote_fixed_comps && $CAND == "$quote_fixed_compv"* ]]; then + local ret; ble/complete/string#escape-for-completion-context "${CAND:quote_fixed_compv_len}" "$escape_flags" + INSERT=$quote_fixed_comps$quote_trav_prefix$ret + else + local ret; ble/complete/string#escape-for-completion-context "$CAND" "$escape_flags" + INSERT=$quote_trav_prefix$ret + fi +} +function ble/complete/action/quote-insert.batch/awk { + local q=\' + local -x comp_opts=$comp_opts + local -x comps=$COMPS + local -x compv=$COMPV + local -x comps_flags=$comps_flags + local -x quote_action=$quote_action + local -x quote_escape_flags=$quote_escape_flags + local -x quote_paramx_comps=$quote_paramx_comps + local -x quote_cont_cutbackslash=$quote_cont_cutbackslash + local -x quote_trav_prefix=$quote_trav_prefix + local -x quote_fixed_comps=$quote_fixed_comps + local -x quote_fixed_compv=$quote_fixed_compv + "$quote_batch_awk" -v quote_batch_nulsep="$quote_batch_nulsep" -v q="$q" ' + function exists(filename) { return substr($0, 1, 1) == "1"; } + function is_file(filename) { return substr($0, 2, 1) == "1"; } + function initialize(_, flags, comp_opts, tmp) { + IS_XPG4 = AWKTYPE == "xpg4"; + REP_SL = "\\"; + if (IS_XPG4) REP_SL = "\\\\"; + REP_DBL_SL = "\\\\"; # gawk, nawk + sub(/.*/, REP_DBL_SL, tmp); + if (tmp == "\\") REP_DBL_SL = "\\\\\\\\"; # mawk, xpg4 + Q = q "\\" q q; + DELIM = 10; + if (quote_batch_nulsep != "") { + RS = "\0"; + DELIM = 0; + } + quote_action = ENVIRON["quote_action"]; + comps = ENVIRON["comps"]; + compv = ENVIRON["compv"]; + compv_len = length(compv); + comps_flags = ENVIRON["comps_flags"]; + escape_type = 0; + if (comps_flags ~ /S/) + escape_type = 1; + else if (comps_flags ~ /E/) + escape_type = 2; + else if (comps_flags ~ /[DI]/) + escape_type = 3; + else + escape_type = 4; + comps_v = (comps_flags ~ /v/); + comps_p = (comps_flags ~ /p/); + comp_opts = ENVIRON["comp_opts"]; + is_noquote = comp_opts ~ /:noquote:/; + is_nospace = comp_opts ~ /:nospace:/; + flags = ENVIRON["quote_escape_flags"]; + escape_c = (flags ~ /c/); + escape_b = (flags ~ /b/); + escape_tilde_always = 1; + escape_tilde_exists = 0; + if (quote_action == "progcomp") { + escape_tilde_always = 0; + escape_tilde_exists = (comp_opts ~ /:filenames:/); + } + quote_cont_cutbackslash = ENVIRON["quote_cont_cutbackslash"] != ""; + quote_paramx_comps = ENVIRON["quote_paramx_comps"]; + quote_trav_prefix = ENVIRON["quote_trav_prefix"]; + quote_fixed_comps = ENVIRON["quote_fixed_comps"]; + quote_fixed_compv = ENVIRON["quote_fixed_compv"]; + quote_fixed_comps_len = length(quote_fixed_comps); + quote_fixed_compv_len = length(quote_fixed_compv); + } + BEGIN { initialize(); } + function escape_for_completion_context(text) { + if (escape_type == 1) { + gsub(/'$q'/, Q, text); + } else if (escape_type == 2) { + if (text ~ /[\\'$q'\a\b\t\n\v\f\r\033]/) { + gsub(/\\/ , REP_DBL_SL, text); + gsub(/'$q'/, REP_SL q , text); + gsub(/\007/, REP_SL "a", text); + gsub(/\010/, REP_SL "b", text); + gsub(/\011/, REP_SL "t", text); + gsub(/\012/, REP_SL "n", text); + gsub(/\013/, REP_SL "v", text); + gsub(/\014/, REP_SL "f", text); + gsub(/\015/, REP_SL "r", text); + gsub(/\033/, REP_SL "e", text); + } + } else if (escape_type == 3) { + gsub(/[\\"$`]/, "\\\\&", text); # Note: All awks behaves the same for "\\\\&" + } else if (escape_type == 4) { + gsub(/[]\\ "'$q'`$|&;<>()!^*?[]/, "\\\\&", text); + if (escape_c) gsub(/[=:]/, "\\\\&", text); + if (escape_b) gsub(/[{,}]/, "\\\\&", text); + if (ret ~ /^~/ && (escape_tilde_always || escape_tilde_exists && exists(cand))) + text = "\\" text; + gsub(/\n/, "$" q REP_SL "n" q, text); + gsub(/\t/, "$" q REP_SL "t" q, text); + } + return text; + } + function quote_insert(cand) { + if (quote_action == "command") { + if (comps == compv && cand ~ /^(\[\[|]]|!)$/) return cand; + } else if (quote_action == "progcomp") { + if (is_noquote) return cand; + if (is_nospace && cand ~ / $/ && !is_file(cand)) return cand; + } + if (comps_v && substr(cand, 1, compv_len) == compv) { + ins = escape_for_completion_context(substr(cand, compv_len + 1)); + if (comps_p && ins ~ /^[_a-zA-Z0-9]/) { + return quote_paramx_comps ins; + } else { + if (quote_cont_cutbackslash) sub(/^\\/, "", ins); + return comps ins; + } + } else if (quote_fixed_comps_len && substr(cand, 1, quote_fixed_compv_len) == quote_fixed_compv) { + ins = substr(cand, quote_fixed_compv_len + 1); + return quote_fixed_comps quote_trav_prefix escape_for_completion_context(ins); + } else { + return quote_trav_prefix escape_for_completion_context(cand); + } + } + { + cand = substr($0, 3); + insert = quote_insert(cand); + printf("%s%c", insert, DELIM); + } + ' +} +function ble/complete/action/quote-insert.batch/proc { + local _ble_local_tmpfile; ble/util/assign/.mktmp + local delim='\n' + [[ $quote_batch_nulsep ]] && delim='\0' + if [[ $quote_action == progcomp ]]; then + local cand file exist + for cand in "${cands[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + f=0 e=0 + [[ -e $cand ]] && e=1 + [[ -f $cand ]] && f=1 + printf "$e$f%s$delim" "$cand" + done + else + printf "00%s$delim" "${cands[@]}" + fi >| "$_ble_local_tmpfile" + local fname_cands=$_ble_local_tmpfile + ble/util/conditional-sync \ + 'ble/complete/action/quote-insert.batch/awk < "$fname_cands"' \ + '! ble/complete/check-cancel < /dev/tty' '' progressive-weight + local ext=$? + ble/util/assign/.rmtmp + return "$ext" +} +function ble/complete/action/quote-insert.batch { + local opts=$1 + local quote_batch_nulsep= + local quote_batch_awk=ble/bin/awk + if [[ :$opts: != *:newline:* ]]; then + if ((_ble_bash>=40400)); then + if [[ $_ble_bin_awk_type == [mg]awk ]]; then + quote_batch_nulsep=1 + elif ble/bin#has mawk; then + quote_batch_nulsep=1 + quote_batch_awk=mawk + elif ble/bin#has gawk; then + quote_batch_nulsep=1 + quote_batch_awk=gawk + fi + fi + [[ ! $quote_batch_nulsep ]] && + [[ "${cands[*]}" == *$'\n'* ]] && + return 1 + fi + if [[ $quote_batch_nulsep ]]; then + ble/util/assign-array0 inserts ble/complete/action/quote-insert.batch/proc + else + ble/util/assign-array inserts ble/complete/action/quote-insert.batch/proc + fi + return $? +} +function ble/complete/action/requote-final-insert { + local comps_prefix= check_optarg= + if [[ $insert == "$COMPS"* ]]; then + [[ $comps_flags == *[SEDI]* ]] && return 0 + [[ $COMPS != *[!':/={,'] ]] && comps_prefix=$COMPS + check_optarg=$COMPS + else + check_optarg=$insert + fi + if [[ $check_optarg ]]; then + if ble/string#match "$check_optarg" '^([_a-zA-Z][_a-zA-Z0-9]*|-[-a-zA-Z0-9.]+)=(([^\'\''"`${}]*|\\.)*:)?'; then + comps_prefix=$BASH_REMATCH + elif [[ $COMP_PREFIX == -[!'-=:/\'\''"$`{};&|<>!^{}'] && $check_optarg == "$COMP_PREFIX"* ]]; then + comps_prefix=${check_optarg::2} + fi + fi + if [[ $comps_fixed ]]; then + local comps_fixed_part=${COMPS::${comps_fixed%%:*}} + [[ $comps_prefix == "$comps_fixed_part"* ]] || + comps_prefix=$comps_fixed_part + fi + if [[ $insert == "$comps_prefix"* && $comps_prefix != *[!':/={,'] ]]; then + local ret ins=${insert:${#comps_prefix}} + if ! ble/syntax:bash/simple-word/is-literal "$ins" && + ble/syntax:bash/simple-word/is-simple "$ins" && + ble/syntax:bash/simple-word/eval "$ins" && + ((${#ret[@]}==1)) + then + ble/string#quote-word "$ret" quote-empty + ((${#ret}<=${#ins})) || return 0 + insert=$comps_prefix$ret + [[ $insert == "$COMPS"* ]] || insert_flags=r$insert_flags # 遡って書き換えた + fi + fi + return 0 +} +function ble/complete/action#inherit-from { + local dst=$1 src=$2 + local member srcfunc dstfunc + for member in initialize{,.batch} complete getg get-desc; do + srcfunc=ble/complete/action:$src/$member + dstfunc=ble/complete/action:$dst/$member + ble/is-function "$srcfunc" && builtin eval "function $dstfunc { $srcfunc; }" + done +} +function ble/complete/action:plain/initialize { + ble/complete/action/quote-insert +} +function ble/complete/action:plain/initialize.batch { + ble/complete/action/quote-insert.batch +} +function ble/complete/action:plain/complete { + ble/complete/action/requote-final-insert +} +function ble/complete/action:literal-substr/initialize { :; } +function ble/complete/action:literal-substr/initialize.batch { inserts=("${cands[@]}"); } +function ble/complete/action:literal-substr/complete { :; } +function ble/complete/action:substr/initialize { + ble/complete/action/quote-insert +} +function ble/complete/action:substr/initialize.batch { + ble/complete/action/quote-insert.batch +} +function ble/complete/action:substr/complete { + ble/complete/action/requote-final-insert +} +function ble/complete/action:literal-word/initialize { :; } +function ble/complete/action:literal-word/initialize.batch { inserts=("${cands[@]}"); } +function ble/complete/action:literal-word/complete { + if [[ $comps_flags == *x* ]]; then + ble/complete/action/complete.addtail ',' + else + ble/complete/action/complete.addtail ' ' + fi +} +function ble/complete/action:word/initialize { + ble/complete/action/quote-insert +} +function ble/complete/action:word/initialize.batch { + ble/complete/action/quote-insert.batch +} +function ble/complete/action:word/complete { + ble/complete/action/requote-final-insert + ble/complete/action/complete.close-quotation + ble/complete/action:literal-word/complete +} +function ble/complete/action:word/get-desc { + [[ $DATA ]] && desc=$DATA +} +function ble/complete/action:file/initialize { + ble/complete/action/quote-insert +} +function ble/complete/action:file/initialize.batch { + ble/complete/action/quote-insert.batch +} +function ble/complete/action:file/complete { + ble/complete/action/requote-final-insert + if [[ -e $CAND || -h $CAND ]]; then + if [[ -d $CAND ]]; then + ble/complete/action/complete.mark-directory + else + ble/complete/action:word/complete + fi + fi +} +function ble/complete/action:file/init-menu-item { + ble/syntax/highlight/getg-from-filename "$CAND" + [[ $g ]] || { local ret; ble/color/face2g filename_warning; g=$ret; } + if [[ :$comp_type: == *:vstat:* ]]; then + if [[ -h $CAND ]]; then + suffix='@' + elif [[ -d $CAND ]]; then + suffix='/' + elif [[ -x $CAND ]]; then + suffix='*' + fi + fi +} +function ble/complete/action:file_rhs/initialize { + ble/complete/action:file/initialize +} +function ble/complete/action:file_rhs/initialize.batch { + ble/complete/action:file/initialize.batch +} +function ble/complete/action:file_rhs/complete { + CAND=${CAND:${#DATA}} ble/complete/action:file/complete +} +function ble/complete/action:file_rhs/init-menu-item { + CAND=${CAND:${#DATA}} ble/complete/action:file/init-menu-item +} +_ble_complete_action_file_desc[_ble_attr_FILE_LINK]='symbolic link' +_ble_complete_action_file_desc[_ble_attr_FILE_ORPHAN]='symbolic link (orphan)' +_ble_complete_action_file_desc[_ble_attr_FILE_DIR]='directory' +_ble_complete_action_file_desc[_ble_attr_FILE_STICKY]='directory (sticky)' +_ble_complete_action_file_desc[_ble_attr_FILE_SETUID]='file (setuid)' +_ble_complete_action_file_desc[_ble_attr_FILE_SETGID]='file (setgid)' +_ble_complete_action_file_desc[_ble_attr_FILE_EXEC]='file (executable)' +_ble_complete_action_file_desc[_ble_attr_FILE_FILE]='file' +_ble_complete_action_file_desc[_ble_attr_FILE_CHR]='character device' +_ble_complete_action_file_desc[_ble_attr_FILE_FIFO]='named pipe' +_ble_complete_action_file_desc[_ble_attr_FILE_SOCK]='socket' +_ble_complete_action_file_desc[_ble_attr_FILE_BLK]='block device' +_ble_complete_action_file_desc[_ble_attr_FILE_URL]='URL' +function ble/complete/action:file/get-desc { + local type; ble/syntax/highlight/filetype "$CAND" + desc=${_ble_complete_action_file_desc[type]:-'file (???)'} +} +function ble/complete/action:progcomp/initialize/.reconstruct-from-noquote { + local simple_flags simple_ibrace ret count + ble/syntax:bash/simple-word/is-simple-or-open-simple "$INSERT" && + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$INSERT" && + ble/complete/source/eval-simple-word "$ret" single:count && + ((count==1)) || return 0 + CAND=$ret + if [[ $quote_fixed_comps && $CAND == "$quote_fixed_compv"* ]]; then + local ret; ble/complete/string#escape-for-completion-context "${CAND:quote_fixed_compv_len}" "$escape_flags" + INSERT=$quote_fixed_comps$quote_trav_prefix$ret + return 3 + fi + return 0 +} +function ble/complete/action:progcomp/initialize { + if [[ :$DATA: == *:noquote:* ]]; then + local progcomp_resolve_brace=$quote_fixed_comps + [[ :$DATA: == *:ble/syntax-raw:* ]] && progcomp_resolve_brace= + ble/complete/action:progcomp/initialize/.reconstruct-from-noquote + return 0 + else + ble/complete/action/quote-insert progcomp + fi +} +function ble/complete/action:progcomp/initialize.batch { + if [[ :$DATA: == *:noquote:* ]]; then + inserts=("${cands[@]}") + local progcomp_resolve_brace=$quote_fixed_comps + [[ :$DATA: == *:ble/syntax-raw:* ]] && progcomp_resolve_brace= + cands=() + local INSERT simple_flags simple_ibrace ret count icand=0 + for INSERT in "${inserts[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + local CAND=$INSERT + ble/complete/action:progcomp/initialize/.reconstruct-from-noquote || + inserts[icand]=$INSERT # INSERT を上書きした時 ($?==3) + cands[icand++]=$CAND + done + else + ble/complete/action/quote-insert.batch newline + fi +} +function ble/complete/action:progcomp/complete { + if [[ $DATA == *:filenames:* ]]; then + ble/complete/action:file/complete + else + if [[ $DATA != *:ble/no-mark-directories:* && -d $CAND ]]; then + ble/complete/action/requote-final-insert + ble/complete/action/complete.mark-directory + else + ble/complete/action:word/complete + fi + fi + [[ $DATA == *:nospace:* ]] && suffix=${suffix%' '} + [[ $DATA == *:ble/no-mark-directories:* && -d $CAND ]] && suffix=${suffix%/} +} +function ble/complete/action:progcomp/init-menu-item { + if [[ $DATA == *:filenames:* ]]; then + ble/complete/action:file/init-menu-item + fi +} +function ble/complete/action:progcomp/get-desc { + if [[ $DATA == *:filenames:* ]]; then + ble/complete/action:file/get-desc + fi +} +function ble/complete/action:command/initialize { + ble/complete/action/quote-insert command +} +function ble/complete/action:command/initialize.batch { + ble/complete/action/quote-insert.batch newline +} +function ble/complete/action:command/complete { + if [[ -d $CAND ]]; then + ble/complete/action/complete.mark-directory + elif ! type "$CAND" &>/dev/null; then + if [[ $CAND == */ ]]; then + insert_flags=${insert_flags}n + fi + else + ble/complete/action:word/complete + fi +} +function ble/complete/action:command/init-menu-item { + if [[ -d $CAND ]]; then + local ret; ble/color/face2g filename_directory; g=$ret + else + local type + if [[ $CAND != "$INSERT" ]]; then + ble/syntax/highlight/cmdtype "$CAND" "$INSERT" + else + local type; ble/util/type type "$CAND" + ble/syntax/highlight/cmdtype1 "$type" "$CAND" + fi + if [[ $CAND == */ ]] && ((type==_ble_attr_ERR)); then + type=_ble_attr_CMD_FUNCTION + fi + ble/syntax/attr2g "$type" + fi +} +_ble_complete_action_command_desc[_ble_attr_CMD_BOLD]=builtin +_ble_complete_action_command_desc[_ble_attr_CMD_BUILTIN]=builtin +_ble_complete_action_command_desc[_ble_attr_CMD_ALIAS]=alias +_ble_complete_action_command_desc[_ble_attr_CMD_FUNCTION]=function +_ble_complete_action_command_desc[_ble_attr_CMD_FILE]=file +_ble_complete_action_command_desc[_ble_attr_KEYWORD]=command +_ble_complete_action_command_desc[_ble_attr_CMD_JOBS]=job +_ble_complete_action_command_desc[_ble_attr_ERR]='command ???' +_ble_complete_action_command_desc[_ble_attr_CMD_DIR]=directory +function ble/complete/action:command/get-desc { + local title= value= + if [[ -d $CAND ]]; then + title=directory + else + local type; ble/util/type type "$CAND" + ble/syntax/highlight/cmdtype1 "$type" "$CAND" + case $type in + ($_ble_attr_CMD_ALIAS) + local ret + ble/alias#expand "$CAND" + title=alias value=$ret ;; + ($_ble_attr_CMD_FILE) + local path; ble/util/assign path 'type -p -- "$CAND"' + [[ $path == ?*/"$CAND" ]] && path="from ${path%/"$CAND"}" + title=file value=$path ;; + ($_ble_attr_CMD_FUNCTION) + local source lineno + ble/function#get-source-and-lineno "$CAND" + local def; ble/function#getdef "$CAND" + ble/string#match "$def" '^[^()]*\(\)[[:space:]]*\{[[:space:]]+(.*[^[:space:]])[[:space:]]+\}[[:space:]]*$' && + def=${BASH_REMATCH[1]} # 関数の中身を抽出する + local ret sgr0=$'\e[27m' sgr1=$'\e[7m' # Note: sgr-ansi で生成 + lines=1 cols=${COLUMNS:-80} x=0 y=0 ble/canvas/trace-text "$def" external-sgr + title=function value="${source##*/}:$lineno $desc_sgrq$ret" ;; + ($_ble_attr_CMD_JOBS) + ble/util/joblist.check + local job; ble/util/assign job 'jobs -- "$CAND" 2>/dev/null' || job='???' + title=job value=${job:-(ambiguous)} ;; + ($_ble_attr_ERR) + if [[ $CAND == */ ]]; then + title='function namespace' + else + title=${_ble_complete_action_command_desc[_ble_attr_ERR]} + fi ;; + (*) + title=${_ble_complete_action_command_desc[type]:-'???'} ;; + esac + fi + desc=${title:+$desc_sgrt($title)$desc_sgr0}${value:+ $value} +} +function ble/complete/action:variable/initialize { ble/complete/action/quote-insert; } +function ble/complete/action:variable/initialize.batch { ble/complete/action/quote-insert.batch newline; } +function ble/complete/action:variable/complete { + case $DATA in + (assignment) + ble/complete/action/complete.addtail '=' ;; + (braced) + ble/complete/action/complete.addtail '}' ;; + (word) ble/complete/action:word/complete ;; + (arithmetic|nosuffix) ;; # do nothing + esac +} +function ble/complete/action:variable/init-menu-item { + local ret; ble/color/face2g syntax_varname; g=$ret +} +function ble/complete/action:variable/get-desc { + local _ble_local_title=variable + if ble/is-array "$CAND"; then + _ble_local_title=array + elif ble/is-assoc "$CAND"; then + _ble_local_title=assoc + fi + local _ble_local_value= + if [[ $_ble_local_title == array || $_ble_local_title == assoc ]]; then + builtin eval "local count=\${#$CAND[@]}" + if ((count==0)); then + count=empty + else + count="$count items" + fi + _ble_local_value=$'\e[94m['$count$']\e[m' + else + local ret; ble/string#quote-word "${!CAND}" ansi:sgrq="$desc_sgrq":quote-empty + _ble_local_value=$ret + fi + desc="$desc_sgrt($_ble_local_title)$desc_sgr0 $_ble_local_value" +} +function ble/complete/source/test-limit { + local value=$1 limit= + if [[ :$comp_type: == *:auto_menu:* && $bleopt_complete_limit_auto_menu ]]; then + limit=$bleopt_complete_limit_auto_menu + elif [[ :$comp_type: == *:auto:* && $bleopt_complete_limit_auto ]]; then + limit=$bleopt_complete_limit_auto + else + limit=$bleopt_complete_limit + fi + if [[ $limit && value -gt limit ]]; then + cand_limit_reached=1 + [[ :$comp_type: == *:auto_menu: ]] && cand_limit_reached=cancel + return 1 + else + return 0 + fi +} +function ble/complete/source/eval-simple-word { + local word=$1 opts=$2 + if [[ :$comp_type: != *:sync:* && :$opts: != *:noglob:* ]]; then + opts=$opts:stopcheck:cached + [[ :$comp_type: == *:auto:* && $bleopt_complete_timeout_auto ]] && + opts=$opts:timeout=$((bleopt_complete_timeout_auto)) + fi + ble/syntax:bash/simple-word/eval "$word" "$opts"; local ext=$? + ((ext==142)) && return 148 + return "$ext" +} +function ble/complete/source/evaluate-path-spec { + local word=$1 sep=$2 opts=$3 + if [[ :$comp_type: != *:sync:* && :$opts: != *:noglob:* ]]; then + opts=$opts:stopcheck:cached:single + [[ :$comp_type: == *:auto:* && $bleopt_complete_timeout_auto ]] && + opts=$opts:timeout=$((bleopt_complete_timeout_auto)) + fi + ble/syntax:bash/simple-word/evaluate-path-spec "$word" "$sep" "$opts"; local ext=$? + ((ext==142)) && return 148 + return "$ext" +} +function ble/complete/source/reduce-compv-for-ambiguous-match { + [[ :$comp_type: == *:[maA]:* ]] || return 0 + local comps=$COMPS compv=$COMPV + local comps_prefix= compv_prefix= + if [[ $comps_fixed ]]; then + comps_prefix=${comps::${comps_fixed%%:*}} + compv_prefix=${comps_fixed#*:} + compv=${COMPV:${#compv_prefix}} + fi + case $comps_flags in + (*S*) comps_prefix=$comps_prefix\' ;; + (*E*) comps_prefix=$comps_prefix\$\' ;; + (*D*) comps_prefix=$comps_prefix\" ;; + (*I*) comps_prefix=$comps_prefix\$\" ;; + esac + if [[ $compv && :$comp_type: == *:a:* ]]; then + compv=${compv::1} + ble/complete/string#escape-for-completion-context "$compv" + comps=$ret + else + compv= comps= + fi + COMPV=$compv_prefix$compv + COMPS=$comps_prefix$comps +} +_ble_complete_yield_varnames=("${_ble_complete_quote_insert_varnames[@]}") +function ble/complete/cand/yield.initialize { + ble/complete/action/quote-insert.initialize "$1" +} +function ble/complete/cand/yield { + local ACTION=$1 CAND=$2 DATA=$3 + [[ $flag_force_fignore ]] && ! ble/complete/.fignore/filter "$CAND" && return 0 + [[ $flag_source_filter ]] || ble/complete/candidates/filter#test "$CAND" || return 0 + local INSERT=$CAND + ble/complete/action:"$ACTION"/initialize || return "$?" + local PREFIX_LEN=0 + [[ $CAND == "$COMP_PREFIX"* ]] && PREFIX_LEN=${#COMP_PREFIX} + local icand + ((icand=cand_count++)) + cand_cand[icand]=$CAND + cand_word[icand]=$INSERT + cand_pack[icand]=$ACTION:${#CAND},${#INSERT},$PREFIX_LEN:$CAND$INSERT$DATA +} +function ble/complete/cand/yield.batch { + local ACTION=$1 DATA=$2 + local inserts threshold=500 + [[ $OSTYPE == cygwin* || $OSTYPE == msys* ]] && threshold=2000 + if ((${#cands[@]}>=threshold)) && ble/function#try ble/complete/action:"$ACTION"/initialize.batch; then + local i n=${#cands[@]} + for ((i=0;i0)); then + ble/array#push joblist %% %+ + ((job_count>=2)) && + ble/array#push joblist %- + fi + builtin compgen -W '"${joblist[@]}"' -- "$compv_quoted" + fi +} +function ble/complete/source:command { + [[ $comps_flags == *v* ]] || return 1 + [[ ! $COMPV ]] && shopt -q no_empty_cmd_completion && return 1 + [[ $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} + local arg=$1 + { + local old_cand_count=$cand_count + local comp_opts=: + ble/complete/source:argument/.generate-user-defined-completion initial; local ext=$? + ((ext==148)) && return "$ext" + if ((ext==0)); then + ((cand_count>old_cand_count)) && return "$ext" + fi + } + ble/complete/source:sabbrev + local arr + local compgen + ble/util/assign compgen 'ble/complete/source:command/gen "$arg"' + [[ $compgen ]] || return 1 + ble/util/assign-array arr 'ble/bin/sort -u <<< "$compgen"' # 1 fork/exec + ble/complete/source/test-limit ${#arr[@]} || return 1 + local action=command "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize "$action" + local is_quoted= + [[ $COMPS != "$COMPV" ]] && is_quoted=1 + local rex_keyword='^(if|then|else|elif|fi|case|esac|for|select|while|until|do|done|function|time|[!{}]|\[\[|coproc|\]\]|in)$' + local expand_aliases= + shopt -q expand_aliases && expand_aliases=1 + local cand icand=0 cands + for cand in "${arr[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + [[ $cand != */ && -d $cand ]] && ! type "$cand" &>/dev/null && continue + if [[ $is_quoted ]]; then + local disable_count= + [[ $cand =~ $rex_keyword ]] && ((disable_count++)) + [[ $expand_aliases ]] && ble/is-alias "$cand" && ((disable_count++)) + if [[ $disable_count ]]; then + local type; ble/util/type type "$cand" + ((${#type[@]}>disable_count)) || continue + fi + else + [[ $cand == ']]' || $cand == in ]] && + ! { [[ $expand_aliases ]] && ble/is-alias "$cand"; } && + continue + if [[ ! $expand_aliases ]]; then + ble/is-alias "$cand" && ! type "$cand" &>/dev/null && continue + fi + if ble/string#match "$cand" '[][*?{,}!^~#]' && ble/is-alias "$cand"; then + ble/complete/cand/yield "$action" "$cand" :noquote: + continue + fi + fi + cands[icand++]=$cand + done + ble/complete/cand/yield.batch "$action" +} +function ble/complete/util/eval-pathname-expansion/.print-def { + local pattern=$1 ret + IFS= builtin eval "ret=($pattern)" 2>/dev/null + ble/string#quote-words "${ret[@]}" + ble/util/print "ret=($ret)" +} +function ble/complete/util/eval-pathname-expansion { + local pattern=$1 + local -a dtor=() + if [[ -o noglob ]]; then + set +f + ble/array#push dtor 'set -f' + fi + if ! shopt -q nullglob; then + shopt -s nullglob + ble/array#push dtor 'shopt -u nullglob' + fi + if ! shopt -q dotglob; then + shopt -s dotglob + ble/array#push dtor 'shopt -u dotglob' + else + ble/array#push dtor 'shopt -s dotglob' + fi + if ! shopt -q extglob; then + shopt -s extglob + ble/array#push dtor 'shopt -u extglob' + fi + if [[ :$comp_type: == *:i:* ]]; then + if ! shopt -q nocaseglob; then + shopt -s nocaseglob + ble/array#push dtor 'shopt -u nocaseglob' + fi + else + if shopt -q nocaseglob; then + shopt -u nocaseglob + ble/array#push dtor 'shopt -s nocaseglob' + fi + fi + if ble/util/is-cygwin-slow-glob "$pattern"; then # Note: #D1168 + if shopt -q failglob &>/dev/null || shopt -q nullglob &>/dev/null; then + pattern= + else + set -f + ble/array#push dtor 'set +f' + fi + fi + if [[ $GLOBIGNORE ]]; then + local GLOBIGNORE_save=$GLOBIGNORE + GLOBIGNORE= + ble/array#push dtor 'GLOBIGNORE=$GLOBIGNORE_save' + fi + ble/array#reverse dtor + ret=() + if [[ :$comp_type: == *:sync:* ]]; then + IFS= builtin eval "ret=($pattern)" 2>/dev/null + else + local sync_command='ble/complete/util/eval-pathname-expansion/.print-def "$pattern"' + local sync_opts=progressive-weight + [[ :$comp_type: == *:auto:* && $bleopt_complete_timeout_auto ]] && + sync_opts=$sync_opts:timeout=$((bleopt_complete_timeout_auto)) + local def + ble/util/assign def 'ble/util/conditional-sync "$sync_command" "" "" "$sync_opts"' &>/dev/null; local ext=$? + if ((ext==148)) || ble/complete/check-cancel; then + ble/util/invoke-hook dtor + return 148 + fi + builtin eval -- "$def" + fi 2>&$_ble_util_fd_stderr + ble/util/invoke-hook dtor + return 0 +} +function ble/complete/source:file/.construct-ambiguous-pathname-pattern { + local path=$1 fixlen=${2:-1} + local pattern= i=0 j + local names; ble/string#split names / "$1" + local name + for name in "${names[@]}"; do + ((i++)) && pattern=$pattern/ + if [[ $name ]]; then + ble/string#quote-word "${name::fixlen}" + pattern=$pattern$ret* + for ((j=fixlen;j<${#name};j++)); do + ble/string#quote-word "${name:j:1}" + if [[ $_ble_bash -lt 50000 && $pattern == *\* ]]; then + pattern=$pattern'([!'$ret'])' + fi + pattern=$pattern$ret* + done + fi + done + [[ $pattern == *'*' ]] || pattern=$pattern* + ret=$pattern +} +function ble/complete/source:file/.construct-pathname-pattern { + local path=$1 pattern + case :$comp_type: in + (*:a:*) ble/complete/source:file/.construct-ambiguous-pathname-pattern "$path"; pattern=$ret ;; + (*:A:*) ble/complete/source:file/.construct-ambiguous-pathname-pattern "$path" 0; pattern=$ret ;; + (*:m:*) ble/string#quote-word "$path"; pattern=*$ret* ;; + (*) ble/string#quote-word "$path"; pattern=$ret* + esac + ret=$pattern +} +function ble/complete/source:file/.impl { + local opts=$1 + [[ $comps_flags == *v* ]] || return 1 + [[ :$comp_type: != *:[maA]:* && $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} + [[ :$comp_type: == *:[maA]:* && ! $COMPV ]] && return 1 + ble/complete/source:tilde; local ext=$? + ((ext==148||ext==0)) && return "$ext" + local -a candidates=() + local ret + ble/complete/source:file/.construct-pathname-pattern "$COMPV" + [[ :$opts: == *:directory:* ]] && ret=$ret/ + ble/complete/util/eval-pathname-expansion "$ret"; (($?==148)) && return 148 + ble/complete/source/test-limit ${#ret[@]} || return 1 + if [[ :$opts: == *:directory:* ]]; then + candidates=("${ret[@]%/}") + else + candidates=("${ret[@]}") + fi + [[ :$opts: == *:no-fd:* ]] && + ble/array#remove-by-regex candidates '^[0-9]+-?$|^-$' + local flag_source_filter=1 + ble/complete/cand/yield-filenames file "${candidates[@]}" +} +function ble/complete/source:file { + ble/complete/source:file/.impl "$1" +} +function ble/complete/source:dir { + ble/complete/source:file/.impl "directory:$1" +} +function ble/complete/source:rhs { ble/complete/source:file; } +function ble/complete/action:tilde/initialize { + CAND=${CAND#\~} ble/complete/action/quote-insert + INSERT=\~$INSERT + local rex='^~[^/'\''"$`\!:]*$'; [[ $INSERT =~ $rex ]] +} +function ble/complete/action:tilde/complete { + ble/complete/action/complete.mark-directory +} +function ble/complete/action:tilde/init-menu-item { + local ret + ble/color/face2g filename_directory; g=$ret +} +function ble/complete/action:tilde/get-desc { + if [[ $CAND == '~+' ]]; then + desc='current directory (tilde expansion)' + elif [[ $CAND == '~-' ]]; then + desc='previous directory (tilde expansion)' + elif local rex='^~[0-9]$'; [[ $CAND =~ $rex ]]; then + desc='DIRSTACK directory (tilde expansion)' + else + desc='user directory (tilde expansion)' + fi +} +function ble/complete/source:tilde/.generate { + local pattern=${COMPS#\~} + [[ :$comp_type: == *:[maA]:* ]] && pattern= + builtin compgen -P \~ -u -- "$pattern" + printf '%s\n' '~' '~+' '~-' + local dirstack_max=$((${#DIRSTACK[@]}-1)) + ((dirstack_max>=0)) && + builtin eval "printf '%s\n' '~'{0..$dirstack_max}" +} +function ble/complete/source:tilde { + local rex='^~[^/'\''"$`\!:]*$'; [[ $COMPS =~ $rex ]] || return 1 + local compgen candidates + ble/util/assign compgen ble/complete/source:tilde/.generate + [[ $compgen ]] || return 1 + ble/util/assign-array candidates 'ble/bin/sort -u <<< "$compgen"' + local flag_source_filter=1 + if [[ $COMPS == '~'?* ]]; then + local filter_type=$comp_filter_type + [[ $filter_type == none ]] && filter_type=head + local comp_filter_type + local comp_filter_pattern + ble/complete/candidates/filter#init "$filter_type" "$COMPS" + ble/array#filter candidates ble/complete/candidates/filter#test + fi + ((${#candidates[@]})) || return 1 + local old_cand_count=$cand_count + ble/complete/cand/yield-filenames tilde "${candidates[@]}"; local ext=$? + return $((ext?ext:cand_count>old_cand_count)) +} +function ble/complete/source:fd { + IFS=: builtin eval 'local fdlist=":${_ble_util_openat_fdlist[*]}:"' + [[ $comp_filter_type == none ]] && + local comp_filter_type=head + local old_cand_count=$cand_count + local action=word "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize "$action" + ble/complete/cand/yield "$action" - + if [[ -d /proc/self/fd ]]; then + local ret + ble/complete/util/eval-pathname-expansion '/proc/self/fd/*' + local fd + for fd in "${ret[@]}"; do + fd=${fd#/proc/self/fd/} + [[ ${fd//[0-9]} ]] && continue + [[ $fdlist == *:"$fd":* ]] && continue + ble/complete/cand/yield "$action" "$fd" + ble/complete/cand/yield "$action" "$fd-" + done + else + local fd + for ((fd=0;fd<10;fd++)); do + ble/fd#is-open "$fd" || continue + ble/complete/cand/yield "$action" "$fd" + ble/complete/cand/yield "$action" "$fd-" + done + fi + return $((cand_count>old_cand_count)) +} +function ble/complete/progcomp/.compvar-initialize-wordbreaks { + local ifs=$_ble_term_IFS q=\'\" delim=';&|<>()' glob='[*?' hist='!^{' esc='`$\' + local escaped=$ifs$q$delim$glob$hist$esc + wordbreaks=${COMP_WORDBREAKS//[$escaped]} # =: +} +function ble/complete/progcomp/.compvar-perform-wordbreaks { + local word=$1 + if [[ ! $word ]]; then + ret=('') + return 0 + fi + ret=() + while local head=${word%%["$wordbreaks"]*}; [[ $head != $word ]]; do + ble/array#push ret "$head" + word=${word:${#head}} + head=${word%%[!"$wordbreaks"]*} + ble/array#push ret "$head" + word=${word:${#head}} + done + ble/array#push ret "$word" +} +function ble/complete/progcomp/.compvar-eval-word { + local opts=$2:single + if [[ :$opts: == *:noglob:* ]]; then + ble/syntax:bash/simple-word/eval "$1" "$opts" + else + [[ $bleopt_complete_timeout_compvar ]] && + opts=timeout=$((bleopt_complete_timeout_compvar)):retry-noglob-on-timeout:$opts + ble/complete/source/eval-simple-word "$1" "$opts" + fi +} +function ble/complete/progcomp/.compvar-generate-subwords/impl1 { + local word=$1 ret simple_flags simple_ibrace + if [[ $point ]]; then + local left=${word::point} right=${word:point} + else + local left=$word right= + local point= # hide + fi + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$left" || return 1 + left=$ret + if [[ $right ]]; then + case $simple_flags in + (*I*) right=\$\"$right ;; + (*D*) right=\"$right ;; + (*E*) right=\$\'$right ;; + (*S*) right=\'$right ;; + (*B*) right=\\$right ;; + esac + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$right" || return 1 + right=$ret + fi + point=0 words=() + local eval_opts=noglob + ((${#ret[@]}==1)) && eval_opts= + ble/syntax:bash/simple-word#break-word "$left" + local subword + for subword in "${ret[@]}"; do + ble/complete/progcomp/.compvar-eval-word "$subword" "$eval_opts" + ble/array#push words "$ret" + ((point+=${#ret})) + done + if [[ $right ]]; then + ble/syntax:bash/simple-word#break-word "$right" + local subword isfirst=1 + for subword in "${ret[@]}"; do + ble/complete/progcomp/.compvar-eval-word "$subword" noglob + if [[ $isfirst ]]; then + isfirst= + local iword=${#words[@]}; ((iword&&iword--)) + words[iword]=${words[iword]}$ret + else + ble/array#push words "$ret" + fi + done + fi + return 0 +} +function ble/complete/progcomp/.compvar-generate-subwords/impl2 { + local word=$1 + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$word" || return 1 + ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148; local value1=$ret + if [[ $point ]]; then + if ((point==${#word})); then + point=${#value1} + elif ble/syntax:bash/simple-word/reconstruct-incomplete-word "${word::point}"; then + ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148 + point=${#ret} + fi + fi + ble/complete/progcomp/.compvar-perform-wordbreaks "$value1"; words=("${ret[@]}") + return 0 +} +function ble/complete/progcomp/.compvar-generate-subwords { + local word1=$1 ret simple_flags simple_ibrace + if [[ ! $word1 ]]; then + subword_flags=E + words=('') + elif [[ $word1 == '~' ]]; then + subword_flags=Q + words=('~') + elif ble/complete/progcomp/.compvar-generate-subwords/impl1 "$word1"; then + subword_flags=E + elif ble/complete/progcomp/.compvar-generate-subwords/impl2 "$word1"; then + subword_flags=E + else + ble/complete/progcomp/.compvar-perform-wordbreaks "$word1"; words=("${ret[@]}") + fi +} +function ble/complete/progcomp/.compvar-quote-subword { + local word=$1 to_quote= is_evaluated= is_quoted= + if [[ $subword_flags == *[EQ]* ]]; then + [[ $subword_flags == *E* ]] && to_quote=1 + elif ble/syntax:bash/simple-word/reconstruct-incomplete-word "$word"; then + is_evaluated=1 + ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148; word=$ret + to_quote=1 + fi + if [[ $to_quote ]]; then + local shell_specialchars=']\ ["'\''`$|&;<>()*?{}!^'$'\n\t' q="'" Q="'\''" qq="''" + if ((index>0)) && [[ $word == *["$shell_specialchars"]* || $word == [#~]* ]]; then + is_quoted=1 + word="'${w//$q/$Q}'" word=${word#"$qq"} word=${word%"$qq"} + fi + fi + if [[ $p && $word != "$1" ]]; then + if ((p==${#1})); then + p=${#word} + else + local left=${word::p} + if [[ $is_evaluated ]]; then + if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$left"; then + ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148; left=$ret + fi + fi + if [[ $is_quoted ]]; then + left="'${left//$q/$Q}" left=${left#"$qq"} + fi + p=${#left} + fi + fi + ret=$word +} +function ble/complete/progcomp/.compvar-initialize { + COMP_TYPE=9 + COMP_KEY=9 + ((${#KEYS[@]})) && COMP_KEY=${KEYS[${#KEYS[@]}-1]:-9} # KEYS defined in ble-decode/widget/.call-keyseq + local wordbreaks + ble/complete/progcomp/.compvar-initialize-wordbreaks + progcomp_prefix= + COMP_CWORD= + COMP_POINT= + COMP_LINE= + COMP_WORDS=() + local ret simple_flags simple_ibrace + local word1 index=0 offset=0 sep= + for word1 in "${comp_words[@]}"; do + local point=$((comp_point-offset)) + ((0<=point&&point<=${#word1})) || point= + ((offset+=${#word1})) + local words subword_flags= + ble/complete/progcomp/.compvar-generate-subwords "$word1" + local w wq i=0 o=0 p + for w in "${words[@]}"; do + p= + if [[ $point ]]; then + ((p=point-o)) + ((i%2==0?p<=${#w}:p<${#w})) || p= + ((o+=${#w},i++)) + fi + [[ $p ]] && point= + [[ $point ]] && progcomp_prefix=$progcomp_prefix$w + ble/complete/progcomp/.compvar-quote-subword "$w"; local wq=$ret + if [[ $p ]]; then + COMP_CWORD=${#COMP_WORDS[*]} + ((COMP_POINT=${#COMP_LINE}+${#sep}+p)) + fi + ble/array#push COMP_WORDS "$wq" + COMP_LINE=$COMP_LINE$sep$wq + sep= + done + sep=' ' + ((offset++)) + ((index++)) + done +} +function ble/complete/progcomp/.compgen-helper-prog { + if [[ $comp_prog ]]; then + local COMP_WORDS COMP_CWORD + local -x COMP_LINE COMP_POINT COMP_TYPE COMP_KEY + ble/complete/progcomp/.compvar-initialize + local cmd=${COMP_WORDS[0]} cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + if [[ $comp_opts == *:ble/prog-trim:* ]]; then + local compreply + ble/util/assign compreply '"$comp_prog" "$cmd" "$cur" "$prev" < /dev/null' + ble/bin/sed "s/[[:space:]]\{1,\}\$//" <<< "$compreply" + else + "$comp_prog" "$cmd" "$cur" "$prev" < /dev/null + fi + fi +} +function ble/complete/progcomp/compopt { + local ext=0 + local -a ospec + while (($#)); do + local arg=$1; shift + case "$arg" in + (-*) + local ic c + for ((ic=1;ic<${#arg};ic++)); do + c=${arg:ic:1} + case "$c" in + (o) ospec[${#ospec[@]}]="-$1"; shift ;; + ([DE]) fDefault=1; break 2 ;; + (*) ((ext==0&&(ext=1))) ;; + esac + done ;; + (+o) ospec[${#ospec[@]}]="+$1"; shift ;; + (*) + return "$ext" ;; + esac + done + local s + for s in "${ospec[@]}"; do + case "$s" in + (-*) comp_opts=${comp_opts//:"${s:1}":/:}${s:1}: ;; + (+*) comp_opts=${comp_opts//:"${s:1}":/:} ;; + esac + done + return "$ext" +} +function ble/complete/progcomp/.check-limits { + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && + [[ ! -t 0 ]] && ble/complete/check-cancel <&$_ble_util_fd_stdin && + return 148 + ble/complete/source/test-limit $((progcomp_read_count++)) + return "$?" +} +function ble/complete/progcomp/.compgen-helper-func { + [[ $comp_func ]] || return 1 + local -a COMP_WORDS + local COMP_LINE COMP_POINT COMP_CWORD COMP_TYPE COMP_KEY + ble/complete/progcomp/.compvar-initialize + local progcomp_read_count=0 + local _ble_builtin_read_hook='ble/complete/progcomp/.check-limits || { builtin read "$@" < /dev/null; return 148; }' + local fDefault= + local cmd=${COMP_WORDS[0]} cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + ble/function#push compopt 'ble/complete/progcomp/compopt "$@"' + ble/function#push ssh ' + local IFS=$_ble_term_IFS + if [[ " ${FUNCNAME[*]} " == *" ble/complete/progcomp/.compgen "* ]]; then + local -a args; args=("$@") + ble/util/conditional-sync "exec ssh \"\${args[@]}\"" \ + "! ble/complete/check-cancel <&$_ble_util_fd_stdin" 128 progressive-weight:killall + else + ble/function#push/call-top "$@" + fi' + builtin eval '"$comp_func" "$cmd" "$cur" "$prev"' < /dev/null >&$_ble_util_fd_stdout 2>&$_ble_util_fd_stderr; local ret=$? + ble/function#pop ssh + ble/function#pop compopt + [[ $ret == 124 ]] && progcomp_retry=1 + return 0 +} +function ble/complete/progcomp/.parse-complete/next { + if [[ $compdef =~ $rex ]]; then + builtin eval "arg=$BASH_REMATCH" + compdef=${compdef:${#BASH_REMATCH}} + return 0 + elif [[ ${compdef%%' '*} ]]; then + arg=${compdef%%' '*} + compdef=${compdef#*' '} + return 0 + else + return 1 + fi +} +function ble/complete/progcomp/.parse-complete/optarg { + optarg= + if ((ic+1<${#arg})); then + optarg=${arg:ic+1} + ic=${#arg} + return 0 + elif [[ $compdef =~ $rex ]]; then + builtin eval "optarg=$BASH_REMATCH" + compdef=${compdef:${#BASH_REMATCH}} + return 0 + else + return 2 + fi +} +function ble/complete/progcomp/.parse-complete { + compoptions=() + comp_prog= + comp_func= + flag_noquote= + local compdef=${1#'complete '} + local arg optarg rex='^([^][*?;&|[:space:]<>()\`$"'\''{}#^!]|\\.|'\''[^'\'']*'\'')+[[:space:]]+' # #D1709 safe (WA gawk 4.0.2) + while ble/complete/progcomp/.parse-complete/next; do + case $arg in + (-*) + local ic c + for ((ic=1;ic<${#arg};ic++)); do + c=${arg:ic:1} + case "$c" in + ([abcdefgjksuvE]) + case $c in + (c) flag_noquote=1 ;; + (d) ((_ble_bash>=40300)) && flag_noquote=1 ;; + (f) ((40000<=_ble_bash&&_ble_bash<40200)) && flag_noquote=1 ;; + esac + ble/array#push compoptions "-$c" ;; + ([pr]) + ;; # 無視 (-p 表示 -r 削除) + ([AGWXPS]) + ble/complete/progcomp/.parse-complete/optarg || break 2 + if [[ $c == A ]]; then + case $optarg in + (command) flag_noquote=1 ;; + (directory) ((_ble_bash>=40300)) && flag_noquote=1 ;; + (file) ((40000<=_ble_bash&&_ble_bash<40200)) && flag_noquote=1 ;; + esac + fi + ble/array#push compoptions "-$c" "$optarg" ;; + (o) + ble/complete/progcomp/.parse-complete/optarg || break 2 + comp_opts=${comp_opts//:"$optarg":/:}$optarg: + ble/array#push compoptions "-$c" "$optarg" ;; + (C) + if ((_ble_bash<40000)); then + comp_prog=${compdef%' '} + compdef= + else + ble/complete/progcomp/.parse-complete/optarg || break 2 + comp_prog=$optarg + fi + ble/array#push compoptions "-$c" ble/complete/progcomp/.compgen-helper-prog ;; + (F) + if ((_ble_bash<40000)) && [[ $compdef == *' -C '* ]]; then + comp_prog=${compdef#*' -C '} + comp_prog=${comp_prog%' '} + ble/array#push compoptions '-C' ble/complete/progcomp/.compgen-helper-prog + comp_func=${compdef%%' -C '*} + else + comp_func=${compdef%' '} + fi + compdef= + ble/array#push compoptions "-$c" ble/complete/progcomp/.compgen-helper-func ;; + (*) + esac + done ;; + (*) + ;; # 無視 + esac + done +} +function ble/complete/progcomp/.filter-and-split-compgen { + flag_mandb= + local sed_script= + { + if [[ $comp_opts == *:ble/filter-by-prefix:* ]]; then + local ret; ble/string#escape-for-sed-regex "$COMPV"; local rex_compv=$ret + sed_script='!/^'$rex_compv'/d' + fi + [[ $use_workaround_for_git ]] && + sed_script=${sed_script:+$sed_script;}'s/[[:space:]]\{1,\}$//' + } + local out= + [[ $sed_script ]] && ble/util/assign out 'ble/bin/sed "$sed_script;/^\$/d" <<< "$compgen"' + [[ $out ]] || out=$compgen + local require_awk= + if [[ $comp_opts != *:nosort:* ]]; then + ble/util/assign out 'ble/bin/sort -u <<< "$out"' + else + require_awk=1 # for uniq + fi + local -a args_mandb=() + if [[ $compcmd == "${comp_words[0]}" && $COMPV != [!-]* ]]; then + if local ret; ble/complete/mandb/generate-cache "$compcmd"; then + require_awk=1 + args_mandb=(mode=mandb "$ret") + fi + fi + if [[ $require_awk ]]; then + local awk_script=' + BEGIN { mandb_count = 0; } + mode == "mandb" { + name = $0 + sub(/'"$_ble_term_FS"'.*/, "", name); + if (!mandb[name]) mandb[name] = $0; + next; + } + !hash[$0]++ { + if (/^$/) next; + name = $0 + sub(/=$/, "", name); + if (mandb[name]) { + mandb_count++; + print mandb[name]; + next; + } else if (sub(/^--no-/, "--", name)) { + if ((entry = mandb[name]) || (entry = mandb[substr(name, 2)])) { + split(entry, record, FS); + if ((desc = record[4])) { + desc = "\033[1mReverse[\033[m " desc " \033[;1m]\033[m"; + if (match($0, /[[:space:]]*[:=[]/)) { + option = substr($0, 1, RSTART - 1); + optarg = substr($0, RSTART); + suffix = substr($0, RSTART, 1); + if (suffix == "[") suffix = ""; + } else { + option = $0; + optarg = ""; + suffix = " "; + } + mandb_count++; + print option FS optarg FS suffix FS desc; + } + next; + } + } + print $0; + } + END { if (mandb_count) exit 10; } + ' + ble/util/assign-array "$1" 'ble/bin/awk -F "$_ble_term_FS" "$awk_script" "${args_mandb[@]}" mode=compgen - <<< "$out"' + (($?==10)) && flag_mandb=1 + else + ble/string#split-lines "$1" "$out" + fi + return 0 +} 2>/dev/null +function ble/complete/progcomp/.cobraV2.patch { + local prefix=$cur + [[ $comps_flags == *v* ]] && prefix=$COMPV + local unprocessed has_desc= + unprocessed=() + local lines line cand desc + for lines in "${out[@]}"; do + ble/string#split-lines lines "$lines" + for line in "${lines[@]}"; do + if [[ $line == *$'\t'* ]]; then + cand=${line%%$'\t'*} + desc=${line#*$'\t'} + [[ $cand == "$prefix"* ]] || continue + ble/complete/cand/yield word "$cand" "$desc" + has_desc=1 + else + ble/array#push unprocessed "$line" + fi + done + done + [[ $has_desc ]] && bleopt complete_menu_style=desc + if ((${#unprocessed[@]})); then + out=("${unprocessed[@]}") + ble/function#advice/do + fi +} +function ble/complete/progcomp/.compgen { + local opts=$1 + local compcmd= is_special_completion= + local -a alias_args=() + if [[ :$opts: == *:initial:* ]]; then + if ((_ble_bash>=50000)); then + is_special_completion=1 + compcmd='-I' + else + compcmd=_InitialWorD_ + fi + elif [[ :$opts: == *:default:* ]]; then + if ((_ble_bash>=40100)); then + builtin complete -p -D &>/dev/null || return 1 + is_special_completion=1 + compcmd='-D' + else + builtin complete -p _DefaultCmD_ &>/dev/null || return 1 + compcmd=_DefaultCmD_ + fi + else + compcmd=${comp_words[0]} + fi + local compdef + if [[ $is_special_completion ]]; then + ble/util/assign compdef 'builtin complete -p "$compcmd" 2>/dev/null' + elif ble/syntax:bash/simple-word/is-simple "$compcmd"; then + ble/util/assign compdef "builtin complete -p -- $compcmd 2>/dev/null" + local ret; ble/syntax:bash/simple-word/eval "$compcmd"; compcmd=$ret + else + ble/util/assign compdef 'builtin complete -p -- "$compcmd" 2>/dev/null' + fi + compdef=${compdef%"${compcmd:-''}"} + compdef=${compdef%' '}' ' + local comp_prog comp_func compoptions flag_noquote + ble/complete/progcomp/.parse-complete "$compdef" + if [[ $comp_func ]]; then + [[ $comp_func == _fzf_* ]] && + ble-import -f contrib/fzf-completion + if ble/is-function _quote_readline_by_ref; then + function _quote_readline_by_ref { + if [[ $1 == \'* ]]; then + printf -v "$2" %s "${1:1}" + else + printf -v "$2" %q "$1" + [[ ${!2} == \$* ]] && builtin eval "$2=${!2}" + fi + } + ble/function#suppress-stderr _filedir 2>/dev/null + ble/function#suppress-stderr _find 2>/dev/null + ble/function#suppress-stderr _scp_remote_files 2>/dev/null + ble/complete/mandb:_parse_help/inject + fi + if [[ $comp_func == __start_* ]]; then + local target=__${comp_func#__start_}_handle_completion_types + ble/is-function "$target" && + ble/function#advice around "$target" ble/complete/progcomp/.cobraV2.patch + fi + ble/function#advice around _dnf_commands_helper ' + ble/util/conditional-sync \ + ble/function#advice/do \ + "! ble/complete/check-cancel <&$_ble_util_fd_stdin" 128 progressive-weight:killall' 2>/dev/null + fi + if [[ $comp_prog ]]; then + if [[ $comp_prog == aws_completer ]]; then + comp_opts=${comp_opts}ble/no-mark-directories:ble/prog-trim: + fi + fi + ble/complete/check-cancel && return 148 + local compgen compgen_compv=$COMPV + if [[ ! $flag_noquote && :$comp_opts: != *:noquote:* ]]; then + local q="'" Q="'\''" + compgen_compv="'${compgen_compv//$q/$Q}'" + fi + local progcomp_prefix= progcomp_retry= + IFS=$IFS word= ble/util/assign compgen 'builtin compgen "${compoptions[@]}" -- "$compgen_compv" 2>/dev/null' + if [[ $progcomp_retry && ! $_ble_complete_retry_guard ]]; then + local _ble_complete_retry_guard=1 + opts=:$opts: + opts=${opts//:default:/:} + ble/complete/progcomp/.compgen "$opts" + return "$?" + fi + [[ $compgen ]] || return 1 + local use_workaround_for_git= + if [[ $comp_func == __git* && $comp_opts == *:nospace:* ]]; then + use_workaround_for_git=1 + comp_opts=${comp_opts//:nospace:/:} + fi + local cands flag_mandb= + ble/complete/progcomp/.filter-and-split-compgen cands # compgen (comp_opts, etc) -> cands, flag_mandb + ble/complete/source/test-limit ${#cands[@]} || return 1 + [[ $comp_opts == *:filenames:* && $COMPV == */* ]] && COMP_PREFIX=${COMPV%/*}/ + local old_cand_count=$cand_count + local action=progcomp "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize "$action" + if [[ $flag_mandb ]]; then + local -a entries; entries=("${cands[@]}") + cands=() + local fs=$_ble_term_FS has_desc= icand=0 entry + for entry in "${entries[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + if [[ $entry == -*"$fs"*"$fs"*"$fs"* ]]; then + local cand=${entry%%"$fs"*} + ble/complete/cand/yield mandb "$cand" "$entry" + [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 + else + cands[icand++]=$progcomp_prefix$entry + fi + done + [[ $has_desc ]] && bleopt complete_menu_style=desc + else + [[ $progcomp_prefix ]] && + if ((_ble_bash>=40300)) && ! shopt -q compat42; then + cands=("${cands[@]/#/"$progcomp_prefix"}") # WA #D1570 #D1751 safe + else + cands=("${cands[@]/#/$progcomp_prefix}") # WA #D1570 #D1738 safe + fi + fi + ble/complete/cand/yield.batch "$action" "$comp_opts" + [[ $comp_opts == *:plusdirs:* ]] && ble/complete/source:dir + ((cand_count>old_cand_count)) +} +function ble/complete/progcomp/.compline-rewrite-command { + local ocmd=${comp_words[0]} + [[ $1 != "$ocmd" ]] || (($#>=2)) || return 1 + local IFS=$_ble_term_IFS + local ins="$*" + if (($#==0)); then + local ret; ble/string#ltrim "${comp_line:${#ocmd}}" + ((comp_point-=${#comp_line}-${#ret})) + comp_line=$ret + else + comp_line=$ins${comp_line:${#ocmd}} + ((comp_point-=${#ocmd})) + fi + ((comp_point<0&&(comp_point=0),comp_point+=${#ins})) + comp_words=("$@" "${comp_words[@]:1}") + ((comp_cword&&(comp_cword+=$#-1))) +} +function ble/complete/progcomp/.split-alias-words { + local tail=$1 + local rex_redir='^'$_ble_syntax_bash_RexRedirect + local rex_word='^'$_ble_syntax_bash_simple_rex_element'+' + local rex_delim=$'^[\n;|&]' + local rex_spaces=$'^[ \t]+' + local rex_misc='^[<>()]+' + local -a words=() + while [[ $tail ]]; do + if [[ $tail =~ $rex_redir && $tail != ['<>']'('* ]]; then + ble/array#push words "$BASH_REMATCH" + tail=${tail:${#BASH_REMATCH}} + elif [[ $tail =~ $rex_word ]]; then + local w=$BASH_REMATCH + tail=${tail:${#w}} + if [[ $tail && $tail != ["$_ble_term_IFS;|&<>()"]* ]]; then + local s=${tail%%["$_ble_term_IFS"]*} + tail=${tail:${#s}} + w=$w$s + fi + ble/array#push words "$w" + elif [[ $tail =~ $rex_delim ]]; then + words=() + tail=${tail:${#BASH_REMATCH}} + elif [[ $tail =~ $rex_spaces ]]; then + tail=${tail:${#BASH_REMATCH}} + elif [[ $tail =~ $rex_misc ]]; then + ble/array#push words "$BASH_REMATCH" + tail=${tail:${#BASH_REMATCH}} + else + local w=${tail%%["$_ble_term_IFS"]*} + ble/array#push words "$w" + tail=${tail:${#w}} + fi + done + local i=0 rex_assign='^[a-zA-Z_0-9]+(\['$_ble_syntax_bash_simple_rex_element'*\])?\+?=' + while ((i<${#words[@]})); do + if [[ ${words[i]} =~ $rex_assign ]]; then + ((i++)) + elif [[ ${words[i]} =~ $rex_redir && ${words[i]} != ['<>']'('* ]]; then + ((i+=2)) + else + break + fi + done + ret=("${words[@]:i}") +} +function ble/complete/progcomp { + local cmd=${1-${comp_words[0]}} opts=$2 + local -a orig_comp_words; orig_comp_words=("${comp_words[@]}") + local comp_words comp_line=$comp_line comp_point=$comp_point comp_cword=$comp_cword + comp_words=("$cmd" "${orig_comp_words[@]:1}") + local orig_qcmds_set= + local -a orig_qcmds=() + local -a alias_args=() + [[ :$opts: == *:__recursive__:* ]] || + local alias_checked=' ' + while :; do + local ret ucmd qcmds + ucmd=$cmd qcmds=("$cmd") + if ble/syntax:bash/simple-word/is-simple "$cmd"; then + if ble/syntax:bash/simple-word/eval "$cmd" noglob && + [[ $ret != "$cmd" || ${#ret[@]} -ne 1 ]]; then + ucmd=${ret[0]} qcmds=() + local word + for word in "${ret[@]}"; do + ble/string#quote-word "$word" quote-empty + ble/array#push qcmds "$ret" + done + else + ble/string#quote-word "$cmd" quote-empty + qcmds=("$ret") + fi + [[ $cmd == "${orig_comp_words[0]}" ]] && + orig_qcmds_set=1 orig_qcmds=("${qcmds[@]}") + fi + if ble/is-function "ble/cmdinfo/complete:$ucmd"; then + ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" + "ble/cmdinfo/complete:$ucmd" "$opts" + return "$?" + elif [[ $ucmd == */?* ]] && ble/is-function "ble/cmdinfo/complete:${ucmd##*/}"; then + ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret + ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" + "ble/cmdinfo/complete:${ucmd##*/}" "$opts" + return "$?" + elif builtin complete -p -- "$ucmd" &>/dev/null; then + ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" + ble/complete/progcomp/.compgen "$opts" + return "$?" + elif [[ $ucmd == */?* ]] && builtin complete -p -- "${ucmd##*/}" &>/dev/null; then + ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret + ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" + ble/complete/progcomp/.compgen "$opts" + return "$?" + elif + ble/function#try __load_completion "${ucmd##*/}" &>/dev/null && + builtin complete -p -- "${ucmd##*/}" &>/dev/null + then + ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret + ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" + ble/complete/progcomp/.compgen "$opts" + return "$?" + fi + alias_checked=$alias_checked$cmd' ' + ((_ble_bash<50000)) || shopt -q progcomp_alias || break + local ret + ble/alias#expand "$cmd" + [[ $ret == "$cmd" ]] && break + ble/complete/progcomp/.split-alias-words "$ret" + if ((${#ret[@]}==0)); then + ble/complete/progcomp/.compline-rewrite-command "${alias_args[@]}" + if ((${#comp_words[@]})); then + if ((comp_cword==0)); then + ble/complete/source:command + else + ble/complete/progcomp "${comp_words[0]}" "__recursive__:$opts" + fi + fi + return $? + fi + [[ $alias_checked != *" $ret "* ]] || break + cmd=$ret + ((${#ret[@]}>=2)) && + alias_args=("${ret[@]:1}" "${alias_args[@]}") + done + comp_words=("${orig_comp_words[@]}") + [[ $orig_qcmds_set ]] && + ble/complete/progcomp/.compline-rewrite-command "${orig_qcmds[@]}" + ble/complete/progcomp/.compgen "default:$opts" +} +_ble_complete_option_chars='_!#$%&:;.,^~|\\?\/*a-zA-Z0-9' +function ble/complete/action:mandb/initialize { + ble/complete/action/quote-insert +} +function ble/complete/action:mandb/initialize.batch { + ble/complete/action/quote-insert.batch newline +} +function ble/complete/action:mandb/complete { + ble/complete/action/complete.close-quotation + local fields + ble/string#split fields "$_ble_term_FS" "$DATA" + local tail=${fields[2]} + [[ $tail == ' ' && $comps_flags == *x* ]] && tail=',' + ble/complete/action/complete.addtail "$tail" +} +function ble/complete/action:mandb/init-menu-item { + local ret; ble/color/face2g argument_option; g=$ret + local fields + ble/string#split fields "$_ble_term_FS" "$DATA" + suffix=${fields[1]} +} +function ble/complete/action:mandb/get-desc { + local fields + ble/string#split fields "$_ble_term_FS" "$DATA" + desc=${fields[3]} +} +function ble/complete/mandb/load-mandb-conf { + [[ -s $1 ]] || return 0 + local line words + while builtin read "${_ble_bash_tmout_wa[@]}" -r line || [[ $line ]]; do + ble/string#split-words words "${line%%'#'*}" + case ${words[0]} in + (MANDATORY_MANPATH) + [[ -d ${words[1]} ]] && + ble/array#push manpath_mandatory "${words[1]}" ;; + (MANPATH_MAP) + ble/dict#set manpath_map "${words[1]}" "${words[2]}" ;; + esac + done < "$1" +} +_ble_complete_mandb_default_manpath=() +function ble/complete/mandb/initialize-manpath { + ((${#_ble_complete_mandb_default_manpath[@]})) && return 0 + local manpath + MANPATH= ble/util/assign manpath 'manpath || ble/bin/man -w' 2>/dev/null + ble/string#split manpath : "$manpath" + if ((${#manpath[@]}==0)); then + local -a manpath_mandatory=() + builtin eval -- "${_ble_util_dict_declare//NAME/manpath_map}" + ble/complete/mandb/load-mandb-conf /etc/man_db.conf + ble/complete/mandb/load-mandb-conf ~/.manpath + if ((${#manpath_mandatory[@]}==0)); then + local ret + ble/complete/util/eval-pathname-expansion '~/*/share/man' + ble/array#push manpath_mandatory "${ret[@]}" + ble/complete/util/eval-pathname-expansion '~/@(opt|.opt)/*/share/man' + ble/array#push manpath_mandatory "${ret[@]}" + for ret in /usr/local/share/man /usr/local/man /usr/share/man; do + [[ -d $ret ]] && ble/array#push manpath_mandatory "$ret" + done + fi + builtin eval -- "${_ble_util_dict_declare//NAME/mark}" + local paths path ret + ble/string#split paths : "$PATH" + for path in "${paths[@]}"; do + [[ -d $path ]] || continue + [[ $path == *?/ ]] && path=${path%/} + if ble/dict#get manpath_map "$path"; then + path=$ret + else + path=${path%/bin}/share/man + fi + if [[ -d $path ]] && ! ble/set#contains mark "$path"; then + ble/set#add mark "$path" + ble/array#push manpath "$path" + fi + done + for path in "${manpath_mandatory[@]}"; do + if [[ -d $path ]] && ! ble/set#contains mark "$path"; then + ble/set#add mark "$path" + ble/array#push manpath "$path" + fi + done + fi + _ble_complete_mandb_default_manpath=("${manpath[@]}") +} +function ble/complete/mandb/search-file/.extract-path { + local command=$1 + [[ $_ble_complete_mandb_lang ]] && + local LC_ALL=$$_ble_complete_mandb_lang + ble/util/assign path 'ble/bin/man -w "$command"' 2>/dev/null +} +ble/function#suppress-stderr ble/complete/mandb/search-file/.extract-path +function ble/complete/mandb/search-file/.check { + local path=$1 + if [[ $path && -s $path ]]; then + ret=$path + return 0 + else + return 1 + fi +} +function ble/complete/mandb/search-file { + local command=$1 + local path + ble/complete/mandb/search-file/.extract-path "$command" + ble/complete/mandb/search-file/.check "$path" && return + ble/string#split ret : "$MANPATH" + ((${#ret[@]})) || ret=('') + local -a manpath=() + for path in "${ret[@]}"; do + if [[ $path ]]; then + ble/array#push manpath "$path" + else + ble/complete/mandb/initialize-manpath + ble/array#push manpath "${_ble_complete_mandb_default_manpath[@]}" + fi + done + local path + for path in "${manpath[@]}"; do + [[ -d $path ]] || continue + ble/complete/mandb/search-file/.check "$path/man1/$command.1" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.8" && return 0 + if ble/is-function ble/bin/gzip; then + ble/complete/mandb/search-file/.check "$path/man1/$command.1.gz" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.8.gz" && return 0 + fi + if ble/is-function ble/bin/bzcat; then + ble/complete/mandb/search-file/.check "$path/man1/$command.1.bz" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.1.bz2" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.8.bz" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.8.bz2" && return 0 + fi + if ble/is-function ble/bin/xzcat; then + ble/complete/mandb/search-file/.check "$path/man1/$command.1.xz" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.8.xz" && return 0 + fi + if ble/is-function ble/bin/lzcat; then + ble/complete/mandb/search-file/.check "$path/man1/$command.1.lzma" && return 0 + ble/complete/mandb/search-file/.check "$path/man1/$command.8.lzma" && return 0 + fi + done + return 1 +} +if ble/bin/.freeze-utility-path preconv; then + function ble/complete/mandb/.preconv { ble/bin/preconv; } +else + function ble/complete/mandb/.preconv { + ble/bin/od -A n -t u1 -v | ble/bin/awk ' + BEGIN { + ECHAR = 65533; # U+FFFD + byte = 0; + for (i = 0; byte < 128; byte++) { mtable[byte] = 0; vtable[byte] = i++; } + for (i = 0; byte < 192; byte++) { mtable[byte] = 0; vtable[byte] = ECHAR; } + for (i = 0; byte < 224; byte++) { mtable[byte] = 1; vtable[byte] = i++; } + for (i = 0; byte < 240; byte++) { mtable[byte] = 2; vtable[byte] = i++; } + for (i = 0; byte < 248; byte++) { mtable[byte] = 3; vtable[byte] = i++; } + for (i = 0; byte < 252; byte++) { mtable[byte] = 4; vtable[byte] = i++; } + for (i = 0; byte < 254; byte++) { mtable[byte] = 5; vtable[byte] = i++; } + for (i = 0; byte < 256; byte++) { mtable[byte] = 0; vtable[byte] = ECHAR; } + M = 0; C = 0; + } + function put_uchar(uchar) { + if (uchar < 128) + printf("%c", uchar); + else + printf("\\[u%04X]", uchar); + } + function process_byte(byte) { + if (M) { + if (128 <= byte && byte < 192) { + C = C * 64 + byte % 64; + if (--M == 0) put_uchar(C); + return; + } else { + put_uchar(ECHAR); + M = 0; + } + } + M = mtable[byte]; + C = vtable[byte]; + if (M == 0) put_uchar(C); + } + { for (i = 1; i <= NF; i++) process_byte($i); } + ' + } +fi +_ble_complete_mandb_lang= +if ble/is-function ble/bin/groff; then + _ble_complete_mandb_convert_type=man + function ble/complete/mandb/convert-mandoc { + if [[ $_ble_util_locale_encoding == UTF-8 ]]; then + ble/bin/groff -k -Tutf8 -man + else + ble/bin/groff -Tascii -man + fi + } + if [[ $OSTYPE == darwin* ]] && ! ble/bin/groff -k -Tutf8 -man &>/dev/null <<< 'α'; then + if ble/bin/groff -T utf8 -m man &>/dev/null <<< '\[u03B1]'; then + function ble/complete/mandb/convert-mandoc { + if [[ $_ble_util_locale_encoding == UTF-8 ]]; then + ble/complete/mandb/.preconv | ble/bin/groff -T utf8 -m man + else + ble/bin/groff -T ascii -m man + fi + } + else + _ble_complete_mandb_lang=C + function ble/complete/mandb/convert-mandoc { + ble/bin/groff -T ascii -m man + } + fi + fi +elif ble/is-function ble/bin/nroff; then + _ble_complete_mandb_convert_type=man + function ble/complete/mandb/convert-mandoc { + if [[ $_ble_util_locale_encoding == UTF-8 ]]; then + ble/bin/nroff -Tutf8 -man + else + ble/bin/groff -Tascii -man + fi + } +elif ble/is-function ble/bin/mandoc; then + _ble_complete_mandb_convert_type=mdoc + function ble/complete/mandb/convert-mandoc { + ble/bin/mandoc -mdoc + } +fi +function ble/complete/mandb/.generate-cache-from-man { + ble/is-function ble/bin/man && + ble/is-function ble/complete/mandb/convert-mandoc || return 1 + local command=$1 + local ret + ble/complete/mandb/search-file "$command" || return 1 + local path=$ret + case $ret in + (*.gz) ble/bin/gzip -cd "$path" ;; + (*.bz|*.bz2) ble/bin/bzcat "$path" ;; + (*.lzma) ble/bin/lzcat "$path" ;; + (*.xz) ble/bin/xzcat "$path" ;; + (*) ble/bin/cat "$path" ;; + esac | ble/bin/awk -v type="$_ble_complete_mandb_convert_type" ' + BEGIN { + g_keys_count = 0; + g_desc = ""; + if (type == "man") { + print ".TH __ble_ignore__ 1 __ble_ignore__ __ble_ignore__"; + print ".ll 9999" + topic_start = ".TP"; + } + mode = "begin"; + fmt3_state = ""; + fmt5_state = ""; + fmt6_state = ""; + } + function output_pair(key, desc) { + print ""; + print "__ble_key__"; + if (topic_start != "") print topic_start; + print key; + print ""; + print "__ble_desc__"; + print ""; + print desc; + } + function flush_topic(_, i) { + if (g_keys_count != 0) { + for (i = 0; i < g_keys_count; i++) + output_pair(g_keys[i], g_desc); + } + g_keys_count = 0; + g_desc = ""; + fmt3_flush(); + fmt5_state = ""; + fmt6_flush(); + } + mode == "begin" && /^\.(Dd|Nm)[[:space:]]/ { + if (type == "man" && /^\.Dd[[:space:]]+\$Mdoc/) topic_start = ""; + print $0; + } + function register_key(key) { + g_keys[g_keys_count++] = key; + g_desc = ""; + } + /^\.ig/ { mode = "ignore"; next; } + mode == "ignore" { + if (/^\.\.[[:space:]]*/) mode = "none"; + next; + } + { + sub(/[[:space:]]+$/, ""); + REQ = match($0, /^\.[_[:alnum:]]+/) ? substr($0, 2, RLENGTH - 1) : ""; + } + REQ ~ /^(S[Ss]|S[Hh]|Pp)$/ { flush_topic(); next; } + REQ == "PP" { + flush_topic(); + fmt5_state = "key"; + fmt5_key = ""; + fmt5_desc = ""; + next; + } + fmt5_state { + if (fmt5_state == "key") { + if (/^\.RS([^_[:alnum:]]|$)/) + fmt5_state = "desc"; + else if (/^\.RE([^_[:alnum:]]|$)/) + fmt5_state = "none"; + else + fmt5_key = (fmt5_key ? "\n" : "") $0; + } else if (fmt5_state == "desc") { + if (/^\.RE([^_[:alnum:]]|$)/) { + register_key(fmt5_key); + g_desc = fmt5_desc; + flush_topic(); + fmt5_state = ""; + } else + fmt5_desc = (fmt5_desc ? "\n" : "") $0; + } + } + REQ == "HP" { + flush_topic(); + fmt3_state = "key"; + fmt3_key_count = 0; + fmt3_desc = ""; + next; + } + function fmt3_process(_, key) { + if (REQ == "TP") { fmt3_flush(); return; } + if (REQ == "PD") return; + if (fmt3_state == "key") { + if (REQ == "IP") { fmt3_state = "desc"; return; } + if (match($0, /( | )[[:space:]]*/)) { + fmt3_keys[fmt3_key_count++] = substr($0, 1, RSTART - 1); + fmt3_desc = substr($0, RSTART + RLENGTH); + fmt3_state = "desc"; + } else { + fmt3_keys[fmt3_key_count++] = $0; + } + } else if (fmt3_state == "desc") { + if (fmt3_desc != "") fmt3_desc = fmt3_desc "\n"; + fmt3_desc = fmt3_desc $0; + } + } + function fmt3_flush(_, i) { + if (fmt3_state == "desc" && fmt3_key_count > 0) { + for (i = 0; i < fmt3_key_count; i++) + register_key(fmt3_keys[i]); + g_desc = fmt3_desc; + } + fmt3_state = ""; + fmt3_key_count = 0; + fmt3_desc = ""; + } + fmt3_state { fmt3_process(); } + /^\.IP[[:space:]]+".*"([[:space:]]+[0-9]+)?$/ && fmt3_state != "key" { + fmt6_init(); + fmt4_init(); + next; + } + function fmt4_init() { + if (mode != "fmt4_desc") + if (!(g_keys_count && g_desc == "")) flush_topic(); + gsub(/^\.IP[[:space:]]+"|"([[:space:]]+[0-9]+)?$/, ""); + register_key($0); + mode = "fmt4_desc"; + } + mode == "fmt4_desc" { + if ($0 == "") { flush_topic(); mode = "none"; next; } + if (g_keys_count == 1 && g_keys[0] == "\\(bu" && match($0, /^\\fC[^\\]+\\fP( or \\fC[^\\]+\\fP)?/) > 0) { + _key = substr($0, 1, RLENGTH); + _desc = substr($0, RLENGTH + 1); + if (match(_key, / or \\fC[^\\]+\\fP/) > 0) + _key = substr(_key, 1, RSTART - 1) ", " substr(_key, RSTART + 4); + g_keys[0] = _key; + g_desc = _desc; + next; + } + if (REQ == "PD") next; + if (/^\.IX[[:space:]]+Item[[:space:]]+/) next; + if (g_desc != "") g_desc = g_desc "\n"; + g_desc = g_desc $0; + } + function fmt6_init() { + fmt6_flush(); + fmt6_state = "desc" + fmt6_key = $0; + fmt6_desc = ""; + } + fmt6_state { + if (REQ == "IX") { + fmt6_state = ""; + } else if (REQ == "IP") { + fmt6_flush(); + } else { + fmt6_desc = fmt6_desc $0 "\n"; + } + } + function fmt6_flush() { + if (!fmt6_state) return; + fmt6_state = ""; + if (fmt6_desc) + output_pair(fmt6_key, fmt6_desc); + } + /^\.It Fl([^_[:alnum:]]|$)/ { + if (g_keys_count && g_desc != "") flush_topic(); + sub(/^\.It Fl/, ".Fl"); + if ($0 ~ / Xo$/) { + g_current_key = $0; + mode = "fmt2_keyc" + } else { + register_key($0); + mode = "desc"; + } + next; + } + mode == "fmt2_keyc" { + if (/^\.PD[[:space:]]*([0-9]+[[:space:]]*)?$/) next; + g_current_key = g_current_key "\n" $0; + if (REQ == "Xc") { + register_key(g_current_key); + mode = "desc"; + } + next; + } + type == "man" && REQ == "TP" { + if (g_keys_count && g_desc != "") flush_topic(); + mode = "key1"; + next; + } + mode == "key1" { + if (/^\.PD[[:space:]]*([0-9]+[[:space:]]*)?$/) next; + register_key($0); + mode = "desc"; + next; + } + mode == "desc" { + if (REQ == "PD") next; + if (g_desc != "") g_desc = g_desc "\n"; + g_desc = g_desc $0; + } + END { flush_topic(); } + ' | ble/complete/mandb/convert-mandoc 2>/dev/null | ble/bin/awk -F "$_ble_term_FS" ' + function flush_pair(_, i) { + if (g_option_count) { + gsub(/\034/, "\x1b[7m^\\\x1b[27m", g_desc); + sub(/(\. |; ).*/, ".", g_desc); # Long descriptions are truncated. + for (i = 0; i < g_option_count; i++) + print g_options[i] FS g_desc; + } + g_option_count = 0; + g_desc = ""; + } + function process_key(line, _, n, specs, i, spec, option, optarg, suffix) { + gsub(/\x1b\[[ -?]*[@-~]/, "", line); # CSI seq + gsub(/\x1b[ -\/]*[0-~]/, "", line); # ESC seq + gsub(/.\x08/, "", line); # CHAR BS + gsub(/\x0E/, "", line); # SO + gsub(/\x0F/, "", line); # SI + gsub(/[\x00-\x1F]/, "", line); # Give up all the other control chars + gsub(/^[[:space:]]*|[[:space:]]*$/, "", line); + gsub(/[[:space:]]+/, " ", line); + if (line !~ /^[-+]./) return; + n = split(line, specs, /,([[:space:]]+|$)| or /); + prev_optarg = ""; + for (i = n; i > 0; i--) { + spec = specs[i]; + sub(/,[[:space:]]+$/, "", spec); + if (spec !~ /^[-+]/ || spec ~ /\034/) { specs[i] = ""; continue; } + if (match(spec, /\[[:=]?|[:=[:space:]]/)) { + option = substr(spec, 1, RSTART - 1); + optarg = substr(spec, RSTART); + suffix = substr(spec, RSTART + RLENGTH - 1, 1); + if (suffix == "[") suffix = ""; + prev_optarg = optarg; + } else { + option = spec; + optarg = ""; + suffix = " "; + if (prev_optarg ~ /[A-Z]|<.+>/) { + optarg = prev_optarg; + if (option ~ /^[-+].$/) { + sub(/^\[=/, "[", optarg); + sub(/^=/, "", optarg); + sub(/^[^[:space:][]/, " &", optarg); + } else { + if (optarg ~ /^\[[^:=]/) + sub(/^\[/, "[=", optarg); + else if (optarg ~ /^[^:=[:space:][]/) + optarg = " " optarg; + } + if (match(optarg, /^\[[:=]?|^[:=[:space:]]/)) { + suffix = substr(optarg, RSTART + RLENGTH - 1, 1); + if (suffix == "[") suffix = ""; + } + } + } + specs[i] = option FS optarg FS suffix; + } + for (i = 1; i <= n; i++) { + if (specs[i] == "") continue; + option = substr(specs[i], 1, index(specs[i], FS) - 1); + if (!g_hash[option]++) + g_options[g_option_count++] = specs[i]; + } + } + function process_desc(line) { + gsub(/^[[:space:]]*|[[:space:]]*$/, "", line); + gsub(/[[:space:]][[:space:]]+/, " ", line); + if (line == "") { + if (g_desc != "") return 0; + return 1; + } + if (g_desc != "") g_desc = g_desc " "; + g_desc = g_desc line; + return 1; + } + sub(/^[[:space:]]*__ble_key__/, "", $0) { + flush_pair(); + mode = "key"; + if (match($0, /__ble_desc__/) > 0) { + s_key = substr($0, 1, RSTART - 1); + s_desc = substr($0, RSTART + RLENGTH); + process_key(s_key); + mode = "desc"; + if (!process_desc(s_desc)) mode = ""; + next; + } + } + sub(/^[[:space:]]*__ble_desc__/, "", $0) { + mode = "desc"; + } + mode == "key" { process_key($0); } + mode == "desc" { if (!process_desc($0)) mode = ""; } + END { flush_pair(); } + ' | ble/bin/sort -t "$_ble_term_FS" -k 1 +} +function ble/complete/mandb:help/generate-cache { + local opts=$1 + local -x cfg_usage= cfg_help=1 cfg_plus= cfg_plus_generate= + [[ :$opts: == *:mandb-help-usage:* ]] && cfg_usage=1 + [[ :$opts: == *:mandb-usage:* ]] && cfg_usage=1 cfg_help= + ble/string#match ":$opts:" ':plus-options(=[^:]+)?:' && + cfg_plus=1 cfg_plus_generate=${BASH_REMATCH[1]:1} + local space=$' \t' # for #D1709 (WA gawk 4.0.2) + local rex_argsep='(\[?[:=]| ?|\[)' + local rex_option='[-+](,|[^]:='$space',[]+)('$rex_argsep'(<[^<>]+>|\([^()]+\)|[^-[:space:]])[^[:space:]]*)?' + ble/bin/awk -F "$_ble_term_FS" ' + BEGIN { + cfg_help = ENVIRON["cfg_help"]; + g_indent = -1; + g_keys_count = 0; + g_desc = ""; + cfg_usage = ENVIRON["cfg_usage"]; + g_usage_count = 0; + cfg_plus_generate = ENVIRON["cfg_plus_generate"]; + cfg_plus = ENVIRON["cfg_plus"] cfg_plus_generate; + } + function split_option_optarg_suffix(optspec, _, key, suffix, optarg) { + if (index(optspec, FS) != 0) return ""; + if ((pos = match(optspec, /'"$rex_argsep"'/)) > 0) { + key = substr(optspec, 1, pos - 1); + suffix = substr(optspec, pos + RLENGTH - 1, 1); + if (suffix == "[") suffix = ""; + optarg = substr(optspec, pos); + } else { + key = optspec; + optarg = ""; + suffix = " "; + } + if (key ~ /[^-+'"$_ble_complete_option_chars"']/) return ""; + return key FS optarg FS suffix; + } + function print_entry(entry, _, name) { + name = entry; + sub(/'"$_ble_term_FS"'.*$/, "", name); + if (name ~ /^\+/ && !cfg_plus) return; + if (!g_hash[name]++) # uniq + print entry; + } + { + gsub(/\x1b\[[ -?]*[@-~]/, ""); # CSI seq + gsub(/\x1b[ -\/]*[0-~]/, ""); # ESC seq + gsub(/[\x00-\x1F]/, ""); # Remove all the other C0 chars + } + function generate_plus(_, i, n) { + if (!cfg_plus_generate) return; + n = length(cfg_plus_generate); + for (i = 1; i <= n; i++) + print_entry("+" substr(cfg_plus_generate, i, 1) FS FS FS); + } + function parse_usage(line, _, optspec, optspec1, option, optarg, n, i, o) { + while (match(line, /\[[[:space:]]*([^][]|\[[^][]*\])+[[:space:]]*\]/)) { + optspec = substr(line, RSTART + 1, RLENGTH - 2); + line = substr(line, RSTART + RLENGTH); + while (match(optspec, /([^][|]|\[[^][]*\])+/)) { + optspec1 = substr(optspec, RSTART, RLENGTH); + optspec = substr(optspec, RSTART + RLENGTH); + gsub(/^[[:space:]]+|[[:space:]]+$/, "", optspec1); + if (match(optspec1, /^[-+][^]:='"$space"'[]+/)) { + option = substr(optspec1, RSTART, RLENGTH); + optarg = substr(optspec1, RSTART + RLENGTH); + n = RLENGTH; + if (option ~ /^-.*-/) { + if ((keyinfo = split_option_optarg_suffix(optspec1)) != "") + g_usage[g_usage_count++] = keyinfo; + } else { + o = substr(option, 1, 1); + for (i = 2; i <= n; i++) + if ((keyinfo = split_option_optarg_suffix(o substr(option, i, 1) optarg)) != "") + g_usage[g_usage_count++] = keyinfo; + } + } + } + } + } + function print_usage(_, i) { + for (i = 0; i < g_usage_count; i++) + print_entry(g_usage[i] FS); + } + cfg_usage { + if (NR <= 20 && (g_usage_start || $0 ~ /^[a-zA-Z_0-9]|^[^-[:space:]][^[:space:]]*(: |:)/) ) { + g_usage_start = 1; + parse_usage($0); + } else if (/^[[:space:]]*$/) + cfg_usage = 0; + } + function get_indent(text, _, i, n, ret) { + ret = 0; + n = length(text); + for (i = 1; i <= n; i++) { + c = substr(text, i, 1); + if (c == " ") + ret++; + else if (c == "\t") + ret = (int(ret / 8) + 1) * 8; + else + break; + } + return ret; + } + function flush_data(_, i) { + if (g_indent < 0) return; + for (i = 0; i < g_keys_count; i++) + print_entry(g_keys[i] FS g_desc); + g_indent = -1; + g_keys_count = 0; + g_desc = ""; + } + function register_key(keydef, _, key, keyinfo, keys, nkey, i, optarg) { + if (g_desc != "") flush_data(); + g_indent = get_indent(keydef); + nkey = 0; + for (;;) { + sub(/^,?[[:space:]]+/, "", keydef); + if (match(keydef, /^'"$rex_option"'/) <= 0) break; + key = substr(keydef, 1, RLENGTH); + keydef = substr(keydef, RLENGTH + 1); + sub(/,$/, "", key); + keys[nkey++] = key; + } + if (nkey >= 2) { + optarg = ""; + for (i = nkey; --i >= 0; ) { + if (match(keys[i], /'"$rex_argsep"'/) > 0) { + optarg = substr(keys[i], RSTART); + sub(/^[[:space:]]+/, "", optarg); + if (optarg !~ /[A-Z]|<.+>/) optarg = ""; + } else if (optarg != ""){ + if (keys[i] ~ /^[-+].$/) { + optarg2 = optarg; + sub(/^\[=/, "[", optarg2); + sub(/^=/, "", optarg2); + sub(/^[^[:space:][]/, " &", optarg2); + keys[i] = keys[i] optarg2; + } else { + optarg2 = optarg; + if (optarg2 ~ /^\[[^:=]/) + sub(/^\[/, "[=", optarg2); + else if (optarg2 ~ /^[^:=[:space:][]/) + optarg2 = " " optarg2; + keys[i] = keys[i] optarg2; + } + } + } + } + for (i = 0; i < nkey; i++) + if ((keyinfo = split_option_optarg_suffix(keys[i])) != "") + g_keys[g_keys_count++] = keyinfo; + } + function append_desc(desc) { + gsub(/^[[:space:]]+|[[:space:]]$/, "", desc); + if (desc == "") return; + if (g_desc == "") + g_desc = desc; + else + g_desc = g_desc " " desc; + } + cfg_help && match($0, /^[[:space:]]*'"$rex_option"'(,?[[:space:]]+'"$rex_option"')*/) { + key = substr($0, 1, RLENGTH); + desc = substr($0, RLENGTH + 1); + if (desc ~ /^,/) next; + register_key(key); + append_desc(desc); + next; + } + g_indent >= 0 { + sub(/[[:space:]]+$/, ""); + indent = get_indent($0); + if (indent <= g_indent) + flush_data(); + else + append_desc($0); + } + END { + flush_data(); + print_usage(); + generate_plus(); + } + ' | ble/bin/sort -t "$_ble_term_FS" -k 1 +} +function ble/complete/mandb:_parse_help/inject { + ble/is-function _parse_help || return 0 + ble/function#advice before _parse_help 'ble/complete/mandb:_parse_help/generate-cache "${ADVICE_WORDS[1]}" "${ADVICE_WORDS[2]}"' + ble/function#advice before _longopt 'ble/complete/mandb:_parse_help/generate-cache "${ADVICE_WORDS[1]}"' + ble/function#advice before _parse_usage 'ble/complete/mandb:_parse_help/generate-cache "${ADVICE_WORDS[1]}" "${ADVICE_WORDS[2]}" usage' + function ble/complete/mandb:_parse_help/inject { return 0; } +} +function ble/string#hash-pjw { + local size=${2:-32} + local S=${3:-$(((size+7)/8))} # shift 4 + local C=$((size-2*S)) # co-shift 24 + local M=$(((1<>C&N)&M)) + done + ret=$h +} +function ble/complete/mandb:_parse_help/generate-cache { + [[ $_ble_attached ]] || return 0 + local command=$1; [[ $1 == ble*/* ]] || command=${1##*/} + local ret; ble/string#hash-pjw "${*:2}" 64; local hash=$ret + local lc_messages=${LC_ALL:-${LC_MESSAGES:-${LANG:-C}}} + local mandb_cache_dir=$_ble_base_cache/complete.mandb/${lc_messages//'/'/%} + local subcache; ble/util/sprintf subcache '%s.%014x' "$mandb_cache_dir/_parse_help.d/$command" "$hash" + [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]] && return 0 + ble/util/mkd "${subcache%/*}" + if [[ $1 == - ]]; then + ble/complete/mandb:help/generate-cache "$3" + else + local args default_option=--help + [[ :$3: == *:usage:* ]] && default_option=--usage + ble/string#split-words args "${2:-$default_option}" + "$1" "${args[@]}" 2>&1 | ble/complete/mandb:help/generate-cache "$3" + fi >| "$subcache" +} +function ble/complete/mandb/generate-cache { + local command=${1##*/} + local lc_messages=${LC_ALL:-${LC_MESSAGES:-${LANG:-C}}} + local mandb_cache_dir=$_ble_base_cache/complete.mandb/${lc_messages//'/'/%} + local fcache=$mandb_cache_dir/$command + local cmdspec_opts; ble/cmdspec/opts#load "$command" + [[ :$cmdspec_opts: == *:no-options:* ]] && return 1 + if ble/opts#extract-all-optargs "$cmdspec_opts" mandb-help --help; then + local -a helpspecs; helpspecs=("${ret[@]}") + local subcache=$mandb_cache_dir/help.d/$command + if ! [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then + ble/util/mkd "${subcache%/*}" + local helpspec + for helpspec in "${helpspecs[@]}"; do + if [[ $helpspec == %* ]]; then + builtin eval -- "${helpspec:1}" + elif [[ $helpspec == @* ]]; then + ble/util/print "${helpspec:1}" + else + ble/string#split-words helpspec "${helpspec#+}" + "$command" "${helpspec[@]}" 2>&1 + fi + done | ble/complete/mandb:help/generate-cache "$cmdspec_opts" >| "$subcache" + fi + fi + if [[ :$cmdspec_opts: != *:mandb-disable-man:* ]] && ble/bin#has "$1"; then + local subcache=$mandb_cache_dir/man.d/$command + if ! [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then + ble/util/mkd "${subcache%/*}" + ble/complete/mandb/.generate-cache-from-man "$command" >| "$subcache" + fi + fi + local -a subcaches=() + local subcache update= + ble/complete/util/eval-pathname-expansion '"$mandb_cache_dir"/_parse_help.d/"$command".??????????????' + for subcache in "${ret[@]}" "$mandb_cache_dir"/{help,man}.d/"$command"; do + if [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then + ble/array#push subcaches "$subcache" + [[ $fcache -nt $subcache ]] || update=1 + fi + done + if [[ $update ]]; then + local fs=$_ble_term_FS + ble/bin/awk -F "$_ble_term_FS" ' + BEGIN { plus_count = 0; nodesc_count = 0; } + $4 == "" { + if ($1 ~ /^\+/) { + plus_name[plus_count] = $1; + plus_entry[plus_count] = $0; + plus_count++; + } else { + nodesc_name[nodesc_count] = $1; + nodesc_entry[nodesc_count] = $0; + nodesc_count++; + } + next; + } + !hash[$1] { hash[$1] = $0; print; } + END { + for (i = 0; i < nodesc_count; i++) { + if (!hash[nodesc_name[i]]) { + hash[nodesc_name[i]] = nodesc_entry[i]; + print nodesc_entry[i]; + } + } + for (i = 0; i < plus_count; i++) { + name = plus_name[i]; + if (hash[name]) continue; + split(plus_entry[i], record, FS); + optarg = record[2]; + suffix = record[3]; + desc = ""; + mname = name; + sub(/^\+/, "-", mname); + if (hash[mname]) { + if (!optarg) { + split(hash[mname], record, FS); + optarg = record[2]; + suffix = record[3]; + } + desc = hash[mname]; + sub(/^[^'$fs']*'$fs'[^'$fs']*'$fs'[^'$fs']*'$fs'/, "", desc); + if (desc) desc = "\033[1mReverse[\033[m " desc " \033[;1m]\033[m"; + } + if (!desc) desc = "reverse of \033[4m" mname "\033[m"; + hash[name] = name FS optarg FS suffix FS desc; + print hash[name]; + } + } + ' "${subcaches[@]}" >| "$fcache" + fi + ret=$fcache + [[ -s $fcache ]] +} +function ble/complete/mandb/load-cache { + ret=() + ble/complete/mandb/generate-cache "$@" && + ble/util/mapfile ret < "$ret" +} +function ble/complete/source:option/.is-option-context { + local rexrej rexreq stopat + ble/progcolor/stop-option#init "$cmdspec_opts" + if [[ $stopat ]] && ((stopat<=$#)); then + return 1 + elif [[ ! $rexrej$rexreq ]]; then + return 0 + fi + local word ret + for word; do + ble/syntax:bash/simple-word/is-simple "$word" && + ble/syntax:bash/simple-word/eval "$word" noglob && + ble/progcolor/stop-option#test "$ret" && + return 1 + done + return 0 +} +function ble/complete/source:option { + local rex='^-[-+'$_ble_complete_option_chars']*$|^\+[_'$_ble_complete_option_chars']*$' + [[ ! $COMPV || $COMPV =~ $rex ]] || return 0 + local COMPS=$COMPS COMPV=$COMPV + ble/complete/source/reduce-compv-for-ambiguous-match + [[ :$comp_type: == *:[maA]:* ]] && local COMP2=$COMP1 + local comp_words comp_line comp_point comp_cword + ble/syntax:bash/extract-command "$COMP2" || return 1 + local -a prev_args=() + ((comp_cword>=1)) && prev_args=("${comp_words[@]:1:comp_cword-1}") + local cmd=${comp_words[0]} + local alias_checked=' ' + while local ret; ! ble/complete/mandb/load-cache "$cmd"; do + alias_checked=$alias_checked$cmd' ' + ble/alias#expand "$cmd" || return 1 + local words; ble/string#split-words ret "$ret"; words=("${ret[@]}") + local iword=0 rex='^[_a-zA-Z][_a-zA-Z0-9]*\+?=' + while [[ ${words[iword]} =~ $rex ]]; do ((iword++)); done + [[ ${words[iword]} && $alias_checked != *" ${words[iword]} "* ]] || return 1 + prev_args=("${words[@]:iword+1}" "${prev_args[@]}") + cmd=${words[iret]} + done + local -a entries; entries=("${ret[@]}") + local ret cmdspec_opts= + ble/syntax:bash/simple-word/is-simple "$cmd" && + ble/syntax:bash/simple-word/eval "$cmd" noglob && + ble/cmdspec/opts#load "$ret" + ble/complete/source:option/.is-option-context "${prev_args[@]}" || return 1 + local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize mandb + local entry fs=$_ble_term_FS has_desc= + for entry in "${entries[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && + ble/complete/check-cancel && return 148 + local CAND=${entry%%$_ble_term_FS*} + [[ $CAND == "$COMPV"* ]] || continue + ble/complete/cand/yield mandb "$CAND" "$entry" + [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 + done + [[ $has_desc ]] && bleopt complete_menu_style=desc +} +function ble/complete/source:argument/.generate-user-defined-completion { + shopt -q progcomp || return 1 + [[ :$comp_type: == *:[maA]:* ]] && local COMP2=$COMP1 + local comp_words comp_line comp_point comp_cword + ble/syntax:bash/extract-command "$COMP2" || return 1 + local forward_words= + ((comp_cword)) && IFS=' ' builtin eval 'forward_words="${comp_words[*]::comp_cword} "' + local comp2_in_word=$((comp_point-${#forward_words})) + local comp1_in_word=$((comp2_in_word-(COMP2-COMP1))) + if ((comp1_in_word>0)); then + local w=${comp_words[comp_cword]} + comp_words=("${comp_words[@]::comp_cword}" "${w::comp1_in_word}" "${w:comp1_in_word}" "${comp_words[@]:comp_cword+1}") + IFS=' ' builtin eval 'comp_line="${comp_words[*]}"' + ((comp_cword++,comp_point++)) + ((comp2_in_word=COMP2-COMP1,comp1_in_word=0)) + fi + if [[ $COMPV && :$comp_type: == *:[maA]:* ]]; then + local oword=${comp_words[comp_cword]::comp2_in_word} ins + local ins=; [[ :$comp_type: == *:a:* ]] && ins=${COMPV::1} + local ret comps_flags= comps_fixed= # referenced in ble/complete/string#escape-for-completion-context + if [[ $oword ]]; then + local simple_flags simple_ibrace + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$oword" || return 1 + comps_flags=v$simple_flags + ((${simple_ibrace%:*})) && comps_fixed=1 + fi + ble/complete/string#escape-for-completion-context "$ins" c; ins=$ret + ble/util/unlocal comps_flags comps_fixed + ((comp_point+=${#ins})) + comp_words=("${comp_words[@]::comp_cword}" "$oword$ins" "${comp_words[@]:comp_cword+1}") + IFS=' ' builtin eval 'comp_line="${comp_words[*]}"' + ((comp2_in_word+=${#ins})) + fi + local opts=$1 + if [[ :$opts: == *:initial:* ]]; then + ble/complete/progcomp/.compgen initial + else + ble/complete/progcomp "${comp_words[0]}" + fi +} +function ble/complete/source:argument { + local comp_opts=: + ble/complete/source:sabbrev + if [[ $comps_flags == *f* && $COMPS != *\* && :$comp_type: != *:[maA]:* ]]; then + local ret simple_flags simple_ibrace + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$COMPS" + ble/complete/source/eval-simple-word "$ret*" && ((${#ret[*]})) && + ble/complete/cand/yield-filenames file "${ret[@]}" + (($?==148)) && return 148 + fi + local old_cand_count=$cand_count + ble/complete/source:argument/.generate-user-defined-completion; local ext=$? + ((ext==148||cand_count>old_cand_count)) && return "$ext" + [[ $comp_opts == *:ble/no-default:* ]] && return "$ext" + ble/complete/source:option; local ext=$? + ((ext==148||cand_count>old_cand_count&&${#COMPV})) && return "$ext" + if [[ $comp_opts == *:dirnames:* ]]; then + ble/complete/source:dir + else + ble/complete/source:file + fi; local ext=$? + ((ext==148||cand_count>old_cand_count)) && return "$ext" + if local rex='^/?[-_a-zA-Z0-9.]+[:=]|^-[^-/=:]'; [[ $COMPV =~ $rex ]]; then + local prefix=$BASH_REMATCH value=${COMPV:${#BASH_REMATCH}} + local COMP_PREFIX=$prefix + [[ :$comp_type: != *:[maA]:* && $value =~ ^.+/ ]] && + COMP_PREFIX=$prefix${BASH_REMATCH[0]} + local ret cand "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/source:file/.construct-pathname-pattern "$value" + ble/complete/util/eval-pathname-expansion "$ret"; (($?==148)) && return 148 + ble/complete/source/test-limit ${#ret[@]} || return 1 + ble/complete/cand/yield.initialize file_rhs + for cand in "${ret[@]}"; do + [[ -e $cand || -h $cand ]] || continue + [[ $FIGNORE ]] && ! ble/complete/.fignore/filter "$cand" && continue + ble/complete/cand/yield file_rhs "$prefix$cand" "$prefix" + done + fi +} +function ble/complete/source/compgen { + [[ $comps_flags == *v* ]] || return 1 + local COMPS=$COMPS COMPV=$COMPV + ble/complete/source/reduce-compv-for-ambiguous-match + local compgen_action=$1 + local action=$2 + local data=$3 + local q="'" Q="'\''" + local compv_quoted="'${COMPV//$q/$Q}'" + local arr + ble/util/assign-array arr 'builtin compgen -A "$compgen_action" -- "$compv_quoted"' + ble/complete/source/test-limit ${#arr[@]} || return 1 + [[ $1 != '=' && ${#arr[@]} == 1 && $arr == "$COMPV" ]] && return 0 + local cand "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize "$action" + for cand in "${arr[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + ble/complete/cand/yield "$action" "$cand" "$data" + done +} +function ble/complete/source:variable { + local data= + case $1 in + ('=') data=assignment ;; + ('b') data=braced ;; + ('a') data=arithmetic ;; + ('n') data=nosuffix ;; + ('w'|*) data=word ;; + esac + ble/complete/source/compgen variable variable "$data" +} +function ble/complete/source:user { + ble/complete/source/compgen user word +} +function ble/complete/source:hostname { + ble/complete/source/compgen hostname word +} +function ble/complete/complete/determine-context-from-opts { + local opts=$1 + context=syntax + if local rex=':context=([^:]+):'; [[ :$opts: =~ $rex ]]; then + local rematch1=${BASH_REMATCH[1]} + if ble/is-function ble/complete/context:"$rematch1"/generate-sources; then + context=$rematch1 + else + ble/util/print "ble/widget/complete: unknown context '$rematch1'" >&2 + fi + fi +} +function ble/complete/context/filter-prefix-sources { + local -a filtered_sources=() + local src asrc + for src in "${sources[@]}"; do + ble/string#split-words asrc "$src" + local comp1=${asrc[1]} + ((comp1=fixlen)) && ble/array#push buff '.*' + ch=${text:i:1} + if [[ $ch == [a-zA-Z] ]]; then + if [[ $opt_icase ]]; then + ble/string#toggle-case "$ch" + ch=[$ch$ret] + fi + else + ble/string#escape-for-extended-regex "$ch"; ch=$ret + fi + ble/array#push buff "$ch" + done + IFS= builtin eval 'ret="${buff[*]}"' +} +function ble/complete/util/construct-glob-pattern { + local text=$1 + if [[ :$comp_type: == *:i:* ]]; then + local i n=${#text} c + local -a buff=() + for ((i=0;i1)); then + local unset_nocasematch= flag_tolower= + if [[ :$comp_type: == *:i:* ]]; then + if ((_ble_bash<30100)); then + flag_tolower=1 + ble/string#tolower "$common"; common=$ret + else + unset_nocasematch=1 + shopt -s nocasematch + fi + fi + local word loop=0 + for word in "${cand_word[@]:1}"; do + ((loop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && break + if [[ $flag_tolower ]]; then + ble/string#tolower "$word"; word=$ret + fi + ((clen>${#word}&&(clen=${#word}))) + while [[ ${word::clen} != "${common::clen}" ]]; do + ((clen--)) + done + common=${common::clen} + done + [[ $unset_nocasematch ]] && shopt -u nocasematch + ble/complete/check-cancel && return 148 + [[ $flag_tolower ]] && common=${cand_word[0]::${#common}} + fi + if [[ $common != "$COMPS"* && ! ( $cand_count -eq 1 && $comp_type == *:i:* ) ]]; then + ble/complete/candidates/determine-common-prefix/.apply-partial-comps + fi + if ((cand_count>1)) && [[ $common != "$COMPS"* ]]; then + local common0=$common + common=$COMPS # 取り敢えず補完挿入をキャンセル + if [[ :$comp_type: == *:[maAi]:* ]]; then + local simple_flags simple_ibrace + if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$common0"; then + local common_reconstructed=$ret + local value=$ret filter_type=head + case :$comp_type: in + (*:m:*) filter_type=substr ;; + (*:a:*) filter_type=hsubseq ;; + (*:A:*) filter_type=subseq ;; + esac + local is_processed= + ble/complete/source/eval-simple-word "$common_reconstructed" single; local ext=$? + ((ext==148)) && return 148 + if ((ext==0)) && ble/complete/candidates/filter:"$filter_type"/count-match-chars "$ret"; then + if [[ $filter_type == head ]] && ((ret<${#COMPV})); then + is_processed=1 + [[ $bleopt_complete_allow_reduction ]] && common=$common0 + elif ((ret)); then + is_processed=1 + ble/string#escape-for-bash-specialchars "${COMPV:ret}" c + common=$common0$ret + fi + fi + if [[ ! $is_processed ]] && + local notilde=\'\' && + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$COMPS" && + ble/syntax:bash/simple-word/eval "$notilde$ret" noglob && + local compv_notilde=$ret && + ble/syntax:bash/simple-word/eval "$notilde$common_reconstructed" noglob && + local commonv_notilde=$ret && + COMPV=$compv_notilde ble/complete/candidates/filter:"$filter_type"/count-match-chars "$commonv_notilde" + then + if [[ $filter_type == head ]] && ((ret<${#COMPV})); then + is_processed=1 + [[ $bleopt_complete_allow_reduction ]] && common=$common0 + elif ((ret)); then + is_processed=1 + ble/string#escape-for-bash-specialchars "${compv_notilde:ret}" TG + common=$common0$ret + fi + fi + [[ $is_processed ]] || common=$common0$COMPS + fi + else + if ble/syntax:bash/simple-word/is-simple-or-open-simple "$common"; then + local flag_reduction= + if [[ $bleopt_complete_allow_reduction ]]; then + flag_reduction=1 + else + local simple_flags simple_ibrace + ble/syntax:bash/simple-word/reconstruct-incomplete-word "$common0" && + ble/complete/source/eval-simple-word "$ret" single && + [[ $ret == "$COMPV"* ]] && + flag_reduction=1 + (($?==148)) && return 148 + fi + [[ $flag_reduction ]] && common=$common0 + fi + fi + fi + ret=$common +} +_ble_complete_menu_active= +_ble_complete_menu_style= +_ble_complete_menu0_beg= +_ble_complete_menu0_end= +_ble_complete_menu0_str= +_ble_complete_menu_common_part= +_ble_complete_menu0_comp=() +_ble_complete_menu0_pack=() +_ble_complete_menu_comp=() +function ble/complete/menu-complete.class/render-item { + local opts=$2 + if [[ :$opts: == *:selected:* ]]; then + local COMP1=${_ble_complete_menu_comp[0]} + local COMP2=${_ble_complete_menu_comp[1]} + local COMPS=${_ble_complete_menu_comp[2]} + local COMPV=${_ble_complete_menu_comp[3]} + local comp_type=${_ble_complete_menu_comp[4]} + local comps_flags=${_ble_complete_menu0_comp[5]} + local comps_fixed=${_ble_complete_menu0_comp[6]} + local menu_common_part=$_ble_complete_menu_common_part + fi + local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked + ble/complete/cand/unpack "$1" + local prefix_len=$PREFIX_LEN + [[ :$comp_type: == *:menu-show-prefix:* ]] && prefix_len=0 + local filter_target=${CAND:prefix_len} + if [[ ! $filter_target ]]; then + ret= + return 0 + fi + local g=0 show=$filter_target suffix= prefix= + ble/function#try ble/complete/action:"$ACTION"/init-menu-item + local g0=$g; [[ :$comp_type: == *:menu-color:* ]] || g0=0 + local m + if [[ :$comp_type: == *:menu-color-match:* && $_ble_complete_menu_common_part && $show == *"$filter_target"* ]]; then + local filter_type=head + case :$comp_type: in + (*:m:*) filter_type=substr ;; + (*:a:*) filter_type=hsubseq ;; + (*:A:*) filter_type=subseq ;; + esac + local needle=${_ble_complete_menu_common_part:prefix_len} + ble/complete/candidates/filter:"$filter_type"/match "$needle" "$filter_target"; m=("${ret[@]}") + if [[ $show != "$filter_target" ]]; then + local show_prefix=${show%%"$filter_target"*} + local offset=${#show_prefix} + local i n=${#m[@]} + for ((i=0;i=0)); then + local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked + ble/complete/cand/unpack "${_ble_complete_menu_items[nsel]}" + insert=$INSERT + fi + ble-edit/content/replace-limited "$_ble_complete_menu0_beg" "$_ble_edit_ind" "$insert" + ((_ble_edit_ind=_ble_complete_menu0_beg+${#insert})) +} +function ble/complete/menu/clear { + if [[ $_ble_complete_menu_active ]]; then + _ble_complete_menu_active= + ble/complete/menu#clear + [[ $_ble_highlight_layer_menu_filter_beg ]] && + ble/textarea#invalidate str # layer:menu_filter 解除 (#D0995) + fi + return 0 +} +blehook widget_bell+=ble/complete/menu/clear +blehook history_onleave+=ble/complete/menu/clear +function ble/complete/menu/get-footprint { + footprint=$_ble_edit_ind:$_ble_edit_mark_active:${_ble_edit_mark_active:+$_ble_edit_mark}:$_ble_edit_overwrite_mode:$_ble_edit_str +} +function ble/complete/menu/show { + local opts=$1 + if [[ :$opts: == *:load-filtered-data:* ]]; then + local COMP1=${_ble_complete_menu_comp[0]} + local COMP2=${_ble_complete_menu_comp[1]} + local COMPS=${_ble_complete_menu_comp[2]} + local COMPV=${_ble_complete_menu_comp[3]} + local comp_type=${_ble_complete_menu_comp[4]} + local comps_flags=${_ble_complete_menu0_comp[5]} + local comps_fixed=${_ble_complete_menu0_comp[6]} + local cand_pack; cand_pack=("${_ble_complete_menu_items[@]}") + local menu_common_part=$_ble_complete_menu_common_part + fi + local menu_style=$bleopt_complete_menu_style + [[ :$opts: == *:filter:* && $_ble_complete_menu_style ]] && + menu_style=$_ble_complete_menu_style + local menu_items; menu_items=("${cand_pack[@]}") + _ble_complete_menu_common_part=$menu_common_part + local menu_class=ble/complete/menu-complete.class menu_param= + local menu_opts=$opts + [[ :$comp_type: == *:sync:* ]] && menu_opts=$menu_opts:sync + ble/complete/menu#construct "$menu_opts" || return "$?" + ble/complete/menu#show + if [[ :$opts: == *:menu-source:* ]]; then + local left0=${_ble_complete_menu0_str::_ble_complete_menu0_end} + local left1=${_ble_edit_str::_ble_edit_ind} + local ret; ble/string#common-prefix "$left0" "$left1"; left0=$ret + local right0=${_ble_complete_menu0_str:_ble_complete_menu0_end} + local right1=${_ble_edit_str:_ble_edit_ind} + local ret; ble/string#common-suffix "$right0" "$right1"; right0=$ret + local footprint; ble/complete/menu/get-footprint + _ble_complete_menu0_str=$left0$right0 + _ble_complete_menu0_end=${#left0} + _ble_complete_menu_footprint=$footprint + elif [[ :$opts: != *:filter:* ]]; then + local beg=$COMP1 end=$_ble_edit_ind # COMP2 でなく補完挿入後の位置 + local str=$_ble_edit_str + [[ $_ble_decode_keymap == auto_complete ]] && + str=${str::_ble_edit_ind}${str:_ble_edit_mark} + local footprint; ble/complete/menu/get-footprint + _ble_complete_menu_active=1 + _ble_complete_menu_style=$menu_style + _ble_complete_menu0_beg=$beg + _ble_complete_menu0_end=$end + _ble_complete_menu0_str=$str + _ble_complete_menu0_comp=("$COMP1" "$COMP2" "$COMPS" "$COMPV" "$comp_type" "$comps_flags" "$comps_fixed") + _ble_complete_menu0_pack=("${cand_pack[@]}") + _ble_complete_menu_selected=-1 + _ble_complete_menu_comp=("$COMP1" "$COMP2" "$COMPS" "$COMPV" "$comp_type") + _ble_complete_menu_footprint=$footprint + fi + return 0 +} +function ble/complete/menu/redraw { + if [[ $_ble_complete_menu_active ]]; then + ble/complete/menu#show + fi +} +function ble/complete/menu/get-active-range { + [[ $_ble_complete_menu_active ]] || return 1 + local str=${1-$_ble_edit_str} ind=${2-$_ble_edit_ind} + local mbeg=$_ble_complete_menu0_beg + local mend=$_ble_complete_menu0_end + local left=${_ble_complete_menu0_str::mend} + local right=${_ble_complete_menu0_str:mend} + if [[ ${str::_ble_edit_ind} == "$left"* && ${str:_ble_edit_ind} == *"$right" ]]; then + ((beg=mbeg,end=${#str}-${#right})) + return 0 + else + ble/complete/menu/clear + return 1 + fi +} +function ble/complete/menu/generate-candidates-from-menu { + COMP1=${_ble_complete_menu_comp[0]} + COMP2=${_ble_complete_menu_comp[1]} + COMPS=${_ble_complete_menu_comp[2]} + COMPV=${_ble_complete_menu_comp[3]} + comp_type=${_ble_complete_menu_comp[4]} + comps_flags=${_ble_complete_menu0_comp[5]} + comps_fixed=${_ble_complete_menu0_comp[6]} + cand_count=${#_ble_complete_menu_items[@]} + cand_cand=() cand_word=() cand_pack=() + local pack "${_ble_complete_cand_varnames[@]/%/=}" # #D1570 WA checked + for pack in "${_ble_complete_menu_items[@]}"; do + ble/complete/cand/unpack "$pack" + ble/array#push cand_cand "$CAND" + ble/array#push cand_word "$INSERT" + ble/array#push cand_pack "$pack" + done + ((cand_count)) +} +function ble/complete/generate-candidates-from-opts { + local opts=$1 + local context; ble/complete/complete/determine-context-from-opts "$opts" + comp_type= + [[ :$opts: == *:auto_menu:* ]] && comp_type=auto_menu + local comp_text=$_ble_edit_str comp_index=$_ble_edit_ind + local sources + ble/complete/context:"$context"/generate-sources "$comp_text" "$comp_index" || return "$?" + ble/complete/candidates/generate "$opts" +} +function ble/complete/insert { + local insert_beg=$1 insert_end=$2 + local insert=$3 suffix=$4 + local original_text=${_ble_edit_str:insert_beg:insert_end-insert_beg} + local ret + local insert_replace= + if [[ $insert == "$original_text"* ]]; then + insert=${insert:insert_end-insert_beg} + ((insert_beg=insert_end)) + else + ble/string#common-prefix "$insert" "$original_text" + if [[ $ret ]]; then + insert=${insert:${#ret}} + ((insert_beg+=${#ret})) + fi + fi + if ble/util/test-rl-variable skip-completed-text; then + if [[ $insert ]]; then + local right_text=${_ble_edit_str:insert_end} + right_text=${right_text%%[$IFS]*} + if ble/string#common-prefix "$insert" "$right_text"; [[ $ret ]]; then + ((insert_end+=${#ret})) + elif ble/complete/string#common-suffix-prefix "$insert" "$right_text"; [[ $ret ]]; then + ((insert_end+=${#ret})) + fi + fi + if [[ $suffix ]]; then + local right_text=${_ble_edit_str:insert_end} + if ble/string#common-prefix "$suffix" "$right_text"; [[ $ret ]]; then + ((insert_end+=${#ret})) + elif ble/complete/string#common-suffix-prefix "$suffix" "$right_text"; [[ $ret ]]; then + ((insert_end+=${#ret})) + fi + fi + fi + local ins=$insert$suffix + ble/widget/.replace-range "$insert_beg" "$insert_end" "$ins" + ((_ble_edit_ind=insert_beg+${#ins}, + _ble_edit_ind>${#_ble_edit_str}&& + (_ble_edit_ind=${#_ble_edit_str}))) +} +function ble/complete/insert-common { + local ret + ble/complete/candidates/determine-common-prefix; (($?==148)) && return 148 + local insert=$ret suffix= + local insert_beg=$COMP1 insert_end=$COMP2 + local insert_flags= + [[ $insert == "$COMPS"* ]] || insert_flags=r + if ((cand_count==1)); then + local ACTION=${cand_pack[0]%%:*} + if ble/is-function ble/complete/action:"$ACTION"/complete; then + local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked + ble/complete/cand/unpack "${cand_pack[0]}" + ble/complete/action:"$ACTION"/complete + (($?==148)) && return 148 + fi + else + insert_flags=${insert_flags}m + fi + local do_insert=1 + if ((cand_count>1)) && [[ $insert_flags == *r* ]]; then + if [[ :$comp_type: != *:[maAi]:* ]]; then + do_insert= + fi + elif [[ $insert$suffix == "$COMPS" ]]; then + do_insert= + fi + if [[ $do_insert ]]; then + ble/complete/insert "$insert_beg" "$insert_end" "$insert" "$suffix" + blehook/invoke complete_insert + fi + if [[ $insert_flags == *m* ]]; then + local menu_common_part=$COMPV + local ret simple_flags simple_ibrace + if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$insert"; then + ble/complete/source/eval-simple-word "$ret" single + (($?==148)) && return 148 + menu_common_part=$ret + fi + ble/complete/menu/show "$menu_show_opts" || return "$?" + elif [[ $insert_flags == *n* ]]; then + ble/widget/complete show_menu:regenerate || return "$?" + else + _ble_complete_state=complete + ble/complete/menu/clear + fi + return 0 +} +function ble/complete/insert-all { + local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked + local pack beg=$COMP1 end=$COMP2 insert= suffix= insert_flags= index=0 + for pack in "${cand_pack[@]}"; do + ble/complete/cand/unpack "$pack" + insert=$INSERT suffix= insert_flags= + if ble/is-function ble/complete/action:"$ACTION"/complete; then + ble/complete/action:"$ACTION"/complete + (($?==148)) && return 148 + fi + [[ $suffix != *' ' ]] && suffix="$suffix " + ble/complete/insert "$beg" "$end" "$insert" "$suffix" + blehook/invoke complete_insert + beg=$_ble_edit_ind end=$_ble_edit_ind + ((index++)) + done + _ble_complete_state=complete + ble/complete/menu/clear + return 0 +} +function ble/complete/insert-braces/.compose { + if ble/bin/awk.supports-null-record-separator; then + local printf_format='%s\0' RS='"\0"' + else + local printf_format='%s\x1E' RS='"\x1E"' + fi + local q=\' + local -x rex_atom='^(\\.|[0-9]+|.)' del_close= del_open= quote_type= + local -x COMPS=$COMPS + if [[ :$comp_type: != *:[maAi]:* ]]; then + local rex_brace='[,{}]|\{[-a-zA-Z0-9]+\.\.[-a-zA-Z0-9]+\}' + case $comps_flags in + (*S*) rex_atom='^('$q'(\\'$q'|'$rex_brace')'$q'|[0-9]+|.)' # '...' + del_close=\' del_open=\' quote_type=S ;; + (*E*) rex_atom='^(\\.|'$q'('$rex_brace')\$'$q'|[0-9]+|.)' # $'...' + del_close=\' del_open=\$\' quote_type=E ;; + (*[DI]*) rex_atom='^(\\[\"$`]|"('$rex_brace')"|[0-9]+|.)' # "...", $"..." + del_close=\" del_open=\" quote_type=D ;; + esac + fi + printf "$printf_format" "$@" | ble/bin/awk ' + function starts_with(str, head) { + return substr(str, 1, length(head)) == head; + } + BEGIN { + RS = '"$RS"'; + rex_atom = ENVIRON["rex_atom"]; + del_close = ENVIRON["del_close"]; + del_open = ENVIRON["del_open"]; + quote_type = ENVIRON["quote_type"]; + COMPS = ENVIRON["COMPS"]; + BRACE_OPEN = del_close "{" del_open; + BRACE_CLOS = del_close "}" del_open; + } + function to_atoms(str, arr, _, chr, atom, level, count, rex) { + count = 0; + while (match(str, rex_atom) > 0) { + chr = substr(str, 1, RLENGTH); + str = substr(str, RLENGTH + 1); + if (chr == BRACE_OPEN) { + atom = chr; + level = 1; + while (match(str, rex_atom) > 0) { + chr = substr(str, 1, RLENGTH); + str = substr(str, RLENGTH + 1); + atom = atom chr; + if (chr == BRACE_OPEN) + level++; + else if (chr == BRACE_CLOS && --level==0) + break; + } + } else { + atom = chr; + } + arr[count++] = atom; + } + return count; + } + function remove_empty_quote(str, _, rex_quote_first, rex_quote, out, empty, m) { + if (quote_type == "S" || quote_type == "E") { + rex_quote_first = "^[^'$q']*'$q'"; + rex_quote = "'$q'[^'$q']*'$q'|(\\\\.|[^'$q'])+"; + } else if (quote_type == "D") { + rex_quote_first = "^[^\"]*\""; + rex_quote = "\"([^\\\"]|\\\\.)*\"|(\\\\.|[^\"])+"; + } else return str; + empty = del_open del_close; + out = ""; + if (starts_with(str, COMPS)) { + out = COMPS; + str = substr(str, length(COMPS) + 1); + if (match(str, rex_quote_first) > 0) { + out = out substr(str, 1, RLENGTH); + str = substr(str, RLENGTH + 1); + } + } + while (match(str, rex_quote) > 0) { + m = substr(str, 1, RLENGTH); + if (m != empty) out = out m; + str = substr(str, RLENGTH + 1); + } + if (str == del_open) + return out; + else + return out str del_close; + } + function zpad(value, width, _, wpad, i, pad) { + wpad = width - length(value); + pad = ""; + for (i = 0; i < wpad; i++) pad = "0" pad; + if (value < 0) + return "-" pad (-value); + else + return pad value; + } + function zpad_remove(value) { + if (value ~ /^0+$/) + value = "0"; + else if (value ~ /^-/) + sub(/^-0+/, "-", value); + else + sub(/^0+/, "", value); + return value; + } + function zpad_a2i(text) { + sub(/^-0+/, "-", text) || sub(/^0+/, "", text); + return 0 + text; + } + function range_contract(arr, len, _, i, value, alpha, lower, upper, keys, ikey, dict, b, e, beg, end, tmp) { + lower = "abcdefghijklmnopqrstuvwxyz"; + upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (i = 0; i < len; i++) { + value = arr[i]; + if (dict[value]) { + dict[value]++; + } else { + keys[ikey++] = value; + dict[value] = 1; + } + } + len = 0; + for (i = 0; i < ikey; i++) { + while (dict[value = keys[i]]--) { + if (value ~ /^([a-zA-Z])$/) { + alpha = (value ~ /^[a-z]$/) ? lower : upper; + beg = end = value; + b = e = index(alpha, value); + while (b > 1 && dict[tmp = substr(alpha, b - 1, 1)]) { + dict[beg = tmp]--; + b--; + } + while (e < 26 && dict[tmp = substr(alpha, e + 1, 1)]) { + dict[end = tmp]--; + e++; + } + if (e == b) { + arr[len++] = beg; + } else if (e == b + 1) { + arr[len++] = beg; + arr[len++] = end; + } else { + arr[len++] = del_close "{" beg ".." end "}" del_open; + } + } else if (value ~ /^(0+|-?0*[1-9][0-9]*)$/) { + beg = end = value; + b = e = zpad_a2i(value); + wmax = wmin = length(value); + if (value ~ /^(0|-?[1-9][0-9]*)$/) { + while (dict[b - 1]) dict[--b]--; + while (dict[e + 1]) dict[++e]--; + tmp = length(beg = "" b); + if (tmp < wmin) wmin = tmp; + else if (tmp > wmax) wmax = tmp; + tmp = length(end = "" e); + if (tmp < wmin) wmin = tmp; + else if (tmp > wmax) wmax = tmp; + } + if (wmax == wmin) { + while (length(tmp = zpad(b - 1, wmin)) == wmin && dict[tmp]) { dict[tmp]--; --b; } + while (length(tmp = zpad(e + 1, wmin)) == wmin && dict[tmp]) { dict[tmp]--; ++e; } + beg = zpad(b, wmin); + end = zpad(e, wmin); + } + if (e == b) { + arr[len++] = beg; + } else if (e == b + 1) { + arr[len++] = beg; + arr[len++] = end; + } else if (b < 0 && e < 0) { + arr[len++] = del_close "-{" substr(end, 2) ".." substr(beg, 2) "}" del_open; + } else { + arr[len++] = del_close "{" beg ".." end "}" del_open; + } + } else { + arr[len++] = value; + } + } + } + return len; + } + function simple_brace(arr, len, _, ret, i) { + if (len == 0) return ""; + len = range_contract(arr, len); + if (len == 1) return arr[0]; + ret = BRACE_OPEN arr[0]; + for (i = 1; i < len; i++) + ret = ret del_close "," del_open arr[i]; + return ret BRACE_CLOS; + } + function rfrag_strlen_common(a, b, _, la, lb, tmp, i, n) { + ret = 0; + alen = to_atoms(a, abuf); + blen = to_atoms(b, bbuf); + while (alen > 0 && blen > 0) { + if (abuf[alen - 1] != bbuf[blen - 1]) break; + ret += length(abuf[alen - 1]); + alen--; + blen--; + } + return ret; + } + function rfrag_get_level(str, _, len, i, rfrag0, rfrag0len, rfrag1) { + len = length(str); + rfrag_matching_offset = len; + for (i = 0; i < rfrag_depth - 1; i++) { + rfrag0 = rfrag[i]; + rfrag0len = length(rfrag0); + rfrag1 = substr(str, len - rfrag0len + 1); + str = substr(str, 1, len -= rfrag0len); + if (rfrag0 != rfrag1) break; + rfrag_matching_offset -= rfrag0len; + } + while (i && rfrag[i - 1] == "") i--; # empty fragment + return i; + } + function rfrag_reduce(new_depth, _, c, i, brace, frags) { + while (rfrag_depth > new_depth) { + rfrag_depth--; + c = rfrag_count[rfrag_depth]; + for (i = 0; i < c; i++) + frags[i] = rfrag[rfrag_depth, i]; + frags[c] = rfrag[rfrag_depth]; + brace = simple_brace(frags, c + 1); + if (rfrag_depth == 0) + return brace; + else + rfrag[rfrag_depth - 1] = brace rfrag[rfrag_depth - 1]; + } + } + function rfrag_register(str, level, _, rfrag0, rfrag1, len) { + if (level == rfrag_depth) { + rfrag_depth = level + 1; + rfrag[level] = ""; + rfrag_count[level] = 0; + } else if (rfrag_depth != level + 1) { + print "ERR(rfrag)"; + } + rfrag0 = rfrag[level]; + rfrag1 = substr(str, 1, rfrag_matching_offset); + len = rfrag_strlen_common(rfrag0, rfrag1); + if (len == 0) { + rfrag[level, rfrag_count[level]++] = rfrag0; + rfrag[level] = rfrag1; + } else { + rfrag[level] = substr(rfrag0, length(rfrag0) - len + 1); + rfrag[level + 1, 0] = substr(rfrag0, 1, length(rfrag0) - len); + rfrag[level + 1] = substr(rfrag1, 1, length(rfrag1) - len); + rfrag_count[level + 1] = 1; + rfrag_depth++; + } + } + function rfrag_dump(_, i, j, prefix) { + print "depth = " rfrag_depth; + for (i = 0; i < rfrag_depth; i++) { + prefix = ""; + for (j = 0; j < i; j++) prefix = prefix " "; + for (j = 0; j < rfrag_count[i]; j++) + print prefix "rfrag[" i "," j "] = " rfrag[i,j]; + print prefix "rfrag[" i "] = " rfrag[i]; + } + } + function rfrag_brace(arr, len, _, i, level) { + if (len == 0) return ""; + if (len == 1) return arr[0]; + rfrag_depth = 1; + rfrag[0] = arr[0]; + rfrag_count[0] = 0; + for (i = 1; i < len; i++) { + level = rfrag_get_level(arr[i]); + rfrag_reduce(level + 1); + rfrag_register(arr[i], level); + } + return rfrag_reduce(0); + } + function lfrag_strlen_common(a, b, _, ret, abuf, bbuf, alen, blen, ia, ib) { + ret = 0; + alen = to_atoms(a, abuf); + blen = to_atoms(b, bbuf); + for (ia = ib = 0; ia < alen && ib < blen; ia++ + ib++) { + if (abuf[ia] != bbuf[ib]) break; + ret += length(abuf[ia]); + } + return ret; + } + function lfrag_get_level(str, _, i, frag0, frag0len, frag1) { + lfrag_matching_offset = 0; + for (i = 0; i < lfrag_depth - 1; i++) { + frag0 = frag[i] + frag0len = length(frag0); + frag1 = substr(str, lfrag_matching_offset + 1, frag0len); + if (frag0 != frag1) break; + lfrag_matching_offset += frag0len; + } + while (i && frag[i - 1] == "") i--; # empty fragment + return i; + } + function lfrag_reduce(new_depth, _, c, i, brace, frags) { + while (lfrag_depth > new_depth) { + lfrag_depth--; + c = frag_count[lfrag_depth]; + for (i = 0; i < c; i++) + frags[i] = frag[lfrag_depth, i]; + frags[c] = frag[lfrag_depth]; + brace = rfrag_brace(frags, c + 1); + if (lfrag_depth == 0) + return brace; + else + frag[lfrag_depth - 1] = frag[lfrag_depth - 1] brace; + } + } + function lfrag_register(str, level, _, frag0, frag1, len) { + if (lfrag_depth == level) { + lfrag_depth = level + 1; + frag[level] = ""; + frag_count[level] = 0; + } else if (lfrag_depth != level + 1) { + print "ERR"; + } + frag0 = frag[level]; + frag1 = substr(str, lfrag_matching_offset + 1); + len = lfrag_strlen_common(frag0, frag1); + if (len == 0) { + frag[level, frag_count[level]++] = frag0; + frag[level] = frag1; + } else { + frag[level] = substr(frag0, 1, len); + frag[level + 1, 0] = substr(frag0, len + 1); + frag[level + 1] = substr(frag1, len + 1); + frag_count[level + 1] = 1; + lfrag_depth++; + } + } + function lfrag_dump(_, i, j, prefix) { + print "depth = " lfrag_depth; + for (i = 0; i < lfrag_depth; i++) { + prefix = ""; + for (j = 0; j < i; j++) prefix = prefix " "; + for (j = 0; j < frag_count[i]; j++) + print prefix "frag[" i "," j "] = " frag[i,j]; + print prefix "frag[" i "] = " frag[i]; + } + } + NR == 1 { + lfrag_depth = 1; + frag[0] = $0; + frag_count[0] = 0; + next + } + { + level = lfrag_get_level($0); + lfrag_reduce(level + 1); + lfrag_register($0, level); + } + END { + result = lfrag_reduce(0); + result = remove_empty_quote(result); + print result; + } + ' +} +function ble/complete/insert-braces { + if ((cand_count==1)); then + ble/complete/insert-common; return "$?" + fi + local comps_len=${#COMPS} loop=0 + local -a tails=() + local common=${cand_word[0]} + ble/array#push tails "${common:comps_len}" + local word clen=${#common} + for word in "${cand_word[@]:1}"; do + ((loop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + ((clen>${#word}&&(clen=${#word}))) + while [[ ${word::clen} != "${common::clen}" ]]; do + ((clen--)) + done + common=${common::clen} + ble/array#push tails "${word:comps_len}" + done + local fixed=$COMPS + if [[ $common != "$COMPS"* ]]; then + tails=() + local fixed= fixval= + { + [[ $comps_fixed ]] && + fixed=${COMPS::${comps_fixed%%:*}} fixval=${comps_fixed#*:} + local ret simple_flags simple_ibrace + ble/complete/candidates/determine-common-prefix/.apply-partial-comps # var[in,out] common + if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$common"; then + ble/complete/source/eval-simple-word "$ret" single + (($?==148)) && return 148 + fixed=$common fixval=$ret + fi + } + local cand ret fixval_len=${#fixval} + for cand in "${cand_cand[@]}"; do + ((loop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 + [[ $cand == "$fixval"* ]] || continue + ble/complete/string#escape-for-completion-context "${cand:fixval_len}" + case $comps in + (*S*) cand=\'$ret\' ;; + (*E*) cand=\$\'$ret\' ;; + (*D*) cand=\"$ret\" ;; + (*I*) cand=\$\"$ret\" ;; + (*) cand=$ret ;; + esac + ble/array#push tails "$cand" + done + fi + local tail; ble/util/assign tail 'ble/complete/insert-braces/.compose "${tails[@]}"' + local beg=$COMP1 end=$COMP2 insert=$fixed$tail suffix= + if [[ $comps_flags == *x* ]]; then + ble/complete/action/complete.addtail ',' + else + ble/complete/action/complete.addtail ' ' + fi + ble/complete/insert "$beg" "$end" "$insert" "$suffix" + blehook/invoke complete_insert + _ble_complete_state=complete + ble/complete/menu/clear + return 0 +} +_ble_complete_state= +function ble/widget/complete { + local opts=$1 + ble-edit/content/clear-arg + local state=$_ble_complete_state + _ble_complete_state=start + local menu_show_opts= + if [[ :$opts: != *:insert_*:* && :$opts: != *:show_menu:* ]]; then + if [[ :$opts: == *:enter_menu:* ]]; then + [[ $_ble_complete_menu_active && :$opts: != *:context=*:* ]] && + ble/complete/menu-complete/enter "$opts" && return 0 + elif [[ $bleopt_complete_menu_complete ]]; then + if [[ $_ble_complete_menu_active && :$opts: != *:context=*:* ]]; then + local footprint; ble/complete/menu/get-footprint + [[ $footprint == "$_ble_complete_menu_footprint" ]] && + ble/complete/menu-complete/enter && return 0 + fi + [[ $WIDGET == "$LASTWIDGET" && $state != complete ]] && opts=$opts:enter_menu + fi + fi + local COMP1 COMP2 COMPS COMPV + local comp_type comps_flags comps_fixed + local cand_count cand_cand cand_word cand_pack + ble/complete/candidates/clear + local cand_limit_reached= + if [[ $_ble_complete_menu_active && :$opts: != *:regenerate:* && + :$opts: != *:context=*:* && ${#_ble_complete_menu_icons[@]} -gt 0 ]] + then + if [[ $_ble_complete_menu_filter_enabled && $bleopt_complete_menu_filter ]] || { + ble/complete/menu-filter; local ext=$? + ((ext==148)) && return 148 + ((ext==0)); }; then + ble/complete/menu/generate-candidates-from-menu; local ext=$? + ((ext==148)) && return 148 + if ((ext==0&&cand_count)); then + local bleopt_complete_menu_style=$_ble_complete_menu_style + menu_show_opts=$menu_show_opts:menu-source # 既存の filter 前候補を保持する + fi + fi + fi + if ((cand_count==0)); then + local bleopt_complete_menu_style=$bleopt_complete_menu_style # source 等に一次変更を認める。 + ble/complete/generate-candidates-from-opts "$opts"; local ext=$? + if ((ext==148)); then + return 148 + fi + if [[ $cand_limit_reached ]]; then + [[ :$opts: != *:no-bell:* ]] && + ble/widget/.bell 'complete: limit reached' + if [[ $cand_limit_reached == cancel ]]; then + ble/edit/info/default + return 1 + fi + fi + if ((ext!=0||cand_count==0)); then + [[ :$opts: != *:no-bell:* && ! $cand_limit_reached ]] && + ble/widget/.bell 'complete: no completions' + ble/edit/info/default + return 1 + fi + fi + if [[ :$opts: == *:insert_common:* ]]; then + ble/complete/insert-common; return "$?" + elif [[ :$opts: == *:insert_braces:* ]]; then + ble/complete/insert-braces; return "$?" + elif [[ :$opts: == *:insert_all:* ]]; then + ble/complete/insert-all; return "$?" + elif [[ :$opts: == *:enter_menu:* ]]; then + local menu_common_part=$COMPV + ble/complete/menu/show "$menu_show_opts" || return "$?" + ble/complete/menu-complete/enter "$opts"; local ext=$? + ((ext==148)) && return 148 + ((ext)) && [[ :$opts: != *:no-bell:* ]] && + ble/widget/.bell 'menu-complete: no completions' + return 0 + elif [[ :$opts: == *:show_menu:* ]]; then + local menu_common_part=$COMPV + ble/complete/menu/show "$menu_show_opts" + return "$?" # exit status of ble/complete/menu/show + fi + ble/complete/insert-common; return "$?" +} +function ble/widget/complete-insert { + local original=$1 insert=$2 suffix=$3 + [[ ${_ble_edit_str::_ble_edit_ind} == *"$original" ]] || return 1 + local insert_beg=$((_ble_edit_ind-${#original})) + local insert_end=$_ble_edit_ind + ble/complete/insert "$insert_beg" "$insert_end" "$insert" "$suffix" +} +function ble/widget/menu-complete { + local opts=$1 + ble/widget/complete enter_menu:$opts +} +function ble/complete/menu-filter/.filter-candidates { + cand_pack=() + local iloop=0 interval=$bleopt_complete_polling_cycle + local filter_type pack "${_ble_complete_cand_varnames[@]/%/=}" # #D1570 WA checked + for filter_type in head substr hsubseq subseq; do + ble/path#remove-glob comp_type '[maA]' + case $filter_type in + (substr) comp_type=${comp_type}:m ;; + (hsubseq) comp_type=${comp_type}:a ;; + (subseq) comp_type=${comp_type}:A ;; + esac + local comp_filter_type + local comp_filter_pattern + ble/complete/candidates/filter#init "$filter_type" "$COMPV" + for pack in "${_ble_complete_menu0_pack[@]}"; do + ((iloop++%interval==0)) && ble/complete/check-cancel && return 148 + ble/complete/cand/unpack "$pack" + ble/complete/candidates/filter#test "$CAND" && + ble/array#push cand_pack "$pack" + done + ((${#cand_pack[@]}!=0)) && return 0 + done +} +function ble/complete/menu-filter/.get-filter-target { + if [[ $_ble_decode_keymap == emacs || $_ble_decode_keymap == vi_[ic]map ]]; then + ret=$_ble_edit_str + elif [[ $_ble_decode_keymap == auto_complete ]]; then + ret=${_ble_edit_str::_ble_edit_ind}${_ble_edit_str:_ble_edit_mark} + else + return 1 + fi +} +function ble/complete/menu-filter { + [[ $_ble_decode_keymap == menu_complete ]] && return 0 + local ret; ble/complete/menu-filter/.get-filter-target || return 1; local str=$ret + local beg end; ble/complete/menu/get-active-range "$str" "$_ble_edit_ind" || return 1 + local input=${str:beg:end-beg} + [[ $input == "${_ble_complete_menu_comp[2]}" ]] && return 0 + local simple_flags simple_ibrace + if ! ble/syntax:bash/simple-word/reconstruct-incomplete-word "$input"; then + ble/syntax:bash/simple-word/is-never-word "$input" && return 1 + return 0 + fi + [[ $simple_ibrace ]] && ((${simple_ibrace%%:*}>10#0${_ble_complete_menu0_comp[6]%%:*})) && return 1 # 別のブレース展開要素に入った時 + ble/syntax:bash/simple-word/eval "$ret" single; (($?==148)) && return 148 + local COMPV=$ret + local comp_type=${_ble_complete_menu0_comp[4]} cand_pack + ble/complete/menu-filter/.filter-candidates; (($?==148)) && return 148 + local menu_common_part=$COMPV + ble/complete/menu/show filter || return "$?" + _ble_complete_menu_comp=("$beg" "$end" "$input" "$COMPV" "$comp_type") + return 0 +} +function ble/complete/menu-filter.idle { + ble/util/idle.wait-user-input + [[ $bleopt_complete_menu_filter ]] || return 1 + [[ $_ble_complete_menu_active ]] || return 1 + ble/complete/menu-filter; local ext=$? + ((ext==148)) && return 148 + ((ext)) && ble/complete/menu/clear + return 0 +} +function ble/highlight/layer/buff#operate-gflags { + local BUFF=$1 beg=$2 end=$3 mask=$4 gflags=$5 + ((beg=0)); then + ((DMAX0<=obeg?(obeg+=DMAX-DMAX0):(DMIN=1)) || return 1 + local beg end; ble/complete/menu/get-active-range || return 1 + local opts=$1 + _ble_edit_mark=$beg + _ble_edit_ind=$end + local comps_fixed=${_ble_complete_menu0_comp[6]} + if [[ $comps_fixed ]]; then + local comps_fixed_length=${comps_fixed%%:*} + ((_ble_edit_mark+=comps_fixed_length)) + fi + _ble_complete_menu_original=${_ble_edit_str:beg:end-beg} + ble/complete/menu/redraw + if [[ :$opts: == *:backward:* ]]; then + ble/complete/menu#select $((${#_ble_complete_menu_items[@]}-1)) + else + ble/complete/menu#select 0 + fi + _ble_edit_mark_active=insert + ble/decode/keymap/push menu_complete + return 0 +} +function ble/widget/menu_complete/exit { + local opts=$1 + ble/decode/keymap/pop + if ((_ble_complete_menu_selected>=0)); then + local new=${_ble_edit_str:_ble_complete_menu0_beg:_ble_edit_ind-_ble_complete_menu0_beg} + local old=$_ble_complete_menu_original + local comp_text=${_ble_edit_str::_ble_complete_menu0_beg}$old${_ble_edit_str:_ble_edit_ind} + local insert_beg=$_ble_complete_menu0_beg + local insert_end=$((_ble_complete_menu0_beg+${#old})) + local insert=$new + local insert_flags= + local suffix= + if [[ :$opts: == *:complete:* ]]; then + local icon=${_ble_complete_menu_icons[_ble_complete_menu_selected-_ble_complete_menu_offset]} + local icon_data=${icon#*:} icon_fields + ble/string#split icon_fields , "${icon%%:*}" + local pack=${icon_data::icon_fields[4]} + local ACTION=${pack%%:*} + if ble/is-function ble/complete/action:"$ACTION"/complete; then + local COMP1=${_ble_complete_menu0_comp[0]} + local COMP2=${_ble_complete_menu0_comp[1]} + local COMPS=${_ble_complete_menu0_comp[2]} + local COMPV=${_ble_complete_menu0_comp[3]} + local comp_type=${_ble_complete_menu0_comp[4]} + local comps_flags=${_ble_complete_menu0_comp[5]} + local comps_fixed=${_ble_complete_menu0_comp[6]} + local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked + ble/complete/cand/unpack "$pack" + ble/complete/action:"$ACTION"/complete + fi + ble/complete/insert "$_ble_complete_menu0_beg" "$_ble_edit_ind" "$insert" "$suffix" + fi + blehook/invoke complete_insert + fi + ble/complete/menu/clear + _ble_edit_mark_active= + _ble_complete_menu_original= +} +function ble/widget/menu_complete/cancel { + ble/decode/keymap/pop + ble/complete/menu#select -1 + _ble_edit_mark_active= + _ble_complete_menu_original= +} +function ble/widget/menu_complete/accept { + ble/widget/menu_complete/exit complete +} +function ble/widget/menu_complete/exit-default { + ble/widget/menu_complete/exit + ble/decode/widget/skip-lastwidget + ble/decode/widget/redispatch-by-keys "${KEYS[@]}" +} +function ble-decode/keymap:menu_complete/define { + ble-bind -f __default__ 'menu_complete/exit-default' + ble-bind -f __line_limit__ nop + ble-bind -f C-m 'menu_complete/accept' + ble-bind -f RET 'menu_complete/accept' + ble-bind -f C-g 'menu_complete/cancel' + ble-bind -f 'C-x C-g' 'menu_complete/cancel' + ble-bind -f 'C-M-g' 'menu_complete/cancel' + ble-bind -f C-f 'menu/forward-column' + ble-bind -f right 'menu/forward-column' + ble-bind -f C-i 'menu/forward cyclic' + ble-bind -f TAB 'menu/forward cyclic' + ble-bind -f C-b 'menu/backward-column' + ble-bind -f left 'menu/backward-column' + ble-bind -f C-S-i 'menu/backward cyclic' + ble-bind -f S-TAB 'menu/backward cyclic' + ble-bind -f C-n 'menu/forward-line' + ble-bind -f down 'menu/forward-line' + ble-bind -f C-p 'menu/backward-line' + ble-bind -f up 'menu/backward-line' + ble-bind -f prior 'menu/backward-page' + ble-bind -f next 'menu/forward-page' + ble-bind -f home 'menu/beginning-of-page' + ble-bind -f end 'menu/end-of-page' +} +function ble/complete/auto-complete/initialize { + local ret + ble-decode-kbd/generate-keycode auto_complete_enter + _ble_complete_KCODE_ENTER=$ret +} +ble/complete/auto-complete/initialize +function ble/highlight/layer:region/mark:auto_complete/get-face { + face=auto_complete +} +_ble_complete_ac_type= +_ble_complete_ac_comp1= +_ble_complete_ac_cand= +_ble_complete_ac_word= +_ble_complete_ac_insert= +_ble_complete_ac_suffix= +function ble/complete/auto-complete/.search-history-light { + [[ $_ble_history_prefix ]] && return 1 + local text=$1 + [[ ! $text ]] && return 1 + local wordbreaks="<>();&|:$_ble_term_IFS" + local word= expand + if [[ $text != [-0-9#?!]* ]]; then + word=${text%%[$wordbreaks]*} + command='!'$word ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1 + if [[ $expand == "$text"* ]]; then + ret=$expand + return 0 + fi + fi + if [[ $word != "$text" ]]; then + local fragments; ble/string#split fragments '?' "$text" + local frag longest_fragments len=0; longest_fragments=('') + for frag in "${fragments[@]}"; do + local len1=${#frag} + ((len1>len&&(len=len1))) && longest_fragments=() + ((len1==len)) && ble/array#push longest_fragments "$frag" + done + for frag in "${longest_fragments[@]}"; do + command='!?'$frag ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1 + [[ $expand == "$text"* ]] || continue + ret=$expand + return 0 + done + fi + return 1 +} +_ble_complete_ac_history_needle= +_ble_complete_ac_history_index= +_ble_complete_ac_history_start= +function ble/complete/auto-complete/.search-history-heavy { + local text=$1 + local count; ble/history/get-count -v count + local start=$((count-1)) + local index=$((count-1)) + local needle=$text + ((start==_ble_complete_ac_history_start)) && + [[ $needle == "$_ble_complete_ac_history_needle"* ]] && + index=$_ble_complete_ac_history_index + local isearch_time=0 isearch_ntask=1 + local isearch_opts=head + [[ :$comp_type: == *:sync:* ]] || isearch_opts=$isearch_opts:stop_check + ble/history/isearch-backward-blockwise "$isearch_opts"; local ext=$? + _ble_complete_ac_history_start=$start + _ble_complete_ac_history_index=$index + _ble_complete_ac_history_needle=$needle + ((ext)) && return "$ext" + ble/history/get-edited-entry -v ret "$index" + return 0 +} +function ble/complete/auto-complete/.enter-auto-complete-mode { + _ble_complete_ac_type=$type + _ble_complete_ac_comp1=$COMP1 + _ble_complete_ac_cand=$cand + _ble_complete_ac_word=$word + _ble_complete_ac_insert=$insert + _ble_complete_ac_suffix=$suffix + _ble_edit_mark_active=auto_complete + ble/decode/keymap/push auto_complete + ble-decode-key "$_ble_complete_KCODE_ENTER" # dummy key input to record keyboard macros +} +function ble/complete/auto-complete/.insert { + local insert=$1 + ble-edit/content/replace-limited "$_ble_edit_ind" "$_ble_edit_ind" "$insert" nobell + ((_ble_edit_mark=_ble_edit_ind+${#insert})) +} +function ble/complete/auto-complete/.check-history { + local opts=$1 + local searcher=.search-history-heavy + [[ :$opts: == *:light:* ]] && searcher=.search-history-light + local ret + ((_ble_edit_ind==${#_ble_edit_str})) || return 1 + ble/complete/auto-complete/"$searcher" "$_ble_edit_str" || return "$?" # 0, 1 or 148 + local word=$ret cand= + local COMP1=0 COMPS=$_ble_edit_str + [[ $word == "$COMPS" ]] && return 1 + local insert=$word suffix= + local type=h + ble/complete/auto-complete/.insert "${insert:${#COMPS}}" + ble/complete/auto-complete/.enter-auto-complete-mode + return 0 +} +function ble/complete/auto-complete/.check-context { + local sources + ble/complete/context:syntax/generate-sources "$comp_text" "$comp_index" && + ble/complete/context/filter-prefix-sources || return 1 + local bleopt_complete_contract_function_names= + local bleopt_complete_menu_style=$bleopt_complete_menu_style # source local settings + ((bleopt_complete_polling_cycle>25)) && + local bleopt_complete_polling_cycle=25 + local COMP1 COMP2 COMPS COMPV + local comps_flags comps_fixed + local cand_count cand_cand cand_word cand_pack + local cand_limit_reached= + ble/complete/candidates/generate; local ext=$? + [[ $COMPV ]] || return 1 + ((ext)) && return "$ext" + ((cand_count)) || return 1 + local word=${cand_word[0]} cand=${cand_cand[0]} + [[ $word == "$COMPS" ]] && return 1 + local insert=$word suffix= + local ACTION=${cand_pack[0]%%:*} + if ble/is-function ble/complete/action:"$ACTION"/complete; then + local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked + ble/complete/cand/unpack "${cand_pack[0]}" + ble/complete/action:"$ACTION"/complete + fi + local type= + if [[ $insert == "$COMPS"* ]]; then + [[ ${comp_text:COMP1} == "$insert"* ]] && return 1 + type=c + ble/complete/auto-complete/.insert "${insert:${#COMPS}}" + else + case :$comp_type: in + (*:a:*) type=a ;; + (*:m:*) type=m ;; + (*:A:*) type=A ;; + (*) type=r ;; + esac + ble/complete/auto-complete/.insert " [$insert] " + fi + ble/complete/auto-complete/.enter-auto-complete-mode + return 0 +} +function ble/complete/auto-complete.impl { + local opts=$1 + local comp_type=auto + [[ :$opts: == *:sync:* ]] && comp_type=${comp_type}:sync + local comp_text=$_ble_edit_str comp_index=$_ble_edit_ind + [[ $comp_text ]] || return 0 + if local beg end; ble/complete/menu/get-active-range "$_ble_edit_str" "$_ble_edit_ind"; then + ((_ble_edit_ind0)) || return 1 + case $_ble_decode_widget_last in + (ble/widget/self-insert) ;; + (ble/widget/complete) ;; + (ble/widget/vi_imap/complete) ;; + (ble/widget/auto_complete/self-insert) ;; + (*) return 0 ;; + esac + [[ $_ble_edit_str ]] || return 0 + local until=$((_idle_clock_start+bleopt_complete_auto_menu)) + ble/util/idle.sleep-until "$until" checked && return 0 + ble/widget/complete auto_menu:show_menu:no-empty:no-bell +} +ble/function#try ble/util/idle.push-background ble/complete/auto-complete.idle +ble/function#try ble/util/idle.push-background ble/complete/auto-menu.idle +function ble/widget/auto-complete-enter { + ble/complete/auto-complete.impl sync +} +function ble/widget/auto_complete/cancel { + ble/decode/keymap/pop + ble-edit/content/replace "$_ble_edit_ind" "$_ble_edit_mark" '' + _ble_edit_mark=$_ble_edit_ind + _ble_edit_mark_active= + _ble_complete_ac_insert= + _ble_complete_ac_suffix= +} +function ble/widget/auto_complete/insert { + ble/decode/keymap/pop + ble-edit/content/replace "$_ble_edit_ind" "$_ble_edit_mark" '' + _ble_edit_mark=$_ble_edit_ind + local comp_text=$_ble_edit_str + local insert_beg=$_ble_complete_ac_comp1 + local insert_end=$_ble_edit_ind + local insert=$_ble_complete_ac_insert + local suffix=$_ble_complete_ac_suffix + ble/complete/insert "$insert_beg" "$insert_end" "$insert" "$suffix" + blehook/invoke complete_insert + _ble_edit_mark_active= + _ble_complete_ac_insert= + _ble_complete_ac_suffix= + ble/complete/menu/clear + ble-edit/content/clear-arg + return 0 +} +function ble/widget/auto_complete/cancel-default { + ble/widget/auto_complete/cancel + ble/decode/widget/skip-lastwidget + ble/decode/widget/redispatch-by-keys "${KEYS[@]}" +} +function ble/widget/auto_complete/self-insert { + local code; ble/widget/self-insert/.get-code + ((code==0)) && return 0 + local ret + ble/util/c2s "$code"; local ins=$ret + local comps_cur=${_ble_edit_str:_ble_complete_ac_comp1:_ble_edit_ind-_ble_complete_ac_comp1} + local comps_new=$comps_cur$ins + local processed= + if [[ $_ble_complete_ac_type == [ch] ]]; then + if [[ $_ble_complete_ac_word == "$comps_new"* ]]; then + ((_ble_edit_ind+=${#ins})) + [[ ! $_ble_complete_ac_word ]] && ble/widget/auto_complete/cancel + processed=1 + fi + elif [[ $_ble_complete_ac_type == [rmaA] && $ins != [{,}] ]]; then + if local ret simple_flags simple_ibrace; ble/syntax:bash/simple-word/reconstruct-incomplete-word "$comps_new"; then + if ble/complete/source/eval-simple-word "$ret" single && local compv_new=$ret; then + local filter_type=head + case $_ble_complete_ac_type in + (*m*) filter_type=substr ;; + (*a*) filter_type=hsubseq ;; + (*A*) filter_type=subseq ;; + esac + local comps_fixed= + local comp_filter_type + local comp_filter_pattern + ble/complete/candidates/filter#init "$filter_type" "$compv_new" + if ble/complete/candidates/filter#test "$_ble_complete_ac_cand"; then + local insert; ble-edit/content/replace-limited "$_ble_edit_ind" "$_ble_edit_ind" "$ins" + ((_ble_edit_ind+=${#insert},_ble_edit_mark+=${#insert})) + [[ $_ble_complete_ac_cand == "$compv_new" ]] && + ble/widget/auto_complete/cancel + processed=1 + fi + fi + fi + fi + if [[ $processed ]]; then + local comp_text= insert_beg=0 insert_end=0 insert=$ins suffix= + blehook/invoke complete_insert + return 0 + else + ble/widget/auto_complete/cancel + ble/decode/widget/skip-lastwidget + ble/decode/widget/redispatch-by-keys "${KEYS[@]}" + fi +} +function ble/widget/auto_complete/insert-on-end { + if ((_ble_edit_mark==${#_ble_edit_str})); then + ble/widget/auto_complete/insert + else + ble/widget/auto_complete/cancel-default + fi +} +function ble/widget/auto_complete/insert-word { + local breaks=${bleopt_complete_auto_wordbreaks:-$_ble_term_IFS} + local rex='^['$breaks']*([^'$breaks']+['$breaks']*)?' + if [[ $_ble_complete_ac_type == [ch] ]]; then + local ins=${_ble_edit_str:_ble_edit_ind:_ble_edit_mark-_ble_edit_ind} + [[ $ins =~ $rex ]] + if [[ $BASH_REMATCH == "$ins" ]]; then + ble/widget/auto_complete/insert + return 0 + else + local ins=$BASH_REMATCH + ((_ble_edit_ind+=${#ins})) + local comp_text=$_ble_edit_str + local insert_beg=$_ble_complete_ac_comp1 + local insert_end=$_ble_edit_ind + local insert=${_ble_edit_str:insert_beg:insert_end-insert_beg}$ins + local suffix= + blehook/invoke complete_insert + return 0 + fi + elif [[ $_ble_complete_ac_type == [rmaA] ]]; then + local ins=$_ble_complete_ac_insert + [[ $ins =~ $rex ]] + if [[ $BASH_REMATCH == "$ins" ]]; then + ble/widget/auto_complete/insert + return 0 + else + local ins=$BASH_REMATCH + _ble_complete_ac_type=c + ble-edit/content/replace "$_ble_complete_ac_comp1" "$_ble_edit_mark" "$_ble_complete_ac_insert" + ((_ble_edit_ind=_ble_complete_ac_comp1+${#ins}, + _ble_edit_mark=_ble_complete_ac_comp1+${#_ble_complete_ac_insert})) + local comp_text=$_ble_edit_str + local insert_beg=$_ble_complete_ac_comp1 + local insert_end=$_ble_edit_ind + local insert=$ins + local suffix= + blehook/invoke complete_insert + return 0 + fi + fi + return 1 +} +function ble/widget/auto_complete/accept-line { + ble/widget/auto_complete/insert + ble-decode-key 13 +} +function ble/widget/auto_complete/notify-enter { + ble/decode/widget/skip-lastwidget +} +function ble-decode/keymap:auto_complete/define { + ble-bind -f __defchar__ auto_complete/self-insert + ble-bind -f __default__ auto_complete/cancel-default + ble-bind -f __line_limit__ nop + ble-bind -f 'C-g' auto_complete/cancel + ble-bind -f 'C-x C-g' auto_complete/cancel + ble-bind -f 'C-M-g' auto_complete/cancel + ble-bind -f S-RET auto_complete/insert + ble-bind -f S-C-m auto_complete/insert + ble-bind -f C-f auto_complete/insert-on-end + ble-bind -f right auto_complete/insert-on-end + ble-bind -f C-e auto_complete/insert-on-end + ble-bind -f end auto_complete/insert-on-end + ble-bind -f M-f auto_complete/insert-word + ble-bind -f M-right auto_complete/insert-word + ble-bind -f C-j auto_complete/accept-line + ble-bind -f C-RET auto_complete/accept-line + ble-bind -f auto_complete_enter auto_complete/notify-enter +} +function ble/complete/sabbrev/.initialize-print { + sgr0= sgr1= sgr2= sgr3= sgro= + if [[ $flags == *c* || $flags != *n* && -t 1 ]]; then + local ret + ble/color/face2sgr command_function; sgr1=$ret + ble/color/face2sgr syntax_varname; sgr2=$ret + ble/color/face2sgr syntax_quoted; sgr3=$ret + ble/color/face2sgr argument_option; sgro=$ret + sgr0=$_ble_term_sgr0 + fi +} +function ble/complete/sabbrev/.print-definition { + local key=$1 type=${2%%:*} value=${2#*:} + local option= + [[ $type == m ]] && option=$sgro'-m'$sgr0' ' + local ret + ble/string#quote-word "$key" quote-empty:sgrq="$sgr3":sgr0="$sgr2" + key=$sgr2$ret$sgr0 + ble/string#quote-word "$value" sgrq="$sgr3":sgr0="$sgr0" + value=$ret + ble/util/print "${sgr1}ble-sabbrev$sgr0 $option$key=$value" +} +function ble/complete/sabbrev/register { + local key=$1 value=$2 + ble/gdict#set _ble_complete_sabbrev "$key" "$value" +} +function ble/complete/sabbrev/list { + local keys ret; keys=("$@") + if ((${#keys[@]}==0)); then + ble/gdict#keys _ble_complete_sabbrev + keys=("${ret[@]}") + ((${#keys[@]})) || return 0 + fi + local sgr0 sgr1 sgr2 sgr3 sgro + ble/complete/sabbrev/.initialize-print + local key ext=0 + for key in "${keys[@]}"; do + if ble/gdict#get _ble_complete_sabbrev "$key"; then + ble/complete/sabbrev/.print-definition "$key" "$ret" + else + ble/util/print "ble-sabbrev: $key: not found." >&2 + ext=1 + fi + done + return "$ext" +} +function ble/complete/sabbrev/reset { + if (($#)); then + local key + for key; do + ble/gdict#unset _ble_complete_sabbrev "$key" + done + else + ble/gdict#clear _ble_complete_sabbrev + fi + return 0 +} +function ble/complete/sabbrev/get { + local key=$1 + ble/gdict#get _ble_complete_sabbrev "$key" +} +function ble/complete/sabbrev/get-keys { + local ret + ble/gdict#keys _ble_complete_sabbrev + keys=("${ret[@]}") +} +function ble/complete/sabbrev/read-arguments { + while (($#)); do + local arg=$1; shift + if [[ $arg == ?*=* ]]; then + ble/array#push specs "s:$arg" + else + case $arg in + (--help) + flags=H$flags ;; + (--reset) + flags=r$flags + (--color|--color=always) + flags=c${flags//[cn]} ;; + (--color=never) + flags=n${flags//[cn]} ;; + (--color=auto) + flags=${flags//[cn]} ;; + (-*) + local i n=${#arg} c + for ((i=1;i&2 + flags=E$flags + elif [[ $1 != ?*=* ]]; then + ble/util/print "ble-sabbrev: invalid option argument '-$c $1' (expected form: '-$c key=value')" >&2 + flags=E$flags + else + ble/array#push specs "$c:$1"; shift + fi ;; + (r) + flags=r$flags ;; + (*) + ble/util/print "ble-sabbrev: unknown option '-$c'." >&2 + flags=E$flags ;; + esac + done ;; + (*) + ble/array#push print "$arg" ;; + esac + fi + done +} +function ble-sabbrev { + local -a specs=() print=() + local flags= + ble/complete/sabbrev/read-arguments "$@" + if [[ $flags == *H* || $flags == *E* ]]; then + [[ $flags == *E* ]] && ble/util/print + ble/util/print-lines \ + 'usage: ble-sabbrev [KEY=VALUE|-m KEY=FUNCTION]...' \ + 'usage: ble-sabbrev [-r|--reset] [KEY...]' \ + 'usage: ble-sabbrev --help' \ + ' Register sabbrev expansion.' + [[ ! $flags == *E* ]]; return "$?" + fi + local ext=0 + if ((${#specs[@]}==0||${#print[@]})); then + if [[ $flags == *r* ]]; then + ble/complete/sabbrev/reset "${print[@]}" + else + ble/complete/sabbrev/list "${print[@]}" + fi || ext=$? + fi + local spec key type value + for spec in "${specs[@]}"; do + type=${spec::1} spec=${spec:2} + key=${spec%%=*} value=${spec#*=} + ble/complete/sabbrev/register "$key" "$type:$value" + done + return "$ext" +} +function ble/complete/sabbrev/expand { + local sources comp_index=$_ble_edit_ind comp_text=$_ble_edit_str + ble/complete/context:syntax/generate-sources + local src asrc pos=$comp_index + for src in "${sources[@]}"; do + ble/string#split-words asrc "$src" + case ${asrc[0]} in + (file|command|argument|variable:w|wordlist:*|sabbrev) + ((asrc[1]=0)) || ble/history/get-count -v start + else + local start index pos; builtin eval -- "$fib_suspend" + fib_suspend= + fi + local dabbrev_match= + local dabbrev_pos=$pos + local dabbrev_current_match=${_ble_edit_str:_ble_edit_mark:_ble_edit_ind-_ble_edit_mark} + local line; ble/history/get-edited-entry -v line "$index" + if ! ble/complete/dabbrev/search-in-history-entry "$line" "$index"; then + ((index--,dabbrev_pos=0)) + local isearch_time=0 + local isearch_opts=stop_check:cyclic + isearch_opts=$isearch_opts:condition + local dabbrev_original=$_ble_complete_dabbrev_original + local dabbrev_regex1=$_ble_complete_dabbrev_regex1 + local needle='[[ $LINE =~ $dabbrev_regex1 ]] && ble/complete/dabbrev/search-in-history-entry "$LINE" "$INDEX"' + [[ $dabbrev_original ]] && needle='[[ $LINE == *"$dabbrev_original"* ]] && '$needle + isearch_opts=$isearch_opts:progress + local isearch_progress_callback=ble/complete/dabbrev/.show-status.fib + ble/history/isearch-backward-blockwise "$isearch_opts"; local ext=$? + ((ext==148)) && fib_suspend="start=$start index=$index pos=$pos" + if ((ext)); then + if ((${#_ble_complete_dabbrev_stack[@]})); then + ble/widget/.bell # 周回したので鳴らす + return 0 + else + return "$ext" + fi + fi + fi + local rec=$_ble_complete_dabbrev_index,$_ble_complete_dabbrev_pos,$_ble_edit_ind,$_ble_edit_mark + ble/array#push _ble_complete_dabbrev_stack "$rec:$_ble_edit_str" + local insert; ble-edit/content/replace-limited "$_ble_edit_mark" "$_ble_edit_ind" "$dabbrev_match" + ((_ble_edit_ind=_ble_edit_mark+${#insert})) + ((index>_ble_complete_dabbrev_index)) && + ble/widget/.bell # 周回 + _ble_complete_dabbrev_index=$index + _ble_complete_dabbrev_pos=$dabbrev_match_pos + ble/textarea#redraw +} +function ble/complete/dabbrev/next.fib { + ble/complete/dabbrev/.search.fib; local ext=$? + if ((ext==0)); then + _ble_edit_mark_active=insert + ble/complete/dabbrev/.show-status.fib + elif ((ext==148)); then + ble/complete/dabbrev/.show-status.fib + else + ble/widget/.bell + ble/widget/dabbrev/exit + ble/complete/dabbrev/reset + fib_kill=1 + fi + return "$ext" +} +function ble/widget/dabbrev-expand { + ble/complete/dabbrev/initialize-variables + ble/decode/keymap/push dabbrev + ble/util/fiberchain#initialize ble/complete/dabbrev + ble/util/fiberchain#push next + ble/util/fiberchain#resume +} +function ble/widget/dabbrev/next { + ble/util/fiberchain#push next + ble/util/fiberchain#resume +} +function ble/widget/dabbrev/prev { + if ((${#_ble_util_fiberchain[@]})); then + local ret; ble/array#pop _ble_util_fiberchain + if ((${#_ble_util_fiberchain[@]})); then + ble/util/fiberchain#resume + else + ble/complete/dabbrev/show-status + fi + elif ((${#_ble_complete_dabbrev_stack[@]})); then + local ret; ble/array#pop _ble_complete_dabbrev_stack + local rec str=${ret#*:} + ble/string#split rec , "${ret%%:*}" + ble-edit/content/reset-and-check-dirty "$str" + _ble_edit_ind=${rec[2]} + _ble_edit_mark=${rec[3]} + _ble_complete_dabbrev_index=${rec[0]} + _ble_complete_dabbrev_pos=${rec[1]} + ble/complete/dabbrev/show-status + else + ble/widget/.bell + return 1 + fi +} +function ble/widget/dabbrev/cancel { + if ((${#_ble_util_fiberchain[@]})); then + ble/util/fiberchain#clear + ble/complete/dabbrev/show-status + else + ble/widget/dabbrev/exit + ble/complete/dabbrev/reset + fi +} +function ble/widget/dabbrev/exit { + ble/decode/keymap/pop + _ble_edit_mark_active= + ble/complete/dabbrev/erase-status +} +function ble/widget/dabbrev/exit-default { + ble/widget/dabbrev/exit + ble/decode/widget/skip-lastwidget + ble/decode/widget/redispatch-by-keys "${KEYS[@]}" +} +function ble/widget/dabbrev/accept-line { + ble/widget/dabbrev/exit + ble-decode-key 13 +} +function ble-decode/keymap:dabbrev/define { + ble-bind -f __default__ 'dabbrev/exit-default' + ble-bind -f __line_limit__ nop + ble-bind -f 'C-g' 'dabbrev/cancel' + ble-bind -f 'C-x C-g' 'dabbrev/cancel' + ble-bind -f 'C-M-g' 'dabbrev/cancel' + ble-bind -f C-r 'dabbrev/next' + ble-bind -f C-s 'dabbrev/prev' + ble-bind -f RET 'dabbrev/exit' + ble-bind -f C-m 'dabbrev/exit' + ble-bind -f C-RET 'dabbrev/accept-line' + ble-bind -f C-j 'dabbrev/accept-line' +} +function ble/complete/action:cdpath/initialize { + DATA=$cdpath_basedir + ble/complete/action:file/initialize +} +function ble/complete/action:cdpath/complete { + CAND=$DATA$CAND ble/complete/action:file/complete +} +function ble/complete/action:cdpath/init-menu-item { + ble/color/face2g cmdinfo_cd_cdpath; g=$ret + if [[ :$comp_type: == *:vstat:* ]]; then + if [[ -h $CAND ]]; then + suffix='@' + elif [[ -d $CAND ]]; then + suffix='/' + fi + fi +} +function ble/complete/action:cdpath/get-desc { + local sgr0=$_ble_term_sgr0 sgr1= sgr2= + local g ret g1 g2 + ble/syntax/highlight/getg-from-filename "$DATA$CAND"; g1=$g + [[ $g1 ]] || { ble/color/face2g filename_warning; g1=$ret; } + ((g2=g1^_ble_color_gflags_Revert)) + ble/color/g2sgr "$g1"; sgr1=$ret + ble/color/g2sgr "$g2"; sgr2=$ret + ble/string#escape-for-display "$DATA$CAND" sgr1="$sgr2":sgr0="$sgr1" + local filename=$sgr1$ret$sgr0 + CAND=$DATA$CAND ble/complete/action:file/get-desc + desc="CDPATH $filename ($desc)" +} +function ble/cmdinfo/complete:cd/.impl { + local type=$1 + [[ $comps_flags == *v* ]] || return 1 + if [[ $COMPV == -* ]]; then + local action=word + case $type in + (pushd) + if [[ $COMPV == - || $COMPV == -n ]]; then + local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize "$action" + ble/complete/cand/yield "$action" -n + fi ;; + (*) + COMP_PREFIX=$COMPV + local -a list=() + local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 safe + ble/complete/cand/yield.initialize "$action" + [[ $COMPV == -* ]] && ble/complete/cand/yield "$action" "${COMPV}" + [[ $COMPV != *L* ]] && ble/complete/cand/yield "$action" "${COMPV}L" + [[ $COMPV != *P* ]] && ble/complete/cand/yield "$action" "${COMPV}P" + ((_ble_bash>=40200)) && [[ $COMPV != *e* ]] && ble/complete/cand/yield "$action" "${COMPV}e" + ((_ble_bash>=40300)) && [[ $COMPV != *@* ]] && ble/complete/cand/yield "$action" "${COMPV}@" ;; + esac + return 0 + fi + [[ :$comp_type: != *:[maA]:* && $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} + [[ :$comp_type: == *:[maA]:* && ! $COMPV ]] && return 1 + if [[ ! $CDPATH ]]; then + ble/complete/source:dir + return "$?" + fi + ble/complete/source:tilde; local ext=$? + ((ext==148||ext==0)) && return "$ext" + local is_pwd_visited= is_cdpath_generated= + "${_ble_util_set_declare[@]//NAME/visited}" # WA #D1570 checked + local name names; ble/string#split names : "$CDPATH" + for name in "${names[@]}"; do + [[ $name ]] || continue + name=${name%/}/ + local action=cdpath + [[ ${name%/} == . || ${name%/} == "${PWD%/}" ]] && + is_pwd_visited=1 action=file + local -a candidates=() + local ret cand + ble/complete/source:file/.construct-pathname-pattern "$COMPV" + ble/complete/util/eval-pathname-expansion "$name$ret"; (($?==148)) && return 148 + ble/complete/source/test-limit ${#ret[@]} || return 1 + for cand in "${ret[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && + ble/complete/check-cancel && return 148 + [[ $cand && -d $cand ]] || continue + [[ $cand == / ]] || cand=${cand%/} + cand=${cand#"$name"} + ble/set#contains visited "$cand" && continue + ble/set#add visited "$cand" + ble/array#push candidates "$cand" + done + ((${#candidates[@]})) || continue + local flag_source_filter=1 + local cdpath_basedir=$name + ble/complete/cand/yield-filenames "$action" "${candidates[@]}" + [[ $action == cdpath ]] && is_cdpath_generated=1 + done + [[ $is_cdpath_generated ]] && + bleopt complete_menu_style=desc + if [[ ! $is_pwd_visited ]]; then + local -a candidates=() + local ret cand + ble/complete/source:file/.construct-pathname-pattern "$COMPV" + ble/complete/util/eval-pathname-expansion "${ret%/}/"; (($?==148)) && return 148 + ble/complete/source/test-limit ${#ret[@]} || return 1 + for cand in "${ret[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && + ble/complete/check-cancel && return 148 + [[ -d $cand ]] || continue + [[ $cand == / ]] || cand=${cand%/} + ble/set#contains visited "$cand" && continue + ble/array#push candidates "$cand" + done + local flag_source_filter=1 + ble/complete/cand/yield-filenames file "${candidates[@]}" + fi +} +function ble/cmdinfo/complete:cd { + ble/cmdinfo/complete:cd/.impl cd +} +function ble/cmdinfo/complete:pushd { + ble/cmdinfo/complete:cd/.impl pushd +} +blehook/invoke complete_load +blehook complete_load= +return 0 -- cgit v1.2.3