sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

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 }