sistema_progs

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

vim-surround.sh (28091B)


      1 #!/bin/bash
      2 
      3 ble-import keymap.vi
      4 
      5 # surround.vim (https://github.com/tpope/vim-surround) の模倣実装
      6 #
      7 # 現在以下のみに対応している。
      8 #
      9 #   nmap: ys{move}{ins}
     10 #   nmap: yss{ins}
     11 #   nmap: yS{move}{ins}
     12 #   nmap: ySS{ins} または ySs{ins}
     13 #   xmap: S{ins}
     14 #   xmap: gS{ins}
     15 #
     16 #     {ins} ~ / ?./
     17 #
     18 #       空白を前置した場合は、囲まれる文字列の両端に半角空白を1つずつ付加する。
     19 #       最後の文字によって囲み文字を指定する。
     20 #
     21 #       <, t        (未対応) タグで囲む
     22 #       右括弧類    括弧で囲む
     23 #       左括弧類    括弧で囲む
     24 #       [a-zA-Z]    エラー
     25 #       他の文字    その文字で囲む
     26 #
     27 #   注意: surround.vim と違って、
     28 #     テキストオブジェクト等に対する引数は有効である。
     29 #     または、aw は iw と異なる位置に挿入する。
     30 #
     31 #   注意: surround.vim と違って、
     32 #     2ys3s のような引数の指定を行うことができる。
     33 #     これは 2y3y と同様に現在行から 6 行に亘って作用する。
     34 #
     35 #   注意: . によってこのオペレータを繰り返すとき、
     36 #     再度入力を求める surround.vim と違って、
     37 #     前回使用した区切り文字を使用する。
     38 #
     39 #   注意: surround.vim では矩形選択の末尾拡張を判定できないため、
     40 #     S は非末尾拡張で gS は末尾拡張として働くが、
     41 #     この実装では S は現在末尾拡張状態を使い gS は末尾拡張を常に行う。
     42 #
     43 #   nmap: ds{del}
     44 #   nmap: cs{del}{ins}
     45 #   nmap: cS{del}{ins}
     46 #
     47 #     {del} ~ /([0-9]+| )?./
     48 #
     49 #       削除される囲み文字を指定する。
     50 #       [0-9]+ を指定した場合は引数に対する倍率となる。
     51 #       空白を前置した場合は囲まれた文字列の両端の空白を trim する。
     52 #
     53 #       最後の文字によって囲み文字を指定する。
     54 #
     55 #       b()B{}r[]a<>    括弧を削除する。左括弧を指定したとき囲まれた文字列は trim される。
     56 #       wW              範囲を指定するのみで、何も削除しない。
     57 #       ps (未対応)     範囲を指定するのみで、何も削除しない。
     58 #       tT              タグ。T を用いたとき囲まれた文字列は trim される。
     59 #       /               /* ... */ で囲まれた領域を削除する。
     60 #       他の文字        その文字を、行内で左右に探して対で削除する。
     61 #
     62 #     {ins} ~ / ?./
     63 #
     64 #       代わりに挿入される囲み文字を指定する。ys, yss と同じ。
     65 #
     66 #   注意: . によってこの操作を繰り返すとき、
     67 #     surround.vim では {del} を空文字列とし {ins} について入力を求めるが、
     68 #     この実装では前回使用した {del} と {ins} を使用して動作する。
     69 #
     70 # 以下には対応していない。
     71 #
     72 #   imap: <C-S>
     73 #   imap: <C-G>s
     74 #   imap: <C-G>S
     75 #
     76 
     77 bleopt/declare -n vim_surround_45 $'$(\r)' # ysiw-
     78 bleopt/declare -n vim_surround_61 $'$((\r))' # ysiw=
     79 bleopt/declare -n vim_surround_q \" # ysiwQ
     80 bleopt/declare -n vim_surround_Q \' # ysiwq
     81 bleopt/declare -v vim_surround_omap_bind 1
     82 
     83 #------------------------------------------------------------------------------
     84 # util
     85 
     86 ## @fn ble/lib/vim-surround.sh/get-char-from-key key
     87 ##   @param[in] key
     88 ##   @var[out] ret
     89 function ble/lib/vim-surround.sh/get-char-from-key {
     90   local key=$1
     91   if ! ble-decode-key/ischar "$key"; then
     92     local flag=$((key&_ble_decode_MaskFlag)) code=$((key&_ble_decode_MaskChar))
     93     if ((flag==_ble_decode_Ctrl&&63<=code&&code<128&&(code&0x1F)!=0)); then
     94       ((key=code==63?127:code&0x1F))
     95     else
     96       return 1
     97     fi
     98   fi
     99 
    100   ble/util/c2s "$key"
    101   return 0
    102 }
    103 
    104 function ble/lib/vim-surround.sh/async-inputtarget.hook {
    105   local mode=$1 hook=${@:2:$#-2} key=${@:$#} ret
    106   if ! ble/lib/vim-surround.sh/get-char-from-key "$key"; then
    107     ble/widget/vi-command/bell
    108     return 1
    109   fi
    110 
    111   local c=$ret
    112   if [[ :$mode: == *:digit:* && $c == [0-9] ]]; then
    113     _ble_edit_arg=$_ble_edit_arg$c
    114     _ble_decode_key__hook="ble/lib/vim-surround.sh/async-inputtarget.hook digit $hook"
    115     return 147
    116   elif [[ :$mode: == *:init:* && $c == ' ' ]]; then
    117     _ble_decode_key__hook="ble/lib/vim-surround.sh/async-inputtarget.hook space $hook"
    118     return 147
    119   fi
    120 
    121   if [[ $c == [$'\e\003'] ]]; then # C-[, C-c
    122     ble/widget/vi-command/bell
    123     return 1
    124   else
    125     [[ $c == \' ]] && c="'\''"
    126     [[ $mode == space ]] && c=' '$c
    127     builtin eval -- "$hook '$c'"
    128   fi
    129 }
    130 function ble/lib/vim-surround.sh/async-inputtarget {
    131   local IFS=$_ble_term_IFS
    132   _ble_decode_key__hook="ble/lib/vim-surround.sh/async-inputtarget.hook init:digit $*"
    133   return 147
    134 }
    135 function ble/lib/vim-surround.sh/async-inputtarget-noarg {
    136   local IFS=$_ble_term_IFS
    137   _ble_decode_key__hook="ble/lib/vim-surround.sh/async-inputtarget.hook init $*"
    138   return 147
    139 }
    140 
    141 _ble_lib_vim_surround_previous_tag=html
    142 
    143 ## @fn ble/lib/vim-surround.sh/load-template ins
    144 ##   @param[in] ins
    145 ##   @var[out] template
    146 function ble/lib/vim-surround.sh/load-template {
    147   local ins=$1
    148 
    149   # read user settings
    150 
    151   if [[ ${ins//[0-9]} && ! ${ins//[_a-zA-Z0-9]} ]]; then
    152     local optname=bleopt_vim_surround_$ins
    153     template=${!optname}
    154     [[ $template ]] && return 0
    155   fi
    156 
    157   local ret; ble/util/s2c "$ins"
    158   local optname=bleopt_vim_surround_$ret
    159   template=${!optname}
    160   [[ $template ]] && return 0
    161 
    162   # default
    163 
    164   case $ins in
    165   (['<tT']*)
    166     local tag=${ins:1}; tag=${tag//$'\r'/' '}
    167     if [[ ! $tag ]]; then
    168       tag=$_ble_lib_vim_surround_previous_tag
    169     else
    170       tag=${tag%'>'}
    171       _ble_lib_vim_surround_previous_tag=$tag
    172     fi
    173     local end_tag=${tag%%["$_ble_term_IFS"]*}
    174     template="<$tag>"$'\r'"</$end_tag>" ;;
    175   ('(') template=$'( \r )' ;;
    176   ('[') template=$'[ \r ]' ;;
    177   ('{') template=$'{ \r }' ;;
    178   (['b)']) template=$'(\r)' ;;
    179   (['r]']) template=$'[\r]' ;;
    180   (['B}']) template=$'{\r}' ;;
    181   (['a>']) template=$'<\r>' ;;
    182   ([a-zA-Z]) return 1 ;;
    183   (*) template=$ins ;;
    184   esac
    185 } &>/dev/null
    186 
    187 ## @fn ble/lib/vim-surround.sh/surround text ins opts
    188 ##   @param[in] text
    189 ##   @param[in] ins
    190 ##   @param[in] opts
    191 ##     linewise
    192 ##       囲まれた文字列を新しい独立した業にします。
    193 ##       cS yS VS VgS などで使用します。
    194 ##     indent
    195 ##       linewise のとき、新しい行のインデントを追加します。
    196 ##
    197 ##   @var[in] beg
    198 ##   @var[out] ret
    199 function ble/lib/vim-surround.sh/surround {
    200   local text=$1 ins=$2 opts=$3
    201 
    202   local instype=
    203   [[ $ins == $'\x1D' ]] && ins='}' instype=indent # C-], C-}
    204 
    205   local has_space=
    206   [[ $ins == ' '?* ]] && ins=${ins:1} has_space=1
    207 
    208   local template=
    209   ble/lib/vim-surround.sh/load-template "$ins" || return 1
    210 
    211   local prefix= suffix=
    212   if [[ $template == *$'\r'* ]]; then
    213     prefix=${template%%$'\r'*}
    214     suffix=${template#*$'\r'}
    215   else
    216     prefix=$template
    217     suffix=$template
    218   fi
    219 
    220   if [[ $prefix == *' ' && $suffix == ' '* ]]; then
    221     prefix=${prefix::${#prefix}-1}
    222     suffix=${suffix:1}
    223     has_space=1
    224   fi
    225 
    226   if [[ $instype == indent || :$opts: == *:linewise:* ]]; then
    227     ble-edit/content/find-logical-bol "$beg"; local bol=$ret
    228     ble-edit/content/find-non-space "$bol"; local nol=$ret
    229     local indent=
    230     if [[ $instype == indent ]] || ((bol<nol)); then
    231       indent=${_ble_edit_str:bol:nol-bol}
    232     elif [[ $has_space ]]; then
    233       indent=' '
    234     fi
    235     text=$indent$text
    236     if [[ $instype == indent || :$opts: == *:indent:* ]]; then
    237       ble/keymap:vi/string#increase-indent "$text" "$bleopt_indent_offset"; text=$ret
    238     fi
    239     text=$'\n'$text$'\n'$indent
    240     # ToDo: 初めから text に改行が含まれていた場合は、
    241     #   更にここで = による自動インデントを実行する?
    242   elif [[ $has_space ]]; then
    243     text=' '$text' '
    244   fi
    245 
    246   ret=$prefix$text$suffix
    247 }
    248 
    249 #------------------------------------------------------------------------------
    250 # async-read-tagname
    251 
    252 function ble/lib/vim-surround.sh/async-read-tagname {
    253   ble/keymap:vi/async-commandline-mode "$1"
    254   _ble_edit_PS1='<'
    255   _ble_keymap_vi_cmap_before_command=ble/lib/vim-surround.sh/async-read-tagname/.before-command.hook
    256   return 147
    257 }
    258 function ble/lib/vim-surround.sh/async-read-tagname/.before-command.hook {
    259   if [[ ${KEYS[0]} == 62 ]]; then # '>'
    260     ble/widget/self-insert
    261     ble/widget/vi_cmap/accept
    262     ble/decode/widget/suppress-widget
    263   fi
    264 }
    265 
    266 #------------------------------------------------------------------------------
    267 # ys yss yS ySS ySs vS vgS
    268 
    269 _ble_lib_vim_surround_ys_type= # ys | yS | vS | vgS
    270 _ble_lib_vim_surround_ys_args=()
    271 _ble_lib_vim_surround_ys_ranges=()
    272 
    273 ## @fn ble/highlight/layer:region/mark:vi_surround/get-selection
    274 ##   入力待ち状態の時の領域着色を定義します。
    275 ##   @arr[out] selection
    276 function ble/highlight/layer:region/mark:vi_surround/get-selection {
    277   local type=$_ble_lib_vim_surround_ys_type
    278   local context=${_ble_lib_vim_surround_ys_args[2]}
    279   if [[ $context == block ]]; then
    280     local -a sub_ranges
    281     sub_ranges=("${_ble_lib_vim_surround_ys_ranges[@]}")
    282 
    283     selection=()
    284     local sub
    285     for sub in "${sub_ranges[@]}"; do
    286       ble/string#split sub : "$sub"
    287       ((sub[0]<sub[1])) || continue
    288       ble/array#push selection "${sub[0]}" "${sub[1]}"
    289     done
    290   else
    291     selection=("${_ble_lib_vim_surround_ys_args[@]::2}")
    292 
    293     # convert to linewise
    294     if [[ $context == char && ( $type == yS || $type == ySS || $type == vgS ) ]]; then
    295       local ret
    296       ble-edit/content/find-logical-bol "${selection[0]}"; selection[0]=$ret
    297       ble-edit/content/find-logical-eol "${selection[1]}"; selection[1]=$ret
    298     fi
    299   fi
    300 }
    301 function ble/highlight/layer:region/mark:vi_surround/get-face {
    302   face=region_target
    303 }
    304 
    305 function ble/lib/vim-surround.sh/operator.impl {
    306   _ble_lib_vim_surround_ys_type=$1; shift
    307   _ble_lib_vim_surround_ys_args=("$@")
    308   [[ $3 == block ]] && _ble_lib_vim_surround_ys_ranges=("${sub_ranges[@]}")
    309   _ble_edit_mark_active=vi_surround
    310   ble/lib/vim-surround.sh/async-inputtarget-noarg ble/widget/vim-surround.sh/ysurround.hook1
    311   ble/lib/vim-surround.sh/ysurround.repeat/entry
    312   return 147
    313 }
    314 function ble/keymap:vi/operator:yS { ble/lib/vim-surround.sh/operator.impl yS "$@"; }
    315 function ble/keymap:vi/operator:ys { ble/lib/vim-surround.sh/operator.impl ys "$@"; }
    316 function ble/keymap:vi/operator:ySS { ble/lib/vim-surround.sh/operator.impl ySS "$@"; }
    317 function ble/keymap:vi/operator:yss { ble/lib/vim-surround.sh/operator.impl yss "$@"; }
    318 function ble/keymap:vi/operator:vS { ble/lib/vim-surround.sh/operator.impl vS "$@"; }
    319 function ble/keymap:vi/operator:vgS { ble/lib/vim-surround.sh/operator.impl vgS "$@"; }
    320 function ble/widget/vim-surround.sh/ysurround.hook1 {
    321   local ins=$1
    322   if local rex='^ ?[<tT]$'; [[ $ins =~ $rex ]]; then
    323     ble/lib/vim-surround.sh/async-read-tagname "ble/widget/vim-surround.sh/ysurround.hook2 '$ins'"
    324   else
    325     ble/widget/vim-surround.sh/ysurround.core "$ins"
    326   fi
    327 }
    328 function ble/widget/vim-surround.sh/ysurround.hook2 {
    329   local ins=$1 tagName=$2
    330   ble/widget/vim-surround.sh/ysurround.core "$ins$tagName"
    331 }
    332 function ble/widget/vim-surround.sh/ysurround.core {
    333   local ins=$1
    334   _ble_edit_mark_active= # mark:vi_surround を解除
    335 
    336   local ret
    337 
    338   # saved arguments
    339   local type=$_ble_lib_vim_surround_ys_type
    340   local beg=${_ble_lib_vim_surround_ys_args[0]}
    341   local end=${_ble_lib_vim_surround_ys_args[1]}
    342   local context=${_ble_lib_vim_surround_ys_args[2]}
    343   local sub_ranges; sub_ranges=("${_ble_lib_vim_surround_ys_ranges[@]}")
    344   _ble_lib_vim_surround_ys_type=
    345   _ble_lib_vim_surround_ys_args=()
    346   _ble_lib_vim_surround_ys_ranges=()
    347 
    348   if [[ $context == block ]]; then
    349     local isub=${#sub_ranges[@]} sub
    350     local smin= smax= slpad= srpad=
    351     while ((isub--)); do
    352       local sub=${sub_ranges[isub]}
    353       local stext=${sub#*:*:*:*:*:}
    354       ble/string#split sub : "${sub::${#sub}-${#stext}}"
    355       smin=${sub[0]} smax=${sub[1]}
    356       slpad=${sub[2]} srpad=${sub[3]}
    357 
    358       if ! ble/lib/vim-surround.sh/surround "$stext" "$ins"; then
    359         ble/widget/vi-command/bell
    360         return 1
    361       fi
    362       stext=$ret
    363 
    364       ((slpad)) && { ble/string#repeat ' ' "$slpad"; stext=$ret$stext; }
    365       ((srpad)) && { ble/string#repeat ' ' "$srpad"; stext=$stext$ret; }
    366       ble/widget/.replace-range "$smin" "$smax" "$stext"
    367     done
    368 
    369   else
    370     # text
    371     local text=${_ble_edit_str:beg:end-beg}
    372     if [[ $type == ys ]]; then
    373       if local rex=$'[ \t\n]+$'; [[ $text =~ $rex ]]; then
    374         # 範囲末端の空白は囲む対象としない
    375         ((end-=${#BASH_REMATCH}))
    376         text=${_ble_edit_str:beg:end-beg}
    377       fi
    378     fi
    379 
    380     # Note: char から linewise への昇格条件の変更は以下の関数にも反映させる必要がある:
    381     #  ble/highlight/layer:region/mark:vi_surround/get-selection
    382     local opts=
    383     if [[ $type == yS || $type == ySS || $context == char && $type == vgS ]]; then
    384       opts=linewise:indent
    385     elif [[ $context == line ]]; then
    386       opts=linewise
    387     fi
    388 
    389     # surround
    390     if ! ble/lib/vim-surround.sh/surround "$text" "$ins" "$opts"; then
    391       ble/widget/vi-command/bell
    392       return 1
    393     fi
    394     local text=$ret
    395 
    396     ble/widget/.replace-range "$beg" "$end" "$text"
    397   fi
    398 
    399   _ble_edit_ind=$beg
    400   if [[ $context == line ]]; then
    401     ble/widget/vi-command/first-non-space
    402   else
    403     ble/keymap:vi/adjust-command-mode
    404   fi
    405   ble/keymap:vi/mark/end-edit-area
    406   ble/lib/vim-surround.sh/ysurround.repeat/record "$type" "$ins"
    407   return 0
    408 }
    409 
    410 function ble/widget/vim-surround.sh/ysurround-current-line {
    411   ble/widget/vi_nmap/linewise-operator yss
    412 }
    413 function ble/widget/vim-surround.sh/ySurround-current-line {
    414   ble/widget/vi_nmap/linewise-operator ySS
    415 }
    416 function ble/widget/vim-surround.sh/vsurround { # vS
    417   ble/widget/vi-command/operator vS
    418 }
    419 function ble/widget/vim-surround.sh/vgsurround { # vgS
    420   [[ $_ble_decode_keymap == vi_xmap ]] &&
    421     ble/keymap:vi/xmap/add-eol-extension # 末尾拡張
    422   ble/widget/vi-command/operator vgS
    423 }
    424 
    425 # repeat (nmap .) 用の変数・関数
    426 _ble_lib_vim_surround_ys_repeat=()
    427 function ble/lib/vim-surround.sh/ysurround.repeat/entry {
    428   local -a _ble_keymap_vi_repeat _ble_keymap_vi_repeat_irepeat
    429   ble/keymap:vi/repeat/record-normal
    430   _ble_lib_vim_surround_ys_repeat=("${_ble_keymap_vi_repeat[@]}")
    431 }
    432 function ble/lib/vim-surround.sh/ysurround.repeat/record {
    433   ble/keymap:vi/repeat/record-special && return 0
    434   local type=$1 ins=$2
    435   _ble_keymap_vi_repeat=("${_ble_lib_vim_surround_ys_repeat[@]}")
    436   _ble_keymap_vi_repeat_irepeat=()
    437   _ble_keymap_vi_repeat[10]=$type
    438   _ble_keymap_vi_repeat[11]=$ins
    439   case $type in
    440   (vS|vgS)
    441     _ble_keymap_vi_repeat[2]='ble/widget/vi-command/operator ysurround.repeat'
    442     _ble_keymap_vi_repeat[4]= ;;
    443   (yss|ySS)
    444     _ble_keymap_vi_repeat[2]='ble/widget/vi_nmap/linewise-operator ysurround.repeat'
    445     _ble_keymap_vi_repeat[4]= ;;
    446   (*)
    447     _ble_keymap_vi_repeat[4]=ysurround.repeat
    448   esac
    449 }
    450 function ble/keymap:vi/operator:ysurround.repeat {
    451   _ble_lib_vim_surround_ys_type=${_ble_keymap_vi_repeat[10]}
    452   _ble_lib_vim_surround_ys_args=("$@")
    453   [[ $3 == block ]] && _ble_lib_vim_surround_ys_ranges=("${sub_ranges[@]}")
    454   local ins=${_ble_keymap_vi_repeat[11]}
    455   ble/widget/vim-surround.sh/ysurround.core "$ins"
    456 }
    457 
    458 #------------------------------------------------------------------------------
    459 # ds cs
    460 
    461 # 仕様: surround.vim の実装は杜撰なのでここで vim-surround.sh の独自仕様を定める。
    462 #
    463 #   ds, cs は続いて /([0-9]+| )?./ の形式の引数を受け取る。
    464 #
    465 #     /[0-9]+/ が指定された時は引数に対する倍率を表す。
    466 #
    467 #     / / が指定された時は囲みの内側にある空白も削除することを表す。
    468 #
    469 #     /./ として wW を指定したときは何も削除しない。
    470 #     c = b)B}r]a> を指定した時は text-object {arg}ic を残して {arg}ac を削除する。
    471 #     c = ({[< を指定した時は更に内側の空白も削除する。
    472 #     c = '"` を指定した場合には引数は無視する。ic を残して ac を削除する。
    473 #     それ以外の c = a-zA-Z は既定として text-object {arg}ic を残し {arg}ac を削除する。
    474 #     それ以外の文字に関しては行内で一致を検索する。
    475 #
    476 #   更に cs は続いて / ?./ の形式の引数を受け取る。
    477 #
    478 #     / / が指定されたtときは左右内側に空白を 1 つずつ付加する。
    479 #
    480 #   オリジナルの surround.vim とはところどころで振る舞いが異なる。
    481 #   振る舞いの違いに関しては ble.sh/memo.txt #D0457 を参照のこと。
    482 #
    483 #   ToDo: ds, cs において囲まれている対象に改行が含まれる場合、
    484 #     置換を行った後に関係する行を == でインデントする。
    485 #     現在、本体で = に対応していないのでこれも未対応である。
    486 #
    487 
    488 ## @fn ble/keymap:vi/operator:surround
    489 ##   @var[in] surround_content
    490 ##   @var[in] surround_ins
    491 ##   @var[in] surround_trim
    492 ##   @var[in] surround_type
    493 ##     ds cs cS の何れかの値
    494 function ble/keymap:vi/operator:surround.record { :; }
    495 function ble/keymap:vi/operator:surround {
    496   local beg=$1 end=$2 context=$3
    497   local content=$surround_content ins=$surround_ins trims=$surround_trim
    498 
    499   local ret
    500   if [[ $trims ]]; then
    501     ble/string#trim "$content"; content=$ret
    502   fi
    503 
    504   local opts=; [[ $surround_type == cS ]] && opts=linewise
    505   if ! ble/lib/vim-surround.sh/surround "$content" "$ins" "$opts"; then
    506     ble/widget/vi-command/bell
    507     return 0
    508   fi
    509   content=$ret
    510 
    511   ble/widget/.replace-range "$beg" "$end" "$content"
    512 
    513   # if [[ $has_nl ]]; then
    514   #   # ToDo: indent
    515   # fi
    516 
    517   return 0
    518 }
    519 ## @fn ble/keymap:vi/operator:surround-extract-region
    520 ##   着色の為に範囲を抽出するオペレータ
    521 ##   @var[out] surround_beg
    522 ##   @var[out] surround_end
    523 function ble/keymap:vi/operator:surround-extract-region {
    524   surround_beg=$beg surround_end=$end
    525   return 147 # 強制中断する為
    526 }
    527 
    528 ## @arr _ble_lib_vim_surround_cs
    529 ##   処理途中の情報はここに記録する。
    530 ##   以下の要素は指定された引数を記録する。
    531 ##
    532 ##   [0]=type
    533 ##     ds | cs | cS
    534 ##   [1]=arg
    535 ##   [2]=reg
    536 ##   [3]=del
    537 ##
    538 ##   以下の要素は引数から計算される途中の変数を保持する。
    539 ##
    540 ##   [11]=del2
    541 ##   [12]=obj1 [13]=obj2
    542 ##   [14]=beg [15]=end
    543 ##   [16]=arg2
    544 ##   [17]=trim
    545 ##
    546 _ble_lib_vim_surround_cs=()
    547 
    548 function ble/widget/vim-surround.sh/nmap/csurround.initialize {
    549   _ble_lib_vim_surround_cs=("${@:1:3}")
    550   return 0
    551 }
    552 function ble/widget/vim-surround.sh/nmap/csurround.set-delimiter {
    553   local type=${_ble_lib_vim_surround_cs[0]}
    554   local arg=${_ble_lib_vim_surround_cs[1]}
    555   local reg=${_ble_lib_vim_surround_cs[2]}
    556   _ble_lib_vim_surround_cs[3]=$1
    557 
    558   local trim=
    559   [[ $del == ' '?* ]] && trim=1 del=${del:1}
    560   if [[ $del == a ]]; then
    561     del='>'
    562   elif [[ $del == r ]]; then
    563     del=']'
    564   elif [[ $del == T ]]; then
    565     del='t' trim=1
    566   fi
    567 
    568   local obj1= obj2=
    569   case $del in
    570   ([wWps])      obj1=i$del obj2=i$del ;;
    571   ([\'\"\`])    obj1=i$del obj2=a$del arg=1 ;;
    572   (['bB)}>]t']) obj1=i$del obj2=a$del ;;
    573   (['({<['])    obj1=i$del obj2=a$del trim=1 ;;
    574   ([a-zA-Z])    obj1=i$del obj2=a$del ;;
    575   esac
    576 
    577   local beg end
    578   if [[ $obj1 && $obj2 ]]; then
    579     # テキストオブジェクトによって指定される範囲
    580 
    581     local surround_beg=$_ble_edit_ind surround_end=$_ble_edit_ind
    582     ble/keymap:vi/text-object.impl "$arg" surround-extract-region '' "$obj2"
    583     beg=$surround_beg end=$surround_end
    584   elif [[ $del == / ]]; then
    585     # /* ..  */ で囲まれた部分
    586 
    587     local rex='(/\*([^/]|/[^*])*/?){1,'$arg'}$'
    588     [[ ${_ble_edit_str::_ble_edit_ind+2} =~ $rex ]] || return 1
    589     beg=$((_ble_edit_ind+2-${#BASH_REMATCH}))
    590 
    591     ble/string#index-of "${_ble_edit_str:beg+2}" '*/' || return 1
    592     end=$((beg+ret+4))
    593   elif [[ $del ]]; then
    594     # 指定した文字で囲まれた部分
    595 
    596     local ret
    597     ble-edit/content/find-logical-bol; local bol=$ret
    598     ble-edit/content/find-logical-eol; local eol=$ret
    599     local line=${_ble_edit_str:bol:eol-bol}
    600     local ind=$((_ble_edit_ind-bol))
    601 
    602     # beg
    603     if ble/string#last-index-of "${line::ind}" "$del"; then
    604       beg=$ret
    605     elif local base=$((ind-(2*${#del}-1))); ((base>=0||(base=0)))
    606          ble/string#index-of "${line:base:ind+${#del}-base}" "$del"; then
    607       beg=$((base+ret))
    608     else
    609       return 1
    610     fi
    611 
    612     # end
    613     ble/string#index-of "${line:beg+${#del}}" "$del" || return 1
    614     end=$((beg+2*${#del}+ret))
    615 
    616     ((beg+=bol,end+=bol))
    617   fi
    618 
    619   _ble_lib_vim_surround_cs[11]=$del
    620   _ble_lib_vim_surround_cs[12]=$obj1
    621   _ble_lib_vim_surround_cs[13]=$obj2
    622   _ble_lib_vim_surround_cs[14]=$beg
    623   _ble_lib_vim_surround_cs[15]=$end
    624   _ble_lib_vim_surround_cs[16]=$arg
    625   _ble_lib_vim_surround_cs[17]=$trim
    626 }
    627 function ble/widget/vim-surround.sh/nmap/csurround.replace {
    628   local ins=$1
    629 
    630   local type=${_ble_lib_vim_surround_cs[0]}
    631   local arg=${_ble_lib_vim_surround_cs[1]}
    632   local reg=${_ble_lib_vim_surround_cs[2]}
    633   local del=${_ble_lib_vim_surround_cs[3]}
    634 
    635   local del2=${_ble_lib_vim_surround_cs[11]}
    636   local obj1=${_ble_lib_vim_surround_cs[12]}
    637   local obj2=${_ble_lib_vim_surround_cs[13]}
    638   local beg=${_ble_lib_vim_surround_cs[14]}
    639   local end=${_ble_lib_vim_surround_cs[15]}
    640   local arg2=${_ble_lib_vim_surround_cs[16]}
    641 
    642   local surround_ins=$ins
    643   local surround_type=$type
    644   local surround_trim=${_ble_lib_vim_surround_cs[17]}
    645 
    646   if [[ $obj1 && $obj2 ]]; then
    647     # テキストオブジェクトによって指定される範囲
    648     local ind=$_ble_edit_ind
    649 
    650     local _ble_edit_kill_ring _ble_edit_kill_type
    651     ble/keymap:vi/text-object.impl "$arg2" y '' "$obj1"; local ext=$?
    652     _ble_edit_ind=$ind
    653     ((ext!=0)) && return 1
    654 
    655     local surround_content=$_ble_edit_kill_ring
    656     ble/keymap:vi/text-object.impl "$arg2" surround '' "$obj2" || return 1
    657   elif [[ $del2 == / ]]; then
    658     # /* ..  */ で囲まれた部分
    659     local surround_content=${_ble_edit_str:beg+2:end-beg-4}
    660     ble/keymap:vi/call-operator surround "$beg" "$end" char '' ''
    661     _ble_edit_ind=$beg
    662   elif [[ $del2 ]]; then
    663     # 指定した文字で囲まれた部分
    664     local surround_content=${_ble_edit_str:beg+${#del2}:end-beg-2*${#del2}}
    665     ble/keymap:vi/call-operator surround "$beg" "$end" char '' ''
    666     _ble_edit_ind=$beg
    667   else
    668     ble/widget/vi-command/bell
    669     return 1
    670   fi
    671   ble/widget/vim-surround.sh/nmap/csurround.record "$type" "$arg" "$reg" "$del" "$ins"
    672   ble/keymap:vi/adjust-command-mode
    673   return 0
    674 }
    675 
    676 #---- repeat ----
    677 
    678 ## @fn ble/widget/vim-surround.sh/nmap/csurround.record
    679 function ble/widget/vim-surround.sh/nmap/csurround.record {
    680   # Note: ble/keymap:vi/repeat/record の実装に合わせた条件判定。
    681   [[ $_ble_keymap_vi_mark_suppress_edit ]] && return 0
    682 
    683   local type=$1 arg=$2 reg=$3 del=$4 ins=$5
    684   local WIDGET=ble/widget/vim-surround.sh/nmap/csurround.repeat ARG=$arg FLAG= REG=$reg
    685   ble/keymap:vi/repeat/record
    686   if [[ $_ble_decode_keymap == vi_imap ]]; then
    687     # Note: ble/keymap:vi/repeat/record の実装に合わせた条件判定。
    688     #   この実装では vi_imap で呼び出される事はない筈だが念の為。
    689     #   ble/keymap:vi/repeat/record は keymap が vi_imap の時は異なる場所に記録する。
    690     _ble_keymap_vi_repeat_insert[10]=$type
    691     _ble_keymap_vi_repeat_insert[11]=$del
    692     _ble_keymap_vi_repeat_insert[12]=$ins
    693   else
    694     _ble_keymap_vi_repeat[10]=$type
    695     _ble_keymap_vi_repeat[11]=$del
    696     _ble_keymap_vi_repeat[12]=$ins
    697   fi
    698 }
    699 function ble/widget/vim-surround.sh/nmap/csurround.repeat {
    700   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    701   local type=${_ble_keymap_vi_repeat[10]}
    702   local del=${_ble_keymap_vi_repeat[11]}
    703   local ins=${_ble_keymap_vi_repeat[12]}
    704   ble/widget/vim-surround.sh/nmap/csurround.initialize "$type" "$ARG" "$REG" &&
    705     ble/widget/vim-surround.sh/nmap/csurround.set-delimiter "$del" &&
    706     ble/widget/vim-surround.sh/nmap/csurround.replace "$ins" && return 0
    707   ble/widget/vi-command/bell
    708   return 1
    709 }
    710 
    711 #---- ds ----
    712 
    713 function ble/widget/vim-surround.sh/nmap/dsurround {
    714   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    715   ble/widget/vim-surround.sh/nmap/csurround.initialize ds "$ARG" "$REG"
    716   ble/lib/vim-surround.sh/async-inputtarget ble/widget/vim-surround.sh/nmap/dsurround.hook
    717 }
    718 function ble/widget/vim-surround.sh/nmap/dsurround.hook {
    719   local del=$1
    720   ble/widget/vim-surround.sh/nmap/csurround.set-delimiter "$del" &&
    721     ble/widget/vim-surround.sh/nmap/csurround.replace '' && return 0
    722   ble/widget/vi-command/bell
    723   return 1
    724 }
    725 
    726 #---- cs ----
    727 
    728 ## @fn ble/highlight/layer:region/mark:vi_surround/get-selection
    729 ##   入力待ち状態の時の領域着色を定義します。
    730 ##   @arr[out] selection
    731 function ble/highlight/layer:region/mark:vi_csurround/get-selection {
    732   local beg=${_ble_lib_vim_surround_cs[14]}
    733   local end=${_ble_lib_vim_surround_cs[15]}
    734   selection=("$beg" "$end")
    735 }
    736 function ble/highlight/layer:region/mark:vi_csurround/get-face {
    737   face=region_target
    738 }
    739 
    740 function ble/widget/vim-surround.sh/nmap/csurround {
    741   ble/widget/vim-surround.sh/nmap/csurround.impl cs
    742 }
    743 function ble/widget/vim-surround.sh/nmap/cSurround {
    744   ble/widget/vim-surround.sh/nmap/csurround.impl cS
    745 }
    746 function ble/widget/vim-surround.sh/nmap/csurround.impl {
    747   local ARG FLAG REG; ble/keymap:vi/get-arg 1
    748   local type=$1
    749   ble/widget/vim-surround.sh/nmap/csurround.initialize "$type" "$ARG" "$REG"
    750   ble/lib/vim-surround.sh/async-inputtarget ble/widget/vim-surround.sh/nmap/csurround.hook1
    751 }
    752 function ble/widget/vim-surround.sh/nmap/csurround.hook1 {
    753   local del=$1
    754   if [[ $del ]] && ble/widget/vim-surround.sh/nmap/csurround.set-delimiter "$del"; then
    755     _ble_edit_mark_active=vi_csurround
    756     ble/lib/vim-surround.sh/async-inputtarget-noarg ble/widget/vim-surround.sh/nmap/csurround.hook2
    757     return "$?"
    758   fi
    759 
    760   _ble_lib_vim_surround_cs=()
    761   ble/widget/vi-command/bell
    762   return 1
    763 }
    764 function ble/widget/vim-surround.sh/nmap/csurround.hook2 {
    765   local ins=$1
    766   if local rex='^ ?[<tT]$'; [[ $ins =~ $rex ]]; then
    767     ble/lib/vim-surround.sh/async-read-tagname "ble/widget/vim-surround.sh/nmap/csurround.hook3 '$ins'"
    768   else
    769     ble/widget/vim-surround.sh/nmap/csurround.hook3 "$ins"
    770   fi
    771 }
    772 ## @fn ble/widget/vim-surround.sh/nmap/csurround.hook3 ins [tagName]
    773 function ble/widget/vim-surround.sh/nmap/csurround.hook3 {
    774   local ins=$1 tagName=$2
    775   _ble_edit_mark_active= # clear mark:vi_csurround
    776   ble/widget/vim-surround.sh/nmap/csurround.replace "$ins$tagName" && return 0
    777   ble/widget/vi-command/bell
    778   return 1
    779 }
    780 
    781 #------------------------------------------------------------------------------
    782 
    783 function ble/widget/vim-surround.sh/omap {
    784   local ret n=${#KEYS[@]}
    785   if ! ble/keymap:vi/k2c "${KEYS[n?n-1:0]}"; then
    786     ble/widget/.bell
    787     return 1
    788   fi
    789   ble/util/c2s "$ret"; local s=$ret
    790 
    791   local opfunc=${_ble_keymap_vi_opfunc%%:*}$s
    792   local opflags=${_ble_keymap_vi_opfunc#*:}
    793   case $opfunc in
    794   (y[sS])
    795     local ARG FLAG REG; ble/keymap:vi/get-arg 1
    796     _ble_edit_arg=$ARG
    797     _ble_keymap_vi_reg=$REG
    798     ble/decode/keymap/pop
    799     ble/widget/vi-command/operator "$opfunc:$opflags" ;;
    800   (yss)
    801     ble/widget/vi_nmap/linewise-operator "yss:$opflags" ;;
    802   (yS[sS])
    803     ble/widget/vi_nmap/linewise-operator "ySS:$opflags" ;;
    804   (ds) ble/widget/vim-surround.sh/nmap/dsurround ;;
    805   (cs) ble/widget/vim-surround.sh/nmap/csurround ;;
    806   (cS) ble/widget/vim-surround.sh/nmap/cSurround ;;
    807   (*) ble/widget/.bell ;;
    808   esac
    809 }
    810 
    811 ble-bind -m vi_xmap -f 'S'   vim-surround.sh/vsurround
    812 ble-bind -m vi_xmap -f 'g S' vim-surround.sh/vgsurround
    813 
    814 if [[ $bleopt_vim_surround_omap_bind ]]; then
    815   ble-bind -m vi_omap -f s 'vim-surround.sh/omap'
    816   ble-bind -m vi_omap -f S 'vim-surround.sh/omap'
    817 else
    818   ble-bind -m vi_nmap -f 'y s'   'vi-command/operator ys'
    819   ble-bind -m vi_nmap -f 'y s s' 'vim-surround.sh/ysurround-current-line'
    820   ble-bind -m vi_nmap -f 'y S'   'vi-command/operator yS'
    821   ble-bind -m vi_nmap -f 'y S s' 'vim-surround.sh/ySurround-current-line'
    822   ble-bind -m vi_nmap -f 'y S S' 'vim-surround.sh/ySurround-current-line'
    823   ble-bind -m vi_nmap -f 'd s' 'vim-surround.sh/nmap/dsurround'
    824   ble-bind -m vi_nmap -f 'c s' 'vim-surround.sh/nmap/csurround'
    825   ble-bind -m vi_nmap -f 'c S' 'vim-surround.sh/nmap/cSurround'
    826 fi