edit.sh (366852B)
1 #!/bin/bash 2 3 # **** sections **** 4 # 5 # @line.ps1 6 # @line.text 7 # @line.info 8 # @edit.content 9 # @edit.ps1 10 # @textarea 11 # @textarea.buffer 12 # @textarea.render 13 # @widget.clear 14 # @widget.mark 15 # @edit.bell 16 # @edit.insert 17 # @edit.delete 18 # @edit.cursor 19 # @edit.word 20 # @edit.exec 21 # @edit.accept 22 # @history 23 # @history.widget 24 # @history.isearch 25 # @comp 26 # @bind 27 # @bind.bind 28 # 29 # 現在の ble/canvas/panel 構成 30 # 0 command-line 31 # 1 追加入力欄 32 # 2 infobar 33 34 ## @bleopt edit_vbell 35 ## 編集時の visible bell の有効・無効を設定します。 36 ## bleopt_edit_vbell=1 37 ## 有効です。 38 ## bleopt_edit_vbell= 39 ## 無効です。 40 bleopt/declare -v edit_vbell '' 41 42 ## @bleopt edit_abell 43 ## 編集時の audible bell (BEL 文字出力) の有効・無効を設定します。 44 ## bleopt_edit_abell=1 45 ## 有効です。 46 ## bleopt_edit_abell= 47 ## 無効です。 48 bleopt/declare -v edit_abell 1 49 50 ## @bleopt history_lazyload 51 ## bleopt_history_lazyload=1 52 ## ble-attach 後、初めて必要になった時に履歴の読込を行います。 53 ## bleopt_history_lazyload= 54 ## ble-attach 時に履歴の読込を行います。 55 ## 56 ## bash-3.1 未満では history -s が思い通りに動作しないので、 57 ## このオプションの値に関係なく ble-attach の時に履歴の読み込みを行います。 58 bleopt/declare -v history_lazyload 1 59 60 ## @bleopt delete_selection_mode 61 ## 文字挿入時に選択範囲をどうするかについて設定します。 62 ## bleopt_delete_selection_mode=1 (既定) 63 ## 選択範囲の内容を新しい文字で置き換えます。 64 ## bleopt_delete_selection_mode= 65 ## 選択範囲を解除して現在位置に新しい文字を挿入します。 66 bleopt/declare -v delete_selection_mode 1 67 68 ## @bleopt indent_offset 69 ## シェルのインデント幅を指定します。既定では 4 です。 70 bleopt/declare -n indent_offset 4 71 72 ## @bleopt indent_tabs 73 ## インデントにタブを使用するかどうかを指定します。 74 ## 0 を指定するとインデントに空白だけを用います。 75 ## それ以外の場合はインデントにタブを使用します。 76 bleopt/declare -n indent_tabs 1 77 78 ## @bleopt undo_point 79 ## undo/redo 実行直後のカーソル位置を設定します。 80 ## 81 ## undo_point=beg 82 ## undo/redo によって変化のあった範囲の先頭に移動します。 83 ## undo_point=end 84 ## undo/redo によって変化のあった範囲の末端に移動します。 85 ## その他の時 86 ## undo/redo 後の状態が記録された時のカーソル位置を復元します。 87 ## 88 bleopt/declare -v undo_point end 89 90 ## @bleopt edit_forced_textmap 91 ## 1 が設定されているとき、矩形選択に先立って配置計算を強制します。 92 ## 0 が設定されているとき、配置情報があるときにそれを使い、 93 ## 配置情報がないときは論理行・論理列による矩形選択にフォールバックします。 94 ## 95 bleopt/declare -n edit_forced_textmap 1 96 97 bleopt/declare -n edit_magic_expand history:sabbrev 98 bleopt/declare -v edit_magic_opts '' 99 100 function ble/edit/use-textmap { 101 ble/textmap#is-up-to-date && return 0 102 ((bleopt_edit_forced_textmap)) || return 1 103 ble/widget/.update-textmap 104 return 0 105 } 106 107 ## @bleopt edit_line_type 108 ## 行頭・行末への移動などの操作を行う時の行の解釈を指定します。 109 ## "logical" が設定されている時、論理行で解釈します。 110 ## つまり編集文字列内の改行文字で区切られた行頭・行末を使用して操作を行います。 111 ## "graphical" が設定されている時、表示行で解釈します。 112 ## つまり端末内での現在行の行頭・行末を使用して操作を行います。 113 bleopt/declare -n edit_line_type logical 114 function bleopt/check:edit_line_type { 115 if [[ $value != logical && $value != graphical ]]; then 116 ble/util/print "bleopt edit_line_type: Unexpected value '$value'. 'logical' or 'graphical' is expected." >&2 117 return 1 118 fi 119 } 120 121 function ble/edit/performs-on-graphical-line { 122 [[ $edit_line_type == graphical ]] || return 1 123 ble/textmap#is-up-to-date && return 0 124 ((bleopt_edit_forced_textmap)) || return 1 125 ble/widget/.update-textmap 126 return 0 127 } 128 129 bleopt/declare -n info_display top 130 function bleopt/check:info_display { 131 case $value in 132 (top) 133 [[ $_ble_canvas_panel_vfill == 3 ]] && return 0 134 _ble_canvas_panel_vfill=3 135 [[ $_ble_attached ]] && ble/canvas/panel/clear 136 return 0 ;; 137 (bottom) 138 [[ $_ble_canvas_panel_vfill == 2 ]] && return 0 139 _ble_canvas_panel_vfill=2 140 [[ $_ble_attached ]] && ble/canvas/panel/clear 141 return 0 ;; 142 (*) 143 ble/util/print "bleopt: Invalid value for 'info_display': $value" 144 return 1 ;; 145 esac 146 } 147 148 ## プロンプトオプション 149 bleopt/declare -v prompt_ps1_final '' 150 bleopt/declare -v prompt_ps1_transient '' 151 bleopt/declare -v prompt_rps1 '' 152 bleopt/declare -v prompt_rps1_final '' 153 bleopt/declare -v prompt_rps1_transient '' 154 bleopt/declare -v prompt_xterm_title '' 155 bleopt/declare -v prompt_screen_title '' 156 bleopt/declare -v prompt_term_status '' 157 # obsoleted options 158 bleopt/declare -o rps1 prompt_rps1 159 bleopt/declare -o rps1_transient prompt_rps1_transient 160 161 bleopt/declare -v prompt_eol_mark $'\e[94m[ble: EOF]\e[m' 162 bleopt/declare -v prompt_ruler '' 163 164 bleopt/declare -v prompt_status_line '' 165 bleopt/declare -n prompt_status_align $'justify=\r' 166 ble/color/defface prompt_status_line fg=231,bg=240 167 168 bleopt/declare -v prompt_command_changes_layout '' 169 170 function bleopt/check:prompt_status_align { 171 case $value in 172 (left|right|center|justify|justify=?*) 173 ble/prompt/unit#clear _ble_prompt_status hash 174 return 0 ;; 175 (*) 176 ble/util/print "bleopt prompt_status_align: unsupported value: '$value'" >&2 177 return 1 ;; 178 esac 179 } 180 181 ## @bleopt internal_exec_type (内部使用) 182 ## コマンドの実行の方法を指定します。 183 ## 184 ## internal_exec_type=exec [廃止] 185 ## 関数内で実行します (削除されました) 186 ## internal_exec_type=gexec 187 ## グローバルな文脈で実行します (新しい方法です) 188 ## 189 ## 要件: 関数 ble-edit/exec:$bleopt_internal_exec_type/process が定義されていること。 190 bleopt/declare -n internal_exec_type gexec 191 function bleopt/check:internal_exec_type { 192 if ! ble/is-function "ble-edit/exec:$value/process"; then 193 ble/util/print "bleopt: Invalid value internal_exec_type='$value'. A function 'ble-edit/exec:$value/process' is not defined." >&2 194 return 1 195 fi 196 } 197 198 ## @bleopt internal_suppress_bash_output (内部使用) 199 ## bash 自体の出力を抑制するかどうかを指定します。 200 ## bleopt_internal_suppress_bash_output=1 201 ## 抑制します。bash のエラーメッセージは visible-bell で表示します。 202 ## bleopt_internal_suppress_bash_output= 203 ## 抑制しません。bash のメッセージは全て端末に出力されます。 204 ## これはデバグ用の設定です。bash の出力を制御するためにちらつきが発生する事があります。 205 ## bash-3 ではこの設定では C-d を捕捉できません。 206 bleopt/declare -v internal_suppress_bash_output 1 207 208 ## @bleopt internal_ignoreeof_trap (内部使用) 209 ## bash-3.0 の時に使用します。C-d を捕捉するのに用いるメッセージです。 210 ## これは自分の bash の設定に合わせる必要があります。 211 bleopt/declare -n internal_ignoreeof_trap 'Use "exit" to leave the shell.' 212 213 ## @bleopt allow_exit_with_jobs 214 ## この変数に空文字列が設定されている時、 215 ## ジョブが残っている時には ble/widget/exit からシェルは終了しません。 216 ## この変数に空文字列以外が設定されている時、 217 ## ジョブがある場合でも条件を満たした時に exit を実行します。 218 ## 停止中のジョブがある場合、または、shopt -s checkjobs かつ実行中のジョブが存在する時は、 219 ## 二回連続で同じ widget から exit を呼び出した時にシェルを終了します。 220 ## それ以外の場合は常にシェルを終了します。 221 ## 既定値は空文字列です。 222 bleopt/declare -v allow_exit_with_jobs '' 223 224 ## @bleopt history_share 225 ## この変数に空文字列が設定されている時、履歴を共有します。 226 bleopt/declare -v history_share '' 227 228 229 ## @bleopt accept_line_threshold 230 ## 編集関数 accept-single-line-or-newline の単一行モードにおける振る舞いを制御します。 231 ## この変数が負の整数の時、常にコマンドを実行します。 232 ## この変数が 0 の時、ユーザの入力がある場合は改行を挿入して複数行モードに入ります。 233 ## 正の整数 n の時、未処理のユーザ入力が n 以上の時に改行を挿入して複数行モードに入ります。 234 bleopt/declare -v accept_line_threshold 5 235 236 bleopt/declare -v exec_restore_pipestatus '' 237 238 ## @bleopt exec_errexit_mark 239 ## 終了ステータスが非零の時に表示するマークの書式を指定します。 240 ## この変数が空の時、終了ステータスは表示しません。 241 bleopt/declare -v exec_errexit_mark $'\e[91m[ble: exit %d]\e[m' 242 243 bleopt/declare -v exec_elapsed_mark $'\e[94m[ble: elapsed %s (CPU %s%%)]\e[m' 244 bleopt/declare -v exec_elapsed_enabled 'usr+sys>=10000' 245 246 ## @bleopt line_limit_length 247 ## 一括挿入時のコマンドライン文字数の上限を指定します。 248 ## 0以下の値は文字数に制限を与えない事を示します。 249 bleopt/declare -v line_limit_length 10000 250 251 ## @bleopt line_limit_type 252 ## 一括挿入で文字数を超過した時の動作を指定します。 253 bleopt/declare -v line_limit_type none 254 255 # 256 #------------------------------------------------------------------------------ 257 # **** Application **** 258 259 _ble_app_render_mode=panel 260 _ble_app_winsize=() 261 function ble/application/.set-up-render-mode { 262 [[ $1 == "$_ble_app_render_mode" ]] && return 0 263 case $1 in 264 (panel) 265 ble/term/leave-altscr 266 ble/canvas/panel/invalidate ;; 267 (forms:*) 268 ble/term/enter-altscr 269 ble/util/buffer "$_ble_term_clear" 270 ble/util/buffer $'\e[H' 271 _ble_canvas_x=0 _ble_canvas_y=0 ;; 272 (*) 273 ble/util/print "ble/edit: unrecognized render mode '$1'." 274 return 1 ;; 275 esac 276 } 277 function ble/application/push-render-mode { 278 ble/application/.set-up-render-mode "$1" || return 1 279 ble/array#unshift _ble_app_render_mode "$1" 280 } 281 function ble/application/pop-render-mode { 282 [[ ${_ble_app_render_mode[1]} ]] || return 1 283 ble/application/.set-up-render-mode "${_ble_app_render_mode[1]}" 284 ble/array#shift _ble_app_render_mode 285 } 286 function ble/application/render { 287 # 既に未処理の winch がある場合には初めから ble/application/onwinch 288 # 経由で再描画を行う。何れにせよ ble/application/onwinch から改めて 289 # ble/application/render が呼び出されるので onwinch 後は直ぐに抜けて 290 # 良い。 291 # 292 # Note: この文脈でも更に ble/application/onwinch が遅延される場合は、 293 # 更に外側の何処かで最終的に ble/application/onwinch が呼び出される 294 # 手筈になっているので問題ない。 295 if [[ $_ble_app_onwinch_Deferred ]]; then 296 ble/application/onwinch 297 return "$?" 298 fi 299 300 local _ble_app_onwinch_Suppress=1 301 { 302 local render=$_ble_app_render_mode 303 case $render in 304 (panel) 305 local _ble_prompt_update=owner 306 ble/prompt/update 307 ble/canvas/panel/render ;; 308 (forms:*) 309 ble/forms/render "${render#*:}" ;; # NYI 310 esac 311 _ble_app_winsize=("$COLUMNS" "$LINES") 312 ble/util/buffer.flush >&2 313 } 314 ble/util/unlocal _ble_app_onwinch_Suppress 315 316 if [[ $_ble_app_onwinch_Deferred ]]; then 317 ble/application/onwinch 318 fi 319 } 320 blehook idle_after_task!=ble/application/render 321 322 ## @fn ble/application/onwinch/panel.process-redraw-here 323 ## @arr[in] _ble_app_winsize 324 ## @var[in] LINES COLUMNS 325 function ble/application/onwinch/panel.process-redraw-here { 326 local old_w=${_ble_app_winsize[0]} 327 328 local -a DRAW_BUFF=() 329 330 # text reflowing によって行数が減ってしまって問題になるのは端末幅が拡大した時 331 # だけである。なので端末幅が拡大した時にのみ、拡大後に最低でも何行存在するか 332 # を求めて、それに基づいて描画開始位置を決定する。 333 if ((COLUMNS>old_w)); then 334 # 下部パネルにいる時は DECRC で何処に戻るか非自明である。移動先も reflow し 335 # ている可能性、端末画面の左上からの絶対位置で戻る可能性、相対位置で移動す 336 # る可能性など。最悪ケースは移動先も reflow して上に移動している場合なので、 337 # それを想定して取り敢えず DECRC で戻った先の座標を使って判定する。 338 ble/canvas/panel/goto-top-dock.draw 339 340 local i npanel=${#_ble_canvas_panel_height[@]} 341 local y0=0 342 local nchar=0 343 for ((i=0;i<npanel;i++)); do 344 ((_ble_canvas_panel_height[i])) || continue 345 ((_ble_canvas_y<=y0)) && break 346 347 if ! ble/function#try "${_ble_canvas_panel_class[i]}#panel::moveReflowInf" "$i" "$_ble_canvas_x" "$((_ble_canvas_y-y0))"; then 348 if ((_ble_canvas_y-y0<_ble_canvas_panel_height[i])); then 349 ((nchar+=(_ble_canvas_y-y0)*(old_w-1)+_ble_canvas_x)) 350 else 351 ((nchar+=(_ble_canvas_panel_height[i]*(old_w-1)))) 352 fi 353 fi 354 355 ((y0+=_ble_canvas_panel_height[i])) 356 done 357 ((_ble_canvas_y>=y0)) && 358 ((nchar+=(_ble_canvas_y-y0)*(old_w-1)*_ble_canvas_x)) 359 360 local new_y_min=$(((nchar-1)/COLUMNS)) 361 ((_ble_canvas_y>new_y_min)) && 362 _ble_canvas_y=$new_y_min 363 fi 364 365 ble/canvas/panel#goto.draw 0 0 0 366 ble/canvas/bflush.draw 367 368 return 0 369 } 370 371 _ble_app_onwinch_Suppress= 372 _ble_app_onwinch_Deferred= 373 function ble/application/onwinch { 374 if [[ $_ble_app_onwinch_Suppress || $_ble_decode_hook_Processing == body || $_ble_decode_hook_Processing == prologue ]]; then 375 # Note #D1762: 別の処理が走っている途中に描画更新すると中途半端なデータに対 376 # して処理が実行されてデータが破壊されるので後で処理する。 377 # 378 # ble_decode_hook_body=1 の時は EPILOGUE が後で必ず呼び出されるのでその時に 379 # ble/application/render が呼び出される。その中で_ble_app_onwinch_Deferred 380 # がチェックされて改めてこの関数が呼び出される。_ble_app_onwinch_Suppress=1 381 # の時には、ble/application/render の末尾でやはりチェックが走ると期待する。 382 _ble_app_onwinch_Deferred=1 383 return 0 384 fi 385 386 local _ble_app_onwinch_Suppress=1 387 _ble_app_onwinch_Deferred= 388 389 _ble_textmap_cols= 390 # 処理中に届いた WINCH は失われる様だ。連続的サイズ変化を通知する端末の場合、 391 # 途中のサイズの WINCH の処理中に最終的なサイズのWINCH を逃して表示が乱れたま 392 # まになる。対策として描画終了時に処理中にサイズ変化が起こっていないか確認す 393 # る。 394 395 local old_size= i 396 for ((i=0;i<20;i++)); do 397 # 次の WINCH を待つと共にサブシェルで checkwinsize を誘発。 398 (ble/util/msleep 50) 399 # Bash 5.2 では trap string / bind -x 内部で COLUMNS/LINES が更新されないの 400 # で明示的に ble/term/update-winsize を呼び出す。 401 if ble/util/is-running-in-subshell || ((50200<=_ble_bash&&_ble_bash<50300)); then 402 ble/term/update-winsize 403 fi 404 405 # trap 中だと bash のバグでジョブが溜まるので逐次捌く 406 ble/util/joblist.check ignore-volatile-jobs 407 local size=$LINES:$COLUMNS 408 [[ $size == "$old_size" ]] && break 409 old_size=$size 410 411 local render=$_ble_app_render_mode 412 case $render in 413 (panel) 414 case $bleopt_canvas_winch_action in 415 (clear) 416 # 全消去して一番上から再描画 417 _ble_prompt_trim_opwd= 418 ble/util/buffer "$_ble_term_clear" ;; 419 (redraw-here) 420 ble/application/onwinch/panel.process-redraw-here ;; 421 (redraw-prev) 422 # 前回の開始相対位置が変化していないと仮定して戻って再描画 423 local -a DRAW_BUFF=() 424 ble/canvas/panel#goto.draw 0 0 0 425 ble/canvas/bflush.draw ;; 426 (redraw-safe) ;; 427 esac 428 # 高さの再確保も含めて。 429 ble/canvas/panel/invalidate height ;; 430 431 (forms:*) 432 ble/forms/invalidate "${render#*:}" ;; # NYI 433 esac 434 435 ble/application/render 436 done 437 ble/util/unlocal _ble_app_onwinch_Suppress 438 439 if [[ $_ble_app_onwinch_Deferred ]]; then 440 ble/application/onwinch 441 fi 442 } 443 444 # canvas.sh 設定 445 446 _ble_canvas_panel_focus=0 447 _ble_canvas_panel_class=(ble/textarea ble/textarea ble/edit/info ble/prompt/status) 448 _ble_canvas_panel_vfill=3 449 450 _ble_edit_command_layout_level=0 451 function ble/edit/enter-command-layout { 452 ((_ble_edit_command_layout_level++==0)) || return 0 453 454 # 一時的に info 及び status を消去する。 455 ble/edit/info#collapse "$_ble_edit_info_panel" 456 ble/prompt/status#collapse 457 } 458 function ble/edit/leave-command-layout { 459 ((_ble_edit_command_layout_level>0&& 460 --_ble_edit_command_layout_level==0)) || return 0 461 462 # 抑制した info を改めて表示し直す。一時的に表示していた内容は消去して 463 # default の内容を表示する。 464 blehook/invoke info_reveal 465 ble/edit/info/default 466 } 467 function ble/edit/clear-command-layout { 468 ((_ble_edit_command_layout_level>0)) || return 0 469 _ble_edit_command_layout_level=1 470 ble/edit/leave-command-layout 471 } 472 function ble/edit/is-command-layout { 473 ((_ble_edit_command_layout_level>0)) 474 } 475 476 # 477 #------------------------------------------------------------------------------ 478 # **** ble/prompt/status **** @prompt.status 479 480 _ble_prompt_status_panel=3 481 _ble_prompt_status_dirty= 482 _ble_prompt_status_data=() 483 _ble_prompt_status_bbox=() 484 485 # Note: 高さは 0 か 1 のどちらかである事を前提に設計してある。より多くの行を表 486 # 示したい場合には _ble_prompt_status_data を計算する時点で調整が必要になる。 487 488 function ble/prompt/status#panel::invalidate { 489 _ble_prompt_status_dirty=1 490 } 491 function ble/prompt/status#panel::render { 492 [[ $_ble_prompt_status_dirty ]] || return 0 493 _ble_prompt_status_dirty= 494 495 # 表示内容がない場合は何もせず抜ける (高さは既に調整されている前提) 496 local index=$1 497 local height; ble/prompt/status#panel::getHeight "$index" 498 [[ ${height#*:} == 1 ]] || return 0 499 500 local -a DRAW_BUFF=() 501 502 # 高さが一致していない場合は取り敢えず再配置を要求してみる。 503 # 高さを取得できなければ諦める。 504 height=$3 505 if ((height!=1)); then 506 ble/canvas/panel/reallocate-height.draw 507 ble/canvas/bflush.draw 508 height=${_ble_canvas_panel_height[index]} 509 ((height==0)) && return 0 510 fi 511 512 local esc=${_ble_prompt_status_data[10]} 513 if [[ $esc ]]; then 514 local prox=${_ble_prompt_status_data[11]} 515 local proy=${_ble_prompt_status_data[12]} 516 ble/canvas/panel#goto.draw "$_ble_prompt_status_panel" 517 ble/canvas/panel#put.draw "$_ble_prompt_status_panel" "$esc" "$prox" "$proy" 518 else 519 ble/canvas/panel#clear.draw "$_ble_prompt_status_panel" 520 fi 521 ble/canvas/bflush.draw 522 } 523 function ble/prompt/status#panel::getHeight { 524 if ble/edit/is-command-layout || [[ ! ${_ble_prompt_status_data[10]} ]]; then 525 height=0:0 526 else 527 height=0:1 528 fi 529 } 530 function ble/prompt/status#panel::onHeightChange { 531 ble/prompt/status#panel::invalidate 532 } 533 function ble/prompt/status#collapse { 534 local -a DRAW_BUFF=() 535 ble/canvas/panel#set-height.draw "$_ble_prompt_status_panel" 0 536 ble/canvas/bflush.draw 537 } 538 539 # 540 #------------------------------------------------------------------------------ 541 # **** prompt **** @line.ps1 542 543 ## @var _ble_prompt_version 544 ## ble/prompt/update でのプロンプト更新の度にインクリメントする変数 545 _ble_prompt_hash= 546 _ble_prompt_version=0 547 548 function ble/prompt/.escape-control-characters { 549 ret=$1 550 551 local ctrl=$'\001-\037\177' 552 case $_ble_util_locale_encoding in 553 (UTF-8) ctrl=$ctrl$'\302\200-\302\237' ;; 554 (C) ctrl=$ctrl$'\200-\237' ;; 555 esac 556 557 local LC_ALL= LC_COLLATE=C glob_ctrl=[$ctrl] 558 [[ $ret == *$glob_ctrl* ]] || return 0 559 560 local out= head tail=$ret cs 561 while head=${tail%%$glob_ctrl*}; [[ $head != "$tail" ]]; do 562 ble/util/s2c "${tail:${#head}:1}" 563 ble/unicode/GraphemeCluster/.get-ascii-rep "$ret" # -> cs 564 out=$out$head$'\e[9807m'$cs$'\e[9807m' 565 tail=${tail#*$glob_ctrl} 566 done 567 ret=$out$tail 568 } 569 ble/function#suppress-stderr ble/prompt/.escape-control-characters # LC_COLLATE 570 571 ## @fn ble/prompt/.initialize-constant ps defeval [opts] 572 ## @param ps 573 ## 初期化に使用する prompt シーケンスを指定します。 574 ## @param defeval 575 ## 初期化に使用するコマンドを指定します。ret に結果を格納します。 576 ## @param[opt] opts 577 ## コロン区切りのオプションリストです。escape が指定されている時、 578 ## 展開結果に含まれる制御文字をエスケープします。 579 function ble/prompt/.initialize-constant { 580 local _ble_local_ps=$1 581 local _ble_local_defeval=$2 582 local _ble_local_opts=$3 583 if ((_ble_bash>=40400)); then 584 ret=${_ble_local_ps@P} 585 else 586 builtin eval -- "$_ble_local_defeval" 587 fi 588 589 if [[ $_ble_local_opts == *:escape:* ]]; then 590 if ((_ble_bash>=50200)); then 591 # bash-5.2 以上では bash が escape を行うが、反転などの処理が実 592 # 装されていないので、制御文字が含まれている場合には ble.sh の側 593 # で処理を行う。 594 if [[ $ret == *\^['A'-'Z[\]^_?']* ]]; then 595 builtin eval -- "$_ble_local_defeval" 596 ble/prompt/.escape-control-characters "$ret" 597 elif [[ $ret == *$'\t'* ]]; then 598 ble/prompt/.escape-control-characters "$ret" 599 fi 600 else 601 ble/prompt/.escape-control-characters "$_ble_prompt_const_s" 602 fi 603 fi 604 } 605 606 ## called by ble-edit/initialize 607 function ble/prompt/initialize { 608 local ret 609 610 # hostname 611 ble/prompt/.initialize-constant '\H' 'ret=${HOSTNAME:-$_ble_base_env_HOSTNAME}' escape 612 _ble_prompt_const_H=$ret 613 if local rex='^[0-9]+(\.[0-9]){3}$'; [[ $_ble_prompt_const_H =~ $rex ]]; then 614 # IPv4 の形式の場合には省略しない 615 _ble_prompt_const_h=$_ble_prompt_const_H 616 else 617 _ble_prompt_const_h=${_ble_prompt_const_H%%.*} 618 fi 619 620 # tty basename 621 ble/prompt/.initialize-constant '\l' 'ble/util/assign ret "ble/bin/tty 2>/dev/null";ret=${ret##*/}' 622 _ble_prompt_const_l=$ret 623 624 # command name 625 ble/prompt/.initialize-constant '\s' 'ret=${0##*/}' escape 626 _ble_prompt_const_s=$ret 627 628 # user 629 ble/prompt/.initialize-constant '\u' 'ret=${USER:-$_ble_base_env_USER}' escape 630 _ble_prompt_const_u=$ret 631 632 # bash versions 633 ble/util/sprintf _ble_prompt_const_v '%d.%d' "${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}" 634 ble/util/sprintf _ble_prompt_const_V '%d.%d.%d' "${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}" "${BASH_VERSINFO[2]}" 635 636 # uid 637 if [[ $EUID -eq 0 ]]; then 638 _ble_prompt_const_root='#' 639 else 640 _ble_prompt_const_root='$' 641 fi 642 643 if [[ $OSTYPE == cygwin* ]]; then 644 local windir=/cygdrive/c/Windows 645 if [[ $WINDIR == [a-zA-Z]:\\* ]]; then 646 local bsl='\' sl=/ 647 local c=${WINDIR::1} path=${WINDIR:3} 648 if [[ $c == [A-Z] ]]; then 649 if ((_ble_bash>=40000)); then 650 c=${c,?} 651 else 652 local ret 653 ble/util/s2c "$c" 654 ble/util/c2s "$((ret+32))" 655 c=$ret 656 fi 657 fi 658 windir=/cygdrive/$c/${path//"$bsl"/"$sl"} 659 fi 660 661 if [[ -e $windir && -w $windir ]]; then 662 _ble_prompt_const_root='#' 663 fi 664 elif [[ $OSTYPE == msys* ]]; then 665 # msys64/etc/bash.bashrc に倣う 666 if ble/bin#has id getent; then 667 local id getent 668 ble/util/assign id 'id -G' 669 ble/util/assign getent 'getent -w group S-1-16-12288' 670 ble/string#split getent : "$getent" 671 [[ " $id " == *" ${getent[1]} "* ]] && 672 _ble_prompt_const_root='#' 673 fi 674 fi 675 } 676 677 ## @arr PREFIX_data 678 ## プロンプトに表示するデータの単位です。 679 ## 他のデータに対する依存性等を管理する機能を有します。 680 ## 681 ## @var PREFIX_data[0] version 682 ## prompt 情報の更新回数を保持します。 683 ## @var PREFIX_data[1] hashref 684 ## @var PREFIX_data[2] hash 685 ## 依存性追跡に使われる変数です。 686 ## 687 ## @fn ble/prompt/unit#update TYPE PREFIX ARGS... 688 ## 依存性を追跡しつつデータを更新します。 689 ## 690 ## @fn[in] ble/prompt/unit:TYPE/update 691 ## データの更新をします。データに変化があった場合に 0 を返します。 692 ## それ以外の場合に 1 を返します。 693 ## 694 ## @var[in] prompt_unit 695 ## @var[in,out] prompt_unit_changed 696 ## @var[in] prompt_unit_expired 697 ## @var[in,out] prompt_hashref_dep 698 ## @var[in,out] prompt_hashref_var 699 ## 700 ## @var[in,opt] prompt_hashref_base 701 ## 702 ## @var[in] prompt_unit 703 ## ble/prompt/unit:PREFIX/update が入れ子で呼び出される時に設定される変数です。 704 ## 親プロンプトの PREFIX を保持します。 705 ## prompt 間の依存性を追跡する為に呼び出し元の以下の変数を更新します。 706 ## 707 ## @var[ref,opt] prompt_hashref_dep 708 ## 709 function ble/prompt/unit#update { 710 local unit=$1 711 712 local prompt_unit_changed= 713 local prompt_unit_expired= 714 715 local ohashref=${unit}_data[1]; ohashref=${!ohashref-} 716 if [[ ! $ohashref ]]; then 717 prompt_unit_expired=1 718 else 719 ble/prompt/unit#update/.update-dependencies "$ohashref" 720 local ohash=${unit}_data[2]; ohash=${!ohash} 721 builtin eval -- "local nhash=\"$ohashref\"" 2>/dev/null 722 [[ $nhash != "$ohash" ]] && prompt_unit_expired=1 723 fi 724 725 if [[ $prompt_unit_expired ]]; then 726 local prompt_unit=$unit 727 local prompt_hashref_dep= # プロンプト間依存性 728 local prompt_hashref_var= # 変数に対する依存性 729 730 ble/prompt/unit:"$unit"/update "$unit" && 731 ((prompt_unit_changed=1,${unit}_data[0]++)) 732 733 local hashref=${prompt_hashref_base-'$_ble_prompt_version'}:$prompt_hashref_dep:$prompt_hashref_var 734 builtin eval -- "${unit}_data[1]=\$hashref" 735 builtin eval -- "${unit}_data[2]=\"$hashref\"" 2>/dev/null 736 ble/util/unlocal prompt_unit prompt_hashref_dep 737 fi 738 739 # 呼び出し元 prompt_hashref_dep の更新 (依存性登録) 740 if [[ $prompt_unit ]]; then 741 local ref1='$'$unit'_data' 742 [[ ,$prompt_hashref_dep, != *,"$ref1",* ]] && 743 prompt_hashref_dep=$prompt_hashref_dep${prompt_hashref_dep:+,}$ref1 744 fi 745 [[ $prompt_unit_changed ]] 746 } 747 function ble/prompt/unit#update/.update-dependencies { 748 local ohashref=$1 749 local otree=${ohashref#*:}; otree=${otree%%:*} 750 if [[ $otree ]]; then 751 ble/string#split otree , "$otree" 752 753 if [[ ! $ble_prompt_unit_processing ]]; then 754 local ble_prompt_unit_processing=1 755 "${_ble_util_set_declare[@]//NAME/ble_prompt_unit_mark}" # WA #D1570 checked 756 elif ble/set#contains ble_prompt_unit_mark "$unit"; then 757 ble/util/print "ble/prompt: FATAL: detected cyclic dependency ($unit required by $ble_prompt_unit_parent)" >&"$_ble_util_fd_stderr" 758 return 1 759 fi 760 local ble_prompt_unit_parent=$unit 761 ble/set#add ble_prompt_unit_mark "$unit" 762 763 local prompt_unit= # 依存関係の登録はしない 764 local child 765 for child in "${otree[@]}"; do 766 [[ $child == '$'?*'_data' ]] || continue 767 child=${child:1:${#child}-6} 768 ble/is-function ble/prompt/unit:"$child"/update && 769 ble/prompt/unit#update "$child" 770 done 771 772 ble/set#remove ble_prompt_unit_mark "$unit" 773 fi 774 } 775 function ble/prompt/unit#clear { 776 local prefix=$1 777 builtin eval -- "${prefix}_data[2]=" 778 } 779 780 function ble/prompt/unit/assign { 781 local var=$1 value=$2 782 [[ $value == "${!var}" ]] && return 1 783 prompt_unit_changed=1 784 builtin eval -- "$var=\$value" 785 } 786 787 ## @fn ble/prompt/unit/add-hash hashref 788 ## プロンプトの更新検出に用いるシェル単語を指定します。 789 function ble/prompt/unit/add-hash { 790 [[ $prompt_unit && ,$prompt_hashref_var, != *,"$1",* ]] && 791 prompt_hashref_var=$prompt_hashref_var${prompt_hashref_var:+,}$1 792 return 0 793 } 794 795 ## @var _ble_prompt_ps1_data 796 ## @var _ble_prompt_rps1_data 797 ## @var _ble_prompt_status_data 798 ## @var _ble_prompt_xterm_title_data 799 ## @var _ble_prompt_screen_title_data 800 ## @var _ble_prompt_term_status_data 801 ## 構築した prompt の情報をキャッシュします。 802 ## 803 ## @var PREFIX_data[3..5] x y g 804 ## prompt を表示し終わった時のカーソルの位置と描画属性を表します。 805 ## @var PREFIX_data[6..7] lc lg 806 ## bleopt_internal_suppress_bash_output= の時、 807 ## prompt を表示し終わった時の左側にある文字とその描画属性を表します。 808 ## それ以外の時はこの値は使われません。 809 ## @var PREFIX_data[8] ps1out (esc) 810 ## prompt を表示する為に出力する制御シーケンスを含んだ文字列です。 811 ## @var PREFIX_data[9] trace_hash 812 ## COLUMNS:ps1esc の形式の文字列です。 813 ## 調整前の ps1out を格納します。 814 ## ps1out の計算 (trace) を省略する為に使用します。 815 ## 816 ## @var PREFIX_data[10...] tailored 817 ## ps1out の結果を加工して得られるデータ。 818 ## 加工だけを後で再実行する事もあるので統一的に管理する。 819 ## 820 _ble_prompt_ps1_dirty= 821 _ble_prompt_ps1_data=(0 '' '' 0 0 0 32 0 '' '') 822 _ble_prompt_ps1_bbox=() 823 _ble_prompt_rps1_dirty= 824 _ble_prompt_rps1_data=() 825 _ble_prompt_rps1_gbox=() 826 _ble_prompt_rps1_shown= 827 828 _ble_prompt_xterm_title_dirty= 829 _ble_prompt_xterm_title_data=() 830 _ble_prompt_screen_title_dirty= 831 _ble_prompt_screen_title_data=() 832 _ble_prompt_term_status_dirty= 833 _ble_prompt_term_status_data=() 834 835 ## @fn ble/prompt/print text 836 ## プロンプト構築中に呼び出す関数です。 837 ## 指定された文字列を、後の評価に対するエスケープをして出力します。 838 ## @param[in] text 839 ## エスケープされる文字列を指定します。 840 ## @var[out] DRAW_BUFF[] 841 ## 出力先の配列です。 842 function ble/prompt/print { 843 local ret=$1 844 [[ $prompt_noesc ]] || 845 ble/string#escape-characters "$ret" '\$"`' 846 ble/canvas/put.draw "$ret" 847 } 848 849 ## @fn ble/prompt/process-prompt-string prompt_string 850 ## プロンプト構築中に呼び出す関数です。 851 ## 指定した引数を PS1 と同様の形式と解釈して処理します。 852 ## @param[in] prompt_string 853 ## @arr[in,out] DRAW_BUFF 854 function ble/prompt/process-prompt-string { 855 local ps1=$1 856 local i=0 iN=${#ps1} 857 local rex_letters='^[^\]+|\\$' 858 while ((i<iN)); do 859 local tail=${ps1:i} 860 if [[ $tail == '\'?* ]]; then 861 ble/prompt/.process-backslash 862 elif [[ $tail =~ $rex_letters ]]; then 863 ble/canvas/put.draw "$BASH_REMATCH" 864 ((i+=${#BASH_REMATCH})) 865 else 866 # ? ここには本来来ないはず。 867 ble/canvas/put.draw "${tail::1}" 868 ((i++)) 869 fi 870 done 871 } 872 ## @fn ble/prompt/.process-backslash 873 ## @var[in] tail 874 ## @arr[in.out] DRAW_BUFF 875 function ble/prompt/.process-backslash { 876 ((i+=2)) 877 878 # \\ の次の文字 879 local c=${tail:1:1} pat='][#!$\' 880 if [[ $c == ["$pat"] ]]; then 881 case $c in 882 (\[) ble/canvas/put.draw $'\001' ;; # \[ \] は後処理の為、適当な識別用の文字列を出力する。 883 (\]) ble/canvas/put.draw $'\002' ;; 884 ('#') # コマンド番号 (本当は history に入らない物もある…) 885 ble/prompt/unit/add-hash '$_ble_edit_CMD' 886 ble/canvas/put.draw "$_ble_edit_CMD" ;; 887 (\!) # 編集行の履歴番号 888 local count 889 ble/history/get-count -v count 890 ble/canvas/put.draw "$((count+1))" ;; 891 ('$') # # or $ 892 ble/prompt/print "$_ble_prompt_const_root" ;; 893 (\\) 894 # '\\' は '\' と出力された後に、更に "" 内で評価された時に次の文字をエスケープする。 895 # 例えば '\\$' は一旦 '\$' となり、更に展開されて '$' となる。'\\\\' も同様に '\' になる。 896 ble/canvas/put.draw '\' ;; 897 esac 898 elif ble/is-function ble/prompt/backslash:"$c"; then 899 ble/function#try ble/prompt/backslash:"$c" 900 elif ble/is-function ble-edit/prompt/backslash:"$c"; then # deprecated name 901 ble/function#try ble-edit/prompt/backslash:"$c" 902 else 903 # その他の文字はそのまま出力される。 904 # - '\"' '\`' はそのまま出力された後に "" 内で評価され '"' '`' となる。 905 # - それ以外の場合は '\?' がそのまま出力された後に、"" 内で評価されても変わらず '\?' 等となる。 906 ble/canvas/put.draw "\\$c" 907 fi 908 } 909 910 ## @fn[custom] ble/prompt/backslash:* 911 ## プロンプト PS1 内で使用するバックスラッシュシーケンスを定義します。 912 ## 内部では ble/canvas/put.draw escaped_text もしくは 913 ## ble/prompt/print unescaped_text を用いて 914 ## シーケンスの展開結果を追記します。 915 ## 916 ## @exit 917 ## 対応する文字列を出力した時に成功します。 918 ## 0 以外の終了ステータスを返した場合、 919 ## シーケンスが処理されなかったと見做され、 920 ## 呼び出し元によって \c (c: 文字) が代わりに書き込まれます。 921 ## 922 function ble/prompt/backslash:0 { # 8進表現 923 local rex='^\\[0-7]{1,3}' 924 if [[ $tail =~ $rex ]]; then 925 local seq=${BASH_REMATCH[0]} 926 ((i+=${#seq}-2)) 927 builtin eval "c=\$'$seq'" 928 fi 929 ble/prompt/print "$c" 930 return 0 931 } 932 function ble/prompt/backslash:1 { ble/prompt/backslash:0; } 933 function ble/prompt/backslash:2 { ble/prompt/backslash:0; } 934 function ble/prompt/backslash:3 { ble/prompt/backslash:0; } 935 function ble/prompt/backslash:4 { ble/prompt/backslash:0; } 936 function ble/prompt/backslash:5 { ble/prompt/backslash:0; } 937 function ble/prompt/backslash:6 { ble/prompt/backslash:0; } 938 function ble/prompt/backslash:7 { ble/prompt/backslash:0; } 939 function ble/prompt/backslash:a { # 0 BEL 940 ble/canvas/put.draw "" 941 return 0 942 } 943 function ble/prompt/backslash:e { 944 ble/canvas/put.draw $'\e' 945 return 0 946 } 947 function ble/prompt/backslash:n { 948 ble/canvas/put.draw $'\n' 949 return 0 950 } 951 function ble/prompt/backslash:r { 952 ble/canvas/put.draw "$_ble_term_cr" 953 return 0 954 } 955 956 _ble_prompt_cache_vars=( 957 prompt_cache_d 958 prompt_cache_t 959 prompt_cache_A 960 prompt_cache_T 961 prompt_cache_at 962 prompt_cache_j 963 prompt_cache_wd 964 ) 965 966 function ble/prompt/backslash:d { # ? 日付 967 [[ $prompt_cache_d ]] || ble/util/strftime -v prompt_cache_d '%a %b %d' 968 ble/prompt/print "$prompt_cache_d" 969 return 0 970 } 971 function ble/prompt/backslash:t { # 8 時刻 972 [[ $prompt_cache_t ]] || ble/util/strftime -v prompt_cache_t '%H:%M:%S' 973 ble/prompt/print "$prompt_cache_t" 974 return 0 975 } 976 function ble/prompt/backslash:A { # 5 時刻 977 [[ $prompt_cache_A ]] || ble/util/strftime -v prompt_cache_A '%H:%M' 978 ble/prompt/print "$prompt_cache_A" 979 return 0 980 } 981 function ble/prompt/backslash:T { # 8 時刻 982 [[ $prompt_cache_T ]] || ble/util/strftime -v prompt_cache_T '%I:%M:%S' 983 ble/prompt/print "$prompt_cache_T" 984 return 0 985 } 986 function ble/prompt/backslash:@ { # ? 時刻 987 [[ $prompt_cache_at ]] || ble/util/strftime -v prompt_cache_at '%I:%M %p' 988 ble/prompt/print "$prompt_cache_at" 989 return 0 990 } 991 function ble/prompt/backslash:D { 992 local rex='^\\D\{([^{}]*)\}' cache_D 993 if [[ $tail =~ $rex ]]; then 994 ble/util/strftime -v cache_D "${BASH_REMATCH[1]}" 995 ble/prompt/print "$cache_D" 996 ((i+=${#BASH_REMATCH}-2)) 997 else 998 ble/prompt/print "\\$c" 999 fi 1000 return 0 1001 } 1002 function ble/prompt/backslash:h { # = ホスト名 1003 ble/prompt/print "$_ble_prompt_const_h" 1004 return 0 1005 } 1006 function ble/prompt/backslash:H { # = ホスト名 1007 ble/prompt/print "$_ble_prompt_const_H" 1008 return 0 1009 } 1010 function ble/prompt/backslash:j { # ジョブの数 1011 if [[ ! $prompt_cache_j ]]; then 1012 local joblist 1013 ble/util/joblist 1014 prompt_cache_j=${#joblist[@]} 1015 fi 1016 ble/canvas/put.draw "$prompt_cache_j" 1017 return 0 1018 } 1019 function ble/prompt/backslash:l { # tty basename 1020 ble/prompt/print "$_ble_prompt_const_l" 1021 return 0 1022 } 1023 function ble/prompt/backslash:s { # 4 "bash" 1024 ble/prompt/print "$_ble_prompt_const_s" 1025 return 0 1026 } 1027 function ble/prompt/backslash:u { # = ユーザ名 1028 ble/prompt/print "$_ble_prompt_const_u" 1029 return 0 1030 } 1031 function ble/prompt/backslash:v { # = bash version %d.%d 1032 ble/prompt/print "$_ble_prompt_const_v" 1033 return 0 1034 } 1035 function ble/prompt/backslash:V { # = bash version %d.%d.%d 1036 ble/prompt/print "$_ble_prompt_const_V" 1037 return 0 1038 } 1039 function ble/prompt/backslash:w { # PWD 1040 ble/prompt/unit/add-hash '$PWD' 1041 ble/prompt/.update-working-directory 1042 local ret 1043 ble/prompt/.escape-control-characters "$prompt_cache_wd" 1044 ble/prompt/print "$ret" 1045 return 0 1046 } 1047 function ble/prompt/backslash:W { # PWD短縮 1048 ble/prompt/unit/add-hash '$PWD' 1049 if [[ ! ${PWD//'/'} ]]; then 1050 ble/prompt/print "$PWD" 1051 else 1052 ble/prompt/.update-working-directory 1053 local ret 1054 ble/prompt/.escape-control-characters "${prompt_cache_wd##*/}" 1055 ble/prompt/print "$ret" 1056 fi 1057 return 0 1058 } 1059 1060 # \q{name} (ble.sh extension) 1061 function ble/prompt/backslash:q { 1062 local rex='^\{([^{}]*)\}' 1063 if [[ ${tail:2} =~ $rex ]]; then 1064 local rematch=$BASH_REMATCH 1065 ((i+=${#rematch})) 1066 local word; ble/string#split-words word "${BASH_REMATCH[1]}" 1067 if [[ $word ]] && ble/is-function ble/prompt/backslash:"$word"; then 1068 ble/util/joblist.check 1069 ble/prompt/backslash:"${word[@]}"; local ext=$? 1070 ble/util/joblist.check ignore-volatile-jobs 1071 return "$?" 1072 else 1073 if [[ ! $word ]]; then 1074 ble/term/visible-bell "ble/prompt: invalid sequence \\q$rematch" 1075 elif ! ble/is-function ble/prompt/backslash:"$word"; then 1076 ble/term/visible-bell "ble/propmt: undefined named sequence \\q{$word}" 1077 fi 1078 ble/prompt/print "\\q$BASH_REMATCH" 1079 return 2 1080 fi 1081 else 1082 ble/prompt/print "\\$c" 1083 fi 1084 return 0 1085 } 1086 function ble/prompt/backslash:g { 1087 local rex='^\{([^{}]*)\}' 1088 if [[ ${tail:2} =~ $rex ]]; then 1089 ((i+=${#BASH_REMATCH})) 1090 local ret 1091 ble/color/spec2g "${BASH_REMATCH[1]}" 1092 ble/color/g2sgr-ansi "$ret" 1093 ble/prompt/print "$ret" 1094 else 1095 ble/prompt/print "\\$c" 1096 fi 1097 return 0 1098 } 1099 function ble/prompt/backslash:position { 1100 ((_ble_textmap_dbeg>=0)) && ble/widget/.update-textmap 1101 local fmt=${1:-'(%s,%s)'} pos 1102 ble/prompt/unit/add-hash '${_ble_textmap_pos[_ble_edit_ind]}' 1103 ble/string#split-words pos "${_ble_textmap_pos[_ble_edit_ind]}" 1104 ble/util/sprintf pos "$fmt" "$((pos[1]+1))" "$((pos[0]+1))" 1105 ble/prompt/print "$pos" 1106 } 1107 function ble/prompt/backslash:row { 1108 ((_ble_textmap_dbeg>=0)) && ble/widget/.update-textmap 1109 local pos 1110 ble/prompt/unit/add-hash '${_ble_textmap_pos[_ble_edit_ind]}' 1111 ble/string#split-words pos "${_ble_textmap_pos[_ble_edit_ind]}" 1112 ble/prompt/print "$((pos[1]+1))" 1113 } 1114 function ble/prompt/backslash:column { 1115 ((_ble_textmap_dbeg>=0)) && ble/widget/.update-textmap 1116 local pos 1117 ble/prompt/unit/add-hash '${_ble_textmap_pos[_ble_edit_ind]}' 1118 ble/string#split-words pos "${_ble_textmap_pos[_ble_edit_ind]}" 1119 ble/prompt/print "$((pos[0]+1))" 1120 } 1121 function ble/prompt/backslash:point { 1122 ble/prompt/unit/add-hash '$_ble_edit_ind' 1123 ble/prompt/print "$_ble_edit_ind" 1124 } 1125 function ble/prompt/backslash:mark { 1126 ble/prompt/unit/add-hash '$_ble_edit_mark' 1127 ble/prompt/print "$_ble_edit_mark" 1128 } 1129 function ble/prompt/backslash:history-index { 1130 ble/prompt/unit/add-hash '$_ble_history_INDEX' 1131 ble/canvas/put.draw "$((_ble_history_INDEX+1))" 1132 } 1133 function ble/prompt/backslash:history-percentile { 1134 ble/prompt/unit/add-hash '$_ble_history_INDEX' 1135 ble/prompt/unit/add-hash '$_ble_history_COUNT' 1136 local index=$_ble_history_INDEX 1137 local count=$_ble_history_COUNT 1138 ((count||count++)) 1139 ble/canvas/put.draw "$((index*100/count))%" 1140 } 1141 1142 ## @fn ble/prompt/.update-working-directory 1143 ## @var[in,out] prompt_cache_wd 1144 function ble/prompt/.update-working-directory { 1145 [[ $prompt_cache_wd ]] && return 0 1146 1147 if [[ ! ${PWD//'/'} ]]; then 1148 prompt_cache_wd=$PWD 1149 return 0 1150 fi 1151 1152 local head= body=${PWD%/} 1153 if [[ $body == "$HOME" ]]; then 1154 prompt_cache_wd='~' 1155 return 0 1156 elif [[ $body == "$HOME"/* ]]; then 1157 head='~/' 1158 body=${body#"$HOME"/} 1159 fi 1160 1161 if [[ $PROMPT_DIRTRIM ]]; then 1162 local dirtrim=$((PROMPT_DIRTRIM)) 1163 local pat='[^/]' 1164 local count=${body//$pat} 1165 if ((${#count}>=dirtrim)); then 1166 local ret 1167 ble/string#repeat '/*' "$dirtrim" 1168 local omit=${body%$ret} 1169 ((${#omit}>3)) && 1170 body=...${body:${#omit}} 1171 fi 1172 fi 1173 1174 prompt_cache_wd=$head$body 1175 } 1176 1177 function ble/prompt/.escape/check-double-quotation { 1178 if [[ $tail == '"'* ]]; then 1179 if [[ ! $nest ]]; then 1180 out=$out'\"' 1181 tail=${tail:1} 1182 else 1183 out=$out'"' 1184 tail=${tail:1} 1185 nest=\"$nest 1186 ble/prompt/.escape/update-rex_skip 1187 fi 1188 return 0 1189 else 1190 return 1 1191 fi 1192 } 1193 function ble/prompt/.escape/check-command-substitution { 1194 if [[ $tail == '$('* ]]; then 1195 out=$out'$(' 1196 tail=${tail:2} 1197 nest=')'$nest 1198 ble/prompt/.escape/update-rex_skip 1199 return 0 1200 else 1201 return 1 1202 fi 1203 } 1204 function ble/prompt/.escape/check-parameter-expansion { 1205 if [[ $tail == '${'* ]]; then 1206 out=$out'${' 1207 tail=${tail:2} 1208 nest='}'$nest 1209 ble/prompt/.escape/update-rex_skip 1210 return 0 1211 else 1212 return 1 1213 fi 1214 } 1215 function ble/prompt/.escape/check-incomplete-quotation { 1216 if [[ $tail == '`'* ]]; then 1217 local rex='^`([^\`]|\\.)*\\$' 1218 [[ $tail =~ $rex ]] && tail=$tail'\' 1219 out=$out$tail'`' 1220 tail= 1221 return 0 1222 elif [[ $nest == ['})']* && $tail == \'* ]]; then 1223 out=$out$tail$q 1224 tail= 1225 return 0 1226 elif [[ $nest == ['})']* && $tail == \$\'* ]]; then 1227 local rex='^\$'$q'([^\'$q']|\\.)*\\$' 1228 [[ $tail =~ $rex ]] && tail=$tail'\' 1229 out=$out$tail$q 1230 tail= 1231 return 0 1232 elif [[ $tail == '\' ]]; then 1233 out=$out'\\' 1234 tail= 1235 return 0 1236 else 1237 return 1 1238 fi 1239 } 1240 function ble/prompt/.escape/update-rex_skip { 1241 if [[ $nest == \)* ]]; then 1242 rex_skip=$rex_skip_paren 1243 elif [[ $nest == \}* ]]; then 1244 rex_skip=$rex_skip_brace 1245 else 1246 rex_skip=$rex_skip_dquot 1247 fi 1248 } 1249 function ble/prompt/.escape { 1250 local tail=$1 out= nest= 1251 1252 # 地の文の " だけをエスケープする。 1253 1254 local q=\' 1255 local rex_bq='`([^\`]|\\.)*`' 1256 local rex_sq=$q'[^'$q']*'$q'|\$'$q'([^\'$q']|\\.)*'$q 1257 1258 local rex_skip 1259 local rex_skip_dquot='^([^\"$`]|'$rex_bq'|\\.)+' 1260 local rex_skip_brace='^([^\"$`'$q'}]|'$rex_bq'|'$rex_sq'|\\.)+' 1261 local rex_skip_paren='^([^\"$`'$q'()]|'$rex_bq'|'$rex_sq'|\\.)+' 1262 ble/prompt/.escape/update-rex_skip 1263 1264 while [[ $tail ]]; do 1265 if [[ $tail =~ $rex_skip ]]; then 1266 out=$out$BASH_REMATCH 1267 tail=${tail:${#BASH_REMATCH}} 1268 elif [[ $nest == ['})"']* && $tail == "${nest::1}"* ]]; then 1269 out=$out${nest::1} 1270 tail=${tail:1} 1271 nest=${nest:1} 1272 ble/prompt/.escape/update-rex_skip 1273 elif [[ $nest == \)* && $tail == \(* ]]; then 1274 out=$out'(' 1275 tail=${tail:1} 1276 nest=')'$nest 1277 elif ble/prompt/.escape/check-double-quotation; then 1278 continue 1279 elif ble/prompt/.escape/check-command-substitution; then 1280 continue 1281 elif ble/prompt/.escape/check-parameter-expansion; then 1282 continue 1283 elif ble/prompt/.escape/check-incomplete-quotation; then 1284 continue 1285 else 1286 out=$out${tail::1} 1287 tail=${tail:1} 1288 fi 1289 done 1290 ret=$out$nest 1291 } 1292 ## @fn ble/prompt/.get-keymap-for-current-mode 1293 ## @var[out] keymap 1294 function ble/prompt/.get-keymap-for-current-mode { 1295 ble/prompt/unit/add-hash '$_ble_decode_keymap,${_ble_decode_keymap_stack[*]}' 1296 1297 keymap=$_ble_decode_keymap 1298 local index=${#_ble_decode_keymap_stack[@]} 1299 while :; do 1300 case $keymap in (vi_?map|emacs) return 0 ;; esac 1301 ((--index<0)) && break 1302 keymap=${_ble_decode_keymap_stack[index]} 1303 done 1304 return 1 1305 } 1306 1307 function ble/prompt/.uses-builtin-prompt-expansion { 1308 ((_ble_bash>=40400)) || return 1 1309 1310 local ps=$1 1311 local chars_safe_esc='][0-7aenrdtAT@DhHjlsuvV!$\wW' 1312 [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $_ble_prompt_const_root == '#' ]] && 1313 chars_safe_esc=${chars_safe_esc//'$'} # Note: cygwin では ble.sh 独自の方法で \$ を処理する。 1314 1315 [[ $ps == *'\'[!"$chars_safe_esc"]* ]] && return 1 1316 1317 local glob_ctrl=$'[\001-\037\177]' 1318 [[ $ps == *'\'[wW]* && $PWD == *$glob_ctrl* ]] && return 1 1319 [[ $ps == *'\s'* && $_ble_prompt_const_s == *$'\e'* ]] && return 1 1320 [[ $ps == *'\u'* && $_ble_prompt_const_u == *$'\e'* ]] && return 1 1321 [[ $ps == *'\h'* && $_ble_prompt_const_h == *$'\e'* ]] && return 1 1322 [[ $ps == *'\H'* && $_ble_prompt_const_H == *$'\e'* ]] && return 1 1323 1324 return 0 1325 } 1326 1327 ## @fn ble/prompt/.instantiate ps opts [x0 y0 g0 lc0 lg0 esc0 trace_hash0] 1328 ## 1329 ## @var[out] x y g 1330 ## プロンプトの描画開始点を指定します。 1331 ## プロンプトを描画した後の位置を返します。 1332 ## @var[out] lc lg 1333 ## bleopt_internal_suppress_bash_output= の際に、 1334 ## 描画開始点の左の文字コードを指定します。 1335 ## 描画終了点の左の文字コードが分かる場合にそれを返します。 1336 ## @var[out] esc 1337 ## プロンプトを描画する為の文字列を返します。 1338 ## @var[out] trace_hash 1339 ## 1340 ## @var[in,out] x1 x2 y1 y2 1341 ## opts に measure-bbox を指定した時。 1342 ## @var[in,out] "${_ble_prompt_cache_vars[@]}" 1343 ## @var[in,out] prompt_rows prompt_cols 1344 ## 1345 function ble/prompt/.instantiate { 1346 trace_hash= esc= x=0 y=0 g=0 lc=32 lg=0 1347 local ps=$1 opts=$2 x0=$3 y0=$4 g0=$5 lc0=$6 lg0=$7 esc0=$8 trace_hash0=$9 1348 [[ ! $ps ]] && return 0 1349 1350 local expanded= 1351 if ble/prompt/.uses-builtin-prompt-expansion "$ps"; then 1352 [[ $ps == *'\'[wW]* ]] && ble/prompt/unit/add-hash '$PWD' 1353 ble-edit/exec/.setexit "$_ble_edit_exec_lastarg" 1354 LINENO=$_ble_edit_LINENO \ 1355 BASH_COMMAND=$_ble_edit_exec_BASH_COMMAND \ 1356 builtin eval 'expanded=${ps@P}' 1357 1358 else 1359 # 展開設定 1360 local prompt_noesc= 1361 shopt -q promptvars &>/dev/null || prompt_noesc=1 1362 1363 # 1. PS1 に含まれる \c を処理する 1364 local -a DRAW_BUFF=() 1365 ble/prompt/process-prompt-string "$ps" 1366 local processed; ble/canvas/sflush.draw -v processed 1367 1368 # 2. PS1 に含まれる \\ や " をエスケープし、 1369 # eval して各種シェル展開を実行する。 1370 if [[ ! $prompt_noesc ]]; then 1371 local ret 1372 ble/prompt/.escape "$processed"; local escaped=$ret 1373 expanded=${trace_hash0#*:} # Note: これは次行が失敗した時の既定値 1374 ble-edit/exec/.setexit "$_ble_edit_exec_lastarg" 1375 LINENO=$_ble_edit_LINENO \ 1376 BASH_COMMAND=$_ble_edit_exec_BASH_COMMAND \ 1377 builtin eval "expanded=\"$escaped\"" 1378 else 1379 expanded=$processed 1380 fi 1381 fi 1382 1383 if [[ :$opts: == *:show-mode-in-prompt:* ]]; then 1384 if ble/util/rlvar#test show-mode-in-prompt; then 1385 local keymap; ble/prompt/.get-keymap-for-current-mode 1386 1387 # Note: plain bash-4.3 では *-mode-string という設定項目は未だなく、 1388 # vi-ins-mode-string は '+', vi-cmd-mode-string は ':', 1389 # emacs-mode-string は '@' に対応する表示になる。ble.sh では bash-4.4 1390 # 以降と同じ既定値を用いる事にする。 1391 local ret= 1392 case $keymap in 1393 (vi_imap) ble/util/rlvar#read vi-ins-mode-string '(ins)' ;; # Note: bash-4.3 では '+' 1394 (vi_[noxs]map) ble/util/rlvar#read vi-cmd-mode-string '(cmd)' ;; # Note: bash-4.3 では ':' 1395 (emacs) ble/util/rlvar#read emacs-mode-string '@' ;; 1396 esac 1397 [[ $ret ]] && expanded=$ret$expanded 1398 fi 1399 fi 1400 1401 # 3. 端末への出力を構成する 1402 if [[ :$opts: == *:no-trace:* ]]; then 1403 # Note: "ESC k ... ESC \" 等を対象とするプロンプト文字列は trace 不要 1404 x=0 y=0 g=0 lc=32 lg=0 1405 esc=$expanded 1406 elif 1407 local rows=${prompt_rows:-${LINES:-25}} 1408 local cols=${prompt_cols:-${COLUMNS:-80}} 1409 local color=$_ble_color_g2sgr_version 1410 local bleopt=$bleopt_char_width_mode,$bleopt_char_width_version,$bleopt_emoji_version,$bleopt_emoji_opts 1411 trace_hash=$opts#$rows,$cols,$color#$bleopt#$expanded 1412 [[ $trace_hash != "$trace_hash0" ]] 1413 then 1414 local trace_opts=$opts:prompt 1415 [[ $bleopt_internal_suppress_bash_output ]] || trace_opts=$trace_opts:left-char 1416 x=0 y=0 g=0 lc=32 lg=0 1417 local ret 1418 LINES=$rows COLUMNS=$cols ble/canvas/trace "$expanded" "$trace_opts"; local traced=$ret 1419 ((lc<0&&(lc=0))) 1420 esc=$traced 1421 return 0 1422 else 1423 x=$x0 y=$y0 g=$g0 lc=$lc0 lg=$lg0 1424 esc=$esc0 1425 return 2 1426 fi 1427 } 1428 1429 ## @fn ble/prompt/unit:{section}/clear prefix type 1430 ## プロンプト内容の再計算を要求。 1431 ## 以前の内容と一致したら付属処理は省略。 1432 ## 1433 ## @param[in,opt] type 1434 ## hash ... hash 消去 (プロンプト内容の再計算を実施) 1435 ## tail ... tail 情報消去 (プロンプト内容計算後の付加処理を再実施) 1436 ## draw ... dirty 設定 (プロンプト内容の再描画) 1437 ## all ... 全消去 (全て再計算) 1438 ## 1439 function ble/prompt/unit:{section}/clear { 1440 local prefix=$1 type=${2:-hash:draw} 1441 [[ :$type: == *:hash:* ]] && 1442 builtin eval -- "${prefix}_data[2]=" 1443 [[ :$type: == *:tail:* ]] && 1444 builtin eval -- "${prefix}_data=(\"\${${prefix}_data[@]::10}\")" 1445 [[ :$type: == *:draw:* ]] && 1446 builtin eval -- "${prefix}_dirty=1" 1447 [[ :$type: == *:all:* ]] && 1448 builtin eval -- "${prefix}_data=(\"\${${prefix}_data[0]}\")" 1449 return 0 1450 } 1451 1452 function ble/prompt/unit:{section}/get { 1453 local ref=${1}_data[8]; ret=${!ref} 1454 } 1455 1456 ## @fn ble/prompt/unit:{section}/update prefix ps opts 1457 ## @param[in] prefix 1458 ## @param[in] ps 1459 ## @param[in] opts 1460 ## コロン区切りの trace オプションです。 1461 ## 1462 ## show-mode-in-prompt 1463 ## 現在のモード名を付加します。 1464 ## 1465 ## no-trace 1466 ## ble/canvas/trace による変換・計測をせず、 1467 ## プロンプト文字列の処理のみを行います。 1468 ## これは制御列など端末に出力しない内容を解析するのに使います。 1469 ## 1470 ## @param[in] prompt_rows prompt_cols 1471 function ble/prompt/unit:{section}/update { 1472 local prefix=$1 ps=$2 opts=$3 1473 1474 # Load variables 1475 local -a vars; vars=(data dirty) 1476 [[ :$opts: == *:measure-bbox:* ]] && ble/array#push vars bbox 1477 [[ :$opts: == *:measure-gbox:* ]] && ble/array#push vars gbox 1478 local "${vars[@]/%/=}" # WA #D1570 checked 1479 ble/util/restore-vars "${prefix}_" "${vars[@]}" 1480 1481 local has_changed= 1482 if [[ $prompt_unit_expired ]]; then 1483 local original_esc=${data[8]}:${data[9]}:${data[10]} # esc:trace_hash:tailor 1484 1485 if [[ $ps ]]; then 1486 # load 1487 [[ :$opts: == *:measure-bbox:* ]] && 1488 local x1=${bbox[0]} y1=${bbox[1]} x2=${bbox[2]} y2=${bbox[3]} 1489 [[ :$opts: == *:measure-gbox:* ]] && 1490 local gx1=${gbox[0]} gy1=${gbox[1]} gx2=${gbox[2]} gy2=${gbox[3]} 1491 1492 local trace_hash esc x y g lc lg 1493 ble/prompt/.instantiate "$ps" "$opts" "${data[@]:3:7}" 1494 data=("${data[0]:-0}" '' '' "$x" "$y" "$g" "$lc" "$lg" "$esc" "$trace_hash" "${data[@]:10}") 1495 1496 # store 1497 [[ :$opts: == *:measure-bbox:* ]] && 1498 bbox=("$x1" "$y1" "$x2" "$y2") 1499 [[ :$opts: == *:measure-gbox:* ]] && 1500 gbox=("$gx1" "$gy1" "$gx2" "$gy2") 1501 else 1502 data=("${data[0]:-0}" '' '' 0 0 0 32 0 '' '' "${data[@]:10}") 1503 [[ :$opts: == *:measure-bbox:* ]] && bbox=() 1504 [[ :$opts: == *:measure-gbox:* ]] && gbox=() 1505 fi 1506 1507 [[ ${data[8]}:${data[9]}:${data[10]} != "$original_esc" ]] && has_changed=1 1508 fi 1509 1510 [[ $has_changed ]] && ((dirty=1)) 1511 1512 # Save variables 1513 ble/util/save-vars "${prefix}_" "${vars[@]}" 1514 1515 [[ $has_changed ]] 1516 } 1517 1518 #---------------------------------------------------------- 1519 # Definitions of prompt sections 1520 1521 function ble/prompt/unit:_ble_prompt_ps1/update { 1522 ble/prompt/unit/add-hash '$prompt_ps1' 1523 ble/prompt/unit:{section}/update _ble_prompt_ps1 "$prompt_ps1" show-mode-in-prompt:measure-bbox 1524 } 1525 1526 function ble/prompt/unit:_ble_prompt_rps1/update { 1527 ble/prompt/unit/add-hash '$prompt_rps1' 1528 ble/prompt/unit/add-hash '$_ble_prompt_ps1_data' 1529 local cols=${COLUMNS-80} 1530 local ps1x=${_ble_prompt_ps1_data[3]} 1531 local ps1y=${_ble_prompt_ps1_data[4]} 1532 local prompt_rows=$((ps1y+1)) prompt_cols=$cols 1533 ble/prompt/unit:{section}/update _ble_prompt_rps1 "$prompt_rps1" confine:relative:right:measure-gbox || return 1 1534 1535 local esc=${_ble_prompt_rps1_data[8]} width= 1536 if [[ $esc ]]; then 1537 ((width=_ble_prompt_rps1_gbox[2]-_ble_prompt_rps1_gbox[0])) 1538 ((width&&20+width<cols&&ps1x+10+width<cols)) || esc= width= 1539 fi 1540 _ble_prompt_rps1_data[10]=$esc 1541 _ble_prompt_rps1_data[11]=$width 1542 return 0 1543 } 1544 1545 function ble/prompt/unit:_ble_prompt_xterm_title/update { 1546 ble/prompt/unit/add-hash '$bleopt_prompt_xterm_title' 1547 local prompt_rows=1 1548 ble/prompt/unit:{section}/update _ble_prompt_xterm_title "$bleopt_prompt_xterm_title" confine:no-trace || return 1 1549 1550 local esc=${_ble_prompt_xterm_title_data[8]} 1551 [[ $esc ]] && esc=$'\e]0;'${esc//[! -~]/'#'}$'\a' 1552 _ble_prompt_xterm_title_data[10]=$esc 1553 return 0 1554 } 1555 1556 function ble/prompt/unit:_ble_prompt_screen_title/update { 1557 ble/prompt/unit/add-hash '$bleopt_prompt_screen_title' 1558 local prompt_rows=1 1559 ble/prompt/unit:{section}/update _ble_prompt_screen_title "$bleopt_prompt_screen_title" confine:no-trace || return 1 1560 1561 local esc=${_ble_prompt_screen_title_data[8]} 1562 [[ $esc ]] && esc=$'\ek'${esc//[! -~]/'#'}$'\e\\' 1563 _ble_prompt_screen_title_data[10]=$esc 1564 return 0 1565 } 1566 1567 function ble/prompt/unit:_ble_prompt_term_status/update { 1568 ble/prompt/unit/add-hash '$bleopt_prompt_term_status' 1569 local prompt_rows=1 1570 ble/prompt/unit:{section}/update _ble_prompt_term_status "$bleopt_prompt_term_status" confine:no-trace || return 1 1571 1572 local esc=${_ble_prompt_term_status_data[8]} 1573 if [[ $esc ]]; then 1574 esc=$_ble_term_tsl${esc//[! -~]/'#'}$_ble_term_fsl 1575 else 1576 # 非空文字列から空文字列になった時はステータス行をクリア 1577 esc=$_ble_term_dsl 1578 fi 1579 _ble_prompt_term_status_data[10]=$esc 1580 return 0 1581 } 1582 1583 function ble/prompt/unit:_ble_prompt_status/update { 1584 ble/prompt/unit/add-hash '$bleopt_prompt_status_align' 1585 ble/prompt/unit/add-hash '$bleopt_prompt_status_line' 1586 local ps=$bleopt_prompt_status_line 1587 local cols=$COLUMNS; ((_ble_term_xenl||cols--)) 1588 local trace_opts=confine:relative:measure-bbox:noscrc:face0=prompt_status_line 1589 local rex='^justify(=[^:]+)?$' 1590 [[ $bleopt_prompt_status_align =~ $rex ]] && 1591 trace_opts=$trace_opts:$BASH_REMATCH 1592 1593 local prompt_rows=1 prompt_cols=$cols 1594 ble/prompt/unit:{section}/update _ble_prompt_status "$ps" "$trace_opts" || return 1 1595 1596 # tailor 1597 local esc=${_ble_prompt_status_data[8]} 1598 if [[ $ps && $esc ]]; then 1599 local x=${_ble_prompt_status_data[3]} 1600 local y=${_ble_prompt_status_data[4]} 1601 local x1=${_ble_prompt_status_bbox[0]} 1602 local x2=${_ble_prompt_status_bbox[2]} 1603 1604 local -a DRAW_BUFF=() 1605 1606 # background color 1607 local ret 1608 ble/color/face2g prompt_status_line; local g0=$ret 1609 ble/color/g2sgr "$g0"; local sgr=$ret 1610 if ((g0==0||_ble_term_bce)); then 1611 ble/canvas/put.draw "$sgr$_ble_term_el$_ble_term_sgr0" 1612 else 1613 ble/string#reserve-prototype "$cols" 1614 ble/canvas/put.draw "$sgr${_ble_string_prototype::cols}" 1615 ble/canvas/put-cub.draw "$cols" 1616 ble/canvas/put.draw "$_ble_term_sgr0" 1617 fi 1618 1619 # bleopt prompt_status_align 1620 local xshift=0 1621 case $bleopt_prompt_status_align in 1622 (center) ((xshift=cols/2-(x2+x1)/2)) ;; 1623 (right) ((xshift=cols-x2)) ;; 1624 esac 1625 if ((xshift>0)); then 1626 ((x+=xshift)) 1627 ble/canvas/put-cuf.draw "$xshift" 1628 fi 1629 1630 ble/canvas/put.draw "$esc" 1631 ble/canvas/sflush.draw -v esc 1632 1633 _ble_prompt_status_data[10]=$esc 1634 _ble_prompt_status_data[11]=$x 1635 _ble_prompt_status_data[12]=$y 1636 else 1637 _ble_prompt_status_data[10]= 1638 _ble_prompt_status_data[11]= 1639 _ble_prompt_status_data[12]= 1640 fi 1641 1642 return 0 1643 } 1644 1645 #---------------------------------------------------------- 1646 # Update prompts for textarea 1647 1648 # process TMOUT 1649 if ble/is-function ble/util/idle.push; then 1650 _ble_prompt_timeout_task= 1651 _ble_prompt_timeout_lineno= 1652 function ble/prompt/timeout/process { 1653 ble/util/idle.suspend # exit に失敗した時の為 task を suspend にする 1654 local msg="${_ble_term_setaf[12]}[ble: auto-logout]$_ble_term_sgr0 timed out waiting for input" 1655 ble/widget/.internal-print-command ' 1656 ble/util/print "$msg" 1657 builtin exit 0 &>/dev/null 1658 builtin exit 0 &>/dev/null' pre-flush 1659 return 1 # exit に失敗した時 1660 } >&"$_ble_util_fd_stdout" 2>&"$_ble_util_fd_stderr" 1661 function ble/prompt/timeout/check { 1662 [[ $_ble_edit_lineno == "$_ble_prompt_timeout_lineno" ]] && return 0 1663 _ble_prompt_timeout_lineno=$_ble_edit_lineno 1664 1665 if [[ ${TMOUT:-} =~ ^[0-9]+ ]] && ((BASH_REMATCH>0)); then 1666 if [[ ! $_ble_prompt_timeout_task ]]; then 1667 ble/util/idle.push -Z 'ble/prompt/timeout/process' 1668 _ble_prompt_timeout_task=$_ble_util_idle_lasttask 1669 fi 1670 ble/util/idle#sleep "$_ble_prompt_timeout_task" "$((BASH_REMATCH*1000))" 1671 elif [[ $_ble_prompt_timeout_task ]]; then 1672 ble/util/idle#suspend "$_ble_prompt_timeout_task" 1673 fi 1674 } 1675 else 1676 function ble/prompt/timeout/check { ((1)); } 1677 fi 1678 1679 function ble/prompt/update/.has-prompt_command { 1680 [[ ${_ble_edit_PROMPT_COMMAND[*]} == *[![:space:]]* ]] 1681 } 1682 function _ble_prompt_update__eval_prompt_command_1 { 1683 # Note: return 等と記述されていた時の対策として関数内評価する。 1684 # Note #D1772: 本来は tempenv として _ble_edit_exec_TRAPDEBUG_enabled=1 も指 1685 # 定すれば ble-edit/exec/.setexit や builtin eval に対する DEBUG trap の除外 1686 # を明示的に確認しなくても済むはずだが、bash-4.4..5.2(少なくとも) にはバグが 1687 # あって builtin eval を使うと DEBUG trap の中から tmpenv が見えなくなってし 1688 # まう。仕方がないので local で _ble_edit_exec_TRAPDEBUG_enabled=1 を設定する。 1689 local _ble_edit_exec_TRAPDEBUG_enabled=1 1690 ble-edit/exec/.setexit "$_ble_edit_exec_lastarg" 1691 LINENO=$_ble_edit_LINENO \ 1692 BASH_COMMAND=$_ble_edit_exec_BASH_COMMAND \ 1693 builtin eval -- "$1" 1694 } 1695 ble/function#trace _ble_prompt_update__eval_prompt_command_1 1696 function ble/prompt/update/.eval-prompt_command { 1697 ((${#PROMPT_COMMAND[@]})) || return 0 1698 local _ble_local_command _ble_edit_exec_TRAPDEBUG_adjusted=1 1699 ble-edit/exec:gexec/.TRAPDEBUG/restore filter 1700 for _ble_local_command in "${PROMPT_COMMAND[@]}"; do 1701 [[ $_ble_local_command ]] || continue 1702 _ble_prompt_update__eval_prompt_command_1 "$_ble_local_command" 1703 done 1704 _ble_edit_exec_gexec__TRAPDEBUG_adjust 1705 } 1706 ## @fn ble/prompt/update opts 1707 ## _ble_edit_PS1 からプロンプトを構築します。 1708 ## @param[in] opts 1709 ## コロン区切りのオプションのリストです。 1710 ## 1711 ## leave ... 次行に行く直前の最後の表示である事を示します。 1712 ## これが指定された時 transient prompt 等の処理を実行します。 1713 ## 1714 ## @var[in,out] _ble_prompt_update_dirty 1715 ## @var[in,out] _ble_prompt_rps1_enabled 1716 ## 1717 ## @var[in] _ble_edit_PS1 1718 ## 構築されるプロンプトの内容を指定します。 1719 ## @var[out] _ble_prompt_ps1_data 1720 ## 構築したプロンプトの情報を格納します。 1721 _ble_prompt_update= 1722 _ble_prompt_update_dirty= 1723 _ble_prompt_rps1_enabled= 1724 function ble/prompt/update { 1725 local opts=:$1: dirty= 1726 1727 local count; ble/history/get-count 1728 local version=$COLUMNS:$_ble_edit_lineno:$count 1729 if [[ :$opts: == *:check-dirty:* && $_ble_prompt_update == owner ]]; then 1730 if [[ $_ble_prompt_update_dirty && :$opts: != *:leave:* && $_ble_prompt_hash == "$version" ]]; then 1731 [[ $_ble_prompt_update_dirty == dirty ]]; local ext=$? 1732 _ble_prompt_update_dirty=done 1733 return "$ext" 1734 fi 1735 fi 1736 1737 ble/prompt/timeout/check 1738 1739 _ble_prompt_rps1_enabled= 1740 1741 # Update PS1 in PROMPT_COMMAND / PRECMD 1742 if ((_ble_textarea_panel==0)); then # 補助プロンプトに対しては PROMPT_COMMAND は実行しない 1743 # Note #D1778: version の内の history count は PROMPT_COMMAND の更新には使わない。 1744 if [[ ${_ble_prompt_hash%:*} != "${version%:*}" && $opts != *:leave:* ]]; then 1745 ble-edit/exec:gexec/invoke-hook-with-setexit internal_PRECMD 1746 if ble/prompt/update/.has-prompt_command || blehook/has-hook PRECMD; then 1747 # #D1750 PROMPT_COMMAND 及び PRECMD が何か出力する時は表示が乱れるので 1748 # クリアする。点滅などを避ける為、既定では off にしておく。 1749 if [[ $bleopt_prompt_command_changes_layout ]]; then 1750 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 1751 local -a DRAW_BUFF=() 1752 ble/canvas/panel#goto.draw 0 0 0 sgr0 1753 ble/canvas/bflush.draw 1754 ble/util/buffer.flush >&2 1755 fi 1756 1757 ((_ble_edit_attached)) && ble-edit/restore-PS1 1758 ble-edit/exec:gexec/invoke-hook-with-setexit PRECMD 1759 ble/prompt/update/.eval-prompt_command 1760 ((_ble_edit_attached)) && ble-edit/adjust-PS1 1761 1762 if [[ $bleopt_prompt_command_changes_layout ]]; then 1763 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 1764 fi 1765 fi 1766 fi 1767 fi 1768 1769 local prompt_opts= 1770 local prompt_ps1=$_ble_edit_PS1 1771 local prompt_rps1=$bleopt_prompt_rps1 1772 if [[ $opts == *:leave:* ]]; then 1773 local ps1f=$bleopt_prompt_ps1_final 1774 local rps1f=$bleopt_prompt_rps1_final 1775 local ps1t=$bleopt_prompt_ps1_transient 1776 [[ :$ps1t: == *:trim:* || :$ps1t: == *:same-dir:* && $PWD != $_ble_prompt_trim_opwd ]] && ps1t= 1777 if [[ $ps1f || $rps1f || $ps1t ]]; then 1778 prompt_opts=$prompt_opts:leave-rewrite 1779 [[ $ps1f || $ps1t ]] && prompt_ps1=$ps1f 1780 [[ $rps1f ]] && prompt_rps1=$rps1f 1781 ble/textarea#invalidate 1782 fi 1783 fi 1784 1785 if [[ :$prompt_opts: == *:leave-rewrite:* || $_ble_prompt_hash != "$version" ]]; then 1786 _ble_prompt_hash=$version 1787 ((_ble_prompt_version++)) 1788 fi 1789 1790 # initialize variables 1791 ble/history/update-position 1792 local prompt_hashref_base='$_ble_prompt_version' 1793 local prompt_rows=${LINES:-25} 1794 local prompt_cols=${COLUMNS:-80} 1795 local "${_ble_prompt_cache_vars[@]/%/=}" # WA #D1570 checked 1796 1797 ble/prompt/unit#update _ble_prompt_ps1 && dirty=1 1798 1799 # Note #D1392: mc (midnight commander) の中では補助プロンプトは全て off 1800 [[ $MC_SID == $$ ]] && { [[ $dirty ]]; return "$?"; } 1801 1802 # Note: 補助プロンプトは _ble_textarea_panel==0 の時だけ有効 #D1027 1803 ((_ble_textarea_panel==0)) || { [[ $dirty ]]; return "$?"; } 1804 1805 # bleopt prompt_rps1 1806 if [[ :$opts: == *:leave:* && ! $rps1f && $bleopt_prompt_rps1_transient ]]; then 1807 # prompt_rps1_transient による消去 (以前の大きさを保持) 1808 [[ ${_ble_prompt_rps1_data[10]} ]] && dirty=1 _ble_prompt_rps1_enabled=erase 1809 1810 else 1811 [[ $prompt_rps1 || ${_ble_prompt_rps1_data[10]} ]] && 1812 ble/prompt/unit#update _ble_prompt_rps1 && dirty=1 1813 [[ ${_ble_prompt_rps1_data[10]} ]] && _ble_prompt_rps1_enabled=1 1814 fi 1815 1816 # bleopt prompt_xterm_title 1817 case ${_ble_term_TERM:-$TERM:-} in 1818 (sun*|minix*) ;; # black list 1819 (*) 1820 [[ $bleopt_prompt_xterm_title || ${_ble_prompt_xterm_title_data[10]} ]] && 1821 ble/prompt/unit#update _ble_prompt_xterm_title && dirty=1 ;; 1822 esac 1823 1824 # bleopt prompt_screen_title 1825 case ${_ble_term_TERM:-$TERM:-} in 1826 (screen:*|tmux:*|contra:*|screen.*|screen-*) 1827 [[ $bleopt_prompt_screen_title || ${_ble_prompt_screen_title_data[10]} ]] && 1828 ble/prompt/unit#update _ble_prompt_screen_title && dirty=1 ;; 1829 esac 1830 1831 # bleopt prompt_term_status 1832 if [[ $_ble_term_tsl && $_ble_term_fsl ]]; then 1833 [[ $bleopt_prompt_term_status || ${_ble_prompt_term_status_data[10]} ]] && 1834 ble/prompt/unit#update _ble_prompt_term_status && dirty=1 1835 fi 1836 1837 # bleopt prompt_status_line 1838 [[ $bleopt_prompt_status_line || ${_ble_prompt_status_data[10]} ]] && 1839 ble/prompt/unit#update _ble_prompt_status && dirty=1 1840 1841 [[ $dirty ]] && _ble_prompt_update_dirty=dirty 1842 [[ $dirty ]] 1843 } 1844 function ble/prompt/clear { 1845 _ble_prompt_hash= 1846 ble/textarea#invalidate 1847 } 1848 1849 #---------------------------------------------------------- 1850 # Postexec prompts 1851 1852 _ble_prompt_ruler=('' '' 0) 1853 1854 function ble/prompt/print-ruler.draw { 1855 [[ $bleopt_prompt_ruler ]] || return 0 1856 1857 local command=$1 opts=$2 cols=$COLUMNS 1858 local rex_eval_prefix='(([!{]|time|if|then|elif|while|until|do|exec|eval|command|env|nice|nohup|xargs|sudo)[[:space:]]+)?' 1859 local rex_clear_command='(tput[[:space:]]+)?(clear|reset)' 1860 local rex=$'(^|[\n;&|(])[[:space:]]*'$rex_eval_prefix$rex_clear_command'([ \t\n;&|)]|$)' 1861 [[ $command =~ $rex ]] && return 0 1862 1863 if [[ :$opts: == *:keep-info:* ]]; then 1864 ble/canvas/panel#increase-height.draw "$_ble_textarea_panel" 1 1865 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 0 1866 ((_ble_canvas_panel_height[_ble_textarea_panel]--)) 1867 fi 1868 1869 if [[ $bleopt_prompt_ruler == empty-line ]]; then 1870 ble/canvas/put.draw $'\n' 1871 else 1872 if [[ $bleopt_prompt_ruler != "${_ble_prompt_ruler[0]}" ]]; then 1873 if [[ $bleopt_prompt_ruler ]]; then 1874 local ret= x=0 y=0 g=0 x1=0 x2=0 y1=0 y2=0 1875 LINES=1 COLUMNS=$cols ble/canvas/trace "$bleopt_prompt_ruler" truncate:measure-bbox 1876 _ble_prompt_ruler=("$bleopt_prompt_ruler" "$ret" "$x2") 1877 if ((!_ble_prompt_ruler[2])); then 1878 _ble_prompt_ruler[1]=${_ble_prompt_ruler[1]}' ' 1879 ((_ble_prompt_ruler[2]++)) 1880 fi 1881 else 1882 _ble_prompt_ruler=('' '' 0) 1883 fi 1884 fi 1885 1886 local w=${_ble_prompt_ruler[2]} 1887 local repeat=$((cols/w)) 1888 ble/string#repeat "${_ble_prompt_ruler[1]}" "$repeat" 1889 ble/canvas/put.draw "$ret" 1890 ble/string#repeat ' ' "$((cols-repeat*w))" 1891 ble/canvas/put.draw "$ret" 1892 ((_ble_term_xenl)) && ble/canvas/put.draw $'\n' 1893 fi 1894 } 1895 function ble/prompt/print-ruler.buff { 1896 local -a DRAW_BUFF=() 1897 ble/prompt/print-ruler.draw "$@" 1898 ble/canvas/bflush.draw 1899 } 1900 1901 # 1902 #------------------------------------------------------------------------------ 1903 # **** information pane **** @line.info 1904 1905 ## @fn ble/edit/info/.initialize-size 1906 ## @var[out] cols lines 1907 function ble/edit/info/.initialize-size { 1908 local ret 1909 ble/canvas/panel/layout/.get-available-height "$_ble_edit_info_panel" 1910 cols=${COLUMNS-80} lines=$ret 1911 } 1912 1913 _ble_edit_info_panel=2 1914 _ble_edit_info=(0 0 "") 1915 _ble_edit_info_invalidated= 1916 1917 function ble/edit/info#panel::getHeight { 1918 (($1!=_ble_edit_info_panel)) && return 0 1919 if ble/edit/is-command-layout || [[ ! ${_ble_edit_info[2]} ]]; then 1920 height=0:0 1921 else 1922 height=1:$((_ble_edit_info[1]+1)) 1923 fi 1924 } 1925 function ble/edit/info#panel::invalidate { 1926 (($1!=_ble_edit_info_panel)) && return 0 1927 _ble_edit_info_invalidated=1 1928 } 1929 function ble/edit/info#panel::render { 1930 (($1!=_ble_edit_info_panel)) && return 0 1931 ble/edit/is-command-layout && return 0 1932 [[ $_ble_edit_info_invalidated ]] || return 0 1933 1934 local x=${_ble_edit_info[0]} y=${_ble_edit_info[1]} content=${_ble_edit_info[2]} 1935 local -a DRAW_BUFF=() 1936 if [[ ! $content ]]; then 1937 ble/canvas/panel#set-height.draw "$_ble_edit_info_panel" 0 1938 else 1939 ble/canvas/panel/reallocate-height.draw 1940 if ((y<_ble_canvas_panel_height[$1])); then 1941 ble/canvas/panel#clear.draw "$_ble_edit_info_panel" 1942 ble/canvas/panel#goto.draw "$_ble_edit_info_panel" 1943 ble/canvas/put.draw "$content" 1944 ((_ble_canvas_y+=y,_ble_canvas_x=x)) 1945 else 1946 # 表示領域が足りない場合は内容消去 (本来 construct-content に於いてちゃん 1947 # と確保できる高さに収められている筈。もしそれが駄目なら前回の 1948 # construct-content 以降に端末の大きさが変わった等の理由が考えられる。) 1949 _ble_edit_info=(0 0 "") 1950 ble/canvas/panel#set-height.draw "$_ble_edit_info_panel" 0 1951 fi 1952 fi 1953 ble/canvas/bflush.draw 1954 _ble_edit_info_invalidated= 1955 } 1956 ## @fn ble/edit/info#collapse 1957 ## 一時的に非表示状態にする (旧 ble/edit/info/hide に対応)。 1958 function ble/edit/info#collapse { 1959 local panel=${1-$_ble_prompt_info_panel} 1960 ((panel!=_ble_edit_info_panel)) && return 0 1961 1962 local -a DRAW_BUFF=() 1963 ble/canvas/panel#set-height.draw "$panel" 0 1964 ble/canvas/bflush.draw 1965 _ble_edit_info_invalidated=1 1966 } 1967 1968 ## @fn ble/edit/info/.construct-content type text 1969 ## @var[out] x y 1970 ## @var[out] content 1971 function ble/edit/info/.construct-content { 1972 local cols lines 1973 ble/edit/info/.initialize-size 1974 x=0 y=0 content= 1975 1976 local type=$1 text=$2 1977 case $1 in 1978 (clear) ;; 1979 (ansi|esc) 1980 local trace_opts=truncate 1981 [[ $bleopt_info_display == bottom ]] && trace_opts=$trace_opts:noscrc 1982 [[ $1 == esc ]] && trace_opts=$trace_opts:terminfo 1983 local ret= g=0 1984 LINES=$lines ble/canvas/trace "$text" "$trace_opts" 1985 content=$ret ;; 1986 (text) 1987 local ret 1988 ble/canvas/trace-text "$text" 1989 content=$ret ;; 1990 (store) 1991 x=$2 y=$3 content=$4 1992 # 現在の高さに入らない時は計測し直す。 1993 ((y<lines)) || ble/edit/info/.construct-content esc "$content" ;; 1994 (*) 1995 ble/util/print "usage: ble/edit/info/.construct-content type text" >&2 ;; 1996 esac 1997 } 1998 1999 ## @fn ble/edit/info/.render-content x y content [opts] 2000 ## @param[in] x y content 2001 function ble/edit/info/.render-content { 2002 local x=$1 y=$2 content=$3 opts=$4 2003 2004 # 新しい内容が設定される時にのみ invalidate を設定する。 2005 if [[ $content != "${_ble_edit_info[2]}" ]]; then 2006 _ble_edit_info=("$x" "$y" "$content") 2007 _ble_edit_info_invalidated=1 2008 fi 2009 2010 [[ :$opts: == *:defer:* ]] && return 0 2011 [[ $_ble_app_render_mode == panel ]] || return 0 2012 ble/edit/info#panel::render "$_ble_edit_info_panel" 2013 } 2014 2015 _ble_edit_info_default=(0 0 "") 2016 _ble_edit_info_scene=default 2017 2018 ## @fn ble/edit/info/show type text 2019 ## 2020 ## @param[in] type 2021 ## 2022 ## 以下の何れかを指定する。 2023 ## 2024 ## text, ansi, esc, store 2025 ## 2026 ## @param[in] text 2027 ## 2028 ## type=text のとき、引数 text は表示する文字列を含む。 2029 ## 改行などの制御文字は代替表現に置き換えられる。 2030 ## type=ansi のとき、引数 text はANSI制御シーケンスを含む文字列を指定する。 2031 ## type=esc のとき、引数 text は現在の端末の制御シーケンスを含む文字列を指定する。 2032 ## 2033 ## これらの文字列について 2034 ## 画面からはみ出る文字列に関しては自動で truncate される。 2035 ## 2036 function ble/edit/info/show { 2037 local type=$1 text=$2 2038 if [[ $text ]]; then 2039 local x y content= 2040 ble/edit/info/.construct-content "$@" 2041 ble/edit/info/.render-content "$x" "$y" "$content" 2042 ble/util/buffer.flush >&2 2043 _ble_edit_info_scene=show 2044 else 2045 ble/edit/info/default 2046 fi 2047 } 2048 function ble/edit/info/set-default { 2049 local type=$1 text=$2 2050 local x y content 2051 ble/edit/info/.construct-content "$type" "$text" 2052 _ble_edit_info_default=("$x" "$y" "$content") 2053 [[ $_ble_edit_info_scene == default ]] && 2054 ble/edit/info/.render-content "${_ble_edit_info_default[@]}" defer 2055 } 2056 function ble/edit/info/default { 2057 _ble_edit_info_scene=default 2058 if (($#)); then 2059 ble/edit/info/set-default "$@" 2060 else 2061 ble/edit/info/.render-content "${_ble_edit_info_default[@]}" defer 2062 fi 2063 return 0 2064 } 2065 2066 function ble/edit/info/clear { 2067 [[ ${_ble_edit_info[2]} ]] || return 1 2068 [[ $_ble_app_render_mode == panel ]] || return 0 2069 _ble_edit_info_scene=clear 2070 ble/edit/info/.render-content 0 0 "" 2071 } 2072 2073 function ble/edit/info/immediate-show { 2074 local ret; ble/canvas/panel/save-position 2075 ble/edit/info/show "$@" 2076 ble/canvas/panel/load-position "$ret" 2077 ble/util/buffer.flush >&2 2078 } 2079 function ble/edit/info/immediate-default { 2080 local ret; ble/canvas/panel/save-position 2081 ble/edit/info/default 2082 ble/edit/info/.render-content "${_ble_edit_info_default[@]}" 2083 ble/canvas/panel/load-position "$ret" 2084 ble/util/buffer.flush >&2 2085 } 2086 2087 # 2088 #------------------------------------------------------------------------------ 2089 # **** edit **** @edit.content 2090 2091 _ble_edit_VARNAMES=( 2092 _ble_edit_str 2093 _ble_edit_ind 2094 _ble_edit_mark 2095 _ble_edit_mark_active 2096 _ble_edit_overwrite_mode 2097 _ble_edit_line_disabled 2098 _ble_edit_arg 2099 _ble_edit_dirty_draw_beg 2100 _ble_edit_dirty_draw_end 2101 _ble_edit_dirty_draw_end0 2102 _ble_edit_dirty_syntax_beg 2103 _ble_edit_dirty_syntax_end 2104 _ble_edit_dirty_syntax_end0 2105 _ble_edit_dirty_observer 2106 _ble_edit_kill_index 2107 _ble_edit_kill_ring 2108 _ble_edit_kill_type) 2109 2110 # 現在の編集状態は以下の変数で表現される 2111 _ble_edit_str= 2112 _ble_edit_ind=0 2113 _ble_edit_mark=0 2114 _ble_edit_mark_active= 2115 _ble_edit_overwrite_mode= 2116 _ble_edit_line_disabled= 2117 _ble_edit_arg= 2118 2119 # 以下は複数の編集文字列が合ったとして全体で共有して良いもの 2120 _ble_edit_kill_index=0 2121 _ble_edit_kill_ring=() 2122 _ble_edit_kill_type=() 2123 2124 # _ble_edit_str は以下の関数を通して変更する。 2125 # 変更範囲を追跡する為。 2126 function ble-edit/content/replace { 2127 local beg=$1 end=$2 2128 local ins=$3 reason=${4:-edit} 2129 2130 # cf. Note#1 2131 _ble_edit_str="${_ble_edit_str::beg}""$ins""${_ble_edit_str:end}" 2132 ble-edit/content/.update-dirty-range "$beg" "$((beg+${#ins}))" "$end" "$reason" 2133 #%if !release 2134 # Note: 何処かのバグで _ble_edit_ind に変な値が入ってエラーになるので、 2135 # ここで誤り訂正を行う。想定として、この関数を呼出した時の _ble_edit_ind の値は、 2136 # replace を実行する前の値とする。この関数の呼び出し元では、 2137 # _ble_edit_ind の更新はこの関数の呼び出しより後で行う様にする必要がある。 2138 # Note: このバグは恐らく #D0411 で解決したが暫く様子見する。 2139 ble/util/assert \ 2140 '((0<=_ble_edit_dirty_syntax_beg&&_ble_edit_dirty_syntax_end<=${#_ble_edit_str}))' \ 2141 "0 <= beg=$_ble_edit_dirty_syntax_beg <= end=$_ble_edit_dirty_syntax_end <= len=${#_ble_edit_str}; beg=$beg, end=$end, ins(${#ins})=$ins" || 2142 { 2143 _ble_edit_dirty_syntax_beg=0 2144 _ble_edit_dirty_syntax_end=${#_ble_edit_str} 2145 _ble_edit_dirty_syntax_end0=0 2146 local olen=$((${#_ble_edit_str}-${#ins}+end-beg)) 2147 ((olen<0&&(olen=0), 2148 _ble_edit_ind>olen&&(_ble_edit_ind=olen), 2149 _ble_edit_mark>olen&&(_ble_edit_mark=olen))) 2150 } 2151 #%end 2152 } 2153 function ble-edit/content/reset { 2154 local str=$1 reason=${2:-edit} 2155 local beg=0 end=${#str} end0=${#_ble_edit_str} 2156 _ble_edit_str=$str 2157 ble-edit/content/.update-dirty-range "$beg" "$end" "$end0" "$reason" 2158 #%if !release 2159 ble/util/assert \ 2160 '((0<=_ble_edit_dirty_syntax_beg&&_ble_edit_dirty_syntax_end<=${#_ble_edit_str}))' \ 2161 "0 <= beg=$_ble_edit_dirty_syntax_beg <= end=$_ble_edit_dirty_syntax_end <= len=${#_ble_edit_str}; str(${#str})=$str" || 2162 { 2163 _ble_edit_dirty_syntax_beg=0 2164 _ble_edit_dirty_syntax_end=${#_ble_edit_str} 2165 _ble_edit_dirty_syntax_end0=0 2166 } 2167 #%end 2168 } 2169 function ble-edit/content/reset-and-check-dirty { 2170 local str=$1 reason=${2:-edit} 2171 [[ $_ble_edit_str == "$str" ]] && return 0 2172 2173 local ret pref suff 2174 ble/string#common-prefix "$_ble_edit_str" "$str"; pref=$ret 2175 local dmin=${#pref} 2176 ble/string#common-suffix "${_ble_edit_str:dmin}" "${str:dmin}"; suff=$ret 2177 local dmax0=$((${#_ble_edit_str}-${#suff})) dmax=$((${#str}-${#suff})) 2178 2179 _ble_edit_str=$str 2180 ble-edit/content/.update-dirty-range "$dmin" "$dmax" "$dmax0" "$reason" 2181 } 2182 ## @fn ble-edit/content/replace-limited beg end insert opts 2183 ## bleopt_line_limit_type の制限をかけて挿入を行います。 2184 ## 実際に挿入された文字列は insert に格納されます。 2185 ## 2186 ## @param[in] beg end insert 2187 ## @param[in] opts 2188 ## nobell ... 何も挿入・削除がない時に bell を鳴らしません。 2189 ## 2190 ## @var[out] insert 2191 ## 2192 function ble-edit/content/replace-limited { 2193 insert=$3 2194 if [[ $bleopt_line_limit_type == discard ]]; then 2195 local ibeg=$1 iend=$2 opts=:$4: 2196 local limit=$((bleopt_line_limit_length)) 2197 if ((limit)); then 2198 local inslimit=$((limit-${#_ble_edit_str}+(iend-ibeg))) 2199 ((inslimit<iend-ibeg&&(inslimit=iend-ibeg))) 2200 ((${#insert}>inslimit)) && insert=${insert::inslimit} 2201 if [[ ! $insert ]] && ((ibeg==iend)); then 2202 [[ $opts == *:nobell:* ]] || 2203 ble/widget/.bell "ble: reached line_limit_length=$limit" 2204 return 1 2205 fi 2206 fi 2207 fi 2208 ble-edit/content/replace "$1" "$2" "$insert" 2209 } 2210 function ble-edit/content/check-limit { 2211 local opts=:${1:-truncate:editor}: 2212 if [[ $opts == *:${bleopt_line_limit_type:-none}:* ]]; then 2213 local limit=$((bleopt_line_limit_length)) 2214 if ((limit>0&&${#_ble_edit_str}>limit)); then 2215 local ble_edit_line_limit=$limit 2216 ble-decode-key "$_ble_decode_KCODE_LINE_LIMIT" 2217 fi 2218 fi 2219 } 2220 function ble/widget/__line_limit__ { 2221 local editor=ble/widget/${1:-edit-and-execute-command.impl} 2222 local limit=$ble_edit_line_limit 2223 case ${bleopt_line_limit_type:-none} in 2224 (editor) 2225 local content=$_ble_edit_str 2226 ble-edit/content/reset "# reached line_limit_length=$limit" 2227 _ble_edit_ind=0 _ble_edit_mark=0 2228 "$editor" "$content" 2229 (($?==127)) && 2230 ble-edit/content/reset "${content::limit}" 2231 return 1 ;; 2232 (truncate|*) 2233 ble-edit/content/replace "$limit" "${#_ble_edit_str}" '' 2234 ((_ble_edit_ind>limit&&(_ble_edit_ind=limit))) 2235 ((_ble_edit_mark>limit&&(_ble_edit_mark=limit))) 2236 return 1 ;; 2237 esac 2238 return 0 2239 } 2240 2241 _ble_edit_dirty_draw_beg=-1 2242 _ble_edit_dirty_draw_end=-1 2243 _ble_edit_dirty_draw_end0=-1 2244 2245 _ble_edit_dirty_syntax_beg=0 2246 _ble_edit_dirty_syntax_end=0 2247 _ble_edit_dirty_syntax_end0=1 2248 2249 _ble_edit_dirty_observer=() 2250 ## @fn ble-edit/content/.update-dirty-range beg end end0 [reason] 2251 ## @param[in] beg end end0 2252 ## 変更範囲を指定します。 2253 ## @param[in] reason 2254 ## 変更の理由を表す文字列を指定します。 2255 function ble-edit/content/.update-dirty-range { 2256 ble/dirty-range#update --prefix=_ble_edit_dirty_draw_ "${@:1:3}" 2257 ble/dirty-range#update --prefix=_ble_edit_dirty_syntax_ "${@:1:3}" 2258 ble/textmap#update-dirty-range "${@:1:3}" 2259 2260 local obs 2261 for obs in "${_ble_edit_dirty_observer[@]}"; do "$obs" "$@"; done 2262 } 2263 2264 function ble-edit/content/update-syntax { 2265 if ble/util/import/is-loaded "$_ble_base/lib/core-syntax.sh"; then 2266 local beg end end0 2267 ble/dirty-range#load --prefix=_ble_edit_dirty_syntax_ 2268 if ((beg>=0)); then 2269 ble/dirty-range#clear --prefix=_ble_edit_dirty_syntax_ 2270 ble/syntax/parse "$_ble_edit_str" '' "$beg" "$end" "$end0" 2271 fi 2272 fi 2273 } 2274 2275 ## @fn ble-edit/content/bolp 2276 ## 現在カーソルが行末に位置しているかどうかを判定します。 2277 function ble-edit/content/eolp { 2278 local pos=${1:-$_ble_edit_ind} 2279 ((pos==${#_ble_edit_str})) || [[ ${_ble_edit_str:pos:1} == $'\n' ]] 2280 } 2281 ## @fn ble-edit/content/bolp 2282 ## 現在カーソルが行頭に位置しているかどうかを判定します。 2283 function ble-edit/content/bolp { 2284 local pos=${1:-$_ble_edit_ind} 2285 ((pos<=0)) || [[ ${_ble_edit_str:pos-1:1} == $'\n' ]] 2286 } 2287 ## @fn ble-edit/content/find-logical-eol [index [offset]] 2288 ## _ble_edit_str 内で位置 index から offset 行だけ次の行の終端位置を返します。 2289 ## 2290 ## @var[out] ret 2291 ## offset が 0 の場合は位置 index を含む行の行末を返します。 2292 ## offset が正で offset 次の行がない場合は ${#_ble_edit_str} を返します。 2293 ## 2294 function ble-edit/content/find-logical-eol { 2295 local index=${1:-$_ble_edit_ind} offset=${2:-0} 2296 if ((offset>0)); then 2297 local text=${_ble_edit_str:index} 2298 local rex=$'^([^\n]*\n){0,'$((offset-1))$'}([^\n]*\n)?[^\n]*' 2299 [[ $text =~ $rex ]] 2300 ((ret=index+${#BASH_REMATCH})) 2301 [[ ${BASH_REMATCH[2]} ]] 2302 elif ((offset<0)); then 2303 local text=${_ble_edit_str::index} 2304 local rex=$'(\n[^\n]*){0,'$((-offset-1))$'}(\n[^\n]*)?$' 2305 [[ $text =~ $rex ]] 2306 if [[ $BASH_REMATCH ]]; then 2307 ((ret=index-${#BASH_REMATCH})) 2308 [[ ${BASH_REMATCH[2]} ]] 2309 else 2310 ble-edit/content/find-logical-eol "$index" 0 2311 return 1 2312 fi 2313 else 2314 local text=${_ble_edit_str:index} 2315 text=${text%%$'\n'*} 2316 ((ret=index+${#text})) 2317 return 0 2318 fi 2319 } 2320 ## @fn ble-edit/content/find-logical-bol [index [offset]] 2321 ## _ble_edit_str 内で位置 index から offset 行だけ次の行の先頭位置を返します。 2322 ## 2323 ## @var[out] ret 2324 ## offset が 0 の場合は位置 index を含む行の行頭を返します。 2325 ## offset が正で offset だけ次の行がない場合は最終行の行頭を返します。 2326 ## 特に次の行がない場合は現在の行頭を返します。 2327 ## 2328 function ble-edit/content/find-logical-bol { 2329 local index=${1:-$_ble_edit_ind} offset=${2:-0} 2330 if ((offset>0)); then 2331 local rex=$'^([^\n]*\n){0,'$((offset-1))$'}([^\n]*\n)?' 2332 [[ ${_ble_edit_str:index} =~ $rex ]] 2333 if [[ $BASH_REMATCH ]]; then 2334 ((ret=index+${#BASH_REMATCH})) 2335 [[ ${BASH_REMATCH[2]} ]] 2336 else 2337 ble-edit/content/find-logical-bol "$index" 0 2338 return 1 2339 fi 2340 elif ((offset<0)); then 2341 ble-edit/content/find-logical-eol "$index" "$offset"; local ext=$? 2342 ble-edit/content/find-logical-bol "$ret" 0 2343 return "$ext" 2344 else 2345 local text=${_ble_edit_str::index} 2346 text=${text##*$'\n'} 2347 ((ret=index-${#text})) 2348 return 0 2349 fi 2350 } 2351 ## @fn ble-edit/content/find-non-space index 2352 ## 指定した位置以降の最初の非空白文字を探します。 2353 ## @param[in] index 2354 ## @var[out] ret 2355 function ble-edit/content/find-non-space { 2356 local bol=$1 2357 local rex=$'^[ \t]*'; [[ ${_ble_edit_str:bol} =~ $rex ]] 2358 ret=$((bol+${#BASH_REMATCH})) 2359 } 2360 2361 2362 ## @fn ble-edit/content/is-single-line 2363 function ble-edit/content/is-single-line { 2364 [[ $_ble_edit_str != *$'\n'* ]] 2365 } 2366 2367 ## @var _ble_edit_arg 2368 ## 入力された引数を保持します。以下の何れかの状態を示します。 2369 ## /^$/ 2370 ## 引数の未入力状態である事を示します。 2371 ## /^\+$/ 2372 ## universal-arugument (M-C-u) 開始直後である事を示します。 2373 ## 次に入力する - または数字を引数として解釈します。 2374 ## /^([0-9]+|-[0-9]*)$/ 2375 ## 引数の入力途中である事を表します。 2376 ## 次に入力する数字を引数として解釈します。 2377 ## /^\+([0-9]+|-[0-9]*)$/ 2378 ## 引数の入力が完了した事を示します。 2379 ## 次に来る数字は引数として解釈しません。 2380 2381 ## @fn ble-edit/content/get-arg 2382 ## @var[out] arg 2383 function ble-edit/content/get-arg { 2384 local default_value=$1 2385 local value=$_ble_edit_arg 2386 _ble_edit_arg= 2387 2388 if [[ $value == +* ]]; then 2389 if [[ $value == + ]]; then 2390 arg=4 2391 return 0 2392 fi 2393 value=${value#+} 2394 fi 2395 2396 if [[ $value == -* ]]; then 2397 if [[ $value == - ]]; then 2398 arg=-1 2399 else 2400 arg=$((-10#0${value#-})) 2401 fi 2402 else 2403 if [[ $value ]]; then 2404 arg=$((10#0$value)) 2405 else 2406 arg=$default_value 2407 fi 2408 fi 2409 } 2410 function ble-edit/content/clear-arg { 2411 _ble_edit_arg= 2412 } 2413 function ble-edit/content/toggle-arg { 2414 if [[ $_ble_edit_arg == + ]]; then 2415 _ble_edit_arg= 2416 elif [[ $_ble_edit_arg && $_ble_edit_arg != +* ]]; then 2417 _ble_edit_arg=+$_ble_edit_arg 2418 else 2419 _ble_edit_arg=+ 2420 fi 2421 } 2422 2423 function ble/keymap:generic/clear-arg { 2424 if [[ $_ble_decode_keymap == vi_[noxs]map ]]; then 2425 ble/keymap:vi/clear-arg 2426 else 2427 ble-edit/content/clear-arg 2428 fi 2429 } 2430 2431 ## @fn ble/widget/append-arg [opts] 2432 ## @fn ble/widget/append-arg-or widget [opts] 2433 ## @param[in] widget 2434 ## @param[in,opt] opts 2435 ## enter-menu 2436 ## 補完 menu が表示されている時、menu に入ってから menu 選択を行います。 2437 ## 修飾なしの数字であっても常に引数として取り扱います。 2438 ## nobell 2439 ## 補完 menu に入った後で対応する項目がなかった時に bell を鳴らしません。 2440 ## 2441 function ble/widget/append-arg-or { 2442 # ble/widget/complete 直後 (menu 表示時) の引数で menu に入る 2443 ble/function#try ble/widget/complete/.select-menu-with-arg "${2-}" && return 0 2444 2445 local n=${#KEYS[@]}; ((n&&n--)) 2446 local code=$((KEYS[n]&_ble_decode_MaskChar)) 2447 ((code==0)) && return 1 2448 local ret; ble/util/c2s "$code"; local ch=$ret 2449 if 2450 if [[ $_ble_edit_arg == + ]]; then 2451 [[ $ch == [-0-9] ]] && _ble_edit_arg= 2452 elif [[ $_ble_edit_arg == +* ]]; then 2453 false 2454 elif [[ $_ble_edit_arg ]]; then 2455 [[ $ch == [0-9] ]] 2456 else 2457 ((KEYS[n]&_ble_decode_MaskFlag)) 2458 fi 2459 then 2460 ble/decode/widget/skip-lastwidget 2461 _ble_edit_arg=$_ble_edit_arg$ch 2462 else 2463 ble/widget/"$1" 2464 fi 2465 } 2466 function ble/widget/append-arg { 2467 ble/widget/append-arg-or self-insert "$@" 2468 } 2469 function ble/widget/universal-arg { 2470 ble/decode/widget/skip-lastwidget 2471 ble-edit/content/toggle-arg 2472 } 2473 2474 ## @fn ble-edit/content/prepend-kill-ring string kill_type 2475 function ble-edit/content/prepend-kill-ring { 2476 _ble_edit_kill_index=0 2477 local otext=${_ble_edit_kill_ring[0]-} ntext=$1 2478 local otype=${_ble_edit_kill_type[0]-} ntype=$2 2479 if [[ $otype == L || $ntype == L ]]; then 2480 ntext=${ntext%$'\n'}$'\n' 2481 otext=${otext%$'\n'}$'\n' 2482 _ble_edit_kill_ring[0]=$ntext$otext 2483 _ble_edit_kill_type[0]=L 2484 elif [[ $otype == B:* ]]; then 2485 if [[ $ntype != B:* ]]; then 2486 ntext=${ntext%$'\n'}$'\n' 2487 local ret; ble/string#count-char "$ntext" $'\n' 2488 ble/string#repeat '0 ' "$ret" 2489 ntype=B:${ret%' '} 2490 fi 2491 _ble_edit_kill_ring[0]=$ntext$otext 2492 _ble_edit_kill_type[0]="B:${ntype#B:} ${otype#B:}" 2493 else 2494 _ble_edit_kill_ring[0]=$ntext$otext 2495 _ble_edit_kill_type[0]=$otype 2496 fi 2497 } 2498 ## @fn ble-edit/content/append-kill-ring string kill_type 2499 function ble-edit/content/append-kill-ring { 2500 _ble_edit_kill_index=0 2501 local otext=${_ble_edit_kill_ring[0]-} ntext=$1 2502 local otype=${_ble_edit_kill_type[0]-} ntype=$2 2503 if [[ $otype == L || $ntype == L ]]; then 2504 ntext=${ntext%$'\n'}$'\n' 2505 otext=${otext%$'\n'}$'\n' 2506 _ble_edit_kill_ring[0]=$otext$ntext 2507 _ble_edit_kill_type[0]=L 2508 elif [[ $otype == B:* ]]; then 2509 if [[ $ntype != B:* ]]; then 2510 ntext=${ntext%$'\n'}$'\n' 2511 local ret; ble/string#count-char "$ntext" $'\n' 2512 ble/string#repeat '0 ' "$ret" 2513 ntype=B:${ret%' '} 2514 fi 2515 _ble_edit_kill_ring[0]=$otext$ntext 2516 _ble_edit_kill_type[0]="B:${otype#B:} ${ntype#B:}" 2517 else 2518 _ble_edit_kill_ring[0]=$otext$ntext 2519 _ble_edit_kill_type[0]=$otype 2520 fi 2521 } 2522 2523 ## @fn ble-edit/content/push-kill-ring string kill_type opts 2524 function ble-edit/content/push-kill-ring { 2525 if ((${#_ble_edit_kill_ring[@]})) && [[ ${LASTWIDGET#ble/widget/} == kill-* || ${LASTWIDGET#ble/widget/} == copy-* ]]; then 2526 local name; ble/string#split-words name "${WIDGET#ble/widget/}" 2527 if [[ $name == kill-backward-* || $name == copy-backward-* ]]; then 2528 ble-edit/content/prepend-kill-ring "$1" "$2" 2529 return "$?" 2530 elif [[ $name != kill-region* && $name != copy-region* ]]; then 2531 ble-edit/content/append-kill-ring "$1" "$2" 2532 return "$?" 2533 fi 2534 fi 2535 2536 _ble_edit_kill_index=0 2537 ble/array#unshift _ble_edit_kill_ring "$1" 2538 ble/array#unshift _ble_edit_kill_type "$2" 2539 } 2540 2541 2542 # 2543 #------------------------------------------------------------------------------ 2544 # **** saved variables such as (PS1/LINENO) **** @edit.ps1 2545 # 2546 # 内部使用変数 2547 ## @var _ble_edit_LINENO 2548 ## LINENO の値を保持します。 2549 ## コマンドラインで処理・キャンセルした行数の合計です。 2550 ## @var _ble_edit_CMD 2551 ## プロンプトで \# として参照される変数です。 2552 ## 実際のコマンド実行の回数を保持します。 2553 ## PS0 の評価後に増加します。 2554 ## @var _ble_edit_PS1 2555 ## @var _ble_edit_IFS 2556 ## @var _ble_edit_IGNOREEOF_adjusted 2557 ## @var _ble_edit_IGNOREEOF 2558 ## @arr _ble_edit_READLINE 2559 2560 _ble_edit_PS1_adjusted= 2561 _ble_edit_PS1='\s-\v\$ ' 2562 _ble_edit_PROMPT_COMMAND= 2563 function ble-edit/adjust-PS1 { 2564 [[ $_ble_edit_PS1_adjusted ]] && return 0 2565 _ble_edit_PS1_adjusted=1 2566 _ble_edit_PS1=$PS1 2567 if [[ $bleopt_internal_suppress_bash_output ]]; then 2568 # Note #D1772: ble.sh の処理中に落ちた場合に表示されるプロンプト。現状でそ 2569 # の様な事が起こった事はない気がするし、実際にそうなった時の動作確認もでき 2570 # ていないが念の為設定しておく。 2571 PS1='[ble: press RET to continue]' 2572 else 2573 # suppress_bash_output をしていない時はそのまま bash のプロンプトが表示され 2574 # てしまわない様に PS1 は空にしておく。 2575 PS1= 2576 fi 2577 2578 if ble/is-array PROMPT_COMMAND; then 2579 ble/idict#copy _ble_edit_PROMPT_COMMAND PROMPT_COMMAND 2580 else 2581 ble/variable#copy-state PROMPT_COMMAND _ble_edit_PROMPT_COMMAND 2582 fi 2583 builtin unset -v PROMPT_COMMAND 2584 } 2585 function ble-edit/restore-PS1 { 2586 [[ $_ble_edit_PS1_adjusted ]] || return 1 2587 _ble_edit_PS1_adjusted= 2588 PS1=$_ble_edit_PS1 2589 if ble/is-array _ble_edit_PROMPT_COMMAND; then 2590 ble/idict#copy PROMPT_COMMAND _ble_edit_PROMPT_COMMAND 2591 else 2592 ble/variable#copy-state _ble_edit_PROMPT_COMMAND PROMPT_COMMAND 2593 fi 2594 } 2595 2596 _ble_edit_IGNOREEOF_adjusted= 2597 _ble_edit_IGNOREEOF= 2598 function ble-edit/adjust-IGNOREEOF { 2599 [[ $_ble_edit_IGNOREEOF_adjusted ]] && return 0 2600 _ble_edit_IGNOREEOF_adjusted=1 2601 2602 if [[ ${IGNOREEOF+set} ]]; then 2603 _ble_edit_IGNOREEOF=$IGNOREEOF 2604 else 2605 builtin unset -v _ble_edit_IGNOREEOF 2606 fi 2607 if ((_ble_bash>=40000)); then 2608 builtin unset -v IGNOREEOF 2609 else 2610 IGNOREEOF=9999 2611 fi 2612 } 2613 function ble-edit/restore-IGNOREEOF { 2614 [[ $_ble_edit_IGNOREEOF_adjusted ]] || return 1 2615 _ble_edit_IGNOREEOF_adjusted= 2616 2617 if [[ ${_ble_edit_IGNOREEOF+set} ]]; then 2618 IGNOREEOF=$_ble_edit_IGNOREEOF 2619 else 2620 builtin unset -v IGNOREEOF 2621 fi 2622 } 2623 2624 _ble_edit_READLINE=() 2625 function ble-edit/adjust-READLINE { 2626 [[ $_ble_edit_READLINE ]] && return 0 2627 _ble_edit_READLINE=1 2628 ble/variable#copy-state READLINE_LINE '_ble_edit_READLINE[1]' 2629 ble/variable#copy-state READLINE_POINT '_ble_edit_READLINE[2]' 2630 ble/variable#copy-state READLINE_MARK '_ble_edit_READLINE[3]' 2631 } 2632 function ble-edit/restore-READLINE { 2633 [[ $_ble_edit_READLINE ]] || return 0 2634 _ble_edit_READLINE= 2635 ble/variable#copy-state '_ble_edit_READLINE[1]' READLINE_LINE 2636 ble/variable#copy-state '_ble_edit_READLINE[2]' READLINE_POINT 2637 ble/variable#copy-state '_ble_edit_READLINE[3]' READLINE_MARK 2638 } 2639 2640 ## @fn ble-edit/eval-IGNOREEOF 2641 ## @var[out] ret 2642 function ble-edit/eval-IGNOREEOF { 2643 local value= 2644 if [[ $_ble_edit_IGNOREEOF_adjusted ]]; then 2645 value=${_ble_edit_IGNOREEOF-0} 2646 else 2647 value=${IGNOREEOF-0} 2648 fi 2649 2650 if [[ $value && ! ${value//[0-9]} ]]; then 2651 # 正の整数は十進数で解釈 2652 ret=$((10#0$value)) 2653 else 2654 # 負の整数、空文字列、その他 2655 ret=10 2656 fi 2657 } 2658 2659 bleopt/declare -n canvas_winch_action redraw-here 2660 2661 function ble-edit/attach/TRAPWINCH { 2662 # 現在前面に出ていなければ関係ない 2663 ((_ble_edit_attached)) && [[ $_ble_term_state == internal ]] && 2664 ! ble/edit/is-command-layout && ! ble/util/is-running-in-subshell || 2665 return 0 2666 ble/application/onwinch 2>&"$_ble_util_fd_stderr" 2667 } 2668 2669 ## called by ble-edit/attach 2670 _ble_edit_attached=0 2671 function ble-edit/attach/.attach { 2672 ((_ble_edit_attached)) && return 0 2673 _ble_edit_attached=1 2674 2675 if [[ ! ${_ble_edit_LINENO+set} ]]; then 2676 _ble_edit_LINENO=${BASH_LINENO[${#BASH_LINENO[@]}-1]} 2677 ((_ble_edit_LINENO<0)) && _ble_edit_LINENO=0 2678 2679 # When _ble_edit_CMD is empty or less than _ble_edit_LINENO, we update it. 2680 ((_ble_edit_CMD<=_ble_edit_LINENO+1)) && ((_ble_edit_CMD=_ble_edit_LINENO+1)) 2681 fi 2682 2683 ble/builtin/trap/install-hook WINCH readline 2684 blehook internal_WINCH!=ble-edit/attach/TRAPWINCH 2685 2686 ble-edit/adjust-PS1 2687 ble-edit/adjust-READLINE 2688 ble-edit/adjust-IGNOREEOF 2689 [[ $bleopt_internal_exec_type == exec ]] && _ble_edit_IFS=$IFS 2690 } 2691 2692 function ble-edit/attach/.detach { 2693 ((!_ble_edit_attached)) && return 0 2694 ble-edit/restore-PS1 2695 ble-edit/restore-READLINE 2696 ble-edit/restore-IGNOREEOF 2697 [[ $bleopt_internal_exec_type == exec ]] && IFS=$_ble_edit_IFS 2698 _ble_edit_attached=0 2699 } 2700 2701 2702 # 2703 #------------------------------------------------------------------------------ 2704 # **** textarea **** @textarea 2705 2706 _ble_textarea_VARNAMES=( 2707 _ble_textarea_buffer 2708 _ble_textarea_bufferName 2709 2710 _ble_textarea_cur 2711 _ble_textarea_panel 2712 _ble_textarea_scroll 2713 _ble_textarea_scroll_new 2714 _ble_textarea_gendx 2715 _ble_textarea_gendy 2716 2717 _ble_textarea_invalidated 2718 _ble_textarea_version 2719 _ble_textarea_caret_state 2720 _ble_textarea_cache 2721 _ble_textarea_render_defer) 2722 2723 _ble_textarea_local_VARNAMES=() 2724 2725 ## @fn ble/textarea#panel::getHeight 2726 ## @var[out] height 2727 function ble/textarea#panel::getHeight { 2728 if [[ $1 == "$_ble_textarea_panel" ]]; then 2729 local min=$((_ble_prompt_ps1_data[4]+1)) max=$((_ble_textmap_endy+1)) 2730 ((min<max&&min++)) 2731 height=$min:$max 2732 else 2733 height=0:${_ble_canvas_panel_height[$1]} 2734 fi 2735 } 2736 function ble/textarea#panel::onHeightChange { 2737 [[ $1 == "$_ble_textarea_panel" ]] || return 1 2738 2739 if [[ ! $ble_textarea_render_flag ]]; then 2740 ble/textarea#invalidate 2741 fi 2742 } 2743 function ble/textarea#panel::invalidate { 2744 if (($1==_ble_textarea_panel)); then 2745 ble/textarea#invalidate 2746 fi 2747 } 2748 function ble/textarea#panel::render { 2749 if (($1==_ble_textarea_panel)); then 2750 ble/textarea#render 2751 fi 2752 } 2753 ## @fn ble/textarea#panel::moveReflowInf ipanel x y 2754 ## (x,y) 以前のこのパネルの内容が端末サイズ変更に伴う text reflowing 後に最低 2755 ## でも何処まで専有するかを文字数で返します。 2756 ## 2757 ## @param[in] x y 2758 ## (端末サイズ変更前の) カーソル位置のパネル左上からの相対位置を指定します。 2759 ## 2760 ## @arr[in] _ble_app_winsize 2761 ## 端末サイズ変更前 (正確には前回 application/render 時) の端末の幅と高さを 2762 ## 保持します。 2763 ## @var[in] LINES COLUMNS 2764 ## 端末サイズ変更後の端末の幅と高さを保持します。 2765 ## @var[ref] nchar 2766 ## このパネルの左上境界の (端末サイズ変更後の) 最小位置を指定します。(端末 2767 ## サイズ変更前の) カーソル位置がこのパネル内にあった時、端末サイズ変更後の 2768 ## カーソルの最小位置を返します。それ以外の時、パネルの右下境界の端末サイズ 2769 ## 変更後の最小位置を返します。 2770 ## 2771 function ble/textarea#panel::moveReflowInf { 2772 local ipanel=$1 x=$2 y=$3 2773 2774 # 右プロンプトが表示されている時は右寄せしている筈なので reflow unsafe である。 2775 [[ $_ble_prompt_rps1_shown ]] && return 1 2776 2777 # プロンプト PS1 が端末の右端に触れている時にも reflow が起こっている可能性が 2778 # あるので、reflow unsafe という事で return 1 で抜ける。 2779 ((_ble_prompt_ps1_bbox[2]>=_ble_app_winsize[0])) && return 1 2780 2781 local height=${_ble_canvas_panel_height[ipanel]} 2782 local proy=${_ble_prompt_ps1_data[4]} 2783 2784 # Note: 現在の実装ではプロンプト以降の実際にコマンドを入力している部分につい 2785 # て改行があるか自動折り返しが起こっているかについては分からないとして、安全 2786 # 側に倒して reflow する想定にしている。実際に改行があったとしても、編集過程 2787 # で折り返しが一度でも起こっていると端末の reflow が発生する可能性を排除でき 2788 # ないし、ECH 等の欠如によって空白埋めしている場合にも reflow が起こっている 2789 # 可能性がある。等の理由でやはり分からない。 2790 2791 local newline= reflow= offset= 2792 if ((y<=proy)); then 2793 # もしプロンプト最終行またはプロンプトの内部 (プロンプトの内部にカーソルい 2794 # る事がありうるのか謎だが) に居た時は、reflow が全く起こらない前提で左上か 2795 # らの相対位置が保持されると見做す。 2796 ((newline=y,reflow=0,offset=x)) 2797 elif ((y<height)); then 2798 # プロンプトの内部にカーソルいる事がありうるのか謎だがもし内部に居た時は 2799 # reflow が起こらない前提で左上からの相対位置が保持されると見做す。 2800 ((newline=proy,reflow=y-proy,offset=x)) 2801 else 2802 # カーソルがこのパネルの中にない場合は単にこのパネルの proy 行は改行があっ 2803 # て、それ以降は reflow で潰れうると考える。 2804 ((newline=proy,reflow=height-proy,offset=0)) 2805 fi 2806 ((newline)) && ((nchar=(nchar/COLUMNS+newline)*COLUMNS)) 2807 ((nchar+=reflow*(_ble_app_winsize[0]-1)+offset)) 2808 2809 return 0 2810 } 2811 2812 # **** textarea.buffer **** @textarea.buffer 2813 2814 _ble_textarea_buffer=() 2815 _ble_textarea_bufferName= 2816 2817 ## @fn lc lg; ble/textarea#update-text-buffer; cx cy lc lg 2818 ## 2819 ## @param[in ] text 編集文字列 2820 ## @var [in,out] umin umax 2821 ## umin,umax は再描画の必要な範囲を文字インデックスで返します。 2822 ## 2823 ## @var[in] _ble_textmap_* 2824 ## 配置情報が最新であることを要求します。 2825 ## 2826 function ble/textarea#update-text-buffer { 2827 local iN=${#text} 2828 2829 local beg end end0 2830 ble/dirty-range#load --prefix=_ble_edit_dirty_draw_ 2831 ble/dirty-range#clear --prefix=_ble_edit_dirty_draw_ 2832 2833 # highlight -> HIGHLIGHT_BUFF 2834 local HIGHLIGHT_BUFF HIGHLIGHT_UMIN HIGHLIGHT_UMAX 2835 ble/highlight/layer/update "$text" '' "$beg" "$end" "$end0" 2836 ble/urange#update "$HIGHLIGHT_UMIN" "$HIGHLIGHT_UMAX" 2837 2838 # 変更文字の適用 2839 if ((${#_ble_textmap_ichg[@]})); then 2840 local ichg g ret 2841 builtin eval "_ble_textarea_buffer=(\"\${$HIGHLIGHT_BUFF[@]}\")" 2842 HIGHLIGHT_BUFF=_ble_textarea_buffer 2843 for ichg in "${_ble_textmap_ichg[@]}"; do 2844 ble/highlight/layer/getg "$ichg" 2845 ble/color/g2sgr "$g" 2846 _ble_textarea_buffer[ichg]=$ret${_ble_textmap_glyph[ichg]} 2847 done 2848 fi 2849 2850 _ble_textarea_bufferName=$HIGHLIGHT_BUFF 2851 } 2852 ## @fn ble/textarea#update-left-char index 2853 ## update lc, lg. 2854 ## 2855 ## @param[in] index 2856 ## カーソルの index 2857 ## @param[out] lc lg 2858 ## カーソル左の文字のコードと gflag を返します。 2859 ## カーソルが先頭にある場合は、編集文字列開始位置の左(プロンプトの最後の文字)について記述します。 2860 ## 2861 ## lc, lg は bleopt_internal_suppress_bash_output= の時に bash に出力させる文字と 2862 ## その属性を表す。READLINE_LINE が空だと C-d を押した時にその場でログアウト 2863 ## してしまったり、エラーメッセージが表示されたりする。その為 READLINE_LINE 2864 ## に有限の長さの文字列を設定したいが、そうするとそれが画面に出てしまう。 2865 ## そこで、ble.sh では現在のカーソル位置にある文字と同じ文字を READLINE_LINE 2866 ## に設定する事で、bash が文字を出力しても見た目に問題がない様にしている。 2867 ## 2868 ## cx==0 の時には現在のカーソル位置の右にある文字を READLINE_LINE に設定し 2869 ## READLINE_POINT=0 とする。cx>0 の時には現在のカーソル位置の左にある文字を 2870 ## READLINE_LINE に設定し READLINE_POINT=(左の文字のバイト数) とする。 2871 ## (READLINE_POINT は文字数ではなくバイトオフセットである事に注意する。) 2872 ## 2873 function ble/textarea#update-left-char { 2874 local index=$1 2875 if [[ $bleopt_internal_suppress_bash_output ]]; then 2876 lc=32 lg=0 2877 return 0 2878 fi 2879 2880 # index==0 の場合はプロンプトの右端に於ける値 2881 if ((index==0)); then 2882 lc=${_ble_prompt_ps1_data[6]} 2883 lg=${_ble_prompt_ps1_data[7]} 2884 return 0 2885 fi 2886 2887 local cx cy 2888 ble/textmap#getxy.cur --prefix=c "$index" 2889 2890 local lcs ret 2891 if ((cx==0)); then 2892 # 次の文字 2893 if ((index==iN)); then 2894 # 次の文字がない時は空白 2895 ret=32 2896 else 2897 lcs=${_ble_textmap_glyph[index]} 2898 ble/util/s2c "$lcs" 2899 fi 2900 2901 # 次が改行の時は空白にする 2902 local g; ble/highlight/layer/getg "$index"; lg=$g 2903 ((lc=ret==10?32:ret)) 2904 else 2905 # 前の文字 2906 lcs=${_ble_textmap_glyph[index-1]} 2907 ble/util/s2c "${lcs:${#lcs}-1}" 2908 local g; ble/highlight/layer/getg "$((index-1))"; lg=$g 2909 ((lc=ret)) 2910 fi 2911 } 2912 ## @fn ble/textarea#slice-text-buffer [beg [end]] 2913 ## @var[out] ret 2914 function ble/textarea#slice-text-buffer { 2915 ble/textmap#assert-up-to-date 2916 local iN=$_ble_textmap_length 2917 local i1=${1:-0} i2=${2:-$iN} 2918 ((i1<0&&(i1+=iN,i1<0&&(i1=0)), 2919 i2<0&&(i2+=iN))) 2920 if ((i1<i2&&i1<iN)); then 2921 local g 2922 ble/highlight/layer/getg "$i1" 2923 ble/color/g2sgr "$g" 2924 IFS= builtin eval "ret=\"\$ret\${$_ble_textarea_bufferName[*]:i1:i2-i1}\"" 2925 2926 if [[ $_ble_textarea_bufferName == _ble_textarea_buffer ]]; then 2927 # Note #D1745: 自動折返し改行は \r で符号化されている。末尾及び \n 直前の 2928 # 自動折返し (\r) は \n に変換し、それ以外の \r は削除する。 2929 local out= rex_nl='^(\[[ -?]*[@-~]|[ -/]+[@-~]|[])*'$_ble_term_nl 2930 while [[ $ret == *"$_ble_term_cr"* ]]; do 2931 out=$out${ret%%"$_ble_term_cr"*} 2932 ret=${ret#*"$_ble_term_cr"} 2933 if [[ $ret =~ $rex_nl ]]; then 2934 # 次の本物の改行がある場合には二重改行として表示する為に改行を挿入。 2935 out=$out$_ble_term_nl 2936 elif [[ ! $ret ]]; then 2937 # 末尾に自動折返しがある時、本当の末尾にいる時には空白で強制的に自動 2938 # 折返しを起こした後に空白を削除する。それ以外の時は、明示的改行に置 2939 # 換する。これにより行が寸断されてしまうが、こうしないと端末の座標計 2940 # 算が壊れるので仕方がない。 2941 if ((i2==iN)); then 2942 out=$out' '$_ble_term_cr${_ble_term_ech//'%d'/1} 2943 else 2944 out=$out$_ble_term_nl 2945 fi 2946 fi 2947 done 2948 ret=$out$ret 2949 fi 2950 else 2951 ret= 2952 fi 2953 } 2954 2955 # 2956 # **** textarea.render **** @textarea.render 2957 2958 # 2959 # 大域変数 2960 # 2961 2962 ## @arr _ble_textarea_cur 2963 ## キャレット位置 (ユーザに対して呈示するカーソル) と其処の文字の情報を保持します。 2964 ## _ble_textarea_cur[0] x キャレット描画位置の y 座標を保持します。 2965 ## _ble_textarea_cur[1] y キャレット描画位置の y 座標を保持します。 2966 ## _ble_textarea_cur[2] lc 2967 ## キャレット位置の左側の文字の文字コードを整数で保持します。 2968 ## キャレットが最も左の列にある場合は右側の文字を保持します。 2969 ## _ble_textarea_cur[3] lg 2970 ## キャレット位置の左側の SGR フラグを保持します。 2971 ## キャレットが最も左の列にある場合は右側の文字に適用される SGR フラグを保持します。 2972 _ble_textarea_cur=(0 0 32 0) 2973 2974 _ble_textarea_panel=0 2975 _ble_textarea_scroll= 2976 _ble_textarea_scroll_new= 2977 _ble_textarea_gendx=0 2978 _ble_textarea_gendy=0 2979 2980 # 2981 # 表示関数 2982 # 2983 2984 ## @var _ble_textarea_invalidated 2985 ## 完全再描画 (プロンプトも含めた) を要求されたことを記録します。 2986 ## 完全再描画の要求前に空文字列で、要求後に 1 の値を持ちます。 2987 _ble_textarea_invalidated=1 2988 2989 function ble/textarea#invalidate { 2990 if [[ $1 == str || $1 == partial ]]; then 2991 ((_ble_textarea_version++)) 2992 else 2993 _ble_textarea_invalidated=1 2994 fi 2995 return 0 2996 } 2997 2998 ## @fn ble/textarea#render/.erase-forward-line.draw opts 2999 ## @var[in] x cols 3000 ## @var[out] DRAW_BUFF 3001 function ble/textarea#render/.erase-forward-line.draw { 3002 local eraser=$_ble_term_sgr0$_ble_term_el 3003 if [[ :$render_opts: == *:relative:* ]]; then 3004 local width=$((cols-x)) 3005 if ((width==0)); then 3006 eraser= 3007 elif [[ $_ble_term_ech ]]; then 3008 eraser=$_ble_term_sgr0${_ble_term_ech//'%d'/$width} 3009 else 3010 ble/string#reserve-prototype "$width" 3011 eraser=$_ble_term_sgr0${_ble_string_prototype::width}${_ble_term_cub//'%d'/$width} 3012 fi 3013 fi 3014 ble/canvas/put.draw "$eraser" 3015 } 3016 3017 ## @fn ble/textarea#render/.determine-scroll 3018 ## 新しい表示高さとスクロール位置を決定します。 3019 ## ble/textarea#render から呼び出されることを想定します。 3020 ## 3021 ## @var[in,out] scroll 3022 ## 現在のスクロール量を指定します。調整後のスクロール量を指定します。 3023 ## @var[in,out] height 3024 ## 現在の表示高さを指定します。再配置後の表示高さを返します。 3025 ## @var[in,out] umin umax 3026 ## 描画範囲を表示領域に制限して返します。 3027 ## @var[out] DRAW_BUFF 3028 ## 3029 ## @var[in] cols 3030 ## @var[in] begx begy endx endy cx cy 3031 ## それぞれ編集文字列の先端・末端・現在カーソル位置の表示座標を指定します。 3032 ## 3033 function ble/textarea#render/.determine-scroll { 3034 local nline=$((endy+1)) 3035 3036 # panel の高さを要求。この後 height <= nline になる筈。 3037 if ((height!=nline)); then 3038 ble/canvas/panel/reallocate-height.draw 3039 height=${_ble_canvas_panel_height[_ble_textarea_panel]} 3040 fi 3041 3042 if ((height<nline)); then 3043 ((scroll<=nline-height)) || ((scroll=nline-height)) 3044 3045 local rheight=$((height-begy)) rnline=$((nline-begy)) rcy=$((cy-begy)) 3046 local margin=$((rheight>=6&&rnline>rheight+2?2:1)) 3047 local smin smax 3048 ((smin=rcy-rheight+margin, 3049 smin>nline-height&&(smin=nline-height), 3050 smax=rcy-margin, 3051 smax<0&&(smax=0))) 3052 if ((scroll>smax)); then 3053 scroll=$smax 3054 elif ((scroll<smin)); then 3055 scroll=$smin 3056 fi 3057 3058 # [umin, umax] を表示範囲で制限する。 3059 # 3060 # Note: scroll == 0 の時は表示1行目から表示する。 3061 # scroll > 0 の時は表示1行目には ... だけを表示し、 3062 # 表示2行目から表示する。 3063 # 3064 local wmin=0 wmax index 3065 if ((scroll)); then 3066 ble/textmap#get-index-at 0 "$((scroll+begy+1))"; wmin=$index 3067 fi 3068 ble/textmap#get-index-at "$cols" "$((scroll+height-1))"; wmax=$index 3069 ((umin<umax)) && 3070 ((umin<wmin&&(umin=wmin), 3071 umax>wmax&&(umax=wmax))) 3072 else 3073 # Note: height == nline の筈 3074 scroll= 3075 if ! ble/util/assert '((height==nline))'; then 3076 ble/canvas/panel#set-height.draw "$_ble_textarea_panel" "$nline" 3077 height=$nline 3078 fi 3079 fi 3080 } 3081 ## @fn ble/textarea#render/.perform-scroll new_scroll 3082 ## 3083 ## @var[out] DRAW_BUFF 3084 ## スクロールを実行するシーケンスの出力先です。 3085 ## 3086 ## @var[in] height cols render_opts 3087 ## @var[in] begx begy 3088 ## 3089 function ble/textarea#render/.perform-scroll { 3090 local new_scroll=$1 3091 if ((new_scroll!=_ble_textarea_scroll)); then 3092 local scry=$((begy+1)) 3093 local scrh=$((height-scry)) 3094 3095 # 行の削除と挿入および新しい領域 [fmin, fmax] の決定 3096 local fmin fmax index 3097 if ((_ble_textarea_scroll>new_scroll)); then 3098 local shift=$((_ble_textarea_scroll-new_scroll)) 3099 local draw_shift=$((shift<scrh?shift:scrh)) 3100 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 "$((height-draw_shift))" 3101 ble/canvas/put-dl.draw "$draw_shift" panel 3102 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 "$scry" 3103 ble/canvas/put-il.draw "$draw_shift" panel 3104 3105 if ((new_scroll==0)); then 3106 fmin=0 3107 else 3108 ble/textmap#get-index-at 0 "$((scry+new_scroll))"; fmin=$index 3109 fi 3110 ble/textmap#get-index-at "$cols" "$((scry+new_scroll+draw_shift-1))"; fmax=$index 3111 else 3112 local shift=$((new_scroll-_ble_textarea_scroll)) 3113 local draw_shift=$((shift<scrh?shift:scrh)) 3114 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 "$scry" 3115 ble/canvas/put-dl.draw "$draw_shift" panel 3116 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 "$((height-draw_shift))" 3117 ble/canvas/put-il.draw "$draw_shift" panel 3118 3119 ble/textmap#get-index-at 0 "$((new_scroll+height-draw_shift))"; fmin=$index 3120 ble/textmap#get-index-at "$cols" "$((new_scroll+height-1))"; fmax=$index 3121 fi 3122 3123 # 新しく現れた範囲 [fmin, fmax] を埋める 3124 if ((fmin<fmax)); then 3125 local fmaxx fmaxy fminx fminy 3126 ble/textmap#getxy.out --prefix=fmin "$fmin" 3127 ble/textmap#getxy.out --prefix=fmax "$fmax" 3128 3129 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$fminx" "$((fminy-new_scroll))" 3130 ((new_scroll==0)) && 3131 x=$fminx ble/textarea#render/.erase-forward-line.draw # ... を消す 3132 local ret; ble/textarea#slice-text-buffer "$fmin" "$fmax" 3133 ble/canvas/put.draw "$ret" 3134 ((_ble_canvas_x=fmaxx, 3135 _ble_canvas_y+=fmaxy-fminy)) 3136 3137 ((umin<umax)) && 3138 ((fmin<=umin&&umin<fmax&&(umin=fmax), 3139 fmin<umax&&umax<=fmax&&(umax=fmin))) 3140 fi 3141 3142 _ble_textarea_scroll=$new_scroll 3143 3144 ble/textarea#render/.show-scroll-at-first-line 3145 fi 3146 } 3147 ## @fn ble/textarea#render/.show-scroll-at-first-line 3148 ## スクロール時 "(line 3) ..." などの表示 3149 ## 3150 ## @var[in] _ble_textarea_scroll 3151 ## @var[in] cols render_opts 3152 ## @var[in,out] DRAW_BUFF _ble_canvas_x _ble_canvas_y 3153 ## 3154 function ble/textarea#render/.show-scroll-at-first-line { 3155 if ((_ble_textarea_scroll!=0)); then 3156 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$begx" "$begy" 3157 local scroll_status="(line $((_ble_textarea_scroll+2))) ..." 3158 scroll_status=${scroll_status::cols-1-begx} 3159 x=$begx ble/textarea#render/.erase-forward-line.draw 3160 ble/canvas/put.draw "$eraser$_ble_term_bold$scroll_status$_ble_term_sgr0" 3161 ((_ble_canvas_x+=${#scroll_status})) 3162 fi 3163 } 3164 3165 ## @fn ble/textarea#render/.erase-rprompt 3166 ## @var[in] cols 3167 ## rps1 の幅の分だけ減少させた後の cols を指定します。 3168 function ble/textarea#render/.erase-rprompt { 3169 [[ $_ble_prompt_rps1_shown ]] || return 0 3170 _ble_prompt_rps1_shown= 3171 local rps1_height=${_ble_prompt_rps1_gbox[3]} 3172 local -a DRAW_BUFF=() 3173 local y=0 3174 for ((y=0;y<rps1_height;y++)); do 3175 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$((cols+1))" "$y" sgr0 3176 ble/canvas/put.draw "$_ble_term_el" 3177 done 3178 ble/canvas/bflush.draw 3179 } 3180 ## @fn ble/textarea#render/.cleanup-trailing-spaces-after-newline 3181 ## rps1_transient の時に、次の行に行く前に行末の無駄な空白を削除します。 3182 ## @var[in] text 3183 ## @var[in] _ble_textmap_pos 3184 ## @var[out] DRAW_BUFF 3185 function ble/textarea#render/.cleanup-trailing-spaces-after-newline { 3186 local -a buffer; ble/string#split-lines buffer "$text" 3187 local line index=0 pos 3188 for line in "${buffer[@]}"; do 3189 ((index+=${#line})) 3190 ble/string#split-words pos "${_ble_textmap_pos[index]}" 3191 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "${pos[0]}" "${pos[1]}" sgr0 3192 ble/canvas/put.draw "$_ble_term_el" 3193 ((index++)) 3194 done 3195 _ble_prompt_rps1_shown= 3196 } 3197 3198 ## @fn ble/textarea#render/.show-control-string prefix [force] 3199 function ble/textarea#render/.show-control-string { 3200 local ref_dirty=${1}_dirty ref_output=${1}_data[10] force=$2 3201 [[ $force || ${!ref_dirty} ]] || return 0 3202 ble/canvas/put.draw "${!ref_output}" 3203 builtin eval -- "$ref_dirty=" 3204 return 0 3205 } 3206 ## @fn ble/textarea#render/.show-prompt [force] 3207 function ble/textarea#render/.show-prompt { 3208 [[ $1 || $_ble_prompt_ps1_dirty ]] || return 0 3209 local esc=${_ble_prompt_ps1_data[8]} 3210 local prox=${_ble_prompt_ps1_data[3]} 3211 local proy=${_ble_prompt_ps1_data[4]} 3212 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 3213 ble/canvas/panel#put.draw "$_ble_textarea_panel" "$esc" "$prox" "$proy" 3214 _ble_prompt_ps1_dirty= 3215 } 3216 ## @fn ble/textarea#render/.show-rprompt [force] 3217 ## @var[in] cols 3218 function ble/textarea#render/.show-rprompt { 3219 [[ $1 || $_ble_prompt_rps1_dirty ]] || return 0 3220 local rps1out=${_ble_prompt_rps1_data[8]}$_ble_term_sgr0$_ble_term_cr 3221 local rps1x=0 3222 local rps1y=${_ble_prompt_rps1_data[4]} 3223 # Note: cols は画面右端ではなく textmap の右端 3224 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 0 3225 ble/canvas/panel#put.draw "$_ble_textarea_panel" "$rps1out" "$rps1x" "$rps1y" 3226 _ble_prompt_rps1_dirty= 3227 _ble_prompt_rps1_shown=1 3228 } 3229 3230 ## @fn ble/textarea#focus 3231 ## プロンプト・編集文字列の現在位置に端末のカーソルを移動します。 3232 function ble/textarea#focus { 3233 local -a DRAW_BUFF=() 3234 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "${_ble_textarea_cur[0]}" "${_ble_textarea_cur[1]}" 3235 ble/canvas/bflush.draw 3236 } 3237 3238 ## @fn ble/textarea#render opts 3239 ## プロンプト・編集文字列の表示更新を ble/util/buffer に対して行う。 3240 ## Post-condition: カーソル位置 (x y) = (_ble_textarea_cur[0] _ble_textarea_cur[1]) に移動する 3241 ## Post-condition: 編集文字列部分の再描画を実行する 3242 ## 3243 ## @param[in] opts 3244 ## leave 3245 ## bleopt prompt_rps1_transient が非空文字列の時、rps1 を消去します。 3246 ## update 3247 ## 強制的に再描画します。例えば非同期の着色を更新する時に用います。 3248 ## 3249 ## @var _ble_textarea_caret_state := inds ':' mark ':' mark_active ':' line_disabled ':' overwrite_mode 3250 ## ble/textarea#render で用いる変数です。 3251 ## 現在の表示内容のカーソル位置・ポイント位置の情報を記録します。 3252 ## 3253 _ble_textarea_caret_state=:: 3254 _ble_textarea_version=0 3255 function ble/textarea#render { 3256 local opts=$1 3257 local ble_textarea_render_flag=1 # ble/textarea#panel::onHeightChange から参照する 3258 local caret_state=$_ble_textarea_version:$_ble_edit_ind:$_ble_edit_mark:$_ble_edit_mark_active:$_ble_edit_line_disabled:$_ble_edit_overwrite_mode 3259 3260 local dirty= 3261 if ble/prompt/update "check-dirty:$opts"; then 3262 dirty=1 3263 elif ((_ble_edit_dirty_draw_beg>=0)); then 3264 dirty=1 3265 elif [[ $_ble_textarea_invalidated ]]; then 3266 dirty=1 3267 elif [[ $_ble_textarea_caret_state != "$caret_state" ]]; then 3268 dirty=1 3269 elif [[ $_ble_textarea_scroll != "$_ble_textarea_scroll_new" ]]; then 3270 dirty=1 3271 elif [[ :$opts: == *:leave:* || :$opts: == *:update:* ]]; then 3272 dirty=1 3273 fi 3274 3275 if [[ ! $dirty ]]; then 3276 ble/textarea#focus 3277 return 0 3278 fi 3279 3280 #------------------- 3281 # 描画内容の計算 (配置情報、着色文字列) 3282 3283 local cols=${COLUMNS-80} 3284 3285 local subprompt_enabled= 3286 ((_ble_textarea_panel==0)) && subprompt_enabled=1 3287 local rps1_enabled=$_ble_prompt_rps1_enabled 3288 local rps1_width=${_ble_prompt_rps1_data[11]} 3289 if [[ $rps1_enabled ]]; then 3290 ((cols-=rps1_width+1,_ble_term_xenl||cols--)) 3291 if [[ $rps1_enabled == erase ]]; then 3292 ble/textarea#render/.erase-rprompt 3293 rps1_enabled= 3294 fi 3295 fi 3296 3297 # 編集内容の構築 3298 local text=$_ble_edit_str index=$_ble_edit_ind 3299 local iN=${#text} 3300 ((index<0?(index=0):(index>iN&&(index=iN)))) 3301 3302 local umin=-1 umax=-1 3303 local x=${_ble_prompt_ps1_data[3]} 3304 local y=${_ble_prompt_ps1_data[4]} 3305 3306 # 配置情報の更新 3307 local render_opts= 3308 [[ $rps1_enabled ]] && render_opts=relative 3309 COLUMNS=$cols ble/textmap#update "$text" "$render_opts" # [ref] x y 3310 ble/urange#update "$_ble_textmap_umin" "$_ble_textmap_umax" # [ref] umin umax 3311 ble/urange#clear --prefix=_ble_textmap_ 3312 3313 # 着色の更新 3314 local DMIN=$_ble_edit_dirty_draw_beg 3315 ble-edit/content/update-syntax 3316 ble/textarea#update-text-buffer # [in] text index [ref] lc lg; 3317 3318 local lc=32 lg=0 3319 [[ $bleopt_internal_suppress_bash_output ]] || 3320 ble/textarea#update-left-char "$index" 3321 3322 #------------------- 3323 # 描画領域の決定とスクロール 3324 3325 local -a DRAW_BUFF=() 3326 3327 # 1 描画領域の決定 3328 local begx=$_ble_textmap_begx begy=$_ble_textmap_begy 3329 local endx=$_ble_textmap_endx endy=$_ble_textmap_endy 3330 local cx cy 3331 ble/textmap#getxy.cur --prefix=c "$index" # → cx cy 3332 3333 local cols=$_ble_textmap_cols 3334 local height=${_ble_canvas_panel_height[_ble_textarea_panel]} 3335 local scroll=${_ble_textarea_scroll_new:-$_ble_textarea_scroll} 3336 ble/textarea#render/.determine-scroll # update: height scroll umin umax 3337 3338 local gend gendx gendy 3339 if [[ $scroll ]]; then 3340 ble/textmap#get-index-at "$cols" "$((height+scroll-1))"; gend=$index 3341 ble/textmap#getxy.out --prefix=gend "$gend" 3342 ((gendy-=scroll)) 3343 else 3344 gend=$iN gendx=$endx gendy=$endy 3345 fi 3346 _ble_textarea_gendx=$gendx _ble_textarea_gendy=$gendy 3347 3348 #------------------- 3349 # 出力 3350 3351 # 2 表示内容 3352 local ret esc_line= esc_line_set= 3353 if [[ ! $_ble_textarea_invalidated ]]; then 3354 # 部分更新の場合 3355 3356 [[ ! $rps1_enabled && $_ble_prompt_rps1_shown || $rps1_enabled && $_ble_prompt_rps1_dirty ]] && 3357 ble/textarea#render/.cleanup-trailing-spaces-after-newline 3358 3359 # スクロール 3360 ble/textarea#render/.perform-scroll "$scroll" # update: umin umax 3361 _ble_textarea_scroll_new=$_ble_textarea_scroll 3362 3363 # プロンプトに更新があれば表示 3364 [[ $rps1_enabled ]] && ble/textarea#render/.show-rprompt 3365 ble/textarea#render/.show-prompt 3366 if [[ $subprompt_enabled ]]; then 3367 ble/textarea#render/.show-control-string _ble_prompt_xterm_title 3368 ble/textarea#render/.show-control-string _ble_prompt_screen_title 3369 ble/textarea#render/.show-control-string _ble_prompt_term_status 3370 fi 3371 3372 # 編集文字列の一部を描画する場合 3373 if ((umin<umax)); then 3374 local uminx uminy umaxx umaxy 3375 ble/textmap#getxy.out --prefix=umin "$umin" 3376 ble/textmap#getxy.out --prefix=umax "$umax" 3377 3378 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$uminx" "$((uminy-_ble_textarea_scroll))" 3379 ble/textarea#slice-text-buffer "$umin" "$umax" 3380 ble/canvas/panel#put.draw "$_ble_textarea_panel" "$ret" "$umaxx" "$((umaxy-_ble_textarea_scroll))" 3381 fi 3382 3383 if ((DMIN>=0)); then 3384 local endY=$((endy-_ble_textarea_scroll)) 3385 if ((endY<height)); then 3386 if [[ :$render_opts: == *:relative:* ]]; then 3387 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$endx" "$endY" 3388 x=$endx ble/textarea#render/.erase-forward-line.draw 3389 ble/canvas/panel#clear-after.draw "$_ble_textarea_panel" 0 "$((endY+1))" 3390 else 3391 ble/canvas/panel#clear-after.draw "$_ble_textarea_panel" "$endx" "$endY" 3392 fi 3393 fi 3394 fi 3395 else 3396 # 全体更新 3397 ble/canvas/panel#clear.draw "$_ble_textarea_panel" 3398 _ble_prompt_rps1_shown= 3399 3400 # プロンプト描画 3401 [[ $rps1_enabled ]] && ble/textarea#render/.show-rprompt force 3402 ble/textarea#render/.show-prompt force 3403 if [[ $subprompt_enabled ]]; then 3404 ble/textarea#render/.show-control-string _ble_prompt_xterm_title force 3405 ble/textarea#render/.show-control-string _ble_prompt_screen_title force 3406 ble/textarea#render/.show-control-string _ble_prompt_term_status force 3407 fi 3408 3409 # 全体描画 3410 _ble_textarea_scroll=$scroll 3411 _ble_textarea_scroll_new=$_ble_textarea_scroll 3412 if [[ ! $_ble_textarea_scroll ]]; then 3413 ble/textarea#slice-text-buffer # → ret 3414 esc_line=$ret esc_line_set=1 3415 ble/canvas/panel#put.draw "$_ble_textarea_panel" "$ret" "$_ble_textarea_gendx" "$_ble_textarea_gendy" 3416 else 3417 ble/textarea#render/.show-scroll-at-first-line 3418 3419 local gbeg=0 3420 if ((_ble_textarea_scroll)); then 3421 ble/textmap#get-index-at 0 "$((_ble_textarea_scroll+begy+1))"; gbeg=$index 3422 fi 3423 3424 local gbegx gbegy 3425 ble/textmap#getxy.out --prefix=gbeg "$gbeg" 3426 ((gbegy-=_ble_textarea_scroll)) 3427 3428 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$gbegx" "$gbegy" 3429 ((_ble_textarea_scroll==0)) && 3430 x=$gbegx ble/textarea#render/.erase-forward-line.draw # ... を消す 3431 3432 ble/textarea#slice-text-buffer "$gbeg" "$gend" 3433 ble/canvas/panel#put.draw "$_ble_textarea_panel" "$ret" "$_ble_textarea_gendx" "$_ble_textarea_gendy" 3434 fi 3435 fi 3436 3437 # 3 移動 3438 local gcx=$cx gcy=$((cy-_ble_textarea_scroll)) 3439 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$gcx" "$gcy" 3440 ble/canvas/bflush.draw 3441 3442 # 4 後で使う情報の記録 3443 _ble_textarea_cur=("$gcx" "$gcy" "$lc" "$lg") 3444 _ble_textarea_invalidated= _ble_textarea_caret_state=$caret_state 3445 3446 if [[ ! $bleopt_internal_suppress_bash_output ]]; then 3447 if [[ ! $esc_line_set ]]; then 3448 if [[ ! $_ble_textarea_scroll ]]; then 3449 ble/textarea#slice-text-buffer 3450 esc_line=$ret 3451 else 3452 local _ble_canvas_x=$begx _ble_canvas_y=$begy 3453 DRAW_BUFF=() 3454 3455 ble/textarea#render/.show-scroll-at-first-line 3456 3457 local gbeg=0 3458 if ((_ble_textarea_scroll)); then 3459 ble/textmap#get-index-at 0 "$((_ble_textarea_scroll+begy+1))"; gbeg=$index 3460 fi 3461 local gbegx gbegy 3462 ble/textmap#getxy.out --prefix=gbeg "$gbeg" 3463 ((gbegy-=_ble_textarea_scroll)) 3464 3465 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$gbegx" "$gbegy" 3466 ((_ble_textarea_scroll==0)) && 3467 x=$gbegx ble/textarea#render/.erase-forward-line.draw # ... を消す 3468 ble/textarea#slice-text-buffer "$gbeg" "$gend" 3469 ble/canvas/put.draw "$ret" 3470 3471 ble/canvas/sflush.draw -v esc_line 3472 fi 3473 fi 3474 3475 local esc=${_ble_prompt_ps1_data[8]} 3476 esc=${_ble_prompt_xterm_title_data[10]}$esc 3477 esc=${_ble_prompt_screen_title_data[10]}$esc 3478 esc=${_ble_prompt_term_status_data[10]}$esc 3479 _ble_textarea_cache=( 3480 "$esc$esc_line" 3481 "${_ble_textarea_cur[@]}" 3482 "$_ble_textarea_gendx" "$_ble_textarea_gendy") 3483 fi 3484 } 3485 function ble/textarea#redraw { 3486 ble/textarea#invalidate 3487 ble/textarea#render 3488 } 3489 3490 ## @arr _ble_textarea_cache 3491 ## 現在表示している内容のキャッシュです。 3492 ## ble/textarea#render で値が設定されます。 3493 ## ble/textarea#redraw-cache はこの情報を元に再描画を行います。 3494 ## _ble_textarea_cache[0]: 表示内容 3495 ## _ble_textarea_cache[1]: curx カーソル位置 x 3496 ## _ble_textarea_cache[2]: cury カーソル位置 y 3497 ## _ble_textarea_cache[3]: curlc カーソル位置の文字の文字コード 3498 ## _ble_textarea_cache[4]: curlg カーソル位置の文字の SGR フラグ 3499 ## _ble_textarea_cache[5]: gendx 表示末端位置 x 3500 ## _ble_textarea_cache[6]: gendy 表示末端位置 y 3501 _ble_textarea_cache=() 3502 3503 function ble/textarea#redraw-cache { 3504 if [[ ! $_ble_textarea_scroll && ${_ble_textarea_cache[0]+set} ]]; then 3505 local -a d; d=("${_ble_textarea_cache[@]}") 3506 3507 local -a DRAW_BUFF=() 3508 3509 ble/canvas/panel#clear.draw "$_ble_textarea_panel" 3510 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 3511 ble/canvas/put.draw "${d[0]}" 3512 ble/canvas/panel#report-cursor-position "$_ble_textarea_panel" "${d[5]}" "${d[6]}" 3513 _ble_textarea_gendx=${d[5]} 3514 _ble_textarea_gendy=${d[6]} 3515 3516 _ble_textarea_cur=("${d[@]:1:4}") 3517 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "${_ble_textarea_cur[0]}" "${_ble_textarea_cur[1]}" 3518 ble/canvas/bflush.draw 3519 else 3520 ble/textarea#redraw 3521 fi 3522 } 3523 3524 ## @fn ble/textarea#adjust-for-bash-bind 3525 ## プロンプト・編集文字列の表示位置修正を行う。 3526 ## 3527 ## @remarks 3528 ## この関数は bind -x される関数から呼び出される事を想定している。 3529 ## 通常のコマンドとして実行される関数から呼び出す事は想定していない。 3530 ## 内部で PS1= 等の設定を行うのでプロンプトの情報が失われる。 3531 ## また、READLINE_LINE, READLINE_POINT 等のグローバル変数の値を変更する。 3532 ## 3533 ## 2018-03-19 3534 ## どうやら stty -echo の時には READLINE_LINE に値が設定されていても、 3535 ## Bash は何も出力しないという事の様である。 3536 ## 従って、単に FEADLINE_LINE に文字を設定すれば良い。 3537 ## 3538 function ble/textarea#adjust-for-bash-bind { 3539 ble-edit/adjust-PS1 3540 if [[ $bleopt_internal_suppress_bash_output ]]; then 3541 READLINE_LINE=$'\n' READLINE_POINT=0 READLINE_MARK=0 3542 else 3543 # bash が表示するプロンプトを見えなくする 3544 # (現在のカーソルの左側にある文字を再度上書きさせる) 3545 local -a DRAW_BUFF=() 3546 local ret lc=${_ble_textarea_cur[2]} lg=${_ble_textarea_cur[3]} 3547 ble/util/c2s "$lc" 3548 READLINE_LINE=$ret READLINE_MARK=0 3549 if ((_ble_textarea_cur[0]==0)); then 3550 READLINE_POINT=0 3551 else 3552 ble/util/c2w "$lc" 3553 ((ret>0)) && ble/canvas/put-cub.draw "$ret" 3554 ble/util/c2bc "$lc" 3555 READLINE_POINT=$ret 3556 fi 3557 3558 ble/color/g2sgr "$lg" 3559 ble/canvas/put.draw "$ret" 3560 3561 # 2018-03-19 stty -echo の時は Bash は何も出力しないので調整は不要 3562 #ble/canvas/bflush.draw 3563 fi 3564 } 3565 3566 function ble/textarea#save-state { 3567 local prefix=$1 3568 local -a vars=() 3569 3570 # _ble_prompt_ps1_data 3571 ble/array#push vars _ble_edit_PS1 _ble_prompt_ps1_data 3572 3573 # _ble_edit_* 3574 ble/array#push vars "${_ble_edit_VARNAMES[@]}" 3575 3576 # _ble_textmap_* 3577 ble/array#push vars "${_ble_textmap_VARNAMES[@]}" 3578 3579 # _ble_highlight_layer_* 3580 ble/array#push vars _ble_highlight_layer_list 3581 local layer names 3582 for layer in "${_ble_highlight_layer_list[@]}"; do 3583 local _ble_local_script=' 3584 if [[ ${_ble_highlight_layer_LAYER_VARNAMES[@]-} ]]; then 3585 ble/array#push vars "${_ble_highlight_layer_LAYER_VARNAMES[@]}" 3586 else 3587 ble/array#push vars "${!_ble_highlight_layer_LAYER_@}" 3588 fi' 3589 builtin eval -- "${_ble_local_script//LAYER/$layer}" 3590 done 3591 3592 # _ble_textarea_* 3593 ble/array#push vars "${_ble_textarea_VARNAMES[@]}" 3594 3595 # _ble_syntax_* 3596 ble/array#push vars "${_ble_syntax_VARNAMES[@]}" 3597 3598 # user-defined local variables 3599 ble/array#push vars "${_ble_textarea_local_VARNAMES[@]}" 3600 3601 builtin eval -- "${prefix}_VARNAMES=(\"\${vars[@]}\")" 3602 ble/util/save-vars "$prefix" "${vars[@]}" 3603 } 3604 function ble/textarea#restore-state { 3605 local prefix=$1 3606 if builtin eval "[[ \$prefix && \${${prefix}_VARNAMES+set} ]]"; then 3607 builtin eval "ble/util/restore-vars $prefix \"\${${prefix}_VARNAMES[@]}\"" 3608 else 3609 ble/util/print "ble/textarea#restore-state: unknown prefix '$prefix'." >&2 3610 return 1 3611 fi 3612 } 3613 function ble/textarea#clear-state { 3614 local prefix=$1 3615 if [[ $prefix ]]; then 3616 local vars=${prefix}_VARNAMES 3617 builtin eval "builtin unset -v \"\${$vars[@]/#/$prefix}\" $vars" 3618 else 3619 ble/util/print "ble/textarea#restore-state: unknown prefix '$prefix'." >&2 3620 return 1 3621 fi 3622 } 3623 3624 # 非同期更新 3625 3626 _ble_textarea_render_defer= 3627 function ble/textarea#render-defer.idle { 3628 ble/util/idle.wait-user-input 3629 [[ $_ble_textarea_render_defer ]] || return 0 3630 3631 local ble_textarea_render_defer_running=1 3632 ble/util/buffer.flush >&2 3633 _ble_textarea_render_defer= 3634 blehook/invoke textarea_render_defer 3635 ble/textarea#render update 3636 3637 [[ $_ble_textarea_render_defer ]] && 3638 ble/util/idle.continue 3639 return 0 3640 } 3641 ble/function#try ble/util/idle.push-background ble/textarea#render-defer.idle 3642 3643 # 3644 #------------------------------------------------------------------------------ 3645 3646 function ble/widget/.update-textmap { 3647 # rps1 がある時の幅の再現 3648 local cols=${COLUMNS:-80} render_opts= 3649 if [[ $_ble_prompt_rps1_enabled ]]; then 3650 local rps1_width=${_ble_prompt_rps1_data[11]} 3651 render_opts=relative 3652 ((cols-=rps1_width+1,_ble_term_xenl||cols--)) 3653 fi 3654 3655 local x=$_ble_textmap_begx y=$_ble_textmap_begy 3656 COLUMNS=$cols ble/textmap#update "$_ble_edit_str" "$render_opts" 3657 } 3658 function ble/widget/do-lowercase-version { 3659 local n=${#KEYS[@]}; ((n&&n--)) 3660 local flag=$((KEYS[n]&_ble_decode_MaskFlag)) 3661 local char=$((KEYS[n]&_ble_decode_MaskChar)) 3662 if ((65<=char&&char<=90)); then 3663 ble/decode/widget/skip-lastwidget 3664 ble/decode/widget/redispatch-by-keys "$((flag|char+32))" "${KEYS[@]:1}" 3665 else 3666 return 125 3667 fi 3668 } 3669 3670 # 3671 # **** redraw, clear-screen, etc **** @widget.clear 3672 3673 function ble/widget/redraw-line { 3674 ble-edit/content/clear-arg 3675 ble/textarea#invalidate 3676 } 3677 function ble/widget/clear-screen { 3678 ble-edit/content/clear-arg 3679 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 3680 _ble_prompt_trim_opwd= 3681 ble/textarea#invalidate 3682 local -a DRAW_BUFF=() 3683 ble/canvas/panel/goto-top-dock.draw 3684 ble/canvas/bflush.draw 3685 ble/util/buffer "$_ble_term_clear" 3686 _ble_canvas_x=0 _ble_canvas_y=0 3687 ble/term/visible-bell/cancel-erasure 3688 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 3689 } 3690 function ble/widget/clear-display { 3691 ble/util/buffer $'\e[3J' 3692 ble/widget/clear-screen 3693 } 3694 3695 function ble/edit/display-version/git-rev-parse { 3696 ret= 3697 local git_base opts=$2 3698 case $1 in 3699 (.) git_base=$PWD ;; 3700 (./*) git_base=$PWD/${1#./} ;; 3701 (..|../*) git_base=$PWD/$1 ;; 3702 (*) git_base=$1 ;; 3703 esac 3704 3705 "${_ble_util_set_declare[@]//NAME/visited}" # WA #D1570 checked 3706 until [[ -s $git_base/HEAD || -s $git_base/.git/HEAD ]]; do 3707 # guard for cyclic refs 3708 ble/set#contains visited "$git_base" && return 1 3709 ble/set#add visited "$git_base" 3710 3711 # submodule? 3712 if [[ -f $git_base/.git ]]; then 3713 local content 3714 ble/util/mapfile content < "$git_base/.git" 3715 if ble/string#match "$content" '^gitdir: (.*)'; then 3716 git_base=$git_base/${BASH_REMATCH[1]} 3717 continue 3718 fi 3719 fi 3720 3721 # parent directory? 3722 if [[ :$opts: == *:parent:* && $git_base == */* ]]; then 3723 git_base=${git_base%/*} 3724 continue 3725 fi 3726 3727 break 3728 done 3729 [[ -s $git_base/HEAD ]] || git_base=$git_base/.git 3730 3731 local head=$git_base/HEAD 3732 if [[ -f $head ]]; then 3733 local content 3734 ble/util/mapfile content < "$head" 3735 if ble/string#match "$content" '^ref: (.*)$'; then 3736 head=$git_base/${BASH_REMATCH[1]} 3737 ble/util/mapfile content < "$head" 3738 fi 3739 if ble/string#match "$content" '^[a-f0-9]+$'; then 3740 content=${content::8} 3741 fi 3742 ret=$content 3743 [[ $ret ]] 3744 return "$?" 3745 fi 3746 return 1 3747 } 3748 function ble/edit/display-version/git-hash-object { 3749 local file=$1 size 3750 if ! ble/util/assign size 'ble/bin/wc -c "$file" 2>/dev/null'; then 3751 ret='error' 3752 return 1 3753 fi 3754 ble/string#split-words size "$size" 3755 3756 if ble/bin#has git; then 3757 ble/util/assign ret 'git hash-object "$file"' 3758 ret="hash:$ret, $size bytes" 3759 elif ble/bin#has sha1sum; then 3760 local _ble_local_tmpfile; ble/util/assign/mktmp 3761 { printf 'blob %d\0' "$size"; ble/bin/cat "$file"; } >| "$_ble_local_tmpfile" 3762 blob_data=$_ble_local_tmpfile ble/util/assign ret 'sha1sum "$blob_data"' 3763 ble/util/assign/rmtmp 3764 3765 ble/string#split-words ret "$ret" 3766 ret="sha1:$ret, $size bytes" 3767 elif ble/bin#has cksum; then 3768 ble/util/assign-words ret 'cksum "$file"' 3769 ble/util/sprintf ret 'cksum:%08x, %d bytes' "$ret" "$size" 3770 else 3771 ret=size:$size 3772 fi 3773 } 3774 function ble/edit/display-version/add-line { 3775 lines[iline++]=$1 3776 } 3777 function ble/edit/display-version/check:bash-completion { 3778 [[ ${BASH_COMPLETION_VERSINFO[0]-} ]] || return 1 3779 3780 local patch=${BASH_COMPLETION_VERSINFO[2]-} 3781 local version=${BASH_COMPLETION_VERSINFO[0]}.${BASH_COMPLETION_VERSINFO[1]:-y}${patch:+.$patch} 3782 local source lineno ret 3783 if ble/function#get-source-and-lineno _init_completion; then 3784 if ble/edit/display-version/git-rev-parse "${source%/*}"; then 3785 version=$sgrV$version+$ret$sgr0 3786 elif ble/edit/display-version/git-hash-object "$source"; then 3787 version="$sgrV$version$sgr0 ($ret)" 3788 fi 3789 fi 3790 ble/edit/display-version/add-line "${sgrF}bash-completion$sgr0, version $version$label_noarch" 3791 } 3792 function ble/edit/display-version/check:bash-preexec { 3793 local source lineno ret 3794 ble/function#get-source-and-lineno __bp_preexec_invoke_exec || return 1 3795 3796 local version="${source/#$HOME/~}$label_noarch" 3797 if ble/edit/display-version/git-rev-parse "${source%/*}"; then 3798 version="version $sgrV+$ret$sgr0$label_noarch" 3799 elif ble/edit/display-version/git-hash-object "$source"; then 3800 version="($ret)$label_noarch" 3801 fi 3802 3803 local file=${source##*/} 3804 if [[ $file == bash-preexec.sh || $file == bash-preexec.bash ]]; then 3805 file= 3806 else 3807 file=" ($file)" 3808 fi 3809 3810 local integ= 3811 ble/util/import/is-loaded contrib/bash-preexec && integ=$label_integration 3812 ble/edit/display-version/add-line "${sgrF}bash-preexec$sgr0$file, $version$integ" 3813 } 3814 function ble/edit/display-version/check:fzf { 3815 # fzf-key-bindings 3816 local source lineno ret 3817 if ble/function#get-source-and-lineno __fzf_select__; then 3818 local version="${source/#$HOME/~}$label_noarch" 3819 if ble/edit/display-version/git-rev-parse "${source%/*}" parent; then 3820 version="version $sgrV+$ret$sgr0$label_noarch" 3821 elif ble/edit/display-version/git-hash-object "$source"; then 3822 version="($ret)$label_noarch" 3823 fi 3824 3825 local integ= 3826 ble/util/import/is-loaded integration/fzf-key-bindings && integ=$label_integration 3827 3828 ble/edit/display-version/add-line "${sgrC}fzf$sgr0 ${sgrF}key-bindings$sgr0, $version$integ" 3829 [[ $integ ]] || ble/edit/display-version/add-line "$label_warning: fzf integration \"integration/fzf-key-bindings\" is not activated." 3830 fi 3831 3832 # fzf-completion 3833 if ble/function#get-source-and-lineno __fzf_orig_completion; then 3834 local version="${source/#$HOME/~}$label_noarch" 3835 if ble/edit/display-version/git-rev-parse "${source%/*}" parent; then 3836 version="version $sgrV+$ret$sgr0$label_noarch" 3837 elif ble/edit/display-version/git-hash-object "$source"; then 3838 version="($ret)$label_noarch" 3839 fi 3840 3841 local integ= 3842 ble/util/import/is-loaded integration/fzf-completion && integ=$label_integration 3843 3844 ble/edit/display-version/add-line "${sgrC}fzf$sgr0 ${sgrF}completion$sgr0, $version$integ" 3845 [[ $integ ]] || ble/edit/display-version/add-line "$label_warning: fzf integration \"integration/fzf-completion\" is not activated." 3846 fi 3847 } 3848 function ble/edit/display-version/check:starship { 3849 local source lineno 3850 ble/function#get-source-and-lineno starship_precmd || return 1 3851 3852 # get starship path 3853 local sed_script='s/^[[:space:]]*PS1="\$(\(.\{1,\}\) prompt .*)";\{0,1\}$/\1/p' 3854 ble/util/assign-array starship 'declare -f starship_precmd | ble/bin/sed -n "$sed_script"' 3855 if ! ble/bin#has "$starship"; then 3856 { builtin eval -- "starship=$starship" && ble/bin#has "$starship"; } || 3857 { starship=starship; ble/bin#has "$starship"; } || return 1 3858 fi 3859 3860 local awk_script=' 3861 sub(/^starship /, "") { version = $0; next; } 3862 sub(/^branch:/, "") { gsub(/['"$_ble_term_space"']/, "_"); if ($0 != "") version = version "-" $0; next; } 3863 sub(/^commit_hash:/, "") { gsub(/['"$_ble_term_space"']/, "_"); if ($0 != "") version = version "+" $0; next; } 3864 sub(/^build_time:/, "") { build_time = $0; } 3865 sub(/^build_env:/, "") { build_env = $0; } 3866 END { 3867 if (version != "") { 3868 print version; 3869 print build_env, build_time 3870 } 3871 } 3872 ' 3873 local version= 3874 ble/util/assign-array version '"$starship" --version | ble/bin/awk "$awk_script"' 3875 [[ $version ]] || return 1 3876 3877 local ret; ble/string#trim "${version[1]}"; local build=$ret 3878 ble/edit/display-version/add-line "${sgrF}starship${sgr0}, version $sgrV$version$sgr0${build:+ ($build)}" 3879 } 3880 function ble/edit/display-version/check:bash-it { 3881 [[ ${BASH_IT-} ]] && ble/is-function bash-it || return 1 3882 3883 local version= ret 3884 if ble/edit/display-version/git-rev-parse "$BASH_IT"; then 3885 version="version $sgrV+$ret$sgr0$label_noarch" 3886 elif ble/edit/display-version/git-hash-object "$BASH_IT/bash_it.sh"; then 3887 version="($ret)$label_noarch" 3888 else 3889 version="(bash-it version)" 3890 fi 3891 3892 # list enabled modules 3893 local modules= 3894 if ble/is-function _bash-it-component-item-is-enabled; then 3895 local category subdir suffix 3896 for category in aliases:alias completion plugins:plugin; do 3897 local subdir=${category%:*} suffix=${category#*:} list 3898 list=() 3899 local file name 3900 for file in "$BASH_IT/$subdir/available"/*.*.bash; do 3901 name=${file##*/} 3902 name=${name%."$suffix"*.bash} 3903 _bash-it-component-item-is-enabled "$suffix" "$name" && ble/array#push list "$name" 3904 done 3905 modules="$modules, $suffix(${list[*]})" 3906 done 3907 fi 3908 ble/edit/display-version/add-line "${sgrF}bash-it$sgr0${theme:+ ($theme)}, $version$modules" 3909 } 3910 function ble/edit/display-version/check:oh-my-bash { 3911 local source lineno ret version= 3912 if [[ ${OMB_VERSINFO-set} ]] && ble/function#get-source-and-lineno _omb_module_require; then 3913 version=${OMB_VERSINFO[0]}.${OMB_VERSINFO[1]}.${OMB_VERSINFO[2]} 3914 if ble/edit/display-version/git-rev-parse "${source%/*}"; then 3915 version="version $sgrV$version+$ret$sgr0$label_noarch" 3916 elif ble/edit/display-version/git-hash-object "$source"; then 3917 version="version $sgrV$version$sgr0 ($ret)$label_noarch" 3918 else 3919 version="version $sgrV$version$sgr0$label_noarch" 3920 fi 3921 elif [[ ${OSH_CUSTOM-set} ]] && ble/function#get-source-and-lineno is_plugin; then 3922 # old version of oh-my-bash 3923 version="${source/#$HOME/~}$label_noarch" 3924 if ble/edit/display-version/git-rev-parse "${source%/*}" parent; then 3925 version="version $sgrV+$ret$sgr0$label_noarch" 3926 elif ble/edit/display-version/git-hash-object "$source"; then 3927 version="($ret)$label_noarch" 3928 fi 3929 fi 3930 3931 if [[ $version ]]; then 3932 local theme=${OMB_THEME-${OSH_THEME-}} 3933 local modules="aliases(${aliases[*]}), completions(${completions[*]}), plugins(${plugins[*]})" 3934 ble/edit/display-version/add-line "${sgrF}oh-my-bash$sgr0${theme:+ ($theme)}, $version, $modules" 3935 fi 3936 } 3937 function ble/edit/display-version/check:sbp { 3938 local source lineno ret 3939 ble/function#get-source-and-lineno _sbp_set_prompt || return 1 3940 3941 local version="${source/#$HOME/~}$label_noarch" 3942 if ble/edit/display-version/git-rev-parse "${source%/*}"; then 3943 version="version $sgrV+$ret$sgr0$label_noarch" 3944 elif ble/edit/display-version/git-hash-object "$source"; then 3945 version="($ret)$label_noarch" 3946 fi 3947 3948 local hooks="hooks(${settings_hooks[*]-${SBP_HOOKS[*]}})" 3949 local left="left(${settings_segments_left[*]-${SBP_SEGMENTS_LEFT[*]}})" 3950 local right="right(${settings_segments_right[*]-${RBP_SEGMENTS_RIGHT[*]}})" 3951 local modules="$hooks, $left, $right" 3952 ble/edit/display-version/add-line "${sgrF}sbp$sgr0, $version, $modules" 3953 } 3954 function ble/edit/display-version/check:gitstatus { 3955 local source lineno ret 3956 ble/function#get-source-and-lineno gitstatus_query || return 1 3957 3958 local version="${source/#$HOME/~}$label_noarch" 3959 if ble/edit/display-version/git-rev-parse "${source%/*}"; then 3960 version="version $sgrV+$ret$sgr0$label_noarch" 3961 elif ble/edit/display-version/git-hash-object "$source"; then 3962 version="($ret)$label_noarch" 3963 fi 3964 3965 ble/edit/display-version/add-line "${sgrF}romkatv/gitstatus$sgr0, $version" 3966 } 3967 function ble/edit/display-version/check:zoxide { 3968 ble/is-function __zoxide_hook || return 1 3969 3970 # get starship path 3971 local sed_script='s/^[[:space:]]*PS1="\$(\(.\{1,\}\) prompt .*)";\{0,1\}$/\1/p' 3972 ble/util/assign-array starship 'declare -f starship_precmd | ble/bin/sed -n "$sed_script"' 3973 if ! ble/bin#has "$starship"; then 3974 { builtin eval -- "starship=$starship" && ble/bin#has "$starship"; } || 3975 { starship=starship; ble/bin#has "$starship"; } || return 1 3976 fi 3977 3978 local path= 3979 ble/util/assign path 'type -P zoxide 2>/dev/null' 3980 [[ $path ]] || return 1 3981 3982 local version= 3983 ble/util/assign-array version '\command zoxide --version' 3984 [[ $version ]] || return 1 3985 version=${version#zoxide } 3986 version=${version#v} 3987 3988 local integ= 3989 ble/util/import/is-loaded contrib/integration/zoxide && integ=$label_integration 3990 ble/edit/display-version/add-line "${sgrF}zoxide${sgr0}, version $sgrV$version$sgr0 ($path)$integ" 3991 } 3992 function ble/widget/display-shell-version { 3993 ble-edit/content/clear-arg 3994 3995 local sgrC= sgrF= sgrV= sgrA= sgr2= sgr3= sgr0= bold= 3996 if [[ -t 1 ]]; then 3997 bold=$_ble_term_bold 3998 sgr0=$_ble_term_sgr0 3999 ble/color/face2sgr command_file; sgrC=$ret 4000 ble/color/face2sgr command_function; sgrF=$ret 4001 ble/color/face2sgr syntax_expr; sgrV=$ret 4002 ble/color/face2sgr varname_readonly; sgrA=$ret 4003 ble/color/face2sgr syntax_varname; sgr2=$ret 4004 ble/color/face2sgr syntax_quoted; sgr3=$ret 4005 fi 4006 local label_noarch=" (${sgrA}noarch$sgr0)" 4007 local label_integration=" $_ble_term_bold(integration: on)$sgr0" 4008 local label_warning="${bold}WARNING$sgr0" 4009 4010 local os_release= 4011 if [[ -s /etc/os-release ]]; then 4012 ble/util/assign os_release '( 4013 builtin unset -v PRETTY_NAME NAME VERSION 4014 source /etc/os-release 4015 ble/util/print "${PRETTY_NAME:-${NAME:+$NAME${VERSION:+ $VERSION}}}")' 2>/dev/null 4016 fi 4017 if [[ ! $os_release && -s /etc/release ]]; then 4018 local ret 4019 ble/util/mapfile ret < /etc/release 4020 ble/string#trim "$ret" 4021 os_release=$ret 4022 fi 4023 4024 local lines="${sgrC}GNU bash$sgr0, version $sgrV$BASH_VERSION$sgr0 ($sgrA$MACHTYPE$sgr0)${os_release:+ [$os_release]}" iline=1 4025 local ble_build_info="${_ble_base_build_git_version/#git version/git}, $_ble_base_build_make_version, $_ble_base_build_gawk_version" 4026 lines[iline++]="${sgrF}ble.sh$sgr0, version $sgrV$BLE_VERSION$sgr0$label_noarch [$ble_build_info]" 4027 4028 ble/edit/display-version/check:bash-completion 4029 ble/edit/display-version/check:fzf 4030 ble/edit/display-version/check:bash-preexec 4031 ble/edit/display-version/check:starship 4032 ble/edit/display-version/check:bash-it 4033 ble/edit/display-version/check:oh-my-bash 4034 ble/edit/display-version/check:sbp 4035 ble/edit/display-version/check:gitstatus 4036 ble/edit/display-version/check:zoxide 4037 4038 # locale 4039 local q=\' 4040 local ret='(unset)' 4041 local var line=${_ble_term_bold}locale$sgr0: 4042 for var in _ble_bash_LANG "${!_ble_bash_LC_@}" LANG "${!LC_@}"; do 4043 case $var in 4044 (LC_ALL|LC_COLLATE) continue ;; 4045 (LANG|LC_CTYPE|LC_MESSAGES|LC_NUMERIC|LC_TIME) 4046 [[ ${_ble_bash_LC_ALL-} ]] && continue ;; 4047 esac 4048 [[ ${!var+set} ]] || continue 4049 ble/string#quote-word "${!var}" quote-empty:sgrq="$sgr3":sgr0="$sgr0" 4050 line="$line $sgr2${var#_ble_bash_}$sgrV=$sgr0$ret" 4051 done 4052 lines[iline++]=$line 4053 4054 # terminal 4055 ret='(unset)' 4056 [[ ${TERM+set} ]] && ble/string#quote-word "$TERM" quote-empty:sgrq="$sgr3":sgr0="$sgr0" 4057 local i line="${_ble_term_bold}terminal$sgr0: ${sgr2}TERM$sgrV=$sgr0$ret" 4058 line="$line ${sgr2}wcwidth$sgrV=$sgr0$bleopt_char_width_version-$bleopt_char_width_mode${bleopt_emoji_width:+/$bleopt_emoji_version-$bleopt_emoji_width+$bleopt_emoji_opts}" 4059 for i in "${!_ble_term_DA2R[@]}"; do 4060 line="$line, $sgrC${_ble_term_TERM[i]-unknown}$sgr0 ($sgrV${_ble_term_DA2R[i]}$sgr0)" 4061 done 4062 lines[iline++]=$line 4063 4064 ble/widget/print "${lines[@]}" 4065 } 4066 function ble/widget/readline-dump-functions { 4067 ble-edit/content/clear-arg 4068 local ret 4069 ble/util/assign ret 'ble/builtin/bind -P' 4070 ble/widget/print "$ret" 4071 } 4072 function ble/widget/readline-dump-macros { 4073 ble-edit/content/clear-arg 4074 local ret 4075 ble/util/assign ret 'ble/builtin/bind -S' 4076 ble/widget/print "$ret" 4077 } 4078 function ble/widget/readline-dump-variables { 4079 ble-edit/content/clear-arg 4080 local ret 4081 ble/util/assign ret 'ble/builtin/bind -V' 4082 ble/widget/print "$ret" 4083 } 4084 function ble/widget/re-read-init-file { 4085 ble-edit/content/clear-arg 4086 4087 local inputrc=$INPUTRC 4088 [[ $inputrc && -e $inputrc ]] || inputrc=~/.inputrc 4089 [[ -e $inputrc ]] || return 0 4090 ble/decode/read-inputrc "$inputrc" 4091 4092 # Note: 読み終わった "後" に "既定" に戻す #D1038 4093 _ble_builtin_bind_keymap= 4094 } 4095 4096 # **** mark, kill, copy **** @widget.mark 4097 4098 function ble/widget/overwrite-mode { 4099 ble-edit/content/clear-arg 4100 if [[ $_ble_edit_overwrite_mode ]]; then 4101 _ble_edit_overwrite_mode= 4102 else 4103 _ble_edit_overwrite_mode=1 4104 fi 4105 } 4106 4107 function ble/widget/set-mark { 4108 ble-edit/content/clear-arg 4109 _ble_edit_mark=$_ble_edit_ind 4110 _ble_edit_mark_active=1 4111 } 4112 function ble/widget/kill-forward-text { 4113 ble-edit/content/clear-arg 4114 ((_ble_edit_ind>=${#_ble_edit_str})) && return 0 4115 ble-edit/content/push-kill-ring "${_ble_edit_str:_ble_edit_ind}" 4116 ble-edit/content/replace "$_ble_edit_ind" "${#_ble_edit_str}" '' 4117 ((_ble_edit_mark>_ble_edit_ind&&(_ble_edit_mark=_ble_edit_ind))) 4118 } 4119 function ble/widget/kill-backward-text { 4120 ble-edit/content/clear-arg 4121 ((_ble_edit_ind==0)) && return 0 4122 ble-edit/content/push-kill-ring "${_ble_edit_str::_ble_edit_ind}" 4123 ble-edit/content/replace 0 "$_ble_edit_ind" '' 4124 ((_ble_edit_mark=_ble_edit_mark<=_ble_edit_ind?0:_ble_edit_mark-_ble_edit_ind)) 4125 _ble_edit_ind=0 4126 } 4127 function ble/widget/exchange-point-and-mark { 4128 ble-edit/content/clear-arg 4129 local m=$_ble_edit_mark p=$_ble_edit_ind 4130 _ble_edit_ind=$m _ble_edit_mark=$p 4131 } 4132 function ble/widget/@marked { 4133 if [[ $_ble_edit_mark_active != S ]]; then 4134 _ble_edit_mark=$_ble_edit_ind 4135 _ble_edit_mark_active=S 4136 fi 4137 ble/decode/widget/dispatch "$@" 4138 } 4139 function ble/widget/@nomarked { 4140 if [[ $_ble_edit_mark_active == S ]]; then 4141 _ble_edit_mark_active= 4142 fi 4143 ble/decode/widget/dispatch "$@" 4144 } 4145 4146 ## @fn ble/widget/.process-range-argument P0 P1; p0 p1 len ? 4147 ## @param[in] P0 範囲の端点を指定します。 4148 ## @param[in] P1 もう一つの範囲の端点を指定します。 4149 ## @param[out] p0 範囲の開始点を返します。 4150 ## @param[out] p1 範囲の終端点を返します。 4151 ## @param[out] len 範囲の長さを返します。 4152 ## @param[out] $? 4153 ## 範囲が有限の長さを持つ場合に正常終了します。 4154 ## 範囲が空の場合に 1 を返します。 4155 function ble/widget/.process-range-argument { 4156 p0=$1 p1=$2 len=${#_ble_edit_str} 4157 local pt 4158 (( 4159 p0>len?(p0=len):p0<0&&(p0=0), 4160 p1>len?(p1=len):p0<0&&(p1=0), 4161 p1<p0&&(pt=p1,p1=p0,p0=pt), 4162 (len=p1-p0)>0 4163 )) 4164 } 4165 ## @fn ble/widget/.delete-range P0 P1 [opts] 4166 function ble/widget/.delete-range { 4167 local p0 p1 len 4168 ble/widget/.process-range-argument "${@:1:2}" || return 1 4169 4170 # delete 4171 if ((len)); then 4172 ble-edit/content/replace "$p0" "$p1" '' 4173 (( 4174 _ble_edit_ind>p1? (_ble_edit_ind-=len): 4175 _ble_edit_ind>p0&&(_ble_edit_ind=p0), 4176 _ble_edit_mark>p1? (_ble_edit_mark-=len): 4177 _ble_edit_mark>p0&&(_ble_edit_mark=p0) 4178 )) 4179 fi 4180 return 0 4181 } 4182 ## @fn ble/widget/.kill-range P0 P1 [opts [kill_type]] 4183 function ble/widget/.kill-range { 4184 local p0 p1 len 4185 ble/widget/.process-range-argument "${@:1:2}" || return 1 4186 4187 # copy 4188 ble-edit/content/push-kill-ring "${_ble_edit_str:p0:len}" "$4" 4189 4190 # delete 4191 if ((len)); then 4192 ble-edit/content/replace "$p0" "$p1" '' 4193 (( 4194 _ble_edit_ind>p1? (_ble_edit_ind-=len): 4195 _ble_edit_ind>p0&&(_ble_edit_ind=p0), 4196 _ble_edit_mark>p1? (_ble_edit_mark-=len): 4197 _ble_edit_mark>p0&&(_ble_edit_mark=p0) 4198 )) 4199 fi 4200 return 0 4201 } 4202 ## @fn ble/widget/.copy-range P0 P1 [opts [kill_type]] 4203 function ble/widget/.copy-range { 4204 local p0 p1 len 4205 ble/widget/.process-range-argument "${@:1:2}" || return 1 4206 4207 # copy 4208 ble-edit/content/push-kill-ring "${_ble_edit_str:p0:len}" "$4" 4209 } 4210 ## @fn ble/widget/.replace-range P0 P1 string 4211 function ble/widget/.replace-range { 4212 local p0 p1 len 4213 ble/widget/.process-range-argument "${@:1:2}" 4214 local insert; ble-edit/content/replace-limited "$p0" "$p1" "$3" 4215 local inslen=${#insert} delta 4216 ((delta=inslen-len)) && 4217 ((_ble_edit_ind>p1?(_ble_edit_ind+=delta): 4218 _ble_edit_ind>=p0&&(_ble_edit_ind=p0+inslen), 4219 _ble_edit_mark>p1?(_ble_edit_mark+=delta): 4220 _ble_edit_mark>p0&&(_ble_edit_mark=p0))) 4221 return 0 4222 } 4223 ## @widget delete-region 4224 ## 領域を削除します。 4225 function ble/widget/delete-region { 4226 ble-edit/content/clear-arg 4227 ble/widget/.delete-range "$_ble_edit_mark" "$_ble_edit_ind" 4228 _ble_edit_mark_active= 4229 } 4230 ## @widget kill-region 4231 ## 領域を切り取ります。 4232 function ble/widget/kill-region { 4233 ble-edit/content/clear-arg 4234 ble/widget/.kill-range "$_ble_edit_mark" "$_ble_edit_ind" 4235 _ble_edit_mark_active= 4236 } 4237 ## @widget copy-region 4238 ## 領域を転写します。 4239 function ble/widget/copy-region { 4240 ble-edit/content/clear-arg 4241 ble/widget/.copy-range "$_ble_edit_mark" "$_ble_edit_ind" 4242 _ble_edit_mark_active= 4243 } 4244 ## @widget delete-region-or widget 4245 ## mark が active の時に領域を削除します。 4246 ## それ以外の時に編集関数 widget を実行します。 4247 ## @param[in] widget 4248 function ble/widget/delete-region-or { 4249 if [[ $_ble_edit_mark_active ]]; then 4250 ble/widget/delete-region 4251 else 4252 ble/decode/widget/dispatch "$@" 4253 fi 4254 } 4255 ## @widget kill-region-or widget 4256 ## mark が active の時に領域を切り取ります。 4257 ## それ以外の時に編集関数 widget を実行します。 4258 ## @param[in] widget 4259 function ble/widget/kill-region-or { 4260 if [[ $_ble_edit_mark_active ]]; then 4261 ble/widget/kill-region 4262 else 4263 ble/decode/widget/dispatch "$@" 4264 fi 4265 } 4266 ## @widget copy-region-or widget 4267 ## mark が active の時に領域を転写します。 4268 ## それ以外の時に編集関数 widget を実行します。 4269 ## @param[in] widget 4270 function ble/widget/copy-region-or { 4271 if [[ $_ble_edit_mark_active ]]; then 4272 ble/widget/copy-region 4273 else 4274 ble/decode/widget/dispatch "$@" 4275 fi 4276 } 4277 4278 ## @widget yank 4279 function ble/widget/yank { 4280 local arg; ble-edit/content/get-arg 1 4281 4282 local nkill=${#_ble_edit_kill_ring[@]} 4283 if ((nkill==0)); then 4284 ble/widget/.bell 'no strings in kill-ring' 4285 _ble_edit_yank_index= 4286 return 1 4287 fi 4288 4289 local index=$_ble_edit_kill_index 4290 local delta=$((arg-1)) 4291 if ((delta)); then 4292 ((index=(index+delta)%nkill, 4293 index=(index+nkill)%nkill)) 4294 _ble_edit_kill_index=$index 4295 fi 4296 4297 local insert=${_ble_edit_kill_ring[index]} 4298 _ble_edit_yank_index=$index 4299 if [[ $insert ]]; then 4300 ble-edit/content/replace-limited "$_ble_edit_ind" "$_ble_edit_ind" "$insert" 4301 ((_ble_edit_mark=_ble_edit_ind, 4302 _ble_edit_ind+=${#insert})) 4303 _ble_edit_mark_active= 4304 fi 4305 } 4306 4307 _ble_edit_yank_index= 4308 function ble/edit/yankpop.impl { 4309 local arg=$1 4310 local nkill=${#_ble_edit_kill_ring[@]} 4311 ((_ble_edit_yank_index=(_ble_edit_yank_index+arg)%nkill, 4312 _ble_edit_yank_index=(_ble_edit_yank_index+nkill)%nkill)) 4313 local insert=${_ble_edit_kill_ring[_ble_edit_yank_index]} 4314 ble-edit/content/replace-limited "$_ble_edit_mark" "$_ble_edit_ind" "$insert" 4315 ((_ble_edit_ind=_ble_edit_mark+${#insert})) 4316 } 4317 function ble/widget/yank-pop { 4318 local opts=$1 4319 local arg; ble-edit/content/get-arg 1 4320 if ! [[ $_ble_edit_yank_index && ${LASTWIDGET%%' '*} == ble/widget/yank ]]; then 4321 ble/widget/.bell 4322 return 1 4323 fi 4324 4325 [[ :$opts: == *:backward:* ]] && ((arg=-arg)) 4326 4327 ble/edit/yankpop.impl "$arg" 4328 _ble_edit_mark_active=insert 4329 ble/decode/keymap/push yankpop 4330 } 4331 function ble/widget/yankpop/next { 4332 local arg; ble-edit/content/get-arg 1 4333 ble/edit/yankpop.impl "$arg" 4334 } 4335 function ble/widget/yankpop/prev { 4336 local arg; ble-edit/content/get-arg 1 4337 ble/edit/yankpop.impl "$((-arg))" 4338 } 4339 function ble/widget/yankpop/exit { 4340 ble/decode/keymap/pop 4341 _ble_edit_mark_active= 4342 } 4343 function ble/widget/yankpop/cancel { 4344 ble-edit/content/replace "$_ble_edit_mark" "$_ble_edit_ind" '' 4345 _ble_edit_ind=$_ble_edit_mark 4346 ble/widget/yankpop/exit 4347 } 4348 function ble/widget/yankpop/exit-default { 4349 ble/widget/yankpop/exit 4350 ble/decode/widget/skip-lastwidget 4351 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 4352 } 4353 function ble-decode/keymap:yankpop/define { 4354 ble-decode/keymap:safe/bind-arg yankpop/exit-default 4355 ble-bind -f __default__ 'yankpop/exit-default' 4356 ble-bind -f __line_limit__ nop 4357 ble-bind -f 'C-g' 'yankpop/cancel' 4358 ble-bind -f 'C-x C-g' 'yankpop/cancel' 4359 ble-bind -f 'C-M-g' 'yankpop/cancel' 4360 ble-bind -f 'M-y' 'yankpop/next' 4361 ble-bind -f 'M-S-y' 'yankpop/prev' 4362 ble-bind -f 'M-Y' 'yankpop/prev' 4363 } 4364 4365 # **** bell **** @edit.bell 4366 4367 function ble/widget/.bell { 4368 [[ $bleopt_edit_vbell ]] && ble/term/visible-bell "$1" 4369 [[ $bleopt_edit_abell ]] && ble/term/audible-bell 4370 return 0 4371 } 4372 4373 # blehook/declare widget_bell (defined in def.sh) 4374 function ble/widget/bell { 4375 ble-edit/content/clear-arg 4376 _ble_edit_mark_active= 4377 _ble_edit_arg= 4378 blehook/invoke widget_bell 4379 ble/widget/.bell "$1" 4380 } 4381 4382 function ble/widget/nop { :; } 4383 4384 # **** insert **** @edit.insert 4385 4386 function ble/widget/insert-string { 4387 local IFS=$_ble_term_IFS 4388 local content="$*" 4389 local arg; ble-edit/content/get-arg 1 4390 if ((arg<0)); then 4391 ble/widget/.bell "negative repetition number $arg" 4392 return 1 4393 elif ((arg==0)); then 4394 return 0 4395 elif ((arg>1)); then 4396 local ret; ble/string#repeat "$content" "$arg"; content=$ret 4397 fi 4398 ble/widget/.insert-string "$content" 4399 } 4400 function ble/widget/.insert-string { 4401 local insert=$1 4402 [[ $insert ]] || return 1 4403 4404 ble-edit/content/replace-limited "$_ble_edit_ind" "$_ble_edit_ind" "$insert" 4405 local dx=${#insert} 4406 (( 4407 _ble_edit_mark>_ble_edit_ind&&(_ble_edit_mark+=dx), 4408 _ble_edit_ind+=dx 4409 )) 4410 _ble_edit_mark_active= 4411 } 4412 if [[ -c /dev/clipboard ]]; then 4413 function ble/widget/paste-from-clipboard { 4414 local clipboard 4415 if ! ble/util/readfile clipboard /dev/clipboard; then 4416 ble/widget/.bell 4417 return 1 4418 fi 4419 4420 ble/widget/insert-string "$clipboard" 4421 return 0 4422 } 4423 fi 4424 4425 ## @fn ble/widget/insert-arg.impl beg end index delta nth 4426 ## @param[in] beg end 4427 ## 置換範囲を指定します。 4428 ## @param[in] index 4429 ## 起点の履歴番号を指定します。 4430 ## @param[in] delta 4431 ## (最低の)移動量を指定します。 4432 ## @param[in] nth 4433 ## '$', '^', n 等の単語指定子を指定します。 4434 ## 4435 ## @var _ble_edit_lastarg_index 4436 ## 最後に挿入した最終引数の履歴番号です。 4437 ## @var _ble_edit_lastarg_delta 4438 ## 最後に挿入した時の移動量です。 4439 ## 繰り返し呼び出した時の移動方向を決定するのに使います。 4440 ## @var _ble_edit_lastarg_nth 4441 ## 最後に挿入した時の単語指定子です。 4442 ## 4443 _ble_edit_lastarg_index= 4444 _ble_edit_lastarg_delta= 4445 _ble_edit_lastarg_nth= 4446 function ble/widget/insert-arg.impl { 4447 local beg=$1 end=$2 index=$3 delta=$4 nth=$5 4448 ((delta)) || delta=1 4449 4450 ble/history/initialize 4451 local hit= lastarg= 4452 local decl=$( 4453 local original=${_ble_edit_str:beg:end-beg} 4454 local count=; ((delta>0)) && count=_ble_history_COUNT 4455 while :; do 4456 # index = next history index to check 4457 if ((delta>0)); then 4458 ((index+1>=count)) && break 4459 ((index+=delta,delta=1)) 4460 ((index>=count&&(index=count-1))) 4461 else 4462 ((index-1<0)) && break 4463 ((index+=delta,delta=-1)) 4464 ((index<0&&(index=0))) 4465 fi 4466 4467 local entry; ble/history/get-edited-entry "$index" 4468 builtin history -s -- "$entry" 4469 local hist_expanded 4470 if ble-edit/hist_expanded.update '!!:'"$nth" && 4471 [[ $hist_expanded != "$original" ]]; then 4472 hit=1 lastarg=$hist_expanded 4473 ble/util/declare-print-definitions hit lastarg 4474 break 4475 fi 4476 done 4477 _ble_edit_lastarg_index=$index 4478 _ble_edit_lastarg_delta=$delta 4479 _ble_edit_lastarg_nth=$nth 4480 ble/util/declare-print-definitions \ 4481 _ble_edit_lastarg_index \ 4482 _ble_edit_lastarg_delta \ 4483 _ble_edit_lastarg_nth 4484 ) 4485 builtin eval -- "$decl" 4486 4487 if [[ $hit ]]; then 4488 local insert; ble-edit/content/replace-limited "$beg" "$end" "$lastarg" 4489 ((_ble_edit_mark=beg,_ble_edit_ind=beg+${#insert})) 4490 return 0 4491 else 4492 ble/widget/.bell 4493 return 1 4494 fi 4495 } 4496 function ble/widget/insert-nth-argument { 4497 ble/history/initialize 4498 local arg; ble-edit/content/get-arg '^' 4499 local beg=$_ble_edit_ind end=$_ble_edit_ind 4500 local index=$_ble_history_INDEX 4501 local delta=-1 nth=$arg 4502 ble/widget/insert-arg.impl "$beg" "$end" "$index" "$delta" "$nth" 4503 } 4504 function ble/widget/insert-last-argument { 4505 ble/history/initialize 4506 local arg; ble-edit/content/get-arg '$' 4507 local beg=$_ble_edit_ind end=$_ble_edit_ind 4508 local index=$_ble_history_INDEX 4509 local delta=-1 nth=$arg 4510 ble/widget/insert-arg.impl "$beg" "$end" "$index" "$delta" "$nth" || return "$?" 4511 _ble_edit_mark_active=insert 4512 ble/decode/keymap/push lastarg 4513 } 4514 function ble/widget/lastarg/next { 4515 local arg; ble-edit/content/get-arg 1 4516 local beg=$_ble_edit_mark 4517 local end=$_ble_edit_ind 4518 local index=$_ble_edit_lastarg_index 4519 4520 local delta 4521 if [[ $arg ]]; then 4522 delta=$((-arg)) 4523 else 4524 ((delta=_ble_edit_lastarg_delta>=0?1:-1)) 4525 fi 4526 4527 local nth=$_ble_edit_lastarg_nth 4528 ble/widget/insert-arg.impl "$beg" "$end" "$index" "$delta" "$nth" 4529 } 4530 function ble/widget/lastarg/exit { 4531 ble/decode/keymap/pop 4532 _ble_edit_mark_active= 4533 } 4534 function ble/widget/lastarg/cancel { 4535 ble-edit/content/replace "$_ble_edit_mark" "$_ble_edit_ind" '' 4536 _ble_edit_ind=$_ble_edit_mark 4537 ble/widget/lastarg/exit 4538 } 4539 function ble/widget/lastarg/exit-default { 4540 ble/widget/lastarg/exit 4541 ble/decode/widget/skip-lastwidget 4542 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 4543 } 4544 function ble/highlight/layer:region/mark:insert/get-face { 4545 face=region_insert 4546 } 4547 4548 function ble-decode/keymap:lastarg/define { 4549 ble-decode/keymap:safe/bind-arg lastarg/exit-default 4550 4551 ble-bind -f __default__ 'lastarg/exit-default' 4552 ble-bind -f __line_limit__ nop 4553 ble-bind -f 'C-g' 'lastarg/cancel' 4554 ble-bind -f 'C-x C-g' 'lastarg/cancel' 4555 ble-bind -f 'C-M-g' 'lastarg/cancel' 4556 ble-bind -f 'M-.' 'lastarg/next' 4557 ble-bind -f 'M-_' 'lastarg/next' 4558 } 4559 4560 ## @widget self-insert 4561 ## 文字を挿入する。 4562 ## 4563 ## @var[in] _ble_edit_arg 4564 ## 繰り返し回数を指定する。 4565 ## 4566 ## @var[in] ble_widget_self_insert_opts 4567 ## コロン区切りの設定のリストを指定する。 4568 ## 4569 ## nolineext は上書きモードにおいて、行の長さを拡張しない。 4570 ## 行の長さが足りない場合は操作をキャンセルする。 4571 ## vi.sh の r, gr による挿入を想定する。 4572 ## 4573 4574 function ble/widget/self-insert/.get-code { 4575 if ((${#KEYS[@]})); then 4576 code=${KEYS[${#KEYS[@]}-1]} 4577 local flag=$((code&_ble_decode_MaskFlag)) 4578 local char=$((code&_ble_decode_MaskChar)) 4579 if ((flag==0&&char<_ble_decode_FunctionKeyBase)); then 4580 code=$char 4581 return 0 4582 elif ((flag==_ble_decode_Ctrl&&(char==63||91<=char&&char<=122)&&(char&0x1F)!=0)); then 4583 ((char=char==63?127:char&0x1F)) 4584 code=$char 4585 return 0 4586 fi 4587 fi 4588 4589 if ((${#CHARS[@]})); then 4590 code=${CHARS[${#CHARS[@]}-1]} 4591 return 0 4592 fi 4593 4594 code=0 4595 return 1 4596 } 4597 4598 function ble/widget/self-insert { 4599 local code; ble/widget/self-insert/.get-code 4600 ((code==0)) && return 0 4601 4602 # Note: Bash 3.0 では ^? (DEL) の処理に問題があるので、 4603 # ^@ (NUL) と同様に単に無視する事にする #D1093 4604 ((code==127&&_ble_bash<30100)) && return 0 4605 4606 local ibeg=$_ble_edit_ind iend=$_ble_edit_ind 4607 local ret ins; ble/util/c2s "$code"; ins=$ret 4608 4609 local arg; ble-edit/content/get-arg 1 4610 if ((arg<0)); then 4611 ble/widget/.bell "negative repetition number $arg" 4612 return 1 4613 elif ((arg==0)) || [[ ! $ins ]]; then 4614 arg=0 ins= 4615 elif ((arg>1)); then 4616 ble/string#repeat "$ins" "$arg"; ins=$ret 4617 fi 4618 # Note: arg はこの時点での ins の文字数になっているとは限らない。 4619 # 現在の LC_CTYPE で対応する文字がない場合 \uXXXX 等に変換される為。 4620 4621 if [[ $bleopt_delete_selection_mode && $_ble_edit_mark_active ]]; then 4622 # 選択範囲を置き換える。 4623 ((_ble_edit_mark<_ble_edit_ind?(ibeg=_ble_edit_mark):(iend=_ble_edit_mark), 4624 _ble_edit_ind=ibeg)) 4625 ((arg==0&&ibeg==iend)) && return 0 4626 elif [[ $_ble_edit_overwrite_mode ]] && ((code!=10&&code!=9)); then 4627 ((arg==0)) && return 0 4628 4629 local removed_width 4630 if [[ $_ble_edit_overwrite_mode == R ]]; then 4631 local removed_text=${_ble_edit_str:ibeg:arg} 4632 removed_text=${removed_text%%[$'\n\t']*} 4633 removed_width=${#removed_text} 4634 ((iend+=removed_width)) 4635 else 4636 # 上書きモードの時は Unicode 文字幅を考慮して既存の文字を置き換える。 4637 # ※現在の LC_CTYPE で対応する文字がない場合でも、意図しない動作を防ぐために、 4638 # 対応していたと想定した時の文字幅で削除する。 4639 local ret w; ble/util/c2w-edit "$code"; w=$((arg*ret)) 4640 4641 local iN=${#_ble_edit_str} 4642 for ((removed_width=0;removed_width<w&&iend<iN;iend++)); do 4643 local c1 w1 4644 ble/util/s2c "${_ble_edit_str:iend:1}"; c1=$ret 4645 [[ $c1 == 0 || $c1 == 10 || $c1 == 9 ]] && break 4646 ble/util/c2w-edit "$c1"; w1=$ret 4647 ((removed_width+=w1)) 4648 done 4649 4650 ((removed_width>w)) && ins=$ins${_ble_string_prototype::removed_width-w} 4651 fi 4652 4653 # これは vi.sh の r gr で設定する変数 4654 if [[ :$ble_widget_self_insert_opts: == *:nolineext:* ]]; then 4655 if ((removed_width<arg)); then 4656 ble/widget/.bell 4657 return 0 4658 fi 4659 fi 4660 fi 4661 4662 # コマンドライン文字数制限 4663 local insert; ble-edit/content/replace-limited "$ibeg" "$iend" "$ins" 4664 ((_ble_edit_ind+=${#insert}, 4665 _ble_edit_mark>ibeg&&( 4666 _ble_edit_mark<iend?( 4667 _ble_edit_mark=_ble_edit_ind 4668 ):( 4669 _ble_edit_mark+=${#insert}-(iend-ibeg))))) 4670 _ble_edit_mark_active= 4671 return 0 4672 } 4673 4674 function ble/widget/batch-insert.progress { 4675 ((index%${1:-257}==0&&N>=2000)) || return 1 4676 local ble_batch_insert_index=$index 4677 local ble_batch_insert_count=$N 4678 builtin eval -- "$_ble_decode_show_progress_hook" 4679 } 4680 function ble/widget/batch-insert { 4681 local -a chars; chars=("${KEYS[@]}") 4682 4683 local -a KEYS=() 4684 local index=0 N=${#chars[@]} 4685 if [[ $_ble_edit_overwrite_mode ]]; then 4686 while ((index<N&&_ble_edit_ind<${#_ble_edit_str})); do 4687 KEYS=${chars[index]} ble/widget/self-insert 4688 ((index++)) 4689 done 4690 ((index<N)) || return 0 4691 fi 4692 4693 # コマンドライン文字数制限 4694 if [[ $bleopt_line_limit_type == discard ]]; then 4695 local limit=$((bleopt_line_limit_length)) 4696 if ((limit&&${#_ble_edit_str}+N-index>=limit)); then 4697 chars=("${chars[@]::limit-${#_ble_edit_str}}") 4698 N=${#chars[@]} 4699 ((index<N)) || { ble/widget/.bell; return 1; } 4700 fi 4701 fi 4702 4703 while ((index<N)) && [[ $_ble_edit_arg || $_ble_edit_mark_active ]]; do 4704 KEYS=${chars[index]} ble/widget/self-insert 4705 ((index++)) 4706 ble/widget/batch-insert.progress 4707 done 4708 4709 if ((index<N)); then 4710 # NUL を unset してから一括で変換する 4711 local index0=$index ret ins 4712 for ((;index<N;index++)); do 4713 ((chars[index])) || builtin unset -v 'chars[index]' 4714 ble/widget/batch-insert.progress 2357 4715 done 4716 ble/util/chars2s "${chars[@]:index0}"; ins=$ret 4717 ble/widget/insert-string "$ins" 4718 fi 4719 4720 ble-edit/content/check-limit truncate 4721 } 4722 4723 # quoted insert 4724 function ble/widget/quoted-insert-char.hook { 4725 ble/widget/self-insert 4726 } 4727 function ble/widget/quoted-insert-char { 4728 _ble_edit_mark_active= 4729 _ble_decode_char__hook=ble/widget/quoted-insert-char.hook 4730 return 147 4731 } 4732 function ble/widget/quoted-insert.hook { 4733 local flag=$((KEYS[0]&_ble_decode_MaskFlag)) 4734 local char=$((KEYS[0]&_ble_decode_MaskChar)) 4735 if ((flag==0&&char<_ble_decode_FunctionKeyBase)); then 4736 ble/widget/self-insert 4737 elif ((flag==_ble_decode_Ctrl&&(char==63||91<=char&&char<=122)&&(char&0x1F)!=0)); then 4738 # C-x (C-@ 以外) は変換して制御文字を挿入する。 4739 ((char=char==63?127:char&0x1F)) 4740 local -a KEYS; KEYS=("$char") 4741 ble/widget/self-insert 4742 else 4743 local -a KEYS; KEYS=("${CHARS[@]}") 4744 ble/widget/batch-insert 4745 fi 4746 } 4747 function ble/widget/quoted-insert { 4748 _ble_edit_mark_active= 4749 _ble_decode_key__hook=ble/widget/quoted-insert.hook 4750 return 147 4751 } 4752 4753 _ble_edit_bracketed_paste=() 4754 _ble_edit_bracketed_paste_proc= 4755 _ble_edit_bracketed_paste_count=0 4756 function ble/widget/bracketed-paste { 4757 ble-edit/content/clear-arg 4758 _ble_edit_mark_active= 4759 _ble_edit_bracketed_paste=() 4760 _ble_edit_bracketed_paste_count=0 4761 _ble_edit_bracketed_paste_proc=ble/widget/bracketed-paste.proc 4762 _ble_decode_char__hook=ble/widget/bracketed-paste.hook 4763 return 147 4764 } 4765 function ble/widget/bracketed-paste.hook/check-end { 4766 local is_end= chars= 4767 if ((_ble_edit_bracketed_paste_count>=5)); then 4768 IFS=: builtin eval '_ble_edit_bracketed_paste=("${_ble_edit_bracketed_paste[*]}")' 4769 chars=:$_ble_edit_bracketed_paste 4770 if [[ $chars == *:50:48:49:126 ]]; then 4771 if [[ $chars == *:27:91:50:48:49:126 ]]; then # ESC [ 2 0 1 ~ 4772 chars=${chars%:27:91:50:48:49:126} is_end=1 4773 elif [[ $chars == *:155:50:48:49:126 ]]; then # CSI 2 0 1 ~ 4774 chars=${chars%:155:50:48:49:126} is_end=1 4775 fi 4776 fi 4777 fi 4778 4779 [[ $is_end ]] || return 1 4780 4781 _ble_decode_char__hook= 4782 chars=:${chars//:/::}: 4783 chars=${chars//:13::10:/:10:} # CR LF -> LF 4784 chars=${chars//:13:/:10:} # CR -> LF 4785 ble/string#split-words chars "${chars//:/ }" 4786 4787 local proc=$_ble_edit_bracketed_paste_proc 4788 _ble_edit_bracketed_paste_proc= 4789 [[ $proc ]] && builtin eval -- "$proc \"\${chars[@]}\"" 4790 return 0 4791 } 4792 function ble/widget/bracketed-paste.hook { 4793 ((_ble_edit_bracketed_paste_count%1000==0)) && 4794 IFS=: builtin eval '_ble_edit_bracketed_paste=("${_ble_edit_bracketed_paste[*]}")' # contract 4795 4796 _ble_edit_bracketed_paste[_ble_edit_bracketed_paste_count++]=$1 4797 (($1==126)) && ble/widget/bracketed-paste.hook/check-end && return 0 4798 4799 # ble-decode-char にある次の文字を取り出してできるだけここで処理する。 4800 if ((!_ble_debug_keylog_enabled)) && [[ ! $_ble_decode_keylog_chars_enabled ]]; then 4801 local char 4802 while ble/decode/char-hook/next-char; do 4803 _ble_edit_bracketed_paste[_ble_edit_bracketed_paste_count++]=$char 4804 ((char==126)) && ble/widget/bracketed-paste.hook/check-end && return 0 4805 done 4806 fi 4807 4808 _ble_decode_char__hook=ble/widget/bracketed-paste.hook 4809 return 147 4810 } 4811 function ble/widget/bracketed-paste.proc { 4812 local -a KEYS; KEYS=("$@") 4813 ble/widget/batch-insert 4814 } 4815 4816 4817 function ble/widget/transpose-chars { 4818 local arg; ble-edit/content/get-arg '' 4819 if ((arg==0)); then 4820 [[ ! $arg ]] && ble-edit/content/eolp && 4821 ((_ble_edit_ind>0&&_ble_edit_ind--)) 4822 arg=1 4823 fi 4824 4825 local p q r 4826 if ((arg>0)); then 4827 ((p=_ble_edit_ind-1, 4828 q=_ble_edit_ind, 4829 r=_ble_edit_ind+arg)) 4830 else # arg<0 4831 ((p=_ble_edit_ind-1+arg, 4832 q=_ble_edit_ind, 4833 r=_ble_edit_ind+1)) 4834 fi 4835 4836 if ((p<0||${#_ble_edit_str}<r)); then 4837 ((_ble_edit_ind=arg<0?0:${#_ble_edit_str})) 4838 ble/widget/.bell 4839 return 1 4840 fi 4841 4842 local a=${_ble_edit_str:p:q-p} 4843 local b=${_ble_edit_str:q:r-q} 4844 ble-edit/content/replace "$p" "$r" "$b$a" 4845 ((_ble_edit_ind+=arg)) 4846 return 0 4847 } 4848 4849 # 4850 # **** delete-char **** @edit.delete 4851 4852 function ble/widget/.delete-backward-char { 4853 local a=${1:-1} 4854 if ((_ble_edit_ind-a<0)); then 4855 return 1 4856 fi 4857 4858 local ins= 4859 if [[ $_ble_edit_overwrite_mode ]]; then 4860 local next=${_ble_edit_str:_ble_edit_ind:1} 4861 if [[ $next && $next != [$'\n\t'] ]]; then 4862 if [[ $_ble_edit_overwrite_mode == R ]]; then 4863 local w=$a 4864 else 4865 local w=0 ret i 4866 for ((i=0;i<a;i++)); do 4867 ble/util/s2c "${_ble_edit_str:_ble_edit_ind-a+i:1}" 4868 ble/util/c2w-edit "$ret" 4869 ((w+=ret)) 4870 done 4871 fi 4872 if ((w)); then 4873 local ret; ble/string#repeat ' ' "$w"; ins=$ret 4874 ((_ble_edit_mark>=_ble_edit_ind&&(_ble_edit_mark+=w))) 4875 fi 4876 fi 4877 fi 4878 4879 ble-edit/content/replace "$((_ble_edit_ind-a))" "$_ble_edit_ind" "$ins" 4880 ((_ble_edit_ind-=a, 4881 _ble_edit_ind+a<_ble_edit_mark?(_ble_edit_mark-=a): 4882 _ble_edit_ind<_ble_edit_mark&&(_ble_edit_mark=_ble_edit_ind))) 4883 return 0 4884 } 4885 4886 function ble/widget/.delete-char { 4887 local a=${1:-1} 4888 if ((a>0)); then 4889 # delete-forward-char 4890 if ((${#_ble_edit_str}<_ble_edit_ind+a)); then 4891 return 1 4892 else 4893 ble-edit/content/replace "$_ble_edit_ind" "$((_ble_edit_ind+a))" '' 4894 fi 4895 elif ((a<0)); then 4896 # delete-backward-char 4897 ble/widget/.delete-backward-char "$((-a))"; return "$?" 4898 else 4899 # delete-forward-backward-char 4900 if ((${#_ble_edit_str}==0)); then 4901 return 1 4902 elif ((_ble_edit_ind<${#_ble_edit_str})); then 4903 ble-edit/content/replace "$_ble_edit_ind" "$((_ble_edit_ind+1))" '' 4904 else 4905 _ble_edit_ind=${#_ble_edit_str} 4906 ble/widget/.delete-backward-char 1; return "$?" 4907 fi 4908 fi 4909 4910 ((_ble_edit_mark>_ble_edit_ind&&_ble_edit_mark--)) 4911 return 0 4912 } 4913 function ble/widget/delete-forward-char { 4914 local arg; ble-edit/content/get-arg 1 4915 ((arg==0)) && return 0 4916 ble/widget/.delete-char "$arg" || ble/widget/.bell 4917 } 4918 function ble/widget/delete-backward-char { 4919 local arg; ble-edit/content/get-arg 1 4920 ((arg==0)) && return 0 4921 4922 # keymap/vi.sh (white widget) 4923 [[ $_ble_decode_keymap == vi_imap ]] && ble/keymap:vi/undo/add more 4924 4925 ble/widget/.delete-char "$((-arg))" || ble/widget/.bell 4926 4927 # keymap/vi.sh (white widget) 4928 [[ $_ble_decode_keymap == vi_imap ]] && ble/keymap:vi/undo/add more 4929 } 4930 4931 _ble_edit_exit_count=0 4932 function ble/widget/exit { 4933 ble-edit/content/clear-arg 4934 4935 if [[ $WIDGET == "$LASTWIDGET" ]]; then 4936 ((_ble_edit_exit_count++)) 4937 else 4938 _ble_edit_exit_count=1 4939 fi 4940 4941 local ret; ble-edit/eval-IGNOREEOF 4942 if ((_ble_edit_exit_count<=ret)); then 4943 local remain=$((ret-_ble_edit_exit_count+1)) 4944 ble/widget/.bell 'IGNOREEOF' 4945 ble/widget/print "IGNOREEOF($remain): Use \"exit\" to leave the shell." 4946 return 0 4947 fi 4948 4949 local opts=$1 4950 ((_ble_bash>=40000)) && shopt -q checkjobs &>/dev/null && opts=$opts:checkjobs 4951 4952 if [[ $bleopt_allow_exit_with_jobs ]]; then 4953 local ret 4954 if ble/util/assign ret 'compgen -A stopped -- ""' 2>/dev/null; [[ $ret ]]; then 4955 opts=$opts:twice 4956 elif [[ :$opts: == *:checkjobs:* ]]; then 4957 if ble/util/assign ret 'compgen -A running -- ""' 2>/dev/null; [[ $ret ]]; then 4958 opts=$opts:twice 4959 fi 4960 else 4961 opts=$opts:force 4962 fi 4963 fi 4964 4965 if ! [[ :$opts: == *:force:* || :$opts: == *:twice:* && _ble_edit_exit_count -ge 2 ]]; then 4966 # job が残っている場合 4967 local joblist 4968 ble/util/joblist 4969 if ((${#joblist[@]})); then 4970 ble/widget/.bell "exit: There are remaining jobs." 4971 local q=\' Q="'\''" message= 4972 if [[ :$opts: == *:twice:* ]]; then 4973 message='There are remaining jobs. Input the same key to exit the shell anyway.' 4974 else 4975 message='There are remaining jobs. Use "exit" to leave the shell.' 4976 fi 4977 ble/widget/internal-command "ble/util/print '${_ble_term_setaf[12]}[ble: ${message//$q/$Q}]$_ble_term_sgr0'; jobs" 4978 return "$?" 4979 fi 4980 elif [[ :$opts: == *:checkjobs:* ]]; then 4981 local joblist 4982 ble/util/joblist 4983 ((${#joblist[@]})) && printf '%s\n' "${#joblist[@]}" 4984 fi 4985 4986 #_ble_edit_detach_flag=exit 4987 4988 #ble/term/visible-bell ' Bye!! ' # 最後に vbell を出すと一時ファイルが残る 4989 _ble_edit_line_disabled=1 ble/textarea#render 4990 4991 # Note: bleopt_syntax_debug=1 の時 ble/textarea#render の中で info が設定されるので、 4992 # これは ble/textarea#render より後である必要がある。 4993 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 4994 4995 local -a DRAW_BUFF=() 4996 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$_ble_textarea_gendx" "$_ble_textarea_gendy" 4997 ble/canvas/bflush.draw 4998 ble/util/buffer.print "${_ble_term_setaf[12]}[ble: exit]$_ble_term_sgr0" 4999 ble/util/buffer.flush >&2 5000 5001 # Note: ジョブが残っている場合でも強制終了させる為 2 回連続で呼び出す必要がある。 5002 builtin exit 0 &>/dev/null 5003 builtin exit 0 &>/dev/null 5004 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 5005 return 1 5006 } 5007 function ble/widget/delete-forward-char-or-exit { 5008 if [[ $_ble_edit_str ]]; then 5009 ble/widget/delete-forward-char 5010 else 5011 ble/widget/exit 5012 fi 5013 } 5014 function ble/widget/delete-forward-backward-char { 5015 ble-edit/content/clear-arg 5016 ble/widget/.delete-char 0 || ble/widget/.bell 5017 } 5018 function ble/widget/delete-forward-char-or-list { 5019 local right=${_ble_edit_str:_ble_edit_ind} 5020 if [[ ! $right || $right == $'\n'* ]]; then 5021 ble/widget/complete show_menu 5022 else 5023 ble/widget/delete-forward-char 5024 fi 5025 } 5026 5027 function ble/widget/delete-horizontal-space { 5028 local arg; ble-edit/content/get-arg '' 5029 5030 local b=0 rex=$'[ \t]+$' 5031 [[ ${_ble_edit_str::_ble_edit_ind} =~ $rex ]] && 5032 b=${#BASH_REMATCH} 5033 5034 local a=0 rex=$'^[ \t]+' 5035 [[ ! $arg && ${_ble_edit_str:_ble_edit_ind} =~ $rex ]] && 5036 a=${#BASH_REMATCH} 5037 5038 ble/widget/.delete-range "$((_ble_edit_ind-b))" "$((_ble_edit_ind+a))" 5039 } 5040 5041 # 5042 # **** cursor move **** @edit.cursor 5043 5044 function ble/widget/.forward-char { 5045 ((_ble_edit_ind+=${1:-1})) 5046 if ((_ble_edit_ind>${#_ble_edit_str})); then 5047 _ble_edit_ind=${#_ble_edit_str} 5048 return 1 5049 elif ((_ble_edit_ind<0)); then 5050 _ble_edit_ind=0 5051 return 1 5052 fi 5053 } 5054 function ble/widget/forward-char { 5055 local arg; ble-edit/content/get-arg 1 5056 ((arg==0)) && return 0 5057 ble/widget/.forward-char "$arg" || ble/widget/.bell 5058 } 5059 function ble/widget/backward-char { 5060 local arg; ble-edit/content/get-arg 1 5061 ((arg==0)) && return 0 5062 ble/widget/.forward-char "$((-arg))" || ble/widget/.bell 5063 } 5064 5065 _ble_edit_character_search_arg= 5066 function ble/widget/character-search-forward { 5067 local arg; ble-edit/content/get-arg 1 5068 _ble_edit_character_search_arg=$arg 5069 _ble_edit_mark_active= 5070 _ble_decode_char__hook=ble/widget/character-search.hook 5071 } 5072 function ble/widget/character-search-backward { 5073 local arg; ble-edit/content/get-arg 1 5074 ((_ble_edit_character_search_arg=-arg)) 5075 _ble_edit_mark_active= 5076 _ble_decode_char__hook=ble/widget/character-search.hook 5077 } 5078 function ble/widget/character-search.hook { 5079 local char=${KEYS[0]} 5080 local ret; ble/util/c2s "${KEYS[0]}"; local c=$ret 5081 [[ $c ]] || return 1 # Note: C-@ の時は無視 5082 local arg=$_ble_edit_character_search_arg 5083 if ((arg>0)); then 5084 local right=${_ble_edit_str:_ble_edit_ind+1} 5085 if ble/string#index-of "$right" "$c" "$arg"; then 5086 ((_ble_edit_ind=_ble_edit_ind+1+ret)) 5087 elif ble/string#last-index-of "$right" "$c"; then 5088 ble/widget/.bell "${arg}th character not found" 5089 ((_ble_edit_ind=_ble_edit_ind+1+ret)) 5090 else 5091 ble/widget/.bell 'character not found' 5092 return 1 5093 fi 5094 elif ((arg<0)); then 5095 local left=${_ble_edit_str::_ble_edit_ind} 5096 if ble/string#last-index-of "$left" "$c" "$((-arg))"; then 5097 _ble_edit_ind=$ret 5098 elif ble/string#index-of "$left" "$c"; then 5099 ble/widget/.bell "$((-arg))th last character not found" 5100 _ble_edit_ind=$ret 5101 else 5102 ble/widget/.bell 'character not found' 5103 return 1 5104 fi 5105 fi 5106 return 0 5107 } 5108 5109 ## @fn ble/widget/.locate-forward-byte delta 5110 ## @param[in] delta 5111 ## @var[in,out] index 5112 function ble/widget/.locate-forward-byte { 5113 local delta=$1 ret 5114 if ((delta==0)); then 5115 return 0 5116 elif ((delta>0)); then 5117 local right=${_ble_edit_str:index:delta} 5118 local rlen=${#right} 5119 ble/util/strlen "$right"; local rsz=$ret 5120 if ((delta>=rsz)); then 5121 ((index+=rlen)) 5122 ((delta==rsz)); return "$?" 5123 else 5124 # 二分法 5125 while ((delta&&rlen>=2)); do 5126 local mlen=$((rlen/2)) 5127 local m=${right::mlen} 5128 ble/util/strlen "$m"; local msz=$ret 5129 if ((delta>=msz)); then 5130 right=${right:mlen} 5131 ((index+=mlen, 5132 rlen-=mlen, 5133 delta-=msz)) 5134 ((rlen>delta)) && 5135 right=${right::delta} rlen=$delta 5136 else 5137 right=$m rlen=$mlen 5138 fi 5139 done 5140 ((delta&&rlen&&index++)) 5141 return 0 5142 fi 5143 elif ((delta<0)); then 5144 ((delta=-delta)) 5145 local left=${_ble_edit_str::index} 5146 local llen=${#left} 5147 ((llen>delta)) && left=${left:llen-delta} llen=$delta 5148 ble/util/strlen "$left"; local lsz=$ret 5149 if ((delta>=lsz)); then 5150 ((index-=llen)) 5151 ((delta==lsz)); return "$?" 5152 else 5153 # 二分法 5154 while ((delta&&llen>=2)); do 5155 local mlen=$((llen/2)) 5156 local m=${left:llen-mlen} 5157 ble/util/strlen "$m"; local msz=$ret 5158 if ((delta>=msz)); then 5159 left=${left::llen-mlen} 5160 ((index-=mlen, 5161 llen-=mlen, 5162 delta-=msz)) 5163 ((llen>delta)) && 5164 left=${left:llen-delta} llen=$delta 5165 else 5166 left=$m llen=$mlen 5167 fi 5168 done 5169 ((delta&&llen&&index--)) 5170 return 0 5171 fi 5172 fi 5173 } 5174 function ble/widget/forward-byte { 5175 local arg; ble-edit/content/get-arg 1 5176 ((arg==0)) && return 0 5177 local index=$_ble_edit_ind 5178 ble/widget/.locate-forward-byte "$arg" || ble/widget/.bell 5179 _ble_edit_ind=$index 5180 } 5181 function ble/widget/backward-byte { 5182 local arg; ble-edit/content/get-arg 1 5183 ((arg==0)) && return 0 5184 local index=$_ble_edit_ind 5185 ble/widget/.locate-forward-byte "$((-arg))" || ble/widget/.bell 5186 _ble_edit_ind=$index 5187 } 5188 5189 function ble/widget/end-of-text { 5190 local arg; ble-edit/content/get-arg '' 5191 if [[ $arg ]]; then 5192 if ((arg>=10)); then 5193 _ble_edit_ind=0 5194 else 5195 ((arg<0&&(arg=0))) 5196 local index=$(((19-2*arg)*${#_ble_edit_str}/20)) 5197 local ret; ble-edit/content/find-logical-bol "$index" 5198 _ble_edit_ind=$ret 5199 fi 5200 else 5201 _ble_edit_ind=${#_ble_edit_str} 5202 fi 5203 } 5204 function ble/widget/beginning-of-text { 5205 local arg; ble-edit/content/get-arg '' 5206 if [[ $arg ]]; then 5207 if ((arg>=10)); then 5208 _ble_edit_ind=${#_ble_edit_str} 5209 else 5210 ((arg<0&&(arg=0))) 5211 local index=$(((2*arg+1)*${#_ble_edit_str}/20)) 5212 local ret; ble-edit/content/find-logical-bol "$index" 5213 _ble_edit_ind=$ret 5214 fi 5215 else 5216 _ble_edit_ind=0 5217 fi 5218 } 5219 5220 function ble/widget/beginning-of-logical-line { 5221 local arg; ble-edit/content/get-arg 1 5222 local ret; ble-edit/content/find-logical-bol "$_ble_edit_ind" "$((arg-1))" 5223 _ble_edit_ind=$ret 5224 } 5225 function ble/widget/end-of-logical-line { 5226 local arg; ble-edit/content/get-arg 1 5227 local ret; ble-edit/content/find-logical-eol "$_ble_edit_ind" "$((arg-1))" 5228 _ble_edit_ind=$ret 5229 } 5230 5231 ## @widget kill-backward-logical-line 5232 ## 5233 ## 現在の行の行頭まで削除する。 5234 ## 既に行頭にいる場合には直前の改行を削除する。 5235 ## 引数 arg を与えたときは arg 行前の行末まで削除する。 5236 ## 5237 function ble/widget/kill-backward-logical-line { 5238 local arg; ble-edit/content/get-arg '' 5239 if [[ $arg ]]; then 5240 local ret; ble-edit/content/find-logical-eol "$_ble_edit_ind" "$((-arg))"; local index=$ret 5241 if ((arg>0)); then 5242 if ((_ble_edit_ind<=index)); then 5243 index=0 5244 else 5245 ble/string#count-char "${_ble_edit_str:index:_ble_edit_ind-index}" $'\n' 5246 ((ret<arg)) && index=0 5247 fi 5248 [[ $flag_beg ]] && index=0 5249 fi 5250 ret=$index 5251 else 5252 local ret; ble-edit/content/find-logical-bol 5253 # 行頭にいるとき無引数で呼び出すと、直前の改行を削除 5254 ((0<ret&&ret==_ble_edit_ind&&ret--)) 5255 fi 5256 ble/widget/.kill-range "$ret" "$_ble_edit_ind" 5257 } 5258 ## @widget kill-forward-logical-line 5259 ## 5260 ## 現在の行の行末まで削除する。 5261 ## 既に行末にいる場合は直後の改行を削除する。 5262 ## 引数 arg を与えたときは arg 行次の行頭まで削除する。 5263 ## 5264 function ble/widget/kill-forward-logical-line { 5265 local arg; ble-edit/content/get-arg '' 5266 if [[ $arg ]]; then 5267 local ret; ble-edit/content/find-logical-bol "$_ble_edit_ind" "$arg"; local index=$ret 5268 if ((arg>0)); then 5269 if ((index<=_ble_edit_ind)); then 5270 index=${#_ble_edit_str} 5271 else 5272 ble/string#count-char "${_ble_edit_str:_ble_edit_ind:index-_ble_edit_ind}" $'\n' 5273 ((ret<arg)) && index=${#_ble_edit_str} 5274 fi 5275 fi 5276 ret=$index 5277 else 5278 local ret; ble-edit/content/find-logical-eol 5279 # 行末にいるとき無引数で呼び出すと、直後の改行を削除 5280 ((ret<${#_ble_edit_str}&&_ble_edit_ind==ret&&ret++)) 5281 fi 5282 ble/widget/.kill-range "$_ble_edit_ind" "$ret" 5283 } 5284 function ble/widget/kill-logical-line { 5285 local arg; ble-edit/content/get-arg 0 5286 local bofs=0 eofs=0 bol=0 eol=${#_ble_edit_str} 5287 ((arg>0?(eofs=arg-1):(arg<0&&(bofs=arg+1)))) 5288 ble-edit/content/find-logical-bol "$_ble_edit_ind" "$bofs" && local bol=$ret 5289 ble-edit/content/find-logical-eol "$_ble_edit_ind" "$eofs" && local eol=$ret 5290 [[ ${_ble_edit_str:eol:1} == $'\n' ]] && ((eol++)) 5291 ((bol<eol)) && ble/widget/.kill-range "$bol" "$eol" 5292 } 5293 5294 function ble/widget/forward-history-line.impl { 5295 local arg=$1 5296 ((arg==0)) && return 0 5297 5298 local rest=$((arg>0?arg:-arg)) 5299 if ((arg>0)); then 5300 if [[ ! $_ble_history_prefix && ! $_ble_history_load_done ]]; then 5301 # 履歴を未だロードしていないので次の項目は存在しない 5302 ble/widget/.bell 'end of history' 5303 return 1 5304 fi 5305 fi 5306 5307 ble/history/initialize 5308 local index=$_ble_history_INDEX 5309 5310 local expr_next='--index>=0' 5311 if ((arg>0)); then 5312 local count=$_ble_history_COUNT 5313 expr_next="++index<=$count" 5314 fi 5315 5316 while ((expr_next)); do 5317 if ((--rest<=0)); then 5318 ble-edit/history/goto "$index" # 位置は goto に任せる 5319 return "$?" 5320 fi 5321 5322 local entry; ble/history/get-edited-entry "$index" 5323 if [[ $entry == *$'\n'* ]]; then 5324 local ret; ble/string#count-char "$entry" $'\n' 5325 if ((rest<=ret)); then 5326 ble-edit/history/goto "$index" 5327 if ((arg>0)); then 5328 ble-edit/content/find-logical-eol 0 "$rest" 5329 else 5330 ble-edit/content/find-logical-eol "${#entry}" "$((-rest))" 5331 fi 5332 _ble_edit_ind=$ret 5333 return 0 5334 fi 5335 ((rest-=ret)) 5336 fi 5337 done 5338 5339 if ((arg>0)); then 5340 ble-edit/history/goto "$count" 5341 _ble_edit_ind=${#_ble_edit_str} 5342 ble/widget/.bell 'end of history' 5343 else 5344 ble-edit/history/goto 0 5345 _ble_edit_ind=0 5346 ble/widget/.bell 'beginning of history' 5347 fi 5348 return 0 5349 } 5350 5351 ## @fn ble/widget/forward-logical-line.impl arg opts 5352 ## 5353 ## @param arg 5354 ## 移動量を表す整数を指定する。 5355 ## @param opts 5356 ## コロン区切りでオプションを指定する。 5357 ## 5358 function ble/widget/forward-logical-line.impl { 5359 local arg=$1 opts=$2 5360 ((arg==0)) && return 0 5361 5362 # 事前チェック 5363 local ind=$_ble_edit_ind 5364 if ((arg>0)); then 5365 ((ind<${#_ble_edit_str})) || return 1 5366 else 5367 ((ind>0)) || return 1 5368 fi 5369 5370 local ret; ble-edit/content/find-logical-bol "$ind" "$arg"; local bol2=$ret 5371 if ((arg>0)); then 5372 if ((ind<bol2)); then 5373 ble/string#count-char "${_ble_edit_str:ind:bol2-ind}" $'\n' 5374 ((arg-=ret)) 5375 fi 5376 else 5377 if ((ind>bol2)); then 5378 ble/string#count-char "${_ble_edit_str:bol2:ind-bol2}" $'\n' 5379 ((arg+=ret)) 5380 fi 5381 fi 5382 5383 # 同じ履歴項目内に移動先行が見つかった場合 5384 if ((arg==0)); then 5385 # 元と同じ列に移動して戻る。 5386 ble-edit/content/find-logical-bol "$ind" ; local bol1=$ret 5387 ble-edit/content/find-logical-eol "$bol2"; local eol2=$ret 5388 local dst=$((bol2+ind-bol1)) 5389 ((_ble_edit_ind=dst<eol2?dst:eol2)) 5390 return 0 5391 fi 5392 5393 # 取り敢えず移動できる所まで移動する 5394 if ((arg>0)); then 5395 ble-edit/content/find-logical-eol "$bol2" 5396 else 5397 ret=$bol2 5398 fi 5399 _ble_edit_ind=$ret 5400 5401 # 履歴項目の移動を行う場合 5402 if [[ :$opts: == *:history:* && ! $_ble_edit_mark_active ]]; then 5403 ble/widget/forward-history-line.impl "$arg" 5404 return "$?" 5405 fi 5406 5407 # 移動先行がない場合は bell 5408 if ((arg>0)); then 5409 ble/widget/.bell 'end of string' 5410 else 5411 ble/widget/.bell 'beginning of string' 5412 fi 5413 return 0 5414 } 5415 function ble/widget/forward-logical-line { 5416 local opts=$1 5417 local arg; ble-edit/content/get-arg 1 5418 ble/widget/forward-logical-line.impl "$arg" "$opts" 5419 } 5420 function ble/widget/backward-logical-line { 5421 local opts=$1 5422 local arg; ble-edit/content/get-arg 1 5423 ble/widget/forward-logical-line.impl "$((-arg))" "$opts" 5424 } 5425 5426 ## @fn ble/keymap:emacs/find-graphical-eol [index [offset]] 5427 ## @var[out] ret 5428 function ble/keymap:emacs/find-graphical-eol { 5429 local axis=${1:-$_ble_edit_ind} arg=${2:-0} 5430 local x y index 5431 ble/textmap#getxy.cur "$axis" 5432 ble/textmap#get-index-at 0 "$((y+arg+1))" 5433 if ((index>0)); then 5434 local ax ay 5435 ble/textmap#getxy.cur --prefix=a "$index" 5436 ((ay>y+arg&&index--)) 5437 fi 5438 ret=$index 5439 } 5440 5441 function ble/widget/beginning-of-graphical-line { 5442 ble/textmap#is-up-to-date || ble/widget/.update-textmap 5443 local arg; ble-edit/content/get-arg 1 5444 local x y index 5445 ble/textmap#getxy.cur "$_ble_edit_ind" 5446 ble/textmap#get-index-at 0 "$((y+arg-1))" 5447 _ble_edit_ind=$index 5448 } 5449 function ble/widget/end-of-graphical-line { 5450 ble/textmap#is-up-to-date || ble/widget/.update-textmap 5451 local arg; ble-edit/content/get-arg 1 5452 local ret; ble/keymap:emacs/find-graphical-eol "$_ble_edit_ind" "$((arg-1))" 5453 _ble_edit_ind=$ret 5454 } 5455 5456 ## @widget kill-backward-graphical-line 5457 ## 現在の行の表示行頭まで削除する。 5458 ## 既に表示行頭にいる場合には直前の文字を削除する。 5459 ## 引数 arg を与えたときは arg 行前の表示行末まで削除する。 5460 function ble/widget/kill-backward-graphical-line { 5461 ble/textmap#is-up-to-date || ble/widget/.update-textmap 5462 local arg; ble-edit/content/get-arg '' 5463 if [[ ! $arg ]]; then 5464 local x y index 5465 ble/textmap#getxy.cur "$_ble_edit_ind" 5466 ble/textmap#get-index-at 0 "$y" 5467 ((index==_ble_edit_ind&&index>0&&index--)) 5468 ble/widget/.kill-range "$index" "$_ble_edit_ind" 5469 else 5470 local ret; ble/keymap:emacs/find-graphical-eol "$_ble_edit_ind" "$((-arg))" 5471 ble/widget/.kill-range "$ret" "$_ble_edit_ind" 5472 fi 5473 } 5474 ## @widget kill-forward-graphical-line 5475 ## 現在の行の表示行末まで削除する。 5476 ## 既に表示行末 (折り返し時は行の最後の文字の手前) にいる場合は直後の文字を削除する。 5477 ## 引数 arg を与えたときは arg 行後の表示行頭まで削除する。 5478 function ble/widget/kill-forward-graphical-line { 5479 ble/textmap#is-up-to-date || ble/widget/.update-textmap 5480 local arg; ble-edit/content/get-arg '' 5481 local x y index ax ay 5482 ble/textmap#getxy.cur "$_ble_edit_ind" 5483 ble/textmap#get-index-at 0 "$((y+${arg:-1}))" 5484 if [[ ! $arg ]] && ((_ble_edit_ind<index-1)); then 5485 # 無引数でかつ行末より前にいた時、 5486 # 行頭までではなくその前の行末までしか消さない。 5487 ble/textmap#getxy.cur --prefix=a "$index" 5488 ((ay>y&&index--)) 5489 fi 5490 ble/widget/.kill-range "$_ble_edit_ind" "$index" 5491 } 5492 ## @widget kill-graphical-line 5493 ## 現在の表示行を削除する。 5494 function ble/widget/kill-graphical-line { 5495 ble/textmap#is-up-to-date || ble/widget/.update-textmap 5496 local arg; ble-edit/content/get-arg 0 5497 local bofs=0 eofs=0 5498 ((arg>0?(eofs=arg-1):(arg<0&&(bofs=arg+1)))) 5499 local x y index ax ay 5500 ble/textmap#getxy.cur "$_ble_edit_ind" 5501 ble/textmap#get-index-at 0 "$((y+bofs))" ; local bol=$index 5502 ble/textmap#get-index-at 0 "$((y+eofs+1))"; local eol=$index 5503 ((bol<eol)) && ble/widget/.kill-range "$bol" "$eol" 5504 } 5505 5506 function ble/widget/forward-graphical-line.impl { 5507 ble/textmap#is-up-to-date || ble/widget/.update-textmap 5508 local arg=$1 opts=$2 5509 ((arg==0)) && return 0 5510 5511 local x y index ax ay 5512 ble/textmap#getxy.cur "$_ble_edit_ind" 5513 ble/textmap#get-index-at "$x" "$((y+arg))" 5514 ble/textmap#getxy.cur --prefix=a "$index" 5515 ((arg-=ay-y)) 5516 _ble_edit_ind=$index # 何れにしても移動は行う 5517 5518 # 現在の履歴項目内で移動が完結する場合 5519 ((arg==0)) && return 0 5520 5521 # 履歴項目の移動を行う場合 5522 if [[ :$opts: == *:history:* && ! $_ble_edit_mark_active ]]; then 5523 ble/widget/forward-history-line.impl "$arg" 5524 return "$?" 5525 fi 5526 5527 if ((arg>0)); then 5528 ble/widget/.bell 'end of string' 5529 else 5530 ble/widget/.bell 'beginning of string' 5531 fi 5532 return 0 5533 } 5534 5535 function ble/widget/forward-graphical-line { 5536 local opts=$1 5537 local arg; ble-edit/content/get-arg 1 5538 ble/widget/forward-graphical-line.impl "$arg" "$opts" 5539 } 5540 function ble/widget/backward-graphical-line { 5541 local opts=$1 5542 local arg; ble-edit/content/get-arg 1 5543 ble/widget/forward-graphical-line.impl "$((-arg))" "$opts" 5544 } 5545 5546 function ble/widget/beginning-of-line { 5547 if ble/edit/performs-on-graphical-line; then 5548 ble/widget/beginning-of-graphical-line 5549 else 5550 ble/widget/beginning-of-logical-line 5551 fi 5552 } 5553 function ble/widget/non-space-beginning-of-line { 5554 local old=$_ble_edit_ind 5555 ble/widget/beginning-of-logical-line 5556 local bol=$_ble_edit_ind ret= 5557 ble-edit/content/find-non-space "$bol" 5558 [[ $ret == $old ]] && ret=$bol # toggle 5559 _ble_edit_ind=$ret 5560 return 0 5561 } 5562 function ble/widget/end-of-line { 5563 if ble/edit/performs-on-graphical-line; then 5564 ble/widget/end-of-graphical-line 5565 else 5566 ble/widget/end-of-logical-line 5567 fi 5568 } 5569 function ble/widget/kill-backward-line { 5570 if ble/edit/performs-on-graphical-line; then 5571 ble/widget/kill-backward-graphical-line 5572 else 5573 ble/widget/kill-backward-logical-line 5574 fi 5575 } 5576 function ble/widget/kill-forward-line { 5577 if ble/edit/performs-on-graphical-line; then 5578 ble/widget/kill-forward-graphical-line 5579 else 5580 ble/widget/kill-forward-logical-line 5581 fi 5582 } 5583 function ble/widget/kill-line { 5584 if ble/edit/performs-on-graphical-line; then 5585 ble/widget/kill-graphical-line 5586 else 5587 ble/widget/kill-logical-line 5588 fi 5589 } 5590 function ble/widget/forward-line { 5591 if ble/edit/use-textmap; then 5592 ble/widget/forward-graphical-line "$@" 5593 else 5594 ble/widget/forward-logical-line "$@" 5595 fi 5596 } 5597 function ble/widget/backward-line { 5598 if ble/edit/use-textmap; then 5599 ble/widget/backward-graphical-line "$@" 5600 else 5601 ble/widget/backward-logical-line "$@" 5602 fi 5603 } 5604 5605 # 5606 # **** word location **** @edit.word 5607 5608 ## @fn ble/edit/word:eword/setup 5609 ## @fn ble/edit/word:cword/setup 5610 ## @fn ble/edit/word:uword/setup 5611 ## @fn ble/edit/word:sword/setup 5612 ## @fn ble/edit/word:fword/setup 5613 ## @var[out] word_set word_sep 5614 function ble/edit/word:eword/setup { 5615 word_set='a-zA-Z0-9'; word_sep="^$word_set" 5616 } 5617 function ble/edit/word:cword/setup { 5618 word_set='_a-zA-Z0-9'; word_sep="^$word_set" 5619 } 5620 function ble/edit/word:uword/setup { 5621 word_sep="$_ble_term_IFS"; word_set="^$word_sep" 5622 } 5623 function ble/edit/word:sword/setup { 5624 word_sep=$'|&;()<> \t\n'; word_set="^$word_sep" 5625 } 5626 function ble/edit/word:fword/setup { 5627 word_sep="/$_ble_term_IFS"; word_set="^$word_sep" 5628 } 5629 5630 ## @fn ble/edit/word/skip-backward set 5631 ## @fn ble/edit/word/skip-forward set 5632 ## @var[in,out] x 5633 function ble/edit/word/skip-backward { 5634 local set=$1 head=${_ble_edit_str::x} 5635 head=${head##*[$set]} 5636 ((x-=${#head},${#head})) 5637 } 5638 function ble/edit/word/skip-forward { 5639 local set=$1 tail=${_ble_edit_str:x} 5640 tail=${tail%%[$set]*} 5641 ((x+=${#tail},${#tail})) 5642 } 5643 5644 ## @fn ble/edit/word/locate-backward x arg 5645 ## 左側の単語の範囲を特定します。 5646 ## @param[in] x arg 5647 ## @var[in] word_set word_sep 5648 ## @var[out] a b c 5649 ## 5650 ## |---|www|---| 5651 ## a b c x 5652 ## 5653 function ble/edit/word/locate-backward { 5654 local x=${1:-$_ble_edit_ind} arg=${2:-1} 5655 while ((arg--)); do 5656 ble/edit/word/skip-backward "$word_set"; c=$x 5657 ble/edit/word/skip-backward "$word_sep"; b=$x 5658 done 5659 ble/edit/word/skip-backward "$word_set"; a=$x 5660 } 5661 ## @fn ble/edit/word/locate-forward x arg 5662 ## 右側の単語の範囲を特定します。 5663 ## @param[in] x arg 5664 ## @var[in] word_set word_sep 5665 ## @var[out] s t u 5666 ## 5667 ## |---|www|---| 5668 ## x s t u 5669 ## 5670 function ble/edit/word/locate-forward { 5671 local x=${1:-$_ble_edit_ind} arg=${2:-1} 5672 while ((arg--)); do 5673 ble/edit/word/skip-forward "$word_set"; s=$x 5674 ble/edit/word/skip-forward "$word_sep"; t=$x 5675 done 5676 ble/edit/word/skip-forward "$word_set"; u=$x 5677 } 5678 5679 ## @fn ble/edit/word/forward-range arg 5680 ## @fn ble/edit/word/backward-range arg 5681 ## @fn ble/edit/word/current-range arg 5682 ## @var[in,out] x y 5683 function ble/edit/word/forward-range { 5684 local arg=$1; ((arg)) || arg=1 5685 if ((arg<0)); then 5686 ble/edit/word/backward-range "$((-arg))" 5687 return "$?" 5688 fi 5689 local s t u; ble/edit/word/locate-forward "$x" "$arg"; y=$t 5690 } 5691 function ble/edit/word/backward-range { 5692 local arg=$1; ((arg)) || arg=1 5693 if ((arg<0)); then 5694 ble/edit/word/forward-range "$((-arg))" 5695 return "$?" 5696 fi 5697 local a b c; ble/edit/word/locate-backward "$x" "$arg"; y=$b 5698 } 5699 function ble/edit/word/current-range { 5700 local arg=$1; ((arg)) || arg=1 5701 if ((arg>0)); then 5702 local a b c; ble/edit/word/locate-backward "$x" 5703 local s t u; ble/edit/word/locate-forward "$a" "$arg" 5704 ((y=a,x<t&&(x=t))) 5705 elif ((arg<0)); then 5706 local s t u; ble/edit/word/locate-forward "$x" 5707 local a b c; ble/edit/word/locate-backward "$u" "$((-arg))" 5708 ((b<x&&(x=b),y=u)) 5709 fi 5710 return 0 5711 } 5712 5713 ## @fn ble/widget/word.impl type direction operator 5714 function ble/widget/word.impl { 5715 local operator=$1 direction=$2 wtype=$3 5716 5717 local arg; ble-edit/content/get-arg 1 5718 local word_set word_sep; ble/edit/word:"$wtype"/setup 5719 5720 local x=$_ble_edit_ind y=$_ble_edit_ind 5721 ble/function#try ble/edit/word/"$direction"-range "$arg" 5722 if ((x==y)); then 5723 ble/widget/.bell 5724 return 1 5725 fi 5726 5727 case $operator in 5728 (goto) _ble_edit_ind=$y ;; 5729 5730 (delete) 5731 # keymap/vi.sh (white list に登録されている編集関数) 5732 [[ $_ble_decode_keymap == vi_imap && $direction == backward ]] && 5733 ble/keymap:vi/undo/add more 5734 5735 ble/widget/.delete-range "$x" "$y" 5736 5737 # keymap/vi.sh (white list に登録されている編集関数) 5738 [[ $_ble_decode_keymap == vi_imap && $direction == backward ]] && 5739 ble/keymap:vi/undo/add more ;; 5740 5741 (kill) ble/widget/.kill-range "$x" "$y" ;; 5742 (copy) ble/widget/.copy-range "$x" "$y" ;; 5743 (*) ble/widget/.bell; return 1 ;; 5744 esac 5745 } 5746 5747 function ble/widget/transpose-words.impl1 { 5748 local wtype=$1 arg=$2 5749 local word_set word_sep; ble/edit/word:"$wtype"/setup 5750 if ((arg==0)); then 5751 local x=$_ble_edit_ind 5752 ble/edit/word/skip-forward "$word_set" 5753 ble/edit/word/skip-forward "$word_sep"; local e1=$x 5754 ble/edit/word/skip-backward "$word_sep"; local b1=$x 5755 local x=$_ble_edit_mark 5756 ble/edit/word/skip-forward "$word_set" 5757 ble/edit/word/skip-forward "$word_sep"; local e2=$x 5758 ble/edit/word/skip-backward "$word_sep"; local b2=$x 5759 else 5760 local x=$_ble_edit_ind 5761 ble/edit/word/skip-backward "$word_set" 5762 ble/edit/word/skip-backward "$word_sep"; local b1=$x 5763 ble/edit/word/skip-forward "$word_sep"; local e1=$x 5764 if ((arg>0)); then 5765 x=$e1 5766 ble/edit/word/skip-forward "$word_set"; local b2=$x 5767 while ble/edit/word/skip-forward "$word_sep" || return 1; ((--arg>0)); do 5768 ble/edit/word/skip-forward "$word_set" 5769 done; local e2=$x 5770 else 5771 x=$b1 5772 ble/edit/word/skip-backward "$word_set"; local e2=$x 5773 while ble/edit/word/skip-backward "$word_sep" || return 1; ((++arg<0)); do 5774 ble/edit/word/skip-backward "$word_set" 5775 done; local b2=$x 5776 fi 5777 fi 5778 5779 ((b1>b2)) && local b1=$b2 e1=$e2 b2=$b1 e2=$e1 5780 if ! ((b1<e1&&e1<=b2&&b2<e2)); then 5781 ble/widget/.bell 5782 return 1 5783 fi 5784 5785 local word1=${_ble_edit_str:b1:e1-b1} 5786 local word2=${_ble_edit_str:b2:e2-b2} 5787 local sep=${_ble_edit_str:e1:b2-e1} 5788 ble/widget/.replace-range "$b1" "$e2" "$word2$sep$word1" 5789 _ble_edit_ind=$e2 5790 } 5791 function ble/widget/transpose-words.impl { 5792 local wtype=$1 arg; ble-edit/content/get-arg 1 5793 ble/widget/transpose-words.impl1 "$wtype" "$arg" && return 0 5794 ble/widget/.bell 5795 return 1 5796 } 5797 5798 ## @fn ble/widget/filter-word.impl xword filter 5799 ## keymap: safe vi_nmap 5800 function ble/widget/filter-word.impl { 5801 local xword=$1 filter=$2 5802 5803 # determine arg 5804 if [[ $_ble_decode_keymap == vi_nmap ]]; then 5805 local ARG FLAG REG; ble/keymap:vi/get-arg 1 5806 local arg=$ARG 5807 else 5808 local arg; ble-edit/content/get-arg 1 5809 fi 5810 5811 local word_set word_sep; ble/edit/word:"$xword"/setup 5812 local x=$_ble_edit_ind s t u 5813 ble/edit/word/locate-forward "$x" "$arg" 5814 if ((x==t)); then 5815 ble/widget/.bell 5816 [[ $_ble_decode_keymap == vi_nmap ]] && 5817 ble/keymap:vi/adjust-command-mode 5818 return 1 5819 fi 5820 5821 local word=${_ble_edit_str:x:t-x} 5822 "$filter" "$word" 5823 [[ $word != $ret ]] && 5824 ble-edit/content/replace "$x" "$t" "$ret" 5825 5826 if [[ $_ble_decode_keymap == vi_nmap ]]; then 5827 ble/keymap:vi/mark/set-previous-edit-area "$x" "$t" 5828 ble/keymap:vi/repeat/record 5829 ble/keymap:vi/adjust-command-mode 5830 fi 5831 _ble_edit_ind=$t 5832 } 5833 5834 #%define 2 5835 function ble/widget/forward-XWORD { ble/widget/word.impl goto forward XWORD; } 5836 function ble/widget/backward-XWORD { ble/widget/word.impl goto backward XWORD; } 5837 #%define 1 5838 function ble/widget/OPERATOR-forward-XWORD { ble/widget/word.impl OPERATOR forward XWORD; } 5839 function ble/widget/OPERATOR-backward-XWORD { ble/widget/word.impl OPERATOR backward XWORD; } 5840 function ble/widget/OPERATOR-XWORD { ble/widget/word.impl OPERATOR current XWORD; } 5841 #%end 5842 #%expand 1.r/OPERATOR/delete/ 5843 #%expand 1.r/OPERATOR/kill/ 5844 #%expand 1.r/OPERATOR/copy/ 5845 function ble/widget/capitalize-XWORD { ble/widget/filter-word.impl XWORD ble/string#capitalize; } 5846 function ble/widget/downcase-XWORD { ble/widget/filter-word.impl XWORD ble/string#tolower; } 5847 function ble/widget/upcase-XWORD { ble/widget/filter-word.impl XWORD ble/string#toupper; } 5848 function ble/widget/transpose-XWORDs { ble/widget/transpose-words.impl XWORD; } 5849 #%end 5850 #%expand 2.r/XWORD/eword/ 5851 #%expand 2.r/XWORD/cword/ 5852 #%expand 2.r/XWORD/uword/ 5853 #%expand 2.r/XWORD/sword/ 5854 #%expand 2.r/XWORD/fword/ 5855 5856 #------------------------------------------------------------------------------ 5857 # **** ble-edit/exec **** @edit.exec 5858 5859 _ble_edit_exec_lines=() 5860 _ble_edit_exec_lastexit=0 5861 _ble_edit_exec_lastarg=$BASH 5862 _ble_edit_exec_BASH_COMMAND=$BASH 5863 _ble_edit_exec_PIPESTATUS=() 5864 function ble-edit/exec/register { 5865 local command=$1 5866 if [[ $command != *[!"$_ble_term_IFS"]* ]]; then 5867 ble/edit/leave-command-layout 5868 return 1 5869 fi 5870 local command_id=$((++_ble_edit_CMD)) # Exposed to blehook exec_register 5871 local lineno=$((_ble_edit_LINENO+1)) # Exposed to blehook exec_register 5872 ble/array#push _ble_edit_exec_lines "$command_id,$lineno:$command" 5873 blehook/invoke exec_register "$command" 5874 } 5875 function ble-edit/exec/has-pending-commands { 5876 ((${#_ble_edit_exec_lines[@]})) 5877 } 5878 function ble-edit/exec/.setexit { 5879 # $? 変数の設定 5880 return "$_ble_edit_exec_lastexit" 5881 } 5882 ## @fn ble-edit/exec/.adjust-eol 5883 ## 文末調整を行います。 5884 _ble_prompt_eol_mark=('' '' 0) 5885 function ble-edit/exec/.adjust-eol { 5886 # bleopt prompt_eol_mark 5887 local cols=${COLUMNS:-80} 5888 local -a DRAW_BUFF=() 5889 if [[ $bleopt_prompt_eol_mark ]]; then 5890 if [[ $bleopt_prompt_eol_mark != "${_ble_prompt_eol_mark[0]}" ]]; then 5891 if [[ $bleopt_prompt_eol_mark ]]; then 5892 local ret= x=0 y=0 g=0 x1=0 x2=0 y1=0 y2=0 5893 LINES=1 COLUMNS=80 ble/canvas/trace "$bleopt_prompt_eol_mark" truncate:measure-bbox 5894 _ble_prompt_eol_mark=("$bleopt_prompt_eol_mark" "$ret" "$x2") 5895 else 5896 _ble_prompt_eol_mark=('' '' 0) 5897 fi 5898 fi 5899 5900 local eol_mark=${_ble_prompt_eol_mark[1]} 5901 # Note #D1458: コマンドを実行前に panel/render で panel 0 に移動している筈。 5902 # 従って bottom-dock には居らず SC/RC を使って OK の筈。 5903 ble/canvas/put.draw "$_ble_term_sgr0$_ble_term_sc" 5904 local width=${_ble_prompt_eol_mark[2]} limit=$cols 5905 [[ $_ble_term_rc ]] || ((limit--)) 5906 if ((width>limit)); then 5907 local x=0 y=0 g=0 5908 LINES=1 COLUMNS=$limit ble/canvas/trace.draw "$bleopt_prompt_eol_mark" truncate 5909 width=$x 5910 else 5911 ble/canvas/put.draw "$eol_mark" 5912 fi 5913 [[ $_ble_term_rc ]] || ble/canvas/put-cub.draw "$width" 5914 ble/canvas/put.draw "$_ble_term_sgr0$_ble_term_rc" 5915 fi 5916 5917 # EOL adjustment 5918 local advance=$((_ble_term_xenl?cols-2:cols-3)) 5919 if [[ $_ble_term_TERM == cygwin:* ]]; then 5920 # Note (#D1144): Cygwin console では何故か行き先が 5921 # 丁度 cols+1 列目になる様な CUF は一文字も動かない。 5922 # cols列目またはcols+2列目以降は大丈夫である。 5923 # 仕方がないので少しずつ慎重に前進する事にする。 5924 while ((advance)); do 5925 ble/canvas/put-cuf.draw "$((advance-advance/2))" 5926 ((advance/=2)) 5927 done 5928 else 5929 ble/canvas/put-cuf.draw "$advance" 5930 fi 5931 ble/canvas/put.draw " $_ble_term_cr$_ble_term_el" 5932 ble/canvas/bflush.draw 5933 } 5934 5935 _ble_prompt_ps10_data=() 5936 function ble/prompt/unit:_ble_prompt_ps10/update { 5937 ble/prompt/unit:{section}/update _ble_prompt_ps10 "$PS0" '' 5938 } 5939 5940 function ble-edit/exec/print-PS0 { 5941 if [[ $PS0 ]]; then 5942 local version=$COLUMNS,$_ble_edit_lineno,$_ble_history_count,$_ble_edit_CMD 5943 local prompt_hashref_base='$version' 5944 local prompt_rows=${LINES:-25} 5945 local prompt_cols=${COLUMNS:-80} 5946 local "${_ble_prompt_cache_vars[@]/%/=}" # WA #D1570 checked 5947 ble/prompt/unit#update _ble_prompt_ps10 5948 local ret; ble/prompt/unit:{section}/get _ble_prompt_ps10 5949 ble/util/put "$ret" 5950 fi 5951 } 5952 5953 _ble_builtin_exit_processing= 5954 function ble/builtin/exit/.read-arguments { 5955 [[ ! $_ble_attached || $_ble_edit_exec_inside_userspace ]] && 5956 ble/base/adjust-BASH_REMATCH 5957 while (($#)); do 5958 local arg=$1; shift 5959 if [[ $arg == --help ]]; then 5960 opt_flags=${opt_flags}H 5961 elif local rex='^[-+]?[0-9]+$'; [[ $arg =~ $rex ]]; then 5962 ble/array#push opt_args "$arg" 5963 else 5964 ble/util/print "exit: unrecognized argument '$arg'" >&2 5965 opt_flags=${opt_flags}E 5966 fi 5967 done 5968 if ((${#opt_args[@]}>=2)); then 5969 ble/util/print "exit: too many arguments" >&2 5970 opt_flags=${opt_flags}E 5971 fi 5972 [[ ! $_ble_attached || $_ble_edit_exec_inside_userspace ]] && 5973 ble/base/restore-BASH_REMATCH 5974 } 5975 function ble/builtin/exit { 5976 local ext=$? 5977 5978 # 現在、同じ(サブ)シェルでの trap 処理実行中かどうか 5979 local trap_processing=$_ble_builtin_trap_processing 5980 [[ $_ble_builtin_trap_processing == "${BASH_SUBSHELL:-0}"/* ]] || trap_processing= 5981 5982 if [[ ! $trap_processing ]] && { ble/util/is-running-in-subshell || [[ $_ble_decode_bind_state == none ]]; }; then 5983 (($#)) || set -- "$ext" 5984 builtin exit "$@" 5985 return "$?" # オプションの指定間違いなどで失敗する可能性がある。 5986 fi 5987 5988 local set shopt; ble/base/.adjust-bash-options set shopt 5989 local opt_flags= 5990 local -a opt_args=() 5991 ble/builtin/exit/.read-arguments "$@" 5992 if [[ $opt_flags == *[EH]* ]]; then 5993 [[ $opt_flags == *H* ]] && builtin exit --help 5994 ble/base/.restore-bash-options set shopt 5995 return 2 5996 fi 5997 ((${#opt_args[@]})) || ble/array#push opt_args "$ext" 5998 5999 if [[ $trap_processing ]]; then 6000 # Note #D1782: trap の中で処理している時は exit は trap の側で処理する。な 6001 # ので exit は延期して一旦元の呼び出し元まで戻る。これによって細かな動作の 6002 # 違いが問題になる可能性はある。例えば trap の中で time で時間計測中だった 6003 # 場合、時間計測が中止されず結果が出力される。 6004 shopt -s extdebug 6005 _ble_edit_exec_TRAPDEBUG_EXIT=$opt_args 6006 ble-edit/exec:gexec/.TRAPDEBUG/trap 6007 return 0 6008 fi 6009 6010 if [[ ! $_ble_builtin_exit_processing ]]; then 6011 # 終了確認と [ble: exit] の出力 6012 6013 local joblist 6014 ble/util/joblist 6015 if ((${#joblist[@]})); then 6016 local ret 6017 while 6018 local cancel_reason= 6019 if ble/util/assign ret 'compgen -A stopped -- ""' 2>/dev/null; [[ $ret ]]; then 6020 cancel_reason='stopped jobs' 6021 elif [[ :$opts: == *:checkjobs:* ]]; then 6022 if ble/util/assign ret 'compgen -A running -- ""' 2>/dev/null; [[ $ret ]]; then 6023 cancel_reason='running jobs' 6024 fi 6025 fi 6026 [[ $cancel_reason ]] 6027 do 6028 jobs 6029 ble/builtin/read -ep "\e[38;5;12m[ble: There are $cancel_reason]\e[m Leave the shell anyway? [yes/No] " ret 6030 case $ret in 6031 ([yY]|[yY][eE][sS]) break ;; 6032 ([nN]|[nN][oO]|'') 6033 ble/base/.restore-bash-options set shopt 6034 return 0 ;; 6035 esac 6036 done 6037 fi 6038 ble/util/print "${_ble_term_setaf[12]}[ble: exit]$_ble_term_sgr0" >&2 6039 fi 6040 6041 # Note #D1765: Bash 4.4..5.1 では "{ time { exit 2>/dev/tty; } } 2>/dev/null" 6042 # に対して、time の時間計測結果を 2>/dev/null ではなくて 2>/dev/tty に出力 6043 # してしまうバグがある。その為に ble/exec/time の計測に使用している time の 6044 # 出力が画面に表示されてしまう。仕方がないので time の出力を空の TIMEFORMAT 6045 # により抑制する。抑々 4.3 以前では exit を実行した時に外側の time の測定も 6046 # 全てキャンセルされていたので time を握り潰しても 4.3 以前の振る舞いに戻る 6047 # だけなので気にしない事にする。 6048 # Note #D1765: 手元の実験では local TIMEFORMAT= だけ指定していれば問題は発生 6049 # しなかったが、実際に ble.sh に実装してみると global TIMEFORMAT を指定しな 6050 # ければ抑制できなかったので、global TIMEFORMAT を一時的に書き換える。 6051 if ((40400<=_ble_bash&&_ble_bash<50200)); then 6052 # TIMEFORMAT の値の保存 6053 local global_TIMEFORMAT local_TIMEFORMAT 6054 ble/util/assign global_TIMEFORMAT 'ble/util/print-global-definitions TIMEFORMAT' 6055 if [[ $global_TIMEFORMAT == 'declare TIMEFORMAT; builtin unset -v TIMEFORMAT' ]]; then 6056 global_TIMEFORMAT='declare -g TIMEFORMAT=$'\''\nreal\t%3lR\nuser\t%3lU\nsys %3lS'\' 6057 else 6058 global_TIMEFORMAT="declare -g ${global_TIMEFORMAT#declare }" 6059 fi 6060 ble/variable#copy-state TIMEFORMAT local_TIMEFORMAT 6061 6062 declare -g TIMEFORMAT= 6063 TIMEFORMAT= 6064 fi 6065 6066 ble/base/.restore-bash-options set shopt 6067 _ble_builtin_exit_processing=1 6068 ble/fd#alloc _ble_builtin_exit_stdout '>&1' # EXIT trap で stdin/stdout を復元する 6069 ble/fd#alloc _ble_builtin_exit_stderr '>&2' 6070 builtin exit "${opt_args[@]}" &>/dev/null 6071 builtin exit "${opt_args[@]}" &>/dev/null 6072 6073 # exit に失敗した時はできるだけ元の状態に戻す 6074 _ble_builtin_exit_processing= 6075 ble/fd#close _ble_builtin_exit_stdout 6076 ble/fd#close _ble_builtin_exit_stderr 6077 if ((40400<=_ble_bash&&_ble_bash<50200)); then 6078 builtin eval -- "$global_TIMEFORMAT" 6079 ble/variable#copy-state local_TIMEFORMAT TIMEFORMAT 6080 fi 6081 return 1 # exit できなかった場合は 1 らしい 6082 } 6083 6084 function exit { ble/builtin/exit "$@"; } 6085 6086 # start time - end time - end 6087 6088 # time Command による計測 6089 _ble_exec_time_TIMEFILE=$_ble_base_run/$$.exec.time 6090 _ble_exec_time_TIMEFORMAT= 6091 _ble_exec_time_tot= 6092 _ble_exec_time_usr= 6093 _ble_exec_time_sys= 6094 function ble/exec/time#adjust-TIMEFORMAT { 6095 if [[ ${TIMEFORMAT+set} ]]; then 6096 _ble_exec_time_TIMEFORMAT=$TIMEFORMAT 6097 else 6098 builtin unset -v _ble_exec_time_TIMEFORMAT 6099 fi 6100 TIMEFORMAT='%R %U %S' 6101 } 6102 function ble/exec/time#restore-TIMEFORMAT { 6103 if [[ ${_ble_exec_time_TIMEFORMAT+set} ]]; then 6104 TIMEFORMAT=$_ble_exec_time_TIMEFORMAT 6105 else 6106 builtin unset -v 'TIMEFORMAT[0]' 6107 fi 6108 local tot usr sys dummy 6109 IFS=' ' ble/bash/read tot usr sys dummy < "$_ble_exec_time_TIMEFILE" 6110 ((_ble_exec_time_tot=10#0${tot//[!0-9]})) 6111 ((_ble_exec_time_usr=10#0${usr//[!0-9]})) 6112 ((_ble_exec_time_sys=10#0${sys//[!0-9]})) 6113 } 6114 6115 _ble_exec_time_TIMES=$_ble_base_run/$$.exec.times 6116 _ble_exec_time_usr_self= 6117 _ble_exec_time_sys_self= 6118 function ble/exec/time/times.parse-time { 6119 local rex='^([0-9]+m)?([0-9]*)([^0-9ms][0-9]{3})?s?$' 6120 [[ $1 =~ $rex ]] || return 1 6121 local min=$((10#0${BASH_REMATCH[1]%m})) 6122 local sec=$((10#0${BASH_REMATCH[2]})) 6123 local msc=$((10#0${BASH_REMATCH[3]#?})) 6124 ((ret=(min*60+sec)*1000+msc)) 6125 return 0 6126 } 2>&"$_ble_util_fd_stderr" 6127 function ble/exec/time/times.start { 6128 builtin times >| "$_ble_exec_time_TIMES" 6129 } 6130 function ble/exec/time/times.end { 6131 builtin times >> "$_ble_exec_time_TIMES" 6132 local times 6133 ble/util/readfile times "$_ble_exec_time_TIMES" 6134 ble/string#split-words times "$times" 6135 6136 _ble_exec_time_usr_self= 6137 _ble_exec_time_sys_self= 6138 local ret= t1 t2 6139 ble/exec/time/times.parse-time "${times[0]}" && t1=$ret && 6140 ble/exec/time/times.parse-time "${times[4]}" && t2=$ret && 6141 ((_ble_exec_time_usr_self=t2>t1?t2-t1:0, 6142 _ble_exec_time_usr_self>_ble_exec_time_usr&&( 6143 _ble_exec_time_usr_self=_ble_exec_time_usr))) 6144 ble/exec/time/times.parse-time "${times[1]}" && t1=$ret && 6145 ble/exec/time/times.parse-time "${times[5]}" && t2=$ret && 6146 ((_ble_exec_time_sys_self=t2>t1?t2-t1:0, 6147 _ble_exec_time_sys_self>_ble_exec_time_sys&&( 6148 _ble_exec_time_sys_self=_ble_exec_time_sys))) 6149 return 0 6150 } 6151 function ble/exec/time#mark-enabled { 6152 # Note: exec_elapsed_enabled から参照できる変数 6153 local real=$_ble_exec_time_tot 6154 local usr=$_ble_exec_time_usr usr_self=$_ble_exec_time_usr_self 6155 local sys=$_ble_exec_time_sys sys_self=$_ble_exec_time_sys_self 6156 local usr_child=$((usr-usr_self)) 6157 local sys_child=$((sys-sys_self)) 6158 local cpu=$((real>0?(usr+sys)*100/real:0)) 6159 ((bleopt_exec_elapsed_enabled)) 6160 } 6161 6162 _ble_exec_time_beg= 6163 _ble_exec_time_end= 6164 _ble_exec_time_ata= 6165 function ble/exec/time#start { 6166 # 初回呼び出しで初期化 6167 6168 if ((_ble_bash>=50000)); then 6169 _ble_exec_time_EPOCHREALTIME_delay=0 6170 _ble_exec_time_EPOCHREALTIME_beg= 6171 _ble_exec_time_EPOCHREALTIME_end= 6172 function ble/exec/time#start { 6173 # EPOCHREALTIME の時は精度が高いので、正確に計測するため直接 6174 # prologue/epilogue に記述する 6175 ble/exec/time/times.start 6176 _ble_exec_time_EPOCHREALTIME_beg= 6177 _ble_exec_time_EPOCHREALTIME_end= 6178 } 6179 function ble/exec/time#end { 6180 local beg=${_ble_exec_time_EPOCHREALTIME_beg//[!0-9]} 6181 local end=${_ble_exec_time_EPOCHREALTIME_end//[!0-9]} 6182 ((beg+=delay,beg>end)) && beg=$end 6183 _ble_exec_time_beg=$beg 6184 _ble_exec_time_end=$end 6185 _ble_exec_time_ata=$((end-beg)) 6186 _ble_exec_time_LINENO=$_ble_edit_LINENO 6187 ble/exec/time/times.end 6188 } 6189 6190 function ble/exec/time#calibrate.restore-lastarg { 6191 _ble_exec_time_EPOCHREALTIME_beg=$EPOCHREALTIME 6192 return "$_ble_edit_exec_lastexit" 6193 } 6194 function ble/exec/time#calibrate.save-lastarg { 6195 _ble_exec_time_EPOCHREALTIME_end=$EPOCHREALTIME 6196 ble/exec/time#adjust-TIMEFORMAT 6197 } 6198 function ble/exec/time#calibrate { 6199 local _ble_edit_exec_lastexit=0 6200 local _ble_edit_exec_lastarg=hello 6201 local _ble_exec_time_EPOCHREALTIME_beg= 6202 local _ble_exec_time_EPOCHREALTIME_end= 6203 local _ble_exec_time_tot= 6204 local _ble_exec_time_usr= 6205 local _ble_exec_time_sys= 6206 local TIMEFORMAT= 6207 6208 # create a script 6209 local script1='ble/exec/time#calibrate.restore-lastarg "$_ble_edit_exec_lastarg"' 6210 local script2='{ ble/exec/time#calibrate.save-lastarg; } &>/dev/null' 6211 local script=$script1$_ble_term_nl$script2$_ble_term_nl 6212 6213 # make a histogram 6214 local -a hist=() 6215 local i 6216 for i in {00..99}; do 6217 { builtin eval -- "$script" 2>&"$_ble_util_fd_stderr"; } 2>| "$_ble_exec_time_TIMEFILE" 6218 ble/exec/time#restore-TIMEFORMAT 6219 local beg=${_ble_exec_time_EPOCHREALTIME_beg//[!0-9]} 6220 local end=${_ble_exec_time_EPOCHREALTIME_end//[!0-9]} 6221 ((hist[end-beg]++)) 6222 done 6223 6224 # calculate weighted average 6225 local -a keys; keys=("${!hist[@]}") 6226 keys=("${keys[@]::(${#keys[@]}+1)/2}") # Remove outliers 6227 local s=0 n=0 t 6228 for t in "${keys[@]}"; do ((s+=t*hist[t],n+=hist[t])); done 6229 ((_ble_exec_time_EPOCHREALTIME_delay=s/n)) 6230 } 6231 ble/exec/time#calibrate 6232 builtin unset -f ble/exec/time#calibrate 6233 builtin unset -f ble/exec/time#calibrate.restore-lastarg 6234 builtin unset -f ble/exec/time#calibrate.save-lastarg 6235 6236 else 6237 _ble_exec_time_CLOCK_base=0 6238 _ble_exec_time_CLOCK_beg= 6239 _ble_exec_time_CLOCK_end= 6240 function ble/exec/time#end.adjust { 6241 # 辻褄合わせ 6242 ((_ble_exec_time_beg<prev_end)) && _ble_exec_time_beg=$prev_end 6243 local delta=$((_ble_exec_time_end-_ble_exec_time_beg)) 6244 if ((delta<_ble_exec_time_ata)); then 6245 _ble_exec_time_end=$((_ble_exec_time_beg+_ble_exec_time_ata)) 6246 else 6247 _ble_exec_time_beg=$((_ble_exec_time_end-_ble_exec_time_ata)) 6248 fi 6249 _ble_exec_time_LINENO=$_ble_edit_LINENO 6250 } 6251 6252 function ble/exec/time#start { 6253 ble/exec/time/times.start 6254 _ble_exec_time_CLOCK_beg= 6255 _ble_exec_time_CLOCK_end= 6256 local ret; ble/util/clock 6257 _ble_exec_time_CLOCK_beg=$ret 6258 } 6259 function ble/exec/time#end { 6260 local ret; ble/util/clock 6261 _ble_exec_time_CLOCK_end=$ret 6262 local prev_end=$_ble_exec_time_end 6263 _ble_exec_time_beg=$((_ble_exec_time_CLOCK_base+_ble_exec_time_CLOCK_beg*1000)) 6264 _ble_exec_time_end=$((_ble_exec_time_CLOCK_base+_ble_exec_time_CLOCK_end*1000)) 6265 _ble_exec_time_ata=$((_ble_exec_time_tot*1000)) 6266 ble/exec/time#end.adjust 6267 ble/exec/time/times.end 6268 } 6269 6270 case $_ble_util_clock_type in 6271 (printf) ;; 6272 (uptime|SECONDS) 6273 # これらの原点は unix epoch でないので補正する。 6274 ble/util/assign _ble_exec_time_CLOCK_base 'ble/bin/date +%s000000' 6275 local ret; ble/util/clock 6276 ((_ble_exec_time_CLOCK_base-=ret*1000)) ;; 6277 (date) 6278 # どうせファイルコマンドを使うのであればより精度の良い物を使う。 6279 if ble/util/assign ret 'ble/bin/date +%6N' 2>/dev/null && [[ $ret ]]; then 6280 function ble/exec/time#start { 6281 ble/exec/time/times.start 6282 _ble_exec_time_CLOCK_beg= 6283 _ble_exec_time_CLOCK_end= 6284 ble/util/assign _ble_exec_time_CLOCK_beg 'ble/bin/date +%s%6N' 6285 } 6286 function ble/exec/time#end { 6287 ble/util/assign _ble_exec_time_CLOCK_end 'ble/bin/date +%s%6N' 6288 local prev_end=$_ble_exec_time_end 6289 _ble_exec_time_beg=$_ble_exec_time_CLOCK_beg 6290 _ble_exec_time_end=$_ble_exec_time_CLOCK_end 6291 _ble_exec_time_ata=$((_ble_exec_time_tot*1000)) 6292 ble/exec/time#end.adjust 6293 ble/exec/time/times.end 6294 } 6295 fi ;; 6296 esac 6297 fi 6298 6299 ble/exec/time#start 6300 } 6301 6302 ## @fn ble-edit/exec:$bleopt_internal_exec_type/process 6303 ## 指定したコマンドを実行します。 6304 ## @param[in,out] _ble_edit_exec_lines 6305 ## 実行するコマンドの配列を指定します。実行したコマンドは削除するか空文字列を代入します。 6306 ## @return 6307 ## 戻り値が 0 の場合、終端 (ble-edit/bind/.tail) に対する処理も行われた事を意味します。 6308 ## つまり、そのまま ble-decode/.hook から抜ける事を期待します。 6309 ## それ以外の場合には終端処理をしていない事を表します。 6310 6311 #-------------------------------------- 6312 # bleopt_internal_exec_type = gexec 6313 #-------------------------------------- 6314 6315 _ble_edit_exec_TRAPDEBUG_enabled= 6316 _ble_edit_exec_TRAPDEBUG_INT= 6317 _ble_edit_exec_TRAPDEBUG_EXIT= 6318 _ble_edit_exec_inside_begin= 6319 _ble_edit_exec_inside_prologue= 6320 _ble_edit_exec_inside_userspace= 6321 ble/builtin/trap/sig#reserve DEBUG override-builtin-signal:user-trap-in-postproc 6322 6323 ## @fn ble-edit/exec:gexec/.TRAPDEBUG/trap [opts] 6324 ## @param[in] opts 6325 ## filter 6326 ## DEBUG trap の filter を (TRAPDEBUG の特別処理がなくても) 明示的に強制 6327 ## する事を示します。PROMPT_COMMAND の処理などで、PROMPT_COMMAND の処理の 6328 ## みに対して DEBUG trap を走らせる為に指定します。 6329 function ble-edit/exec:gexec/.TRAPDEBUG/trap { 6330 # Note #D1772: 本来は ! $_ble_attached の時には user trap を直接 trap したい 6331 # が、それだと ble-attach 直後に ble.sh の関数 (特に ble-decode/.hook) に対 6332 # して意図しない DEBUG trap が発火する事を防げないので TRAPDEBUG 経由にして、 6333 # DEBUG を選別することにする。 6334 # Note #D1772: コマンド実行の為の TRAPDEBUG の場合でも、やはり 6335 # ble-edit/exec:gexec/.* を除外する為に TRAPDEBUG 経由で user trap を実行す 6336 # る事にする。もし FUNCNAME, BASH_SOURCE 等を DEBUG trap から参照したいユー 6337 # ザーがいれば、コマンド実行の時には既定で user trap を直接 trap する様にし 6338 # ても良い。 6339 local trap_command 6340 ble/builtin/trap/install-hook/.compose-trap_command "$_ble_builtin_trap_DEBUG" 6341 builtin eval -- "builtin $trap_command" 6342 6343 # Note: 以下は条件付きで user trap を直接 trap するコード。 6344 # if [[ $_ble_attached && _ble_edit_exec_TRAPDEBUG_INT || :$1: == *:filter:* ]]; then 6345 # builtin trap -- 'ble-edit/exec:gexec/.TRAPDEBUG "$*"; builtin eval -- "${_ble_builtin_trap_postproc[1000]}"' DEBUG 6346 # else 6347 # local user_trap=${_ble_builtin_trap_handlers[_ble_builtin_trap_DEBUG]} 6348 # builtin trap -- "$user_trap" DEBUG 6349 # fi 6350 } 6351 6352 _ble_edit_exec_TRAPDEBUG_adjusted= 6353 # Note: bash-3.1 以下では特殊な関数名の関数には declare -ft を付加する事ができない。 6354 function _ble_edit_exec_gexec__TRAPDEBUG_adjust { 6355 builtin trap - DEBUG 6356 _ble_edit_exec_TRAPDEBUG_adjusted=1 6357 } 6358 ble/function#trace _ble_edit_exec_gexec__TRAPDEBUG_adjust 6359 function ble-edit/exec:gexec/.TRAPDEBUG/restore { 6360 _ble_edit_exec_TRAPDEBUG_adjusted= 6361 local opts=$1 6362 if ble/builtin/trap/user-handler#has "$_ble_builtin_trap_DEBUG"; then 6363 ble-edit/exec:gexec/.TRAPDEBUG/trap "$opts" 6364 fi 6365 } 6366 6367 function ble-edit/exec:gexec/.TRAPDEBUG/.filter { 6368 [[ $_ble_edit_exec_TRAPDEBUG_enabled || ! $_ble_attached ]] || return 1 6369 [[ $_ble_trap_bash_command != *ble-edit/exec:gexec/.* ]] || return 1 6370 [[ ! ( ${FUNCNAME[1]-} == _ble_prompt_update__eval_prompt_command_1 && ( $_ble_trap_bash_command == 'ble-edit/exec/.setexit '* || $_ble_trap_bash_command == 'BASH_COMMAND='*' builtin eval -- '* ) ) ]] || return 1 6371 [[ ! ${_ble_builtin_trap_inside-} ]] || return 1 6372 return 0 6373 } 6374 _ble_trap_builtin_handler_DEBUG_filter=ble-edit/exec:gexec/.TRAPDEBUG/.filter 6375 6376 ## @fn ble-edit/exec:gexec/.TRAPDEBUG 6377 ## @var[in] BLE_TRAP_FUNCNAME 6378 ## @var[in] BLE_TRAP_LINENO 6379 ## @var[in] _ble_trap_args 6380 ## @var[in] _ble_trap_bash_command 6381 ## @var[in] _ble_trap_lastarg 6382 ## @var[in] _ble_trap_lastexit 6383 ## @var[in] _ble_trap_sig 6384 ## @var[in,out] _ble_builtin_trap_postproc[_ble_trap_sig] 6385 function ble-edit/exec:gexec/.TRAPDEBUG { 6386 if [[ $_ble_edit_exec_TRAPDEBUG_EXIT ]]; then 6387 # Handle EXIT (#D1782) 6388 # 他の trap を ble/builtin/trap/.handler で処理中に exit を呼び出した時の 6389 # 処理を DEBUG trap を用いて調整している。元々の trap の動作に干渉する為 6390 # に元々の trap に対する _ble_builtin_trap_processing や _ble_trap_done, 6391 # _ble_trap_lastarg (ble/builtin/trap/invoke) や_ble_local_ext 6392 # (blehook/invoke) などをを書き換える。 6393 # 6394 # 前提: _ble_edit_exec_TRAPDEBUG_EXIT が設定される時には extdebug も設定 6395 # されていると仮定する。 6396 6397 # 或る特定のレベルまでは素通りする (そもそも exit なのでユーザーの DEBUG 6398 # trap も処理しなくて良い)。 6399 local flag_clear= flag_exit= postproc= 6400 6401 # Note: Here, we want to read and rewrite the one-upper-level 6402 # "_ble_builtin_trap_processing". We remove the slot in 6403 # ble/builtin/trap/.handler for DEBUG and reveal the slot defined in the 6404 # upper call of ble/builtin/trap/.handler for another signal. 6405 ble/util/unlocal _ble_builtin_trap_processing 6406 if [[ ! $_ble_builtin_trap_processing ]] || ((${#BLE_TRAP_FUNCNAME[*]}==0)); then 6407 # 本来は此処に来る事はない筈 6408 flag_clear=2 6409 flag_exit=$_ble_edit_exec_TRAPDEBUG_EXIT 6410 else 6411 # 本来は extdebug が設定されている筈なので extdebug が設定されていない時 6412 # の対処は不要だが、念の為 extdebug が設定されていない時の動作も定義して 6413 # おく。 6414 case " ${BLE_TRAP_FUNCNAME[*]} " in 6415 (' ble/builtin/trap/invoke.sandbox ble/builtin/trap/invoke '*) 6416 6417 # Rewrite variables declared for the other signal 6418 ble/util/unlocal _ble_trap_lastarg # declared in ble/builtin/trap/.handler for DEBUG 6419 _ble_trap_done=exit # declared in ble/builtin/trap/invoke for the other signal 6420 _ble_trap_lastarg=$_ble_edit_exec_TRAPDEBUG_EXIT # declared in ble/builtin/trap/invoke for the other signal 6421 6422 postproc='ble/util/setexit 2' 6423 shopt -q extdebug || postproc='return 0' ;; 6424 (' blehook/invoke.sandbox blehook/invoke ble/builtin/trap/.handler '*) 6425 6426 # Rewrite variables declared for the other signal (Note: the local 6427 # _ble_builtin_trap_processing is already removed above). 6428 # The following is declared in "blehook/invoke" for the other signal. 6429 _ble_local_ext=$_ble_edit_exec_TRAPDEBUG_EXIT 6430 # The following is declared in "ble/builtin/trap/.handler" for the other signal. 6431 _ble_builtin_trap_processing=${_ble_builtin_trap_processing%%/*}/exit:$_ble_edit_exec_TRAPDEBUG_EXIT 6432 6433 postproc='ble/util/setexit 2' 6434 shopt -q extdebug || postproc='return 0' ;; 6435 (' ble/builtin/trap/invoke '* | ' blehook/invoke '*) 6436 # 此処で確実に trap DEBUG を解除する為には sandbox の呼び出しよりも後に 6437 # 少なくとも1つコマンドが必要。現在は return が必ず両 invoke の終わりに 6438 # 実行される様になっているので大丈夫の筈。 6439 flag_clear=1 ;; 6440 (' ble/builtin/trap/.handler '* | ' ble-edit/exec:gexec/.TRAPDEBUG '*) 6441 # 本来此処には来ない筈。extdebug には触れずに DEBUG trap だけ解除する。 6442 flag_clear=2 ;; 6443 (*) 6444 # trap handler 内部の処理は全てスキップして呼び出し元に戻る。 6445 postproc='ble/util/setexit 2' 6446 shopt -q extdebug || postproc='return 128' ;; 6447 esac 6448 fi 6449 6450 if [[ $flag_clear ]]; then 6451 [[ $flag_clear == 2 ]] || shopt -u extdebug 6452 _ble_edit_exec_TRAPDEBUG_EXIT= 6453 if ! ble/builtin/trap/user-handler#has "$_ble_trap_sig"; then 6454 postproc="builtin trap - DEBUG${postproc:+;$postproc}" 6455 fi 6456 if [[ $flag_exit ]]; then 6457 builtin exit "$flag_exit" 6458 fi 6459 fi 6460 6461 _ble_builtin_trap_postproc[_ble_trap_sig]=$postproc 6462 return 126 # skip user hooks/traps 6463 6464 elif [[ $_ble_edit_exec_TRAPDEBUG_INT ]]; then 6465 # Handle INT 6466 6467 # Run user DEBUG trap in the sandbox 6468 ble/util/setexit "$_ble_trap_lastexit" "$_ble_trap_lastarg" 6469 BASH_COMMAND=$_ble_trap_bash_command LINENO=$BLE_TRAP_LINENO \ 6470 ble/builtin/trap/invoke "$_ble_trap_sig" "${_ble_trap_args[@]}" 6471 6472 # Handle INT 6473 local depth=${#BLE_TRAP_FUNCNAME[*]} 6474 if ((depth>=1)) && ! ble/string#match "${BLE_TRAP_FUNCNAME[*]}" '^ble-edit/exec:gexec/\.|(^| )ble/builtin/trap/\.handler'; then 6475 # 関数内にいるが、ble-edit/exec:gexec/. の中ではない時 6476 local source=${_ble_term_setaf[5]}${BLE_TRAP_SOURCE[0]} 6477 local sep=${_ble_term_setaf[6]}: 6478 local lineno=${_ble_term_setaf[2]}${BLE_TRAP_LINENO[0]} 6479 local func=${_ble_term_setaf[6]}' ('${_ble_term_setaf[4]}${BLE_TRAP_FUNCNAME[0]}${1:+ $1}${_ble_term_setaf[6]}')' 6480 ble/util/print "${_ble_term_setaf[9]}[SIGINT]$_ble_term_sgr0 $source$sep$lineno$func$_ble_term_sgr0" >&"$_ble_util_fd_stderr" 6481 _ble_builtin_trap_postproc[_ble_trap_sig]="{ return $_ble_edit_exec_TRAPDEBUG_INT || break; } &>/dev/null" 6482 elif ((depth==0)) && ! ble/string#match "$_ble_trap_bash_command" '^ble-edit/exec:gexec/\.'; then 6483 # 一番外側で、ble-edit/exec:gexec/. 関数ではない時 6484 local source=${_ble_term_setaf[5]}global 6485 local sep=${_ble_term_setaf[6]}: 6486 ble/util/print "${_ble_term_setaf[9]}[SIGINT]$_ble_term_sgr0 $source$sep$_ble_term_sgr0 $_ble_trap_bash_command" >&"$_ble_util_fd_stderr" 6487 _ble_builtin_trap_postproc[_ble_trap_sig]="break &>/dev/null" 6488 fi 6489 6490 return 126 # skip user hooks/traps 6491 6492 elif ! ble/builtin/trap/user-handler#has "$_ble_trap_sig"; then 6493 # ユーザー DEBUG trap がなくかつ INT 処理中でもない場合は DEBUG は削除して 6494 # 良い [ Note: builtin trap - DEBUG は此処では効かない ] 6495 _ble_builtin_trap_postproc[_ble_trap_sig]='builtin trap -- - DEBUG' 6496 return 126 # skip user hooks/traps 6497 fi 6498 6499 return 0 6500 } 6501 blehook internal_DEBUG!=ble-edit/exec:gexec/.TRAPDEBUG 6502 6503 _ble_builtin_trap_DEBUG_userTrapInitialized= 6504 function ble/builtin/trap:DEBUG { 6505 _ble_builtin_trap_DEBUG_userTrapInitialized=1 6506 # Note (#D1155): ユーザコマンド実行中に新しく ble/builtin/trap DEBUG 6507 # が設定された場合は builtin trap DEBUG を仕掛ける。 6508 if [[ $1 != - && ( $_ble_edit_exec_TRAPDEBUG_enabled || ! $_ble_attached ) ]]; then 6509 ble-edit/exec:gexec/.TRAPDEBUG/trap 6510 fi 6511 } 6512 6513 ## @fn _ble_builtin_trap_DEBUG__initialize 6514 ## ユーザーの設定した DEBUG trap を何処かの時点で読み取る。 6515 ## 6516 ## DEBUG trap は基本的には ble.sh 内部では無効化される。但し、PROMPT_COMMAND を 6517 ## 評価する時には一時的に有効化される。ble/builtin/trap による DEBUG は関数の入 6518 ## れ子等は考慮に入れていない。 6519 ## 6520 ## Note: 関数名が POSIX の要求する物になっているのは bash-3.1 以下で特殊文字を 6521 ## 含む関数名に対して declare -ft を実行することができない為。 6522 ## 6523 ## Note: 先に ble.sh を source した場合は殆どの場合は上の trap:DEBUG 経由で正し 6524 ## い trap string が登録するので殆どの場合動く。 6525 ## 6526 ## 但し、bash-5.0 以下で先に ble.sh を source して prompt-attach を行い、更に 6527 ## PROMPT_COMMAND を書き換えた場合には、PROMPT_COMMAND の中で 6528 ## attach-from-PROMPT_COMMAND よりも後に実行している処理は DEBUG trap が無効 6529 ## 化された状態で実行される事になる。これは PROMPT_COMMAND 内で DEBUG trap を 6530 ## 有効にしている動作とずれる。 6531 ## 6532 ## Note: 先に DEBUG trap を設定した後に ble.sh を source した場合には、以下の場 6533 ## 合にロード直後は DEBUG trap が ble.sh 内部の処理に対しても有効になっている 6534 ## 事に注意する。最初のユーザー入力または端末による DA2 応答等の時に改めて 6535 ## DEBUG trap の読み取り 6536 ## 6537 ## - rcfile の名前が .bashrc でも .profile でも .bash_profile でもない場合 6538 ## (これは現在 rcfile の中にいるかどうかを判定する方法が bash にはない事か 6539 ## ら、rcfile かどうかをファイル名と行番号だけから判定しなければならない事 6540 ## に由来する) 6541 ## 6542 ## - コマンドラインから source ~/.bashrc 等の様にして手動で bashrc を読み込ん 6543 ## だ時 (これは source が DEBUG trap を継承しないという Bash の制限に由来す 6544 ## る) 6545 ## 6546 ## - rcfile から一旦別のファイルを source してそのファイルから ble.sh を 6547 ## source した時。または関数内から ble.sh を source した時 (これも DEBUG 6548 ## trap の継承に関する Bash の制限に由来する) 6549 ## 6550 ## - bash-3.1 以下の時 (これは declare -ft で trace を付加できる関数の関数名 6551 ## に対する制限に由来する) 6552 function _ble_builtin_trap_DEBUG__initialize { 6553 if [[ $_ble_builtin_trap_DEBUG_userTrapInitialized ]]; then 6554 # Note: 既に ble/builtin/trap:DEBUG 等によって user trap が設定されている場 6555 # 合は改めて読み取る事はしない (読み取っても TRAPDEBUG が見えるだけ)。 6556 builtin eval -- "function $FUNCNAME { ((1)); }" 6557 return 0 6558 elif [[ $1 == force ]] || ble/function/is-global-trace-context; then 6559 _ble_builtin_trap_DEBUG_userTrapInitialized=1 6560 builtin eval -- "function $FUNCNAME { ((1)); }" 6561 6562 # Note: ble/util/assign は DEBUG を継承しないのでその場で trap -p で出力する 6563 local _ble_local_tmpfile; ble/util/assign/mktmp 6564 builtin trap -p DEBUG >| "$_ble_local_tmpfile" 6565 local content; ble/util/readfile content "$_ble_local_tmpfile" 6566 ble/util/assign/rmtmp 6567 6568 # ble.sh の設定した DEBUG trap は無視する。 6569 case ${content#"trap -- '"} in 6570 (ble-edit/exec:gexec/.TRAPDEBUG*|ble/builtin/trap/.handler*) ;; # ble-0.4 6571 (ble-edit/exec:exec/.eval-TRAPDEBUG*|ble-edit/exec:gexec/.eval-TRAPDEBUG*) ;; # ble-0.2 6572 (.ble-edit/exec:exec/eval-TRAPDEBUG*|.ble-edit/exec:gexec/eval-TRAPDEBUG*) ;; # ble-0.1 6573 (*) builtin eval -- "$content" ;; # ble/builtin/trap に処理させる 6574 esac 6575 return 0 6576 fi 6577 } 6578 ble/function#trace _ble_builtin_trap_DEBUG__initialize 6579 _ble_builtin_trap_DEBUG__initialize 6580 6581 function ble-edit/exec:gexec/.TRAPINT { 6582 # ユーザートラップがある時は中断処理は実行しない 6583 local ret; ble/builtin/trap/sig#resolve INT 6584 ble/builtin/trap/user-handler#has "$ret" && return 0 6585 6586 local ext=130 6587 ((_ble_bash>=40300)) || ext=128 # bash-4.2 以下は 128 6588 if [[ $_ble_attached ]]; then 6589 ble/util/print "$_ble_term_bold^C$_ble_term_sgr0" >&2 6590 _ble_edit_exec_TRAPDEBUG_INT=$ext 6591 ble-edit/exec:gexec/.TRAPDEBUG/trap 6592 else 6593 _ble_builtin_trap_postproc="{ return $ext || break; } 2>&$_ble_util_fd_stderr" 6594 fi 6595 } 6596 function ble-edit/exec:gexec/.TRAPINT/reset { 6597 blehook internal_INT-='ble-edit/exec:gexec/.TRAPINT' 6598 } 6599 function ble-edit/exec:gexec/invoke-hook-with-setexit { 6600 local -a BLE_PIPESTATUS 6601 BLE_PIPESTATUS=("${_ble_edit_exec_PIPESTATUS[@]}") 6602 ble-edit/exec/.setexit "$_ble_edit_exec_lastarg" 6603 LINENO=$_ble_edit_LINENO \ 6604 BASH_COMMAND=$_ble_edit_exec_BASH_COMMAND \ 6605 blehook/invoke "$@" 6606 } >&"$_ble_util_fd_stdout" 2>&"$_ble_util_fd_stderr" 6607 6608 function ble-edit/exec:gexec/.TRAPERR { 6609 if [[ $_ble_attached ]]; then 6610 [[ $_ble_edit_exec_inside_userspace ]] || return 126 6611 [[ $_ble_trap_bash_command != *'return "$_ble_edit_exec_lastexit"'* ]] || return 126 6612 fi 6613 return 0 6614 } 6615 blehook internal_ERR!='ble-edit/exec:gexec/.TRAPERR' 6616 6617 # ble-edit/exec:gexec/TERM 6618 # 6619 # Note #D1287: Bash は途中で TERM が変更されると勝手に TERM 固有のキー 6620 # を bind してしまう。これにより ble.sh がキーを読み取れなくなってし 6621 # まう。ここでは bash による bind を検出して rebind を実行する。因み 6622 # に再読み込みを強制すると其処でコマンド実行が失敗する可能性があるの 6623 # で ble/term/enter の後で rebind するべき。 6624 _ble_edit_exec_TERM= 6625 function ble-edit/exec:gexec/TERM/is-dirty { 6626 [[ $TERM != "$_ble_edit_exec_TERM" ]] && return 0 6627 local bindp 6628 ble/util/assign bindp 'builtin bind -p' 6629 [[ $bindp != "$_ble_decode_bind_bindp" ]] 6630 } 6631 function ble-edit/exec:gexec/TERM/leave { 6632 _ble_edit_exec_TERM=$TERM 6633 } 6634 function ble-edit/exec:gexec/TERM/enter { 6635 if [[ $_ble_decode_bind_state != none ]] && ble-edit/exec:gexec/TERM/is-dirty; then 6636 # Note: ble/decode/rebind ではなく元の binding の記録・復元も含めてやり直す。 6637 ble/edit/info/immediate-show text 'ble: TERM has changed. rebinding...' 6638 ble/decode/detach 6639 if ! ble/decode/attach; then 6640 ble-detach 6641 ble-edit/bind/.check-detach && return 1 6642 fi 6643 ble/edit/info/immediate-default 6644 fi 6645 } 6646 6647 ## @fn ble-edit/exec:gexec/.begin 6648 ## @fn ble-edit/exec:gexec/.end 6649 ## 端末や入出力などの設定をコマンド実行用に調整します。 6650 ## また DEBUG や INT に対する trap の設定も行います。 6651 ## DEBUG の設定の解除はトップレベルでないと実行できないので、 6652 ## 実際に使う時には以下の様にする必要があります。 6653 ## 6654 ## ble-edit/exec:gexec/.begin 6655 ## 6656 ## コマンド実行 6657 ## 6658 ## builtin trap -- - DEBUG 6659 ## ble-edit/exec:gexec/.end 6660 ## 6661 function ble-edit/exec:gexec/.begin { 6662 _ble_edit_exec_inside_begin=1 6663 local IFS=$_ble_term_IFS 6664 _ble_edit_exec_PWD=$PWD 6665 ble-edit/exec:gexec/TERM/leave 6666 ble/term/leave 6667 ble-edit/bind/stdout.on 6668 ble/util/buffer.flush >&2 6669 6670 # C-c に対して 6671 ble/builtin/trap/install-hook INT # 何故か改めて実行しないと有効にならない 6672 blehook internal_INT!='ble-edit/exec:gexec/.TRAPINT' 6673 ble-edit/exec:gexec/.TRAPDEBUG/restore 6674 } 6675 function ble-edit/exec:gexec/.end { 6676 _ble_edit_exec_inside_begin= 6677 local IFS=$_ble_term_IFS 6678 6679 # Note: builtin trap -- - DEBUG は何故か此処では効かないので 6680 # ble-edit/exec:gexec/.end を呼び出す直前に外側で実行する。 6681 ble-edit/exec:gexec/.TRAPINT/reset 6682 builtin trap -- - DEBUG 6683 6684 blehook/invoke exec_end 6685 [[ $PWD != "$_ble_edit_exec_PWD" ]] && blehook/invoke CHPWD 6686 ble/util/joblist.flush >&2 6687 ble-edit/bind/.check-detach && return 0 6688 ble/term/enter 6689 ble-edit/exec:gexec/TERM/enter || return 0 # rebind に失敗した時 .tail せずに抜ける 6690 ble/util/c2w:auto/check 6691 ble/edit/clear-command-layout 6692 [[ $1 == restore ]] && return 0 # Note: 前回の呼出で .end に失敗した時 #D1170 6693 ble-edit/bind/.tail # flush will be called here 6694 } 6695 6696 ## @fn ble-edit/exec:gexec/.prologue command command_id 6697 ## @param[in] command 6698 ## 次に実行するコマンド。_ble_edit_exec_BASH_COMMAND に記録する。 6699 function ble-edit/exec:gexec/.prologue { 6700 _ble_edit_exec_inside_prologue=1 6701 local IFS=$_ble_term_IFS 6702 _ble_edit_exec_BASH_COMMAND=$1 6703 _ble_edit_exec_command_id=$2 6704 BLE_COMMAND_ID=$2 6705 BLE_PIPESTATUS=("${_ble_edit_exec_PIPESTATUS[@]}") 6706 6707 _ble_edit_exec_BASH_COMMAND_eval=$_ble_edit_exec_BASH_COMMAND 6708 if [[ $bleopt_exec_restore_pipestatus ]] && ((${#BLE_PIPESTATUS[@]} > 0)); then 6709 local i pipe= 6710 for ((i=0;i<${#BLE_PIPESTATUS[@]};i++)); do 6711 pipe=$pipe'| (exit '${BLE_PIPESTATUS[i]}')' 6712 done 6713 _ble_edit_exec_BASH_COMMAND_eval="${pipe:2}; $_ble_edit_exec_BASH_COMMAND_eval" 6714 fi 6715 6716 ble-edit/restore-PS1 6717 ble-edit/restore-READLINE 6718 ble-edit/restore-IGNOREEOF 6719 builtin unset -v HISTCMD; ble/history/get-count -v HISTCMD 6720 6721 _ble_edit_exec_TRAPDEBUG_INT= 6722 ble/util/joblist.clear 6723 ble-edit/exec:gexec/invoke-hook-with-setexit internal_PREEXEC "$_ble_edit_exec_BASH_COMMAND" 6724 ble-edit/exec:gexec/invoke-hook-with-setexit PREEXEC "$_ble_edit_exec_BASH_COMMAND" 6725 ble-edit/exec/print-PS0 >&"$_ble_util_fd_stdout" 2>&"$_ble_util_fd_stderr" 6726 6727 ble/exec/time#start 6728 ble/base/restore-BASH_REMATCH 6729 } 6730 6731 ## @fn ble-edit/exec:gexec/.restore-lastarg lastarg 6732 ## @param[dummy] lastarg 6733 ## この引数は続くコマンドが $_ で参照する為の物なのでこの関数自身は利用しな 6734 ## い。 6735 function ble-edit/exec:gexec/.restore-lastarg { 6736 ble/base/restore-bash-options 6737 ble/base/restore-POSIXLY_CORRECT 6738 ble/base/restore-builtin-wrappers 6739 6740 # Note: これ以降関数は呼び出せない。但し一重までなら関数を呼び出せるので 6741 # ble-edit/exec:gexec/.restore-lastarg だけなら問題ない筈。 6742 builtin eval -- "$_ble_bash_FUNCNEST_restore" 6743 _ble_edit_exec_TRAPDEBUG_enabled=1 6744 _ble_edit_exec_inside_userspace=1 6745 _ble_exec_time_EPOCHREALTIME_beg=$EPOCHREALTIME 6746 return "$_ble_edit_exec_lastexit" # set $? 6747 } &>/dev/null # set -x 対策 #D0930 6748 function ble-edit/exec:gexec/.save-lastarg { 6749 _ble_exec_time_EPOCHREALTIME_end=$EPOCHREALTIME \ 6750 _ble_edit_exec_lastexit=$? \ 6751 _ble_edit_exec_lastarg=$_ \ 6752 _ble_edit_exec_PIPESTATUS=("${PIPESTATUS[@]}") 6753 _ble_edit_exec_inside_userspace= 6754 _ble_edit_exec_TRAPDEBUG_enabled= 6755 6756 # Note: 他の関数呼び出しよりも先。FUNCNEST の効果があるのは最低でも 6757 # FUNCNEST=1 なので一重なら関数はいつでも呼び出せる。なのでこの関数 6758 # .save-lastarg 自体の呼び出しは問題ない。 6759 builtin eval -- "$_ble_bash_FUNCNEST_adjust" 6760 ble/base/adjust-bash-options 6761 ble/exec/time#adjust-TIMEFORMAT 6762 return "$_ble_edit_exec_lastexit" 6763 } 6764 function ble-edit/exec:gexec/.epilogue { 6765 # Note: $_ は同じ eval の中でないと取れないのでここでは読み取らない。 6766 _ble_exec_time_EPOCHREALTIME_end=${_ble_exec_time_EPOCHREALTIME_end:-$EPOCHREALTIME} \ 6767 _ble_edit_exec_lastexit=$? 6768 _ble_edit_exec_inside_userspace= 6769 _ble_edit_exec_TRAPDEBUG_enabled= 6770 # Note: 他の関数呼び出しよりも先 6771 builtin eval -- "$_ble_bash_FUNCNEST_adjust" 6772 ble/base/adjust-builtin-wrappers-1 6773 if [[ $_ble_edit_exec_TRAPDEBUG_INT ]]; then 6774 if ((_ble_edit_exec_lastexit==0)); then 6775 _ble_edit_exec_lastexit=$_ble_edit_exec_TRAPDEBUG_INT 6776 fi 6777 _ble_edit_exec_TRAPDEBUG_INT= 6778 fi 6779 6780 local IFS=$_ble_term_IFS 6781 # Note: builtin trap -- - DEBUG は此処では何故か効かない 6782 builtin trap -- - DEBUG 6783 6784 ble/base/adjust-bash-options 6785 ble/base/adjust-POSIXLY_CORRECT 6786 ble/base/adjust-builtin-wrappers-2 6787 ble/base/adjust-BASH_REMATCH 6788 ble-edit/adjust-IGNOREEOF 6789 ble-edit/adjust-READLINE 6790 ble-edit/adjust-PS1 6791 ble/exec/time#restore-TIMEFORMAT 6792 ble/exec/time#end 6793 ble/util/reset-keymap-of-editing-mode 6794 ble-edit/exec/.adjust-eol 6795 _ble_edit_exec_inside_prologue= 6796 6797 ble/util/buffer.flush >&"$_ble_util_fd_stderr" 6798 ble-edit/exec:gexec/invoke-hook-with-setexit POSTEXEC "$_ble_edit_exec_BASH_COMMAND" 6799 6800 local msg= 6801 if ((_ble_edit_exec_lastexit)); then 6802 # ERREXEC処理 6803 ble-edit/exec:gexec/invoke-hook-with-setexit ERREXEC "$_ble_edit_exec_BASH_COMMAND" 6804 if [[ $bleopt_exec_errexit_mark ]]; then 6805 local ret 6806 ble/util/sprintf ret "$bleopt_exec_errexit_mark" "$_ble_edit_exec_lastexit" 6807 msg=$ret 6808 fi 6809 fi 6810 6811 if ble/exec/time#mark-enabled; then 6812 local format=$bleopt_exec_elapsed_mark 6813 if [[ $format ]]; then 6814 # ata 6815 local ata=$((_ble_exec_time_ata/1000)) 6816 if ((ata<1000)); then 6817 ata="${ata}ms" 6818 elif ((ata<1000*1000)); then 6819 ata="${ata::${#ata}-3}.${ata:${#ata}-3}s" 6820 elif ((ata/=1000,ata<3600*100)); then # ata [s] 6821 local min 6822 ((min=ata/60,ata%=60)) 6823 if ((min<100)); then 6824 ata="${min}m${ata}s" 6825 else 6826 ata="$((min/60))h$((min%60))m${ata}s" 6827 fi 6828 else 6829 local hour 6830 ((ata/=60,hour=ata/60,ata%=60)) 6831 ata="$((hour/24))d$((hour%24))h${ata}m" 6832 fi 6833 6834 # cpu 6835 local cpu='--.-' 6836 if ((_ble_exec_time_tot)); then 6837 cpu=$(((_ble_exec_time_usr+_ble_exec_time_sys)*1000/_ble_exec_time_tot)) 6838 cpu=$((cpu/10)).$((cpu%10)) 6839 fi 6840 6841 local ret 6842 ble/util/sprintf ret "$format" "$ata" "$cpu" 6843 msg=$msg$ret 6844 ble/string#ltrim "$_ble_edit_exec_BASH_COMMAND" 6845 msg="$msg $ret" 6846 fi 6847 fi 6848 6849 if [[ $msg ]]; then 6850 x=0 y=0 g=0 LINES=1 ble/canvas/trace "$msg" confine:truncate 6851 ble/util/buffer.print "$ret" 6852 fi 6853 6854 # bleopt prompt_ruler 6855 local -a DRAW_BUFF=() 6856 ble/prompt/print-ruler.draw "$_ble_edit_exec_BASH_COMMAND" 6857 ble/canvas/bflush.draw 6858 } 6859 function ble-edit/exec:gexec/.setup { 6860 # コマンドを _ble_decode_bind_hook に設定してグローバルで評価する。 6861 # 6862 # ※ユーザの入力したコマンドをグローバルではなく関数内で評価すると 6863 # declare した変数がコマンドローカルになってしまう。 6864 # 配列でない単純な変数に関しては declare を上書きする事で何とか誤魔化していたが、 6865 # declare -a arr=(a b c) の様な特殊な構文の物は上書きできない。 6866 # この所為で、例えば source 内で declare した配列などが壊れる。 6867 # 6868 ((${#_ble_edit_exec_lines[@]})) || [[ ! $_ble_edit_exec_TRAPDEBUG_adjusted ]] || return 1 6869 6870 local buff='_ble_decode_bind_hook=' ibuff=1 6871 6872 if [[ ! $_ble_edit_exec_TRAPDEBUG_adjusted ]]; then 6873 # Note #D1772: bash-3.1 以下で prompt attach すると、何故か一番外側で実行し 6874 # ていても attach-from-PROMPT_COMMAND の中で実行している事になっているので、 6875 # 明示的に force を指定して DEBUG trap を読み取らせる。 6876 buff[ibuff++]='_ble_builtin_trap_DEBUG__initialize force' 6877 buff[ibuff++]=_ble_edit_exec_gexec__TRAPDEBUG_adjust 6878 fi 6879 6880 local count=${#_ble_edit_exec_lines[@]} 6881 if ((count)); then 6882 ble/util/buffer.flush >&2 6883 6884 local q=\' Q="'\''" cmd cmd_id lineno 6885 buff[ibuff++]=ble-edit/exec:gexec/.begin 6886 for cmd in "${_ble_edit_exec_lines[@]}"; do 6887 cmd_id=${cmd%%,*} cmd=${cmd#*,} 6888 lineno=${cmd%%:*} cmd=${cmd#*:} 6889 buff[ibuff++]="ble-edit/exec:gexec/.prologue '${cmd//$q/$Q}' $cmd_id" 6890 # Note #D1823: LINENO を unset せずに上書きする為に tempenv を用いる。 6891 # Note #D1823: Bash に "builtin eval" で tempenv が消滅するバグがあるので 6892 # builtin を付けずに eval を直接呼び出す。adjust-builtin-wrappers して 6893 # いる筈 (restore-builtin-wrappers は eval の中の .restore-lastarg で実 6894 # 行している) なので、前回のコマンド実行後の状態調整に失敗したなどの事 6895 # がない限りは問題ない筈。 6896 # Note #D0465: restore-lastarg と実際のコマンドを同じ eval の中に入れるの 6897 # は set -v の時の出力を抑える為である。prologue で set -v を復元した直 6898 # 後にそのままコマンドを実行しないと無駄な出力がされてしまう。 6899 # Note: restore-lastarg の $_ble_edit_exec_lastarg は $_ を設定するための 6900 # ものである。 6901 buff[ibuff++]='{ time LINENO='$lineno' builtin eval -- "ble-edit/exec:gexec/.restore-lastarg \"\$_ble_edit_exec_lastarg\"' 6902 buff[ibuff++]='$_ble_edit_exec_BASH_COMMAND_eval' 6903 # Note #D0465: 実際のコマンドと save-lastarg を同じ eval の中に入れている 6904 # のは、同じ eval の中でないと $_ が失われてしまうから (特に eval を出 6905 # る時に eval の最終引数になってしまう)。 6906 buff[ibuff++]='{ ble-edit/exec:gexec/.save-lastarg; } &>/dev/null' # Note: &>/dev/null は set -x 対策 #D0930 6907 buff[ibuff++]='" 2>&"$_ble_util_fd_stderr"; } 2>| "$_ble_exec_time_TIMEFILE"' 6908 buff[ibuff++]='{ ble-edit/exec:gexec/.epilogue; } 3>&2 &>/dev/null' 6909 6910 # ※直接 $cmd と書き込むと文法的に破綻した物を入れた時に 6911 # 続きの行が実行されない事になってしまう。 6912 done 6913 _ble_edit_exec_lines=() 6914 6915 # Note: 現在は _ble_decode_bind_hook 経由で処理しているので問題ないが、 6916 # builtin trap - INT DEBUG を使う時一番外側 (此処) でないと効かない 6917 buff[ibuff++]=_ble_edit_exec_gexec__TRAPDEBUG_adjust 6918 buff[ibuff++]=ble-edit/exec:gexec/.end 6919 fi 6920 6921 if ((ibuff>=2)); then 6922 IFS=$'\n' builtin eval '_ble_decode_bind_hook="${buff[*]}"' 6923 fi 6924 6925 # コマンド実行をする場合は ble-edit/bind/.tail は遅延する 6926 ((count>=1)); return "$?" 6927 } 6928 6929 function ble-edit/exec:gexec/process { 6930 ble-edit/exec:gexec/.setup 6931 return "$?" 6932 } 6933 function ble-edit/exec:gexec/restore-state { 6934 # 構文エラー等で epilogue/end が呼び出されなかった時の為 #D1170 6935 [[ $_ble_edit_exec_inside_prologue ]] && ble-edit/exec:gexec/.epilogue 3>&2 &>/dev/null 6936 [[ $_ble_edit_exec_inside_begin ]] && ble-edit/exec:gexec/.end restore 6937 } 6938 6939 # **** accept-line **** @edit.accept 6940 6941 : "${_ble_edit_lineno:=0}" 6942 _ble_prompt_trim_opwd= 6943 6944 ## @fn ble/widget/.insert-newline/trim-prompt 6945 ## @var[ref] DRAW_BUFF 6946 function ble/widget/.insert-newline/trim-prompt { 6947 local ps1f=$bleopt_prompt_ps1_final 6948 local ps1t=$bleopt_prompt_ps1_transient 6949 if [[ ! $ps1f && :$ps1t: == *:trim:* ]]; then 6950 [[ :$ps1t: == *:same-dir:* && $PWD != $_ble_prompt_trim_opwd ]] && return 0 6951 local y=${_ble_prompt_ps1_data[4]} 6952 if ((y)); then 6953 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 0 6954 ble/canvas/panel#increase-height.draw "$_ble_textarea_panel" "$((-y))" shift 6955 ((_ble_textarea_gendy-=y)) 6956 fi 6957 fi 6958 } 6959 ## @fn ble/widget/.insert-newline [opts] 6960 ## @param[in,opt] opts 6961 ## 6962 ## @remarks keep-info が指定されていない場合は 6963 ## ble/edit/enter-command-layout が一段階呼び出されます。keep-info 6964 ## が指定されている場合は ble/edit/enter-command-layout の階層は変更 6965 ## しません。 6966 function ble/widget/.insert-newline { 6967 local opts=$1 6968 local -a DRAW_BUFF=() 6969 if [[ :$opts: == *:keep-info:* && $_ble_textarea_panel == 0 ]] && 6970 ! ble/util/joblist.has-events 6971 then 6972 # 最終状態の描画 6973 ble/textarea#render leave 6974 ble/widget/.insert-newline/trim-prompt 6975 6976 # info を表示したまま行を挿入し、今までの panel 0 の内容を範囲外に破棄 6977 local textarea_height=${_ble_canvas_panel_height[_ble_textarea_panel]} 6978 ble/canvas/panel#increase-height.draw "$_ble_textarea_panel" 1 6979 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 "$textarea_height" sgr0 6980 ble/canvas/bflush.draw 6981 else 6982 # 最終状態の描画 6983 ble/edit/enter-command-layout # #D1800 checked=.insert-newline 6984 ble/textarea#render leave 6985 ble/widget/.insert-newline/trim-prompt 6986 6987 # 新しい描画領域 6988 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "$_ble_textarea_gendx" "$_ble_textarea_gendy" sgr0 6989 ble/canvas/put.draw "$_ble_term_nl" 6990 ble/canvas/bflush.draw 6991 ble/util/joblist.bflush 6992 6993 # keep-info の時は階層をバランスする 6994 [[ :$opts: == *:keep-info:* ]] && ble/edit/leave-command-layout 6995 fi 6996 6997 # 描画領域情報の初期化 6998 ((_ble_edit_lineno++)) 6999 _ble_prompt_trim_opwd=$PWD 7000 ble/textarea#invalidate 7001 _ble_canvas_x=0 _ble_canvas_y=0 7002 _ble_textarea_gendx=0 _ble_textarea_gendy=0 7003 _ble_canvas_panel_height[_ble_textarea_panel]=1 7004 } 7005 ## @fn ble/widget/.hide-current-line [opts] 7006 ## @param[in] opts 7007 ## a colon-separated list of the following fields: 7008 ## 7009 ## keep-header 7010 ## keep the multiline prompt displayed in the terminal except 7011 ## for the last line. 7012 ## 7013 function ble/widget/.hide-current-line { 7014 local opts=$1 y_erase=0 7015 [[ :$opts: == *:keep-header:* ]] && y_erase=${_ble_prompt_ps1_data[4]} 7016 local -a DRAW_BUFF=() 7017 if ((y_erase)); then 7018 ble/canvas/panel#clear-after.draw "$_ble_textarea_panel" 0 "$y_erase" 7019 else 7020 ble/canvas/panel#clear.draw "$_ble_textarea_panel" 7021 fi 7022 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 "$y_erase" 7023 ble/canvas/bflush.draw 7024 ble/textarea#invalidate 7025 _ble_canvas_x=0 _ble_canvas_y=$y_erase 7026 _ble_textarea_gendx=0 _ble_textarea_gendy=$y_erase 7027 ((_ble_canvas_panel_height[_ble_textarea_panel]=1+y_erase)) 7028 } 7029 7030 function ble/widget/.newline/clear-content { 7031 # カーソルを表示する。 7032 # layer:overwrite でカーソルを消している時の為。 7033 [[ $_ble_edit_overwrite_mode ]] && 7034 ble/term/cursor-state/reveal 7035 7036 # 行内容の初期化 7037 ble-edit/content/reset '' newline 7038 _ble_edit_ind=0 7039 _ble_edit_mark=0 7040 _ble_edit_mark_active= 7041 _ble_edit_overwrite_mode= 7042 } 7043 7044 ## @fn ble/widget/.newline opts 7045 ## @param[in] opts 7046 ## コロン区切りのオプションです。 7047 ## keep-info 7048 ## info を隠さずに表示したままにします。 7049 ## (但し menu-complete は必ずクリアします。) 7050 function ble/widget/.newline { 7051 local opts=$1 7052 _ble_edit_mark_active= 7053 7054 # (for lib/core-complete.sh layer:menu_filter) 7055 if [[ $_ble_complete_menu_active ]]; then 7056 [[ $_ble_highlight_layer_menu_filter_beg ]] && 7057 ble/textarea#invalidate str # (#D0995) 7058 fi 7059 7060 # 現在のプロンプトの最終描画 & 次の行へ移動 7061 _ble_complete_menu_active= _ble_edit_overwrite_mode= ble/widget/.insert-newline "$opts" # #D1800 checked=.newline 7062 7063 # update LINENO 7064 local ret; ble/string#count-char "$_ble_edit_str" $'\n' 7065 ((_ble_edit_LINENO+=1+ret)) 7066 7067 ble/history/onleave.fire 7068 ble/widget/.newline/clear-content 7069 } 7070 7071 function ble/widget/discard-line { 7072 ble-edit/content/clear-arg 7073 [[ $bleopt_history_share ]] && ble/builtin/history/option:n 7074 _ble_edit_line_disabled=1 ble/widget/.newline keep-info 7075 ble/textarea#render 7076 } 7077 7078 function ble/edit/hist_expanded/.core { 7079 ble/builtin/history/option:p "$command" 7080 } 7081 function ble-edit/hist_expanded/.expand { 7082 ble/edit/hist_expanded/.core 2>/dev/null; local ext=$? 7083 ((ext)) && ble/util/print "$command" 7084 ble/util/put : 7085 return "$ext" 7086 } 7087 7088 ## @var[out] hist_expanded 7089 function ble-edit/hist_expanded.update { 7090 local command=$1 7091 if [[ ! -o histexpand || ! ${command//[ ]} ]]; then 7092 hist_expanded=$command 7093 return 0 7094 elif ble/util/assign hist_expanded 'ble-edit/hist_expanded/.expand'; then 7095 hist_expanded=${hist_expanded%$_ble_term_nl:} 7096 return 0 7097 else 7098 hist_expanded=$command 7099 return 1 7100 fi 7101 } 7102 7103 _ble_edit_integration_mc_precmd_stop= 7104 function ble/widget/accept-line/.is-mc-init { 7105 [[ $MC_SID == $$ ]] && ((_ble_edit_LINENO<=5)) || return 1 7106 7107 # Note #D2062: mc-4.8.29 以前は最初の行だけ不完全かチェックすれば良かった 7108 ((_ble_edit_LINENO==0)) && return 0 7109 7110 # Note #D2062: mc-4.8.29 以降では複数行の初期化スクリプトを送信してくる。特に 7111 # 4行目が不完全な状態で C-j を送信してくるので不完全な状態で実行されエラーに 7112 # なる。不完全な状態のものについてはコマンド実行ではなく改行挿入に変換する。 7113 # 7114 # ---- mc の初期化入力スクリプト例 ---- 7115 # mc_print_command_buffer () { printf "%s\\n" "$READLINE_LINE" >&13; } 7116 # bind -x '"\e_":"mc_print_command_buffer"' 7117 # bind -x '"\e+":"echo $BASH_VERSINFO:$READLINE_POINT >&18"' 7118 # PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND 7119 # }'pwd>&16;kill -STOP $$' 7120 # PS1='\u@\h:\w\$ ' 7121 # ------------------------------------- 7122 if [[ $_ble_edit_str == *'PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND'* ]]; then 7123 ble/string#match "$_ble_edit_str" 'pwd>&[0-9]+;kill -STOP \$\$' && 7124 _ble_edit_integration_mc_precmd_stop=1 7125 return 0 7126 fi 7127 7128 # Note #D2062: mc-4.8.29 は C-o C-o で mc 画面に戻る直前に M-_ M-+ を送信して 7129 # 現在の状態を抽出する。この時の最後の画面の状態を記録して、更に次に C-o が押 7130 # された時にそれを復元する。ところが ble.sh と一緒に使っているとこの復元がで 7131 # きない。M-+ における内容送信の直前で ble/textarea#redraw & 7132 # ble/util/buffer.flush を実行しておけば回避できる。M-+ の束縛を書き換える。 7133 if ble/string#match "$_ble_edit_str" 'bind -x '\''"\\e\+":"([^"'\'']+)"'\'''; then 7134 function ble/widget/.mc_exec_command { 7135 ble/textarea#redraw 7136 ble/util/buffer.flush >&2 7137 builtin eval -- "$1" 7138 } 7139 local str=${_ble_edit_str//"$BASH_REMATCH"/"ble-bind -f M-+ '.mc_exec_command '\''${BASH_REMATCH[1]}'\'''"} && 7140 [[ $str != "$_ble_edit_str" ]] && 7141 ble-edit/content/reset-and-check-dirty "$str" 7142 fi 7143 7144 return 1 7145 } 7146 7147 function ble/widget/accept-line { 7148 ble/decode/widget/keymap-dispatch "$@" 7149 } 7150 function ble/widget/default/accept-line { 7151 # 文法的に不完全の時は改行挿入 7152 # Note: mc (midnight commander) が改行を含むコマンドを書き込んでくる #D1392 7153 if [[ :$1: == *:syntax:* ]] || ble/widget/accept-line/.is-mc-init; then 7154 ble-edit/content/update-syntax 7155 if ! ble/syntax:bash/is-complete; then 7156 ble/widget/newline 7157 return "$?" 7158 fi 7159 fi 7160 7161 ble-edit/content/clear-arg 7162 local command=$_ble_edit_str 7163 7164 if [[ ! ${command//["$_ble_term_IFS"]} ]]; then 7165 [[ $bleopt_history_share ]] && 7166 ble/builtin/history/option:n 7167 ble/widget/.newline keep-info 7168 ble/prompt/print-ruler.buff '' keep-info 7169 ble/textarea#render 7170 ble/util/buffer.flush >&2 7171 return 0 7172 fi 7173 7174 # 履歴展開 7175 local hist_expanded 7176 if ! ble-edit/hist_expanded.update "$command"; then 7177 ble/widget/.internal-print-command \ 7178 'ble/edit/hist_expanded/.core 1>/dev/null' pre-flush # エラーメッセージを表示 7179 shopt -q histreedit &>/dev/null || ble/widget/.newline/clear-content 7180 return "$?" 7181 fi 7182 7183 local hist_is_expanded= 7184 if [[ $hist_expanded != "$command" ]]; then 7185 if shopt -q histverify &>/dev/null; then 7186 _ble_edit_line_disabled=1 ble/widget/.insert-newline keep-info 7187 ble-edit/content/reset-and-check-dirty "$hist_expanded" 7188 _ble_edit_ind=${#hist_expanded} 7189 _ble_edit_mark=0 7190 _ble_edit_mark_active= 7191 return 0 7192 fi 7193 7194 command=$hist_expanded 7195 hist_is_expanded=1 7196 fi 7197 7198 # 実行を登録 7199 local old_cmd=$_ble_edit_CMD 7200 ble-edit/exec/register "$command" 7201 7202 # 編集文字列を履歴に追加 7203 ble/history/add "$command" 7204 7205 _ble_edit_CMD=$old_cmd ble/widget/.newline # #D1800 register 7206 [[ $hist_is_expanded ]] && ble/util/buffer.print "${_ble_term_setaf[12]}[ble: expand]$_ble_term_sgr0 $command" 7207 } 7208 7209 function ble/widget/accept-and-next { 7210 ble-edit/content/clear-arg 7211 ble/history/initialize 7212 local index=$_ble_history_INDEX 7213 local count=$_ble_history_COUNT 7214 7215 if ((index+1<count)); then 7216 local HISTINDEX_NEXT=$((index+1)) # to be modified in accept-line 7217 ble/widget/accept-line 7218 ble-edit/history/goto "$HISTINDEX_NEXT" 7219 else 7220 local content=$_ble_edit_str 7221 ble/widget/accept-line 7222 7223 count=$_ble_history_COUNT 7224 if ((count)); then 7225 local entry; ble/history/get-entry "$((count-1))" 7226 if [[ $entry == "$content" ]]; then 7227 ble-edit/history/goto "$((count-1))" 7228 fi 7229 fi 7230 7231 [[ $_ble_edit_str != "$content" ]] && 7232 ble-edit/content/reset "$content" 7233 fi 7234 } 7235 function ble/widget/newline { 7236 ble/decode/widget/keymap-dispatch "$@" 7237 } 7238 function ble/widget/default/newline { 7239 local -a KEYS=(10) 7240 ble/widget/self-insert 7241 } 7242 function ble/widget/tab-insert { 7243 local -a KEYS=(9) 7244 ble/widget/self-insert 7245 } 7246 function ble-edit/is-single-complete-line { 7247 ble-edit/content/is-single-line || return 1 7248 [[ $_ble_edit_str ]] && ble/decode/has-input && 7249 ((0<=bleopt_accept_line_threshold&&bleopt_accept_line_threshold<=_ble_decode_input_count+ble_decode_char_rest)) && 7250 return 1 7251 if shopt -q cmdhist &>/dev/null; then 7252 ble-edit/content/update-syntax 7253 ble/syntax:bash/is-complete || return 1 7254 fi 7255 return 0 7256 } 7257 function ble/widget/accept-single-line-or { 7258 ble/decode/widget/keymap-dispatch "$@" 7259 } 7260 function ble/widget/default/accept-single-line-or { 7261 if ble-edit/is-single-complete-line; then 7262 ble/widget/accept-line 7263 else 7264 ble/widget/"$@" 7265 fi 7266 } 7267 function ble/widget/accept-single-line-or-newline { 7268 ble/widget/accept-single-line-or newline 7269 } 7270 ## @fn ble/widget/edit-and-execute-command.edit content opts 7271 ## @var[in] content 7272 ## @var[in] opts 7273 ## no-newline が指定されていない時、内部で enter-command-layout を実行する。 7274 ## 続けて ble-edit/exec/register が実行される事を想定する。 7275 ## @var[out] ret 7276 function ble/widget/edit-and-execute-command.edit { 7277 local content=$1 opts=:$2: 7278 7279 local file=$_ble_base_run/$$.blesh-fc.bash 7280 ble/util/print "$content" >| "$file" 7281 7282 local fallback=vi 7283 if type emacs &>/dev/null; then 7284 fallback='emacs -nw' 7285 elif type vim &>/dev/null; then 7286 fallback=vim 7287 elif type nano &>/dev/null; then 7288 fallback=nano 7289 fi 7290 7291 [[ $opts == *:no-newline:* ]] || 7292 _ble_edit_line_disabled=1 ble/widget/.newline # #D1800 (呼び出し元で exec/register) 7293 7294 ble/term/leave 7295 ${bleopt_editor:-${VISUAL:-${EDITOR:-$fallback}}} "$file"; local ext=$? 7296 ble/term/enter 7297 7298 if ((ext)); then 7299 ble/widget/.bell 7300 return 127 7301 fi 7302 7303 ble/util/readfile ret "$file" 7304 return 0 7305 } 7306 function ble/widget/edit-and-execute-command.impl { 7307 local ret= 7308 ble/widget/edit-and-execute-command.edit "$1" 7309 local command=$ret 7310 7311 ble/string#match "$command" $'[\n]+$' && 7312 command=${command::${#command}-${#BASH_REMATCH}} 7313 if [[ $command != *[!"$_ble_term_IFS"]* ]]; then 7314 ble/edit/leave-command-layout 7315 ble/widget/.bell 7316 return 1 7317 fi 7318 7319 # Note: accept-line を参考にした 7320 ble/util/buffer.print "${_ble_term_setaf[12]}[ble: fc]$_ble_term_sgr0 $command" 7321 ble/history/add "$command" 7322 ble-edit/exec/register "$command" 7323 } 7324 function ble/widget/edit-and-execute-command { 7325 ble-edit/content/clear-arg 7326 ble/widget/edit-and-execute-command.impl "$_ble_edit_str" 7327 } 7328 7329 function ble/widget/insert-comment/.remove-comment { 7330 local comment_begin=$1 7331 ret= 7332 7333 [[ $comment_begin ]] || return 1 7334 ble/string#escape-for-extended-regex "$comment_begin"; local rex_comment_begin=$ret 7335 local rex1=$'([ \t]*'$rex_comment_begin$')[^\n]*(\n|$)|[ \t]+(\n|$)|\n' 7336 local rex=$'^('$rex1')*$'; [[ $_ble_edit_str =~ $rex ]] || return 1 7337 7338 local tail=$_ble_edit_str out= 7339 while [[ $tail && $tail =~ ^$rex1 ]]; do 7340 local rematch1=${BASH_REMATCH[1]} 7341 if [[ $rematch1 ]]; then 7342 out=$out${rematch1%?}${BASH_REMATCH:${#rematch1}} 7343 else 7344 out=$out$BASH_REMATCH 7345 fi 7346 tail=${tail:${#BASH_REMATCH}} 7347 done 7348 7349 [[ $tail ]] && return 1 7350 7351 ret=$out 7352 } 7353 function ble/widget/insert-comment/.insert { 7354 local arg=$1 7355 local ret; ble/util/rlvar#read comment-begin '#' 7356 local comment_begin=${ret::1} 7357 local text= 7358 if [[ $arg ]] && ble/widget/insert-comment/.remove-comment "$comment_begin"; then 7359 text=$ret 7360 else 7361 text=$comment_begin${_ble_edit_str//$'\n'/$'\n'"$comment_begin"} 7362 fi 7363 ble-edit/content/reset-and-check-dirty "$text" 7364 } 7365 function ble/widget/insert-comment { 7366 local arg; ble-edit/content/get-arg '' 7367 ble/widget/insert-comment/.insert "$arg" 7368 ble/widget/accept-line 7369 } 7370 7371 function ble/widget/alias-expand-line.proc { 7372 if ((tchild>=0)); then 7373 ble/syntax/tree-enumerate-children \ 7374 ble/widget/alias-expand-line.proc 7375 elif [[ $wtype && ! ${wtype//[0-9]} ]] && ((wtype==_ble_ctx_CMDI)); then 7376 local word=${_ble_edit_str:wbegin:wlen} 7377 local ret; ble/alias#expand "$word" 7378 [[ $word == "$ret" ]] && return 0 7379 changed=1 7380 ble/widget/.replace-range "$wbegin" "$((wbegin+wlen))" "$ret" 7381 fi 7382 } 7383 function ble/widget/alias-expand-line { 7384 ble-edit/content/clear-arg 7385 ble-edit/content/update-syntax 7386 local iN= changed= 7387 ble/syntax/tree-enumerate ble/widget/alias-expand-line.proc 7388 [[ $changed ]] && _ble_edit_mark_active= 7389 } 7390 7391 function ble/widget/tilde-expand { 7392 ble-edit/content/clear-arg 7393 ble-edit/content/update-syntax 7394 local len=${#_ble_edit_str} 7395 local i=$len j=$len 7396 while ((--i>=0)); do 7397 ((_ble_syntax_attr[i])) || continue 7398 if ((_ble_syntax_attr[i]==_ble_attr_TILDE)); then 7399 local word=${_ble_edit_str:i:j-i} 7400 builtin eval "local path=$word" 7401 [[ $path != "$word" ]] && 7402 ble/widget/.replace-range "$i" "$j" "$path" 7403 fi 7404 j=$i 7405 done 7406 } 7407 7408 _ble_edit_shell_expand_ExpandWtype=() 7409 function ble/widget/shell-expand-line.initialize { 7410 function ble/widget/shell-expand-line.initialize { :; } 7411 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_CMDI]=1 7412 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_ARGI]=1 7413 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_ARGEI]=1 7414 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_ARGVI]=1 7415 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_RDRF]=1 7416 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_RDRD]=1 7417 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_RDRS]=1 7418 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_VALI]=1 7419 _ble_edit_shell_expand_ExpandWtype[_ble_ctx_CONDI]=1 7420 } 7421 ## @fn ble/widget/shell-expand-line.expand-word 7422 ## @var[in] wtype 7423 ## @var[out] ret flags 7424 function ble/widget/shell-expand-line.expand-word { 7425 local word=$1 7426 7427 # 未知の wtype については処理しない。 7428 ble/widget/shell-expand-line.initialize 7429 if [[ ! ${_ble_edit_shell_expand_ExpandWtype[wtype]} ]]; then 7430 ret=$word 7431 return 0 7432 fi 7433 7434 # 単語展開 7435 ret=$word; [[ $ret == '~'* ]] && ret='\'$word 7436 ble/syntax:bash/simple-word/eval "$ret" noglob 7437 if [[ $word != $ret || ${#ret[@]} -ne 1 ]]; then 7438 [[ $opts == *:quote:* ]] && flags=${flags}q 7439 return 0 7440 fi 7441 7442 # エイリアス展開 7443 if ((wtype==_ble_ctx_CMDI)); then 7444 ble/alias#expand "$word" 7445 [[ $word != $ret ]] && return 0 7446 fi 7447 7448 ret=$word 7449 } 7450 function ble/widget/shell-expand-line.proc { 7451 [[ $wtype ]] || return 0 7452 7453 # 単語以外の構造の場合には中に入る (例: < file や [[ arg ]] など) 7454 if [[ ${wtype//[0-9]} ]]; then 7455 ble/syntax/tree-enumerate-children ble/widget/shell-expand-line.proc 7456 return 0 7457 fi 7458 7459 local word=${_ble_edit_str:wbegin:wlen} 7460 7461 # 配列代入の時は配列要素に対して適用 7462 local rex='^[_a-zA-Z][_a-zA-Z0-9]*=+?\(' 7463 if ((wtype==_ble_attr_VAR)) && [[ $word =~ $rex ]]; then 7464 ble/syntax/tree-enumerate-children ble/widget/shell-expand-line.proc 7465 return 0 7466 fi 7467 7468 local flags= 7469 local -a ret=() words=() 7470 ble/widget/shell-expand-line.expand-word "$word" 7471 words=("${ret[@]}") 7472 [[ ${#words[@]} -eq 1 && $word == "$ret" ]] && return 0 7473 7474 if ((wtype==_ble_ctx_RDRF||wtype==_ble_ctx_RDRD||wtype==_ble_ctx_RDRS)); then 7475 local IFS=$_ble_term_IFS 7476 words=("${words[*]}") 7477 fi 7478 7479 local q=\' Q="'\''" specialchars='\ ["'\''`$|&;<>()*?!^{,}' 7480 local w index=0 out= 7481 for w in "${words[@]}"; do 7482 ((index++)) && out=$out' ' 7483 [[ $flags == *q* && $w == *["$specialchars"]* ]] && w=$q${w//$q/$Q}$q 7484 out=$out$w 7485 done 7486 7487 changed=1 7488 ble/widget/.replace-range "$wbegin" "$((wbegin+wlen))" "$out" 7489 } 7490 ## @widget shell-expand-line opts 7491 ## @param[in] opts 7492 ## コロン区切りのオプションです。 7493 ## quote 直接実行した時と振る舞いが同じになる様に、 7494 ## 展開結果を適切に quote します。 7495 function ble/widget/shell-expand-line { 7496 local opts=:$1: 7497 ble-edit/content/clear-arg 7498 ble/widget/history-expand-line 7499 ble-edit/content/update-syntax 7500 local iN= changed= 7501 ble/syntax/tree-enumerate ble/widget/shell-expand-line.proc 7502 [[ $changed ]] && _ble_edit_mark_active= 7503 } 7504 7505 # 7506 #------------------------------------------------------------------------------ 7507 # **** ble-edit/undo **** @edit.undo 7508 7509 ## @var _ble_edit_undo_hindex= 7510 ## 現在の _ble_edit_undo が保持する情報の履歴項目番号。 7511 ## 初期は空文字列でどの履歴項目でもない状態を表す。 7512 ## 7513 7514 _ble_edit_undo=() 7515 _ble_edit_undo_index=0 7516 _ble_edit_undo_history=() 7517 _ble_edit_undo_hindex= 7518 ble/array#push _ble_textarea_local_VARNAMES \ 7519 _ble_edit_undo \ 7520 _ble_edit_undo_index \ 7521 _ble_edit_undo_history \ 7522 _ble_edit_undo_hindex 7523 function ble-edit/undo/.check-hindex { 7524 local hindex; ble/history/get-index -v hindex 7525 [[ $_ble_edit_undo_hindex == "$hindex" ]] && return 0 7526 7527 # save 7528 if [[ $_ble_edit_undo_hindex ]]; then 7529 local uindex=${_ble_edit_undo_index:-${#_ble_edit_undo[@]}} 7530 local ret; ble/string#quote-words "$uindex" "${_ble_edit_undo[@]}" 7531 _ble_edit_undo_history[_ble_edit_undo_hindex]=$ret 7532 fi 7533 7534 # load 7535 if [[ ${_ble_edit_undo_history[hindex]} ]]; then 7536 local data; builtin eval -- "data=(${_ble_edit_undo_history[hindex]})" 7537 _ble_edit_undo=("${data[@]:1}") 7538 _ble_edit_undo_index=${data[0]} 7539 else 7540 _ble_edit_undo=() 7541 _ble_edit_undo_index=0 7542 fi 7543 _ble_edit_undo_hindex=$hindex 7544 } 7545 function ble-edit/undo/clear-all { 7546 _ble_edit_undo=() 7547 _ble_edit_undo_index=0 7548 _ble_edit_undo_history=() 7549 _ble_edit_undo_hindex= 7550 } 7551 function ble-edit/undo/history-change.hook { 7552 local kind=$1; shift 7553 case $kind in 7554 (delete) 7555 ble/builtin/history/array#delete-hindex _ble_edit_undo_history "$@" 7556 _ble_edit_undo_hindex= ;; 7557 (clear) 7558 ble-edit/undo/clear-all ;; 7559 (insert) 7560 ble/builtin/history/array#insert-range _ble_edit_undo_history "$@" 7561 local beg=$1 len=$2 7562 [[ $_ble_edit_undo_hindex ]] && 7563 ((_ble_edit_undo_hindex>=beg)) && 7564 ((_ble_edit_undo_hindex+=len)) ;; 7565 esac 7566 } 7567 blehook history_change!=ble-edit/undo/history-change.hook 7568 7569 ## @fn ble-edit/undo/.get-current-state 7570 ## @var[out] str ind 7571 function ble-edit/undo/.get-current-state { 7572 if ((_ble_edit_undo_index==0)); then 7573 str= 7574 if [[ $_ble_history_prefix || $_ble_history_load_done ]]; then 7575 local index; ble/history/get-index 7576 ble/history/get-entry -v str "$index" 7577 fi 7578 ind=${#entry} 7579 else 7580 local entry=${_ble_edit_undo[_ble_edit_undo_index-1]} 7581 str=${entry#*:} ind=${entry%%:*} 7582 fi 7583 } 7584 7585 function ble-edit/undo/add { 7586 ble-edit/undo/.check-hindex 7587 7588 # 変更がない場合は記録しない 7589 local str ind; ble-edit/undo/.get-current-state 7590 [[ $str == "$_ble_edit_str" ]] && return 0 7591 7592 _ble_edit_undo[_ble_edit_undo_index++]=$_ble_edit_ind:$_ble_edit_str 7593 if ((${#_ble_edit_undo[@]}>_ble_edit_undo_index)); then 7594 _ble_edit_undo=("${_ble_edit_undo[@]::_ble_edit_undo_index}") 7595 fi 7596 } 7597 function ble-edit/undo/.load { 7598 local str ind; ble-edit/undo/.get-current-state 7599 if [[ $bleopt_undo_point == end || $bleopt_undo_point == beg ]]; then 7600 7601 # Note: 実際の編集過程に依らず、現在位置 _ble_edit_ind の周辺で 7602 # 変更前と変更後の文字列だけから「変更範囲」を決定する事にする。 7603 local old=$_ble_edit_str new=$str ret 7604 if [[ $bleopt_undo_point == end ]]; then 7605 ble/string#common-suffix "${old:_ble_edit_ind}" "$new"; local s1=${#ret} 7606 local old=${old::${#old}-s1} new=${new:${#new}-s1} 7607 ble/string#common-prefix "${old::_ble_edit_ind}" "$new"; local p1=${#ret} 7608 local old=${old:p1} new=${new:p1} 7609 ble/string#common-suffix "$old" "$new"; local s2=${#ret} 7610 local old=${old::${#old}-s2} new=${new:${#new}-s2} 7611 ble/string#common-prefix "$old" "$new"; local p2=${#ret} 7612 else 7613 ble/string#common-prefix "${old::_ble_edit_ind}" "$new"; local p1=${#ret} 7614 local old=${old:p1} new=${new:p1} 7615 ble/string#common-suffix "${old:_ble_edit_ind-p1}" "$new"; local s1=${#ret} 7616 local old=${old::${#old}-s1} new=${new:${#new}-s1} 7617 ble/string#common-prefix "$old" "$new"; local p2=${#ret} 7618 local old=${old:p2} new=${new:p2} 7619 ble/string#common-suffix "$old" "$new"; local s2=${#ret} 7620 fi 7621 7622 local beg=$((p1+p2)) end0=$((${#_ble_edit_str}-s1-s2)) end=$((${#str}-s1-s2)) 7623 ble-edit/content/replace "$beg" "$end0" "${str:beg:end-beg}" 7624 7625 if [[ $bleopt_undo_point == end ]]; then 7626 ind=$end 7627 else 7628 ind=$beg 7629 fi 7630 else 7631 ble-edit/content/reset-and-check-dirty "$str" 7632 fi 7633 7634 _ble_edit_ind=$ind 7635 return 0 7636 } 7637 function ble-edit/undo/undo { 7638 local arg=${1:-1} 7639 ble-edit/undo/.check-hindex 7640 ble-edit/undo/add # 最後に add/load してから変更があれば記録 7641 ((_ble_edit_undo_index)) || return 1 7642 ((_ble_edit_undo_index-=arg)) 7643 ((_ble_edit_undo_index<0&&(_ble_edit_undo_index=0))) 7644 ble-edit/undo/.load 7645 } 7646 function ble-edit/undo/redo { 7647 local arg=${1:-1} 7648 ble-edit/undo/.check-hindex 7649 ble-edit/undo/add # 最後に add/load してから変更があれば記録 7650 local ucount=${#_ble_edit_undo[@]} 7651 ((_ble_edit_undo_index<ucount)) || return 1 7652 ((_ble_edit_undo_index+=arg)) 7653 ((_ble_edit_undo_index>=ucount&&(_ble_edit_undo_index=ucount))) 7654 ble-edit/undo/.load 7655 } 7656 function ble-edit/undo/revert { 7657 ble-edit/undo/.check-hindex 7658 ble-edit/undo/add # 最後に add/load してから変更があれば記録 7659 ((_ble_edit_undo_index)) || return 1 7660 ((_ble_edit_undo_index=0)) 7661 ble-edit/undo/.load 7662 } 7663 function ble-edit/undo/revert-toggle { 7664 local arg=${1:-1} 7665 ((arg%2==0)) && return 0 7666 ble-edit/undo/.check-hindex 7667 ble-edit/undo/add # 最後に add/load してから変更があれば記録 7668 if ((_ble_edit_undo_index)); then 7669 ((_ble_edit_undo_index=0)) 7670 ble-edit/undo/.load 7671 elif ((${#_ble_edit_undo[@]})); then 7672 ((_ble_edit_undo_index=${#_ble_edit_undo[@]})) 7673 ble-edit/undo/.load 7674 else 7675 return 1 7676 fi 7677 } 7678 7679 # 7680 #------------------------------------------------------------------------------ 7681 # **** ble-edit/keyboard-macro **** @edit.macro 7682 7683 _ble_edit_kbdmacro_record= 7684 _ble_edit_kbdmacro_last=() 7685 _ble_edit_kbdmacro_onplay= 7686 function ble/widget/start-keyboard-macro { 7687 ble/keymap:generic/clear-arg 7688 [[ $_ble_edit_kbdmacro_onplay ]] && return 0 # 再生中は無視 7689 if ! ble/decode/charlog#start kbd-macro; then 7690 if [[ $_ble_decode_keylog_chars_enabled == kbd-macro ]]; then 7691 ble/widget/.bell 'kbd-macro: recording is already started' 7692 else 7693 ble/widget/.bell 'kbd-macro: the logging system is currently busy' 7694 fi 7695 return 1 7696 fi 7697 7698 _ble_edit_kbdmacro_record=1 7699 if [[ $_ble_decode_keymap == emacs ]]; then 7700 ble/keymap:emacs/update-mode-indicator 7701 elif [[ $_ble_decode_keymap == vi_nmap ]]; then 7702 ble/keymap:vi/adjust-command-mode 7703 fi 7704 return 0 7705 } 7706 function ble/widget/end-keyboard-macro { 7707 ble/keymap:generic/clear-arg 7708 [[ $_ble_edit_kbdmacro_onplay ]] && return 0 # 再生中は無視 7709 if [[ $_ble_decode_keylog_chars_enabled != kbd-macro ]]; then 7710 ble/widget/.bell 'kbd-macro: recording is not running' 7711 return 1 7712 fi 7713 _ble_edit_kbdmacro_record= 7714 7715 ble/decode/charlog#end-exclusive-depth1 7716 _ble_edit_kbdmacro_last=("${ret[@]}") 7717 if [[ $_ble_decode_keymap == emacs ]]; then 7718 ble/keymap:emacs/update-mode-indicator 7719 elif [[ $_ble_decode_keymap == vi_nmap ]]; then 7720 ble/keymap:vi/adjust-command-mode 7721 fi 7722 return 0 7723 } 7724 function ble/widget/call-keyboard-macro { 7725 local arg; ble-edit/content/get-arg 1 7726 ble/keymap:generic/clear-arg 7727 ((arg>0)) || return 1 7728 [[ $_ble_edit_kbdmacro_onplay ]] && return 0 # 再生中は無視 7729 7730 local _ble_edit_kbdmacro_onplay=1 7731 if ((arg==1)); then 7732 ble/widget/.MACRO "${_ble_edit_kbdmacro_last[@]}" 7733 else 7734 local -a chars=() 7735 while ((arg-->0)); do 7736 ble/array#push chars "${_ble_edit_kbdmacro_last[@]}" 7737 done 7738 ble/widget/.MACRO "${chars[@]}" 7739 fi 7740 [[ $_ble_decode_keymap == vi_nmap ]] && 7741 ble/keymap:vi/adjust-command-mode 7742 } 7743 function ble/widget/print-keyboard-macro { 7744 ble/keymap:generic/clear-arg 7745 local ret; ble/decode/charlog#encode "${_ble_edit_kbdmacro_last[@]}" 7746 ble/edit/info/show text "kbd-macro: $ret" 7747 [[ $_ble_decode_keymap == vi_nmap ]] && 7748 ble/keymap:vi/adjust-command-mode 7749 return 0 7750 } 7751 7752 # 7753 #------------------------------------------------------------------------------ 7754 # **** history **** @history 7755 7756 bleopt/declare -v history_preserve_point '' 7757 7758 function ble-edit/history/goto { 7759 ble/history/initialize 7760 7761 local histlen=$_ble_history_COUNT 7762 local index0=$_ble_history_INDEX 7763 local index1=$1 7764 7765 ((index0==index1)) && return 0 7766 7767 if ((index1>histlen)); then 7768 index1=$histlen 7769 ble/widget/.bell 7770 elif ((index1<0)); then 7771 index1=0 7772 ble/widget/.bell 7773 fi 7774 7775 ((index0==index1)) && return 0 7776 7777 if [[ $bleopt_history_share && ! $_ble_history_prefix && $_ble_decode_keymap != isearch ]]; then 7778 # Note: isearch の途中の history/goto で履歴情報が書き換わると変な事になるので 7779 # isearch では history_share による読み込みは行わない。 7780 # 一方で nsearch や lastarg は過去の履歴項目を参照するが 7781 # ble-edit/history/goto を呼び出す事はない。 7782 if ((index0==histlen||index1==histlen)); then 7783 ble/builtin/history/option:n 7784 local histlen2=$_ble_history_COUNT 7785 if ((histlen!=histlen2)); then 7786 ble/textarea#invalidate 7787 ble-edit/history/goto "$((index1==histlen?histlen:index1))" 7788 return "$?" 7789 fi 7790 fi 7791 fi 7792 7793 # store 7794 ble/history/set-edited-entry "$index0" "$_ble_edit_str" 7795 ble/history/onleave.fire 7796 7797 # restore 7798 ble/history/set-index "$index1" 7799 local entry; ble/history/get-edited-entry -v entry "$index1" 7800 ble-edit/content/reset "$entry" history 7801 7802 # point 7803 if [[ $bleopt_history_preserve_point ]]; then 7804 if ((_ble_edit_ind>${#_ble_edit_str})); then 7805 _ble_edit_ind=${#_ble_edit_str} 7806 fi 7807 else 7808 if ((index1<index0)); then 7809 # 遡ったときは最後の行の末尾 7810 _ble_edit_ind=${#_ble_edit_str} 7811 else 7812 # 進んだときは最初の行の末尾 7813 local first_line=${_ble_edit_str%%$'\n'*} 7814 _ble_edit_ind=${#first_line} 7815 fi 7816 fi 7817 _ble_edit_mark=0 7818 _ble_edit_mark_active= 7819 } 7820 7821 function ble-edit/history/history-message.hook { 7822 ((_ble_edit_attached)) || return 1 7823 local message=$1 7824 if [[ $message ]]; then 7825 ble/edit/info/immediate-show text "$message" 7826 else 7827 ble/edit/info/immediate-default 7828 fi 7829 } 7830 blehook history_message!=ble-edit/history/history-message.hook 7831 7832 # 7833 #------------------------------------------------------------------------------ 7834 # **** basic history widgets **** @history.widget 7835 7836 function ble/widget/history-next { 7837 if [[ $_ble_history_prefix || $_ble_history_load_done ]]; then 7838 local arg; ble-edit/content/get-arg 1 7839 ble/history/initialize 7840 ble-edit/history/goto "$((_ble_history_INDEX+arg))" 7841 else 7842 ble-edit/content/clear-arg 7843 ble/widget/.bell 7844 fi 7845 } 7846 function ble/widget/history-prev { 7847 local arg; ble-edit/content/get-arg 1 7848 ble/history/initialize 7849 ble-edit/history/goto "$((_ble_history_INDEX-arg))" 7850 } 7851 function ble/widget/history-beginning { 7852 ble-edit/content/clear-arg 7853 ble-edit/history/goto 0 7854 } 7855 function ble/widget/history-end { 7856 ble-edit/content/clear-arg 7857 if [[ $_ble_history_prefix || $_ble_history_load_done ]]; then 7858 ble/history/initialize 7859 ble-edit/history/goto "$_ble_history_COUNT" 7860 else 7861 ble/widget/.bell 7862 fi 7863 } 7864 7865 ## @widget history-expand-line 7866 ## @exit 展開が行われた時に成功します。それ以外の時に失敗します。 7867 function ble/widget/history-expand-line { 7868 ble-edit/content/clear-arg 7869 local hist_expanded 7870 ble-edit/hist_expanded.update "$_ble_edit_str" || return 1 7871 [[ $_ble_edit_str == "$hist_expanded" ]] && return 1 7872 7873 ble-edit/content/reset-and-check-dirty "$hist_expanded" 7874 _ble_edit_ind=${#hist_expanded} 7875 _ble_edit_mark=0 7876 _ble_edit_mark_active= 7877 return 0 7878 } 7879 function ble/widget/history-and-alias-expand-line { 7880 ble/widget/history-expand-line 7881 ble/widget/alias-expand-line 7882 } 7883 ## @widget history-expand-backward-line 7884 ## @exit 展開が行われた時に成功します。それ以外の時に失敗します。 7885 function ble/widget/history-expand-backward-line { 7886 ble-edit/content/clear-arg 7887 local prevline=${_ble_edit_str::_ble_edit_ind} hist_expanded 7888 ble-edit/hist_expanded.update "$prevline" || return 1 7889 [[ $prevline == "$hist_expanded" ]] && return 1 7890 7891 local ret 7892 ble/string#common-prefix "$prevline" "$hist_expanded"; local dmin=${#ret} 7893 7894 local insert; ble-edit/content/replace-limited "$dmin" "$_ble_edit_ind" "${hist_expanded:dmin}" 7895 ((_ble_edit_ind=dmin+${#insert})) 7896 _ble_edit_mark=0 7897 _ble_edit_mark_active= 7898 return 0 7899 } 7900 ## @widget magic-space 7901 ## 履歴展開と静的略語展開を実行してから空白を挿入します。 7902 function ble/widget/magic-space { 7903 # keymap/vi.sh 7904 [[ $_ble_decode_keymap == vi_imap ]] && 7905 local oind=$_ble_edit_ind ostr=$_ble_edit_str 7906 7907 local arg; ble-edit/content/get-arg '' 7908 local opts=$bleopt_edit_magic_opts 7909 7910 local expanded= opt_noinsert= 7911 # (1) history expansion 7912 if [[ :$bleopt_edit_magic_expand: == *:history:* ]]; then 7913 ble/widget/history-expand-backward-line && expanded=1 7914 fi 7915 # (2) sabbrev expansion 7916 if [[ ! $expanded && :$bleopt_edit_magic_expand: == *:sabbrev:* ]]; then 7917 ble/complete/sabbrev/expand type-status; local ext=$? 7918 if ((ext==0||32<=ext&&ext<=126)); then 7919 expanded=1 7920 ((ext==105)) && # 105 = 'i' (inline sabbrev) 7921 [[ :$opts: == *:inline-sabbrev-no-insert:* ]] && 7922 opt_noinsert=1 7923 elif ((ext==147)); then 7924 return 147 # メニュー補完に入った時 7925 fi 7926 fi 7927 # (3) alias expansion 7928 if [[ ! $expanded && :$bleopt_edit_magic_expand: == *:alias:* ]]; then 7929 ble/complete/alias/expand && expanded=1 7930 fi 7931 7932 # keymap/vi.sh 7933 if [[ $_ble_decode_keymap == vi_imap && $ostr != "$_ble_edit_str" ]]; then 7934 _ble_edit_ind=$oind _ble_edit_str=$ostr ble/keymap:vi/undo/add more 7935 ble/keymap:vi/undo/add more 7936 fi 7937 7938 if [[ ! $opt_noinsert ]]; then 7939 local -a KEYS=(32) 7940 _ble_edit_arg=$arg 7941 ble/widget/self-insert 7942 fi 7943 } 7944 function ble/widget/magic-slash { 7945 ble/complete/sabbrev/expand wordwise:pattern='~*':strip-slash 7946 (($?==147)) && return 147 # sabbrev/expand の中でメニュー補完に入った時など。 7947 7948 local -a KEYS=(47) # / 7949 ble/widget/self-insert 7950 } 7951 7952 # 7953 #------------------------------------------------------------------------------ 7954 # **** basic search functions **** @history.search 7955 7956 function ble/highlight/layer:region/mark:search/get-face { face=region_match; } 7957 7958 ## @fn ble-edit/isearch/search/.match str rex 7959 ## @var[in] flag_icase 7960 ## @var[out] BASH_REMATCH 7961 function ble-edit/isearch/search/.match { 7962 if [[ $flag_icase ]]; then 7963 shopt -s nocasematch 7964 [[ $1 =~ $2 ]]; local ext=$? 7965 shopt -u nocasematch 7966 return "$ext" 7967 fi 7968 7969 [[ $1 =~ $2 ]] 7970 } 7971 7972 ## @fn ble-edit/isearch/search/.index str needle 7973 ## @var[in] flag_icase 7974 ## @var[out] beg end 7975 function ble-edit/isearch/search/.index { 7976 local target=${1:$3} needle=$2 7977 if [[ $flag_icase ]]; then 7978 local ret 7979 ble/string#tolower "$target"; target=$ret 7980 ble/string#tolower "$needle"; needle=$ret 7981 fi 7982 local suffix=${target#*"$needle"} 7983 [[ $target != "$suffix" ]] || return 1 7984 ((end=${#1}-${#suffix})) 7985 ((beg=end-${#needle})) 7986 return 0 7987 } 7988 7989 ## @fn ble-edit/isearch/search/.last-index str needle 7990 ## @var[in] flag_icase 7991 ## @var[out] beg end 7992 function ble-edit/isearch/search/.last-index { 7993 local target=$1 needle=$2 7994 if [[ $flag_icase ]]; then 7995 local ret 7996 ble/string#tolower "$target"; target=$ret 7997 ble/string#tolower "$needle"; needle=$ret 7998 fi 7999 local prefix=${target%"$needle"*} 8000 [[ $target != "$prefix" ]] || return 1 8001 beg=${#prefix} 8002 end=$((beg+${#needle})) 8003 return 0 8004 } 8005 8006 ## @fn ble-edit/isearch/search needle opts ; beg end 8007 ## @param[in] needle 8008 ## 8009 ## @param[in] opts 8010 ## コロン区切りのオプションです。 8011 ## 8012 ## + ... forward に検索します (既定) 8013 ## - ... backward に検索します。終端位置が現在位置以前にあるものに一致します。 8014 ## B ... backward に検索します。開始位置が現在位置より前のものに一致します。 8015 ## extend 8016 ## これが指定された時、現在位置における一致の伸長が試みられます。 8017 ## 指定されなかったとき、現在一致範囲と重複のない新しい一致が試みられます。 8018 ## regex 8019 ## 正規表現による一致を試みます。 8020 ## ignore-case 8021 ## 大文字・小文字を区別せずに検索します。 8022 ## allow_empty 8023 ## 空一致 (長さ0の一致) が現在位置で起こることを許容します。 8024 ## 既定では空一致の時には一つ次の位置から再検索を実行します。 8025 ## 8026 ## @var[out] beg end 8027 ## 検索対象が見つかった時に一致範囲の先頭と終端を返します。 8028 ## 8029 ## @exit 8030 ## 検索対象が見つかった時に 0 を返します。 8031 ## それ以外のときに 1 を返します。 8032 function ble-edit/isearch/search { 8033 local needle=$1 opts=$2 8034 beg= end= 8035 [[ :$opts: != *:regex:* ]]; local has_regex=$? 8036 [[ :$opts: != *:extend:* ]]; local has_extend=$? 8037 local flag_icase= 8038 [[ :$opts: == *:ignore-case:* ]] && flag_icase=1 8039 8040 local flag_empty_retry= 8041 if [[ :$opts: == *:-:* ]]; then 8042 local start=$((has_extend?_ble_edit_mark+1:_ble_edit_ind)) 8043 8044 if ((has_regex)); then 8045 ble-edit/isearch/.shift-backward-references 8046 local rex="^.*($needle)" padding=$((${#_ble_edit_str}-start)) 8047 ((padding)) && rex="$rex.{$padding}" 8048 if ble-edit/isearch/search/.match "$_ble_edit_str" "$rex"; then 8049 local rematch1=${BASH_REMATCH[1]} 8050 if [[ $rematch1 || $BASH_REMATCH == "$_ble_edit_str" || :$opts: == *:allow_empty:* ]]; then 8051 ((end=${#BASH_REMATCH}-padding, 8052 beg=end-${#rematch1})) 8053 return 0 8054 else 8055 flag_empty_retry=1 8056 fi 8057 fi 8058 else 8059 if [[ $needle ]]; then 8060 ble-edit/isearch/search/.last-index "${_ble_edit_str::start}" "$needle" && return 0 8061 else 8062 if [[ :$opts: == *:allow_empty:* ]] || ((--start>=0)); then 8063 ((beg=end=start)) 8064 return 0 8065 fi 8066 fi 8067 fi 8068 elif [[ :$opts: == *:B:* ]]; then 8069 local start=$((has_extend?_ble_edit_ind:_ble_edit_ind-1)) 8070 ((start<0)) && return 1 8071 8072 if ((has_regex)); then 8073 ble-edit/isearch/.shift-backward-references 8074 local rex="^.{0,$start}($needle)" 8075 ((start==0)) && rex="^($needle)" 8076 if ble-edit/isearch/search/.match "$_ble_edit_str" "$rex"; then 8077 local rematch1=${BASH_REMATCH[1]} 8078 if [[ $rematch1 || :$opts: == *:allow_empty:* ]]; then 8079 ((end=${#BASH_REMATCH}, 8080 beg=end-${#rematch1})) 8081 return 0 8082 else 8083 flag_empty_retry=1 8084 fi 8085 fi 8086 else 8087 if [[ $needle ]]; then 8088 ble-edit/isearch/search/.last-index "${_ble_edit_str::start+${#needle}}" "$needle" && return 0 8089 else 8090 if [[ :$opts: == *:allow_empty:* ]] && ((--start>=0)); then 8091 ((beg=end=start)) 8092 return 0 8093 fi 8094 fi 8095 fi 8096 else 8097 local start=$((has_extend?_ble_edit_mark:_ble_edit_ind)) 8098 if ((has_regex)); then 8099 ble-edit/isearch/.shift-backward-references 8100 local rex="($needle).*\$" 8101 ((start)) && rex=".{$start}$rex" 8102 if ble-edit/isearch/search/.match "$_ble_edit_str" "$rex"; then 8103 local rematch1=${BASH_REMATCH[1]} 8104 if [[ $rematch1 || :$opts: == *:allow_empty:* ]]; then 8105 ((beg=${#_ble_edit_str}-${#BASH_REMATCH}+start)) 8106 ((end=beg+${#rematch1})) 8107 return 0 8108 else 8109 flag_empty_retry=1 8110 fi 8111 fi 8112 else 8113 if [[ $needle ]]; then 8114 ble-edit/isearch/search/.index "$_ble_edit_str" "$needle" "$start" && return 0 8115 else 8116 if [[ :$opts: == *:allow_empty:* ]] || ((++start<=${#_ble_edit_str})); then 8117 ((beg=end=start)) 8118 return 0 8119 fi 8120 fi 8121 fi 8122 fi 8123 8124 # (正規表現一致の時) 現在地の空一致に対して再一致 8125 if [[ $flag_empty_retry ]]; then 8126 if [[ :$opts: == *:[-B]:* ]]; then 8127 if ((--start>=0)); then 8128 local mark=$_ble_edit_mark; ((mark&&mark--)) 8129 local ind=$_ble_edit_ind; ((ind&&ind--)) 8130 opts=$opts:allow_empty 8131 _ble_edit_mark=$mark _ble_edit_ind=$ind ble-edit/isearch/search "$needle" "$opts" 8132 return 0 8133 fi 8134 else 8135 if ((++start<=${#_ble_edit_str})); then 8136 local mark=$_ble_edit_mark; ((mark<${#_ble_edit_str}&&mark++)) 8137 local ind=$_ble_edit_ind; ((ind<${#_ble_edit_str}&&ind++)) 8138 opts=$opts:allow_empty 8139 _ble_edit_mark=$mark _ble_edit_ind=$ind ble-edit/isearch/search "$needle" "$opts" 8140 return 0 8141 fi 8142 fi 8143 fi 8144 return 1 8145 } 8146 ## @fn ble-edit/isearch/.shift-backward-references 8147 ## @var[in,out] needle 8148 ## 処理する正規表現を指定します。 8149 ## 後方参照をおきかえた正規表現を返します。 8150 function ble-edit/isearch/.shift-backward-references { 8151 # 後方参照 (backward references) の番号を 1 ずつ増やす。 8152 # bash 正規表現は 2 桁以上の後方参照に対応していないので、 8153 # \1 - \8 を \2-\9 にずらすだけにする (\9 が存在するときに問題になるが仕方がない)。 8154 local rex_cc='\[[@][^]@]+[@]\]' # [:space:] [=a=] [.a.] など。 8155 local rex_bracket_expr='\[\^?]?('${rex_cc//@/:}'|'${rex_cc//@/=}'|'${rex_cc//@/.}'|[^][]|\[[^]:=.])*\[?\]' 8156 local rex='^('$rex_bracket_expr'|\\[^1-8])*\\[1-8]' 8157 local buff= 8158 while [[ $needle =~ $rex ]]; do 8159 local mlen=${#BASH_REMATCH} 8160 buff=$buff${BASH_REMATCH::mlen-1}$((10#0${BASH_REMATCH:mlen-1}+1)) 8161 needle=${needle:mlen} 8162 done 8163 needle=$buff$needle 8164 } 8165 8166 # 8167 #------------------------------------------------------------------------------ 8168 # **** incremental search **** @history.isearch 8169 8170 ## @var _ble_edit_isearch_str 8171 ## 一致した文字列 8172 ## @var _ble_edit_isearch_dir 8173 ## 現在・直前の検索方法 8174 ## @arr _ble_edit_isearch_arr[] 8175 ## インクリメンタル検索の過程を記録する。 8176 ## 各要素は ind:dir:beg:end:needle の形式をしている。 8177 ## ind は履歴項目の番号を表す。dir は履歴検索の方向を表す。 8178 ## beg, end はそれぞれ一致開始位置と終了位置を表す。 8179 ## 丁度 _ble_edit_ind 及び _ble_edit_mark に対応する。 8180 ## needle は検索に使用した文字列を表す。 8181 ## @var _ble_edit_isearch_old 8182 ## 前回の検索に使用した文字列 8183 _ble_edit_isearch_opts= 8184 _ble_edit_isearch_str= 8185 _ble_edit_isearch_dir=- 8186 _ble_edit_isearch_arr=() 8187 _ble_edit_isearch_old= 8188 8189 ## @fn ble-edit/isearch/status/append-progress-bar pos count 8190 ## @var[in,out] text 8191 function ble-edit/isearch/status/append-progress-bar { 8192 ble/util/is-unicode-output || return 1 8193 local pos=$1 count=$2 dir=$3 8194 [[ :$dir: == *:-:* || :$dir: == *:backward:* ]] && ((pos=count-1-pos)) 8195 local ret; ble/string#create-unicode-progress-bar "$pos" "$count" 5 8196 text=$text$' \e[1;38;5;69;48;5;253m'$ret$'\e[m ' 8197 } 8198 8199 ## @fn ble-edit/isearch/.show-status-with-progress.fib [pos] 8200 ## @param[in,opt] pos 8201 ## 検索の途中の時に現在の検索位置を指定します。 8202 ## 検索の進行状況を表示します。 8203 ## 8204 ## @var[in] fib_ntask 8205 ## 現在の待ちスクの数を指定します。 8206 ## 8207 ## @var[in] _ble_edit_isearch_str 8208 ## @var[in] _ble_edit_isearch_dir 8209 ## @var[in] _ble_edit_isearch_arr 8210 ## 現在の検索状態を保持する変数です。 8211 ## 8212 function ble-edit/isearch/.show-status-with-progress.fib { 8213 # 出力 8214 local ll rr 8215 if [[ $_ble_edit_isearch_dir == - ]]; then 8216 # Emacs workaround: '<<' や "<<" と書けない。 8217 ll=\<\< rr=" " 8218 else 8219 ll=" " rr=">>" 8220 fi 8221 local index; ble/history/get-index 8222 local histIndex='!'$((index+1)) 8223 local text="(${#_ble_edit_isearch_arr[@]}: $ll $histIndex $rr) \`$_ble_edit_isearch_str'" 8224 8225 if [[ $1 ]]; then 8226 local pos=$1 8227 local count; ble/history/get-count 8228 text=$text' searching...' 8229 ble-edit/isearch/status/append-progress-bar "$pos" "$count" "$_ble_edit_isearch_dir" 8230 local percentage=$((count?pos*1000/count:1000)) 8231 text=$text" @$pos ($((percentage/10)).$((percentage%10))%)" 8232 fi 8233 ((fib_ntask)) && text="$text *$fib_ntask" 8234 8235 ble/edit/info/show ansi "$text" 8236 } 8237 8238 ## @fn ble-edit/isearch/.show-status.fib 8239 ## @var[in] fib_ntask 8240 function ble-edit/isearch/.show-status.fib { 8241 ble-edit/isearch/.show-status-with-progress.fib 8242 } 8243 function ble-edit/isearch/show-status { 8244 local fib_ntask=${#_ble_util_fiberchain[@]} 8245 ble-edit/isearch/.show-status.fib 8246 } 8247 function ble-edit/isearch/erase-status { 8248 ble/edit/info/default 8249 } 8250 function ble-edit/isearch/.set-region { 8251 local beg=$1 end=$2 8252 if ((beg<end)); then 8253 if [[ $_ble_edit_isearch_dir == - ]]; then 8254 _ble_edit_ind=$beg 8255 _ble_edit_mark=$end 8256 else 8257 _ble_edit_ind=$end 8258 _ble_edit_mark=$beg 8259 fi 8260 _ble_edit_mark_active=search 8261 elif ((beg==end)); then 8262 _ble_edit_ind=$beg 8263 _ble_edit_mark=$beg 8264 _ble_edit_mark_active= 8265 else 8266 _ble_edit_mark_active= 8267 fi 8268 } 8269 ## @fn ble-edit/isearch/.push-isearch-array 8270 ## 現在の isearch の情報を配列 _ble_edit_isearch_arr に待避する。 8271 ## 8272 ## これから登録しようとしている情報が現在のものと同じならば何もしない。 8273 ## これから登録しようとしている情報が配列の最上にある場合は、 8274 ## 検索の巻き戻しと解釈して配列の最上の要素を削除する。 8275 ## それ以外の場合は、現在の情報を配列に追加する。 8276 ## @var[in] ind beg end needle 8277 ## これから登録しようとしている isearch の情報。 8278 function ble-edit/isearch/.push-isearch-array { 8279 local hash=$beg:$end:$needle 8280 8281 # [... A | B] -> A と来た時 (A を _ble_edit_isearch_arr から削除) [... | A] になる。 8282 local ilast=$((${#_ble_edit_isearch_arr[@]}-1)) 8283 if ((ilast>=0)) && [[ ${_ble_edit_isearch_arr[ilast]} == "$ind:"[-+]":$hash" ]]; then 8284 builtin unset -v "_ble_edit_isearch_arr[$ilast]" 8285 return 0 8286 fi 8287 8288 local oind; ble/history/get-index -v oind 8289 local obeg=$_ble_edit_ind oend=$_ble_edit_mark 8290 [[ $_ble_edit_mark_active ]] || oend=$obeg 8291 ((obeg>oend)) && local obeg=$oend oend=$obeg 8292 local oneedle=$_ble_edit_isearch_str 8293 local ohash=$obeg:$oend:$oneedle 8294 8295 # [... A | B] -> B と来た時 (何もしない) [... A | B] になる。 8296 [[ $ind == "$oind" && $hash == "$ohash" ]] && return 0 8297 8298 # [... A | B] -> C と来た時 (B を _ble_edit_isearch_arr に移動) [... A B | C] になる。 8299 ble/array#push _ble_edit_isearch_arr "$oind:$_ble_edit_isearch_dir:$ohash" 8300 } 8301 ## @fn ble-edit/isearch/.goto-match.fib 8302 ## @var[in] fib_ntask 8303 function ble-edit/isearch/.goto-match.fib { 8304 local ind=$1 beg=$2 end=$3 needle=$4 8305 8306 # 検索履歴に待避 (変数 ind beg end needle 使用) 8307 ble-edit/isearch/.push-isearch-array 8308 8309 # 状態を更新 8310 _ble_edit_isearch_str=$needle 8311 [[ $needle ]] && _ble_edit_isearch_old=$needle 8312 local oind; ble/history/get-index -v oind 8313 ((oind!=ind)) && ble-edit/history/goto "$ind" 8314 ble-edit/isearch/.set-region "$beg" "$end" 8315 8316 # isearch 表示 8317 ble-edit/isearch/.show-status.fib 8318 ble/textarea#redraw 8319 } 8320 8321 # ---- isearch fibers --------------------------------------------------------- 8322 8323 ## @fn ble-edit/isearch/.next.fib opts [needle] 8324 ## @param[in] opts 8325 ## コロン区切りのリストです。 8326 ## append 8327 ## 前回の検索の続きを新しい needle で実行します。 8328 ## forward 8329 ## 検索方向を前方に変更します。 8330 ## backward 8331 ## 検索方向を後方に変更します。 8332 ## ignore-case 8333 ## 大文字小文字の区別をしません。 8334 function ble-edit/isearch/.next.fib { 8335 local opts=$1 8336 if [[ ! $fib_suspend ]]; then 8337 if [[ :$opts: == *:forward:* || :$opts: == *:backward:* ]]; then 8338 if [[ :$opts: == *:forward:* ]]; then 8339 _ble_edit_isearch_dir=+ 8340 else 8341 _ble_edit_isearch_dir=- 8342 fi 8343 fi 8344 8345 # 現在行の別の位置での一致 8346 local needle=${2-$_ble_edit_isearch_str} 8347 local beg= end= search_opts=$_ble_edit_isearch_dir 8348 if [[ :$opts: == *:append:* ]]; then 8349 search_opts=$search_opts:extend 8350 # Note: 現在の項目はここで処理するので 8351 # .next-history.fib には append は指定しない #D1025 8352 ble/path#remove opts append 8353 fi 8354 [[ :$opts: == *:ignore-case:* ]] && 8355 search_opts=$search_opts:ignore-case 8356 if [[ $needle ]] && ble-edit/isearch/search "$needle" "$search_opts"; then 8357 local ind; ble/history/get-index -v ind 8358 ble-edit/isearch/.goto-match.fib "$ind" "$beg" "$end" "$needle" 8359 return 0 8360 fi 8361 fi 8362 ble-edit/isearch/.next-history.fib "$opts" "$needle" 8363 } 8364 8365 ## @fn ble-edit/isearch/.next-history.fib [opts [needle]] 8366 ## 8367 ## @param[in,opt] opts 8368 ## コロン区切りのリストです。 8369 ## append 8370 ## 現在の履歴項目を検索対象とします。 8371 ## ignore-case 8372 ## 大文字・小文字の区別をしません。 8373 ## 8374 ## @param[in,opt] needle 8375 ## 新しい検索を開始する場合に、検索対象を明示的に指定します。 8376 ## needle に検索対象の文字列を指定します。 8377 ## 8378 ## @var[in,out] fib_suspend 8379 ## 中断した時にこの変数に再開用のデータを格納します。 8380 ## 再開する時はこの変数の中断時の内容を復元してこの関数を呼び出します。 8381 ## この変数が空の場合は新しい検索を開始します。 8382 ## @var[in] _ble_edit_isearch_str 8383 ## 最後に一致した検索文字列を指定します。 8384 ## 検索対象を明示的に指定しなかった場合に使う検索対象です。 8385 ## 8386 ## @var[in] _ble_edit_isearch_dir 8387 ## 現在の検索方向を指定します。 8388 ## @var[in] PREFIX_history_edit[] 8389 ## @var[in,out] isearch_time 8390 ## 8391 function ble-edit/isearch/.next-history.fib { 8392 local opts=$1 8393 if [[ $fib_suspend ]]; then 8394 # resume the previous search 8395 local needle=${fib_suspend#*:} isAdd= 8396 local index start; builtin eval -- "${fib_suspend%%:*}" 8397 fib_suspend= 8398 else 8399 # initialize new search 8400 local needle=${2-$_ble_edit_isearch_str} isAdd= 8401 [[ :$opts: == *:append:* ]] && isAdd=1 8402 ble/history/initialize 8403 local start=$_ble_history_INDEX 8404 local index=$start 8405 fi 8406 8407 if ((!isAdd)); then 8408 if [[ $_ble_edit_isearch_dir == - ]]; then 8409 ((index--)) 8410 else 8411 ((index++)) 8412 fi 8413 fi 8414 8415 # 検索 8416 local isearch_progress_callback=ble-edit/isearch/.show-status-with-progress.fib 8417 local isearch_opts=stop_check:progress 8418 [[ :$opts: == *:ignore-case:* ]] && isearch_opts=$isearch_opts:ignore-case 8419 if [[ $_ble_edit_isearch_dir == - ]]; then 8420 ble/history/isearch-backward-blockwise "$isearch_opts" 8421 else 8422 ble/history/isearch-forward "$isearch_opts" 8423 fi 8424 local ext=$? 8425 8426 if ((ext==0)); then 8427 # 見付かった場合 8428 8429 # 一致範囲 beg-end を取得 8430 local str; ble/history/get-edited-entry -v str "$index" 8431 if [[ $needle ]]; then 8432 local ndl=$needle 8433 if [[ :$opts: == *:ignore-case:* ]]; then 8434 local ret 8435 ble/string#tolower "$str"; str=$ret 8436 ble/string#tolower "$ndl"; ndl=$ret 8437 fi 8438 8439 if [[ $_ble_edit_isearch_dir == - ]]; then 8440 local prefix=${str%"$ndl"*} 8441 else 8442 local prefix=${str%%"$ndl"*} 8443 fi 8444 local beg=${#prefix} end=$((${#prefix}+${#ndl})) 8445 else 8446 local beg=${#str} end=${#str} 8447 fi 8448 8449 ble-edit/isearch/.goto-match.fib "$index" "$beg" "$end" "$needle" 8450 elif ((ext==148)); then 8451 # 中断した場合 8452 fib_suspend="index=$index start=$start:$needle" 8453 return 0 8454 else 8455 # 見つからなかった場合 8456 ble/widget/.bell "isearch: \`$needle' not found" 8457 return 0 8458 fi 8459 } 8460 8461 function ble-edit/isearch/forward.fib { 8462 if [[ ! $_ble_edit_isearch_str ]]; then 8463 ble-edit/isearch/.next.fib "$_ble_edit_isearch_opts:forward" "$_ble_edit_isearch_old" 8464 else 8465 ble-edit/isearch/.next.fib "$_ble_edit_isearch_opts:forward" 8466 fi 8467 } 8468 function ble-edit/isearch/backward.fib { 8469 if [[ ! $_ble_edit_isearch_str ]]; then 8470 ble-edit/isearch/.next.fib "$_ble_edit_isearch_opts:backward" "$_ble_edit_isearch_old" 8471 else 8472 ble-edit/isearch/.next.fib "$_ble_edit_isearch_opts:backward" 8473 fi 8474 } 8475 function ble-edit/isearch/self-insert.fib { 8476 local needle= 8477 if [[ ! $fib_suspend ]]; then 8478 local code=$1 8479 ((code==0)) && return 0 8480 local ret; ble/util/c2s "$code" 8481 needle=$_ble_edit_isearch_str$ret 8482 fi 8483 ble-edit/isearch/.next.fib "$_ble_edit_isearch_opts:append" "$needle" 8484 } 8485 function ble-edit/isearch/insert-string.fib { 8486 local needle= 8487 [[ ! $fib_suspend ]] && 8488 needle=$_ble_edit_isearch_str$1 8489 ble-edit/isearch/.next.fib "$_ble_edit_isearch_opts:append" "$needle" 8490 } 8491 function ble-edit/isearch/history-forward.fib { 8492 _ble_edit_isearch_dir=+ 8493 ble-edit/isearch/.next-history.fib "$_ble_edit_isearch_opts" 8494 } 8495 function ble-edit/isearch/history-backward.fib { 8496 _ble_edit_isearch_dir=- 8497 ble-edit/isearch/.next-history.fib "$_ble_edit_isearch_opts" 8498 } 8499 function ble-edit/isearch/history-self-insert.fib { 8500 local needle= 8501 if [[ ! $fib_suspend ]]; then 8502 local code=$1 8503 ((code==0)) && return 0 8504 local ret; ble/util/c2s "$code" 8505 needle=$_ble_edit_isearch_str$ret 8506 fi 8507 ble-edit/isearch/.next-history.fib "$_ble_edit_isearch_opts:append" "$needle" 8508 } 8509 8510 function ble-edit/isearch/prev { 8511 local sz=${#_ble_edit_isearch_arr[@]} 8512 ((sz==0)) && return 0 8513 8514 local ilast=$((sz-1)) 8515 local top=${_ble_edit_isearch_arr[ilast]} 8516 builtin unset -v '_ble_edit_isearch_arr[ilast]' 8517 8518 local ind dir beg end 8519 ind=${top%%:*}; top=${top#*:} 8520 dir=${top%%:*}; top=${top#*:} 8521 beg=${top%%:*}; top=${top#*:} 8522 end=${top%%:*}; top=${top#*:} 8523 8524 _ble_edit_isearch_dir=$dir 8525 ble-edit/history/goto "$ind" 8526 ble-edit/isearch/.set-region "$beg" "$end" 8527 _ble_edit_isearch_str=$top 8528 [[ $top ]] && _ble_edit_isearch_old=$top 8529 8530 # isearch 表示 8531 ble-edit/isearch/show-status 8532 } 8533 8534 function ble-edit/isearch/process { 8535 local isearch_time=0 8536 ble/util/fiberchain#resume 8537 ble-edit/isearch/show-status 8538 } 8539 function ble/widget/isearch/forward { 8540 ble/util/fiberchain#push forward 8541 ble-edit/isearch/process 8542 } 8543 function ble/widget/isearch/backward { 8544 ble/util/fiberchain#push backward 8545 ble-edit/isearch/process 8546 } 8547 function ble/widget/isearch/self-insert { 8548 local code; ble/widget/self-insert/.get-code 8549 ((code==0)) && return 0 8550 ble/util/fiberchain#push "self-insert $code" 8551 ble-edit/isearch/process 8552 } 8553 function ble/widget/isearch/history-forward { 8554 ble/util/fiberchain#push history-forward 8555 ble-edit/isearch/process 8556 } 8557 function ble/widget/isearch/history-backward { 8558 ble/util/fiberchain#push history-backward 8559 ble-edit/isearch/process 8560 } 8561 function ble/widget/isearch/history-self-insert { 8562 local code; ble/widget/self-insert/.get-code 8563 ((code==0)) && return 0 8564 ble/util/fiberchain#push "history-self-insert $code" 8565 ble-edit/isearch/process 8566 } 8567 function ble/widget/isearch/prev { 8568 local nque 8569 if ((nque=${#_ble_util_fiberchain[@]})); then 8570 local ret; ble/array#pop _ble_util_fiberchain 8571 ble-edit/isearch/process 8572 else 8573 ble-edit/isearch/prev 8574 fi 8575 } 8576 8577 function ble/widget/isearch/.restore-mark-state { 8578 local old_mark_active=${_ble_edit_isearch_save[3]} 8579 if [[ $old_mark_active ]]; then 8580 local index; ble/history/get-index 8581 if ((index==_ble_edit_isearch_save[0])); then 8582 _ble_edit_mark=${_ble_edit_isearch_save[2]} 8583 if [[ $old_mark_active != S ]] || ((_ble_edit_ind==_ble_edit_isearch_save[1])); then 8584 _ble_edit_mark_active=$old_mark_active 8585 fi 8586 fi 8587 fi 8588 } 8589 function ble/widget/isearch/exit.impl { 8590 ble/decode/keymap/pop 8591 _ble_edit_isearch_arr=() 8592 _ble_edit_isearch_dir= 8593 _ble_edit_isearch_str= 8594 ble-edit/isearch/erase-status 8595 } 8596 function ble/widget/isearch/exit-with-region { 8597 ble/widget/isearch/exit.impl 8598 [[ $_ble_edit_mark_active ]] && 8599 _ble_edit_mark_active=S 8600 } 8601 function ble/widget/isearch/exit { 8602 ble/widget/isearch/exit.impl 8603 8604 _ble_edit_mark_active= 8605 ble/widget/isearch/.restore-mark-state 8606 } 8607 function ble/widget/isearch/cancel { 8608 if ((${#_ble_util_fiberchain[@]})); then 8609 ble/util/fiberchain#clear 8610 ble-edit/isearch/show-status # 進捗状況だけ消去 8611 else 8612 if ((${#_ble_edit_isearch_arr[@]})); then 8613 local step 8614 ble/string#split step : "${_ble_edit_isearch_arr[0]}" 8615 ble-edit/history/goto "${step[0]}" 8616 fi 8617 8618 ble/widget/isearch/exit.impl 8619 _ble_edit_ind=${_ble_edit_isearch_save[1]} 8620 _ble_edit_mark=${_ble_edit_isearch_save[2]} 8621 _ble_edit_mark_active=${_ble_edit_isearch_save[3]} 8622 fi 8623 } 8624 function ble/widget/isearch/exit-default { 8625 ble/widget/isearch/exit-with-region 8626 ble/decode/widget/skip-lastwidget 8627 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 8628 } 8629 function ble/widget/isearch/accept-line { 8630 if ((${#_ble_util_fiberchain[@]})); then 8631 ble/widget/.bell "isearch: now searching..." 8632 else 8633 ble/widget/isearch/exit 8634 ble-decode-key 13 # RET 8635 fi 8636 } 8637 function ble/widget/isearch/exit-delete-forward-char { 8638 ble/widget/isearch/exit 8639 ble/widget/delete-forward-char 8640 } 8641 8642 ## @fn ble/widget/history-isearch.impl opts 8643 function ble/widget/history-isearch.impl { 8644 local opts=$1 8645 ble/keymap:generic/clear-arg 8646 ble/decode/keymap/push isearch 8647 ble/util/fiberchain#initialize ble-edit/isearch 8648 8649 local index; ble/history/get-index 8650 _ble_edit_isearch_save=("$index" "$_ble_edit_ind" "$_ble_edit_mark" "$_ble_edit_mark_active") 8651 8652 _ble_edit_isearch_opts= 8653 ble/util/rlvar#test search-ignore-case 0 && 8654 _ble_edit_isearch_opts=ignore-case 8655 8656 if [[ :$opts: == *:forward:* ]]; then 8657 _ble_edit_isearch_dir=+ 8658 else 8659 _ble_edit_isearch_dir=- 8660 fi 8661 _ble_edit_isearch_arr=() 8662 _ble_edit_mark=$_ble_edit_ind 8663 ble-edit/isearch/show-status 8664 } 8665 function ble/widget/history-isearch-backward { 8666 ble/widget/history-isearch.impl backward 8667 } 8668 function ble/widget/history-isearch-forward { 8669 ble/widget/history-isearch.impl forward 8670 } 8671 8672 function ble-decode/keymap:isearch/define { 8673 ble-bind -f __defchar__ isearch/self-insert 8674 ble-bind -f __line_limit__ nop 8675 8676 ble-bind -f C-r isearch/backward 8677 ble-bind -f C-s isearch/forward 8678 ble-bind -f 'C-?' isearch/prev 8679 ble-bind -f 'DEL' isearch/prev 8680 ble-bind -f 'C-h' isearch/prev 8681 ble-bind -f 'BS' isearch/prev 8682 8683 ble-bind -f __default__ isearch/exit-default 8684 ble-bind -f 'C-g' isearch/cancel 8685 ble-bind -f 'C-x C-g' isearch/cancel 8686 ble-bind -f 'C-M-g' isearch/cancel 8687 ble-bind -f C-m isearch/exit 8688 ble-bind -f RET isearch/exit 8689 ble-bind -f C-j isearch/accept-line 8690 ble-bind -f C-RET isearch/accept-line 8691 } 8692 8693 # 8694 #------------------------------------------------------------------------------ 8695 # **** non-incremental-search **** @history.nsearch 8696 8697 ## @var _ble_edit_nsearch_needle 8698 ## 検索対象の文字列を保持します。 8699 ## @var _ble_edit_nsearch_input 8700 ## 最後にユーザ入力された検索対象を保持します。 8701 ## @var _ble_edit_nsearch_opts 8702 ## 検索の振る舞いを制御するオプションを保持します。 8703 ## @arr _ble_edit_nsearch_stack[] 8704 ## 検索が一致する度に記録される。 8705 ## 各要素は "direction,index,ind,mark:line" の形式をしている。 8706 ## 前回の検索の方向 (direction) と、検索前の状態を記録する。 8707 ## index は検索の履歴位置で ind と mark はカーソル位置とマークの位置。 8708 ## line は編集文字列である。 8709 ## @var _ble_edit_nsearch_match 8710 ## 現在表示している行内容がどの履歴番号に対応するかを保持します。 8711 ## nsearch 開始位置もしくは最後に一致した位置に対応します。 8712 ## @var _ble_edit_nsearch_index 8713 ## 最後に検索した位置を表します。 8714 ## 検索が一致した場合は _ble_edit_nsearch_match と同じになります。 8715 ## @var _ble_edit_nsearch_prev 8716 ## 前回の検索文字列 8717 _ble_edit_nsearch_input= 8718 _ble_edit_nsearch_needle= 8719 _ble_edit_nsearch_index0= 8720 _ble_edit_nsearch_opts= 8721 _ble_edit_nsearch_stack=() 8722 _ble_edit_nsearch_match= 8723 _ble_edit_nsearch_index= 8724 _ble_edit_nsearch_prev= 8725 8726 function ble/highlight/layer:region/mark:nsearch/get-face { 8727 face=region_match 8728 } 8729 function ble/highlight/layer:region/mark:nsearch/get-selection { 8730 local beg=$_ble_edit_mark 8731 local end=$((_ble_edit_mark+${#_ble_edit_nsearch_needle})) 8732 selection=("$beg" "$end") 8733 } 8734 8735 ## @fn ble-edit/nsearch/.show-status.fib [pos_progress] 8736 ## @var[in] fib_ntask 8737 function ble-edit/nsearch/.show-status.fib { 8738 [[ :$_ble_edit_nsearch_opts: == *:hide-status:* ]] && return 0 8739 8740 local ll=\<\< rr=">>" # Note: Emacs workaround: '<<' や "<<" と書けない。 8741 local match=$_ble_edit_nsearch_match index0=$_ble_edit_nsearch_index0 8742 if ((match>index0)); then 8743 ll=" " 8744 elif ((match<index0)); then 8745 rr=" " 8746 fi 8747 8748 local sindex='!'$((_ble_edit_nsearch_match+1)) 8749 local nmatch=${#_ble_edit_nsearch_stack[@]} 8750 local needle=$_ble_edit_nsearch_needle 8751 local text="(nsearch#$nmatch: $ll $sindex $rr) \`$needle'" 8752 8753 if [[ $1 ]]; then 8754 local pos=$1 8755 local count; ble/history/get-count 8756 text=$text' searching...' 8757 ble-edit/isearch/status/append-progress-bar "$pos" "$count" "$_ble_edit_nsearch_opts" 8758 local percentage=$((count?pos*1000/count:1000)) 8759 text=$text" @$pos ($((percentage/10)).$((percentage%10))%)" 8760 fi 8761 8762 local ntask=$fib_ntask 8763 ((ntask)) && text="$text *$ntask" 8764 8765 ble/edit/info/show ansi "$text" 8766 } 8767 function ble-edit/nsearch/show-status { 8768 local fib_ntask=${#_ble_util_fiberchain[@]} 8769 ble-edit/nsearch/.show-status.fib 8770 } 8771 function ble-edit/nsearch/erase-status { 8772 ble/edit/info/default 8773 } 8774 8775 #@ToDo backward/forward backward 固定になっているがそれで良いのか? 8776 function ble-edit/nsearch/.goto-match { 8777 local index=$1 opts=$2 8778 local direction=backward 8779 [[ :$opts: == *:forward:* ]] && direction=forward 8780 local needle=$_ble_edit_nsearch_needle 8781 local old_match=$_ble_edit_nsearch_match 8782 ble/array#push _ble_edit_nsearch_stack "$direction,$old_match,$_ble_edit_ind,$_ble_edit_mark:$_ble_edit_str" 8783 8784 if [[ ! $index ]]; then 8785 ble/history/get-index 8786 elif [[ :$opts: == *:action=load:* ]]; then 8787 local old_index; ble/history/get-index -v old_index 8788 if ((index!=old_index)); then 8789 local line; ble/history/get-edited-entry -v line "$index" 8790 ble-edit/content/reset-and-check-dirty "$line" 8791 fi 8792 else 8793 ble-edit/history/goto "$index" 8794 fi 8795 8796 # 一致範囲の決定 8797 local s=$_ble_edit_str n=$needle 8798 if [[ :$opts: == *:ignore-case:* ]]; then 8799 local ret 8800 ble/string#tolower "$s"; s=$ret 8801 ble/string#tolower "$n"; n=$ret 8802 fi 8803 local prefix=${s%%"$n"*} 8804 local beg=${#prefix} 8805 local end=$((beg+${#needle})) 8806 8807 _ble_edit_nsearch_match=$index 8808 _ble_edit_nsearch_index=$index 8809 _ble_edit_mark=$beg 8810 local is_end_marker= 8811 local rex=':point=([^:]*):' 8812 [[ :$opts: =~ $rex ]] 8813 case ${BASH_REMATCH[1]} in 8814 (begin) _ble_edit_ind=0 ;; 8815 (end) _ble_edit_ind=${#_ble_edit_str} is_end_marker=1 ;; 8816 (match-begin) _ble_edit_ind=$beg ;; 8817 (match-end|*) _ble_edit_ind=$end is_end_marker=1 ;; 8818 esac 8819 8820 # vi_nmap の中にいる時は一致範囲の最後の文字にカーソルを置く 8821 if [[ $is_end_marker ]] && ((_ble_edit_ind)); then 8822 if local ret; ble/decode/keymap/get-parent; [[ $ret == vi_[noxs]map ]]; then 8823 ble-edit/content/bolp || ((_ble_edit_ind--)) 8824 fi 8825 fi 8826 8827 if ((beg!=end)); then 8828 _ble_edit_mark_active=nsearch 8829 else 8830 _ble_edit_mark_active= 8831 fi 8832 } 8833 8834 function ble-edit/nsearch/.search.fib { 8835 local opts=$1 8836 local opt_forward= 8837 [[ :$opts: == *:forward:* ]] && opt_forward=1 8838 8839 # 前回の一致と逆方向の時は前回の一致前の状態に戻す 8840 # Note: stack[0] は一致結果ではなくて現在行の記録に使われているので 8841 # nstack >= 2 の時にのみ状態を戻すことにする。 8842 local nstack=${#_ble_edit_nsearch_stack[@]} 8843 if ((nstack>=2)); then 8844 local record_type=${_ble_edit_nsearch_stack[nstack-1]%%,*} 8845 if 8846 if [[ $opt_forward ]]; then 8847 [[ $record_type == backward ]] 8848 else 8849 [[ $record_type == forward ]] 8850 fi 8851 then 8852 local ret; ble/array#pop _ble_edit_nsearch_stack 8853 local record line=${ret#*:} 8854 ble/string#split record , "${ret%%:*}" 8855 8856 if [[ :$opts: == *:action=load:* ]]; then 8857 ble-edit/content/reset-and-check-dirty "$line" 8858 else 8859 ble-edit/history/goto "${record[1]}" 8860 fi 8861 _ble_edit_nsearch_match=${record[1]} 8862 _ble_edit_nsearch_index=${record[1]} 8863 _ble_edit_ind=${record[2]} 8864 _ble_edit_mark=${record[3]} 8865 if ((_ble_edit_mark!=_ble_edit_ind)); then 8866 _ble_edit_mark_active=nsearch 8867 else 8868 _ble_edit_mark_active= 8869 fi 8870 ble-edit/nsearch/.show-status.fib 8871 ble/textarea#redraw 8872 fib_suspend= 8873 return 0 8874 fi 8875 fi 8876 8877 # 検索の実行 8878 local index start opt_resume= 8879 if [[ $fib_suspend ]]; then 8880 opt_resume=1 8881 builtin eval -- "$fib_suspend" 8882 fib_suspend= 8883 else 8884 local index=$_ble_edit_nsearch_index 8885 if ((nstack==1)); then 8886 # 検索方向反転があった時は検索開始位置を初期化 8887 local index0=$_ble_edit_nsearch_index0 8888 ((opt_forward?index<index0:index>index0)) && 8889 index=$index0 8890 fi 8891 local start=$index 8892 fi 8893 local needle=$_ble_edit_nsearch_needle 8894 if 8895 if [[ $opt_forward ]]; then 8896 local count; ble/history/get-count 8897 [[ $opt_resume ]] || ((++index)) 8898 ((index<=count)) 8899 else 8900 [[ $opt_resume ]] || ((--index)) 8901 ((index>=0)) 8902 fi 8903 then 8904 local isearch_time=$fib_clock 8905 local isearch_progress_callback=ble-edit/nsearch/.show-status.fib 8906 local isearch_opts=stop_check:progress 8907 [[ :$opts: != *:substr:* ]] && isearch_opts=$isearch_opts:head 8908 [[ :$opts: == *:ignore-case:* ]] && isearch_opts=$isearch_opts:ignore-case 8909 if [[ $opt_forward ]]; then 8910 ble/history/isearch-forward "$isearch_opts"; local ext=$? 8911 else 8912 ble/history/isearch-backward-blockwise "$isearch_opts"; local ext=$? 8913 fi 8914 fib_clock=$isearch_time 8915 else 8916 local ext=1 8917 fi 8918 8919 # 書き換え 8920 if ((ext==0)); then 8921 ble-edit/nsearch/.goto-match "$index" "$opts" 8922 ble-edit/nsearch/.show-status.fib 8923 ble/textarea#redraw 8924 elif ((ext==148)); then 8925 fib_suspend="index=$index start=$start" 8926 return 148 8927 else 8928 ble/widget/.bell "ble.sh: nsearch: '$needle' not found" 8929 ble-edit/nsearch/.show-status.fib 8930 if [[ $opt_forward ]]; then 8931 local count; ble/history/get-count 8932 ((_ble_edit_nsearch_index=count-1)) 8933 else 8934 ((_ble_edit_nsearch_index=0)) 8935 fi 8936 return "$ext" 8937 fi 8938 } 8939 function ble-edit/nsearch/forward.fib { 8940 ble-edit/nsearch/.search.fib "$_ble_edit_nsearch_opts:forward" 8941 } 8942 function ble-edit/nsearch/backward.fib { 8943 ble-edit/nsearch/.search.fib "$_ble_edit_nsearch_opts:backward" 8944 } 8945 8946 ## @fn ble-edit/nsearch/.test str ndl opts 8947 ## 指定した文字列が一致するかどうかを判定します。 8948 function ble-edit/nsearch/.test { 8949 local str=$1 ndl=$2 opts=$3 8950 [[ :$opts: == *:ignore-case:* ]] && 8951 shopt -s nocasematch 8952 if [[ :$opts: == *:substr:* ]]; then 8953 [[ $str == *"$ndl"* ]] 8954 else 8955 [[ $str == "$ndl"* ]] 8956 fi; local ext=$? 8957 shopt -u nocasematch 8958 return "$ext" 8959 } 8960 8961 ## @widget history-search opts 8962 ## @param[in] opts 8963 ## 8964 ## forward 前方に検索します 8965 ## backward 後方に検索します 8966 ## substr 部分一致を行います 8967 ## input 検索文字列をユーザー入力します 8968 ## again 前回ユーザー入力した検索文字列を使います 8969 ## 8970 ## empty=EMPTY 8971 ## 空文字列で検索を開始した時の動作を指定します。 8972 ## previous-search 前回の検索文字列を使用して検索します [既定] 8973 ## empty-search 空文字列で検索します。 8974 ## hide-status 空文字列検索。nsearch 状態は隠します。 8975 ## history-move 履歴項目移動。コマンドライン先頭に移動します。 8976 ## emulate-readline Readline の動作を模倣します。hide-status 及び point=end を設定します。 8977 ## 8978 ## action=ACTION 8979 ## 文字列が見つかった時の動作を指定します。 8980 ## goto 見つかった履歴項目に移動します [既定] 8981 ## load 現在の履歴項目の位置で見つかったコマンド文字列をロードします 8982 ## 8983 ## point=POINT 8984 ## 文字列が見つかった時のカーソル位置を指定します。 8985 ## begin コマンドラインの先頭に移動します。 8986 ## end コマンドラインの末尾に移動します。 8987 ## match-begin 一致範囲の先頭に移動します。 8988 ## match-end 一致範囲の末尾に移動します。 8989 ## 8990 ## hide-status 8991 ## 現在の検索状態を表示しません。 8992 ## 8993 ## immediate-accept 8994 ## nsearch を正常終了する時にコマンドを即座に実行します。 8995 ## 8996 function ble/widget/history-search { 8997 local opts=$1 8998 8999 # initialize variables 9000 if [[ :$opts: == *:input:* || :$opts: == *:again:* && ! $_ble_edit_nsearch_input ]]; then 9001 ble/builtin/read -ep "nsearch> " _ble_edit_nsearch_needle || return 1 9002 _ble_edit_nsearch_input=$_ble_edit_nsearch_needle 9003 elif [[ :$opts: == *:again:* ]]; then 9004 _ble_edit_nsearch_needle=$_ble_edit_nsearch_input 9005 else 9006 local len=$_ble_edit_ind 9007 if [[ $_ble_decode_keymap == vi_[noxs]map ]]; then 9008 # vi_nmap の中にいる時は現在カーソルがある文字も検索文字列に含める 9009 ble-edit/content/eolp || ((len++)) 9010 fi 9011 _ble_edit_nsearch_needle=${_ble_edit_str::len} 9012 fi 9013 9014 # 検索文字列が空の時は別の動作を行う 9015 if [[ ! $_ble_edit_nsearch_needle ]]; then 9016 local empty=empty-search 9017 local rex='.*:empty=([^:]*):' 9018 [[ :$opts: =~ $rex ]] && empty=${BASH_REMATCH[1]} 9019 case $empty in 9020 (history-move) 9021 if [[ :$opts: == *:forward:* ]]; then 9022 ble/widget/history-next 9023 else 9024 ble/widget/history-prev 9025 fi && _ble_edit_ind=0 9026 return "$?" ;; 9027 (hide-status) 9028 opts=$opts:hide-status ;; 9029 (emulate-readline) 9030 opts=hide-status:point=end:$opts ;; 9031 (previous-search) 9032 _ble_edit_nsearch_needle=$_ble_edit_nsearch_prev ;; 9033 esac 9034 fi 9035 _ble_edit_nsearch_prev=$_ble_edit_nsearch_needle 9036 9037 ble/keymap:generic/clear-arg 9038 9039 # ignore-case も match-case も指定されていない時は readline の 9040 # search-ignore-case を参照する。 9041 [[ :$opts: != *:ignore-case:* && :$opts: != *:match-case:* ]] && 9042 ble/util/rlvar#test search-ignore-case 0 && 9043 opts=$opts:ignore-case 9044 9045 _ble_edit_nsearch_stack=() 9046 local index; ble/history/get-index 9047 _ble_edit_nsearch_index0=$index 9048 _ble_edit_nsearch_opts=$opts 9049 ble/path#remove _ble_edit_nsearch_opts forward 9050 ble/path#remove _ble_edit_nsearch_opts backward 9051 _ble_edit_nsearch_match=$index 9052 _ble_edit_nsearch_index=$index 9053 _ble_edit_mark_active= 9054 ble/decode/keymap/push nsearch 9055 9056 # 現在履歴位置が一致する場合は戻って来れる様に記録する。 9057 if ble-edit/nsearch/.test "$_ble_edit_str" "$_ble_edit_nsearch_needle" "$opts"; then 9058 ble-edit/nsearch/.goto-match '' "$opts" 9059 fi 9060 9061 # start search 9062 ble/util/fiberchain#initialize ble-edit/nsearch 9063 if [[ :$opts: == *:forward:* ]]; then 9064 ble/util/fiberchain#push forward 9065 else 9066 ble/util/fiberchain#push backward 9067 fi 9068 ble/util/fiberchain#resume 9069 } 9070 function ble/widget/history-nsearch-backward { 9071 ble/widget/history-search "input:substr:backward:$1" 9072 } 9073 function ble/widget/history-nsearch-forward { 9074 ble/widget/history-search "input:substr:forward:$1" 9075 } 9076 function ble/widget/history-nsearch-backward-again { 9077 ble/widget/history-search "again:substr:backward:$1" 9078 } 9079 function ble/widget/history-nsearch-forward-again { 9080 ble/widget/history-search "again:substr:forward:$1" 9081 } 9082 function ble/widget/history-search-backward { 9083 ble/widget/history-search "backward:$1" 9084 } 9085 function ble/widget/history-search-forward { 9086 ble/widget/history-search "forward:$1" 9087 } 9088 function ble/widget/history-substring-search-backward { 9089 ble/widget/history-search "substr:backward:$1" 9090 } 9091 function ble/widget/history-substring-search-forward { 9092 ble/widget/history-search "substr:forward:$1" 9093 } 9094 9095 function ble/widget/nsearch/forward { 9096 local ntask=${#_ble_util_fiberchain[@]} 9097 if ((ntask>=1)) && [[ ${_ble_util_fiberchain[ntask-1]%%:*} == backward ]]; then 9098 # 最後の逆方向の検索をキャンセル 9099 local ret; ble/array#pop _ble_util_fiberchain 9100 else 9101 ble/util/fiberchain#push forward 9102 fi 9103 ble/util/fiberchain#resume 9104 } 9105 function ble/widget/nsearch/backward { 9106 local ntask=${#_ble_util_fiberchain[@]} 9107 if ((ntask>=1)) && [[ ${_ble_util_fiberchain[ntask-1]%%:*} == forward ]]; then 9108 # 最後の逆方向の検索をキャンセル 9109 local ret; ble/array#pop _ble_util_fiberchain 9110 else 9111 ble/util/fiberchain#push backward 9112 fi 9113 ble/util/fiberchain#resume 9114 } 9115 function ble/widget/nsearch/.exit { 9116 ble/decode/keymap/pop 9117 _ble_edit_mark_active= 9118 ble-edit/nsearch/erase-status 9119 } 9120 function ble/widget/nsearch/exit { 9121 if [[ :$_ble_edit_nsearch_opts: == *:immediate-accept:* ]]; then 9122 ble/widget/nsearch/accept-line 9123 else 9124 ble/widget/nsearch/.exit 9125 fi 9126 } 9127 function ble/widget/nsearch/exit-default { 9128 ble/widget/nsearch/.exit 9129 ble/decode/widget/skip-lastwidget 9130 ble/decode/widget/redispatch-by-keys "${KEYS[@]}" 9131 } 9132 function ble/widget/nsearch/cancel { 9133 if ((${#_ble_util_fiberchain[@]})); then 9134 ble/util/fiberchain#clear 9135 ble-edit/nsearch/show-status 9136 else 9137 ble/widget/nsearch/.exit 9138 local record=${_ble_edit_nsearch_stack[0]} 9139 if [[ $record ]]; then 9140 local line=${record#*:} 9141 ble/string#split record , "${record%%:*}" 9142 if [[ :$_ble_edit_nsearch_opts: == *:action=load:* ]]; then 9143 ble-edit/content/reset-and-check-dirty "$line" 9144 else 9145 ble-edit/history/goto "$_ble_edit_nsearch_index0" 9146 fi 9147 _ble_edit_ind=${record[2]} 9148 _ble_edit_mark=${record[3]} 9149 fi 9150 fi 9151 } 9152 function ble/widget/nsearch/accept-line { 9153 if ((${#_ble_util_fiberchain[@]})); then 9154 ble/widget/.bell "nsearch: now searching..." 9155 else 9156 ble/widget/nsearch/.exit 9157 ble-decode-key 13 # RET 9158 fi 9159 } 9160 9161 function ble-decode/keymap:nsearch/define { 9162 ble-bind -f __default__ nsearch/exit-default 9163 ble-bind -f __line_limit__ nop 9164 9165 ble-bind -f 'C-g' nsearch/cancel 9166 ble-bind -f 'C-x C-g' nsearch/cancel 9167 ble-bind -f 'C-M-g' nsearch/cancel 9168 ble-bind -f C-m nsearch/exit 9169 ble-bind -f RET nsearch/exit 9170 ble-bind -f C-j nsearch/accept-line 9171 ble-bind -f C-RET nsearch/accept-line 9172 9173 ble-bind -f C-r nsearch/backward 9174 ble-bind -f C-s nsearch/forward 9175 ble-bind -f C-p nsearch/backward 9176 ble-bind -f C-n nsearch/forward 9177 ble-bind -f up nsearch/backward 9178 ble-bind -f down nsearch/forward 9179 ble-bind -f prior nsearch/backward 9180 ble-bind -f next nsearch/forward 9181 } 9182 9183 # 9184 #------------------------------------------------------------------------------ 9185 # **** common bindings **** @edit.safe 9186 9187 function ble-decode/keymap:safe/.bind { 9188 [[ $ble_bind_nometa && $1 == *M-* ]] && return 0 9189 ble-bind -f "$1" "$2" 9190 } 9191 function ble-decode/keymap:safe/bind-common { 9192 ble-decode/keymap:safe/.bind insert 'overwrite-mode' 9193 9194 # ins 9195 ble-decode/keymap:safe/.bind __batch_char__ 'batch-insert' 9196 ble-decode/keymap:safe/.bind __defchar__ 'self-insert' 9197 ble-decode/keymap:safe/.bind 'C-q' 'quoted-insert' 9198 ble-decode/keymap:safe/.bind 'C-v' 'quoted-insert' 9199 ble-decode/keymap:safe/.bind 'M-C-m' 'newline' 9200 ble-decode/keymap:safe/.bind 'M-RET' 'newline' 9201 ble-decode/keymap:safe/.bind paste_begin 'bracketed-paste' 9202 9203 # kill 9204 ble-decode/keymap:safe/.bind 'C-@' 'set-mark' 9205 ble-decode/keymap:safe/.bind 'C-SP' 'set-mark' 9206 ble-decode/keymap:safe/.bind 'NUL' 'set-mark' 9207 ble-decode/keymap:safe/.bind 'M-SP' 'set-mark' 9208 ble-decode/keymap:safe/.bind 'C-x C-x' 'exchange-point-and-mark' 9209 ble-decode/keymap:safe/.bind 'C-w' 'kill-region-or kill-backward-uword' 9210 ble-decode/keymap:safe/.bind 'M-w' 'copy-region-or copy-backward-uword' 9211 ble-decode/keymap:safe/.bind 'C-y' 'yank' 9212 ble-decode/keymap:safe/.bind 'M-y' 'yank-pop' 9213 ble-decode/keymap:safe/.bind 'M-S-y' 'yank-pop backward' 9214 ble-decode/keymap:safe/.bind 'M-Y' 'yank-pop backward' 9215 9216 # spaces 9217 ble-decode/keymap:safe/.bind 'M-\' 'delete-horizontal-space' 9218 9219 # charwise operations 9220 ble-decode/keymap:safe/.bind 'C-f' '@nomarked forward-char' 9221 ble-decode/keymap:safe/.bind 'C-b' '@nomarked backward-char' 9222 ble-decode/keymap:safe/.bind 'right' '@nomarked forward-char' 9223 ble-decode/keymap:safe/.bind 'left' '@nomarked backward-char' 9224 ble-decode/keymap:safe/.bind 'S-C-f' '@marked forward-char' 9225 ble-decode/keymap:safe/.bind 'S-C-b' '@marked backward-char' 9226 ble-decode/keymap:safe/.bind 'S-right' '@marked forward-char' 9227 ble-decode/keymap:safe/.bind 'S-left' '@marked backward-char' 9228 ble-decode/keymap:safe/.bind 'C-d' 'delete-region-or delete-forward-char' 9229 ble-decode/keymap:safe/.bind 'delete' 'delete-region-or delete-forward-char' 9230 ble-decode/keymap:safe/.bind 'C-?' 'delete-region-or delete-backward-char' 9231 ble-decode/keymap:safe/.bind 'DEL' 'delete-region-or delete-backward-char' 9232 ble-decode/keymap:safe/.bind 'C-h' 'delete-region-or delete-backward-char' 9233 ble-decode/keymap:safe/.bind 'BS' 'delete-region-or delete-backward-char' 9234 ble-decode/keymap:safe/.bind 'C-t' 'transpose-chars' 9235 9236 # wordwise operations 9237 ble-decode/keymap:safe/.bind 'C-right' '@nomarked forward-cword' 9238 ble-decode/keymap:safe/.bind 'C-left' '@nomarked backward-cword' 9239 ble-decode/keymap:safe/.bind 'M-right' '@nomarked forward-sword' 9240 ble-decode/keymap:safe/.bind 'M-left' '@nomarked backward-sword' 9241 ble-decode/keymap:safe/.bind 'S-C-right' '@marked forward-cword' 9242 ble-decode/keymap:safe/.bind 'S-C-left' '@marked backward-cword' 9243 ble-decode/keymap:safe/.bind 'M-S-right' '@marked forward-sword' 9244 ble-decode/keymap:safe/.bind 'M-S-left' '@marked backward-sword' 9245 ble-decode/keymap:safe/.bind 'M-d' 'kill-forward-cword' 9246 ble-decode/keymap:safe/.bind 'M-h' 'kill-backward-cword' 9247 ble-decode/keymap:safe/.bind 'C-delete' 'delete-forward-cword' 9248 ble-decode/keymap:safe/.bind 'C-_' 'delete-backward-cword' 9249 ble-decode/keymap:safe/.bind 'C-DEL' 'delete-backward-cword' 9250 ble-decode/keymap:safe/.bind 'C-BS' 'delete-backward-cword' 9251 ble-decode/keymap:safe/.bind 'M-delete' 'copy-forward-sword' 9252 ble-decode/keymap:safe/.bind 'M-C-?' 'copy-backward-sword' 9253 ble-decode/keymap:safe/.bind 'M-DEL' 'copy-backward-sword' 9254 ble-decode/keymap:safe/.bind 'M-C-h' 'copy-backward-sword' 9255 ble-decode/keymap:safe/.bind 'M-BS' 'copy-backward-sword' 9256 9257 ble-decode/keymap:safe/.bind 'M-f' '@nomarked forward-cword' 9258 ble-decode/keymap:safe/.bind 'M-b' '@nomarked backward-cword' 9259 ble-decode/keymap:safe/.bind 'M-F' '@marked forward-cword' 9260 ble-decode/keymap:safe/.bind 'M-B' '@marked backward-cword' 9261 ble-decode/keymap:safe/.bind 'M-S-f' '@marked forward-cword' 9262 ble-decode/keymap:safe/.bind 'M-S-b' '@marked backward-cword' 9263 9264 ble-decode/keymap:safe/.bind 'M-c' 'capitalize-eword' 9265 ble-decode/keymap:safe/.bind 'M-l' 'downcase-eword' 9266 ble-decode/keymap:safe/.bind 'M-u' 'upcase-eword' 9267 ble-decode/keymap:safe/.bind 'M-t' 'transpose-ewords' 9268 9269 # linewise operations 9270 ble-decode/keymap:safe/.bind 'C-a' '@nomarked beginning-of-line' 9271 ble-decode/keymap:safe/.bind 'C-e' '@nomarked end-of-line' 9272 ble-decode/keymap:safe/.bind 'home' '@nomarked beginning-of-line' 9273 ble-decode/keymap:safe/.bind 'end' '@nomarked end-of-line' 9274 ble-decode/keymap:safe/.bind 'S-C-a' '@marked beginning-of-line' 9275 ble-decode/keymap:safe/.bind 'S-C-e' '@marked end-of-line' 9276 ble-decode/keymap:safe/.bind 'S-home' '@marked beginning-of-line' 9277 ble-decode/keymap:safe/.bind 'S-end' '@marked end-of-line' 9278 ble-decode/keymap:safe/.bind 'M-m' '@nomarked non-space-beginning-of-line' 9279 ble-decode/keymap:safe/.bind 'M-S-m' '@marked non-space-beginning-of-line' 9280 ble-decode/keymap:safe/.bind 'M-M' '@marked non-space-beginning-of-line' 9281 ble-decode/keymap:safe/.bind 'C-p' '@nomarked backward-line' # overwritten by bind-history 9282 ble-decode/keymap:safe/.bind 'up' '@nomarked backward-line' # overwritten by bind-history 9283 ble-decode/keymap:safe/.bind 'C-n' '@nomarked forward-line' # overwritten by bind-history 9284 ble-decode/keymap:safe/.bind 'down' '@nomarked forward-line' # overwritten by bind-history 9285 ble-decode/keymap:safe/.bind 'C-k' 'kill-forward-line' 9286 ble-decode/keymap:safe/.bind 'C-u' 'kill-backward-line' 9287 9288 ble-decode/keymap:safe/.bind 'S-C-p' '@marked backward-line' 9289 ble-decode/keymap:safe/.bind 'S-up' '@marked backward-line' 9290 ble-decode/keymap:safe/.bind 'S-C-n' '@marked forward-line' 9291 ble-decode/keymap:safe/.bind 'S-down' '@marked forward-line' 9292 9293 ble-decode/keymap:safe/.bind 'C-home' '@nomarked beginning-of-text' 9294 ble-decode/keymap:safe/.bind 'C-end' '@nomarked end-of-text' 9295 ble-decode/keymap:safe/.bind 'S-C-home' '@marked beginning-of-text' 9296 ble-decode/keymap:safe/.bind 'S-C-end' '@marked end-of-text' 9297 9298 # macros 9299 ble-decode/keymap:safe/.bind 'C-x (' 'start-keyboard-macro' 9300 ble-decode/keymap:safe/.bind 'C-x )' 'end-keyboard-macro' 9301 ble-decode/keymap:safe/.bind 'C-x e' 'call-keyboard-macro' 9302 ble-decode/keymap:safe/.bind 'C-x P' 'print-keyboard-macro' 9303 9304 # Note: vi では C-] は sabbrev-expand で上書きされる 9305 ble-decode/keymap:safe/.bind 'C-]' 'character-search-forward' 9306 ble-decode/keymap:safe/.bind 'M-C-]' 'character-search-backward' 9307 } 9308 function ble-decode/keymap:safe/bind-history { 9309 ble-decode/keymap:safe/.bind 'C-r' 'history-isearch-backward' 9310 ble-decode/keymap:safe/.bind 'C-s' 'history-isearch-forward' 9311 ble-decode/keymap:safe/.bind 'M-<' 'history-beginning' 9312 ble-decode/keymap:safe/.bind 'M->' 'history-end' 9313 ble-decode/keymap:safe/.bind 'C-prior' 'history-beginning' 9314 ble-decode/keymap:safe/.bind 'C-next' 'history-end' 9315 ble-decode/keymap:safe/.bind 'C-p' '@nomarked backward-line history' 9316 ble-decode/keymap:safe/.bind 'up' '@nomarked backward-line history' 9317 ble-decode/keymap:safe/.bind 'C-n' '@nomarked forward-line history' 9318 ble-decode/keymap:safe/.bind 'down' '@nomarked forward-line history' 9319 ble-decode/keymap:safe/.bind 'prior' 'history-search-backward' # bash-5.2 9320 ble-decode/keymap:safe/.bind 'next' 'history-search-forward' # bash-5.2 9321 ble-decode/keymap:safe/.bind 'C-x C-p' 'history-search-backward' 9322 ble-decode/keymap:safe/.bind 'C-x up' 'history-search-backward' 9323 ble-decode/keymap:safe/.bind 'C-x C-n' 'history-search-forward' 9324 ble-decode/keymap:safe/.bind 'C-x down' 'history-search-forward' 9325 ble-decode/keymap:safe/.bind 'C-x p' 'history-substring-search-backward' 9326 ble-decode/keymap:safe/.bind 'C-x n' 'history-substring-search-forward' 9327 ble-decode/keymap:safe/.bind 'C-x <' 'history-nsearch-backward' 9328 ble-decode/keymap:safe/.bind 'C-x >' 'history-nsearch-forward' 9329 ble-decode/keymap:safe/.bind 'C-x ,' 'history-nsearch-backward-again' 9330 ble-decode/keymap:safe/.bind 'C-x .' 'history-nsearch-forward-again' 9331 9332 ble-decode/keymap:safe/.bind 'M-.' 'insert-last-argument' 9333 ble-decode/keymap:safe/.bind 'M-_' 'insert-last-argument' 9334 ble-decode/keymap:safe/.bind 'M-C-y' 'insert-nth-argument' 9335 } 9336 function ble-decode/keymap:safe/bind-complete { 9337 ble-decode/keymap:safe/.bind 'C-i' 'complete' 9338 ble-decode/keymap:safe/.bind 'TAB' 'complete' 9339 ble-decode/keymap:safe/.bind 'M-?' 'complete show_menu' 9340 ble-decode/keymap:safe/.bind 'M-*' 'complete insert_all' 9341 ble-decode/keymap:safe/.bind 'M-{' 'complete insert_braces' 9342 ble-decode/keymap:safe/.bind 'C-TAB' 'menu-complete' 9343 ble-decode/keymap:safe/.bind 'S-C-i' 'menu-complete backward' 9344 ble-decode/keymap:safe/.bind 'S-TAB' 'menu-complete backward' 9345 ble-decode/keymap:safe/.bind 'auto_complete_enter' 'auto-complete-enter' 9346 9347 ble-decode/keymap:safe/.bind 'M-/' 'complete context=filename' 9348 ble-decode/keymap:safe/.bind 'M-~' 'complete context=username' 9349 ble-decode/keymap:safe/.bind 'M-$' 'complete context=variable' 9350 ble-decode/keymap:safe/.bind 'M-@' 'complete context=hostname' 9351 ble-decode/keymap:safe/.bind 'M-!' 'complete context=command' 9352 ble-decode/keymap:safe/.bind 'C-x /' 'complete show_menu:context=filename' 9353 ble-decode/keymap:safe/.bind 'C-x ~' 'complete show_menu:context=username' 9354 ble-decode/keymap:safe/.bind 'C-x $' 'complete show_menu:context=variable' 9355 ble-decode/keymap:safe/.bind 'C-x @' 'complete show_menu:context=hostname' 9356 ble-decode/keymap:safe/.bind 'C-x !' 'complete show_menu:context=command' 9357 9358 ble-decode/keymap:safe/.bind "M-'" 'sabbrev-expand' 9359 ble-decode/keymap:safe/.bind "C-x '" 'sabbrev-expand' 9360 ble-decode/keymap:safe/.bind 'C-x C-r' 'dabbrev-expand' 9361 9362 ble-decode/keymap:safe/.bind 'M-g' 'complete context=glob' 9363 ble-decode/keymap:safe/.bind 'C-x *' 'complete insert_all:context=glob' 9364 ble-decode/keymap:safe/.bind 'C-x g' 'complete show_menu:context=glob' 9365 9366 ble-decode/keymap:safe/.bind 'M-C-i' 'complete context=dynamic-history' 9367 ble-decode/keymap:safe/.bind 'M-TAB' 'complete context=dynamic-history' 9368 } 9369 function ble-decode/keymap:safe/bind-arg { 9370 local append_arg=append-arg${1:+'-or '}$1 9371 9372 ble-decode/keymap:safe/.bind M-C-u 'universal-arg' 9373 9374 ble-decode/keymap:safe/.bind M-- "$append_arg" 9375 ble-decode/keymap:safe/.bind M-0 "$append_arg" 9376 ble-decode/keymap:safe/.bind M-1 "$append_arg" 9377 ble-decode/keymap:safe/.bind M-2 "$append_arg" 9378 ble-decode/keymap:safe/.bind M-3 "$append_arg" 9379 ble-decode/keymap:safe/.bind M-4 "$append_arg" 9380 ble-decode/keymap:safe/.bind M-5 "$append_arg" 9381 ble-decode/keymap:safe/.bind M-6 "$append_arg" 9382 ble-decode/keymap:safe/.bind M-7 "$append_arg" 9383 ble-decode/keymap:safe/.bind M-8 "$append_arg" 9384 ble-decode/keymap:safe/.bind M-9 "$append_arg" 9385 9386 ble-decode/keymap:safe/.bind C-- "$append_arg" 9387 ble-decode/keymap:safe/.bind C-0 "$append_arg" 9388 ble-decode/keymap:safe/.bind C-1 "$append_arg" 9389 ble-decode/keymap:safe/.bind C-2 "$append_arg" 9390 ble-decode/keymap:safe/.bind C-3 "$append_arg" 9391 ble-decode/keymap:safe/.bind C-4 "$append_arg" 9392 ble-decode/keymap:safe/.bind C-5 "$append_arg" 9393 ble-decode/keymap:safe/.bind C-6 "$append_arg" 9394 ble-decode/keymap:safe/.bind C-7 "$append_arg" 9395 ble-decode/keymap:safe/.bind C-8 "$append_arg" 9396 ble-decode/keymap:safe/.bind C-9 "$append_arg" 9397 9398 ble-decode/keymap:safe/.bind - "$append_arg" 9399 ble-decode/keymap:safe/.bind 0 "$append_arg" 9400 ble-decode/keymap:safe/.bind 1 "$append_arg" 9401 ble-decode/keymap:safe/.bind 2 "$append_arg" 9402 ble-decode/keymap:safe/.bind 3 "$append_arg" 9403 ble-decode/keymap:safe/.bind 4 "$append_arg" 9404 ble-decode/keymap:safe/.bind 5 "$append_arg" 9405 ble-decode/keymap:safe/.bind 6 "$append_arg" 9406 ble-decode/keymap:safe/.bind 7 "$append_arg" 9407 ble-decode/keymap:safe/.bind 8 "$append_arg" 9408 ble-decode/keymap:safe/.bind 9 "$append_arg" 9409 } 9410 9411 function ble/widget/safe/__attach__ { 9412 ble/edit/info/set-default text '' 9413 } 9414 function ble-decode/keymap:safe/define { 9415 local ble_bind_nometa= 9416 ble-decode/keymap:safe/bind-common 9417 ble-decode/keymap:safe/bind-history 9418 ble-decode/keymap:safe/bind-complete 9419 9420 ble-bind -f 'C-d' 'delete-region-or delete-forward-char-or-exit' 9421 9422 ble-bind -f 'SP' magic-space 9423 ble-bind -f '/' magic-slash 9424 ble-bind -f 'M-^' history-expand-line 9425 9426 ble-bind -f __attach__ safe/__attach__ 9427 ble-bind -f __line_limit__ __line_limit__ 9428 9429 ble-bind -f 'C-c' discard-line 9430 ble-bind -f 'C-j' accept-line 9431 ble-bind -f 'C-RET' accept-line 9432 ble-bind -f 'C-m' accept-single-line-or-newline 9433 ble-bind -f 'RET' accept-single-line-or-newline 9434 ble-bind -f 'C-o' accept-and-next 9435 ble-bind -f 'C-x C-e' edit-and-execute-command 9436 ble-bind -f 'M-#' insert-comment 9437 ble-bind -f 'M-C-e' shell-expand-line 9438 ble-bind -f 'M-&' tilde-expand 9439 ble-bind -f 'C-g' bell 9440 ble-bind -f 'C-x C-g' bell 9441 ble-bind -f 'C-M-g' bell 9442 9443 ble-bind -f 'C-l' clear-screen 9444 ble-bind -f 'C-M-l' redraw-line 9445 9446 ble-bind -f 'f1' command-help 9447 ble-bind -f 'C-x C-v' display-shell-version 9448 ble-bind -c 'C-z' fg 9449 ble-bind -c 'M-z' fg 9450 } 9451 9452 function ble-edit/bind/load-editing-mode:safe { 9453 ble/decode/keymap#load safe 9454 } 9455 9456 ble/util/autoload "lib/keymap.emacs.sh" \ 9457 ble-decode/keymap:emacs/define 9458 ble/util/autoload "lib/keymap.vi.sh" \ 9459 ble-decode/keymap:vi_{i,n,o,x,s,c}map/define 9460 ble/util/autoload "lib/keymap.vi_digraph.sh" \ 9461 ble-decode/keymap:vi_digraph/define 9462 9463 function ble/widget/.change-editing-mode { 9464 [[ $_ble_decode_bind_state == none ]] && return 0 9465 local mode=$1 9466 if [[ $bleopt_default_keymap == auto ]]; then 9467 if [[ ! -o $mode ]]; then 9468 set -o "$mode" 9469 ble/decode/reset-default-keymap 9470 ble/decode/detach 9471 ble/decode/attach || ble-detach 9472 fi 9473 else 9474 bleopt default_keymap="$mode" 9475 fi 9476 } 9477 function ble/widget/emacs-editing-mode { 9478 ble/widget/.change-editing-mode emacs 9479 } 9480 function ble/widget/vi-editing-mode { 9481 ble/widget/.change-editing-mode vi 9482 } 9483 9484 # 9485 #------------------------------------------------------------------------------ 9486 # **** ble/builtin/read **** @edit.read 9487 9488 _ble_edit_read_accept= 9489 _ble_edit_read_result= 9490 function ble/widget/read/accept { 9491 _ble_edit_read_accept=1 9492 _ble_edit_read_result=$_ble_edit_str 9493 # [[ $_ble_edit_read_result ]] && 9494 # ble/history/add "$_ble_edit_read_result" # Note: cancel でも登録する 9495 ble/decode/keymap/pop 9496 } 9497 function ble/widget/read/cancel { 9498 local _ble_edit_line_disabled=1 9499 ble/widget/read/accept 9500 _ble_edit_read_accept=2 9501 } 9502 function ble/widget/read/delete-forward-char-or-cancel { 9503 if [[ $_ble_edit_str ]]; then 9504 ble/widget/delete-forward-char 9505 else 9506 ble/widget/read/cancel 9507 fi 9508 } 9509 9510 function ble/widget/read/__line_limit__.edit { 9511 local content=$1 9512 ble/widget/edit-and-execute-command.edit "$content" no-newline; local ext=$? 9513 ((ext==127)) && return "$ext" 9514 ble-edit/content/reset "$ret" 9515 ble/widget/read/accept 9516 } 9517 function ble/widget/read/__line_limit__ { 9518 ble/widget/__line_limit__ read/__line_limit__.edit 9519 } 9520 9521 function ble-decode/keymap:read/define { 9522 local ble_bind_nometa= 9523 ble-decode/keymap:safe/bind-common 9524 ble-decode/keymap:safe/bind-history 9525 # ble-decode/keymap:safe/bind-complete 9526 9527 ble-bind -f __line_limit__ read/__line_limit__ 9528 9529 ble-bind -f 'C-c' read/cancel 9530 ble-bind -f 'C-\' read/cancel 9531 ble-bind -f 'C-m' read/accept 9532 ble-bind -f 'RET' read/accept 9533 ble-bind -f 'C-j' read/accept 9534 ble-bind -f 'C-d' 'delete-region-or read/delete-forward-char-or-cancel' 9535 9536 # shell functions 9537 ble-bind -f 'C-g' bell 9538 # ble-bind -f 'C-l' clear-screen 9539 ble-bind -f 'C-l' redraw-line 9540 ble-bind -f 'C-M-l' redraw-line 9541 ble-bind -f 'C-x C-v' display-shell-version 9542 9543 # command-history 9544 # ble-bind -f 'M-^' history-expand-line 9545 # ble-bind -f 'SP' magic-space 9546 # ble-bind -f '/' magic-slash 9547 9548 # ble-bind -f 'C-[' bell # unbound for "bleopt decode_isolated_esc=auto" 9549 ble-bind -f 'C-^' bell 9550 } 9551 9552 _ble_edit_read_history=() 9553 _ble_edit_read_history_edit=() 9554 _ble_edit_read_history_dirt=() 9555 _ble_edit_read_history_index=0 9556 9557 function ble/builtin/read/.process-option { 9558 case $1 in 9559 (-e) opt_flags=${opt_flags}r ;; 9560 (-i) opt_default=$2 ;; 9561 (-p) opt_prompt=$2 ;; 9562 (-u) opt_fd=$2 9563 ble/array#push opts_in "$@" ;; 9564 (-t) opt_timeout=$2 ;; 9565 (*) ble/array#push opts "$@" ;; 9566 esac 9567 } 9568 function ble/builtin/read/.read-arguments { 9569 local is_normal_args= 9570 vars=() 9571 opts=() 9572 while (($#)); do 9573 local arg=$1; shift 9574 if [[ $is_normal_args || $arg != -* ]]; then 9575 ble/array#push vars "$arg" 9576 elif [[ $arg == -- ]]; then 9577 is_normal_args=1 9578 elif [[ $arg == --* ]]; then 9579 case $arg in 9580 (--help) 9581 opt_flags=${opt_flags}H ;; 9582 (*) 9583 ble/util/print "read: unrecognized long option '$arg'" >&2 9584 opt_flags=${opt_flags}E ;; 9585 esac 9586 else 9587 local i n=${#arg} c 9588 for ((i=1;i<n;i++)); do 9589 c=${arg:i:1} 9590 case ${arg:i} in 9591 ([adinNptu]) 9592 if (($#)); then 9593 ble/builtin/read/.process-option -$c "$1"; shift 9594 else 9595 ble/util/print "read: missing option argument for '-$c'" >&2 9596 opt_flags=${opt_flags}E 9597 fi 9598 break ;; 9599 ([adinNptu]*) ble/builtin/read/.process-option -$c "${arg:i+1}"; break ;; 9600 ([ers]*) ble/builtin/read/.process-option -$c ;; 9601 (*) 9602 ble/util/print "read: unrecognized option '-$c'" >&2 9603 opt_flags=${opt_flags}E ;; 9604 esac 9605 done 9606 fi 9607 done 9608 } 9609 9610 function ble/builtin/read/.set-up-textarea { 9611 # 初期化 9612 ble/decode/keymap/push read || return 1 9613 9614 [[ $_ble_edit_read_context == external ]] && 9615 _ble_canvas_panel_height[0]=0 9616 9617 # textarea, info 9618 _ble_textarea_panel=1 9619 _ble_canvas_panel_focus=1 9620 ble/textarea#invalidate 9621 ble/edit/info/set-default ansi '' 9622 9623 # edit/prompt 9624 _ble_edit_PS1=$opt_prompt 9625 _ble_prompt_ps1_data=(0 '' '' 0 0 0 32 0 "" "") 9626 9627 # edit 9628 _ble_edit_dirty_observer=() 9629 ble/widget/.newline/clear-content 9630 _ble_edit_arg= 9631 ble-edit/content/reset "$opt_default" newline 9632 _ble_edit_ind=${#opt_default} 9633 9634 # edit/undo 9635 ble-edit/undo/clear-all 9636 9637 # edit/history 9638 ble/history/set-prefix _ble_edit_read_ 9639 9640 # syntax, highlight 9641 _ble_syntax_lang=text 9642 _ble_highlight_layer_list=(plain region overwrite_mode disabled) 9643 return 0 9644 } 9645 function ble/builtin/read/TRAPWINCH { 9646 local IFS=$_ble_term_IFS 9647 ble/application/onwinch 9648 } 9649 function ble/builtin/read/.loop { 9650 # この関数はサブシェルの中で実行される事を前提としている。 9651 9652 set +m # ジョブ管理を無効にする 9653 9654 # Note: サブシェルの中では eval で failglob を防御できない様だ。 9655 # それが理由で visible-bell を呼び出すと read が終了してしまう。 9656 # 対策として failglob を外す。サブシェルの中なので影響はない筈。 9657 # ref #D1090 9658 shopt -u failglob 9659 9660 local ret; ble/canvas/panel/save-position; local pos0=$ret 9661 ble/builtin/read/.set-up-textarea || return 1 9662 ble/builtin/trap/install-hook WINCH readline 9663 blehook internal_WINCH=ble/builtin/read/TRAPWINCH 9664 9665 local ret= timeout= 9666 if [[ $opt_timeout ]]; then 9667 ble/util/clock; local start_time=$ret 9668 9669 # Note: 時間分解能が低いとき、実際は 1999ms なのに 9670 # 1000ms に切り捨てられている可能性もある。 9671 # 待ち時間が長くなる方向に倒して処理する。 9672 ((start_time&&(start_time-=_ble_util_clock_reso-1))) 9673 9674 if [[ $opt_timeout == *.* ]]; then 9675 local mantissa=${opt_timeout%%.*} 9676 local fraction=${opt_timeout##*.}000 9677 ((timeout=mantissa*1000+10#0${fraction::3})) 9678 else 9679 ((timeout=opt_timeout*1000)) 9680 fi 9681 ((timeout<0)) && timeout= 9682 fi 9683 9684 ble/application/render 9685 9686 # Note: ble-decode-key が中断しない為の設定 #D0998 9687 # ble/encoding:.../is-intermediate の状態にはないと仮定して、 9688 # それによって ble-decode-key が中断する事はないと考える。 9689 local _ble_decode_input_count=0 9690 local ble_decode_char_nest= 9691 local -a _ble_decode_char_buffer=() 9692 9693 local char= 9694 local _ble_edit_read_accept= 9695 local _ble_edit_read_result= 9696 while [[ ! $_ble_edit_read_accept ]]; do 9697 local timeout_option= 9698 if [[ $timeout ]]; then 9699 if ((_ble_bash>=40000)); then 9700 local timeout_frac=000$((timeout%1000)) 9701 timeout_option="-t $((timeout/1000)).${timeout_frac:${#timeout_frac}-3}" 9702 else 9703 timeout_option="-t $((timeout/1000))" 9704 fi 9705 fi 9706 9707 # read 1 character 9708 IFS= ble/bash/read -d '' -n 1 $timeout_option char "${opts_in[@]}"; local ext=$? 9709 if ((ext>128)); then 9710 # timeout 9711 # Note: #D1467 Cygwin/Linux では read の timeout は 142 だが、これはシステム依存。 9712 # man bash にある様に 128 より大きいかどうかで判定する。 9713 _ble_edit_read_accept=142 9714 break 9715 fi 9716 9717 # update timeout 9718 if [[ $timeout ]]; then 9719 ble/util/clock; local current_time=$ret 9720 ((timeout-=current_time-start_time)) 9721 if ((timeout<=0)); then 9722 # timeout 9723 _ble_edit_read_accept=142 9724 break 9725 fi 9726 start_time=$current_time 9727 fi 9728 9729 # process 9730 ble/util/s2c "$char" 9731 ble-decode-char "$ret" 9732 [[ $_ble_edit_read_accept ]] && break 9733 9734 # render 9735 ble/util/is-stdin-ready && continue 9736 ble-edit/content/check-limit 9737 ble-decode/.hook/erase-progress 9738 ble/application/render 9739 done 9740 9741 # 入力が終わったら消すか次の行へ行く 9742 if [[ $_ble_edit_read_context == internal ]]; then 9743 local -a DRAW_BUFF=() 9744 ble/canvas/panel#set-height.draw "$_ble_textarea_panel" 0 9745 ble/canvas/panel/load-position.draw "$pos0" 9746 ble/canvas/bflush.draw 9747 else 9748 if ((_ble_edit_read_accept==1)); then 9749 ble/widget/.insert-newline # #D1800 (既に外部状態なのでOK) 9750 else 9751 _ble_edit_line_disabled=1 ble/widget/.insert-newline # #D1800 (既に外部状態なのでOK) 9752 fi 9753 fi 9754 9755 ble/util/buffer.flush >&2 9756 ble/term/visible-bell/erase 9757 9758 if ((_ble_edit_read_accept==1)); then 9759 local q=\' Q="'\''" 9760 printf %s "__ble_input='${_ble_edit_read_result//$q/$Q}'" 9761 elif ((_ble_edit_read_accept==142)); then 9762 # timeout 9763 return "$ext" 9764 else 9765 return 1 9766 fi 9767 } 9768 9769 function ble/builtin/read/.impl { 9770 local -a opts=() vars=() opts_in=() 9771 # opt_flags ... E: error, H: help (--help), r: readline (-e) 9772 local opt_flags= opt_prompt= opt_default= opt_timeout= opt_fd=0 9773 9774 # シェル変数 TMOUT 9775 local rex1='^[0-9]+(\.[0-9]*)?$|^\.[0-9]+$' rex2='^[0.]+$' 9776 [[ $TMOUT =~ $rex1 && ! ( $TMOUT =~ $rex2 ) ]] && opt_timeout=$TMOUT 9777 9778 ble/builtin/read/.read-arguments "$@" 9779 if [[ $opt_flags == *[HE]* ]]; then 9780 if [[ $opt_flags == *H* ]]; then 9781 builtin read --help 9782 elif [[ $opt_flags == *E* ]]; then 9783 builtin read --usage 2>&1 1>/dev/null | ble/bin/grep ^read >&2 9784 fi 9785 return 2 9786 fi 9787 9788 if ! [[ $opt_flags == *r* && -t $opt_fd ]]; then 9789 # "-e オプションが指定されてかつ端末からの読み取り" のとき以外は builtin read する。 9790 [[ $opt_prompt ]] && ble/array#push opts -p "$opt_prompt" 9791 [[ $opt_timeout ]] && ble/array#push opts -t "$opt_timeout" 9792 __ble_args=("${opts[@]}" "${opts_in[@]}" -- "${vars[@]}") 9793 __ble_command='ble/bash/read "${__ble_args[@]}"' 9794 return 0 9795 fi 9796 9797 ble/decode/keymap#load read 9798 local result _ble_edit_read_context=$_ble_term_state 9799 9800 # Note: サブシェル中で重複して出力されない様に空にしておく 9801 ble/util/buffer.flush >&2 9802 9803 [[ $_ble_edit_read_context == external ]] && ble/term/enter # 外側にいたら入る 9804 result=$(ble/builtin/read/.loop); local ext=$? 9805 [[ $_ble_edit_read_context == external ]] && ble/term/leave # 元の状態に戻る 9806 9807 # Note: サブシェルを抜ける時に set-height 1 0 するので辻褄合わせ。 9808 [[ $_ble_edit_read_context == internal ]] && ((_ble_canvas_panel_height[1]=0)) 9809 9810 if ((ext==0)); then 9811 builtin eval -- "$result" 9812 __ble_args=("${opts[@]}" -- "${vars[@]}") 9813 __ble_command='ble/bash/read "${__ble_args[@]}" <<< "$__ble_input"' 9814 fi 9815 return "$ext" 9816 } 9817 9818 ## @fn read [-ers] [-adinNptu arg] [name...] 9819 ## 9820 ## ble.sh の所為で builtin read -e が全く動かなくなるので、 9821 ## read -e を ble.sh の枠組みで再実装する。 9822 ## 9823 function ble/builtin/read { 9824 if [[ $_ble_decode_bind_state == none ]]; then 9825 builtin read "$@" 9826 return "$?" 9827 fi 9828 9829 local _ble_local_set _ble_local_shopt 9830 ble/base/.adjust-bash-options _ble_local_set _ble_local_shopt 9831 9832 # used by core-complete to cancel progcomp 9833 [[ $_ble_builtin_read_hook ]] && 9834 builtin eval -- "$_ble_builtin_read_hook" 9835 9836 local __ble_command= __ble_args= __ble_input= 9837 [[ ! $_ble_attached || $_ble_edit_exec_inside_userspace ]] && ble/base/adjust-BASH_REMATCH 9838 ble/builtin/read/.impl "$@"; local __ble_ext=$? 9839 [[ ! $_ble_attached || $_ble_edit_exec_inside_userspace ]] && ble/base/restore-BASH_REMATCH 9840 9841 ble/base/.restore-bash-options _ble_local_set _ble_local_shopt 9842 [[ $__ble_command ]] || return "$__ble_ext" 9843 # 局所変数により被覆されないように外側で評価 9844 builtin eval -- "$__ble_command" 9845 } 9846 function read { ble/builtin/read "$@"; } 9847 9848 #------------------------------------------------------------------------------ 9849 # **** command-help **** @command-help 9850 9851 ## @fn[custom] ble/cmdinfo/help 9852 ## @fn[custom] ble/cmdinfo/help:$command 9853 ## 9854 ## ヘルプを表示するシェル関数を定義します。 9855 ## ble/widget/command-help から呼び出されます。 9856 ## ble/cmdinfo/help:$command はコマンド $command に対するヘルプ表示で使われます。 9857 ## ble/cmdinfo/help はその他のコマンドに対するヘルプ表示で使われます。 9858 ## 9859 ## @var[in] command 9860 ## @var[in] type 9861 ## コマンド名と種類 (type -t によって得られるもの) を指定します。 9862 ## 9863 ## @var[in] comp_line comp_point comp_words comp_cword 9864 ## 現在のコマンドラインと位置、コマンド名・引数と現在の引数番号を指定します。 9865 ## 9866 ## @exit[out] 9867 ## ヘルプの終了が完了したときに 0 を返します。 9868 ## それ以外の時は 0 以外を返します。 9869 ## 9870 9871 ## @fn ble/widget/command-help/.read-man 9872 ## @var[out] man_content 9873 function ble/widget/command-help/.read-man { 9874 local -x _ble_local_tmpfile; ble/util/assign/mktmp 9875 local pager="sh -c 'cat >| \"\$_ble_local_tmpfile\"'" 9876 MANPAGER=$pager PAGER=$pager MANOPT= man "$@" 2>/dev/null; local ext=$? # 668ms 9877 ble/util/readfile man_content "$_ble_local_tmpfile" # 80ms 9878 ble/util/assign/rmtmp 9879 return "$ext" 9880 } 9881 9882 function ble/widget/command-help/.locate-in-man-bash { 9883 local command=$1 9884 local ret rex 9885 local rex_esc=$'(\e\\[[ -?]*[@-~]||.\b)' cr=$'\r' 9886 9887 # check if pager is less 9888 local pager; ble/util/get-pager pager 9889 local pager_cmd=${pager%%["$_ble_term_IFS"]*} 9890 [[ ${pager_cmd##*/} == less ]] || return 1 9891 9892 # awk/gawk 9893 local awk=ble/bin/awk; type -t gawk &>/dev/null && awk=gawk 9894 9895 # man bash 9896 local man_content; ble/widget/command-help/.read-man bash || return 1 # 733ms (3 fork: man, sh, cat) 9897 9898 # locate line number 9899 local cmd_awk 9900 case $command in 9901 ('function') cmd_awk='name () compound-command' ;; 9902 ('until') cmd_awk=while ;; 9903 ('command') cmd_awk='command [' ;; 9904 ('source') cmd_awk=. ;; 9905 ('typeset') cmd_awk=declare ;; 9906 ('readarray') cmd_awk=mapfile ;; 9907 ('[') cmd_awk=test ;; 9908 (*) cmd_awk=$command ;; 9909 esac 9910 ble/string#escape-for-awk-regex "$cmd_awk"; local rex_awk=$ret 9911 rex='\b$'; [[ $awk == gawk && $cmd_awk =~ $rex ]] && rex_awk=$rex_awk'\y' 9912 local awk_script='{ 9913 gsub(/'"$rex_esc"'/, ""); 9914 if (!par && $0 ~ /^['"$_ble_term_space"']*'"$rex_awk"'/) { print NR; exit; } 9915 par = !($0 ~ /^['"$_ble_term_space"']*$/); 9916 }' 9917 local awk_out; ble/util/assign awk_out '"$awk" "$awk_script" 2>/dev/null <<< "$man_content"' || return 1 # 206ms (1 fork) 9918 local iline=${awk_out%$'\n'}; [[ $iline ]] || return 1 9919 9920 # show 9921 ble/string#escape-for-extended-regex "$command"; local rex_ext=$ret 9922 rex='\b$'; [[ $command =~ $rex ]] && rex_ext=$rex_ext'\b' 9923 rex='^\b'; [[ $command =~ $rex ]] && rex_ext="($rex_esc|\b)$rex_ext" 9924 local manpager="$pager -r +'/$rex_ext$cr$((iline-1))g'" 9925 builtin eval -- "$manpager" <<< "$man_content" # 1 fork 9926 } 9927 function ble/widget/command-help/.show-bash-script { 9928 local _ble_local_pipeline=$1 9929 local -x LESS="${LESS:+$LESS }-r" # Note: Bash のバグで tempenv builtin eval は消滅するので #D1438 9930 type -t source-highlight &>/dev/null && 9931 _ble_local_pipeline='source-highlight -s sh -f esc | '$_ble_local_pipeline 9932 builtin eval -- "$_ble_local_pipeline" 9933 } 9934 function ble/widget/command-help/.locate-function-in-source { 9935 local func=$1 source lineno line 9936 ble/function#get-source-and-lineno "$func" || return 1 9937 [[ -f $source && -s $source ]] || return 1 # pipe 等は読み取らない 9938 9939 # check if pager is less 9940 local pager; ble/util/get-pager pager 9941 local pager_cmd=${pager%%["$_ble_term_IFS"]*} 9942 [[ ${pager_cmd##*/} == less ]] || return 1 9943 9944 # check if the file really contains the function definition 9945 ble/util/assign line 'ble/bin/sed -n "${lineno}{p;q;}" "$source"' 9946 [[ $line == *"$func"* ]] || return 1 9947 9948 ble/widget/command-help/.show-bash-script '"$pager" +"${lineno}g"' < "$source" 9949 } 9950 9951 ## @fn ble/widget/command-help.core 9952 ## @var[in] type 9953 ## @var[in] command 9954 ## @var[in] comp_cword comp_words comp_line comp_point 9955 function ble/widget/command-help.core { 9956 ble/function#try ble/cmdinfo/help:"$command" && return 0 9957 ble/function#try ble/cmdinfo/help "$command" && return 0 9958 9959 if [[ $type == builtin || $type == keyword ]]; then 9960 # 組み込みコマンド・キーワードは man bash を表示 9961 ble/widget/command-help/.locate-in-man-bash "$command" && return 0 9962 elif [[ $type == function ]]; then 9963 ble/widget/command-help/.locate-function-in-source "$command" && return 0 9964 9965 # シェル関数は定義を表示 9966 local def; ble/function#getdef "$command" 9967 ble/widget/command-help/.show-bash-script ble/util/pager <<< "$def" && return 0 9968 fi 9969 9970 if ble/is-function ble/bin/man; then 9971 MANOPT= ble/bin/man "${command##*/}" 2>/dev/null && return 0 9972 # Note: $(man "${command##*/}") だと (特に日本語で) 正しい結果が得られない。 9973 # if local content=$(MANOPT= ble/bin/man "${command##*/}" 2>&1) && [[ $content ]]; then 9974 # ble/util/print "$content" | ble/util/pager 9975 # return 0 9976 # fi 9977 fi 9978 9979 if local content; content=$("$command" --help 2>&1) && [[ $content ]]; then 9980 ble/util/print "$content" | ble/util/pager 9981 return 0 9982 fi 9983 9984 ble/util/print "ble: help of \`$command' not found" >&2 9985 return 1 9986 } 9987 9988 ## @fn ble/widget/command-help/type.resolve-alias 9989 ## サブシェルで実行してエイリアスを解決する。 9990 ## 解決のために unalias を使用する為にサブシェルで実行する。 9991 ## 9992 ## @stdout type:command 9993 ## command はエイリアスを解決した後の最終的なコマンド 9994 ## type はそのコマンドの種類 9995 ## 解決に失敗した時は何も出力しない。 9996 ## 9997 function ble/widget/command-help/.type/.resolve-alias { 9998 local literal=$1 command=$2 type=alias 9999 local last_literal=$1 last_command=$2 10000 10001 while 10002 [[ $command == "$literal" ]] || break # Note: type=alias 10003 10004 local alias_def 10005 ble/util/assign alias_def "alias $command" 10006 builtin unalias "$command" 10007 builtin eval "alias_def=${alias_def#*=}" # remove quote 10008 literal=${alias_def%%["$_ble_term_IFS"]*} command= type= 10009 ble/syntax:bash/simple-word/is-simple "$literal" || break # Note: type= 10010 local ret; ble/syntax:bash/simple-word/eval "$literal"; command=$ret 10011 ble/util/type type "$command" 10012 [[ $type ]] || break # Note: type= 10013 10014 last_literal=$literal 10015 last_command=$command 10016 [[ $type == alias ]] 10017 do :; done 10018 10019 if [[ ! $type || $type == alias ]]; then 10020 # - command はエイリアスに一致するが literal では quote されている時、 10021 # type=alias の状態でループを抜ける。 10022 # - 途中で複雑なコマンドに展開された時、必ずしも先頭の単語がコマンド名ではない。 10023 # 例: alias which='(alias; declare -f) | /usr/bin/which ...' 10024 # この時途中で type= になってループを抜ける。 10025 # 10026 # これらの時、直前の成功した command 名で非エイリアス名を探す。 10027 literal=$last_literal 10028 command=$last_command 10029 builtin unalias "$command" &>/dev/null 10030 ble/util/type type "$command" 10031 fi 10032 10033 local q="'" Q="'\''" 10034 printf "type='%s'\n" "${type//$q/$Q}" 10035 printf "literal='%s'\n" "${literal//$q/$Q}" 10036 printf "command='%s'\n" "${command//$q/$Q}" 10037 return 0 10038 } 2>/dev/null 10039 10040 ## @fn ble/widget/command-help/.type 10041 ## @var[out] type command 10042 function ble/widget/command-help/.type { 10043 local literal=$1 10044 type= command= 10045 ble/syntax:bash/simple-word/is-simple "$literal" || return 1 10046 local ret; ble/syntax:bash/simple-word/eval "$literal"; command=$ret 10047 ble/util/type type "$command" 10048 10049 # alias の時はサブシェルで解決 10050 if [[ $type == alias ]]; then 10051 builtin eval -- "$(ble/widget/command-help/.type/.resolve-alias "$literal" "$command")" 10052 fi 10053 10054 if [[ $type == keyword && $command != "$literal" ]]; then 10055 if [[ $command == %* ]] && jobs -- "$command" &>/dev/null; then 10056 type=jobs 10057 else 10058 # type -a の第二候補を用いる #D1406 10059 type=${type[1]} 10060 [[ $type ]] || return 1 10061 fi 10062 fi 10063 } 10064 10065 function ble/widget/command-help.impl { 10066 local literal=$1 10067 if [[ ! $literal ]]; then 10068 ble/widget/.bell 10069 return 1 10070 fi 10071 10072 local type command; ble/widget/command-help/.type "$literal" 10073 if [[ ! $type ]]; then 10074 ble/widget/.bell "command \`$command' not found" 10075 return 1 10076 fi 10077 10078 ble/widget/external-command ble/widget/command-help.core 10079 } 10080 10081 function ble/widget/command-help { 10082 # ToDo: syntax update? 10083 ble-edit/content/clear-arg 10084 local comp_cword comp_words comp_line comp_point 10085 if ble/syntax:bash/extract-command "$_ble_edit_ind"; then 10086 local cmd=${comp_words[0]} 10087 else 10088 local args; ble/string#split-words args "$_ble_edit_str" 10089 local cmd=${args[0]} 10090 fi 10091 10092 ble/widget/command-help.impl "$cmd" 10093 } 10094 10095 # 10096 #------------------------------------------------------------------------------ 10097 # **** ble-edit/bind **** @bind 10098 10099 function ble-edit/bind/stdout.on { :;} 10100 function ble-edit/bind/stdout.off { ble/util/buffer.flush >&2;} 10101 function ble-edit/bind/stdout.finalize { :;} 10102 10103 if [[ $bleopt_internal_suppress_bash_output ]]; then 10104 _ble_edit_io_fname2=$_ble_base_run/$$.stderr 10105 10106 function ble-edit/bind/stdout.on { 10107 exec 2>&"$_ble_util_fd_stderr" 10108 } 10109 function ble-edit/bind/stdout.off { 10110 ble/util/buffer.flush >&2 10111 ble-edit/io/check-stderr 10112 exec 2>>"$_ble_edit_io_fname2" 10113 } 10114 function ble-edit/bind/stdout.finalize { 10115 ble-edit/bind/stdout.on 10116 [[ -f $_ble_edit_io_fname2 ]] && : >| "$_ble_edit_io_fname2" 10117 } 10118 10119 ## @fn ble-edit/io/check-stderr 10120 ## bash が stderr にエラーを出力したかチェックし表示する。 10121 function ble-edit/io/check-stderr { 10122 local file=${1:-$_ble_edit_io_fname2} 10123 10124 # if the visible bell function is already defined. 10125 if ble/is-function ble/term/visible-bell; then 10126 # checks if "$file" is an ordinary non-empty file 10127 # since the $file might be /dev/null depending on the configuration. 10128 # /dev/null の様なデバイスではなく、中身があるファイルの場合。 10129 if [[ -f $file && -s $file ]]; then 10130 local message= line 10131 while IFS= ble/bash/read line || [[ $line ]]; do 10132 # * The head of error messages seems to be ${BASH##*/}. 10133 # 例えば ~/bin/bash-3.1 等から実行していると 10134 # "bash-3.1: ~" 等というエラーメッセージになる。 10135 if [[ $line == 'bash: '* || $line == "${BASH##*/}: "* || $line == "ble.sh ("*"): "* ]]; then 10136 message="$message${message:+; }$line" 10137 fi 10138 done < "$file" 10139 10140 [[ $message ]] && ble/term/visible-bell "$message" 10141 : >| "$file" 10142 fi 10143 fi 10144 } 10145 10146 # * bash-3.1, bash-3.2, bash-3.0 では C-d は直接検知できない。 10147 # IGNOREEOF を設定しておくと C-d を押した時に 10148 # stderr に bash が文句を吐くのでそれを捕まえて C-d が押されたと見做す。 10149 if ((_ble_bash<40000)); then 10150 function ble-edit/io/TRAPUSR1 { 10151 [[ $_ble_term_state == internal ]] || return 1 10152 10153 local FUNCNEST= 10154 local IFS=$_ble_term_IFS 10155 local file=$_ble_edit_io_fname2.proc 10156 if [[ -s $file ]]; then 10157 local content cmd 10158 ble/util/readfile content "$file" 10159 : >| "$file" 10160 for cmd in $content; do 10161 case $cmd in 10162 (eof) 10163 # C-d 10164 ble-decode/.hook 4 10165 builtin eval -- "$_ble_decode_bind_hook" ;; 10166 esac 10167 done 10168 fi 10169 ble/builtin/trap/invoke USR1 10170 } 10171 blehook/declare internal_USR1 10172 blehook internal_USR1!=ble-edit/io/TRAPUSR1 10173 ble/builtin/trap/install-hook USR1 10174 10175 function ble-edit/io/check-ignoreeof-message { 10176 local line=$1 10177 10178 # 様々の Bash のバージョンで使われているメッセージと照合する。 10179 [[ ( $bleopt_internal_ignoreeof_trap && $line == *$bleopt_internal_ignoreeof_trap* ) || 10180 $line == *'Use "exit" to leave the shell.'* || 10181 $line == *'ログアウトする為には exit を入力して下さい'* || 10182 $line == *'シェルから脱出するには "exit" を使用してください。'* || 10183 $line == *'シェルから脱出するのに "exit" を使いなさい.'* || 10184 $line == *'Gebruik Kaart na Los Tronk'* ]] && return 0 10185 10186 # lib/core-edit.ignoreeof-messages.txt の中身をキャッシュする様にする? 10187 [[ $line == *exit* ]] && ble/bin/grep -q -F "$line" "$_ble_base"/lib/core-edit.ignoreeof-messages.txt 10188 } 10189 10190 function ble-edit/io/check-ignoreeof-loop { 10191 local line opts=:$1: 10192 while IFS= ble/bash/read line; do 10193 if [[ $line == *[^$_ble_term_IFS]* ]]; then 10194 ble/util/print "$line" >> "$_ble_edit_io_fname2" 10195 fi 10196 10197 if ble-edit/io/check-ignoreeof-message "$line"; then 10198 ble/util/print eof >> "$_ble_edit_io_fname2.proc" 10199 kill -USR1 $$ 10200 ble/util/msleep 100 # 連続で送ると bash が落ちるかも (落ちた事はないが念の為) 10201 fi 10202 done 10203 } &>/dev/null 10204 10205 ble/bin/rm -f "$_ble_edit_io_fname2.pipe" 10206 if ble/bin/mkfifo "$_ble_edit_io_fname2.pipe" 2>/dev/null; then 10207 { 10208 ble-edit/io/check-ignoreeof-loop fifo < "$_ble_edit_io_fname2.pipe" & disown 10209 } &>/dev/null 10210 10211 ble/fd#alloc _ble_edit_io_fd2 '> "$_ble_edit_io_fname2.pipe"' 10212 10213 function ble-edit/bind/stdout.off { 10214 ble/util/buffer.flush >&2 10215 ble-edit/io/check-stderr 10216 exec 2>&"$_ble_edit_io_fd2" 10217 } 10218 elif . "$_ble_base/lib/init-msys1.sh"; ble-edit/io:msys1/start-background; then 10219 function ble-edit/bind/stdout.off { 10220 ble/util/buffer.flush >&2 10221 ble-edit/io/check-stderr 10222 10223 # Note: 一気に入力すると permission denied のエラーメッセージが出る。 10224 # メッセージを抑制するには先に >/dev/null してから別の exec で繋がな 10225 # ければならない。同じ exec でリダイレクトしようとするとメッセージ本 10226 # 体は表示されないが、エラーメッセージの改行だけは出力されてしなう。 10227 exec 2>/dev/null 10228 exec 2>>"$_ble_edit_io_fname2.buff" 10229 } 10230 fi 10231 fi 10232 fi 10233 10234 [[ ${_ble_edit_detach_flag-} != reload ]] && 10235 _ble_edit_detach_flag= 10236 function ble-edit/bind/.exit-TRAPRTMAX { 10237 # シグナルハンドラの中では stty は bash によって設定されている。 10238 local FUNCNEST= 10239 ble/base/unload 10240 builtin exit 0 10241 } 10242 10243 ## @fn ble-edit/bind/.check-detach 10244 ## 10245 ## @exit detach した場合に 0 を返します。それ以外の場合に 1 を返します。 10246 ## 10247 function ble-edit/bind/.check-detach { 10248 if [[ ! -o emacs && ! -o vi ]]; then 10249 # 実は set +o emacs などとした時点で eval の評価が中断されるので、これを検知することはできない。 10250 # 従って、現状ではここに入ってくることはないようである。 10251 ble/util/print "${_ble_term_setaf[9]}[ble: unsupported]$_ble_term_sgr0 Sorry, ble.sh is supported only with some editing mode (set -o emacs/vi)." 1>&2 10252 ble-detach 10253 fi 10254 10255 # reload & prompt-attach の時は素通り (detach 後の処理は不要) 10256 [[ $_ble_edit_detach_flag == prompt-attach ]] && return 1 10257 10258 if [[ $_ble_edit_detach_flag || ! $_ble_attached ]]; then 10259 type=$_ble_edit_detach_flag 10260 _ble_edit_detach_flag= 10261 #ble/term/visible-bell ' Bye!! ' 10262 10263 local attached=$_ble_attached 10264 [[ $attached ]] && ble-detach/impl 10265 10266 if [[ $type == exit ]]; then 10267 # ※この部分は現在使われていない。 10268 # exit 時の処理は trap EXIT を用いて行う事に決めた為。 10269 # 一応 _ble_edit_detach_flag=exit と直に入力する事で呼び出す事はできる。 10270 ble-detach/message "${_ble_term_setaf[12]}[ble: exit]$_ble_term_sgr0" 10271 10272 # bind -x の中から exit すると bash が stty を「前回の状態」に復元してしまう様だ。 10273 # シグナルハンドラの中から exit すれば stty がそのままの状態で抜けられる様なのでそうする。 10274 builtin trap 'ble-edit/bind/.exit-TRAPRTMAX' RTMAX 10275 kill -RTMAX $$ 10276 else 10277 ble-detach/message \ 10278 "${_ble_term_setaf[12]}[ble: detached]$_ble_term_sgr0" \ 10279 "Please run \`stty sane' to recover the correct TTY state." 10280 10281 if ((_ble_bash>=40000)); then 10282 READLINE_LINE=' stty sane;' READLINE_POINT=11 READLINE_MARK=0 10283 printf %s "$READLINE_LINE" 10284 fi 10285 fi 10286 10287 if [[ $attached ]]; then 10288 # ここで ble-detach/impl した時は調整は最低限でOK 10289 ble/base/restore-BASH_REMATCH 10290 ble/base/restore-bash-options 10291 ble/base/restore-POSIXLY_CORRECT 10292 ble/base/restore-builtin-wrappers 10293 builtin eval -- "$_ble_bash_FUNCNEST_restore" # これ以降関数は呼び出せない 10294 else 10295 # Note: 既に ble-detach/impl されていた時 (reload 時) は 10296 # epilogue によって detach 後の状態が壊されているので 10297 # 改めて prologue を呼び出す必要がある。 10298 # #D1130 #D1199 #D1223 10299 ble-edit/exec:"$bleopt_internal_exec_type"/.prologue 10300 _ble_edit_exec_inside_prologue= 10301 fi 10302 10303 return 0 10304 else 10305 # Note: ここに入った時 -o emacs か -o vi のどちらかが成立する。なぜなら、 10306 # [[ ! -o emacs && ! -o vi ]] のときは ble-detach が呼び出されるのでここには来ない。 10307 local state=$_ble_decode_bind_state 10308 if [[ ( $state == emacs || $state == vi ) && ! -o $state ]]; then 10309 ble/decode/reset-default-keymap 10310 ble/decode/detach 10311 if ! ble/decode/attach; then 10312 ble-detach 10313 ble-edit/bind/.check-detach # 改めて終了処理 10314 return "$?" 10315 fi 10316 fi 10317 10318 return 1 10319 fi 10320 } 10321 10322 if ((_ble_bash>=40100)); then 10323 function ble-edit/bind/.head/adjust-bash-rendering { 10324 # bash-4.1 以降では呼出直前にプロンプトが消される 10325 ble/textarea#redraw-cache 10326 ble/util/buffer.flush >&2 10327 } 10328 else 10329 function ble-edit/bind/.head/adjust-bash-rendering { 10330 # bash-3.*, bash-4.0 では呼出直前に次の行に移動する 10331 ((_ble_canvas_y++,_ble_canvas_x=0)) 10332 local -a DRAW_BUFF=() 10333 ble/canvas/panel#goto.draw "$_ble_textarea_panel" "${_ble_textarea_cur[0]}" "${_ble_textarea_cur[1]}" 10334 ble/canvas/flush.draw 10335 } 10336 fi 10337 10338 function ble-edit/bind/.head { 10339 ble-edit/bind/stdout.on 10340 ble/base/recover-bash-options 10341 [[ $bleopt_internal_suppress_bash_output ]] || 10342 ble-edit/bind/.head/adjust-bash-rendering 10343 } 10344 10345 function ble-edit/bind/.tail-without-draw { 10346 ble-edit/bind/stdout.off 10347 } 10348 10349 if ((_ble_bash>=40000)); then 10350 function ble-edit/bind/.tail { 10351 #%if leakvar 10352 ble/debug/leakvar#check $"leakvar" tail.beg 10353 #%end.i 10354 ble/application/render 10355 ble/util/idle.do 10356 ble/textarea#adjust-for-bash-bind # bash-4.0+ 10357 #%if leakvar 10358 ble/debug/leakvar#check $"leakvar" tail.end 10359 #%end.i 10360 ble-edit/bind/stdout.off 10361 } 10362 else 10363 function ble-edit/bind/.tail { 10364 ble/application/render 10365 ble/util/idle.do 10366 # bash-3 では READLINE_LINE を設定する方法はないので常に 0 幅 10367 ble-edit/bind/stdout.off 10368 } 10369 fi 10370 10371 ## src/decode.sh 用の設定 10372 function ble-decode/PROLOGUE { 10373 ble-edit/exec:gexec/restore-state 10374 ble-edit/bind/.head 10375 ble/decode/bind/adjust-uvw 10376 ble/term/enter 10377 } 10378 10379 ## src/decode.sh 用の設定 10380 function ble-decode/EPILOGUE { 10381 if ((_ble_bash>=40000)); then 10382 # 貼付対策: 10383 # 大量の文字が入力された時に毎回再描画をすると滅茶苦茶遅い。 10384 # 次の文字が既に来て居る場合には描画処理をせずに抜ける。 10385 # (再描画は次の文字に対する bind 呼出でされる筈。) 10386 # 現在は ble-decode/.hook の段階で連続入力を縮約しているので 10387 # この関数はそんなに沢山呼び出される事はない。 10388 # bash 4.0 以降でないとユーザー入力検出できない事に注意。 10389 if ble/decode/has-input && ! ble-edit/exec/has-pending-commands; then 10390 ble-edit/bind/.tail-without-draw 10391 return 0 10392 fi 10393 fi 10394 10395 ble-edit/content/check-limit 10396 10397 # コマンド実行が設定された時には _ble_decode_bind_hook の最後で bind/.tail 10398 # が実行される。 10399 ble-edit/exec:"$bleopt_internal_exec_type"/process && return 0 10400 10401 ble-edit/bind/.tail 10402 return 0 10403 } 10404 10405 function ble/widget/.internal-print-command { 10406 local _ble_local_command=$1 _ble_command_opts=$2 10407 _ble_edit_line_disabled=1 ble/widget/.insert-newline # #D1800 pair=leave-command-layout 10408 [[ :$_ble_command_opts: != *:pre-flush:* ]] || ble/util/buffer.flush >&2 10409 BASH_COMMAND=$_ble_local_command builtin eval -- "$_ble_local_command" 10410 ble/edit/leave-command-layout # #D1800 pair=.insert-newline 10411 [[ :$_ble_command_opts: != *:post-flush:* ]] || ble/util/buffer.flush >&2 10412 } 10413 10414 function ble/widget/print { 10415 ble-edit/content/clear-arg 10416 local message="$*" lines 10417 [[ ${message//["$_ble_term_IFS"]} ]] || return 1 10418 lines=("$@") 10419 10420 if [[ ! ${_ble_attached-} || ${_ble_edit_exec_inside_begin-} ]]; then 10421 ble/util/print-lines "${lines[@]}" 10422 else 10423 ble/widget/.internal-print-command \ 10424 'ble/util/print-lines "${lines[@]}" >&2' pre-flush 10425 fi 10426 } 10427 function ble/widget/internal-command { 10428 ble-edit/content/clear-arg 10429 local command=$1 10430 [[ ${command//[$_ble_term_IFS]} ]] || return 1 10431 ble/widget/.internal-print-command "$command" 10432 } 10433 function ble/widget/external-command { 10434 ble-edit/content/clear-arg 10435 local _ble_local_command=$1 10436 [[ ${_ble_local_command//[$_ble_term_IFS]} ]] || return 1 10437 10438 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 10439 ble/textarea#invalidate 10440 local -a DRAW_BUFF=() 10441 ble/canvas/panel#set-height.draw "$_ble_textarea_panel" 0 10442 ble/canvas/panel#goto.draw "$_ble_textarea_panel" 0 0 sgr0 10443 ble/canvas/bflush.draw 10444 ble/term/leave 10445 ble/util/buffer.flush >&2 10446 BASH_COMMAND=$_ble_local_command builtin eval -- "$_ble_local_command"; local ext=$? 10447 ble/term/enter 10448 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 10449 return "$ext" 10450 } 10451 function ble/widget/execute-command { 10452 ble-edit/content/clear-arg 10453 local command=$1 10454 if [[ $command != *[!"$_ble_term_IFS"]* ]]; then 10455 # Note: 空コマンドでも .insert-newline は実行する。 10456 _ble_edit_line_disabled=1 ble/widget/.insert-newline keep-info 10457 return 1 10458 fi 10459 10460 # やはり通常コマンドはちゃんとした環境で評価するべき 10461 _ble_edit_line_disabled=1 ble/widget/.insert-newline # #D1800 pair=exec/register 10462 ble-edit/exec/register "$command" 10463 } 10464 10465 ## @fn ble/widget/.SHELL_COMMAND command 10466 ## ble-bind -c で登録されたコマンドを処理します。 10467 function ble/widget/.SHELL_COMMAND { ble/widget/execute-command "$@"; } 10468 10469 ## @fn ble/widget/.EDIT_COMMAND command 10470 ## ble-bind -x で登録されたコマンドを処理します。 10471 function ble/widget/.EDIT_COMMAND { 10472 local command=$1 10473 local -x READLINE_LINE=$_ble_edit_str 10474 local -x READLINE_POINT=$_ble_edit_ind 10475 local -x READLINE_MARK=$_ble_edit_mark 10476 [[ $_ble_edit_arg ]] && 10477 local -x READLINE_ARGUMENT=$_ble_edit_arg 10478 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 10479 ble/widget/.hide-current-line keep-header 10480 ble-edit/restore-PS1 10481 ble/term/leave-for-widget 10482 builtin eval -- "$command"; local ext=$? 10483 ble/term/enter-for-widget 10484 ble-edit/adjust-PS1 10485 ble-edit/content/clear-arg 10486 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 10487 10488 [[ $READLINE_LINE != "$_ble_edit_str" ]] && 10489 ble-edit/content/reset-and-check-dirty "$READLINE_LINE" 10490 ((_ble_edit_ind=READLINE_POINT)) 10491 ((_ble_edit_mark=READLINE_MARK)) 10492 10493 local N=${#_ble_edit_str} 10494 ((_ble_edit_ind<0?_ble_edit_ind=0:(_ble_edit_ind>N&&(_ble_edit_ind=N)))) 10495 ((_ble_edit_mark<0?_ble_edit_mark=0:(_ble_edit_mark>N&&(_ble_edit_mark=N)))) 10496 10497 return "$ext" 10498 } 10499 10500 ## ble-decode.sh 用の設定 10501 function ble-decode/INITIALIZE_DEFMAP { 10502 local ret 10503 bleopt/get:default_keymap; local defmap=$ret 10504 if ble-edit/bind/load-editing-mode "$defmap"; then 10505 local base_keymap=$defmap 10506 [[ $defmap == vi ]] && base_keymap=vi_imap 10507 builtin eval -- "$2=\$base_keymap" 10508 ble/decode/is-keymap "$base_keymap" && return 0 10509 fi 10510 10511 # エラーメッセージ 10512 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 10513 ble/widget/.hide-current-line 10514 local -a DRAW_BUFF=() 10515 ble/canvas/put.draw "$_ble_term_cr$_ble_term_el${_ble_term_setaf[9]}" 10516 ble/canvas/put.draw "[ble.sh: The definition of the default keymap \"$defmap\" is not found. ble.sh uses \"safe\" keymap instead.]" 10517 ble/canvas/put.draw "$_ble_term_sgr0$_ble_term_nl" 10518 ble/canvas/bflush.draw 10519 ble/util/buffer.flush >&2 10520 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 10521 10522 # Fallback keymap "safe" 10523 ble-edit/bind/load-editing-mode safe && 10524 ble/decode/keymap#load safe && 10525 builtin eval -- "$2=safe" && 10526 bleopt_default_keymap=safe 10527 } 10528 10529 function ble-edit/bind/load-editing-mode { 10530 local name=$1 10531 if ble/is-function ble-edit/bind/load-editing-mode:"$name"; then 10532 ble-edit/bind/load-editing-mode:"$name" 10533 else 10534 ble/util/import "$_ble_base/lib/keymap.$name.sh" 10535 fi 10536 } 10537 function ble-edit/bind/clear-keymap-definition-loader { 10538 builtin unset -f ble-edit/bind/load-editing-mode:safe 10539 builtin unset -f ble-edit/bind/load-editing-mode:emacs 10540 builtin unset -f ble-edit/bind/load-editing-mode:vi 10541 } 10542 10543 #------------------------------------------------------------------------------ 10544 # **** entry points **** 10545 10546 function ble-edit/initialize { 10547 ble/prompt/initialize 10548 } 10549 function ble-edit/attach { 10550 # user DEBUG trap 取得を試行 10551 _ble_builtin_trap_DEBUG__initialize 10552 # user DEBUG trap が取得済みなら DEBUG trap 削除 10553 [[ $_ble_builtin_trap_DEBUG_userTrapInitialized ]] && 10554 _ble_edit_exec_gexec__TRAPDEBUG_adjust 10555 10556 ble-edit/attach/.attach 10557 _ble_canvas_x=0 _ble_canvas_y=0 10558 ble/util/buffer "$_ble_term_cr" 10559 } 10560 function ble-edit/detach { 10561 ble-edit/bind/stdout.finalize 10562 ble-edit/attach/.detach 10563 ble-edit/exec:gexec/.TRAPDEBUG/restore 10564 } 10565 10566 ble/function#trace ble-edit/attach 10567 10568 #------------------------------------------------------------------------------ 10569 # messages 10570 10571 function ble/util/message/handler:edit/append-line { 10572 local data=${1%$'\n'}; data=${data#$'\n'} 10573 [[ ${_ble_edit_str##*$'\n'} ]] && data=$'\n'$data 10574 local len=${#_ble_edit_str} 10575 ble-edit/content/replace-limited "$len" "$len" "$data" nobell 10576 _ble_edit_ind=${#_ble_edit_str} 10577 return 0 10578 } 10579 10580 function ble-append-line { 10581 local data="${*-}" 10582 [[ $data ]] || return 0 10583 ble/util/message.post "$$" precmd edit/append-line "$data" 10584 }