sistema_progs

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

keymap.vi.sh (274965B)


      1 #!/bin/bash
      2 
      3 # Note: bind (INITIALIZE_DEFMAP) の中から再帰的に呼び出されうるので、
      4 # 先に ble-edit/bind/load-editing-mode:vi を上書きする必要がある。
      5 ble/is-function ble-edit/bind/load-editing-mode:vi && return 0
      6 function ble-edit/bind/load-editing-mode:vi { :; }
      7 
      8 # 2020-04-29 force update (rename ble-decode/keymap/.register)
      9 # 2021-01-25 force update (change mapping of C-w and M-w)
     10 # 2021-04-26 force update (rename ble/decode/keymap#.register)
     11 # 2021-09-23 force update (change to nsearch and bind-history)
     12 
     13 source "$_ble_base/lib/keymap.vi_digraph.sh"
     14 
     15 ## @bleopt keymap_vi_macro_depth
     16 bleopt/declare -n keymap_vi_macro_depth 64
     17 
     18 ## @fn ble/keymap:vi/k2c key
     19 ##   @var[out] ret
     20 function ble/keymap:vi/k2c {
     21   local key=$1
     22   local flag=$((key&_ble_decode_MaskFlag)) char=$((key&_ble_decode_MaskChar))
     23   if ((flag==0&&(32<=char&&char<_ble_decode_FunctionKeyBase))); then
     24     ret=$char
     25     return 0
     26   elif ((flag==_ble_decode_Ctrl&&63<=char&&char<128&&(char&0x1F)!=0)); then
     27     ((char=char==63?127:char&0x1F))
     28     ret=$char
     29     return 0
     30   else
     31     return 1
     32   fi
     33 }
     34 
     35 #------------------------------------------------------------------------------
     36 # utils
     37 
     38 ## @fn ble/string#index-of-chars text chars [index]
     39 ##   文字集合に含まれる文字を、文字列中で順方向に探索します。
     40 ## @fn ble/string#last-index-of-chars text chars [index]
     41 ##   文字集合に含まれる文字を、文字列中で逆方向に探索します。
     42 ##
     43 ##   @param[in] text
     44 ##     検索する対象の文字列を指定します。
     45 ##   @param[in] chars
     46 ##     検索する文字の集合を指定します
     47 ##   @param[in] index
     48 ##     text の内の検索開始位置を指定します。
     49 ##   @var[out] ret
     50 ##
     51 function ble/string#index-of-chars {
     52   local chars=$2 index=${3:-0}
     53   local text=${1:index}
     54   local cut=${text%%["$chars"]*}
     55   if ((${#cut}<${#text})); then
     56     ((ret=index+${#cut}))
     57     return 0
     58   else
     59     ret=-1
     60     return 1
     61   fi
     62 }
     63 function ble/string#last-index-of-chars {
     64   local text=$1 chars=$2 index=$3
     65   [[ $index ]] && text=${text::index}
     66   local cut=${text%["$chars"]*}
     67   if ((${#cut}<${#text})); then
     68     ((ret=${#cut}))
     69     return 0
     70   else
     71     ret=-1
     72     return 1
     73   fi
     74 }
     75 
     76 ## @fn ble-edit/content/nonbol-eolp text
     77 ##   @var[out] ret
     78 function ble-edit/content/nonbol-eolp {
     79   local pos=${1:-$_ble_edit_ind}
     80   ! ble-edit/content/bolp "$pos" && ble-edit/content/eolp "$pos"
     81 }
     82 
     83 ## @fn ble/keymap:vi/string#encode-rot13 text
     84 ##   @var[out] ret
     85 function ble/keymap:vi/string#encode-rot13 {
     86   local text=$1
     87   local -a buff=() ch
     88   for ((i=0;i<${#text};i++)); do
     89     ch=${text:i:1}
     90     if [[ $ch == [A-Z] ]]; then
     91       ch=${_ble_util_string_upper_list%%"$ch"*}
     92       ch=${_ble_util_string_upper_list:(${#ch}+13)%26:1}
     93     elif [[ $ch == [a-z] ]]; then
     94       ch=${_ble_util_string_lower_list%%"$ch"*}
     95       ch=${_ble_util_string_lower_list:(${#ch}+13)%26:1}
     96     fi
     97     ble/array#push buff "$ch"
     98   done
     99   IFS= builtin eval 'ret="${buff[*]-}"'
    100 }
    101 
    102 #------------------------------------------------------------------------------
    103 # constants
    104 
    105 _ble_keymap_vi_REX_WORD=$'[_a-zA-Z0-9]+|[!-/:-@[-`{-~]+|[^ \t\na-zA-Z0-9!-/:-@[-`{-~]+'
    106 
    107 #------------------------------------------------------------------------------
    108 # vi_imap/__default__, vi-command/decompose-meta
    109 
    110 function ble/widget/vi_imap/__default__ {
    111   local flag=$((KEYS[0]&_ble_decode_MaskFlag)) code=$((KEYS[0]&_ble_decode_MaskChar))
    112 
    113   # メタ修飾付きの入力 M-key は ESC + key に分解する
    114   if ((flag&_ble_decode_Meta)); then
    115     ble/keymap:vi/imap-repeat/pop
    116 
    117     local esc=27 # ESC
    118     # local esc=$((_ble_decode_Ctrl|0x5b)) # もしくは C-[
    119     ble/decode/widget/skip-lastwidget
    120     ((flag&=~_ble_decode_Meta))
    121     ((flag==_ble_decode_Shft&&0x61<=code&&code<=0x7A&&(flag=0,code-=0x20)))
    122     ble/decode/widget/redispatch-by-keys "$esc" "$((flag|code))" "${KEYS[@]:1}"
    123     return 0
    124   fi
    125 
    126   # Control 修飾された文字 C-@ - C-\, C-? は制御文字 \000 - \037, \177 に戻して挿入
    127   if local ret; ble/keymap:vi/k2c "${KEYS[0]}"; then
    128     local -a KEYS; KEYS=("$ret")
    129     ble/widget/self-insert
    130     return 0
    131   fi
    132 
    133   return 125
    134 }
    135 
    136 function ble/widget/vi-command/decompose-meta {
    137   local flag=$((KEYS[0]&_ble_decode_MaskFlag)) code=$((KEYS[0]&_ble_decode_MaskChar))
    138 
    139   # メタ修飾付きの入力 M-key は ESC + key に分解する
    140   if ((flag&_ble_decode_Meta)); then
    141     local esc=$((_ble_decode_Ctrl|0x5b)) # C-[ (もしくは esc=27 ESC?)
    142     ble/decode/widget/skip-lastwidget
    143     ((flag&=~_ble_decode_Meta))
    144     ((flag==_ble_decode_Shft&&0x61<=code&&code<=0x7A&&(flag=0,code-=0x20)))
    145     ble/decode/widget/redispatch-by-keys "$esc" "$((flag|code))" "${KEYS[@]:1}"
    146     return 0
    147   fi
    148 
    149   return 125
    150 }
    151 
    152 function ble/widget/vi_omap/__default__ {
    153   ble/widget/vi-command/decompose-meta || ble/widget/vi-command/bell
    154   return 0
    155 }
    156 function ble/widget/vi_omap/cancel {
    157   ble/keymap:vi/adjust-command-mode
    158   return 0
    159 }
    160 
    161 #------------------------------------------------------------------------------
    162 # repeat
    163 
    164 ## @var _ble_keymap_vi_irepeat_count
    165 ##   挿入モードに入る時に指定された引数を記録する。
    166 _ble_keymap_vi_irepeat_count=
    167 
    168 ## @arr _ble_keymap_vi_irepeat
    169 ##   挿入モードに入るときに指定された引数が 1 より大きい時、
    170 ##   後で操作を繰り返すために操作内容を記録する配列。
    171 ##
    172 ##   各要素は keys:widget の形式を持つ。
    173 ##   keys は空白区切りの key (整数値) の列、つまり ${KEYS[*]} である。
    174 ##   widget は実際に呼び出す WIDGET の内容である。
    175 ##
    176 _ble_keymap_vi_irepeat=()
    177 
    178 ble/array#push _ble_textarea_local_VARNAMES \
    179                _ble_keymap_vi_irepeat_count \
    180                _ble_keymap_vi_irepeat
    181 
    182 function ble/keymap:vi/imap-repeat/pop {
    183   local top_index=$((${#_ble_keymap_vi_irepeat[*]}-1))
    184   ((top_index>=0)) && builtin unset -v '_ble_keymap_vi_irepeat[top_index]'
    185 }
    186 function ble/keymap:vi/imap-repeat/push {
    187   local IFS=$_ble_term_IFS
    188   ble/array#push _ble_keymap_vi_irepeat "${KEYS[*]-}:$WIDGET"
    189 }
    190 
    191 function ble/keymap:vi/imap-repeat/reset {
    192   local count=${1-}
    193   _ble_keymap_vi_irepeat_count=
    194   _ble_keymap_vi_irepeat=()
    195   ((count>1)) && _ble_keymap_vi_irepeat_count=$count
    196 }
    197 function ble/keymap:vi/imap-repeat/process {
    198   if ((_ble_keymap_vi_irepeat_count>1)); then
    199     local repeat=$_ble_keymap_vi_irepeat_count
    200     local -a widgets; widgets=("${_ble_keymap_vi_irepeat[@]}")
    201 
    202     local i widget
    203     for ((i=1;i<repeat;i++)); do
    204       for widget in "${widgets[@]}"; do
    205         ble/decode/widget/call "${widget#*:}" ${widget%%:*}
    206       done
    207     done
    208   fi
    209 }
    210 
    211 function ble/keymap:vi/imap/invoke-widget {
    212   local WIDGET=$1
    213   local -a KEYS; KEYS=("${@:2}")
    214   ble/keymap:vi/imap-repeat/push
    215   builtin eval -- "$WIDGET"
    216 }
    217 
    218 ## @arr _ble_keymap_vi_imap_white_list
    219 ##   引数を指定して入った挿入モードを抜けるときの繰り返しで許されるコマンドのリスト
    220 _ble_keymap_vi_imap_white_list=(
    221   self-insert
    222   batch-insert
    223   nop
    224   magic-space magic-slash
    225   delete-backward-{c,f,s,u}word
    226   copy{,-forward,-backward}-{c,f,s,u}word
    227   copy-region{,-or}
    228   clear-screen
    229   command-help
    230   display-shell-version
    231   redraw-line
    232 )
    233 function ble/keymap:vi/imap/is-command-white {
    234   if [[ $1 == ble/widget/self-insert ]]; then
    235     # frequently used command is checked first
    236     return 0
    237   elif [[ $1 == ble/widget/* ]]; then
    238     local IFS=$_ble_term_IFS
    239     local cmd=${1#ble/widget/}; cmd=${cmd%%["$_ble_term_IFS"]*}
    240     [[ $cmd == vi_imap/* || " ${_ble_keymap_vi_imap_white_list[*]} " == *" $cmd "*  ]] && return 0
    241   fi
    242   return 1
    243 }
    244 
    245 function ble/widget/vi_imap/__before_widget__ {
    246   if ble/keymap:vi/imap/is-command-white "$WIDGET"; then
    247     ble/keymap:vi/imap-repeat/push
    248   else
    249     if ((_ble_keymap_vi_mark_edit_dbeg>=0)); then
    250       ble/keymap:vi/mark/end-edit-area
    251       ble/keymap:vi/repeat/record-insert
    252       ble/keymap:vi/mark/start-edit-area
    253     fi
    254     ble/keymap:vi/imap-repeat/reset
    255   fi
    256 }
    257 
    258 #------------------------------------------------------------------------------
    259 # vi_imap/complete
    260 
    261 function ble/widget/vi_imap/complete {
    262   ble/keymap:vi/imap-repeat/pop
    263   ble/keymap:vi/undo/add more
    264   ble/widget/complete "$@"
    265 }
    266 function ble/keymap:vi/complete/insert.hook {
    267   [[ $_ble_decode_keymap == vi_imap ||
    268        $_ble_decode_keymap == auto_complete ]] || return 1
    269 
    270   local original=${comp_text:insert_beg:insert_end-insert_beg}
    271   local q="'" Q="'\''"
    272   local WIDGET="ble/widget/complete-insert '${original//$q/$Q}' '${insert//$q/$Q}' '${suffix//$q/$Q}'"
    273   ble/keymap:vi/imap-repeat/push
    274   [[ $_ble_decode_keymap == vi_imap ]] &&
    275     ble/keymap:vi/undo/add more
    276 }
    277 blehook complete_insert!=ble/keymap:vi/complete/insert.hook
    278 
    279 function ble-decode/keymap:vi_imap/bind-complete {
    280   ble-bind -f 'C-i'                 'vi_imap/complete'
    281   ble-bind -f 'TAB'                 'vi_imap/complete'
    282   ble-bind -f 'C-TAB'               'menu-complete'
    283   ble-bind -f 'S-C-i'               'menu-complete backward'
    284   ble-bind -f 'S-TAB'               'menu-complete backward'
    285   ble-bind -f 'auto_complete_enter' 'auto-complete-enter'
    286 
    287   ble-bind -f 'C-x /' 'menu-complete context=filename'
    288   ble-bind -f 'C-x ~' 'menu-complete context=username'
    289   ble-bind -f 'C-x $' 'menu-complete context=variable'
    290   ble-bind -f 'C-x @' 'menu-complete context=hostname'
    291   ble-bind -f 'C-x !' 'menu-complete context=command'
    292 
    293   ble-bind -f 'C-]'     'sabbrev-expand'
    294   ble-bind -f 'C-x C-r' 'dabbrev-expand'
    295 
    296   ble-bind -f 'C-x *' 'complete insert_all:context=glob'
    297   ble-bind -f 'C-x g' 'complete show_menu:context=glob'
    298 }
    299 
    300 #------------------------------------------------------------------------------
    301 # modes
    302 
    303 ## @var _ble_keymap_vi_insert_overwrite
    304 ##   挿入モードに入った時の上書き文字
    305 _ble_keymap_vi_insert_overwrite=
    306 
    307 ## @var _ble_keymap_vi_insert_leave
    308 ##   挿入モードから抜ける時に実行する関数を設定します
    309 _ble_keymap_vi_insert_leave=
    310 
    311 ## @var _ble_keymap_vi_single_command
    312 ##   ノーマルモードにおいて 1 つコマンドを実行したら
    313 ##   元の挿入モードに戻るモード (C-o) にいるかどうかを表します。
    314 _ble_keymap_vi_single_command=
    315 _ble_keymap_vi_single_command_overwrite=
    316 
    317 ble/array#push _ble_textarea_local_VARNAMES \
    318                _ble_keymap_vi_insert_overwrite \
    319                _ble_keymap_vi_insert_leave \
    320                _ble_keymap_vi_single_command \
    321                _ble_keymap_vi_single_command_overwrite
    322 
    323 ## @bleopt keymap_vi_mode_string_nmap
    324 ##   ノーマルモードの時に表示する文字列を指定します。
    325 ##   空文字列を指定したときは何も表示しません。
    326 bleopt/declare -n keymap_vi_mode_string_nmap $'\e[1m~\e[m'
    327 bleopt/declare -o keymap_vi_nmap_name keymap_vi_mode_string_nmap
    328 
    329 bleopt/declare -v term_vi_imap ''
    330 bleopt/declare -v term_vi_nmap ''
    331 bleopt/declare -v term_vi_omap ''
    332 bleopt/declare -v term_vi_xmap ''
    333 bleopt/declare -v term_vi_smap ''
    334 bleopt/declare -v term_vi_cmap ''
    335 
    336 bleopt/declare -v keymap_vi_imap_cursor ''
    337 bleopt/declare -v keymap_vi_nmap_cursor ''
    338 bleopt/declare -v keymap_vi_omap_cursor ''
    339 bleopt/declare -v keymap_vi_xmap_cursor ''
    340 bleopt/declare -v keymap_vi_smap_cursor ''
    341 bleopt/declare -v keymap_vi_cmap_cursor ''
    342 function ble/keymap:vi/.process-cursor-options {
    343   local keymap=${FUNCNAME[1]#bleopt/check:keymap_}; keymap=${keymap%_cursor}
    344   ble-bind -m "$keymap" --cursor "$value"
    345   local locate=$'\e[32m'$bleopt_source:$bleopt_lineno$'\e[m'
    346   ble/util/print-lines \
    347     "bleopt ($locate): The option 'keymap_${keymap}_cursor' has been removed." \
    348     "  Please use 'ble-bind -m $keymap --cursor $value' instead." >&2
    349 }
    350 function bleopt/check:keymap_vi_imap_cursor { ble/keymap:vi/.process-cursor-options; }
    351 function bleopt/check:keymap_vi_nmap_cursor { ble/keymap:vi/.process-cursor-options; }
    352 function bleopt/check:keymap_vi_omap_cursor { ble/keymap:vi/.process-cursor-options; }
    353 function bleopt/check:keymap_vi_xmap_cursor { ble/keymap:vi/.process-cursor-options; }
    354 function bleopt/check:keymap_vi_smap_cursor { ble/keymap:vi/.process-cursor-options; }
    355 function bleopt/check:keymap_vi_cmap_cursor { ble/keymap:vi/.process-cursor-options; }
    356 function bleopt/obsolete:keymap_vi_imap_cursor { :; }
    357 function bleopt/obsolete:keymap_vi_nmap_cursor { :; }
    358 function bleopt/obsolete:keymap_vi_omap_cursor { :; }
    359 function bleopt/obsolete:keymap_vi_xmap_cursor { :; }
    360 function bleopt/obsolete:keymap_vi_smap_cursor { :; }
    361 function bleopt/obsolete:keymap_vi_cmap_cursor { :; }
    362 
    363 bleopt/declare -v keymap_vi_mode_show 1
    364 function bleopt/check:keymap_vi_mode_show {
    365   local bleopt_keymap_vi_mode_show=$value
    366   [[ $_ble_attached ]] &&
    367     ble/keymap:vi/update-mode-indicator
    368   return 0
    369 }
    370 
    371 bleopt/declare -v keymap_vi_mode_update_prompt ''
    372 bleopt/declare -v keymap_vi_mode_name_insert    'INSERT'
    373 bleopt/declare -v keymap_vi_mode_name_replace   'REPLACE'
    374 bleopt/declare -v keymap_vi_mode_name_vreplace  'VREPLACE'
    375 bleopt/declare -v keymap_vi_mode_name_visual    'VISUAL'
    376 bleopt/declare -v keymap_vi_mode_name_select    'SELECT'
    377 bleopt/declare -v keymap_vi_mode_name_linewise  'LINE'
    378 bleopt/declare -v keymap_vi_mode_name_blockwise 'BLOCK'
    379 function bleopt/check:keymap_vi_mode_name_insert    { ble/keymap:vi/update-mode-indicator; }
    380 function bleopt/check:keymap_vi_mode_name_replace   { ble/keymap:vi/update-mode-indicator; }
    381 function bleopt/check:keymap_vi_mode_name_vreplace  { ble/keymap:vi/update-mode-indicator; }
    382 function bleopt/check:keymap_vi_mode_name_visual    { ble/keymap:vi/update-mode-indicator; }
    383 function bleopt/check:keymap_vi_mode_name_select    { ble/keymap:vi/update-mode-indicator; }
    384 function bleopt/check:keymap_vi_mode_name_linewise  { ble/keymap:vi/update-mode-indicator; }
    385 function bleopt/check:keymap_vi_mode_name_blockwise { ble/keymap:vi/update-mode-indicator; }
    386 
    387 
    388 ## @fn ble/keymap:vi/script/get-vi-keymap
    389 ##   現在の vi キーマップ名 (vi_?map) を取得します。
    390 ##   もし現在 vi キーマップにない場合には失敗します。
    391 function ble/keymap:vi/script/get-vi-keymap {
    392   ble/prompt/unit/add-hash '$_ble_decode_keymap,${_ble_decode_keymap_stack[*]}'
    393   local i=${#_ble_decode_keymap_stack[@]}
    394 
    395   keymap=$_ble_decode_keymap
    396   while [[ $keymap != vi_?map && $keymap != emacs ]]; do
    397     ((i--)) || return 1
    398     keymap=${_ble_decode_keymap_stack[i]}
    399   done
    400   [[ $keymap == vi_?map ]]
    401 }
    402 
    403 ## @fn ble/keymap:vi/script/get-mode
    404 ##   @var[out] mode
    405 function ble/keymap:vi/script/get-mode {
    406   ble/prompt/unit/add-hash '$_ble_decode_keymap,${_ble_decode_keymap_stack[*]}'
    407   ble/prompt/unit/add-hash '$_ble_keymap_vi_single_command,$_ble_edit_mark_active'
    408 
    409   mode=
    410 
    411   local keymap; ble/keymap:vi/script/get-vi-keymap
    412 
    413   # /[iR^R]?/
    414   if [[ $_ble_keymap_vi_single_command || $keymap == vi_imap ]]; then
    415     local overwrite=
    416     if [[ $keymap == vi_imap ]]; then
    417       overwrite=$_ble_edit_overwrite_mode
    418     elif [[ $keymap == vi_[noxs]map ]]; then
    419       overwrite=$_ble_keymap_vi_single_command_overwrite
    420     fi
    421     case $overwrite in
    422     ('') mode=i ;;
    423     (R)  mode=R ;;
    424     (*)  mode=$'\x12' ;; # C-r
    425     esac
    426   fi
    427 
    428   # /[nvV^VsS^S]?/
    429   case $keymap:${_ble_edit_mark_active%+} in
    430   (vi_xmap:vi_line) mode=$mode'V' ;;
    431   (vi_xmap:vi_block)mode=$mode$'\x16' ;; # C-v
    432   (vi_xmap:*)       mode=$mode'v' ;;
    433   (vi_smap:vi_line) mode=$mode'S' ;;
    434   (vi_smap:vi_block)mode=$mode$'\x13' ;; # C-s
    435   (vi_smap:*)       mode=$mode's' ;;
    436   (vi_[no]map:*)    mode=$mode'n' ;;
    437   (vi_cmap:*)       mode=$mode'c' ;;
    438   (vi_imap:*) ;;
    439   (*:*)             mode=$mode'?' ;;
    440   esac
    441 }
    442 
    443 _ble_keymap_vi_mode_name_dirty=
    444 function ble/keymap:vi/info_reveal.hook {
    445   [[ $_ble_keymap_vi_mode_name_dirty ]] || return 0
    446   _ble_keymap_vi_mode_name_dirty=
    447   ble/keymap:vi/update-mode-indicator
    448 }
    449 blehook info_reveal!=ble/keymap:vi/info_reveal.hook
    450 
    451 bleopt/declare -v prompt_vi_mode_indicator '\q{keymap:vi/mode-indicator}'
    452 function bleopt/check:prompt_vi_mode_indicator {
    453   local bleopt_prompt_vi_mode_indicator=$value
    454   [[ $_ble_attached ]] && ble/keymap:vi/update-mode-indicator
    455   return 0
    456 }
    457 
    458 _ble_keymap_vi_mode_indicator_data=()
    459 function ble/prompt/unit:_ble_keymap_vi_mode_indicator/update {
    460   local trace_opts=truncate:relative:noscrc:ansi
    461   local prompt_rows=1
    462   local prompt_cols=${COLUMNS:-80}
    463   ((prompt_cols&&prompt_cols--))
    464   local "${_ble_prompt_cache_vars[@]/%/=}" # WA #D1570 checked
    465   ble/prompt/unit:{section}/update _ble_keymap_vi_mode_indicator "$bleopt_prompt_vi_mode_indicator" "$trace_opts"
    466 }
    467 
    468 function ble/keymap:vi/update-mode-indicator {
    469   if [[ ! $_ble_attached ]] || ble/edit/is-command-layout; then
    470     _ble_keymap_vi_mode_name_dirty=1
    471     return 0
    472   fi
    473 
    474   local keymap
    475   ble/keymap:vi/script/get-vi-keymap || return 0
    476 
    477   if [[ $keymap == vi_imap ]]; then
    478     ble/util/buffer "$bleopt_term_vi_imap"
    479   elif [[ $keymap == vi_nmap ]]; then
    480     ble/util/buffer "$bleopt_term_vi_nmap"
    481   elif [[ $keymap == vi_xmap ]]; then
    482     ble/util/buffer "$bleopt_term_vi_xmap"
    483   elif [[ $keymap == vi_smap ]]; then
    484     ble/util/buffer "$bleopt_term_vi_smap"
    485   elif [[ $keymap == vi_omap ]]; then
    486     ble/util/buffer "$bleopt_term_vi_omap"
    487   elif [[ $keymap == vi_cmap ]]; then
    488     ble/edit/info/default text ''
    489     ble/util/buffer "$bleopt_term_vi_cmap"
    490     return 0
    491   fi
    492 
    493   [[ $bleopt_keymap_vi_mode_update_prompt ]] && ble/prompt/clear
    494 
    495   # prompt_vi_mode_indicator
    496   local prompt_vi_keymap=$keymap
    497   local version=$COLUMNS,$_ble_edit_lineno,$_ble_history_count,$_ble_edit_CMD
    498   local prompt_hashref_base='$version'
    499   ble/prompt/unit#update _ble_keymap_vi_mode_indicator
    500   local ret; ble/prompt/unit:{section}/get _ble_keymap_vi_mode_indicator; local mode=$ret
    501 
    502   local str=$mode
    503   if [[ $_ble_keymap_vi_reg_record ]]; then
    504     str=$str${str:+' '}$'\e[1;31mREC @'$_ble_keymap_vi_reg_record_char$'\e[m'
    505   elif [[ $_ble_edit_kbdmacro_record ]]; then
    506     str=$str${str:+' '}$'\e[1;31mREC\e[m'
    507   fi
    508 
    509   # Note #D2062: mc-4.8.29 以降ではコマンド終了直後に "-- INSERT --" 等の mode
    510   # indicator を出力すると、それをプロンプトと勘違いして抽出してしまう。仕方が
    511   # ないので mc の中では imap に対しては mode indicator は表示しない様にする。
    512   if [[ $_ble_edit_integration_mc_precmd_stop && $keymap == vi_imap ]]; then
    513     ble/edit/info/clear
    514     return 0
    515   fi
    516 
    517   ble/edit/info/default ansi "$str" # 6ms
    518 }
    519 blehook internal_PRECMD!=ble/keymap:vi/update-mode-indicator
    520 
    521 ## @fn ble/prompt/backslash:keymap:vi/mode-indicator
    522 ##   @var[in,opt] prompt_vi_keymap
    523 ##     ble/keymap:vi/script/get-vi-keymap のキャッシュ
    524 function ble/prompt/backslash:keymap:vi/mode-indicator {
    525   [[ $bleopt_keymap_vi_mode_show ]] || return 0
    526 
    527   local keymap=${prompt_vi_keymap-}
    528   if [[ $keymap ]]; then
    529     ble/prompt/unit/add-hash '$_ble_decode_keymap,${_ble_decode_keymap_stack[*]}'
    530   else
    531     ble/keymap:vi/script/get-vi-keymap || return 0
    532   fi
    533 
    534   local name= show= overwrite=
    535   ble/prompt/unit/add-hash '$_ble_edit_overwrite_mode,$_ble_keymap_vi_single_command,$_ble_keymap_vi_single_command_overwrite'
    536   if [[ $keymap == vi_imap ]]; then
    537     show=1 overwrite=$_ble_edit_overwrite_mode
    538   elif [[ $_ble_keymap_vi_single_command && ( $keymap == vi_nmap || $keymap == vi_omap ) ]]; then
    539     show=1 overwrite=$_ble_keymap_vi_single_command_overwrite
    540   elif [[ $keymap == vi_[xs]map ]]; then
    541     show=x overwrite=$_ble_keymap_vi_single_command_overwrite
    542   else
    543     name=$bleopt_keymap_vi_mode_string_nmap
    544   fi
    545 
    546   if [[ $show ]]; then
    547     if [[ $overwrite == R ]]; then
    548       name=$bleopt_keymap_vi_mode_name_replace
    549     elif [[ $overwrite ]]; then
    550       name=$bleopt_keymap_vi_mode_name_vreplace
    551     else
    552       name=$bleopt_keymap_vi_mode_name_insert
    553     fi
    554 
    555     if [[ $_ble_keymap_vi_single_command ]]; then
    556       local ret; ble/string#tolower "$name"; name="($ret)"
    557     fi
    558 
    559     if [[ $show == x ]]; then
    560       ble/prompt/unit/add-hash '${_ble_edit_mark_active%+}'
    561       local mark_type=${_ble_edit_mark_active%+}
    562       local visual_name=$bleopt_keymap_vi_mode_name_visual
    563       [[ $keymap == vi_smap ]] && visual_name=$bleopt_keymap_vi_mode_name_select
    564       if [[ $mark_type == vi_line ]]; then
    565         visual_name=$visual_name' '$bleopt_keymap_vi_mode_name_linewise
    566       elif [[ $mark_type == vi_block ]]; then
    567         visual_name=$visual_name' '$bleopt_keymap_vi_mode_name_blockwise
    568       fi
    569 
    570       if [[ $_ble_keymap_vi_single_command ]]; then
    571         name="$name $visual_name"
    572       else
    573         name=$visual_name
    574       fi
    575     fi
    576 
    577     name=$'\e[1m-- '$name$' --\e[m'
    578   fi
    579 
    580   [[ ! $name ]] || ble/prompt/print "$name"
    581 }
    582 
    583 function ble/widget/vi_imap/normal-mode.impl {
    584   local opts=$1
    585 
    586   # finalize insert mode
    587   ble/keymap:vi/mark/set-local-mark 94 "$_ble_edit_ind" # `^
    588   ble/keymap:vi/mark/end-edit-area
    589   [[ :$opts: == *:InsertLeave:* ]] && builtin eval -- "$_ble_keymap_vi_insert_leave"
    590 
    591   # set up normal mode
    592   _ble_edit_mark_active=
    593   _ble_edit_overwrite_mode=
    594   _ble_keymap_vi_insert_leave=
    595   _ble_keymap_vi_single_command=
    596   _ble_keymap_vi_single_command_overwrite=
    597   ble-edit/content/bolp || ((_ble_edit_ind--))
    598   ble/decode/keymap/push vi_nmap
    599 }
    600 function ble/widget/vi_imap/normal-mode {
    601   ble-edit/content/clear-arg
    602   ble/keymap:vi/imap-repeat/pop
    603   ble/keymap:vi/imap-repeat/process
    604   ble/keymap:vi/repeat/record-insert
    605   ble/widget/vi_imap/normal-mode.impl InsertLeave
    606   ble/keymap:vi/update-mode-indicator
    607   return 0
    608 }
    609 function ble/widget/vi_imap/normal-mode-without-insert-leave {
    610   ble-edit/content/clear-arg
    611   ble/keymap:vi/imap-repeat/pop
    612   ble/keymap:vi/repeat/record-insert
    613   ble/widget/vi_imap/normal-mode.impl
    614   ble/keymap:vi/update-mode-indicator
    615   return 0
    616 }
    617 function ble/widget/vi_imap/single-command-mode {
    618   ble-edit/content/clear-arg
    619   local single_command=1
    620   local single_command_overwrite=$_ble_edit_overwrite_mode
    621   ble-edit/content/eolp && _ble_keymap_vi_single_command=2
    622 
    623   ble/keymap:vi/imap-repeat/pop
    624   ble/widget/vi_imap/normal-mode.impl
    625   _ble_keymap_vi_single_command=$single_command
    626   _ble_keymap_vi_single_command_overwrite=$single_command_overwrite
    627   ble/keymap:vi/update-mode-indicator
    628   return 0
    629 }
    630 
    631 ## @fn ble/keymap:vi/needs-eol-fix
    632 ##
    633 ##   Note: この関数を使った後は ble/keymap:vi/adjust-command-mode を呼び出す必要がある。
    634 ##     そうしないとノーマルモードにおいてありえない位置にカーソルが来ることになる。
    635 ##
    636 function ble/keymap:vi/needs-eol-fix {
    637   [[ $_ble_decode_keymap == vi_nmap || $_ble_decode_keymap == vi_omap ]] || return 1
    638   [[ $_ble_keymap_vi_single_command ]] && return 1
    639   local index=${1:-$_ble_edit_ind}
    640   ble-edit/content/nonbol-eolp "$index"
    641 }
    642 function ble/keymap:vi/adjust-command-mode {
    643   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
    644     # 移動コマンドが来たら末尾拡張を無効にする。
    645     # 移動コマンドはここを通るはず…
    646     ble/keymap:vi/xmap/remove-eol-extension
    647   fi
    648 
    649   local kmap_popped=
    650   if [[ $_ble_decode_keymap == vi_omap ]]; then
    651     ble/decode/keymap/pop
    652     kmap_popped=1
    653   fi
    654 
    655   # search による mark の設定・解除
    656   if [[ $_ble_keymap_vi_search_activate ]]; then
    657     if [[ $_ble_decode_keymap != vi_[xs]map ]]; then
    658       _ble_edit_mark_active=$_ble_keymap_vi_search_activate
    659     fi
    660     _ble_keymap_vi_search_matched=1
    661     _ble_keymap_vi_search_activate=
    662   else
    663     [[ $_ble_edit_mark_active == vi_search ]] && _ble_edit_mark_active=
    664     ((_ble_keymap_vi_search_matched)) && _ble_keymap_vi_search_matched=
    665   fi
    666 
    667   if [[ $_ble_decode_keymap == vi_nmap && $_ble_keymap_vi_single_command ]]; then
    668     if ((_ble_keymap_vi_single_command==2)); then
    669       local index=$((_ble_edit_ind+1))
    670       ble-edit/content/nonbol-eolp "$index" && _ble_edit_ind=$index
    671     fi
    672     ble/widget/vi_nmap/.insert-mode 1 "$_ble_keymap_vi_single_command_overwrite" resume
    673     ble/keymap:vi/repeat/clear-insert
    674   elif [[ $kmap_popped ]]; then
    675     ble/keymap:vi/update-mode-indicator
    676   fi
    677 
    678   return 0
    679 }
    680 function ble/widget/vi-command/bell {
    681   ble/widget/.bell "$1"
    682   ble/keymap:vi/adjust-command-mode
    683   return 0
    684 }
    685 
    686 ## @fn ble/widget/vi_nmap/.insert-mode [arg [overwrite [opts]]]
    687 ##   @param[in] arg
    688 ##   @param[in] overwrite
    689 ##   @param[in] opts
    690 function ble/widget/vi_nmap/.insert-mode {
    691   [[ $_ble_decode_keymap == vi_[xs]map ]] && ble/decode/keymap/pop
    692   [[ $_ble_decode_keymap == vi_omap ]] && ble/decode/keymap/pop
    693   local arg=$1 overwrite=$2
    694   ble/keymap:vi/imap-repeat/reset "$arg"
    695   _ble_edit_mark_active=
    696   _ble_edit_overwrite_mode=$overwrite
    697   _ble_keymap_vi_insert_leave=
    698   _ble_keymap_vi_insert_overwrite=$overwrite
    699   _ble_keymap_vi_single_command=
    700   _ble_keymap_vi_single_command_overwrite=
    701   ble/keymap:vi/search/clear-matched
    702   ble/decode/keymap/pop
    703   ble/keymap:vi/update-mode-indicator
    704 
    705   ble/keymap:vi/mark/start-edit-area
    706   if [[ :$opts: != *:resume:* ]]; then
    707     ble/keymap:vi/mark/commit-edit-area "$_ble_edit_ind" "$_ble_edit_ind"
    708   fi
    709 }
    710 function ble/widget/vi_nmap/insert-mode {
    711   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    712   ble/widget/vi_nmap/.insert-mode "$ARG"
    713   ble/keymap:vi/repeat/record
    714   return 0
    715 }
    716 function ble/widget/vi_nmap/append-mode {
    717   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    718   if ! ble-edit/content/eolp; then
    719     ((_ble_edit_ind++))
    720   fi
    721   ble/widget/vi_nmap/.insert-mode "$ARG"
    722   ble/keymap:vi/repeat/record
    723   return 0
    724 }
    725 function ble/widget/vi_nmap/append-mode-at-end-of-line {
    726   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    727   local ret; ble-edit/content/find-logical-eol
    728   _ble_edit_ind=$ret
    729   ble/widget/vi_nmap/.insert-mode "$ARG"
    730   ble/keymap:vi/repeat/record
    731   return 0
    732 }
    733 function ble/widget/vi_nmap/insert-mode-at-beginning-of-line {
    734   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    735   local ret; ble-edit/content/find-logical-bol
    736   _ble_edit_ind=$ret
    737   ble/widget/vi_nmap/.insert-mode "$ARG"
    738   ble/keymap:vi/repeat/record
    739   return 0
    740 }
    741 function ble/widget/vi_nmap/insert-mode-at-first-non-space {
    742   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    743   ble/widget/vi-command/first-non-space
    744   [[ ${_ble_edit_str:_ble_edit_ind:1} == [$' \t'] ]] &&
    745     ((_ble_edit_ind++)) # 逆eol補正
    746   ble/widget/vi_nmap/.insert-mode "$ARG"
    747   ble/keymap:vi/repeat/record
    748   return 0
    749 }
    750 # nmap: gi
    751 function ble/widget/vi_nmap/insert-mode-at-previous-point {
    752   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    753   local ret
    754   ble/keymap:vi/mark/get-local-mark 94 && _ble_edit_ind=$ret
    755   ble/widget/vi_nmap/.insert-mode "$ARG"
    756   ble/keymap:vi/repeat/record
    757   return 0
    758 }
    759 function ble/widget/vi_nmap/replace-mode {
    760   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    761   ble/widget/vi_nmap/.insert-mode "$ARG" R
    762   ble/keymap:vi/repeat/record
    763   return 0
    764 }
    765 function ble/widget/vi_nmap/virtual-replace-mode {
    766   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    767   ble/widget/vi_nmap/.insert-mode "$ARG" 1
    768   ble/keymap:vi/repeat/record
    769   return 0
    770 }
    771 function ble/widget/vi_nmap/accept-line {
    772   ble/keymap:vi/clear-arg
    773   ble/widget/vi_nmap/.insert-mode
    774   ble/keymap:vi/repeat/clear-insert
    775   [[ $_ble_keymap_vi_reg_record ]] &&
    776     ble/widget/vi_nmap/record-register
    777   ble/widget/default/accept-line
    778 }
    779 function ble/widget/vi-command/edit-and-execute-command {
    780   ble/keymap:vi/clear-arg
    781   ble/widget/vi_nmap/.insert-mode
    782   ble/keymap:vi/repeat/clear-insert
    783   [[ $_ble_keymap_vi_reg_record ]] &&
    784     ble/widget/vi_nmap/record-register
    785   ble/widget/edit-and-execute-command
    786 }
    787 
    788 #------------------------------------------------------------------------------
    789 # args
    790 #
    791 # arg     : 0-9 d y c
    792 # command : dd yy cc [dyc]0 Y S
    793 
    794 _ble_keymap_vi_oparg=
    795 _ble_keymap_vi_opfunc=
    796 _ble_keymap_vi_reg=
    797 
    798 ble/array#push _ble_textarea_local_VARNAMES \
    799                _ble_keymap_vi_oparg \
    800                _ble_keymap_vi_opfunc \
    801                _ble_keymap_vi_reg
    802 
    803 # ble/keymap:vi における _ble_edit_kill_ring の扱いついて
    804 #
    805 # _ble_edit_kill_type=L のとき
    806 #   行指向の切り取り文字列であることを表し、
    807 #   _ble_edit_kill_ring の末端には必ず改行文字が来ると仮定して良い。
    808 #
    809 # _ble_edit_kill_type=B:* の形式をしているとき、
    810 #   矩形の切り取り文字列であることを表し、
    811 #   _ble_edit_kill_ring は改行区切りで各行を連結したものである。
    812 #   末端には改行文字は付加しない。末端に改行文字があるときは、
    813 #   それは付加された改行ではなく、最後に空行があることを意味する。
    814 #
    815 #   _ble_edit_kill_type の 2 文字目以降は数字を空白区切りで並べたもので、
    816 #   各数字は _ble_edit_kill_ring 内の各行に対応する。
    817 #   意味は、行の途中に挿入する際に矩形を保つために右に補填するべき空白の数である。
    818 #   行末に挿入する際にはこの空白の補填は起こらないことに注意する。
    819 #
    820 # _ble_edit_kill_type= (空文字列) もしくは それ意外の場合は
    821 #   通常の切り取り文字列であることを表す。
    822 #   _ble_edit_kill_ring は任意の文字列である。
    823 #
    824 _ble_keymap_vi_register=()
    825 _ble_keymap_vi_register_onplay=
    826 
    827 ## @fn ble/keymap:vi/clear-arg
    828 function ble/keymap:vi/clear-arg {
    829   _ble_edit_arg=
    830   _ble_keymap_vi_oparg=
    831   _ble_keymap_vi_opfunc=
    832   _ble_keymap_vi_reg=
    833 }
    834 ## @fn ble/keymap:vi/get-arg [default_value]; ARG FLAG REG
    835 ##
    836 ## 引数の内容について
    837 ##   vi_nmap, vi_xmap, vi_smap においては FLAG は空であると仮定して良い。
    838 ##   vi_omap においては FLAG は非空である。
    839 ##   get-arg{,-reg} を呼び出すことによって空になる。
    840 ##   つまり vi_omap においてこの関数を呼び出したとき、vi_omap から vi_nmap に戻る必要がある。
    841 ##   これは通例 ble/keymap:vi/adjust-command-mode によって実施される。
    842 ##
    843 function ble/keymap:vi/get-arg {
    844   local default_value=$1
    845   REG=$_ble_keymap_vi_reg
    846   FLAG=$_ble_keymap_vi_opfunc
    847   if [[ ! $_ble_edit_arg && ! $_ble_keymap_vi_oparg ]]; then
    848     ARG=$default_value
    849   else
    850     ARG=$((10#0${_ble_edit_arg:-1}*10#0${_ble_keymap_vi_oparg:-1}))
    851   fi
    852   ble/keymap:vi/clear-arg
    853 }
    854 ## @fn ble/keymap:vi/register#load reg
    855 function ble/keymap:vi/register#load {
    856   local reg=$1
    857   if [[ $reg ]] && ((reg!=34)); then
    858     if [[ $reg == 37 ]]; then # "%
    859       ble-edit/content/push-kill-ring "$HISTFILE" ''
    860       return 0
    861     fi
    862 
    863     local value=${_ble_keymap_vi_register[reg]}
    864     if [[ $value == */* ]]; then
    865       ble-edit/content/push-kill-ring "${value#*/}" "${value%%/*}"
    866       return 0
    867     else
    868       ble-edit/content/push-kill-ring
    869       return 1
    870     fi
    871   fi
    872 }
    873 ## @fn ble/keymap:vi/register#set reg type content
    874 function ble/keymap:vi/register#set {
    875   local reg=$1 type=$2 content=$3
    876 
    877   # type = L は行指向の値
    878   # type = B は矩形指向の値
    879   # type = '' は文字指向の値
    880   # type = q はキーボードマクロ
    881   #
    882   # Note: 実際に記録される type は以下の何れかである。
    883   #  type = L
    884   #  type = B:*
    885   #  type = ''
    886 
    887   # 追記の場合
    888   if [[ $reg == +* ]]; then
    889     local value=${_ble_keymap_vi_register[reg]}
    890     if [[ $value == */* ]]; then
    891       local otype=${value%%/*}
    892       local oring=${value#*/}
    893 
    894       if [[ $otype == L ]]; then
    895         if [[ $type == q ]]; then
    896           type=L content=${oring%$'\n'}$content # V + * → V
    897         else
    898           type=L content=$oring$content # V + * → V
    899         fi
    900       elif [[ $type == L ]]; then
    901         type=L content=$oring$'\n'$content # C-v + V, v + V → V
    902       elif [[ $otype == B:* ]]; then
    903         if [[ $type == B:* ]]; then
    904           type=$otype' '${type#B:}
    905           content=$oring$'\n'$content # C-v + C-v → C-v
    906         elif [[ $type == q ]]; then
    907           local ret; ble/string#count-char "$content" $'\n'
    908           ble/string#repeat ' 0' "$ret"
    909           type=$otype$ret
    910           content=$oring$$content # C-v + q → C-v
    911         else
    912           local ret; ble/string#count-char "$content" $'\n'
    913           ble/string#repeat ' 0' "$((ret+1))"
    914           type=$otype$ret
    915           content=$oring$'\n'$content # C-v + v → C-v
    916         fi
    917       else
    918         type= content=$oring$content # v + C-v, v + v, v + q → v
    919       fi
    920     fi
    921   fi
    922 
    923   [[ $type == L && $content != *$'\n' ]] && content=$content$'\n'
    924 
    925   local suppress_default=
    926   [[ $type == q ]] && type= suppress_default=1
    927 
    928   if [[ ! $reg ]] || ((reg==34)); then # ""
    929     # unnamed register
    930     ble-edit/content/push-kill-ring "$content" "$type"
    931     return 0
    932   elif ((reg==58||reg==46||reg==37||reg==126)); then # ": ". "% "~
    933     # read only register
    934     ble/widget/.bell "attempted to write on a read-only register #$reg"
    935     return 1
    936   elif ((reg==95)); then # "_
    937     # black hole register
    938     return 0
    939   else
    940     if [[ ! $suppress_default ]]; then
    941       ble-edit/content/push-kill-ring "$content" "$type"
    942     fi
    943     _ble_keymap_vi_register[reg]=$type/$content
    944     return 0
    945   fi
    946 }
    947 
    948 ## @fn ble/keymap:vi/register#set-yank reg type content
    949 ##   レジスタ "0 に文字列を登録します。
    950 ##
    951 function ble/keymap:vi/register#set-yank {
    952   ble/keymap:vi/register#set "$@" || return 1
    953   local reg=$1 type=$2 content=$3
    954   if [[ $reg == '' || $reg == 34 ]]; then
    955     ble/keymap:vi/register#set 48 "$type" "$content" # "0
    956   fi
    957 }
    958 ## @fn ble/keymap:vi/register#set-edit reg type content
    959 ##   レジスタ "1 に文字列を登録します。
    960 ##
    961 ##   content に改行が含まれる場合、または、特定の WIDGET の時、
    962 ##   元々レジスタ "1 - "8 にあった内容をレジスタ "2 - "9 に移動し、
    963 ##   新しい文字列をレジスタ "1 に登録します。
    964 ##   それ以外の時、新しい文字列はレジスタ "- に登録します。
    965 ##
    966 _ble_keymap_vi_register_49_widget_list=(
    967   # %
    968   ble/widget/vi-command/search-matchpair-or
    969   ble/widget/vi-command/percentage-line
    970 
    971   # `
    972   ble/widget/vi-command/goto-mark
    973 
    974   # / ? n N
    975   ble/widget/vi-command/search-forward
    976   ble/widget/vi-command/search-backward
    977   ble/widget/vi-command/search-repeat
    978   ble/widget/vi-command/search-reverse-repeat
    979 
    980   # ( ) { }
    981   # ToDo
    982 )
    983 function ble/keymap:vi/register#set-edit {
    984   ble/keymap:vi/register#set "$@" || return 1
    985   local reg=$1 type=$2 content=$3
    986   if [[ $reg == '' || $reg == 34 ]]; then
    987     local IFS=$_ble_term_IFS
    988     local widget=${WIDGET%%["$_ble_term_IFS"]*}
    989     if [[ $content == *$'\n'* || " $widget " == " ${_ble_keymap_vi_register_49_widget_list[*]} " ]]; then
    990       local n
    991       for ((n=9;n>=2;n--)); do
    992         _ble_keymap_vi_register[48+n]=${_ble_keymap_vi_register[48+n-1]}
    993       done
    994       ble/keymap:vi/register#set 49 "$type" "$content" # "1
    995     else
    996       ble/keymap:vi/register#set 45 "$type" "$content" # "-
    997     fi
    998   fi
    999 }
   1000 
   1001 function ble/keymap:vi/register#play {
   1002   local reg=$1 value
   1003   if [[ $reg ]] && ((reg!=34)); then
   1004     value=${_ble_keymap_vi_register[reg]}
   1005     if [[ $value == */* ]]; then
   1006       value=${value#*/}
   1007     else
   1008       value=
   1009       return 1
   1010     fi
   1011   else
   1012     value=$_ble_edit_kill_ring
   1013   fi
   1014 
   1015   local _ble_keymap_vi_register_onplay=1
   1016   local ret; ble/decode/charlog#decode "$value"
   1017   ble/widget/.MACRO "${ret[@]}"
   1018   return 0
   1019 }
   1020 ## @fn ble/keymap:vi/register#dump/escape text
   1021 ##   @var[out] ret
   1022 function ble/keymap:vi/register#dump/escape {
   1023   local text=$1
   1024   local out= i=0 iN=${#text}
   1025   while ((i<iN)); do
   1026     local tail=${text:i}
   1027     if ble/util/isprint+ "$tail"; then
   1028       out=$out$BASH_REMATCH
   1029       ((i+=${#BASH_REMATCH}))
   1030     else
   1031       ble/util/s2c "$tail"
   1032       local code=$ret
   1033       if ((code<32)); then
   1034         ble/util/c2s "$((code+64))"
   1035         out=$out$_ble_term_rev^$ret$_ble_term_sgr0
   1036       elif ((code==127)); then
   1037         out=$out$_ble_term_rev^?$_ble_term_sgr0
   1038       elif ((128<=code&&code<160)); then
   1039         ble/util/c2s "$((code-64))"
   1040         out=$out${_ble_term_rev}M-^$ret$_ble_term_sgr0
   1041       else
   1042         out=$out${tail::1}
   1043       fi
   1044       ((i++))
   1045     fi
   1046   done
   1047   ret=$out
   1048 }
   1049 function ble/keymap:vi/register#dump {
   1050   local k ret out=
   1051   local value type content
   1052   for k in 34 "${!_ble_keymap_vi_register[@]}"; do
   1053     if ((k==34)); then
   1054       type=$_ble_edit_kill_type
   1055       content=$_ble_edit_kill_ring
   1056     else
   1057       value=${_ble_keymap_vi_register[k]}
   1058       type=${value%%/*} content=${value#*/}
   1059     fi
   1060 
   1061     ble/util/c2s "$k"; k=$ret
   1062     case $type in
   1063     (L)   type=line ;;
   1064     (B:*) type=block ;;
   1065     (*)   type=char ;;
   1066     esac
   1067     ble/keymap:vi/register#dump/escape "$content"; content=$ret
   1068 
   1069     out=$out'"'$k' ('$type') '$content$'\n'
   1070   done
   1071   ble/edit/info/show ansi "$out"
   1072   return 0
   1073 }
   1074 function ble/widget/vi-command:reg { ble/keymap:vi/register#dump; }
   1075 function ble/widget/vi-command:regi { ble/keymap:vi/register#dump; }
   1076 function ble/widget/vi-command:regis { ble/keymap:vi/register#dump; }
   1077 function ble/widget/vi-command:regist { ble/keymap:vi/register#dump; }
   1078 function ble/widget/vi-command:registe { ble/keymap:vi/register#dump; }
   1079 function ble/widget/vi-command:register { ble/keymap:vi/register#dump; }
   1080 function ble/widget/vi-command:registers { ble/keymap:vi/register#dump; }
   1081 
   1082 function ble/widget/vi-command/append-arg {
   1083   local ret ch=$1
   1084   if [[ ! $ch ]]; then
   1085     local n=${#KEYS[@]}
   1086     local code=$((KEYS[n?n-1:0]&_ble_decode_MaskChar))
   1087     ((code==0)) && return 1
   1088     ble/util/c2s "$code"; ch=$ret
   1089   fi
   1090   ble/util/assert '[[ ! ${ch//[0-9]} ]]'
   1091 
   1092   # 0
   1093   if [[ $ch == 0 && ! $_ble_edit_arg ]]; then
   1094     ble/widget/vi-command/beginning-of-line
   1095     return "$?"
   1096   fi
   1097 
   1098   _ble_edit_arg="$_ble_edit_arg$ch"
   1099   return 0
   1100 }
   1101 function ble/widget/vi-command/register {
   1102   _ble_decode_key__hook="ble/widget/vi-command/register.hook"
   1103 }
   1104 function ble/widget/vi-command/register.hook {
   1105   local key=$1
   1106   ble/keymap:vi/clear-arg
   1107   local ret
   1108   if ble/keymap:vi/k2c "$key" && local c=$ret; then
   1109     if ((65<=c&&c<91)); then # A-Z
   1110       _ble_keymap_vi_reg=+$((c+32))
   1111       return 0
   1112     elif ((97<=c&&c<123||48<=c&&c<58||c==45||c==58||c==46||c==37||c==35||c==61||c==42||c==43||c==126||c==95||c==47)); then # a-z 0-9 - : . % # = * + ~ _ /
   1113       _ble_keymap_vi_reg=$c
   1114       return 0
   1115     elif ((c==34)); then # ""
   1116       # Note: vim の内部的には "" を指定するのと何も指定しないのは区別される。
   1117       # 例えば diw"y. は "y に記録されるが ""diw"y. は "" に記録される。
   1118       _ble_keymap_vi_reg=$c
   1119       return 0
   1120     fi
   1121   fi
   1122   ble/widget/vi-command/bell
   1123   return 1
   1124 }
   1125 
   1126 _ble_keymap_vi_reg_record=
   1127 _ble_keymap_vi_reg_record_char=
   1128 _ble_keymap_vi_reg_record_play=0
   1129 ble/array#push _ble_textarea_local_VARNAMES \
   1130                _ble_keymap_vi_reg_record \
   1131                _ble_keymap_vi_reg_record_char \
   1132                _ble_keymap_vi_reg_record_play
   1133 
   1134 # nmap q
   1135 function ble/widget/vi_nmap/record-register {
   1136   # レジスタに含まれる q は再生中には何も起こさない
   1137   if [[ $_ble_keymap_vi_register_onplay ]]; then
   1138     ble/keymap:vi/clear-arg
   1139     ble/keymap:vi/adjust-command-mode
   1140     return 0
   1141   fi
   1142 
   1143   if [[ $_ble_keymap_vi_reg_record ]]; then
   1144     ble/keymap:vi/clear-arg
   1145     local -a ret
   1146     ble/decode/charlog#end-exclusive-depth1
   1147     ble/decode/charlog#encode "${ret[@]}"
   1148     ble/keymap:vi/register#set "$_ble_keymap_vi_reg_record" q "$ret"
   1149 
   1150     _ble_keymap_vi_reg_record=
   1151     ble/keymap:vi/update-mode-indicator
   1152   else
   1153     _ble_decode_key__hook="ble/widget/vi_nmap/record-register.hook"
   1154   fi
   1155 }
   1156 function ble/widget/vi_nmap/record-register.hook {
   1157   local key=$1 ret
   1158   ble/keymap:vi/clear-arg
   1159 
   1160   # check register
   1161   local reg= c=
   1162   if ble/keymap:vi/k2c "$key" && c=$ret; then
   1163     if ((65<=c&&c<91)); then # q{A-Z}
   1164       reg=+$((c+32))
   1165     elif ((48<=c&&c<58||97<=c&&c<123)); then # q{0-9a-z}
   1166       reg=$c
   1167     elif ((c==34)); then # q"
   1168       reg=$c
   1169     fi
   1170   fi
   1171   if [[ ! $reg ]]; then
   1172     ble/widget/vi-command/bell "invalid register key=$key"
   1173     return 1
   1174   fi
   1175 
   1176   # start logging
   1177   if ! ble/decode/charlog#start vi-macro; then
   1178     ble/widget/.bell 'vi-macro: the logging system is currently busy'
   1179     return 1
   1180   fi
   1181 
   1182   # update status
   1183   ble/util/c2s "$c"
   1184   _ble_keymap_vi_reg_record=$reg
   1185   _ble_keymap_vi_reg_record_char=$ret
   1186   ble/keymap:vi/update-mode-indicator
   1187   return 0
   1188 }
   1189 # nmap @
   1190 function ble/widget/vi_nmap/play-register {
   1191   _ble_decode_key__hook="ble/widget/vi_nmap/play-register.hook"
   1192 }
   1193 function ble/widget/vi_nmap/play-register.hook {
   1194   ble/keymap:vi/clear-arg
   1195 
   1196   local depth=$_ble_keymap_vi_reg_record_play
   1197   if ((depth>=bleopt_keymap_vi_macro_depth)) || ble/util/is-stdin-ready; then
   1198     return 1 # 無限ループを防ぐため
   1199   fi
   1200 
   1201   local _ble_keymap_vi_reg_record_play=$((depth+1))
   1202   local key=$1
   1203   local ret
   1204   if ble/keymap:vi/k2c "$key" && local c=$ret; then
   1205     ((65<=c&&c<91)) && ((c+=32)) # A-Z -> a-z
   1206     if ((48<=c&&c<58||97<=c&&c<123)); then # 0-9a-z
   1207       ble/keymap:vi/register#play "$c" && return 0
   1208     fi
   1209   fi
   1210   ble/widget/vi-command/bell
   1211   return 1
   1212 }
   1213 
   1214 function ble/widget/vi-command/operator {
   1215   local ret opname=$1
   1216 
   1217   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   1218     local ARG FLAG REG; ble/keymap:vi/get-arg ''
   1219     # ※FLAG はユーザにより設定されているかもしれないが無視
   1220 
   1221     local a=$_ble_edit_ind b=$_ble_edit_mark
   1222     ((a<=b||(a=_ble_edit_mark,b=_ble_edit_ind)))
   1223 
   1224     ble/widget/vi_xmap/.save-visual-state
   1225     local ble_keymap_vi_mark_active=$_ble_edit_mark_active # used in call-operator-blockwise
   1226     local mark_type=${_ble_edit_mark_active%+}
   1227     ble/widget/vi_xmap/exit
   1228 
   1229     local ble_keymap_vi_opmode=$mark_type
   1230     if [[ $mark_type == vi_line ]]; then
   1231       ble/keymap:vi/call-operator-linewise "$opname" "$a" "$b" "$ARG" "$REG"
   1232     elif [[ $mark_type == vi_block ]]; then
   1233       ble/keymap:vi/call-operator-blockwise "$opname" "$a" "$b" "$ARG" "$REG"
   1234     else
   1235       local end=$b
   1236       ((end<${#_ble_edit_str}&&end++))
   1237       ble/keymap:vi/call-operator-charwise "$opname" "$a" "$end" "$ARG" "$REG"
   1238     fi; local ext=$?
   1239     ((ext==147)) && return 147
   1240     ((ext)) && ble/widget/.bell
   1241     ble/keymap:vi/adjust-command-mode
   1242     return "$ext"
   1243   elif [[ $_ble_decode_keymap == vi_nmap ]]; then
   1244     ble/decode/keymap/push vi_omap
   1245     _ble_keymap_vi_oparg=$_ble_edit_arg
   1246     _ble_keymap_vi_opfunc=$opname
   1247     _ble_edit_arg=
   1248     ble/keymap:vi/update-mode-indicator
   1249 
   1250   elif [[ $_ble_decode_keymap == vi_omap ]]; then
   1251     local opname1=${_ble_keymap_vi_opfunc%%:*}
   1252     if [[ $opname == "$opname1" ]]; then
   1253       # 2つの同じオペレータ (yy, dd, cc, etc.) = 行指向の処理
   1254       ble/widget/vi_nmap/linewise-operator "$_ble_keymap_vi_opfunc"
   1255     else
   1256       ble/keymap:vi/clear-arg
   1257       ble/widget/vi-command/bell
   1258       return 1
   1259     fi
   1260   fi
   1261   return 0
   1262 }
   1263 
   1264 function ble/widget/vi_nmap/linewise-operator {
   1265   local opname=${1%%:*} opflags=${1#*:}
   1266   local ARG FLAG REG; ble/keymap:vi/get-arg 1 # _ble_edit_arg is consumed here
   1267   if ((ARG==1)) || [[ ${_ble_edit_str:_ble_edit_ind} == *$'\n'* ]]; then
   1268     if [[ :$opflags: == *:vi_char:* || :$opflags: == *:vi_block:* ]]; then
   1269       local beg=$_ble_edit_ind
   1270       local ret; ble-edit/content/find-logical-bol "$beg" "$((ARG-1))"; local end=$ret
   1271       ((beg<=end)) || local beg=$end end=$beg
   1272       if [[ :$opflags: == *:vi_block:* ]]; then
   1273         ble/keymap:vi/call-operator-blockwise "$opname" "$beg" "$end" '' "$REG"
   1274       else
   1275         ble/keymap:vi/call-operator-charwise "$opname" "$beg" "$end" '' "$REG"
   1276       fi
   1277     else
   1278       ble/keymap:vi/call-operator-linewise "$opname" "$_ble_edit_ind" "$_ble_edit_ind:$((ARG-1))" '' "$REG"; local ext=$?
   1279     fi
   1280     if ((ext==0)); then
   1281       ble/keymap:vi/adjust-command-mode
   1282       return 0
   1283     elif ((ext==147)); then
   1284       return 147
   1285     fi
   1286   fi
   1287   ble/widget/vi-command/bell
   1288   return 1
   1289 }
   1290 # nmap Y
   1291 function ble/widget/vi_nmap/copy-current-line {
   1292   ble/widget/vi_nmap/linewise-operator y
   1293 }
   1294 function ble/widget/vi_nmap/kill-current-line {
   1295   ble/widget/vi_nmap/linewise-operator d
   1296 }
   1297 # nmap S
   1298 function ble/widget/vi_nmap/kill-current-line-and-insert {
   1299   ble/widget/vi_nmap/linewise-operator c
   1300 }
   1301 
   1302 # nmap: 0, <home>
   1303 function ble/widget/vi-command/beginning-of-line {
   1304   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   1305   local ret; ble-edit/content/find-logical-bol; local beg=$ret
   1306   ble/widget/vi-command/exclusive-goto.impl "$beg" "$FLAG" "$REG" nobell
   1307 }
   1308 
   1309 #------------------------------------------------------------------------------
   1310 # Operators
   1311 
   1312 ## オペレータは以下の形式の関数として定義される。
   1313 ##
   1314 ## @fn ble/keymap:vi/operator:名称 a b context [count [reg]]
   1315 ##
   1316 ##   @param[in] a b
   1317 ##     範囲の開始点と終了点。終了点は開始点以降にあることが保証される。
   1318 ##     context が 'line' のとき、それぞれ行頭・行末にあることが保証される。
   1319 ##     ただし、行末に改行があるときは b は次の行頭を指す。
   1320 ##
   1321 ##   @param[in] context
   1322 ##     範囲の種類を表す文字列。char, line, block の何れか。
   1323 ##
   1324 ##   @param[in] count
   1325 ##     オペレータの操作に対する引数。
   1326 ##     これはビジュアルモードで指定される。
   1327 ##
   1328 ##   @var[in,out] beg end
   1329 ##     範囲の開始点と終了点。a b と同一の値。
   1330 ##     行指向オペレータのとき範囲が拡大されることがある。
   1331 ##     その時 beg に拡大後の開始点を返す。
   1332 ##
   1333 ##   @var[out] ble_keymap_vi_operator_index
   1334 ##     オペレータ作用後のカーソル位置を明示するとき、
   1335 ##     オペレータ内部でこの変数に値を設定する。
   1336 ##
   1337 ##   @exit
   1338 ##     operator 関数が終了ステータス 147 を返したとき、
   1339 ##     operator が非同期に入力を読み取ることを表す。
   1340 ##     147 を返した operator は、実際に操作が完了した時に:
   1341 ##
   1342 ##     1 ble/keymap:vi/mark/end-edit-area を呼び出す必要がある。
   1343 ##     2 適切な位置にカーソルを移動する必要がある。
   1344 ##
   1345 ##
   1346 ## オペレータは現在以下の4箇所で呼び出されている。
   1347 ##
   1348 ## - ble/widget/vi-command/linewise-range.impl
   1349 ## - ble/keymap:vi/call-operator
   1350 ## - ble/keymap:vi/call-operator-charwise
   1351 ## - ble/keymap:vi/call-operator-linewise
   1352 ## - ble/keymap:vi/call-operator-blockwise
   1353 
   1354 
   1355 ## @fn ble/keymap:vi/call-operator op beg end type arg reg
   1356 ## @fn ble/keymap:vi/call-operator-charwise op beg end arg reg
   1357 ## @fn ble/keymap:vi/call-operator-linewise op beg end arg reg
   1358 ## @fn ble/keymap:vi/call-operator-blockwise op beg end arg reg
   1359 ##
   1360 ##   @var[in] ble_keymap_vi_mark_active
   1361 ##     オペレータ作用前の $_ble_edit_mark_active を指定する。
   1362 ##     call-operator-blockwise での矩形領域を決定するのに用いる。
   1363 ##     演算子の呼び出し時には既に $_ble_edit_mark_active は
   1364 ##     作用後の値に変わっていることに注意する。
   1365 ##
   1366 function ble/keymap:vi/call-operator {
   1367   ble/keymap:vi/mark/start-edit-area
   1368   local _ble_keymap_vi_mark_suppress_edit=1
   1369   ble/keymap:vi/operator:"$@"; local ext=$?
   1370   ble/util/unlocal _ble_keymap_vi_mark_suppress_edit
   1371   ble/keymap:vi/mark/end-edit-area
   1372   if ((ext==0)); then
   1373     if ble/is-function ble/keymap:vi/operator:"$1".record; then
   1374       ble/keymap:vi/operator:"$1".record
   1375     else
   1376       ble/keymap:vi/repeat/record
   1377     fi
   1378   fi
   1379   return "$ext"
   1380 }
   1381 function ble/keymap:vi/call-operator-charwise {
   1382   local ch=$1 beg=$2 end=$3 arg=$4 reg=$5
   1383   ((beg<=end||(beg=$3,end=$2)))
   1384   if ble/is-function ble/keymap:vi/operator:"$ch"; then
   1385     local ble_keymap_vi_operator_index=
   1386     ble/keymap:vi/call-operator "$ch" "$beg" "$end" char "$arg" "$reg"; local ext=$?
   1387     ((ext==147)) && return 147
   1388 
   1389     local index=${ble_keymap_vi_operator_index:-$beg}
   1390     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   1391     _ble_edit_ind=$index
   1392     return 0
   1393   else
   1394     return 1
   1395   fi
   1396 }
   1397 function ble/keymap:vi/call-operator-linewise {
   1398   local ch=$1 a=$2 b=$3 arg=$4 reg=$5 ia=0 ib=0
   1399   [[ $a == *:* ]] && local a=${a%%:*} ia=${a#*:}
   1400   [[ $b == *:* ]] && local b=${b%%:*} ib=${b#*:}
   1401   local ret
   1402   ble-edit/content/find-logical-bol "$a" "$ia"; local beg=$ret
   1403   ble-edit/content/find-logical-eol "$b" "$ib"; local end=$ret
   1404 
   1405   if ble/is-function ble/keymap:vi/operator:"$ch"; then
   1406     local ble_keymap_vi_operator_index=
   1407     ((end<${#_ble_edit_str}&&end++))
   1408     ble/keymap:vi/call-operator "$ch" "$beg" "$end" line "$arg" "$reg"; local ext=$?
   1409     ((ext==147)) && return 147
   1410 
   1411     # index
   1412     if [[ $ble_keymap_vi_operator_index ]]; then
   1413       local index=$ble_keymap_vi_operator_index
   1414     else
   1415       ble-edit/content/find-logical-bol "$beg"; beg=$ret # operator 中で beg が変更されているかも
   1416       ble-edit/content/find-non-space "$beg"; local index=$ret
   1417     fi
   1418     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   1419     _ble_edit_ind=$index
   1420     return 0
   1421   else
   1422     return 1
   1423   fi
   1424 }
   1425 function ble/keymap:vi/call-operator-blockwise {
   1426   local ch=$1 beg=$2 end=$3 arg=$4 reg=$5
   1427   if ble/is-function ble/keymap:vi/operator:"$ch"; then
   1428     local mark_active=${ble_keymap_vi_mark_active:-vi_block}
   1429     local sub_ranges sub_x1 sub_x2
   1430     _ble_edit_mark_active=$mark_active ble/keymap:vi/extract-block "$beg" "$end"
   1431     local nrange=${#sub_ranges[@]}
   1432     ((nrange)) || return 1
   1433 
   1434     local ble_keymap_vi_operator_index=
   1435     local beg=${sub_ranges[0]}; beg=${beg%%:*}
   1436     local end=${sub_ranges[nrange-1]}; end=${end#*:}; end=${end%%:*}
   1437     ble/keymap:vi/call-operator "$ch" "$beg" "$end" block "$arg" "$reg"
   1438     ((ext==147)) && return 147
   1439 
   1440     local index=${ble_keymap_vi_operator_index:-$beg}
   1441     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   1442     _ble_edit_ind=$index
   1443     return 0
   1444   else
   1445     return 1
   1446   fi
   1447 }
   1448 
   1449 
   1450 function ble/keymap:vi/operator:d {
   1451   local context=$3 arg=$4 reg=$5 # beg end は上書きする
   1452   if [[ $context == line ]]; then
   1453     ble/keymap:vi/register#set-edit "$reg" L "${_ble_edit_str:beg:end-beg}" || return 1
   1454 
   1455     # 最後の行が削除される時は前の行の非空白行頭まで後退
   1456     if ((end==${#_ble_edit_str}&&beg>0)); then
   1457       # fix start position
   1458       local ret
   1459       ((beg--))
   1460       ble-edit/content/find-logical-bol "$beg"
   1461       ble-edit/content/find-non-space "$ret"
   1462       ble_keymap_vi_operator_index=$ret
   1463     fi
   1464 
   1465     ble/widget/.delete-range "$beg" "$end"
   1466   elif [[ $context == block ]]; then
   1467     local -a afill=() atext=() arep=()
   1468     local sub shift=0 slpad0=
   1469     local smin smax slpad srpad sfill stext
   1470     for sub in "${sub_ranges[@]}"; do
   1471       stext=${sub#*:*:*:*:*:}
   1472       ble/string#split sub : "$sub"
   1473       smin=${sub[0]} smax=${sub[1]}
   1474       slpad=${sub[2]} srpad=${sub[3]}
   1475       sfill=${sub[4]}
   1476 
   1477       [[ $slpad0 ]] || slpad0=$slpad # 最初の slpad
   1478 
   1479       ble/array#push afill "$sfill"
   1480       ble/array#push atext "$stext"
   1481       local ret; ble/string#repeat ' ' "$((slpad+srpad))"
   1482       ble/array#push arep "$((smin+shift)):$((smax+shift)):$ret"
   1483       ((shift+=(slpad+srpad)-(smax-smin)))
   1484     done
   1485 
   1486     # yank
   1487     IFS=$'\n' builtin eval 'local yank_content="${atext[*]-}"'
   1488     local IFS=$_ble_term_IFS
   1489     local yank_type=B:"${afill[*]-}"
   1490     ble/keymap:vi/register#set-edit "$reg" "$yank_type" "$yank_content" || return 1
   1491 
   1492     # delete
   1493     local rep
   1494     for rep in "${arep[@]}"; do
   1495       smin=${rep%%:*}; rep=${rep:${#smin}+1}
   1496       smax=${rep%%:*}; rep=${rep:${#smax}+1}
   1497       ble/widget/.replace-range "$smin" "$smax" "$rep"
   1498     done
   1499     ((beg+=slpad)) # fix start position
   1500   else
   1501     if ((beg<end)); then
   1502 
   1503       if [[ $ble_keymap_vi_opmode != vi_char && ${_ble_edit_str:beg:end-beg} == *$'\n'* ]]; then
   1504         # d の例外動作: 文字単位で開始点と終了点が異なる行で、
   1505         #   開始点より前・終了点より後に空白しかない時、行指向で処理する。
   1506         if local rex=$'(^|\n)([ \t]*)$'; [[ ${_ble_edit_str::beg} =~ $rex ]]; then
   1507           local prefix=${BASH_REMATCH[2]}
   1508           if rex=$'^[ \t]*(\n|$)'; [[ ${_ble_edit_str:end} =~ $rex ]]; then
   1509             local suffix=$BASH_REMATCH
   1510             ((beg-=${#prefix},end+=${#suffix}))
   1511             ble/keymap:vi/operator:d "$beg" "$end" line "$arg" "$reg"
   1512             return "$?"
   1513           fi
   1514         fi
   1515       fi
   1516 
   1517       ble/keymap:vi/register#set-edit "$reg" '' "${_ble_edit_str:beg:end-beg}" || return 1
   1518       ble/widget/.delete-range "$beg" "$end"
   1519     fi
   1520   fi
   1521   return 0
   1522 }
   1523 function ble/keymap:vi/operator:c {
   1524   local context=$3 arg=$4 reg=$5 # beg は上書き対象
   1525   if [[ $context == line ]]; then
   1526     ble/keymap:vi/register#set-edit "$reg" L "${_ble_edit_str:beg:end-beg}" || return 1
   1527 
   1528     local end2=$end
   1529     ((end2)) && [[ ${_ble_edit_str:end2-1:1} == $'\n' ]] && ((end2--))
   1530 
   1531     local indent= ret
   1532     ble-edit/content/find-non-space "$beg"; local nol=$ret
   1533     ((beg<nol)) && indent=${_ble_edit_str:beg:nol-beg}
   1534 
   1535     ble/widget/.replace-range "$beg" "$end2" "$indent"
   1536     ble/widget/vi_nmap/.insert-mode
   1537   elif [[ $context == block ]]; then
   1538     ble/keymap:vi/operator:d "$@" || return 1 # @var beg will be overwritten here
   1539 
   1540     # operator:d によってずれた矩形領域を修正する。
   1541     # 一から計算し直すのは面倒なので sub_ranges を直接弄る。
   1542     # 実のところ block-insert-mode insert は sub_ranges[0] の smin と sub_x1
   1543     # しか参照しないので、sub_ranges[0] だけ修正すれば良い。
   1544     local sub=${sub_ranges[0]}
   1545     local smin=${sub%%:*} sub=${sub#*:}
   1546     local smax=${sub%%:*} sub=${sub#*:}
   1547     local slpad=${sub%%:*} sub=${sub#*:}
   1548     ((smin+=slpad,smax=smin,slpad=0))
   1549     sub_ranges[0]=$smin:$smax:$slpad:$sub
   1550 
   1551     ble/widget/vi_xmap/block-insert-mode.impl insert
   1552   else
   1553     local ble_keymap_vi_opmode=vi_char
   1554     ble/keymap:vi/operator:d "$@" || return 1
   1555     ble/widget/vi_nmap/.insert-mode
   1556   fi
   1557   return 0
   1558 }
   1559 function ble/keymap:vi/operator:y.record { :; }
   1560 function ble/keymap:vi/operator:y {
   1561   local beg=$1 end=$2 context=$3 arg=$4 reg=$5
   1562   local yank_type= yank_content=
   1563   if [[ $context == line ]]; then
   1564     ble_keymap_vi_operator_index=$_ble_edit_ind # operator:y では現在位置を動かさない
   1565     yank_type=L
   1566     yank_content=${_ble_edit_str:beg:end-beg}
   1567   elif [[ $context == block ]]; then
   1568     local sub
   1569     local -a afill=() atext=()
   1570     for sub in "${sub_ranges[@]}"; do
   1571       local sub4=${sub#*:*:*:*:}
   1572       local sfill=${sub4%%:*} stext=${sub4#*:}
   1573       ble/array#push afill "$sfill"
   1574       ble/array#push atext "$stext"
   1575     done
   1576 
   1577     IFS=$'\n' builtin eval 'local yank_content="${atext[*]-}"'
   1578     local IFS=$_ble_term_IFS
   1579     yank_type=B:"${afill[*]-}"
   1580   else
   1581     yank_type=
   1582     yank_content=${_ble_edit_str:beg:end-beg}
   1583   fi
   1584 
   1585   ble/keymap:vi/register#set-yank "$reg" "$yank_type" "$yank_content" || return 1
   1586   ble/keymap:vi/mark/commit-edit-area "$beg" "$end"
   1587   return 0
   1588 }
   1589 function ble/keymap:vi/operator:tr.impl {
   1590   local beg=$1 end=$2 context=$3 filter=$4
   1591   if [[ $context == block ]]; then
   1592     local isub=${#sub_ranges[@]}
   1593     while ((isub--)); do
   1594       ble/string#split sub : "${sub_ranges[isub]}"
   1595       local smin=${sub[0]} smax=${sub[1]}
   1596       local ret; "$filter" "${_ble_edit_str:smin:smax-smin}"
   1597       ble/widget/.replace-range "$smin" "$smax" "$ret"
   1598     done
   1599   else
   1600     local ret; "$filter" "${_ble_edit_str:beg:end-beg}"
   1601     ble/widget/.replace-range "$beg" "$end" "$ret"
   1602   fi
   1603   return 0
   1604 }
   1605 function ble/keymap:vi/operator:u {
   1606   ble/keymap:vi/operator:tr.impl "$1" "$2" "$3" ble/string#tolower
   1607 }
   1608 function ble/keymap:vi/operator:U {
   1609   ble/keymap:vi/operator:tr.impl "$1" "$2" "$3" ble/string#toupper
   1610 }
   1611 function ble/keymap:vi/operator:toggle_case {
   1612   ble/keymap:vi/operator:tr.impl "$1" "$2" "$3" ble/string#toggle-case
   1613 }
   1614 function ble/keymap:vi/operator:rot13 {
   1615   ble/keymap:vi/operator:tr.impl "$1" "$2" "$3" ble/keymap:vi/string#encode-rot13
   1616 }
   1617 
   1618 ## @fn ble/keymap:vi/expand-range-for-linewise-operator
   1619 ##   @var[in,out] beg, end
   1620 function ble/keymap:vi/expand-range-for-linewise-operator {
   1621   local ret
   1622 
   1623   # 行頭補正:
   1624   ble-edit/content/find-logical-bol "$beg"; beg=$ret
   1625 
   1626   # 行末補正:
   1627   #   行前進時は非空白行頭以前に end がある場合はその行は無視
   1628   #   行後退時は行頭に end (_ble_edit_ind) がある場合はその行は無視
   1629   #   同一行内の移動の場合は無条件にその行は含まれる。
   1630   ble-edit/content/find-logical-bol "$end"; local bol2=$ret
   1631   ble-edit/content/find-non-space "$bol2"; local nol2=$ret
   1632   if ((beg<bol2&&_ble_edit_ind<=bol2&&end<=nol2)); then
   1633     end=$bol2
   1634   else
   1635     ble-edit/content/find-logical-eol "$end"; end=$ret
   1636     [[ ${_ble_edit_str:end:1} == $'\n' ]] && ((end++))
   1637   fi
   1638 }
   1639 
   1640 #--------------------------------------
   1641 # Indent operators < >
   1642 
   1643 ## @fn ble/keymap:vi/string#increase-indent
   1644 ##   @var[out] ret
   1645 function ble/keymap:vi/string#increase-indent {
   1646   local text=$1 delta=$2
   1647   local space=$' \t' it=${bleopt_tab_width:-$_ble_term_it}
   1648   local arr; ble/string#split-lines arr "$text"
   1649   local -a arr2=()
   1650   local line indent i len x r
   1651   for line in "${arr[@]}"; do
   1652     indent=${line%%[!$space]*}
   1653     line=${line:${#indent}}
   1654 
   1655     ((x=0))
   1656     if [[ $indent ]]; then
   1657       ((len=${#indent}))
   1658       for ((i=0;i<len;i++)); do
   1659         if [[ ${indent:i:1} == ' ' ]]; then
   1660           ((x++))
   1661         else
   1662           ((x=(x+it)/it*it))
   1663         fi
   1664       done
   1665     fi
   1666 
   1667     ((x+=delta,x<0&&(x=0)))
   1668 
   1669     indent=
   1670     if ((x)); then
   1671       if ((bleopt_indent_tabs&&(r=x/it))); then
   1672         ble/string#repeat $'\t' "$r"
   1673         indent=$ret
   1674         ((x%=it))
   1675       fi
   1676       if ((x)); then
   1677         ble/string#repeat ' ' "$x"
   1678         indent=$indent$ret
   1679       fi
   1680     fi
   1681 
   1682     ble/array#push arr2 "$indent$line"
   1683   done
   1684 
   1685   IFS=$'\n' builtin eval 'ret="${arr2[*]-}"'
   1686 }
   1687 ## @fn ble/keymap:vi/operator:indent.impl/increase-block-indent width
   1688 ##   @param[in] width
   1689 ##   @var[in] sub_ranges
   1690 function ble/keymap:vi/operator:indent.impl/increase-block-indent {
   1691   local width=$1
   1692   local isub=${#sub_ranges[@]}
   1693   local sub smin slpad ret
   1694   while ((isub--)); do
   1695     ble/string#split sub : "${sub_ranges[isub]}"
   1696     smin=${sub[0]} slpad=${sub[2]}
   1697     ble/string#repeat ' ' "$((slpad+width))"
   1698     ble/widget/.replace-range "$smin" "$smin" "$ret"
   1699   done
   1700 }
   1701 ## @fn ble/keymap:vi/operator:indent.impl/decrease-graphical-block-indent width
   1702 ##   @param[in] width
   1703 ##   @var[in] sub_ranges
   1704 function ble/keymap:vi/operator:indent.impl/decrease-graphical-block-indent {
   1705   local width=$1
   1706   local it=${bleopt_tab_width:-$_ble_term_it} cols=$_ble_textmap_cols
   1707   local sub smin slpad ret
   1708   local -a replaces=()
   1709   local isub=${#sub_ranges[@]}
   1710   while ((isub--)); do
   1711     ble/string#split sub : "${sub_ranges[isub]}"
   1712     smin=${sub[0]} slpad=${sub[2]}
   1713     ble-edit/content/find-non-space "$smin"; local nsp=$ret
   1714     ((smin<nsp)) || continue
   1715 
   1716     local ax ay bx by
   1717     ble/textmap#getxy.out --prefix=a "$smin"
   1718     ble/textmap#getxy.out --prefix=b "$nsp"
   1719     local w=$(((bx-ax)-(by-ay)*cols-width))
   1720     ((w<slpad)) && w=$slpad
   1721 
   1722     local ins=
   1723     if ((w)); then
   1724       local r
   1725       if ((bleopt_indent_tabs&&(r=(ax+w)/it-ax/it))); then
   1726         ble/string#repeat $'\t' "$r"; ins=$ret
   1727         ((w=(ax+w)%it))
   1728       fi
   1729       if ((w)); then
   1730         ble/string#repeat ' ' "$w"
   1731         ins=$ins$ret
   1732       fi
   1733     fi
   1734 
   1735     ble/array#push replaces "$smin:$nsp:$ins"
   1736   done
   1737 
   1738   local rep
   1739   for rep in "${replaces[@]}"; do
   1740     ble/string#split rep : "$rep"
   1741     ble/widget/.replace-range "${rep[@]::3}"
   1742   done
   1743 }
   1744 ## @fn ble/keymap:vi/operator:indent.impl/decrease-logical-block-indent width
   1745 ##   @param[in] width
   1746 ##   @var[in] sub_ranges
   1747 function ble/keymap:vi/operator:indent.impl/decrease-logical-block-indent {
   1748   # タブは幅 it で固定と見做して削除する
   1749   local width=$1
   1750   local it=${bleopt_tab_width:-$_ble_term_it}
   1751   local sub smin ret nsp
   1752   local isub=${#sub_ranges[@]}
   1753   while ((isub--)); do
   1754     ble/string#split sub : "${sub_ranges[isub]}"
   1755     smin=${sub[0]}
   1756     ble-edit/content/find-non-space "$smin"; nsp=$ret
   1757     ((smin<nsp)) || continue
   1758 
   1759     local stext=${_ble_edit_str:smin:nsp-smin}
   1760     local i=0 n=${#stext} c=0 pad=0
   1761     for ((i=0;i<n;i++)); do
   1762       if [[ ${stext:i:1} == $'\t' ]]; then
   1763         ((c+=it))
   1764       else
   1765         ((c++))
   1766       fi
   1767       if ((c>=width)); then
   1768         pad=$((c-width))
   1769         nsp=$((smin+i+1))
   1770         break
   1771       fi
   1772     done
   1773 
   1774     local padding=
   1775     ((pad)) && { ble/string#repeat ' ' "$pad"; padding=$ret; }
   1776     ble/widget/.replace-range "$smin" "$nsp" "$padding"
   1777   done
   1778 }
   1779 function ble/keymap:vi/operator:indent.impl {
   1780   local delta=$1 context=$2
   1781   ((delta)) || return 0
   1782   if [[ $context == block ]]; then
   1783     if ((delta>=0)); then
   1784       ble/keymap:vi/operator:indent.impl/increase-block-indent "$delta"
   1785     elif ble/edit/use-textmap; then
   1786       ble/keymap:vi/operator:indent.impl/decrease-graphical-block-indent "$((-delta))"
   1787     else
   1788       ble/keymap:vi/operator:indent.impl/decrease-logical-block-indent "$((-delta))"
   1789     fi
   1790   else
   1791     [[ $context == char ]] && ble/keymap:vi/expand-range-for-linewise-operator
   1792     ((beg<end)) && [[ ${_ble_edit_str:end-1:1} == $'\n' ]] && ((end--))
   1793 
   1794     local ret
   1795     ble/keymap:vi/string#increase-indent "${_ble_edit_str:beg:end-beg}" "$delta"; local content=$ret
   1796     ble/widget/.replace-range "$beg" "$end" "$content"
   1797 
   1798     if [[ $context == char ]]; then
   1799       ble-edit/content/find-non-space "$beg"
   1800       ble_keymap_vi_operator_index=$ret
   1801     fi
   1802   fi
   1803   return 0
   1804 }
   1805 function ble/keymap:vi/operator:indent-left {
   1806   local context=$3 arg=${4:-1}
   1807   ble/keymap:vi/operator:indent.impl "$((-bleopt_indent_offset*arg))" "$context"
   1808 }
   1809 function ble/keymap:vi/operator:indent-right {
   1810   local context=$3 arg=${4:-1}
   1811   ble/keymap:vi/operator:indent.impl "$((bleopt_indent_offset*arg))" "$context"
   1812 }
   1813 
   1814 #--------------------------------------
   1815 # Fold operators gq gw
   1816 
   1817 ## @fn ble/keymap:vi/string#measure-width text
   1818 ##   指定した文字列の表示上の幅を計測します。
   1819 ##
   1820 ##   @param[in] text
   1821 ##   @var[out] ret
   1822 ##
   1823 ##   折り返し処理は行いません。
   1824 ##   タブや改行などの特別処理は行いません。
   1825 ##   C0 文字は 2 文字として取り扱います。
   1826 ##   C1 文字は 4 文字として取り扱います。
   1827 function ble/keymap:vi/string#measure-width {
   1828   local text=$1 iN=${#1} i=0 s=0
   1829   while ((i<iN)); do
   1830     if ble/util/isprint+ "${text:i}"; then
   1831       ((s+=${#BASH_REMATCH},
   1832         i+=${#BASH_REMATCH}))
   1833     else
   1834       ble/util/s2c "${text:i:1}"
   1835       ble/util/c2w-edit "$ret"
   1836       ((s+=ret,i++))
   1837     fi
   1838   done
   1839   ret=$s
   1840 }
   1841 ## @fn ble/keymap:vi/string#fold/.get-interval text x
   1842 ##   単語間のスペースの表示上の幅を計算します。
   1843 ##
   1844 ##   @param[in] text
   1845 ##     スペース・タブで構成される文字列を指定します。
   1846 ##   @param[in] x
   1847 ##     画面上の初期位置を指定します。
   1848 ##   @var[out] ret
   1849 ##
   1850 function ble/keymap:vi/string#fold/.get-interval {
   1851   local text=$1 x=$2
   1852   local it=${bleopt_tab_width:-${_ble_term_it:-8}}
   1853 
   1854   local i=0 iN=${#text}
   1855   for ((i=0;i<iN;i++)); do
   1856     if [[ ${text:i:1} == $'\t' ]]; then
   1857       ((x=(x/it+1)*it))
   1858     else
   1859       ((x++))
   1860     fi
   1861   done
   1862   ret=$((x-$2))
   1863 }
   1864 ## @fn ble/keymap:vi/string#fold text [cols]
   1865 ##   @param[in]     text
   1866 ##   @param[in,opt] cols [既定値 ${COLUMNS:-80}]
   1867 ##     折り返す幅を指定します。
   1868 ##     cols-1 列以内に表示文字が収まる様に折り返し処理されます。
   1869 ##     但し長い単語の途中で折り返しは起こりません。
   1870 ##   @var[out] ret
   1871 function ble/keymap:vi/string#fold {
   1872   local text=$1
   1873   local cols=${2:-${COLUMNS-80}}
   1874   local sp=$' \t' nl=$'\n'
   1875 
   1876   # 途中状態について
   1877   #   @var i       text 内の現在処理している位置
   1878   #   @var out     今までに確定した結果文字列
   1879   #   @var otmp    保留中の単語間スペース。次の単語が来て初めて確定する。
   1880   #   @var x       $out を処理した後の表示横位置
   1881   #   @var xtmp    $out$otmp を処理した後の表示横位置
   1882   #   @var isfirst 初回の正規表現一致かどうか
   1883   #   @var indent  インデント (改行直後に挿入する空白類)
   1884   #   @var xindent インデント挿入直後の表示横位置
   1885   local i=0 out= otmp= x=0 xtmp=0
   1886   local isfirst=1 indent= xindent=0
   1887 
   1888   local rex='^([^'$nl$sp']+)|^(['$sp']+)|^.'
   1889   while [[ ${text:i} =~ $rex ]]; do
   1890     ((i+=${#BASH_REMATCH}))
   1891     if [[ ${BASH_REMATCH[1]} ]]; then
   1892       # 単語
   1893       local word=${BASH_REMATCH[1]}
   1894       ble/keymap:vi/string#measure-width "$word"
   1895       if ((xtmp+ret<cols||xtmp<=xindent)); then
   1896         out=$out$otmp$word
   1897         ((x=xtmp+=ret))
   1898       else
   1899         out=$out$'\n'$indent$word
   1900         ((x=xtmp=xindent+ret))
   1901       fi
   1902       otmp=
   1903     else
   1904       local w=1
   1905       if [[ ${BASH_REMATCH[2]} ]]; then
   1906         [[ $otmp ]] && continue # 改行直後の空白は無視
   1907         # 単語間のスペース
   1908         otmp=${BASH_REMATCH[2]}
   1909         ble/keymap:vi/string#fold/.get-interval "$otmp" "$x"; w=$ret
   1910         [[ $isfirst ]] && indent=$otmp xindent=$ret # インデント記録
   1911       else
   1912         # 改行は空白に置換。既存の空白 otmp は消去。
   1913         otmp=' ' w=1
   1914       fi
   1915 
   1916       if ((x+w<cols)); then
   1917         ((xtmp=x+w))
   1918       else
   1919         ((xtmp=xindent))
   1920         otmp=$'\n'$indent
   1921       fi
   1922     fi
   1923     isfirst=
   1924   done
   1925   ret=$out
   1926 }
   1927 ## @fn ble/keymap:vi/operator:fold/.fold-paragraphwise text [cols]
   1928 ##   @var[out] ret
   1929 function ble/keymap:vi/operator:fold/.fold-paragraphwise {
   1930   local text=$1
   1931   local cols=${2:-${COLUMNS:-80}}
   1932 
   1933   local nl=$'\n' sp=$' \t'
   1934   local rex_paragraph='^((['$sp']*'$nl')*)(['$sp']*[^'$sp$nl'][^'$nl']*('$nl'|$))+'
   1935 
   1936   local i=0 out=
   1937   while [[ ${text:i} =~ $rex_paragraph ]]; do
   1938     ((i+=${#BASH_REMATCH}))
   1939     local rematch1=${BASH_REMATCH[1]}
   1940     local len1=${#rematch1}
   1941     local paragraph=${BASH_REMATCH:len1}
   1942 
   1943     # fold (実はここだけ変えれば paragraphwise の様々な処理を実装できる)
   1944     ble/keymap:vi/string#fold "$paragraph" "$cols"
   1945     paragraph=${ret%$'\n'}$'\n'
   1946 
   1947     out=$out$rematch1$paragraph
   1948   done
   1949   ret=$out${text:i}
   1950 }
   1951 
   1952 function ble/keymap:vi/operator:fold.impl {
   1953   local context=$1 opts=$2
   1954   local ret
   1955 
   1956   [[ $context != line ]] && ble/keymap:vi/expand-range-for-linewise-operator
   1957   local old=${_ble_edit_str:beg:end-beg} oind=$_ble_edit_ind
   1958 
   1959   local cols=${COLUMNS:-80}; ((cols>80&&(cols=80)))
   1960   ble/keymap:vi/operator:fold/.fold-paragraphwise "$old" "$cols"; local new=$ret
   1961   ble/widget/.replace-range "$beg" "$end" "$new"
   1962 
   1963   # 変換後のカーソル位置を修正。
   1964   if [[ :$opts: == *:preserve_point:* ]]; then
   1965     # gw: もともとカーソルが合った文字に移動。
   1966     if ((end<=oind)); then
   1967       ble_keymap_vi_operator_index=$((beg+${#new}))
   1968     elif ((beg<oind)); then
   1969       ble/keymap:vi/operator:fold/.fold-paragraphwise "${old::oind-beg}" "$cols"
   1970       ble_keymap_vi_operator_index=$((beg+${#ret}))
   1971     fi
   1972   else
   1973     # gq: 最終行の非空白行頭 (gq) に移動。
   1974     if [[ $new ]]; then
   1975       ble-edit/content/find-logical-bol "$((beg+${#new}-1))"
   1976       ble-edit/content/find-non-space "$ret"
   1977       ble_keymap_vi_operator_index=$ret
   1978     fi
   1979   fi
   1980   return 0
   1981 }
   1982 function ble/keymap:vi/operator:fold {
   1983   local context=$3
   1984   ble/keymap:vi/operator:fold.impl "$context"
   1985 }
   1986 function ble/keymap:vi/operator:fold-preserve-point {
   1987   local context=$3
   1988   ble/keymap:vi/operator:fold.impl "$context" preserve_point
   1989 }
   1990 
   1991 #--------------------------------------
   1992 # Filter operator: !
   1993 
   1994 _ble_keymap_vi_filter_args=()
   1995 _ble_keymap_vi_filter_repeat=()
   1996 
   1997 _ble_keymap_vi_filter_history=()
   1998 _ble_keymap_vi_filter_history_edit=()
   1999 _ble_keymap_vi_filter_history_dirt=()
   2000 _ble_keymap_vi_filter_history_index=0
   2001 
   2002 function ble/highlight/layer:region/mark:vi_filter/get-face {
   2003   face=region_target
   2004 }
   2005 function ble/keymap:vi/operator:filter/.cache-repeat {
   2006   local -a _ble_keymap_vi_repeat _ble_keymap_vi_repeat_irepeat
   2007   ble/keymap:vi/repeat/record-normal
   2008   _ble_keymap_vi_filter_repeat=("${_ble_keymap_vi_repeat[@]}")
   2009 }
   2010 function ble/keymap:vi/operator:filter/.record-repeat {
   2011   ble/keymap:vi/repeat/record-special && return 0
   2012   local command=$1
   2013   _ble_keymap_vi_repeat=("${_ble_keymap_vi_filter_repeat[@]}")
   2014   _ble_keymap_vi_repeat_irepeat=()
   2015   _ble_keymap_vi_repeat[10]=$command
   2016 }
   2017 function ble/keymap:vi/operator:filter {
   2018   local context=$3
   2019   [[ $context != line ]] && ble/keymap:vi/expand-range-for-linewise-operator
   2020   _ble_keymap_vi_filter_args=("$beg" "$end" "${@:3}")
   2021 
   2022   if [[ $_ble_keymap_vi_repeat_invoke ]]; then
   2023     # nmap . によって繰り返しが要求された時
   2024     local command=${_ble_keymap_vi_repeat[10]}
   2025     ble/keymap:vi/operator:filter/.hook "$command"
   2026     return "$?"
   2027   else
   2028     # 通常の呼び出し時
   2029     ble/keymap:vi/operator:filter/.cache-repeat
   2030     _ble_edit_ind=$beg
   2031     _ble_edit_mark=$end
   2032     _ble_edit_mark_active=vi_filter
   2033     ble/keymap:vi/async-commandline-mode 'ble/keymap:vi/operator:filter/.hook'
   2034     _ble_edit_PS1='!'
   2035     ble/history/set-prefix _ble_keymap_vi_filter
   2036     _ble_keymap_vi_cmap_before_command=ble/keymap:vi/commandline/before-command.hook
   2037     _ble_keymap_vi_cmap_cancel_hook=ble/keymap:vi/operator:filter/cancel.hook
   2038     _ble_syntax_lang=bash
   2039     _ble_highlight_layer_list=(plain syntax region overwrite_mode)
   2040     return 147
   2041   fi
   2042 }
   2043 function ble/keymap:vi/operator:filter/cancel.hook {
   2044   _ble_edit_mark_active= # clear mark:vi_filter
   2045 }
   2046 function ble/keymap:vi/operator:filter/.hook {
   2047   local command=$1 # 入力されたコマンド
   2048   if [[ ! $command ]]; then
   2049     ble/widget/vi-command/bell
   2050     return 1
   2051   fi
   2052   local beg=${_ble_keymap_vi_filter_args[0]}
   2053   local end=${_ble_keymap_vi_filter_args[1]}
   2054   local context=${_ble_keymap_vi_filter_args[2]}
   2055 
   2056   _ble_edit_mark_active= # clear mark:vi_filter
   2057 
   2058   local old=${_ble_edit_str:beg:end-beg} new
   2059   old=${old%$'\n'}
   2060   if ! ble/util/assign new 'builtin eval -- "$command" <<< "$old" 2>/dev/null'; then
   2061     ble/widget/vi-command/bell
   2062     return 1
   2063   fi
   2064   new=${new%$'\n'}
   2065   ((end<${#_ble_edit_str})) && new=$new$'\n'
   2066   ble/widget/.replace-range "$beg" "$end" "$new"
   2067 
   2068   _ble_edit_ind=$beg
   2069   if [[ $context == line ]]; then
   2070     ble/widget/vi-command/first-non-space
   2071   else
   2072     ble/keymap:vi/adjust-command-mode
   2073   fi
   2074 
   2075   ble/keymap:vi/mark/set-previous-edit-area "$beg" "$((beg+${#new}))"
   2076   ble/keymap:vi/operator:filter/.record-repeat "$command"
   2077   return 0
   2078 }
   2079 
   2080 #--------------------------------------
   2081 # User operator: g@
   2082 
   2083 bleopt/declare -v keymap_vi_operatorfunc ''
   2084 
   2085 function ble/keymap:vi/operator:map {
   2086   local context=$3
   2087   if [[ $bleopt_keymap_vi_operatorfunc ]]; then
   2088     local opfunc=ble/keymap:vi/operator:$bleopt_keymap_vi_operatorfunc
   2089     if ble/is-function "$opfunc"; then
   2090       "$opfunc" "$@"
   2091       return "$?"
   2092     fi
   2093   fi
   2094   return 1
   2095 }
   2096 
   2097 #------------------------------------------------------------------------------
   2098 # Motions
   2099 
   2100 #--------------------------------------
   2101 # Primitive motion
   2102 
   2103 ## @fn ble/widget/vi-command/exclusive-range.impl src dst flag reg nobell
   2104 ## @fn ble/widget/vi-command/exclusive-goto.impl index flag reg nobell
   2105 ## @fn ble/widget/vi-command/inclusive-goto.impl index flag reg nobell
   2106 ##
   2107 ##   @param[in] src, dst
   2108 ##     移動前の位置と移動先の位置を指定します。
   2109 ##
   2110 ##   @param[in] flag
   2111 ##     オペレータ名を指定します。
   2112 ##
   2113 ##   @param[in] reg
   2114 ##     レジスタ番号を指定します。
   2115 ##
   2116 ##   @param[in] opts
   2117 ##     コロン区切りのオプションです。
   2118 ##     nobell    移動前と移動後の位置が同じときにベルを鳴らしません。
   2119 ##     inclusive 移動の既定の動作が inclusive である事を示します。
   2120 ##
   2121 function ble/widget/vi-command/exclusive-range.impl {
   2122   local src=$1 dst=$2 flag=$3 reg=$4 opts=$5
   2123   if [[ $flag ]]; then
   2124     local opname=${flag%%:*} opflags=${flag#*:}
   2125     if [[ :$opflags: == *:vi_line:* ]]; then
   2126       local ble_keymap_vi_opmode=vi_line
   2127       ble/keymap:vi/call-operator-linewise "$opname" "$src" "$dst" '' "$reg"; local ext=$?
   2128     elif [[ :$opflags: == *:vi_block:* ]]; then
   2129       local ble_keymap_vi_opmode=vi_line
   2130       ble/keymap:vi/call-operator-blockwise "$opname" "$src" "$dst" '' "$reg"; local ext=$?
   2131     elif [[ :$opflags: == *:vi_char:* ]]; then
   2132       local ble_keymap_vi_opmode=vi_char
   2133 
   2134       # 規則 o_v (omap v) の toggle inclusive/exclusive
   2135       if [[ :$opts: == *:inclusive:* ]]; then
   2136         ((src<dst?dst--:(dst<src&&src--)))
   2137       else
   2138         if ((src<=dst)); then
   2139           ((dst<${#_ble_edit_str})) &&
   2140             [[ ${_ble_edit_str:dst:1} != $'\n' ]] &&
   2141             ((dst++))
   2142         else
   2143           ((src<${#_ble_edit_str})) &&
   2144             [[ ${_ble_edit_str:src:1} != $'\n' ]] &&
   2145             ((src++))
   2146         fi
   2147       fi
   2148 
   2149       ble/keymap:vi/call-operator-charwise "$opname" "$src" "$dst" '' "$reg"; local ext=$?
   2150     else
   2151       local ble_keymap_vi_opmode=
   2152       ble/keymap:vi/call-operator-charwise "$opname" "$src" "$dst" '' "$reg"; local ext=$?
   2153     fi
   2154     ((ext==147)) && return 147
   2155     ((ext)) && ble/widget/.bell
   2156     ble/keymap:vi/adjust-command-mode
   2157     return "$ext"
   2158   else
   2159     ble/keymap:vi/needs-eol-fix "$dst" && ((dst--))
   2160     if ((dst!=_ble_edit_ind)); then
   2161       _ble_edit_ind=$dst
   2162     elif [[ :$opts: != *:nobell:* ]]; then
   2163       ble/widget/vi-command/bell
   2164       return 1
   2165     fi
   2166     ble/keymap:vi/adjust-command-mode
   2167     return 0
   2168   fi
   2169 }
   2170 function ble/widget/vi-command/exclusive-goto.impl {
   2171   local index=$1 flag=$2 reg=$3 opts=$4
   2172   if [[ $flag ]]; then
   2173     if ble-edit/content/bolp "$index"; then
   2174       local is_linewise=
   2175       if ((_ble_edit_ind<index)); then
   2176         # :help exclusive-linewise の規則1 (src<ind の時のみ)
   2177         ((index--))
   2178         # :help exclusive-linewise の規則2
   2179         rex=$'(^|\n)[ \t]*$'
   2180         [[ ${_ble_edit_str::_ble_edit_ind} =~ $rex ]] &&
   2181           is_linewise=1
   2182       elif ((index<_ble_edit_ind)); then
   2183         # :help exclusive-linewise の規則2 (条件が異なる)
   2184         ble-edit/content/bolp &&
   2185           is_linewise=1
   2186       fi
   2187 
   2188       if [[ $is_linewise ]]; then
   2189         ble/widget/vi-command/linewise-goto.impl "$index" "$flag" "$reg"
   2190         return "$?"
   2191       fi
   2192     fi
   2193   fi
   2194   ble/widget/vi-command/exclusive-range.impl "$_ble_edit_ind" "$index" "$flag" "$reg" "$opts"
   2195 }
   2196 function ble/widget/vi-command/inclusive-goto.impl {
   2197   local index=$1 flag=$2 reg=$3 opts=$4
   2198   if [[ $flag ]]; then
   2199     if ((_ble_edit_ind<=index)); then
   2200       ble-edit/content/eolp "$index" || ((index++))
   2201     else
   2202       ble-edit/content/eolp || ((_ble_edit_ind++))
   2203     fi
   2204   fi
   2205   ble/widget/vi-command/exclusive-range.impl "$_ble_edit_ind" "$index" "$flag" "$reg" "$opts:inclusive"
   2206 }
   2207 
   2208 ## @fn ble/widget/vi-command/linewise-range.impl p q flag reg opts
   2209 ## @fn ble/widget/vi-command/linewise-goto.impl index flag reg opts
   2210 ##
   2211 ##   @param[in] p, q
   2212 ##     開始位置と終了位置を指定します。
   2213 ##     flag が設定されていない場合は q に移動します。
   2214 ##
   2215 ##   @param[in] index
   2216 ##     開始位置を _ble_edit_ind とし、
   2217 ##     移動先または終了位置を指定します。
   2218 ##
   2219 ##     index=indx:linex の形をしているとき、
   2220 ##     基準の位置 indx から linex 行目を移動先とします。
   2221 ##     index=整数 の場合 index を含む行を移動先とします。
   2222 ##
   2223 ##   @param[in] flag
   2224 ##   @param[in] reg
   2225 ##
   2226 ##   @param[in] opts
   2227 ##     以下のフィールドを似にに含むコロン区切りのリスト
   2228 ##
   2229 ##     preserve_column
   2230 ##     require_multiline
   2231 ##     goto_bol
   2232 ##
   2233 ##     bolx=NUMBER
   2234 ##       既に計算済みの移動先 (index, q) の行の行頭がある場合はここに指定します。
   2235 ##
   2236 ##     nolx=NUMBER
   2237 ##       既に計算済みの移動先 (index, q) の行の非空白行頭位置がある場合はここに指定します。
   2238 ##
   2239 function ble/widget/vi-command/linewise-range.impl {
   2240   local p=$1 q=$2 flag=$3 reg=$4 opts=$5
   2241   local ret
   2242   if [[ $q == *:* ]]; then
   2243     local qbase=${q%%:*} qline=${q#*:}
   2244   else
   2245     local qbase=$q qline=0
   2246   fi
   2247 
   2248   local bolx=; local rex=':bolx=([0-9]+):'; [[ :$opts: =~ $rex ]] && bolx=${BASH_REMATCH[1]}
   2249   local nolx=; local rex=':nolx=([0-9]+):'; [[ :$opts: =~ $rex ]] && nolx=${BASH_REMATCH[1]}
   2250 
   2251   # 移動時 (オペレータが設定されていない時)
   2252   if [[ ! $flag ]]; then
   2253     if [[ ! $nolx ]]; then
   2254       if [[ ! $bolx ]]; then
   2255         ble-edit/content/find-logical-bol "$qbase" "$qline"; bolx=$ret
   2256       fi
   2257       ble-edit/content/find-non-space "$bolx"; nolx=$ret
   2258     fi
   2259     ble-edit/content/nonbol-eolp "$nolx" && ((nolx--))
   2260     _ble_edit_ind=$nolx
   2261     ble/keymap:vi/adjust-command-mode
   2262     return 0
   2263   fi
   2264 
   2265   local opname=${flag%%:*} opflags=${flag#*:}
   2266   if ! ble/is-function ble/keymap:vi/operator:"$opname"; then
   2267     ble/widget/vi-command/bell
   2268     return 1
   2269   fi
   2270 
   2271   local bolp bolq=$bolx nolq=$nolx
   2272   ble-edit/content/find-logical-bol "$p"; bolp=$ret
   2273   [[ $bolq ]] || { ble-edit/content/find-logical-bol "$qbase" "$qline"; bolq=$ret; }
   2274 
   2275   # jk+- で1行も移動できない場合は操作をキャンセルする。
   2276   # Note: qline を用いる場合は必ずしも望みどおり
   2277   #   qline 行目が存在するとは限らないことに注意する。
   2278   if [[ :$opts: == *:require_multiline:* ]]; then
   2279     if ((bolq==bolp)); then
   2280       ble/widget/vi-command/bell
   2281       return 1
   2282     fi
   2283   fi
   2284 
   2285   # オペレータ呼び出し
   2286   if [[ :$opflags: == *:vi_char:* || :$opflags: == *:vi_block:* ]]; then
   2287     # 行き先の決定
   2288     local beg=$p end
   2289     if [[ :$opts: == *:preserve_column:* ]]; then
   2290       local index
   2291       ble/keymap:vi/get-index-of-relative-line "$qbase" "$qline"; end=$index
   2292     elif [[ :$opts: == *:goto_bol:* ]]; then
   2293       end=$bolq
   2294     else
   2295       [[ $nolq ]] || { ble-edit/content/find-non-space "$bolq"; nolq=$ret; }
   2296       end=$nolq
   2297     fi
   2298     ((beg<=end)) || local beg=$end end=$beg
   2299 
   2300     if [[ :$opflags: == *:vi_block:* ]]; then
   2301       local ble_keymap_vi_opmode=vi_block
   2302       ble/keymap:vi/call-operator "$opname" "$beg" "$end" block '' "$reg"; local ext=$?
   2303     else
   2304       local ble_keymap_vi_opmode=vi_char
   2305       ble/keymap:vi/call-operator "$opname" "$beg" "$end" char '' "$reg"; local ext=$?
   2306     fi
   2307     if ((ext)); then
   2308       ((ext==147)) && return 147
   2309       ble/widget/vi-command/bell
   2310       return "$ext"
   2311     fi
   2312   else
   2313     # 行指向の処理 (既定)
   2314 
   2315     # 最初の行の行頭 beg と最後の行の行末 end
   2316     local beg end
   2317     if ((bolp<=bolq)); then
   2318       ble-edit/content/find-logical-eol "$bolq"; beg=$bolp end=$ret
   2319     else
   2320       ble-edit/content/find-logical-eol "$bolp"; beg=$bolq end=$ret
   2321     fi
   2322     ((end<${#_ble_edit_str}&&end++))
   2323 
   2324     local ble_keymap_vi_opmode=
   2325     [[ :$opflags: == *:vi_line:* ]] && ble_keymap_vi_opmode=vi_line
   2326     ble/keymap:vi/call-operator "$opname" "$beg" "$end" line '' "$reg"; local ext=$?
   2327     if ((ext)); then
   2328       ((ext==147)) && return 147
   2329       ble/widget/vi-command/bell
   2330       return "$ext"
   2331     fi
   2332 
   2333     # 範囲の先頭に移動
   2334     local ind=$_ble_edit_ind
   2335     if [[ $opname == [cd] ]]; then
   2336       # これらは常に first-non-space になる。
   2337       _ble_edit_ind=$beg
   2338       ble/widget/vi-command/first-non-space
   2339     elif [[ :$opts: == *:preserve_column:* ]]; then # j k
   2340       if ((beg<ind)); then
   2341         ble/string#count-char "${_ble_edit_str:beg:ind-beg}" $'\n'
   2342         ((ret=-ret))
   2343       elif ((ind<beg)); then
   2344         ble/string#count-char "${_ble_edit_str:ind:beg-ind}" $'\n'
   2345       else
   2346         ret=0
   2347       fi
   2348 
   2349       if ((ret)); then
   2350         local index; ble/keymap:vi/get-index-of-relative-line "$_ble_edit_ind" "$ret"
   2351         ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   2352         _ble_edit_ind=$index
   2353       fi
   2354     elif [[ :$opts: == *:goto_bol:* ]]; then # 行指向 yis
   2355       _ble_edit_ind=$beg
   2356     else # + - gg G L H
   2357       if ((beg==bolq||ind<beg)) || [[ ${_ble_edit_str:beg:ind-beg} == *$'\n'* ]] ; then
   2358         # 先頭行の非空白行頭に移動する
   2359         if ((bolq<=bolp)) && [[ $nolq ]]; then
   2360           local nolb=$nolq
   2361         else
   2362           ble-edit/content/find-non-space "$beg"; local nolb=$ret
   2363         fi
   2364         ble-edit/content/nonbol-eolp "$nolb" && ((nolb--))
   2365         ((ind<beg||nolb<ind)) && _ble_edit_ind=$nolb
   2366       fi
   2367     fi
   2368   fi
   2369   ble/keymap:vi/adjust-command-mode
   2370   return 0
   2371 }
   2372 function ble/widget/vi-command/linewise-goto.impl {
   2373   ble/widget/vi-command/linewise-range.impl "$_ble_edit_ind" "$@"
   2374 }
   2375 
   2376 #------------------------------------------------------------------------------
   2377 # single char arguments
   2378 
   2379 function ble/keymap:vi/async-read-char.hook {
   2380   local IFS=$_ble_term_IFS
   2381   local command="${*:1:$#-1}" key="${*:$#}"
   2382   if ((key==(_ble_decode_Ctrl|0x6B))); then # C-k
   2383     ble/decode/keymap/push vi_digraph
   2384     _ble_keymap_vi_digraph__hook="$command"
   2385   else
   2386     builtin eval -- "$command $key"
   2387   fi
   2388 }
   2389 
   2390 function ble/keymap:vi/async-read-char {
   2391   local IFS=$_ble_term_IFS
   2392   _ble_decode_key__hook="ble/keymap:vi/async-read-char.hook $*"
   2393   return 147
   2394 }
   2395 
   2396 #------------------------------------------------------------------------------
   2397 # marks
   2398 
   2399 ## @arr _ble_keymap_vi_mark_local
   2400 ##   添字は mark の文字コードで指定する。
   2401 ##   各要素は point:bytes の形をしている。
   2402 ## @arr _ble_keymap_vi_mark_global
   2403 ##   添字は mark の文字コードで指定する。
   2404 ##   各要素は hindex:point:bytes の形をしている。
   2405 _ble_keymap_vi_mark_Offset=32
   2406 _ble_keymap_vi_mark_hindex=
   2407 _ble_keymap_vi_mark_local=()
   2408 _ble_keymap_vi_mark_global=()
   2409 _ble_keymap_vi_mark_history=()
   2410 _ble_keymap_vi_mark_edit_dbeg=-1
   2411 _ble_keymap_vi_mark_edit_dend=-1
   2412 _ble_keymap_vi_mark_edit_dend0=-1
   2413 ble/array#push _ble_textarea_local_VARNAMES \
   2414                _ble_keymap_vi_mark_hindex \
   2415                _ble_keymap_vi_mark_local \
   2416                _ble_keymap_vi_mark_global \
   2417                _ble_keymap_vi_mark_history \
   2418                _ble_keymap_vi_mark_edit_dbeg \
   2419                _ble_keymap_vi_mark_edit_dend \
   2420                _ble_keymap_vi_mark_edit_dend0
   2421 
   2422 # mark 番号と用途の対応
   2423 #
   2424 #
   2425 #   1     内部使用。矩形挿入モードの開始点を記録するためのもの
   2426 #   91 93 `[ と `]。編集・ヤンク範囲を保持する。
   2427 #   96 39 `` と `'。最後のジャンプ位置を保持する。39 は実際には使用されない。
   2428 #   60 62 `< と `>。最後のビジュアル範囲。
   2429 #
   2430 
   2431 ble/array#push _ble_edit_dirty_observer ble/keymap:vi/mark/shift-by-dirty-range
   2432 blehook history_leave!=ble/keymap:vi/mark/history-onleave.hook
   2433 
   2434 ## @fn ble/keymap:vi/mark/history-onleave.hook
   2435 function ble/keymap:vi/mark/history-onleave.hook {
   2436   if [[ $_ble_decode_keymap == vi_[inoxs]map ]]; then
   2437     ble/keymap:vi/mark/set-local-mark 34 "$_ble_edit_ind" # `"
   2438   fi
   2439 }
   2440 
   2441 # 履歴がロードされていない時は取り敢えず _ble_history_index=0 で登録をしておく。
   2442 # 履歴がロードされた後の初めての利用のときに正しい履歴番号に修正する。
   2443 function ble/keymap:vi/mark/update-mark-history {
   2444   local h; ble/history/get-index -v h
   2445   if [[ ! $_ble_keymap_vi_mark_hindex ]]; then
   2446     _ble_keymap_vi_mark_hindex=$h
   2447   elif ((_ble_keymap_vi_mark_hindex!=h)); then
   2448     local imark value
   2449 
   2450     # save
   2451     local -a save=()
   2452     for imark in "${!_ble_keymap_vi_mark_local[@]}"; do
   2453       local value=${_ble_keymap_vi_mark_local[imark]}
   2454       ble/array#push save "$imark:$value"
   2455     done
   2456     local IFS=$_ble_term_IFS
   2457     _ble_keymap_vi_mark_history[_ble_keymap_vi_mark_hindex]="${save[*]-}"
   2458 
   2459     # load
   2460     _ble_keymap_vi_mark_local=()
   2461     local entry
   2462     for entry in ${_ble_keymap_vi_mark_history[h]-}; do
   2463       imark=${entry%%:*} value=${entry#*:}
   2464       _ble_keymap_vi_mark_local[imark]=$value
   2465     done
   2466 
   2467     _ble_keymap_vi_mark_hindex=$h
   2468   fi
   2469 }
   2470 blehook history_change!=ble/keymap:vi/mark/history-change.hook
   2471 ## @fn ble/keymap:vi/mark/history-change.hook 'delete' index...
   2472 ## @fn ble/keymap:vi/mark/history-change.hook 'clear'
   2473 ## @fn ble/keymap:vi/mark/history-change.hook 'insert' beg len
   2474 ##   @param[in] index...
   2475 ##     削除する項目の番号を指定します。昇順に並んでいる事と重複がない事を仮定します。
   2476 ##   @param[in] beg len
   2477 ##     挿入位置と挿入項目の個数を指定します。
   2478 function ble/keymap:vi/mark/history-change.hook {
   2479   local kind=$1; shift
   2480   case $kind in
   2481   (delete)
   2482     # update _ble_keymap_vi_mark_global
   2483     local imark
   2484     for imark in "${!_ble_keymap_vi_mark_global[@]}"; do
   2485       local value=${_ble_keymap_vi_mark_global[imark]}
   2486       local h=${value%%:*} v=${value#*:}
   2487       local idel shift=0
   2488       for idel; do
   2489         if [[ $idel == *-* ]]; then
   2490           local b=${idel%-*} e=${idel#*-}
   2491           ((b<=h&&h<e)) && shift= # delete
   2492           ((h<e)) && break
   2493           ((shift+=e-b))
   2494         else
   2495           ((idel==h)) && shift= # delete
   2496           ((idel>=h)) && break
   2497           ((shift++))
   2498         fi
   2499       done
   2500       [[ $shift ]] &&
   2501         _ble_keymap_vi_mark_global[imark]=$((h-shift)):$v
   2502     done
   2503 
   2504     # update _ble_keymap_vi_mark_history
   2505     ble/builtin/history/array#delete-hindex _ble_keymap_vi_mark_history "$@"
   2506 
   2507     # reset _ble_keymap_vi_mark_hindex
   2508     _ble_keymap_vi_mark_hindex= ;;
   2509 
   2510   (clear)
   2511     _ble_keymap_vi_mark_global=()
   2512     _ble_keymap_vi_mark_history=()
   2513     _ble_keymap_vi_mark_hindex= ;;
   2514 
   2515   (insert)
   2516     local beg=$1 len=$2
   2517 
   2518     # update _ble_keymap_vi_mark_global
   2519     local imark
   2520     for imark in "${!_ble_keymap_vi_mark_global[@]}"; do
   2521       local value=${_ble_keymap_vi_mark_global[imark]}
   2522 
   2523       local h=${value%%:*} v=${value#*:}
   2524       ((h>=beg)) && _ble_keymap_vi_mark_global[imark]=$((h+len)):$v
   2525     done
   2526 
   2527     # update _ble_keymap_vi_mark_history
   2528     ble/builtin/history/array#insert-range _ble_keymap_vi_mark_history "$@"
   2529 
   2530     # reset _ble_keymap_vi_mark_hindex
   2531     _ble_keymap_vi_mark_hindex= ;;
   2532   esac
   2533 }
   2534 
   2535 function ble/keymap:vi/mark/shift-by-dirty-range {
   2536   local beg=$1 end=$2 end0=$3 reason=$4
   2537   if [[ $4 == edit ]]; then
   2538     ble/dirty-range#update --prefix=_ble_keymap_vi_mark_edit_d "${@:1:3}"
   2539     ble/keymap:vi/xmap/update-dirty-range "$@"
   2540 
   2541     ble/keymap:vi/mark/update-mark-history
   2542     local shift=$((end-end0))
   2543     local imark
   2544     for imark in "${!_ble_keymap_vi_mark_local[@]}"; do
   2545       local value=${_ble_keymap_vi_mark_local[imark]}
   2546       local index=${value%%:*} rest=${value#*:}
   2547       ((index<beg)) || _ble_keymap_vi_mark_local[imark]=$((index<end0?beg:index+shift)):$rest
   2548     done
   2549     local h; ble/history/get-index -v h
   2550     for imark in "${!_ble_keymap_vi_mark_global[@]}"; do
   2551       local value=${_ble_keymap_vi_mark_global[imark]}
   2552       [[ $value == "$h":* ]] || continue
   2553       local h=${value%%:*}; value=${value:${#h}+1}
   2554       local index=${value%%:*}; value=${value:${#index}+1}
   2555       ((index<beg)) || _ble_keymap_vi_mark_global[imark]=$h:$((index<end0?beg:index+shift)):$value
   2556     done
   2557     ble/keymap:vi/mark/set-local-mark 46 "$beg" # `.
   2558   else
   2559     ble/dirty-range#clear --prefix=_ble_keymap_vi_mark_edit_d
   2560     if [[ $4 == newline && $_ble_decode_keymap != vi_cmap ]]; then
   2561       ble/keymap:vi/mark/set-local-mark 96 0 # ``
   2562     fi
   2563   fi
   2564 }
   2565 function ble/keymap:vi/mark/set-global-mark {
   2566   local c=$1 index=$2 ret
   2567   ble/keymap:vi/mark/update-mark-history
   2568   ble-edit/content/find-logical-bol "$index"; local bol=$ret
   2569   local h; ble/history/get-index -v h
   2570   _ble_keymap_vi_mark_global[c]=$h:$bol:$((index-bol))
   2571 }
   2572 function ble/keymap:vi/mark/set-local-mark {
   2573   local c=$1 index=$2 ret
   2574   ble/keymap:vi/mark/update-mark-history
   2575   ble-edit/content/find-logical-bol "$index"; local bol=$ret
   2576   _ble_keymap_vi_mark_local[c]=$bol:$((index-bol))
   2577 }
   2578 ## @fn ble/keymap:vi/mark/get-mark.impl index bytes
   2579 ##   @param[in] index bytes
   2580 ##     記録された行頭の位置と列を指定します。
   2581 ##   @var[out] ret
   2582 ##     マークが見つかったとき対応する位置を返します。
   2583 function ble/keymap:vi/mark/get-mark.impl {
   2584   local index=$1 bytes=$2
   2585   local len=${#_ble_edit_str}
   2586   ((index>len&&(index=len)))
   2587   ble-edit/content/find-logical-bol "$index"; index=$ret
   2588   ble-edit/content/find-logical-eol "$index"; local eol=$ret
   2589   ((index+=bytes,index>eol&&(index=eol))) # ToDo: calculate by byte offset
   2590   ret=$index
   2591   return 0
   2592 }
   2593 ## @fn ble/keymap:vi/mark/get-mark.impl c
   2594 ##   @param[in] c
   2595 ##     mark の番号 (文字コード) を指定します。
   2596 ##   @var[out] ret
   2597 ##     マークが見つかったとき対応する位置を返します。
   2598 function ble/keymap:vi/mark/get-local-mark {
   2599   local c=$1
   2600   ble/keymap:vi/mark/update-mark-history
   2601   local value=${_ble_keymap_vi_mark_local[c]}
   2602   [[ $value ]] || return 1
   2603 
   2604   local data
   2605   ble/string#split data : "$value"
   2606   ble/keymap:vi/mark/get-mark.impl "${data[0]}" "${data[1]}" # -> ret
   2607 }
   2608 
   2609 # `[ `]
   2610 _ble_keymap_vi_mark_suppress_edit=
   2611 ## @fn ble/keymap:vi/mark/set-previous-edit-area beg end
   2612 ##   @param[in] beg
   2613 ##   @param[in] end
   2614 function ble/keymap:vi/mark/set-previous-edit-area {
   2615   [[ $_ble_keymap_vi_mark_suppress_edit ]] && return 0
   2616   local beg=$1 end=$2
   2617   ((beg<end)) && ! ble-edit/content/bolp "$end" && ((end--))
   2618   ble/keymap:vi/mark/set-local-mark 91 "$beg" # `[
   2619   ble/keymap:vi/mark/set-local-mark 93 "$end" # `]
   2620   ble/keymap:vi/undo/add
   2621 }
   2622 function ble/keymap:vi/mark/start-edit-area {
   2623   [[ $_ble_keymap_vi_mark_suppress_edit ]] && return 0
   2624   ble/dirty-range#clear --prefix=_ble_keymap_vi_mark_edit_d
   2625 }
   2626 function ble/keymap:vi/mark/commit-edit-area {
   2627   local beg=$1 end=$2
   2628   ble/dirty-range#update --prefix=_ble_keymap_vi_mark_edit_d "$beg" "$end" "$end"
   2629 }
   2630 function ble/keymap:vi/mark/end-edit-area {
   2631   [[ $_ble_keymap_vi_mark_suppress_edit ]] && return 0
   2632   local beg=$_ble_keymap_vi_mark_edit_dbeg
   2633   local end=$_ble_keymap_vi_mark_edit_dend
   2634   ((beg>=0)) && ble/keymap:vi/mark/set-previous-edit-area "$beg" "$end"
   2635 }
   2636 
   2637 # ``
   2638 function ble/keymap:vi/mark/set-jump {
   2639   # ToDo: jumplist?
   2640   ble/keymap:vi/mark/set-local-mark 96 "$_ble_edit_ind"
   2641 }
   2642 
   2643 function ble/widget/vi-command/set-mark {
   2644   _ble_decode_key__hook="ble/widget/vi-command/set-mark.hook"
   2645   return 147
   2646 }
   2647 function ble/widget/vi-command/set-mark.hook {
   2648   local key=$1
   2649   ble/keymap:vi/clear-arg
   2650   local ret
   2651   if ble/keymap:vi/k2c "$key" && local c=$ret; then
   2652     if ((65<=c&&c<91)); then # A-Z
   2653       ble/keymap:vi/mark/set-global-mark "$c" "$_ble_edit_ind"
   2654       ble/keymap:vi/adjust-command-mode
   2655       return 0
   2656     elif ((97<=c&&c<123||c==91||c==93||c==60||c==62||c==96||c==39)); then # a-z [ ] < > ` '
   2657       ((c==39)) && c=96 # m' は m` に読み替える
   2658       ble/keymap:vi/mark/set-local-mark "$c" "$_ble_edit_ind"
   2659       ble/keymap:vi/adjust-command-mode
   2660       return 0
   2661     fi
   2662   fi
   2663   ble/widget/vi-command/bell
   2664   return 1
   2665 }
   2666 
   2667 function ble/widget/vi-command/goto-mark.impl {
   2668   local index=$1 flag=$2 reg=$3 opts=$4
   2669   [[ $flag ]] || ble/keymap:vi/mark/set-jump # ``
   2670   if [[ :$opts: == *:line:* ]]; then
   2671     ble/widget/vi-command/linewise-goto.impl "$index" "$flag" "$reg"
   2672   else
   2673     ble/widget/vi-command/exclusive-goto.impl "$index" "$flag" "$reg" nobell
   2674   fi
   2675 }
   2676 function ble/widget/vi-command/goto-local-mark.impl {
   2677   local c=$1 opts=$2 ret
   2678   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   2679   if ble/keymap:vi/mark/get-local-mark "$c" && local index=$ret; then
   2680     ble/widget/vi-command/goto-mark.impl "$index" "$FLAG" "$REG" "$opts"
   2681   else
   2682     ble/widget/vi-command/bell
   2683     return 1
   2684   fi
   2685 }
   2686 function ble/widget/vi-command/goto-global-mark.impl {
   2687   local c=$1 opts=$2
   2688   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   2689 
   2690   ble/keymap:vi/mark/update-mark-history
   2691   local value=${_ble_keymap_vi_mark_global[c]}
   2692   if [[ ! $value ]]; then
   2693     ble/widget/vi-command/bell
   2694     return 1
   2695   fi
   2696 
   2697   local data
   2698   ble/string#split data : "$value"
   2699 
   2700   # find a history entry by data[0]
   2701   local index; ble/history/get-index
   2702   if ((index!=data[0])); then
   2703     if [[ $FLAG ]]; then
   2704       ble/widget/vi-command/bell
   2705       return 1
   2706     fi
   2707     ble-edit/history/goto "${data[0]}"
   2708   fi
   2709 
   2710   # find position by data[1]:data[2]
   2711   local ret
   2712   ble/keymap:vi/mark/get-mark.impl "${data[1]}" "${data[2]}"
   2713   ble/widget/vi-command/goto-mark.impl "$ret" "$FLAG" "$REG" "$opts"
   2714 }
   2715 
   2716 function ble/widget/vi-command/goto-mark {
   2717   _ble_decode_key__hook="ble/widget/vi-command/goto-mark.hook ${1:-char}"
   2718   return 147
   2719 }
   2720 function ble/widget/vi-command/goto-mark.hook {
   2721   local opts=$1 key=$2
   2722   local ret
   2723   if ble/keymap:vi/k2c "$key" && local c=$ret; then
   2724     if ((65<=c&&c<91)); then # A-Z
   2725       ble/widget/vi-command/goto-global-mark.impl "$c" "$opts"
   2726       return "$?"
   2727     elif ((_ble_keymap_vi_mark_Offset<=c)); then
   2728       ((c==39)) && c=96 # `' は `` に読み替える
   2729       ble/widget/vi-command/goto-local-mark.impl "$c" "$opts"
   2730       return "$?"
   2731     fi
   2732   fi
   2733   ble/keymap:vi/clear-arg
   2734   ble/widget/vi-command/bell
   2735   return 1
   2736 }
   2737 
   2738 #------------------------------------------------------------------------------
   2739 # repeat (nmap .)
   2740 
   2741 ## @arr _ble_keymap_vi_repeat
   2742 ## @arr _ble_keymap_vi_repeat_insert
   2743 ##
   2744 ##   _ble_keymap_vi_repeat が前回の操作を記録する
   2745 ##   _ble_keymap_vi_repeat_insert は挿入モードにいる時に、
   2746 ##   その挿入モードに突入するきっかけとなった操作を保持する。
   2747 ##   これは <C-[> または <C-c> で挿入モードを完了する際に、
   2748 ##   _ble_keymap_vi_repeat に書き込まれるものである。
   2749 ##
   2750 ##   ${_ble_keymap_vi_repeat[0]} = KEYMAP
   2751 ##     呼び出し時の kmap を保持します。
   2752 ##   ${_ble_keymap_vi_repeat[1]} = KEYS
   2753 ##     呼び出しに用いられたキーの列を保持します。
   2754 ##   ${_ble_keymap_vi_repeat[2]} = WIDGET
   2755 ##     呼び出された編集コマンドを保持します。
   2756 ##   ${_ble_keymap_vi_repeat[@]:3:3} = ARG FLAG REG
   2757 ##     呼び出し時の修飾状態を保持します。
   2758 ##   ${_ble_keymap_vi_repeat[6]} = _ble_keymap_vi_xmap_prev_edit
   2759 ##     vi_xmap のとき範囲の大きさと種類を記録します。
   2760 ##   ${_ble_keymap_vi_repeat[@]:10}
   2761 ##     各 WIDGET が自由に使える領域
   2762 ##
   2763 ## @arr _ble_keymap_vi_repeat_irepeat
   2764 ##
   2765 ##   _ble_keymap_vi_repeat の操作によって挿入モードに入るとき、
   2766 ##   そこで行われる挿入操作の列を記録する配列である。
   2767 ##   形式は _ble_keymap_vi_irepeat と同じ。
   2768 ##
   2769 ## @var _ble_keymap_vi_repeat_invoke
   2770 ##   ble/keymap:vi/repeat/invoke を通して呼び出された widget かどうかを保持するローカル変数です。
   2771 ##   この変数が非空白のとき ble/keymap:vi/repeat/invoke 内部での呼び出しであることを表します。
   2772 ##
   2773 _ble_keymap_vi_repeat=()
   2774 _ble_keymap_vi_repeat_insert=()
   2775 _ble_keymap_vi_repeat_irepeat=()
   2776 _ble_keymap_vi_repeat_invoke=
   2777 function ble/keymap:vi/repeat/record-special {
   2778   [[ $_ble_keymap_vi_mark_suppress_edit ]] && return 0
   2779 
   2780   if [[ $_ble_keymap_vi_repeat_invoke ]]; then
   2781     # repeat に引数が指定されたときは以降それを使う
   2782     [[ $repeat_arg ]] && _ble_keymap_vi_repeat[3]=$repeat_arg
   2783     # レジスタが記録されていないときは、以降新しく指定されたレジスタを使う
   2784     [[ ! ${_ble_keymap_vi_repeat[5]} ]] && _ble_keymap_vi_repeat[5]=$repeat_reg
   2785     return 0
   2786   fi
   2787 
   2788   return 1
   2789 }
   2790 function ble/keymap:vi/repeat/record-normal {
   2791   local IFS=$_ble_term_IFS
   2792   local -a repeat; repeat=("$KEYMAP" "${KEYS[*]-}" "$WIDGET" "$ARG" "$FLAG" "$REG" '')
   2793   if [[ $KEYMAP == vi_[xs]map ]]; then
   2794     repeat[6]=$_ble_keymap_vi_xmap_prev_edit
   2795   fi
   2796   if [[ $_ble_decode_keymap == vi_imap ]]; then
   2797     _ble_keymap_vi_repeat_insert=("${repeat[@]}")
   2798   else
   2799     _ble_keymap_vi_repeat=("${repeat[@]}")
   2800     _ble_keymap_vi_repeat_irepeat=()
   2801   fi
   2802 }
   2803 function ble/keymap:vi/repeat/record {
   2804   ble/keymap:vi/repeat/record-special && return 0
   2805   ble/keymap:vi/repeat/record-normal
   2806 }
   2807 ## @fn ble/keymap:vi/repeat/record-insert
   2808 ##   挿入モードを抜ける時に、挿入モードに入るきっかけになった操作と、
   2809 ##   挿入モードで行われた挿入操作の列を記録します。
   2810 function ble/keymap:vi/repeat/record-insert {
   2811   ble/keymap:vi/repeat/record-special && return 0
   2812   if [[ ${_ble_keymap_vi_repeat_insert-} ]]; then
   2813     # 挿入モード突入操作が未だ有効ならば、挿入操作の有無に拘らず記録
   2814     _ble_keymap_vi_repeat=("${_ble_keymap_vi_repeat_insert[@]}")
   2815     _ble_keymap_vi_repeat_irepeat=("${_ble_keymap_vi_irepeat[@]}")
   2816   elif ((${#_ble_keymap_vi_irepeat[@]})); then
   2817     # 挿入モード突入操作が初期化されていたら、挿入操作がある時のみに記録
   2818     local IFS=$_ble_term_IFS
   2819     _ble_keymap_vi_repeat=(vi_nmap "${KEYS[*]-}" ble/widget/vi_nmap/insert-mode 1 '' '')
   2820     _ble_keymap_vi_repeat_irepeat=("${_ble_keymap_vi_irepeat[@]}")
   2821   fi
   2822   ble/keymap:vi/repeat/clear-insert
   2823 }
   2824 ## @fn ble/keymap:vi/repeat/clear-insert
   2825 ##   挿入モードにおいて white list にないコマンドが実行された時に、
   2826 ##   挿入モードに入るきっかけになった操作を初期化します。
   2827 function ble/keymap:vi/repeat/clear-insert {
   2828   _ble_keymap_vi_repeat_insert=()
   2829 }
   2830 
   2831 function ble/keymap:vi/repeat/invoke {
   2832   local repeat_arg=$_ble_edit_arg
   2833   local repeat_reg=$_ble_keymap_vi_reg
   2834   local KEYMAP=${_ble_keymap_vi_repeat[0]}
   2835   local -a KEYS; ble/string#split-words KEYS "${_ble_keymap_vi_repeat[1]}"
   2836   local WIDGET=${_ble_keymap_vi_repeat[2]}
   2837 
   2838   # keymap の状態復元
   2839   if [[ $KEYMAP != vi_[onxs]map ]]; then
   2840     ble/widget/vi-command/bell
   2841     return 1
   2842   elif [[ $KEYMAP == vi_omap ]]; then
   2843     ble/decode/keymap/push vi_omap
   2844   elif [[ $KEYMAP == vi_[xs]map ]]; then
   2845     local _ble_keymap_vi_xmap_prev_edit=${_ble_keymap_vi_repeat[6]}
   2846     ble/widget/vi_xmap/.restore-visual-state
   2847     ble/decode/keymap/push "$KEYMAP"
   2848     # Note: vim では . によって領域の大きさは更新されない。
   2849     #   従ってここでは敢えて _ble_keymap_vi_xmap_prev_edit を unset しない
   2850   fi
   2851 
   2852   # ※本体の _ble_keymap_vi_repeat は成功した時にのみ repeat/record で書き換える
   2853   _ble_edit_arg=
   2854   _ble_keymap_vi_oparg=${_ble_keymap_vi_repeat[3]}
   2855   _ble_keymap_vi_opfunc=${_ble_keymap_vi_repeat[4]}
   2856   [[ $repeat_arg ]] && _ble_keymap_vi_oparg=$repeat_arg
   2857 
   2858   # vim ではレジスタは記録されたものが優先されるようだ
   2859   local REG=${_ble_keymap_vi_repeat[5]}
   2860   [[ $REG ]] && _ble_keymap_vi_reg=$REG
   2861 
   2862   local _ble_keymap_vi_single_command{,_overwrite}= # single-command-mode は持続させる。
   2863   local _ble_keymap_vi_repeat_invoke=1
   2864   local LASTWIDGET=$_ble_decode_widget_last
   2865   _ble_decode_widget_last=$WIDGET
   2866   builtin eval -- "$WIDGET"
   2867 
   2868   if [[ $_ble_decode_keymap == vi_imap ]]; then
   2869     ((_ble_keymap_vi_irepeat_count<=1?(_ble_keymap_vi_irepeat_count=2):_ble_keymap_vi_irepeat_count++))
   2870     local -a _ble_keymap_vi_irepeat
   2871     _ble_keymap_vi_irepeat=("${_ble_keymap_vi_repeat_irepeat[@]}")
   2872 
   2873     ble/array#push _ble_keymap_vi_irepeat '0:ble/widget/dummy' # Note: normal-mode が自分自身を pop しようとするので。
   2874     ble/widget/vi_imap/normal-mode
   2875   fi
   2876   ble/util/unlocal _ble_keymap_vi_single_command{,_overwrite}
   2877 }
   2878 
   2879 # nmap .
   2880 function ble/widget/vi_nmap/repeat {
   2881   ble/keymap:vi/repeat/invoke
   2882   ble/keymap:vi/adjust-command-mode
   2883 }
   2884 
   2885 #------------------------------------------------------------------------------
   2886 # command: [cdy]?[hl]
   2887 
   2888 ## @widget vi-command/forward-char [type]
   2889 ## @widget vi-command/backward-char [type]
   2890 ##
   2891 ##   @param[in] type
   2892 ##     type=wrap のとき複数行に亘る移動を許します。
   2893 ##
   2894 function ble/widget/vi-command/forward-char {
   2895   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   2896 
   2897   local index
   2898   if [[ $1 == wrap ]]; then
   2899     # SP
   2900     if [[ $FLAG || $_ble_decode_keymap == vi_[xs]map ]]; then
   2901       ((index=_ble_edit_ind+ARG,
   2902         index>${#_ble_edit_str}&&(index=${#_ble_edit_str})))
   2903     else
   2904       local nl=$'\n'
   2905       local rex="^([^$nl]$nl?|$nl){0,$ARG}"
   2906       [[ ${_ble_edit_str:_ble_edit_ind} =~ $rex ]]
   2907       ((index=_ble_edit_ind+${#BASH_REMATCH}))
   2908     fi
   2909   else
   2910     local line=${_ble_edit_str:_ble_edit_ind:ARG}
   2911     line=${line%%$'\n'*}
   2912     ((index=_ble_edit_ind+${#line}))
   2913   fi
   2914 
   2915   ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG"
   2916 }
   2917 
   2918 function ble/widget/vi-command/backward-char {
   2919   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   2920 
   2921   local index
   2922   ((ARG>_ble_edit_ind&&(ARG=_ble_edit_ind)))
   2923   if [[ $1 == wrap ]]; then
   2924     # DEL
   2925     if [[ $FLAG || $_ble_decode_keymap == vi_[xs]map ]]; then
   2926       ((index=_ble_edit_ind-ARG,index<0&&(index=0)))
   2927     else
   2928       local width=$ARG line
   2929       while ((width<=_ble_edit_ind)); do
   2930         line=${_ble_edit_str:_ble_edit_ind-width:width}
   2931         line=${line//[!$'\n']$'\n'/x}
   2932         ((${#line}>=ARG)) && break
   2933         ((width+=ARG-${#line}))
   2934       done
   2935       ((index=_ble_edit_ind-width,index<0&&(index=0)))
   2936     fi
   2937   else
   2938     local line=${_ble_edit_str:_ble_edit_ind-ARG:ARG}
   2939     line=${line##*$'\n'}
   2940     ((index=_ble_edit_ind-${#line}))
   2941   fi
   2942 
   2943   ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG"
   2944 }
   2945 
   2946 # nmap ~
   2947 function ble/widget/vi_nmap/forward-char-toggle-case {
   2948   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   2949   local line=${_ble_edit_str:_ble_edit_ind:ARG}
   2950   line=${line%%$'\n'*}
   2951   local len=${#line}
   2952   if ((len==0)); then
   2953     ble/widget/vi-command/bell
   2954     return 1
   2955   fi
   2956 
   2957   local index=$((_ble_edit_ind+len))
   2958   local ret; ble/string#toggle-case "${_ble_edit_str:_ble_edit_ind:len}"
   2959   ble/widget/.replace-range "$_ble_edit_ind" "$index" "$ret"
   2960   ble/keymap:vi/mark/set-previous-edit-area "$_ble_edit_ind" "$index"
   2961   ble/keymap:vi/repeat/record
   2962   ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   2963   _ble_edit_ind=$index
   2964   ble/keymap:vi/adjust-command-mode
   2965   return 0
   2966 }
   2967 
   2968 #------------------------------------------------------------------------------
   2969 # command: [cdy]?[jk]
   2970 
   2971 ## @fn ble/widget/vi-command/.history-relative-line offset
   2972 ##
   2973 ##   @param[in] offset
   2974 ##     移動する相対行数を指定する。負の値は前に移動することを表し、
   2975 ##     正の値は後に移動することを表す。
   2976 ##
   2977 ##   @exit
   2978 ##     全く移動しなかった場合は 1 を返します。
   2979 ##     それ以外の場合は 0 を返します。
   2980 ##
   2981 function ble/widget/vi-command/.history-relative-line {
   2982   local offset=$1
   2983   ((offset)) || return 0
   2984 
   2985   # 履歴が初期化されていないとき最終行にいる。
   2986   if [[ ! $_ble_history_prefix && ! $_ble_history_load_done ]]; then
   2987     ((offset<0)) || return 1
   2988     ble/history/initialize # to use ble/history/get-index
   2989   fi
   2990 
   2991   local index histsize
   2992   ble/history/get-index
   2993   ble/history/get-count -v histsize
   2994 
   2995   local ret count=$((offset<0?-offset:offset)) exit=1
   2996   ((count--))
   2997   while ((count>=0)); do
   2998     if ((offset<0)); then
   2999       ((index>0)) || return "$exit"
   3000       ble/widget/history-prev
   3001       ret=${#_ble_edit_str}
   3002       ble/keymap:vi/needs-eol-fix "$ret" && ((ret--))
   3003       _ble_edit_ind=$ret
   3004     else
   3005       ((index<histsize)) || return "$exit"
   3006       ble/widget/history-next
   3007       _ble_edit_ind=0
   3008     fi
   3009     exit=0
   3010     ble/string#count-char "$_ble_edit_str" $'\n'; local nline=$((ret+1))
   3011     ((count<nline)) && break
   3012     ((count-=nline))
   3013   done
   3014 
   3015   if ((count)); then
   3016     if ((offset<0)); then
   3017       ble-edit/content/find-logical-eol 0 "$((nline-count-1))"
   3018       ble/keymap:vi/needs-eol-fix "$ret" && ((ret--))
   3019     else
   3020       ble-edit/content/find-logical-bol 0 "$count"
   3021     fi
   3022     _ble_edit_ind=$ret
   3023   fi
   3024 
   3025   return 0
   3026 }
   3027 
   3028 ## @fn ble/keymap:vi/get-index-of-relative-line p offset
   3029 ##   列を保持した行移動の先の位置を計算します。
   3030 ##   @param[in,opt] p
   3031 ##     基準となる位置を指定します。空文字列を指定した時は現在位置が使われます。
   3032 ##   @param[in] offset
   3033 ##     移動する行数を指定します。
   3034 ##   @param[out] index
   3035 function ble/keymap:vi/get-index-of-relative-line {
   3036   local ind=${1:-$_ble_edit_ind} offset=$2
   3037   if ((offset==0)); then
   3038     index=$ind
   3039     return 0
   3040   fi
   3041 
   3042   local count=$((offset<0?-offset:offset))
   3043   local ret
   3044   ble-edit/content/find-logical-bol "$ind" 0; local bol1=$ret
   3045   ble-edit/content/find-logical-bol "$ind" "$offset"; local bol2=$ret
   3046   if ble/edit/use-textmap; then
   3047     # 列の表示相対位置 (x,y) を保持
   3048     local b1x b1y; ble/textmap#getxy.cur --prefix=b1 "$bol1"
   3049     local b2x b2y; ble/textmap#getxy.cur --prefix=b2 "$bol2"
   3050 
   3051     ble-edit/content/find-logical-eol "$bol2"; local eol2=$ret
   3052     local c1x c1y; ble/textmap#getxy.cur --prefix=c1 "$ind"
   3053     local e2x e2y; ble/textmap#getxy.cur --prefix=e2 "$eol2"
   3054 
   3055     local x=$c1x y=$((b2y+c1y-b1y))
   3056     ((y>e2y&&(x=e2x,y=e2y)))
   3057 
   3058     ble/textmap#get-index-at "$x" "$y" # local variable "index" is set here
   3059   else
   3060     # 論理列を保持
   3061     ble-edit/content/find-logical-eol "$bol2"; local eol2=$ret
   3062     ((index=bol2+ind-bol1,index>eol2&&(index=eol2)))
   3063   fi
   3064 }
   3065 
   3066 ## @fn ble/widget/vi-command/relative-line.impl offset flag reg opts
   3067 ## @widget vi-command/forward-line  # nmap j
   3068 ## @widget vi-command/backward-line # nmap k
   3069 ##
   3070 ##   j, k による移動の動作について。論理行を移動するとする。
   3071 ##   配置情報があるとき、列は行頭からの相対表示位置 (dx,dy) を保持する。
   3072 ##   配置情報がないとき、論理列を保持する。
   3073 ##
   3074 ##   より前の履歴項目に移った時は列は行末に移る。
   3075 ##   より後の履歴項目に移った時は列は先頭に移る。
   3076 ##
   3077 ##   ToDo: 移動開始時の相対表示位置の記録は現在行っていない。
   3078 ##
   3079 ##   @param[in] offset flag
   3080 ##
   3081 ##   @param[in] opts
   3082 ##     以下の値をコロンで区切って繋げた物を指定する。
   3083 ##
   3084 ##     history
   3085 ##       現在の履歴項目内で要求された行数だけ移動できないとき、
   3086 ##       履歴項目内の論理行を移動する。
   3087 ##       但し flag がある場合は履歴項目の移動は行わない。
   3088 ##
   3089 function ble/widget/vi-command/relative-line.impl {
   3090   local offset=$1 flag=$2 reg=$3 opts=$4
   3091   ((offset==0)) && return 0
   3092   if [[ $flag ]]; then
   3093     ble/widget/vi-command/linewise-goto.impl "$_ble_edit_ind:$offset" "$flag" "$reg" preserve_column:require_multiline
   3094     return "$?"
   3095   fi
   3096 
   3097   # 現在履歴項目内で移動できる行数の判定
   3098   local count=$((offset<0?-offset:offset)) ret
   3099   if ((offset<0)); then
   3100     ble/string#count-char "${_ble_edit_str::_ble_edit_ind}" $'\n'
   3101   else
   3102     ble/string#count-char "${_ble_edit_str:_ble_edit_ind}" $'\n'
   3103   fi
   3104   ((count-=count<ret?count:ret))
   3105 
   3106   # 現在の履歴項目内での探索
   3107   if ((count==0)); then
   3108     local index; ble/keymap:vi/get-index-of-relative-line "$_ble_edit_ind" "$offset"
   3109     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   3110     _ble_edit_ind=$index
   3111     ble/keymap:vi/adjust-command-mode
   3112     return 0
   3113   fi
   3114 
   3115   # 履歴項目を行数を数えつつ移動
   3116   if [[ $_ble_decode_keymap == vi_nmap && :$opts: == *:history:* ]]; then
   3117     if ble/widget/vi-command/.history-relative-line "$((offset>=0?count:-count))" || ((nmove)); then
   3118       ble/keymap:vi/adjust-command-mode
   3119       return 0
   3120     fi
   3121   fi
   3122 
   3123   ble/widget/vi-command/bell
   3124   return 1
   3125 }
   3126 function ble/widget/vi-command/forward-line {
   3127   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3128   ble/widget/vi-command/relative-line.impl "$ARG" "$FLAG" "$REG" history
   3129 }
   3130 function ble/widget/vi-command/backward-line {
   3131   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3132   ble/widget/vi-command/relative-line.impl "$((-ARG))" "$FLAG" "$REG" history
   3133 }
   3134 
   3135 ## @fn ble/widget/vi-command/graphical-relative-line.impl offset flag reg opts
   3136 ## @widget vi-command/graphical-forward-line  # nmap gj
   3137 ## @widget vi-command/graphical-backward-line # nmap gk
   3138 ##
   3139 ##   @param[in] offset
   3140 ##     移動する相対行数。負の値は上の行へ行くことを表す。正の値は下の行へ行くことを表す。
   3141 ##   @param[in] flag
   3142 ##     オペレータを指定する。
   3143 ##   @param[in] opts
   3144 ##     以下のオプションをコロンで繋げたものを指定する。
   3145 ##
   3146 ##     history
   3147 ##
   3148 function ble/widget/vi-command/graphical-relative-line.impl {
   3149   local offset=$1 flag=$2 reg=$3 opts=$4
   3150   local index move
   3151   if ble/edit/use-textmap; then
   3152     local x y ax ay
   3153     ble/textmap#getxy.cur "$_ble_edit_ind"
   3154     ((ax=x,ay=y+offset,
   3155       ay<_ble_textmap_begy?(ay=_ble_textmap_begy):
   3156       (ay>_ble_textmap_endy?(ay=_ble_textmap_endy):0)))
   3157     ble/textmap#get-index-at "$ax" "$ay"
   3158     ble/textmap#getxy.cur --prefix=a "$index"
   3159     ((offset-=move=ay-y))
   3160   else
   3161     local ret ind=$_ble_edit_ind
   3162     ble-edit/content/find-logical-bol "$ind" 0; local bol1=$ret
   3163     ble-edit/content/find-logical-bol "$ind" "$offset"; local bol2=$ret
   3164     ble-edit/content/find-logical-eol "$bol2"; local eol2=$ret
   3165     ((index=bol2+ind-bol1,index>eol2&&(index=eol2)))
   3166 
   3167     if ((index>ind)); then
   3168       ble/string#count-char "${_ble_edit_str:ind:index-ind}" $'\n'
   3169       ((offset+=move=-ret))
   3170     elif ((index<ind)); then
   3171       ble/string#count-char "${_ble_edit_str:index:ind-index}" $'\n'
   3172       ((offset+=move=ret))
   3173     fi
   3174   fi
   3175 
   3176   if ((offset==0)); then
   3177     ble/widget/vi-command/exclusive-goto.impl "$index" "$flag" "$reg"
   3178     return "$?"
   3179   fi
   3180 
   3181   if [[ ! $flag && $_ble_decode_keymap == vi_nmap && :$opts: == *:history:* ]]; then
   3182     if ble/widget/vi-command/.history-relative-line "$offset"; then
   3183       ble/keymap:vi/adjust-command-mode
   3184       return 0
   3185     fi
   3186   fi
   3187 
   3188   # 失敗: オペレータは実行されないが移動はする。
   3189   ((move)) && ble/widget/vi-command/exclusive-goto.impl "$index"
   3190   ble/widget/vi-command/bell
   3191   return 1
   3192 }
   3193 function ble/widget/vi-command/graphical-forward-line {
   3194   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3195   ble/widget/vi-command/graphical-relative-line.impl "$ARG" "$FLAG" "$REG"
   3196 }
   3197 function ble/widget/vi-command/graphical-backward-line {
   3198   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3199   ble/widget/vi-command/graphical-relative-line.impl "$((-ARG))" "$FLAG" "$REG"
   3200 }
   3201 
   3202 #------------------------------------------------------------------------------
   3203 # command: ^ + - _ $
   3204 
   3205 function ble/widget/vi-command/relative-first-non-space.impl {
   3206   local arg=$1 flag=$2 reg=$3 opts=$4
   3207   local ret ind=$_ble_edit_ind
   3208   ble-edit/content/find-logical-bol "$ind" "$arg"; local bolx=$ret
   3209   ble-edit/content/find-non-space "$bolx"; local nolx=$ret
   3210 
   3211   # 2017-09-12 何故か分からないが vim はこういう振る舞いに見える。
   3212   ((_ble_keymap_vi_single_command==2&&_ble_keymap_vi_single_command--))
   3213 
   3214   if [[ $flag ]]; then
   3215     if [[ :$opts: == *:charwise:* ]]; then
   3216       # command: ^
   3217       ble-edit/content/nonbol-eolp "$nolx" && ((nolx--))
   3218       ble/widget/vi-command/exclusive-goto.impl "$nolx" "$flag" "$reg" nobell
   3219     elif [[ :$opts: == *:multiline:* ]]; then
   3220       # command: + -
   3221       ble/widget/vi-command/linewise-goto.impl "$nolx" "$flag" "$reg" require_multiline:bolx="$bolx":nolx="$nolx"
   3222     else
   3223       # command: _
   3224       ble/widget/vi-command/linewise-goto.impl "$nolx" "$flag" "$reg" bolx="$bolx":nolx="$nolx"
   3225     fi
   3226     return "$?"
   3227   fi
   3228 
   3229   local count=$((arg<0?-arg:arg)) nmove=0
   3230   if ((count)); then
   3231     local beg end; ((nolx<ind?(beg=nolx,end=ind):(beg=ind,end=nolx)))
   3232     ble/string#count-char "${_ble_edit_str:beg:end-beg}" $'\n'; nmove=$ret
   3233     ((count-=nmove))
   3234   fi
   3235 
   3236   if ((count==0)); then
   3237     ble/keymap:vi/needs-eol-fix "$nolx" && ((nolx--))
   3238     _ble_edit_ind=$nolx
   3239     ble/keymap:vi/adjust-command-mode
   3240     return 0
   3241   fi
   3242 
   3243   # 履歴項目の移動
   3244   if [[ $_ble_decode_keymap == vi_nmap && :$opts: == *:history:* ]] && ble/widget/vi-command/.history-relative-line "$((arg>=0?count:-count))"; then
   3245     ble/widget/vi-command/first-non-space
   3246   elif ((nmove)); then
   3247     ble/keymap:vi/needs-eol-fix "$nolx" && ((nolx--))
   3248     _ble_edit_ind=$nolx
   3249     ble/keymap:vi/adjust-command-mode
   3250   else
   3251     ble/widget/vi-command/bell
   3252     return 1
   3253   fi
   3254 }
   3255 # nmap ^
   3256 function ble/widget/vi-command/first-non-space {
   3257   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3258   ble/widget/vi-command/relative-first-non-space.impl 0 "$FLAG" "$REG" charwise:history
   3259 }
   3260 # nmap +
   3261 function ble/widget/vi-command/forward-first-non-space {
   3262   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3263   ble/widget/vi-command/relative-first-non-space.impl "$ARG" "$FLAG" "$REG" multiline:history
   3264 }
   3265 # nmap -
   3266 function ble/widget/vi-command/backward-first-non-space {
   3267   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3268   ble/widget/vi-command/relative-first-non-space.impl "$((-ARG))" "$FLAG" "$REG" multiline:history
   3269 }
   3270 # nmap _
   3271 function ble/widget/vi-command/first-non-space-forward {
   3272   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3273   ble/widget/vi-command/relative-first-non-space.impl "$((ARG-1))" "$FLAG" "$REG" history
   3274 }
   3275 # nmap $
   3276 function ble/widget/vi-command/forward-eol {
   3277   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3278   if ((ARG>1)) && [[ ${_ble_edit_str:_ble_edit_ind}  != *$'\n'* ]]; then
   3279     ble/widget/vi-command/bell
   3280     return 1
   3281   fi
   3282 
   3283   local ret index
   3284   ble-edit/content/find-logical-eol "$_ble_edit_ind" "$((ARG-1))"; index=$ret
   3285   ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   3286   ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   3287   [[ $_ble_decode_keymap == vi_[xs]map ]] &&
   3288     ble/keymap:vi/xmap/add-eol-extension # 末尾拡張
   3289 }
   3290 # nmap g0 g<home>
   3291 function ble/widget/vi-command/beginning-of-graphical-line {
   3292   if ble/edit/use-textmap; then
   3293     local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3294     local x y index
   3295     ble/textmap#getxy.cur "$_ble_edit_ind"
   3296     ble/textmap#get-index-at 0 "$y"
   3297     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   3298     ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   3299   else
   3300     ble/widget/vi-command/beginning-of-line
   3301   fi
   3302 }
   3303 # nmap g^
   3304 function ble/widget/vi-command/graphical-first-non-space {
   3305   if ble/edit/use-textmap; then
   3306     local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3307     local x y index ret
   3308     ble/textmap#getxy.cur "$_ble_edit_ind"
   3309     ble/textmap#get-index-at 0 "$y"
   3310     ble-edit/content/find-non-space "$index"
   3311     ble/keymap:vi/needs-eol-fix "$ret" && ((ret--))
   3312     ble/widget/vi-command/exclusive-goto.impl "$ret" "$FLAG" "$REG" nobell
   3313   else
   3314     ble/widget/vi-command/first-non-space
   3315   fi
   3316 }
   3317 # nmap g$ g<end>
   3318 function ble/widget/vi-command/graphical-forward-eol {
   3319   if ble/edit/use-textmap; then
   3320     local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3321     local x y index
   3322     ble/textmap#getxy.cur "$_ble_edit_ind"
   3323     ble/textmap#get-index-at "$((_ble_textmap_cols-1))" "$((y+ARG-1))"
   3324     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   3325     ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   3326   else
   3327     ble/widget/vi-command/forward-eol
   3328   fi
   3329 }
   3330 # nmap gm
   3331 function ble/widget/vi-command/middle-of-graphical-line {
   3332   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3333   local index
   3334   if ble/edit/use-textmap; then
   3335     local x y
   3336     ble/textmap#getxy.cur "$_ble_edit_ind"
   3337     ble/textmap#get-index-at "$((_ble_textmap_cols/2))" "$y"
   3338     ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   3339   else
   3340     local ret
   3341     ble-edit/content/find-logical-bol; local bol=$ret
   3342     ble-edit/content/find-logical-eol; local eol=$ret
   3343     ((index=(bol+${COLUMNS:-eol})/2,
   3344       index>eol&&(index=eol),
   3345       bol<eol&&index==eol&&(index--)))
   3346   fi
   3347   ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   3348 }
   3349 # nmap g_
   3350 function ble/widget/vi-command/last-non-space {
   3351   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3352   local ret
   3353   ble-edit/content/find-logical-eol "$_ble_edit_ind" "$((ARG-1))"; local index=$ret
   3354   if ((ARG>1)) && [[ ${_ble_edit_str:_ble_edit_ind:index-_ble_edit_ind} != *$'\n'* ]]; then
   3355     # 行移動を起こすはずだったのに一行も進めなかった場合は失敗
   3356     ble/widget/vi-command/bell
   3357     return 1
   3358   fi
   3359 
   3360   local rex=$'([^ \t\n]?[ \t]+|[^ \t\n])$'
   3361   [[ ${_ble_edit_str::index} =~ $rex ]] && ((index-=${#BASH_REMATCH}))
   3362   ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   3363 
   3364 }
   3365 
   3366 #------------------------------------------------------------------------------
   3367 # vi_nmap: scroll
   3368 #   C-d, C-u, C-e, C-y
   3369 #   C-b, prior, C-f, next
   3370 
   3371 _ble_keymap_vi_previous_scroll=
   3372 ## @fn ble/widget/vi_nmap/scroll.impl opts
   3373 ##   @arg[in] opts
   3374 ##     forward
   3375 ##     backward
   3376 function ble/widget/vi_nmap/scroll.impl {
   3377   local opts=$1
   3378   local height=${_ble_canvas_panel_height[_ble_textarea_panel]}
   3379 
   3380   # adjust arguments
   3381   local ARG FLAG REG; ble/keymap:vi/get-arg "$_ble_keymap_vi_previous_scroll"
   3382   _ble_keymap_vi_previous_scroll=$ARG
   3383   [[ $ARG ]] || ((ARG=height/2))
   3384   [[ :$opts: == *:backward:* ]] && ((ARG=-ARG))
   3385 
   3386   ble/widget/.update-textmap
   3387   if [[ :$opts: == *:cursor:* ]]; then
   3388     # move
   3389     local x y index ret
   3390     ble/textmap#getxy.cur "$_ble_edit_ind"
   3391     ble/textmap#get-index-at 0 "$((y+ARG))"
   3392     ble-edit/content/find-non-space "$index"
   3393     ble/keymap:vi/needs-eol-fix "$ret" && ((ret--))
   3394     _ble_edit_ind=$ret
   3395     ble/keymap:vi/adjust-command-mode
   3396 
   3397     ((_ble_textmap_endy<height)) && return 0
   3398     local ax ay
   3399     ble/textmap#getxy.cur --prefix=a "$_ble_edit_ind"
   3400     local max_scroll=$((_ble_textmap_endy+1-height))
   3401     ((_ble_textarea_scroll_new+=ay-y))
   3402     if ((_ble_textarea_scroll_new<0)); then
   3403       _ble_textarea_scroll_new=0
   3404     elif ((_ble_textarea_scroll_new>max_scroll)); then
   3405       _ble_textarea_scroll_new=$max_scroll
   3406     fi
   3407   else
   3408     ((_ble_textmap_endy<height)) && return 0
   3409 
   3410     local max_scroll=$((_ble_textmap_endy+1-height))
   3411     ((_ble_textarea_scroll_new+=ARG))
   3412     if ((_ble_textarea_scroll_new<0)); then
   3413       _ble_textarea_scroll_new=0
   3414     elif ((_ble_textarea_scroll_new>max_scroll)); then
   3415       _ble_textarea_scroll_new=$max_scroll
   3416     fi
   3417 
   3418     # ax ay 表示範囲
   3419     local ay=$((_ble_textarea_scroll_new+_ble_textmap_begy))
   3420     local by=$((_ble_textarea_scroll_new+height-1))
   3421     ((_ble_textarea_scroll_new&&ay++))
   3422 
   3423     # カーソル範囲
   3424     ((_ble_textarea_scroll_new!=0&&ay<by&&ay++,
   3425       _ble_textarea_scroll_new!=max_scroll&&ay<by&&by--))
   3426     local x y
   3427     ble/textmap#getxy.cur "$_ble_edit_ind"
   3428     if ((y<ay?(y=ay,1):(y>by?(y=by,1):0))); then
   3429       local index
   3430       ble/textmap#get-index-at "$x" "$y"
   3431       _ble_edit_ind=$index
   3432     fi
   3433 
   3434     ble/keymap:vi/adjust-command-mode
   3435   fi
   3436 }
   3437 
   3438 # nmap C-d
   3439 function ble/widget/vi_nmap/forward-line-scroll {
   3440   ble/widget/vi_nmap/scroll.impl forward:cursor
   3441 }
   3442 # nmap C-u
   3443 function ble/widget/vi_nmap/backward-line-scroll {
   3444   ble/widget/vi_nmap/scroll.impl backward:cursor
   3445 }
   3446 # nmap C-e
   3447 function ble/widget/vi_nmap/forward-scroll {
   3448   ble/widget/vi_nmap/scroll.impl forward
   3449 }
   3450 # nmap C-y
   3451 function ble/widget/vi_nmap/backward-scroll {
   3452   ble/widget/vi_nmap/scroll.impl backward
   3453 }
   3454 
   3455 # nmap C-f, next
   3456 function ble/widget/vi_nmap/pagedown {
   3457   local height=${_ble_canvas_panel_height[_ble_textarea_panel]}
   3458 
   3459   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3460 
   3461   ble/widget/.update-textmap
   3462 
   3463   # 最終行以外にいる事を確認
   3464   local x y
   3465   ble/textmap#getxy.cur "$_ble_edit_ind"
   3466   if ((y==_ble_textmap_endy)); then
   3467     ble/widget/vi-command/bell
   3468     return 1
   3469   fi
   3470 
   3471   # 行き先を決定
   3472   local vheight=$((height-_ble_textmap_begy-1))
   3473   local ybase=$((_ble_textarea_scroll_new+height-1))
   3474   local y1=$((ybase+(ARG-1)*(vheight-2)))
   3475   local index ret
   3476   ble/textmap#get-index-at 0 "$y1"
   3477   ble-edit/content/bolp "$index" &&
   3478     ble-edit/content/find-non-space "$index"; index=$ret
   3479   _ble_edit_ind=$index
   3480 
   3481   # スクロール (現在位置が上から2行目になる様に)
   3482   local max_scroll=$((_ble_textmap_endy+1-height))
   3483   ble/textmap#getxy.cur "$_ble_edit_ind"
   3484   local scroll=$((y<=_ble_textmap_begy+1?0:(y-_ble_textmap_begy-1)))
   3485   ((scroll>max_scroll&&(scroll=max_scroll)))
   3486   _ble_textarea_scroll_new=$scroll
   3487   ble/keymap:vi/adjust-command-mode
   3488 }
   3489 # nmap C-b, prior
   3490 function ble/widget/vi_nmap/pageup {
   3491   local height=${_ble_canvas_panel_height[_ble_textarea_panel]}
   3492 
   3493   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3494 
   3495   ble/widget/.update-textmap
   3496 
   3497   # 少なくとも1行目が表示されていない事を確認
   3498   if ((!_ble_textarea_scroll_new)); then
   3499     ble/widget/vi-command/bell
   3500     return 1
   3501   fi
   3502 
   3503   # 行き先を決定
   3504   local vheight=$((height-_ble_textmap_begy-1))
   3505   local ybase=$((_ble_textarea_scroll_new+_ble_textmap_begy+1))
   3506   local y1=$((ybase-(ARG-1)*(vheight-2)))
   3507   ((y1<_ble_textmap_begy&&(y1=_ble_textmap_begy)))
   3508   local index ret
   3509   ble/textmap#get-index-at 0 "$y1"
   3510   ble-edit/content/bolp "$index" &&
   3511     ble-edit/content/find-non-space "$index"; index=$ret
   3512   _ble_edit_ind=$index
   3513 
   3514   # スクロール (現在位置が下から2行目になる様に)
   3515   local x y
   3516   ble/textmap#getxy.cur "$_ble_edit_ind"
   3517   local scroll=$((y-height+2))
   3518   ((scroll<0&&(scroll=0)))
   3519   _ble_textarea_scroll_new=$scroll
   3520   ble/keymap:vi/adjust-command-mode
   3521 }
   3522 
   3523 function ble/widget/vi_nmap/scroll-to-center.impl {
   3524   local opts=$1
   3525   ble/widget/.update-textmap
   3526   local height=${_ble_canvas_panel_height[_ble_textarea_panel]}
   3527 
   3528   local ARG FLAG REG; ble/keymap:vi/get-arg ''
   3529   if [[ ! $ARG && :$opts: == *:pagedown:* ]]; then
   3530     local y1=$((_ble_textarea_scroll_new+height))
   3531     local index
   3532     ble/textmap#get-index-at 0 "$y1"
   3533     ((_ble_edit_ind=index))
   3534   fi
   3535 
   3536   local ret
   3537   ble-edit/content/find-logical-bol "$_ble_edit_ind"; local bol1=$ret
   3538   if [[ $ARG || :$opts: == *:nol:* ]]; then
   3539     if [[ $ARG ]]; then
   3540       ble-edit/content/find-logical-bol 0 "$((ARG-1))"; local bol2=$ret
   3541     else
   3542       local bol2=$bol1
   3543     fi
   3544 
   3545     if [[ :$opts: == *:nol:* ]]; then
   3546       # 非空白行頭に移動する
   3547       ble-edit/content/find-non-space "$bol2"
   3548       _ble_edit_ind=$ret
   3549     elif ((bol1!=bol2)); then
   3550       # 行内の同じ相対位置に移動する
   3551 
   3552       # dx dy = 行頭からの相対位置
   3553       local b1x b1y p1x p1y dx dy
   3554       ble/textmap#getxy.cur --prefix=b1 "$bol1"
   3555       ble/textmap#getxy.cur --prefix=p1 "$_ble_edit_ind"
   3556       ((dx=p1x,dy=p1y-b1y))
   3557 
   3558       # index = 行き先の行 bol2 の同じ相対位置のインデックス
   3559       local b2x b2y p2x p2y index
   3560       ble/textmap#getxy.cur --prefix=b2 "$bol2"
   3561       ((p2x=b2x,p2y=b2y+dy))
   3562       ble/textmap#get-index-at "$p2x" "$p2y"
   3563 
   3564       if ble-edit/content/find-logical-bol "$index"; ((ret==bol2)); then
   3565         _ble_edit_ind=$index
   3566       else
   3567         # 別の行になっている時は行末に移動
   3568         ble-edit/content/find-logical-eol "$bol2"
   3569         _ble_edit_ind=$ret
   3570       fi
   3571     fi
   3572     ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   3573   fi
   3574 
   3575   # スクロール量の計算
   3576   if ((_ble_textmap_endy+1>height)); then
   3577     local max_scroll=$((_ble_textmap_endy+1-height))
   3578 
   3579     local b1x b1y
   3580     ble/textmap#getxy.cur --prefix=b1 "$bol1"
   3581 
   3582     local scroll=
   3583     if [[ :$opts: == *:top:* ]]; then
   3584       ((scroll=b1y-(_ble_textmap_begy+2)))
   3585     elif [[ :$opts: == *:bottom:* ]]; then
   3586       ((scroll=b1y-(height-2)))
   3587     else
   3588       local vheight=$((height-_ble_textmap_begy-1))
   3589       ((scroll=b1y-(_ble_textmap_begy+1+vheight/2)))
   3590     fi
   3591 
   3592     if ((scroll<0)); then
   3593       scroll=0
   3594     elif ((scroll>max_scroll)); then
   3595       scroll=$max_scroll
   3596     fi
   3597     _ble_textarea_scroll_new=$scroll
   3598   fi
   3599 
   3600   ble/keymap:vi/adjust-command-mode
   3601 }
   3602 
   3603 # nmap zz
   3604 function ble/widget/vi_nmap/scroll-to-center-and-redraw {
   3605   ble/widget/vi_nmap/scroll-to-center.impl
   3606   ble/widget/redraw-line
   3607 }
   3608 # nmap zt
   3609 function ble/widget/vi_nmap/scroll-to-top-and-redraw {
   3610   ble/widget/vi_nmap/scroll-to-center.impl top
   3611   ble/widget/redraw-line
   3612 }
   3613 # nmap zb
   3614 function ble/widget/vi_nmap/scroll-to-bottom-and-redraw {
   3615   ble/widget/vi_nmap/scroll-to-center.impl bottom
   3616   ble/widget/redraw-line
   3617 }
   3618 # nmap z.
   3619 function ble/widget/vi_nmap/scroll-to-center-non-space-and-redraw {
   3620   ble/widget/vi_nmap/scroll-to-center.impl nol
   3621   ble/widget/redraw-line
   3622 }
   3623 # nmap z<C-m>
   3624 function ble/widget/vi_nmap/scroll-to-top-non-space-and-redraw {
   3625   ble/widget/vi_nmap/scroll-to-center.impl top:nol
   3626   ble/widget/redraw-line
   3627 }
   3628 # nmap z-
   3629 function ble/widget/vi_nmap/scroll-to-bottom-non-space-and-redraw {
   3630   ble/widget/vi_nmap/scroll-to-center.impl bottom:nol
   3631   ble/widget/redraw-line
   3632 }
   3633 # nmap z+
   3634 function ble/widget/vi_nmap/scroll-or-pagedown-and-redraw {
   3635   ble/widget/vi_nmap/scroll-to-center.impl top:nol:pagedown
   3636   ble/widget/redraw-line
   3637 }
   3638 
   3639 #------------------------------------------------------------------------------
   3640 # command: p P
   3641 
   3642 ## @fn ble/widget/vi_nmap/paste.impl/block arg [type]
   3643 ##
   3644 ##   @param[in] arg
   3645 ##     挿入する各行の繰り返し回数を指定します。
   3646 ##
   3647 ##   @param[in] type
   3648 ##     graphical を指定すると配置情報を用いて挿入します。
   3649 ##     省略したときは、配置情報があるときにそれを使用します。
   3650 ##     それ以外を指定すると論理列に基いて挿入を行います。
   3651 ##
   3652 ##   @var[in] _ble_edit_kill_ring
   3653 ##     改行区切りの文字列リストです。
   3654 ##
   3655 ##   @var[in] _ble_edit_kill_type == B:*
   3656 ##     B: に続き空白区切りの数字のリストを保持します。
   3657 ##     数字は _ble_edit_kill_ring に含まれる行の数と同じだけ指定します。
   3658 ##     数字は行の途中に挿入する際に後ろに追加する空白の数を表します。
   3659 ##
   3660 function ble/widget/vi_nmap/paste.impl/block {
   3661   local arg=${1:-1} type=$2
   3662   local graphical=
   3663   if [[ $type ]]; then
   3664     [[ $type == graphical ]] && graphical=1
   3665   else
   3666     ble/edit/use-textmap && graphical=1
   3667   fi
   3668 
   3669   local ret cols=$_ble_textmap_cols
   3670 
   3671   local -a afill; ble/string#split-words afill "${_ble_edit_kill_type:2}"
   3672   local atext; ble/string#split-lines atext "$_ble_edit_kill_ring"
   3673   local ntext=${#atext[@]}
   3674 
   3675   if [[ $graphical ]]; then
   3676     ble-edit/content/find-logical-bol; local bol=$ret
   3677     local bx by x y c
   3678     ble/textmap#getxy.cur --prefix=b "$bol"
   3679     ble/textmap#getxy.cur "$_ble_edit_ind"
   3680     ((y-=by,c=y*cols+x))
   3681   else
   3682     ble-edit/content/find-logical-bol; local bol=$ret
   3683     local c=$((_ble_edit_ind-bol))
   3684   fi
   3685 
   3686   local -a ins_beg=() ins_end=() ins_text=()
   3687   local i is_newline=
   3688   for ((i=0;i<ntext;i++)); do
   3689     if ((i>0)); then
   3690       ble-edit/content/find-logical-bol "$bol" 1
   3691       if ((bol==ret)); then
   3692         is_newline=1
   3693       else
   3694         bol=$ret
   3695         [[ $graphical ]] && ble/textmap#getxy.cur --prefix=b "$bol"
   3696       fi
   3697     fi
   3698 
   3699     # 挿入文字列
   3700     local text=${atext[i]}
   3701     local fill=$((afill[i]))
   3702     if ((arg>1)); then
   3703       ret=
   3704       ((fill)) && ble/string#repeat ' ' "$fill"
   3705       ble/string#repeat "$text$ret" "$arg"
   3706       text=${ret::${#ret}-fill}
   3707     fi
   3708 
   3709     # 挿入位置と padding
   3710     local index iend=
   3711     if [[ $is_newline ]]; then
   3712       index=${#_ble_edit_str}
   3713       ble/string#repeat ' ' "$c"
   3714       text=$'\n'$ret$text
   3715 
   3716     elif [[ $graphical ]]; then
   3717       ble-edit/content/find-logical-eol "$bol"; local eol=$ret
   3718       ble/textmap#get-index-at "$x" "$((by+y))"; ((index>eol&&(index=eol)))
   3719 
   3720       # left padding (行末がより左にある、または、全角文字があるとき)
   3721       local ax ay ac; ble/textmap#getxy.out --prefix=a "$index"
   3722       ((ay-=by,ac=ay*cols+ax))
   3723       if ((ac<c)); then
   3724         ble/string#repeat ' ' "$((c-ac))"
   3725         text=$ret$text
   3726 
   3727         # タブを空白に変換
   3728         if ((index<eol)) && [[ ${_ble_edit_str:index:1} == $'\t' ]]; then
   3729           local rx ry rc; ble/textmap#getxy.out --prefix=r "$((index+1))"
   3730           ((rc=(ry-by)*cols+rx))
   3731           ble/string#repeat ' ' "$((rc-c))"
   3732           text=$text$ret
   3733           iend=$((index+1))
   3734         fi
   3735       fi
   3736 
   3737       # right padding (行末がより右にあるとき)
   3738       if ((index<eol&&fill)); then
   3739         ble/string#repeat ' ' "$fill"
   3740         text=$text$ret
   3741       fi
   3742 
   3743     else
   3744       ble-edit/content/find-logical-eol "$bol"; local eol=$ret
   3745       local index=$((bol+c))
   3746 
   3747       if ((index<eol)); then
   3748         if ((fill)); then
   3749           ble/string#repeat ' ' "$fill"
   3750           text=$text$ret
   3751         fi
   3752       elif ((index>eol)); then
   3753         ble/string#repeat ' ' "$((index-eol))"
   3754         text=$ret$text
   3755         index=$eol
   3756       fi
   3757     fi
   3758 
   3759     ble/array#push ins_beg "$index"
   3760     ble/array#push ins_end "${iend:-$index}"
   3761     ble/array#push ins_text "$text"
   3762   done
   3763 
   3764   # 逆順に挿入
   3765   ble/keymap:vi/mark/start-edit-area
   3766   local i=${#ins_beg[@]}
   3767   while ((i--)); do
   3768     local ibeg=${ins_beg[i]} iend=${ins_end[i]} text=${ins_text[i]}
   3769     ble/widget/.replace-range "$ibeg" "$iend" "$text"
   3770   done
   3771   ble/keymap:vi/mark/end-edit-area
   3772   ble/keymap:vi/repeat/record
   3773 
   3774   ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   3775   ble/keymap:vi/adjust-command-mode
   3776 }
   3777 
   3778 function ble/widget/vi_nmap/paste.impl {
   3779   local arg=$1 reg=$2 is_after=$3
   3780   if [[ $reg ]]; then
   3781     local _ble_edit_kill_ring _ble_edit_kill_type
   3782     ble/keymap:vi/register#load "$reg"
   3783   fi
   3784 
   3785   [[ $_ble_edit_kill_ring ]] || return 0
   3786   local ret
   3787   if [[ $_ble_edit_kill_type == L ]]; then
   3788     ble/string#repeat "$_ble_edit_kill_ring" "$arg"
   3789     local content=$ret
   3790 
   3791     local index dbeg dend
   3792     if ((is_after)); then
   3793       ble-edit/content/find-logical-eol; index=$ret
   3794       if ((index==${#_ble_edit_str})); then
   3795         content=$'\n'${content%$'\n'}
   3796         ((dbeg=index+1,dend=index+${#content}))
   3797       else
   3798         ((index++,dbeg=index,dend=index+${#content}-1))
   3799       fi
   3800     else
   3801       ble-edit/content/find-logical-bol
   3802       ((index=ret,dbeg=index,dend=index+${#content}-1))
   3803     fi
   3804 
   3805     ble/widget/.replace-range "$index" "$index" "$content"
   3806     _ble_edit_ind=$dbeg
   3807     ble/keymap:vi/mark/set-previous-edit-area "$dbeg" "$dend"
   3808     ble/keymap:vi/repeat/record
   3809     ble/widget/vi-command/first-non-space
   3810   elif [[ $_ble_edit_kill_type == B:* ]]; then
   3811     if ((is_after)) && ! ble-edit/content/eolp; then
   3812       ((_ble_edit_ind++))
   3813     fi
   3814     ble/widget/vi_nmap/paste.impl/block "$arg"
   3815   else
   3816     if ((is_after)) && ! ble-edit/content/eolp; then
   3817       ((_ble_edit_ind++))
   3818     fi
   3819     ble/string#repeat "$_ble_edit_kill_ring" "$arg"
   3820     local beg=$_ble_edit_ind
   3821     ble/widget/.insert-string "$ret"
   3822     local end=$_ble_edit_ind
   3823     ble/keymap:vi/mark/set-previous-edit-area "$beg" "$end"
   3824     ble/keymap:vi/repeat/record
   3825     [[ $_ble_keymap_vi_single_command ]] || ((_ble_edit_ind--))
   3826     ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   3827     ble/keymap:vi/adjust-command-mode
   3828   fi
   3829   return 0
   3830 }
   3831 
   3832 function ble/widget/vi_nmap/paste-after {
   3833   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3834   ble/widget/vi_nmap/paste.impl "$ARG" "$REG" 1
   3835 }
   3836 function ble/widget/vi_nmap/paste-before {
   3837   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3838   ble/widget/vi_nmap/paste.impl "$ARG" "$REG" 0
   3839 }
   3840 
   3841 #------------------------------------------------------------------------------
   3842 # command: x s X C D
   3843 
   3844 # nmap x, <delete>
   3845 function ble/widget/vi_nmap/kill-forward-char {
   3846   _ble_keymap_vi_opfunc=d
   3847   ble/widget/vi-command/forward-char
   3848 }
   3849 # nmap s
   3850 function ble/widget/vi_nmap/kill-forward-char-and-insert {
   3851   _ble_keymap_vi_opfunc=c
   3852   ble/widget/vi-command/forward-char
   3853 }
   3854 # nmap X
   3855 function ble/widget/vi_nmap/kill-backward-char {
   3856   _ble_keymap_vi_opfunc=d
   3857   ble/widget/vi-command/backward-char
   3858 }
   3859 # nmap D
   3860 function ble/widget/vi_nmap/kill-forward-line {
   3861   _ble_keymap_vi_opfunc=d
   3862   ble/widget/vi-command/forward-eol
   3863 }
   3864 # nmap C
   3865 function ble/widget/vi_nmap/kill-forward-line-and-insert {
   3866   _ble_keymap_vi_opfunc=c
   3867   ble/widget/vi-command/forward-eol
   3868 }
   3869 
   3870 #------------------------------------------------------------------------------
   3871 # command: w W b B e E
   3872 
   3873 function ble/widget/vi-command/forward-word.impl {
   3874   local arg=$1 flag=$2 reg=$3 rex_word=$4
   3875   local ifs=$_ble_term_IFS
   3876   if [[ $flag == c && ${_ble_edit_str:_ble_edit_ind:1} != [$ifs] ]]; then
   3877     # Note: cw cW は特別な動作
   3878     #   http://vim-jp.org/vimdoc-ja/change.html#cw
   3879     ble/widget/vi-command/forward-word-end.impl "$arg" "$flag" "$reg" "$rex_word" allow_here
   3880     return "$?"
   3881   fi
   3882   local b=$'[ \t]' n=$'\n'
   3883   local rex="^((($rex_word)$n?|$b+$n?|$n)($b+$n)*$b*){0,$arg}" # 単語先頭または空行に止まる
   3884   [[ ${_ble_edit_str:_ble_edit_ind} =~ $rex ]]
   3885   local index=$((_ble_edit_ind+${#BASH_REMATCH}))
   3886   if [[ $flag ]]; then
   3887     # :help word-motions の特別規則 (通過した最後の単語が行末にあるとき)
   3888     local rematch1=${BASH_REMATCH[1]}
   3889     if local rex="$n$b*\$"; [[ $rematch1 =~ $rex ]]; then
   3890       local suffix_len=${#BASH_REMATCH}
   3891       ((suffix_len<${#rematch1})) &&
   3892         ((index-=suffix_len))
   3893     fi
   3894   fi
   3895   ble/widget/vi-command/exclusive-goto.impl "$index" "$flag" "$reg"
   3896 }
   3897 function ble/widget/vi-command/forward-word-end.impl {
   3898   local arg=$1 flag=$2 reg=$3 rex_word=$4 opts=$5
   3899   local IFS=$_ble_term_IFS
   3900   local rex="^([$IFS]*($rex_word)?){0,$arg}" # 単語末端に止まる。空行には止まらない
   3901   local offset=1; [[ :$opts: == *:allow_here:* ]] && offset=0
   3902   [[ ${_ble_edit_str:_ble_edit_ind+offset} =~ $rex ]]
   3903   local index=$((_ble_edit_ind+offset+${#BASH_REMATCH}-1))
   3904   ((index<_ble_edit_ind&&(index=_ble_edit_ind)))
   3905   [[ ! $flag && $BASH_REMATCH && ${_ble_edit_str:index:1} == [$IFS] ]] && ble/widget/.bell
   3906   ble/widget/vi-command/inclusive-goto.impl "$index" "$flag" "$reg"
   3907 }
   3908 function ble/widget/vi-command/backward-word.impl {
   3909   local arg=$1 flag=$2 reg=$3 rex_word=$4
   3910   local b=$'[ \t]' n=$'\n'
   3911   local rex="((($rex_word)$n?|$b+$n?|$n)($b+$n)*$b*){0,$arg}\$" # 単語先頭または空行に止まる
   3912   [[ ${_ble_edit_str::_ble_edit_ind} =~ $rex ]]
   3913   local index=$((_ble_edit_ind-${#BASH_REMATCH}))
   3914   ble/widget/vi-command/exclusive-goto.impl "$index" "$flag" "$reg"
   3915 }
   3916 function ble/widget/vi-command/backward-word-end.impl {
   3917   local arg=$1 flag=$2 reg=$3 rex_word=$4
   3918   local i=$'[ \t\n]' b=$'[ \t]' n=$'\n' w="($rex_word)"
   3919   local rex1="(^|$w$n?|$n)($b+$n)*$b*"
   3920   local rex="($rex1)($rex1){$((arg-1))}($rex_word|$i)\$" # 単語末端または空行に止まる
   3921   [[ ${_ble_edit_str::_ble_edit_ind+1} =~ $rex ]]
   3922   local index=$((_ble_edit_ind+1-${#BASH_REMATCH}))
   3923   local rematch3=${BASH_REMATCH[3]} # 最初の ($rex_word)
   3924   [[ $rematch3 ]] && ((index+=${#rematch3}-1))
   3925   ble/widget/vi-command/inclusive-goto.impl "$index" "$flag" "$reg"
   3926 }
   3927 
   3928 # motion w
   3929 function ble/widget/vi-command/forward-vword {
   3930   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3931   ble/widget/vi-command/forward-word.impl "$ARG" "$FLAG" "$REG" "$_ble_keymap_vi_REX_WORD"
   3932 }
   3933 # motion e
   3934 function ble/widget/vi-command/forward-vword-end {
   3935   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3936   ble/widget/vi-command/forward-word-end.impl "$ARG" "$FLAG" "$REG" "$_ble_keymap_vi_REX_WORD"
   3937 }
   3938 # motion b
   3939 function ble/widget/vi-command/backward-vword {
   3940   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3941   ble/widget/vi-command/backward-word.impl "$ARG" "$FLAG" "$REG" "$_ble_keymap_vi_REX_WORD"
   3942 }
   3943 # motion ge
   3944 function ble/widget/vi-command/backward-vword-end {
   3945   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3946   ble/widget/vi-command/backward-word-end.impl "$ARG" "$FLAG" "$REG" "$_ble_keymap_vi_REX_WORD"
   3947 }
   3948 # motion W
   3949 function ble/widget/vi-command/forward-uword {
   3950   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3951   ble/widget/vi-command/forward-word.impl "$ARG" "$FLAG" "$REG" $'[^ \t\n]+'
   3952 }
   3953 # motion E
   3954 function ble/widget/vi-command/forward-uword-end {
   3955   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3956   ble/widget/vi-command/forward-word-end.impl "$ARG" "$FLAG" "$REG" $'[^ \t\n]+'
   3957 }
   3958 # motion B
   3959 function ble/widget/vi-command/backward-uword {
   3960   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3961   ble/widget/vi-command/backward-word.impl "$ARG" "$FLAG" "$REG" $'[^ \t\n]+'
   3962 }
   3963 # motion gE
   3964 function ble/widget/vi-command/backward-uword-end {
   3965   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3966   ble/widget/vi-command/backward-word-end.impl "$ARG" "$FLAG" "$REG" $'[^ \t\n]+'
   3967 }
   3968 
   3969 #------------------------------------------------------------------------------
   3970 # command: [cdy]?[|HL] G gg
   3971 
   3972 # nmap |
   3973 function ble/widget/vi-command/nth-column {
   3974   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   3975 
   3976   local ret index
   3977   ble-edit/content/find-logical-bol; local bol=$ret
   3978   ble-edit/content/find-logical-eol; local eol=$ret
   3979   if ble/edit/use-textmap; then
   3980     local bx by; ble/textmap#getxy.cur --prefix=b "$bol" # Note: 先頭行はプロンプトにより bx!=0
   3981     local ex ey; ble/textmap#getxy.cur --prefix=e "$eol"
   3982     local dstx=$((bx+ARG-1)) dsty=$by cols=${COLUMNS:-80}
   3983     ((dsty+=dstx/cols,dstx%=cols))
   3984     ((dsty>ey&&(dsty=ey,dstx=ex)))
   3985     ble/textmap#get-index-at "$dstx" "$dsty" # local variable "index" is set here
   3986 
   3987     # Note: 何故かノーマルモードで d や c を実行するときには行末に行かないのに、
   3988     # ビジュアルモードでは行末に行くことができるようだ。
   3989     [[ $_ble_decode_keymap != vi_[xs]map ]] &&
   3990       ble-edit/content/nonbol-eolp "$index" && ((index--))
   3991   else
   3992     [[ $_ble_decode_keymap != vi_[xs]map ]] &&
   3993       ble-edit/content/nonbol-eolp "$eol" && ((eol--))
   3994     ((index=bol+ARG-1,index>eol&&(index=eol)))
   3995   fi
   3996 
   3997   ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   3998 }
   3999 
   4000 # nmap H
   4001 function ble/widget/vi-command/nth-line {
   4002   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4003   [[ $FLAG ]] || ble/keymap:vi/mark/set-jump # ``
   4004   ble/widget/vi-command/linewise-goto.impl "0:$((ARG-1))" "$FLAG" "$REG"
   4005 }
   4006 # nmap L
   4007 function ble/widget/vi-command/nth-last-line {
   4008   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4009   [[ $FLAG ]] || ble/keymap:vi/mark/set-jump # ``
   4010   ble/widget/vi-command/linewise-goto.impl "${#_ble_edit_str}:$((-(ARG-1)))" "$FLAG" "$REG"
   4011 }
   4012 
   4013 # nmap gg in history
   4014 function ble/widget/vi-command/history-beginning {
   4015   local ARG FLAG REG; ble/keymap:vi/get-arg 0
   4016   if [[ $FLAG ]]; then
   4017     if ((ARG)); then
   4018       _ble_keymap_vi_oparg=$ARG
   4019     else
   4020       _ble_keymap_vi_oparg=
   4021     fi
   4022     _ble_keymap_vi_opfunc=$FLAG
   4023     _ble_keymap_vi_reg=$REG
   4024     ble/widget/vi-command/nth-line
   4025     return "$?"
   4026   fi
   4027 
   4028   if ((ARG)); then
   4029     ble-edit/history/goto "$((ARG-1))"
   4030   else
   4031     ble/widget/history-beginning
   4032   fi
   4033   ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   4034   ble/keymap:vi/adjust-command-mode
   4035   return 0
   4036 }
   4037 
   4038 # nmap G in history
   4039 function ble/widget/vi-command/history-end {
   4040   local ARG FLAG REG; ble/keymap:vi/get-arg 0
   4041   if [[ $FLAG ]]; then
   4042     _ble_keymap_vi_opfunc=$FLAG
   4043     _ble_keymap_vi_reg=$REG
   4044     if ((ARG)); then
   4045       _ble_keymap_vi_oparg=$ARG
   4046       ble/widget/vi-command/nth-line
   4047     else
   4048       _ble_keymap_vi_oparg=
   4049       ble/widget/vi-command/nth-last-line
   4050     fi
   4051     return "$?"
   4052   fi
   4053 
   4054   if ((ARG)); then
   4055     ble-edit/history/goto "$((ARG-1))"
   4056   else
   4057     ble/widget/history-end
   4058   fi
   4059   ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   4060   ble/keymap:vi/adjust-command-mode
   4061   return 0
   4062 }
   4063 
   4064 # nmap G
   4065 #   Note: vim では G はこの振る舞いだが、blesh では実際には
   4066 #     vi-command/history-end が束縛されるのでこれは既定では使われない。
   4067 function ble/widget/vi-command/last-line {
   4068   local ARG FLAG REG; ble/keymap:vi/get-arg 0
   4069   [[ $FLAG ]] || ble/keymap:vi/mark/set-jump # ``
   4070   if ((ARG)); then
   4071     ble/widget/vi-command/linewise-goto.impl "0:$((ARG-1))" "$FLAG" "$REG"
   4072   else
   4073     ble/widget/vi-command/linewise-goto.impl "${#_ble_edit_str}:0" "$FLAG" "$REG"
   4074   fi
   4075 }
   4076 
   4077 # nmap C-home / gg
   4078 #   Note: nth-line (H) との違いは jump でない事のみである。
   4079 #   Note: vim では gg もこの振る舞いだが、blesh では gg は
   4080 #     既定では vi-command/history-beginning に束縛される。
   4081 function ble/widget/vi-command/first-nol {
   4082   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4083   ble/widget/vi-command/linewise-goto.impl "0:$((ARG-1))" "$FLAG" "$REG"
   4084 }
   4085 
   4086 # nmap C-end
   4087 function ble/widget/vi-command/last-eol {
   4088   local ARG FLAG REG; ble/keymap:vi/get-arg ''
   4089   local ret index
   4090   if [[ $ARG ]]; then
   4091     ble-edit/content/find-logical-eol 0 "$((ARG-1))"; index=$ret
   4092   else
   4093     ble-edit/content/find-logical-eol "${#_ble_edit_str}"; index=$ret
   4094   fi
   4095   ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   4096   ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   4097 }
   4098 
   4099 #------------------------------------------------------------------------------
   4100 # command: r gr
   4101 
   4102 ## @fn ble/widget/vi_nmap/replace-char.impl code [overwrite_mode]
   4103 ##   @param[in] overwrite_mode
   4104 ##     置換する文字の挿入方法を指定します。
   4105 function ble/widget/vi_nmap/replace-char.impl {
   4106   local key=$1 overwrite_mode=${2:-R}
   4107   _ble_edit_overwrite_mode=
   4108 
   4109   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4110   local ret
   4111   if ((key==(_ble_decode_Ctrl|91))); then # C-[
   4112     ble/keymap:vi/adjust-command-mode
   4113     return 27
   4114   elif ! ble/keymap:vi/k2c "$key"; then
   4115     ble/widget/vi-command/bell
   4116     return 1
   4117   fi
   4118 
   4119   local pos=$_ble_edit_ind
   4120 
   4121   ble/keymap:vi/mark/start-edit-area
   4122   {
   4123     local -a KEYS; KEYS=("$ret")
   4124     local _ble_edit_arg=$ARG
   4125     local _ble_edit_overwrite_mode=$overwrite_mode
   4126     local ble_widget_self_insert_opts=nolineext
   4127     ble/widget/self-insert
   4128     ble/util/unlocal KEYS
   4129   }
   4130   ble/keymap:vi/mark/end-edit-area
   4131   ble/keymap:vi/repeat/record
   4132 
   4133   ((pos<_ble_edit_ind&&_ble_edit_ind--))
   4134   ble/keymap:vi/adjust-command-mode
   4135   return 0
   4136 }
   4137 
   4138 function ble/widget/vi_nmap/replace-char.hook {
   4139   ble/widget/vi_nmap/replace-char.impl "$1" R
   4140 }
   4141 function ble/widget/vi_nmap/replace-char {
   4142   _ble_edit_overwrite_mode=R
   4143   ble/keymap:vi/async-read-char ble/widget/vi_nmap/replace-char.hook
   4144 }
   4145 function ble/widget/vi_nmap/virtual-replace-char.hook {
   4146   ble/widget/vi_nmap/replace-char.impl "$1" 1
   4147 }
   4148 function ble/widget/vi_nmap/virtual-replace-char {
   4149   _ble_edit_overwrite_mode=1
   4150   ble/keymap:vi/async-read-char ble/widget/vi_nmap/virtual-replace-char.hook
   4151 }
   4152 
   4153 #------------------------------------------------------------------------------
   4154 # command: J gJ o O
   4155 
   4156 # nmap J
   4157 function ble/widget/vi_nmap/connect-line-with-space {
   4158   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4159   local ret
   4160   ble-edit/content/find-logical-eol; local eol1=$ret
   4161   ble-edit/content/find-logical-eol "$_ble_edit_ind" "$((ARG<=1?1:ARG-1))"; local eol2=$ret
   4162   ble-edit/content/find-logical-bol "$eol2"; local bol2=$ret
   4163   if ((eol1<eol2)); then
   4164     local text=${_ble_edit_str:eol1:eol2-eol1}
   4165     text=${text//$'\n'/' '}
   4166     ble/widget/.replace-range "$eol1" "$eol2" "$text"
   4167     ble/keymap:vi/mark/set-previous-edit-area "$eol1" "$eol2"
   4168     ble/keymap:vi/repeat/record
   4169     _ble_edit_ind=$((bol2-1))
   4170     ble/keymap:vi/adjust-command-mode
   4171     return 0
   4172   else
   4173     ble/widget/vi-command/bell
   4174     return 1
   4175   fi
   4176 }
   4177 # nmap gJ
   4178 function ble/widget/vi_nmap/connect-line {
   4179   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4180   local ret
   4181   ble-edit/content/find-logical-eol; local eol1=$ret
   4182   ble-edit/content/find-logical-eol "$_ble_edit_ind" "$((ARG<=1?1:ARG-1))"; local eol2=$ret
   4183   ble-edit/content/find-logical-bol "$eol2"; local bol2=$ret
   4184   if ((eol1<eol2)); then
   4185     local text=${_ble_edit_str:eol1:bol2-eol1}
   4186     text=${text//$'\n'}
   4187     ble/widget/.replace-range "$eol1" "$bol2" "$text"
   4188     local delta=$((${#text}-(bol2-eol1)))
   4189     ble/keymap:vi/mark/set-previous-edit-area "$eol1" "$((eol2+delta))"
   4190     ble/keymap:vi/repeat/record
   4191     _ble_edit_ind=$((bol2+delta))
   4192     ble/keymap:vi/adjust-command-mode
   4193     return 0
   4194   else
   4195     ble/widget/vi-command/bell
   4196     return 1
   4197   fi
   4198 }
   4199 
   4200 function ble/widget/vi_nmap/insert-mode-at-forward-line {
   4201   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4202   local ret
   4203   ble-edit/content/find-logical-bol; local bol=$ret
   4204   ble-edit/content/find-logical-eol; local eol=$ret
   4205   ble-edit/content/find-non-space "$bol"; local indent=${_ble_edit_str:bol:ret-bol}
   4206   _ble_edit_ind=$eol
   4207   ble/widget/.insert-string $'\n'"$indent"
   4208   ble/widget/vi_nmap/.insert-mode "$ARG"
   4209   ble/keymap:vi/repeat/record
   4210   return 0
   4211 }
   4212 function ble/widget/vi_nmap/insert-mode-at-backward-line {
   4213   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4214   local ret
   4215   ble-edit/content/find-logical-bol; local bol=$ret
   4216   ble-edit/content/find-non-space "$bol"; local indent=${_ble_edit_str:bol:ret-bol}
   4217   _ble_edit_ind=$bol
   4218   ble/widget/.insert-string "$indent"$'\n'
   4219   _ble_edit_ind=$((bol+${#indent}))
   4220   ble/widget/vi_nmap/.insert-mode "$ARG"
   4221   ble/keymap:vi/repeat/record
   4222   return 0
   4223 }
   4224 
   4225 #------------------------------------------------------------------------------
   4226 # command: f F t F
   4227 
   4228 
   4229 ## @var _ble_keymap_vi_char_search
   4230 ##   前回の ble/widget/vi-command/search-char.impl/core の検索を記録します。
   4231 _ble_keymap_vi_char_search=
   4232 
   4233 ## @fn ble/widget/vi-command/search-char.impl/core opts key|char
   4234 ##
   4235 ##   @param[in] opts
   4236 ##     以下のフラグ文字から構成される文字列です。
   4237 ##
   4238 ##     b 後方検索であることを表します。
   4239 ##
   4240 ##     p 見つかった文字の1つ手前に移動することを表します。
   4241 ##
   4242 ##     r 繰り返し検索であることを表します。
   4243 ##       このとき第1引数は文字 char と解釈されます。
   4244 ##       これ以外のとき第1引数はキーコード key と解釈されます。
   4245 ##
   4246 ##   @param[in] key
   4247 ##   @param[in] char
   4248 ##     key は検索対象のキーコードを指定します。
   4249 ##     char は検索対象の文字を指定します。
   4250 ##     どちらで解釈されるかは後述する opts のフラグ r に依存します。
   4251 ##
   4252 ##
   4253 function ble/widget/vi-command/search-char.impl/core {
   4254   local opts=$1 key=$2
   4255   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4256 
   4257   local ret c
   4258   [[ $opts != *p* ]]; local isprev=$?
   4259   [[ $opts != *r* ]]; local isrepeat=$?
   4260   if ((isrepeat)); then
   4261     c=$key
   4262   elif ((key==(_ble_decode_Ctrl|91))); then # C-[ -> cancel
   4263     return 27
   4264   else
   4265     ble/keymap:vi/k2c "$key" || return 1
   4266     ble/util/c2s "$ret"; local c=$ret
   4267   fi
   4268   [[ $c ]] || return 1
   4269 
   4270   ((isrepeat)) || _ble_keymap_vi_char_search=$c$opts
   4271 
   4272   local index
   4273   if [[ $opts == *b* ]]; then
   4274     # backward search
   4275     ble-edit/content/find-logical-bol; local bol=$ret
   4276     local base=$_ble_edit_ind
   4277     ((isrepeat&&isprev&&base--,base>bol)) || return 1
   4278     local line=${_ble_edit_str:bol:base-bol}
   4279     ble/string#last-index-of "$line" "$c" "$ARG"
   4280     ((ret>=0)) || return 1
   4281 
   4282     ((index=bol+ret,isprev&&index++))
   4283     ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   4284     return "$?"
   4285   else
   4286     # forward search
   4287     ble-edit/content/find-logical-eol; local eol=$ret
   4288     local base=$((_ble_edit_ind+1))
   4289     ((isrepeat&&isprev&&base++,base<eol)) || return 1
   4290 
   4291     local line=${_ble_edit_str:base:eol-base}
   4292     ble/string#index-of "$line" "$c" "$ARG"
   4293     ((ret>=0)) || return 1
   4294 
   4295     ((index=base+ret,isprev&&index--))
   4296     ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   4297     return "$?"
   4298   fi
   4299 }
   4300 function ble/widget/vi-command/search-char.impl {
   4301   if ble/widget/vi-command/search-char.impl/core "$1" "$2"; then
   4302     ble/keymap:vi/adjust-command-mode
   4303     return 0
   4304   else
   4305     ble/widget/vi-command/bell
   4306     return 1
   4307   fi
   4308 }
   4309 
   4310 function ble/widget/vi-command/search-forward-char {
   4311   ble/keymap:vi/async-read-char ble/widget/vi-command/search-char.impl f
   4312 }
   4313 function ble/widget/vi-command/search-forward-char-prev {
   4314   ble/keymap:vi/async-read-char ble/widget/vi-command/search-char.impl fp
   4315 }
   4316 function ble/widget/vi-command/search-backward-char {
   4317   ble/keymap:vi/async-read-char ble/widget/vi-command/search-char.impl b
   4318 }
   4319 function ble/widget/vi-command/search-backward-char-prev {
   4320   ble/keymap:vi/async-read-char ble/widget/vi-command/search-char.impl bp
   4321 }
   4322 function ble/widget/vi-command/search-char-repeat {
   4323   [[ $_ble_keymap_vi_char_search ]] || ble/widget/.bell
   4324   local c=${_ble_keymap_vi_char_search::1} opts=${_ble_keymap_vi_char_search:1}
   4325   ble/widget/vi-command/search-char.impl "r$opts" "$c"
   4326 }
   4327 function ble/widget/vi-command/search-char-reverse-repeat {
   4328   [[ $_ble_keymap_vi_char_search ]] || ble/widget/.bell
   4329   local c=${_ble_keymap_vi_char_search::1} opts=${_ble_keymap_vi_char_search:1}
   4330   if [[ $opts == *b* ]]; then
   4331     opts=f${opts//b}
   4332   else
   4333     opts=b${opts//f}
   4334   fi
   4335   ble/widget/vi-command/search-char.impl "r$opts" "$c"
   4336 }
   4337 
   4338 #------------------------------------------------------------------------------
   4339 # command: %
   4340 
   4341 ## @fn ble/widget/vi-command/search-matchpair/.search-forward
   4342 ##   @var[in] _ble_edit_str, ch1, ch2, index
   4343 ##   @var[out] ret
   4344 function ble/widget/vi-command/search-matchpair/.search-forward {
   4345   ble/string#index-of-chars "$_ble_edit_str" "$ch1$ch2" "$((index+1))"
   4346 }
   4347 function ble/widget/vi-command/search-matchpair/.search-backward {
   4348   ble/string#last-index-of-chars "$_ble_edit_str" "$ch1$ch2" "$index"
   4349 }
   4350 
   4351 function ble/widget/vi-command/search-matchpair-or {
   4352   local ARG FLAG REG; ble/keymap:vi/get-arg -1
   4353   if ((ARG>=0)); then
   4354     _ble_keymap_vi_oparg=$ARG
   4355     _ble_keymap_vi_opfunc=$FLAG
   4356     _ble_keymap_vi_reg=$REG
   4357     ble/widget/"$@"
   4358     return "$?"
   4359   fi
   4360 
   4361   local open='({[' close=')}]'
   4362 
   4363   local ret
   4364   ble-edit/content/find-logical-eol; local eol=$ret
   4365   if ! ble/string#index-of-chars "${_ble_edit_str::eol}" '(){}[]' "$_ble_edit_ind"; then
   4366     ble/keymap:vi/adjust-command-mode
   4367     return 1
   4368   fi
   4369   local index1=$ret ch1=${_ble_edit_str:ret:1}
   4370 
   4371   if [[ $ch1 == ["$open"] ]]; then
   4372     local i=${open%%"$ch"*}; i=${#i}
   4373     local ch2=${close:i:1}
   4374     local searcher=ble/widget/vi-command/search-matchpair/.search-forward
   4375   else
   4376     local i=${close%%"$ch"*}; i=${#i}
   4377     local ch2=${open:i:1}
   4378     local searcher=ble/widget/vi-command/search-matchpair/.search-backward
   4379   fi
   4380 
   4381   local index=$index1 count=1
   4382   while "$searcher"; do
   4383     index=$ret
   4384     if [[ ${_ble_edit_str:ret:1} == "$ch1" ]]; then
   4385       ((++count))
   4386     else
   4387       ((--count==0)) && break
   4388     fi
   4389   done
   4390 
   4391   if ((count)); then
   4392     ble/keymap:vi/adjust-command-mode
   4393     return 1
   4394   fi
   4395 
   4396   [[ $FLAG ]] || ble/keymap:vi/mark/set-jump # ``
   4397   ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   4398 }
   4399 
   4400 function ble/widget/vi-command/percentage-line {
   4401   local ARG FLAG REG; ble/keymap:vi/get-arg 0
   4402   local ret; ble/string#count-char "$_ble_edit_str" $'\n'; local nline=$((ret+1))
   4403   local iline=$(((ARG*nline+99)/100))
   4404   ble/widget/vi-command/linewise-goto.impl "0:$((iline-1))" "$FLAG" "$REG"
   4405 }
   4406 
   4407 #------------------------------------------------------------------------------
   4408 # command: go
   4409 
   4410 function ble/widget/vi-command/nth-byte {
   4411   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   4412   ((ARG--))
   4413   local offset=0 text=$_ble_edit_str len=${#_ble_edit_str}
   4414   local left nleft ret
   4415   while ((ARG>0&&len>1)); do
   4416     left=${text::len/2}
   4417     ble/util/strlen "$left"; nleft=$ret
   4418     if ((ARG<nleft)); then
   4419       text=$left
   4420       ((len/=2))
   4421     else
   4422       text=${text:len/2}
   4423       ((offset+=len/2,
   4424         ARG-=nleft,
   4425         len-=len/2))
   4426     fi
   4427   done
   4428   ble/keymap:vi/needs-eol-fix "$offset" && ((offset--))
   4429   ble/widget/vi-command/exclusive-goto.impl "$offset" "$FLAG" "$REG" nobell
   4430 }
   4431 
   4432 #------------------------------------------------------------------------------
   4433 # text objects
   4434 
   4435 _ble_keymap_vi_text_object=
   4436 
   4437 ## @fn ble/keymap:vi/text-object/word.impl      arg flag reg type
   4438 ## @fn ble/keymap:vi/text-object/quote.impl     arg flag reg type
   4439 ## @fn ble/keymap:vi/text-object/block.impl     arg flag reg type
   4440 ## @fn ble/keymap:vi/text-object/tag.impl       arg flag reg type
   4441 ## @fn ble/keymap:vi/text-object/sentence.impl  arg flag reg type
   4442 ## @fn ble/keymap:vi/text-object/paragraph.impl arg flag reg type
   4443 ##
   4444 ##   @exit テキストオブジェクトの処理が完了したときに 0 を返します。
   4445 ##
   4446 
   4447 
   4448 ## @fn ble/keymap:vi/text-object/word.extend-forward
   4449 ##   Note #D0855
   4450 ##   @var[in] type arg
   4451 ##   @var[in] rex_word nl space ifs
   4452 ##   @var[in,out] beg end
   4453 ##   @var[out] flags
   4454 ##     A 先頭に空白が含まれる事を表す。
   4455 ##     Z 末尾に空白が含まれる事を表す。
   4456 ##     I 単語前半の取り込みが試みられた事を表す。
   4457 function ble/keymap:vi/text-object/word.extend-forward {
   4458   local rex
   4459 
   4460   flags=
   4461   [[ ${_ble_edit_str:beg:1} == ["$ifs"] ]] && flags=${flags}A
   4462   if [[ $_ble_decode_keymap != vi_[xs]map ]]; then
   4463     flags=${flags}I
   4464   elif ((_ble_edit_mark==_ble_edit_ind)); then
   4465     flags=${flags}I
   4466   fi
   4467 
   4468   local rex_unit
   4469   local W='('$rex_word')' b='['$space']' n=$nl
   4470   if [[ $type == i* ]]; then
   4471     rex_unit='^'$W'|^'$b'+|^'$n
   4472   elif [[ $type == a* ]]; then
   4473     rex_unit='^'$W$b'*|^'$b'+'$W'|^'$b'*'$n'('$b'+'$n')*('$n'|'$b'*'$W')'
   4474   else
   4475     return 1
   4476   fi
   4477 
   4478   local i rematch=
   4479   for ((i=0;i<arg;i++)); do
   4480     if ((i==0)) && [[ $flags == *I* ]]; then
   4481       # 単語前方を取り込む
   4482       rex='('$rex_word')$|['$space']*['$ifs']$'
   4483       [[ ${_ble_edit_str::beg+1} =~ $rex ]] &&
   4484         ((beg-=${#BASH_REMATCH}-1,end=beg))
   4485     else
   4486       [[ ${_ble_edit_str:end:1} == $'\n' ]] && ((end++))
   4487     fi
   4488 
   4489     [[ ${_ble_edit_str:end} =~ $rex_unit ]] || return 1
   4490     rematch=$BASH_REMATCH
   4491     ((end+=${#rematch}))
   4492 
   4493     # Note: aw に対する正規表現では二重改行を読むが後退する。
   4494     [[ $type == a* && $rematch == *$'\n\n' ]] && ((end--))
   4495 
   4496     # Note: Vim では何故か最初の一致だけ改行を除去。
   4497     #   最後の一致の改行は exclusive にする事で、
   4498     #   呼び出し元に除去させている様な気がする。
   4499     # Note: aw の時は "非空白から改行" に一致する事はない。
   4500     if ((i==0)) && [[ $flags == *I* ]] || ((i==arg-1)); then
   4501       [[ $type == i* && $rematch == *"$nl" ]] && ((end--))
   4502     fi
   4503   done
   4504 
   4505   [[ ${_ble_edit_str:end-1:1} == *["$ifs"] ]] && flags=${flags}Z
   4506 
   4507   if [[ $type == a* && $flags != *[AZ]* ]]; then
   4508     # aw で前後に空白が含まれない時、前方の空白を取り込む
   4509     # Note: vim の実装 (search.c (current_word)) では
   4510     #   行頭 exclusive でも前方空白を取り込むが、
   4511     #   aw において行頭 exclusive になる事は普通はないので謎。
   4512     #   virtual_active() の時行の途中で oneleft() が失敗する事はあるが、
   4513     #   この様な状況を意図してこの条件が加えられたとは思えない。
   4514     if rex='['$space']+$'; [[ ${_ble_edit_str::beg} =~ $rex ]]; then
   4515       local p=$((beg-${#BASH_REMATCH}))
   4516       ble-edit/content/bolp "$p" || beg=$p
   4517     fi
   4518   fi
   4519 
   4520   return 0
   4521 }
   4522 ## @fn ble/keymap:vi/text-object/word.extend-backward
   4523 ##   Note #D0855
   4524 ##   @var[in,out] beg
   4525 ##   @var[in] type arg
   4526 ##   @var[in] rex_word nl space ifs
   4527 function ble/keymap:vi/text-object/word.extend-backward {
   4528   local rex_unit=
   4529   local W='('$rex_word')' b='['$space']' n=$nl
   4530   if [[ $type == i* ]]; then
   4531     rex_unit='('$W'|'$b'+)'$n'?$|'$n'$'
   4532   elif [[ $type == a* ]]; then
   4533     rex_unit=$b'*'$W$n'?$|'$W'?'$b'*('$n'('$b'+'$n')*'$b'*)?('$b$n'?|'$n')$'
   4534   else
   4535     return 1
   4536   fi
   4537 
   4538   local count=$arg
   4539   while ((count--)); do
   4540     [[ ${_ble_edit_str::beg} =~ $rex_unit ]] || return 1
   4541     ((beg-=${#BASH_REMATCH}))
   4542 
   4543     # Note: vim の振る舞いに倣って
   4544     local match=${BASH_REMATCH%"$nl"}
   4545     if ((beg==0&&${#match}>=2)); then
   4546       if [[ $type == i* ]]; then
   4547         [[ $match == ["$space"]* ]] && beg=1
   4548       elif [[ $type == a* ]]; then
   4549         [[ $match == *[!"$ifs"] ]] && beg=1
   4550       fi
   4551     fi
   4552   done
   4553 
   4554   return 0
   4555 }
   4556 
   4557 function ble/keymap:vi/text-object/word.impl {
   4558   local arg=$1 flag=$2 reg=$3 type=$4
   4559   local space=$' \t' nl=$'\n' ifs=$_ble_term_IFS
   4560   ((arg==0)) && return 0
   4561 
   4562   local rex_word
   4563   if [[ $type == ?W ]]; then
   4564     rex_word="[^$ifs]+"
   4565   else
   4566     rex_word=$_ble_keymap_vi_REX_WORD
   4567   fi
   4568 
   4569   local index=$_ble_edit_ind
   4570   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   4571     if ((index<_ble_edit_mark)); then
   4572       local beg=$index
   4573       if ble/keymap:vi/text-object/word.extend-backward; then
   4574         _ble_edit_ind=$beg
   4575       else
   4576         _ble_edit_ind=0
   4577         ble/widget/.bell
   4578       fi
   4579       ble/keymap:vi/adjust-command-mode
   4580       return 0
   4581     fi
   4582   fi
   4583 
   4584   local beg=$index end=$index flags=
   4585   if ! ble/keymap:vi/text-object/word.extend-forward; then
   4586     # 一致失敗
   4587     index=${#_ble_edit_str}
   4588     ble-edit/content/nonbol-eolp "$index" && ((index--))
   4589     _ble_edit_ind=$index
   4590     ble/widget/vi-command/bell
   4591     return 1
   4592   fi
   4593 
   4594   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   4595     ((end--))
   4596     ble-edit/content/nonbol-eolp "$end" && ((end--))
   4597     ((beg<_ble_edit_mark)) && _ble_edit_mark=$beg
   4598     [[ $_ble_edit_mark_active == vi_line ]] &&
   4599       _ble_edit_mark_active=vi_char
   4600     _ble_edit_ind=$end
   4601     ble/keymap:vi/adjust-command-mode
   4602     return 0
   4603   else
   4604     ble/widget/vi-command/exclusive-range.impl "$beg" "$end" "$flag" "$reg"
   4605   fi
   4606 }
   4607 
   4608 ## @fn ble/keymap:vi/text-object:quote/.next [index]
   4609 ##   @var[out] ret
   4610 function ble/keymap:vi/text-object:quote/.next {
   4611   local index=${1:-$((_ble_edit_ind+1))} nl=$'\n'
   4612   local rex="^[^$nl$quote]*$quote"
   4613   [[ ${_ble_edit_str:index} =~ $rex ]] || return 1
   4614   ((ret=index+${#BASH_REMATCH}-1))
   4615   return 0
   4616 }
   4617 ## @fn ble/keymap:vi/text-object:quote/.prev [index]
   4618 ##   @var[out] ret
   4619 function ble/keymap:vi/text-object:quote/.prev {
   4620   local index=${1:-_ble_edit_ind} nl=$'\n'
   4621   local rex="$quote[^$nl$quote]*\$"
   4622   [[ ${_ble_edit_str::index} =~ $rex ]] || return 1
   4623   ((ret=index-${#BASH_REMATCH}))
   4624   return 0
   4625 }
   4626 function ble/keymap:vi/text-object/quote.impl {
   4627   local arg=$1 flag=$2 reg=$3 type=$4
   4628   local ret quote=${type:1}
   4629   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   4630     if ble/keymap:vi/text-object:quote/.xmap; then
   4631       ble/keymap:vi/adjust-command-mode
   4632       return 0
   4633     else
   4634       ble/widget/vi-command/bell
   4635       return 1
   4636     fi
   4637   fi
   4638 
   4639   local beg= end=
   4640   if [[ ${_ble_edit_str:_ble_edit_ind:1} == "$quote" ]]; then
   4641     ble-edit/content/find-logical-bol; local bol=$ret
   4642     ble/string#count-char "${_ble_edit_str:bol:_ble_edit_ind-bol}" "$quote"
   4643     if ((ret%2==1)); then
   4644       # 現在終了引用符
   4645       ((end=_ble_edit_ind+1))
   4646       ble/keymap:vi/text-object:quote/.prev && beg=$ret
   4647     else
   4648       ((beg=_ble_edit_ind))
   4649       ble/keymap:vi/text-object:quote/.next && end=$((ret+1))
   4650     fi
   4651   elif ble/keymap:vi/text-object:quote/.prev && beg=$ret; then
   4652     ble/keymap:vi/text-object:quote/.next && end=$((ret+1))
   4653   elif ble/keymap:vi/text-object:quote/.next && beg=$ret; then
   4654     ble/keymap:vi/text-object:quote/.next "$((beg+1))" && end=$((ret+1))
   4655   fi
   4656 
   4657   if [[ $beg && $end ]]; then
   4658     [[ $type == i* || arg -gt 1 ]] && ((beg++,end--))
   4659     ble/widget/vi-command/exclusive-range.impl "$beg" "$end" "$flag" "$reg"
   4660   else
   4661     ble/widget/vi-command/bell
   4662     return 1
   4663   fi
   4664 }
   4665 ## @fn ble/keymap:vi/text-object:quote/.expand-xmap-range mode
   4666 ##   @param[in] mode
   4667 ##   @var[in,out] beg
   4668 ##   @var[in,out] end
   4669 function ble/keymap:vi/text-object:quote/.expand-xmap-range {
   4670   local inclusive=$1
   4671   ((end++))
   4672   if ((inclusive==2)); then
   4673     local rex
   4674     rex=$'^[ \t]+'; [[ ${_ble_edit_str:end} =~ $rex ]] && ((end+=${#BASH_REMATCH}))
   4675   elif ((inclusive==0&&end-beg>2)); then
   4676     ((beg++,end--))
   4677   fi
   4678 }
   4679 function ble/keymap:vi/text-object:quote/.xmap {
   4680   # 複数行に亘る場合は失敗
   4681   local min=$_ble_edit_ind max=$_ble_edit_mark
   4682   ((min>max)) && local min=$max max=$min
   4683   [[ ${_ble_edit_str:min:max+1-min} == *$'\n'* ]] && return 1
   4684 
   4685   local inclusive=0
   4686   if [[ $type == a* ]]; then
   4687     inclusive=2
   4688   elif ((arg>1)); then
   4689     inclusive=1
   4690   fi
   4691 
   4692   local ret
   4693   if ((_ble_edit_ind==_ble_edit_mark)); then
   4694     ble/keymap:vi/text-object:quote/.prev "$((_ble_edit_ind+1))" ||
   4695       ble/keymap:vi/text-object:quote/.next "$((_ble_edit_ind+1))" || return 1
   4696     local beg=$ret
   4697     ble/keymap:vi/text-object:quote/.next "$((beg+1))" || return 1
   4698     local end=$ret
   4699 
   4700     ble/keymap:vi/text-object:quote/.expand-xmap-range "$inclusive"
   4701     _ble_edit_mark=$beg
   4702     _ble_edit_ind=$((end-1))
   4703     return 0
   4704   elif ((_ble_edit_ind>_ble_edit_mark)); then
   4705     local updates_mark=
   4706     if [[ ${_ble_edit_str:_ble_edit_ind:1} == "$quote" ]]; then
   4707       # 現在位置に " があるとき。
   4708       ble/keymap:vi/text-object:quote/.next "$((_ble_edit_ind+1))" || return 1; local beg=$ret
   4709       if ble/keymap:vi/text-object:quote/.next "$((beg+1))"; then
   4710         local end=$ret
   4711       else
   4712         local end=$beg beg=$_ble_edit_ind
   4713       fi
   4714     else
   4715       # 現在位置以降の最初の 右" (その行の偶数番目の ") と対応する 左"
   4716       ble-edit/content/find-logical-bol; local bol=$ret
   4717       ble/string#count-char "${_ble_edit_str:bol:_ble_edit_ind-bol}" "$quote"
   4718       if ((ret%2==0)); then
   4719         ble/keymap:vi/text-object:quote/.next "$((_ble_edit_ind+1))" || return 1; local beg=$ret
   4720         ble/keymap:vi/text-object:quote/.next "$((beg+1))" || return 1; local end=$ret
   4721       else
   4722         ble/keymap:vi/text-object:quote/.prev "$_ble_edit_ind" || return 1; local beg=$ret
   4723         ble/keymap:vi/text-object:quote/.next "$((_ble_edit_ind+1))" || return 1; local end=$ret
   4724       fi
   4725       local i1=$((_ble_edit_mark?_ble_edit_mark-1:0))
   4726       [[ ${_ble_edit_str:i1:_ble_edit_ind-i1} != *"$quote"* ]] && updates_mark=1
   4727     fi
   4728 
   4729     ble/keymap:vi/text-object:quote/.expand-xmap-range "$inclusive"
   4730     [[ $updates_mark ]] && _ble_edit_mark=$beg
   4731     _ble_edit_ind=$((end-1))
   4732     return 0
   4733   else
   4734     ble-edit/content/find-logical-bol; local bol=$ret nl=$'\n'
   4735     local rex="^([^$nl$quote]*$quote[^$nl$quote]*$quote)*[^$nl$quote]*$quote"
   4736     [[ ${_ble_edit_str:bol:_ble_edit_ind-bol} =~ $rex ]] || return 1
   4737     local beg=$((bol+${#BASH_REMATCH}-1))
   4738     ble/keymap:vi/text-object:quote/.next "$((beg+1))" || return 1
   4739     local end=$ret
   4740 
   4741     ble/keymap:vi/text-object:quote/.expand-xmap-range "$inclusive"
   4742     [[ ${_ble_edit_str:_ble_edit_ind:_ble_edit_mark+2-_ble_edit_ind} != *"$quote"* ]] && _ble_edit_mark=$((end-1))
   4743     _ble_edit_ind=$beg
   4744     return 0
   4745   fi
   4746 }
   4747 
   4748 function ble/keymap:vi/text-object/block.impl {
   4749   local arg=$1 flag=$2 reg=$3 type=$4
   4750   local ret paren=${type:1} lparen=${type:1:1} rparen=${type:2:1}
   4751   local axis=$_ble_edit_ind
   4752   [[ ${_ble_edit_str:axis:1} == "$lparen" ]] && ((axis++))
   4753 
   4754   local count=$arg beg=$axis
   4755   while ble/string#last-index-of-chars "$_ble_edit_str" "$paren" "$beg"; do
   4756     beg=$ret
   4757     if [[ ${_ble_edit_str:beg:1} == "$lparen" ]]; then
   4758       ((--count==0)) && break
   4759     else
   4760       ((++count))
   4761     fi
   4762   done
   4763   if ((count)); then
   4764     ble/widget/vi-command/bell
   4765     return 1
   4766   fi
   4767 
   4768   local count=$arg end=$axis
   4769   while ble/string#index-of-chars "$_ble_edit_str" "$paren" "$end"; do
   4770     end=$((ret+1))
   4771     if [[ ${_ble_edit_str:end-1:1} == "$rparen" ]]; then
   4772       ((--count==0)) && break
   4773     else
   4774       ((++count))
   4775     fi
   4776   done
   4777   if ((count)); then
   4778     ble/widget/vi-command/bell
   4779     return 1
   4780   fi
   4781 
   4782   local linewise=
   4783   if [[ $type == *i* ]]; then
   4784     ((beg++,end--))
   4785     [[ ${_ble_edit_str:beg:1} == $'\n' ]] && ((beg++))
   4786     ((beg<end)) && ble-edit/content/bolp "$end" && ((end--))
   4787     ((beg<end)) && ble-edit/content/bolp "$beg" && ble-edit/content/eolp "$end" && linewise=1
   4788   fi
   4789 
   4790   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   4791     _ble_edit_mark=$beg
   4792     ble/widget/vi-command/exclusive-goto.impl "$end"
   4793   elif [[ $linewise ]]; then
   4794     ble/widget/vi-command/linewise-range.impl "$beg" "$end" "$flag" "$reg" goto_bol
   4795   else
   4796     ble/widget/vi-command/exclusive-range.impl "$beg" "$end" "$flag" "$reg"
   4797   fi
   4798 }
   4799 
   4800 ## @fn ble/keymap:vi/text-object:tag/.find-end-tag
   4801 ##   @var[in] beg
   4802 ##   @var[out] end
   4803 function ble/keymap:vi/text-object:tag/.find-end-tag {
   4804   local ifs=$_ble_term_IFS ret rex
   4805 
   4806   rex="^<([^$ifs/>!]+)"; [[ ${_ble_edit_str:beg} =~ $rex ]] || return 1
   4807   ble/string#escape-for-extended-regex "${BASH_REMATCH[1]}"; local tagname=$ret
   4808   rex="^</?$tagname([$ifs]+([^>]*[^/])?)?>"
   4809 
   4810   end=$beg
   4811   local count=0
   4812   while ble/string#index-of-chars "$_ble_edit_str" '<' "$end" && end=$((ret+1)); do
   4813     [[ ${_ble_edit_str:end-1} =~ $rex ]] || continue
   4814     ((end+=${#BASH_REMATCH}-1))
   4815 
   4816     if [[ ${BASH_REMATCH::2} == '</' ]]; then
   4817       ((--count==0)) && return 0
   4818     else
   4819       ((++count))
   4820     fi
   4821   done
   4822   return 1
   4823 }
   4824 function ble/keymap:vi/text-object/tag.impl {
   4825   local arg=$1 flag=$2 reg=$3 type=$4
   4826   local ret rex
   4827 
   4828   local pivot=$_ble_edit_ind ret=$_ble_edit_ind
   4829   if [[ ${_ble_edit_str:ret:1} == '<' ]] || ble/string#last-index-of-chars "${_ble_edit_str::_ble_edit_ind}" '<>'; then
   4830     if rex='^<[^/][^>]*>' && [[ ${_ble_edit_str:ret} =~ $rex ]]; then
   4831       ((pivot=ret+${#BASH_REMATCH}))
   4832     else
   4833       ((pivot=ret+1))
   4834     fi
   4835   fi
   4836 
   4837   local ifs=$_ble_term_IFS
   4838 
   4839   local beg=$pivot count=$arg
   4840   rex="<([^$ifs/>!]+([$ifs]+([^>]*[^/])?)?|/[^>]*)>\$"
   4841   while ble/string#last-index-of-chars "${_ble_edit_str::beg}" '>' && beg=$ret; do
   4842     [[ ${_ble_edit_str::beg+1} =~ $rex ]] || continue
   4843     ((beg-=${#BASH_REMATCH}-1))
   4844 
   4845     if [[ ${BASH_REMATCH::2} == '</' ]]; then
   4846       ((++count))
   4847     else
   4848       if ((--count==0)); then
   4849         if ble/keymap:vi/text-object:tag/.find-end-tag "$beg" && ((_ble_edit_ind<end)); then
   4850           break
   4851         else
   4852           ((count++))
   4853         fi
   4854       fi
   4855     fi
   4856   done
   4857   if ((count)); then
   4858     ble/widget/vi-command/bell
   4859     return 1
   4860   fi
   4861 
   4862   if [[ $type == i* ]]; then
   4863     rex='^<[^>]*>'; [[ ${_ble_edit_str:beg:end-beg} =~ $rex ]] && ((beg+=${#BASH_REMATCH}))
   4864     rex='<[^>]*>$'; [[ ${_ble_edit_str:beg:end-beg} =~ $rex ]] && ((end-=${#BASH_REMATCH}))
   4865   fi
   4866   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   4867     _ble_edit_mark=$beg
   4868     ble/widget/vi-command/exclusive-goto.impl "$end"
   4869   else
   4870     ble/widget/vi-command/exclusive-range.impl "$beg" "$end" "$flag" "$reg"
   4871   fi
   4872 }
   4873 
   4874 ## @fn ble/keymap:vi/text-object:sentence/.beg
   4875 ##   @var[out] beg
   4876 ##   @var[out] is_interval
   4877 ##   @var[in] lf, ht
   4878 function ble/keymap:vi/text-object:sentence/.beg {
   4879   beg= is_interval=
   4880   local pivot=$_ble_edit_ind rex=
   4881   if ble-edit/content/bolp && ble-edit/content/eolp; then
   4882     if rex=$'^\n+[^\n]'; [[ ${_ble_edit_str:pivot} =~ $rex ]]; then
   4883       # 前方に非空白が見つかればその手前の行を開始点とする
   4884       beg=$((pivot+${#BASH_REMATCH}-2))
   4885     else
   4886       # 前の非空行末を基点に取り直す
   4887       if rex=$'\n+$'; [[ ${_ble_edit_str::pivot} =~ $rex ]]; then
   4888         ((pivot-=${#BASH_REMATCH}))
   4889       fi
   4890     fi
   4891   fi
   4892   if [[ ! $beg ]]; then
   4893     rex="^.*((^$lf?|$lf$lf)([ $ht]*)|[.!?][])'\"]*([ $ht$lf]+))"
   4894     if [[ ${_ble_edit_str::pivot+1} =~ $rex ]]; then
   4895       beg=${#BASH_REMATCH}
   4896       if ((pivot<beg)); then
   4897         # pivot < beg は beg == pivot + 1 (終端まで一致) を意味する。
   4898         # この時点で pivot は必ず非空行または先頭行にいるので /\n\n/ に一致することはない。
   4899         local rematch34=${BASH_REMATCH[3]}${BASH_REMATCH[4]}
   4900         if [[ $rematch34 ]]; then
   4901           # /(^\n\s+|\n\n\s+|[.!?]\s+)$/
   4902           beg=$((pivot+1-${#rematch34})) is_interval=1
   4903         else
   4904           # /^\n$/
   4905           beg=$pivot
   4906         fi
   4907       fi
   4908     else
   4909       beg=0
   4910     fi
   4911   fi
   4912 }
   4913 ## @fn ble/keymap:vi/text-object:sentence/.next {
   4914 ##   @var[in,out] end
   4915 ##   @var[in,out] is_interval
   4916 ##   @var[in] lf, ht
   4917 function ble/keymap:vi/text-object:sentence/.next {
   4918   if [[ $is_interval ]]; then
   4919     is_interval=
   4920     local rex=$'[ \t]*((\n[ \t]+)*\n[ \t]*)?'
   4921     [[ ${_ble_edit_str:end} =~ $rex ]]
   4922     local index=$((end+${#BASH_REMATCH}))
   4923     ((end<index)) && [[ ${_ble_edit_str:index-1:1} == $'\n' ]] && ((index--))
   4924     ((end=index))
   4925   else
   4926     is_interval=1
   4927     if local rex=$'^\n+'; [[ ${_ble_edit_str:end} =~ $rex ]]; then
   4928       # 連続する LF を読み切る
   4929       ((end+=${#BASH_REMATCH}))
   4930     elif rex="(([.!?][])\"']*)[ $ht$lf]|$lf$lf).*\$"; [[ ${_ble_edit_str:end} =~ $rex ]]; then
   4931       # 文を次の文末記号まで
   4932       local rematch2=${BASH_REMATCH[2]}
   4933       end=$((${#_ble_edit_str}-${#BASH_REMATCH}+${#rematch2}))
   4934     else
   4935       # 最後の文
   4936       local index=${#_ble_edit_str}
   4937       ((end<index)) && [[ ${_ble_edit_str:index-1:1} == $'\n' ]] && ((index--))
   4938       ((end=index))
   4939     fi
   4940   fi
   4941 }
   4942 function ble/keymap:vi/text-object/sentence.impl {
   4943   local arg=$1 flag=$2 reg=$3 type=$4
   4944   local lf=$'\n' ht=$'\t'
   4945   local rex
   4946 
   4947   local beg is_interval
   4948   ble/keymap:vi/text-object:sentence/.beg
   4949 
   4950   local end=$beg i n=$arg
   4951   [[ $type != i* ]] && ((n*=2))
   4952   for ((i=0;i<n;i++)); do
   4953     ble/keymap:vi/text-object:sentence/.next
   4954   done
   4955   ((beg<end)) && [[ ${_ble_edit_str:end-1:1} == $'\n' ]] && ((end--))
   4956 
   4957   # at は後方 (forward) に空白を確保できなければ前方 (backward) に空白を確保する。
   4958   if [[ $type != i* && ! $is_interval ]]; then
   4959     local ifs=$_ble_term_IFS
   4960     if ((end)) && [[ ${_ble_edit_str:end-1:1} != ["$ifs"] ]]; then
   4961       rex="^.*(^$lf$lf|[.!?][])'\"]*([ $ht$lf]))([ $ht$lf]*)\$"
   4962       if [[ ${_ble_edit_str::beg} =~ $rex ]]; then
   4963         local rematch2=${BASH_REMATCH[2]}
   4964         local rematch3=${BASH_REMATCH[3]}
   4965         ((beg-=${#rematch2}+${#rematch3}))
   4966         [[ ${_ble_edit_str:beg:1} == $'\n' ]] && ((beg++))
   4967       fi
   4968     fi
   4969   fi
   4970 
   4971   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   4972     _ble_edit_mark=$beg
   4973     ble/widget/vi-command/exclusive-goto.impl "$end"
   4974   elif ble-edit/content/bolp "$beg" && [[ ${_ble_edit_str:end:1} == $'\n' ]]; then
   4975     # 行頭から LF の手前までのときに linewise になる。
   4976     # _ble_edit_str の末端までのときは linewise ではないことに注意する。
   4977     ble/widget/vi-command/linewise-range.impl "$beg" "$end" "$flag" "$reg" goto_bol
   4978   else
   4979     ble/widget/vi-command/exclusive-range.impl "$beg" "$end" "$flag" "$reg"
   4980   fi
   4981 }
   4982 
   4983 function ble/keymap:vi/text-object/paragraph.impl {
   4984   local arg=$1 flag=$2 reg=$3 type=$4
   4985   local rex ret
   4986 
   4987   local beg= empty_start=
   4988   ble-edit/content/find-logical-bol; local bol=$ret
   4989   ble-edit/content/find-non-space "$bol"; local nol=$ret
   4990   if rex=$'[ \t]*(\n|$)' ble-edit/content/eolp "$nol"; then
   4991     # 空行のときは連続する一番初めの空行に移動する
   4992     empty_start=1
   4993     rex=$'(^|\n)([ \t]*\n)*$'
   4994     [[ ${_ble_edit_str::bol} =~ $rex ]]
   4995     local rematch1=${BASH_REMATCH[1]} # Note: for bash-3.1 ${#arr[n]} bug
   4996     ((beg=bol-(${#BASH_REMATCH}-${#rematch1})))
   4997   else
   4998     # 非空行のときは最初の非空行の先頭まで移動する。
   4999     if rex=$'^(.*\n)?[ \t]*\n'; [[ ${_ble_edit_str::bol} =~ $rex ]]; then
   5000       ((beg=${#BASH_REMATCH}))
   5001     else
   5002       ((beg=0))
   5003     fi
   5004   fi
   5005 
   5006   local end=$beg
   5007   local rex_empty_line=$'([ \t]*\n|[ \t]+$)' rex_paragraph_line=$'([ \t]*[^ \t\n][^\n]*(\n|$))'
   5008   if [[ $type == i* ]]; then
   5009     rex="$rex_empty_line+|$rex_paragraph_line+"
   5010   elif [[ $empty_start ]]; then
   5011     rex="$rex_empty_line*$rex_paragraph_line+"
   5012   else
   5013     rex="$rex_paragraph_line+$rex_empty_line*"
   5014   fi
   5015   local i
   5016   for ((i=0;i<arg;i++)); do
   5017     if [[ ${_ble_edit_str:end} =~ $rex ]]; then
   5018       ((end+=${#BASH_REMATCH}))
   5019     else
   5020       # paragraph の場合は次が見つからない場合はエラー
   5021       ble/widget/vi-command/bell
   5022       return 1
   5023     fi
   5024   done
   5025 
   5026   # at で後続の空行がなければ backward の空行を取り入れる
   5027   if [[ $type != i* && ! $empty_start ]]; then
   5028     if rex=$'(^|\n)[ \t]*\n$'; ! [[ ${_ble_edit_str::end} =~ $rex ]]; then
   5029       if rex=$'(^|\n)([ \t]*\n)*$'; [[ ${_ble_edit_str::beg} =~ $rex ]]; then
   5030         local rematch1=${BASH_REMATCH[1]}
   5031         ((beg-=${#BASH_REMATCH}-${#rematch1}))
   5032       fi
   5033     fi
   5034   fi
   5035   ((beg<end)) && [[ ${_ble_edit_str:end-1:1} == $'\n' ]] && ((end--))
   5036   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   5037     _ble_edit_mark=$beg
   5038     ble/widget/vi-command/exclusive-goto.impl "$end"
   5039   else
   5040     ble/widget/vi-command/linewise-range.impl "$beg" "$end" "$flag" "$reg"
   5041   fi
   5042 }
   5043 
   5044 ## @fn ble/keymap:vi/text-object.impl
   5045 ##
   5046 ##   @exit テキストオブジェクトの処理が完了したときに 0 を返します。
   5047 ##
   5048 function ble/keymap:vi/text-object.impl {
   5049   local arg=$1 flag=$2 reg=$3 type=$4
   5050   case $type in
   5051   ([ia][wW]) ble/keymap:vi/text-object/word.impl "$arg" "$flag" "$reg" "$type" ;;
   5052   ([ia][\"\'\`]) ble/keymap:vi/text-object/quote.impl "$arg" "$flag" "$reg" "$type" ;;
   5053   ([ia]['b()']) ble/keymap:vi/text-object/block.impl "$arg" "$flag" "$reg" "${type::1}()" ;;
   5054   ([ia]['B{}']) ble/keymap:vi/text-object/block.impl "$arg" "$flag" "$reg" "${type::1}{}" ;;
   5055   ([ia]['<>']) ble/keymap:vi/text-object/block.impl "$arg" "$flag" "$reg" "${type::1}<>" ;;
   5056   ([ia]['][']) ble/keymap:vi/text-object/block.impl "$arg" "$flag" "$reg" "${type::1}[]" ;;
   5057   ([ia]t) ble/keymap:vi/text-object/tag.impl "$arg" "$flag" "$reg" "$type" ;;
   5058   ([ia]s) ble/keymap:vi/text-object/sentence.impl "$arg" "$flag" "$reg" "$type" ;;
   5059   ([ia]p) ble/keymap:vi/text-object/paragraph.impl "$arg" "$flag" "$reg" "$type" ;;
   5060   (*)
   5061     ble/widget/vi-command/bell
   5062     return 1;;
   5063   esac
   5064 }
   5065 
   5066 function ble/keymap:vi/text-object.hook {
   5067   local key=$1
   5068   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5069   if ! ble-decode-key/ischar "$key"; then
   5070     ble/widget/vi-command/bell
   5071     return 1
   5072   fi
   5073 
   5074   local ret; ble/util/c2s "$key"
   5075   local type=$_ble_keymap_vi_text_object$ret
   5076   ble/keymap:vi/text-object.impl "$ARG" "$FLAG" "$REG" "$type"
   5077   return 0
   5078 }
   5079 
   5080 function ble/keymap:vi/.check-text-object {
   5081   local n=${#KEYS[@]}; ((n&&n--))
   5082   ble-decode-key/ischar "${KEYS[n]}" || return 1
   5083 
   5084   local ret; ble/util/c2s "${KEYS[n]}"; local c=$ret
   5085   [[ $c == [ia] ]] || return 1
   5086 
   5087   [[ $_ble_keymap_vi_opfunc || $_ble_decode_keymap == vi_[xs]map ]] || return 1
   5088 
   5089   _ble_keymap_vi_text_object=$c
   5090   _ble_decode_key__hook=ble/keymap:vi/text-object.hook
   5091   return 0
   5092 }
   5093 
   5094 function ble/widget/vi-command/text-object {
   5095   ble/keymap:vi/.check-text-object && return 0
   5096   ble/widget/vi-command/bell
   5097   return 1
   5098 }
   5099 
   5100 #------------------------------------------------------------------------------
   5101 # Command
   5102 #
   5103 # map: :cmd
   5104 
   5105 # 既定の cmap 履歴
   5106 _ble_keymap_vi_commandline_history=()
   5107 _ble_keymap_vi_commandline_history_edit=()
   5108 _ble_keymap_vi_commandline_history_dirt=()
   5109 _ble_keymap_vi_commandline_history_index=0
   5110 
   5111 ## @arr _ble_keymap_vi_cmap_is_cancel_key
   5112 ##   コマンドラインが空の時にキャンセルに使うキーの辞書です。
   5113 _ble_keymap_vi_cmap_is_cancel_key[63|_ble_decode_Ctrl]=1  # C-?
   5114 _ble_keymap_vi_cmap_is_cancel_key[127]=1                  # DEL
   5115 _ble_keymap_vi_cmap_is_cancel_key[104|_ble_decode_Ctrl]=1 # C-h
   5116 _ble_keymap_vi_cmap_is_cancel_key[8]=1                    # BS
   5117 function ble/keymap:vi/commandline/before-command.hook {
   5118   if [[ ! $_ble_edit_str ]] && ((_ble_keymap_vi_cmap_is_cancel_key[KEYS[0]])); then
   5119     ble/widget/vi_cmap/cancel
   5120     ble/decode/widget/suppress-widget
   5121   fi
   5122 }
   5123 
   5124 function ble/widget/vi-command/commandline {
   5125   ble/keymap:vi/clear-arg
   5126   ble/keymap:vi/async-commandline-mode ble/widget/vi-command/commandline.hook
   5127   _ble_edit_PS1=:
   5128   ble/history/set-prefix _ble_keymap_vi_commandline
   5129   _ble_keymap_vi_cmap_before_command=ble/keymap:vi/commandline/before-command.hook
   5130   return 147
   5131 }
   5132 function ble/widget/vi-command/commandline.hook {
   5133   local command
   5134   ble/string#split-words command "$1"
   5135   local cmd="ble/widget/vi-command:${command[0]}"
   5136   if ble/is-function "$cmd"; then
   5137     "$cmd" "${command[@]:1}"; local ext=$?
   5138   else
   5139     ble/widget/vi-command/bell "unknown command $1"; local ext=1
   5140   fi
   5141   [[ $1 ]] && _ble_keymap_vi_register[58]=/$result # ":
   5142   return "$ext"
   5143 }
   5144 
   5145 function ble/widget/vi-command:w {
   5146   local file=
   5147   if [[ $1 ]]; then
   5148     ble/builtin/history -a "$1"
   5149     file=$1
   5150   elif [[ ${HISTFILE-} ]]; then
   5151     ble/builtin/history -a
   5152     file=$HISTFILE
   5153   else
   5154     ble/widget/vi-command/bell 'w: the history filename is empty or not specified'
   5155     return 1
   5156   fi
   5157   local wc
   5158   ble/util/assign-words wc 'ble/bin/wc "$file"'
   5159   ble/edit/info/show text "\"$file\" ${wc[0]}L, ${wc[2]}C written"
   5160   ble/keymap:vi/adjust-command-mode
   5161   return 0
   5162 }
   5163 function ble/widget/vi-command:q! {
   5164   ble/widget/exit force
   5165   return 1
   5166 }
   5167 function ble/widget/vi-command:q {
   5168   ble/widget/exit
   5169   ble/keymap:vi/adjust-command-mode # ジョブがあるときは終了しないので。
   5170   return 1
   5171 }
   5172 function ble/widget/vi-command:wq {
   5173   ble/widget/vi-command:w "$@"
   5174   ble/widget/exit
   5175   ble/keymap:vi/adjust-command-mode
   5176   return 1
   5177 }
   5178 
   5179 #------------------------------------------------------------------------------
   5180 # Search
   5181 #
   5182 # map: / ? n N
   5183 
   5184 _ble_keymap_vi_search_obackward=
   5185 _ble_keymap_vi_search_ohistory=
   5186 _ble_keymap_vi_search_needle=
   5187 _ble_keymap_vi_search_activate=
   5188 _ble_keymap_vi_search_matched=
   5189 
   5190 _ble_keymap_vi_search_history=()
   5191 _ble_keymap_vi_search_history_edit=()
   5192 _ble_keymap_vi_search_history_dirt=()
   5193 _ble_keymap_vi_search_history_index=0
   5194 
   5195 ## @bleopt keymap_vi_search_match_current
   5196 ##   非空の文字列が設定されている時 /, ?, n, N で
   5197 ##   現在のカーソルの下にある単語に一致します。
   5198 ##   既定値は空文字列で vim の振る舞いに倣います。
   5199 bleopt/declare -v keymap_vi_search_match_current ''
   5200 
   5201 function ble/highlight/layer:region/mark:vi_search/get-selection {
   5202   ble/highlight/layer:region/mark:vi_char/get-selection
   5203 }
   5204 function ble/keymap:vi/search/matched {
   5205   [[ $_ble_keymap_vi_search_matched || $_ble_edit_mark_active == vi_search || $_ble_keymap_vi_search_activate ]]
   5206 }
   5207 function ble/keymap:vi/search/clear-matched {
   5208   _ble_keymap_vi_search_activate=
   5209   _ble_keymap_vi_search_matched=
   5210   [[ $_ble_edit_mark_active == vi_search ]] && _ble_edit_mark_active=
   5211 }
   5212 ## @fn ble/keymap:vi/search/invoke-search needle
   5213 ##
   5214 ##   @param[in] needle
   5215 ##     検索パターンを表す正規表現を指定する。
   5216 ##
   5217 ##   @var[out] beg end
   5218 ##     一致範囲を返す。
   5219 ##
   5220 ##   @exit
   5221 ##     一致が見つかった場合に正常終了 0。それ以外の時は 0 以外の値を返す。
   5222 ##
   5223 ##   @var[in] opt_optional_next
   5224 ##     現在位置より後または前の一致を検索する。
   5225 ##     vi_omap で呼び出した時、現在位置で一致した時に設定される。
   5226 ##     keymap_vi_search_match_current が非空の時はここには来ない。
   5227 ##
   5228 ##   @var[in] opt_locate
   5229 ##     現在位置に一致可能な検索を行う。
   5230 ##     履歴を遡って検索して一致する履歴項目が見つかった時、
   5231 ##     その履歴項目内で最初に見つかったものを特定する為に使われる。
   5232 ##
   5233 ##   @var[in] opt_backward
   5234 ##     検索方向を指定する。
   5235 ##
   5236 function ble/keymap:vi/search/invoke-search {
   5237   local needle=$1
   5238   local dir=+; ((opt_backward)) && dir=B
   5239   local ind=$_ble_edit_ind
   5240 
   5241   # 検索開始位置
   5242   if ((opt_optional_next)); then
   5243     if ((!opt_backward)); then
   5244       ((_ble_edit_ind<${#_ble_edit_str}&&_ble_edit_ind++))
   5245     fi
   5246   elif ((opt_locate)) || ! ble/keymap:vi/search/matched; then
   5247     # 何にも一致していない状態から
   5248     if ((opt_locate)) || [[ $bleopt_keymap_vi_search_match_current ]]; then
   5249       # 現在位置に一致可能
   5250       #   前方検索: @hello → @hello (そのまま)
   5251       #   後方検索: hell@o → hello@ (ずらす)
   5252       if ((opt_backward)); then
   5253         ble-edit/content/eolp || ((_ble_edit_ind++))
   5254       fi
   5255     else
   5256       # 現在位置には一致させない
   5257       #   前方検索: @hello → h@ello (ずらす)
   5258       #   後方検索: hell@o → hell@o (そのまま)
   5259       if ((!opt_backward)); then
   5260         ble-edit/content/eolp || ((_ble_edit_ind++))
   5261       fi
   5262     fi
   5263   else
   5264     # _ble_edit_ind .. _ble_edit_mark[+1] に一致しているとき
   5265     if ((!opt_backward)); then
   5266       if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   5267         # vi_xmap, vi_smap では _ble_edit_mark は別の用途に使われていて
   5268         # 終端点の情報が失われているので再度一致を試みる。
   5269         if ble-edit/isearch/search "$@" && ((beg==_ble_edit_ind)); then
   5270           _ble_edit_ind=$end
   5271         else
   5272           ((_ble_edit_ind<${#_ble_edit_str}&&_ble_edit_ind++))
   5273         fi
   5274       else
   5275         ((_ble_edit_ind=_ble_edit_mark))
   5276         ble-edit/content/eolp || ((_ble_edit_ind++))
   5277       fi
   5278     else
   5279       # 2回目以降の一致では opts=- で検索する。
   5280       dir=-
   5281     fi
   5282   fi
   5283 
   5284   ble-edit/isearch/search "$needle" "$dir":regex; local ret=$?
   5285   _ble_edit_ind=$ind
   5286   return "$ret"
   5287 }
   5288 
   5289 ## @fn ble/widget/vi-command/search.core
   5290 ##
   5291 ##   @var[in] needle
   5292 ##   @var[in] opt_backward
   5293 ##   @var[in] opt_history
   5294 ##   @var[in] opt_locate
   5295 ##   @var[in] start
   5296 ##   @var[in] ntask
   5297 ##
   5298 function ble/widget/vi-command/search.core {
   5299   local beg= end= is_empty_match=
   5300   if ble/keymap:vi/search/invoke-search "$needle"; then
   5301     if ((beg<end)); then
   5302       ble-edit/content/bolp "$end" || ((end--))
   5303       _ble_edit_ind=$beg # eol 補正は search.impl 側で最後に行う
   5304       [[ $_ble_decode_keymap != vi_[xs]map ]] && _ble_edit_mark=$end
   5305       _ble_keymap_vi_search_activate=vi_search
   5306       return 0
   5307     else
   5308       # vim では空一致は即座に失敗のようだ。
   5309       # 続きを検索するということはしない。
   5310       opt_history=
   5311       is_empty_match=1
   5312     fi
   5313   fi
   5314 
   5315   if ((opt_history)) && [[ $_ble_history_load_done || opt_backward -ne 0 ]]; then
   5316     ble/history/initialize
   5317     local index; ble/history/get-index
   5318     [[ $start ]] || start=$index
   5319     if ((opt_backward)); then
   5320       ((index--))
   5321     else
   5322       ((index++))
   5323     fi
   5324 
   5325     local _ble_edit_isearch_dir=+; ((opt_backward)) && _ble_edit_isearch_dir=-
   5326     local _ble_edit_isearch_str=$needle
   5327     local isearch_ntask=$ntask
   5328     local isearch_time=0
   5329     local isearch_progress_callback=ble-edit/isearch/.show-status-with-progress.fib
   5330     if ((opt_backward)); then
   5331       ble/history/isearch-backward-blockwise regex:progress
   5332     else
   5333       ble/history/isearch-forward regex:progress
   5334     fi; local r=$?
   5335     ble/edit/info/default
   5336 
   5337     if ((r==0)); then
   5338       local new_index; ble/history/get-index -v new_index
   5339       [[ $index != "$new_index" ]] &&
   5340         ble-edit/history/goto "$index"
   5341       if ((opt_backward)); then
   5342         local i=${#_ble_edit_str}
   5343         ble/keymap:vi/needs-eol-fix "$i" && ((i--))
   5344         _ble_edit_ind=$i
   5345       else
   5346         _ble_edit_ind=0
   5347       fi
   5348 
   5349       opt_locate=1 opt_history=0 ble/widget/vi-command/search.core
   5350       return "$?"
   5351     fi
   5352   fi
   5353 
   5354   if ((!opt_optional_next)); then
   5355     if [[ $is_empty_match ]]; then
   5356       ble/widget/.bell "search: empty match"
   5357     else
   5358       ble/widget/.bell "search: not found"
   5359     fi
   5360     if [[ $_ble_edit_mark_active == vi_search ]]; then
   5361       _ble_keymap_vi_search_activate=vi_search
   5362     fi
   5363   fi
   5364   return 1
   5365 }
   5366 function ble/widget/vi-command/search.impl {
   5367   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5368 
   5369   local opts=$1 needle=$2
   5370   [[ :$opts: != *:repeat:* ]]; local opt_repeat=$? # 再検索 n N
   5371   [[ :$opts: != *:history:* ]]; local opt_history=$? # 履歴検索が有効か
   5372   [[ :$opts: != *:-:* ]]; local opt_backward=$? # 逆方向
   5373   local opt_locate=0
   5374   local opt_optional_next=0
   5375   if ((opt_repeat)); then
   5376     # n N
   5377     if [[ $_ble_keymap_vi_search_needle ]]; then
   5378       needle=$_ble_keymap_vi_search_needle
   5379       ((opt_backward^=_ble_keymap_vi_search_obackward,
   5380         opt_history=_ble_keymap_vi_search_ohistory))
   5381     else
   5382       ble/widget/vi-command/bell 'no previous search'
   5383       return 1
   5384     fi
   5385   else
   5386     # / ? * #
   5387     ble/keymap:vi/search/clear-matched
   5388     if [[ $needle ]]; then
   5389       _ble_keymap_vi_search_needle=$needle
   5390       _ble_keymap_vi_search_obackward=$opt_backward
   5391       _ble_keymap_vi_search_ohistory=$opt_history
   5392     elif [[ $_ble_keymap_vi_search_needle ]]; then
   5393       needle=$_ble_keymap_vi_search_needle
   5394       _ble_keymap_vi_search_obackward=$opt_backward
   5395       _ble_keymap_vi_search_ohistory=$opt_history
   5396     else
   5397       ble/widget/vi-command/bell 'no previous search'
   5398       return 1
   5399     fi
   5400   fi
   5401 
   5402   local original_ind=$_ble_edit_ind
   5403   if [[ $FLAG || $_ble_decode_keymap == vi_[xs]map ]]; then
   5404     opt_history=0
   5405   else
   5406     local old_hindex; ble/history/get-index -v old_hindex
   5407   fi
   5408 
   5409   local start= # 初めの履歴番号。search.core 内で最初に履歴を読み込んだあとで設定される。
   5410   local ntask=$ARG
   5411   while ((ntask)); do
   5412     ble/widget/vi-command/search.core || break
   5413     ((ntask--))
   5414   done
   5415 
   5416   if [[ $FLAG ]]; then
   5417     if ((ntask)); then
   5418       # 検索対象が見つからなかったとき
   5419       _ble_keymap_vi_search_activate=
   5420       _ble_edit_ind=$original_ind
   5421       ble/keymap:vi/adjust-command-mode
   5422       return 1
   5423     else
   5424       # 見つかったとき
   5425       if ((_ble_edit_ind==original_index)); then
   5426         # 範囲が空のときは次の一致場所まで。
   5427         # 次の一致場所がないとき (自分自身のとき) は空領域になる。
   5428         opt_optional_next=1 ble/widget/vi-command/search.core
   5429       fi
   5430       local index=$_ble_edit_ind
   5431 
   5432       _ble_keymap_vi_search_activate=
   5433       _ble_edit_ind=$original_ind
   5434       ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   5435     fi
   5436   else
   5437     if ((ntask<ARG)); then
   5438       # 同じ履歴項目内でのジャンプ
   5439       if ((opt_history)); then
   5440         local new_hindex; ble/history/get-index -v new_hindex
   5441         ((new_hindex==old_hindex))
   5442       fi && ble/keymap:vi/mark/set-local-mark 96 "$original_index" # ``
   5443 
   5444       # 行末補正
   5445       if ble/keymap:vi/needs-eol-fix; then
   5446         if ((!opt_backward&&_ble_edit_ind<_ble_edit_mark)); then
   5447           ((_ble_edit_ind++))
   5448         else
   5449           ((_ble_edit_ind--))
   5450         fi
   5451       fi
   5452     fi
   5453     ble/keymap:vi/adjust-command-mode
   5454     return 0
   5455   fi
   5456 }
   5457 function ble/widget/vi-command/search-forward {
   5458   ble/keymap:vi/async-commandline-mode 'ble/widget/vi-command/search.impl +:history'
   5459   _ble_edit_PS1='/'
   5460   ble/history/set-prefix _ble_keymap_vi_search
   5461   _ble_keymap_vi_cmap_before_command=ble/keymap:vi/commandline/before-command.hook
   5462   return 147
   5463 }
   5464 function ble/widget/vi-command/search-backward {
   5465   ble/keymap:vi/async-commandline-mode 'ble/widget/vi-command/search.impl -:history'
   5466   _ble_edit_PS1='?'
   5467   ble/history/set-prefix _ble_keymap_vi_search
   5468   _ble_keymap_vi_cmap_before_command=ble/keymap:vi/commandline/before-command.hook
   5469   return 147
   5470 }
   5471 function ble/widget/vi-command/search-repeat {
   5472   ble/widget/vi-command/search.impl repeat:+
   5473 }
   5474 function ble/widget/vi-command/search-reverse-repeat {
   5475   ble/widget/vi-command/search.impl repeat:-
   5476 }
   5477 
   5478 function ble/widget/vi-command/search-word.impl {
   5479   local opts=$1
   5480   local rex=$'^([^[:alnum:]_\n]*)([[:alnum:]_]*)'
   5481   if ! [[ ${_ble_edit_str:_ble_edit_ind} =~ $rex ]]; then
   5482     ble/keymap:vi/clear-arg
   5483     ble/widget/vi-command/bell 'word is not found'
   5484     return 1
   5485   fi
   5486 
   5487   local end=$((_ble_edit_ind+${#BASH_REMATCH}))
   5488   local word=${BASH_REMATCH[2]}
   5489   if [[ ! ${BASH_REMATCH[1]} ]]; then
   5490     rex=$'[[:alnum:]_]+$'
   5491     [[ ${_ble_edit_str::_ble_edit_ind} =~ $rex ]] &&
   5492       word=$BASH_REMATCH$word
   5493   fi
   5494 
   5495   # Note: Bash 正規表現は <regex.h> を用いるので、
   5496   #   必ずしも非 POSIX ERE \<\> に対応しているとは限らない。
   5497   #   また [[:alnum:]_] と符合しているかも分からない。
   5498   #   従って適用できるか確認してから境界に一致することを要求する。
   5499   local needle=$word
   5500   rex='\<'$needle; [[ $word =~ $rex ]] && needle=$rex
   5501   rex=$needle'\>'; [[ $word =~ $rex ]] && needle=$rex
   5502 
   5503   if [[ $opts == backward ]]; then
   5504     ble/widget/vi-command/search.impl -:history "$needle"
   5505   else
   5506     local original_ind=$_ble_edit_ind
   5507     _ble_edit_ind=$((end-1))
   5508     ble/widget/vi-command/search.impl +:history "$needle" && return 0
   5509     _ble_edit_ind=$original_ind
   5510     return 1
   5511   fi
   5512 }
   5513 # nmap *
   5514 function ble/widget/vi-command/search-word-forward {
   5515   ble/widget/vi-command/search-word.impl forward
   5516 }
   5517 # nmap #
   5518 function ble/widget/vi-command/search-word-backward {
   5519   ble/widget/vi-command/search-word.impl backward
   5520 }
   5521 
   5522 #------------------------------------------------------------------------------
   5523 # command-help
   5524 
   5525 # nmap K
   5526 function ble/widget/vi_nmap/command-help {
   5527   ble/keymap:vi/clear-arg
   5528   ble/widget/command-help; local ext=$?
   5529   ble/keymap:vi/adjust-command-mode
   5530   return "$ext"
   5531 }
   5532 function ble/widget/vi_xmap/command-help.core {
   5533   ble/keymap:vi/clear-arg
   5534   local get_selection=ble/highlight/layer:region/mark:$_ble_edit_mark_active/get-selection
   5535   ble/is-function "$get_selection" || return 1
   5536 
   5537   local selection
   5538   "$get_selection" || return 1
   5539   ((${#selection[*]}==2)) || return 1
   5540 
   5541   local comp_cword=0 comp_line=$_ble_edit_str comp_point=$_ble_edit_ind
   5542   local -a comp_words; comp_words=("$cmd")
   5543   local cmd=${_ble_edit_str:selection[0]:selection[1]-selection[0]}
   5544   ble/widget/command-help.impl "$cmd"; local ext=$?
   5545   ble/keymap:vi/adjust-command-mode
   5546   return "$ext"
   5547 }
   5548 function ble/widget/vi_xmap/command-help {
   5549   if ! ble/widget/vi_xmap/command-help.core; then
   5550     ble/widget/vi-command/bell
   5551     return 1
   5552   fi
   5553 }
   5554 
   5555 #------------------------------------------------------------------------------
   5556 
   5557 ## @fn ble/keymap:vi/set-up-command-map
   5558 ##   @var[in] ble_bind_keymap
   5559 function ble/keymap:vi/set-up-command-map {
   5560   ble-bind -f 0 vi-command/append-arg
   5561   ble-bind -f 1 vi-command/append-arg
   5562   ble-bind -f 2 vi-command/append-arg
   5563   ble-bind -f 3 vi-command/append-arg
   5564   ble-bind -f 4 vi-command/append-arg
   5565   ble-bind -f 5 vi-command/append-arg
   5566   ble-bind -f 6 vi-command/append-arg
   5567   ble-bind -f 7 vi-command/append-arg
   5568   ble-bind -f 8 vi-command/append-arg
   5569   ble-bind -f 9 vi-command/append-arg
   5570   ble-bind -f y 'vi-command/operator y'
   5571   ble-bind -f d 'vi-command/operator d'
   5572   ble-bind -f c 'vi-command/operator c'
   5573   ble-bind -f '<' 'vi-command/operator indent-left'
   5574   ble-bind -f '>' 'vi-command/operator indent-right'
   5575   ble-bind -f '!' 'vi-command/operator filter'
   5576   ble-bind -f 'g ~' 'vi-command/operator toggle_case'
   5577   ble-bind -f 'g u' 'vi-command/operator u'
   5578   ble-bind -f 'g U' 'vi-command/operator U'
   5579   ble-bind -f 'g ?' 'vi-command/operator rot13'
   5580   ble-bind -f 'g q' 'vi-command/operator fold'
   5581   ble-bind -f 'g w' 'vi-command/operator fold-preserve-point'
   5582   ble-bind -f 'g @' 'vi-command/operator map'
   5583   # ble-bind -f '='   'vi-command/operator =' # インデント (equalprg, ep)
   5584   # ble-bind -f 'z f' 'vi-command/operator f'
   5585 
   5586   ble-bind -f paste_begin vi-command/bracketed-paste
   5587 
   5588   ble-bind -f 'home'    vi-command/beginning-of-line
   5589   ble-bind -f '$'       vi-command/forward-eol
   5590   ble-bind -f 'end'     vi-command/forward-eol
   5591   ble-bind -f '^'       vi-command/first-non-space
   5592   ble-bind -f '_'       vi-command/first-non-space-forward
   5593   ble-bind -f '+'       vi-command/forward-first-non-space
   5594   ble-bind -f 'C-m'     vi-command/forward-first-non-space
   5595   ble-bind -f 'RET'     vi-command/forward-first-non-space
   5596   ble-bind -f '-'       vi-command/backward-first-non-space
   5597   ble-bind -f 'g 0'     vi-command/beginning-of-graphical-line
   5598   ble-bind -f 'g home'  vi-command/beginning-of-graphical-line
   5599   ble-bind -f 'g ^'     vi-command/graphical-first-non-space
   5600   ble-bind -f 'g $'     vi-command/graphical-forward-eol
   5601   ble-bind -f 'g end'   vi-command/graphical-forward-eol
   5602   ble-bind -f 'g m'     vi-command/middle-of-graphical-line
   5603   ble-bind -f 'g _'     vi-command/last-non-space
   5604 
   5605   ble-bind -f h     vi-command/backward-char
   5606   ble-bind -f l     vi-command/forward-char
   5607   ble-bind -f left  vi-command/backward-char
   5608   ble-bind -f right vi-command/forward-char
   5609   ble-bind -f 'C-?' 'vi-command/backward-char wrap'
   5610   ble-bind -f 'DEL' 'vi-command/backward-char wrap'
   5611   ble-bind -f 'C-h' 'vi-command/backward-char wrap'
   5612   ble-bind -f 'BS'  'vi-command/backward-char wrap'
   5613   ble-bind -f SP    'vi-command/forward-char wrap'
   5614 
   5615   ble-bind -f j     vi-command/forward-line
   5616   ble-bind -f down  vi-command/forward-line
   5617   ble-bind -f C-n   vi-command/forward-line
   5618   ble-bind -f C-j   vi-command/forward-line
   5619   ble-bind -f k     vi-command/backward-line
   5620   ble-bind -f up    vi-command/backward-line
   5621   ble-bind -f C-p   vi-command/backward-line
   5622   ble-bind -f 'g j'    vi-command/graphical-forward-line
   5623   ble-bind -f 'g down' vi-command/graphical-forward-line
   5624   ble-bind -f 'g k'    vi-command/graphical-backward-line
   5625   ble-bind -f 'g up'   vi-command/graphical-backward-line
   5626 
   5627   ble-bind -f w       vi-command/forward-vword
   5628   ble-bind -f W       vi-command/forward-uword
   5629   ble-bind -f b       vi-command/backward-vword
   5630   ble-bind -f B       vi-command/backward-uword
   5631   ble-bind -f e       vi-command/forward-vword-end
   5632   ble-bind -f E       vi-command/forward-uword-end
   5633   ble-bind -f 'g e'   vi-command/backward-vword-end
   5634   ble-bind -f 'g E'   vi-command/backward-uword-end
   5635   ble-bind -f C-right vi-command/forward-vword
   5636   ble-bind -f S-right vi-command/forward-vword
   5637   ble-bind -f C-left  vi-command/backward-vword
   5638   ble-bind -f S-left  vi-command/backward-vword
   5639 
   5640   ble-bind -f 'g o'  vi-command/nth-byte
   5641   ble-bind -f '|'    vi-command/nth-column
   5642   ble-bind -f H      vi-command/nth-line
   5643   ble-bind -f L      vi-command/nth-last-line
   5644   ble-bind -f 'g g'  vi-command/history-beginning
   5645   ble-bind -f G      vi-command/history-end
   5646   ble-bind -f C-home vi-command/first-nol
   5647   ble-bind -f C-end  vi-command/last-eol
   5648 
   5649   ble-bind -f 'f' vi-command/search-forward-char
   5650   ble-bind -f 'F' vi-command/search-backward-char
   5651   ble-bind -f 't' vi-command/search-forward-char-prev
   5652   ble-bind -f 'T' vi-command/search-backward-char-prev
   5653   ble-bind -f ';' vi-command/search-char-repeat
   5654   ble-bind -f ',' vi-command/search-char-reverse-repeat
   5655 
   5656   ble-bind -f '%' 'vi-command/search-matchpair-or vi-command/percentage-line'
   5657 
   5658   ble-bind -f 'C-\ C-n' nop
   5659 
   5660   ble-bind -f ':' vi-command/commandline
   5661   ble-bind -f '/' vi-command/search-forward
   5662   ble-bind -f '?' vi-command/search-backward
   5663   ble-bind -f 'n' vi-command/search-repeat
   5664   ble-bind -f 'N' vi-command/search-reverse-repeat
   5665   ble-bind -f '*' vi-command/search-word-forward
   5666   ble-bind -f '#' vi-command/search-word-backward
   5667 
   5668   ble-bind -f '`' 'vi-command/goto-mark'
   5669   ble-bind -f \'  'vi-command/goto-mark line'
   5670 
   5671   # bash
   5672   ble-bind -c 'C-z' fg
   5673 }
   5674 
   5675 #------------------------------------------------------------------------------
   5676 # Operator pending mode
   5677 
   5678 function ble/widget/vi_omap/operator-rot13-or-search-backward {
   5679   if [[ $_ble_keymap_vi_opfunc == rot13 ]]; then
   5680     # g?? の時だけは rot13-encode lines
   5681     ble/widget/vi-command/operator rot13
   5682   else
   5683     ble/widget/vi-command/search-backward
   5684   fi
   5685 }
   5686 
   5687 # o_v o_V
   5688 function ble/widget/vi_omap/switch-visual-mode.impl {
   5689   local new_mode=$1
   5690 
   5691   local old=$_ble_keymap_vi_opfunc
   5692   [[ $old ]] || return 1
   5693 
   5694   # clear existing visual-mode
   5695   local new=$old:
   5696   new=${new/:vi_char:/:}
   5697   new=${new/:vi_line:/:}
   5698   new=${new/:vi_block:/:}
   5699 
   5700   # add new visual-mode
   5701   [[ $new_mode ]] && new=$new:$new_mode
   5702 
   5703   _ble_keymap_vi_opfunc=$new
   5704 }
   5705 # omap v
   5706 function ble/widget/vi_omap/switch-to-charwise {
   5707   ble/widget/vi_omap/switch-visual-mode.impl vi_char
   5708 }
   5709 # omap V
   5710 function ble/widget/vi_omap/switch-to-linewise {
   5711   ble/widget/vi_omap/switch-visual-mode.impl vi_line
   5712 }
   5713 # omap <C-v>
   5714 function ble/widget/vi_omap/switch-to-blockwise {
   5715   ble/widget/vi_omap/switch-visual-mode.impl vi_block
   5716 }
   5717 
   5718 function ble-decode/keymap:vi_omap/define {
   5719   ble/keymap:vi/set-up-command-map
   5720 
   5721   ble-bind -f __default__ vi_omap/__default__
   5722   ble-bind -f __line_limit__ nop
   5723   ble-bind -f 'ESC' vi_omap/cancel
   5724   ble-bind -f 'C-[' vi_omap/cancel
   5725   ble-bind -f 'C-c' vi_omap/cancel
   5726 
   5727   ble-bind -f a   vi-command/text-object
   5728   ble-bind -f i   vi-command/text-object
   5729 
   5730   # 範囲の種類の変更 (vim o_v o_V)
   5731   ble-bind -f v      vi_omap/switch-to-charwise
   5732   ble-bind -f V      vi_omap/switch-to-linewise
   5733   ble-bind -f C-v    vi_omap/switch-to-blockwise
   5734   ble-bind -f C-q    vi_omap/switch-to-blockwise
   5735 
   5736   # 2文字オペレータの短縮形
   5737   ble-bind -f '~' 'vi-command/operator toggle_case'
   5738   ble-bind -f 'u' 'vi-command/operator u'
   5739   ble-bind -f 'U' 'vi-command/operator U'
   5740   ble-bind -f '?' 'vi_omap/operator-rot13-or-search-backward'
   5741   ble-bind -f 'q' 'vi-command/operator fold'
   5742   # Note: w は前方単語。例: {N}gww は format {N} words
   5743   # Note: @ は omap では定義されない。例: {N}g@@ は bell
   5744 }
   5745 
   5746 #------------------------------------------------------------------------------
   5747 # Normal mode
   5748 
   5749 # nmap C-d
   5750 function ble/widget/vi-command/exit-on-empty-line {
   5751   if [[ $_ble_edit_str ]]; then
   5752     ble/widget/vi_nmap/forward-scroll
   5753     return "$?"
   5754   else
   5755     ble/widget/exit
   5756     ble/keymap:vi/adjust-command-mode # ジョブがあるときは終了しないので。
   5757     return 1
   5758   fi
   5759 }
   5760 
   5761 # nmap C-g (show line and column)
   5762 function ble/widget/vi-command/show-line-info {
   5763   local index count
   5764   ble/history/get-index -v index
   5765   ble/history/get-count -v count
   5766   local hist_ratio=$(((100*index+count-1)/count))%
   5767   local hist_stat=$'!\e[32m'$index$'\e[m / \e[32m'$count$'\e[m (\e[32m'$hist_ratio$'\e[m)'
   5768 
   5769   local ret
   5770   ble/string#count-char "$_ble_edit_str" $'\n'; local nline=$((ret+1))
   5771   ble/string#count-char "${_ble_edit_str::_ble_edit_ind}" $'\n'; local iline=$((ret+1))
   5772   local line_ratio=$(((100*iline+nline-1)/nline))%
   5773   local line_stat=$'line \e[34m'$iline$'\e[m / \e[34m'$nline$'\e[m --\e[34m'$line_ratio$'\e[m--'
   5774 
   5775   ble/edit/info/show ansi "\"$hist_stat\" $line_stat"
   5776   ble/keymap:vi/adjust-command-mode
   5777   return 0
   5778 }
   5779 
   5780 # nmap C-c (jobs)
   5781 function ble/widget/vi-command/cancel {
   5782   if [[ $_ble_keymap_vi_single_command ]]; then
   5783     _ble_keymap_vi_single_command=
   5784     _ble_keymap_vi_single_command_overwrite=
   5785     ble/keymap:vi/update-mode-indicator
   5786   else
   5787     local joblist; ble/util/joblist
   5788     if ((${#joblist[*]})); then
   5789       ble/array#push joblist $'Type  \e[35m:q!\e[m  and press \e[35m<Enter>\e[m to abandon all \e[31mjobs\e[m and exit Bash'
   5790       IFS=$'\n' builtin eval 'ble/edit/info/show ansi "${joblist[*]}"'
   5791     else
   5792       ble/edit/info/show ansi $'Type  \e[35m:q\e[m  and press \e[35m<Enter>\e[m to exit Bash'
   5793     fi
   5794   fi
   5795   ble/widget/vi-command/bell
   5796   return 0
   5797 }
   5798 
   5799 # nmap u, U, C-r
   5800 #
   5801 #   `[`] は設定する。vim と違って実際に変更のあった範囲を抽出する。
   5802 #   . は設定しない。
   5803 #
   5804 bleopt/declare -v keymap_vi_imap_undo ''
   5805 _ble_keymap_vi_undo_suppress=
   5806 function ble/keymap:vi/undo/add {
   5807   [[ $_ble_keymap_vi_undo_suppress ]] && return 0
   5808   [[ $1 == more && $bleopt_keymap_vi_imap_undo != more ]] && return 0
   5809   ble-edit/undo/add
   5810 }
   5811 function ble/widget/vi_nmap/undo {
   5812   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5813   local _ble_keymap_vi_undo_suppress=1
   5814   ble/keymap:vi/mark/start-edit-area
   5815   if ble-edit/undo/undo "$ARG"; then
   5816     ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   5817     ble/keymap:vi/mark/end-edit-area
   5818     ble/keymap:vi/adjust-command-mode
   5819   else
   5820     ble/widget/vi-command/bell
   5821     return 1
   5822   fi
   5823 }
   5824 function ble/widget/vi_nmap/redo {
   5825   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5826   local _ble_keymap_vi_undo_suppress=1
   5827   ble/keymap:vi/mark/start-edit-area
   5828   if ble-edit/undo/redo "$ARG"; then
   5829     ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   5830     ble/keymap:vi/mark/end-edit-area
   5831     ble/keymap:vi/adjust-command-mode
   5832   else
   5833     ble/widget/vi-command/bell
   5834     return 1
   5835   fi
   5836 }
   5837 function ble/widget/vi_nmap/revert {
   5838   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5839   local _ble_keymap_vi_undo_suppress=1
   5840   ble/keymap:vi/mark/start-edit-area
   5841   if ble-edit/undo/revert-toggle "$ARG"; then
   5842     ble/keymap:vi/needs-eol-fix && ((_ble_edit_ind--))
   5843     ble/keymap:vi/mark/end-edit-area
   5844     ble/keymap:vi/adjust-command-mode
   5845   else
   5846     ble/widget/vi-command/bell
   5847     return 1
   5848   fi
   5849 }
   5850 
   5851 # nmap C-a, C-x
   5852 function ble/widget/vi_nmap/increment.impl {
   5853   local delta=$1
   5854   ((delta==0)) && return 0
   5855 
   5856   # 数字の範囲の確定
   5857   local line=${_ble_edit_str:_ble_edit_ind}
   5858   line=${line%%$'\n'*}
   5859   local rex='^([^0-9]*)[0-9]+'
   5860   if ! [[ $line =~ $rex ]]; then
   5861     # 行末にいる時(空行を意味する)にはベルは鳴らさない。
   5862     [[ $line ]] && ble/widget/.bell 'number not found'
   5863     ble/keymap:vi/adjust-command-mode
   5864     return 0
   5865   fi
   5866   local rematch1=${BASH_REMATCH[1]}
   5867   local beg=$((_ble_edit_ind+${#rematch1}))
   5868   local end=$((_ble_edit_ind+${#BASH_REMATCH}))
   5869   rex='-?[0-9]*$'; [[ ${_ble_edit_str::beg} =~ $rex ]]
   5870   ((beg-=${#BASH_REMATCH}))
   5871 
   5872   # 数の抽出
   5873   local number=${_ble_edit_str:beg:end-beg}
   5874   local abs=${number#-}
   5875   if [[ $abs == 0?* ]]; then
   5876     if [[ $number == -* ]]; then
   5877       number=-$((10#0$abs))
   5878     else
   5879       number=$((10#0$abs))
   5880     fi
   5881   fi
   5882 
   5883   # 数の増加・減少
   5884   ((number+=delta))
   5885   if [[ $abs == 0?* ]]; then
   5886     # Zero padding
   5887     local wsign=$((number<0?1:0))
   5888     local zpad=$((wsign+${#abs}-${#number}))
   5889     if ((zpad>0)); then
   5890       local ret; ble/string#repeat 0 "$zpad"
   5891       number=${number::wsign}$ret${number:wsign}
   5892     fi
   5893   fi
   5894 
   5895   ble/widget/.replace-range "$beg" "$end" "$number"
   5896   ble/keymap:vi/mark/set-previous-edit-area "$beg" "$((beg+${#number}))"
   5897   ble/keymap:vi/repeat/record
   5898   _ble_edit_ind=$((beg+${#number}-1))
   5899   ble/keymap:vi/adjust-command-mode
   5900   return 0
   5901 }
   5902 function ble/widget/vi_nmap/increment {
   5903   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5904   ble/widget/vi_nmap/increment.impl "$ARG"
   5905 }
   5906 function ble/widget/vi_nmap/decrement {
   5907   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   5908   ble/widget/vi_nmap/increment.impl "$((-ARG))"
   5909 }
   5910 function ble/widget/vi_nmap/__line_limit__.edit {
   5911   ble/keymap:vi/clear-arg
   5912   ble/widget/vi_nmap/.insert-mode
   5913   ble/keymap:vi/repeat/clear-insert
   5914   ble/widget/edit-and-execute-command.impl "$1"
   5915 }
   5916 function ble/widget/vi_nmap/__line_limit__ {
   5917   ble/widget/__line_limit__ vi_nmap/__line_limit__.edit
   5918 }
   5919 
   5920 function ble-decode/keymap:vi_nmap/define {
   5921   ble/keymap:vi/set-up-command-map
   5922 
   5923   ble-bind -f __default__    vi-command/decompose-meta
   5924   ble-bind -f __line_limit__ vi_nmap/__line_limit__
   5925   ble-bind -f 'ESC' vi-command/bell
   5926   ble-bind -f 'C-[' vi-command/bell
   5927   ble-bind -f 'C-c' vi-command/cancel
   5928 
   5929   ble-bind -f a      vi_nmap/append-mode
   5930   ble-bind -f A      vi_nmap/append-mode-at-end-of-line
   5931   ble-bind -f i      vi_nmap/insert-mode
   5932   ble-bind -f insert vi_nmap/insert-mode
   5933   ble-bind -f I      vi_nmap/insert-mode-at-first-non-space
   5934   ble-bind -f 'g I'  vi_nmap/insert-mode-at-beginning-of-line
   5935   ble-bind -f o      vi_nmap/insert-mode-at-forward-line
   5936   ble-bind -f O      vi_nmap/insert-mode-at-backward-line
   5937   ble-bind -f R      vi_nmap/replace-mode
   5938   ble-bind -f 'g R'  vi_nmap/virtual-replace-mode
   5939   ble-bind -f 'g i'  vi_nmap/insert-mode-at-previous-point
   5940 
   5941   ble-bind -f '~'    vi_nmap/forward-char-toggle-case
   5942 
   5943   ble-bind -f Y      vi_nmap/copy-current-line
   5944   ble-bind -f S      vi_nmap/kill-current-line-and-insert
   5945   ble-bind -f D      vi_nmap/kill-forward-line
   5946   ble-bind -f C      vi_nmap/kill-forward-line-and-insert
   5947 
   5948   ble-bind -f p      vi_nmap/paste-after
   5949   ble-bind -f P      vi_nmap/paste-before
   5950 
   5951   ble-bind -f x      vi_nmap/kill-forward-char
   5952   ble-bind -f s      vi_nmap/kill-forward-char-and-insert
   5953   ble-bind -f X      vi_nmap/kill-backward-char
   5954   ble-bind -f delete vi_nmap/kill-forward-char
   5955 
   5956   ble-bind -f 'r'    vi_nmap/replace-char
   5957   ble-bind -f 'g r'  vi_nmap/virtual-replace-char # vim で実際に試すとこの機能はない
   5958 
   5959   ble-bind -f J      vi_nmap/connect-line-with-space
   5960   ble-bind -f 'g J'  vi_nmap/connect-line
   5961 
   5962   ble-bind -f v      vi_nmap/charwise-visual-mode
   5963   ble-bind -f V      vi_nmap/linewise-visual-mode
   5964   ble-bind -f C-v    vi_nmap/blockwise-visual-mode
   5965   ble-bind -f C-q    vi_nmap/blockwise-visual-mode
   5966   ble-bind -f 'g v'  vi-command/previous-visual-area
   5967   ble-bind -f 'g h'    vi_nmap/charwise-select-mode
   5968   ble-bind -f 'g H'    vi_nmap/linewise-select-mode
   5969   ble-bind -f 'g C-h'  vi_nmap/blockwise-select-mode
   5970 
   5971   ble-bind -f .      vi_nmap/repeat
   5972 
   5973   ble-bind -f K      vi_nmap/command-help
   5974   ble-bind -f f1     vi_nmap/command-help
   5975 
   5976   ble-bind -f 'C-d'   vi_nmap/forward-line-scroll
   5977   ble-bind -f 'C-u'   vi_nmap/backward-line-scroll
   5978   ble-bind -f 'C-e'   vi_nmap/forward-scroll
   5979   ble-bind -f 'C-y'   vi_nmap/backward-scroll
   5980   ble-bind -f 'C-f'   vi_nmap/pagedown
   5981   ble-bind -f 'next'  vi_nmap/pagedown
   5982   ble-bind -f 'C-b'   vi_nmap/pageup
   5983   ble-bind -f 'prior' vi_nmap/pageup
   5984   ble-bind -f 'z t'   vi_nmap/scroll-to-top-and-redraw
   5985   ble-bind -f 'z z'   vi_nmap/scroll-to-center-and-redraw
   5986   ble-bind -f 'z b'   vi_nmap/scroll-to-bottom-and-redraw
   5987   ble-bind -f 'z RET' vi_nmap/scroll-to-top-non-space-and-redraw
   5988   ble-bind -f 'z C-m' vi_nmap/scroll-to-top-non-space-and-redraw
   5989   ble-bind -f 'z +'   vi_nmap/scroll-or-pagedown-and-redraw
   5990   ble-bind -f 'z -'   vi_nmap/scroll-to-bottom-non-space-and-redraw
   5991   ble-bind -f 'z .'   vi_nmap/scroll-to-center-non-space-and-redraw
   5992 
   5993   ble-bind -f m      vi-command/set-mark
   5994   ble-bind -f '"'    vi-command/register
   5995 
   5996   ble-bind -f 'C-g' vi-command/show-line-info
   5997 
   5998   ble-bind -f 'q' vi_nmap/record-register
   5999   ble-bind -f '@' vi_nmap/play-register
   6000 
   6001   ble-bind -f u   vi_nmap/undo
   6002   ble-bind -f C-r vi_nmap/redo
   6003   ble-bind -f U   vi_nmap/revert
   6004 
   6005   ble-bind -f C-a vi_nmap/increment
   6006   ble-bind -f C-x vi_nmap/decrement
   6007 
   6008   ble-bind -f 'Z Z' 'vi-command:q'
   6009   ble-bind -f 'Z Q' 'vi-command:q'
   6010 
   6011   #----------------------------------------------------------------------------
   6012   # bash
   6013 
   6014   ble-bind -f 'C-j'     'accept-line'
   6015   ble-bind -f 'C-RET'   'accept-line'
   6016   ble-bind -f 'C-m'     'accept-single-line-or vi-command/forward-first-non-space'
   6017   ble-bind -f 'RET'     'accept-single-line-or vi-command/forward-first-non-space'
   6018   #ble-bind -f 'C-x C-e' 'vi-command/edit-and-execute-command'
   6019   ble-bind -f 'C-l'     'clear-screen'
   6020   ble-bind -f 'C-d'     'vi-command/exit-on-empty-line' # overwrites vi_nmap/forward-scroll
   6021   ble-bind -f 'auto_complete_enter' auto-complete-enter
   6022 
   6023   # Note #D1256: Bash vi-command 互換性の為
   6024   ble-bind -f M-left   'vi-command/backward-vword'
   6025   ble-bind -f M-right  'vi-command/forward-vword'
   6026   ble-bind -f C-delete 'vi-rlfunc/kill-word'
   6027   ble-bind -f '#'      'vi-rlfunc/insert-comment'
   6028   ble-bind -f '&'      'vi_nmap/@edit tilde-expand'
   6029 
   6030   # ble-bind -f 'C-u' 'vi-rlfunc/unix-line-discard'
   6031   # ble-bind -f 'C-q' 'vi-rlfunc/quoted-insert'
   6032   # ble-bind -f 'C-v' 'vi-rlfunc/quoted-insert'
   6033   # ble-bind -f 'C-d' 'vi-rlfunc/eof-maybe'
   6034   # ble-bind -f '_'   'vi-rlfunc/yank-arg'
   6035 }
   6036 
   6037 # lib/core-decode.vi_nmap-rlfunc.txt 用
   6038 
   6039 function ble/widget/vi-rlfunc/.is-uppercase {
   6040   local n=${#KEYS[@]}
   6041   local code=$((KEYS[n?n-1:0]&_ble_decode_MaskChar))
   6042   ((0x41<=code&&code<=0x5a))
   6043 }
   6044 
   6045 # d or D
   6046 function ble/widget/vi-rlfunc/delete-to {
   6047   if ble/widget/vi-rlfunc/.is-uppercase; then
   6048     ble/widget/vi_nmap/kill-forward-line
   6049   else
   6050     ble/widget/vi-command/operator d
   6051   fi
   6052 }
   6053 # c or C
   6054 function ble/widget/vi-rlfunc/change-to {
   6055   if ble/widget/vi-rlfunc/.is-uppercase; then
   6056     ble/widget/vi_nmap/kill-forward-line-and-insert
   6057   else
   6058     ble/widget/vi-command/operator c
   6059   fi
   6060 }
   6061 # y or Y
   6062 function ble/widget/vi-rlfunc/yank-to {
   6063   if ble/widget/vi-rlfunc/.is-uppercase; then
   6064     ble/widget/vi_nmap/copy-current-line
   6065   else
   6066     ble/widget/vi-command/operator y
   6067   fi
   6068 }
   6069 function ble/widget/vi-rlfunc/char-search {
   6070   local n=${#KEYS[@]}
   6071   local code=$((KEYS[n?n-1:0]&_ble_decode_MaskChar))
   6072   ((code==0)) && return 1
   6073   ble/util/c2s "$code"
   6074   case $ret in
   6075   ('f') ble/widget/vi-command/search-forward-char ;;
   6076   ('F') ble/widget/vi-command/search-backward-char ;;
   6077   ('t') ble/widget/vi-command/search-forward-char-prev ;;
   6078   ('T') ble/widget/vi-command/search-backward-char-prev ;;
   6079   (';') ble/widget/vi-command/search-char-repeat ;;
   6080   (',') ble/widget/vi-command/search-char-reverse-repeat ;;
   6081   (*) return 1 ;;
   6082   esac
   6083 }
   6084 # w or W
   6085 function ble/widget/vi-rlfunc/next-word {
   6086   if ble/widget/vi-rlfunc/.is-uppercase; then
   6087     ble/widget/vi-command/forward-uword
   6088   else
   6089     ble/widget/vi-command/forward-vword
   6090   fi
   6091 }
   6092 # b or B
   6093 function ble/widget/vi-rlfunc/prev-word {
   6094   if ble/widget/vi-rlfunc/.is-uppercase; then
   6095     ble/widget/vi-command/backward-uword
   6096   else
   6097     ble/widget/vi-command/backward-vword
   6098   fi
   6099 }
   6100 # e or E
   6101 function ble/widget/vi-rlfunc/end-word {
   6102   if ble/widget/vi-rlfunc/.is-uppercase; then
   6103     ble/widget/vi-command/forward-uword-end
   6104   else
   6105     ble/widget/vi-command/forward-vword-end
   6106   fi
   6107 }
   6108 # p or P
   6109 function ble/widget/vi-rlfunc/put {
   6110   if ble/widget/vi-rlfunc/.is-uppercase; then
   6111     ble/widget/vi_nmap/paste-before
   6112   else
   6113     ble/widget/vi_nmap/paste-after
   6114   fi
   6115 }
   6116 # / or ?
   6117 function ble/widget/vi-rlfunc/search {
   6118   local n=${#KEYS[@]}
   6119   local code=$((KEYS[n?n-1:0]&_ble_decode_MaskChar))
   6120   if ((code==63)); then
   6121     ble/widget/vi-command/search-backward
   6122   else
   6123     ble/widget/vi-command/search-forward
   6124   fi
   6125 }
   6126 # n or N
   6127 function ble/widget/vi-rlfunc/search-again {
   6128   if ble/widget/vi-rlfunc/.is-uppercase; then
   6129     ble/widget/vi-command/search-reverse-repeat
   6130   else
   6131     ble/widget/vi-command/search-repeat
   6132   fi
   6133 }
   6134 # s or S
   6135 function ble/widget/vi-rlfunc/subst {
   6136   if ble/widget/vi-rlfunc/.is-uppercase; then
   6137     ble/widget/vi_nmap/kill-current-line-and-insert
   6138   else
   6139     ble/widget/vi_nmap/kill-forward-char-and-insert
   6140   fi
   6141 }
   6142 # rl_nmap C-delete
   6143 function ble/widget/vi-rlfunc/kill-word {
   6144   _ble_keymap_vi_opfunc=d
   6145   ble/widget/vi-command/forward-vword-end
   6146 }
   6147 # rl_nmap C-u
   6148 function ble/widget/vi-rlfunc/unix-line-discard {
   6149   _ble_keymap_vi_opfunc=d
   6150   ble/widget/vi-command/beginning-of-line
   6151 }
   6152 # rl_nmap #
   6153 function ble/widget/vi-rlfunc/insert-comment {
   6154   local ARG FLAG REG; ble/keymap:vi/get-arg ''
   6155   ble/keymap:vi/mark/start-edit-area
   6156   ble/widget/insert-comment/.insert "$ARG"
   6157   ble/keymap:vi/mark/end-edit-area
   6158   ble/widget/vi_nmap/accept-line
   6159 }
   6160 # rl_nmap C-v, C-q
   6161 function ble/widget/vi-rlfunc/quoted-insert-char.hook {
   6162   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6163   ble/keymap:vi/mark/start-edit-area
   6164   _ble_edit_arg=$ARG ble/widget/quoted-insert-char.hook
   6165   ble/keymap:vi/mark/end-edit-area
   6166   ble/keymap:vi/repeat/record
   6167   ble/keymap:vi/adjust-command-mode
   6168   return 0
   6169 }
   6170 function ble/widget/vi-rlfunc/quoted-insert-char {
   6171   _ble_edit_mark_active=
   6172   _ble_decode_char__hook=ble/widget/vi-rlfunc/quoted-insert-char.hook
   6173   return 147
   6174 }
   6175 function ble/widget/vi-rlfunc/quoted-insert.hook {
   6176   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6177   ble/keymap:vi/mark/start-edit-area
   6178   _ble_edit_arg=$ARG ble/widget/quoted-insert.hook
   6179   ble/keymap:vi/mark/end-edit-area
   6180   ble/keymap:vi/repeat/record
   6181   ble/keymap:vi/adjust-command-mode
   6182   return 0
   6183 }
   6184 function ble/widget/vi-rlfunc/quoted-insert {
   6185   _ble_edit_mark_active=
   6186   _ble_decode_key__hook=ble/widget/vi-rlfunc/quoted-insert.hook
   6187   return 147
   6188 }
   6189 # rl_nmap C-d
   6190 function ble/widget/vi-rlfunc/eof-maybe {
   6191   if [[ ! $_ble_edit_str ]]; then
   6192     ble/widget/exit
   6193     ble/keymap:vi/adjust-command-mode # ジョブがあるときは終了しないので。
   6194     return 1
   6195   elif ble-edit/is-single-complete-line; then
   6196     ble/widget/vi_nmap/accept-line
   6197   else
   6198     local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6199     ble/keymap:vi/mark/start-edit-area
   6200     _ble_edit_ind=${#_ble_edit_str}
   6201     _ble_edit_arg=$ARG
   6202     ble/widget/self-insert
   6203     ble/keymap:vi/mark/end-edit-area
   6204     ble/keymap:vi/adjust-command-mode
   6205   fi
   6206 }
   6207 # rl_nmap _
   6208 function ble/widget/vi-rlfunc/yank-arg {
   6209   ble/widget/vi_nmap/append-mode
   6210   ble/keymap:vi/imap-repeat/reset
   6211   local -a KEYS; KEYS=(32)
   6212   ble/widget/self-insert
   6213   ble/util/unlocal KEYS
   6214   ble/widget/insert-last-argument
   6215   return "$?"
   6216 }
   6217 
   6218 # rlfunc: forward-byte, backward-byte
   6219 function ble/widget/vi-command/forward-byte {
   6220   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6221   local index=$_ble_edit_ind
   6222   ble/widget/.locate-forward-byte "$ARG" || [[ $FLAG ]] || ble/widget/.bell
   6223   ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG"
   6224 }
   6225 function ble/widget/vi-command/backward-byte {
   6226   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6227   local index=$_ble_edit_ind
   6228   ble/widget/.locate-forward-byte "$((-ARG))" || [[ $FLAG ]] || ble/widget/.bell
   6229   ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG"
   6230 }
   6231 
   6232 # rlfunc: capitalize-word, downcase-word, upcase-word
   6233 #%define 2
   6234 function ble/widget/vi_nmap/capitalize-XWORD { ble/widget/filter-word.impl XWORD ble/string#capitalize; }
   6235 function ble/widget/vi_nmap/downcase-XWORD   { ble/widget/filter-word.impl XWORD ble/string#tolower; }
   6236 function ble/widget/vi_nmap/upcase-XWORD     { ble/widget/filter-word.impl XWORD ble/string#toupper; }
   6237 #%end
   6238 #%expand 2.r/XWORD/eword/
   6239 #%expand 2.r/XWORD/cword/
   6240 #%expand 2.r/XWORD/uword/
   6241 #%expand 2.r/XWORD/sword/
   6242 #%expand 2.r/XWORD/fword/
   6243 
   6244 function ble/widget/vi_nmap/@edit {
   6245   ble/keymap:vi/clear-arg
   6246   ble/keymap:vi/repeat/record
   6247   ble/keymap:vi/mark/start-edit-area
   6248   ble/widget/"$@"
   6249   ble/keymap:vi/mark/end-edit-area
   6250   ble/keymap:vi/adjust-command-mode
   6251 }
   6252 function ble/widget/vi_nmap/@adjust {
   6253   ble/keymap:vi/clear-arg
   6254   ble/widget/"$@"
   6255   ble/keymap:vi/adjust-command-mode
   6256 }
   6257 function ble/widget/vi_nmap/@motion {
   6258   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6259   local _ble_edit_ind=$_ble_edit_ind _ble_edit_arg=$ARG
   6260   if ble/widget/"$@"; then
   6261     local index=$_ble_edit_ind
   6262     ble/util/unlocal _ble_edit_ind
   6263     ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
   6264   else
   6265     ble/keymap:vi/adjust-command-mode
   6266   fi
   6267 }
   6268 
   6269 #------------------------------------------------------------------------------
   6270 # Visual mode
   6271 
   6272 # 選択の種類は _ble_edit_mark_active に設定される文字列で区別する。
   6273 #
   6274 #   _ble_edit_mark_active は vi_char, vi_line, vi_block のどれかである
   6275 #   更に末尾拡張 (行末までの選択範囲の拡張) が設定されているときには
   6276 #   vi_char+, vi_line+, vi_block+ などの様に + が末尾に付く。
   6277 
   6278 function ble/keymap:vi/xmap/has-eol-extension {
   6279   [[ $_ble_edit_mark_active == *+ ]]
   6280 }
   6281 function ble/keymap:vi/xmap/add-eol-extension {
   6282   [[ $_ble_edit_mark_active ]] &&
   6283     _ble_edit_mark_active=${_ble_edit_mark_active%+}+
   6284 }
   6285 function ble/keymap:vi/xmap/remove-eol-extension {
   6286   [[ $_ble_edit_mark_active ]] &&
   6287     _ble_edit_mark_active=${_ble_edit_mark_active%+}
   6288 }
   6289 function ble/keymap:vi/xmap/switch-type {
   6290   local suffix; [[ $_ble_edit_mark_active == *+ ]] && suffix=+
   6291   _ble_edit_mark_active=$1$suffix
   6292 }
   6293 
   6294 #--------------------------------------
   6295 # xmap/矩形範囲の抽出
   6296 
   6297 ## @fn local p0 q0 lx ly rx ry; ble/keymap:vi/get-graphical-rectangle [index1 [index2]]
   6298 ## @fn local p0 q0 lx ly rx ry; ble/keymap:vi/get-logical-rectangle   [index1 [index2]]
   6299 ## @fn local p0 q0 lx ly rx ry; ble/keymap:vi/get-rectangle [index1 [index2]]
   6300 ## @fn local ret              ; ble/keymap:vi/get-rectangle-height [index1 [index2]]
   6301 ##
   6302 ##   @param[in,opt] index1 [=_ble_edit_mark]
   6303 ##   @param[in,opt] index2 [=_ble_edit_ind]
   6304 ##
   6305 ##   @var[out] p0 q0
   6306 ##   @var[out] lx ly rx ry
   6307 ##
   6308 function ble/keymap:vi/get-graphical-rectangle {
   6309   local p=${1:-$_ble_edit_mark} q=${2:-$_ble_edit_ind}
   6310   local ret
   6311   ble-edit/content/find-logical-bol "$p"; p0=$ret
   6312   ble-edit/content/find-logical-bol "$q"; q0=$ret
   6313 
   6314   local p0x p0y q0x q0y
   6315   ble/textmap#getxy.out --prefix=p0 "$p0"
   6316   ble/textmap#getxy.out --prefix=q0 "$q0"
   6317 
   6318   local plx ply qlx qly
   6319   ble/textmap#getxy.cur --prefix=pl "$p"
   6320   ble/textmap#getxy.cur --prefix=ql "$q"
   6321 
   6322   local prx=$plx pry=$ply qrx=$qlx qry=$qly
   6323   ble-edit/content/eolp "$p" && ((prx++)) || ble/textmap#getxy.out --prefix=pr "$((p+1))"
   6324   ble-edit/content/eolp "$q" && ((qrx++)) || ble/textmap#getxy.out --prefix=qr "$((q+1))"
   6325 
   6326   ((ply-=p0y,qly-=q0y,pry-=p0y,qry-=q0y,
   6327     (ply<qly||ply==qly&&plx<qlx)?(lx=plx,ly=ply):(lx=qlx,ly=qly),
   6328     (pry>qry||pry==qry&&prx>qrx)?(rx=prx,ry=pry):(rx=qrx,ry=qry)))
   6329 }
   6330 function ble/keymap:vi/get-logical-rectangle {
   6331   local p=${1:-$_ble_edit_mark} q=${2:-$_ble_edit_ind}
   6332   local ret
   6333   ble-edit/content/find-logical-bol "$p"; p0=$ret
   6334   ble-edit/content/find-logical-bol "$q"; q0=$ret
   6335   ((p-=p0,q-=q0,p<=q)) || local p=$q q=$p
   6336   lx=$p rx=$((q+1)) ly=0 ry=0
   6337 }
   6338 function ble/keymap:vi/get-rectangle {
   6339   if ble/edit/use-textmap; then
   6340     ble/keymap:vi/get-graphical-rectangle "$@"
   6341   else
   6342     ble/keymap:vi/get-logical-rectangle "$@"
   6343   fi
   6344 }
   6345 ## @fn ble/keymap:vi/get-rectangle-height [index1 [index2]]
   6346 ##   @var[out] ret
   6347 function ble/keymap:vi/get-rectangle-height {
   6348   local p0 q0 lx ly rx ry
   6349   ble/keymap:vi/get-rectangle "$@"
   6350   ble/string#count-char "${_ble_edit_str:p0:q0-p0}" $'\n'
   6351   ((ret++))
   6352   return 0
   6353 }
   6354 
   6355 
   6356 ## @fn ble/keymap:vi/extract-graphical-block-by-geometry bol1 bol2 x1:y1 x2:y2 opts
   6357 ## @fn ble/keymap:vi/extract-logical-block-by-geometry bol1 bol2 c1 c2 opts
   6358 ##   指定した引数の範囲を元に矩形範囲を抽出します。
   6359 ## @fn ble/keymap:vi/extract-graphical-block [index1 [index2 [opts]]]
   6360 ## @fn ble/keymap:vi/extract-logical-block [index1 [index2 [opts]]]
   6361 ## @fn ble/keymap:vi/extract-block [index1 [index2 [opts]]]
   6362 ##   現在位置 (_ble_edit_ind) とマーク (_ble_edit_mark) を元に矩形範囲を抽出します。
   6363 ##
   6364 ##   @param[in] bol1 bol2
   6365 ##     2つの行の行頭を指定します。
   6366 ##   @param[in] x1:y1 x2:y2
   6367 ##     2つの列を行頭からの相対位置で指定します。
   6368 ##   @param[in] c1 c2
   6369 ##     2つの列を論理列で指定します。
   6370 ##
   6371 ##   @param[in,opt] index1 [$_ble_edit_mark]
   6372 ##   @param[in,opt] index2 [$_ble_edit_ind]
   6373 ##     矩形の端点の文字インデックスを指定します。
   6374 ##
   6375 ##   @param[in,opt] opts
   6376 ##     コロン区切りのフラグ指定です。
   6377 ##
   6378 ##     first_line
   6379 ##       矩形を構成する最初の行についてだけ情報を取得します。
   6380 ##       その他の行については空の情報 (':::::') を sub_ranges に格納します。
   6381 ##
   6382 ##     skip_middle
   6383 ##       矩形を構成する最初と最後の行についてだけ情報を取得します。
   6384 ##       その他の行については空の情報 (':::::') を sub_ranges に格納します。
   6385 ##
   6386 ##   @var[in] _ble_edit_mark_active
   6387 ##     末尾拡張を行うばあいにこの引数の末端に + を指定します。
   6388 ##
   6389 ##   @arr[out] sub_ranges
   6390 ##     矩形を構成する各行の情報を格納します。
   6391 ##     各要素は以下の形式を持ちます。
   6392 ##
   6393 ##     smin:smax:slpad:srpad:sfill:stext
   6394 ##
   6395 ##     smin smax
   6396 ##       選択範囲を強調するとき・切り取るときの範囲を指定します。
   6397 ##     slpad srpad
   6398 ##       選択範囲を切り取ったときに左右に補填する空白の数を指定します。
   6399 ##     sfill
   6400 ##       矩形の挿入時に右端に補填するべき空白の数を指定します。
   6401 ##     stext
   6402 ##       選択範囲から読み取られる文字列を指定します。
   6403 ##       全角文字などが範囲の境界を跨ぐとき、
   6404 ##       その文字は (範囲に被る幅と同数の) 空白に置き換えられます。
   6405 ##   @var[out] sub_x1 sub_x2
   6406 ##
   6407 function ble/keymap:vi/extract-graphical-block-by-geometry {
   6408   local bol1=$1 bol2=$2 x1=$3 x2=$4 y1=0 y2=0 opts=$5
   6409   ((bol1<=bol2||(bol1=$2,bol2=$1)))
   6410   [[ $x1 == *:* ]] && local x1=${x1%%:*} y1=${x1#*:}
   6411   [[ $x2 == *:* ]] && local x2=${x2%%:*} y2=${x2#*:}
   6412 
   6413   local cols=$_ble_textmap_cols
   6414   local c1=$((cols*y1+x1)) c2=$((cols*y2+x2))
   6415   sub_x1=$c1 sub_x2=$c2
   6416 
   6417   local ret index lx ly rx ly
   6418 
   6419   ble-edit/content/find-logical-eol "$bol2"; local eol2=$ret
   6420   local lines; ble/string#split-lines lines "${_ble_edit_str:bol1:eol2-bol1}"
   6421 
   6422   sub_ranges=()
   6423   local min_sfill=0
   6424   local line bol=$bol1 eol bolx boly
   6425   local c1l c1r c2l c2r
   6426   for line in "${lines[@]}"; do
   6427     ((eol=bol+${#line}))
   6428 
   6429     if [[ :$opts: == *:first_line:* ]] && ((${#sub_ranges[@]})); then
   6430       ble/array#push sub_ranges :::::
   6431     elif [[ :$opts: == *:skip_middle:* ]] && ((0<${#sub_ranges[@]}&&${#sub_ranges[@]}<${#lines[@]}-1)); then
   6432       ble/array#push sub_ranges :::::
   6433     else
   6434       ble/textmap#getxy.out --prefix=bol "$bol"
   6435       ble/textmap#hit out "$x1" "$((boly+y1))" "$bol" "$eol"
   6436       local smin=$index x1l=$lx y1l=$ly x1r=$rx y1r=$ry
   6437       if ble/keymap:vi/xmap/has-eol-extension; then
   6438         local eolx eoly; ble/textmap#getxy.out --prefix=eol "$eol"
   6439         local smax=$eol x2l=$eolx y2l=$eoly x2r=$eolx y2r=$eoly
   6440       else
   6441         ble/textmap#hit out "$x2" "$((boly+y2))" "$bol" "$eol"
   6442         local smax=$index x2l=$lx y2l=$ly x2r=$rx y2r=$ry
   6443       fi
   6444 
   6445       local sfill=0 slpad=0 srpad=0
   6446       local stext=${_ble_edit_str:smin:smax-smin}
   6447       if ((smin<smax)); then
   6448         # 1. 左の境界 c1 を大きな文字が跨いでいるときは空白に変換する。
   6449         ((c1l=(y1l-boly)*cols+x1l))
   6450         if ((c1l<c1)); then
   6451           ((slpad=c1-c1l))
   6452 
   6453           # assert: smin < smax <= eol なので行末ではない
   6454           ble/util/assert '! ble-edit/content/eolp "$smin"'
   6455 
   6456           ((c1r=(y1r-boly)*cols+x1r))
   6457           ble/util/assert '((c1r>c1))' || ((c1r=c1))
   6458           ble/string#repeat ' ' "$((c1r-c1))"
   6459           stext=$ret${stext:1}
   6460         fi
   6461 
   6462         # 2. 右の境界 c2 を大きな文字が跨いでいるときは空白に変換する
   6463         ((c2l=(y2l-boly)*cols+x2l))
   6464         if ((c2l<c2)); then
   6465           if ((smax==eol)); then
   6466             ((sfill=c2-c2l))
   6467           else
   6468             ble/string#repeat ' ' "$((c2-c2l))"
   6469             stext=$stext$ret
   6470             ((smax++))
   6471 
   6472             ((c2r=(y2r-boly)*cols+x2r))
   6473             ble/util/assert '((c2r>c2))' || ((c2r=c2))
   6474             ((srpad=c2r-c2))
   6475           fi
   6476         elif ((c2l>c2)); then
   6477           # ここに来るのは ble/keymap:vi/xmap/has-eol-extension のときのみの筈
   6478           ((sfill=c2-c2l,
   6479             sfill<min_sfill&&(min_sfill=sfill)))
   6480         fi
   6481       else
   6482         if ((smin==eol)); then
   6483           # 行末
   6484           ((sfill=c2-c1))
   6485         elif ((c2>c1)); then
   6486           # 範囲の両端が単一の文字の左端または内部にある
   6487           ble/string#repeat ' ' "$((c2-c1))"
   6488           stext=$ret${stext:1}
   6489           ((smax++))
   6490 
   6491           ((c1l=(y1l-boly)*cols+x1l,slpad=c1-c1l))
   6492           ((c1r=(y1r-boly)*cols+x1r,srpad=c1r-c1))
   6493         fi
   6494       fi
   6495 
   6496       ble/array#push sub_ranges "$smin:$smax:$slpad:$srpad:$sfill:$stext"
   6497     fi
   6498 
   6499     ((bol=eol+1))
   6500   done
   6501 
   6502   if ((min_sfill<0)); then
   6503     local isub=${#sub_ranges[@]}
   6504     while ((isub--)); do
   6505       local sub=${sub_ranges[isub]}
   6506       local sub45=${sub#*:*:*:*:}
   6507       local sfill=${sub45%%:*}
   6508       sub_ranges[isub]=${sub::${#sub}-${#sub45}}$((sfill-min_sfill))${sub45:${#sfill}}
   6509     done
   6510   fi
   6511 }
   6512 function ble/keymap:vi/extract-graphical-block {
   6513   local opts=$3
   6514   local p0 q0 lx ly rx ry
   6515   ble/keymap:vi/get-graphical-rectangle "$@"
   6516   ble/keymap:vi/extract-graphical-block-by-geometry "$p0" "$q0" "$lx:$ly" "$rx:$ry" "$opts"
   6517 }
   6518 function ble/keymap:vi/extract-logical-block-by-geometry {
   6519   local bol1=$1 bol2=$2 x1=$3 x2=$4 opts=$5
   6520   ((bol1<=bol2||(bol1=$2,bol2=$1)))
   6521   sub_x1=$c1 sub_x2=$c2
   6522 
   6523   local ret min_sfill=0
   6524   local bol=$bol1 eol smin smax slpad srpad sfill
   6525   sub_ranges=()
   6526   while :; do
   6527     ble-edit/content/find-logical-eol "$bol"; eol=$ret
   6528     slpad=0 srpad=0 sfill=0
   6529     ((smin=bol+x1,smin>eol&&(smin=eol)))
   6530     if ble/keymap:vi/xmap/has-eol-extension; then
   6531       ((smax=eol,
   6532         sfill=bol+x2-eol,
   6533         sfill<min_sfill&&(min_sfill=sfill)))
   6534     else
   6535       ((smax=bol+x2,smax>eol&&(sfill=smax-eol,smax=eol)))
   6536     fi
   6537 
   6538     local stext=${_ble_edit_str:smin:smax-smin}
   6539 
   6540     ble/array#push sub_ranges "$smin:$smax:$slpad:$srpad:$sfill:$stext"
   6541 
   6542     ((bol>=bol2)) && break
   6543     ble-edit/content/find-logical-bol "$bol" 1; bol=$ret
   6544   done
   6545 
   6546   if ((min_sfill<0)); then
   6547     local isub=${#sub_ranges[@]}
   6548     while ((isub--)); do
   6549       local sub=${sub_ranges[isub]}
   6550       local sub45=${sub#*:*:*:*:}
   6551       local sfill=${sub45%%:*}
   6552       sub_ranges[isub]=${sub::${#sub}-${#sub45}}$((sfill-min_sfill))${sub45:${#sfill}}
   6553     done
   6554   fi
   6555 }
   6556 function ble/keymap:vi/extract-logical-block {
   6557   local opts=$3
   6558   local p0 q0 lx ly rx ry
   6559   ble/keymap:vi/get-logical-rectangle "$@"
   6560   ble/keymap:vi/extract-logical-block-by-geometry "$p0" "$q0" "$lx" "$rx" "$opts"
   6561 }
   6562 function ble/keymap:vi/extract-block {
   6563   if ble/edit/use-textmap; then
   6564     ble/keymap:vi/extract-graphical-block "$@"
   6565   else
   6566     ble/keymap:vi/extract-logical-block "$@"
   6567   fi
   6568 }
   6569 
   6570 #--------------------------------------
   6571 # xmap/選択範囲の着色の設定
   6572 
   6573 ## @fn ble/highlight/layer:region/mark:vi_char/get-selection
   6574 ## @fn ble/highlight/layer:region/mark:vi_line/get-selection
   6575 ## @fn ble/highlight/layer:region/mark:vi_block/get-selection
   6576 ##   @arr[out] selection
   6577 function ble/highlight/layer:region/mark:vi_char/get-selection {
   6578   local rmin rmax
   6579   if ((_ble_edit_mark<_ble_edit_ind)); then
   6580     rmin=$_ble_edit_mark rmax=$_ble_edit_ind
   6581   else
   6582     rmin=$_ble_edit_ind rmax=$_ble_edit_mark
   6583   fi
   6584   ble-edit/content/eolp "$rmax" || ((rmax++))
   6585   selection=("$rmin" "$rmax")
   6586 }
   6587 function ble/highlight/layer:region/mark:vi_line/get-selection {
   6588   local rmin rmax
   6589   if ((_ble_edit_mark<_ble_edit_ind)); then
   6590     rmin=$_ble_edit_mark rmax=$_ble_edit_ind
   6591   else
   6592     rmin=$_ble_edit_ind rmax=$_ble_edit_mark
   6593   fi
   6594   local ret
   6595   ble-edit/content/find-logical-bol "$rmin"; rmin=$ret
   6596   ble-edit/content/find-logical-eol "$rmax"; rmax=$ret
   6597   selection=("$rmin" "$rmax")
   6598 }
   6599 function ble/highlight/layer:region/mark:vi_block/get-selection {
   6600   local sub_ranges sub_x1 sub_x2
   6601   ble/keymap:vi/extract-block
   6602 
   6603   selection=()
   6604   local sub
   6605   for sub in "${sub_ranges[@]}"; do
   6606     ble/string#split sub : "$sub"
   6607     ((sub[0]<sub[1])) || continue
   6608     ble/array#push selection "${sub[0]}" "${sub[1]}"
   6609   done
   6610 }
   6611 function ble/highlight/layer:region/mark:vi_char+/get-selection {
   6612   ble/highlight/layer:region/mark:vi_char/get-selection
   6613 }
   6614 function ble/highlight/layer:region/mark:vi_line+/get-selection {
   6615   ble/highlight/layer:region/mark:vi_line/get-selection
   6616 }
   6617 function ble/highlight/layer:region/mark:vi_block+/get-selection {
   6618   ble/highlight/layer:region/mark:vi_block/get-selection
   6619 }
   6620 
   6621 function ble/highlight/layer:region/mark:vi_char/get-face   { [[ $_ble_edit_overwrite_mode ]] && face=region_target; }
   6622 function ble/highlight/layer:region/mark:vi_char+/get-face  { ble/highlight/layer:region/mark:vi_char/get-face; }
   6623 function ble/highlight/layer:region/mark:vi_line/get-face   { ble/highlight/layer:region/mark:vi_char/get-face; }
   6624 function ble/highlight/layer:region/mark:vi_line+/get-face  { ble/highlight/layer:region/mark:vi_char/get-face; }
   6625 function ble/highlight/layer:region/mark:vi_block/get-face  { ble/highlight/layer:region/mark:vi_char/get-face; }
   6626 function ble/highlight/layer:region/mark:vi_block+/get-face { ble/highlight/layer:region/mark:vi_char/get-face; }
   6627 
   6628 
   6629 #--------------------------------------
   6630 # xmap/前回の選択サイズ
   6631 
   6632 _ble_keymap_vi_xmap_prev_edit=vi_char:1:1
   6633 ble/array#push _ble_textarea_local_VARNAMES \
   6634                _ble_keymap_vi_xmap_prev_edit
   6635 function ble/widget/vi_xmap/.save-visual-state {
   6636   local nline nchar mark_type=${_ble_edit_mark_active%+}
   6637   if [[ $mark_type == vi_block ]]; then
   6638     local p0 q0 lx rx ly ry
   6639     if ble/edit/use-textmap; then
   6640       local cols=$_ble_textmap_cols
   6641       ble/keymap:vi/get-graphical-rectangle
   6642       ((lx+=ly*cols,rx+=ry*cols))
   6643     else
   6644       ble/keymap:vi/get-logical-rectangle
   6645     fi
   6646 
   6647     nchar=$((rx-lx))
   6648 
   6649     local ret
   6650     ((p0<=q0)) || local p0=$q0 q0=$p0
   6651     ble/string#count-char "${_ble_edit_str:p0:q0-p0}" $'\n'
   6652     nline=$((ret+1))
   6653 
   6654   else
   6655     local ret
   6656     local p=$_ble_edit_mark q=$_ble_edit_ind
   6657     ((p<=q)) || local p=$q q=$p
   6658     ble/string#count-char "${_ble_edit_str:p:q-p}" $'\n'
   6659     nline=$((ret+1))
   6660 
   6661     local base
   6662     if ((nline==1)) && [[ $mark_type != vi_line ]]; then
   6663       base=$p
   6664     else
   6665       ble-edit/content/find-logical-bol "$q"; base=$ret
   6666     fi
   6667 
   6668     if ble/edit/use-textmap; then
   6669       local cols=$_ble_textmap_cols
   6670       local bx by x y
   6671       ble/textmap#getxy.cur --prefix=b "$base"
   6672       ble/textmap#getxy.cur "$q"
   6673       nchar=$((x-bx+(y-by)*cols+1))
   6674     else
   6675       nchar=$((q-base+1))
   6676     fi
   6677   fi
   6678 
   6679   _ble_keymap_vi_xmap_prev_edit=$_ble_edit_mark_active:$nchar:$nline
   6680 }
   6681 function ble/widget/vi_xmap/.restore-visual-state {
   6682   local arg=$1; ((arg>0)) || arg=1
   6683   local prev; ble/string#split prev : "$_ble_keymap_vi_xmap_prev_edit"
   6684   _ble_edit_mark_active=${prev[0]:-vi_char}
   6685   local nchar=${prev[1]:-1}
   6686   local nline=${prev[2]:-1}
   6687   ((nchar<1&&(nchar=1),nline<1&&(nline=1)))
   6688 
   6689   local is_x_relative=0
   6690   if [[ ${_ble_edit_mark_active%+} == vi_block ]]; then
   6691     ((is_x_relative=1,nchar*=arg,nline*=arg))
   6692   elif [[ ${_ble_edit_mark_active%+} == vi_line ]]; then
   6693     ((nline*=arg,is_x_relative=1,nchar=1))
   6694   else
   6695     ((nline==1?(is_x_relative=1,nchar*=arg):(nline*=arg)))
   6696   fi
   6697   ((nchar--,nline--))
   6698 
   6699   local index ret
   6700   ble-edit/content/find-logical-bol "$_ble_edit_ind" 0; local b1=$ret
   6701   ble-edit/content/find-logical-bol "$_ble_edit_ind" "$nline"; local b2=$ret
   6702   ble-edit/content/find-logical-eol "$b2"; local e2=$ret
   6703   if ble/keymap:vi/xmap/has-eol-extension; then
   6704     index=$e2
   6705   elif ble/edit/use-textmap; then
   6706     local cols=$_ble_textmap_cols
   6707     local b1x b1y b2x b2y x y
   6708     ble/textmap#getxy.out --prefix=b1 "$b1"
   6709     ble/textmap#getxy.out --prefix=b2 "$b2"
   6710     if ((is_x_relative)); then
   6711       ble/textmap#getxy.out "$_ble_edit_ind"
   6712       local c=$((x+(y-b1y)*cols+nchar))
   6713     else
   6714       local c=$nchar
   6715     fi
   6716     ((y=c/cols,x=c%cols))
   6717 
   6718     local lx ly rx ry
   6719     ble/textmap#hit out "$x" "$((b2y+y))" "$b2" "$e2"
   6720   else
   6721     local c=$((is_x_relative?_ble_edit_ind-b1+nchar:nchar))
   6722     ((index=b2+c,index>e2&&(index=e2)))
   6723   fi
   6724 
   6725   _ble_edit_mark=$_ble_edit_ind
   6726   _ble_edit_ind=$index
   6727 }
   6728 
   6729 #--------------------------------------
   6730 # xmap/前回の選択範囲
   6731 
   6732 # mark `< `>
   6733 _ble_keymap_vi_xmap_prev_visual=
   6734 ble/array#push _ble_textarea_local_VARNAMES \
   6735                _ble_keymap_vi_xmap_prev_visual
   6736 function ble/keymap:vi/xmap/set-previous-visual-area {
   6737   local beg end
   6738   local mark_type=${_ble_edit_mark_active%+}
   6739   if [[ $mark_type == vi_block ]]; then
   6740     local sub_ranges sub_x1 sub_x2
   6741     ble/keymap:vi/extract-block
   6742     local nrange=${#sub_ranges[*]}
   6743     ((nrange)) || return 1
   6744     local beg=${sub_ranges[0]%%:*}
   6745     local sub2_slice1=${sub_ranges[nrange-1]#*:}
   6746     local end=${sub2_slice1%%:*}
   6747     ((beg<end)) && ! ble-edit/content/bolp "$end" && ((end--))
   6748   else
   6749     local beg=$_ble_edit_mark end=$_ble_edit_ind
   6750     ((beg<=end)) || local beg=$end end=$beg
   6751     if [[ $mark_type == vi_line ]]; then
   6752       local ret
   6753       ble-edit/content/find-logical-bol "$beg"; beg=$ret
   6754       ble-edit/content/find-logical-eol "$end"; end=$ret
   6755       ble-edit/content/bolp "$end" || ((end--))
   6756     fi
   6757   fi
   6758   _ble_keymap_vi_xmap_prev_visual=$_ble_edit_mark_active
   6759   ble/keymap:vi/mark/set-local-mark 60 "$beg" # `<
   6760   ble/keymap:vi/mark/set-local-mark 62 "$end" # `>
   6761 }
   6762 # nmap/xmap gv
   6763 function ble/widget/vi-command/previous-visual-area {
   6764   local mark=$_ble_keymap_vi_xmap_prev_visual
   6765   local ret beg= end=
   6766   ble/keymap:vi/mark/get-local-mark 60 && beg=$ret # `<
   6767   ble/keymap:vi/mark/get-local-mark 62 && end=$ret # `>
   6768   [[ $beg && $end ]] || return 1
   6769 
   6770   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   6771     ble/keymap:vi/clear-arg
   6772     ble/keymap:vi/xmap/set-previous-visual-area
   6773     _ble_edit_ind=$end
   6774     _ble_edit_mark=$beg
   6775     _ble_edit_mark_active=$mark
   6776     ble/keymap:vi/update-mode-indicator
   6777   else
   6778     ble/keymap:vi/clear-arg
   6779     ble/widget/vi-command/visual-mode.impl vi_xmap "$mark"
   6780     _ble_edit_ind=$end
   6781     _ble_edit_mark=$beg
   6782   fi
   6783   return 0
   6784 }
   6785 
   6786 #--------------------------------------
   6787 # xmap/モード遷移
   6788 
   6789 function ble/widget/vi-command/visual-mode.impl {
   6790   local keymap=$1 visual_type=$2
   6791   local ARG FLAG REG; ble/keymap:vi/get-arg 0
   6792   if [[ $FLAG ]]; then
   6793     ble/widget/vi-command/bell
   6794     return 1
   6795   fi
   6796 
   6797   _ble_edit_overwrite_mode=
   6798   _ble_edit_mark=$_ble_edit_ind
   6799   _ble_edit_mark_active=$visual_type
   6800   _ble_keymap_vi_xmap_insert_data= # ※矩形挿入の途中で更に xmap に入ったときはキャンセル
   6801 
   6802   ((ARG)) && ble/widget/vi_xmap/.restore-visual-state "$ARG"
   6803 
   6804   ble/decode/keymap/push "$keymap"
   6805   ble/keymap:vi/update-mode-indicator
   6806   return 0
   6807 }
   6808 function ble/widget/vi_nmap/charwise-visual-mode {
   6809   ble/widget/vi-command/visual-mode.impl vi_xmap vi_char
   6810 }
   6811 function ble/widget/vi_nmap/linewise-visual-mode {
   6812   ble/widget/vi-command/visual-mode.impl vi_xmap vi_line
   6813 }
   6814 function ble/widget/vi_nmap/blockwise-visual-mode {
   6815   ble/widget/vi-command/visual-mode.impl vi_xmap vi_block
   6816 }
   6817 function ble/widget/vi_nmap/charwise-select-mode {
   6818   ble/widget/vi-command/visual-mode.impl vi_smap vi_char
   6819 }
   6820 function ble/widget/vi_nmap/linewise-select-mode {
   6821   ble/widget/vi-command/visual-mode.impl vi_smap vi_line
   6822 }
   6823 function ble/widget/vi_nmap/blockwise-select-mode {
   6824   ble/widget/vi-command/visual-mode.impl vi_smap vi_block
   6825 }
   6826 
   6827 function ble/widget/vi_xmap/exit {
   6828   # Note: xmap operator:c
   6829   #   -> vi_xmap/block-insert-mode.impl
   6830   #   → vi_xmap/cancel 経由で呼び出されるとき、
   6831   #   既に vi_nmap に戻っていることがあるので、vi_xmap, vi_smap のときだけ処理する。
   6832   if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   6833     ble/keymap:vi/xmap/set-previous-visual-area
   6834     _ble_edit_mark_active=
   6835     ble/decode/keymap/pop
   6836     ble/keymap:vi/update-mode-indicator
   6837     ble/keymap:vi/adjust-command-mode
   6838   fi
   6839   return 0
   6840 }
   6841 function ble/widget/vi_xmap/cancel {
   6842   # もし single-command-mode にいたとしても消去して normal へ移動する
   6843 
   6844   _ble_keymap_vi_single_command=
   6845   _ble_keymap_vi_single_command_overwrite=
   6846   ble-edit/content/nonbol-eolp && ((_ble_edit_ind--))
   6847   ble/widget/vi_xmap/exit
   6848 }
   6849 function ble/widget/vi_xmap/switch-visual-mode.impl {
   6850   local visual_type=$1
   6851   local ARG FLAG REG; ble/keymap:vi/get-arg 0
   6852   if [[ $FLAG ]]; then
   6853     ble/widget/.bell
   6854     return 1
   6855   fi
   6856 
   6857   if [[ ${_ble_edit_mark_active%+} == "$visual_type" ]]; then
   6858     ble/widget/vi_xmap/cancel
   6859   else
   6860     ble/keymap:vi/xmap/switch-type "$visual_type"
   6861     ble/keymap:vi/update-mode-indicator
   6862     return 0
   6863   fi
   6864 
   6865 }
   6866 # xmap v
   6867 function ble/widget/vi_xmap/switch-to-charwise {
   6868   ble/widget/vi_xmap/switch-visual-mode.impl vi_char
   6869 }
   6870 # xmap V
   6871 function ble/widget/vi_xmap/switch-to-linewise {
   6872   ble/widget/vi_xmap/switch-visual-mode.impl vi_line
   6873 }
   6874 # xmap <C-v>
   6875 function ble/widget/vi_xmap/switch-to-blockwise {
   6876   ble/widget/vi_xmap/switch-visual-mode.impl vi_block
   6877 }
   6878 # xmap <C-g>
   6879 function ble/widget/vi_xmap/switch-to-select {
   6880   if [[ $_ble_decode_keymap == vi_xmap ]]; then
   6881     ble/decode/keymap/pop
   6882     ble/decode/keymap/push vi_smap
   6883     ble/keymap:vi/update-mode-indicator
   6884   fi
   6885 }
   6886 # smap <C-g>
   6887 function ble/widget/vi_xmap/switch-to-visual {
   6888   if [[ $_ble_decode_keymap == vi_smap ]]; then
   6889     ble/decode/keymap/pop
   6890     ble/decode/keymap/push vi_xmap
   6891     ble/keymap:vi/update-mode-indicator
   6892   fi
   6893 }
   6894 # smap <C-v>
   6895 function ble/widget/vi_xmap/switch-to-visual-blockwise {
   6896   if [[ $_ble_decode_keymap == vi_smap ]]; then
   6897     ble/decode/keymap/pop
   6898     ble/decode/keymap/push vi_xmap
   6899   fi
   6900   if [[ ${_ble_edit_mark_active%+} != vi_block ]]; then
   6901     ble/widget/vi_xmap/switch-to-blockwise
   6902   else
   6903     xble/keymap:vi/update-mode-indicator
   6904   fi
   6905 }
   6906 
   6907 ## @bleopt keymap_vi_keymodel
   6908 ##   選択モードにおける移動コマンドの振る舞いを制御します。
   6909 bleopt/declare -v keymap_vi_keymodel ''
   6910 function ble/widget/vi_smap/@nomarked {
   6911   [[ ,$bleopt_keymap_vi_keymodel, == *,stopsel,* ]] &&
   6912     ble/widget/vi_xmap/exit
   6913   ble/widget/"$@"
   6914 }
   6915 
   6916 #--------------------------------------
   6917 # xmap/各種コマンド
   6918 
   6919 function ble/widget/vi_smap/self-insert {
   6920   # Note: repeat (nmap .) についてはこの実装で良い。
   6921   #   KEYS=(...) vi_smap/self-insert として記録されるので。
   6922   ble/widget/vi-command/operator c
   6923   ble/widget/self-insert
   6924 }
   6925 
   6926 # xmap o
   6927 function ble/widget/vi_xmap/exchange-points {
   6928   ble/keymap:vi/xmap/remove-eol-extension
   6929   ble/widget/exchange-point-and-mark
   6930   return 0
   6931 }
   6932 # xmap O
   6933 function ble/widget/vi_xmap/exchange-boundaries {
   6934   if [[ ${_ble_edit_mark_active%+} == vi_block ]]; then
   6935     ble/keymap:vi/xmap/remove-eol-extension
   6936 
   6937     local sub_ranges sub_x1 sub_x2
   6938     ble/keymap:vi/extract-block '' '' skip_middle
   6939     local nline=${#sub_ranges[@]}
   6940     ble/util/assert '((nline))'
   6941 
   6942     local data1; ble/string#split data1 : "${sub_ranges[0]}"
   6943     local lpos1=${data1[0]} rpos1=$((data1[4]?data1[1]:data1[1]-1))
   6944     if ((nline==1)); then
   6945       local lpos2=$lpos1 rpos2=$rpos1
   6946     else
   6947       local data2; ble/string#split data2 : "${sub_ranges[nline-1]}"
   6948       local lpos2=${data2[0]} rpos2=$((data2[4]?data2[1]:data2[1]-1))
   6949     fi
   6950 
   6951     # lpos2:rpos2 が _ble_edit_ind に対応していないとき swap する
   6952     if ! ((lpos2<=_ble_edit_ind&&_ble_edit_ind<=rpos2)); then
   6953       local lpos1=$lpos2 lpos2=$lpos1
   6954       local rpos1=$rpos2 rpos2=$rpos1
   6955     fi
   6956 
   6957     _ble_edit_mark=$((_ble_edit_mark==lpos1?rpos1:lpos1))
   6958     _ble_edit_ind=$((_ble_edit_ind==lpos2?rpos2:lpos2))
   6959     return 0
   6960   else
   6961     ble/widget/vi_xmap/exchange-points
   6962   fi
   6963 }
   6964 
   6965 # xmap r{char}
   6966 function ble/widget/vi_xmap/visual-replace-char.hook {
   6967   local key=$1
   6968   _ble_edit_overwrite_mode=
   6969   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   6970 
   6971   local ret
   6972   if [[ $FLAG ]]; then
   6973     ble/widget/.bell
   6974     return 1
   6975   elif ((key==(_ble_decode_Ctrl|91))); then # C-[ -> cancel
   6976     return 27
   6977   elif ! ble/keymap:vi/k2c "$key"; then
   6978     ble/widget/.bell
   6979     return 1
   6980   fi
   6981   local c=$ret
   6982   ble/util/c2s "$c"; local s=$ret
   6983 
   6984   local old_mark_active=$_ble_edit_mark_active # save
   6985   local mark_type=${_ble_edit_mark_active%+}
   6986   ble/widget/vi_xmap/.save-visual-state
   6987   ble/widget/vi_xmap/exit # Note: _ble_edit_mark_active will be cleared here
   6988   if [[ $mark_type == vi_block ]]; then
   6989     ble/util/c2w "$c"; local w=$ret
   6990     ((w<=0)) && w=1
   6991 
   6992     local sub_ranges sub_x1 sub_x2
   6993     _ble_edit_mark_active=$old_mark_active ble/keymap:vi/extract-block
   6994     local n=${#sub_ranges[@]}
   6995     if ((n==0)); then
   6996       ble/widget/.bell
   6997       return 1
   6998     fi
   6999 
   7000     # create ins
   7001     local width=$((sub_x2-sub_x1))
   7002     local count=$((width/w))
   7003     ble/string#repeat "$s" "$count"; local ins=$ret
   7004     local pad=$((width-count*w))
   7005     if ((pad)); then
   7006       ble/string#repeat ' ' "$pad"; ins=$ins$ret
   7007     fi
   7008 
   7009     local i=$n sub smin=0
   7010     ble/keymap:vi/mark/start-edit-area
   7011     while ((i--)); do
   7012       ble/string#split sub : "${sub_ranges[i]}"
   7013       local smin=${sub[0]} smax=${sub[1]}
   7014       local slpad=${sub[2]} srpad=${sub[3]} sfill=${sub[4]}
   7015 
   7016       local ins1=$ins
   7017       ((sfill)) && ins1=${ins1::(width-sfill)/w}
   7018       ((slpad)) && { ble/string#repeat ' ' "$slpad"; ins1=$ret$ins1; }
   7019       ((srpad)) && { ble/string#repeat ' ' "$srpad"; ins1=$ins1$ret; }
   7020       ble/widget/.replace-range "$smin" "$smax" "$ins1"
   7021     done
   7022     local beg=$smin
   7023     ble/keymap:vi/needs-eol-fix "$beg" && ((beg--))
   7024     _ble_edit_ind=$beg
   7025     ble/keymap:vi/mark/end-edit-area
   7026     ble/keymap:vi/repeat/record
   7027   else
   7028     local beg=$_ble_edit_mark end=$_ble_edit_ind
   7029     ((beg<=end)) || local beg=$end end=$beg
   7030     if [[ $mark_type == vi_line ]]; then
   7031       ble-edit/content/find-logical-bol "$beg"; local beg=$ret
   7032       ble-edit/content/find-logical-eol "$end"; local end=$ret
   7033     else
   7034       ble-edit/content/eolp "$end" || ((end++))
   7035     fi
   7036 
   7037     local ins=${_ble_edit_str:beg:end-beg}
   7038     ins=${ins//[!$'\n']/"$s"}
   7039     ble/widget/.replace-range "$beg" "$end" "$ins"
   7040     ble/keymap:vi/needs-eol-fix "$beg" && ((beg--))
   7041     _ble_edit_ind=$beg
   7042     ble/keymap:vi/mark/set-previous-edit-area "$beg" "$end"
   7043     ble/keymap:vi/repeat/record
   7044   fi
   7045   return 0
   7046 }
   7047 function ble/widget/vi_xmap/visual-replace-char {
   7048   _ble_edit_overwrite_mode=R
   7049   ble/keymap:vi/async-read-char ble/widget/vi_xmap/visual-replace-char.hook
   7050 }
   7051 
   7052 function ble/widget/vi_xmap/linewise-operator.impl {
   7053   local op=$1 opts=$2
   7054   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   7055   if [[ $FLAG ]]; then
   7056     ble/widget/.bell 'wrong keymap: xmap ではオペレータは設定されないはず'
   7057     return 1
   7058   fi
   7059 
   7060   local mark_type=${_ble_edit_mark_active%+}
   7061   local beg=$_ble_edit_mark end=$_ble_edit_ind
   7062   ((beg<=end)) || local beg=$end end=$beg
   7063 
   7064   local call_operator=
   7065   if [[ :$opts: != *:force_line:* && $mark_type == vi_block ]]; then
   7066     call_operator=ble/keymap:vi/call-operator-blockwise
   7067     _ble_edit_mark_active=vi_block
   7068     [[ :$opts: == *:extend:* ]] && _ble_edit_mark_active=vi_block+
   7069   else
   7070     call_operator=ble/keymap:vi/call-operator-linewise
   7071     _ble_edit_mark_active=vi_line
   7072   fi
   7073 
   7074   local ble_keymap_vi_mark_active=$_ble_edit_mark_active
   7075   ble/widget/vi_xmap/.save-visual-state
   7076   ble/widget/vi_xmap/exit
   7077   "$call_operator" "$op" "$beg" "$end" "$ARG" "$REG"; local ext=$?
   7078   ((ext==147)) && return 147
   7079   ((ext)) && ble/widget/.bell
   7080   ble/keymap:vi/adjust-command-mode
   7081   return "$ext"
   7082 }
   7083 
   7084 # xmap C
   7085 function ble/widget/vi_xmap/replace-block-lines { ble/widget/vi_xmap/linewise-operator.impl c extend; }
   7086 # xmap D X
   7087 function ble/widget/vi_xmap/delete-block-lines { ble/widget/vi_xmap/linewise-operator.impl d extend; }
   7088 # xmap R S
   7089 function ble/widget/vi_xmap/delete-lines { ble/widget/vi_xmap/linewise-operator.impl d force_line; }
   7090 # xmap Y
   7091 function ble/widget/vi_xmap/copy-block-or-lines { ble/widget/vi_xmap/linewise-operator.impl y; }
   7092 
   7093 function ble/widget/vi_xmap/connect-line.impl {
   7094   local name=$1
   7095   local ARG FLAG REG; ble/keymap:vi/get-arg 1 # ignored
   7096 
   7097   local beg=$_ble_edit_mark end=$_ble_edit_ind
   7098   ((beg<=end)) || local beg=$end end=$beg
   7099   local ret; ble/string#count-char "${_ble_edit_str:beg:end-beg}" $'\n'; local nline=$((ret+1))
   7100 
   7101   ble/widget/vi_xmap/.save-visual-state
   7102   ble/widget/vi_xmap/exit # Note: _ble_edit_mark_active will be cleared here
   7103 
   7104   _ble_edit_ind=$beg
   7105   _ble_edit_arg=$nline
   7106   _ble_keymap_vi_oparg=
   7107   _ble_keymap_vi_opfunc=
   7108   _ble_keymap_vi_reg=
   7109   "ble/widget/$name"
   7110 }
   7111 # xmap J
   7112 function ble/widget/vi_xmap/connect-line-with-space {
   7113   ble/widget/vi_xmap/connect-line.impl vi_nmap/connect-line-with-space
   7114 }
   7115 # xmap gJ
   7116 function ble/widget/vi_xmap/connect-line {
   7117   ble/widget/vi_xmap/connect-line.impl vi_nmap/connect-line
   7118 }
   7119 
   7120 #--------------------------------------
   7121 # xmap/矩形挿入モード
   7122 
   7123 ## @var _ble_keymap_vi_xmap_insert_data
   7124 ##   矩形挿入モードの情報を保持します。
   7125 ##   iline:x1:width:content の形式です。
   7126 ##
   7127 ##   iline
   7128 ##     編集を行う行の番号を保持します。
   7129 ##   x1
   7130 ##     挿入開始位置を表示列で保持します。
   7131 ##   width
   7132 ##     編集行の元々の幅を保持します。
   7133 ##   nline
   7134 ##     行数を保持します。
   7135 ##
   7136 _ble_keymap_vi_xmap_insert_data=
   7137 _ble_keymap_vi_xmap_insert_dbeg=-1
   7138 ble/array#push _ble_textarea_local_VARNAMES \
   7139                _ble_keymap_vi_xmap_insert_data \
   7140                _ble_keymap_vi_xmap_insert_dbeg
   7141 function ble/keymap:vi/xmap/update-dirty-range {
   7142   [[ $_ble_keymap_vi_insert_leave == ble/widget/vi_xmap/block-insert-mode.onleave ]] &&
   7143     ((_ble_keymap_vi_xmap_insert_dbeg<0||beg<_ble_keymap_vi_xmap_insert_dbeg)) &&
   7144     _ble_keymap_vi_xmap_insert_dbeg=$beg
   7145 }
   7146 
   7147 ## @fn ble/widget/vi_xmap/block-insert-mode.impl
   7148 ##   @var[in] sub_ranges sub_x1 sub_x2
   7149 function ble/widget/vi_xmap/block-insert-mode.impl {
   7150   local type=$1
   7151   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   7152 
   7153   local nline=${#sub_ranges[@]}
   7154   ble/util/assert '((nline))'
   7155 
   7156   local index ins_x
   7157   if [[ $type == append ]]; then
   7158     local sub=${sub_ranges[0]#*:}
   7159     local smax=${sub%%:*}
   7160     index=$smax
   7161     if ble/keymap:vi/xmap/has-eol-extension; then
   7162       ins_x='$'
   7163     else
   7164       ins_x=$sub_x2
   7165     fi
   7166   else
   7167     local sub=${sub_ranges[0]}
   7168     local smin=${sub%%:*}
   7169     index=$smin
   7170     ins_x=$sub_x1
   7171   fi
   7172 
   7173   ble/widget/vi_xmap/cancel
   7174   _ble_edit_ind=$index
   7175   ble/widget/vi_nmap/.insert-mode "$ARG"
   7176   ble/keymap:vi/repeat/record
   7177   ble/keymap:vi/mark/set-local-mark 1 "$_ble_edit_ind"
   7178   _ble_keymap_vi_xmap_insert_dbeg=-1
   7179 
   7180   local ret display_width
   7181   ble/string#count-char "${_ble_edit_str::_ble_edit_ind}" $'\n'; local iline=$ret
   7182   ble-edit/content/find-logical-bol; local bol=$ret
   7183   ble-edit/content/find-logical-eol; local eol=$ret
   7184   if ble/edit/use-textmap; then
   7185     local bx by ex ey
   7186     ble/textmap#getxy.out --prefix=b "$bol"
   7187     ble/textmap#getxy.out --prefix=e "$eol"
   7188     ((display_width=ex+_ble_textmap_cols*(ey-by)))
   7189   else
   7190     ((display_width=eol-bol))
   7191   fi
   7192   _ble_keymap_vi_xmap_insert_data=$iline:$ins_x:$display_width:$nline
   7193   _ble_keymap_vi_insert_leave=ble/widget/vi_xmap/block-insert-mode.onleave
   7194   return 0
   7195 }
   7196 function ble/widget/vi_xmap/block-insert-mode.onleave {
   7197   local data=$_ble_keymap_vi_xmap_insert_data
   7198   [[ $data ]] || continue
   7199   _ble_keymap_vi_xmap_insert_data=
   7200 
   7201   ble/string#split data : "$data"
   7202 
   7203   # カーソル行が記録行と同じか
   7204   local ret
   7205   ble-edit/content/find-logical-bol; local bol=$ret
   7206   ble/string#count-char "${_ble_edit_str::bol}" $'\n'; ((ret==data[0])) || return  1 # 行番号
   7207   ble/keymap:vi/mark/get-local-mark 1 || return 1; local mark=$ret # `[
   7208   ble-edit/content/find-logical-bol "$mark"; ((bol==ret)) || return 1 # 記録行 `[ と同じか
   7209 
   7210   local has_textmap=
   7211   if ble/edit/use-textmap; then
   7212     local cols=$_ble_textmap_cols
   7213     has_textmap=1
   7214   fi
   7215 
   7216   # 表示幅の変量
   7217   local new_width delta
   7218   ble-edit/content/find-logical-eol; local eol=$ret
   7219   if [[ $has_textmap ]]; then
   7220     local bx by ex ey
   7221     ble/textmap#getxy.out --prefix=b "$bol"
   7222     ble/textmap#getxy.out --prefix=e "$eol"
   7223     ((new_width=ex+cols*(ey-by)))
   7224   else
   7225     ((new_width=eol-bol))
   7226   fi
   7227   ((delta=new_width-data[2]))
   7228   ((delta>0)) || return 1 # 縮んだ場合は処理しない
   7229 
   7230   # 切り出し列の決定
   7231   local x1=${data[1]}
   7232   [[ $x1 == '$' ]] && ((x1=data[2]))
   7233   ((x1>new_width&&(x1=new_width)))
   7234   if ((bol<=_ble_keymap_vi_xmap_insert_dbeg&&_ble_keymap_vi_xmap_insert_dbeg<=eol)); then
   7235     local px py
   7236     if [[ $has_textmap ]]; then
   7237       ble/textmap#getxy.out --prefix=p "$_ble_keymap_vi_xmap_insert_dbeg"
   7238       ((px+=cols*(py-by)))
   7239     else
   7240       ((px=_ble_keymap_vi_xmap_insert_dbeg-bol))
   7241     fi
   7242     ((px>x1&&(x1=px)))
   7243   fi
   7244   local x2=$((x1+delta))
   7245 
   7246   # 切り出し
   7247   local ins= p1 p2
   7248   if [[ $has_textmap ]]; then
   7249     local index lx ly rx ry
   7250     ble/textmap#hit out "$((x1%cols))" "$((by+x1/cols))" "$bol" "$eol"; p1=$index
   7251     ble/textmap#hit out "$((x2%cols))" "$((by+x2/cols))" "$bol" "$eol"; p2=$index
   7252     ((lx+=(ly-by)*cols,rx+=(ry-by)*cols,lx!=rx&&p2++))
   7253   else
   7254     ((p1=bol+x1,p2=bol+x2))
   7255   fi
   7256   ins=${_ble_edit_str:p1:p2-p1}
   7257 
   7258   # 挿入の決定
   7259   local -a ins_beg=() ins_text=()
   7260   local iline=1 nline=${data[3]} strlen=${#_ble_edit_str}
   7261   for ((iline=1;iline<nline;iline++)); do
   7262     local index= lpad=
   7263     if ((eol<strlen)); then
   7264       bol=$((eol+1))
   7265       ble-edit/content/find-logical-eol "$bol"; eol=$ret
   7266     else
   7267       bol=$eol lpad=$'\n'
   7268     fi
   7269 
   7270     if [[ ${data[1]} == '$' ]]; then
   7271       index=$eol
   7272     elif [[ $has_textmap ]]; then
   7273       ble/textmap#getxy.out --prefix=b "$bol"
   7274       ble/textmap#hit out "$((x1%cols))" "$((by+x1/cols))" "$bol" "$eol" # -> index
   7275 
   7276       local nfill
   7277       if ((index==eol&&(nfill=x1-lx+(ly-by)*cols)>0)); then
   7278         ble/string#repeat ' ' "$nfill"; lpad=$lpad$ret
   7279       fi
   7280     else
   7281       index=$((bol+x1))
   7282       if ((index>eol)); then
   7283         ble/string#repeat ' ' "$((index-eol))"; lpad=$lpad$ret
   7284         ((index=eol))
   7285       fi
   7286     fi
   7287 
   7288     ble/array#push ins_beg "$index"
   7289     ble/array#push ins_text "$lpad$ins"
   7290   done
   7291 
   7292   # 挿入実行
   7293   local i=${#ins_beg[@]}
   7294   ble/keymap:vi/mark/start-edit-area
   7295   ble/keymap:vi/mark/commit-edit-area "$p1" "$p2"
   7296   while ((i--)); do
   7297     local index=${ins_beg[i]} text=${ins_text[i]}
   7298     ble/widget/.replace-range "$index" "$index" "$text"
   7299   done
   7300   ble/keymap:vi/mark/end-edit-area
   7301   # Note: この編集は record-insert 経由で記録されるので
   7302   # ここで明示的に ble/keymap:vi/repeat/record を呼び出す必要はない。
   7303 
   7304   # 領域の最初に
   7305   local index
   7306   if ble/keymap:vi/mark/get-local-mark 60 && index=$ret; then
   7307     ble/widget/vi-command/goto-mark.impl "$index"
   7308   else
   7309     ble-edit/content/find-logical-bol; index=$ret
   7310   fi
   7311 
   7312   # ノーマルモードに戻る時に一文字カーソルが戻るので一文字進めておく。
   7313   ble-edit/content/eolp || ((index++))
   7314   _ble_edit_ind=$index
   7315   return 0
   7316 }
   7317 # xmap I
   7318 function ble/widget/vi_xmap/insert-mode {
   7319   local mark_type=${_ble_edit_mark_active%+}
   7320   if [[ $mark_type == vi_block ]]; then
   7321     local sub_ranges sub_x1 sub_x2
   7322     ble/keymap:vi/extract-block '' '' first_line
   7323     ble/widget/vi_xmap/block-insert-mode.impl insert
   7324   else
   7325     local ARG FLAG REG; ble/keymap:vi/get-arg 1
   7326 
   7327     local beg=$_ble_edit_mark end=$_ble_edit_ind
   7328     ((beg<=end)) || local beg=$end end=$beg
   7329     if [[ $mark_type == vi_line ]]; then
   7330       local ret
   7331       ble-edit/content/find-logical-bol "$beg"; beg=$ret
   7332     fi
   7333 
   7334     ble/widget/vi_xmap/cancel
   7335     _ble_edit_ind=$beg
   7336     ble/widget/vi_nmap/.insert-mode "$ARG"
   7337     ble/keymap:vi/repeat/record
   7338     return 0
   7339   fi
   7340 }
   7341 # xmap A
   7342 function ble/widget/vi_xmap/append-mode {
   7343   local mark_type=${_ble_edit_mark_active%+}
   7344   if [[ $mark_type == vi_block ]]; then
   7345     local sub_ranges sub_x1 sub_x2
   7346     ble/keymap:vi/extract-block '' '' first_line
   7347     ble/widget/vi_xmap/block-insert-mode.impl append
   7348   else
   7349     local ARG FLAG REG; ble/keymap:vi/get-arg 1
   7350 
   7351     local beg=$_ble_edit_mark end=$_ble_edit_ind
   7352     ((beg<=end)) || local beg=$end end=$beg
   7353     if [[ $mark_type == vi_line ]]; then
   7354       # 行指向のときは最終行の先頭か _ble_edit_ind の内、
   7355       # 後にある文字の後に移動する。
   7356       if ((_ble_edit_mark>_ble_edit_ind)); then
   7357         local ret
   7358         ble-edit/content/find-logical-bol "$end"; end=$ret
   7359       fi
   7360     fi
   7361     ble-edit/content/eolp "$end" || ((end++))
   7362 
   7363     ble/widget/vi_xmap/cancel
   7364     _ble_edit_ind=$end
   7365     ble/widget/vi_nmap/.insert-mode "$ARG"
   7366     ble/keymap:vi/repeat/record
   7367     return 0
   7368   fi
   7369 }
   7370 
   7371 #--------------------------------------
   7372 # xmap/貼り付け
   7373 
   7374 # xmap: p, P
   7375 function ble/widget/vi_xmap/paste.impl {
   7376   local opts=$1
   7377   [[ :$opts: != *:after:* ]]; local is_after=$?
   7378 
   7379   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   7380   [[ $REG ]] && ble/keymap:vi/register#load "$REG"
   7381 
   7382   local mark_type=${_ble_edit_mark_active%+}
   7383   local kill_ring=$_ble_edit_kill_ring
   7384   local kill_type=$_ble_edit_kill_type
   7385 
   7386   local adjustment=
   7387   if [[ $mark_type == vi_block ]]; then
   7388     if [[ $kill_type == L ]]; then
   7389       # P: V → C-v のときは C-v の最終行直後に挿入
   7390       if ((is_after)); then
   7391         local ret; ble/keymap:vi/get-rectangle-height; local nline=$ret
   7392         adjustment=lastline:$nline
   7393       fi
   7394     elif [[ $kill_type == B:* ]]; then
   7395       # C-v → C-v
   7396       is_after=0
   7397     else
   7398       # 単純 v → C-v はブロック挿入に切り替え
   7399       is_after=0
   7400       if [[ $kill_ring != *$'\n'* ]]; then
   7401         ((${#kill_ring}>=2)) && adjustment=index:$((${#kill_ring}*ARG-1))
   7402         local ret; ble/keymap:vi/get-rectangle-height; local nline=$ret
   7403         ble/string#repeat "$kill_ring"$'\n' "$nline"; kill_ring=${ret%$'\n'}
   7404         ble/string#repeat '0 ' "$nline"; kill_type=B:${ret% }
   7405       fi
   7406     fi
   7407   elif [[ $mark_type == vi_line ]]; then
   7408     if [[ $kill_type == L ]]; then
   7409       # V → V のとき
   7410       is_after=0
   7411     elif [[ $kill_type == B:* ]]; then
   7412       # C-v → V のとき、行貼り付け。
   7413       # kill_type=B:* のとき kill_ring の末端の改行は空行を意味するので、
   7414       # 空行が消えないように $'\n' を付加する必要がある。
   7415       is_after=0 kill_type=L kill_ring=$kill_ring$'\n'
   7416     else
   7417       # v → V のとき、行貼り付けになる。
   7418       is_after=0 kill_type=L
   7419       [[ $kill_ring == *$'\n' ]] && kill_ring=$kill_ring$'\n'
   7420     fi
   7421   else
   7422     # v, V, C-v → v のとき
   7423     is_after=0
   7424     [[ $kill_type == L ]] && adjustment=newline
   7425   fi
   7426 
   7427   ble/keymap:vi/mark/start-edit-area
   7428   local _ble_keymap_vi_mark_suppress_edit=1
   7429   {
   7430     ble/widget/vi-command/operator d; local ext=$? # _ble_edit_kill_{ring,type} is set here
   7431     if [[ $adjustment == newline ]]; then
   7432       local -a KEYS=(10)
   7433       ble/widget/self-insert
   7434     elif [[ $adjustment == lastline:* ]]; then
   7435       local ret
   7436       ble-edit/content/find-logical-bol "$_ble_edit_ind" "$((${adjustment#*:}-1))"
   7437       _ble_edit_ind=$ret
   7438     fi
   7439     local _ble_edit_kill_ring=$kill_ring
   7440     local _ble_edit_kill_type=$kill_type
   7441     ble/widget/vi_nmap/paste.impl "$ARG" '' "$is_after"
   7442     if [[ $adjustment == index:* ]]; then
   7443       local index=$((_ble_edit_ind+${adjustment#*:}))
   7444       ((index>${#_ble_edit_str}&&(index=${#_ble_edit_str})))
   7445       ble/keymap:vi/needs-eol-fix "$index" && ((index--))
   7446       _ble_edit_ind=$index
   7447     fi
   7448   }
   7449   ble/util/unlocal _ble_keymap_vi_mark_suppress_edit
   7450   ble/keymap:vi/mark/end-edit-area
   7451   ble/keymap:vi/repeat/record
   7452   return "$ext"
   7453 }
   7454 function ble/widget/vi_xmap/paste-after {
   7455   ble/widget/vi_xmap/paste.impl after
   7456 }
   7457 function ble/widget/vi_xmap/paste-before {
   7458   ble/widget/vi_xmap/paste.impl before
   7459 }
   7460 
   7461 #--------------------------------------
   7462 # xmap <C-a>, <C-x>, g<C-a>, g<C-x>
   7463 
   7464 ## @fn ble/widget/vi_xmap/increment.impl opts
   7465 ##
   7466 ##   @param[in] opts
   7467 ##     以下の項目をコロンで区切って指定したものです。
   7468 ##
   7469 ##     - increase [既定]
   7470 ##       数字を増加させます。
   7471 ##     - decrease
   7472 ##       数字を減少させるます。
   7473 ##     - progressive
   7474 ##       k 個目の数字について増加・減少量を k 倍します。
   7475 ##
   7476 function ble/widget/vi_xmap/increment.impl {
   7477   local opts=$1
   7478   local ARG FLAG REG; ble/keymap:vi/get-arg 1
   7479   if [[ $FLAG ]]; then
   7480     ble/widget/.bell
   7481     return 1
   7482   fi
   7483 
   7484   local delta=$ARG
   7485   [[ :$opts: == *:decrease:* ]] && ((delta=-delta))
   7486   local progress=0
   7487   [[ :$opts: == *:progressive:* ]] && progress=$delta
   7488 
   7489   local old_mark_active=$_ble_edit_mark_active # save
   7490   local mark_type=${_ble_edit_mark_active%+}
   7491   ble/widget/vi_xmap/.save-visual-state
   7492   ble/widget/vi_xmap/exit # Note: _ble_edit_mark_active will be cleared here
   7493   if [[ $mark_type == vi_block ]]; then
   7494     local sub_ranges sub_x1 sub_x2
   7495     _ble_edit_mark_active=$old_mark_active ble/keymap:vi/extract-block
   7496     if ((${#sub_ranges[@]}==0)); then
   7497       ble/widget/.bell
   7498       return 1
   7499     fi
   7500   else
   7501     local beg=$_ble_edit_mark end=$_ble_edit_ind
   7502     ((beg<=end)) || local beg=$end end=$beg
   7503     if [[ $mark_type == vi_line ]]; then
   7504       local ret
   7505       ble-edit/content/find-logical-bol "$beg"; local beg=$ret
   7506       ble-edit/content/find-logical-eol "$end"; local end=$ret
   7507     else
   7508       ble-edit/content/eolp "$end" || ((end++))
   7509     fi
   7510 
   7511     local -a lines
   7512     ble/string#split-lines lines "${_ble_edit_str:beg:end-beg}"
   7513 
   7514     # sub_ranges 生成
   7515     local line index=$beg
   7516     local -a sub_ranges
   7517     for line in "${lines[@]}"; do
   7518       [[ $line ]] && ble/array#push sub_ranges "$index:::::$line"
   7519       ((index+=${#line}+1))
   7520     done
   7521 
   7522     ((${#sub_ranges[@]})) || return 0
   7523   fi
   7524 
   7525   local sub rex_number='^([^0-9]*)([0-9]+)' shift=0 dmin=-1 dmax=-1
   7526   for sub in "${sub_ranges[@]}"; do
   7527     local stext=${sub#*:*:*:*:*:}
   7528     [[ $stext =~ $rex_number ]] || continue
   7529 
   7530     # 元々の数
   7531     local rematch1=${BASH_REMATCH[1]}
   7532     local rematch2=${BASH_REMATCH[2]}
   7533     local offset=${#rematch1} length=${#rematch2}
   7534     local number=$((10#0$rematch2))
   7535     [[ $rematch1 == *- ]] && ((number=-number,offset--,length++))
   7536 
   7537     # 新しい数
   7538     ((number+=delta,delta+=progress))
   7539     if [[ $rematch2 == 0?* ]]; then
   7540       # Zero padding
   7541       local wsign=$((number<0?1:0))
   7542       local zpad=$((wsign+${#rematch2}-${#number}))
   7543       if ((zpad>0)); then
   7544         local ret; ble/string#repeat 0 "$zpad"
   7545         number=${number::wsign}$ret${number:wsign}
   7546       fi
   7547     fi
   7548 
   7549     local smin=${sub%%:*}
   7550     local beg=$((shift+smin+offset))
   7551     local end=$((beg+length))
   7552     ble/widget/.replace-range "$beg" "$end" "$number"
   7553     ((shift+=${#number}-length,
   7554       dmin<0&&(dmin=beg),
   7555       dmax=beg+${#number}))
   7556   done
   7557   local beg=${sub_ranges[0]%%:*}
   7558   ble/keymap:vi/needs-eol-fix "$beg" && ((beg--))
   7559   _ble_edit_ind=$beg
   7560 
   7561   ((dmin>=0)) && ble/keymap:vi/mark/set-previous-edit-area "$dmin" "$dmax"
   7562   ble/keymap:vi/repeat/record
   7563   return 0
   7564 }
   7565 # xmap <C-a>
   7566 function ble/widget/vi_xmap/increment { ble/widget/vi_xmap/increment.impl increase; }
   7567 # xmap <C-x>
   7568 function ble/widget/vi_xmap/decrement { ble/widget/vi_xmap/increment.impl decrease; }
   7569 # xmap g<C-a>
   7570 function ble/widget/vi_xmap/progressive-increment { ble/widget/vi_xmap/increment.impl progressive:increase; }
   7571 # xmap g<C-x>
   7572 function ble/widget/vi_xmap/progressive-decrement { ble/widget/vi_xmap/increment.impl progressive:decrease; }
   7573 
   7574 #--------------------------------------
   7575 
   7576 function ble-decode/keymap:vi_xmap/define {
   7577   ble/keymap:vi/set-up-command-map
   7578 
   7579   ble-bind -f __default__ vi-command/decompose-meta
   7580 
   7581   ble-bind -f 'ESC' vi_xmap/exit
   7582   ble-bind -f 'C-[' vi_xmap/exit
   7583   ble-bind -f 'C-c' vi_xmap/cancel
   7584 
   7585   ble-bind -f '"' vi-command/register
   7586 
   7587   ble-bind -f a vi-command/text-object
   7588   ble-bind -f i vi-command/text-object
   7589 
   7590   ble-bind -f 'C-\ C-n' vi_xmap/cancel
   7591   ble-bind -f 'C-\ C-g' vi_xmap/cancel
   7592   ble-bind -f v      vi_xmap/switch-to-charwise
   7593   ble-bind -f V      vi_xmap/switch-to-linewise
   7594   ble-bind -f C-v    vi_xmap/switch-to-blockwise
   7595   ble-bind -f C-q    vi_xmap/switch-to-blockwise
   7596   ble-bind -f 'g v'  vi-command/previous-visual-area
   7597   ble-bind -f C-g    vi_xmap/switch-to-select
   7598 
   7599   ble-bind -f o vi_xmap/exchange-points
   7600   ble-bind -f O vi_xmap/exchange-boundaries
   7601 
   7602   ble-bind -f '~' 'vi-command/operator toggle_case'
   7603   ble-bind -f 'u' 'vi-command/operator u'
   7604   ble-bind -f 'U' 'vi-command/operator U'
   7605 
   7606   ble-bind -f 's' 'vi-command/operator c'
   7607   ble-bind -f 'x'    'vi-command/operator d'
   7608   ble-bind -f delete 'vi-command/operator d'
   7609 
   7610   ble-bind -f r vi_xmap/visual-replace-char
   7611 
   7612   ble-bind -f C vi_xmap/replace-block-lines
   7613   ble-bind -f D vi_xmap/delete-block-lines
   7614   ble-bind -f X vi_xmap/delete-block-lines
   7615   ble-bind -f S vi_xmap/delete-lines
   7616   ble-bind -f R vi_xmap/delete-lines
   7617   ble-bind -f Y vi_xmap/copy-block-or-lines
   7618   ble-bind -f J     vi_xmap/connect-line-with-space
   7619   ble-bind -f 'g J' vi_xmap/connect-line
   7620 
   7621   ble-bind -f I vi_xmap/insert-mode
   7622   ble-bind -f A vi_xmap/append-mode
   7623   ble-bind -f p vi_xmap/paste-after
   7624   ble-bind -f P vi_xmap/paste-before
   7625 
   7626   ble-bind -f 'C-a'   vi_xmap/increment
   7627   ble-bind -f 'C-x'   vi_xmap/decrement
   7628   ble-bind -f 'g C-a' vi_xmap/progressive-increment
   7629   ble-bind -f 'g C-x' vi_xmap/progressive-decrement
   7630 
   7631   ble-bind -f f1 vi_xmap/command-help
   7632   ble-bind -f K  vi_xmap/command-help
   7633 }
   7634 
   7635 function ble-decode/keymap:vi_smap/define {
   7636   ble-bind -f __default__ vi-command/decompose-meta
   7637 
   7638   ble-bind -f 'ESC' vi_xmap/exit
   7639   ble-bind -f 'C-[' vi_xmap/exit
   7640   ble-bind -f 'C-c' vi_xmap/cancel
   7641 
   7642   ble-bind -f 'C-\ C-n' nop
   7643   ble-bind -f 'C-\ C-n' vi_xmap/cancel
   7644   ble-bind -f 'C-\ C-g' vi_xmap/cancel
   7645   ble-bind -f C-v    vi_xmap/switch-to-visual-blockwise
   7646   ble-bind -f C-q    vi_xmap/switch-to-visual-blockwise
   7647   ble-bind -f C-g    vi_xmap/switch-to-visual
   7648 
   7649   ble-bind -f delete 'vi-command/operator d'
   7650   ble-bind -f 'C-?'  'vi-command/operator d'
   7651   ble-bind -f 'DEL'  'vi-command/operator d'
   7652   ble-bind -f 'C-h'  'vi-command/operator d'
   7653   ble-bind -f 'BS'   'vi-command/operator d'
   7654 
   7655   #----------------------------------------------------------------------------
   7656 
   7657   ble-bind -f __defchar__ vi_smap/self-insert
   7658   ble-bind -f paste_begin vi-command/bracketed-paste
   7659 
   7660   ble-bind -f 'C-a'  vi_xmap/increment
   7661   ble-bind -f 'C-x'  vi_xmap/decrement
   7662   ble-bind -f f1     vi_xmap/command-help
   7663   ble-bind -c 'C-z' fg
   7664 
   7665   #----------------------------------------------------------------------------
   7666   # motion, etc.
   7667 
   7668   ble-bind -f home      'vi_smap/@nomarked vi-command/beginning-of-line'
   7669   ble-bind -f end       'vi_smap/@nomarked vi-command/forward-eol'
   7670   ble-bind -f C-m       'vi_smap/@nomarked vi-command/forward-first-non-space'
   7671   ble-bind -f RET       'vi_smap/@nomarked vi-command/forward-first-non-space'
   7672   ble-bind -f S-home    'vi-command/beginning-of-line'
   7673   ble-bind -f S-end     'vi-command/forward-eol'
   7674   ble-bind -f S-C-m     'vi-command/forward-first-non-space'
   7675   ble-bind -f S-RET     'vi-command/forward-first-non-space'
   7676 
   7677   ble-bind -f C-right   'vi_smap/@nomarked vi-command/forward-vword'
   7678   ble-bind -f C-left    'vi_smap/@nomarked vi-command/backward-vword'
   7679   ble-bind -f S-C-right 'vi-command/forward-vword'
   7680   ble-bind -f S-C-left  'vi-command/backward-vword'
   7681 
   7682   ble-bind -f left      'vi_smap/@nomarked vi-command/backward-char'
   7683   ble-bind -f right     'vi_smap/@nomarked vi-command/forward-char'
   7684   ble-bind -f 'C-?'     'vi_smap/@nomarked vi-command/backward-char wrap'
   7685   ble-bind -f 'DEL'     'vi_smap/@nomarked vi-command/backward-char wrap'
   7686   ble-bind -f 'C-h'     'vi_smap/@nomarked vi-command/backward-char wrap'
   7687   ble-bind -f 'BS'      'vi_smap/@nomarked vi-command/backward-char wrap'
   7688   ble-bind -f SP        'vi_smap/@nomarked vi-command/forward-char wrap'
   7689   ble-bind -f S-left    'vi-command/backward-char'
   7690   ble-bind -f S-right   'vi-command/forward-char'
   7691   ble-bind -f 'S-C-?'   'vi-command/backward-char wrap'
   7692   ble-bind -f 'S-DEL'   'vi-command/backward-char wrap'
   7693   ble-bind -f 'S-C-h'   'vi-command/backward-char wrap'
   7694   ble-bind -f 'S-BS'    'vi-command/backward-char wrap'
   7695   ble-bind -f S-SP      'vi-command/forward-char wrap'
   7696 
   7697   ble-bind -f down      'vi_smap/@nomarked vi-command/forward-line'
   7698   ble-bind -f C-n       'vi_smap/@nomarked vi-command/forward-line'
   7699   ble-bind -f C-j       'vi_smap/@nomarked vi-command/forward-line'
   7700   ble-bind -f up        'vi_smap/@nomarked vi-command/backward-line'
   7701   ble-bind -f C-p       'vi_smap/@nomarked vi-command/backward-line'
   7702   ble-bind -f C-home    'vi_smap/@nomarked vi-command/first-nol'
   7703   ble-bind -f C-end     'vi_smap/@nomarked vi-command/last-eol'
   7704   ble-bind -f S-down    'vi-command/forward-line'
   7705   ble-bind -f S-C-n     'vi-command/forward-line'
   7706   ble-bind -f S-C-j     'vi-command/forward-line'
   7707   ble-bind -f S-up      'vi-command/backward-line'
   7708   ble-bind -f S-C-p     'vi-command/backward-line'
   7709   ble-bind -f S-C-home  'vi-command/first-nol'
   7710   ble-bind -f S-C-end   'vi-command/last-eol'
   7711 }
   7712 
   7713 #------------------------------------------------------------------------------
   7714 # vi_imap
   7715 
   7716 function ble/widget/vi_imap/__attach__ {
   7717   ble/keymap:vi/update-mode-indicator
   7718   return 0
   7719 }
   7720 function ble/widget/vi_imap/__detach__ {
   7721   ble/edit/info/default clear
   7722   ble/keymap:vi/clear-arg
   7723   ble/keymap:vi/search/clear-matched
   7724   return 0
   7725 }
   7726 function ble/widget/vi_imap/accept-single-line-or {
   7727   if ble-edit/is-single-complete-line; then
   7728     ble/keymap:vi/imap-repeat/reset
   7729     ble/widget/accept-line
   7730   else
   7731     ble/widget/"$@"
   7732   fi
   7733 }
   7734 function ble/widget/vi_imap/delete-region-or {
   7735   if [[ $_ble_edit_mark_active ]]; then
   7736     ble/keymap:vi/imap-repeat/reset
   7737     if ((_ble_edit_ind!=_ble_edit_mark)); then
   7738       ble/keymap:vi/undo/add more
   7739       ble/widget/delete-region
   7740       ble/keymap:vi/undo/add more
   7741     fi
   7742   else
   7743     ble/widget/"$@"
   7744   fi
   7745 }
   7746 function ble/widget/vi_imap/overwrite-mode {
   7747   ble-edit/content/clear-arg
   7748   if [[ $_ble_edit_overwrite_mode ]]; then
   7749     _ble_edit_overwrite_mode=
   7750   else
   7751     _ble_edit_overwrite_mode=${_ble_keymap_vi_insert_overwrite:-R}
   7752   fi
   7753   ble/keymap:vi/update-mode-indicator
   7754   return 0
   7755 }
   7756 
   7757 # imap C-w
   7758 function ble/widget/vi_imap/delete-backward-word {
   7759   local space=$' \t' nl=$'\n'
   7760   local rex="($_ble_keymap_vi_REX_WORD)[$space]*\$|[$space]+\$|$nl\$"
   7761   if [[ ${_ble_edit_str::_ble_edit_ind} =~ $rex ]]; then
   7762     local index=$((_ble_edit_ind-${#BASH_REMATCH}))
   7763     if ((index!=_ble_edit_ind)); then
   7764       ble/keymap:vi/undo/add more
   7765       ble/widget/.delete-range "$index" "$_ble_edit_ind"
   7766       ble/keymap:vi/undo/add more
   7767     fi
   7768     return 0
   7769   else
   7770     ble/widget/.bell
   7771     return 1
   7772   fi
   7773 }
   7774 
   7775 # imap C-q, C-v
   7776 function ble/widget/vi_imap/quoted-insert-char {
   7777   ble/keymap:vi/imap-repeat/pop
   7778   _ble_edit_mark_active=
   7779   _ble_decode_char__hook=ble/widget/vi_imap/quoted-insert-char.hook
   7780   return 147
   7781 }
   7782 function ble/widget/vi_imap/quoted-insert-char.hook {
   7783   ble/keymap:vi/imap/invoke-widget ble/widget/self-insert "$1"
   7784 }
   7785 function ble/widget/vi_imap/quoted-insert {
   7786   ble/keymap:vi/imap-repeat/pop
   7787   _ble_edit_mark_active=
   7788   _ble_decode_key__hook=ble/widget/vi_imap/quoted-insert.hook
   7789   return 147
   7790 }
   7791 function ble/widget/vi_imap/quoted-insert.hook {
   7792   ble/keymap:vi/imap/invoke-widget ble/widget/quoted-insert.hook "$1"
   7793 }
   7794 
   7795 # bracketed paste mode
   7796 
   7797 function ble/widget/vi_imap/bracketed-paste {
   7798   ble/keymap:vi/imap-repeat/pop
   7799   ble/widget/bracketed-paste
   7800   _ble_edit_bracketed_paste_proc=ble/widget/vi_imap/bracketed-paste.proc
   7801   return 147
   7802 }
   7803 function ble/widget/vi_imap/bracketed-paste.proc {
   7804   local WIDGET=ble/widget/batch-insert
   7805   local -a KEYS; KEYS=("$@")
   7806   ble/keymap:vi/imap-repeat/push
   7807   builtin eval -- "$WIDGET"
   7808 }
   7809 
   7810 _ble_keymap_vi_brackated_paste_mark_active=
   7811 function ble/widget/vi-command/bracketed-paste {
   7812   local ARG FLAG REG; ble/keymap:vi/get-arg 1 # discard args
   7813   _ble_keymap_vi_brackated_paste_mark_active=$_ble_edit_mark_active
   7814   _ble_edit_mark_active=
   7815   ble/widget/bracketed-paste
   7816   _ble_edit_bracketed_paste_proc=ble/widget/vi-command/bracketed-paste.proc
   7817   return 147
   7818 }
   7819 function ble/widget/vi-command/bracketed-paste.proc {
   7820   if [[ $_ble_decode_keymap == vi_nmap ]]; then
   7821     local isbol index=$_ble_edit_ind
   7822     ble-edit/content/bolp && isbol=1
   7823     ble/decode/widget/call-interactively 'ble/widget/vi_nmap/append-mode' 97
   7824     [[ $isbol ]] && ((_ble_edit_ind=index)) # 行頭にいたときは戻る
   7825 
   7826     ble/widget/vi_imap/bracketed-paste.proc "$@"
   7827     ble/keymap:vi/imap/invoke-widget \
   7828       ble/widget/vi_imap/normal-mode "$((_ble_decode_Ctrl|0x5b))"
   7829   elif [[ $_ble_decode_keymap == vi_[xs]map ]]; then
   7830     local _ble_edit_mark_active=$_ble_keymap_vi_brackated_paste_mark_active
   7831     ble/decode/widget/call-interactively 'ble/widget/vi-command/operator c' 99 || return 1
   7832     ble/widget/vi_imap/bracketed-paste.proc "$@"
   7833     ble/keymap:vi/imap/invoke-widget \
   7834       ble/widget/vi_imap/normal-mode "$((_ble_decode_Ctrl|0x5b))"
   7835   elif [[ $_ble_decode_keymap == vi_omap ]]; then
   7836     ble/widget/vi_omap/cancel
   7837     ble/widget/.bell
   7838     return 1
   7839   else # vi_omap
   7840     ble/widget/.bell
   7841     return 1
   7842   fi
   7843 }
   7844 
   7845 #------------------------------------------------------------------------------
   7846 # imap: C-k (digraph)
   7847 
   7848 function ble/widget/vi_imap/insert-digraph.hook {
   7849   local -a KEYS; KEYS=("$1")
   7850   ble/widget/self-insert
   7851 }
   7852 
   7853 function ble/widget/vi_imap/insert-digraph {
   7854   ble/decode/keymap/push vi_digraph
   7855   _ble_keymap_vi_digraph__hook=ble/widget/vi_imap/insert-digraph.hook
   7856   return 0
   7857 }
   7858 
   7859 # imap: CR, LF (newline)
   7860 function ble/widget/vi_imap/newline {
   7861   local ret
   7862   ble-edit/content/find-logical-bol; local bol=$ret
   7863   ble-edit/content/find-non-space "$bol"; local nol=$ret
   7864   ble/widget/default/newline
   7865   ((bol<nol)) && ble/widget/.insert-string "${_ble_edit_str:bol:nol-bol}"
   7866   return 0
   7867 }
   7868 
   7869 # imap: C-h, DEL
   7870 function ble/widget/vi_imap/delete-backward-indent-or {
   7871   local rex=$'(^|\n)([ \t]+)$'
   7872   if [[ ${_ble_edit_str::_ble_edit_ind} =~ $rex ]]; then
   7873     local rematch2=${BASH_REMATCH[2]} # Note: for bash-3.1 ${#arr[n]} bug
   7874     if [[ $rematch2 ]]; then
   7875       ble/keymap:vi/undo/add more
   7876       ble/widget/.delete-range "$((_ble_edit_ind-${#rematch2}))" "$_ble_edit_ind"
   7877       ble/keymap:vi/undo/add more
   7878     fi
   7879     return 0
   7880   else
   7881     ble/widget/"$@"
   7882   fi
   7883 }
   7884 
   7885 #------------------------------------------------------------------------------
   7886 
   7887 function ble-decode/keymap:vi_imap/define {
   7888   #----------------------------------------------------------------------------
   7889   # common bindings
   7890 
   7891   local ble_bind_nometa=1
   7892   ble-decode/keymap:safe/bind-common
   7893   ble-decode/keymap:safe/bind-history
   7894 
   7895   #----------------------------------------------------------------------------
   7896   # from ble-decode/keymap:safe/define
   7897 
   7898   ble-bind -f 'C-d'       'delete-region-or delete-forward-char-or-exit'
   7899 
   7900   ble-bind -f 'SP'        'magic-space'
   7901   ble-bind -f '/'         'magic-slash'
   7902 
   7903   # ble-bind -f  'C-c'      'discard-line'
   7904   ble-bind -f 'C-j'       'accept-line'
   7905   ble-bind -f 'C-RET'     'accept-line'
   7906   ble-bind -f 'C-m'       'accept-single-line-or-newline'
   7907   ble-bind -f 'RET'       'accept-single-line-or-newline'
   7908   # ble-bind -f  'C-o'      'accept-and-next'
   7909   ble-bind -f 'C-x C-e'   'edit-and-execute-command'
   7910   ble-bind -f 'C-g'       'bell'
   7911   ble-bind -f 'C-x C-g'   'bell'
   7912 
   7913   ble-bind -f 'C-l'       'clear-screen'
   7914 
   7915   ble-bind -f 'f1'        'command-help'
   7916   ble-bind -f 'C-x C-v'   'display-shell-version'
   7917   ble-bind -c 'C-z'       'fg'
   7918 
   7919   # args
   7920   local key
   7921   for key in {,C-}{0..9}; do
   7922     ble-bind -f "$key"    'append-arg'
   7923   done
   7924 
   7925   #----------------------------------------------------------------------------
   7926   # vi_imap modifications
   7927 
   7928   ble-bind -f insert      'vi_imap/overwrite-mode'
   7929 
   7930   # insert
   7931   ble-bind -f 'C-q'       'vi_imap/quoted-insert'
   7932   ble-bind -f 'C-v'       'vi_imap/quoted-insert'
   7933   ble-bind -f 'C-RET'     'newline'
   7934   ble-bind -f paste_begin 'vi_imap/bracketed-paste'
   7935 
   7936   # charwise operations
   7937   ble-bind -f 'C-?'       'vi_imap/delete-region-or vi_imap/delete-backward-indent-or delete-backward-char'
   7938   ble-bind -f 'DEL'       'vi_imap/delete-region-or vi_imap/delete-backward-indent-or delete-backward-char'
   7939   ble-bind -f 'C-h'       'vi_imap/delete-region-or vi_imap/delete-backward-indent-or delete-backward-char'
   7940   ble-bind -f 'BS'        'vi_imap/delete-region-or vi_imap/delete-backward-indent-or delete-backward-char'
   7941 
   7942   # wordwise operations
   7943   ble-bind -f 'C-w'       'vi_imap/delete-backward-word'
   7944 
   7945   # complete
   7946   ble-decode/keymap:vi_imap/bind-complete
   7947 
   7948   #----------------------------------------------------------------------------
   7949   # shell functions (from keymap emacs-standard)
   7950 
   7951   ble-bind -f 'C-\' bell
   7952   ble-bind -f 'C-^' bell
   7953 
   7954   #----------------------------------------------------------------------------
   7955   # vi bindings
   7956 
   7957   ble-bind -f __attach__        vi_imap/__attach__
   7958   ble-bind -f __detach__        vi_imap/__detach__
   7959   ble-bind -f __default__       vi_imap/__default__
   7960   ble-bind -f __before_widget__ vi_imap/__before_widget__
   7961   ble-bind -f __line_limit__    __line_limit__
   7962 
   7963   ble-bind -f 'ESC' 'vi_imap/normal-mode'
   7964   ble-bind -f 'C-[' 'vi_imap/normal-mode'
   7965   ble-bind -f 'C-c' 'vi_imap/normal-mode-without-insert-leave'
   7966 
   7967   ble-bind -f 'C-o' 'vi_imap/single-command-mode'
   7968 
   7969   # ble-bind -f 'C-l' vi_imap/normal-mode
   7970   # ble-bind -f 'C-k' vi_imap/insert-digraph
   7971 }
   7972 
   7973 ## @fn ble-decode/keymap:vi_imap/define-meta-bindings
   7974 ##   M- で始まるキーバインディングを定義します。
   7975 ##   ユーザから呼び出すための関数です。
   7976 function ble-decode/keymap:vi_imap/define-meta-bindings {
   7977   local ble_bind_keymap=vi_imap
   7978 
   7979   #----------------------------------------------------------------------------
   7980   # from ble-decode/keymap:safe/define
   7981 
   7982   ble-bind -f 'M-^'       'history-expand-line'
   7983   ble-bind -f 'C-M-l'     'redraw-line'
   7984   ble-bind -f 'M-#'       'insert-comment'
   7985   ble-bind -f 'M-C-e'     'shell-expand-line'
   7986   ble-bind -f 'M-&'       'tilde-expand'
   7987   ble-bind -f 'C-M-g'     'bell'
   7988   ble-bind -c 'M-z'       'fg'
   7989 
   7990   #----------------------------------------------------------------------------
   7991   # from ble-decode/keymap:safe/bind-common
   7992 
   7993   ble-bind -f 'M-C-m'     'newline'
   7994   ble-bind -f 'M-RET'     'newline'
   7995   ble-bind -f 'M-SP'      'set-mark'
   7996   ble-bind -f 'M-w'       'copy-region-or copy-uword'
   7997   ble-bind -f 'M-y'       'yank-pop'
   7998   ble-bind -f 'M-S-y'     'yank-pop backward'
   7999   ble-bind -f 'M-Y'       'yank-pop backward'
   8000   ble-bind -f 'M-\'       'delete-horizontal-space'
   8001 
   8002   ble-bind -f 'M-right'   '@nomarked forward-sword'
   8003   ble-bind -f 'M-left'    '@nomarked backward-sword'
   8004   ble-bind -f 'S-M-right' '@marked forward-sword'
   8005   ble-bind -f 'S-M-left'  '@marked backward-sword'
   8006   ble-bind -f 'M-d'       'kill-forward-cword'
   8007   ble-bind -f 'M-h'       'kill-backward-cword'
   8008   ble-bind -f 'M-delete'  'copy-forward-sword'
   8009   ble-bind -f 'M-C-?'     'copy-backward-sword'
   8010   ble-bind -f 'M-DEL'     'copy-backward-sword'
   8011   ble-bind -f 'M-C-h'     'copy-backward-sword'
   8012   ble-bind -f 'M-BS'      'copy-backward-sword'
   8013 
   8014   ble-bind -f 'M-f'       '@nomarked forward-cword'
   8015   ble-bind -f 'M-b'       '@nomarked backward-cword'
   8016   ble-bind -f 'M-F'       '@marked forward-cword'
   8017   ble-bind -f 'M-B'       '@marked backward-cword'
   8018   ble-bind -f 'M-S-f'     '@marked forward-cword'
   8019   ble-bind -f 'M-S-b'     '@marked backward-cword'
   8020   ble-bind -f 'M-c'       'capitalize-eword'
   8021   ble-bind -f 'M-l'       'downcase-eword'
   8022   ble-bind -f 'M-u'       'upcase-eword'
   8023   ble-bind -f 'M-t'       'transpose-ewords'
   8024 
   8025   ble-bind -f 'M-m'       '@nomarked non-space-beginning-of-line'
   8026   ble-bind -f 'M-S-m'     '@marked non-space-beginning-of-line'
   8027   ble-bind -f 'M-M'       '@marked non-space-beginning-of-line'
   8028 
   8029   #----------------------------------------------------------------------------
   8030   # from ble-decode/keymap:safe/bind-history
   8031 
   8032   ble-bind -f 'M-<'       'history-beginning'
   8033   ble-bind -f 'M->'       'history-end'
   8034   ble-bind -f 'M-.'       'insert-last-argument'
   8035   ble-bind -f 'M-_'       'insert-last-argument'
   8036   ble-bind -f 'M-C-y'     'insert-nth-argument'
   8037 
   8038   #----------------------------------------------------------------------------
   8039   # from ble-decode/keymap:safe/bind-complete
   8040 
   8041   ble-bind -f 'M-?'       'complete show_menu'
   8042   ble-bind -f 'M-*'       'complete insert_all'
   8043   ble-bind -f 'M-{'       'complete insert_braces'
   8044   ble-bind -f 'M-/'       'complete context=filename'
   8045   ble-bind -f 'M-~'       'complete context=username'
   8046   ble-bind -f 'M-$'       'complete context=variable'
   8047   ble-bind -f 'M-@'       'complete context=hostname'
   8048   ble-bind -f 'M-!'       'complete context=command'
   8049   ble-bind -f "M-'"       'sabbrev-expand'
   8050   ble-bind -f 'M-g'       'complete context=glob'
   8051   ble-bind -f 'M-C-i'     'complete context=dynamic-history'
   8052   ble-bind -f 'M-TAB'     'complete context=dynamic-history'
   8053 
   8054   #----------------------------------------------------------------------------
   8055   # from ble-decode/keymap:safe/bind-arg
   8056 
   8057   ble-bind -f 'M-0'       'append-arg'
   8058   ble-bind -f 'M-1'       'append-arg'
   8059   ble-bind -f 'M-2'       'append-arg'
   8060   ble-bind -f 'M-3'       'append-arg'
   8061   ble-bind -f 'M-4'       'append-arg'
   8062   ble-bind -f 'M-5'       'append-arg'
   8063   ble-bind -f 'M-6'       'append-arg'
   8064   ble-bind -f 'M-7'       'append-arg'
   8065   ble-bind -f 'M-8'       'append-arg'
   8066   ble-bind -f 'M-9'       'append-arg'
   8067 }
   8068 
   8069 #------------------------------------------------------------------------------
   8070 # vi_cmap
   8071 
   8072 _ble_keymap_vi_cmap_hook=
   8073 _ble_keymap_vi_cmap_cancel_hook=
   8074 _ble_keymap_vi_cmap_before_command=
   8075 
   8076 # 既定の cmap 履歴
   8077 _ble_keymap_vi_cmap_history=()
   8078 _ble_keymap_vi_cmap_history_edit=()
   8079 _ble_keymap_vi_cmap_history_dirt=()
   8080 _ble_keymap_vi_cmap_history_index=0
   8081 
   8082 function ble/keymap:vi/async-commandline-mode {
   8083   local hook=$1
   8084   _ble_keymap_vi_cmap_hook=$hook
   8085   _ble_keymap_vi_cmap_cancel_hook=
   8086   _ble_keymap_vi_cmap_before_command=
   8087 
   8088   # 記録
   8089   ble/textarea#render
   8090   ble/textarea#save-state _ble_keymap_vi_cmap
   8091   ble/util/save-vars _ble_keymap_vi_cmap _ble_canvas_panel_focus
   8092   _ble_keymap_vi_cmap_history_prefix=$_ble_history_prefix
   8093 
   8094   # 初期化
   8095   ble/decode/keymap/push vi_cmap
   8096   ble/keymap:vi/update-mode-indicator
   8097 
   8098   # textarea
   8099   _ble_textarea_panel=1
   8100   _ble_canvas_panel_focus=1
   8101   ble/textarea#invalidate
   8102 
   8103   # edit/prompt
   8104   _ble_edit_PS1=$PS2
   8105   _ble_prompt_ps1_data=(0 '' '' 0 0 0 32 0 '' '')
   8106 
   8107   # edit
   8108   #   Note: ble/widget/.newline/clear-content の中で
   8109   #   ble-edit/content/reset が呼び出され、更に _ble_edit_dirty_observer が呼び出さる。
   8110   #   ble/keymap:vi/mark/shift-by-dirty-range が呼び出されないように、
   8111   #   _ble_edit_dirty_observer=() より後である必要がある。
   8112   _ble_edit_dirty_observer=()
   8113   ble/widget/.newline/clear-content
   8114   _ble_edit_arg=
   8115 
   8116   # edit/undo
   8117   ble-edit/undo/clear-all
   8118 
   8119   # edit/history
   8120   ble/history/set-prefix _ble_keymap_vi_cmap
   8121 
   8122   # syntax, highlight
   8123   _ble_syntax_lang=text
   8124   _ble_highlight_layer_list=(plain region overwrite_mode)
   8125 }
   8126 
   8127 function ble/widget/vi_cmap/accept {
   8128   local hook=${_ble_keymap_vi_cmap_hook}
   8129   _ble_keymap_vi_cmap_hook=
   8130 
   8131   local result=$_ble_edit_str
   8132   [[ $result ]] && ble/history/add "$result" # Note: cancel でも登録する
   8133 
   8134   # 消去
   8135   local -a DRAW_BUFF=()
   8136   ble/canvas/panel#set-height.draw "$_ble_textarea_panel" 0
   8137   ble/canvas/bflush.draw
   8138 
   8139   # 復元
   8140   ble/textarea#restore-state _ble_keymap_vi_cmap
   8141   ble/textarea#clear-state _ble_keymap_vi_cmap
   8142   ble/util/restore-vars _ble_keymap_vi_cmap _ble_canvas_panel_focus
   8143   [[ $_ble_edit_overwrite_mode ]] && ble/util/buffer "$_ble_term_civis"
   8144   ble/history/set-prefix "$_ble_keymap_vi_cmap_history_prefix"
   8145 
   8146   ble/decode/keymap/pop
   8147   ble/keymap:vi/update-mode-indicator
   8148   if [[ $hook ]]; then
   8149     builtin eval -- "$hook \"\$result\""
   8150   else
   8151     ble/keymap:vi/adjust-command-mode
   8152     return 0
   8153   fi
   8154 }
   8155 
   8156 function ble/widget/vi_cmap/cancel {
   8157   _ble_keymap_vi_cmap_hook=$_ble_keymap_vi_cmap_cancel_hook
   8158   ble/widget/vi_cmap/accept
   8159 }
   8160 
   8161 function ble/widget/vi_cmap/__before_widget__ {
   8162   if [[ $_ble_keymap_vi_cmap_before_command ]]; then
   8163     builtin eval -- "$_ble_keymap_vi_cmap_before_command"
   8164   fi
   8165 }
   8166 
   8167 function ble/widget/vi_cmap/__line_limit__.edit {
   8168   local content=$1
   8169   ble/widget/edit-and-execute-command.edit "$content" no-newline; local ext=$?
   8170   ((ext==127)) && return "$ext"
   8171   ble-edit/content/reset "$ret"
   8172   ble/widget/vi_cmap/accept
   8173 }
   8174 function ble/widget/vi_cmap/__line_limit__ {
   8175   ble/widget/__line_limit__ vi_cmap/__line_limit__.edit
   8176 }
   8177 
   8178 function ble-decode/keymap:vi_cmap/define {
   8179   #----------------------------------------------------------------------------
   8180   # common bindings + modifications
   8181 
   8182   local ble_bind_nometa=
   8183   ble-decode/keymap:safe/bind-common
   8184   ble-decode/keymap:safe/bind-history
   8185   ble-decode/keymap:safe/bind-complete
   8186 
   8187   #----------------------------------------------------------------------------
   8188 
   8189   ble-bind -f __before_widget__ vi_cmap/__before_widget__
   8190   ble-bind -f __line_limit__    vi_cmap/__line_limit__
   8191 
   8192   # accept/cancel
   8193   ble-bind -f 'ESC'     vi_cmap/cancel
   8194   ble-bind -f 'C-['     vi_cmap/cancel
   8195   ble-bind -f 'C-c'     vi_cmap/cancel
   8196   ble-bind -f 'C-m'     vi_cmap/accept
   8197   ble-bind -f 'RET'     vi_cmap/accept
   8198   ble-bind -f 'C-j'     vi_cmap/accept
   8199   ble-bind -f 'C-g'     bell
   8200   ble-bind -f 'C-x C-g' bell
   8201   ble-bind -f 'C-M-g'   bell
   8202 
   8203   # shell function
   8204   # ble-bind -f  'C-l'     clear-screen
   8205   ble-bind -f  'C-l'     redraw-line
   8206   ble-bind -f  'C-M-l'   redraw-line
   8207   ble-bind -f  'C-x C-v' display-shell-version
   8208 
   8209   # command-history
   8210   # ble-bind -f 'M-^'     history-expand-line
   8211   # ble-bind -f 'SP'      magic-space
   8212   # ble-bind -f '/'       magic-slash
   8213 
   8214   ble-bind -f 'C-\' bell
   8215   ble-bind -f 'C-^' bell
   8216 }
   8217 
   8218 #------------------------------------------------------------------------------
   8219 
   8220 function ble-decode/keymap:vi/initialize {
   8221   local fname_keymap_cache=$_ble_base_cache/keymap.vi
   8222   if [[ -s $fname_keymap_cache &&
   8223           $fname_keymap_cache -nt $_ble_base/lib/keymap.vi.sh &&
   8224           $fname_keymap_cache -nt $_ble_base/lib/init-cmap.sh ]]; then
   8225     source "$fname_keymap_cache" && return 0
   8226   fi
   8227 
   8228   ble/edit/info/immediate-show text "ble.sh: updating cache/keymap.vi..."
   8229 
   8230   {
   8231     ble/decode/keymap#load isearch dump
   8232     ble/decode/keymap#load nsearch dump
   8233     ble/decode/keymap#load vi_imap dump
   8234     ble/decode/keymap#load vi_nmap dump
   8235     ble/decode/keymap#load vi_omap dump
   8236     ble/decode/keymap#load vi_xmap dump
   8237     ble/decode/keymap#load vi_cmap dump
   8238   } 3>| "$fname_keymap_cache"
   8239 
   8240   ble/edit/info/immediate-show text "ble.sh: updating cache/keymap.vi... done"
   8241 }
   8242 
   8243 ble-decode/keymap:vi/initialize
   8244 blehook/invoke keymap_load
   8245 blehook/invoke keymap_vi_load
   8246 return 0