core-complete.sh (328516B)
1 #!/bin/bash 2 3 ble/util/import "$_ble_base/lib/core-syntax.sh" 4 5 ## @fn ble/complete/string#search-longest-suffix-in needle haystack 6 ## @var[out] ret 7 function ble/complete/string#search-longest-suffix-in { 8 local needle=$1 haystack=$2 9 local l=0 u=${#needle} 10 while ((l<u)); do 11 local m=$(((l+u)/2)) 12 if [[ $haystack == *"${needle:m}"* ]]; then 13 u=$m 14 else 15 l=$((m+1)) 16 fi 17 done 18 ret=${needle:l} 19 } 20 ## @fn ble/complete/string#common-suffix-prefix lhs rhs 21 ## @var[out] ret 22 function ble/complete/string#common-suffix-prefix { 23 local lhs=$1 rhs=$2 24 if ((${#lhs}<${#rhs})); then 25 local i n=${#lhs} 26 for ((i=0;i<n;i++)); do 27 ret=${lhs:i} 28 [[ $rhs == "$ret"* ]] && return 0 29 done 30 ret= 31 else 32 local j m=${#rhs} 33 for ((j=m;j>0;j--)); do 34 ret=${rhs::j} 35 [[ $lhs == *"$ret" ]] && return 0 36 done 37 ret= 38 fi 39 } 40 41 ## @fn ble/complete/string#match-patterns str patterns... 42 ## 指定した文字列が patterns 集合の何れかのパターンに一致するか検査します。 43 ## @param[in] str 44 ## @param[in] patterns 45 ## @exit 46 function ble/complete/string#match-patterns { 47 local s=$1 found= pattern; shift 48 for pattern; do 49 if [[ $s == $pattern ]]; then 50 return 0 51 fi 52 done 53 return 1 54 } 55 56 ## @fn ble/complete/get-wordbreaks 57 ## @var[out] wordbreaks 58 function ble/complete/get-wordbreaks { 59 wordbreaks=$_ble_term_IFS$COMP_WORDBREAKS 60 [[ $wordbreaks == *'('* ]] && wordbreaks=${wordbreaks//['()']}'()' 61 [[ $wordbreaks == *']'* ]] && wordbreaks=']'${wordbreaks//']'} 62 [[ $wordbreaks == *'-'* ]] && wordbreaks=${wordbreaks//'-'}'-' 63 } 64 65 # 66 #============================================================================== 67 # 選択インターフェイス (ble/complete/menu) 68 69 ## @arr _ble_complete_menu_icons 70 ## 71 ## 各要素は以下の形式の文字列である。 72 ## 73 ## x0,y0,x1,y1,${#pack},${#esc1}[,bbox]:$pack$esc1 74 ## 75 ## * x0,y0 と x1,y1 は menu 項目の描画開始点と終了点。 76 ## * esc1 は実際に出力する描画シーケンス。 77 ## * bbox は "x y cols lines" の形式をしていて、描画シーケンスを生成する際に 78 ## 使った bbox の情報を格納する。これは特に truncate が起こった時に、選択状 79 ## 態の描画を同じ条件で実行する時に参照する。 80 81 _ble_complete_menu_items=() 82 _ble_complete_menu_class= 83 _ble_complete_menu_param= 84 _ble_complete_menu_version= 85 _ble_complete_menu_page_style= 86 _ble_complete_menu_ipage= 87 _ble_complete_menu_offset= 88 _ble_complete_menu_icons=() 89 _ble_complete_menu_info_data=() 90 _ble_complete_menu_selected=-1 91 92 function ble/complete/menu#check-cancel { 93 ((menu_iloop++%menu_interval==0)) && 94 [[ :$comp_type: != *:sync:* ]] && 95 ble/decode/has-input 96 } 97 98 ## @fn ble/complete/menu-style:$menu_style/construct-page 99 ## 候補一覧メニューの表示・配置を計算します。 100 ## 101 ## @var[out] x y esc 102 ## @var[in] menu_style 103 ## @arr[in] menu_items 104 ## @var[in] menu_class menu_param 105 ## @var[in] cols lines 106 ## 107 ## @fn ble/complete/menu-style:$menu_style/guess 108 ## scroll 番目の候補がどのページにいるかを予測します。 109 ## 可能性のある最初のページ番号 ipage を返します。 110 ## 111 ## @var[in] scroll 112 ## @var[out] ipage begin end 113 ## @var[in] cols lines 114 ## 115 116 _ble_complete_menu_style_measure=() 117 _ble_complete_menu_style_icons=() 118 _ble_complete_menu_style_pages=() 119 120 # 121 # ble/complete/menu-style:align 122 # 123 124 ## @fn ble/complete/menu#render-item item opts 125 ## @var[in] cols lines 126 ## Note: "$menu_class"/render-item の中で用いる。 127 ## @var[out] x y ret 128 function ble/complete/menu#render-item { 129 # use custom renderer 130 if ble/is-function "$menu_class"/render-item; then 131 "$menu_class"/render-item "$@" 132 return "$?" 133 fi 134 135 local item=$1 opts=$2 136 #g=0 lc=0 lg=0 LINES=$lines COLUMNS=$cols ble/canvas/trace "$item" truncate:ellipsis 137 138 local sgr0=$_ble_term_sgr0 sgr1=$_ble_term_rev 139 [[ :$opts: == *:selected:* ]] && local sgr0=$sgr1 sgr1=$sgr0 140 ble/canvas/trace-text "$item" nonewline:external-sgr 141 ret=$sgr0$ret$_ble_term_sgr0 142 } 143 144 ## @fn ble/complete/menu#get-prefix-width format column_width 145 ## @param[in] format 146 ## @param[in] column_width 147 ## @var[out] prefix_width 148 ## @var[out] prefix_format 149 function ble/complete/menu#get-prefix-width { 150 prefix_width=0 151 prefix_format=${1:-$bleopt_menu_prefix} 152 if [[ $prefix_format ]]; then 153 local prefix1 column_width=$2 154 ble/util/sprintf prefix1 "$prefix_format" "${#menu_items[@]}" 155 local x1 y1 x2 y2 156 LINES=1 COLUMNS=$column_width x=0 y=0 ble/canvas/trace "$prefix1" truncate:measure-bbox 157 if ((x2<=column_width/2)); then 158 prefix_width=$x2 159 ble/string#reserve-prototype "$prefix_width" 160 fi 161 fi 162 } 163 164 ## @fn ble/complete/menu#render-prefix index 165 ## @param[in] index 166 ## @param[in,opt] column_width 167 ## @var[in] prefix_width 168 ## @var[in] prefix_format 169 ## @var[out] prefix_esc 170 function ble/complete/menu#render-prefix { 171 prefix_esc= 172 local index=$1 173 if ((prefix_width)); then 174 local prefix1; ble/util/sprintf prefix1 "$prefix_format" "$((index+1))" 175 local x=0 y=0 176 LINES=1 COLUMNS=$prefix_width ble/canvas/trace "$prefix1" truncate:relative:measure-bbox 177 prefix_esc=$ret$_ble_term_sgr0 178 if ((x<prefix_width)); then 179 prefix_esc=${_ble_string_prototype::prefix_width-x}$prefix_esc 180 fi 181 fi 182 } 183 184 185 ## @fn ble/complete/menu-style:align/construct/.measure-candidates-in-page 186 ## その頁に入り切る範囲で候補の幅を計測する 187 ## @var[in] begin 188 ## その頁の一番最初に表示する候補を指定します。 189 ## @var[out] end 190 ## その頁に表示する候補の範囲の終端を返します。 191 ## 実際には描画の際に全角文字などの文字送りによって 192 ## ここまで表示できるとは限りません。 193 ## @var[out] wcell 194 ## その頁を描画する時のセル幅を返します。 195 ## @arr[in,out] _ble_complete_menu_style_measure 196 ## 計測結果をキャッシュしておく配列です。 197 ## 198 ## @var[in] lines cols menu_iloop 199 function ble/complete/menu-style:align/construct/.measure-candidates-in-page { 200 local max_wcell=$bleopt_menu_align_max; ((max_wcell>cols&&(max_wcell=cols))) 201 ((wcell=bleopt_menu_align_min,wcell<2&&(wcell=2))) 202 local ncell=0 index=$begin 203 local item ret esc1 w 204 for item in "${menu_items[@]:begin}"; do 205 ble/complete/menu#check-cancel && return 148 206 local wcell_old=$wcell 207 208 # 候補の表示幅 w を計算 209 local w=${_ble_complete_menu_style_measure[index]%%:*} 210 if [[ ! $w ]]; then 211 local prefix_esc 212 ble/complete/menu#render-prefix "$index" 213 local x=$prefix_width y=0 214 ble/complete/menu#render-item "$item"; esc1=$ret 215 local w=$((y*cols+x)) 216 _ble_complete_menu_style_measure[index]=$w:${#item},${#esc1}:$item$esc1$prefix_esc 217 fi 218 219 # wcell, ncell 更新 220 local wcell_request=$((w++,w<=max_wcell?w:max_wcell)) 221 ((wcell<wcell_request)) && wcell=$wcell_request 222 223 # 新しい ncell 224 local line_ncell=$((cols/wcell)) 225 local cand_ncell=$(((w+wcell-1)/wcell)) 226 if [[ $menu_style == align-nowrap ]]; then 227 # Note: nowrap が起こるのはすでに wcell == max_wcell の時なので、 228 # 改行処理が終わった後に wcell が変化するという事はない。 229 local x1=$((ncell%line_ncell*wcell)) 230 local ncell_eol=$(((ncell/line_ncell+1)*line_ncell)) 231 if ((x1>0&&x1+w>=cols)); then 232 # 行送り 233 ((ncell=ncell_eol+cand_ncell)) 234 elif ((x1+w<cols)); then 235 # 余白に収まる場合 236 ((ncell+=cand_ncell)) 237 ((ncell>ncell_eol&&(ncell=ncell_eol))) 238 else 239 ((ncell+=cand_ncell)) 240 fi 241 else 242 ((ncell+=cand_ncell)) 243 fi 244 245 local max_ncell=$((line_ncell*lines)) 246 ((index&&ncell>max_ncell)) && { wcell=$wcell_old; break; } 247 ((index++)) 248 done 249 end=$index 250 } 251 252 ## @fn ble/complete/menu-style:align/construct-page 253 ## @var[in,out] begin end x y esc 254 ## @arr[out] _ble_complete_menu_style_icons 255 ## 256 ## @var[in,out] cols lines menu_iloop 257 function ble/complete/menu-style:align/construct-page { 258 x=0 y=0 esc= 259 260 local prefix_width prefix_format 261 ble/complete/menu#get-prefix-width "$bleopt_menu_align_prefix" "$bleopt_menu_align_max" 262 263 local wcell=2 264 ble/complete/menu-style:align/construct/.measure-candidates-in-page 265 (($?==148)) && return 148 266 267 local ncell=$((cols/wcell)) 268 local index=$begin entry 269 for entry in "${_ble_complete_menu_style_measure[@]:begin:end-begin}"; do 270 ble/complete/menu#check-cancel && return 148 271 272 local w=${entry%%:*}; entry=${entry#*:} 273 local s=${entry%%:*}; entry=${entry#*:} 274 local len; ble/string#split len , "$s" 275 local item=${entry::len[0]} esc1=${entry:len[0]:len[1]} prefix_esc=${entry:len[0]+len[1]} 276 277 local x0=$x y0=$y 278 if ((x==0||x+w<cols)); then 279 ((x+=w%cols,y+=w/cols)) 280 ((y>=lines&&(x=x0,y=y0,1))) && break 281 else 282 if [[ $menu_style == align-nowrap ]]; then 283 ((y+1>=lines)) && break 284 esc=$esc$'\n' 285 ((x0=x=0,y0=++y)) 286 ((x=w%cols,y+=w/cols)) 287 ((y>=lines&&(x=x0,y=y0,1))) && break 288 else 289 ((x+=prefix_width)) 290 ble/complete/menu#render-item "$item" || 291 ((begin==index)) || # [Note: 少なくとも1個ははみ出ても表示する] 292 { x=$x0 y=$y0; break; }; esc1=$ret 293 fi 294 fi 295 296 _ble_complete_menu_style_icons[index]=$((x0+prefix_width)),$y0,$x,$y,${#item},${#esc1}:$item$esc1 297 esc=$esc$prefix_esc$esc1 298 299 # 候補と候補の間の空白 300 if ((++index<end)); then 301 local icell=$((x==0?0:(x+wcell)/wcell)) 302 if ((icell<ncell)); then 303 # 次の升目 304 local pad=$((icell*wcell-x)) 305 ble/string#reserve-prototype "$pad" 306 esc=$esc${_ble_string_prototype::pad} 307 ((x=icell*wcell)) 308 else 309 # 次の行 310 ((y+1>=lines)) && break 311 esc=$esc$'\n' 312 ((x=0,++y)) 313 fi 314 fi 315 done 316 end=$index 317 } 318 function ble/complete/menu-style:align-nowrap/construct-page { 319 ble/complete/menu-style:align/construct-page "$@" 320 } 321 322 # 323 # ble/complete/menu-style:dense 324 # 325 326 ## @fn ble/complete/menu-style:dense/construct-page 327 ## @var[in,out] begin end x y esc 328 ## @var[in,out] cols lines menu_iloop 329 function ble/complete/menu-style:dense/construct-page { 330 331 local prefix_width prefix_format 332 ble/complete/menu#get-prefix-width "$bleopt_menu_dense_prefix" "$cols" 333 334 x=0 y=0 esc= 335 local item index=$begin N=${#menu_items[@]} 336 for item in "${menu_items[@]:begin}"; do 337 ble/complete/menu#check-cancel && return 148 338 339 local x0=$x y0=$y 340 341 local prefix_esc esc1 342 ble/complete/menu#render-prefix "$index" 343 ((x+=prefix_width,x>cols&&(y+=x/cols,x%=cols))) 344 ble/complete/menu#render-item "$item" || 345 ((index==begin)) || 346 { x=$x0 y=$y0; break; }; esc1=$ret 347 348 if [[ $menu_style == dense-nowrap ]]; then 349 if ((y>y0&&x>0||y>y0+1)); then 350 ((++y0>=lines)) && break 351 esc=$esc$'\n' 352 ((y=y0,x0=0,x=prefix_width)) 353 ble/complete/menu#render-item "$item" || 354 ((begin==index)) || 355 { x=$x0 y=$y0; break; }; esc1=$ret 356 fi 357 fi 358 359 local x1=$((x0+prefix_width)) y1=$y0 360 ((x1>=cols)) && ((y1+=x1/cols,x1%=cols)) 361 _ble_complete_menu_style_icons[index]=$x1,$y1,$x,$y,${#item},${#esc1}:$item$esc1 362 esc=$esc$prefix_esc$esc1 363 364 # 候補と候補の間の空白 365 if ((++index<N)); then 366 if [[ $menu_style == dense-nowrap ]] && ((x==0)); then 367 : skip 368 elif ((x+1<cols)); then 369 esc=$esc' ' 370 ((x++)) 371 else 372 ((y+1>=lines)) && break 373 esc=$esc$'\n' 374 ((x=0,++y)) 375 fi 376 fi 377 done 378 end=$index 379 } 380 ## @fn ble/complete/menu-style:dense/construct opts 381 ## complete_menu_style=align{,-nowrap} に対して候補を配置します。 382 function ble/complete/menu-style:dense-nowrap/construct-page { 383 ble/complete/menu-style:dense/construct-page "$@" 384 } 385 386 # 387 # ble/complete/menu-style:linewise 388 # 389 390 ## @fn ble/complete/menu-style:linewise/construct-page opts 391 ## @var[in,out] begin end x y esc 392 function ble/complete/menu-style:linewise/construct-page { 393 local opts=$1 ret 394 local max_icon_width=$((cols-1)) 395 396 local prefix_width prefix_format 397 ble/complete/menu#get-prefix-width "$bleopt_menu_linewise_prefix" "$max_icon_width" 398 399 local item x0 y0 esc1 index=$begin 400 end=$begin x=0 y=0 esc= 401 for item in "${menu_items[@]:begin:lines}"; do 402 ble/complete/menu#check-cancel && return 148 403 404 local prefix_esc= 405 ble/complete/menu#render-prefix "$index" "$max_icon_width" 406 esc=$esc$prefix_esc 407 ((x=prefix_width)) 408 409 ((x0=x,y0=y)) 410 local lines1=1 cols1=$max_icon_width 411 lines=$lines1 cols=$cols1 y=0 ble/complete/menu#render-item "$item"; esc1=$ret 412 _ble_complete_menu_style_icons[index]=$x0,$y0,$x,$y,${#item},${#esc1},"$x0 0 $cols1 $lines1":$item$esc1 413 ((index++)) 414 esc=$esc$esc1 415 416 ((y+1>=lines)) && break 417 ((x=0,++y)) 418 esc=$esc$'\n' 419 done 420 end=$index 421 } 422 function ble/complete/menu-style:linewise/guess { 423 ((ipage=scroll/lines, 424 begin=ipage*lines, 425 end=begin)) 426 } 427 428 # 429 # ble/complete/menu-style:desc 430 # 431 432 _ble_complete_menu_desc_pageheight=() 433 434 ## @fn ble/complete/menu-style:desc/construct-page opts 435 ## @var[in,out] begin end x y esc 436 ## @var[in] ipage 437 function ble/complete/menu-style:desc/construct-page { 438 local opts=$1 ret 439 local opt_raw=; [[ $menu_style != desc-text ]] && opt_raw=1 440 441 # 失敗時・エラー時の既定値 442 end=$begin esc= x=0 y=0 443 444 local colsep=' | ' 445 local desc_sgr0=$'\e[m' 446 ble/color/face2sgr-ansi syntax_quoted; local desc_sgrq=$ret 447 ble/color/face2sgr-ansi syntax_delimiter; local desc_sgrt=$ret 448 449 local ncolumn=1 nline=$lines 450 local nrest_item=$((${#menu_items[@]}-begin)) 451 if [[ $bleopt_menu_desc_multicolumn_width ]]; then 452 ncolumn=$((cols/bleopt_menu_desc_multicolumn_width)) 453 if ((ncolumn<1)); then 454 ncolumn=1 455 elif ((ncolumn>nrest_item)); then 456 ncolumn=$nrest_item 457 fi 458 fi 459 ((nline=(${#menu_items[@]}-begin+ncolumn-1)/ncolumn, 460 nline>lines&&(nline=lines))) 461 local ncolumn_max=$(((nrest_item+nline-1)/nline)) 462 ((ncolumn>ncolumn_max&&(ncolumn=ncolumn_max))) 463 464 # Note #D1727: 相対移動の時は、右端に接すると端末による振る舞いの違 465 # いが問題になるので、右端に接しない様に col-1 にする。一部の端末 466 # については右端に接しても相対移動が壊れないと分かっているので、 467 # white list で右端に接する事を許可する。 468 local available_width=$cols 469 case $_ble_term_TERM in 470 (screen:*|tmux:*|kitty:*|contra:*) ;; 471 (*) ((available_width--)) ;; 472 esac 473 474 local wcolumn=$(((available_width-${#colsep}*(ncolumn-1))/ncolumn)) 475 476 local prefix_width prefix_format 477 ble/complete/menu#get-prefix-width "$bleopt_menu_desc_prefix" "$wcolumn" 478 ((wcolumn>=prefix_width+15)) || prefix_width=0 479 480 local wcand_limit=$(((wcolumn-prefix_width+1)*2/3)) 481 ((wcand_limit<10&&(wcand_limit=wcolumn-prefix_width))) 482 483 local -a DRAW_BUFF=() 484 local index=$begin icolumn ymax=0 485 for ((icolumn=0;icolumn<ncolumn;icolumn++)); do 486 487 # 各候補を描画して幅を計算する 488 local measure; measure=() 489 local pack w esc1 max_width=0 490 for pack in "${menu_items[@]:index:nline}"; do 491 ble/complete/menu#check-cancel && return 148 492 493 x=0 y=0 494 lines=1 cols=$wcand_limit ble/complete/menu#render-item "$pack"; esc1=$ret 495 ((w=y*wcand_limit+x,w>max_width&&(max_width=w))) 496 497 ble/array#push measure "$w:${#pack}:$pack$esc1" 498 done 499 500 local cand_width=$max_width 501 local desc_x=$((prefix_width+cand_width+1)); ((desc_x>wcolumn&&(desc_x=wcolumn))) 502 local desc_prefix=; ((wcolumn-prefix_width-desc_x>30)) && desc_prefix=': ' 503 504 local xcolumn=$((icolumn*(wcolumn+${#colsep}))) 505 506 x=0 y=0 507 local entry w s pack esc1 x0 y0 pad 508 for entry in "${measure[@]}"; do 509 ble/complete/menu#check-cancel && return 148 510 511 w=${entry%%:*} entry=${entry#*:} 512 s=${entry%%:*} entry=${entry#*:} 513 pack=${entry::s} esc1=${entry:s} 514 515 local prefix_esc 516 ble/complete/menu#render-prefix "$index" 517 ble/canvas/put.draw "$prefix_esc" 518 ((x+=prefix_width)) 519 520 # 候補表示 521 ((x0=x,y0=y,x+=w)) 522 _ble_complete_menu_style_icons[index]=$((xcolumn+x0)),$y0,$((xcolumn+x)),$y,${#pack},${#esc1},"0 0 $wcand_limit 1":$pack$esc1 523 ((index++)) 524 ble/canvas/put.draw "$esc1" 525 526 # 余白 527 ble/canvas/put-spaces.draw "$((pad=desc_x-x))" 528 ble/canvas/put.draw "$desc_prefix" 529 ((x+=pad+${#desc_prefix})) 530 531 # 説明表示 532 local desc=$desc_sgrt'(no description)'$desc_sgr0 533 ble/function#try "$menu_class"/get-desc "$pack" 534 if [[ $opt_raw ]]; then 535 y=0 g=0 lc=0 lg=0 LINES=1 COLUMNS=$wcolumn ble/canvas/trace.draw "$desc" truncate:relative:ellipsis 536 else 537 y=0 lines=1 cols=$wcolumn ble/canvas/trace-text "$desc" nonewline 538 ble/canvas/put.draw "$ret" 539 fi 540 ble/canvas/put.draw "$_ble_term_sgr0" 541 ((y+1>=nline)) && break 542 ble/canvas/put-move.draw "$((-x))" 1 543 ((x=0,++y)) 544 done 545 ((y>ymax)) && ymax=$y 546 547 if ((icolumn+1<ncolumn)); then 548 # カラム仕切りを出力 (最後に次のカラムの先頭に移動) 549 ble/canvas/put-move.draw "$((wcolumn-x))" "$((-y))" 550 for ((y=0;y<=ymax;y++)); do 551 ble/canvas/put.draw "$colsep" 552 if ((y<ymax)); then 553 ble/canvas/put-move.draw -${#colsep} 1 554 else 555 ble/canvas/put-move-y.draw "$((-y))" 556 fi 557 done 558 else 559 ((y<ymax)) && ble/canvas/put-move-y.draw "$((ymax-y))" 560 ((x+=xcolumn,y=ymax)) 561 fi 562 done 563 564 _ble_complete_menu_desc_pageheight[ipage]=$nline 565 end=$index 566 ble/canvas/sflush.draw -v esc 567 } 568 function ble/complete/menu-style:desc/guess { 569 local ncolumn=1 570 if [[ $bleopt_menu_desc_multicolumn_width ]]; then 571 ncolumn=$((cols/bleopt_menu_desc_multicolumn_width)) 572 ((ncolumn<1)) && ncolumn=1 573 fi 574 local nitem_per_page=$((ncolumn*lines)) 575 ((ipage=scroll/nitem_per_page, 576 begin=ipage*nitem_per_page, 577 end=begin)) 578 } 579 function ble/complete/menu-style:desc/locate { 580 local type=$1 osel=$2 581 local ipage=$_ble_complete_menu_ipage 582 local nline=${_ble_complete_menu_desc_pageheight[ipage]:-1} 583 584 case $type in 585 (right) ((ret=osel+nline)) ;; 586 (left) ((ret=osel-nline)) ;; 587 (down) ((ret=osel+1)) ;; 588 (up) ((ret=osel-1)) ;; 589 (*) return 1 ;; 590 esac 591 592 local beg=$_ble_complete_menu_offset 593 local end=$((beg+${#_ble_complete_menu_icons[@]})) 594 if ((ret<beg)); then 595 ((ret=beg-1)) 596 elif ((ret>end)); then 597 ((ret=end)) 598 fi 599 return 0 600 } 601 602 function ble/complete/menu-style:desc-text/construct-page { ble/complete/menu-style:desc/construct-page "$@"; } 603 function ble/complete/menu-style:desc-text/guess { ble/complete/menu-style:desc/guess; } 604 function ble/complete/menu-style:desc-text/locate { ble/complete/menu-style:desc/locate "$@"; } 605 606 # Obsolete menu_style (now synonym to "desc") 607 function ble/complete/menu-style:desc-raw/construct-page { ble/complete/menu-style:desc/construct-page "$@"; } 608 function ble/complete/menu-style:desc-raw/guess { ble/complete/menu-style:desc/guess; } 609 function ble/complete/menu-style:desc-raw/locate { ble/complete/menu-style:desc/locate "$@"; } 610 611 ## @fn ble/complete/menu#construct/.initialize-size 612 ## @var[out] cols lines 613 function ble/complete/menu#construct/.initialize-size { 614 ble/edit/info/.initialize-size 615 local maxlines=$((bleopt_complete_menu_maxlines)) 616 ((maxlines>0&&lines>maxlines)) && lines=$maxlines 617 } 618 ## @fn ble/complete/menu#construct menu_opts 619 ## 実装分離の adapter 部分 620 ## 621 ## @var[in] menu_style 622 ## 623 ## @arr[in] menu_items 624 ## 項目のリストを指定します。 625 ## 626 ## @var[in] menu_class menu_param 627 ## 以下に掲げる様々な callback を呼び出す為の変数です。 628 ## 629 ## @fn[in,opt] $menu_class/render-item item opts 630 ## 各項目に対応する描画内容を決定する renderer 関数を指定します。 631 ## @param[in] item 632 ## 描画される項目を指定します。 633 ## @param[in] opts 634 ## selected 635 ## 選択されている項目の描画を行う事を示します。 636 ## @var[in] lines cols 637 ## 描画範囲の行数と列数を指定します。 638 ## @var[in,out] x y 639 ## 描画開始位置を指定します。終了位置を返します。 640 ## @var[out] ret 641 ## 描画に用いるシーケンスを返します。 642 ## 643 ## @fn[in,opt] $menu_class/onselect nsel osel 644 ## 項目が選択された時に呼び出される callback を指定します。 645 ## @param[in] nsel osel 646 ## 647 ## @fn[in,opt] $menu_class/get-desc item 648 ## 項目の説明を取得します。 649 ## @param[out] desc 650 ## 651 ## @fn[in,opt] $menu_class/onaccept nsel [item] 652 ## @fn[in,opt] $menu_class/oncancel nsel 653 ## 654 function ble/complete/menu#construct { 655 local menu_opts=$1 656 local menu_iloop=0 657 local menu_interval=$bleopt_complete_polling_cycle 658 659 local cols lines 660 ble/complete/menu#construct/.initialize-size 661 local nitem=${#menu_items[@]} 662 local version=$nitem:$lines:$cols 663 664 # 項目がない時の特別表示 665 if ((nitem==0)); then 666 _ble_complete_menu_version=$version 667 _ble_complete_menu_items=() 668 _ble_complete_menu_page_style= 669 _ble_complete_menu_ipage=0 670 _ble_complete_menu_offset=0 671 _ble_complete_menu_icons=() 672 _ble_complete_menu_info_data=(ansi $'\e[38;5;242m(no items)\e[m') 673 _ble_complete_menu_selected=-1 674 return 0 675 fi 676 677 # 表示したい項目の指定 678 local scroll=0 rex=':scroll=([0-9]+):' use_cache= 679 if [[ :$menu_opts: =~ $rex ]]; then 680 scroll=${BASH_REMATCH[1]} 681 ((nitem&&(scroll%=nitem))) 682 [[ $_ble_complete_menu_version == $version ]] && use_cache=1 683 fi 684 if [[ ! $use_cache ]]; then 685 _ble_complete_menu_style_measure=() 686 _ble_complete_menu_style_icons=() 687 _ble_complete_menu_style_pages=() 688 fi 689 690 local begin=0 end=0 ipage=0 x y esc 691 ble/function#try ble/complete/menu-style:"$menu_style"/guess 692 while ((end<nitem)); do 693 ((scroll<begin)) && return 1 694 local page_data=${_ble_complete_menu_style_pages[ipage]} 695 if [[ $page_data ]]; then 696 # キャッシュがある時はキャッシュから読み取り 697 local fields; ble/string#split fields , "${page_data%%:*}" 698 begin=${fields[0]} end=${fields[1]} 699 if ((begin<=scroll&&scroll<end)); then 700 x=${fields[2]} y=${fields[3]} esc=${page_data#*:} 701 break 702 fi 703 else 704 # キャッシュがない時は頁を構築 705 ble/complete/menu-style:"$menu_style"/construct-page "$menu_opts" || return "$?" 706 _ble_complete_menu_style_pages[ipage]=$begin,$end,$x,$y:$esc 707 ((begin<=scroll&&scroll<end)) && break 708 fi 709 begin=$end 710 ((ipage++)) 711 done 712 713 _ble_complete_menu_version=$version 714 _ble_complete_menu_items=("${menu_items[@]}") 715 _ble_complete_menu_class=$menu_class 716 _ble_complete_menu_param=$menu_param 717 _ble_complete_menu_page_style=$menu_style 718 _ble_complete_menu_ipage=$ipage 719 _ble_complete_menu_offset=$begin 720 _ble_complete_menu_icons=("${_ble_complete_menu_style_icons[@]:begin:end-begin}") 721 _ble_complete_menu_info_data=(store "$x" "$y" "$esc") 722 _ble_complete_menu_selected=-1 723 return 0 724 } 725 726 function ble/complete/menu#show { 727 ble/edit/info/immediate-show "${_ble_complete_menu_info_data[@]}" 728 } 729 function ble/complete/menu#clear { 730 ble/edit/info/default 731 } 732 733 734 ## @fn ble/complete/menu#select index [opts] 735 ## @param[in] opts 736 ## goto-page-top 737 ## 指定した項目を含む頁に移動した後に、 738 ## その頁の一番上の項目に移動する事を指定します。 739 function ble/complete/menu#select { 740 local menu_class=$_ble_complete_menu_class 741 local menu_param=$_ble_complete_menu_param 742 local osel=$_ble_complete_menu_selected nsel=$1 opts=$2 743 local ncand=${#_ble_complete_menu_items[@]} 744 ((0<=osel&&osel<ncand)) || osel=-1 745 ((0<=nsel&&nsel<ncand)) || nsel=-1 746 ((osel==nsel)) && return 0 747 748 local infox infoy 749 ble/canvas/panel#get-origin "$_ble_edit_info_panel" --prefix=info 750 751 # ページ更新 752 local visible_beg=$_ble_complete_menu_offset 753 local visible_end=$((visible_beg+${#_ble_complete_menu_icons[@]})) 754 if ((nsel>=0&&!(visible_beg<=nsel&&nsel<visible_end))); then 755 ble/complete/menu/show filter:load-filtered-data:scroll="$nsel"; local ext=$? 756 ((ext)) && return "$ext" 757 758 if [[ $_ble_complete_menu_ipage ]]; then 759 local ipage=$_ble_complete_menu_ipage 760 ble/term/visible-bell "menu: Page $((ipage+1))" persistent 761 else 762 ble/term/visible-bell "menu: Offset $_ble_complete_menu_offset/$ncand" persistent 763 fi 764 765 visible_beg=$_ble_complete_menu_offset 766 visible_end=$((visible_beg+${#_ble_complete_menu_icons[@]})) 767 768 # スクロールに対応していない menu_style や、スクロールしすぎた時の為。 769 ((visible_end<=nsel&&(nsel=visible_end-1))) 770 ((nsel<=visible_beg&&(nsel=visible_beg))) 771 ((visible_beg<=osel&&osel<visible_end)) || osel=-1 772 fi 773 774 local -a DRAW_BUFF=() 775 local ret; ble/canvas/panel/save-position; local pos0=$ret 776 if ((osel>=0)); then 777 # 消去 778 local entry=${_ble_complete_menu_icons[osel-visible_beg]} 779 local fields text=${entry#*:} 780 ble/string#split fields , "${entry%%:*}" 781 782 if ((fields[3]<_ble_canvas_panel_height[_ble_edit_info_panel])); then 783 # Note: 編集文字列の内容の変化により info panel が削れている事がある。 784 # 現在の項目がちゃんと info panel の中にある時にだけ描画する。(#D0880) 785 786 ble/canvas/panel#goto.draw "$_ble_edit_info_panel" "${fields[@]::2}" 787 ble/canvas/put.draw "${text:fields[4]}" 788 _ble_canvas_x=${fields[2]} _ble_canvas_y=$((infoy+fields[3])) 789 fi 790 fi 791 792 local value= 793 if ((nsel>=0)); then 794 [[ :$opts: == *:goto-page-top:* ]] && nsel=$visible_beg 795 local entry=${_ble_complete_menu_icons[nsel-visible_beg]} 796 local fields text=${entry#*:} 797 ble/string#split fields , "${entry%%:*}" 798 799 local x=${fields[0]} y=${fields[1]} 800 local item=${text::fields[4]} 801 802 # construct reverted candidate 803 local ret 804 if [[ ${fields[6]} ]]; then 805 local box cols lines 806 ble/string#split-words box "${fields[6]}" 807 x=${box[0]} y=${box[1]} cols=${box[2]} lines=${box[3]} 808 ble/complete/menu#render-item "$item" selected 809 ((x+=fields[0]-box[0])) 810 ((y+=fields[1]-box[1])) 811 else 812 local cols lines 813 ble/complete/menu#construct/.initialize-size 814 ble/complete/menu#render-item "$item" selected 815 fi 816 817 if ((y<_ble_canvas_panel_height[_ble_edit_info_panel])); then 818 # Note: 編集文字列の内容の変化により info panel が削れている事がある。 819 # 現在の項目がちゃんと info panel の中にある時にだけ描画する。(#D0880) 820 821 ble/canvas/panel#goto.draw "$_ble_edit_info_panel" "${fields[@]::2}" 822 ble/canvas/put.draw "$ret" 823 _ble_canvas_x=$x _ble_canvas_y=$((infoy+y)) 824 fi 825 826 _ble_complete_menu_selected=$nsel 827 else 828 _ble_complete_menu_selected=-1 829 value=$_ble_complete_menu_original 830 fi 831 ble/canvas/panel/load-position.draw "$pos0" 832 ble/canvas/bflush.draw 833 834 ble/function#try "$menu_class"/onselect "$nsel" "$osel" 835 return 0 836 } 837 838 # widgets 839 840 function ble/widget/menu/forward { 841 local opts=$1 842 local nsel=$((_ble_complete_menu_selected+1)) 843 local ncand=${#_ble_complete_menu_items[@]} 844 if ((nsel>=ncand)); then 845 if [[ :$opts: == *:cyclic:* ]] && ((ncand>=2)); then 846 nsel=0 847 else 848 ble/widget/.bell "menu: no more candidates" 849 return 1 850 fi 851 fi 852 ble/complete/menu#select "$nsel" 853 } 854 function ble/widget/menu/backward { 855 local opts=$1 856 local nsel=$((_ble_complete_menu_selected-1)) 857 if ((nsel<0)); then 858 local ncand=${#_ble_complete_menu_items[@]} 859 if [[ :$opts: == *:cyclic:* ]] && ((ncand>=2)); then 860 ((nsel=ncand-1)) 861 else 862 ble/widget/.bell "menu: no more candidates" 863 return 1 864 fi 865 fi 866 ble/complete/menu#select "$nsel" 867 } 868 869 function ble/widget/menu/forward-column { 870 local osel=$((_ble_complete_menu_selected)) 871 if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate right "$osel"; then 872 local nsel=$ret ncand=${#_ble_complete_menu_items[@]} 873 if ((0<=nsel&&nsel<ncand&&nsel!=osel)); then 874 ble/complete/menu#select "$nsel" 875 else 876 ble/widget/.bell "menu: no more candidates" 877 fi 878 else 879 ble/widget/menu/forward 880 fi 881 } 882 function ble/widget/menu/backward-column { 883 local osel=$((_ble_complete_menu_selected)) 884 if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate left "$osel"; then 885 local nsel=$ret ncand=${#_ble_complete_menu_items[@]} 886 if ((0<=nsel&&nsel<ncand&&nsel!=osel)); then 887 ble/complete/menu#select "$nsel" 888 else 889 ble/widget/.bell "menu: no more candidates" 890 fi 891 else 892 ble/widget/menu/backward 893 fi 894 } 895 896 _ble_complete_menu_lastcolumn= 897 ## @fn ble/widget/menu/.check-last-column 898 ## @var[in,out] ox 899 function ble/widget/menu/.check-last-column { 900 if [[ $_ble_complete_menu_lastcolumn ]]; then 901 local lastwidget=${LASTWIDGET%%' '*} 902 if [[ $lastwidget == ble/widget/menu/forward-line || 903 $lastwidget == ble/widget/menu/backward-line ]] 904 then 905 ox=$_ble_complete_menu_lastcolumn 906 return 0 907 fi 908 fi 909 _ble_complete_menu_lastcolumn=$ox 910 } 911 ## @fn ble/widget/menu/.goto-column column 912 ## 現在行の中で指定した列に対応する要素に移動する。 913 ## @param[in] column 914 function ble/widget/menu/.goto-column { 915 local column=$1 916 local offset=$_ble_complete_menu_offset 917 local osel=$_ble_complete_menu_selected 918 ((osel>=0)) || return 1 919 local entry=${_ble_complete_menu_icons[osel-offset]} 920 local fields; ble/string#split fields , "${entry%%:*}" 921 local ox=${fields[0]} oy=${fields[1]} 922 local nsel=-1 923 if ((ox<column)); then 924 # forward search within the line 925 nsel=$osel 926 for entry in "${_ble_complete_menu_icons[@]:osel+1-offset}"; do 927 ble/string#split fields , "${entry%%:*}" 928 local x=${fields[0]} y=${fields[1]} 929 ((y==oy&&x<=column)) || break 930 ((nsel++)) 931 done 932 elif ((ox>column)); then 933 # backward search within the line 934 local i=$osel 935 while ((--i>=offset)); do 936 entry=${_ble_complete_menu_icons[i-offset]} 937 ble/string#split fields , "${entry%%:*}" 938 local x=${fields[0]} y=${fields[1]} 939 ((y<oy||x<=column&&(nsel=i,1))) && break 940 done 941 fi 942 ((nsel>=0&&nsel!=osel)) && 943 ble/complete/menu#select "$nsel" 944 } 945 function ble/widget/menu/forward-line { 946 local offset=$_ble_complete_menu_offset 947 local osel=$_ble_complete_menu_selected 948 ((osel>=0)) || return 1 949 950 local nsel=-1 goto_column= 951 if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate down "$osel"; then 952 nsel=$ret 953 else 954 local entry=${_ble_complete_menu_icons[osel-offset]} 955 local fields; ble/string#split fields , "${entry%%:*}" 956 local ox=${fields[0]} oy=${fields[1]} 957 ble/widget/menu/.check-last-column 958 local i=$osel nsel=-1 is_next_page= 959 for entry in "${_ble_complete_menu_icons[@]:osel+1-offset}"; do 960 ble/string#split fields , "${entry%%:*}" 961 local x=${fields[0]} y=${fields[1]} 962 ((y<=oy||y==oy+1&&x<=ox||nsel<0)) || break 963 ((++i,y>oy&&(nsel=i))) 964 done 965 ((nsel<0&&(is_next_page=1,nsel=offset+${#_ble_complete_menu_icons[@]}))) 966 ((is_next_page)) && goto_column=$ox 967 fi 968 969 local ncand=${#_ble_complete_menu_items[@]} 970 if ((0<=nsel&&nsel<ncand)); then 971 ble/complete/menu#select "$nsel" 972 [[ $goto_column ]] && ble/widget/menu/.goto-column "$goto_column" 973 return 0 974 else 975 ble/widget/.bell 'menu: no more candidates' 976 return 1 977 fi 978 } 979 function ble/widget/menu/backward-line { 980 local offset=$_ble_complete_menu_offset 981 local osel=$_ble_complete_menu_selected 982 ((osel>=0)) || return 1 983 984 local nsel=-1 goto_column= 985 if local ret; ble/function#try ble/complete/menu-style:"$_ble_complete_menu_page_style"/locate up "$osel"; then 986 nsel=$ret 987 else 988 local entry=${_ble_complete_menu_icons[osel-offset]} 989 local fields; ble/string#split fields , "${entry%%:*}" 990 local ox=${fields[0]} oy=${fields[1]} 991 ble/widget/menu/.check-last-column 992 local nsel=$osel 993 while ((--nsel>=offset)); do 994 entry=${_ble_complete_menu_icons[nsel-offset]} 995 ble/string#split fields , "${entry%%:*}" 996 local x=${fields[0]} y=${fields[1]} 997 ((y<oy-1||y==oy-1&&x<=ox)) && break 998 done 999 ((0<=nsel&&nsel<offset)) && goto_column=$ox 1000 fi 1001 1002 local ncand=${#_ble_complete_menu_items[@]} 1003 if ((0<=nsel&&nsel<ncand)); then 1004 ble/complete/menu#select "$nsel" 1005 [[ $goto_column ]] && ble/widget/menu/.goto-column "$goto_column" 1006 else 1007 ble/widget/.bell 'menu: no more candidates' 1008 return 1 1009 fi 1010 } 1011 function ble/widget/menu/backward-page { 1012 if ((_ble_complete_menu_offset>0)); then 1013 ble/complete/menu#select "$((_ble_complete_menu_offset-1))" goto-page-top 1014 else 1015 ble/widget/.bell "menu: this is the first page." 1016 return 1 1017 fi 1018 } 1019 function ble/widget/menu/forward-page { 1020 local next=$((_ble_complete_menu_offset+${#_ble_complete_menu_icons[@]})) 1021 if ((next<${#_ble_complete_menu_items[@]})); then 1022 ble/complete/menu#select "$next" 1023 else 1024 ble/widget/.bell "menu: this is the last page." 1025 return 1 1026 fi 1027 } 1028 function ble/widget/menu/beginning-of-page { 1029 ble/complete/menu#select "$_ble_complete_menu_offset" 1030 } 1031 function ble/widget/menu/end-of-page { 1032 local nicon=${#_ble_complete_menu_icons[@]} 1033 ((nicon)) && ble/complete/menu#select "$((_ble_complete_menu_offset+nicon-1))" 1034 } 1035 1036 function ble/widget/menu/cancel { 1037 ble/decode/keymap/pop 1038 ble/complete/menu#clear 1039 "$_ble_complete_menu_class"/oncancel 1040 } 1041 function ble/widget/menu/accept { 1042 ble/decode/keymap/pop 1043 ble/complete/menu#clear 1044 local nsel=$_ble_complete_menu_selected 1045 local hook=$_ble_complete_menu_accept_hook 1046 _ble_complete_menu_accept_hook= 1047 if ((nsel>=0)); then 1048 "$_ble_complete_menu_class"/onaccept "$nsel" "${_ble_complete_menu_items[nsel]}" 1049 else 1050 "$_ble_complete_menu_class"/onaccept "$nsel" 1051 fi 1052 } 1053 1054 function ble-decode/keymap:menu/define { 1055 # ble-bind -f __defchar__ menu_complete/self-insert 1056 # ble-bind -f __default__ 'menu_complete/exit-default' 1057 ble-bind -f __default__ 'bell' 1058 ble-bind -f __line_limit__ nop 1059 ble-bind -f C-m 'menu/accept' 1060 ble-bind -f RET 'menu/accept' 1061 ble-bind -f C-g 'menu/cancel' 1062 ble-bind -f 'C-x C-g' 'menu/cancel' 1063 ble-bind -f 'C-M-g' 'menu/cancel' 1064 ble-bind -f C-f 'menu/forward-column' 1065 ble-bind -f right 'menu/forward-column' 1066 ble-bind -f C-i 'menu/forward cyclic' 1067 ble-bind -f TAB 'menu/forward cyclic' 1068 ble-bind -f C-b 'menu/backward-column' 1069 ble-bind -f left 'menu/backward-column' 1070 ble-bind -f C-S-i 'menu/backward cyclic' 1071 ble-bind -f S-TAB 'menu/backward cyclic' 1072 ble-bind -f C-n 'menu/forward-line' 1073 ble-bind -f down 'menu/forward-line' 1074 ble-bind -f C-p 'menu/backward-line' 1075 ble-bind -f up 'menu/backward-line' 1076 ble-bind -f prior 'menu/backward-page' 1077 ble-bind -f next 'menu/forward-page' 1078 ble-bind -f home 'menu/beginning-of-page' 1079 ble-bind -f end 'menu/end-of-page' 1080 } 1081 1082 # sample implementation 1083 function ble/complete/menu.class/onaccept { 1084 local hook=$_ble_complete_menu_accept_hook 1085 _ble_complete_menu_accept_hook= 1086 "$hook" "$@" 1087 } 1088 function ble/complete/menu.class/oncancel { 1089 local hook=$_ble_complete_menu_cancel_hook 1090 _ble_complete_menu_cancel_hook= 1091 "$hook" "$@" 1092 } 1093 function ble/complete/menu#start { 1094 _ble_complete_menu_accept_hook=$1; shift 1095 _ble_complete_menu_cancel_hook= 1096 1097 local menu_style=linewise 1098 local menu_items; menu_items=("$@") 1099 local menu_class=ble/complete/menu.class menu_param= 1100 ble/complete/menu#construct sync || return "$?" 1101 ble/complete/menu#show 1102 ble/complete/menu#select 0 1103 ble/decode/keymap/push menu 1104 return 147 1105 } 1106 1107 # 1108 #============================================================================== 1109 # 候補源 (context, source, action) 1110 1111 ## ble/complete 内で共通で使われるローカル変数 1112 ## 1113 ## @var COMP1 COMP2 COMPS COMPV 1114 ## COMP1-COMP2 は補完対象の範囲を指定します。 1115 ## COMPS は COMP1-COMP2 にある文字列を表し、 1116 ## COMPV は COMPS の評価値 (クォート除去、簡単なパラメータ展開をした値) を表します。 1117 ## COMPS に複雑な構造が含まれていて即時評価ができない場合は 1118 ## COMPV は unset になります。必要な場合は [[ $comps_flags == *v* ]] で判定して下さい。 1119 ## ※ [[ -v COMPV ]] は bash-4.2 以降です。 1120 ## 1121 ## @var comp_type 1122 ## 候補生成の方法を制御します。 1123 ## 以下のオプションのコロン区切りの組み合わせからなる文字列です。 1124 ## 1125 ## a 曖昧補完に用いる候補を生成する。 1126 ## 曖昧一致するかどうかは呼び出し元で判定されるので、 1127 ## 曖昧一致する可能性のある候補をできるだけ多く生成すれば良い。 1128 ## m 曖昧補完 (中間部分に一致) 1129 ## A 曖昧補完 (部分列・最初の文字も一致しなくて良い) 1130 ## 1131 ## i (rlvar completion-ignore-case) 1132 ## 大文字小文字を区別しない補完候補生成を行う。 1133 ## vstat (rlvar visible-stats) 1134 ## ファイル名末尾にファイルの種類を示す記号を付加する。 1135 ## markdir (rlvar mark-directories) 1136 ## ディレクトリ名の補完後に / を付加する。 1137 ## 1138 ## sync 1139 ## ユーザの入力があっても中断しない事を表す。 1140 ## raw 1141 ## COMPV としてシェル評価前の文字列を使用します。 1142 ## 1143 1144 function ble/complete/check-cancel { 1145 [[ :$comp_type: != *:sync:* ]] && ble/decode/has-input 1146 } 1147 1148 #------------------------------------------------------------------------------ 1149 # action 1150 1151 ## 既存の action 1152 ## 1153 ## ble/complete/action:plain 1154 ## ble/complete/action:word 1155 ## ble/complete/action:file 1156 ## ble/complete/action:progcomp 1157 ## ble/complete/action:command 1158 ## ble/complete/action:variable 1159 ## 1160 ## action の実装 1161 ## 1162 ## @fn ble/complete/action:$ACTION/initialize 1163 ## 基本的に INSERT を設定すれば良い 1164 ## @var[in ] CAND 1165 ## @var[in,out] ACTION 1166 ## @var[in,out] DATA 1167 ## @var[in,out] INSERT 1168 ## COMP1-COMP2 を置き換える文字列を指定します 1169 ## 1170 ## @var[in] COMP1 COMP2 COMPS COMPV comp_type 1171 ## 1172 ## @var[in ] COMP_PREFIX 1173 ## 1174 ## @var[in ] comps_flags 1175 ## 以下のフラグ文字からなる文字列です。 1176 ## 1177 ## p パラメータ展開の直後に於ける補完である事を表します。 1178 ## 直後に識別子を構成する文字を追記する時に対処が必要です。 1179 ## 1180 ## v COMPV が利用可能である事を表します。 1181 ## f failglob で COMPV 評価が失敗した事を表します。 1182 ## 1183 ## S クォート '' の中にいる事を表します。 1184 ## E クォート $'' の中にいる事を表します。 1185 ## D クォート "" の中にいる事を表します。 1186 ## I クォート $"" の中にいる事を表します。 1187 ## B クォート \ の直後にいる事を表します。 1188 ## x ブレース展開の中にいる事を表します。 1189 ## 1190 ## Note: shopt -s nocaseglob のため、フラグ文字は 1191 ## 大文字・小文字でも重複しないように定義する必要がある。 1192 ## 1193 ## @var[in ] comps_fixed 1194 ## 補完対象がブレース展開を含む場合に ibrace:value の形式になります。 1195 ## それ以外の場合は空文字列です。 1196 ## ibrace はブレース展開の構造を保持するのに必要な COMPS 接頭辞の長さです。 1197 ## value は ${COMPS::ibrace} のブレース展開を実行した結果の最後の単語の評価結果です。 1198 ## 1199 ## @fn ble/complete/action:$ACTION/complete 1200 ## 一意確定時に、挿入文字列・範囲に対する加工を行います。 1201 ## 例えばディレクトリ名の場合に / を後に付け加える等です。 1202 ## 1203 ## @var[in] CAND 1204 ## @var[in] ACTION 1205 ## @var[in] DATA 1206 ## @var[in] COMP1 COMP2 COMPS COMPV comp_type comps_flags 1207 ## 1208 ## @var[in,out] insert suffix 1209 ## 補完によって挿入される文字列を指定します。 1210 ## 加工後の挿入する文字列を返します。 1211 ## 1212 ## @var[in] insert_beg insert_end 1213 ## 補完によって置換される範囲を指定します。 1214 ## 1215 ## @var[in,out] insert_flags 1216 ## 以下のフラグ文字の組み合わせの文字列です。 1217 ## 1218 ## r [in] 既存の部分を保持したまま補完が実行される事を表します。 1219 ## それ以外の時、既存の入力部分も含めて置換されます。 1220 ## m [out] 候補一覧 (menu) の表示を要求する事を表します。 1221 ## n [out] 再度補完を試み (確定せずに) 候補一覧を表示する事を要求します。 1222 ## 1223 1224 function ble/complete/string#escape-for-completion-context { 1225 local str=$1 escape_flags=$2 1226 case $comps_flags in 1227 (*S*) ble/string#escape-for-bash-single-quote "$str" ;; 1228 (*E*) ble/string#escape-for-bash-escape-string "$str" ;; 1229 (*[DI]*) ble/string#escape-for-bash-double-quote "$str" ;; 1230 (*) 1231 if [[ $comps_fixed ]]; then 1232 ble/string#escape-for-bash-specialchars "$str" "b$escape_flags" 1233 else 1234 ble/string#escape-for-bash-specialchars "$str" "$escape_flags" 1235 fi ;; 1236 esac 1237 } 1238 1239 function ble/complete/action/complete.addtail { 1240 suffix=$suffix$1 1241 } 1242 function ble/complete/action/complete.mark-directory { 1243 [[ :$comp_type: == *:markdir:* && $CAND != */ ]] && 1244 [[ ! -h $CAND || ( $insert == "$COMPS" || :$comp_type: == *:marksymdir:* ) ]] && 1245 ble/complete/action/complete.addtail / 1246 } 1247 function ble/complete/action/complete.close-quotation { 1248 case $comps_flags in 1249 (*[SE]*) ble/complete/action/complete.addtail \' ;; 1250 (*[DI]*) ble/complete/action/complete.addtail \" ;; 1251 esac 1252 } 1253 1254 ## @fn ble/complete/action/quote-insert.initialize action 1255 ## @var[out] ${_ble_complete_quote_insert_varnames[@]} 1256 ## 1257 ## @fn ble/complete/action/quote-insert action 1258 ## @var[ref] INSERT 1259 ## @var[in] ${_ble_complete_quote_insert_varnames[@]} 1260 ## 1261 ## Note: quote-insert を呼び出す前に予め quote-insert.initialize を呼び出して 1262 ## quote_... 変数を初期化しておく必要があります。 1263 ## 1264 ## Example: 1265 ## 1266 ## local "${_ble_complete_quote_insert_varnames[@]/%/=}" # WA #D1570 checked 1267 ## ble/complete/action/quote-insert.initialize "$action" 1268 ## for INSERT; do 1269 ## ble/complete/action/quote-insert "$action" 1270 ## : do something with INSERT 1271 ## done 1272 ## 1273 1274 _ble_complete_quote_insert_varnames=( 1275 quote_action 1276 quote_escape_flags 1277 quote_cont_cutbackslash 1278 quote_paramx_comps 1279 quote_trav_prefix 1280 quote_fixed_comps 1281 quote_fixed_compv 1282 quote_fixed_comps_len 1283 quote_fixed_compv_len) 1284 1285 function ble/complete/action/quote-insert.initialize { 1286 quote_action=$1 1287 1288 quote_escape_flags=c 1289 if [[ $quote_action == command ]]; then 1290 quote_escape_flags= 1291 elif [[ $quote_action == progcomp ]]; then 1292 # #D1362 Bash は "compopt -o filenames" が指定されている時、 1293 # '~' で始まる補完候補と同名のファイルがある時にのみチルダをクォートする。 1294 # [[ $CAND == '~'* && ! ( $comp_opts == *:filenames:* && -e $CAND ) ]] && 1295 # quote_escape_flags=T$quote_escape_flags 1296 # #D1434 = 及び : は filenames がついていない限りは quote しない事にする。 1297 # bash-complete が unquoted =, : を生成する可能性があるので。 1298 [[ $comp_opts != *:filenames:* ]] && 1299 quote_escape_flags=${quote_escape_flags//c} 1300 fi 1301 [[ $comps_fixed ]] && quote_escape_flags=b$quote_escape_flags 1302 1303 # 孤立 backslash が前置している時は二重クォートを防ぐ為に削除 1304 quote_cont_cutbackslash= 1305 [[ $comps_flags == *B* && $COMPS == *'\' ]] && 1306 quote_cont_cutbackslash=1 1307 1308 # 直前にパラメータ展開があればエスケープ 1309 quote_paramx_comps=$COMPS 1310 if [[ $comps_flags == *p* ]]; then 1311 # Note: 安全策 (本来 comps_flags に p がある時点で '\' では終わらない筈) 1312 [[ $comps_flags == *B* && $quote_paramx_comps == *'\' ]] && 1313 quote_paramx_comps=${quote_paramx_comps%'\'} 1314 1315 case $comps_flags in 1316 (*[DI]*) 1317 if [[ $COMPS =~ $rex_raw_paramx ]]; then 1318 local rematch1=${BASH_REMATCH[1]} 1319 quote_paramx_comps=$rematch1'${'${COMPS:${#rematch1}+1}'}' 1320 else 1321 # Note: 安全策 (本来上で一致する筈) 1322 quote_paramx_comps=$quote_paramx_comps'""' 1323 fi ;; 1324 (*) 1325 quote_paramx_comps=$quote_paramx_comps'\' ;; 1326 esac 1327 fi 1328 1329 # 遡って書き換えた時に文脈を復元 1330 quote_trav_prefix= 1331 case $comps_flags in 1332 (*S*) quote_trav_prefix=\' ;; 1333 (*E*) quote_trav_prefix=\$\' ;; 1334 (*D*) quote_trav_prefix=\" ;; 1335 (*I*) quote_trav_prefix=\$\" ;; 1336 esac 1337 1338 # 遡って書き換える時に comps_fixed には注意する。 1339 quote_fixed_comps=('') 1340 quote_fixed_compv=('') 1341 quote_fixed_comps_len=('') 1342 quote_fixed_compv_len=('') 1343 if [[ $comps_fixed ]]; then 1344 quote_fixed_compv=${comps_fixed#*:} 1345 quote_fixed_compv_len=${#quote_fixed_compv} 1346 quote_fixed_comps_len=${comps_fixed%%:*} 1347 quote_fixed_comps=${COMPS::quote_fixed_comps_len} 1348 fi 1349 1350 # 遡って書き換える時に '/' 区切りでできるだけ元の展開を保持する。 1351 # comps_fixed[1] 以降に '/' 区切りで展開した結果を短い順に格納する。 1352 local i v 1353 for ((i=1;i<${#comps_fixed[@]};i++)); do 1354 v=${comps_fixed[i]#*:} 1355 quote_fixed_compv[i]=$v 1356 quote_fixed_compv_len[i]=${#v} 1357 quote_fixed_comps_len[i]=${comps_fixed[i]%%:*} 1358 quote_fixed_comps[i]=${COMPS::quote_fixed_comps_len[i]} 1359 done 1360 } 1361 1362 ## @fn ble/complete/action/quote-insert 1363 # Note: この関数の処理は ble/complete/action/quote-insert.batch/awk と一貫して 1364 # いる必要がある。この関数を変更する時には quote-insert.batch/awk にも等価の変 1365 # 更を適用する必要がある。 1366 function ble/complete/action/quote-insert { 1367 if [[ ! $quote_action ]]; then 1368 local "${_ble_complete_quote_insert_varnames[@]/%/=}" # WA #D1570 checked 1369 ble/complete/action/quote-insert.initialize "${1:-plain}" 1370 fi 1371 1372 local escape_flags=$quote_escape_flags 1373 if [[ $quote_action == command ]]; then 1374 # Note (#D1715,#D1978): "*:noquote:*" の判定について。action=command 1375 # DATA=:noquote: は alias 生成のみで使われる。そして alias 生成は 1376 # yield.batch を使わずに直接 yield を呼び出して行われる。なので :noquote: 1377 # の判定は awk batch の側では行わなくて良い。 1378 [[ $DATA == *:noquote:* || $COMPS == "$COMPV" && ( $CAND == '[[' || $CAND == '!' ) ]] && return 0 1379 elif [[ $quote_action == progcomp ]]; then 1380 [[ $comp_opts == *:noquote:* ]] && return 0 1381 [[ $comp_opts == *:ble/syntax-raw:* && $comp_opts != *:filenames:* ]] && return 0 1382 1383 # bash-completion には compopt -o nospace として、 1384 # 自分でスペースを付加する補完関数がある。この時クォートすると問題。 1385 [[ $comp_opts == *:nospace:* && $CAND == *' ' && ! -f $CAND ]] && return 0 1386 1387 # #D1362 Bash は "compopt -o filenames" が指定されていてかつ 1388 # '~' で始まる補完候補と同名のファイルがある時にのみチルダをクォートする。 1389 [[ $CAND == '~'* && ! ( $comp_opts == *:filenames:* && -e $CAND ) ]] && 1390 escape_flags=T$escape_flags 1391 fi 1392 1393 # 入力済み文字列への追記の場合、元の単語を保持する。 1394 if [[ $comps_flags == *v* && $CAND == "$COMPV"* ]]; then 1395 local ins ret 1396 ble/complete/string#escape-for-completion-context "${CAND:${#COMPV}}" "$escape_flags"; ins=$ret 1397 if [[ $comps_flags == *p* && $ins == [_a-zA-Z0-9]* ]]; then 1398 INSERT=$quote_paramx_comps$ins 1399 else 1400 [[ $quote_cont_cutbackslash ]] && ins=${ins#'\'} 1401 INSERT=$COMPS$ins; 1402 fi 1403 return 0 1404 fi 1405 1406 # 遡って書き換わる場合には単語内のできるだけ長い部分パスを保持する。 1407 local i=${#quote_fixed_comps[@]} 1408 while ((--i>=0)); do 1409 if [[ ${quote_fixed_comps[i]} && $CAND == "${quote_fixed_compv[i]}"* ]]; then 1410 local ret; ble/complete/string#escape-for-completion-context "${CAND:quote_fixed_compv_len[i]}" "$escape_flags" 1411 INSERT=${quote_fixed_comps[i]}$quote_trav_prefix$ret 1412 return 0 1413 fi 1414 done 1415 1416 # 既存の物に一致しない場合、完全に書き換える。 1417 local ret; ble/complete/string#escape-for-completion-context "$CAND" "$escape_flags" 1418 INSERT=$quote_trav_prefix$ret 1419 } 1420 1421 function ble/complete/action/quote-insert.batch/awk { 1422 local q=\' 1423 local -x comp_opts=$comp_opts 1424 local -x comps=$COMPS 1425 local -x compv=$COMPV 1426 local -x comps_flags=$comps_flags 1427 local -x quote_action=$quote_action 1428 local -x quote_escape_flags=$quote_escape_flags 1429 local -x quote_paramx_comps=$quote_paramx_comps 1430 local -x quote_cont_cutbackslash=$quote_cont_cutbackslash 1431 local -x quote_trav_prefix=$quote_trav_prefix 1432 1433 local -x quote_fixed_count=${#quote_fixed_comps[@]} 1434 local i 1435 for ((i=0;i<quote_fixed_count;i++)); do 1436 local -x "quote_fixed_comps$i=${quote_fixed_comps[i]}" 1437 local -x "quote_fixed_compv$i=${quote_fixed_compv[i]}" 1438 done 1439 1440 "$quote_batch_awk" -v quote_batch_nulsep="$quote_batch_nulsep" -v q="$q" ' 1441 function exists(filename) { return substr($0, 1, 1) == "1"; } 1442 function is_file(filename) { return substr($0, 2, 1) == "1"; } 1443 1444 function initialize(_, flags, comp_opts, tmp, i) { 1445 IS_XPG4 = AWKTYPE == "xpg4"; 1446 REP_SL = "\\"; 1447 if (IS_XPG4) REP_SL = "\\\\"; 1448 1449 REP_DBL_SL = "\\\\"; # gawk, nawk 1450 sub(/.*/, REP_DBL_SL, tmp); 1451 if (tmp == "\\") REP_DBL_SL = "\\\\\\\\"; # mawk, xpg4 1452 1453 Q = q "\\" q q; 1454 1455 DELIM = 10; 1456 if (quote_batch_nulsep != "") { 1457 RS = "\0"; 1458 DELIM = 0; 1459 } 1460 1461 quote_action = ENVIRON["quote_action"]; 1462 1463 comps = ENVIRON["comps"]; 1464 compv = ENVIRON["compv"]; 1465 compv_len = length(compv); 1466 1467 comps_flags = ENVIRON["comps_flags"]; 1468 escape_type = 0; 1469 if (comps_flags ~ /S/) 1470 escape_type = 1; 1471 else if (comps_flags ~ /E/) 1472 escape_type = 2; 1473 else if (comps_flags ~ /[DI]/) 1474 escape_type = 3; 1475 else 1476 escape_type = 4; 1477 comps_v = (comps_flags ~ /v/); 1478 comps_p = (comps_flags ~ /p/); 1479 1480 comp_opts = ENVIRON["comp_opts"]; 1481 is_noquote = comp_opts ~ /:noquote:/; 1482 is_nospace = comp_opts ~ /:nospace:/; 1483 is_syntaxraw = comp_opts ~ /:ble\/syntax-raw:/ && comp_opts !~ /:filenames:/; 1484 1485 flags = ENVIRON["quote_escape_flags"]; 1486 escape_c = (flags ~ /c/); 1487 escape_b = (flags ~ /b/); 1488 escape_tilde_always = 1; 1489 escape_tilde_exists = 0; 1490 if (quote_action == "progcomp") { 1491 escape_tilde_always = 0; 1492 escape_tilde_exists = (comp_opts ~ /:filenames:/); 1493 } 1494 1495 quote_cont_cutbackslash = ENVIRON["quote_cont_cutbackslash"] != ""; 1496 quote_paramx_comps = ENVIRON["quote_paramx_comps"]; 1497 quote_trav_prefix = ENVIRON["quote_trav_prefix"]; 1498 1499 quote_fixed_count = ENVIRON["quote_fixed_count"]; 1500 for (i = 0; i < quote_fixed_count; i++) { 1501 quote_fixed_comps[i] = ENVIRON["quote_fixed_comps" i]; 1502 quote_fixed_compv[i] = ENVIRON["quote_fixed_compv" i]; 1503 quote_fixed_comps_len[i] = length(quote_fixed_comps[i]); 1504 quote_fixed_compv_len[i] = length(quote_fixed_compv[i]); 1505 } 1506 } 1507 BEGIN { initialize(); } 1508 1509 function escape_for_completion_context(text) { 1510 if (escape_type == 1) { 1511 # single quote 1512 gsub(/'$q'/, Q, text); 1513 } else if (escape_type == 2) { 1514 # escape string 1515 if (text ~ /[\\'$q'\a\b\t\n\v\f\r\033]/) { 1516 gsub(/\\/ , REP_DBL_SL, text); 1517 gsub(/'$q'/, REP_SL q , text); 1518 gsub(/\007/, REP_SL "a", text); 1519 gsub(/\010/, REP_SL "b", text); 1520 gsub(/\011/, REP_SL "t", text); 1521 gsub(/\012/, REP_SL "n", text); 1522 gsub(/\013/, REP_SL "v", text); 1523 gsub(/\014/, REP_SL "f", text); 1524 gsub(/\015/, REP_SL "r", text); 1525 gsub(/\033/, REP_SL "e", text); 1526 } 1527 } else if (escape_type == 3) { 1528 # double quote 1529 gsub(/[\\"$`]/, "\\\\&", text); # Note: All awks behaves the same for "\\\\&" 1530 } else if (escape_type == 4) { 1531 # bash specialchars 1532 gsub(/[]\\ "'$q'`$|&;<>()!^*?[]/, "\\\\&", text); 1533 if (escape_c) gsub(/[=:]/, "\\\\&", text); 1534 if (escape_b) gsub(/[{,}]/, "\\\\&", text); 1535 if (ret ~ /^~/ && (escape_tilde_always || escape_tilde_exists && exists(cand))) 1536 text = "\\" text; 1537 gsub(/\n/, "$" q REP_SL "n" q, text); 1538 gsub(/\t/, "$" q REP_SL "t" q, text); 1539 } 1540 return text; 1541 } 1542 1543 function quote_insert(cand, _, i) { 1544 # progcomp 特有 1545 if (quote_action == "command") { 1546 if (comps == compv && cand ~ /^(\[\[|]]|!)$/) return cand; 1547 } else if (quote_action == "progcomp") { 1548 if (is_noquote || is_syntaxraw) return cand; 1549 if (is_nospace && cand ~ / $/ && !is_file(cand)) return cand; 1550 } 1551 1552 if (comps_v && substr(cand, 1, compv_len) == compv) { 1553 ins = escape_for_completion_context(substr(cand, compv_len + 1)); 1554 if (comps_p && ins ~ /^[_a-zA-Z0-9]/) { 1555 return quote_paramx_comps ins; 1556 } else { 1557 if (quote_cont_cutbackslash) sub(/^\\/, "", ins); 1558 return comps ins; 1559 } 1560 } 1561 1562 for (i = quote_fixed_count; --i >= 0; ) { 1563 if (quote_fixed_comps_len[i] && substr(cand, 1, quote_fixed_compv_len[i]) == quote_fixed_compv[i]) { 1564 ins = substr(cand, quote_fixed_compv_len[i] + 1); 1565 return quote_fixed_comps[i] quote_trav_prefix escape_for_completion_context(ins); 1566 } 1567 } 1568 1569 return quote_trav_prefix escape_for_completion_context(cand); 1570 } 1571 1572 { 1573 cand = substr($0, 3); 1574 insert = quote_insert(cand); 1575 printf("%s%c", insert, DELIM); 1576 } 1577 ' 1578 } 1579 function ble/complete/action/quote-insert.batch/proc { 1580 local _ble_local_tmpfile; ble/util/assign/mktmp 1581 1582 local delim='\n' 1583 [[ $quote_batch_nulsep ]] && delim='\0' 1584 if [[ $quote_action == progcomp ]]; then 1585 local cand file exist 1586 for cand in "${cands[@]}"; do 1587 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 1588 f=0 e=0 1589 [[ -e $cand ]] && e=1 1590 [[ -f $cand ]] && f=1 1591 printf "$e$f%s$delim" "$cand" 1592 done 1593 else 1594 printf "00%s$delim" "${cands[@]}" 1595 fi >| "$_ble_local_tmpfile" 1596 1597 local fname_cands=$_ble_local_tmpfile 1598 ble/util/conditional-sync \ 1599 'ble/complete/action/quote-insert.batch/awk < "$fname_cands"' \ 1600 '! ble/complete/check-cancel <&"$_ble_util_fd_stdin"' '' progressive-weight 1601 local ext=$? 1602 1603 ble/util/assign/rmtmp 1604 return "$ext" 1605 } 1606 ## @fn ble/complete/action/quote-insert.batch 1607 ## @arr[in] cands 1608 ## @arr[out] inserts 1609 function ble/complete/action/quote-insert.batch { 1610 local opts=$1 1611 1612 local quote_batch_nulsep= 1613 local quote_batch_awk=ble/bin/awk 1614 if [[ :$opts: != *:newline:* ]]; then 1615 if ((_ble_bash>=40400)); then 1616 if [[ $_ble_bin_awk_type == [mg]awk ]]; then 1617 quote_batch_nulsep=1 1618 elif ble/bin#has mawk; then 1619 quote_batch_nulsep=1 1620 quote_batch_awk=mawk 1621 elif ble/bin#has gawk; then 1622 quote_batch_nulsep=1 1623 quote_batch_awk=gawk 1624 fi 1625 fi 1626 [[ ! $quote_batch_nulsep ]] && 1627 [[ "${cands[*]}" == *$'\n'* ]] && 1628 return 1 1629 fi 1630 1631 if [[ $quote_batch_nulsep ]]; then 1632 ble/util/assign-array0 inserts ble/complete/action/quote-insert.batch/proc 1633 else 1634 ble/util/assign-array inserts ble/complete/action/quote-insert.batch/proc 1635 fi 1636 return "$?" 1637 } 1638 1639 ## @fn ble/complete/action/requote-final-insert 1640 ## @var[ref] insert insert_flags 1641 function ble/complete/action/requote-final-insert { 1642 local threshold=$((bleopt_complete_requote_threshold)) 1643 ((threshold>=0)) || return 0 1644 1645 local comps_prefix= check_optarg= 1646 if [[ $insert == "$COMPS"* ]]; then 1647 [[ $comps_flags == *[SEDI]* ]] && return 0 1648 1649 # Note: 以下の設定は遡って書き換える事を許す事になる 1650 [[ $COMPS != *[!':/={,'] ]] && comps_prefix=$COMPS 1651 check_optarg=$COMPS 1652 else 1653 # 遡って書き換える場合 (中途半端な quote 状態ではないと仮定) 1654 check_optarg=$insert 1655 fi 1656 1657 # Note: --prefix='/usr/local', PREFIX='/usr/local', -L'/usr/local/share/lib' 1658 # 等、オプション・変数代入の右辺などの quote は、その開始点と思われる箇所から 1659 # 始める。 1660 if [[ $check_optarg ]]; then 1661 if ble/string#match "$check_optarg" '^([_a-zA-Z][_a-zA-Z0-9]*|-[-a-zA-Z0-9.]+)=(([^\'\''"`${}]*|\\.)*:)?'; then 1662 # --prefix= や PREFIX=, PATH=xxxx: 等があった場合には = や : の直後から quote する。 1663 comps_prefix=$BASH_REMATCH 1664 elif [[ $COMP_PREFIX == -[!'-=:/\'\''"$`{};&|<>!^{}'] && $check_optarg == "$COMP_PREFIX"* ]]; then 1665 # -L'/path/to/library' 等。COMP_PREFIX=-L かつ COMPS が -L で始まっている時のみ。 1666 comps_prefix=${check_optarg::2} 1667 fi 1668 fi 1669 1670 if [[ $comps_fixed ]]; then 1671 local comps_fixed_part=${COMPS::${comps_fixed%%:*}} 1672 [[ $comps_prefix == "$comps_fixed_part"* ]] || 1673 comps_prefix=$comps_fixed_part 1674 fi 1675 1676 if [[ $insert == "$comps_prefix"* && $comps_prefix != *[!':/={,'] ]]; then 1677 local ret ins=${insert:${#comps_prefix}} 1678 if ! ble/syntax:bash/simple-word/is-literal "$ins" && 1679 ble/syntax:bash/simple-word/is-simple "$ins" && 1680 ble/syntax:bash/simple-word/eval "$ins" && 1681 ((${#ret[@]}==1)) 1682 then 1683 ble/string#quote-word "$ret" quote-empty 1684 ((${#ret}+threshold<=${#ins})) || return 0 1685 insert=$comps_prefix$ret 1686 [[ $insert == "$COMPS"* ]] || insert_flags=r$insert_flags # 遡って書き換えた 1687 fi 1688 fi 1689 return 0 1690 } 1691 1692 function ble/complete/action#inherit-from { 1693 local dst=$1 src=$2 1694 local member srcfunc dstfunc 1695 for member in initialize{,.batch} complete getg get-desc; do 1696 srcfunc=ble/complete/action:$src/$member 1697 dstfunc=ble/complete/action:$dst/$member 1698 ble/is-function "$srcfunc" && builtin eval "function $dstfunc { $srcfunc; }" 1699 done 1700 } 1701 1702 # action:plain 1703 function ble/complete/action:plain/initialize { 1704 ble/complete/action/quote-insert 1705 } 1706 function ble/complete/action:plain/initialize.batch { 1707 ble/complete/action/quote-insert.batch 1708 } 1709 function ble/complete/action:plain/complete { 1710 ble/complete/action/requote-final-insert 1711 } 1712 1713 # action:literal-substr 1714 function ble/complete/action:literal-substr/initialize { :; } 1715 function ble/complete/action:literal-substr/initialize.batch { inserts=("${cands[@]}"); } 1716 function ble/complete/action:literal-substr/complete { :; } 1717 1718 # action:substr (equivalent to plain) 1719 function ble/complete/action:substr/initialize { 1720 ble/complete/action/quote-insert 1721 } 1722 function ble/complete/action:substr/initialize.batch { 1723 ble/complete/action/quote-insert.batch 1724 } 1725 function ble/complete/action:substr/complete { 1726 ble/complete/action/requote-final-insert 1727 } 1728 1729 # action:literal-word 1730 function ble/complete/action:literal-word/initialize { :; } 1731 function ble/complete/action:literal-word/initialize.batch { inserts=("${cands[@]}"); } 1732 function ble/complete/action:literal-word/complete { 1733 if [[ $comps_flags == *x* ]]; then 1734 ble/complete/action/complete.addtail ',' 1735 else 1736 ble/complete/action/complete.addtail ' ' 1737 fi 1738 } 1739 1740 # action:word 1741 # 1742 # DATA ... 候補の説明として使用する文字列を指定します 1743 # 1744 function ble/complete/action:word/initialize { 1745 ble/complete/action/quote-insert 1746 } 1747 function ble/complete/action:word/initialize.batch { 1748 ble/complete/action/quote-insert.batch 1749 } 1750 function ble/complete/action:word/complete { 1751 ble/complete/action/requote-final-insert 1752 ble/complete/action/complete.close-quotation 1753 ble/complete/action:literal-word/complete 1754 } 1755 function ble/complete/action:word/get-desc { 1756 [[ $DATA ]] && desc=$DATA 1757 } 1758 1759 # action:file 1760 # action:file_rhs (source:argument 内部使用) 1761 1762 ## @fn ble/complete/action:file/.get-filename word 1763 ## "compopt -o ble/syntax-raw" の場合も考慮してファイル名を抽出する。 1764 ## Bash の振る舞いを見るとチルダ展開だけを実行する様だ。 1765 ## @var[in] CAND DATA 1766 function ble/complete/action:file/.get-filename { 1767 ret=$CAND 1768 if [[ $ACTION == progcomp && :$DATA: == *:ble/syntax-raw:* && $ret == '~'* ]]; then 1769 local tilde=${ret%%/*} chars='\ "'\''`$|&;<>()!^*?[=:{,}' 1770 [[ $tilde == *["$chars"]* ]] && return 0 1771 builtin eval "local expand=$tilde" 1772 [[ $expand == "$tilde" ]] && return 0 1773 ret=$expand${ret:${#tilde}} 1774 fi 1775 } 1776 function ble/complete/action:file/initialize { 1777 ble/complete/action/quote-insert 1778 } 1779 function ble/complete/action:file/initialize.batch { 1780 ble/complete/action/quote-insert.batch 1781 } 1782 function ble/complete/action:file/complete { 1783 ble/complete/action/requote-final-insert 1784 1785 local ret 1786 ble/complete/action:file/.get-filename 1787 if [[ -e $ret || -h $ret ]]; then 1788 if [[ -d $ret ]]; then 1789 ble/complete/action/complete.mark-directory 1790 else 1791 ble/complete/action:word/complete 1792 fi 1793 fi 1794 } 1795 function ble/complete/action:file/init-menu-item { 1796 local ret 1797 ble/complete/action:file/.get-filename; local file=$ret 1798 ble/syntax/highlight/getg-from-filename "$file" 1799 [[ $g ]] || { local ret; ble/color/face2g filename_warning; g=$ret; } 1800 1801 if [[ :$comp_type: == *:vstat:* ]]; then 1802 if [[ -h $file ]]; then 1803 suffix='@' 1804 elif [[ -d $file ]]; then 1805 suffix='/' 1806 elif [[ -x $file ]]; then 1807 suffix='*' 1808 fi 1809 fi 1810 } 1811 function ble/complete/action:file_rhs/initialize { 1812 ble/complete/action:file/initialize 1813 } 1814 function ble/complete/action:file_rhs/initialize.batch { 1815 ble/complete/action:file/initialize.batch 1816 } 1817 function ble/complete/action:file_rhs/complete { 1818 CAND=${CAND:${#DATA}} ble/complete/action:file/complete 1819 } 1820 function ble/complete/action:file_rhs/init-menu-item { 1821 CAND=${CAND:${#DATA}} ble/complete/action:file/init-menu-item 1822 } 1823 1824 _ble_complete_action_file_desc[_ble_attr_FILE_LINK]='symbolic link' 1825 _ble_complete_action_file_desc[_ble_attr_FILE_ORPHAN]='symbolic link (orphan)' 1826 _ble_complete_action_file_desc[_ble_attr_FILE_DIR]='directory' 1827 _ble_complete_action_file_desc[_ble_attr_FILE_STICKY]='directory (sticky)' 1828 _ble_complete_action_file_desc[_ble_attr_FILE_SETUID]='file (setuid)' 1829 _ble_complete_action_file_desc[_ble_attr_FILE_SETGID]='file (setgid)' 1830 _ble_complete_action_file_desc[_ble_attr_FILE_EXEC]='file (executable)' 1831 _ble_complete_action_file_desc[_ble_attr_FILE_FILE]='file' 1832 _ble_complete_action_file_desc[_ble_attr_FILE_CHR]='character device' 1833 _ble_complete_action_file_desc[_ble_attr_FILE_FIFO]='named pipe' 1834 _ble_complete_action_file_desc[_ble_attr_FILE_SOCK]='socket' 1835 _ble_complete_action_file_desc[_ble_attr_FILE_BLK]='block device' 1836 _ble_complete_action_file_desc[_ble_attr_FILE_URL]='URL' 1837 function ble/complete/action:file/get-desc { 1838 local type; ble/syntax/highlight/filetype "$CAND" 1839 desc=${_ble_complete_action_file_desc[type]:-'file (???)'} 1840 } 1841 1842 # action:progcomp 1843 # 1844 # DATA ... compopt 互換のオプションをコロン区切りで指定します 1845 # 1846 ## @fn ble/complete/action:progcomp/initialize/.reconstruct-from-noquote 1847 ## @var[in,out] INSERT CAND 1848 ## @var[in] progcomp_resolve_brace 1849 function ble/complete/action:progcomp/initialize/.reconstruct-from-noquote { 1850 local simple_flags simple_ibrace ret count 1851 ble/syntax:bash/simple-word/is-simple-or-open-simple "$INSERT" && 1852 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$INSERT" && 1853 ble/complete/source/eval-simple-word "$ret" single:count && 1854 ((count==1)) || return 0 1855 1856 CAND=$ret 1857 1858 # ブレース展開がある時は逆に INSERT を補正し返す。 1859 if [[ $quote_fixed_comps && $CAND == "$quote_fixed_compv"* ]]; then 1860 local ret; ble/complete/string#escape-for-completion-context "${CAND:quote_fixed_compv_len}" "$escape_flags" 1861 INSERT=$quote_fixed_comps$quote_trav_prefix$ret 1862 return 3 1863 fi 1864 return 0 1865 } 1866 1867 function ble/complete/action:progcomp/initialize { 1868 if [[ :$DATA: == *:noquote:* ]]; then 1869 local progcomp_resolve_brace=$quote_fixed_comps 1870 [[ :$DATA: == *:ble/syntax-raw:* ]] && progcomp_resolve_brace= 1871 ble/complete/action:progcomp/initialize/.reconstruct-from-noquote 1872 return 0 1873 else 1874 ble/complete/action/quote-insert progcomp 1875 fi 1876 } 1877 ## @fn ble/complete/action:progcomp/initialize.batch 1878 ## @arr[in] cands 1879 ## @arr[out] inserts 1880 function ble/complete/action:progcomp/initialize.batch { 1881 if [[ :$DATA: == *:noquote:* ]]; then 1882 inserts=("${cands[@]}") 1883 1884 # Note: 直接 comp_words に対して補完した時は意図的にブレース展開を潰してい 1885 # ると解釈できるので、ブレース展開を復元する事はしない。 1886 local progcomp_resolve_brace=$quote_fixed_comps 1887 [[ :$DATA: == *:ble/syntax-raw:* ]] && progcomp_resolve_brace= 1888 1889 cands=() 1890 local INSERT simple_flags simple_ibrace ret count icand=0 1891 for INSERT in "${inserts[@]}"; do 1892 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 1893 local CAND=$INSERT 1894 ble/complete/action:progcomp/initialize/.reconstruct-from-noquote || 1895 inserts[icand]=$INSERT # INSERT を上書きした時 ($?==3) 1896 cands[icand++]=$CAND 1897 done 1898 else 1899 ble/complete/action/quote-insert.batch newline 1900 fi 1901 } 1902 1903 function ble/complete/action:progcomp/complete { 1904 if [[ $DATA == *:filenames:* ]]; then 1905 ble/complete/action:file/complete 1906 else 1907 if [[ $DATA != *:ble/no-mark-directories:* && -d $CAND ]]; then 1908 ble/complete/action/requote-final-insert 1909 ble/complete/action/complete.mark-directory 1910 else 1911 ble/complete/action:word/complete 1912 fi 1913 fi 1914 1915 [[ $DATA == *:nospace:* ]] && suffix=${suffix%' '} 1916 [[ $DATA == *:ble/no-mark-directories:* && -d $CAND ]] && suffix=${suffix%/} 1917 } 1918 function ble/complete/action:progcomp/init-menu-item { 1919 if [[ $DATA == *:filenames:* ]]; then 1920 ble/complete/action:file/init-menu-item 1921 fi 1922 } 1923 function ble/complete/action:progcomp/get-desc { 1924 if [[ $DATA == *:filenames:* ]]; then 1925 ble/complete/action:file/get-desc 1926 fi 1927 } 1928 1929 # action:command 1930 1931 function ble/complete/action:command/initialize { 1932 ble/complete/action/quote-insert command 1933 } 1934 function ble/complete/action:command/initialize.batch { 1935 ble/complete/action/quote-insert.batch newline 1936 } 1937 function ble/complete/action:command/complete { 1938 if [[ -d $CAND ]]; then 1939 ble/complete/action/complete.mark-directory 1940 elif ! type "$CAND" &>/dev/null; then 1941 # 関数名について縮約されたもので一意確定した時。 1942 # 1943 # Note: 関数名について縮約されている時、 1944 # 本来は一意確定でなくても一意確定として此処に来ることがある。 1945 # そのコマンドが存在していない時に、縮約されていると判定する。 1946 # 1947 if [[ $CAND == */ ]]; then 1948 # 縮約されていると想定し続きの補完候補を出す。 1949 insert_flags=${insert_flags}n 1950 fi 1951 else 1952 ble/complete/action:word/complete 1953 fi 1954 } 1955 function ble/complete/action:command/init-menu-item { 1956 if [[ -d $CAND ]]; then 1957 local ret; ble/color/face2g filename_directory; g=$ret 1958 else 1959 local type 1960 if [[ $CAND != "$INSERT" ]]; then 1961 ble/syntax/highlight/cmdtype "$CAND" "$INSERT" 1962 else 1963 # Note: ble/syntax/highlight/cmdtype はキャッシュ機能がついているが、 1964 # キーワードに対して呼び出さない前提なのでキーワードを渡すと 1965 # _ble_attr_ERR を返してしまう。 1966 local type; ble/util/type type "$CAND" 1967 ble/syntax/highlight/cmdtype1 "$type" "$CAND" 1968 fi 1969 if [[ $CAND == */ ]] && ((type==_ble_attr_ERR)); then 1970 type=_ble_attr_CMD_FUNCTION 1971 fi 1972 ble/syntax/attr2g "$type" 1973 fi 1974 } 1975 1976 _ble_complete_action_command_desc[_ble_attr_CMD_BOLD]=builtin 1977 _ble_complete_action_command_desc[_ble_attr_CMD_BUILTIN]=builtin 1978 _ble_complete_action_command_desc[_ble_attr_CMD_ALIAS]=alias 1979 _ble_complete_action_command_desc[_ble_attr_CMD_FUNCTION]=function 1980 _ble_complete_action_command_desc[_ble_attr_CMD_FILE]=file 1981 _ble_complete_action_command_desc[_ble_attr_KEYWORD]=command 1982 _ble_complete_action_command_desc[_ble_attr_CMD_JOBS]=job 1983 _ble_complete_action_command_desc[_ble_attr_ERR]='command ???' 1984 _ble_complete_action_command_desc[_ble_attr_CMD_DIR]=directory 1985 function ble/complete/action:command/get-desc { 1986 local title= value= 1987 if [[ -d $CAND ]]; then 1988 title=directory 1989 else 1990 local type; ble/util/type type "$CAND" 1991 ble/syntax/highlight/cmdtype1 "$type" "$CAND" 1992 1993 case $type in 1994 ($_ble_attr_CMD_ALIAS) 1995 local ret 1996 ble/alias#expand "$CAND" 1997 title=alias value=$ret ;; 1998 ($_ble_attr_CMD_FILE) 1999 local path; ble/util/assign path 'type -p -- "$CAND"' 2000 [[ $path == ?*/"$CAND" ]] && path="from ${path%/"$CAND"}" 2001 title=file value=$path ;; 2002 ($_ble_attr_CMD_FUNCTION) 2003 2004 local source lineno 2005 ble/function#get-source-and-lineno "$CAND" 2006 2007 local def; ble/function#getdef "$CAND" 2008 ble/string#match "$def" '^[^()]*\(\)[[:space:]]*\{[[:space:]]+(.*[^[:space:]])[[:space:]]+\}[[:space:]]*$' && 2009 def=${BASH_REMATCH[1]} # 関数の中身を抽出する 2010 local ret sgr0=$'\e[27m' sgr1=$'\e[7m' # Note: sgr-ansi で生成 2011 lines=1 cols=${COLUMNS:-80} x=0 y=0 ble/canvas/trace-text "$def" external-sgr 2012 2013 title=function value="${source##*/}:$lineno $desc_sgrq$ret" ;; 2014 ($_ble_attr_CMD_JOBS) 2015 ble/util/joblist.check 2016 local job; ble/util/assign job 'jobs -- "$CAND" 2>/dev/null' || job='???' 2017 title=job value=${job:-(ambiguous)} ;; 2018 ($_ble_attr_ERR) 2019 if [[ $CAND == */ ]]; then 2020 title='function namespace' 2021 else 2022 title=${_ble_complete_action_command_desc[_ble_attr_ERR]} 2023 fi ;; 2024 (*) 2025 title=${_ble_complete_action_command_desc[type]:-'???'} ;; 2026 esac 2027 fi 2028 desc=${title:+$desc_sgrt($title)$desc_sgr0}${value:+ $value} 2029 } 2030 2031 # action:variable 2032 # 2033 # DATA ... 変数名の文脈を指定します。 2034 # assignment braced word arithmetic の何れかです。 2035 # 2036 function ble/complete/action:variable/initialize { ble/complete/action/quote-insert; } 2037 function ble/complete/action:variable/initialize.batch { ble/complete/action/quote-insert.batch newline; } 2038 function ble/complete/action:variable/complete { 2039 case $DATA in 2040 (assignment) 2041 # var= 等に於いて = を挿入 2042 ble/complete/action/complete.addtail '=' ;; 2043 (braced) 2044 # ${var 等に於いて } を挿入 2045 ble/complete/action/complete.addtail '}' ;; 2046 (word) ble/complete/action:word/complete ;; 2047 (arithmetic|nosuffix) ;; # do nothing 2048 esac 2049 } 2050 function ble/complete/action:variable/init-menu-item { 2051 local ret; ble/color/face2g syntax_varname; g=$ret 2052 } 2053 function ble/complete/action:variable/get-desc { 2054 local _ble_local_title=variable 2055 if ble/is-array "$CAND"; then 2056 _ble_local_title=array 2057 elif ble/is-assoc "$CAND"; then 2058 _ble_local_title=assoc 2059 fi 2060 2061 local _ble_local_value= 2062 if [[ $_ble_local_title == array || $_ble_local_title == assoc ]]; then 2063 builtin eval "local count=\${#$CAND[@]}" 2064 if ((count==0)); then 2065 count=empty 2066 else 2067 count="$count items" 2068 fi 2069 _ble_local_value=$'\e[94m['$count$']\e[m' 2070 else 2071 local ret; ble/string#quote-word "${!CAND}" ansi:sgrq="$desc_sgrq":quote-empty 2072 _ble_local_value=$ret 2073 fi 2074 desc="$desc_sgrt($_ble_local_title)$desc_sgr0 $_ble_local_value" 2075 } 2076 2077 #------------------------------------------------------------------------------ 2078 # source 2079 2080 ## @fn ble/complete/source/test-limit value 2081 ## Tests whether "value" exceeds the completion limit 2082 ## specified by bleopt complete_limit or complete_limit_auto. 2083 ## 2084 ## @var[in] comp_type 2085 ## @var[in,out] cand_limit_reached 2086 ## 2087 function ble/complete/source/test-limit { 2088 local value=$1 limit= 2089 if [[ :$comp_type: == *:auto_menu:* && $bleopt_complete_limit_auto_menu ]]; then 2090 limit=$bleopt_complete_limit_auto_menu 2091 elif [[ :$comp_type: == *:auto:* && $bleopt_complete_limit_auto ]]; then 2092 limit=$bleopt_complete_limit_auto 2093 else 2094 limit=$bleopt_complete_limit 2095 fi 2096 2097 if [[ $limit && value -gt limit ]]; then 2098 cand_limit_reached=1 2099 2100 # Note: #D1618 自動候補一覧表示で失敗した時は不完全なリストが生成 2101 # されるのを防ぐ為に補完全体をキャンセルする。 2102 [[ :$comp_type: == *:auto_menu: ]] && cand_limit_reached=cancel 2103 return 1 2104 else 2105 return 0 2106 fi 2107 } 2108 2109 ## @fn ble/complete/source/eval-simple-word 2110 ## @fn ble/complete/source/evaluate-path-spec 2111 ## 補完用の中断設定・timeout設定(auto-complete時のみ)を指定して、 2112 ## それぞれ simple-word/{eval,evaluate-path-spec} を呼び出します。 2113 ## 2114 ## 注意: 現在の実装では、ユーザー入力による中断 148 及びtimeout による 2115 ## 中断 142 の両方に対してこれらの関数は 148 を返す様に振る舞いを変更 2116 ## している。呼び出し元で両方を区別なく取り扱うのに都合が良い為。 2117 ## 2118 function ble/complete/source/eval-simple-word { 2119 local word=$1 opts=$2 2120 if [[ :$comp_type: != *:sync:* && :$opts: != *:noglob:* ]]; then 2121 opts=$opts:stopcheck:cached 2122 [[ :$comp_type: == *:auto:* && $bleopt_complete_timeout_auto ]] && 2123 opts=$opts:timeout=$((bleopt_complete_timeout_auto)) 2124 fi 2125 ble/syntax:bash/simple-word/eval "$word" "$opts"; local ext=$? 2126 ((ext==142)) && return 148 2127 return "$ext" 2128 } 2129 function ble/complete/source/evaluate-path-spec { 2130 local word=$1 sep=$2 opts=$3 2131 if [[ :$comp_type: != *:sync:* && :$opts: != *:noglob:* ]]; then 2132 opts=$opts:stopcheck:cached:single 2133 [[ :$comp_type: == *:auto:* && $bleopt_complete_timeout_auto ]] && 2134 opts=$opts:timeout=$((bleopt_complete_timeout_auto)) 2135 fi 2136 ble/syntax:bash/simple-word/evaluate-path-spec "$word" "$sep" "$opts"; local ext=$? 2137 ((ext==142)) && return 148 2138 return "$ext" 2139 } 2140 2141 2142 ## @fn ble/complete/source/reduce-compv-for-ambiguous-match 2143 ## 曖昧補完の為に擬似的な COMPV と COMPS を生成・設定します。 2144 ## @var[in,out] COMPS COMPV 2145 function ble/complete/source/reduce-compv-for-ambiguous-match { 2146 [[ :$comp_type: == *:[maA]:* ]] || return 0 2147 2148 local comps=$COMPS compv=$COMPV 2149 local comps_prefix= compv_prefix= 2150 if [[ $comps_fixed ]]; then 2151 comps_prefix=${comps::${comps_fixed%%:*}} 2152 compv_prefix=${comps_fixed#*:} 2153 compv=${COMPV:${#compv_prefix}} 2154 fi 2155 2156 case $comps_flags in 2157 (*S*) comps_prefix=$comps_prefix\' ;; 2158 (*E*) comps_prefix=$comps_prefix\$\' ;; 2159 (*D*) comps_prefix=$comps_prefix\" ;; 2160 (*I*) comps_prefix=$comps_prefix\$\" ;; 2161 esac 2162 2163 if [[ $compv && :$comp_type: == *:a:* ]]; then 2164 compv=${compv::1} 2165 ble/complete/string#escape-for-completion-context "$compv" 2166 comps=$ret 2167 else 2168 compv= comps= 2169 fi 2170 2171 COMPV=$compv_prefix$compv 2172 COMPS=$comps_prefix$comps 2173 } 2174 2175 2176 _ble_complete_yield_varnames=("${_ble_complete_quote_insert_varnames[@]}") 2177 2178 ## @fn ble/complete/cand/yield.initialize action 2179 function ble/complete/cand/yield.initialize { 2180 ble/complete/action/quote-insert.initialize "$1" 2181 } 2182 2183 ## @fn ble/complete/cand/yield ACTION CAND DATA 2184 ## @param[in] ACTION 2185 ## @param[in] CAND 2186 ## @param[in] DATA 2187 ## @var[in] COMP_PREFIX 2188 ## @var[in] flag_force_fignore 2189 ## @var[in] flag_source_filter 2190 function ble/complete/cand/yield { 2191 local ACTION=$1 CAND=$2 DATA=$3 2192 [[ $flag_force_fignore ]] && ! ble/complete/.fignore/filter "$CAND" && return 0 2193 [[ $flag_source_filter ]] || ble/complete/candidates/filter#test "$CAND" || return 0 2194 2195 local INSERT=$CAND 2196 ble/complete/action:"$ACTION"/initialize || return "$?" 2197 2198 local PREFIX_LEN=0 2199 [[ $CAND == "$COMP_PREFIX"* ]] && PREFIX_LEN=${#COMP_PREFIX} 2200 2201 local icand 2202 ((icand=cand_count++)) 2203 cand_cand[icand]=$CAND 2204 cand_word[icand]=$INSERT 2205 cand_pack[icand]=$ACTION:${#CAND},${#INSERT},$PREFIX_LEN:$CAND$INSERT$DATA 2206 } 2207 2208 ## @fn ble/complete/cand/yield.batch action data 2209 ## @arr[in] cands 2210 function ble/complete/cand/yield.batch { 2211 local ACTION=$1 DATA=$2 2212 2213 local inserts threshold=500 2214 [[ $OSTYPE == cygwin* || $OSTYPE == msys* ]] && threshold=2000 2215 if ((${#cands[@]}>=threshold)) && ble/function#try ble/complete/action:"$ACTION"/initialize.batch; then 2216 local i n=${#cands[@]} 2217 for ((i=0;i<n;i++)); do 2218 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 2219 local CAND=${cands[i]} INSERT=${inserts[i]} 2220 2221 [[ $flag_force_fignore ]] && ! ble/complete/.fignore/filter "$CAND" && continue 2222 [[ $flag_source_filter ]] || ble/complete/candidates/filter#test "$CAND" || continue 2223 2224 local PREFIX_LEN=0 2225 [[ $CAND == "$COMP_PREFIX"* ]] && PREFIX_LEN=${#COMP_PREFIX} 2226 2227 local icand 2228 ((icand=cand_count++)) 2229 cand_cand[icand]=$CAND 2230 cand_word[icand]=$INSERT 2231 cand_pack[icand]=$ACTION:${#CAND},${#INSERT},$PREFIX_LEN:$CAND$INSERT$DATA 2232 done 2233 else 2234 local cand 2235 for cand in "${cands[@]}"; do 2236 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 2237 ble/complete/cand/yield "$ACTION" "$cand" "$DATA" 2238 done 2239 fi 2240 } 2241 2242 function ble/complete/cand/yield-filenames { 2243 local action=$1; shift 2244 2245 local rex_hidden= 2246 [[ :$comp_type: != *:match-hidden:* ]] && 2247 rex_hidden=${COMPV:+'.{'${#COMPV}'}'}'(^|/)\.[^/]*$' 2248 2249 local -a cands=() 2250 local cand icand=0 2251 for cand; do 2252 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 2253 [[ $rex_hidden && $cand =~ $rex_hidden ]] && continue 2254 cands[icand++]=$cand 2255 done 2256 2257 [[ $FIGNORE ]] && local flag_force_fignore=1 2258 local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 2259 ble/complete/cand/yield.initialize "$action" 2260 ble/complete/cand/yield.batch "$action" 2261 } 2262 2263 _ble_complete_cand_varnames=(ACTION CAND INSERT DATA PREFIX_LEN) 2264 2265 ## @fn ble/complete/cand/unpack data 2266 ## @param[in] data 2267 ## ACTION:ncand,ninsert,PREFIX_LEN:$CAND$INSERT$DATA 2268 ## @var[out] ACTION CAND INSERT DATA PREFIX_LEN 2269 function ble/complete/cand/unpack { 2270 local pack=$1 2271 ACTION=${pack%%:*} pack=${pack#*:} 2272 local text=${pack#*:} 2273 IFS=, builtin eval 'pack=(${pack%%:*})' 2274 CAND=${text::pack[0]} 2275 INSERT=${text:pack[0]:pack[1]} 2276 DATA=${text:pack[0]+pack[1]} 2277 PREFIX_LEN=${pack[2]} 2278 } 2279 2280 ## 定義されている source 2281 ## 2282 ## source:wordlist 2283 ## source:command 2284 ## source:file 2285 ## source:dir 2286 ## source:argument 2287 ## source:variable 2288 ## 2289 ## source の実装 2290 ## 2291 ## @fn ble/complete/source:$name args... 2292 ## @param[in] args... 2293 ## ble/syntax/completion-context/generate で設定されるユーザ定義の引数。 2294 ## 2295 ## @var[in] COMP1 COMP2 COMPS COMPV comp_type 2296 ## @var[in] comp_filter_type 2297 ## @var[out] COMP_PREFIX 2298 ## ble/complete/cand/yield で参照される一時変数。 2299 ## 2300 ## @var[in,out] cand_count cand_cand cand_word cand_pack 2301 ## @var[in,out] cand_limit_reached 2302 2303 # source:none 2304 function ble/complete/source:none { return 0; } 2305 2306 # source:wordlist 2307 # 2308 # -r 指定された単語をエスケープせずにそのまま挿入する 2309 # -W 補完完了時に空白を挿入しない 2310 # -s sabbrev 候補も一緒に生成する 2311 # 2312 function ble/complete/source:wordlist { 2313 [[ $comps_flags == *v* ]] || return 1 2314 local COMPS=$COMPS COMPV=$COMPV 2315 ble/complete/source/reduce-compv-for-ambiguous-match 2316 [[ $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} 2317 2318 # process options 2319 local opt_raw= opt_noword= opt_sabbrev= 2320 while (($#)) && [[ $1 == -* ]]; do 2321 local arg=$1; shift 2322 case $arg in 2323 (--) break ;; 2324 (--*) ;; # ignore 2325 (-*) 2326 local i iN=${#arg} 2327 for ((i=1;i<iN;i++)); do 2328 case ${arg:i:1} in 2329 (r) opt_raw=1 ;; 2330 (W) opt_noword=1 ;; 2331 (s) opt_sabbrev=1 ;; 2332 (*) ;; # ignore 2333 esac 2334 done ;; 2335 esac 2336 done 2337 2338 [[ $opt_sabbrev ]] && 2339 ble/complete/source:sabbrev 2340 2341 local action=word 2342 [[ $opt_noword ]] && action=substr 2343 [[ $opt_raw ]] && action=literal-$action 2344 2345 local cand "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 2346 ble/complete/cand/yield.initialize "$action" 2347 for cand; do 2348 [[ $cand == "$COMPV"* ]] && ble/complete/cand/yield "$action" "$cand" 2349 done 2350 } 2351 2352 # source:command 2353 2354 function ble/complete/source:command/.contract-by-slashes { 2355 local slashes=${COMPV//[!'/']} 2356 ble/bin/awk -F / -v baseNF="${#slashes}" ' 2357 function initialize_common() { 2358 common_NF = NF; 2359 for (i = 1; i <= NF; i++) common[i] = $i; 2360 common_degeneracy = 1; 2361 common0_NF = NF; 2362 common0_str = $0; 2363 } 2364 function print_common(_, output) { 2365 if (!common_NF) return; 2366 2367 if (common_degeneracy == 1) { 2368 print common0_str; 2369 common_NF = 0; 2370 return; 2371 } 2372 2373 output = common[1]; 2374 for (i = 2; i <= common_NF; i++) 2375 output = output "/" common[i]; 2376 2377 # Note: 2378 # For candidates `a/b/c/1` and `a/b/c/2`, prints `a/b/c/`. 2379 # For candidates `a/b/c` and `a/b/c/1`, prints `a/b/c` and `a/b/c/1`. 2380 if (common_NF == common0_NF) print output; 2381 print output "/"; 2382 2383 common_NF = 0; 2384 } 2385 2386 { 2387 if (NF <= baseNF + 1) { 2388 print_common(); 2389 print $0; 2390 } else if (!common_NF) { 2391 initialize_common(); 2392 } else { 2393 n = common_NF < NF ? common_NF : NF; 2394 for (i = baseNF + 1; i <= n; i++) 2395 if (common[i] != $i) break; 2396 matched_length = i - 1; 2397 2398 if (matched_length <= baseNF) { 2399 print_common(); 2400 initialize_common(); 2401 } else { 2402 common_NF = matched_length; 2403 common_degeneracy++; 2404 } 2405 } 2406 } 2407 2408 END { print_common(); } 2409 ' 2410 } 2411 2412 ## @fn ble/complete/source:command/gen.1 2413 function ble/complete/source:command/gen.1 { 2414 # Note #D1922: パス名コマンドの曖昧補完は compgen -c ではなく自前で処理する。 2415 # ディレクトリ名に関しては ble/complete/source:command/gen の側で生成されるの 2416 # でここでは生成しない。 2417 if [[ $COMPV == */* && :$comp_type: == *:[maA]:* ]]; then 2418 local ret 2419 ble/complete/source:file/.construct-pathname-pattern "$COMPV" 2420 ble/complete/util/eval-pathname-expansion "$ret"; (($?==148)) && return 148 2421 ble/complete/source/test-limit "${#ret[@]}" || return 1 2422 ble/array#filter ret '[[ ! -d $1 && -x $1 ]]' 2423 ((${#ret[@]})) && printf '%s\n' "${ret[@]}" 2424 2425 local COMPS=$COMPS COMPV=$COMPV 2426 ble/complete/source/reduce-compv-for-ambiguous-match 2427 2428 else 2429 local COMPS=$COMPS COMPV=$COMPV 2430 ble/complete/source/reduce-compv-for-ambiguous-match 2431 2432 # Note: cygwin では cyg,x86,i68 等で始まる場合にとても遅い。他の環境でも空 2433 # の補完を実行すると遅くなる可能性がある。 2434 local slow_compgen= 2435 if [[ ! $COMPV ]]; then 2436 slow_compgen=1 2437 elif [[ $OSTYPE == cygwin* ]]; then 2438 case $COMPV in 2439 (?|cy*|x8*|i6*) 2440 slow_compgen=1 ;; 2441 esac 2442 fi 2443 2444 # Note: 何故か compgen -A command はクォート除去が実行されない。compgen -A 2445 # function はクォート除去が実行される。従って、compgen -A command には直 2446 # 接 COMPV を渡し、compgen -A function には compv_quoted を渡す。 2447 if [[ $slow_compgen ]]; then 2448 shopt -q no_empty_cmd_completion && return 0 2449 ble/util/conditional-sync \ 2450 'builtin compgen -c -- "$COMPV"' \ 2451 '! ble/complete/check-cancel' 128 progressive-weight 2452 else 2453 builtin compgen -c -- "$COMPV" 2454 fi 2455 fi 2456 2457 if [[ $COMPV == */* ]]; then 2458 local q="'" Q="'\''" 2459 local compv_quoted="'${COMPV//$q/$Q}'" 2460 builtin compgen -A function -- "$compv_quoted" 2461 fi 2462 } 2463 2464 function ble/complete/source:command/gen { 2465 if [[ :$comp_type: != *:[maA]:* && $bleopt_complete_contract_function_names ]]; then 2466 ble/complete/source:command/gen.1 | 2467 ble/complete/source:command/.contract-by-slashes 2468 else 2469 ble/complete/source:command/gen.1 2470 fi 2471 2472 # ディレクトリ名列挙 (/ 付きで生成する) 2473 # 2474 # Note: shopt -q autocd &>/dev/null かどうかに拘らず列挙する。 2475 # 2476 # Note: compgen -A directory (以下のコード参照) はバグがあって、 2477 # bash-4.3 以降でクォート除去が実行されないので使わない (#D0714 #M0009) 2478 # 2479 # [[ :$comp_type: == *:a:* ]] && local COMPS=${COMPS::1} COMPV=${COMPV::1} 2480 # compgen -A directory -S / -- "$compv_quoted" 2481 # 2482 if [[ $arg != *D* ]]; then 2483 local ret 2484 ble/complete/source:file/.construct-pathname-pattern "$COMPV" 2485 ble/complete/util/eval-pathname-expansion "$ret/"; (($?==148)) && return 148 2486 ble/complete/source/test-limit "${#ret[@]}" || return 1 2487 ((${#ret[@]})) && printf '%s\n' "${ret[@]}" 2488 fi 2489 2490 # ジョブ名列挙 2491 if [[ ! $COMPV || $COMPV == %* ]]; then 2492 # %コマンド名 2493 local q="'" Q="'\''" 2494 local compv_quoted=${COMPV#'%'} 2495 compv_quoted="'${compv_quoted//$q/$Q}'" 2496 builtin compgen -j -P % -- "$compv_quoted" 2497 2498 # %ジョブ番号 2499 local i joblist; ble/util/joblist 2500 local job_count=${#joblist[@]} 2501 for i in "${!joblist[@]}"; do 2502 if local rex='^\[([0-9]+)\]'; [[ ${joblist[i]} =~ $rex ]]; then 2503 joblist[i]=%${BASH_REMATCH[1]} 2504 else 2505 builtin unset -v 'joblist[i]' 2506 fi 2507 done 2508 joblist=("${joblist[@]}") 2509 2510 # %% %+ %- 2511 if ((job_count>0)); then 2512 ble/array#push joblist %% %+ 2513 ((job_count>=2)) && 2514 ble/array#push joblist %- 2515 fi 2516 2517 builtin compgen -W '"${joblist[@]}"' -- "$compv_quoted" 2518 fi 2519 } 2520 ## ble/complete/source:command arg 2521 ## @param[in] arg 2522 ## arg に D が含まれている時、 2523 ## ディレクトリ名の列挙を抑制する事を表します。 2524 function ble/complete/source:command { 2525 [[ $comps_flags == *v* ]] || return 1 2526 [[ ! $COMPV ]] && shopt -q no_empty_cmd_completion && return 1 2527 [[ $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} 2528 local arg=$1 2529 2530 # Try progcomp by "complete -p -I" / "complete -p _InitialWorD_" 2531 { 2532 local old_cand_count=$cand_count 2533 2534 local comp_opts=: 2535 ble/complete/source:argument/.generate-user-defined-completion initial; local ext=$? 2536 ((ext==148)) && return "$ext" 2537 if ((ext==0)); then 2538 ((cand_count>old_cand_count)) && return "$ext" 2539 fi 2540 } 2541 2542 ble/complete/source:sabbrev 2543 2544 local arr 2545 local compgen 2546 ble/util/assign compgen 'ble/complete/source:command/gen "$arg"' 2547 [[ $compgen ]] || return 1 2548 ble/util/assign-array arr 'ble/bin/sort -u <<< "$compgen"' # 1 fork/exec 2549 2550 ble/complete/source/test-limit "${#arr[@]}" || return 1 2551 2552 local action=command "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 2553 ble/complete/cand/yield.initialize "$action" 2554 2555 # 無効 keyword, alias 判定用 2556 local is_quoted= 2557 [[ $COMPS != "$COMPV" ]] && is_quoted=1 2558 local rex_keyword='^(if|then|else|elif|fi|case|esac|for|select|while|until|do|done|function|time|[!{}]|\[\[|coproc|\]\]|in)$' 2559 local expand_aliases= 2560 shopt -q expand_aliases && expand_aliases=1 2561 2562 local cand icand=0 cands 2563 for cand in "${arr[@]}"; do 2564 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 2565 2566 # workaround: 何故か compgen -c -- "$compv_quoted" で 2567 # 厳密一致のディレクトリ名が混入するので削除する。 2568 [[ $cand != */ && -d $cand ]] && ! type "$cand" &>/dev/null && continue 2569 2570 if [[ $is_quoted ]]; then 2571 local disable_count= 2572 # #D1691 keyword は quote されている場合には無効 2573 [[ $cand =~ $rex_keyword ]] && ((disable_count++)) 2574 # #D1715 alias も quote されている場合には無効 2575 [[ $expand_aliases ]] && ble/is-alias "$cand" && ((disable_count++)) 2576 if [[ $disable_count ]]; then 2577 local type; ble/util/type type "$cand" 2578 ((${#type[@]}>disable_count)) || continue 2579 fi 2580 else 2581 # 'in' と ']]' は alias でない限り常にエラー 2582 [[ $cand == ']]' || $cand == in ]] && 2583 ! { [[ $expand_aliases ]] && ble/is-alias "$cand"; } && 2584 continue 2585 2586 if [[ ! $expand_aliases ]]; then 2587 # #D1715 expand_aliases が無効でも compgen -c は alias を列挙してしまうので、 2588 # ここで alias は除外 (type は expand_aliases をちゃんと考慮してくれる)。 2589 ble/is-alias "$cand" && ! type "$cand" &>/dev/null && continue 2590 fi 2591 2592 # alias は quote されては困るので、quote される可能性のある文字を含んでい 2593 # る場合は個別に :noquote: 指定で yield する [ Note: alias 内で許される特 2594 # 殊文字は !#%-~^[]{}+*:@,.?_ である。更にその中で escape/quote の対象と 2595 # なり得る文字は、[*?]{,}!^~#: だけである。_.@+%- は quote されない ]。 2596 if ble/string#match "$cand" '[][*?{,}!^~#]' && ble/is-alias "$cand"; then 2597 ble/complete/cand/yield "$action" "$cand" :noquote: 2598 continue 2599 fi 2600 fi 2601 2602 cands[icand++]=$cand 2603 done 2604 ble/complete/cand/yield.batch "$action" 2605 } 2606 2607 # source:file, source:dir 2608 2609 function ble/complete/util/eval-pathname-expansion/.print-def { 2610 local pattern=$1 ret 2611 IFS= builtin eval "ret=($pattern)" 2>/dev/null 2612 ble/string#quote-words "${ret[@]}" 2613 ble/util/print "ret=($ret)" 2614 } 2615 2616 ## @fn ble/complete/util/eval-pathname-expansion pattern 2617 ## @var[out] ret 2618 function ble/complete/util/eval-pathname-expansion { 2619 local pattern=$1 2620 2621 local -a dtor=() 2622 2623 if [[ -o noglob ]]; then 2624 set +f 2625 ble/array#push dtor 'set -f' 2626 fi 2627 2628 if ! shopt -q nullglob; then 2629 shopt -s nullglob 2630 ble/array#push dtor 'shopt -u nullglob' 2631 fi 2632 2633 if ! shopt -q dotglob; then 2634 shopt -s dotglob 2635 ble/array#push dtor 'shopt -u dotglob' 2636 else 2637 # GLOBIGNORE に触ると設定が変わるので 2638 # dotglob は明示的に保存・復元する。 2639 ble/array#push dtor 'shopt -s dotglob' 2640 fi 2641 2642 if ! shopt -q extglob; then 2643 shopt -s extglob 2644 ble/array#push dtor 'shopt -u extglob' 2645 fi 2646 2647 if [[ :$comp_type: == *:i:* ]]; then 2648 if ! shopt -q nocaseglob; then 2649 shopt -s nocaseglob 2650 ble/array#push dtor 'shopt -u nocaseglob' 2651 fi 2652 else 2653 if shopt -q nocaseglob; then 2654 shopt -u nocaseglob 2655 ble/array#push dtor 'shopt -s nocaseglob' 2656 fi 2657 fi 2658 2659 if ble/util/is-cygwin-slow-glob "$pattern"; then # Note: #D1168 2660 if shopt -q failglob &>/dev/null || shopt -q nullglob &>/dev/null; then 2661 pattern= 2662 else 2663 set -f 2664 ble/array#push dtor 'set +f' 2665 fi 2666 fi 2667 2668 if [[ $GLOBIGNORE ]]; then 2669 local GLOBIGNORE_save=$GLOBIGNORE 2670 GLOBIGNORE= 2671 ble/array#push dtor 'GLOBIGNORE=$GLOBIGNORE_save' 2672 fi 2673 2674 ble/array#reverse dtor 2675 2676 ret=() 2677 if [[ :$comp_type: == *:sync:* ]]; then 2678 IFS= builtin eval "ret=($pattern)" 2>/dev/null 2679 else 2680 local sync_command='ble/complete/util/eval-pathname-expansion/.print-def "$pattern"' 2681 local sync_opts=progressive-weight 2682 [[ :$comp_type: == *:auto:* && $bleopt_complete_timeout_auto ]] && 2683 sync_opts=$sync_opts:timeout=$((bleopt_complete_timeout_auto)) 2684 2685 local def 2686 ble/util/assign def 'ble/util/conditional-sync "$sync_command" "" "" "$sync_opts"' &>/dev/null; local ext=$? 2687 if ((ext==148)) || ble/complete/check-cancel; then 2688 ble/util/invoke-hook dtor 2689 return 148 2690 fi 2691 builtin eval -- "$def" 2692 fi 2>&"$_ble_util_fd_stderr" 2693 2694 ble/util/invoke-hook dtor 2695 return 0 2696 } 2697 2698 ## @fn ble/complete/source:file/.construct-ambiguous-pathname-pattern path 2699 ## 指定された path に対応する曖昧一致パターンを生成します。 2700 ## 例えば alpha/beta/gamma に対して a*/b*/g* でファイル名を生成します。 2701 ## 但し "../" や "./" については (".*.*/" や ".*/" 等に変換せず) そのままにします。 2702 ## 2703 ## @param[in] path 2704 ## @var[out] ret 2705 ## 2706 ## @remarks 2707 ## 当初は a*/b*/g* で生成して、後のフィルタに一致しないものの除外を一任していたが遅い。 2708 ## 従って、a*l*p*h*a*/b*e*t*a*/g*a*m*m*a* の様なパターンを生成する様に変更した。 2709 ## 2710 function ble/complete/source:file/.construct-ambiguous-pathname-pattern { 2711 local path=$1 fixlen=${2:-1} 2712 local pattern= i=0 j 2713 local names; ble/string#split names / "$1" 2714 local name 2715 for name in "${names[@]}"; do 2716 ((i++)) && pattern=$pattern/ 2717 if [[ $name == .. || $name == . && i -lt ${#names[@]} ]]; then 2718 pattern=$pattern$name 2719 elif [[ $name ]]; then 2720 ble/string#quote-word "${name::fixlen}" 2721 pattern=$pattern$ret* 2722 for ((j=fixlen;j<${#name};j++)); do 2723 ble/string#quote-word "${name:j:1}" 2724 if [[ $_ble_bash -lt 50000 && $pattern == *\* ]]; then 2725 # * を extglob *([!ch]) に変換 #D1389 2726 pattern=$pattern'([!'$ret'])' 2727 fi 2728 pattern=$pattern$ret* 2729 done 2730 fi 2731 done 2732 [[ $pattern == *'*' ]] || pattern=$pattern* 2733 ret=$pattern 2734 } 2735 ## @fn ble/complete/source:file/.construct-pathname-pattern path 2736 ## @param[in] path 2737 ## @var[out] ret 2738 function ble/complete/source:file/.construct-pathname-pattern { 2739 local path=$1 pattern 2740 case :$comp_type: in 2741 (*:a:*) ble/complete/source:file/.construct-ambiguous-pathname-pattern "$path"; pattern=$ret ;; 2742 (*:A:*) ble/complete/source:file/.construct-ambiguous-pathname-pattern "$path" 0; pattern=$ret ;; 2743 (*:m:*) ble/string#quote-word "$path"; pattern=*$ret* ;; 2744 (*) ble/string#quote-word "$path"; pattern=$ret* 2745 esac 2746 ret=$pattern 2747 } 2748 2749 function ble/complete/source:file/.impl { 2750 local opts=$1 2751 [[ $comps_flags == *v* ]] || return 1 2752 [[ :$comp_type: != *:[maA]:* && $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} 2753 # 入力文字列が空の場合は曖昧補完は本質的に通常の補完と同じなのでスキップ 2754 [[ :$comp_type: == *:[maA]:* && ! $COMPV ]] && return 1 2755 2756 # Note: compgen -A file/directory (以下のコード参照) はバグがあって、 2757 # bash-4.0 と 4.1 でクォート除去が実行されないので使わない (#D0714 #M0009) 2758 # 2759 # local q="'" Q="'\''"; local compv_quoted="'${COMPV//$q/$Q}'" 2760 # local candidates; ble/util/assign-array candidates 'builtin compgen -A file -- "$compv_quoted"' 2761 2762 ble/complete/source:tilde; local ext=$? 2763 ((ext==148||ext==0)) && return "$ext" 2764 2765 local -a candidates=() 2766 local ret 2767 ble/complete/source:file/.construct-pathname-pattern "$COMPV" 2768 [[ :$opts: == *:directory:* ]] && ret=$ret/ 2769 ble/complete/util/eval-pathname-expansion "$ret"; (($?==148)) && return 148 2770 ble/complete/source/test-limit "${#ret[@]}" || return 1 2771 2772 if [[ :$opts: == *:directory:* ]]; then 2773 candidates=("${ret[@]%/}") 2774 else 2775 candidates=("${ret[@]}") 2776 fi 2777 [[ :$opts: == *:no-fd:* ]] && 2778 ble/array#remove-by-regex candidates '^[0-9]+-?$|^-$' 2779 2780 local flag_source_filter=1 2781 ble/complete/cand/yield-filenames file "${candidates[@]}" 2782 } 2783 2784 function ble/complete/source:file { 2785 ble/complete/source:file/.impl "$1" 2786 } 2787 function ble/complete/source:dir { 2788 ble/complete/source:file/.impl "directory:$1" 2789 } 2790 2791 # source:rhs 2792 2793 function ble/complete/source:rhs { ble/complete/source:file; } 2794 2795 #------------------------------------------------------------------------------ 2796 # source:tilde 2797 2798 function ble/complete/action:tilde/initialize { 2799 # チルダは quote しない 2800 CAND=${CAND#\~} ble/complete/action/quote-insert 2801 INSERT=\~$INSERT 2802 2803 # Note: Windows 等でチルダ展開の無効なユーザー名があるのでチェック 2804 local rex='^~[^/'\''"$`\!:]*$'; [[ $INSERT =~ $rex ]] 2805 } 2806 function ble/complete/action:tilde/complete { 2807 ble/complete/action/complete.mark-directory 2808 } 2809 function ble/complete/action:tilde/init-menu-item { 2810 local ret 2811 ble/color/face2g filename_directory; g=$ret 2812 } 2813 function ble/complete/action:tilde/get-desc { 2814 if [[ $CAND == '~+' ]]; then 2815 desc='current directory (tilde expansion)' 2816 elif [[ $CAND == '~-' ]]; then 2817 desc='previous directory (tilde expansion)' 2818 elif local rex='^~[0-9]$'; [[ $CAND =~ $rex ]]; then 2819 desc='DIRSTACK directory (tilde expansion)' 2820 else 2821 desc='user directory (tilde expansion)' 2822 fi 2823 } 2824 2825 function ble/complete/source:tilde/.generate { 2826 # generate user directories 2827 local pattern=${COMPS#\~} 2828 [[ :$comp_type: == *:[maA]:* ]] && pattern= 2829 builtin compgen -P \~ -u -- "$pattern" 2830 2831 # generate special tilde expansions 2832 printf '%s\n' '~' '~+' '~-' 2833 local dirstack_max=$((${#DIRSTACK[@]}-1)) 2834 ((dirstack_max>=0)) && 2835 builtin eval "printf '%s\n' '~'{0..$dirstack_max}" 2836 2837 } 2838 2839 # tilde expansion 2840 function ble/complete/source:tilde { 2841 local rex='^~[^/'\''"$`\!:]*$'; [[ $COMPS =~ $rex ]] || return 1 2842 2843 # Generate candidates 2844 # Note: Windows で同じユーザ名が compgen によって 2845 # 複数回列挙されるので sort -u を実行する。 2846 local compgen candidates 2847 ble/util/assign compgen ble/complete/source:tilde/.generate 2848 [[ $compgen ]] || return 1 2849 ble/util/assign-array candidates 'ble/bin/sort -u <<< "$compgen"' 2850 2851 # COMPS を用いて自前でフィルタ 2852 local flag_source_filter=1 2853 if [[ $COMPS == '~'?* ]]; then 2854 local filter_type=$comp_filter_type 2855 [[ $filter_type == none ]] && filter_type=head 2856 local comp_filter_type 2857 local comp_filter_pattern 2858 ble/complete/candidates/filter#init "$filter_type" "$COMPS" 2859 ble/array#filter candidates ble/complete/candidates/filter#test 2860 fi 2861 2862 ((${#candidates[@]})) || return 1 2863 2864 local old_cand_count=$cand_count 2865 ble/complete/cand/yield-filenames tilde "${candidates[@]}"; local ext=$? 2866 return "$((ext?ext:cand_count>old_cand_count))" 2867 } 2868 2869 function ble/complete/source:fd { 2870 IFS=: builtin eval 'local fdlist=":${_ble_util_openat_fdlist[*]}:"' 2871 2872 [[ $comp_filter_type == none ]] && 2873 local comp_filter_type=head 2874 2875 local old_cand_count=$cand_count 2876 local action=word "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 2877 ble/complete/cand/yield.initialize "$action" 2878 ble/complete/cand/yield "$action" - 2879 if [[ -d /proc/self/fd ]]; then 2880 local ret 2881 ble/complete/util/eval-pathname-expansion '/proc/self/fd/*' 2882 2883 local fd 2884 for fd in "${ret[@]}"; do 2885 fd=${fd#/proc/self/fd/} 2886 [[ ${fd//[0-9]} ]] && continue 2887 [[ $fdlist == *:"$fd":* ]] && continue 2888 ble/complete/cand/yield "$action" "$fd" 2889 ble/complete/cand/yield "$action" "$fd-" 2890 done 2891 else 2892 local fd 2893 for ((fd=0;fd<10;fd++)); do 2894 ble/fd#is-open "$fd" || continue 2895 ble/complete/cand/yield "$action" "$fd" 2896 ble/complete/cand/yield "$action" "$fd-" 2897 done 2898 fi 2899 2900 return "$((cand_count>old_cand_count))" 2901 } 2902 2903 #------------------------------------------------------------------------------ 2904 # progcomp 2905 2906 # progcomp/.compgen 2907 2908 ## @fn ble/complete/progcomp/.compvar-initialize-wordbreaks 2909 ## @var[out] wordbreaks 2910 function ble/complete/progcomp/.compvar-initialize-wordbreaks { 2911 local ifs=$_ble_term_IFS q=\'\" delim=';&|<>()' glob='[*?' hist='!^{' esc='`$\' 2912 local escaped=$ifs$q$delim$glob$hist$esc 2913 wordbreaks=${COMP_WORDBREAKS//[$escaped]} # =: 2914 } 2915 ## @fn ble/complete/progcomp/.compvar-perform-wordbreaks word 2916 ## @var[in] wordbreaks 2917 ## @arr[out] ret 2918 function ble/complete/progcomp/.compvar-perform-wordbreaks { 2919 local word=$1 2920 if [[ ! $word ]]; then 2921 ret=('') 2922 return 0 2923 fi 2924 2925 ret=() 2926 while local head=${word%%["$wordbreaks"]*}; [[ $head != $word ]]; do 2927 # Note: #D1094 bash の動作に倣って wordbreaks の連続は一つにまとめる。 2928 ble/array#push ret "$head" 2929 word=${word:${#head}} 2930 head=${word%%[!"$wordbreaks"]*} 2931 ble/array#push ret "$head" 2932 word=${word:${#head}} 2933 done 2934 2935 # Note: #D1094 $word が空の時でも ret に push する。 2936 # $word が空の時は wordbreaks で終わっている事を意味するが、 2937 # その場合には wordbreaks の次に新しい単語を開始していると考える。 2938 ble/array#push ret "$word" 2939 } 2940 function ble/complete/progcomp/.compvar-eval-word { 2941 local opts=$2:single 2942 if [[ :$opts: == *:noglob:* ]]; then 2943 ble/syntax:bash/simple-word/eval "$1" "$opts" 2944 else 2945 [[ $bleopt_complete_timeout_compvar ]] && 2946 opts=timeout=$((bleopt_complete_timeout_compvar)):retry-noglob-on-timeout:$opts 2947 ble/complete/source/eval-simple-word "$1" "$opts" 2948 fi 2949 } 2950 2951 ## @fn ble/complete/progcomp/.compvar-generate-subwords/impl1 word 2952 ## $wordbreaks で分割してから評価する戦略。 2953 ## 2954 ## @param word 2955 ## @arr[out] words 2956 ## @var[in,out] point 2957 ## @var[in] wordbreaks 2958 ## @exit 2959 ## 単純単語として処理できなかった場合に失敗します。 2960 ## それ以外の場合は 0 を返します。 2961 function ble/complete/progcomp/.compvar-generate-subwords/impl1 { 2962 local word=$1 ret simple_flags simple_ibrace 2963 if [[ $point ]]; then 2964 # point で単語を前半と後半に分割 2965 local left=${word::point} right=${word:point} 2966 else 2967 local left=$word right= 2968 local point= # hide 2969 fi 2970 2971 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$left" || return 1 2972 left=$ret 2973 if [[ $right ]]; then 2974 case $simple_flags in 2975 (*I*) right=\$\"$right ;; 2976 (*D*) right=\"$right ;; 2977 (*E*) right=\$\'$right ;; 2978 (*S*) right=\'$right ;; 2979 (*B*) right=\\$right ;; 2980 esac 2981 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$right" || return 1 2982 right=$ret 2983 fi 2984 2985 point=0 words=() 2986 2987 # 単語毎に評価 (前半) 2988 local eval_opts=noglob 2989 ((${#ret[@]}==1)) && eval_opts= 2990 ble/syntax:bash/simple-word#break-word "$left" "$wordbreaks" 2991 local subword 2992 for subword in "${ret[@]}"; do 2993 ble/complete/progcomp/.compvar-eval-word "$subword" "$eval_opts" 2994 ble/array#push words "$ret" 2995 ((point+=${#ret})) 2996 done 2997 2998 # 単語毎に評価 (後半) 2999 if [[ $right ]]; then 3000 ble/syntax:bash/simple-word#break-word "$right" "$wordbreaks" 3001 local subword isfirst=1 3002 for subword in "${ret[@]}"; do 3003 ble/complete/progcomp/.compvar-eval-word "$subword" noglob 3004 if [[ $isfirst ]]; then 3005 isfirst= 3006 local iword=${#words[@]}; ((iword&&iword--)) 3007 words[iword]=${words[iword]}$ret 3008 else 3009 ble/array#push words "$ret" 3010 fi 3011 done 3012 fi 3013 return 0 3014 } 3015 ## @fn ble/complete/progcomp/.compvar-generate-subwords/impl2 word 3016 ## 評価してから $wordbreaks で分割する戦略。 3017 ## 3018 ## @param word 3019 ## @arr[out] words 3020 ## @var[in,out] point 3021 ## @var[in] wordbreaks 3022 ## @exit 3023 ## 単純単語として処理できなかった場合に失敗します。 3024 ## それ以外の場合は 0 を返します。 3025 function ble/complete/progcomp/.compvar-generate-subwords/impl2 { 3026 local word=$1 3027 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$word" || return 1 3028 3029 ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148; local value1=$ret 3030 if [[ $point ]]; then 3031 if ((point==${#word})); then 3032 point=${#value1} 3033 elif ble/syntax:bash/simple-word/reconstruct-incomplete-word "${word::point}"; then 3034 ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148 3035 point=${#ret} 3036 fi 3037 fi 3038 3039 ble/complete/progcomp/.compvar-perform-wordbreaks "$value1"; words=("${ret[@]}") 3040 return 0 3041 } 3042 ## @fn ble/complete/progcomp/.compvar-generate-subwords word1 3043 ## word1 を COMP_WORDBREAKS で分割します。 3044 ## 3045 ## @arr[out] words 3046 ## 分割して得られた単語片を格納します。 3047 ## 3048 ## @var[in,out] subword_flags 3049 ## E が含まれる時、単語の展開・分割が実施された事を示す。 3050 ## 全体が単純単語になっている時、先に eval して COMP_WORDBREAKS で分割する。 3051 ## そうでない時に先に COMP_WORDBREAKS で分割して、 3052 ## 各単語片に対して単純単語 eval を試みる。 3053 ## 3054 ## Q が含まれている時、後続の処理における展開・クォートを抑制し、 3055 ## 補完関数に対してそのままの形で単語を渡す事を示す。 3056 ## これはチルダ ~ に対してユーザ名を補完させるのに使う。 3057 ## 3058 ## @var[in,out] point 3059 ## @var[in] wordbreaks 3060 ## 3061 ## Note: 全体が単純単語になっている時には先に eval して COMP_WORDBREAKS で分割する。 3062 ## この時 subword_flags=E を設定する。 3063 ## 3064 function ble/complete/progcomp/.compvar-generate-subwords { 3065 local word1=$1 ret simple_flags simple_ibrace 3066 if [[ ! $word1 ]]; then 3067 # Note: 空文字列に対して正しい単語とする為に '' とすると git の補完関数が動かなくなる。 3068 # 仕方がないので空文字列のままで登録する事にする。 3069 subword_flags=E 3070 words=('') 3071 elif [[ $word1 == '~' ]]; then 3072 # #D1362: ~ は展開するとユーザ名を補完できなくので特別にそのまま渡す。 3073 subword_flags=Q 3074 words=('~') 3075 elif ble/complete/progcomp/.compvar-generate-subwords/impl1 "$word1"; then 3076 # 初めに、先に分割してから評価する戦略を試す。 3077 subword_flags=E 3078 elif ble/complete/progcomp/.compvar-generate-subwords/impl2 "$word1"; then 3079 # 次に、評価してから分割する戦略を試す。 3080 subword_flags=E 3081 else 3082 ble/complete/progcomp/.compvar-perform-wordbreaks "$word1"; words=("${ret[@]}") 3083 fi 3084 } 3085 ## @fn ble/complete/progcomp/.compvar-quote-subword word 3086 ## @var[in] index subword_flags 3087 ## @var[out] ret 3088 ## @var[in,out] p 3089 function ble/complete/progcomp/.compvar-quote-subword { 3090 local word=$1 to_quote= is_evaluated= is_quoted= 3091 if [[ $subword_flags == *[EQ]* ]]; then 3092 [[ $subword_flags == *E* ]] && to_quote=1 3093 elif ble/syntax:bash/simple-word/reconstruct-incomplete-word "$word"; then 3094 is_evaluated=1 3095 ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148; word=$ret 3096 to_quote=1 3097 fi 3098 3099 # コマンド名以外は再クォート 3100 if [[ $to_quote ]]; then 3101 local shell_specialchars=']\ ["'\''`$|&;<>()*?{}!^'$'\n\t' q="'" Q="'\''" qq="''" 3102 if ((index>0)) && [[ $word == *["$shell_specialchars"]* || $word == [#~]* ]]; then 3103 is_quoted=1 3104 word="'${w//$q/$Q}'" word=${word#"$qq"} word=${word%"$qq"} 3105 fi 3106 fi 3107 3108 # 単語片が補正されている時、p も補正する 3109 if [[ $p && $word != "$1" ]]; then 3110 if ((p==${#1})); then 3111 p=${#word} 3112 else 3113 local left=${word::p} 3114 if [[ $is_evaluated ]]; then 3115 if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$left"; then 3116 ble/complete/progcomp/.compvar-eval-word "$ret"; (($?==148)) && return 148; left=$ret 3117 fi 3118 fi 3119 if [[ $is_quoted ]]; then 3120 left="'${left//$q/$Q}" left=${left#"$qq"} 3121 fi 3122 p=${#left} 3123 fi 3124 fi 3125 3126 ret=$word 3127 } 3128 3129 ## @fn ble/complete/progcomp/.compvar-reduce-cur current_subword 3130 ## @param[in] current_subword 3131 ## @var[out] cur 3132 builtin unset -v _ble_complete_progcomp_cur_wordbreaks 3133 _ble_complete_progcomp_cur_rex_simple= 3134 _ble_complete_progcomp_cur_rex_break= 3135 function ble/complete/progcomp/.compvar-reduce-cur { 3136 # 正規表現の更新 3137 if [[ ! ${_ble_complete_progcomp_cur_wordbreaks+set} || $COMP_WORDBREAKS != "$_ble_complete_progcomp_cur_wordbreaks" ]]; then 3138 _ble_complete_progcomp_cur_wordbreaks=$COMP_WORDBREAKS 3139 _ble_complete_progcomp_cur_rex_simple='^([^\"'\'']|\\.|"([^\"]|\\.)*"|'\''[^'\'']*'\'')*' 3140 local chars=${COMP_WORDBREAKS//[\'\"]/} rex_break= 3141 [[ $chars == *\\* ]] && chars=${chars//\\/} rex_break='\\(.|$)' 3142 [[ $chars == *\$* ]] && chars=${chars//\$/} rex_break+=${rex_break:+'|'}'\$([^$'\'${rex_break:+\\}']|$)' 3143 if [[ $chars == '^' ]]; then 3144 rex_break+=${rex_break:+'|'}'\^' 3145 elif [[ $chars ]]; then 3146 [[ $chars == ?*']'* ]] && chars=']'${chars//']'/} 3147 [[ $chars == '^'* ]] && chars=${chars:1}${chars::1} 3148 [[ $chars == *'-'*? ]] && chars=${chars//'-'/}'-' 3149 rex_break+=${rex_break:+'|'}[$chars] 3150 fi 3151 _ble_complete_progcomp_cur_rex_break='^([^\"'\''$]|\$*\\.|\$*"([^\"]|\\.)*"|'\''[^'\'']*'\''|\$+'\''([^'\''\]|\\.)*'\''|\$+([^'\'']|$))*\$*('${rex_break:-'^$'}')' 3152 fi 3153 3154 cur=$1 3155 if [[ $cur =~ $_ble_complete_progcomp_cur_rex_simple && ${cur:${#BASH_REMATCH}} == [\'\"]* ]]; then 3156 cur=${cur:${#BASH_REMATCH}+1} 3157 elif [[ $cur =~ $_ble_complete_progcomp_cur_rex_break ]]; then 3158 cur=${cur:${#BASH_REMATCH}} 3159 case ${BASH_REMATCH[5]} in (\$*|@|\\?) cur=${BASH_REMATCH[5]#\\}$cur ;; esac 3160 fi 3161 } 3162 3163 ## @fn ble/complete/progcomp/.compvar-initialize 3164 ## プログラム補完で提供される変数を構築します。 3165 ## @var[in] comp_words comp_cword comp_line comp_point 3166 ## @var[out] COMP_WORDS COMP_CWORD COMP_LINE COMP_POINT COMP_KEY COMP_TYPE 3167 ## @var[out] cmd cur prev 3168 ## 補完関数に渡す引数を格納します。cmd は COMP_WORDBREAKS による分割前のコ 3169 ## マンド名を保持します。cur は現在の単語のカーソル前の部分を保持します。但 3170 ## し、閉じていない引用符がある時は引用符の中身を、COMP_WORDBREAKS の文字が 3171 ## 含まれる場合にはそれによって分割された後の最後の単語を返します。 3172 ## @var[out] progcomp_prefix 3173 function ble/complete/progcomp/.compvar-initialize { 3174 COMP_TYPE=9 3175 COMP_KEY=9 3176 ((${#KEYS[@]})) && COMP_KEY=${KEYS[${#KEYS[@]}-1]:-9} # KEYS defined in ble-decode/widget/.call-keyseq 3177 3178 # Note: 以降の処理は基本的には comp_words, comp_line, comp_point, comp_cword を 3179 # COMP_WORDS COMP_LINE COMP_POINT COMP_CWORD にコピーする。 3180 # (1) 但し、直接代入する場合。$'' などがあると bash-completion が正しく動かないので、 3181 # エスケープを削除して適当に処理する。 3182 # (2) シェルの特殊文字以外の COMP_WORDBREAKS に含まれる文字で単語を分割する。 3183 3184 local wordbreaks 3185 ble/complete/progcomp/.compvar-initialize-wordbreaks 3186 3187 progcomp_prefix= 3188 COMP_CWORD= 3189 COMP_POINT= 3190 COMP_LINE= 3191 COMP_WORDS=() 3192 cmd=${comp_words[0]} 3193 cur= prev= 3194 local ret simple_flags simple_ibrace 3195 local word1 index=0 offset=0 sep= 3196 for word1 in "${comp_words[@]}"; do 3197 # @var offset_dst 3198 # 現在の単語の COMP_LINE 内部に於ける開始位置 3199 local offset_dst=${#COMP_LINE} 3200 # @var point 3201 # word が現在の単語の時、word 内のカーソル位置を保持する。 3202 # それ以外の時は空文字列。 3203 local point=$((comp_point-offset)) 3204 ((0<=point&&point<=${#word1})) || point= 3205 ((offset+=${#word1})) 3206 3207 local words subword_flags= 3208 ble/complete/progcomp/.compvar-generate-subwords "$word1" 3209 3210 local w wq i=0 o=0 p 3211 for w in "${words[@]}"; do 3212 # @var p 3213 # 現在の単語片の内部におけるカーソルの位置。 3214 # 現在の単語片の内部にカーソルがない場合は空文字列。 3215 p= 3216 if [[ $point ]]; then 3217 ((p=point-o)) 3218 # Note: #D1094 境界上にいる場合には偶数番目の単語片 3219 # (非 wordbreaks) に属させる。 3220 ((i%2==0?p<=${#w}:p<${#w})) || p= 3221 ((o+=${#w},i++)) 3222 fi 3223 # カーソルが subword の境界にある時は左側の subword に属させる。 3224 # 右側の subword で処理が行われない様に point をクリア。 3225 [[ $p ]] && point= 3226 [[ $point ]] && progcomp_prefix=$progcomp_prefix$w 3227 3228 # Note: w -> wq の修正に伴ってここで p も修正される。 3229 ble/complete/progcomp/.compvar-quote-subword "$w"; local wq=$ret 3230 3231 # 単語登録 3232 if [[ $p ]]; then 3233 COMP_CWORD=${#COMP_WORDS[*]} 3234 ((COMP_POINT=${#COMP_LINE}+${#sep}+p)) 3235 ble/complete/progcomp/.compvar-reduce-cur "${COMP_LINE:offset_dst}${wq::p}" 3236 prev=${COMP_WORDS[COMP_CWORD-1]} 3237 fi 3238 ble/array#push COMP_WORDS "$wq" 3239 COMP_LINE=$COMP_LINE$sep$wq 3240 sep= 3241 done 3242 3243 sep=' ' 3244 ((offset++)) 3245 ((index++)) 3246 done 3247 } 3248 function ble/complete/progcomp/.compgen-helper-prog { 3249 if [[ $comp_prog ]]; then 3250 local COMP_WORDS COMP_CWORD cmd cur prev 3251 local -x COMP_LINE COMP_POINT COMP_TYPE COMP_KEY 3252 ble/complete/progcomp/.compvar-initialize 3253 3254 if [[ $comp_opts == *:ble/prog-trim:* ]]; then 3255 # WA: aws_completer 3256 local compreply 3257 ble/util/assign compreply '"$comp_prog" "$cmd" "$cur" "$prev" < /dev/null' 3258 ble/bin/sed "s/[[:space:]]\{1,\}\$//" <<< "$compreply" 3259 else 3260 "$comp_prog" "$cmd" "$cur" "$prev" < /dev/null 3261 fi 3262 fi 3263 } 3264 ## @fn ble/complete/progcomp/compopt [-o OPTION|+o OPTION] 3265 ## compopt を上書きして -o/+o option を読み取る為の関数です。 3266 ## 3267 ## OPTION 3268 ## ble/syntax-raw 3269 ## 生成した候補をそのまま挿入する事を示します。 3270 ## 3271 ## ble/no-default 3272 ## ble.sh の既定の候補生成 (候補が生成されなかった時の既定の候補生成、お 3273 ## よび、sabbrev 候補生成) を抑制します。 3274 ## 3275 function ble/complete/progcomp/compopt { 3276 # Note: Bash補完以外から builtin compopt を呼び出しても 3277 # エラーになるので呼び出さない事にした (2019-02-05) 3278 #builtin compopt "$@" 2>/dev/null; local ext=$? 3279 local ext=0 3280 3281 local -a ospec 3282 while (($#)); do 3283 local arg=$1; shift 3284 case $arg in 3285 (-*) 3286 local ic c 3287 for ((ic=1;ic<${#arg};ic++)); do 3288 c=${arg:ic:1} 3289 case $c in 3290 (o) ospec[${#ospec[@]}]="-$1"; shift ;; 3291 ([DE]) fDefault=1; break 2 ;; 3292 (*) ((ext==0&&(ext=1))) ;; 3293 esac 3294 done ;; 3295 (+o) ospec[${#ospec[@]}]="+$1"; shift ;; 3296 (*) 3297 # 特定のコマンドに対する compopt 指定 3298 return "$ext" ;; 3299 esac 3300 done 3301 3302 local s 3303 for s in "${ospec[@]}"; do 3304 case $s in 3305 (-*) comp_opts=${comp_opts//:"${s:1}":/:}${s:1}: ;; 3306 (+*) comp_opts=${comp_opts//:"${s:1}":/:} ;; 3307 esac 3308 done 3309 3310 return "$ext" 3311 } 3312 function ble/complete/progcomp/.check-limits { 3313 # user-input check 3314 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && 3315 [[ ! -t 0 ]] && ble/complete/check-cancel <&"$_ble_util_fd_stdin" && 3316 return 148 3317 ble/complete/source/test-limit "$((progcomp_read_count++))" 3318 return "$?" 3319 } 3320 function ble/complete/progcomp/.compgen-helper-func { 3321 [[ $comp_func ]] || return 1 3322 local -a COMP_WORDS 3323 local COMP_LINE COMP_POINT COMP_CWORD COMP_TYPE COMP_KEY cmd cur prev 3324 ble/complete/progcomp/.compvar-initialize 3325 3326 local progcomp_read_count=0 3327 local _ble_builtin_read_hook='ble/complete/progcomp/.check-limits || { ble/bash/read "$@" < /dev/null; return 148; }' 3328 3329 local fDefault= 3330 ble/function#push compopt 'ble/complete/progcomp/compopt "$@"' 3331 3332 # WA (#D1807): A workaround for blocking scp/ssh 3333 ble/function#push ssh ' 3334 local IFS=$_ble_term_IFS 3335 if [[ " ${FUNCNAME[*]} " == *" ble/complete/progcomp/.compgen "* ]]; then 3336 local -a args; args=("$@") 3337 ble/util/conditional-sync "exec ssh \"\${args[@]}\"" \ 3338 "! ble/complete/check-cancel <&$_ble_util_fd_stdin" 128 progressive-weight:killall 3339 else 3340 ble/function#push/call-top "$@" 3341 fi' 3342 3343 # WA (#D1834): Suppress invocation of "command_not_found_handle" in the 3344 # completion functions 3345 ble/function#push command_not_found_handle 3346 3347 builtin eval '"$comp_func" "$cmd" "$cur" "$prev"' < /dev/null >&"$_ble_util_fd_stdout" 2>&"$_ble_util_fd_stderr"; local ret=$? 3348 3349 ble/function#pop command_not_found_handle 3350 ble/function#pop ssh 3351 ble/function#pop compopt 3352 3353 [[ $ret == 124 ]] && progcomp_retry=1 3354 return 0 3355 } 3356 3357 ## @fn ble/complete/progcomp/.parse-complete/next 3358 ## @var[out] optarg 3359 ## @var[in,out] compdef 3360 ## @var[in] rex 3361 function ble/complete/progcomp/.parse-complete/next { 3362 if [[ $compdef =~ $rex ]]; then 3363 builtin eval "arg=$BASH_REMATCH" 3364 compdef=${compdef:${#BASH_REMATCH}} 3365 return 0 3366 elif [[ ${compdef%%' '*} ]]; then 3367 # 本来此処には来ない筈 3368 arg=${compdef%%' '*} 3369 compdef=${compdef#*' '} 3370 return 0 3371 else 3372 return 1 3373 fi 3374 } 3375 function ble/complete/progcomp/.parse-complete/optarg { 3376 optarg= 3377 if ((ic+1<${#arg})); then 3378 optarg=${arg:ic+1} 3379 ic=${#arg} 3380 return 0 3381 elif [[ $compdef =~ $rex ]]; then 3382 builtin eval "optarg=$BASH_REMATCH" 3383 compdef=${compdef:${#BASH_REMATCH}} 3384 return 0 3385 else 3386 return 2 3387 fi 3388 } 3389 ## @fn ble/complete/progcomp/.parse-complete compdef 3390 ## @param[in] compdef 3391 ## @var[in,out] comp_opts 3392 ## @var[out] compoptions comp_prog comp_func flag_noquote 3393 function ble/complete/progcomp/.parse-complete { 3394 compoptions=() 3395 comp_prog= 3396 comp_func= 3397 flag_noquote= 3398 local compdef=${1#'complete '} 3399 3400 local arg optarg rex='^([^][*?;&|[:space:]<>()\`$"'\''{}#^!]|\\.|'\''[^'\'']*'\'')+[[:space:]]+' # #D1709 safe (WA gawk 4.0.2) 3401 while ble/complete/progcomp/.parse-complete/next; do 3402 case $arg in 3403 (-*) 3404 local ic c 3405 for ((ic=1;ic<${#arg};ic++)); do 3406 c=${arg:ic:1} 3407 case $c in 3408 ([abcdefgjksuvE]) 3409 # Note: workaround #D0714 #M0009 #D0870 3410 case $c in 3411 (c) flag_noquote=1 ;; 3412 (d) ((_ble_bash>=40300)) && flag_noquote=1 ;; 3413 (f) ((40000<=_ble_bash&&_ble_bash<40200)) && flag_noquote=1 ;; 3414 esac 3415 ble/array#push compoptions "-$c" ;; 3416 ([pr]) 3417 ;; # 無視 (-p 表示 -r 削除) 3418 ([AGWXPS]) 3419 # Note: workaround #D0714 #M0009 #D0870 3420 ble/complete/progcomp/.parse-complete/optarg || break 2 3421 if [[ $c == A ]]; then 3422 case $optarg in 3423 (command) flag_noquote=1 ;; 3424 (directory) ((_ble_bash>=40300)) && flag_noquote=1 ;; 3425 (file) ((40000<=_ble_bash&&_ble_bash<40200)) && flag_noquote=1 ;; 3426 esac 3427 fi 3428 ble/array#push compoptions "-$c" "$optarg" ;; 3429 (o) 3430 ble/complete/progcomp/.parse-complete/optarg || break 2 3431 comp_opts=${comp_opts//:"$optarg":/:}$optarg: 3432 ble/array#push compoptions "-$c" "$optarg" ;; 3433 (C) 3434 if ((_ble_bash<40000)); then 3435 # bash-3.2以下では -C は一番最後に出力される (unquoted) 3436 comp_prog=${compdef%' '} 3437 compdef= 3438 else 3439 # bash-4.0以降では -C は quoted 3440 ble/complete/progcomp/.parse-complete/optarg || break 2 3441 comp_prog=$optarg 3442 fi 3443 ble/array#push compoptions "-$c" ble/complete/progcomp/.compgen-helper-prog ;; 3444 (F) 3445 # unquoted optarg (bash-3.2 以下では続きに unquoted -C prog が来得る) 3446 if ((_ble_bash<40000)) && [[ $compdef == *' -C '* ]]; then 3447 comp_prog=${compdef#*' -C '} 3448 comp_prog=${comp_prog%' '} 3449 ble/array#push compoptions '-C' ble/complete/progcomp/.compgen-helper-prog 3450 comp_func=${compdef%%' -C '*} 3451 else 3452 comp_func=${compdef%' '} 3453 fi 3454 compdef= 3455 3456 ble/array#push compoptions "-$c" ble/complete/progcomp/.compgen-helper-func ;; 3457 (*) 3458 # -D, -I, etc. just discard 3459 esac 3460 done ;; 3461 (*) 3462 ;; # 無視 3463 esac 3464 done 3465 } 3466 3467 ## @fn ble/complete/progcomp/.filter-and-split-compgen arr 3468 ## filter/sort/uniq candidates 3469 ## 3470 ## @var[out] $arr, flag_mandb 3471 ## @var[in] compgen 3472 ## @var[in] COMPV compcmd comp_words 3473 ## @var[in] comp_opts use_workaround_for_git 3474 function ble/complete/progcomp/.filter-and-split-compgen { 3475 flag_mandb= 3476 3477 # 1. sed (sort 前処理) 3478 local sed_script= 3479 { 3480 # $comp_opts == *:ble/filter-by-prefix:* 3481 # 3482 # Note: "$COMPV" で始まる単語だけを sed /^$rex_compv/ でフィルタする。 3483 # それで候補が一つもなくなる場合にはフィルタ無しで単語を列挙する。 3484 # 3485 # 2019-02-03 実は、現在の実装ではわざわざフィルタする必要はないかもしれない。 3486 # 以前 compgen に -- "$COMPV" を渡してもフィルタしてくれなかったのは、 3487 # #D0245 cdd38598 で ble/complete/progcomp/.compgen-helper-func に於いて、 3488 # "$comp_func" に引数を渡し忘れていたのが原因と思われる。 3489 # これは 1929132b に於いて修正されたが念のためにフィルタを残していた気がする。 3490 if [[ $comp_opts == *:ble/filter-by-prefix:* ]]; then 3491 local ret; ble/string#escape-for-sed-regex "$COMPV"; local rex_compv=$ret 3492 sed_script='!/^'$rex_compv'/d' 3493 fi 3494 3495 [[ $use_workaround_for_git ]] && 3496 sed_script=${sed_script:+$sed_script;}'s/[[:space:]]\{1,\}$//' 3497 } 3498 local out= 3499 [[ $sed_script ]] && ble/util/assign out 'ble/bin/sed "$sed_script;/^\$/d" <<< "$compgen"' 3500 [[ $out ]] || out=$compgen 3501 3502 # 2. sort 3503 local require_awk= 3504 if [[ $comp_opts != *:nosort:* ]]; then 3505 ble/util/assign out 'ble/bin/sort -u <<< "$out"' 3506 else 3507 require_awk=1 # for uniq 3508 fi 3509 3510 # 3. awk (sort 後処理) 3511 local -a args_mandb=() 3512 if [[ $compcmd == "${comp_words[0]}" && $COMPV != [!-]* ]]; then 3513 if local ret; ble/complete/mandb/generate-cache "$compcmd"; then 3514 require_awk=1 3515 args_mandb=(mode=mandb "$ret") 3516 fi 3517 fi 3518 if [[ $require_awk ]]; then 3519 local awk_script=' 3520 BEGIN { mandb_count = 0; } 3521 mode == "mandb" { 3522 name = $0 3523 sub(/'"$_ble_term_FS"'.*/, "", name); 3524 if (!mandb[name]) mandb[name] = $0; 3525 next; 3526 } 3527 3528 function register_mandb_entry(name, display, entry) { 3529 if (name2index[name] != "") { 3530 # Remove duplicates after removing trailing /=$/. If the new 3531 # "display" is longer, overwrite the existing one. 3532 if (length(display) <= length(name2display[name])) return; 3533 name2display[name] = display; 3534 entries[name2index[name]] = entry; 3535 } else { 3536 name2index[name] = mandb_count; 3537 name2display[name] = display; 3538 entries[mandb_count++] = entry; 3539 } 3540 } 3541 3542 !hash[$0]++ { 3543 if (/^$/) next; 3544 3545 name = $0 3546 sub(/=$/, "", name); 3547 if (mandb[name]) { 3548 register_mandb_entry(name, $0, mandb[name]); 3549 next; 3550 } else if (sub(/^--no-/, "--", name)) { 3551 3552 # Synthesize description of "--no-OPTION" 3553 if ((entry = mandb[name]) || (entry = mandb[substr(name, 2)])) { 3554 split(entry, record, FS); 3555 if ((desc = record[4])) { 3556 desc = "\033[1mReverse[\033[m " desc " \033[;1m]\033[m"; 3557 if (match($0, /['"$_ble_term_space"']*[:=[]/)) { 3558 option = substr($0, 1, RSTART - 1); 3559 optarg = substr($0, RSTART); 3560 suffix = substr($0, RSTART, 1); 3561 if (suffix == "[") suffix = ""; 3562 } else { 3563 option = $0; 3564 optarg = ""; 3565 suffix = " "; 3566 } 3567 register_mandb_entry(name, $0, option FS optarg FS suffix FS desc); 3568 } 3569 next; 3570 } 3571 3572 } 3573 3574 print $0; 3575 } 3576 3577 END { 3578 if (mandb_count) { 3579 for (i = 0; i < mandb_count; i++) 3580 print entries[i]; 3581 exit 10; 3582 } 3583 } 3584 ' 3585 ble/util/assign-array "$1" 'ble/bin/awk -F "$_ble_term_FS" "$awk_script" "${args_mandb[@]}" mode=compgen - <<< "$out"' 3586 (($?==10)) && flag_mandb=1 3587 else 3588 ble/string#split-lines "$1" "$out" 3589 fi 3590 return 0 3591 } 2>/dev/null 3592 3593 function ble/complete/progcomp/.cobraV2.patch { 3594 local cobra_version=$1 3595 if ((cobra_version<10500)); then 3596 local -a completions 3597 completions=("${out[@]}") 3598 fi 3599 3600 local prefix=$cur 3601 [[ $comps_flags == *v* ]] && prefix=$COMPV 3602 local unprocessed has_desc= 3603 unprocessed=() 3604 local lines line cand desc 3605 for lines in "${out[@]}"; do 3606 ble/string#split-lines lines "$lines" 3607 for line in "${lines[@]}"; do 3608 if [[ $line == *$'\t'* ]]; then 3609 cand=${line%%$'\t'*} 3610 desc=${line#*$'\t'} 3611 [[ $cand == "$prefix"* ]] || continue 3612 ble/complete/cand/yield word "$cand" "$desc" 3613 has_desc=1 3614 elif [[ $line ]]; then 3615 ble/array#push unprocessed "$line" 3616 fi 3617 done 3618 done 3619 3620 [[ $has_desc ]] && bleopt complete_menu_style=desc 3621 if ((${#unprocessed[@]})); then 3622 if ((cobra_version>=10500)); then 3623 completions=("${unprocessed[@]}") 3624 else 3625 out=("${unprocessed[@]}") 3626 fi 3627 ble/function#advice/do 3628 fi 3629 } 3630 3631 ## @fn ble/complete/progcomp/.compgen opts 3632 ## 3633 ## @param[in] opts 3634 ## コロン区切りのオプションリストです。 3635 ## 3636 ## initial ... 最初の単語 (コマンド名) の補完に用いる関数を指定します。 3637 ## 3638 ## @param[in,opt] cmd 3639 ## プログラム補完規則を検索するのに使う名前を指定します。 3640 ## 省略した場合 ${comp_words[0]} が使われます。 3641 ## 3642 ## @var[out] comp_opts 3643 ## 3644 ## @var[in] COMP1 COMP2 COMPV COMPS comp_type 3645 ## ble/complete/source の標準的な変数たち。 3646 ## 3647 ## @var[in] comp_words comp_line comp_point comp_cword 3648 ## ble/syntax:bash/extract-command によって生成される変数たち。 3649 ## 3650 ## @var[in] 他色々 3651 ## @exit 入力がある時に 148 を返します。 3652 function ble/complete/progcomp/.compgen { 3653 local opts=$1 3654 3655 local compcmd= is_special_completion= 3656 local -a alias_args=() 3657 if [[ :$opts: == *:initial:* ]]; then 3658 if ((_ble_bash>=50000)); then 3659 is_special_completion=1 3660 compcmd='-I' 3661 else 3662 compcmd=_InitialWorD_ 3663 fi 3664 elif [[ :$opts: == *:default:* ]]; then 3665 if ((_ble_bash>=40100)); then 3666 builtin complete -p -D &>/dev/null || return 1 3667 is_special_completion=1 3668 compcmd='-D' 3669 else 3670 builtin complete -p _DefaultCmD_ &>/dev/null || return 1 3671 compcmd=_DefaultCmD_ 3672 fi 3673 else 3674 compcmd=${comp_words[0]} 3675 fi 3676 3677 local compdef 3678 if [[ $is_special_completion ]]; then 3679 # -I, -D, etc. 3680 ble/util/assign compdef 'builtin complete -p "$compcmd" 2>/dev/null' 3681 elif ble/syntax:bash/simple-word/is-simple "$compcmd"; then 3682 # 既に呼び出し元で quote されている想定 3683 ble/util/assign compdef "builtin complete -p -- $compcmd 2>/dev/null" 3684 local ret; ble/syntax:bash/simple-word/eval "$compcmd"; compcmd=$ret 3685 else 3686 ble/util/assign compdef 'builtin complete -p -- "$compcmd" 2>/dev/null' 3687 fi 3688 # strip -I, -D, or command_name 3689 # #D1579 bash-5.1 では空コマンドに限り '' と出力する様である。 3690 compdef=${compdef%"${compcmd:-''}"} 3691 compdef=${compdef%' '}' ' 3692 3693 local comp_prog comp_func compoptions flag_noquote 3694 ble/complete/progcomp/.parse-complete "$compdef" 3695 3696 # WA: Workarounds for third-party plugins 3697 if [[ $comp_func ]]; then 3698 # fzf 3699 [[ $comp_func == _fzf_* ]] && 3700 ble-import -f contrib/integration/fzf-completion 3701 3702 # bash_completion 3703 if ble/is-function _comp_initialize; then 3704 # bash-completion 2.12 3705 ble/complete/mandb:bash-completion/inject 3706 elif ble/is-function _quote_readline_by_ref; then 3707 # https://github.com/scop/bash-completion/pull/492 (fixed in bash-completion 2.12) 3708 function _quote_readline_by_ref { 3709 if [[ $1 == \'* ]]; then 3710 printf -v "$2" %s "${1:1}" 3711 else 3712 printf -v "$2" %q "$1" 3713 [[ ${!2} == \$* ]] && builtin eval "$2=${!2}" 3714 fi 3715 } 3716 ble/function#suppress-stderr _filedir 2>/dev/null 3717 3718 # https://github.com/scop/bash-completion/issues/509 (fixed in bash-completion 2.12) 3719 ble/function#suppress-stderr _find 2>/dev/null 3720 3721 # https://github.com/scop/bash-completion/pull/556 (fixed in bash-completion 2.12) 3722 ble/function#suppress-stderr _scp_remote_files 2>/dev/null 3723 3724 # https://github.com/scop/bash-completion/pull/773 (fixed in bash-completion 2.12) 3725 ble/function#suppress-stderr _function 2>/dev/null 3726 3727 ble/complete/mandb:bash-completion/inject 3728 fi 3729 3730 # cobra GenBashCompletionV2 3731 if [[ $comp_func == __start_* ]]; then 3732 local target=__${comp_func#__start_}_handle_completion_types 3733 if ble/is-function "$target"; then 3734 local cobra_version= 3735 if ble/is-function "__${comp_func#__start_}_extract_activeHelp"; then 3736 cobra_version=10500 # v1.5.0 (Release 2022-06-21) 3737 fi 3738 ble/function#advice around "$target" "ble/complete/progcomp/.cobraV2.patch $cobra_version" 3739 fi 3740 fi 3741 3742 # WA for dnf completion 3743 ble/function#advice around _dnf_commands_helper ' 3744 ble/util/conditional-sync \ 3745 ble/function#advice/do \ 3746 "! ble/complete/check-cancel <&$_ble_util_fd_stdin" 128 progressive-weight:killall' 2>/dev/null 3747 3748 # WA for zoxide TAB 3749 if [[ $comp_func == _z ]]; then 3750 ble-import -f contrib/integration/zoxide 3751 ble/contrib/integration:zoxide/adjust 3752 fi 3753 3754 # WA for _complete_nix 3755 if [[ $comp_func == _complete_nix ]]; then 3756 ble-import -f integration/nix-completion 3757 ble/contrib/integration:nix-completion/adjust 3758 fi 3759 3760 # https://github.com/akinomyoga/ble.sh/issues/292 (Android Debug Bridge) 3761 ble/function#suppress-stderr _adb 2>/dev/null 3762 fi 3763 if [[ $comp_prog ]]; then 3764 # aws 3765 if [[ $comp_prog == aws_completer ]]; then 3766 comp_opts=${comp_opts}ble/no-mark-directories:ble/prog-trim: 3767 fi 3768 fi 3769 3770 3771 ble/complete/check-cancel && return 148 3772 3773 # Note: 一旦 compgen だけで ble/util/assign するのは、compgen をサブシェルではなく元のシェルで評価する為である。 3774 # 補完関数が遅延読込になっている場合などに、読み込まれた補完関数が次回から使える様にする為に必要である。 3775 local compgen compgen_compv=$COMPV 3776 if [[ ! $flag_noquote && :$comp_opts: != *:noquote:* ]]; then 3777 local q="'" Q="'\''" 3778 compgen_compv="'${compgen_compv//$q/$Q}'" 3779 fi 3780 # WA #D1682: libvirt の virsh 用の補完が勝手に変数 IFS 及び word を書き換えて 3781 # そのまま放置して抜けてしまう。仕方がないので tmpenv で変数の内容を復元する 3782 # 事にする。 3783 local progcomp_prefix= progcomp_retry= 3784 IFS=$IFS word= ble/util/assign compgen 'builtin compgen "${compoptions[@]}" -- "$compgen_compv" 2>/dev/null' 3785 3786 # Note #D0534: complete -D 補完仕様に従った補完関数が 124 を返したとき再度始 3787 # めから補完を行う。ble/complete/progcomp/.compgen-helper-func 関数内で補間 3788 # 関数の終了ステータスを確認し、もし 124 だった場合には 3789 # progcomp_retry に retry を設定する。 3790 # Note #D1760: complete -D 以外の時でも 124 が返された時再試行する。 3791 if [[ $progcomp_retry && ! $_ble_complete_retry_guard ]]; then 3792 local _ble_complete_retry_guard=1 3793 opts=:$opts: 3794 opts=${opts//:default:/:} 3795 ble/complete/progcomp/.compgen "$opts" 3796 return "$?" 3797 fi 3798 3799 [[ $compgen ]] || return 1 3800 3801 # WA: git の補完関数など勝手に末尾に space をつけ -o nospace を指定する物が存在する。 3802 # 単語の後にスペースを挿入する事を意図していると思われるが、 3803 # 通常 compgen (例: compgen -f) で生成される候補に含まれるスペースは、 3804 # 挿入時のエスケープ対象であるので末尾の space もエスケープされてしまう。 3805 # 3806 # 仕方がないので sed で各候補の末端の [[:space:]]+ を除去する。 3807 # これだとスペースで終わるファイル名を挿入できないという実害が発生するが、 3808 # そのような変な補完関数を作るのが悪いのである。 3809 local use_workaround_for_git= 3810 if [[ $comp_func == __git* && $comp_opts == *:nospace:* ]]; then 3811 use_workaround_for_git=1 3812 comp_opts=${comp_opts//:nospace:/:} 3813 fi 3814 3815 local cands flag_mandb= 3816 ble/complete/progcomp/.filter-and-split-compgen cands # compgen (comp_opts, etc) -> cands, flag_mandb 3817 3818 ble/complete/source/test-limit "${#cands[@]}" || return 1 3819 3820 # determine COMP_PREFIX for filenames 3821 if [[ $comp_opts == *:filenames:* ]]; then 3822 if [[ $comp_opts == *:ble/syntax-raw:* ]]; then 3823 [[ $COMPS == */* ]] && COMP_PREFIX=${COMPS%/*}/ 3824 else 3825 [[ $COMPV == */* ]] && COMP_PREFIX=${COMPV%/*}/ 3826 fi 3827 fi 3828 3829 local old_cand_count=$cand_count 3830 3831 local action=progcomp "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 3832 ble/complete/cand/yield.initialize "$action" 3833 if [[ $flag_mandb ]]; then 3834 local -a entries; entries=("${cands[@]}") 3835 cands=() 3836 local fs=$_ble_term_FS has_desc= icand=0 entry 3837 for entry in "${entries[@]}"; do 3838 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 3839 if [[ $entry == -*"$fs"*"$fs"*"$fs"* ]]; then 3840 local cand=${entry%%"$fs"*} 3841 ble/complete/cand/yield mandb "$cand" "$entry" 3842 [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 3843 else 3844 cands[icand++]=$progcomp_prefix$entry 3845 fi 3846 done 3847 [[ $has_desc ]] && bleopt complete_menu_style=desc 3848 else 3849 [[ $progcomp_prefix ]] && 3850 if ((_ble_bash>=40300)) && ! shopt -q compat42; then 3851 cands=("${cands[@]/#/"$progcomp_prefix"}") # WA #D1570 #D1751 checked 3852 else 3853 cands=("${cands[@]/#/$progcomp_prefix}") # WA #D1570 #D1738 checked 3854 fi 3855 fi 3856 ble/complete/cand/yield.batch "$action" "$comp_opts" 3857 3858 # plusdirs の時はディレクトリ名も候補として列挙 3859 # Note: 重複候補や順序については考えていない 3860 [[ $comp_opts == *:plusdirs:* ]] && ble/complete/source:dir 3861 3862 ((cand_count>old_cand_count)) 3863 } 3864 3865 ## @fn ble/complete/progcomp/.compline-rewrite-command cmd [args...] 3866 ## alias 展開等によるコマンド名の変更に対応して、 3867 ## 補完対象のコマンド名を指定の物に書き換えます。 3868 ## 3869 ## @var[in,out] comp_line comp_words comp_point comp_cword 3870 ## 3871 function ble/complete/progcomp/.compline-rewrite-command { 3872 local ocmd=${comp_words[0]} 3873 [[ $1 != "$ocmd" ]] || (($#>=2)) || return 1 3874 local IFS=$_ble_term_IFS 3875 local ins="$*" 3876 if (($#==0)); then 3877 # コマンド除去 (aliasで空に展開された時) 3878 local ret; ble/string#ltrim "${comp_line:${#ocmd}}" 3879 ((comp_point-=${#comp_line}-${#ret})) 3880 comp_line=$ret 3881 else 3882 comp_line=$ins${comp_line:${#ocmd}} 3883 ((comp_point-=${#ocmd})) 3884 fi 3885 ((comp_point<0&&(comp_point=0),comp_point+=${#ins})) 3886 comp_words=("$@" "${comp_words[@]:1}") 3887 ((comp_cword&&(comp_cword+=$#-1))) 3888 } 3889 3890 function ble/complete/progcomp/.split-alias-words { 3891 local tail=$1 3892 local rex_redir='^'$_ble_syntax_bash_RexRedirect 3893 local rex_word='^'$_ble_syntax_bash_simple_rex_element'+' 3894 local rex_delim=$'^[\n;|&]' 3895 local rex_spaces=$'^[ \t]+' 3896 local rex_misc='^[<>()]+' 3897 3898 local -a words=() 3899 while [[ $tail ]]; do 3900 if [[ $tail =~ $rex_redir && $tail != ['<>']'('* ]]; then 3901 ble/array#push words "$BASH_REMATCH" 3902 tail=${tail:${#BASH_REMATCH}} 3903 elif [[ $tail =~ $rex_word ]]; then 3904 local w=$BASH_REMATCH 3905 tail=${tail:${#w}} 3906 if [[ $tail && $tail != ["$_ble_term_IFS;|&<>()"]* ]]; then 3907 local s=${tail%%["$_ble_term_IFS"]*} 3908 tail=${tail:${#s}} 3909 w=$w$s 3910 fi 3911 ble/array#push words "$w" 3912 elif [[ $tail =~ $rex_delim ]]; then 3913 words=() 3914 tail=${tail:${#BASH_REMATCH}} 3915 elif [[ $tail =~ $rex_spaces ]]; then 3916 tail=${tail:${#BASH_REMATCH}} 3917 elif [[ $tail =~ $rex_misc ]]; then 3918 ble/array#push words "$BASH_REMATCH" 3919 tail=${tail:${#BASH_REMATCH}} 3920 else 3921 local w=${tail%%["$_ble_term_IFS"]*} 3922 ble/array#push words "$w" 3923 tail=${tail:${#w}} 3924 fi 3925 done 3926 3927 # skip assignments/redirections 3928 local i=0 rex_assign='^[_a-zA-Z0-9]+(\['$_ble_syntax_bash_simple_rex_element'*\])?\+?=' 3929 while ((i<${#words[@]})); do 3930 if [[ ${words[i]} =~ $rex_assign ]]; then 3931 ((i++)) 3932 elif [[ ${words[i]} =~ $rex_redir && ${words[i]} != ['<>']'('* ]]; then 3933 ((i+=2)) 3934 else 3935 break 3936 fi 3937 done 3938 3939 ret=("${words[@]:i}") 3940 } 3941 3942 ## @fn ble/complete/progcomp/.try-load-completion cmd 3943 ## bash-completion の loader を呼び出して遅延補完設定をチェックする。 3944 function ble/complete/progcomp/.try-load-completion { 3945 ble/is-function __load_completion || return 1 3946 3947 ble/function#push command_not_found_handle 3948 __load_completion "$1" < /dev/null &>/dev/null; local ext=$? 3949 ble/function#pop command_not_found_handle 3950 ((ext==0)) || return "$ext" 3951 3952 builtin complete -p -- "$1" &>/dev/null 3953 } 3954 3955 ## @fn ble/complete/progcomp cmd opts 3956 ## 補完指定を検索して対応する補完関数を呼び出します。 3957 ## @var[in] comp_line comp_words comp_point comp_cword 3958 function ble/complete/progcomp { 3959 local cmd=${1-${comp_words[0]}} opts=$2 3960 3961 # copy compline variables 3962 local orig_comp_words orig_comp_cword=$comp_cword orig_comp_line=$comp_line orig_comp_point=$comp_point 3963 orig_comp_words=("${comp_words[@]}") 3964 local comp_words comp_cword=$comp_cword comp_line=$comp_line comp_point=$comp_point 3965 comp_words=("${orig_comp_words[@]}") 3966 [[ $cmd == "${orig_comp_words[0]}" ]] || 3967 ble/complete/progcomp/.compline-rewrite-command "$cmd" 3968 3969 local orig_qcmds_set= 3970 local -a orig_qcmds=() 3971 local -a alias_args=() 3972 [[ :$opts: == *:__recursive__:* ]] || 3973 local alias_checked=' ' 3974 while :; do 3975 3976 # @var cmd ... 元のコマンド名 3977 # @var ucmd ... simple-word/eval したコマンド名 3978 # @var qcmds ... simple-word/eval x quote-word したコマンド 3979 local ret ucmd qcmds 3980 ucmd=$cmd qcmds=("$cmd") 3981 if ble/syntax:bash/simple-word/is-simple "$cmd"; then 3982 if ble/syntax:bash/simple-word/eval "$cmd" noglob && 3983 [[ $ret != "$cmd" || ${#ret[@]} -ne 1 ]]; then 3984 3985 ucmd=${ret[0]} qcmds=() 3986 local word 3987 for word in "${ret[@]}"; do 3988 ble/string#quote-word "$word" quote-empty 3989 ble/array#push qcmds "$ret" 3990 done 3991 else 3992 ble/string#quote-word "$cmd" quote-empty 3993 qcmds=("$ret") 3994 fi 3995 3996 [[ $cmd == "${orig_comp_words[0]}" ]] && 3997 orig_qcmds_set=1 orig_qcmds=("${qcmds[@]}") 3998 fi 3999 4000 if ble/is-function "ble/cmdinfo/complete:$ucmd"; then 4001 ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" 4002 "ble/cmdinfo/complete:$ucmd" "$opts" 4003 return "$?" 4004 elif [[ $ucmd == */?* ]] && ble/is-function "ble/cmdinfo/complete:${ucmd##*/}"; then 4005 ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret 4006 ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" 4007 "ble/cmdinfo/complete:${ucmd##*/}" "$opts" 4008 return "$?" 4009 elif builtin complete -p -- "$ucmd" &>/dev/null; then 4010 ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" 4011 ble/complete/progcomp/.compgen "$opts" 4012 return "$?" 4013 elif [[ $ucmd == */?* ]] && builtin complete -p -- "${ucmd##*/}" &>/dev/null; then 4014 ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret 4015 ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" 4016 ble/complete/progcomp/.compgen "$opts" 4017 return "$?" 4018 elif ble/complete/progcomp/.try-load-completion "${ucmd##*/}"; then 4019 ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret 4020 ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}" 4021 ble/complete/progcomp/.compgen "$opts" 4022 return "$?" 4023 fi 4024 alias_checked=$alias_checked$cmd' ' 4025 4026 # progcomp_alias が有効でなければ break 4027 ((_ble_bash<50000)) || shopt -q progcomp_alias || break 4028 4029 local ret 4030 ble/alias#expand "$cmd" 4031 [[ $ret == "$cmd" ]] && break 4032 ble/complete/progcomp/.split-alias-words "$ret" 4033 if ((${#ret[@]}==0)); then 4034 # alias 展開により内容が消滅した時は次の単語をコマンドとして再度展開を繰り返す 4035 ble/complete/progcomp/.compline-rewrite-command "${alias_args[@]}" 4036 if ((${#comp_words[@]})); then 4037 if ((comp_cword==0)); then 4038 ble/complete/source:command 4039 else 4040 ble/complete/progcomp "${comp_words[0]}" "__recursive__:$opts" 4041 fi 4042 fi 4043 return "$?" 4044 fi 4045 4046 [[ $alias_checked != *" $ret "* ]] || break 4047 cmd=$ret 4048 ((${#ret[@]}>=2)) && 4049 alias_args=("${ret[@]:1}" "${alias_args[@]}") 4050 done 4051 4052 # comp_words の再構築 4053 comp_words=("${orig_comp_words[@]}") 4054 comp_cword=$orig_comp_cword 4055 comp_line=$orig_comp_line 4056 comp_point=$orig_comp_point 4057 [[ $orig_qcmds_set ]] && 4058 ble/complete/progcomp/.compline-rewrite-command "${orig_qcmds[@]}" 4059 ble/complete/progcomp/.compgen "default:$opts" 4060 } 4061 4062 #------------------------------------------------------------------------------ 4063 # mandb 4064 4065 # オプション名に現れる事を許す文字の集合 (- と + を除く) 4066 # Exclude non-ASCII or symbols /[][()<>{}="'\''`]/ 4067 # Note: awk の正規表現内部で使っても大丈夫な様に \ と / をエスケープしている。 4068 # Note (#D2039): @ は cd -@ で使われている 4069 _ble_complete_option_chars='_!#$%&:;.,^~|\\?\/*a-zA-Z0-9@' 4070 4071 # action:mandb 4072 # 4073 # DATA ... cmd FS menu_suffix FS insert_suffix FS desc 4074 # 4075 function ble/complete/action:mandb/initialize { 4076 ble/complete/action/quote-insert 4077 } 4078 function ble/complete/action:mandb/initialize.batch { 4079 ble/complete/action/quote-insert.batch newline 4080 } 4081 function ble/complete/action:mandb/complete { 4082 ble/complete/action/complete.close-quotation 4083 local fields 4084 ble/string#split fields "$_ble_term_FS" "$DATA" 4085 local tail=${fields[2]} 4086 [[ $tail == ' ' && $comps_flags == *x* ]] && tail=',' 4087 ble/complete/action/complete.addtail "$tail" 4088 } 4089 function ble/complete/action:mandb/init-menu-item { 4090 local ret; ble/color/face2g argument_option; g=$ret 4091 4092 local fields 4093 ble/string#split fields "$_ble_term_FS" "$DATA" 4094 suffix=${fields[1]} 4095 } 4096 function ble/complete/action:mandb/get-desc { 4097 local fields 4098 ble/string#split fields "$_ble_term_FS" "$DATA" 4099 desc=${fields[3]} 4100 } 4101 4102 function ble/complete/mandb/load-mandb-conf { 4103 [[ -s $1 ]] || return 0 4104 local line words 4105 while ble/bash/read line || [[ $line ]]; do 4106 ble/string#split-words words "${line%%'#'*}" 4107 case ${words[0]} in 4108 (MANDATORY_MANPATH) 4109 [[ -d ${words[1]} ]] && 4110 ble/array#push manpath_mandatory "${words[1]}" ;; 4111 (MANPATH_MAP) 4112 ble/dict#set manpath_map "${words[1]}" "${words[2]}" ;; 4113 esac 4114 done < "$1" 4115 } 4116 4117 _ble_complete_mandb_default_manpath=() 4118 function ble/complete/mandb/initialize-manpath { 4119 ((${#_ble_complete_mandb_default_manpath[@]})) && return 0 4120 local manpath 4121 MANPATH= ble/util/assign manpath 'manpath || ble/bin/man -w' 2>/dev/null 4122 ble/string#split manpath : "$manpath" 4123 if ((${#manpath[@]}==0)); then 4124 local -a manpath_mandatory=() 4125 builtin eval -- "${_ble_util_dict_declare//NAME/manpath_map}" 4126 ble/complete/mandb/load-mandb-conf /etc/man_db.conf 4127 ble/complete/mandb/load-mandb-conf ~/.manpath 4128 4129 # default mandatory manpath 4130 if ((${#manpath_mandatory[@]}==0)); then 4131 local ret 4132 ble/complete/util/eval-pathname-expansion '~/*/share/man' 4133 ble/array#push manpath_mandatory "${ret[@]}" 4134 ble/complete/util/eval-pathname-expansion '~/@(opt|.opt)/*/share/man' 4135 ble/array#push manpath_mandatory "${ret[@]}" 4136 for ret in /usr/local/share/man /usr/local/man /usr/share/man; do 4137 [[ -d $ret ]] && ble/array#push manpath_mandatory "$ret" 4138 done 4139 fi 4140 4141 builtin eval -- "${_ble_util_dict_declare//NAME/mark}" 4142 4143 local paths path ret 4144 ble/string#split paths : "$PATH" 4145 for path in "${paths[@]}"; do 4146 [[ -d $path ]] || continue 4147 [[ $path == *?/ ]] && path=${path%/} 4148 if ble/dict#get manpath_map "$path"; then 4149 path=$ret 4150 else 4151 path=${path%/bin}/share/man 4152 fi 4153 if [[ -d $path ]] && ! ble/set#contains mark "$path"; then 4154 ble/set#add mark "$path" 4155 ble/array#push manpath "$path" 4156 fi 4157 done 4158 4159 for path in "${manpath_mandatory[@]}"; do 4160 if [[ -d $path ]] && ! ble/set#contains mark "$path"; then 4161 ble/set#add mark "$path" 4162 ble/array#push manpath "$path" 4163 fi 4164 done 4165 fi 4166 _ble_complete_mandb_default_manpath=("${manpath[@]}") 4167 } 4168 4169 function ble/complete/mandb/search-file/.extract-path { 4170 local command=$1 4171 [[ $_ble_complete_mandb_lang ]] && 4172 local LC_ALL=$$_ble_complete_mandb_lang 4173 ble/util/assign path 'ble/bin/man -w "$command"' 2>/dev/null 4174 } 4175 ble/function#suppress-stderr ble/complete/mandb/search-file/.extract-path 4176 4177 function ble/complete/mandb/search-file/.check { 4178 local path=$1 4179 if [[ $path && -s $path ]]; then 4180 ret=$path 4181 return 0 4182 else 4183 return 1 4184 fi 4185 } 4186 ## @fn ble/complete/mandb/search-file command 4187 ## 指定したコマンドに対応する man ページのファイルを検索します。 4188 ## @var[out] ret 4189 ## 見つかったファイルへのパスを格納します。 4190 ## @exit 4191 ## 該当するファイルが見つかった時に成功します。 4192 function ble/complete/mandb/search-file { 4193 local command=$1 4194 4195 local path 4196 ble/complete/mandb/search-file/.extract-path "$command" 4197 ble/complete/mandb/search-file/.check "$path" && return 0 4198 4199 # Get manpaths 4200 ble/string#split ret : "$MANPATH" 4201 4202 # Replace empty paths with the default manpaths 4203 ((${#ret[@]})) || ret=('') 4204 local -a manpath=() 4205 for path in "${ret[@]}"; do 4206 if [[ $path ]]; then 4207 ble/array#push manpath "$path" 4208 else 4209 # system manpath 4210 ble/complete/mandb/initialize-manpath 4211 ble/array#push manpath "${_ble_complete_mandb_default_manpath[@]}" 4212 fi 4213 done 4214 4215 local path 4216 for path in "${manpath[@]}"; do 4217 [[ -d $path ]] || continue 4218 ble/complete/mandb/search-file/.check "$path/man1/$command.1" && return 0 4219 ble/complete/mandb/search-file/.check "$path/man1/$command.8" && return 0 4220 if ble/is-function ble/bin/gzip; then 4221 ble/complete/mandb/search-file/.check "$path/man1/$command.1.gz" && return 0 4222 ble/complete/mandb/search-file/.check "$path/man1/$command.8.gz" && return 0 4223 fi 4224 if ble/is-function ble/bin/bzcat; then 4225 ble/complete/mandb/search-file/.check "$path/man1/$command.1.bz" && return 0 4226 ble/complete/mandb/search-file/.check "$path/man1/$command.1.bz2" && return 0 4227 ble/complete/mandb/search-file/.check "$path/man1/$command.8.bz" && return 0 4228 ble/complete/mandb/search-file/.check "$path/man1/$command.8.bz2" && return 0 4229 fi 4230 if ble/is-function ble/bin/xzcat; then 4231 ble/complete/mandb/search-file/.check "$path/man1/$command.1.xz" && return 0 4232 ble/complete/mandb/search-file/.check "$path/man1/$command.8.xz" && return 0 4233 fi 4234 if ble/is-function ble/bin/lzcat; then 4235 ble/complete/mandb/search-file/.check "$path/man1/$command.1.lzma" && return 0 4236 ble/complete/mandb/search-file/.check "$path/man1/$command.8.lzma" && return 0 4237 fi 4238 done 4239 return 1 4240 } 4241 4242 if ble/bin#freeze-utility-path preconv; then 4243 function ble/complete/mandb/.preconv { ble/bin/preconv; } 4244 else 4245 # macOS では preconv がない 4246 function ble/complete/mandb/.preconv { 4247 ble/bin/od -A n -t u1 -v | ble/bin/awk ' 4248 BEGIN { 4249 ECHAR = 65533; # U+FFFD 4250 4251 # Initialize table 4252 byte = 0; 4253 for (i = 0; byte < 128; byte++) { mtable[byte] = 0; vtable[byte] = i++; } 4254 for (i = 0; byte < 192; byte++) { mtable[byte] = 0; vtable[byte] = ECHAR; } 4255 for (i = 0; byte < 224; byte++) { mtable[byte] = 1; vtable[byte] = i++; } 4256 for (i = 0; byte < 240; byte++) { mtable[byte] = 2; vtable[byte] = i++; } 4257 for (i = 0; byte < 248; byte++) { mtable[byte] = 3; vtable[byte] = i++; } 4258 for (i = 0; byte < 252; byte++) { mtable[byte] = 4; vtable[byte] = i++; } 4259 for (i = 0; byte < 254; byte++) { mtable[byte] = 5; vtable[byte] = i++; } 4260 for (i = 0; byte < 256; byte++) { mtable[byte] = 0; vtable[byte] = ECHAR; } 4261 4262 M = 0; C = 0; 4263 } 4264 function put_uchar(uchar) { 4265 if (uchar < 128) 4266 printf("%c", uchar); 4267 else 4268 printf("\\[u%04X]", uchar); 4269 } 4270 function process_byte(byte) { 4271 if (M) { 4272 if (128 <= byte && byte < 192) { 4273 C = C * 64 + byte % 64; 4274 if (--M == 0) put_uchar(C); 4275 return; 4276 } else { 4277 # while (M--) C *= 64; put_uchar(C); 4278 put_uchar(ECHAR); 4279 M = 0; 4280 } 4281 } 4282 4283 M = mtable[byte]; 4284 C = vtable[byte]; 4285 if (M == 0) put_uchar(C); 4286 } 4287 { for (i = 1; i <= NF; i++) process_byte($i); } 4288 ' 4289 } 4290 fi 4291 4292 _ble_complete_mandb_lang= 4293 if ble/is-function ble/bin/groff; then 4294 # ENCODING: UTF-8 4295 _ble_complete_mandb_convert_type=man 4296 function ble/complete/mandb/convert-mandoc { 4297 if [[ $_ble_util_locale_encoding == UTF-8 ]]; then 4298 ble/bin/groff -k -Tutf8 -man 4299 else 4300 ble/bin/groff -Tascii -man 4301 fi 4302 } 4303 4304 # Note #D1551: macOS (groff-1.19.2) では groff -k も preconv も既定では存在しない 4305 if [[ $OSTYPE == darwin* ]] && ! ble/bin/groff -k -Tutf8 -man &>/dev/null <<< 'α'; then 4306 if ble/bin/groff -T utf8 -m man &>/dev/null <<< '\[u03B1]'; then 4307 function ble/complete/mandb/convert-mandoc { 4308 if [[ $_ble_util_locale_encoding == UTF-8 ]]; then 4309 ble/complete/mandb/.preconv | ble/bin/groff -T utf8 -m man 4310 else 4311 ble/bin/groff -T ascii -m man 4312 fi 4313 } 4314 else 4315 _ble_complete_mandb_lang=C 4316 function ble/complete/mandb/convert-mandoc { 4317 ble/bin/groff -T ascii -m man 4318 } 4319 fi 4320 fi 4321 elif ble/is-function ble/bin/nroff; then 4322 _ble_complete_mandb_convert_type=man 4323 function ble/complete/mandb/convert-mandoc { 4324 if [[ $_ble_util_locale_encoding == UTF-8 ]]; then 4325 ble/bin/nroff -Tutf8 -man 4326 else 4327 ble/bin/groff -Tascii -man 4328 fi 4329 } 4330 elif ble/is-function ble/bin/mandoc; then 4331 # bsd 4332 _ble_complete_mandb_convert_type=mdoc 4333 function ble/complete/mandb/convert-mandoc { 4334 ble/bin/mandoc -mdoc 4335 } 4336 fi 4337 4338 function ble/complete/mandb/.generate-cache-from-man { 4339 ble/is-function ble/bin/man && 4340 ble/is-function ble/complete/mandb/convert-mandoc || return 1 4341 4342 local command=$1 4343 local ret 4344 ble/complete/mandb/search-file "$command" || return 1 4345 local LC_ALL= LC_COLLATE=C 2>/dev/null 4346 local path=$ret 4347 case $ret in 4348 (*.gz) ble/bin/gzip -cd "$path" ;; 4349 (*.bz|*.bz2) ble/bin/bzcat "$path" ;; 4350 (*.lzma) ble/bin/lzcat "$path" ;; 4351 (*.xz) ble/bin/xzcat "$path" ;; 4352 (*) ble/bin/cat "$path" ;; 4353 esac | ble/bin/awk -v type="$_ble_complete_mandb_convert_type" ' 4354 BEGIN { 4355 g_keys_count = 0; 4356 g_desc = ""; 4357 if (type == "man") { 4358 print ".TH __ble_ignore__ 1 __ble_ignore__ __ble_ignore__"; 4359 print ".ll 9999" 4360 topic_start = ".TP"; 4361 } 4362 mode = "begin"; 4363 4364 fmt3_state = ""; 4365 fmt5_state = ""; 4366 fmt6_state = ""; 4367 } 4368 function output_pair(key, desc) { 4369 print ""; 4370 print "__ble_key__"; 4371 if (topic_start != "") print topic_start; 4372 print key; 4373 print ""; 4374 print "__ble_desc__"; 4375 print ""; 4376 print desc; 4377 } 4378 function flush_topic(_, i) { 4379 if (g_keys_count != 0) { 4380 for (i = 0; i < g_keys_count; i++) 4381 output_pair(g_keys[i], g_desc); 4382 } 4383 g_keys_count = 0; 4384 g_desc = ""; 4385 4386 fmt3_flush(); 4387 fmt5_state = ""; 4388 fmt6_flush(); 4389 } 4390 4391 # ".Dd" seems to be the include directive for macros? 4392 # ".Nm" (in mdoc) specifies the name of the target the man page describes 4393 mode == "begin" && /^\.(Dd|Nm)['"$_ble_term_space"']/ { 4394 if (type == "man" && /^\.Dd['"$_ble_term_space"']+\$Mdoc/) topic_start = ""; 4395 print $0; 4396 } 4397 4398 function register_key(key) { 4399 g_keys[g_keys_count++] = key; 4400 g_desc = ""; 4401 } 4402 4403 # Comment: [.ig \n comments \n ..] 4404 /^\.ig/ { mode = "ignore"; next; } 4405 mode == "ignore" { 4406 if (/^\.\.['"$_ble_term_space"']*/) mode = "none"; 4407 next; 4408 } 4409 4410 { 4411 sub(/['"$_ble_term_space"']+$/, ""); 4412 REQ = match($0, /^\.[_a-zA-Z0-9]+/) ? substr($0, 2, RLENGTH - 1) : ""; 4413 } 4414 4415 REQ ~ /^(S[Ss]|S[Hh]|Pp)$/ { flush_topic(); next; } 4416 4417 #-------------------------------------------------------------------------- 4418 # Format #5: [.PP \n key \n .RS \n desc \n .RE] 4419 # used by "ping". 4420 4421 REQ == "PP" { 4422 flush_topic(); 4423 fmt5_state = "key"; 4424 fmt5_key = ""; 4425 fmt5_desc = ""; 4426 next; 4427 } 4428 4429 fmt5_state { 4430 if (fmt5_state == "key") { 4431 if (/^\.RS([^_a-zA-Z0-9]|$)/) 4432 fmt5_state = "desc"; 4433 else if (/^\.RE([^_a-zA-Z0-9]|$)/) 4434 fmt5_state = "none"; 4435 else 4436 fmt5_key = (fmt5_key ? "\n" : "") $0; 4437 } else if (fmt5_state == "desc") { 4438 if (/^\.RE([^_a-zA-Z0-9]|$)/) { 4439 register_key(fmt5_key); 4440 g_desc = fmt5_desc; 4441 flush_topic(); 4442 fmt5_state = ""; 4443 } else 4444 fmt5_desc = (fmt5_desc ? "\n" : "") $0; 4445 } 4446 } 4447 4448 #-------------------------------------------------------------------------- 4449 # Format #3: [.HP \n keys \n .IP \n desc] 4450 # GNU sed seems to use this format. 4451 # GNU coreutils mv seems to contain [.HP \n key desc ] (for option "-b") 4452 4453 REQ == "HP" { 4454 flush_topic(); 4455 fmt3_state = "key"; 4456 fmt3_key_count = 0; 4457 fmt3_desc = ""; 4458 next; 4459 } 4460 4461 function fmt3_process(_, key) { 4462 if (REQ == "TP") { fmt3_flush(); return; } 4463 if (REQ == "PD") return; 4464 4465 if (fmt3_state == "key") { 4466 if (REQ == "IP") { fmt3_state = "desc"; return; } 4467 if (match($0, /( | )['"$_ble_term_space"']*/)) { 4468 fmt3_keys[fmt3_key_count++] = substr($0, 1, RSTART - 1); 4469 fmt3_desc = substr($0, RSTART + RLENGTH); 4470 fmt3_state = "desc"; 4471 } else { 4472 fmt3_keys[fmt3_key_count++] = $0; 4473 } 4474 } else if (fmt3_state == "desc") { 4475 if (fmt3_desc != "") fmt3_desc = fmt3_desc "\n"; 4476 fmt3_desc = fmt3_desc $0; 4477 } 4478 } 4479 function fmt3_flush(_, i) { 4480 if (fmt3_state == "desc" && fmt3_key_count > 0) { 4481 for (i = 0; i < fmt3_key_count; i++) 4482 register_key(fmt3_keys[i]); 4483 g_desc = fmt3_desc; 4484 } 4485 fmt3_state = ""; 4486 fmt3_key_count = 0; 4487 fmt3_desc = ""; 4488 } 4489 4490 fmt3_state { fmt3_process(); } 4491 4492 #-------------------------------------------------------------------------- 4493 # Format #4: [[.IP "key" 4 \n .IX Item "..."]+ \n .PD \n desc] 4494 # This format is used by "wget". 4495 4496 /^\.IP['"$_ble_term_space"']+".*"(['"$_ble_term_space"']+[0-9]+)?$/ && fmt3_state != "key" { 4497 fmt6_init(); 4498 fmt4_init(); 4499 next; 4500 } 4501 4502 function fmt4_init() { 4503 if (mode != "fmt4_desc") 4504 if (!(g_keys_count && g_desc == "")) flush_topic(); 4505 4506 gsub(/^\.IP['"$_ble_term_space"']+"|"(['"$_ble_term_space"']+[0-9]+)?$/, ""); 4507 register_key($0); 4508 mode = "fmt4_desc"; 4509 } 4510 mode == "fmt4_desc" { 4511 if ($0 == "") { flush_topic(); mode = "none"; next; } 4512 4513 # fish has a special format of [.IP "\(bu" 2 \n keys desc] 4514 if (g_keys_count == 1 && g_keys[0] == "\\(bu" && match($0, /^\\fC[^\\]+\\fP( or \\fC[^\\]+\\fP)?/) > 0) { 4515 _key = substr($0, 1, RLENGTH); 4516 _desc = substr($0, RLENGTH + 1); 4517 if (match(_key, / or \\fC[^\\]+\\fP/) > 0) 4518 _key = substr(_key, 1, RSTART - 1) ", " substr(_key, RSTART + 4); 4519 g_keys[0] = _key; 4520 g_desc = _desc; 4521 next; 4522 } 4523 4524 if (REQ == "PD") next; 4525 if (/^\.IX['"$_ble_term_space"']+Item['"$_ble_term_space"']+/) next; 4526 4527 if (g_desc != "") g_desc = g_desc "\n"; 4528 g_desc = g_desc $0; 4529 } 4530 4531 #-------------------------------------------------------------------------- 4532 # Format #6: [[.IP "key" \n desc .IP] 4533 # This format is used by "rsync". 4534 4535 function fmt6_init() { 4536 fmt6_flush(); 4537 fmt6_state = "desc" 4538 fmt6_key = $0; 4539 fmt6_desc = ""; 4540 } 4541 fmt6_state { 4542 if (REQ == "IX") { 4543 # Exclude fmt4 case 4544 fmt6_state = ""; 4545 } else if (REQ == "IP") { 4546 fmt6_flush(); 4547 } else { 4548 fmt6_desc = fmt6_desc $0 "\n"; 4549 } 4550 } 4551 function fmt6_flush() { 4552 if (!fmt6_state) return; 4553 fmt6_state = ""; 4554 if (fmt6_desc) 4555 output_pair(fmt6_key, fmt6_desc); 4556 } 4557 4558 #-------------------------------------------------------------------------- 4559 # Format #2: [.It Fl key \n desc] or [.It Fl Xo \n key \n .Xc desc] 4560 # This form was found in both "mdoc" and "man" 4561 /^\.It Fl([^_a-zA-Z0-9]|$)/ { 4562 if (g_keys_count && g_desc != "") flush_topic(); 4563 sub(/^\.It Fl/, ".Fl"); 4564 if ($0 ~ / Xo$/) { 4565 g_current_key = $0; 4566 mode = "fmt2_keyc" 4567 } else { 4568 register_key($0); 4569 mode = "desc"; 4570 } 4571 next; 4572 } 4573 mode == "fmt2_keyc" { 4574 if (/^\.PD['"$_ble_term_space"']*([0-9]+['"$_ble_term_space"']*)?$/) next; 4575 g_current_key = g_current_key "\n" $0; 4576 if (REQ == "Xc") { 4577 register_key(g_current_key); 4578 mode = "desc"; 4579 } 4580 next; 4581 } 4582 #-------------------------------------------------------------------------- 4583 # Format #1: [.TP \n key \n desc] 4584 # Format #1: [.TP \n key desc \n desc...] 4585 # This is the typical format in "man". 4586 type == "man" && REQ == "TP" { 4587 if (g_keys_count && g_desc != "") flush_topic(); 4588 mode = "key1"; 4589 next; 4590 } 4591 mode == "key1" { 4592 if (/^\.PD['"$_ble_term_space"']*([0-9]+['"$_ble_term_space"']*)?$/) next; 4593 4594 # In Japanese version of "man ls", key and desc is separated by multiple 4595 # spaces, where the number of spaces seem to vary from 5 to more than 10 4596 # spaces. 4597 if (match($0, /['"$_ble_term_space"']['"$_ble_term_space"']['"$_ble_term_space"']/) > 0) { 4598 register_key(substr($0, 1, RSTART - 1)); 4599 g_desc = substr($0, RSTART); 4600 sub(/^['"$_ble_term_space"']+/, "", g_desc); 4601 } else { 4602 register_key($0); 4603 } 4604 4605 mode = "desc"; 4606 next; 4607 } 4608 mode == "desc" { 4609 if (REQ == "PD") next; 4610 4611 if (g_desc != "") g_desc = g_desc "\n"; 4612 g_desc = g_desc $0; 4613 } 4614 #-------------------------------------------------------------------------- 4615 4616 END { flush_topic(); } 4617 ' | ble/complete/mandb/convert-mandoc 2>/dev/null | ble/bin/awk -F "$_ble_term_FS" ' 4618 function flush_pair(_, i, desc, prev_opt) { 4619 if (g_option_count) { 4620 gsub(/\034/, "\x1b[7m^\\\x1b[27m", g_desc); 4621 sub(/(\. |; ).*/, ".", g_desc); # Long descriptions are truncated. 4622 4623 for (i = 0; i < g_option_count; i++) { 4624 desc = g_desc; 4625 4626 # show a short option 4627 if (i > 0 && g_options[i] ~ /^--/) { 4628 prev_opt = g_options[i - 1]; 4629 sub(/\034.*/, "", prev_opt); 4630 if (prev_opt ~ /^-[^-]$/) 4631 desc = "\033[1m[\033[0;36m" prev_opt "\033[0;1m]\033[m " desc; 4632 } 4633 4634 print g_options[i] FS desc; 4635 } 4636 } 4637 g_option_count = 0; 4638 g_desc = ""; 4639 } 4640 4641 function process_key(line, _, n, specs, i, spec, option, optarg, suffix) { 4642 gsub(/^['"$_ble_term_space"']+|['"$_ble_term_space"']+$/, "", line); 4643 if (line == "") return; 4644 4645 gsub(/\x1b\[[ -?]*[@-~]/, "", line); # CSI seq 4646 gsub(/\x1b[ -\/]*[0-~]/, "", line); # ESC seq 4647 gsub(/\t/, " ", line); # HT 4648 gsub(/.\x08/, "", line); # CHAR BS 4649 gsub(/\x0E/, "", line); # SO 4650 gsub(/\x0F/, "", line); # SI 4651 gsub(/[\x00-\x1F]/, "", line); # Give up all the other control chars 4652 gsub(/^['"$_ble_term_space"']*|['"$_ble_term_space"']*$/, "", line); 4653 gsub(/['"$_ble_term_space"']+/, " ", line); 4654 if (line !~ /^[-+]./) return; 4655 4656 n = split(line, specs, /,(['"$_ble_term_space"']+|$)| or /); 4657 prev_optarg = ""; 4658 for (i = n; i > 0; i--) { 4659 spec = specs[i]; 4660 sub(/,['"$_ble_term_space"']+$/, "", spec); 4661 4662 # Exclude non-options. 4663 # Exclude FS (\034) because it is used for separators in the cache format. 4664 if (spec !~ /^[-+]/ || spec ~ /\034/) { specs[i] = ""; continue; } 4665 4666 if (match(spec, /\[[:=]?|[:='"$_ble_term_space"']/)) { 4667 option = substr(spec, 1, RSTART - 1); 4668 optarg = substr(spec, RSTART); 4669 suffix = substr(spec, RSTART + RLENGTH - 1, 1); 4670 if (suffix == "[") suffix = ""; 4671 prev_optarg = optarg; 4672 } else { 4673 option = spec; 4674 optarg = ""; 4675 suffix = " "; 4676 4677 # Carry previous optarg 4678 if (prev_optarg ~ /[A-Z]|<.+>/) { 4679 optarg = prev_optarg; 4680 if (option ~ /^[-+].$/) { 4681 sub(/^\[=/, "[", optarg); 4682 sub(/^=/, "", optarg); 4683 sub(/^[^'"$_ble_term_space"'[]/, " &", optarg); 4684 } else { 4685 if (optarg ~ /^\[[^:=]/) 4686 sub(/^\[/, "[=", optarg); 4687 else if (optarg ~ /^[^:='"$_ble_term_space"'[]/) 4688 optarg = " " optarg; 4689 } 4690 4691 if (match(optarg, /^\[[:=]?|^[:='"$_ble_term_space"']/)) { 4692 suffix = substr(optarg, RSTART + RLENGTH - 1, 1); 4693 if (suffix == "[") suffix = ""; 4694 } 4695 } 4696 } 4697 4698 specs[i] = option FS optarg FS suffix; 4699 } 4700 4701 for (i = 1; i <= n; i++) { 4702 if (specs[i] == "") continue; 4703 option = substr(specs[i], 1, index(specs[i], FS) - 1); 4704 if (!g_hash[option]++) 4705 g_options[g_option_count++] = specs[i]; 4706 } 4707 } 4708 4709 function process_desc(line) { 4710 gsub(/^['"$_ble_term_space"']*|['"$_ble_term_space"']*$/, "", line); 4711 if (line == "") { 4712 if (g_desc != "") return 0; 4713 return 1; 4714 } 4715 4716 gsub(/['"$_ble_term_space"']['"$_ble_term_space"']+/, " ", line); 4717 if (g_desc != "") g_desc = g_desc " "; 4718 g_desc = g_desc line; 4719 return 1; 4720 } 4721 4722 function process_string_fragment(str) { 4723 if (mode == "key") { 4724 process_key(str); 4725 } else if (mode == "desc") { 4726 if (!process_desc(str)) mode = ""; 4727 } 4728 } 4729 4730 function process_line(line, _, head, m0) { 4731 while (match(line, /__ble_(key|desc)__/) > 0) { 4732 head = substr(line, 1, RSTART - 1); 4733 m0 = substr(line, RSTART, RLENGTH); 4734 line = substr(line, RSTART + RLENGTH); 4735 4736 process_string_fragment(head); 4737 4738 if (m0 == "__ble_key__") { 4739 flush_pair(); 4740 mode = "key"; 4741 } else { 4742 mode = "desc"; 4743 } 4744 } 4745 4746 process_string_fragment(line); 4747 } 4748 4749 { process_line($0); } 4750 END { flush_pair(); } 4751 ' | ble/bin/sort -t "$_ble_term_FS" -k 1 4752 ble/util/unlocal LC_COLLATE LC_ALL 2>/dev/null 4753 } 4754 4755 ## @fn ble/complete/mandb:help/generate-cache [opts] 4756 function ble/complete/mandb:help/generate-cache { 4757 local opts=$1 4758 local -x cfg_usage= cfg_help=1 cfg_plus= cfg_plus_generate= 4759 [[ :$opts: == *:mandb-help-usage:* ]] && cfg_usage=1 4760 [[ :$opts: == *:mandb-usage:* ]] && cfg_usage=1 cfg_help= 4761 ble/string#match ":$opts:" ':plus-options(=[^:]+)?:' && 4762 cfg_plus=1 cfg_plus_generate=${BASH_REMATCH[1]:1} 4763 4764 local space=$' \t' # for #D1709 (WA gawk 4.0.2) 4765 local rex_argsep='(\[?[:=]| ?|\[)' 4766 local rex_option='[-+](,|[^]:='$space',[]+)('$rex_argsep'(<[^<>]+>|\([^()]+\)|\[[^][]+\]|[^-'"$_ble_term_space"'、。][^'"$_ble_term_space"'、。]*))?([,'"$_ble_term_space"']|$)' 4767 local LC_ALL= LC_COLLATE=C 2>/dev/null 4768 ble/bin/awk -F "$_ble_term_FS" ' 4769 BEGIN { 4770 cfg_help = ENVIRON["cfg_help"]; 4771 g_help_indent = -1; 4772 g_help_score = -1; # score based on indent and the interval between the 4773 # option and desc. smaller is better. 4774 g_help_keys_count = 0; 4775 g_help_desc = ""; 4776 4777 cfg_usage = ENVIRON["cfg_usage"]; 4778 g_usage_count = 0; 4779 4780 cfg_plus_generate = ENVIRON["cfg_plus_generate"]; 4781 cfg_plus = ENVIRON["cfg_plus"] cfg_plus_generate; 4782 4783 entries_init(); 4784 } 4785 4786 #-------------------------------------------------------------------------- 4787 # entries 4788 4789 function entries_init() { 4790 entries_count = 0; 4791 } 4792 4793 function entries_register(entry, score, _, name, ientry) { 4794 name = entry; 4795 sub(/'"$_ble_term_FS"'.*$/, "", name); 4796 if (name ~ /^\+/ && !cfg_plus) return; 4797 4798 if (entries_index[name] != "") { 4799 if (score >= entries_score[name]) return; 4800 ientry = entries_index[name]; 4801 } else { 4802 ientry = entries_count++; 4803 entries_keys[ientry] = name; 4804 } 4805 4806 entries_index[name] = ientry; 4807 entries_entry[name] = entry; 4808 entries_score[name] = score; 4809 } 4810 4811 function entries_dump(_, ientry, name) { 4812 for (ientry = 0; ientry < entries_count; ientry++) { 4813 name = entries_keys[ientry]; 4814 print entries_entry[name]; 4815 } 4816 } 4817 4818 #-------------------------------------------------------------------------- 4819 4820 function split_option_optarg_suffix(optspec, _, key, suffix, optarg) { 4821 # Note: Skip options that contain FS (due to the limitation by the cache format) 4822 if (index(optspec, FS) != 0) return ""; 4823 4824 if ((pos = match(optspec, /'"$rex_argsep"'/)) > 0) { 4825 key = substr(optspec, 1, pos - 1); 4826 suffix = substr(optspec, pos + RLENGTH - 1, 1); 4827 if (suffix == "[") suffix = ""; 4828 optarg = substr(optspec, pos); 4829 } else { 4830 key = optspec; 4831 optarg = ""; 4832 suffix = " "; 4833 } 4834 4835 # Note: Exclude option names containing non-option characters 4836 if (key ~ /[^-+'"$_ble_complete_option_chars"']/) return ""; 4837 4838 return key FS optarg FS suffix; 4839 } 4840 4841 { 4842 gsub(/\x1b\[[ -?]*[@-~]/, ""); # CSI seq 4843 gsub(/\x1b[ -\/]*[0-~]/, ""); # ESC seq 4844 gsub(/\t/, " "); # HT 4845 gsub(/[\x00-\x1F]/, ""); # Remove all the other C0 chars 4846 } 4847 4848 #-------------------------------------------------------------------------- 4849 # Generate + options without descriptions 4850 4851 function generate_plus(_, i, n) { 4852 if (!cfg_plus_generate) return; 4853 n = length(cfg_plus_generate); 4854 for (i = 1; i <= n; i++) 4855 entries_register("+" substr(cfg_plus_generate, i, 1) FS FS FS, 999); 4856 } 4857 4858 #-------------------------------------------------------------------------- 4859 # Extract usage [-DEI] [-f[helo] | --prefix=PATH] 4860 4861 function usage_parse(line, _, optspec, optspec1, option, optarg, n, i, o) { 4862 while (match(line, /\[['"$_ble_term_space"']*([^][]|\[[^][]*\])+['"$_ble_term_space"']*\]/)) { 4863 optspec = substr(line, RSTART + 1, RLENGTH - 2); 4864 line = substr(line, RSTART + RLENGTH); 4865 4866 # optspec: " -DEI | --prefix=PATH | ... ", etc. 4867 while (match(optspec, /([^][|]|\[[^][]*\])+/)) { 4868 optspec1 = substr(optspec, RSTART, RLENGTH); 4869 optspec = substr(optspec, RSTART + RLENGTH); 4870 gsub(/^['"$_ble_term_space"']+|['"$_ble_term_space"']+$/, "", optspec1); 4871 4872 # optspec1: "--option optarg", "-f[optarg]", "-xzvf", etc. 4873 if (match(optspec1, /^[-+][^]:='"$space"'[]+/)) { 4874 option = substr(optspec1, RSTART, RLENGTH); 4875 optarg = substr(optspec1, RSTART + RLENGTH); 4876 n = RLENGTH; 4877 if (option ~ /^-.*-/) { 4878 if ((keyinfo = split_option_optarg_suffix(optspec1)) != "") 4879 g_usage[g_usage_count++] = keyinfo; 4880 } else { 4881 o = substr(option, 1, 1); 4882 for (i = 2; i <= n; i++) 4883 if ((keyinfo = split_option_optarg_suffix(o substr(option, i, 1) optarg)) != "") 4884 g_usage[g_usage_count++] = keyinfo; 4885 } 4886 } 4887 } 4888 } 4889 } 4890 function usage_generate(_, i) { 4891 for (i = 0; i < g_usage_count; i++) 4892 entries_register(g_usage[i] FS, 999); 4893 } 4894 4895 cfg_usage { 4896 if (NR <= 20 && (g_usage_start || $0 ~ /^[_a-zA-Z0-9]|^[^-'"$_ble_term_space"'][^'"$_ble_term_space"']*(: |:)/) ) { 4897 g_usage_start = 1; 4898 usage_parse($0); 4899 } else if (/^['"$_ble_term_space"']*$/) 4900 cfg_usage = 0; 4901 } 4902 4903 #-------------------------------------------------------------------------- 4904 # Extract option descriptions 4905 4906 function get_indent(text, _, i, n, ret) { 4907 ret = 0; 4908 n = length(text); 4909 for (i = 1; i <= n; i++) { 4910 c = substr(text, i, 1); 4911 if (c == " ") 4912 ret++; 4913 else if (c == "\t") 4914 ret = (int(ret / 8) + 1) * 8; 4915 else 4916 break; 4917 } 4918 return ret; 4919 } 4920 function help_flush(_, i, desc, prev_opt) { 4921 if (g_help_indent < 0) return; 4922 for (i = 0; i < g_help_keys_count; i++) { 4923 desc = g_help_desc; 4924 4925 # show a short option 4926 if (i > 0 && g_help_keys[i] ~ /^--/) { 4927 prev_opt = g_help_keys[i - 1]; 4928 sub(/\034.*/, "", prev_opt); 4929 if (prev_opt ~ /^-[^-]$/) { 4930 # Note: This particular form of desc is used by 4931 # ble/complete/mandb:bash-completion/_parse_help.advice. When we 4932 # change the format, the function also needs to be updated. 4933 desc = "\033[1m[\033[0;36m" prev_opt "\033[0;1m]\033[m " desc; 4934 } 4935 } 4936 4937 entries_register(g_help_keys[i] FS desc, g_help_score); 4938 } 4939 g_help_indent = -1; 4940 g_help_keys_count = 0; 4941 g_help_desc = ""; 4942 } 4943 function help_start(keydef, _, key, keyinfo, keys, nkey, i, optarg) { 4944 if (g_help_desc != "") help_flush(); 4945 g_help_indent = get_indent(keydef); 4946 g_help_score = g_help_indent; 4947 4948 nkey = 0; 4949 for (;;) { 4950 sub(/^,?['"$_ble_term_space"']+/, "", keydef); 4951 4952 if (match(keydef, /^'"$rex_option"'/) <= 0) break; 4953 key = substr(keydef, 1, RLENGTH); 4954 keydef = substr(keydef, RLENGTH + 1); 4955 4956 sub(/[,'"$_ble_term_space"']$/, "", key); 4957 keys[nkey++] = key; 4958 } 4959 4960 # Copy optarg "-A, --accept=LIST" => "-A LIST, --accept=LIST" 4961 if (nkey >= 2) { 4962 optarg = ""; 4963 for (i = nkey; --i >= 0; ) { 4964 if (match(keys[i], /'"$rex_argsep"'/) > 0) { 4965 optarg = substr(keys[i], RSTART); 4966 sub(/^['"$_ble_term_space"']+/, "", optarg); 4967 if (optarg !~ /[A-Z]|<.+>/) optarg = ""; 4968 } else if (optarg != ""){ 4969 if (keys[i] ~ /^[-+].$/) { 4970 optarg2 = optarg; 4971 sub(/^\[=/, "[", optarg2); 4972 sub(/^=/, "", optarg2); 4973 sub(/^[^'"$_ble_term_space"'[]/, " &", optarg2); 4974 keys[i] = keys[i] optarg2; 4975 } else { 4976 optarg2 = optarg; 4977 if (optarg2 ~ /^\[[^:=]/) 4978 sub(/^\[/, "[=", optarg2); 4979 else if (optarg2 ~ /^[^:='"$_ble_term_space"'[]/) 4980 optarg2 = " " optarg2; 4981 keys[i] = keys[i] optarg2; 4982 } 4983 } 4984 } 4985 } 4986 4987 for (i = 0; i < nkey; i++) 4988 if ((keyinfo = split_option_optarg_suffix(keys[i])) != "") 4989 g_help_keys[g_help_keys_count++] = keyinfo; 4990 } 4991 function help_append_desc(desc) { 4992 gsub(/^['"$_ble_term_space"']+|['"$_ble_term_space"']$/, "", desc); 4993 if (desc == "") return; 4994 if (g_help_desc == "") 4995 g_help_desc = desc; 4996 else 4997 g_help_desc = g_help_desc " " desc; 4998 } 4999 5000 # Note (#D1847): We here restrict the number of spaces between synonymous 5001 # options within 2 or 3. Note that "rex_option" already contains the 5002 # trailing comma or space. 5003 cfg_help && match($0, /^['"$_ble_term_space"']*'"$rex_option"'((['"$_ble_term_space"']['"$_ble_term_space"']?)?'"$rex_option"')*/) { 5004 key = substr($0, 1, RLENGTH); 5005 desc = substr($0, RLENGTH + 1); 5006 if (desc ~ /^,/) next; 5007 help_start(key); 5008 help_append_desc(desc); 5009 if (desc !~ /^['"$_ble_term_space"']/) g_help_score += 100; 5010 next; 5011 } 5012 g_help_indent >= 0 { 5013 sub(/['"$_ble_term_space"']+$/, ""); 5014 indent = get_indent($0); 5015 if (indent <= g_help_indent) 5016 help_flush(); 5017 else 5018 help_append_desc($0); 5019 } 5020 5021 #-------------------------------------------------------------------------- 5022 5023 END { 5024 help_flush(); 5025 usage_generate(); 5026 generate_plus(); 5027 entries_dump(); 5028 } 5029 ' | ble/bin/sort -t "$_ble_term_FS" -k 1 5030 ble/util/unlocal LC_COLLATE LC_ALL 2>/dev/null 5031 } 5032 5033 function ble/complete/mandb:bash-completion/inject { 5034 if ble/is-function _comp_compgen_help; then 5035 # bash-completion 2.12 5036 ble/function#advice after _comp_compgen_help__get_help_lines 'ble/complete/mandb:bash-completion/_get_help_lines.advice' && 5037 ble/function#advice before _comp_longopt 'ble/complete/mandb:bash-completion/_parse_help.advice "${ADVICE_WORDS[1]}"' && 5038 function ble/complete/mandb:bash-completion/inject { return 0; } 5039 elif ble/is-function _parse_help; then 5040 ble/function#advice before _parse_help 'ble/complete/mandb:bash-completion/_parse_help.advice "${ADVICE_WORDS[1]}" "${ADVICE_WORDS[2]}"' && 5041 ble/function#advice before _longopt 'ble/complete/mandb:bash-completion/_parse_help.advice "${ADVICE_WORDS[1]}"' && 5042 ble/function#advice before _parse_usage 'ble/complete/mandb:bash-completion/_parse_help.advice "${ADVICE_WORDS[1]}" "${ADVICE_WORDS[2]}"' && 5043 function ble/complete/mandb:bash-completion/inject { return 0; } 5044 fi 5045 } 2>/dev/null # _parse_help が別の枠組みで定義されている事がある? #D1900 5046 5047 ## @fn ble/string#hash-pjw text [size shift] 5048 ## @var[out] ret 5049 function ble/string#hash-pjw { 5050 local size=${2:-32} 5051 local S=${3:-$(((size+7)/8))} # shift 4 5052 local C=$((size-2*S)) # co-shift 24 5053 local M=$(((1<<size-S)-1)) # mask 0x0FFFFFFF 5054 local N=$(((1<<S)-1<<S)) # mask2 0x000000F0 5055 5056 ble/util/s2bytes "$1" 5057 local c h=0 5058 for c in "${ret[@]}"; do 5059 ((h=(h<<S)+c,h=(h^h>>C&N)&M)) 5060 done 5061 ret=$h 5062 } 5063 5064 ## @fn ble/complete/mandb:bash-completion/.alloc-subcache command hash [opts] 5065 ## @var[out] ret 5066 function ble/complete/mandb:bash-completion/.alloc-subcache { 5067 ret= 5068 [[ $_ble_attached ]] || return 1 5069 5070 local command=$1 hash=$2 opts=$3 5071 if [[ :$opts: == *:dequote:* ]]; then 5072 ble/syntax:bash/simple-word/is-simple "$command" && 5073 ble/syntax:bash/simple-word/eval "$command" noglob && 5074 command=$ret 5075 fi 5076 [[ $command ]] || return 1 5077 5078 [[ $command == ble*/* ]] || command=${1##*/} 5079 ble/string#hash-pjw "$args" 64; local hash=$ret 5080 local lc_messages=${LC_ALL:-${LC_MESSAGES:-${LANG:-C}}} 5081 local mandb_cache_dir=$_ble_base_cache/complete.mandb/${lc_messages//'/'/%} 5082 ble/util/sprintf ret '%s.%014x' "$mandb_cache_dir/_parse_help.d/$command" "$hash" 5083 5084 [[ -s $ret && $ret -nt $_ble_base/lib/core-complete.sh ]] && return 1 5085 5086 ble/util/mkd "${ret%/*}" 5087 } 5088 5089 ## @fn ble/complete/mandb:bash-completion/_parse_help.advice command args 5090 function ble/complete/mandb:bash-completion/_parse_help.advice { 5091 local cmd=$1 args=$2 func=$ADVICE_FUNCNAME 5092 # 現在のコマンド名。 Note: ADVICE_WORDS には実際に現在補完しようとしているコ 5093 # マンドとは異なるものが指定される場合があるので (例えば help や - 等) 信用で 5094 # きない。 5095 local command=${COMP_WORDS[0]-} hash="${ADVICE_WORDS[*]}" ret 5096 ble/complete/mandb:bash-completion/.alloc-subcache "$command" "$hash" dequote || return 0 5097 local subcache=$ret 5098 5099 local default_option=--help help_opts= 5100 [[ $func == _parse_usage ]] && 5101 default_option=--usage help_opts=mandb-usage 5102 5103 if [[ ( $func == _parse_help || $func == _parse_usage ) && $cmd == - ]]; then 5104 # 標準入力からの読み取り 5105 ble/complete/mandb:help/generate-cache "$help_opts" >| "$subcache" 5106 5107 # Note: _parse_help が読み取る筈だった内容を横取りしたので抽出した内容を標 5108 # 準出力に出力する。但し、対応する long option がある short option は除外す 5109 # る。 5110 LC_ALL= LC_COLLATE=C ble/bin/awk -F "$_ble_term_FS" ' 5111 BEGIN { entry_count = 0; } 5112 { 5113 entries[entry_count++] = $1; 5114 5115 # Assumption: the descriptions of long options have the form 5116 # "[short_opt] desc". The format is defined by 5117 # ble/complete/mandb:help/generate-cache. 5118 desc = $4; 5119 gsub(/\033\[[ -?]*[@-~]/, "", desc); 5120 if (match(desc, /^\[[^]'"$_ble_term_space"'[]*\] /) > 0) { # #D1709 safe 5121 short_opt = substr(desc, 2, RLENGTH - 3); 5122 excludes[short_opt] =1; 5123 } 5124 } 5125 END { 5126 for (i = 0; i < entry_count; i++) 5127 if (!excludes[entries[i]]) 5128 print entries[i]; 5129 } 5130 ' "$subcache" 2>/dev/null # suppress locale error #D1440 5131 else 5132 local cmd_args 5133 ble/string#split-words cmd_args "${args:-$default_option}" 5134 "$cmd" "${cmd_args[@]}" 2>&1 | ble/complete/mandb:help/generate-cache "$help_opts" >| "$subcache" 5135 fi 5136 } 5137 5138 function ble/complete/mandb:bash-completion/_get_help_lines.advice { 5139 ((${#_lines[@]})) || return 0 5140 5141 # @var cmd 5142 # 現在のコマンド名。Note: _comp_command_offset 等によって別のコマンドの補完 5143 # を呼び出している場合があるので ble.sh の用意する comp_words は信用できな 5144 # い。bash-completion の使っている _comp_args[0] または bash-completion が 5145 # 上書きしている COMP_WORDS を参照する。 5146 local cmd=${_comp_args[0]-${COMP_WORDS[0]-}} hash="${ADVICE_WORDS[*]}" 5147 ble/complete/mandb:bash-completion/.alloc-subcache "$cmd" "$hash" dequote || return 0 5148 local subcache=$ret 5149 5150 local help_opts= 5151 [[ ${ADVICE_FUNCNAME[1]} == *_usage ]] && help_opts=mandb-usage 5152 printf '%s\n' "${_lines[@]}" | ble/complete/mandb:help/generate-cache "$help_opts" >| "$subcache" 5153 } 5154 5155 ## @fn ble/complete/mandb/generate-cache cmdname 5156 ## @var[out] ret 5157 ## キャッシュファイル名を返します。 5158 function ble/complete/mandb/generate-cache { 5159 local command=${1##*/} 5160 local lc_messages=${LC_ALL:-${LC_MESSAGES:-${LANG:-C}}} 5161 local mandb_cache_dir=$_ble_base_cache/complete.mandb/${lc_messages//'/'/%} 5162 local fcache=$mandb_cache_dir/$command 5163 5164 local cmdspec_opts; ble/cmdspec/opts#load "$command" 5165 [[ :$cmdspec_opts: == *:no-options:* ]] && return 1 5166 5167 # fcache_help 5168 if ble/opts#extract-all-optargs "$cmdspec_opts" mandb-help --help; then 5169 local -a helpspecs; helpspecs=("${ret[@]}") 5170 local subcache=$mandb_cache_dir/help.d/$command 5171 if ! [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then 5172 ble/util/mkd "${subcache%/*}" 5173 local helpspec 5174 for helpspec in "${helpspecs[@]}"; do 5175 if [[ $helpspec == %* ]]; then 5176 builtin eval -- "${helpspec:1}" 5177 elif [[ $helpspec == @* ]]; then 5178 ble/util/print "${helpspec:1}" 5179 else 5180 ble/string#split-words helpspec "${helpspec#+}" 5181 "$command" "${helpspec[@]}" 2>&1 5182 fi 5183 done | ble/complete/mandb:help/generate-cache "$cmdspec_opts" >| "$subcache" 5184 fi 5185 fi 5186 5187 # fcache_man 5188 if [[ :$cmdspec_opts: != *:mandb-disable-man:* ]] && ble/bin#has "$1"; then 5189 local subcache=$mandb_cache_dir/man.d/$command 5190 if ! [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then 5191 ble/util/mkd "${subcache%/*}" 5192 ble/complete/mandb/.generate-cache-from-man "$command" >| "$subcache" 5193 fi 5194 fi 5195 5196 # collect available caches 5197 local -a subcaches=() 5198 local subcache update= 5199 ble/complete/util/eval-pathname-expansion '"$mandb_cache_dir"/_parse_help.d/"$command".??????????????' 5200 for subcache in "${ret[@]}" "$mandb_cache_dir"/{help,man}.d/"$command"; do 5201 if [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then 5202 ble/array#push subcaches "$subcache" 5203 [[ $fcache -nt $subcache ]] || update=1 5204 fi 5205 done 5206 5207 if [[ $update ]]; then 5208 local -x exclude= 5209 ble/opts#extract-last-optarg "$cmdspec_opts" mandb-exclude && exclude=$ret 5210 5211 local fs=$_ble_term_FS 5212 ble/bin/awk -F "$_ble_term_FS" ' 5213 BEGIN { 5214 plus_count = 0; 5215 nodesc_count = 0; 5216 exclude = ENVIRON["exclude"]; 5217 } 5218 function emit(name, entry) { 5219 hash[name] = entry; 5220 if (exclude != "" && name ~ exclude) return; 5221 print entry; 5222 } 5223 5224 $4 == "" { 5225 if ($1 ~ /^\+/) { 5226 plus_name[plus_count] = $1; 5227 plus_entry[plus_count] = $0; 5228 plus_count++; 5229 } else { 5230 nodesc_name[nodesc_count] = $1; 5231 nodesc_entry[nodesc_count] = $0; 5232 nodesc_count++; 5233 } 5234 next; 5235 } 5236 !hash[$1] { emit($1, $0); } 5237 5238 END { 5239 # minus options 5240 for (i = 0; i < nodesc_count; i++) 5241 if (!hash[nodesc_name[i]]) 5242 emit(nodesc_name[i], nodesc_entry[i]); 5243 5244 # plus options 5245 for (i = 0; i < plus_count; i++) { 5246 name = plus_name[i]; 5247 if (hash[name]) continue; 5248 5249 split(plus_entry[i], record, FS); 5250 optarg = record[2]; 5251 suffix = record[3]; 5252 desc = ""; 5253 5254 mname = name; 5255 sub(/^\+/, "-", mname); 5256 if (hash[mname]) { 5257 if (!optarg) { 5258 split(hash[mname], record, FS); 5259 optarg = record[2]; 5260 suffix = record[3]; 5261 } 5262 5263 desc = hash[mname]; 5264 sub(/^[^'$fs']*'$fs'[^'$fs']*'$fs'[^'$fs']*'$fs'/, "", desc); 5265 if (desc) desc = "\033[1mReverse[\033[m " desc " \033[;1m]\033[m"; 5266 } 5267 5268 if (!desc) desc = "reverse of \033[4m" mname "\033[m"; 5269 emit(name, name FS optarg FS suffix FS desc); 5270 } 5271 } 5272 ' "${subcaches[@]}" >| "$fcache" 5273 fi 5274 5275 ret=$fcache 5276 [[ -s $fcache ]] 5277 } 5278 function ble/complete/mandb/load-cache { 5279 ret=() 5280 ble/complete/mandb/generate-cache "$@" && 5281 ble/util/mapfile ret < "$ret" 5282 } 5283 5284 ## @fn ble/complete/source:option/.is-option-context args... 5285 ## args... に "--" などのオプション解釈を停止する様な引数が含まれて 5286 ## いないか判定します。 5287 ## 5288 ## @param[in] args... 5289 ## @var[in] cmdspec_opts 5290 ## 5291 function ble/complete/source:option/.is-option-context { 5292 #(($#)) || return 0 5293 5294 local rexrej rexreq stopat 5295 ble/progcolor/stop-option#init "$cmdspec_opts" 5296 if [[ $stopat ]] && ((stopat<=$#)); then 5297 return 1 5298 elif [[ ! $rexrej$rexreq ]]; then 5299 return 0 5300 fi 5301 5302 local word ret 5303 for word; do 5304 ble/syntax:bash/simple-word/is-simple "$word" && 5305 ble/syntax:bash/simple-word/eval "$word" noglob && 5306 ble/progcolor/stop-option#test "$ret" && 5307 return 1 5308 done 5309 return 0 5310 } 5311 5312 function ble/complete/source:option { 5313 local opts=$1 5314 if [[ :$opts: == *:empty:* ]]; then 5315 # 空文字列に対する補完を明示的に実行 5316 [[ ! $COMPV ]] || return 0 5317 else 5318 # /^[-+].*/ の時にだけ候補生成 (曖昧補完で最初の /^[-+]/ は補わない) 5319 local rex='^-[-+'$_ble_complete_option_chars']*$|^\+[_'$_ble_complete_option_chars']*$' 5320 [[ $COMPV =~ $rex ]] || return 0 5321 fi 5322 5323 local COMPS=$COMPS COMPV=$COMPV 5324 ble/complete/source/reduce-compv-for-ambiguous-match 5325 [[ :$comp_type: == *:[maA]:* ]] && local COMP2=$COMP1 5326 5327 local comp_words comp_line comp_point comp_cword 5328 ble/syntax:bash/extract-command "$COMP2" || return 1 5329 5330 ble/complete/source:option/generate-for-command "${comp_words[@]::comp_cword}" 5331 } 5332 5333 ## @fn ble/complete/source:option/generate-for-command command prev_args... 5334 ## This function generates the option names based on man pages. 5335 ## 5336 ## @param[in] command 5337 ## The command name 5338 ## @param[in] prev_args 5339 ## The previous arguments before the word we currently try to complete. 5340 ## 5341 ## For example, when one would like to generate the option 5342 ## candidates for "cmd abc def ghi -xx[TAB]", command is "cmd", and 5343 ## prev_args are "abc" "def" "ghi". 5344 ## 5345 ## @var[in] COMP1 COMP2 COMPV COMPS comp_type 5346 ## These variables carry the information on the completion 5347 ## context. [COMP1, COMP2] specifies the range of the complete 5348 ## target in the command-line text. COMPS is the word to 5349 ## complete. COMPV is, if available, its current value after 5350 ## evaluation. The variable "comp_type" contains additional flags 5351 ## for the completion context. 5352 ## @var[ref] cand_iloop 5353 ## 5354 function ble/complete/source:option/generate-for-command { 5355 local cmd=$1 prev_args 5356 prev_args=("${@:2}") 5357 5358 local alias_checked=' ' 5359 while local ret; ! ble/complete/mandb/load-cache "$cmd"; do 5360 alias_checked=$alias_checked$cmd' ' 5361 ble/alias#expand "$cmd" || return 1 5362 local words; ble/string#split-words ret "$ret"; words=("${ret[@]}") 5363 5364 # 変数代入は読み飛ばし 5365 local iword=0 rex='^[_a-zA-Z][_a-zA-Z0-9]*\+?=' 5366 while [[ ${words[iword]} =~ $rex ]]; do ((iword++)); done 5367 [[ ${words[iword]} && $alias_checked != *" ${words[iword]} "* ]] || return 1 5368 prev_args=("${words[@]:iword+1}" "${prev_args[@]}") 5369 cmd=${words[iret]} 5370 done 5371 local -a entries; entries=("${ret[@]}") 5372 5373 local ret cmdspec_opts= 5374 ble/syntax:bash/simple-word/is-simple "$cmd" && 5375 ble/syntax:bash/simple-word/eval "$cmd" noglob && 5376 ble/cmdspec/opts#load "$ret" 5377 # "--" や非オプション引数など、オプション無効化条件をチェック 5378 ble/complete/source:option/.is-option-context "${prev_args[@]}" || return 1 5379 5380 local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 5381 ble/complete/cand/yield.initialize mandb 5382 local entry fs=$_ble_term_FS has_desc= 5383 for entry in "${entries[@]}"; do 5384 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && 5385 ble/complete/check-cancel && return 148 5386 local CAND=${entry%%$fs*} 5387 [[ $CAND == "$COMPV"* ]] || continue 5388 ble/complete/cand/yield mandb "$CAND" "$entry" 5389 [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 5390 done 5391 5392 [[ $has_desc && :$opts: != *:empty:* ]] && bleopt complete_menu_style=desc 5393 } 5394 5395 #------------------------------------------------------------------------------ 5396 # source:argument 5397 5398 ## @fn ble/complete/source:argument/.generate-user-defined-completion opts 5399 ## ユーザ定義の補完を実行します。ble/cmdinfo/complete:コマンド名 5400 ## という関数が定義されている場合はそれを使います。 5401 ## それ以外の場合は complete によって登録されているプログラム補完が使用されます。 5402 ## 5403 ## @param[in] opts 5404 ## コロン区切りのオプションリストを指定します。 5405 ## initial ... 最初の単語(コマンド名)の補完である事を示します。 5406 ## @var[in] COMP1 COMP2 5407 ## @var[in] (variables set by ble/syntax/parse) 5408 ## 5409 function ble/complete/source:argument/.generate-user-defined-completion { 5410 shopt -q progcomp || return 1 5411 5412 [[ :$comp_type: == *:[maA]:* ]] && local COMP2=$COMP1 5413 5414 local comp_words comp_line comp_point comp_cword 5415 ble/syntax:bash/extract-command "$COMP2" || return 1 5416 5417 # @var comp2_in_word 単語内のカーソルの位置 5418 # @var comp1_in_word 単語内の補完開始点 5419 local forward_words= 5420 ((comp_cword)) && IFS=' ' builtin eval 'forward_words="${comp_words[*]::comp_cword} "' 5421 local comp2_in_word=$((comp_point-${#forward_words})) 5422 local comp1_in_word=$((comp2_in_word-(COMP2-COMP1))) 5423 5424 # 単語の途中に補完開始点がある時、単語を分割する 5425 if ((comp1_in_word>0)); then 5426 local w=${comp_words[comp_cword]} 5427 comp_words=("${comp_words[@]::comp_cword}" "${w::comp1_in_word}" "${w:comp1_in_word}" "${comp_words[@]:comp_cword+1}") 5428 IFS=' ' builtin eval 'comp_line="${comp_words[*]}"' 5429 ((comp_cword++,comp_point++)) 5430 ((comp2_in_word=COMP2-COMP1,comp1_in_word=0)) 5431 fi 5432 5433 # 曖昧補完の場合は単語の内容を reduce する #D1413 5434 if [[ $COMPV && :$comp_type: == *:[maA]:* ]]; then 5435 local oword=${comp_words[comp_cword]::comp2_in_word} ins 5436 local ins=; [[ :$comp_type: == *:a:* ]] && ins=${COMPV::1} 5437 5438 # escape ins 5439 local ret comps_flags= comps_fixed= # referenced in ble/complete/string#escape-for-completion-context 5440 if [[ $oword ]]; then 5441 # Note: 実は曖昧補完の時は COMP2=$COMP1 としていて、 5442 # 更に COMP1 で単語分割しているのでここには入らない筈。 5443 local simple_flags simple_ibrace 5444 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$oword" || return 1 5445 comps_flags=v$simple_flags 5446 ((${simple_ibrace%:*})) && comps_fixed=1 5447 fi 5448 ble/complete/string#escape-for-completion-context "$ins" c; ins=$ret 5449 ble/util/unlocal comps_flags comps_fixed 5450 5451 # rewrite 5452 ((comp_point+=${#ins})) 5453 comp_words=("${comp_words[@]::comp_cword}" "$oword$ins" "${comp_words[@]:comp_cword+1}") 5454 IFS=' ' builtin eval 'comp_line="${comp_words[*]}"' 5455 ((comp2_in_word+=${#ins})) 5456 fi 5457 5458 local opts=$1 5459 if [[ :$opts: == *:initial:* ]]; then 5460 ble/complete/progcomp/.compgen initial 5461 else 5462 ble/complete/progcomp "${comp_words[0]}" 5463 fi 5464 } 5465 5466 function ble/complete/source:argument/generate { 5467 local old_cand_count=$cand_count 5468 5469 #---------------------------------------------------------------------------- 5470 # 1. Attempt user-defined completion 5471 ble/complete/source:argument/.generate-user-defined-completion; local ext=$? 5472 ((ext==148||cand_count>old_cand_count)) && return "$ext" 5473 [[ $comp_opts == *:ble/no-default:* ]] && return "$ext" 5474 5475 #---------------------------------------------------------------------------- 5476 # 2. Attempt built-in argument completion 5477 5478 # "-option" の時は complete options based on mandb 5479 ble/complete/source:option; local ext=$? 5480 ((ext==148)) && return "$ext" 5481 5482 # 候補が見付からない場合 (または曖昧補完で COMPV に / が含まれる場合) 5483 if [[ $comp_opts == *:dirnames:* ]]; then 5484 ble/complete/source:dir 5485 else 5486 # filenames, default, bashdefault 5487 ble/complete/source:file 5488 fi; local ext=$? 5489 ((ext==148)) && return "$ext" 5490 5491 # 空文字列に対するオプション生成はファイル名よりも後で試みる 5492 ble/complete/source:option empty; local ext=$? 5493 ((ext==148||cand_count>old_cand_count)) && return "$ext" 5494 5495 #---------------------------------------------------------------------------- 5496 # 3. Attempt rhs completion 5497 5498 if local rex='^/?[-_a-zA-Z0-9.]+\+?[:=]|^-[^-/=:]'; [[ $COMPV =~ $rex ]]; then 5499 # var=filename --option=filename /I:filename など。 5500 local prefix=$BASH_REMATCH value=${COMPV:${#BASH_REMATCH}} 5501 local COMP_PREFIX=$prefix 5502 [[ :$comp_type: != *:[maA]:* && $value =~ ^.+/ ]] && 5503 COMP_PREFIX=$prefix${BASH_REMATCH[0]} 5504 5505 local ret cand "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 5506 ble/complete/source:file/.construct-pathname-pattern "$value" 5507 ble/complete/util/eval-pathname-expansion "$ret"; (($?==148)) && return 148 5508 ble/complete/source/test-limit "${#ret[@]}" || return 1 5509 ble/complete/cand/yield.initialize file_rhs 5510 for cand in "${ret[@]}"; do 5511 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 5512 [[ -e $cand || -h $cand ]] || continue 5513 [[ $FIGNORE ]] && ! ble/complete/.fignore/filter "$cand" && continue 5514 ble/complete/cand/yield file_rhs "$prefix$cand" "$prefix" 5515 done 5516 fi 5517 5518 ((cand_count>old_cand_count)) 5519 } 5520 5521 function ble/complete/source:argument { 5522 local comp_opts=: 5523 5524 # failglob で展開に失敗した時は * を付加して再度展開を試みる 5525 if [[ $comps_flags == *f* && $COMPS != *\* && :$comp_type: != *:[maA]:* ]]; then 5526 local ret simple_flags simple_ibrace 5527 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$COMPS" 5528 ble/complete/source/eval-simple-word "$ret*" && ((${#ret[*]})) && 5529 ble/complete/cand/yield-filenames file "${ret[@]}" 5530 (($?==148)) && return 148 5531 fi 5532 5533 ble/complete/source:argument/generate 5534 local ext=$? 5535 ((ext==148)) && return 148 5536 [[ $comp_opts == *:ble/no-default:* ]] && return "$ext" 5537 5538 ble/complete/source:sabbrev 5539 } 5540 5541 # source:variable 5542 # source:user 5543 # source:hostname 5544 5545 function ble/complete/source/compgen { 5546 [[ $comps_flags == *v* ]] || return 1 5547 local COMPS=$COMPS COMPV=$COMPV 5548 ble/complete/source/reduce-compv-for-ambiguous-match 5549 5550 local compgen_action=$1 5551 local action=$2 5552 local data=$3 5553 5554 local q="'" Q="'\''" 5555 local compv_quoted="'${COMPV//$q/$Q}'" 5556 local arr 5557 ble/util/assign-array arr 'builtin compgen -A "$compgen_action" -- "$compv_quoted"' 5558 5559 ble/complete/source/test-limit "${#arr[@]}" || return 1 5560 5561 # 既に完全一致している場合は、より前の起点から補完させるために省略 5562 [[ $1 != '=' && ${#arr[@]} == 1 && $arr == "$COMPV" ]] && return 0 5563 5564 local cand "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 5565 ble/complete/cand/yield.initialize "$action" 5566 for cand in "${arr[@]}"; do 5567 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 5568 ble/complete/cand/yield "$action" "$cand" "$data" 5569 done 5570 } 5571 5572 function ble/complete/source:variable { 5573 local data= 5574 case $1 in 5575 ('=') data=assignment ;; 5576 ('b') data=braced ;; 5577 ('a') data=arithmetic ;; 5578 ('n') data=nosuffix ;; 5579 ('w'|*) data=word ;; 5580 esac 5581 ble/complete/source/compgen variable variable "$data" 5582 } 5583 function ble/complete/source:user { 5584 ble/complete/source/compgen user word 5585 } 5586 function ble/complete/source:hostname { 5587 ble/complete/source/compgen hostname word 5588 } 5589 5590 #------------------------------------------------------------------------------ 5591 # context 5592 5593 ## @fn ble/complete/complete/determine-context-from-opts opts 5594 ## @param[in] opts 5595 ## @var[out] context 5596 function ble/complete/complete/determine-context-from-opts { 5597 local opts=$1 5598 context=syntax 5599 if local rex=':context=([^:]+):'; [[ :$opts: =~ $rex ]]; then 5600 local rematch1=${BASH_REMATCH[1]} 5601 if ble/is-function ble/complete/context:"$rematch1"/generate-sources; then 5602 context=$rematch1 5603 else 5604 ble/util/print "ble/widget/complete: unknown context '$rematch1'" >&2 5605 fi 5606 fi 5607 } 5608 ## @fn ble/complete/context/filter-prefix-sources 5609 ## @var[in] comp_text comp_index 5610 ## @var[in,out] sources 5611 function ble/complete/context/filter-prefix-sources { 5612 # 現在位置より前に始まる補完文脈だけを選択する 5613 local -a filtered_sources=() 5614 local src asrc 5615 for src in "${sources[@]}"; do 5616 ble/string#split-words asrc "$src" 5617 local comp1=${asrc[1]} 5618 ((comp1<comp_index)) && 5619 ble/array#push filtered_sources "$src" 5620 done 5621 sources=("${filtered_sources[@]}") 5622 ((${#sources[@]})) 5623 } 5624 ## @fn ble/complete/context/overwrite-sources source 5625 ## @param[in] source 5626 ## @var[in] comp_text comp_index 5627 ## @var[in,out] comp_type 5628 ## @var[in,out] sources 5629 function ble/complete/context/overwrite-sources { 5630 local source_name=$1 5631 local -a new_sources=() 5632 local src asrc mark 5633 for src in "${sources[@]}"; do 5634 ble/string#split-words asrc "$src" 5635 [[ ${mark[asrc[1]]} ]] && continue 5636 ble/array#push new_sources "$source_name ${asrc[1]}" 5637 mark[asrc[1]]=1 5638 done 5639 ((${#new_sources[@]})) || 5640 ble/array#push new_sources "$source_name $comp_index" 5641 sources=("${new_sources[@]}") 5642 } 5643 5644 ## @fn ble/complete/context:syntax/generate-sources comp_text comp_index 5645 ## @var[in] comp_text comp_index 5646 ## @var[out] sources 5647 function ble/complete/context:syntax/generate-sources { 5648 ble/syntax/import 5649 ble-edit/content/update-syntax 5650 ble/cmdspec/initialize # load user configruation 5651 ble/syntax/completion-context/generate "$comp_text" "$comp_index" 5652 ((${#sources[@]})) 5653 } 5654 function ble/complete/context:filename/generate-sources { 5655 ble/complete/context:syntax/generate-sources || return "$?" 5656 ble/complete/context/overwrite-sources file 5657 } 5658 function ble/complete/context:command/generate-sources { 5659 ble/complete/context:syntax/generate-sources || return "$?" 5660 ble/complete/context/overwrite-sources command 5661 } 5662 function ble/complete/context:variable/generate-sources { 5663 ble/complete/context:syntax/generate-sources || return "$?" 5664 ble/complete/context/overwrite-sources variable 5665 } 5666 function ble/complete/context:username/generate-sources { 5667 ble/complete/context:syntax/generate-sources || return "$?" 5668 ble/complete/context/overwrite-sources user 5669 } 5670 function ble/complete/context:hostname/generate-sources { 5671 ble/complete/context:syntax/generate-sources || return "$?" 5672 ble/complete/context/overwrite-sources hostname 5673 } 5674 5675 function ble/complete/context:glob/generate-sources { 5676 comp_type=$comp_type:raw 5677 ble/complete/context:syntax/generate-sources || return "$?" 5678 ble/complete/context/overwrite-sources glob 5679 } 5680 function ble/complete/source:glob { 5681 [[ $comps_flags == *v* ]] || return 1 5682 [[ :$comp_type: == *:[maA]:* ]] && return 1 5683 5684 local pattern=$COMPV 5685 ble/complete/source/eval-simple-word "$pattern"; (($?==148)) && return 148 5686 if ((!${#ret[@]})) && [[ $pattern != *'*' ]]; then 5687 ble/complete/source/eval-simple-word "$pattern*"; (($?==148)) && return 148 5688 fi 5689 5690 local cand action=file "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 5691 ble/complete/cand/yield.initialize "$action" 5692 for cand in "${ret[@]}"; do 5693 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 5694 ble/complete/cand/yield "$action" "$cand" 5695 done 5696 } 5697 5698 function ble/complete/context:dynamic-history/generate-sources { 5699 comp_type=$comp_type:raw 5700 ble/complete/context:syntax/generate-sources || return "$?" 5701 ble/complete/context/overwrite-sources dynamic-history 5702 } 5703 function ble/complete/source:dynamic-history { 5704 [[ $comps_flags == *v* ]] || return 1 5705 [[ :$comp_type: == *:[maA]:* ]] && return 1 5706 [[ $COMPV ]] || return 1 5707 5708 local wordbreaks; ble/complete/get-wordbreaks 5709 wordbreaks=${wordbreaks//$'\n'} 5710 5711 local ret; ble/string#escape-for-extended-regex "$COMPV" 5712 local rex_needle='(^|['$wordbreaks'])'$ret'[^'$wordbreaks']+' 5713 local rex_wordbreaks='['$wordbreaks']' 5714 ble/util/assign-array ret 'HISTTIMEFORMAT= builtin history | ble/bin/grep -Eo "$rex_needle" | ble/bin/sed "s/^$rex_wordbreaks//" | ble/bin/sort -u' 5715 5716 local cand action=literal-word "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 5717 ble/complete/cand/yield.initialize "$action" 5718 for cand in "${ret[@]}"; do 5719 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 5720 ble/complete/cand/yield "$action" "$cand" 5721 done 5722 } 5723 5724 # 5725 #============================================================================== 5726 # 候補生成 5727 5728 ## @var[out] cand_count 5729 ## 候補の数 5730 ## @arr[out] cand_cand 5731 ## 候補文字列 5732 ## @arr[out] cand_word 5733 ## 挿入文字列 (~ エスケープされた候補文字列) 5734 ## 5735 ## @arr[out] cand_pack 5736 ## 補完候補のデータを一つの配列に纏めたもの。 5737 ## 要素を使用する際は以下の様に変数に展開して使う。 5738 ## 5739 ## local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 5740 ## ble/complete/cand/unpack "${cand_pack[0]}" 5741 ## 5742 ## 先頭に ACTION が格納されているので 5743 ## ACTION だけ参照する場合には以下の様にする。 5744 ## 5745 ## local ACTION=${cand_pack[0]%%:*} 5746 ## 5747 5748 ## @fn ble/complete/util/construct-ambiguous-regex text fixlen 5749 ## 曖昧一致に使う正規表現を生成します。 5750 ## @param[in] text 5751 ## @param[in,out] fixlen=1 5752 ## @var[in] comp_type 5753 ## @var[out] ret 5754 function ble/complete/util/construct-ambiguous-regex { 5755 local text=$1 fixlen=${2:-1} 5756 local opt_icase=; [[ :$comp_type: == *:i:* ]] && opt_icase=1 5757 local -a buff=() 5758 local i=0 n=${#text} ch= 5759 for ((i=0;i<n;i++)); do 5760 ((i>=fixlen)) && ble/array#push buff '.*' 5761 ch=${text:i:1} 5762 if [[ $ch == [a-zA-Z] ]]; then 5763 if [[ $opt_icase ]]; then 5764 ble/string#toggle-case "$ch" 5765 ch=[$ch$ret] 5766 fi 5767 else 5768 ble/string#escape-for-extended-regex "$ch"; ch=$ret 5769 fi 5770 ble/array#push buff "$ch" 5771 done 5772 IFS= builtin eval 'ret="${buff[*]}"' 5773 } 5774 ## @fn ble/complete/util/construct-glob-pattern text 5775 ## 部分一致に使うグロブを生成します。 5776 function ble/complete/util/construct-glob-pattern { 5777 local text=$1 5778 if [[ :$comp_type: == *:i:* ]]; then 5779 local i n=${#text} c 5780 local -a buff=() 5781 for ((i=0;i<n;i++)); do 5782 c=${text:i:1} 5783 if [[ $c == [a-zA-Z] ]]; then 5784 ble/string#toggle-case "$c" 5785 c=[$c$ret] 5786 else 5787 ble/string#escape-for-bash-glob "$c"; c=$ret 5788 fi 5789 ble/array#push buff "$c" 5790 done 5791 IFS= builtin eval 'ret="${buff[*]}"' 5792 else 5793 ble/string#escape-for-bash-glob "$1" 5794 fi 5795 } 5796 5797 5798 function ble/complete/.fignore/prepare { 5799 comp_fignore=() 5800 local i=0 leaf tmp 5801 ble/string#split tmp ':' "$FIGNORE" 5802 for leaf in "${tmp[@]}"; do 5803 [[ $leaf ]] && comp_fignore[i++]="$leaf" 5804 done 5805 } 5806 function ble/complete/.fignore/filter { 5807 local pat 5808 for pat in "${comp_fignore[@]}"; do 5809 [[ $1 == *"$pat" ]] && return 1 5810 done 5811 return 0 5812 } 5813 5814 ## @fn ble/complete/candidates/.pick-nearest-sources 5815 ## 一番開始点に近い補完源の一覧を求めます。 5816 ## 5817 ## @var[in] comp_index 5818 ## @arr[in,out] remaining_sources 5819 ## @arr[out] nearest_sources 5820 ## @var[out] COMP1 COMP2 5821 ## 補完範囲 5822 ## @var[out] COMPS 5823 ## 補完範囲の (クオートが含まれうる) コマンド文字列 5824 ## @var[out] COMPV 5825 ## 補完範囲のコマンド文字列が意味する実際の文字列 5826 ## @var[out] comps_flags comps_fixed 5827 function ble/complete/candidates/.pick-nearest-sources { 5828 COMP1= COMP2=$comp_index 5829 nearest_sources=() 5830 5831 local -a unused_sources=() 5832 local src asrc 5833 for src in "${remaining_sources[@]}"; do 5834 ble/string#split-words asrc "$src" 5835 if ((COMP1<asrc[1])); then 5836 COMP1=${asrc[1]} 5837 ble/array#push unused_sources "${nearest_sources[@]}" 5838 nearest_sources=("$src") 5839 elif ((COMP1==asrc[1])); then 5840 ble/array#push nearest_sources "$src" 5841 else 5842 ble/array#push unused_sources "$src" 5843 fi 5844 done 5845 remaining_sources=("${unused_sources[@]}") 5846 5847 COMPS=${comp_text:COMP1:COMP2-COMP1} 5848 comps_flags= 5849 comps_fixed=('') 5850 5851 if [[ ! $COMPS ]]; then 5852 comps_flags=${comps_flags}v COMPV= 5853 elif local ret simple_flags simple_ibrace; ble/syntax:bash/simple-word/reconstruct-incomplete-word "$COMPS"; then 5854 local reconstructed=$ret 5855 if [[ :$comp_type: == *:raw:* ]]; then 5856 # 展開前の値を COMPV に格納する。ブレース展開内部の場合は失敗 5857 if ((${simple_ibrace%:*})); then 5858 COMPV= 5859 else 5860 comps_flags=$comps_flags${simple_flags}v 5861 COMPV=$reconstructed 5862 fi 5863 elif ble/complete/source/eval-simple-word "$reconstructed"; local ext=$?; ((ext==148)) && return 148; ((ext==0)); then 5864 # 展開後の値を COMPV に格納する (既定) 5865 COMPV=("${ret[@]}") 5866 comps_flags=$comps_flags${simple_flags}v 5867 5868 if ((${simple_ibrace%:*})); then 5869 ble/complete/source/eval-simple-word "${reconstructed::${simple_ibrace#*:}}" single; (($?==148)) && return 148 5870 comps_fixed=${simple_ibrace%:*}:$ret 5871 comps_flags=${comps_flags}x 5872 fi 5873 5874 local path spec i s 5875 ble/syntax:bash/simple-word/evaluate-path-spec "$reconstructed" '' noglob:fixlen="${simple_ibrace#*:}" 5876 for ((i=0;i<${#spec[@]};i++)); do 5877 s=${spec[i]} 5878 [[ $s == "$comps_fixed" || $s == "$reconstructed" ]] && continue 5879 ble/array#push comps_fixed "${#s}:${path[i]}" 5880 done 5881 else 5882 # Note: failglob により simple-word/eval が失敗した時にここに来る。 5883 COMPV= 5884 comps_flags=$comps_flags${simple_flags}f 5885 fi 5886 [[ $COMPS =~ $rex_raw_paramx ]] && comps_flags=${comps_flags}p 5887 5888 else 5889 COMPV= 5890 fi 5891 } 5892 5893 function ble/complete/candidates/clear { 5894 cand_count=0 5895 cand_cand=() 5896 cand_word=() 5897 cand_pack=() 5898 } 5899 5900 ## @fn ble/complete/candidates/filter-by-command command [start] 5901 ## 生成された候補 (cand_*) に対して指定したコマンドを実行し、 5902 ## 成功した候補のみを残して他を削除します。 5903 ## @param[in] command 5904 ## @param[in,opt] start 5905 ## @var[in,out] cand_count 5906 ## @arr[in,out] cand_{prop,cand,word,show,data} 5907 ## @exit 5908 ## ユーザ入力によって中断された時に 148 を返します。 5909 function ble/complete/candidates/filter-by-command { 5910 local command=$1 start=${2:-0} 5911 # todo: 複数の配列に触る非効率な実装だが後で考える 5912 local i j=$start 5913 local -a prop=() cand=() word=() show=() data=() 5914 for ((i=start;i<cand_count;i++)); do 5915 ((i%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 5916 builtin eval -- "$command" || continue 5917 cand[j]=${cand_cand[i]} 5918 word[j]=${cand_word[i]} 5919 data[j]=${cand_pack[i]} 5920 ((j++)) 5921 done 5922 cand_count=$j 5923 cand_cand=("${cand[@]}") 5924 cand_word=("${word[@]}") 5925 cand_pack=("${data[@]}") 5926 } 5927 ## @fn ble/complete/candidates/.filter-by-regex rex_filter 5928 ## 生成された候補 (cand_*) において指定した正規表現に一致する物だけを残します。 5929 ## @param[in] rex_filter 5930 ## @var[in,out] cand_count 5931 ## @arr[in,out] cand_{prop,cand,word,show,data} 5932 ## @exit 5933 ## ユーザ入力によって中断された時に 148 を返します。 5934 function ble/complete/candidates/.filter-by-regex { 5935 local rex_filter=$1 5936 ble/complete/candidates/filter-by-command '[[ ${cand_cand[i]} =~ $rex_filter ]]' 5937 } 5938 function ble/complete/candidates/.filter-by-glob { 5939 local globpat=$1 5940 ble/complete/candidates/filter-by-command '[[ ${cand_cand[i]} == $globpat ]]' 5941 } 5942 function ble/complete/candidates/.filter-word-by-prefix { 5943 local prefix=$1 5944 ble/complete/candidates/filter-by-command '[[ ${cand_word[i]} == "$prefix"* ]]' 5945 } 5946 5947 function ble/complete/candidates/.initialize-rex_raw_paramx { 5948 local element=$_ble_syntax_bash_simple_rex_element 5949 local open_dquot=$_ble_syntax_bash_simple_rex_open_dquot 5950 rex_raw_paramx='^('$element'*('$open_dquot')?)\$[_a-zA-Z][_a-zA-Z0-9]*$' 5951 } 5952 5953 ## 候補フィルタ (candidate filters) は以下の関数を通して実装される。 5954 ## 5955 ## @fn ble/complete/candidates/filter:FILTER_TYPE/init compv 5956 ## @fn ble/complete/candidates/filter:FILTER_TYPE/test cand 5957 ## @var[in] comp_filter_type 5958 ## @var[in,out] comp_filter_pattern 5959 ## 5960 ## @fn ble/complete/candidates/filter:FILTER_TYPE/match needle text 5961 ## @param[in] needle text 5962 ## 5963 ## 関数 ble/complete/candidates/filter:FILTER_TYPE/count-match-chars value 5964 ## @var[in] COMPV 5965 ## 5966 ## 使用するときには以下の関数を通して呼び出す (match, count-match-chars は直接呼び出す)。 5967 ## 5968 ## @fn ble/complete/candidates/filter#init type compv 5969 ## @fn ble/complete/candidates/filter#test value 5970 ## @var[in,out] comp_filter_type 5971 ## @var[in,out] comp_filter_pattern 5972 ## 5973 function ble/complete/candidates/filter#init { 5974 comp_filter_type=$1 5975 comp_filter_pattern= 5976 ble/complete/candidates/filter:"$comp_filter_type"/init "$2" 5977 } 5978 function ble/complete/candidates/filter#test { 5979 ble/complete/candidates/filter:"$comp_filter_type"/test "$1" 5980 } 5981 5982 function ble/complete/candidates/filter:none/init { ble/complete/candidates/filter:head/init "$@"; } 5983 function ble/complete/candidates/filter:none/test { true; } 5984 function ble/complete/candidates/filter:none/count-match-chars { ble/complete/candidates/filter:head/count-match-chars "$@"; } 5985 function ble/complete/candidates/filter:none/match { ble/complete/candidates/filter:head/match "$@"; } 5986 5987 function ble/complete/candidates/filter:head/init { 5988 local ret; ble/complete/util/construct-glob-pattern "$1" 5989 comp_filter_pattern=$ret* 5990 } 5991 function ble/complete/candidates/filter:head/count-match-chars { # unused but for completeness 5992 local value=$1 compv=$COMPV 5993 if [[ :$comp_type: == *:i:* ]]; then 5994 ble/string#tolower "$value"; value=$ret 5995 ble/string#tolower "$compv"; compv=$ret 5996 fi 5997 5998 if [[ $value == "$compv"* ]]; then 5999 ret=${#compv} 6000 elif [[ $compv == "$value"* ]]; then 6001 ret=${#value} 6002 else 6003 ret=0 6004 fi 6005 } 6006 function ble/complete/candidates/filter:head/test { [[ $1 == $comp_filter_pattern ]]; } 6007 6008 ## @fn ble/complete/candidates/filter:head/match needle text 6009 ## @arr[out] ret 6010 function ble/complete/candidates/filter:head/match { 6011 local needle=$1 text=$2 6012 if [[ :$comp_type: == *:i:* ]]; then 6013 ble/string#tolower "$needle"; needle=$ret 6014 ble/string#tolower "$text"; text=$ret 6015 fi 6016 6017 if [[ ! $needle || ! $text ]]; then 6018 ret=() 6019 elif [[ $text == "$needle"* ]]; then 6020 ret=(0 "${#needle}") 6021 return 0 6022 elif [[ $text == "${needle::${#text}}" ]]; then 6023 ret=(0 "${#text}") 6024 return 0 6025 else 6026 ret=() 6027 return 1 6028 fi 6029 } 6030 6031 function ble/complete/candidates/filter:substr/init { 6032 local ret; ble/complete/util/construct-glob-pattern "$1" 6033 comp_filter_pattern=*$ret* 6034 } 6035 function ble/complete/candidates/filter:substr/count-match-chars { 6036 local value=$1 compv=$COMPV 6037 if [[ :$comp_type: == *:i:* ]]; then 6038 ble/string#tolower "$value"; value=$ret 6039 ble/string#tolower "$compv"; compv=$ret 6040 fi 6041 6042 if [[ $value == *"$compv"* ]]; then 6043 ret=${#compv} 6044 return 0 6045 fi 6046 ble/complete/string#common-suffix-prefix "$value" "$compv" 6047 ret=${#ret} 6048 } 6049 function ble/complete/candidates/filter:substr/test { [[ $1 == $comp_filter_pattern ]]; } 6050 function ble/complete/candidates/filter:substr/match { 6051 local needle=$1 text=$2 6052 if [[ :$comp_type: == *:i:* ]]; then 6053 ble/string#tolower "$needle"; needle=$ret 6054 ble/string#tolower "$text"; text=$ret 6055 fi 6056 6057 if [[ ! $needle ]]; then 6058 ret=() 6059 elif [[ $text == *"$needle"* ]]; then 6060 text=${text%%"$needle"*} 6061 local beg=${#text} 6062 local end=$((beg+${#needle})) 6063 ret=("$beg" "$end") 6064 elif ble/complete/string#common-suffix-prefix "$text" "$needle"; ((${#ret})); then 6065 local end=${#text} 6066 local beg=$((end-${#ret})) 6067 ret=("$beg" "$end") 6068 else 6069 ret=() 6070 fi 6071 } 6072 6073 function ble/complete/candidates/filter:hsubseq/.determine-fixlen { 6074 fixlen=${1:-1} 6075 if [[ $comps_fixed ]]; then 6076 local compv_fixed_part=${comps_fixed#*:} 6077 [[ $compv_fixed_part ]] && fixlen=${#compv_fixed_part} 6078 fi 6079 } 6080 ## @fn ble/complete/candidates/filter:hsubseq/init compv [fixlen] 6081 ## @param[in] compv 6082 ## @param[in,opt] fixlen 6083 ## @var[in] comps_fixed 6084 ## @var[out] comp_filter_pattern 6085 function ble/complete/candidates/filter:hsubseq/init { 6086 local fixlen; ble/complete/candidates/filter:hsubseq/.determine-fixlen "$2" 6087 local ret; ble/complete/util/construct-ambiguous-regex "$1" "$fixlen" 6088 comp_filter_pattern=^$ret 6089 } 6090 ## @fn ble/complete/candidates/filter:hsubseq/count-match-chars value [fixlen] 6091 ## 指定した文字列が COMPV の何処まで一致するかを返します。 6092 ## @var[out] ret 6093 function ble/complete/candidates/filter:hsubseq/count-match-chars { 6094 local value=$1 compv=$COMPV 6095 if [[ :$comp_type: == *:i:* ]]; then 6096 ble/string#tolower "$value"; value=$ret 6097 ble/string#tolower "$compv"; compv=$ret 6098 fi 6099 6100 local fixlen 6101 ble/complete/candidates/filter:hsubseq/.determine-fixlen "$2" 6102 [[ $value == "${compv::fixlen}"* ]] || return 1 6103 6104 value=${value:fixlen} 6105 local i n=${#COMPV} 6106 for ((i=fixlen;i<n;i++)); do 6107 local a=${value%%"${compv:i:1}"*} 6108 [[ $a == "$value" ]] && { ret=$i; return 0; } 6109 value=${value:${#a}+1} 6110 done 6111 ret=$n 6112 } 6113 function ble/complete/candidates/filter:hsubseq/test { [[ $1 =~ $comp_filter_pattern ]]; } 6114 function ble/complete/candidates/filter:hsubseq/match { 6115 local needle=$1 text=$2 6116 if [[ :$comp_type: == *:i:* ]]; then 6117 ble/string#tolower "$needle"; needle=$ret 6118 ble/string#tolower "$text"; text=$ret 6119 fi 6120 6121 local fixlen; ble/complete/candidates/filter:hsubseq/.determine-fixlen "$3" 6122 6123 local prefix=${needle::fixlen} 6124 if [[ $text != "$prefix"* ]]; then 6125 if [[ $text && $text == "${prefix::${#text}}" ]]; then 6126 ret=(0 "${#text}") 6127 else 6128 ret=() 6129 fi 6130 return 0 6131 fi 6132 6133 local pN=${#text} iN=${#needle} 6134 local first=1 6135 ret=() 6136 while :; do 6137 if [[ $first ]]; then 6138 first= 6139 local p0=0 p=${#prefix} i=${#prefix} 6140 else 6141 ((i<iN)) || return 0 6142 6143 while ((p<pN)) && [[ ${text:p:1} != "${needle:i:1}" ]]; do 6144 ((p++)) 6145 done 6146 ((p<pN)) || return 1 6147 p0=$p 6148 fi 6149 6150 while ((i<iN&&p<pN)) && [[ ${text:p:1} == "${needle:i:1}" ]]; do 6151 ((p++,i++)) 6152 done 6153 ((p0<p)) && ble/array#push ret "$p0" "$p" 6154 done 6155 } 6156 6157 ## @fn ble/complete/candidates/filter:subseq/init compv 6158 ## @param[in] compv 6159 ## @var[in] comps_fixed 6160 ## @var[out] comp_filter_pattern 6161 function ble/complete/candidates/filter:subseq/init { 6162 [[ $comps_fixed ]] && return 1 6163 ble/complete/candidates/filter:hsubseq/init "$1" 0 6164 } 6165 function ble/complete/candidates/filter:subseq/count-match-chars { 6166 ble/complete/candidates/filter:hsubseq/count-match-chars "$1" 0 6167 } 6168 function ble/complete/candidates/filter:subseq/test { [[ $1 =~ $comp_filter_pattern ]]; } 6169 function ble/complete/candidates/filter:subseq/match { 6170 ble/complete/candidates/filter:hsubseq/match "$1" "$2" 0 6171 } 6172 6173 function ble/complete/candidates/generate-with-filter { 6174 local filter_type=$1 opts=$2 6175 local -a remaining_sources nearest_sources 6176 remaining_sources=("${sources[@]}") 6177 6178 local src asrc source 6179 while ((${#remaining_sources[@]})); do 6180 nearest_sources=() 6181 ble/complete/candidates/.pick-nearest-sources; (($?==148)) && return 148 6182 6183 [[ ! $COMPV && :$opts: == *:no-empty:* ]] && continue 6184 local comp_filter_type 6185 local comp_filter_pattern 6186 ble/complete/candidates/filter#init "$filter_type" "$COMPV" || continue 6187 6188 for src in "${nearest_sources[@]}"; do 6189 ble/string#split-words asrc "$src" 6190 ble/string#split source : "${asrc[0]}" 6191 6192 local COMP_PREFIX= # 既定値 (yield-candidate で参照) 6193 ble/complete/source:"${source[@]}" 6194 ble/complete/check-cancel && return 148 6195 done 6196 6197 [[ $comps_fixed ]] && 6198 ble/complete/candidates/.filter-word-by-prefix "${COMPS::${comps_fixed%%:*}}" 6199 ((cand_count)) && return 0 6200 done 6201 return 0 6202 } 6203 6204 function ble/complete/candidates/comp_type#read-rl-variables { 6205 local _ble_local_rlvars; ble/util/rlvar#load 6206 ble/util/rlvar#test completion-ignore-case 0 && comp_type=${comp_type}:i 6207 ble/util/rlvar#test visible-stats 0 && comp_type=${comp_type}:vstat 6208 ble/util/rlvar#test mark-directories 1 && comp_type=${comp_type}:markdir 6209 ble/util/rlvar#test mark-symlinked-directories 1 && comp_type=${comp_type}:marksymdir 6210 ble/util/rlvar#test match-hidden-files 1 && comp_type=${comp_type}:match-hidden 6211 ble/util/rlvar#test menu-complete-display-prefix 0 && comp_type=${comp_type}:menu-show-prefix 6212 6213 # color settings are always enabled 6214 comp_type=$comp_type${bleopt_complete_menu_color:+:menu-color} 6215 comp_type=$comp_type${bleopt_complete_menu_color_match:+:menu-color-match} 6216 } 6217 6218 ## @fn ble/complete/candidates/generate opts 6219 ## @param[in] opts 6220 ## @var[in] comp_text comp_index 6221 ## @arr[in] sources 6222 ## @var[out] COMP1 COMP2 COMPS COMPV 6223 ## @var[out] comp_type comps_flags comps_fixed 6224 ## @var[out] cand_count cand_cand cand_word cand_pack 6225 ## @var[in,out] cand_limit_reached 6226 function ble/complete/candidates/generate { 6227 local opts=$1 6228 local flag_force_fignore= 6229 local flag_source_filter= 6230 local -a comp_fignore=() 6231 if [[ $FIGNORE ]]; then 6232 ble/complete/.fignore/prepare 6233 ((${#comp_fignore[@]})) && shopt -q force_fignore && flag_force_fignore=1 6234 fi 6235 6236 local rex_raw_paramx 6237 ble/complete/candidates/.initialize-rex_raw_paramx 6238 ble/complete/candidates/comp_type#read-rl-variables 6239 6240 local cand_iloop=0 6241 ble/complete/candidates/clear 6242 # #D1416 filter:none にするのは ~[TAB] の時など COMPV ではなく COMPS で補完したい事がある為 6243 ble/complete/candidates/generate-with-filter none "$opts" || return "$?" 6244 ((cand_count)) && return 0 6245 6246 if [[ $bleopt_complete_ambiguous && $COMPV ]]; then 6247 local original_comp_type=$comp_type 6248 comp_type=${original_comp_type}:m 6249 ble/complete/candidates/generate-with-filter substr "$opts" || return "$?" 6250 ((cand_count)) && return 0 6251 comp_type=${original_comp_type}:a 6252 ble/complete/candidates/generate-with-filter hsubseq "$opts" || return "$?" 6253 ((cand_count)) && return 0 6254 comp_type=${original_comp_type}:A 6255 ble/complete/candidates/generate-with-filter subseq "$opts" || return "$?" 6256 ((cand_count)) && return 0 6257 comp_type=$original_comp_type 6258 fi 6259 6260 return 0 6261 } 6262 6263 ## @fn ble/complete/candidates/determine-common-prefix/.apply-partial-comps 6264 ## @var[in] COMPS 6265 ## @var[in] comps_fixed 6266 ## @var[in,out] common 6267 function ble/complete/candidates/determine-common-prefix/.apply-partial-comps { 6268 local word0=$COMPS word1=$common fixed= 6269 if [[ $comps_fixed ]]; then 6270 local fixlen=${comps_fixed%%:*} 6271 fixed=${word0::fixlen} 6272 word0=${word0:fixlen} 6273 word1=${word1:fixlen} 6274 fi 6275 6276 local ret spec path spec0 path0 spec1 path1 6277 ble/complete/source/evaluate-path-spec "$word0"; (($?==148)) && return 148; spec0=("${spec[@]}") path0=("${path[@]}") 6278 ble/complete/source/evaluate-path-spec "$word1"; (($?==148)) && return 148; spec1=("${spec[@]}") path1=("${path[@]}") 6279 local i=${#path1[@]} 6280 while ((i--)); do 6281 if ble/array#last-index path0 "${path1[i]}"; then 6282 local elem=${spec1[i]} # workaround bash-3.1 ${#arr[i]} bug 6283 word1=${spec0[ret]}${word1:${#elem}} 6284 break 6285 fi 6286 done 6287 common=$fixed$word1 6288 } 6289 6290 # Note (#D1978): progcomp (syntax-raw) による単一確定の場合には遡って書き換わっ 6291 # ている場合でも、元の単語の部分を復元しようとはしない。 6292 function ble/completion/candidates/determine-common-prefix/.is-progcomp-raw { 6293 ((cand_count==1)) && [[ ${cand_pack[0]} == progcomp:*:ble/syntax-raw:* ]] || return 0 6294 6295 # 念の為、本当に DATA に :ble/syntax-raw: が含まれている事を確認する 6296 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6297 ble/complete/cand/unpack "${cand_pack[0]}" 6298 [[ $DATA == *:ble/syntax-raw:* ]] 6299 } 6300 6301 ## @fn ble/complete/candidates/determine-common-prefix 6302 ## cand_* を元に common prefix を算出します。 6303 ## @var[in] cand_* 6304 ## @var[out] ret 6305 function ble/complete/candidates/determine-common-prefix { 6306 # 共通部分 6307 local common=${cand_word[0]} 6308 local clen=${#common} 6309 if ((cand_count>1)); then 6310 # set up ignore case 6311 local unset_nocasematch= flag_tolower= 6312 if [[ :$comp_type: == *:i:* ]]; then 6313 if ((_ble_bash<30100)); then 6314 flag_tolower=1 6315 ble/string#tolower "$common"; common=$ret 6316 else 6317 unset_nocasematch=1 6318 shopt -s nocasematch 6319 fi 6320 fi 6321 6322 local word loop=0 6323 for word in "${cand_word[@]:1}"; do 6324 ((loop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && break 6325 6326 if [[ $flag_tolower ]]; then 6327 ble/string#tolower "$word"; word=$ret 6328 fi 6329 6330 ((clen>${#word}&&(clen=${#word}))) 6331 while [[ ${word::clen} != "${common::clen}" ]]; do 6332 ((clen--)) 6333 done 6334 common=${common::clen} 6335 done 6336 6337 [[ $unset_nocasematch ]] && shopt -u nocasematch 6338 ble/complete/check-cancel && return 148 6339 6340 [[ $flag_tolower ]] && common=${cand_word[0]::${#common}} 6341 fi 6342 6343 if [[ $common != "$COMPS"* && ! ( $cand_count -eq 1 && $comp_type == *:i:* ) ]]; then 6344 if ! ble/completion/candidates/determine-common-prefix/.is-progcomp-raw; then 6345 # common を部分的に COMPS に置換する試み 6346 # Note: ignore-case で一意確定の時は case を候補に合わせたいので COMPS に 6347 # は置換しない。 6348 ble/complete/candidates/determine-common-prefix/.apply-partial-comps 6349 fi 6350 fi 6351 6352 if ((cand_count>1)) && [[ $common != "$COMPS"* ]]; then 6353 local common0=$common 6354 common=$COMPS # 取り敢えず補完挿入をキャンセル 6355 6356 if [[ :$comp_type: == *:[maAi]:* ]]; then 6357 # 曖昧一致の時は遡って書き換えを起こし得る、 6358 # 一致する部分までを置換し一致しなかった部分を末尾に追加する。 6359 6360 local simple_flags simple_ibrace 6361 if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$common0"; then 6362 local common_reconstructed=$ret 6363 local value=$ret filter_type=head 6364 case :$comp_type: in 6365 (*:m:*) filter_type=substr ;; 6366 (*:a:*) filter_type=hsubseq ;; 6367 (*:A:*) filter_type=subseq ;; 6368 esac 6369 6370 local is_processed= 6371 ble/complete/source/eval-simple-word "$common_reconstructed" single; local ext=$? 6372 ((ext==148)) && return 148 6373 if ((ext==0)) && ble/complete/candidates/filter:"$filter_type"/count-match-chars "$ret"; then 6374 if [[ $filter_type == head ]] && ((ret<${#COMPV})); then 6375 is_processed=1 6376 # Note: #D1181 ここに来たという事は外部の枠組みで 6377 # 生成された先頭一致しない候補があるという事。 6378 # 入力済み文字列が失われてしまう危険性を承知の上と思われるので書き換えを許可する。 6379 [[ $bleopt_complete_allow_reduction ]] && common=$common0 6380 elif ((ret)); then 6381 is_processed=1 6382 ble/string#escape-for-bash-specialchars "${COMPV:ret}" c 6383 common=$common0$ret 6384 fi 6385 fi 6386 6387 # #D1417 チルダ展開やパス名展開など途中で切ると全く異なる展開になる物について 6388 # より正しく処理する為に、完全解ではないが notilde, noglob でも部分一致を調べる。 6389 # 6390 # 例えば既に ~nouser と入力して共通一致部分が ~ だった時に 6391 # ~ の何処までが ~nouser に部分一致するか調べる時、チルダ展開が有効だと 6392 # ~ が /home/user に展開されてから部分一致が調べられる為、 6393 # 一致が起こらずに "~nouser" の全てが追加で挿入されて "~~nouser" になってしまう。 6394 # なのでチルダ展開・パス名展開を無効にして部分一致を試みる必要がある。 6395 if [[ ! $is_processed ]] && 6396 local notilde=\'\' && 6397 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$COMPS" && 6398 ble/syntax:bash/simple-word/eval "$notilde$ret" noglob && 6399 local compv_notilde=$ret && 6400 ble/syntax:bash/simple-word/eval "$notilde$common_reconstructed" noglob && 6401 local commonv_notilde=$ret && 6402 COMPV=$compv_notilde ble/complete/candidates/filter:"$filter_type"/count-match-chars "$commonv_notilde" 6403 then 6404 if [[ $filter_type == head ]] && ((ret<${#COMPV})); then 6405 is_processed=1 6406 [[ $bleopt_complete_allow_reduction ]] && common=$common0 6407 elif ((ret)); then 6408 # Note: 今の実装では展開結果に含まれている *?[ は全て glob として取 6409 # り扱う事になっている。つまり 'a*b' が曖昧部分一致した時には元々 6410 # の quote が外れて a*b になってしまうという事。これは現在の実装 6411 # の制限である。 6412 is_processed=1 6413 ble/string#escape-for-bash-specialchars "${compv_notilde:ret}" TG 6414 common=$common0$ret 6415 fi 6416 fi 6417 6418 [[ $is_processed ]] || common=$common0$COMPS 6419 fi 6420 6421 else 6422 # Note: #D0768 文法的に単純であれば (構造を破壊しなければ) 遡って書き換えが起こることを許す。 6423 # Note: #D1181 外部の枠組みで生成された先頭一致しない共通部分の時でも書き換えを許す。 6424 if ble/syntax:bash/simple-word/is-simple-or-open-simple "$common"; then 6425 local flag_reduction= 6426 if [[ $bleopt_complete_allow_reduction ]]; then 6427 flag_reduction=1 6428 else 6429 local simple_flags simple_ibrace 6430 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$common0" && 6431 ble/complete/source/eval-simple-word "$ret" single && 6432 [[ $ret == "$COMPV"* ]] && 6433 flag_reduction=1 6434 (($?==148)) && return 148 6435 fi 6436 6437 [[ $flag_reduction ]] && common=$common0 6438 fi 6439 fi 6440 fi 6441 6442 ret=$common 6443 } 6444 6445 # 6446 #============================================================================== 6447 # 候補一覧 6448 6449 _ble_complete_menu_active= 6450 _ble_complete_menu_style= 6451 _ble_complete_menu0_beg= 6452 _ble_complete_menu0_end= 6453 _ble_complete_menu0_str= 6454 _ble_complete_menu_common_part= 6455 _ble_complete_menu0_comp=() 6456 _ble_complete_menu0_pack=() 6457 _ble_complete_menu_comp=() 6458 6459 ## @fn ble/complete/menu-complete.class/render-item pack opts 6460 ## @param[in] pack 6461 ## cand_pack の要素と同様の形式の文字列です。 6462 ## @param[in] opts 6463 ## コロン区切りのオプションです。 6464 ## selected 6465 ## 選択されている候補の描画シーケンスを生成します。 6466 ## @var[in,out] x y 6467 ## @var[out] ret 6468 ## @var[in] cols lines 6469 ## @var[in] _ble_complete_menu_common_part 6470 function ble/complete/menu-complete.class/render-item { 6471 local opts=$2 6472 6473 # Note: select は menu 表示の文脈ではないので、 6474 # 補完文脈を復元しなければ参照できない。 6475 if [[ :$opts: == *:selected:* ]]; then 6476 local COMP1=${_ble_complete_menu_comp[0]} 6477 local COMP2=${_ble_complete_menu_comp[1]} 6478 local COMPS=${_ble_complete_menu_comp[2]} 6479 local COMPV=${_ble_complete_menu_comp[3]} 6480 local comp_type=${_ble_complete_menu_comp[4]} 6481 local comps_flags=${_ble_complete_menu0_comp[5]} 6482 local comps_fixed=${_ble_complete_menu0_comp[6]} 6483 local menu_common_part=$_ble_complete_menu_common_part 6484 fi 6485 6486 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6487 ble/complete/cand/unpack "$1" 6488 6489 local prefix_len=$PREFIX_LEN 6490 [[ :$comp_type: == *:menu-show-prefix:* ]] && prefix_len=0 6491 6492 local filter_target=${CAND:prefix_len} 6493 if [[ ! $filter_target ]]; then 6494 ret= 6495 return 0 6496 fi 6497 6498 # 色の設定・表示内容・前置詞・後置詞を取得 6499 local g=0 show=$filter_target suffix= prefix= 6500 ble/function#try ble/complete/action:"$ACTION"/init-menu-item 6501 local g0=$g; [[ :$comp_type: == *:menu-color:* ]] || g0=0 6502 6503 # 一致部分の抽出 6504 local m 6505 if [[ :$comp_type: == *:menu-color-match:* && $_ble_complete_menu_common_part && $show == *"$filter_target"* ]]; then 6506 local filter_type=head 6507 case :$comp_type: in 6508 (*:m:*) filter_type=substr ;; 6509 (*:a:*) filter_type=hsubseq ;; 6510 (*:A:*) filter_type=subseq ;; 6511 esac 6512 6513 local needle=${_ble_complete_menu_common_part:prefix_len} 6514 ble/complete/candidates/filter:"$filter_type"/match "$needle" "$filter_target"; m=("${ret[@]}") 6515 6516 # 表示文字列の部分文字列で絞り込みが起こっている場合 6517 if [[ $show != "$filter_target" ]]; then 6518 local show_prefix=${show%%"$filter_target"*} 6519 local offset=${#show_prefix} 6520 local i n=${#m[@]} 6521 for ((i=0;i<n;i++)); do ((m[i]+=offset)); done 6522 fi 6523 else 6524 m=() 6525 fi 6526 6527 # 基本色の初期化 (Note: 高速化の為、直接 _ble_color_g2sgr を参照する) 6528 local sgrN0= sgrN1= sgrB0= sgrB1= 6529 [[ :$opts: == *:selected:* ]] && ((g0^=_ble_color_gflags_Revert)) 6530 ret=${_ble_color_g2sgr[g=g0]} 6531 [[ $ret ]] || ble/color/g2sgr "$g"; sgrN0=$ret 6532 ret=${_ble_color_g2sgr[g=g0^_ble_color_gflags_Revert]} 6533 [[ $ret ]] || ble/color/g2sgr "$g"; sgrN1=$ret 6534 if ((${#m[@]})); then 6535 # 一致色の初期化 6536 ret=${_ble_color_g2sgr[g=g0|_ble_color_gflags_Bold]} 6537 [[ $ret ]] || ble/color/g2sgr "$g"; sgrB0=$ret 6538 ret=${_ble_color_g2sgr[g=(g0|_ble_color_gflags_Bold)^_ble_color_gflags_Revert]} 6539 [[ $ret ]] || ble/color/g2sgr "$g"; sgrB1=$ret 6540 fi 6541 6542 # 前置部分の出力 6543 local out= flag_overflow= p0=0 6544 if [[ $prefix ]]; then 6545 ble/canvas/trace-text "$prefix" nonewline || flag_overflow=1 6546 out=$out$_ble_term_sgr0$ret 6547 fi 6548 6549 # 一致部分の出力 6550 if ((${#m[@]})); then 6551 local i iN=${#m[@]} p p0=0 6552 for ((i=0;i<iN;i++)); do 6553 ((p=m[i])) 6554 if ((p0<p)); then 6555 if ((i%2==0)); then 6556 local sgr0=$sgrN0 sgr1=$sgrN1 6557 else 6558 local sgr0=$sgrB0 sgr1=$sgrB1 6559 fi 6560 ble/canvas/trace-text "${show:p0:p-p0}" nonewline:external-sgr || flag_overflow=1 6561 out=$out$sgr0$ret 6562 fi 6563 p0=$p 6564 done 6565 fi 6566 6567 # 残りの出力 6568 if ((p0<${#show})); then 6569 local sgr0=$sgrN0 sgr1=$sgrN1 6570 ble/canvas/trace-text "${show:p0}" nonewline:external-sgr || flag_overflow=1 6571 out=$out$sgr0$ret 6572 fi 6573 6574 # 後置部分の出力 6575 if [[ $suffix ]]; then 6576 ble/canvas/trace-text "$suffix" nonewline || flag_overflow=1 6577 out=$out$_ble_term_sgr0$ret 6578 fi 6579 6580 ret=$out$_ble_term_sgr0 6581 [[ ! $flag_overflow ]] 6582 } 6583 6584 function ble/complete/menu-complete.class/get-desc { 6585 local item=$1 6586 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6587 ble/complete/cand/unpack "$item" 6588 desc="$desc_sgrt(action:$ACTION)$desc_sgr0" 6589 ble/function#try ble/complete/action:"$ACTION"/get-desc 6590 } 6591 6592 function ble/complete/menu-complete.class/onselect { 6593 local nsel=$1 osel=$2 6594 local insert=${_ble_complete_menu_original:-${_ble_complete_menu_comp[2]}} 6595 if ((nsel>=0)); then 6596 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6597 ble/complete/cand/unpack "${_ble_complete_menu_items[nsel]}" 6598 insert=$INSERT 6599 fi 6600 6601 if [[ :$bleopt_complete_menu_complete_opts: == *:insert-selection:* ]]; then 6602 ble-edit/content/replace-limited "$_ble_complete_menu0_beg" "$_ble_edit_ind" "$insert" 6603 ((_ble_edit_ind=_ble_complete_menu0_beg+${#insert})) 6604 else 6605 ((_ble_edit_ind=_ble_complete_menu0_beg)) 6606 fi 6607 } 6608 6609 function ble/complete/menu/clear { 6610 if [[ $_ble_complete_menu_active ]]; then 6611 _ble_complete_menu_active= 6612 ble/complete/menu#clear 6613 [[ $_ble_highlight_layer_menu_filter_beg ]] && 6614 ble/textarea#invalidate str # layer:menu_filter 解除 (#D0995) 6615 fi 6616 return 0 6617 } 6618 blehook widget_bell!=ble/complete/menu/clear 6619 blehook history_leave!=ble/complete/menu/clear 6620 6621 ## @fn ble/complete/menu/get-footprint 6622 ## @var[out] footprint 6623 function ble/complete/menu/get-footprint { 6624 footprint=$_ble_edit_ind:$_ble_edit_mark_active:${_ble_edit_mark_active:+$_ble_edit_mark}:$_ble_edit_overwrite_mode:$_ble_edit_str 6625 } 6626 6627 ## @fn ble/complete/menu/show opts 6628 ## @param[in] opts 6629 ## filter 6630 ## menu-source 6631 ## offset=NUMBER 6632 ## @var[in] comp_type 6633 ## @var[in] COMP1 COMP2 COMPS COMPV comps_flags comps_fixed 6634 ## @arr[in] cand_pack 6635 ## @var[in] menu_common_part 6636 ## 6637 function ble/complete/menu/show { 6638 local opts=$1 6639 6640 if [[ :$opts: == *:load-filtered-data:* ]]; then 6641 local COMP1=${_ble_complete_menu_comp[0]} 6642 local COMP2=${_ble_complete_menu_comp[1]} 6643 local COMPS=${_ble_complete_menu_comp[2]} 6644 local COMPV=${_ble_complete_menu_comp[3]} 6645 local comp_type=${_ble_complete_menu_comp[4]} 6646 local comps_flags=${_ble_complete_menu0_comp[5]} 6647 local comps_fixed=${_ble_complete_menu0_comp[6]} 6648 local cand_pack; cand_pack=("${_ble_complete_menu_items[@]}") 6649 local menu_common_part=$_ble_complete_menu_common_part 6650 fi 6651 6652 # settings 6653 local menu_style=$bleopt_complete_menu_style 6654 [[ :$opts: == *:filter:* && $_ble_complete_menu_style ]] && 6655 menu_style=$_ble_complete_menu_style 6656 local menu_items; menu_items=("${cand_pack[@]}") 6657 6658 _ble_complete_menu_common_part=$menu_common_part 6659 local menu_class=ble/complete/menu-complete.class menu_param= 6660 6661 local menu_opts=$opts 6662 [[ :$comp_type: == *:sync:* ]] && menu_opts=$menu_opts:sync 6663 6664 ble/complete/menu#construct "$menu_opts" || return "$?" 6665 ble/complete/menu#show 6666 6667 if [[ :$opts: == *:menu-source:* ]]; then 6668 # menu に既に表示されている内容を元にした補完後のメニュー再表示。 6669 # 補完開始時の情報を保持したまま調整を行う。 6670 6671 # 編集領域左側の文字列が曖昧補完によって書き換わる可能性がある 6672 local left0=${_ble_complete_menu0_str::_ble_complete_menu0_end} 6673 local left1=${_ble_edit_str::_ble_edit_ind} 6674 local ret; ble/string#common-prefix "$left0" "$left1"; left0=$ret 6675 6676 # 編集領域右側の文字列が吸収されて書き換わる可能性がある 6677 local right0=${_ble_complete_menu0_str:_ble_complete_menu0_end} 6678 local right1=${_ble_edit_str:_ble_edit_ind} 6679 local ret; ble/string#common-suffix "$right0" "$right1"; right0=$ret 6680 6681 local footprint; ble/complete/menu/get-footprint 6682 _ble_complete_menu0_str=$left0$right0 6683 _ble_complete_menu0_end=${#left0} 6684 _ble_complete_menu_footprint=$footprint 6685 elif [[ :$opts: != *:filter:* ]]; then 6686 local beg=$COMP1 end=$_ble_edit_ind # COMP2 でなく補完挿入後の位置 6687 local str=$_ble_edit_str 6688 [[ $_ble_decode_keymap == auto_complete ]] && 6689 str=${str::_ble_edit_ind}${str:_ble_edit_mark} 6690 local footprint; ble/complete/menu/get-footprint 6691 _ble_complete_menu_active=1 6692 _ble_complete_menu_style=$menu_style 6693 _ble_complete_menu0_beg=$beg 6694 _ble_complete_menu0_end=$end 6695 _ble_complete_menu0_str=$str 6696 _ble_complete_menu0_comp=("$COMP1" "$COMP2" "$COMPS" "$COMPV" "$comp_type" "$comps_flags" "$comps_fixed") 6697 _ble_complete_menu0_pack=("${cand_pack[@]}") 6698 _ble_complete_menu_selected=-1 6699 _ble_complete_menu_comp=("$COMP1" "$COMP2" "$COMPS" "$COMPV" "$comp_type") 6700 _ble_complete_menu_footprint=$footprint 6701 fi 6702 return 0 6703 } 6704 6705 function ble/complete/menu/redraw { 6706 if [[ $_ble_complete_menu_active ]]; then 6707 ble/complete/menu#show 6708 fi 6709 } 6710 6711 ## ble/complete/menu/get-active-range [str [ind]] 6712 ## @param[in,opt] str ind 6713 ## @var[out] beg end 6714 function ble/complete/menu/get-active-range { 6715 [[ $_ble_complete_menu_active ]] || return 1 6716 6717 local str=${1-$_ble_edit_str} ind=${2-$_ble_edit_ind} 6718 local mbeg=$_ble_complete_menu0_beg 6719 local mend=$_ble_complete_menu0_end 6720 local left=${_ble_complete_menu0_str::mend} 6721 local right=${_ble_complete_menu0_str:mend} 6722 if [[ ${str::_ble_edit_ind} == "$left"* && ${str:_ble_edit_ind} == *"$right" ]]; then 6723 ((beg=mbeg,end=${#str}-${#right})) 6724 return 0 6725 else 6726 ble/complete/menu/clear 6727 return 1 6728 fi 6729 } 6730 6731 ## @fn ble/complete/menu/generate-candidates-from-menu 6732 ## 現在表示されている menu 内容から候補を再抽出します。 6733 ## @var[out] COMP1 COMP2 COMPS COMPV comp_type comps_flags comps_fixed 6734 ## @var[out] cand_count cand_cand cand_word cand_pack 6735 function ble/complete/menu/generate-candidates-from-menu { 6736 # completion context information 6737 COMP1=${_ble_complete_menu_comp[0]} 6738 COMP2=${_ble_complete_menu_comp[1]} 6739 COMPS=${_ble_complete_menu_comp[2]} 6740 COMPV=${_ble_complete_menu_comp[3]} 6741 comp_type=${_ble_complete_menu_comp[4]} 6742 comps_flags=${_ble_complete_menu0_comp[5]} 6743 comps_fixed=${_ble_complete_menu0_comp[6]} 6744 6745 # remaining candidates 6746 cand_count=${#_ble_complete_menu_items[@]} 6747 cand_cand=() cand_word=() cand_pack=() 6748 local pack "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6749 for pack in "${_ble_complete_menu_items[@]}"; do 6750 ble/complete/cand/unpack "$pack" 6751 ble/array#push cand_cand "$CAND" 6752 ble/array#push cand_word "$INSERT" 6753 ble/array#push cand_pack "$pack" 6754 done 6755 ((cand_count)) 6756 } 6757 6758 # 6759 #============================================================================== 6760 # 補完 6761 6762 ## @fn ble/complete/generate-candidates-from-opts opts 6763 ## @var[out] COMP1 COMP2 COMPS COMPV comp_type comps_flags comps_fixed 6764 ## @var[out] cand_count cand_cand cand_word cand_pack 6765 ## @var[in,out] cand_limit_reached 6766 function ble/complete/generate-candidates-from-opts { 6767 local opts=$1 6768 6769 # 文脈の決定 6770 local context; ble/complete/complete/determine-context-from-opts "$opts" 6771 6772 # 補完源の生成 6773 comp_type= 6774 [[ :$opts: == *:auto_menu:* ]] && comp_type=auto_menu 6775 local comp_text=$_ble_edit_str comp_index=$_ble_edit_ind 6776 local sources 6777 ble/complete/context:"$context"/generate-sources "$comp_text" "$comp_index" || return "$?" 6778 6779 ble/complete/candidates/generate "$opts" 6780 } 6781 6782 ## @fn ble/complete/insert insert_beg insert_end insert suffix 6783 function ble/complete/insert { 6784 local insert_beg=$1 insert_end=$2 6785 local insert=$3 suffix=$4 6786 local original_text=${_ble_edit_str:insert_beg:insert_end-insert_beg} 6787 local ret 6788 6789 # 編集範囲の最小化 6790 local insert_replace= 6791 if [[ $insert == "$original_text"* ]]; then 6792 # 既存部分の置換がない場合 6793 insert=${insert:insert_end-insert_beg} 6794 ((insert_beg=insert_end)) 6795 else 6796 # 既存部分の置換がある場合 6797 ble/string#common-prefix "$insert" "$original_text" 6798 if [[ $ret ]]; then 6799 insert=${insert:${#ret}} 6800 ((insert_beg+=${#ret})) 6801 fi 6802 fi 6803 6804 if [[ $bleopt_complete_skip_matched ]]; then 6805 # カーソルの右のテキストの吸収 6806 if [[ $insert ]]; then 6807 local right_text=${_ble_edit_str:insert_end} 6808 right_text=${right_text%%[$IFS]*} 6809 if ble/string#common-prefix "$insert" "$right_text"; [[ $ret ]]; then 6810 # カーソルの右に先頭一致する場合に吸収 6811 ((insert_end+=${#ret})) 6812 elif ble/complete/string#common-suffix-prefix "$insert" "$right_text"; [[ $ret ]]; then 6813 # カーソルの右に末尾一致する場合に吸収 6814 ((insert_end+=${#ret})) 6815 fi 6816 fi 6817 6818 # suffix の吸収 6819 if [[ $suffix ]]; then 6820 local right_text=${_ble_edit_str:insert_end} 6821 if ble/string#common-prefix "$suffix" "$right_text"; [[ $ret ]]; then 6822 ((insert_end+=${#ret})) 6823 elif ble/complete/string#common-suffix-prefix "$suffix" "$right_text"; [[ $ret ]]; then 6824 ((insert_end+=${#ret})) 6825 fi 6826 fi 6827 fi 6828 6829 local ins=$insert$suffix 6830 ble/widget/.replace-range "$insert_beg" "$insert_end" "$ins" 6831 ((_ble_edit_ind=insert_beg+${#ins}, 6832 _ble_edit_ind>${#_ble_edit_str}&& 6833 (_ble_edit_ind=${#_ble_edit_str}))) 6834 } 6835 6836 ## @fn ble/complete/insert-common 6837 ## @var[out] COMP1 COMP2 COMPS COMPV comp_type comps_flags comps_fixed 6838 ## @var[out] cand_count cand_cand cand_word cand_pack 6839 function ble/complete/insert-common { 6840 local ret 6841 ble/complete/candidates/determine-common-prefix; (($?==148)) && return 148 6842 local insert=$ret suffix= 6843 local insert_beg=$COMP1 insert_end=$COMP2 6844 local insert_flags= 6845 [[ $insert == "$COMPS"* ]] || insert_flags=r 6846 6847 if ((cand_count==1)); then 6848 # 一意確定の時 6849 local ACTION=${cand_pack[0]%%:*} 6850 if ble/is-function ble/complete/action:"$ACTION"/complete; then 6851 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6852 ble/complete/cand/unpack "${cand_pack[0]}" 6853 ble/complete/action:"$ACTION"/complete 6854 (($?==148)) && return 148 6855 fi 6856 else 6857 # 候補が複数ある時 6858 insert_flags=${insert_flags}m 6859 fi 6860 6861 local do_insert=1 6862 if ((cand_count>1)) && [[ $insert_flags == *r* ]]; then 6863 # 既存部分を置換し、かつ一意確定でない場合は置換しない。 6864 # 曖昧補完の時は determine-common-prefix 内で調整されるので挿入する。 6865 if [[ :$comp_type: != *:[maAi]:* ]]; then 6866 do_insert= 6867 fi 6868 elif [[ $insert$suffix == "$COMPS" ]]; then 6869 # 何も変化がない時は、挿入しない。 6870 do_insert= 6871 fi 6872 if [[ $do_insert ]]; then 6873 ble/complete/insert "$insert_beg" "$insert_end" "$insert" "$suffix" 6874 blehook/invoke complete_insert 6875 fi 6876 6877 if [[ $insert_flags == *m* ]]; then 6878 # menu_common_part (メニュー強調文字列) 6879 # もし insert が単純単語の場合には 6880 # menu_common_part を挿入後の評価値とする。 6881 # そうでなければ仕方がないので挿入前の値 COMPV とする。 6882 local menu_common_part=$COMPV 6883 local ret simple_flags simple_ibrace 6884 if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$insert"; then 6885 ble/complete/source/eval-simple-word "$ret" single 6886 (($?==148)) && return 148 6887 menu_common_part=$ret 6888 fi 6889 ble/complete/menu/show "$menu_show_opts" || return "$?" 6890 elif [[ $insert_flags == *n* ]]; then 6891 ble/widget/complete show_menu:regenerate || return "$?" 6892 else 6893 _ble_complete_state=complete 6894 ble/complete/menu/clear 6895 fi 6896 return 0 6897 } 6898 6899 ## @fn ble/complete/insert-all 6900 ## @var[out] COMP1 COMP2 COMPS COMPV comp_type comps_flags comps_fixed 6901 ## @var[out] cand_count cand_cand cand_word cand_pack 6902 function ble/complete/insert-all { 6903 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 6904 local pack beg=$COMP1 end=$COMP2 insert= suffix= insert_flags= index=0 6905 for pack in "${cand_pack[@]}"; do 6906 ble/complete/cand/unpack "$pack" 6907 insert=$INSERT suffix= insert_flags= 6908 6909 if ble/is-function ble/complete/action:"$ACTION"/complete; then 6910 ble/complete/action:"$ACTION"/complete 6911 (($?==148)) && return 148 6912 fi 6913 [[ $suffix != *' ' ]] && suffix="$suffix " 6914 6915 ble/complete/insert "$beg" "$end" "$insert" "$suffix" 6916 blehook/invoke complete_insert 6917 beg=$_ble_edit_ind end=$_ble_edit_ind 6918 ((index++)) 6919 done 6920 6921 _ble_complete_state=complete 6922 ble/complete/menu/clear 6923 return 0 6924 } 6925 6926 ## @fn ble/complete/insert-braces/.compose words... 6927 ## 指定した単語をブレース展開に圧縮します。 6928 ## @var[in] comp_type 6929 ## @stdout 6930 ## 圧縮したブレース展開を返します。 6931 function ble/complete/insert-braces/.compose { 6932 # Note: awk が RS = "\0" に対応していれば \0 で区切る。 6933 # それ以外の場合には \x1E (ASCII RS) で区切る。 6934 if ble/bin/awk0.available; then 6935 local printf_format='%s\0' char_RS='"\0"' awk=ble/bin/awk0 6936 else 6937 local printf_format='%s\x1E' char_RS='"\x1E"' awk=ble/bin/awk 6938 fi 6939 6940 local q=\' 6941 local -x rex_atom='^(\\.|[0-9]+|.)' del_close= del_open= quote_type= 6942 local -x COMPS=$COMPS 6943 if [[ :$comp_type: != *:[maAi]:* ]]; then 6944 local rex_brace='[,{}]|\{[-a-zA-Z0-9]+\.\.[-a-zA-Z0-9]+\}' 6945 case $comps_flags in 6946 (*S*) rex_atom='^('$q'(\\'$q'|'$rex_brace')'$q'|[0-9]+|.)' # '...' 6947 del_close=\' del_open=\' quote_type=S ;; 6948 (*E*) rex_atom='^(\\.|'$q'('$rex_brace')\$'$q'|[0-9]+|.)' # $'...' 6949 del_close=\' del_open=\$\' quote_type=E ;; 6950 (*[DI]*) rex_atom='^(\\[\"$`]|"('$rex_brace')"|[0-9]+|.)' # "...", $"..." 6951 del_close=\" del_open=\" quote_type=D ;; 6952 esac 6953 fi 6954 6955 printf "$printf_format" "$@" | "$awk" ' 6956 function starts_with(str, head) { 6957 return substr(str, 1, length(head)) == head; 6958 } 6959 6960 BEGIN { 6961 RS = '"$char_RS"'; 6962 rex_atom = ENVIRON["rex_atom"]; 6963 del_close = ENVIRON["del_close"]; 6964 del_open = ENVIRON["del_open"]; 6965 quote_type = ENVIRON["quote_type"]; 6966 COMPS = ENVIRON["COMPS"]; 6967 6968 BRACE_OPEN = del_close "{" del_open; 6969 BRACE_CLOS = del_close "}" del_open; 6970 } 6971 6972 function to_atoms(str, arr, _, chr, atom, level, count, rex) { 6973 count = 0; 6974 while (match(str, rex_atom) > 0) { 6975 chr = substr(str, 1, RLENGTH); 6976 str = substr(str, RLENGTH + 1); 6977 if (chr == BRACE_OPEN) { 6978 atom = chr; 6979 level = 1; 6980 while (match(str, rex_atom) > 0) { 6981 chr = substr(str, 1, RLENGTH); 6982 str = substr(str, RLENGTH + 1); 6983 atom = atom chr; 6984 if (chr == BRACE_OPEN) 6985 level++; 6986 else if (chr == BRACE_CLOS && --level==0) 6987 break; 6988 } 6989 } else { 6990 atom = chr; 6991 } 6992 arr[count++] = atom; 6993 } 6994 return count; 6995 } 6996 6997 function remove_empty_quote(str, _, rex_quote_first, rex_quote, out, empty, m) { 6998 if (quote_type == "S" || quote_type == "E") { 6999 rex_quote_first = "^[^'$q']*'$q'"; 7000 rex_quote = "'$q'[^'$q']*'$q'|(\\\\.|[^'$q'])+"; 7001 } else if (quote_type == "D") { 7002 rex_quote_first = "^[^\"]*\""; 7003 rex_quote = "\"([^\\\"]|\\\\.)*\"|(\\\\.|[^\"])+"; 7004 } else return str; 7005 empty = del_open del_close; 7006 7007 out = ""; 7008 7009 if (starts_with(str, COMPS)) { 7010 out = COMPS; 7011 str = substr(str, length(COMPS) + 1); 7012 if (match(str, rex_quote_first) > 0) { 7013 out = out substr(str, 1, RLENGTH); 7014 str = substr(str, RLENGTH + 1); 7015 } 7016 } 7017 7018 while (match(str, rex_quote) > 0) { 7019 m = substr(str, 1, RLENGTH); 7020 if (m != empty) out = out m; 7021 str = substr(str, RLENGTH + 1); 7022 } 7023 7024 if (str == del_open) 7025 return out; 7026 else 7027 return out str del_close; 7028 } 7029 7030 function zpad(value, width, _, wpad, i, pad) { 7031 wpad = width - length(value); 7032 pad = ""; 7033 for (i = 0; i < wpad; i++) pad = "0" pad; 7034 if (value < 0) 7035 return "-" pad (-value); 7036 else 7037 return pad value; 7038 } 7039 function zpad_remove(value) { 7040 if (value ~ /^0+$/) 7041 value = "0"; 7042 else if (value ~ /^-/) 7043 sub(/^-0+/, "-", value); 7044 else 7045 sub(/^0+/, "", value); 7046 return value; 7047 } 7048 function zpad_a2i(text) { 7049 sub(/^-0+/, "-", text) || sub(/^0+/, "", text); 7050 return 0 + text; 7051 } 7052 7053 function range_contract(arr, len, _, i, value, alpha, lower, upper, keys, ikey, dict, b, e, beg, end, tmp) { 7054 lower = "abcdefghijklmnopqrstuvwxyz"; 7055 upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 7056 for (i = 0; i < len; i++) { 7057 value = arr[i]; 7058 if (dict[value]) { 7059 dict[value]++; 7060 } else { 7061 keys[ikey++] = value; 7062 dict[value] = 1; 7063 } 7064 } 7065 7066 len = 0; 7067 7068 for (i = 0; i < ikey; i++) { 7069 while (dict[value = keys[i]]--) { 7070 if (value ~ /^([a-zA-Z])$/) { 7071 alpha = (value ~ /^[a-z]$/) ? lower : upper; 7072 beg = end = value; 7073 b = e = index(alpha, value); 7074 while (b > 1 && dict[tmp = substr(alpha, b - 1, 1)]) { 7075 dict[beg = tmp]--; 7076 b--; 7077 } 7078 while (e < 26 && dict[tmp = substr(alpha, e + 1, 1)]) { 7079 dict[end = tmp]--; 7080 e++; 7081 } 7082 7083 if (e == b) { 7084 arr[len++] = beg; 7085 } else if (e == b + 1) { 7086 arr[len++] = beg; 7087 arr[len++] = end; 7088 } else { 7089 arr[len++] = del_close "{" beg ".." end "}" del_open; 7090 } 7091 7092 } else if (value ~ /^(0+|-?0*[1-9][0-9]*)$/) { 7093 beg = end = value; 7094 b = e = zpad_a2i(value); 7095 wmax = wmin = length(value); 7096 7097 # range extension for normal numbers 7098 if (value ~ /^(0|-?[1-9][0-9]*)$/) { 7099 while (dict[b - 1]) dict[--b]--; 7100 while (dict[e + 1]) dict[++e]--; 7101 7102 tmp = length(beg = "" b); 7103 if (tmp < wmin) wmin = tmp; 7104 else if (tmp > wmax) wmax = tmp; 7105 7106 tmp = length(end = "" e); 7107 if (tmp < wmin) wmin = tmp; 7108 else if (tmp > wmax) wmax = tmp; 7109 } 7110 7111 # try range extension for zpad numbers 7112 if (wmax == wmin) { 7113 while (length(tmp = zpad(b - 1, wmin)) == wmin && dict[tmp]) { dict[tmp]--; --b; } 7114 while (length(tmp = zpad(e + 1, wmin)) == wmin && dict[tmp]) { dict[tmp]--; ++e; } 7115 beg = zpad(b, wmin); 7116 end = zpad(e, wmin); 7117 } 7118 7119 if (e == b) { 7120 arr[len++] = beg; 7121 } else if (e == b + 1) { 7122 arr[len++] = beg; 7123 arr[len++] = end; 7124 } else if (b < 0 && e < 0) { 7125 # if all the numbers are negative, factorize - 7126 arr[len++] = del_close "-{" substr(end, 2) ".." substr(beg, 2) "}" del_open; 7127 } else { 7128 arr[len++] = del_close "{" beg ".." end "}" del_open; 7129 } 7130 7131 } else { 7132 arr[len++] = value; 7133 } 7134 } 7135 } 7136 return len; 7137 } 7138 7139 function simple_brace(arr, len, _, ret, i) { 7140 if (len == 0) return ""; 7141 7142 len = range_contract(arr, len); 7143 if (len == 1) return arr[0]; 7144 7145 ret = BRACE_OPEN arr[0]; 7146 for (i = 1; i < len; i++) 7147 ret = ret del_close "," del_open arr[i]; 7148 return ret BRACE_CLOS; 7149 } 7150 7151 #-------------------------------------------------------------------------- 7152 # right factorization 7153 7154 function rfrag_strlen_common(a, b, _, la, lb, tmp, i, n) { 7155 ret = 0; 7156 alen = to_atoms(a, abuf); 7157 blen = to_atoms(b, bbuf); 7158 while (alen > 0 && blen > 0) { 7159 if (abuf[alen - 1] != bbuf[blen - 1]) break; 7160 ret += length(abuf[alen - 1]); 7161 alen--; 7162 blen--; 7163 } 7164 return ret; 7165 } 7166 function rfrag_get_level(str, _, len, i, rfrag0, rfrag0len, rfrag1) { 7167 len = length(str); 7168 rfrag_matching_offset = len; 7169 for (i = 0; i < rfrag_depth - 1; i++) { 7170 rfrag0 = rfrag[i]; 7171 rfrag0len = length(rfrag0); 7172 rfrag1 = substr(str, len - rfrag0len + 1); 7173 str = substr(str, 1, len -= rfrag0len); 7174 if (rfrag0 != rfrag1) break; 7175 rfrag_matching_offset -= rfrag0len; 7176 } 7177 while (i && rfrag[i - 1] == "") i--; # empty fragment 7178 return i; 7179 } 7180 function rfrag_reduce(new_depth, _, c, i, brace, frags) { 7181 while (rfrag_depth > new_depth) { 7182 rfrag_depth--; 7183 c = rfrag_count[rfrag_depth]; 7184 for (i = 0; i < c; i++) 7185 frags[i] = rfrag[rfrag_depth, i]; 7186 frags[c] = rfrag[rfrag_depth]; 7187 brace = simple_brace(frags, c + 1); 7188 7189 if (rfrag_depth == 0) 7190 return brace; 7191 else 7192 rfrag[rfrag_depth - 1] = brace rfrag[rfrag_depth - 1]; 7193 } 7194 } 7195 function rfrag_register(str, level, _, rfrag0, rfrag1, len) { 7196 if (level == rfrag_depth) { 7197 rfrag_depth = level + 1; 7198 rfrag[level] = ""; 7199 rfrag_count[level] = 0; 7200 } else if (rfrag_depth != level + 1) { 7201 print "ERR(rfrag)"; 7202 } 7203 7204 rfrag0 = rfrag[level]; 7205 rfrag1 = substr(str, 1, rfrag_matching_offset); 7206 len = rfrag_strlen_common(rfrag0, rfrag1); 7207 if (len == 0) { 7208 rfrag[level, rfrag_count[level]++] = rfrag0; 7209 rfrag[level] = rfrag1; 7210 } else { 7211 rfrag[level] = substr(rfrag0, length(rfrag0) - len + 1); 7212 rfrag[level + 1, 0] = substr(rfrag0, 1, length(rfrag0) - len); 7213 rfrag[level + 1] = substr(rfrag1, 1, length(rfrag1) - len); 7214 rfrag_count[level + 1] = 1; 7215 rfrag_depth++; 7216 } 7217 } 7218 function rfrag_dump(_, i, j, prefix) { 7219 print "depth = " rfrag_depth; 7220 for (i = 0; i < rfrag_depth; i++) { 7221 prefix = ""; 7222 for (j = 0; j < i; j++) prefix = prefix " "; 7223 for (j = 0; j < rfrag_count[i]; j++) 7224 print prefix "rfrag[" i "," j "] = " rfrag[i,j]; 7225 print prefix "rfrag[" i "] = " rfrag[i]; 7226 } 7227 } 7228 function rfrag_brace(arr, len, _, i, level) { 7229 if (len == 0) return ""; 7230 if (len == 1) return arr[0]; 7231 7232 rfrag_depth = 1; 7233 rfrag[0] = arr[0]; 7234 rfrag_count[0] = 0; 7235 for (i = 1; i < len; i++) { 7236 level = rfrag_get_level(arr[i]); 7237 rfrag_reduce(level + 1); 7238 rfrag_register(arr[i], level); 7239 } 7240 7241 return rfrag_reduce(0); 7242 } 7243 7244 #-------------------------------------------------------------------------- 7245 # left factorization 7246 7247 function lfrag_strlen_common(a, b, _, ret, abuf, bbuf, alen, blen, ia, ib) { 7248 ret = 0; 7249 alen = to_atoms(a, abuf); 7250 blen = to_atoms(b, bbuf); 7251 for (ia = ib = 0; ia < alen && ib < blen; ia++ + ib++) { 7252 if (abuf[ia] != bbuf[ib]) break; 7253 ret += length(abuf[ia]); 7254 } 7255 return ret; 7256 } 7257 function lfrag_get_level(str, _, i, frag0, frag0len, frag1) { 7258 lfrag_matching_offset = 0; 7259 for (i = 0; i < lfrag_depth - 1; i++) { 7260 frag0 = frag[i] 7261 frag0len = length(frag0); 7262 frag1 = substr(str, lfrag_matching_offset + 1, frag0len); 7263 if (frag0 != frag1) break; 7264 lfrag_matching_offset += frag0len; 7265 } 7266 while (i && frag[i - 1] == "") i--; # empty fragment 7267 return i; 7268 } 7269 function lfrag_reduce(new_depth, _, c, i, brace, frags) { 7270 while (lfrag_depth > new_depth) { 7271 lfrag_depth--; 7272 c = frag_count[lfrag_depth]; 7273 for (i = 0; i < c; i++) 7274 frags[i] = frag[lfrag_depth, i]; 7275 frags[c] = frag[lfrag_depth]; 7276 brace = rfrag_brace(frags, c + 1); 7277 7278 if (lfrag_depth == 0) 7279 return brace; 7280 else 7281 frag[lfrag_depth - 1] = frag[lfrag_depth - 1] brace; 7282 } 7283 } 7284 function lfrag_register(str, level, _, frag0, frag1, len) { 7285 if (lfrag_depth == level) { 7286 lfrag_depth = level + 1; 7287 frag[level] = ""; 7288 frag_count[level] = 0; 7289 } else if (lfrag_depth != level + 1) { 7290 print "ERR"; 7291 } 7292 7293 frag0 = frag[level]; 7294 frag1 = substr(str, lfrag_matching_offset + 1); 7295 len = lfrag_strlen_common(frag0, frag1); 7296 if (len == 0) { 7297 frag[level, frag_count[level]++] = frag0; 7298 frag[level] = frag1; 7299 } else { 7300 frag[level] = substr(frag0, 1, len); 7301 frag[level + 1, 0] = substr(frag0, len + 1); 7302 frag[level + 1] = substr(frag1, len + 1); 7303 frag_count[level + 1] = 1; 7304 lfrag_depth++; 7305 } 7306 } 7307 7308 function lfrag_dump(_, i, j, prefix) { 7309 print "depth = " lfrag_depth; 7310 for (i = 0; i < lfrag_depth; i++) { 7311 prefix = ""; 7312 for (j = 0; j < i; j++) prefix = prefix " "; 7313 for (j = 0; j < frag_count[i]; j++) 7314 print prefix "frag[" i "," j "] = " frag[i,j]; 7315 print prefix "frag[" i "] = " frag[i]; 7316 } 7317 } 7318 7319 NR == 1 { 7320 lfrag_depth = 1; 7321 frag[0] = $0; 7322 frag_count[0] = 0; 7323 #lfrag_dump(); 7324 next 7325 } 7326 { 7327 level = lfrag_get_level($0); 7328 lfrag_reduce(level + 1); 7329 lfrag_register($0, level); 7330 #lfrag_dump(); 7331 } 7332 7333 END { 7334 result = lfrag_reduce(0); 7335 result = remove_empty_quote(result); 7336 print result; 7337 } 7338 ' 7339 } 7340 7341 ## @fn ble/complete/insert-braces 7342 ## @var[out] COMP1 COMP2 COMPS COMPV comp_type comps_flags comps_fixed 7343 ## @var[out] cand_count cand_cand cand_word cand_pack 7344 function ble/complete/insert-braces { 7345 if ((cand_count==1)); then 7346 ble/complete/insert-common; return "$?" 7347 fi 7348 7349 local comps_len=${#COMPS} loop=0 7350 local -a tails=() 7351 7352 # 共通部分 (大文字・小文字は区別する) 7353 local common=${cand_word[0]} 7354 ble/array#push tails "${common:comps_len}" 7355 local word clen=${#common} 7356 for word in "${cand_word[@]:1}"; do 7357 ((loop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 7358 7359 # 共通部分 7360 ((clen>${#word}&&(clen=${#word}))) 7361 while [[ ${word::clen} != "${common::clen}" ]]; do 7362 ((clen--)) 7363 done 7364 common=${common::clen} 7365 7366 # COMPS 以降の部分 7367 ble/array#push tails "${word:comps_len}" 7368 done 7369 7370 local fixed=$COMPS 7371 if [[ $common != "$COMPS"* ]]; then 7372 # 遡って書き換えが起こる場合 7373 tails=() 7374 7375 # 前方固定部分 7376 local fixed= fixval= 7377 { 7378 # comps_fixed 迄は確実に固定する 7379 [[ $comps_fixed ]] && 7380 fixed=${COMPS::${comps_fixed%%:*}} fixval=${comps_fixed#*:} 7381 7382 # もし COMPS を部分的に適用できればそれを用いる 7383 local ret simple_flags simple_ibrace 7384 ble/complete/candidates/determine-common-prefix/.apply-partial-comps # var[in,out] common 7385 if ble/syntax:bash/simple-word/reconstruct-incomplete-word "$common"; then 7386 ble/complete/source/eval-simple-word "$ret" single 7387 (($?==148)) && return 148 7388 fixed=$common fixval=$ret 7389 fi 7390 } 7391 7392 # cand_cand から cand_word を再構築 7393 local cand ret fixval_len=${#fixval} 7394 for cand in "${cand_cand[@]}"; do 7395 ((loop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148 7396 [[ $cand == "$fixval"* ]] || continue 7397 7398 ble/complete/string#escape-for-completion-context "${cand:fixval_len}" 7399 case $comps in 7400 (*S*) cand=\'$ret\' ;; 7401 (*E*) cand=\$\'$ret\' ;; 7402 (*D*) cand=\"$ret\" ;; 7403 (*I*) cand=\$\"$ret\" ;; 7404 (*) cand=$ret ;; 7405 esac 7406 7407 ble/array#push tails "$cand" 7408 done 7409 fi 7410 7411 local tail; ble/util/assign tail 'ble/complete/insert-braces/.compose "${tails[@]}"' 7412 local beg=$COMP1 end=$COMP2 insert=$fixed$tail suffix= 7413 7414 if [[ $comps_flags == *x* ]]; then 7415 ble/complete/action/complete.addtail ',' 7416 else 7417 ble/complete/action/complete.addtail ' ' 7418 fi 7419 7420 ble/complete/insert "$beg" "$end" "$insert" "$suffix" 7421 blehook/invoke complete_insert 7422 _ble_complete_state=complete 7423 ble/complete/menu/clear 7424 return 0 7425 } 7426 7427 _ble_complete_state= 7428 7429 ## @widget complete opts 7430 ## @param[in] opts 7431 ## コロン区切りのリストです。 7432 ## 以下は動作を指定するオプションです。 7433 ## 7434 ## insert_common (既定) 7435 ## 共通一致部分を挿入します。 7436 ## insert_all 7437 ## 候補を全て挿入します。 7438 ## insert_braces 7439 ## 候補をブレース展開にまとめて挿入します。 7440 ## insert_unique 7441 ## 候補が一意のときメニュー補完に入らずに挿入します。 7442 ## show_menu 7443 ## メニューを表示します。 7444 ## enter_menu 7445 ## メニュー補完に入ります。 7446 ## 7447 ## context=* 7448 ## 候補生成の文脈を指定します。 7449 ## backward 7450 ## メニュー補完に入る時に最後の候補に移動します。 7451 ## no-empty 7452 ## 空の COMPV による補完を抑制します。 7453 ## no-bell 7454 ## 候補が存在しなかった時のベルを発生させません。 7455 ## 7456 ## auto_menu 7457 ## auto-menu 経由で呼び出されている事を指定します。 7458 ## 補完候補数の制限に complete_limit_auto_menu を使います。 7459 ## 一部の補完源で complete_limit に達した時に補完全体を中止します。 7460 ## 7461 function ble/widget/complete { 7462 local opts=$1 7463 ble-edit/content/clear-arg 7464 7465 local state=$_ble_complete_state 7466 _ble_complete_state=start 7467 7468 local menu_show_opts= 7469 7470 if [[ :$opts: != *:insert_*:* && :$opts: != *:show_menu:* ]]; then 7471 if [[ :$opts: == *:enter_menu:* ]]; then 7472 [[ $_ble_complete_menu_active && :$opts: != *:context=*:* ]] && 7473 ble/complete/menu-complete/enter "$opts" && return 0 7474 elif [[ $bleopt_complete_menu_complete ]]; then 7475 if [[ $_ble_complete_menu_active && :$opts: != *:context=*:* ]]; then 7476 local footprint; ble/complete/menu/get-footprint 7477 [[ $footprint == "$_ble_complete_menu_footprint" ]] && 7478 ble/complete/menu-complete/enter "$opts" && return 0 7479 fi 7480 [[ $WIDGET == "$LASTWIDGET" && $state != complete ]] && opts=$opts:enter_menu 7481 fi 7482 fi 7483 7484 local COMP1 COMP2 COMPS COMPV 7485 local comp_type comps_flags comps_fixed 7486 local cand_count cand_cand cand_word cand_pack 7487 ble/complete/candidates/clear 7488 local cand_limit_reached= 7489 if [[ $_ble_complete_menu_active && :$opts: != *:regenerate:* && 7490 :$opts: != *:context=*:* && ${#_ble_complete_menu_icons[@]} -gt 0 ]] 7491 then 7492 if [[ $_ble_complete_menu_filter_enabled && $bleopt_complete_menu_filter ]] || { 7493 ble/complete/menu-filter; local ext=$? 7494 ((ext==148)) && return 148 7495 ((ext==0)); }; then 7496 ble/complete/menu/generate-candidates-from-menu; local ext=$? 7497 ((ext==148)) && return 148 7498 if ((ext==0&&cand_count)); then 7499 local bleopt_complete_menu_style=$_ble_complete_menu_style 7500 menu_show_opts=$menu_show_opts:menu-source # 既存の filter 前候補を保持する 7501 fi 7502 fi 7503 fi 7504 if ((cand_count==0)); then 7505 local bleopt_complete_menu_style=$bleopt_complete_menu_style # source 等に一次変更を認める。 7506 ble/complete/generate-candidates-from-opts "$opts"; local ext=$? 7507 if ((ext==148)); then 7508 return 148 7509 fi 7510 if [[ $cand_limit_reached ]]; then 7511 [[ :$opts: != *:no-bell:* ]] && 7512 ble/widget/.bell 'complete: limit reached' 7513 if [[ $cand_limit_reached == cancel ]]; then 7514 ble/edit/info/default 7515 return 1 7516 fi 7517 fi 7518 if ((ext!=0||cand_count==0)); then 7519 [[ :$opts: != *:no-bell:* && ! $cand_limit_reached ]] && 7520 ble/widget/.bell 'complete: no completions' 7521 ble/edit/info/default 7522 return 1 7523 fi 7524 fi 7525 7526 if [[ :$opts: == *:insert_common:* || :$opts: == *:insert_unique:* && cand_count -eq 1 ]]; then 7527 ble/complete/insert-common; return "$?" 7528 7529 elif [[ :$opts: == *:insert_braces:* ]]; then 7530 ble/complete/insert-braces; return "$?" 7531 7532 elif [[ :$opts: == *:insert_all:* ]]; then 7533 ble/complete/insert-all; return "$?" 7534 7535 elif [[ :$opts: == *:enter_menu:* ]]; then 7536 local menu_common_part=$COMPV 7537 ble/complete/menu/show "$menu_show_opts" || return "$?" 7538 ble/complete/menu-complete/enter "$opts"; local ext=$? 7539 ((ext==148)) && return 148 7540 ((ext)) && [[ :$opts: != *:no-bell:* ]] && 7541 ble/widget/.bell 'menu-complete: no completions' 7542 return 0 7543 7544 elif [[ :$opts: == *:show_menu:* ]]; then 7545 local menu_common_part=$COMPV 7546 ble/complete/menu/show "$menu_show_opts" 7547 return "$?" # exit status of ble/complete/menu/show 7548 7549 fi 7550 7551 ble/complete/insert-common; return "$?" 7552 } 7553 7554 function ble/widget/complete-insert { 7555 local original=$1 insert=$2 suffix=$3 7556 [[ ${_ble_edit_str::_ble_edit_ind} == *"$original" ]] || return 1 7557 7558 local insert_beg=$((_ble_edit_ind-${#original})) 7559 local insert_end=$_ble_edit_ind 7560 ble/complete/insert "$insert_beg" "$insert_end" "$insert" "$suffix" 7561 } 7562 7563 function ble/widget/menu-complete { 7564 local opts=$1 7565 ble/widget/complete enter_menu:insert_unique:$opts 7566 } 7567 7568 function ble/widget/complete/.select-menu-with-arg { 7569 [[ $bleopt_complete_menu_complete && $_ble_complete_menu_active ]] || return 1 7570 7571 local footprint; ble/complete/menu/get-footprint 7572 [[ $footprint == "$_ble_complete_menu_footprint" ]] || return 1 7573 7574 local arg_opts= opts=$1 7575 [[ :$opts: == *:enter-menu:* ]] && arg_opts=always 7576 [[ :$opts: == *:nobell:* ]] && arg_opts=$arg_opts:nobell 7577 7578 # 現在のキーが実際に引数の一部として解釈され得る時のみ menu に入る 7579 ble/widget/menu/append-arg/.is-argument "$arg_opts" || return 1 7580 ble/complete/menu-complete/enter 7581 ble/widget/menu/append-arg "$arg_opts" 7582 return 0 7583 } 7584 7585 #------------------------------------------------------------------------------ 7586 # menu-filter 7587 7588 ## @fn ble/complete/menu-filter/.filter-candidates 7589 ## @var[in,out] comp_type 7590 ## @var[out] cand_pack 7591 function ble/complete/menu-filter/.filter-candidates { 7592 cand_pack=() 7593 7594 local iloop=0 interval=$bleopt_complete_polling_cycle 7595 local filter_type pack "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 7596 for filter_type in head substr hsubseq subseq; do 7597 ble/path#remove-glob comp_type '[maA]' 7598 case $filter_type in 7599 (substr) comp_type=${comp_type}:m ;; 7600 (hsubseq) comp_type=${comp_type}:a ;; 7601 (subseq) comp_type=${comp_type}:A ;; 7602 esac 7603 7604 local comp_filter_type 7605 local comp_filter_pattern 7606 ble/complete/candidates/filter#init "$filter_type" "$COMPV" 7607 for pack in "${_ble_complete_menu0_pack[@]}"; do 7608 ((iloop++%interval==0)) && ble/complete/check-cancel && return 148 7609 ble/complete/cand/unpack "$pack" 7610 ble/complete/candidates/filter#test "$CAND" && 7611 ble/array#push cand_pack "$pack" 7612 done 7613 ((${#cand_pack[@]}!=0)) && return 0 7614 done 7615 } 7616 function ble/complete/menu-filter/.get-filter-target { 7617 if [[ $_ble_decode_keymap == emacs || $_ble_decode_keymap == vi_[ic]map ]]; then 7618 ret=$_ble_edit_str 7619 elif [[ $_ble_decode_keymap == auto_complete ]]; then 7620 ret=${_ble_edit_str::_ble_edit_ind}${_ble_edit_str:_ble_edit_mark} 7621 else 7622 return 1 7623 fi 7624 } 7625 function ble/complete/menu-filter { 7626 [[ $_ble_decode_keymap == menu_complete ]] && return 0 7627 local ret; ble/complete/menu-filter/.get-filter-target || return 1; local str=$ret 7628 7629 local beg end; ble/complete/menu/get-active-range "$str" "$_ble_edit_ind" || return 1 7630 local input=${str:beg:end-beg} 7631 [[ $input == "${_ble_complete_menu_comp[2]}" ]] && return 0 7632 7633 local simple_flags simple_ibrace 7634 if ! ble/syntax:bash/simple-word/reconstruct-incomplete-word "$input"; then 7635 ble/syntax:bash/simple-word/is-never-word "$input" && return 1 7636 return 0 7637 fi 7638 [[ $simple_ibrace ]] && ((${simple_ibrace%%:*}>10#0${_ble_complete_menu0_comp[6]%%:*})) && return 1 # 別のブレース展開要素に入った時 7639 ble/syntax:bash/simple-word/eval "$ret" single; (($?==148)) && return 148 7640 local COMPV=$ret 7641 7642 local comp_type=${_ble_complete_menu0_comp[4]} cand_pack 7643 ble/complete/menu-filter/.filter-candidates; (($?==148)) && return 148 7644 7645 local menu_common_part=$COMPV 7646 ble/complete/menu/show filter || return "$?" 7647 _ble_complete_menu_comp=("$beg" "$end" "$input" "$COMPV" "$comp_type") 7648 return 0 7649 } 7650 7651 function ble/complete/menu-filter.idle { 7652 ble/util/idle.wait-user-input 7653 [[ $bleopt_complete_menu_filter ]] || return 1 7654 [[ $_ble_complete_menu_active ]] || return 1 7655 ble/complete/menu-filter; local ext=$? 7656 ((ext==148)) && return 148 7657 ((ext)) && ble/complete/menu/clear 7658 return 0 7659 } 7660 7661 # ble/highlight/layer:menu_filter 7662 7663 ## @fn ble/highlight/layer/buff#operate-gflags name beg end mask gflags 7664 function ble/highlight/layer/buff#operate-gflags { 7665 local BUFF=$1 beg=$2 end=$3 mask=$4 gflags=$5 7666 ((beg<end)) || return 1 7667 7668 if [[ $mask == auto ]]; then 7669 mask=0 7670 ((gflags&(_ble_color_gflags_FgIndexed|_ble_color_gflags_FgMask))) && 7671 ((mask|=_ble_color_gflags_FgIndexed|_ble_color_gflags_FgMask)) 7672 ((gflags&(_ble_color_gflags_BgIndexed|_ble_color_gflags_BgMask))) && 7673 ((mask|=_ble_color_gflags_BgIndexed|_ble_color_gflags_BgMask)) 7674 fi 7675 7676 local i g ret 7677 for ((i=beg;i<end;i++)); do 7678 ble/highlight/layer/update/getg "$i" 7679 ((g=g&~mask|gflags)) 7680 ble/color/g2sgr "$g" 7681 builtin eval -- "$BUFF[$i]=\$ret\${_ble_highlight_layer_plain_buff[$i]}" 7682 done 7683 } 7684 ## @fn ble/highlight/layer/buff#set-explicit-sgr name index 7685 function ble/highlight/layer/buff#set-explicit-sgr { 7686 local BUFF=$1 index=$2 7687 builtin eval "((index<\${#$BUFF[@]}))" || return 1 7688 local g; ble/highlight/layer/update/getg "$index" 7689 local ret; ble/color/g2sgr "$g" 7690 builtin eval "$BUFF[index]=\$ret\${_ble_highlight_layer_plain_buff[index]}" 7691 } 7692 7693 _ble_highlight_layer_menu_filter_buff=() 7694 _ble_highlight_layer_menu_filter_beg= 7695 _ble_highlight_layer_menu_filter_end= 7696 function ble/highlight/layer:menu_filter/update { 7697 local text=$1 player=$2 7698 7699 # shift 7700 local obeg=$_ble_highlight_layer_menu_filter_beg 7701 local oend=$_ble_highlight_layer_menu_filter_end 7702 if [[ $obeg ]] && ((DMIN>=0)); then 7703 ((DMAX0<=obeg?(obeg+=DMAX-DMAX0):(DMIN<obeg&&(obeg=DMIN)), 7704 DMAX0<=oend?(oend+=DMAX-DMAX0):(DMIN<oend&&(oend=DMIN)))) 7705 fi 7706 _ble_highlight_layer_menu_filter_beg=$obeg 7707 _ble_highlight_layer_menu_filter_end=$oend 7708 7709 # determine range 7710 local beg= end= ret 7711 if [[ $bleopt_complete_menu_filter && $_ble_complete_menu_active && ${#_ble_complete_menu_icons[@]} -gt 0 ]]; then 7712 ble/complete/menu-filter/.get-filter-target && local str=$ret && 7713 ble/complete/menu/get-active-range "$str" "$_ble_edit_ind" && 7714 [[ ${str:beg:end-beg} != "${_ble_complete_menu0_comp[2]}" ]] || beg= end= 7715 fi 7716 7717 # 変更のない場合スキップ 7718 [[ ! $obeg && ! $beg ]] && return 0 7719 ((PREV_UMIN<0)) && [[ $beg == "$obeg" && $end == "$oend" ]] && 7720 PREV_BUFF=_ble_highlight_layer_menu_filter_buff && return 0 7721 7722 local umin=$PREV_UMIN umax=$PREV_UMAX 7723 if [[ $beg ]]; then 7724 ble/color/face2g menu_filter_fixed; local gF=$ret 7725 ble/color/face2g menu_filter_input; local gI=$ret 7726 local mid=$_ble_complete_menu0_end 7727 ((mid<beg?(mid=beg):(end<mid&&(mid=end)))) 7728 7729 local buff_name=_ble_highlight_layer_menu_filter_buff 7730 builtin eval "$buff_name=(\"\${$PREV_BUFF[@]}\")" 7731 ble/highlight/layer/buff#operate-gflags "$buff_name" "$beg" "$mid" auto "$gF" 7732 ble/highlight/layer/buff#operate-gflags "$buff_name" "$mid" "$end" auto "$gI" 7733 ble/highlight/layer/buff#set-explicit-sgr "$buff_name" "$end" 7734 PREV_BUFF=$buff_name 7735 7736 if [[ $obeg ]]; then : 7737 ble/highlight/layer:{selection}/.invalidate "$beg" "$obeg" 7738 ble/highlight/layer:{selection}/.invalidate "$end" "$oend" 7739 else 7740 ble/highlight/layer:{selection}/.invalidate "$beg" "$end" 7741 fi 7742 else 7743 if [[ $obeg ]]; then 7744 ble/highlight/layer:{selection}/.invalidate "$obeg" "$oend" 7745 fi 7746 fi 7747 _ble_highlight_layer_menu_filter_beg=$beg 7748 _ble_highlight_layer_menu_filter_end=$end 7749 ((PREV_UMIN=umin,PREV_UMAX=umax)) 7750 } 7751 function ble/highlight/layer:menu_filter/getg { 7752 local index=$1 7753 local obeg=$_ble_highlight_layer_menu_filter_beg 7754 local oend=$_ble_highlight_layer_menu_filter_end 7755 local mid=$_ble_complete_menu0_end 7756 if [[ $obeg ]] && ((obeg<=index&&index<oend)); then 7757 local ret 7758 if ((index<mid)); then 7759 ble/color/face2g menu_filter_fixed; local g0=$ret 7760 else 7761 ble/color/face2g menu_filter_input; local g0=$ret 7762 fi 7763 ble/highlight/layer/update/getg "$index" 7764 ble/color/g.append "$g0" 7765 fi 7766 } 7767 7768 _ble_complete_menu_filter_enabled= 7769 if ble/is-function ble/util/idle.push-background; then 7770 _ble_complete_menu_filter_enabled=1 7771 ble/util/idle.push -n 9999 ble/complete/menu-filter.idle 7772 ble/array#insert-before _ble_highlight_layer_list region menu_filter 7773 fi 7774 7775 #------------------------------------------------------------------------------ 7776 # 7777 # menu-complete 7778 # 7779 7780 ## メニュー補完では以下の変数を参照する 7781 ## 7782 ## @var[in] _ble_complete_menu0_beg 7783 ## @var[in] _ble_complete_menu0_end 7784 ## @var[in] _ble_complete_menu_original 7785 ## @var[in] _ble_complete_menu_selected 7786 ## @var[in] _ble_complete_menu_common_part 7787 ## @arr[in] _ble_complete_menu_icons 7788 ## 7789 ## 更に以下の変数を使用する 7790 ## 7791 ## @var[in,out] _ble_complete_menu_original= 7792 7793 _ble_complete_menu_original= 7794 7795 ## @fn ble/complete/menu-complete/select index [opts] 7796 function ble/complete/menu-complete/select { 7797 ble/complete/menu#select "$@" 7798 } 7799 7800 ## @fn ble/complete/menu-complete/enter [opts] 7801 ## @var[in,opt] opts 7802 ## backward 7803 ## insert_unique 7804 function ble/complete/menu-complete/enter { 7805 ((${#_ble_complete_menu_icons[@]}>=1)) || return 1 7806 local beg end; ble/complete/menu/get-active-range || return 1 7807 7808 local opts=$1 7809 7810 _ble_edit_mark=$beg 7811 _ble_edit_ind=$end 7812 local comps_fixed=${_ble_complete_menu0_comp[6]} 7813 if [[ $comps_fixed ]]; then 7814 local comps_fixed_length=${comps_fixed%%:*} 7815 ((_ble_edit_mark+=comps_fixed_length)) 7816 fi 7817 7818 # 一意確定時。menu の処理も含めて menu-complete の枠組みの中で確定を実行する。 7819 if [[ :$opts: == *:insert_unique:* ]] && ((${#_ble_complete_menu_items[@]}==1)); then 7820 ble/complete/menu#select 0 7821 ble/decode/keymap/push menu_complete 7822 ble/widget/menu_complete/exit complete 7823 return 0 7824 fi 7825 7826 _ble_complete_menu_original=${_ble_edit_str:beg:end-beg} 7827 ble/complete/menu/redraw 7828 7829 if [[ :$opts: == *:backward:* ]]; then 7830 ble/complete/menu#select "$((${#_ble_complete_menu_items[@]}-1))" 7831 else 7832 ble/complete/menu#select 0 7833 fi 7834 7835 _ble_edit_mark_active=insert 7836 ble/decode/keymap/push menu_complete 7837 return 0 7838 } 7839 7840 function ble/widget/menu_complete/exit { 7841 local opts=$1 7842 ble/decode/keymap/pop 7843 7844 if ((_ble_complete_menu_selected>=0)); then 7845 # 置換情報を再構成 7846 local new=${_ble_edit_str:_ble_complete_menu0_beg:_ble_edit_ind-_ble_complete_menu0_beg} 7847 if [[ :$bleopt_complete_menu_complete_opts: != *:insert-selection:* ]]; then 7848 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 7849 ble/complete/cand/unpack "${_ble_complete_menu_items[_ble_complete_menu_selected]}" 7850 new=$INSERT 7851 fi 7852 local old=$_ble_complete_menu_original 7853 local comp_text=${_ble_edit_str::_ble_complete_menu0_beg}$old${_ble_edit_str:_ble_edit_ind} 7854 local insert_beg=$_ble_complete_menu0_beg 7855 local insert_end=$((_ble_complete_menu0_beg+${#old})) 7856 local insert=$new 7857 local insert_flags= 7858 7859 # suffix の決定と挿入 7860 local suffix= 7861 if [[ :$opts: == *:complete:* ]]; then 7862 local icon=${_ble_complete_menu_icons[_ble_complete_menu_selected-_ble_complete_menu_offset]} 7863 local icon_data=${icon#*:} icon_fields 7864 ble/string#split icon_fields , "${icon%%:*}" 7865 local pack=${icon_data::icon_fields[4]} 7866 7867 local ACTION=${pack%%:*} 7868 if ble/is-function ble/complete/action:"$ACTION"/complete; then 7869 # 補完文脈の復元 7870 local COMP1=${_ble_complete_menu0_comp[0]} 7871 local COMP2=${_ble_complete_menu0_comp[1]} 7872 local COMPS=${_ble_complete_menu0_comp[2]} 7873 local COMPV=${_ble_complete_menu0_comp[3]} 7874 local comp_type=${_ble_complete_menu0_comp[4]} 7875 local comps_flags=${_ble_complete_menu0_comp[5]} 7876 local comps_fixed=${_ble_complete_menu0_comp[6]} 7877 7878 # 補完候補のロード 7879 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 7880 ble/complete/cand/unpack "$pack" 7881 7882 ble/complete/action:"$ACTION"/complete 7883 fi 7884 ble/complete/insert "$_ble_complete_menu0_beg" "$_ble_edit_ind" "$insert" "$suffix" 7885 fi 7886 7887 # 通知 7888 blehook/invoke complete_insert 7889 fi 7890 7891 ble/complete/menu/clear 7892 _ble_edit_mark_active= 7893 _ble_complete_menu_original= 7894 } 7895 function ble/widget/menu_complete/cancel { 7896 ble/decode/keymap/pop 7897 ble/complete/menu#select -1 7898 _ble_edit_mark_active= 7899 _ble_complete_menu_original= 7900 } 7901 function ble/widget/menu_complete/accept { 7902 ble/widget/menu_complete/exit complete 7903 } 7904 function ble/widget/menu_complete/exit-default { 7905 ble/widget/menu_complete/exit 7906 ble/decode/widget/skip-lastwidget 7907 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 7908 } 7909 7910 function ble-decode/keymap:menu_complete/define { 7911 # ble-bind -f __defchar__ menu_complete/self-insert 7912 ble-bind -f __default__ 'menu_complete/exit-default' 7913 ble-bind -f __line_limit__ nop 7914 ble-bind -f C-m 'menu_complete/accept' 7915 ble-bind -f RET 'menu_complete/accept' 7916 ble-bind -f C-g 'menu_complete/cancel' 7917 ble-bind -f 'C-x C-g' 'menu_complete/cancel' 7918 ble-bind -f 'C-M-g' 'menu_complete/cancel' 7919 ble-bind -f C-f 'menu/forward-column' 7920 ble-bind -f right 'menu/forward-column' 7921 ble-bind -f C-i 'menu/forward cyclic' 7922 ble-bind -f TAB 'menu/forward cyclic' 7923 ble-bind -f C-b 'menu/backward-column' 7924 ble-bind -f left 'menu/backward-column' 7925 ble-bind -f C-S-i 'menu/backward cyclic' 7926 ble-bind -f S-TAB 'menu/backward cyclic' 7927 ble-bind -f C-n 'menu/forward-line' 7928 ble-bind -f down 'menu/forward-line' 7929 ble-bind -f C-p 'menu/backward-line' 7930 ble-bind -f up 'menu/backward-line' 7931 ble-bind -f prior 'menu/backward-page' 7932 ble-bind -f next 'menu/forward-page' 7933 ble-bind -f home 'menu/beginning-of-page' 7934 ble-bind -f end 'menu/end-of-page' 7935 7936 local key 7937 for key in {,M-,C-}{0..9}; do 7938 ble-bind -f "$key" 'menu/append-arg' 7939 done 7940 } 7941 7942 _ble_complete_menu_arg= 7943 ## @fn ble/widget/menu/append-arg [opts] 7944 ## @param[in,opt] opts 7945 ## A colon-separated list of the options: 7946 ## 7947 ## always 7948 ## When a numeric argument is not started, the normal digit is by default 7949 ## treated as normal user input. This option makes the normal digit 7950 ## always start a numeric argument. 7951 ## nobell 7952 ## Do not ring edit bell when no corresponding item is found. 7953 ## 7954 function ble/widget/menu/append-arg { 7955 [[ ${LASTWIDGET%%' '*} == */append-arg ]] || _ble_complete_menu_arg= 7956 7957 # 引数入力が開始されていなくて (修飾なしの) 数字キーの時はそのまま通常の数字 7958 # 入力として扱う。 7959 local i=${#KEYS[@]}; ((i&&i--)) 7960 local flag=$((KEYS[i]&_ble_decode_MaskFlag)) 7961 if ! [[ :$1: == *:always:* || flag -ne 0 || $_ble_complete_menu_arg ]]; then 7962 ble/widget/menu_complete/exit-default 7963 return "$?" 7964 fi 7965 7966 local code=$((KEYS[i]&_ble_decode_MaskChar)) 7967 ((48<=code&&code<=57)) || return 1 7968 local ret; ble/util/c2s "$code"; local ch=$ret 7969 ((_ble_complete_menu_arg=10#0$_ble_complete_menu_arg$ch)) 7970 7971 # 番号が範囲内になければ頭から数字を削っていく 7972 local count=${#_ble_complete_menu_items[@]} 7973 while ((_ble_complete_menu_arg>count)); do 7974 ((_ble_complete_menu_arg=10#0${_ble_complete_menu_arg:1})) 7975 done 7976 if ! ((_ble_complete_menu_arg)); then 7977 [[ :$1: == *:nobell:* ]] || 7978 ble/widget/.bell 'menu: out of range' 7979 return 0 7980 fi 7981 7982 # 移動 7983 ble/complete/menu#select "$((_ble_complete_menu_arg-1))" 7984 } 7985 7986 ## @fn ble/widget/menu/append-arg/.is-argument [opts] 7987 ## @param[in,opt] opts 7988 function ble/widget/menu/append-arg/.is-argument { 7989 local i=${#KEYS[@]}; ((i&&i--)) 7990 local flag=$((KEYS[i]&_ble_decode_MaskFlag)) 7991 local code=$((KEYS[i]&_ble_decode_MaskChar)) 7992 [[ :$1: == *:always:* ]] || ((flag)) || return 1 7993 ((48<=code&&code<=57)) 7994 } 7995 7996 #------------------------------------------------------------------------------ 7997 # 7998 # auto-complete 7999 # 8000 8001 function ble/complete/auto-complete/initialize { 8002 local ret 8003 ble-decode-kbd/generate-keycode auto_complete_enter 8004 _ble_complete_KCODE_ENTER=$ret 8005 } 8006 ble/complete/auto-complete/initialize 8007 8008 function ble/highlight/layer:region/mark:auto_complete/get-face { 8009 face=auto_complete 8010 } 8011 8012 _ble_complete_ac_type= 8013 _ble_complete_ac_comp1= 8014 _ble_complete_ac_cand= 8015 _ble_complete_ac_word= 8016 _ble_complete_ac_insert= 8017 _ble_complete_ac_suffix= 8018 8019 ## @fn ble/complete/auto-complete/enter type comp1 suggest cand word [insert suffix] 8020 ## @param[in] type 8021 ## c ... 接頭辞補完 8022 ## h ... 履歴による接頭辞補完。c と同じ取り扱い 8023 ## m ... 部分文字列補完 8024 ## a ... 曖昧補完(1文字目確定) 8025 ## A ... 曖昧補完 8026 ## @param[in] comp1 8027 ## 補完開始点 8028 ## @param[in] suggest 8029 ## 提示文字列 8030 ## @param[in] cand 8031 ## 元の単語 8032 ## @param[in] word 8033 ## 挿入文字列(確定前) 8034 ## @param[in,opt] insert 8035 ## 挿入文字列(確定時)。省略時は word と同じと見做されます。 8036 ## @param[in] suffix 8037 ## 接尾挿入文字列。省略時は空文字列と見做されます。 8038 ## 8039 ## @var[in] _ble_edit_ind 8040 ## 提示文字列挿入位置を指定します。 8041 ## @var[out] _ble_edit_mark 8042 ## 提示文字列の終端点を返します。 8043 ## 8044 function ble/complete/auto-complete/enter { 8045 local type=$1 COMP1=$2 suggest=$3 cand=$4 word=$5 insert1=${6-$5} suffix=${7-} 8046 8047 local limit=$((bleopt_line_limit_length)) 8048 if ((limit&&${#_ble_edit_str}+${#suggest}>limit)); then 8049 # 文字数制限に引っかかる場合には単純に auto-complete は失敗する 8050 return 1 8051 fi 8052 8053 # 提示 8054 local insert; ble-edit/content/replace-limited "$_ble_edit_ind" "$_ble_edit_ind" "$suggest" nobell 8055 ((_ble_edit_mark=_ble_edit_ind+${#suggest})) 8056 8057 _ble_complete_ac_type=$type 8058 _ble_complete_ac_comp1=$COMP1 8059 _ble_complete_ac_cand=$cand 8060 _ble_complete_ac_word=$word 8061 _ble_complete_ac_insert=$insert1 8062 _ble_complete_ac_suffix=$suffix 8063 8064 _ble_edit_mark_active=auto_complete 8065 ble/decode/keymap/push auto_complete 8066 ble-decode-key "$_ble_complete_KCODE_ENTER" # dummy key input to record keyboard macros 8067 return 0 8068 } 8069 8070 ## @fn ble/complete/auto-complete/source:history/.search-light text 8071 ## !string もしくは !?string を用いて履歴の検索を行います 8072 ## @param[in] text 8073 ## @var[out] ret 8074 function ble/complete/auto-complete/source:history/.search-light { 8075 [[ $_ble_history_prefix ]] && return 1 8076 8077 local text=$1 8078 [[ ! $text ]] && return 1 8079 8080 # !string による一致を試みる 8081 # string には [$wordbreaks] は含められない。? はOK 8082 local wordbreaks="<>();&|:$_ble_term_IFS" 8083 local word= expand 8084 if [[ $text != [-0-9#?!]* ]]; then 8085 word=${text%%[$wordbreaks]*} 8086 command='!'$word ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1 8087 if [[ $expand == "$text"* ]]; then 8088 ret=$expand 8089 return 0 8090 fi 8091 fi 8092 8093 # !?string による一致を試みる 8094 # string には "?" は含められない 8095 if [[ $word != "$text" ]]; then 8096 # ? を含まない最長一致部分 8097 local fragments; ble/string#split fragments '?' "$text" 8098 local frag longest_fragments len=0; longest_fragments=('') 8099 for frag in "${fragments[@]}"; do 8100 local len1=${#frag} 8101 ((len1>len&&(len=len1))) && longest_fragments=() 8102 ((len1==len)) && ble/array#push longest_fragments "$frag" 8103 done 8104 8105 for frag in "${longest_fragments[@]}"; do 8106 command='!?'$frag ble/util/assign expand 'ble/edit/hist_expanded/.core' &>/dev/null || return 1 8107 [[ $expand == "$text"* ]] || continue 8108 ret=$expand 8109 return 0 8110 done 8111 fi 8112 8113 return 1 8114 } 8115 8116 _ble_complete_ac_history_needle= 8117 _ble_complete_ac_history_index= 8118 _ble_complete_ac_history_start= 8119 ## @fn ble/complete/auto-complete/source:history/.search-heavy text 8120 ## @var[out] ret 8121 function ble/complete/auto-complete/source:history/.search-heavy { 8122 local text=$1 8123 8124 local count; ble/history/get-count -v count 8125 local start=$((count-1)) 8126 local index=$((count-1)) 8127 local needle=$text 8128 8129 # 途中からの検索再開 8130 ((start==_ble_complete_ac_history_start)) && 8131 [[ $needle == "$_ble_complete_ac_history_needle"* ]] && 8132 index=$_ble_complete_ac_history_index 8133 8134 local isearch_time=0 isearch_ntask=1 8135 local isearch_opts=head 8136 [[ :$comp_type: == *:sync:* ]] || isearch_opts=$isearch_opts:stop_check 8137 ble/history/isearch-backward-blockwise "$isearch_opts"; local ext=$? 8138 _ble_complete_ac_history_start=$start 8139 _ble_complete_ac_history_index=$index 8140 _ble_complete_ac_history_needle=$needle 8141 ((ext)) && return "$ext" 8142 8143 ble/history/get-edited-entry -v ret "$index" 8144 return 0 8145 } 8146 8147 ## @fn ble/complete/auto-complete/source:history/.impl opts 8148 ## @param[in] opts 8149 ## @var[in] comp_type comp_text comp_index 8150 function ble/complete/auto-complete/source:history/.impl { 8151 local opts=$1 8152 local searcher=.search-heavy 8153 [[ :$opts: == *:light:* ]] && searcher=.search-light 8154 8155 local ret 8156 ((_ble_edit_ind==${#_ble_edit_str})) || return 1 8157 ble/complete/auto-complete/source:history/"$searcher" "$_ble_edit_str" || return "$?" # 0, 1 or 148 8158 local command=$ret 8159 [[ $command == "$_ble_edit_str" ]] && return 1 8160 ble/complete/auto-complete/enter h 0 "${command:${#_ble_edit_str}}" '' "$command" 8161 } 8162 function ble/complete/auto-complete/source:history { 8163 [[ $bleopt_complete_auto_history ]] || return 1 8164 ble/complete/auto-complete/source:history/.impl light; local ext=$? 8165 ((ext==0||ext==148)) && return "$ext" 8166 8167 [[ $_ble_history_prefix || $_ble_history_load_done ]] && 8168 ble/complete/auto-complete/source:history/.impl; local ext=$? 8169 ((ext==0||ext==148)) && return "$ext" 8170 } 8171 8172 ## @fn ble/complete/auto-complete/source:syntax 8173 ## @var[in] comp_type comp_text comp_index 8174 function ble/complete/auto-complete/source:syntax { 8175 local sources 8176 ble/complete/context:syntax/generate-sources "$comp_text" "$comp_index" && 8177 ble/complete/context/filter-prefix-sources || return 1 8178 8179 # ble/complete/candidates/generate 設定 8180 local bleopt_complete_contract_function_names= 8181 local bleopt_complete_menu_style=$bleopt_complete_menu_style # source local settings 8182 ((bleopt_complete_polling_cycle>25)) && 8183 local bleopt_complete_polling_cycle=25 8184 local COMP1 COMP2 COMPS COMPV 8185 local comps_flags comps_fixed 8186 local cand_count cand_cand cand_word cand_pack 8187 local cand_limit_reached= 8188 ble/complete/candidates/generate; local ext=$? 8189 [[ $COMPV ]] || return 1 8190 ((ext)) && return "$ext" 8191 8192 ((cand_count)) || return 1 8193 8194 local word=${cand_word[0]} cand=${cand_cand[0]} 8195 [[ $word == "$COMPS" ]] && return 1 8196 8197 # addtail 等の修飾 8198 local insert=$word suffix= 8199 local ACTION=${cand_pack[0]%%:*} 8200 if ble/is-function ble/complete/action:"$ACTION"/complete; then 8201 local "${_ble_complete_cand_varnames[@]/%/=}" # WA #D1570 checked 8202 ble/complete/cand/unpack "${cand_pack[0]}" 8203 ble/complete/action:"$ACTION"/complete 8204 fi 8205 8206 local type= suggest= 8207 if [[ $insert == "$COMPS"* ]]; then 8208 # 入力候補が既に続きに入力されている時は提示しない 8209 [[ ${comp_text:COMP1} == "$insert"* ]] && return 1 8210 8211 type=c 8212 suggest="${insert:${#COMPS}}" 8213 else 8214 case :$comp_type: in 8215 (*:a:*) type=a ;; 8216 (*:m:*) type=m ;; 8217 (*:A:*) type=A ;; 8218 (*) type=r ;; 8219 esac 8220 suggest=" [$insert] " 8221 fi 8222 ble/complete/auto-complete/enter "$type" "$COMP1" "$suggest" "$cand" "$word" "$insert" "$suffix" 8223 } 8224 8225 _ble_complete_auto_source=(history syntax) 8226 8227 ## @fn ble/complete/auto-complete.impl opts 8228 ## @param[in] opts 8229 # コロン区切りのオプションのリストです。 8230 ## sync ユーザ入力があっても処理を中断しない事を指定します。 8231 function ble/complete/auto-complete.impl { 8232 local opts=$1 8233 local comp_type=auto 8234 [[ :$opts: == *:sync:* ]] && comp_type=${comp_type}:sync 8235 8236 local comp_text=$_ble_edit_str comp_index=$_ble_edit_ind 8237 [[ $comp_text ]] || return 0 8238 8239 # menu-filter 編集領域内部では auto-complete は抑制する 8240 if local beg end; ble/complete/menu/get-active-range "$_ble_edit_str" "$_ble_edit_ind"; then 8241 ((_ble_edit_ind<end)) && return 0 8242 fi 8243 8244 local source 8245 for source in "${_ble_complete_auto_source[@]}"; do 8246 ble/complete/auto-complete/source:"$source"; local ext=$? 8247 ((ext==0)) && break 8248 ((ext==148)) && return "$ext" 8249 done 8250 } 8251 8252 ## 背景関数 ble/complete/auto-complete.idle 8253 function ble/complete/auto-complete.idle { 8254 # ※特に上書きしなければ常に wait-user-input で抜ける。 8255 ble/util/idle.wait-user-input 8256 8257 [[ $bleopt_complete_auto_complete ]] || return 1 8258 [[ $_ble_decode_keymap == emacs || $_ble_decode_keymap == vi_[ic]map ]] || return 0 8259 8260 case $_ble_decode_widget_last in 8261 (ble/widget/self-insert|ble/widget/magic-space|ble/widget/magic-slash) ;; 8262 (ble/widget/complete|ble/widget/vi_imap/complete) 8263 [[ :$bleopt_complete_auto_complete_opts: == *:suppress-after-complete:* ]] && return 0 ;; 8264 (*) return 0 ;; 8265 esac 8266 8267 [[ $_ble_edit_str ]] || return 0 8268 8269 # bleopt_complete_auto_delay だけ経過してから処理 8270 ble/util/idle.sleep-until "$((_ble_idle_clock_start+bleopt_complete_auto_delay))" checked && return 0 8271 8272 ble/complete/auto-complete.impl 8273 } 8274 8275 ## 背景関数 ble/complete/auto-menu.idle 8276 function ble/complete/auto-menu.idle { 8277 ble/util/idle.wait-user-input 8278 [[ $_ble_complete_menu_active ]] && return 0 8279 ((bleopt_complete_auto_menu>0)) || return 1 8280 8281 case $_ble_decode_widget_last in 8282 (ble/widget/self-insert|ble/widget/magic-slash) ;; 8283 (ble/widget/complete) ;; 8284 (ble/widget/vi_imap/complete) ;; 8285 (ble/widget/auto_complete/self-insert) ;; 8286 (*) return 0 ;; 8287 esac 8288 8289 [[ $_ble_edit_str ]] || return 0 8290 8291 # bleopt_complete_auto_delay だけ経過してから処理 8292 local until=$((_ble_idle_clock_start+bleopt_complete_auto_menu)) 8293 ble/util/idle.sleep-until "$until" checked && return 0 8294 8295 ble/widget/complete auto_menu:show_menu:no-empty:no-bell 8296 } 8297 8298 ble/function#try ble/util/idle.push-background ble/complete/auto-complete.idle 8299 ble/function#try ble/util/idle.push-background ble/complete/auto-menu.idle 8300 8301 ## @widget auto-complete-enter 8302 ## 8303 ## Note: 8304 ## キーボードマクロで自動補完を明示的に起動する時に用いる編集関数です。 8305 ## auto-complete.idle に於いて ble-decode-key を用いて 8306 ## キー auto_complete_enter を発生させ、 8307 ## 再生時にはこのキーを通して自動補完が起動されます。 8308 ## 8309 function ble/widget/auto-complete-enter { 8310 ble/complete/auto-complete.impl sync 8311 } 8312 function ble/widget/auto_complete/cancel { 8313 ble/decode/keymap/pop 8314 ble-edit/content/replace "$_ble_edit_ind" "$_ble_edit_mark" '' 8315 _ble_edit_mark=$_ble_edit_ind 8316 _ble_edit_mark_active= 8317 _ble_complete_ac_insert= 8318 _ble_complete_ac_suffix= 8319 } 8320 function ble/widget/auto_complete/insert { 8321 ble/decode/keymap/pop 8322 ble-edit/content/replace "$_ble_edit_ind" "$_ble_edit_mark" '' 8323 _ble_edit_mark=$_ble_edit_ind 8324 8325 local comp_text=$_ble_edit_str 8326 local insert_beg=$_ble_complete_ac_comp1 8327 local insert_end=$_ble_edit_ind 8328 local insert=$_ble_complete_ac_insert 8329 local suffix=$_ble_complete_ac_suffix 8330 ble/complete/insert "$insert_beg" "$insert_end" "$insert" "$suffix" 8331 blehook/invoke complete_insert 8332 8333 _ble_edit_mark_active= 8334 _ble_complete_ac_insert= 8335 _ble_complete_ac_suffix= 8336 ble/complete/menu/clear 8337 ble-edit/content/clear-arg 8338 return 0 8339 } 8340 function ble/widget/auto_complete/cancel-default { 8341 ble/widget/auto_complete/cancel 8342 ble/decode/widget/skip-lastwidget 8343 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 8344 } 8345 8346 ## @fn ble/widget/auto_complete/self-insert/.is-magic-space 8347 ## @var[in] KEYS 8348 ## 現在のキー入力が親 keymap で magic-space に対応するかどうかを判定します。 8349 8350 function ble/widget/auto_complete/self-insert/.is-magic-space { 8351 ((${#KEYS[@]}==1)) || return 1 8352 8353 local ikeymap=$((${#_ble_decode_keymap_stack[@]}-1)) 8354 ((ikeymap>=0)) || return 1 8355 8356 local dicthead=_ble_decode_${_ble_decode_keymap_stack[ikeymap]}_kmap_ 8357 builtin eval "local ent=\${$dicthead$_ble_decode_key__seq[KEYS[0]]-}" 8358 local command=${ent#*:} 8359 [[ $command == ble/widget/magic-space || $command == ble/widget/magic-slash ]] 8360 } 8361 8362 function ble/widget/auto_complete/self-insert { 8363 if [[ $_ble_edit_overwrite_mode ]] || ble/widget/auto_complete/self-insert/.is-magic-space; then 8364 ble/widget/auto_complete/cancel-default 8365 return "$?" 8366 fi 8367 8368 local code; ble/widget/self-insert/.get-code 8369 ((code==0)) && return 0 8370 8371 local ret 8372 8373 # もし挿入によって現在の候補が変わらないのであれば、 8374 # 候補を表示したまま挿入を実行する。 8375 ble/util/c2s "$code"; local ins=$ret 8376 local comps_cur=${_ble_edit_str:_ble_complete_ac_comp1:_ble_edit_ind-_ble_complete_ac_comp1} 8377 local comps_new=$comps_cur$ins 8378 local processed= 8379 if [[ $_ble_complete_ac_type == [ch] ]]; then 8380 # c: 入力済み部分が補完結果の先頭に含まれる場合 8381 # 挿入した後でも補完結果の先頭に含まれる場合、その文字数だけ確定。 8382 if [[ $_ble_complete_ac_word == "$comps_new"* ]]; then 8383 ((_ble_edit_ind+=${#ins})) 8384 8385 # Note: 途中で完全一致した場合は tail を挿入せずに終了する事にする 8386 [[ $_ble_complete_ac_word == "$comps_new" ]] && ble/widget/auto_complete/cancel 8387 processed=1 8388 fi 8389 elif [[ $_ble_complete_ac_type == [rmaA] && $ins != [{,}] ]]; then 8390 if local ret simple_flags simple_ibrace; ble/syntax:bash/simple-word/reconstruct-incomplete-word "$comps_new"; then 8391 if ble/complete/source/eval-simple-word "$ret" single && local compv_new=$ret; then 8392 # r: 遡って書き換わる時 8393 # 挿入しても展開後に一致する時、そのまま挿入。 8394 # 元から展開後に一致していない場合もあるが、その場合は一旦候補を消してやり直し。 8395 # a: 曖昧一致の時 8396 # 文字を挿入後に展開してそれが曖昧一致する時、そのまま挿入。 8397 8398 local filter_type=head 8399 case $_ble_complete_ac_type in 8400 (*m*) filter_type=substr ;; 8401 (*a*) filter_type=hsubseq ;; 8402 (*A*) filter_type=subseq ;; 8403 esac 8404 8405 local comps_fixed= 8406 local comp_filter_type 8407 local comp_filter_pattern 8408 ble/complete/candidates/filter#init "$filter_type" "$compv_new" 8409 if ble/complete/candidates/filter#test "$_ble_complete_ac_cand"; then 8410 local insert; ble-edit/content/replace-limited "$_ble_edit_ind" "$_ble_edit_ind" "$ins" 8411 ((_ble_edit_ind+=${#insert},_ble_edit_mark+=${#insert})) 8412 [[ $_ble_complete_ac_cand == "$compv_new" ]] && 8413 ble/widget/auto_complete/cancel 8414 processed=1 8415 fi 8416 fi 8417 fi 8418 fi 8419 8420 if [[ $processed ]]; then 8421 # notify dummy insertion 8422 local comp_text= insert_beg=0 insert_end=0 insert=$ins suffix= 8423 blehook/invoke complete_insert 8424 return 0 8425 else 8426 ble/widget/auto_complete/cancel 8427 ble/decode/widget/skip-lastwidget 8428 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 8429 fi 8430 } 8431 8432 function ble/widget/auto_complete/insert-on-end { 8433 if ((_ble_edit_mark==${#_ble_edit_str})); then 8434 ble/widget/auto_complete/insert 8435 else 8436 ble/widget/auto_complete/cancel-default 8437 fi 8438 } 8439 function ble/widget/auto_complete/insert-word { 8440 local breaks=${bleopt_complete_auto_wordbreaks:-$_ble_term_IFS} 8441 local rex='^['$breaks']*([^'$breaks']+['$breaks']*)?' 8442 if [[ $_ble_complete_ac_type == [ch] ]]; then 8443 local ins=${_ble_edit_str:_ble_edit_ind:_ble_edit_mark-_ble_edit_ind} 8444 [[ $ins =~ $rex ]] 8445 if [[ $BASH_REMATCH == "$ins" ]]; then 8446 ble/widget/auto_complete/insert 8447 return 0 8448 else 8449 local ins=$BASH_REMATCH 8450 8451 # Note: 以下の様に _ble_edit_ind だけずらす。 8452 # <C>he<I>llo world<M> → <C>hello <I>world<M> 8453 # (<C> = comp1, <I> = _ble_edit_ind, <M> = _ble_edit_mark) 8454 ((_ble_edit_ind+=${#ins})) 8455 8456 # 通知 8457 local comp_text=$_ble_edit_str 8458 local insert_beg=$_ble_complete_ac_comp1 8459 local insert_end=$_ble_edit_ind 8460 local insert=${_ble_edit_str:insert_beg:insert_end-insert_beg}$ins 8461 local suffix= 8462 blehook/invoke complete_insert 8463 return 0 8464 fi 8465 elif [[ $_ble_complete_ac_type == [rmaA] ]]; then 8466 local ins=$_ble_complete_ac_insert 8467 [[ $ins =~ $rex ]] 8468 if [[ $BASH_REMATCH == "$ins" ]]; then 8469 ble/widget/auto_complete/insert 8470 return 0 8471 else 8472 local ins=$BASH_REMATCH 8473 8474 # Note: 以下の様に内容を書き換える。 8475 # <C>hll<I> [hello world] <M> → <C>hello <I>world<M> 8476 # (<C> = comp1, <I> = _ble_edit_ind, <M> = _ble_edit_mark) 8477 _ble_complete_ac_type=c 8478 # Note: 内容としては短くなるので replace-limited は使わなくて良い。 8479 ble-edit/content/replace "$_ble_complete_ac_comp1" "$_ble_edit_mark" "$_ble_complete_ac_insert" 8480 ((_ble_edit_ind=_ble_complete_ac_comp1+${#ins}, 8481 _ble_edit_mark=_ble_complete_ac_comp1+${#_ble_complete_ac_insert})) 8482 8483 # 通知 8484 local comp_text=$_ble_edit_str 8485 local insert_beg=$_ble_complete_ac_comp1 8486 local insert_end=$_ble_edit_ind 8487 local insert=$ins 8488 local suffix= 8489 blehook/invoke complete_insert 8490 8491 return 0 8492 fi 8493 fi 8494 return 1 8495 } 8496 function ble/widget/auto_complete/accept-line { 8497 ble/widget/auto_complete/insert 8498 ble-decode-key 13 8499 } 8500 function ble/widget/auto_complete/notify-enter { 8501 ble/decode/widget/skip-lastwidget 8502 } 8503 function ble-decode/keymap:auto_complete/define { 8504 ble-bind -f __defchar__ auto_complete/self-insert 8505 ble-bind -f __default__ auto_complete/cancel-default 8506 ble-bind -f __line_limit__ nop 8507 ble-bind -f 'C-g' auto_complete/cancel 8508 ble-bind -f 'C-x C-g' auto_complete/cancel 8509 ble-bind -f 'C-M-g' auto_complete/cancel 8510 ble-bind -f S-RET auto_complete/insert 8511 ble-bind -f S-C-m auto_complete/insert 8512 ble-bind -f C-f auto_complete/insert-on-end 8513 ble-bind -f right auto_complete/insert-on-end 8514 ble-bind -f C-e auto_complete/insert-on-end 8515 ble-bind -f end auto_complete/insert-on-end 8516 ble-bind -f M-f auto_complete/insert-word 8517 ble-bind -f M-right auto_complete/insert-word 8518 ble-bind -f C-j auto_complete/accept-line 8519 ble-bind -f C-RET auto_complete/accept-line 8520 ble-bind -f auto_complete_enter auto_complete/notify-enter 8521 } 8522 8523 #------------------------------------------------------------------------------ 8524 # 8525 # sabbrev 8526 # 8527 8528 # The following are variables defined in core-complete-def.sh: 8529 # 8530 # @var _ble_complete_sabbrev_wordwise 8531 # @var _ble_complete_sabbrev_literal 8532 8533 function ble/complete/sabbrev/.initialize-print { 8534 sgr0= sgr1= sgr2= sgr3= sgro= 8535 if [[ $flags == *c* || $flags != *n* && -t 1 ]]; then 8536 local ret 8537 ble/color/face2sgr command_function; sgr1=$ret 8538 ble/color/face2sgr syntax_varname; sgr2=$ret 8539 ble/color/face2sgr syntax_quoted; sgr3=$ret 8540 ble/color/face2sgr argument_option; sgro=$ret 8541 sgr0=$_ble_term_sgr0 8542 fi 8543 } 8544 function ble/complete/sabbrev/.print-definition { 8545 local key=$1 type=${2%%:*} value=${2#*:} 8546 local option= 8547 [[ $type != w ]] && option=$sgro'-'$type$sgr0' ' 8548 8549 local ret 8550 ble/string#quote-word "$key" quote-empty:sgrq="$sgr3":sgr0="$sgr2" 8551 key=$sgr2$ret$sgr0 8552 ble/string#quote-word "$value" sgrq="$sgr3":sgr0="$sgr0" 8553 value=$ret 8554 ble/util/print "${sgr1}ble-sabbrev$sgr0 $option$key=$value" 8555 } 8556 8557 ## @fn ble/complete/sabbrev/register key value 8558 ## 静的略語展開を登録します。 8559 ## @param[in] key value 8560 ## 8561 ## @fn ble/complete/sabbrev/list type [keys...] 8562 ## 登録されている静的略語展開の一覧を表示します。 8563 ## @var[in] flags 8564 ## 8565 ## @fn ble/complete/sabbrev/reset type [keys...] 8566 ## 登録されている静的略語展開を削除します。 8567 ## @var[in] flags 8568 ## 8569 ## @fn ble/complete/sabbrev/wordwise.get key 8570 ## 静的略語展開の展開値を取得します。 8571 ## @param[in] key 8572 ## @var[out] ret 8573 ## 8574 8575 # Note: _ble_complete_sabbrev_wordwise は core-complete-def.sh で定義 8576 function ble/complete/sabbrev/register { 8577 local key=$1 value=$2 8578 if [[ $value == [il]:* ]]; then 8579 ble/gdict#set _ble_complete_sabbrev_literal "$key" "$value" 8580 ble/gdict#unset _ble_complete_sabbrev_wordwise "$key" 8581 else 8582 ble/gdict#set _ble_complete_sabbrev_wordwise "$key" "$value" 8583 ble/gdict#unset _ble_complete_sabbrev_literal "$key" 8584 fi 8585 } 8586 function ble/complete/sabbrev/list { 8587 local type=$1; shift 8588 local keys ret; keys=("$@") 8589 if ((${#keys[@]}==0)); then 8590 if [[ $type ]]; then 8591 # type が指定されている時は、その type の sabbrev だけ表示する 8592 local dict=_ble_complete_sabbrev_wordwise 8593 case $type in 8594 ([wm]) dict=_ble_complete_sabbrev_wordwise ;; 8595 ([il]) dict=_ble_complete_sabbrev_literal ;; 8596 esac 8597 8598 local ret key 8599 ble/gdict#keys "$dict" 8600 for key in "${ret[@]}"; do 8601 ble/gdict#get "$dict" "$key" && [[ $ret == "$type":* ]] || continue 8602 ble/array#push keys "$key" 8603 done 8604 else 8605 ble/gdict#keys _ble_complete_sabbrev_wordwise 8606 keys=("${ret[@]}") 8607 ble/gdict#keys _ble_complete_sabbrev_literal 8608 ble/array#push keys "${ret[@]}" 8609 fi 8610 ((${#keys[@]})) || return 0 8611 fi 8612 8613 local sgr0 sgr1 sgr2 sgr3 sgro 8614 ble/complete/sabbrev/.initialize-print 8615 8616 local key ext=0 8617 for key in "${keys[@]}"; do 8618 if ble/gdict#get _ble_complete_sabbrev_wordwise "$key"; then 8619 ble/complete/sabbrev/.print-definition "$key" "$ret" 8620 elif ble/gdict#get _ble_complete_sabbrev_literal "$key"; then 8621 ble/complete/sabbrev/.print-definition "$key" "$ret" 8622 else 8623 ble/util/print "ble-sabbrev: $key: not found." >&2 8624 ext=1 8625 fi 8626 done 8627 8628 return "$ext" 8629 } 8630 function ble/complete/sabbrev/reset { 8631 local type=$1; shift 8632 if (($#)); then 8633 local key 8634 for key; do 8635 ble/gdict#unset _ble_complete_sabbrev_wordwise "$key" 8636 ble/gdict#unset _ble_complete_sabbrev_literal "$key" 8637 done 8638 elif [[ $type ]]; then 8639 # type が指定されている時は、その type の sabbrev だけ削除する 8640 8641 local dict=_ble_complete_sabbrev_wordwise 8642 case $type in 8643 ([wm]) dict=_ble_complete_sabbrev_wordwise ;; 8644 ([il]) dict=_ble_complete_sabbrev_literal ;; 8645 esac 8646 8647 local ret key 8648 ble/gdict#keys "$dict" 8649 for key in "${ret[@]}"; do 8650 ble/gdict#get "$dict" "$key" && [[ $ret == "$type":* ]] || continue 8651 ble/gdict#unset "$dict" "$key" 8652 done 8653 else 8654 ble/gdict#clear _ble_complete_sabbrev_wordwise 8655 ble/gdict#clear _ble_complete_sabbrev_literal 8656 fi 8657 return 0 8658 } 8659 function ble/complete/sabbrev/wordwise.get { 8660 local key=$1 8661 ble/gdict#get _ble_complete_sabbrev_wordwise "$key" 8662 } 8663 function ble/complete/sabbrev/wordwise.get-keys { 8664 local ret 8665 ble/gdict#keys _ble_complete_sabbrev_wordwise 8666 keys=("${ret[@]}") 8667 } 8668 8669 ## @fn ble/complete/sabbrev/literal.find str [opts] 8670 ## 最長一致するリテラル略語とその値を取得します。 8671 ## @param[in] str 8672 ## @param[in,opt] opts 8673 ## コロン区切りのオプションです。 8674 ## 8675 ## filter-by-patterns 8676 ## patterns 配列に指定されているパターンに一致する sabbrev だけを一致対象 8677 ## とします。 8678 ## @arr[in] patterns 8679 ## 8680 ## @var[out] key 8681 ## @var[out] ret 8682 ## 8683 function ble/complete/sabbrev/literal.find { 8684 key= 8685 local ent= opts=$2 key1 ent1 8686 ble/gdict#keys _ble_complete_sabbrev_literal 8687 for key1 in "${ret[@]}"; do 8688 ((${#key1}>${#key})) || continue 8689 8690 ble/gdict#get _ble_complete_sabbrev_literal "$key1" || continue; ent1=$ret 8691 [[ $1 == *"$key1" ]] || continue 8692 if [[ $ent1 == l:* ]]; then 8693 ble/string#match "${1%"$key1"}" $'(^|\n)[ \t]*$' || continue 8694 fi 8695 8696 [[ :$opts: == *:filter-by-patterns:* ]] && 8697 ((${#patterns[@]})) && 8698 ! ble/complete/string#match-patterns "$key1" "${patterns[@]}" && 8699 continue 8700 8701 key=$key1 ent=$ent1 8702 done 8703 8704 ret=$ent 8705 [[ $key ]] 8706 } 8707 8708 ## @fn ble/complete/sabbrev/read-arguments/.set-type opt 8709 ## @var[in,out] flags type 8710 function ble/complete/sabbrev/read-arguments/.set-type { 8711 local new_type 8712 case $1 in 8713 (--type=wordwise | -w) new_type=w ;; 8714 (--type=dynamic | -m) new_type=m ;; 8715 (--type=inline | -i) new_type=i ;; 8716 (--type=linewise | -l) new_type=l ;; 8717 (*) 8718 ble/util/print "ble-sabbrev: unknown sabbrev type '${1#--type=}'." >&2 8719 flags=E$flags 8720 return 1 ;; 8721 esac 8722 8723 if [[ $type && $type != "$new_type" ]]; then 8724 ble/util/print "ble-sabbrev: arg $1: a conflicting sabbrev type (-$type) has already been specified." >&2 8725 flags=E$flags 8726 fi 8727 type=$new_type 8728 } 8729 8730 ## @fn ble/complete/sabbrev/read-arguments args... 8731 ## @arr[out] specs print 8732 ## @var[out] flags type 8733 function ble/complete/sabbrev/read-arguments { 8734 specs=() print=() 8735 flags= type= 8736 while (($#)); do 8737 local arg=$1; shift 8738 if [[ $flags != L && $arg == -* ]]; then 8739 case $arg in 8740 (--) 8741 flags=L$flags ;; 8742 (--help) 8743 flags=H$flags ;; 8744 (--reset) 8745 flags=r$flags 8746 (--color|--color=always) 8747 flags=c${flags//[cn]} ;; 8748 (--color=never) 8749 flags=n${flags//[cn]} ;; 8750 (--color=auto) 8751 flags=${flags//[cn]} ;; 8752 (--color=*) 8753 ble/util/print "ble-sabbrev: unknown color type '$arg'." >&2 8754 flags=E$flags ;; 8755 (--type=*) 8756 ble/complete/sabbrev/read-arguments/.set-type "$arg" ;; 8757 (--type) 8758 if ((!$#)); then 8759 ble/util/print "ble-sabbrev: option argument for '$arg' is missing" >&2 8760 flags=E$flags 8761 else 8762 ble/complete/sabbrev/read-arguments/.set-type "--type=$1"; shift 8763 fi ;; 8764 (--*) 8765 ble/util/print "ble-sabbrev: unknown option '$arg'." >&2 8766 flags=E$flags ;; 8767 (-*) 8768 local i n=${#arg} c 8769 for ((i=1;i<n;i++)); do 8770 c=${arg:i:1} 8771 case $c in 8772 ([wmil]) ble/complete/sabbrev/read-arguments/.set-type "-$c" ;; 8773 (r) flags=r$flags ;; 8774 (*) 8775 ble/util/print "ble-sabbrev: unknown option '-$c'." >&2 8776 flags=E$flags ;; 8777 esac 8778 done ;; 8779 esac 8780 else 8781 if [[ $arg == ?*=* ]]; then 8782 ble/array#push specs "$arg" 8783 else 8784 ble/array#push print "$arg" 8785 fi 8786 fi 8787 done 8788 return 0 8789 } 8790 8791 ## @fn ble-sabbrev key=value 8792 ## 静的略語展開を登録します。 8793 function ble-sabbrev { 8794 local flags type specs print 8795 ble/complete/sabbrev/read-arguments "$@" 8796 if [[ $flags == *H* || $flags == *E* ]]; then 8797 [[ $flags == *E* ]] && ble/util/print 8798 ble/util/print-lines \ 8799 'usage: ble-sabbrev [--type=TYPE|-wmil] [KEY=VALUE]...' \ 8800 'usage: ble-sabbrev [-r|--reset] [--type=TYPE|-wmil|KEY...]' \ 8801 'usage: ble-sabbrev [--color[=auto|always|never]] [--type=TYPE|-wmil|KEY...]' \ 8802 'usage: ble-sabbrev --help' \ 8803 ' Register sabbrev expansion.' \ 8804 '' \ 8805 'OPTIONS' \ 8806 ' -w, --type=wordwise replace matching word.' \ 8807 ' -m, --type=dynamic run command and replace matching word.' \ 8808 ' -i, --type=inline replace matching suffix.' \ 8809 ' -l, --type=linewise replace matching line.' \ 8810 '' \ 8811 ' -r, --reset remove specified set of sabbrev.' \ 8812 '' \ 8813 ' --color=always enable color output.' \ 8814 ' --color=never disable color output.' \ 8815 ' --color, --color=auto automatically determine color output (default).' \ 8816 '' 8817 [[ ! $flags == *E* ]]; return "$?" 8818 fi 8819 8820 local ext=0 8821 if ((${#specs[@]}==0||${#print[@]})); then 8822 if [[ $flags == *r* ]]; then 8823 ble/complete/sabbrev/reset "$type" "${print[@]}" 8824 else 8825 ble/complete/sabbrev/list "$type" "${print[@]}" 8826 fi || ext=$? 8827 fi 8828 8829 local spec key value 8830 for spec in "${specs[@]}"; do 8831 # spec は key=value の形式 8832 key=${spec%%=*} value=${spec#*=} 8833 ble/complete/sabbrev/register "$key" "${type:-w}:$value" 8834 done 8835 return "$ext" 8836 } 8837 8838 ## @fn ble/complete/sabbrev/locate-key rex_source_type 8839 ## @var[out] pos 8840 ## @var[in] comp_index comp_text 8841 function ble/complete/sabbrev/locate-key { 8842 pos=$comp_index 8843 local rex_source_type='^('$1')$' 8844 local sources src asrc 8845 ble/complete/context:syntax/generate-sources 8846 for src in "${sources[@]}"; do 8847 ble/string#split-words asrc "$src" 8848 [[ ${asrc[0]} =~ $rex_source_type ]] || continue 8849 8850 if [[ ${asrc[0]} == argument ]]; then 8851 # source:argument かつ変数代入形式の時は右辺を sabbrev の対象とする。 8852 # wtype を (恰も declare の引数の様に) ATTR_VAR にして find-rhs を呼び出 8853 # す。 8854 local wtype=$_ble_attr_VAR wbeg=${asrc[1]} wlen=$((comp_index-asrc[1])) ret 8855 ble/syntax:bash/find-rhs "$wtype" "$wbeg" "$wlen" long-option && 8856 asrc[0]=rhs asrc[1]=$ret 8857 fi 8858 8859 if [[ ${asrc[0]} == rhs ]]; then 8860 # 変数代入形式の右辺では : で区切った最後のフィールドを対象とする。最後の 8861 # unquoted colon まで読み飛ばす。[Note: 文法情報の参照をすればより厳密に 8862 # 決定できるかもしれないが今は実装しない] 8863 local rex_element 8864 ble/syntax:bash/simple-word/get-rex_element : 8865 local rex='^:*('$rex_element':+)' 8866 [[ ${_ble_edit_str:asrc[1]:comp_index-asrc[1]} =~ $rex ]] && 8867 ((asrc[1]+=${#BASH_REMATCH})) 8868 fi 8869 8870 ((asrc[1]<pos)) && pos=${asrc[1]} 8871 done 8872 ((pos<comp_index)) 8873 } 8874 8875 ## @fn ble/complete/sabbrev/expand [opts] 8876 ## @param[in,opt] opts 8877 ## コロン区切りのオプションです。 8878 ## 8879 ## wordwise 8880 ## literal 8881 ## それぞれ wordwise sabbrev および literal sabbrev (line, inline) の展開 8882 ## を実行します。どちらも指定されていない場合は両方実行します。 8883 ## 8884 ## pattern=PATTERN 8885 ## これが一つ以上指定されていた時は何れかの PATTERN で指定された名前を持 8886 ## つ sabbrev だけ有効にします。 8887 ## 8888 ## strip-slash 8889 ## 展開後の末尾に含まれる / を削除します。 8890 ## 8891 ## type-status 8892 ## 実行した sabbrev の種類を終了ステータスで返します。 8893 ## 8894 function ble/complete/sabbrev/expand { 8895 local opts=$1 8896 local comp_index=$_ble_edit_ind comp_text=$_ble_edit_str 8897 8898 [[ :$opts: == *:wordwise:* || :$opts: == *:literal:* ]] || 8899 opts=$opts:wordwise:literal 8900 8901 local -a patterns=() 8902 local ret 8903 ble/opts#extract-all-optargs "$opts" pattern && 8904 patterns=("${ret[@]}") 8905 8906 # wordwise sabbrev と literal sabbrev を両方検索しより長い一致を選択する 8907 local key1= ent1= key2= ent2= 8908 if [[ :$opts: == *:wordwise:* ]]; then 8909 local pos key ret 8910 ble/complete/sabbrev/locate-key 'file|command|argument|variable:w|wordlist:.*|sabbrev|rhs' && 8911 key=${_ble_edit_str:pos:comp_index-pos} && 8912 ble/complete/sabbrev/wordwise.get "$key" && 8913 { ((${#patterns[@]}==0)) || ble/complete/string#match-patterns "$key" "${patterns[@]}"; } && 8914 key1=$key ent1=$ret 8915 fi 8916 if [[ :$opts: == *:literal:* ]]; then 8917 local key ret 8918 ble/complete/sabbrev/literal.find "${_ble_edit_str::comp_index}" filter-by-patterns && 8919 key2=$key ent2=$ret 8920 fi 8921 if ((${#key1}>=${#key2})); then 8922 local key=$key1 ent=$ent1 8923 else 8924 local key=$key2 ent=$ent2 8925 fi 8926 [[ $key ]] || return 1 8927 8928 local type=${ent%%:*} value=${ent#*:} 8929 8930 local exit=0 8931 if [[ :$opts: == *:type-status:* ]]; then 8932 local ret 8933 ble/util/s2c "$type" 8934 exit=$ret 8935 fi 8936 8937 case $type in 8938 ([wil]) 8939 [[ :$opts: == *:strip-slash:* ]] && value=${value%/} 8940 local pos=$((comp_index-${#key})) 8941 ble/widget/.replace-range "$pos" "$comp_index" "$value" 8942 ((_ble_edit_ind=pos+${#value})) ;; 8943 (m) 8944 # prepare completion context 8945 local comp_type= comps_flags= comps_fixed= 8946 local COMP1=$pos COMP2=$pos COMPS=$key COMPV= 8947 ble/complete/candidates/comp_type#read-rl-variables 8948 8949 local flag_force_fignore= 8950 local flag_source_filter=1 8951 8952 # construct cand_pack 8953 local cand_count cand_cand cand_word cand_pack 8954 ble/complete/candidates/clear 8955 local COMP_PREFIX= 8956 8957 # local settings 8958 local bleopt_sabbrev_menu_style=$bleopt_complete_menu_style 8959 local bleopt_sabbrev_menu_opts= 8960 8961 # generate candidates 8962 # COMPREPLY に候補を追加してもらうか、 8963 # 或いは手動で ble/complete/cand/yield 等を呼び出してもらう。 8964 local -a COMPREPLY=() 8965 builtin eval -- "$value" 8966 8967 local cand action=word "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 8968 ble/complete/cand/yield.initialize "$action" 8969 for cand in "${COMPREPLY[@]}"; do 8970 ble/complete/cand/yield "$action" "$cand" "" 8971 done 8972 8973 if ((cand_count==0)); then 8974 return 1 8975 elif ((cand_count==1)); then 8976 local value=${cand_word[0]} 8977 [[ :$opts: == *:strip-slash:* ]] && value=${value%/} 8978 ble/widget/.replace-range "$pos" "$comp_index" "$value" 8979 ((_ble_edit_ind=pos+${#value})) 8980 return "$exit" 8981 fi 8982 8983 # Note: 既存の内容 (key) は削除する 8984 ble/widget/.replace-range "$pos" "$comp_index" '' 8985 8986 local bleopt_complete_menu_style=$bleopt_sabbrev_menu_style 8987 local menu_common_part= 8988 ble/complete/menu/show || return "$?" 8989 [[ :$bleopt_sabbrev_menu_opts: == *:enter_menu:* ]] && 8990 ble/complete/menu-complete/enter "$bleopt_sabbrev_menu_opts" 8991 return 147 ;; 8992 (*) return 1 ;; 8993 esac 8994 return "$exit" 8995 } 8996 function ble/widget/sabbrev-expand { 8997 if ! ble/complete/sabbrev/expand; then 8998 ble/widget/.bell 8999 return 1 9000 fi 9001 } 9002 9003 # sabbrev の補完候補 9004 function ble/complete/action:sabbrev/initialize { CAND=$value; } 9005 function ble/complete/action:sabbrev/complete { :; } 9006 function ble/complete/action:sabbrev/init-menu-item { 9007 local ret; ble/color/face2g command_alias; g=$ret 9008 show=$INSERT 9009 } 9010 function ble/complete/action:sabbrev/get-desc { 9011 local ret; ble/complete/sabbrev/wordwise.get "$INSERT" 9012 desc="$desc_sgrt(sabbrev)$desc_sgr0 $ret" 9013 } 9014 function ble/complete/source:sabbrev { 9015 local opts=$bleopt_complete_source_sabbrev_opts 9016 [[ ! $COMPS && :$opts: == *:no-empty-completion:* ]] && return 1 9017 9018 local keys; ble/complete/sabbrev/wordwise.get-keys "$opts" 9019 9020 local filter_type=$comp_filter_type 9021 [[ $filter_type == none ]] && filter_type=head 9022 local comps_fixed= 9023 9024 # フィルタリング用設定を COMPS で再初期化 9025 local comp_filter_type 9026 local comp_filter_pattern 9027 ble/complete/candidates/filter#init "$filter_type" "$COMPS" 9028 local cand action=sabbrev "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 9029 ble/complete/cand/yield.initialize "$action" 9030 for cand in "${keys[@]}"; do 9031 ble/complete/candidates/filter#test "$cand" || continue 9032 ble/complete/string#match-patterns "$cand" "${_ble_complete_source_sabbrev_ignore[@]}" && continue 9033 9034 # filter で除外されない為に cand には評価後の値を入れる必要がある。 9035 local ret simple_flags simple_ibrace 9036 ble/syntax:bash/simple-word/reconstruct-incomplete-word "$cand" && 9037 ble/complete/source/eval-simple-word "$ret" single || continue 9038 9039 local value=$ret # referenced in "ble/complete/action:sabbrev/initialize" 9040 local flag_source_filter=1 9041 ble/complete/cand/yield "$action" "$cand" 9042 done 9043 } 9044 9045 function ble/complete/alias/expand { 9046 local pos comp_index=$_ble_edit_ind comp_text=$_ble_edit_str 9047 ble/complete/sabbrev/locate-key 'command' 9048 ((pos<comp_index)) || return 1 9049 9050 local word=${_ble_edit_str:pos:comp_index-pos} 9051 local ret; ble/alias#expand "$word" 9052 [[ $ret != "$word" ]] || return 1 9053 ble/widget/.replace-range "$pos" "$comp_index" "$ret" 9054 return 0 9055 } 9056 9057 #------------------------------------------------------------------------------ 9058 # 9059 # dabbrev 9060 # 9061 9062 _ble_complete_dabbrev_original= 9063 _ble_complete_dabbrev_regex1= 9064 _ble_complete_dabbrev_regex2= 9065 _ble_complete_dabbrev_index= 9066 _ble_complete_dabbrev_pos= 9067 _ble_complete_dabbrev_stack=() 9068 9069 function ble/complete/dabbrev/.show-status.fib { 9070 local index='!'$((_ble_complete_dabbrev_index+1)) 9071 local nmatch=${#_ble_complete_dabbrev_stack[@]} 9072 local needle=$_ble_complete_dabbrev_original 9073 local text="(dabbrev#$nmatch: << $index) \`$needle'" 9074 9075 local pos=$1 9076 if [[ $pos ]]; then 9077 local count; ble/history/get-count 9078 local percentage=$((count?pos*1000/count:1000)) 9079 text="$text searching... @$pos ($((percentage/10)).$((percentage%10))%)" 9080 fi 9081 9082 ((fib_ntask)) && text="$text *$fib_ntask" 9083 9084 ble/edit/info/show text "$text" 9085 } 9086 function ble/complete/dabbrev/show-status { 9087 local fib_ntask=${#_ble_util_fiberchain[@]} 9088 ble/complete/dabbrev/.show-status.fib 9089 } 9090 function ble/complete/dabbrev/erase-status { 9091 ble/edit/info/default 9092 } 9093 9094 ## @fn ble/complete/dabbrev/initialize-variables 9095 function ble/complete/dabbrev/initialize-variables { 9096 # Note: _ble_term_IFS を前置しているので ! や ^ が先頭に来ない事は保証される 9097 local wordbreaks; ble/complete/get-wordbreaks 9098 _ble_complete_dabbrev_wordbreaks=$wordbreaks 9099 9100 local left=${_ble_edit_str::_ble_edit_ind} 9101 local original=${left##*[$wordbreaks]} 9102 local p1=$((_ble_edit_ind-${#original})) p2=$_ble_edit_ind 9103 _ble_edit_mark=$p1 9104 _ble_edit_ind=$p2 9105 _ble_complete_dabbrev_original=$original 9106 9107 local ret; ble/string#escape-for-extended-regex "$original" 9108 local needle='(^|['$wordbreaks'])'$ret 9109 _ble_complete_dabbrev_regex1=$needle 9110 _ble_complete_dabbrev_regex2='('$needle'[^'$wordbreaks']*).*' 9111 9112 local index; ble/history/get-index 9113 _ble_complete_dabbrev_index=$index 9114 _ble_complete_dabbrev_pos=${#_ble_edit_str} 9115 9116 _ble_complete_dabbrev_stack=() 9117 } 9118 9119 function ble/complete/dabbrev/reset { 9120 local original=$_ble_complete_dabbrev_original 9121 ble-edit/content/replace "$_ble_edit_mark" "$_ble_edit_ind" "$original" 9122 ((_ble_edit_ind=_ble_edit_mark+${#original})) 9123 _ble_edit_mark_active= 9124 } 9125 9126 ## @fn ble/complete/dabbrev/search-in-history-entry line index 9127 ## @param[in] line 9128 ## 検索対象の内容を指定します。 9129 ## @param[in] index 9130 ## 検索対象の履歴番号を指定します。 9131 ## @var[in] dabbrev_current_match 9132 ## 現在の一致内容を指定します。 9133 ## @var[in] dabbrev_pos 9134 ## 履歴項目内の検索開始位置を指定します。 9135 ## @var[out] dabbrev_match 9136 ## 一致した場合に、一致した内容を返します。 9137 ## @var[out] dabbrev_match_pos 9138 ## 一致した場合に、一致範囲の最後の位置を返します。 9139 ## これは次の検索開始位置に対応します。 9140 function ble/complete/dabbrev/search-in-history-entry { 9141 local line=$1 index=$2 9142 9143 # 現在編集している行自身には一致させない。 9144 local index_editing; ble/history/get-index -v index_editing 9145 if ((index!=index_editing)); then 9146 local pos=$dabbrev_pos 9147 while [[ ${line:pos} && ${line:pos} =~ $_ble_complete_dabbrev_regex2 ]]; do 9148 local rematch1=${BASH_REMATCH[1]} rematch2=${BASH_REMATCH[2]} 9149 local match=${rematch1:${#rematch2}} 9150 if [[ $match && $match != "$dabbrev_current_match" ]]; then 9151 dabbrev_match=$match 9152 dabbrev_match_pos=$((${#line}-${#BASH_REMATCH}+${#match})) 9153 return 0 9154 else 9155 ((pos++)) 9156 fi 9157 done 9158 fi 9159 9160 return 1 9161 } 9162 9163 function ble/complete/dabbrev/.search.fib { 9164 if [[ ! $fib_suspend ]]; then 9165 local start=$_ble_complete_dabbrev_index 9166 local index=$_ble_complete_dabbrev_index 9167 local pos=$_ble_complete_dabbrev_pos 9168 9169 # Note: start は最初に backward-history-search が呼ばれる時の index。 9170 # backward-history-search が呼び出される前に index-- されるので、 9171 # start は最初から 1 減らして定義しておく。 9172 # これにより cyclic 検索で再度自分に一致する事が保証される。 9173 # Note: start がこれで負になった時は "履歴項目の数" を設定する。 9174 # 未だ "履歴" に登録されていない最新の項目 (_ble_history_edit 9175 # には格納されている) も検索の対象とするため。 9176 ((--start>=0)) || ble/history/get-count -v start 9177 else 9178 local start index pos; builtin eval -- "$fib_suspend" 9179 fib_suspend= 9180 fi 9181 9182 local dabbrev_match= 9183 local dabbrev_pos=$pos 9184 local dabbrev_current_match=${_ble_edit_str:_ble_edit_mark:_ble_edit_ind-_ble_edit_mark} 9185 9186 local line; ble/history/get-edited-entry -v line "$index" 9187 if ! ble/complete/dabbrev/search-in-history-entry "$line" "$index"; then 9188 ((index--,dabbrev_pos=0)) 9189 9190 local isearch_time=0 9191 local isearch_opts=stop_check:cyclic 9192 9193 # 条件による一致判定の設定 9194 isearch_opts=$isearch_opts:condition 9195 local dabbrev_original=$_ble_complete_dabbrev_original 9196 local dabbrev_regex1=$_ble_complete_dabbrev_regex1 9197 local needle='[[ $LINE =~ $dabbrev_regex1 ]] && ble/complete/dabbrev/search-in-history-entry "$LINE" "$INDEX"' 9198 # Note: glob で先に枝刈りした方が速い。 9199 [[ $dabbrev_original ]] && needle='[[ $LINE == *"$dabbrev_original"* ]] && '$needle 9200 9201 # 検索進捗の表示 9202 isearch_opts=$isearch_opts:progress 9203 local isearch_progress_callback=ble/complete/dabbrev/.show-status.fib 9204 9205 ble/history/isearch-backward-blockwise "$isearch_opts"; local ext=$? 9206 ((ext==148)) && fib_suspend="start=$start index=$index pos=$pos" 9207 if ((ext)); then 9208 if ((${#_ble_complete_dabbrev_stack[@]})); then 9209 ble/widget/.bell # 周回したので鳴らす 9210 return 0 9211 else 9212 # 一つも見つからない場合 9213 return "$ext" 9214 fi 9215 fi 9216 fi 9217 9218 local rec=$_ble_complete_dabbrev_index,$_ble_complete_dabbrev_pos,$_ble_edit_ind,$_ble_edit_mark 9219 ble/array#push _ble_complete_dabbrev_stack "$rec:$_ble_edit_str" 9220 local insert; ble-edit/content/replace-limited "$_ble_edit_mark" "$_ble_edit_ind" "$dabbrev_match" 9221 ((_ble_edit_ind=_ble_edit_mark+${#insert})) 9222 9223 ((index>_ble_complete_dabbrev_index)) && 9224 ble/widget/.bell # 周回 9225 _ble_complete_dabbrev_index=$index 9226 _ble_complete_dabbrev_pos=$dabbrev_match_pos 9227 9228 ble/textarea#redraw 9229 } 9230 function ble/complete/dabbrev/next.fib { 9231 ble/complete/dabbrev/.search.fib; local ext=$? 9232 if ((ext==0)); then 9233 _ble_edit_mark_active=insert 9234 ble/complete/dabbrev/.show-status.fib 9235 elif ((ext==148)); then 9236 ble/complete/dabbrev/.show-status.fib 9237 else 9238 ble/widget/.bell 9239 ble/widget/dabbrev/exit 9240 ble/complete/dabbrev/reset 9241 fib_kill=1 9242 fi 9243 return "$ext" 9244 } 9245 function ble/widget/dabbrev-expand { 9246 ble/complete/dabbrev/initialize-variables 9247 ble/decode/keymap/push dabbrev 9248 ble/util/fiberchain#initialize ble/complete/dabbrev 9249 ble/util/fiberchain#push next 9250 ble/util/fiberchain#resume 9251 } 9252 function ble/widget/dabbrev/next { 9253 ble/util/fiberchain#push next 9254 ble/util/fiberchain#resume 9255 } 9256 function ble/widget/dabbrev/prev { 9257 if ((${#_ble_util_fiberchain[@]})); then 9258 # 処理中の物がある時はひとつずつ取り消す 9259 local ret; ble/array#pop _ble_util_fiberchain 9260 if ((${#_ble_util_fiberchain[@]})); then 9261 ble/util/fiberchain#resume 9262 else 9263 ble/complete/dabbrev/show-status 9264 fi 9265 elif ((${#_ble_complete_dabbrev_stack[@]})); then 9266 # 前の一致がある時は遡る 9267 local ret; ble/array#pop _ble_complete_dabbrev_stack 9268 local rec str=${ret#*:} 9269 ble/string#split rec , "${ret%%:*}" 9270 ble-edit/content/reset-and-check-dirty "$str" 9271 _ble_edit_ind=${rec[2]} 9272 _ble_edit_mark=${rec[3]} 9273 _ble_complete_dabbrev_index=${rec[0]} 9274 _ble_complete_dabbrev_pos=${rec[1]} 9275 ble/complete/dabbrev/show-status 9276 else 9277 ble/widget/.bell 9278 return 1 9279 fi 9280 } 9281 function ble/widget/dabbrev/cancel { 9282 if ((${#_ble_util_fiberchain[@]})); then 9283 ble/util/fiberchain#clear 9284 ble/complete/dabbrev/show-status 9285 else 9286 ble/widget/dabbrev/exit 9287 ble/complete/dabbrev/reset 9288 fi 9289 } 9290 function ble/widget/dabbrev/exit { 9291 ble/decode/keymap/pop 9292 _ble_edit_mark_active= 9293 ble/complete/dabbrev/erase-status 9294 } 9295 function ble/widget/dabbrev/exit-default { 9296 ble/widget/dabbrev/exit 9297 ble/decode/widget/skip-lastwidget 9298 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 9299 } 9300 function ble/widget/dabbrev/accept-line { 9301 ble/widget/dabbrev/exit 9302 ble-decode-key 13 9303 } 9304 function ble-decode/keymap:dabbrev/define { 9305 ble-bind -f __default__ 'dabbrev/exit-default' 9306 ble-bind -f __line_limit__ nop 9307 ble-bind -f 'C-g' 'dabbrev/cancel' 9308 ble-bind -f 'C-x C-g' 'dabbrev/cancel' 9309 ble-bind -f 'C-M-g' 'dabbrev/cancel' 9310 ble-bind -f C-r 'dabbrev/next' 9311 ble-bind -f C-s 'dabbrev/prev' 9312 ble-bind -f RET 'dabbrev/exit' 9313 ble-bind -f C-m 'dabbrev/exit' 9314 ble-bind -f C-RET 'dabbrev/accept-line' 9315 ble-bind -f C-j 'dabbrev/accept-line' 9316 } 9317 9318 #------------------------------------------------------------------------------ 9319 # default cmdinfo/complete 9320 9321 ## @fn ble/cmdinfo/complete/yield-flag cmd flags [opts] 9322 ## "-${flags}X" の X を補完する。 9323 ## @param[in] cmd 9324 ## mandb 検索に用いるコマンド名 9325 ## @param[in] flags 9326 ## 可能なオプション文字の一覧 9327 ## @param[in,opt] opts 9328 ## コロン区切りのリスト 9329 ## 9330 ## dedup[=XFLAGS] 9331 ## 既に指定されている排他的フラグは除外します。XFLAGS には排他的フラグの 9332 ## 集合を指定します。省略または空文字列を指定した場合は全てのフラグが排他 9333 ## 的であると見なします。 9334 ## 9335 ## cancel-on-empty 9336 ## 候補のフラグがもうない場合に補完候補生成をキャンセルします。既定では、 9337 ## 候補のフラグがもうない場合には現在入力済みの内容で補完確定します。 9338 ## 9339 ## hasarg=AFLAGS 9340 ## オプション引数を持つフラグの集合を指定します。この文字集合に含まれる文 9341 ## 字が既に COMPV に指定されている場合にはオプションは補完しません。 9342 ## 9343 ## @var[in] COMPV 9344 9345 ble/complete/action#inherit-from mandb.flag mandb 9346 function ble/complete/action:mandb.flag/initialize { 9347 ble/complete/action:mandb/initialize "$@" 9348 } 9349 function ble/complete/action:mandb.flag/init-menu-item { 9350 ble/complete/action:mandb/init-menu-item 9351 prefix=${CAND::!!PREFIX_LEN} 9352 } 9353 9354 function ble/cmdinfo/complete/yield-flag { 9355 local cmd=$1 flags=$2 opts=$3 9356 [[ $COMPV != [!-]* && $COMPV != --* && $flags ]] || return 1 9357 9358 local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 9359 ble/complete/cand/yield.initialize mandb 9360 9361 # opts dedup 9362 local ret 9363 if [[ ${COMPV:1} ]] && ble/opts#extract-last-optarg "$opts" dedup "$flags"; then 9364 local specified_flags=${ret//[!"${COMPV:1}"]} 9365 flags=${flags//["$specified_flags"]} 9366 fi 9367 9368 if ble/opts#extract-last-optarg "$opts" hasarg; then 9369 [[ $COMPV == -*["$ret"]* ]] && return 1 9370 fi 9371 9372 if [[ ! $flags ]]; then 9373 [[ :$opts: == *:cancel-on-empty:* ]] && return 1 9374 9375 # 候補のフラグがもうない場合は現在の内容で一意確定 9376 local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 9377 ble/complete/cand/yield.initialize word 9378 ble/complete/cand/yield word "$COMPV" 9379 return "$?" 9380 fi 9381 9382 local COMP_PREFIX=$COMPV 9383 9384 # desc が mandb に見つかればそれを適用する 9385 local has_desc= 9386 if local ret; ble/complete/mandb/load-cache "$cmd"; then 9387 local entry fs=$_ble_term_FS 9388 for entry in "${ret[@]}"; do 9389 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && 9390 ble/complete/check-cancel && return 148 9391 local option=${entry%%$fs*} 9392 [[ $option == -? && ${option:1} == ["$flags"] ]] || continue 9393 ble/complete/cand/yield mandb.flag "$COMPV${option:1}" "$entry" 9394 [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 9395 flags=${flags//${option:1}} 9396 done 9397 [[ $has_desc ]] && bleopt complete_menu_style=desc 9398 fi 9399 9400 # 見つからない場合には説明なしで生成する 9401 local i 9402 for ((i=0;i<${#flags};i++)); do 9403 ble/complete/cand/yield mandb.flag "$COMPV${flags:i:1}" 9404 done 9405 } 9406 9407 9408 # action:cdpath (action:file を修正) 9409 9410 function ble/complete/action:cdpath/initialize { 9411 DATA=$cdpath_basedir 9412 ble/complete/action:file/initialize 9413 } 9414 function ble/complete/action:cdpath/complete { 9415 CAND=$DATA$CAND ble/complete/action:file/complete 9416 } 9417 function ble/complete/action:cdpath/init-menu-item { 9418 ble/color/face2g cmdinfo_cd_cdpath; g=$ret 9419 if [[ :$comp_type: == *:vstat:* ]]; then 9420 if [[ -h $CAND ]]; then 9421 suffix='@' 9422 elif [[ -d $CAND ]]; then 9423 suffix='/' 9424 fi 9425 fi 9426 } 9427 function ble/complete/action:cdpath/get-desc { 9428 local sgr0=$_ble_term_sgr0 sgr1= sgr2= 9429 local g ret g1 g2 9430 ble/syntax/highlight/getg-from-filename "$DATA$CAND"; g1=$g 9431 [[ $g1 ]] || { ble/color/face2g filename_warning; g1=$ret; } 9432 ((g2=g1^_ble_color_gflags_Revert)) 9433 ble/color/g2sgr "$g1"; sgr1=$ret 9434 ble/color/g2sgr "$g2"; sgr2=$ret 9435 ble/string#escape-for-display "$DATA$CAND" sgr1="$sgr2":sgr0="$sgr1" 9436 local filename=$sgr1$ret$sgr0 9437 9438 CAND=$DATA$CAND ble/complete/action:file/get-desc 9439 desc="CDPATH $filename ($desc)" 9440 } 9441 9442 ## @fn ble/cmdinfo/complete:cd/.impl 9443 ## @remarks 9444 ## この実装は ble/complete/source:file/.impl を元にしている。 9445 ## 実装に関する注意点はこの元の実装も参照の事。 9446 function ble/cmdinfo/complete:cd/.impl { 9447 local type=$1 9448 [[ $comps_flags == *v* ]] || return 1 9449 9450 case $type in 9451 (pushd|popd|dirs) 9452 # todo: -- より後の [-+]* は処理しない 9453 # todo: 実は -N/+N はオプションではなく通常引数 9454 if [[ $COMPV == [-+]* ]]; then 9455 local old_cand_count=$cand_count 9456 9457 # yield options 9458 local flags=n 9459 [[ $type == dirs ]] && flags=clpv 9460 ble/cmdinfo/complete/yield-flag "$type" "$flags" dedup:hasarg=0123456789:cancel-on-empty 9461 9462 local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 9463 ble/complete/cand/yield.initialize word 9464 local ret 9465 ble/color/face2sgr-ansi filename_directory 9466 local sgr1=$ret sgr0=$'\e[m' 9467 9468 # yield -N/+N 9469 local i n=${#DIRSTACK[@]} 9470 for ((i=0;i<n;i++)); do 9471 local cand=${COMPV::1}$i 9472 [[ $cand == "$COMPV"* ]] || continue 9473 local j=$i; [[ $COMPV == -* ]] && j=$((n-1-i)) 9474 ble/complete/cand/yield word "$cand" "DIRSTACK[$j] $sgr1${DIRSTACK[j]}$sgr0" 9475 done 9476 9477 # yield - and -- for pushd 9478 if [[ $type == pushd ]]; then 9479 [[ ${OLDPWD:-} && $COMPV == - ]] && 9480 ble/complete/cand/yield word - "OLDPWD $sgr1$OLDPWD$sgr0" 9481 [[ -- == "$COMPV"* ]] && 9482 ble/complete/cand/yield word -- '(indicate the end of options)' 9483 fi 9484 9485 ((cand_count!=old_cand_count)) && return 0 9486 fi 9487 [[ $type == pushd ]] || return 0 ;; 9488 (*) 9489 # todo: -- より後の [-+]* は処理しない 9490 if [[ $COMPV == -* ]]; then 9491 local list=LP 9492 ((_ble_bash>=40200)) && list=${list}e 9493 ((_ble_bash>=40300)) && list=${list}@ 9494 ble/cmdinfo/complete/yield-flag cd "$list" dedup 9495 9496 local "${_ble_complete_yield_varnames[@]/%/=}" # WA #D1570 checked 9497 ble/complete/cand/yield.initialize word 9498 if [[ ${OLDPWD:-} && $COMPV == - ]]; then 9499 local ret 9500 ble/color/face2sgr-ansi filename_directory 9501 local sgr1=$ret sgr0=$'\e[m' 9502 ble/complete/cand/yield word - "OLDPWD $sgr1$OLDPWD$sgr0" 9503 fi 9504 [[ -- == "$COMPV"* ]] && 9505 ble/complete/cand/yield word -- '(indicate the end of options)' 9506 9507 return 0 9508 fi 9509 esac 9510 9511 [[ :$comp_type: != *:[maA]:* && $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]} 9512 [[ :$comp_type: == *:[maA]:* && ! $COMPV ]] && return 1 9513 9514 if [[ ! $CDPATH ]]; then 9515 ble/complete/source:dir 9516 return "$?" 9517 fi 9518 9519 ble/complete/source:tilde; local ext=$? 9520 ((ext==148||ext==0)) && return "$ext" 9521 9522 local is_pwd_visited= is_cdpath_generated= 9523 "${_ble_util_set_declare[@]//NAME/visited}" # WA #D1570 checked 9524 9525 # Check CDPATH first 9526 local name names; ble/string#split names : "$CDPATH" 9527 for name in "${names[@]}"; do 9528 [[ $name ]] || continue 9529 name=${name%/}/ 9530 9531 # カレントディレクトリが CDPATH に含まれている時は action=file で登録 9532 local action=cdpath 9533 [[ ${name%/} == . || ${name%/} == "${PWD%/}" ]] && 9534 is_pwd_visited=1 action=file 9535 9536 local -a candidates=() 9537 local ret cand 9538 ble/complete/source:file/.construct-pathname-pattern "$COMPV" 9539 ble/complete/util/eval-pathname-expansion "$name$ret"; (($?==148)) && return 148 9540 ble/complete/source/test-limit "${#ret[@]}" || return 1 9541 for cand in "${ret[@]}"; do 9542 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && 9543 ble/complete/check-cancel && return 148 9544 [[ $cand && -d $cand ]] || continue 9545 [[ $cand == / ]] || cand=${cand%/} 9546 cand=${cand#"$name"} 9547 9548 ble/set#contains visited "$cand" && continue 9549 ble/set#add visited "$cand" 9550 ble/array#push candidates "$cand" 9551 done 9552 ((${#candidates[@]})) || continue 9553 9554 local flag_source_filter=1 9555 local cdpath_basedir=$name 9556 ble/complete/cand/yield-filenames "$action" "${candidates[@]}" 9557 [[ $action == cdpath ]] && is_cdpath_generated=1 9558 done 9559 [[ $is_cdpath_generated ]] && 9560 bleopt complete_menu_style=desc 9561 9562 # Check PWD next 9563 # カレントディレクトリが CDPATH に含まれていなかった時に限り通常の候補生成 9564 if [[ ! $is_pwd_visited ]]; then 9565 local -a candidates=() 9566 local ret cand 9567 ble/complete/source:file/.construct-pathname-pattern "$COMPV" 9568 ble/complete/util/eval-pathname-expansion "${ret%/}/"; (($?==148)) && return 148 9569 ble/complete/source/test-limit "${#ret[@]}" || return 1 9570 for cand in "${ret[@]}"; do 9571 ((cand_iloop++%bleopt_complete_polling_cycle==0)) && 9572 ble/complete/check-cancel && return 148 9573 [[ -d $cand ]] || continue 9574 [[ $cand == / ]] || cand=${cand%/} 9575 ble/set#contains visited "$cand" && continue 9576 ble/array#push candidates "$cand" 9577 done 9578 local flag_source_filter=1 9579 ble/complete/cand/yield-filenames file "${candidates[@]}" 9580 fi 9581 } 9582 function ble/cmdinfo/complete:cd { 9583 ble/cmdinfo/complete:cd/.impl cd 9584 } 9585 function ble/cmdinfo/complete:pushd { 9586 ble/cmdinfo/complete:cd/.impl pushd 9587 } 9588 function ble/cmdinfo/complete:popd { 9589 ble/cmdinfo/complete:cd/.impl popd 9590 } 9591 function ble/cmdinfo/complete:dirs { 9592 ble/cmdinfo/complete:cd/.impl dirs 9593 } 9594 9595 blehook/invoke complete_load 9596 blehook complete_load= 9597 return 0