canvas.sh (115499B)
1 #!/bin/bash 2 3 ## @bleopt tab_width 4 ## タブの表示幅を指定します。 5 ## 6 ## bleopt_tab_width= (既定) 7 ## 空文字列を指定したときは $(tput it) を用います。 8 ## bleopt_tab_width=NUM 9 ## 数字を指定したときはその値をタブの幅として用います。 10 bleopt/declare -v tab_width '' 11 function bleopt/check:tab_width { 12 if [[ $value ]] && (((value=value)<=0)); then 13 ble/util/print "bleopt: an empty string or a positive value is required for tab_width." >&2 14 return 1 15 fi 16 } 17 18 #------------------------------------------------------------------------------ 19 # ble/arithmetic 20 21 ## ble/arithmetic/sum integer... 22 ## @var[out] ret 23 function ble/arithmetic/sum { 24 IFS=+ builtin eval 'let "ret=$*+0"' 25 } 26 27 #------------------------------------------------------------------------------ 28 # ble/util/c2w 29 30 # ※注意 [ -~] の範囲の文字は全て幅1であるという事を仮定したコードが幾らかある 31 # もしこれらの範囲の文字を幅1以外で表示する端末が有ればそれらのコードを実装し 32 # 直す必要がある。その様な変な端末があるとは思えないが。 33 34 _ble_util_c2w=() 35 _ble_util_c2w_cache=() 36 function ble/util/c2w/clear-cache { 37 _ble_util_c2w_cache=() 38 } 39 40 ## @bleopt char_width_mode 41 ## 文字の表示幅の計算方法を指定します。 42 ## bleopt_char_width_mode=east 43 ## Unicode East_Asian_Width=A (Ambiguous) の文字幅を全て 2 とします 44 ## bleopt_char_width_mode=west 45 ## Unicode East_Asian_Width=A (Ambiguous) の文字幅を全て 1 とします 46 ## bleopt_char_width_mode=auto 47 ## east または west を自動判定します。 48 ## bleopt_char_width_mode=emacs 49 ## emacs で用いられている既定の文字幅の設定です 50 ## 定義 ble/util/c2w:$bleopt_char_width_mode 51 bleopt/declare -n char_width_mode auto 52 function bleopt/check:char_width_mode { 53 if ! ble/is-function "ble/util/c2w:$value"; then 54 ble/util/print "bleopt: Invalid value char_width_mode='$value'. A function 'ble/util/c2w:$value' is not defined." >&2 55 return 1 56 fi 57 58 case $value in 59 (auto) 60 _ble_unicode_c2w_ambiguous=1 61 ble && ble/util/c2w:auto/test.buff first-line ;; 62 (west) _ble_unicode_c2w_ambiguous=1 ;; 63 (east) _ble_unicode_c2w_ambiguous=2 ;; 64 esac 65 ((_ble_prompt_version++)) 66 ble/util/c2w/clear-cache 67 } 68 69 ## @fn ble/util/c2w ccode 70 ## @var[out] ret 71 function ble/util/c2w { 72 ret=${_ble_util_c2w_cache[$1]:-${_ble_util_c2w[$1]}} 73 if [[ ! $ret ]]; then 74 "ble/util/c2w:$bleopt_char_width_mode" "$1" 75 _ble_util_c2w_cache[$1]=$ret 76 fi 77 } 78 ## @fn ble/util/c2w-edit ccode 79 ## 編集画面での表示上の文字幅を返します。 80 ## @var[out] ret 81 function ble/util/c2w-edit { 82 local cs=${_ble_unicode_GraphemeCluster_ControlRepresentation[$1]} 83 if [[ $cs ]]; then 84 ret=${#cs} 85 elif (($1<32||127<=$1&&$1<160)); then 86 # 制御文字は ^? と表示される。 87 ret=2 88 # TAB は??? 89 90 # 128-159: M-^? 91 ((128<=$1&&(ret=4))) 92 else 93 ble/util/c2w "$1" 94 fi 95 } 96 ## @fn ble/util/s2w text 97 ## @fn ble/util/s2w-edit text [opts] 98 ## @param[in] text 99 ## @var[out] ret 100 function ble/util/s2w-edit { 101 local text=$1 iN=${#1} flags=$2 i 102 ret=0 103 for ((i=0;i<iN;i++)); do 104 local c w cs cb extend 105 ble/unicode/GraphemeCluster/match "$text" "$i" "$flags" 106 ((ret+=w,i+=extend)) 107 done 108 } 109 function ble/util/s2w { 110 ble/util/s2w-edit "$1" R 111 } 112 113 # ---- 文字種判定 ---- 114 115 #%< canvas.c2w.sh 116 _ble_unicode_c2w_version=14 117 _ble_unicode_c2w_ambiguous=1 118 _ble_unicode_c2w_invalid=1 119 _ble_unicode_c2w_custom=() 120 121 bleopt/declare -n char_width_version auto 122 function bleopt/check:char_width_version { 123 if [[ $value == auto ]]; then 124 ble && ble/util/c2w:auto/test.buff first-line 125 ((_ble_prompt_version++)) 126 ble/util/c2w/clear-cache 127 return 0 128 elif local ret; ble/unicode/c2w/version2index "$value"; then 129 _ble_unicode_c2w_version=$ret 130 ((_ble_prompt_version++)) 131 ble/util/c2w/clear-cache 132 return 0 133 else 134 ble/util/print "bleopt: char_width_version: invalid value '$value'." >&2 135 return 1 136 fi 137 } 138 139 # wcwdith 例外 (Unicode 特性からは予想できない値を持っている物) 140 # この表は make/canvas.c2w.wcwidth.exe compare_eaw の出力より。 141 _ble_unicode_c2w_custom[173]=1 # U+00ad Cf A SHY(soft-hyphen) 142 let '_ble_unicode_c2w_custom['{1536..1541}']=1' # U+0600..0605 Cf 1 アラブの数字? 143 _ble_unicode_c2w_custom[1757]=1 # U+06dd Cf 1 ARABIC END OF AYAH 144 _ble_unicode_c2w_custom[1807]=1 # U+070f Cf 1 SYRIAC ABBREVIATION MARK 145 _ble_unicode_c2w_custom[2274]=1 # U+08e2 Cf 1 ARABIC DISPUTED END OF AYAH 146 _ble_unicode_c2w_custom[69821]=1 # U+110bd Cf 1 KAITHI NUMBER SIGN 147 _ble_unicode_c2w_custom[69837]=1 # U+110cd Cf 1 KAITHI NUMBER SIGN ABOVE 148 let '_ble_unicode_c2w_custom['{12872..12879}']=2' # U+3248..324f No A 囲み文字10-80 (8字) 149 let '_ble_unicode_c2w_custom['{19904..19967}']=2' # U+4dc0..4dff So 1 易経記号 (6字) 150 let '_ble_unicode_c2w_custom['{4448..4607}']=0' # U+1160..11ff Lo 1 HANGUL JAMO (160字) 151 let '_ble_unicode_c2w_custom['{55216..55238}']=0' # U+d7b0..d7c6 Lo 1 HANGUL JAMO EXTENDED-B (1) (23字) 152 let '_ble_unicode_c2w_custom['{55243..55291}']=0' # U+d7cb..d7fb Lo 1 HANGUL JAMO EXTENDED-B (2) (49字) 153 154 function ble/unicode/c2w { 155 local c=$1 156 ret=${_ble_unicode_c2w_custom[c]} 157 [[ $ret ]] && return 0 158 159 ret=${_ble_unicode_c2w[c]} 160 if [[ ! $ret ]]; then 161 ret=${_ble_unicode_c2w_index[c<0x20000?c>>8:((c>>12)-32+512)]} 162 if [[ $ret == *:* ]]; then 163 local l=${ret%:*} u=${ret#*:} m 164 while ((l+1<u)); do 165 ((m=(l+u)/2)) 166 if ((_ble_unicode_c2w_ranges[m]<=c)); then 167 l=$m 168 else 169 u=$m 170 fi 171 done 172 ret=${_ble_unicode_c2w[_ble_unicode_c2w_ranges[l]]} 173 fi 174 fi 175 ret=${_ble_unicode_c2w_UnicodeVersionMapping[ret*_ble_unicode_c2w_UnicodeVersionCount+_ble_unicode_c2w_version]} 176 ((ret<0)) && ret=${_ble_unicode_c2w_invalid:-$((-ret))} 177 ((ret==3)) && 178 ret=${_ble_unicode_c2w_ambiguous:-1} 179 return 0 180 } 181 182 183 ## @const _ble_unicode_EmojiStatus_* 184 ## 185 ## @var _ble_unicode_EmojiStatus_xmaybe 186 ## @arr _ble_unicode_EmojiStatus 187 ## @arr _ble_unicode_EmojiStatus_ranges 188 ## @var _ble_unicode_EmojiStatus_version 189 ## @bleopt emoji_version 190 ## 191 ## ファイル src/canvas.emoji.sh は以下のコマンドで生成する。 192 ## $ ./make_command.sh update-emoji-database 193 ## 194 #%< canvas.emoji.sh 195 196 bleopt/declare -v emoji_width 2 197 bleopt/declare -v emoji_opts ri 198 199 function bleopt/check:emoji_version { 200 local ret 201 if ! ble/unicode/EmojiStatus/version2index "$value"; then 202 local rex='^0*([0-9]+)\.0*([0-9]+)$' 203 if ! [[ $value =~ $rex ]]; then 204 ble/util/print "bleopt: Invalid format for emoji_version: '$value'." >&2 205 return 1 206 else 207 ble/util/print "bleopt: Unsupported emoji_version: '$value'." >&2 208 return 1 209 fi 210 fi 211 212 _ble_unicode_EmojiStatus_version=$ret 213 ((_ble_prompt_version++)) 214 ble/util/c2w/clear-cache 215 return 0 216 } 217 function bleopt/check:emoji_width { ble/util/c2w/clear-cache; } 218 219 # 2021-06-18 unqualified は絵文字に含めない。多くの場合は既定では通常文字で 220 # EPVS によって絵文字として表示する様である。component は肌の色(Extend) と髪 221 # (Pictographic) の2種類がある。取り敢えず幅2で計算する。 222 _ble_unicode_EmojiStatus_xIsEmoji='ret&&ret!=_ble_unicode_EmojiStatus_Unqualified' 223 function bleopt/check:emoji_opts { 224 _ble_unicode_EmojiStatus_xIsEmoji='ret' 225 [[ :$value: != *:unqualified:* ]] && 226 _ble_unicode_EmojiStatus_xIsEmoji=$_ble_unicode_EmojiStatus_xIsEmoji'&&ret!=_ble_unicode_EmojiStatus_Unqualified' 227 local rex=':min=U\+([0-9a-fA-F]+):' 228 [[ :$value: =~ $rex ]] && 229 _ble_unicode_EmojiStatus_xIsEmoji=$_ble_unicode_EmojiStatus_xIsEmoji'&&code>=0x'${BASH_REMATCH[1]} 230 ((_ble_prompt_version++)) 231 ble/util/c2w/clear-cache 232 return 0 233 } 234 235 function ble/unicode/EmojiStatus { 236 local code=$1 V=$_ble_unicode_EmojiStatus_version 237 ret=${_ble_unicode_EmojiStatus[code]} 238 if [[ ! $ret ]]; then 239 ret=$_ble_unicode_EmojiStatus_None 240 if ((_ble_unicode_EmojiStatus_xmaybe)); then 241 local l=0 u=${#_ble_unicode_EmojiStatus_ranges[@]} m 242 while ((l+1<u)); do 243 ((_ble_unicode_EmojiStatus_ranges[m=(l+u)/2]<=code?(l=m):(u=m))) 244 done 245 ret=${_ble_unicode_EmojiStatus[_ble_unicode_EmojiStatus_ranges[l]]:-0} 246 fi 247 _ble_unicode_EmojiStatus[code]=$ret 248 fi 249 ((ret=ret)) 250 return 0 251 } 252 253 ## @fn ble/util/c2w/is-emoji code 254 ## @param[in] code 255 function ble/util/c2w/is-emoji { 256 local code=$1 ret 257 ble/unicode/EmojiStatus "$code" 258 ((_ble_unicode_EmojiStatus_xIsEmoji)) 259 } 260 261 # ---- char_width_mode ---- 262 263 function ble/util/c2w:west { 264 if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then 265 ((ret=bleopt_emoji_width)) 266 else 267 ble/unicode/c2w "$1" 268 fi 269 } 270 271 function ble/util/c2w:east { 272 if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then 273 ((ret=bleopt_emoji_width)) 274 else 275 ble/unicode/c2w "$1" 276 fi 277 } 278 279 ## @fn ble/util/c2w:emacs 280 ## emacs-24.2.1 default char-width-table 281 ## @var[out] ret 282 _ble_util_c2w_emacs_wranges=( 283 162 164 167 169 172 173 176 178 180 181 182 183 215 216 247 248 272 273 276 279 284 280 282 284 286 288 290 293 295 304 305 306 308 315 316 515 516 534 535 545 546 285 555 556 608 618 656 660 722 723 724 725 768 769 770 772 775 777 779 780 785 787 286 794 795 797 801 805 806 807 813 814 815 820 822 829 830 850 851 864 866 870 872 287 874 876 898 900 902 904 933 934 959 960 1042 1043 1065 1067 1376 1396 1536 1540 1548 1549 288 1551 1553 1555 1557 1559 1561 1563 1566 1568 1569 1571 1574 1576 1577 1579 1581 1583 1585 1587 1589 289 1591 1593 1595 1597 1599 1600 1602 1603 1611 1612 1696 1698 1714 1716 1724 1726 1734 1736 1739 1740 290 1742 1744 1775 1776 1797 1799 1856 1857 1858 1859 1898 1899 1901 1902 1903 1904) 291 292 function ble/util/c2w:emacs { 293 local code=$1 294 295 # bash-4.0 bug workaround 296 # 中で使用している変数に日本語などの文字列が入っているとエラーになる。 297 # その値を参照していなくても、その分岐に入らなくても関係ない。 298 # なので ret に予め適当な値を設定しておく事にする。 299 ret=1 300 ((code<0xA0)) && return 0 301 302 if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$code"; then 303 ((ret=bleopt_emoji_width)) 304 return 0 305 fi 306 307 # Note: ble/unicode/c2w を使うとずれる。考えてみれば emacs は各端末 308 # で同じテーブルを使って実装しているので ble/unicode/c2w 等外部の物 309 # を参照せずに実装するべきなのであった。 310 #ble/unicode/c2w "$1" 311 #((ret==3)) || return 0 312 313 # 実は EastAsianWidth=A だけ考えれば良いので下の条件式は単純化できる筈 314 local al=0 ah=0 tIndex= 315 (( 316 0x3100<=code&&code<0xA4D0||0xAC00<=code&&code<0xD7A4?( 317 ret=2 318 ):(0x2000<=code&&code<0x2700?( 319 tIndex=0x0100+code-0x2000 320 ):( 321 al=code&0xFF, 322 ah=code/256, 323 ah==0x00?( 324 tIndex=al 325 ):(ah==0x03?( 326 ret=0xFF&((al-0x91)&~0x20), 327 ret=ret<25&&ret!=17?2:1 328 ):(ah==0x04?( 329 ret=al==1||0x10<=al&&al<=0x50||al==0x51?2:1 330 ):(ah==0x11?( 331 ret=al<0x60?2:1 332 ):(ah==0x2e?( 333 ret=al>=0x80?2:1 334 ):(ah==0x2f?( 335 ret=2 336 ):(ah==0x30?( 337 ret=al!=0x3f?2:1 338 ):(ah==0xf9||ah==0xfa?( 339 ret=2 340 ):(ah==0xfe?( 341 ret=0x30<=al&&al<0x70?2:1 342 ):(ah==0xff?( 343 ret=0x01<=al&&al<0x61||0xE0<=al&&al<=0xE7?2:1 344 ):(ret=1)))))))))) 345 )) 346 )) 347 348 [[ $tIndex ]] || return 0 349 350 if ((tIndex<_ble_util_c2w_emacs_wranges[0])); then 351 ret=1 352 return 0 353 fi 354 355 local l=0 u=${#_ble_util_c2w_emacs_wranges[@]} m 356 while ((l+1<u)); do 357 ((_ble_util_c2w_emacs_wranges[m=(l+u)/2]<=tIndex?(l=m):(u=m))) 358 done 359 ((ret=((l&1)==0)?2:1)) 360 return 0 361 } 362 363 #%< canvas.c2w.musl.sh 364 365 function ble/util/c2w:musl { 366 local code=$1 367 368 ret=1 369 ((code&&code<0x300)) && return 0 370 371 if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$code"; then 372 ((ret=bleopt_emoji_width)) 373 return 0 374 fi 375 376 local l=0 u=${#_ble_util_c2w_musl_ranges[@]} m 377 while ((l+1<u)); do 378 ((_ble_util_c2w_musl_ranges[m=(l+u)/2]<=code?(l=m):(u=m))) 379 done 380 ret=${_ble_util_c2w_musl[_ble_util_c2w_musl_ranges[l]]} 381 } 382 383 _ble_util_c2w_auto_update_x0=0 384 _ble_util_c2w_auto_update_result=() 385 _ble_util_c2w_auto_update_processing=0 386 function ble/util/c2w:auto { 387 if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then 388 ((ret=bleopt_emoji_width)) 389 else 390 ble/unicode/c2w "$1" 391 fi 392 } 393 394 function ble/util/c2w:auto/check { 395 [[ $bleopt_char_width_mode == auto || $bleopt_char_width_version == auto ]] && 396 ble/util/c2w:auto/test.buff 397 return 0 398 } 399 400 function ble/util/c2w:auto/test.buff { 401 local opts=$1 402 local -a DRAW_BUFF=() 403 local ret saved_pos= 404 405 # 現在既に処理中の場合 DSR は省略。char_width_@=auto 等で一括して要 406 # 求した時などに一回だけ実行する為。 407 ((_ble_util_c2w_auto_update_processing)) && return 0 408 409 [[ $_ble_attached ]] && { ble/canvas/panel/save-position goto-top-dock; saved_pos=$ret; } 410 ble/canvas/put.draw "$_ble_term_sc" 411 if ble/util/is-unicode-output; then 412 413 local -a codes=( 414 # index=0,1 [EastAsianWidth=A 判定] 415 0x25bd 0x25b6 416 417 # index=2..15 [Unicode version 判定] #D1645 #D1668 418 # 判定用の文字コードは "source 419 # make/canvas.c2w.list-ucsver-detection-codes.sh" を用いて生 420 # 成されたリストから選択した。新しい Unicode version が出たら 421 # 再びこれを実行して判定コードを書く事になる。 422 0x9FBC 0x9FC4 0x31B8 0xD7B0 0x3099 423 0x9FCD 0x1F93B 0x312E 0x312F 0x16FE2 424 0x32FF 0x31BB 0x9FFD 0x1B132) 425 426 _ble_util_c2w_auto_update_processing=${#codes[@]} 427 _ble_util_c2w_auto_update_result=() 428 if [[ :$opts: == *:first-line:* ]]; then 429 # 画面の右上で判定を行います。 430 local cols=${COLUMNS:-80} 431 local x0=$((cols-4)); ((x0<0)) && x0=0 432 _ble_util_c2w_auto_update_x0=$x0 433 434 local code index=0 435 for code in "${codes[@]}"; do 436 ble/canvas/put-cup.draw 1 "$((x0+1))" 437 ble/canvas/put.draw "$_ble_term_el" 438 ble/util/c2s "$((code))" 439 ble/canvas/put.draw "$ret" 440 ble/term/CPR/request.draw "ble/util/c2w/test.hook $((index++))" 441 done 442 ble/canvas/put-cup.draw 1 "$((x0+1))" 443 ble/canvas/put.draw "$_ble_term_el" 444 else 445 _ble_util_c2w_auto_update_x0=2 446 local code index=0 447 for code in "${codes[@]}"; do 448 ble/util/c2s "$((code))" 449 ble/canvas/put.draw "$_ble_term_cr$_ble_term_el[$ret]" 450 ble/term/CPR/request.draw "ble/util/c2w/test.hook $((index++))" 451 done 452 ble/canvas/put.draw "$_ble_term_cr$_ble_term_el" 453 fi 454 fi 455 ble/canvas/put.draw "$_ble_term_rc" 456 [[ $_ble_attached ]] && ble/canvas/panel/load-position.draw "$saved_pos" 457 ble/canvas/bflush.draw 458 } 459 function ble/util/c2w/test.hook { 460 local index=$1 l=$2 c=$3 461 local w=$((c-1-_ble_util_c2w_auto_update_x0)) 462 _ble_util_c2w_auto_update_result[index]=$w 463 ((index==_ble_util_c2w_auto_update_processing-1)) || return 0 464 _ble_util_c2w_auto_update_processing=0 465 466 local ws 467 if [[ $bleopt_char_width_version == auto ]]; then 468 ws=("${_ble_util_c2w_auto_update_result[@]:2}") 469 if ((ws[13]==2)); then 470 bleopt char_width_version=15.0 471 elif ((ws[11]==2)); then 472 if ((ws[12]==2)); then 473 bleopt char_width_version=14.0 474 else 475 bleopt char_width_version=13.0 476 fi 477 elif ((ws[10]==2)); then 478 bleopt char_width_version=12.1 479 elif ((ws[9]==2)); then 480 bleopt char_width_version=12.0 481 elif ((ws[8]==2)); then 482 bleopt char_width_version=11.0 483 elif ((ws[7]==2)); then 484 bleopt char_width_version=10.0 485 elif ((ws[6]==2)); then 486 bleopt char_width_version=9.0 487 elif ((ws[4]==0)); then 488 if ((ws[5]==2)); then 489 bleopt char_width_version=8.0 490 else 491 bleopt char_width_version=7.0 492 fi 493 elif ((ws[3]==1&&ws[1]==2)); then 494 bleopt char_width_version=6.3 # or 6.2 495 elif ((ws[2]==2)); then 496 bleopt char_width_version=6.1 # or 6.0 497 elif ((ws[1]==2)); then 498 bleopt char_width_version=5.2 499 elif ((ws[0]==2)); then 500 bleopt char_width_version=5.0 501 else 502 bleopt char_width_version=4.1 503 fi 504 fi 505 506 # 先に char_width_version を確定してから musl の判定でそれを参照する。 507 if [[ $bleopt_char_width_mode == auto ]]; then 508 IFS=: builtin eval 'ws="${_ble_util_c2w_auto_update_result[*]::2}:${_ble_util_c2w_auto_update_result[*]:5:2}"' 509 case $ws in 510 (2:2:*:*) bleopt char_width_mode=east ;; 511 (2:1:*:*) bleopt char_width_mode=emacs ;; 512 (1:1:2:0) 513 if [[ $bleopt_char_width_version == 10.0 ]]; then 514 bleopt char_width_mode=musl 515 else 516 bleopt char_width_mode=west 517 fi ;; 518 (*) bleopt char_width_mode=west ;; 519 esac 520 fi 521 522 return 0 523 } 524 525 bleopt/declare -v grapheme_cluster extended 526 function bleopt/check:grapheme_cluster { 527 case $value in 528 (extended|legacy|'') return 0 ;; 529 (*) 530 ble/util/print "bleopt: invalid value for grapheme_cluster: '$value'." >&2 531 return 1 ;; 532 esac 533 } 534 535 #%< canvas.GraphemeClusterBreak.sh 536 537 # Note #D2076: 多くの端末 (glibc の wcwidth/wcswidth を参照している端末) で以下 538 # の文字は Unicode とは違う振る舞いで実装されている。kitty 及び RLogin では独自 539 # に Unicode に従って実装している様だが、取り敢えずは大勢に合わせて 540 # GraphemeClusterBreak を補正する。 541 _ble_unicode_GraphemeClusterBreak_custom[0x1F3FB]=$_ble_unicode_GraphemeClusterBreak_Pictographic 542 _ble_unicode_GraphemeClusterBreak_custom[0x1F3FC]=$_ble_unicode_GraphemeClusterBreak_Pictographic 543 _ble_unicode_GraphemeClusterBreak_custom[0x1F3FD]=$_ble_unicode_GraphemeClusterBreak_Pictographic 544 _ble_unicode_GraphemeClusterBreak_custom[0x1F3FE]=$_ble_unicode_GraphemeClusterBreak_Pictographic 545 _ble_unicode_GraphemeClusterBreak_custom[0x1F3FF]=$_ble_unicode_GraphemeClusterBreak_Pictographic 546 547 # Note #D2076: 半角カナの濁点と半濁点は Extended Lm だが、端末上の振る舞いは独 548 # 立した文字として振る舞っている (xterm, lxterminal, terminology, kitty)。 549 _ble_unicode_GraphemeClusterBreak_custom[0xFF9E]=$_ble_unicode_GraphemeClusterBreak_Other 550 _ble_unicode_GraphemeClusterBreak_custom[0xFF9F]=$_ble_unicode_GraphemeClusterBreak_Other 551 552 function ble/unicode/GraphemeCluster/c2break { 553 local code=$1 554 ret=${_ble_unicode_GraphemeClusterBreak_custom[code]} 555 [[ $ret ]] && return 0 556 ret=${_ble_unicode_GraphemeClusterBreak[code]} 557 [[ $ret ]] && return 0 558 ((ret>_ble_unicode_GraphemeClusterBreak_MaxCode)) && { ret=0; return 0; } 559 560 local l=0 u=${#_ble_unicode_GraphemeClusterBreak_ranges[@]} m 561 while ((l+1<u)); do 562 ((_ble_unicode_GraphemeClusterBreak_ranges[m=(l+u)/2]<=code?(l=m):(u=m))) 563 done 564 565 ret=${_ble_unicode_GraphemeClusterBreak[_ble_unicode_GraphemeClusterBreak_ranges[l]]:-0} 566 _ble_unicode_GraphemeClusterBreak[code]=$ret 567 return 0 568 } 569 570 _ble_unicode_GraphemeCluster_bomlen=1 571 _ble_unicode_GraphemeCluster_ucs4len=1 572 function ble/unicode/GraphemeCluster/s2break/.initialize { 573 local LC_ALL=C.UTF-8 574 builtin eval "local v1=\$'\\uFE0F' v2=\$'\\U1F6D1'" 575 _ble_unicode_GraphemeCluster_bomlen=${#v1} 576 _ble_unicode_GraphemeCluster_ucs4len=${#v2} 577 ble/util/unlocal LC_ALL 578 builtin unset -f "$FUNCNAME" 579 } 2>/dev/null # suppress locale error #D1440 580 ble/unicode/GraphemeCluster/s2break/.initialize 581 582 ## @fn ble/unicode/GraphemeCluster/s2break/.combine-surrogate code1 code2 str 583 ## @var[out] c 584 function ble/unicode/GraphemeCluster/s2break/.combine-surrogate { 585 local code1=$1 code2=$2 s=$3 586 if ((0xDC00<=code2&&code2<=0xDFFF)); then 587 ((c=0x10000+(code1-0xD800)*1024+(code2&0x3FF))) 588 else 589 local ret 590 ble/util/s2bytes "$s" 591 ble/encoding:UTF-8/b2c "${ret[@]}" 592 c=$ret 593 fi 594 } 595 ## @fn ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF code 596 ## (#D1881) Bash 4.3, 4.4 [sizeof(wchar_t) == 2] で $'\uE000'.. $'\uFFFF' が 597 ## 壊れたサロゲートになるバグに対する対策。この時、前半サロゲートは不正な値 598 ## U+D7F8..D7FF になるが、これはハングル字母などと被る。U+D7F8..D7FF の時は、 599 ## 次の文字が後半サロゲートの時に限り前半サロゲートとして取り扱う。 600 ## 601 ## @param[in] code 602 ## 壊れた前半サロゲータの可能性がある文字コード 603 ## @var[in,out] ret 604 ## 調整前後の GraphemeClusterBreak 値 605 ## @exit 606 ## 調整が行われた時に成功です (0)。それ以外の時は失敗 (1) です。 607 ## 608 if ((_ble_unicode_GraphemeCluster_bomlen==2&&40300<=_ble_bash&&_ble_bash<50000)); then 609 function ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF { 610 local code=$1 611 ((0xD7F8<=code&&code<0xD800)) && ble/util/is-unicode-output && 612 ret=$_ble_unicode_GraphemeClusterBreak_HighSurrogate 613 } 614 else 615 function ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF { ((0)); } 616 fi 617 ## @fn ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG code 618 ## (#D1881) Cygwin では UCS-2 に入らないコードポイントの後半サロゲートをs2cで 619 ## 取ろうとしても 0 になってしまう (Bash 5.0 以降では 4-byte UTF-8 の最後のバ 620 ## イト値) ので、後半について code == 0 の場合も前半サロゲートをチェックする。 621 ## 622 ## @param[in] code 623 ## UCS-4 の後半サロゲートの可能性がある文字コード 624 ## @var[in,out] ret 625 ## 調整前後の GraphemeClusterBreak 値 626 ## @exit 627 ## 調整が行われた時に成功です (0)。それ以外の時は失敗 (1) です。 628 ## 629 if ((_ble_unicode_GraphemeCluster_ucs4len==2)); then 630 if ((_ble_bash<50000)); then 631 function ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG { 632 local code=$1 633 ((code==0)) && ble/util/is-unicode-output && 634 ret=$_ble_unicode_GraphemeClusterBreak_LowSurrogate 635 } 636 else 637 function ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG { 638 local code=$1 639 ((0x80<=code&&code<0xC0)) && ble/util/is-unicode-output && 640 ret=$_ble_unicode_GraphemeClusterBreak_LowSurrogate 641 } 642 fi 643 else 644 function ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG { ((0)); } 645 fi 646 647 ## @fn ble/unicode/GraphemeCluster/s2break-left str index [opts] 648 ## @fn ble/unicode/GraphemeCluster/s2break-right str index [opts] 649 ## 指定した文字列の指定した境界の左右の code point の GraphemeCulsterBreak 値 650 ## を求めます。単に bash の文字単位ではなく、サロゲートペアも考慮に入れたコー 651 ## ドポイント単位で処理を行います。 652 ## 653 ## @param str 654 ## @param index 655 ## @param[opt] opts 656 ## @var[out] ret 657 ## GraphemeCulsterBreak 値を返します。 658 ## @var[out,opt] shift 659 ## opts に shift が指定された時に対象の code point の文字数を返します。 660 ## surrogate pair の時に 2 になります。それ以外の時は 1 です。 661 ## @var[out,opt] code 662 ## opts に code が指定された時に対象の code point を返します。 663 ## 664 ## * Note2 (#D1881): ${s:i-1:2} 等として 2 文字切り出すのは、Cygwin では 665 ## ${s:i-1:1} として最初の文字を切り出そうとすると UCS-2 に入らない code 666 ## point の文字が破壊されてしまって surrogate 前半すら取り出せなくなる為。少 667 ## なくとも wchar_t*2 の分だけ渡せば printf %d '$1 で surrogate 前半の code 668 ## point を取り出す事ができる。 669 function ble/unicode/GraphemeCluster/s2break-left { 670 ret=0 671 local s=$1 N=${#1} i=$2 opts=$3 sh=1 672 ((i>0)) && ble/util/s2c "${s:i-1:2}"; local c=$ret code2=$ret # Note2 (上述) 673 ble/unicode/GraphemeCluster/c2break "$code2"; local break=$ret 674 675 # process surrogate pairs 676 ((i-1<N)) && ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG "$code2" 677 if ((i-2>=0&&ret==_ble_unicode_GraphemeClusterBreak_LowSurrogate)); then 678 ble/util/s2c "${s:i-2:2}"; local code1=$ret # Note2 (上述) 679 ble/unicode/GraphemeCluster/c2break "$code1" 680 ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code1" 681 if ((ret==_ble_unicode_GraphemeClusterBreak_HighSurrogate)); then 682 ble/unicode/GraphemeCluster/s2break/.combine-surrogate "$code1" "$code2" "${s:i-2:2}" 683 ble/unicode/GraphemeCluster/c2break "$c" 684 break=$ret 685 sh=2 686 fi 687 elif ((i<N)) && ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code2"; then 688 # 壊れた前半サロゲートの可能性があるので次の文字を確認して break を確定する。 689 # (Note: 壊れたサロゲートペアの場合には UTF-8 4B 表現になる事はないので 690 # Cygwin で code_next==0 になる可能性は考えなくて良い。) 691 ble/util/s2c "${s:i:1}"; local code_next=$ret 692 ble/unicode/GraphemeCluster/c2break "$code_next" 693 ((ret==_ble_unicode_GraphemeClusterBreak_LowSurrogate)) && 694 break=$_ble_unicode_GraphemeClusterBreak_HighSurrogate 695 fi 696 697 [[ :$opts: == *:shift:* ]] && shift=$sh 698 [[ :$opts: == *:code:* ]] && code=$c 699 ret=$break 700 } 701 function ble/unicode/GraphemeCluster/s2break-right { 702 ret=0 703 local s=$1 N=${#1} i=$2 opts=$3 sh=1 704 ble/util/s2c "${s:i:2}"; local c=$ret code1=$ret # Note2 (上述) 705 ble/unicode/GraphemeCluster/c2break "$code1"; local break=$ret 706 707 # process surrogate pairs 708 ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code1" 709 if ((i+1<N&&ret==_ble_unicode_GraphemeClusterBreak_HighSurrogate)); then 710 ble/util/s2c "${s:i+1:1}"; local code2=$ret 711 ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG "$code2" || 712 ble/unicode/GraphemeCluster/c2break "$code2" 713 714 if ((ret==_ble_unicode_GraphemeClusterBreak_LowSurrogate)); then 715 ble/unicode/GraphemeCluster/s2break/.combine-surrogate "$code1" "$code2" "${s:i:2}" 716 ble/unicode/GraphemeCluster/c2break "$c" 717 break=$ret 718 sh=2 719 fi 720 elif ((0<i&&i<N)) && ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG "$code1"; then 721 # Note #D1881: Cygwin では UCS-2 に入らない code point の surrogate 後半を 722 # s2c で取ろうとしても 0 になってしまうので code1==0 の時は念入りに調べる。 723 # 前に HighSurrogate がない時は通常文字と同様に取り扱って問題ない。 724 ble/util/s2c "${s:i-1:1}"; local code_prev=$ret 725 ble/unicode/GraphemeCluster/c2break "$code_prev" 726 ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code_prev" 727 if ((ret==_ble_unicode_GraphemeClusterBreak_HighSurrogate)); then 728 break=$_ble_unicode_GraphemeClusterBreak_LowSurrogate 729 if [[ :$opts: == *:code:* ]]; then 730 ble/util/s2bytes "${s:i-1:2}" 731 ble/encoding:UTF-8/b2c "${ret[@]}" 732 ((c=0xDC00|ret&0x3FF)) 733 else 734 c=0 735 fi 736 fi 737 fi 738 739 [[ :$opts: == *:shift:* ]] && shift=$sh 740 [[ :$opts: == *:code:* ]] && code=$c 741 ret=$break 742 } 743 744 ## @fn ble/unicode/GraphemeCluster/find-previous-boundary/.ZWJ 745 ## @var[in] text i 746 ## @var[out] ret 747 function ble/unicode/GraphemeCluster/find-previous-boundary/.ZWJ { 748 if [[ :$bleopt_emoji_opts: != *:zwj:* ]]; then 749 ((ret=i)) 750 return 0 751 fi 752 753 local j=$((i-1)) shift=1 754 for ((j=i-1;j>0;j-=shift)); do 755 ble/unicode/GraphemeCluster/s2break-left "$text" "$j" shift 756 ((_ble_unicode_GraphemeClusterBreak_isExtend[ret])) || break 757 done 758 759 if ((j==0||ret!=_ble_unicode_GraphemeClusterBreak_Pictographic)); then 760 # sot | Extend* ZWJ | Pictographic 761 # [^Pictographic] | Extend* ZWJ | Pictographic 762 # ^--- j ^--- i 763 ((ret=i)) 764 return 0 765 else 766 # Pictographic | Extend* ZWJ | Pictographic 767 # ^--- j ^--- i 768 ((i=j-shift,b1=ret)) 769 return 1 770 fi 771 } 772 ## @fn ble/unicode/GraphemeCluster/find-previous-boundary/.RI 773 ## @var[in] text i shift 774 ## @var[out] ret 775 function ble/unicode/GraphemeCluster/find-previous-boundary/.RI { 776 if [[ :$bleopt_emoji_opts: != *:ri:* ]]; then 777 ((ret=i)) 778 return 0 779 fi 780 local j1=$((i-shift)) 781 local j shift=1 countRI=1 782 for ((j=j1;j>0;j-=shift,countRI++)); do 783 ble/unicode/GraphemeCluster/s2break-left "$text" "$j" shift 784 ((ret==_ble_unicode_GraphemeClusterBreak_Regional_Indicator)) || break 785 done 786 787 if ((j==j1)); then 788 ((i=j,b1=_ble_unicode_GraphemeClusterBreak_Regional_Indicator)) 789 return 1 790 else 791 ((ret=countRI%2==1?j1:i)) 792 return 0 793 fi 794 } 795 ## @fn ble/unicode/GraphemeCluster/find-previous-boundary/.InCB 796 ## @var[in] text 797 ## @var[in,out] i 798 ## 現在位置 i を指定します。Indic_Conjunct_Break を読み終わった新しい現在位 799 ## 置を返します。 800 ## @var[in] shift 801 ## 現在位置 i の左にある文字の UTF-8 文字数を指定します。通常は 1 です。未 802 ## 解決のサロゲートペアがある場合に 2 になります。 803 ## @var[in] b1 804 ## 現在位置 i の左側の GraphemeClusterBreak 値を指定します。 805 ## @var[out] ret 806 ## 境界が見つかった時に境界の位置を返します。 807 ## @remarks 808 ## shift 及び b1 は現在位置 i に於いて 809 ## ble/unicode/GraphemeCluster/s2break-left を呼び出した状態である事を前提 810 ## とします。 811 function ble/unicode/GraphemeCluster/find-previous-boundary/.InCB { 812 # Grapheme Cluster with InCB is supported by Unicode >= 15.1.0 813 if ((_ble_unicode_c2w_version<17)); then 814 ret=$i 815 return 0 816 fi 817 818 local out=$i j=$i count_linker=0 819 local b1=$b1 shift=$shift 820 while 821 case $b1 in 822 ("$_ble_unicode_GraphemeClusterBreak_InCB_Consonant") 823 if ((count_linker)); then 824 count_linker=0 825 ((out=j-shift)) 826 fi ;; 827 ("$_ble_unicode_GraphemeClusterBreak_InCB_Linker") 828 ((count_linker++)) ;; 829 ("$_ble_unicode_GraphemeClusterBreak_InCB_Extend"|"$_ble_unicode_GraphemeClusterBreak_ZWJ") ;; 830 (*) break ;; 831 esac 832 ((j-=shift,j>0)) 833 do 834 ble/unicode/GraphemeCluster/s2break-left "$text" "$j" shift 835 b1=$ret 836 done 837 838 if ((out<i)); then 839 i=$out 840 return 1 841 else 842 ret=$out 843 return 0 844 fi 845 } 846 function ble/unicode/GraphemeCluster/find-previous-boundary { 847 local text=$1 i=$2 shift 848 if [[ $bleopt_grapheme_cluster ]] && ((i&&--i)); then 849 ble/unicode/GraphemeCluster/s2break-right "$text" "$i" shift; local b1=$ret 850 while ((i>0)); do 851 local b2=$b1 852 ble/unicode/GraphemeCluster/s2break-left "$text" "$i" shift; local b1=$ret 853 case ${_ble_unicode_GraphemeClusterBreak_rule[b1*_ble_unicode_GraphemeClusterBreak_Count+b2]} in 854 (0) break ;; 855 (1) ((i-=shift)) ;; 856 (2) [[ $bleopt_grapheme_cluster != extended ]] && break; ((i-=shift)) ;; 857 (3) ble/unicode/GraphemeCluster/find-previous-boundary/.ZWJ && return 0 ;; 858 (4) ble/unicode/GraphemeCluster/find-previous-boundary/.RI && return 0 ;; 859 (6) ble/unicode/GraphemeCluster/find-previous-boundary/.InCB && return 0;; 860 (5) 861 # surrogate pair の間にいた時は GraphemeClusterBreak を取得し直す 862 ((i-=shift)) 863 ble/unicode/GraphemeCluster/s2break-right "$text" "$i"; b1=$ret ;; 864 esac 865 done 866 fi 867 ret=$i 868 return 0 869 } 870 871 _ble_unicode_GraphemeClusterBreak_isCore=() 872 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Other]=1 873 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Control]=1 874 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Regional_Indicator]=1 875 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_L]=1 876 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_V]=1 877 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_T]=1 878 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_LV]=1 879 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_LVT]=1 880 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Pictographic]=1 881 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_HighSurrogate]=1 882 _ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_InCB_Consonant]=1 883 884 _ble_unicode_GraphemeClusterBreak_isExtend=() 885 _ble_unicode_GraphemeClusterBreak_isExtend[_ble_unicode_GraphemeClusterBreak_Extend]=1 886 _ble_unicode_GraphemeClusterBreak_isExtend[_ble_unicode_GraphemeClusterBreak_InCB_Extend]=1 887 _ble_unicode_GraphemeClusterBreak_isExtend[_ble_unicode_GraphemeClusterBreak_InCB_Linker]=1 888 889 ## @fn ble/unicode/GraphemeCluster/extend-ascii text i 890 ## @var[out] extend 891 function ble/unicode/GraphemeCluster/extend-ascii { 892 extend=0 893 [[ $_ble_util_locale_encoding != UTF-8 || ! $bleopt_grapheme_cluster ]] && return 1 894 local text=$1 iN=${#1} i=$2 ret shift=1 895 for ((;i<iN;i+=shift,extend+=shift)); do 896 ble/unicode/GraphemeCluster/s2break-right "$text" "$i" shift 897 if ((!_ble_unicode_GraphemeClusterBreak_isExtend[ret])); then 898 case $ret in 899 ("$_ble_unicode_GraphemeClusterBreak_SpacingMark") 900 [[ $bleopt_grapheme_cluster == extended ]] || break ;; 901 (*) break ;; 902 esac 903 fi 904 done 905 ((extend)) 906 } 907 908 _ble_unicode_GraphemeCluster_ControlRepresentation=() 909 function ble/unicode/GraphemeCluster/.get-ascii-rep { 910 local c=$1 911 cs=${_ble_unicode_GraphemeCluster_ControlRepresentation[c]} 912 if [[ ! $cs ]]; then 913 if ((c<32)); then 914 ble/util/c2s "$((c+64))" 915 cs=^$ret 916 elif ((c==127)); then 917 cs=^? 918 elif ((128<=c&&c<160)); then 919 ble/util/c2s "$((c-64))" 920 cs=M-^$ret 921 else 922 ble/util/sprintf cs 'U+%X' "$c" 923 fi 924 _ble_unicode_GraphemeCluster_ControlRepresentation[c]=$cs 925 fi 926 } 927 928 ## @fn ble/unicode/GraphemeCluster/match text i flags 929 ## @param[in] text i 930 ## @param[in] flags 931 ## R が含まれている時制御文字を (ASCII 表現ではなく) そのまま cs に格納しま 932 ## す。幅は 0 で換算されます。 933 ## @var[out] c w cs cb extend 934 function ble/unicode/GraphemeCluster/match { 935 local text=$1 iN=${#1} i=$2 j=$2 flags=$3 ret 936 if ((i>=iN)); then 937 c=0 w=0 cs= cb= extend=0 938 return 1 939 elif ! ble/util/is-unicode-output || [[ ! $bleopt_grapheme_cluster ]]; then 940 cs=${text:i:1} 941 ble/util/s2c "$cs"; c=$ret 942 if [[ $flags != *R* ]] && { 943 ble/unicode/GraphemeCluster/c2break "$c" 944 ((ret==_ble_unicode_GraphemeClusterBreak_Control)); }; then 945 ble/unicode/GraphemeCluster/.get-ascii-rep "$c" 946 w=${#cs} 947 else 948 ble/util/c2w "$c"; w=$ret 949 fi 950 extend=0 951 return 0 952 fi 953 954 local b0 b1 b2 c0 c2 shift code 955 ble/unicode/GraphemeCluster/s2break-right "$text" "$i" code:shift; c0=$code b0=$ret 956 957 local coreb= corec= npre=0 vs= ri= InCB_state= 958 c2=$c0 b2=$b0 959 while ((j<iN)); do 960 if ((_ble_unicode_GraphemeClusterBreak_isCore[b2])); then 961 [[ $coreb ]] || coreb=$b2 corec=$c2 962 else 963 if ((b2==_ble_unicode_GraphemeClusterBreak_Prepend)); then 964 ((npre++)) 965 elif ((c2==0xFE0E)); then # Variation selector TPVS 966 vs=tpvs 967 elif ((c2==0xFE0F)); then # Variation selector EPVS 968 vs=epvs 969 fi 970 fi 971 972 # update InCB_state 973 if ((b2==_ble_unicode_GraphemeClusterBreak_InCB_Consonant)); then 974 InCB_state=0 975 elif [[ $InCB_state ]]; then 976 if ((b2==_ble_unicode_GraphemeClusterBreak_InCB_Linker)); then 977 InCB_state=1 978 elif ((b2!=_ble_unicode_GraphemeClusterBreak_InCB_Extend&&b2!=_ble_unicode_GraphemeClusterBreak_ZWJ)); then 979 InCB_state= 980 fi 981 fi 982 983 ((j+=shift)) 984 b1=$b2 985 ble/unicode/GraphemeCluster/s2break-right "$text" "$j" code:shift; c2=$code b2=$ret 986 case ${_ble_unicode_GraphemeClusterBreak_rule[b1*_ble_unicode_GraphemeClusterBreak_Count+b2]} in 987 (0) break ;; 988 (1) continue ;; 989 (2) [[ $bleopt_grapheme_cluster != extended ]] && break ;; 990 (3) [[ :$bleopt_emoji_opts: == *:zwj:* ]] && 991 ((coreb==_ble_unicode_GraphemeClusterBreak_Pictographic)) || break ;; 992 (4) [[ :$bleopt_emoji_opts: == *:ri:* && ! $ri ]] || break; ri=1 ;; 993 (6) ((_ble_unicode_c2w_version>=17&&InCB_state)) || break ;; 994 (5) 995 # surrogate pair の間にいた時は GraphemeClusterBreak を取得し直す 996 ble/unicode/GraphemeCluster/s2break-left "$text" "$((j+shift))" code; c2=$code b2=$ret ;; 997 esac 998 done 999 1000 c=$corec cb=$coreb cs=${text:i:j-i} 1001 ((extend=j-i-1)) 1002 if [[ ! $corec ]]; then 1003 if [[ $flags != *R* ]]; then 1004 ((c=c0,cb=0,corec=0x25CC)) # 基底が存在しない時は点線円 1005 ble/util/c2s "$corec" 1006 cs=${text:i:npre}$ret${text:i+npre:j-i-npre} 1007 else 1008 local code 1009 ble/unicode/GraphemeCluster/s2break-right "$cs" 0 code 1010 c=$code corec=$code cb=$ret 1011 fi 1012 fi 1013 1014 if ((cb==_ble_unicode_GraphemeClusterBreak_Control)); then 1015 if [[ $flags != *R* ]]; then 1016 ble/unicode/GraphemeCluster/.get-ascii-rep "$c" 1017 w=${#cs} 1018 else 1019 # ToDo: 全ての制御文字が幅0とは限らない。というより色々処理が必要。 1020 w=0 1021 fi 1022 1023 else 1024 # 幅の計算 (Variation Selector を考慮に入れる) 1025 if [[ $vs == tpvs && :$bleopt_emoji_opts: == *:tpvs:* ]]; then 1026 bleopt_emoji_width= ble/util/c2w "$corec"; w=$ret 1027 elif [[ $vs == epvs && :$bleopt_emoji_opts: == *:epvs:* ]]; then 1028 w=${bleopt_emoji_width:-2} 1029 else 1030 ble/util/c2w "$corec"; w=$ret 1031 fi 1032 fi 1033 1034 return 0 1035 } 1036 1037 #------------------------------------------------------------------------------ 1038 # ble/canvas/attach 1039 1040 function ble/canvas/attach { 1041 ble/util/c2w:auto/check 1042 } 1043 1044 #------------------------------------------------------------------------------ 1045 # ble/canvas 1046 1047 function ble/canvas/put.draw { 1048 DRAW_BUFF[${#DRAW_BUFF[*]}]=$1 1049 } 1050 function ble/canvas/put-ind.draw { 1051 local count=${1-1} ind=$_ble_term_ind 1052 [[ :$2: == *:true-ind:* ]] && ind=$'\eD' 1053 local ret; ble/string#repeat "$ind" "$count" 1054 DRAW_BUFF[${#DRAW_BUFF[*]}]=$ret 1055 } 1056 function ble/canvas/put-ri.draw { 1057 local count=${1-1} 1058 local ret; ble/string#repeat "$_ble_term_ri" "$count" 1059 DRAW_BUFF[${#DRAW_BUFF[*]}]=$ret 1060 } 1061 ## @fn ble/canvas/put-il.draw [nline] [opts] 1062 ## @fn ble/canvas/put-dl.draw [nline] [opts] 1063 ## @param[in,opt] nline 1064 ## 消去・挿入する行数を指定します。 1065 ## 省略した場合は 1 と解釈されます。 1066 ## @param[in,opt] opts 1067 ## panel 1068 ## vfill 1069 ## no-lastline 1070 ## Cygwin console 最終行バグ判定用の情報です。 1071 function ble/canvas/put-il.draw { 1072 local value=${1-1} 1073 ((value>0)) || return 0 1074 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_il//'%d'/$value} 1075 DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux 1076 } 1077 function ble/canvas/put-dl.draw { 1078 local value=${1-1} 1079 ((value>0)) || return 0 1080 DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux 1081 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_dl//'%d'/$value} 1082 } 1083 # Cygwin console (pcon) では最終行で IL/DL すると画面全体がクリアされるバグの対策 (#D1482) 1084 if ((_ble_bash>=40000)) && [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $TERM == xterm-256color ]]; then 1085 function ble/canvas/.is-il-workaround-required { 1086 local value=$1 opts=$2 1087 1088 # Cygwin console 以外の端末ではそもそも対策不要。 1089 [[ ! $_ble_term_DA2R ]] || return 1 1090 1091 # 複数行挿入・削除する場合は現在位置は最終行ではない筈。 1092 ((value==1)) || return 1 1093 1094 # 対策不要と明示されている場合は対策不要。 1095 [[ :$opts: == *:vfill:* || :$opts: == *:no-lastline:* ]] && return 1 1096 1097 # ble/canvas/panel 内部で移動中の時は opts=panel が指定される。 1098 # panel 集合の最終行にいない場合は対策不要。 1099 [[ :$opts: == *:panel:* ]] && 1100 ! ble/canvas/panel/is-last-line && 1101 return 1 1102 1103 return 0 1104 } 1105 1106 function ble/canvas/put-il.draw { 1107 local value=${1-1} opts=$2 1108 ((value>0)) || return 0 1109 if ble/canvas/.is-il-workaround-required "$value" "$2"; then 1110 if [[ :$opts: == *:panel:* ]]; then 1111 DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 1112 else 1113 DRAW_BUFF[${#DRAW_BUFF[*]}]=$'\e[S\e[A\e[L\e[B\e[T' 1114 fi 1115 else 1116 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_il//'%d'/$value} 1117 DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux 1118 fi 1119 } 1120 function ble/canvas/put-dl.draw { 1121 local value=${1-1} opts=$2 1122 ((value>0)) || return 0 1123 if ble/canvas/.is-il-workaround-required "$value" "$2"; then 1124 if [[ :$opts: == *:panel:* ]]; then 1125 DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 1126 else 1127 DRAW_BUFF[${#DRAW_BUFF[*]}]=$'\e[S\e[A\e[M\e[B\e[T' 1128 fi 1129 else 1130 DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux 1131 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_dl//'%d'/$value} 1132 fi 1133 } 1134 fi 1135 function ble/canvas/put-cuu.draw { 1136 local value=${1-1} 1137 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cuu//'%d'/$value} 1138 } 1139 function ble/canvas/put-cud.draw { 1140 local value=${1-1} 1141 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cud//'%d'/$value} 1142 } 1143 function ble/canvas/put-cuf.draw { 1144 local value=${1-1} 1145 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cuf//'%d'/$value} 1146 } 1147 function ble/canvas/put-cub.draw { 1148 local value=${1-1} 1149 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cub//'%d'/$value} 1150 } 1151 function ble/canvas/put-cup.draw { 1152 local l=${1-1} c=${2-1} 1153 local out=$_ble_term_cup 1154 out=${out//'%l'/$l} 1155 out=${out//'%c'/$c} 1156 out=${out//'%y'/$((l-1))} 1157 out=${out//'%x'/$((c-1))} 1158 DRAW_BUFF[${#DRAW_BUFF[*]}]=$out 1159 } 1160 function ble/canvas/put-hpa.draw { 1161 local c=${1-1} 1162 local out=$_ble_term_hpa 1163 out=${out//'%c'/$c} 1164 out=${out//'%x'/$((c-1))} 1165 DRAW_BUFF[${#DRAW_BUFF[*]}]=$out 1166 } 1167 function ble/canvas/put-vpa.draw { 1168 local l=${1-1} 1169 local out=$_ble_term_vpa 1170 out=${out//'%l'/$l} 1171 out=${out//'%y'/$((l-1))} 1172 DRAW_BUFF[${#DRAW_BUFF[*]}]=$out 1173 } 1174 function ble/canvas/put-ech.draw { 1175 local value=${1:-1} esc 1176 if [[ $_ble_term_ech ]]; then 1177 esc=${_ble_term_ech//'%d'/$value} 1178 else 1179 ble/string#reserve-prototype "$value" 1180 esc=${_ble_string_prototype::value}${_ble_term_cub//'%d'/$value} 1181 fi 1182 DRAW_BUFF[${#DRAW_BUFF[*]}]=$esc 1183 } 1184 function ble/canvas/put-spaces.draw { 1185 local value=${1:-1} 1186 ble/string#reserve-prototype "$value" 1187 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_string_prototype::value} 1188 } 1189 function ble/canvas/put-move-x.draw { 1190 local dx=$1 1191 ((dx)) || return 1 1192 if ((dx>0)); then 1193 ble/canvas/put-cuf.draw "$dx" 1194 else 1195 ble/canvas/put-cub.draw "$((-dx))" 1196 fi 1197 } 1198 function ble/canvas/put-move-y.draw { 1199 local dy=$1 1200 ((dy)) || return 1 1201 if ((dy>0)); then 1202 if [[ $MC_SID == $$ ]]; then 1203 # Note #D1392: mc (midnight commander) の中だと layout が破壊されるので、 1204 # 必ずしも CUD で想定した行だけ移動できると限らない。 1205 ble/canvas/put-ind.draw "$dy" true-ind 1206 else 1207 ble/canvas/put-cud.draw "$dy" 1208 fi 1209 else 1210 ble/canvas/put-cuu.draw "$((-dy))" 1211 fi 1212 } 1213 function ble/canvas/put-move.draw { 1214 ble/canvas/put-move-x.draw "$1" 1215 ble/canvas/put-move-y.draw "$2" 1216 } 1217 function ble/canvas/flush.draw { 1218 IFS= builtin eval 'ble/util/put "${DRAW_BUFF[*]}"' 1219 DRAW_BUFF=() 1220 } 1221 ## @fn ble/canvas/sflush.draw [-v var] 1222 ## @param[in] var 1223 ## 出力先の変数名を指定します。 1224 ## @var[out] !var 1225 function ble/canvas/sflush.draw { 1226 local _ble_local_var=ret 1227 [[ $1 == -v ]] && _ble_local_var=$2 1228 IFS= builtin eval "$_ble_local_var=\"\${DRAW_BUFF[*]}\"" 1229 DRAW_BUFF=() 1230 } 1231 function ble/canvas/bflush.draw { 1232 IFS= builtin eval 'ble/util/buffer "${DRAW_BUFF[*]}"' 1233 DRAW_BUFF=() 1234 } 1235 1236 ## @fn ble/canvas/put-clear-lines.draw [old] [new] [opts] 1237 ## @param[in,opt] old new 1238 ## 消去前と消去後の行数を指定します。 1239 ## old を省略した場合は 1 が使われます。 1240 ## new を省略した場合は old が使われます。 1241 ## @param[in,opt] opts 1242 ## panel 1243 ## vfill 1244 ## no-lastline 1245 function ble/canvas/put-clear-lines.draw { 1246 local old=${1:-1} 1247 local new=${2:-$old} 1248 if ((old==1&&new==1)); then 1249 ble/canvas/put.draw "$_ble_term_el2" 1250 else 1251 ble/canvas/put-dl.draw "$old" "$3" 1252 ble/canvas/put-il.draw "$new" "$3" 1253 fi 1254 } 1255 1256 #------------------------------------------------------------------------------ 1257 # ble/canvas/trace.draw 1258 # ble/canvas/trace 1259 1260 ## @fn ble/canvas/trace.draw text [opts] 1261 ## @fn ble/canvas/trace text [opts] 1262 ## 制御シーケンスを含む文字列を出力すると共にカーソル位置の移動を計算します。 1263 ## 1264 ## @param[in] text 1265 ## 出力する (制御シーケンスを含む) 文字列を指定します。 1266 ## 1267 ## @param[in,opt] opts 1268 ## コロン区切りのオプションの列を指定します。 1269 ## 1270 ## [配置制御] 1271 ## 1272 ## truncate 1273 ## LINES COLUMNS で指定される範囲外に出た時、処理を中断します。 1274 ## 1275 ## confine 1276 ## LINES COLUMNS の範囲外に文字出力・移動を行いません。 1277 ## 制御シーケンスにより範囲内に戻る可能性もあります。 1278 ## 1279 ## ellipsis 1280 ## LINES COLUMNS の範囲外に文字を出力しようとした時に、 1281 ## 三点リーダを末尾に上書きします。 1282 ## 1283 ## clip=X1xY1,X2xY2 1284 ## clip=XxY+WxH 1285 ## @param[in] X1 Y1 X2 Y2 1286 ## @param[in] X Y W H 1287 ## 指定した矩形範囲内の描画内容だけを抽出します。 1288 ## 矩形の左上の点が出力の描画開始点であると想定します。 1289 ## 1290 ## justify 1291 ## justify=SEPSPEC 1292 ## 横揃えを設定します。 1293 ## 1294 ## [範囲計測] 1295 ## 1296 ## measure-bbox 1297 ## @var[out] x1 x2 y1 y2 1298 ## カーソル移動範囲を x1 x2 y1 y2 に返します。 1299 ## measure-gbox 1300 ## @var[out] gx1 gx2 gy1 gy2 1301 ## 描画範囲を x1 x2 y1 y2 に返します。 1302 ## left-char 1303 ## @var[in,out] lc lg 1304 ## bleopt_internal_suppress_bash_output= の時、 1305 ## 出力開始時のカーソル左の文字コードを指定します。 1306 ## 出力終了時のカーソル左の文字コードが分かる場合にそれを返します。 1307 ## 1308 ## [出力制御機能] 1309 ## 1310 ## relative 1311 ## x y を相対位置と考えて移動を行います。 1312 ## 改行などの制御は全て座標に基づいた移動に変換されます。 1313 ## ansi 1314 ## ANSI制御シーケンスで出力を構築します。 1315 ## 後で trace で再解析を行う場合などに指定できます。 1316 ## g0 face0 1317 ## 背景色・既定属性として用いる属性値または描画設定を指定します。 1318 ## 両方指定された場合は g0 を優先させます。 1319 ## 1320 ## [その他] 1321 ## 1322 ## terminfo 1323 ## ANSI制御シーケンスではなく現在の端末のシーケンスとして 1324 ## 制御機能SGRを解釈します。 1325 ## 1326 ## @var[in,out] DRAW_BUFF[] 1327 ## ble/canvas/trace.draw の出力先の配列です。 1328 ## @var[out] ret 1329 ## ble/canvas/trace の結果の格納先の変数です。 1330 ## 1331 ## @var[in,out] x y g 1332 ## 出力の開始位置を指定します。出力終了時の位置を返します。 1333 ## 1334 ## 以下のシーケンスを認識します 1335 ## 1336 ## - Control Characters (C0 の文字 及び DEL) 1337 ## BS HT LF VT CR はカーソル位置の変更を行います。 1338 ## それ以外の文字はカーソル位置の変更は行いません。 1339 ## 1340 ## - CSI Sequence (Control Sequence) 1341 ## | CUU CSI A | CHB CSI Z | 1342 ## | CUD CSI B | HPR CSI a | 1343 ## | CUF CSI C | VPR CSI e | 1344 ## | CUB CSI D | HPA CSI ` | 1345 ## | CNL CSI E | VPA CSI d | 1346 ## | CPL CSI F | HVP CSI f | 1347 ## | CHA CSI G | SGR CSI m | 1348 ## | CUP CSI H | SCOSC CSI s | 1349 ## | CHT CSI I | SCORC CSI u | 1350 ## 上記のシーケンスはカーソル位置の計算に含め、 1351 ## また、端末 (TERM) に応じた出力を実施します。 1352 ## 上記以外のシーケンスはカーソル位置を変更しません。 1353 ## 1354 ## - SOS, DCS, SOS, PM, APC, ESC k ~ ESC \ 1355 ## - ISO-2022 に含まれる 3 byte 以上のシーケンス 1356 ## これらはそのまま通します。位置計算の考慮には入れません。 1357 ## 1358 ## - ESC Sequence 1359 ## DECSC DECRC IND RI NEL はカーソル位置の変更を行います。 1360 ## それ以外はカーソル位置の変更は行いません。 1361 ## 1362 ## 内部実装で用いている変数を整理する 1363 ## 1364 ## @var[local] xinit yinit ginit 1365 ## 初期カーソル状態を格納する。 1366 ## 1367 ## @var x1 x2 y1 y2 1368 ## これは measure-bbox または justify を指定した時に描画範囲を追跡するのに使っている。 1369 ## 1370 ## @var[local] cx cy cg 1371 ## clip 時に DRAW_BUFF 出力済みの内容のカーソル状態を追跡する変数。 1372 ## clip 時は x y g は仮想的に clip していない時のカーソル状態を追跡している。 1373 ## @var[local] cx1 cy1 cx2 cy2 1374 ## clip 範囲を保持する変数 1375 ## 1376 ## 1377 1378 function ble/canvas/trace/.put-sgr.draw { 1379 local ret g=$1 1380 if ((g==0)); then 1381 ble/canvas/put.draw "$opt_sgr0" 1382 else 1383 ble/color/g.compose "$opt_g0" "$g" 1384 "$trace_g2sgr" "$g" 1385 ble/canvas/put.draw "$ret" 1386 fi 1387 } 1388 1389 function ble/canvas/trace/.measure-point { 1390 if [[ $flag_bbox ]]; then 1391 ((x<x1?(x1=x):(x2<x&&(x2=x)))) 1392 ((y<y1?(y1=y):(y2<y&&(y2=y)))) 1393 fi 1394 } 1395 ## @fn ble/canvas/trace/.goto x1 y1 1396 ## @var[in,out] x y 1397 ## Note: lc lg の面倒は呼び出し元で見る。 1398 function ble/canvas/trace/.goto { 1399 local dstx=$1 dsty=$2 1400 if [[ ! $flag_clip ]]; then 1401 if [[ $trace_flags == *[RJ]* ]]; then 1402 ble/canvas/put-move.draw "$((dstx-x))" "$((dsty-y))" 1403 else 1404 ble/canvas/put-cup.draw "$((dsty+1))" "$((dstx+1))" 1405 fi 1406 fi 1407 ((x=dstx,y=dsty)) 1408 ble/canvas/trace/.measure-point 1409 } 1410 1411 function ble/canvas/trace/.implicit-move { 1412 local w=$1 type=$2 1413 # gbox は開始点と終了点を記録する。bbox の開始点は既に記録されている 1414 # 前提。終了点及び行折返しが発生した時の極値を此処で記録する。 1415 1416 ((w>0)) || return 0 1417 1418 if [[ $flag_gbox ]]; then 1419 if [[ ! $gx1 ]]; then 1420 ((gx1=gx2=x,gy1=gy2=y)) 1421 else 1422 ((x<gx1?(gx1=x):(gx2<x&&(gx2=x)))) 1423 ((y<gy1?(gy1=y):(gy2<y&&(gy2=y)))) 1424 fi 1425 fi 1426 ((x+=w)) 1427 1428 if ((x<=cols)); then 1429 # 行内に収まった時 1430 [[ $flag_bbox ]] && ((x>x2)) && x2=$x 1431 [[ $flag_gbox ]] && ((x>gx2)) && gx2=$x 1432 if ((x==cols&&!xenl)); then 1433 ((y++,x=0)) 1434 if [[ $flag_bbox ]]; then 1435 ((x<x1)) && x1=0 1436 ((y>y2)) && y2=$y 1437 fi 1438 fi 1439 else 1440 # 端末による折り返し 1441 if [[ $type == atomic ]]; then 1442 # [Note: 文字が横幅より大きい場合は取り敢えず次の行が一杯になると仮定して 1443 # いるが端末による。端末によっては更に次の行にカーソルが移動するのではな 1444 # いかとも思われる。] 1445 ((y++,x=w<xlimit?w:xlimit)) 1446 else 1447 ((y+=x/cols,x%=cols, 1448 xenl&&x==0&&(y--,x=cols))) 1449 fi 1450 if [[ $flag_bbox ]]; then 1451 ((x1>0&&(x1=0))) 1452 ((x2<cols&&(x2=cols))) 1453 ((y>y2)) && y2=$y 1454 fi 1455 if [[ $flag_gbox ]]; then 1456 ((gx1>0&&(gx1=0))) 1457 ((gx2<cols&&(gx2=cols))) 1458 ((y>gy2)) && gy2=$y 1459 fi 1460 fi 1461 ((x==0&&(lc=32,lg=0))) 1462 return 0 1463 } 1464 1465 function ble/canvas/trace/.put-atomic.draw { 1466 local c=$1 w=$2 1467 if [[ $flag_clip ]]; then 1468 ((cy1<=y&&y<cy2&&cx1<=x&&x<cx2&&x+w<=cx2)) || return 0 1469 if [[ $cg != "$g" ]]; then 1470 ble/canvas/trace/.put-sgr.draw "$g" 1471 cg=$g 1472 fi 1473 ble/canvas/put-move.draw "$((x-cx))" "$((y-cy))" 1474 ble/canvas/put.draw "$c" 1475 ((cx+=x+w,cy=y)) 1476 else 1477 ble/canvas/put.draw "$c" 1478 fi 1479 1480 ble/canvas/trace/.implicit-move "$w" atomic 1481 } 1482 function ble/canvas/trace/.put-ascii.draw { 1483 local value=$1 w=${#1} 1484 [[ $value ]] || return 0 1485 1486 if [[ $flag_clip ]]; then 1487 local xL=$x xR=$((x+w)) 1488 ((xR<=cx1||cx2<=xL||y+1<=cy1||cy2<=y)) && return 0 1489 if [[ $cg != "$g" ]]; then 1490 ble/canvas/trace/.put-sgr.draw "$g" 1491 cg=$g 1492 fi 1493 ((xL<cx1)) && value=${value:cx1-xL} xL=$cx1 1494 ((xR>cx2)) && value=${value::${#value}-(xR-cx2)} xR=$cx2 1495 ble/canvas/put-move.draw "$((x-cx))" "$((y-cy))" 1496 ble/canvas/put.draw "$value" 1497 ((cx=xR,cy=y)) 1498 else 1499 ble/canvas/put.draw "$value" 1500 fi 1501 1502 ble/canvas/trace/.implicit-move "$w" 1503 } 1504 function ble/canvas/trace/.process-overflow { 1505 [[ :$opts: == *:truncate:* ]] && i=$iN # stop 1506 if ((y+1==lines)) && [[ :$opts: == *:ellipsis:* ]]; then 1507 local ellipsis=... w=3 wmax=$xlimit 1508 ((w>wmax)) && ellipsis=${ellipsis::wmax} w=$wmax 1509 if ble/util/is-unicode-output; then 1510 local symbol='…' ret 1511 ble/util/s2c "$symbol" 1512 ble/util/c2w "$ret" 1513 ((ret<=wmax)) && ellipsis=$symbol w=$ret 1514 fi 1515 1516 local ox=$x oy=$y 1517 ble/canvas/trace/.goto "$((wmax-w))" "$((lines-1))" 1518 ble/canvas/trace/.put-atomic.draw "$ellipsis" "$w" 1519 ble/canvas/trace/.goto "$ox" "$oy" 1520 fi 1521 } 1522 1523 #-------------------------------------- 1524 ## (trace 内部変数) justify 関連 1525 ## 1526 ## @var[local] justify_sep 1527 ## @arr[local] justify_fields 1528 ## @arr[local] justify_buff 1529 ## @arr[local] justify_out 1530 ## @var[local] jx0 jy0 1531 ## 各フィールドの開始カーソル位置を保持する。 1532 ## @var[local] jx1 jy1 jx2 jy2 1533 ## measure-bbox も指定されていた時に、 1534 ## justify 後の描画範囲追跡に用いている。 1535 ## justify 処理中は x1 y1 x2 y2 は align 前のフィールドの描画範囲追跡に使っている。 1536 ## 関数の一番最後で jx1 jy1 jx2 jy2 で x1 y1 x2 y2 を上書きする。 1537 ## 1538 function ble/canvas/trace/.justify/inc-quote { 1539 [[ $trace_flags == *J* ]] || return 0 1540 ((trace_sclevel++)) 1541 flag_justify= 1542 } 1543 function ble/canvas/trace/.justify/dec-quote { 1544 [[ $trace_flags == *J* ]] || return 0 1545 ((--trace_sclevel)) || flag_justify=1 1546 } 1547 ## @fn ble/canvas/trace/.justify/begin-line 1548 ## @var[out] jx0 jy0 x1 y1 x2 y2 1549 function ble/canvas/trace/.justify/begin-line { 1550 ((jx0=x1=x2=x,jy0=y1=y2=y)) 1551 gx1= gx2= gy1= gy2= 1552 [[ $justify_align == *[cr]* ]] && 1553 ble/canvas/trace/.justify/next-field 1554 } 1555 ## @fn ble/canvas/trace/.justify/next-field [sep] 1556 ## @param[in,opt] sep 1557 ## 省略時は最後のフィールドを意味する。 1558 ## @var[out] jx0 jy0 x1 y1 x2 y2 1559 ## @var[in,out] DRAW_BUFF justify_fields 1560 function ble/canvas/trace/.justify/next-field { 1561 local sep=$1 wmin=0 1562 local esc; ble/canvas/sflush.draw -v esc 1563 [[ $sep == ' ' ]] && wmin=1 1564 ble/array#push justify_fields "${sep:-\$}:$wmin:$jx0,$jy0,$x,$y:$x1,$y1,$x2,$y2:$gx1,$gy1,$gx2,$gy2:$esc" 1565 ((x+=wmin,jx0=x1=x2=x,jy0=y1=y2=y)) 1566 } 1567 ## @fn ble/canvas/trace/.justify/unpack packed_data 1568 ## @var[out] sep wmin xI yI xF yF x1 y1 x2 y2 esc 1569 function ble/canvas/trace/.justify/unpack { 1570 local data=$1 buff 1571 sep=${data::1}; data=${data:2} 1572 wmin=${data%%:*}; data=${data#*:} 1573 ble/string#split buff , "${data%%:*}"; data=${data#*:} 1574 xI=${buff[0]} yI=${buff[1]} xF=${buff[2]} yF=${buff[3]} 1575 ble/string#split buff , "${data%%:*}"; data=${data#*:} 1576 x1=${buff[0]} y1=${buff[1]} x2=${buff[2]} y2=${buff[3]} 1577 ble/string#split buff , "${data%%:*}"; data=${data#*:} 1578 gx1=${buff[0]} gy1=${buff[1]} gx2=${buff[2]} gy2=${buff[3]} 1579 esc=$data 1580 } 1581 ## @fn ble/canvas/trace/.justify/end-line 1582 ## これまでに justify_fields に記録した各フィールドの esc を align しつつ結合 1583 ## する。 1584 ## @var[in,out] justify_fields DRAW_BUFF justify_buff 1585 function ble/canvas/trace/.justify/end-line { 1586 # Note: 行内容がなかった場合でも行の高さだけは記録する 1587 # (NEL で新しい行が形成される事に注意)。 1588 if [[ $trace_flags == *B* ]]; then 1589 ((y<jy1&&(jy1=y))) 1590 ((y>jy2&&(jy2=y))) 1591 fi 1592 ((${#justify_fields[@]}||${#DRAW_BUFF[@]})) || return 0 1593 1594 # 最後のフィールドを justify_fields に移動。 1595 ble/canvas/trace/.justify/next-field 1596 [[ $justify_align == *c* ]] && 1597 ble/canvas/trace/.justify/next-field 1598 1599 local i width=0 ispan=0 has_content= 1600 for ((i=0;i<${#justify_fields[@]};i++)); do 1601 local sep wmin xI yI xF yF x1 y1 x2 y2 gx1 gy1 gx2 gy2 esc 1602 ble/canvas/trace/.justify/unpack "${justify_fields[i]}" 1603 1604 ((width+=xF-xI)) 1605 [[ $esc ]] && has_content=1 1606 1607 # Note: 最後の要素の次には余白はない。 1608 ((i+1==${#justify_fields[@]})) && break 1609 1610 ((width+=wmin)) 1611 ((ispan++)) 1612 done 1613 if [[ ! $has_content ]]; then 1614 justify_fields=() 1615 return 0 1616 fi 1617 1618 local nspan=$ispan 1619 1620 local -a DRAW_BUFF=() 1621 1622 # fill に使える余白を計算する。 1623 # Note: _ble_term_xenl 及び opt_relative の時には本当の端末の右端には接触しな 1624 # いと想定して範囲の右端まで使用する。 1625 local xlimit=$cols 1626 [[ $_ble_term_xenl$opt_relative ]] || ((xlimit--)) 1627 local span=$((xlimit-width)) 1628 1629 x= y= 1630 local ispan=0 vx=0 spanx=0 1631 for ((i=0;i<${#justify_fields[@]};i++)); do 1632 local sep wmin xI yI xF yF x1 y1 x2 y2 gx1 gy1 gx2 gy2 esc 1633 ble/canvas/trace/.justify/unpack "${justify_fields[i]}" 1634 1635 if [[ ! $x ]]; then 1636 x=$xI y=$yI 1637 if [[ $justify_align == right ]]; then 1638 ble/canvas/put-move-x.draw "$((cols-1-x))" 1639 ((x=cols-1)) 1640 fi 1641 fi 1642 1643 if [[ $esc ]]; then 1644 local delta=0 1645 ((vx+x1-xI<0)) && ((delta=-(vx+x1-xI))) 1646 ((vx+x2-xI>xlimit)) && ((delta=xlimit-(vx+x2-xI))) 1647 ble/canvas/put-move-x.draw "$((vx+delta-x))" 1648 ((x=vx+delta)) 1649 ble/canvas/put.draw "$esc" 1650 if [[ $trace_flags == *B* ]]; then 1651 ((x+x1-xI<jx1&&(jx1=x+x1-xI))) 1652 ((y+y1-yI<jy1&&(jy1=y+y1-yI))) 1653 ((x+x2-xI>jx2&&(jx2=x+x2-xI))) 1654 ((y+y2-yI>jy2&&(jy2=y+y2-yI))) 1655 fi 1656 if [[ $flag_gbox && $gx1 ]]; then 1657 ((gx1+=x-xI,gx2+=x-xI)) 1658 ((gy1+=y-yI,gy2+=y-yI)) 1659 if [[ ! $jgx1 ]]; then 1660 ((jgx1=gx1,jgy1=gy1,jgx2=gx2,jgy2=gy2)) 1661 else 1662 ((gx1<jgx1&&(jgx1=gx1))) 1663 ((gy1<jgy1&&(jgy1=gy1))) 1664 ((gx2>jgx2&&(jgx2=gx2))) 1665 ((gy2>jgy2&&(jgy2=gy2))) 1666 fi 1667 fi 1668 ((x+=xF-xI,y+=yF-yI,vx+=xF-xI)) 1669 fi 1670 1671 ((i+1==${#justify_fields[@]})) && break 1672 1673 local new_spanx=$((span*++ispan/nspan)) 1674 local wfill=$((wmin+new_spanx-spanx)) 1675 ((vx+=wfill,spanx=new_spanx)) 1676 1677 # fillchar: 取り敢えず現在の実装では空白で fill 1678 if [[ $sep == ' ' ]]; then 1679 ble/string#reserve-prototype "$wfill" 1680 ble/canvas/put.draw "${_ble_string_prototype::wfill}" 1681 ((x+=wfill)) 1682 fi 1683 done 1684 1685 local ret 1686 ble/canvas/sflush.draw 1687 ble/array#push justify_buff "$ret" 1688 justify_fields=() 1689 } 1690 1691 #-------------------------------------- 1692 ## (trace 内部変数) sc/rc 関連 1693 ## 1694 ## @arr[local] trace_decsc 1695 ## @arr[local] trace_scosc 1696 ## @arr[local] trace_brack 1697 ## 1698 function ble/canvas/trace/.decsc { 1699 [[ ${trace_decsc[5]} ]] || ble/canvas/trace/.justify/inc-quote 1700 trace_decsc=("$x" "$y" "$g" "$lc" "$lg" active) 1701 if [[ ! $flag_clip ]]; then 1702 [[ :$opts: == *:noscrc:* ]] || 1703 ble/canvas/put.draw "$_ble_term_sc" 1704 fi 1705 } 1706 function ble/canvas/trace/.decrc { 1707 [[ ${trace_decsc[5]} ]] && ble/canvas/trace/.justify/dec-quote 1708 if [[ ! $flag_clip ]]; then 1709 ble/canvas/trace/.put-sgr.draw "${trace_decsc[2]}" # g を明示的に復元。 1710 if [[ :$opts: == *:noscrc:* ]]; then 1711 ble/canvas/put-move.draw "$((trace_decsc[0]-x))" "$((trace_decsc[1]-y))" 1712 else 1713 ble/canvas/put.draw "$_ble_term_rc" 1714 fi 1715 fi 1716 x=${trace_decsc[0]} 1717 y=${trace_decsc[1]} 1718 g=${trace_decsc[2]} 1719 lc=${trace_decsc[3]} 1720 lg=${trace_decsc[4]} 1721 trace_decsc[5]= 1722 } 1723 function ble/canvas/trace/.scosc { 1724 [[ ${trace_scosc[5]} ]] || ble/canvas/trace/.justify/inc-quote 1725 trace_scosc=("$x" "$y" "$g" "$lc" "$lg" active) 1726 if [[ ! $flag_clip ]]; then 1727 [[ :$opts: == *:noscrc:* ]] || 1728 ble/canvas/put.draw "$_ble_term_sc" 1729 fi 1730 } 1731 function ble/canvas/trace/.scorc { 1732 [[ ${trace_scosc[5]} ]] && ble/canvas/trace/.justify/dec-quote 1733 if [[ ! $flag_clip ]]; then 1734 ble/canvas/trace/.put-sgr.draw "$g" # g は変わらない様に。 1735 if [[ :$opts: == *:noscrc:* ]]; then 1736 ble/canvas/put-move.draw "$((trace_scosc[0]-x))" "$((trace_scosc[1]-y))" 1737 else 1738 ble/canvas/put.draw "$_ble_term_rc" 1739 fi 1740 fi 1741 x=${trace_scosc[0]} 1742 y=${trace_scosc[1]} 1743 lc=${trace_scosc[3]} 1744 lg=${trace_scosc[4]} 1745 trace_scosc[5]= 1746 } 1747 function ble/canvas/trace/.ps1sc { 1748 ble/canvas/trace/.justify/inc-quote 1749 trace_brack[${#trace_brack[*]}]="$x $y" 1750 } 1751 function ble/canvas/trace/.ps1rc { 1752 local lastIndex=$((${#trace_brack[*]}-1)) 1753 if ((lastIndex>=0)); then 1754 ble/canvas/trace/.justify/dec-quote 1755 local -a scosc 1756 ble/string#split-words scosc "${trace_brack[lastIndex]}" 1757 ((x=scosc[0])) 1758 ((y=scosc[1])) 1759 builtin unset -v "trace_brack[$lastIndex]" 1760 fi 1761 } 1762 1763 #-------------------------------------- 1764 function ble/canvas/trace/.NEL { 1765 if [[ $opt_nooverflow ]] && ((y+1>=lines)); then 1766 ble/canvas/trace/.process-overflow 1767 return 1 1768 fi 1769 1770 [[ $flag_justify ]] && 1771 ble/canvas/trace/.justify/end-line 1772 if [[ ! $flag_clip ]]; then 1773 if [[ $opt_relative ]]; then 1774 ((x)) && ble/canvas/put-cub.draw "$x" 1775 ble/canvas/put-cud.draw 1 1776 else 1777 ble/canvas/put.draw "$_ble_term_cr" 1778 ble/canvas/put.draw "$_ble_term_nl" 1779 fi 1780 fi 1781 ((y++,x=0,lc=32,lg=0)) 1782 if [[ $flag_bbox ]]; then 1783 ((x<x1)) && x1=$x 1784 ((y>y2)) && y2=$y 1785 fi 1786 [[ $flag_justify ]] && 1787 ble/canvas/trace/.justify/begin-line 1788 return 0 1789 } 1790 ## @fn ble/canvas/trace/.SGR 1791 ## @param[in] param seq 1792 ## @var[out] DRAW_BUFF 1793 ## @var[in,out] g 1794 function ble/canvas/trace/.SGR { 1795 local param=$1 seq=$2 specs i iN 1796 if [[ ! $param ]]; then 1797 g=0 1798 [[ $flag_clip ]] || ble/canvas/put.draw "$opt_sgr0" 1799 return 0 1800 fi 1801 1802 # update g 1803 if [[ $opt_terminfo ]]; then 1804 ble/color/read-sgrspec "$param" 1805 else 1806 ble/color/read-sgrspec "$param" ansi 1807 fi 1808 [[ $flag_clip ]] || ble/canvas/trace/.put-sgr.draw "$g" 1809 } 1810 function ble/canvas/trace/.process-csi-sequence { 1811 local seq=$1 seq1=${1:2} rex 1812 local char=${seq1:${#seq1}-1:1} param=${seq1::${#seq1}-1} 1813 if [[ ! ${param//[0-9:;]} ]]; then 1814 # CSI 数字引数 + 文字 1815 case $char in 1816 (m) # SGR 1817 ble/canvas/trace/.SGR "$param" "$seq" 1818 return 0 ;; 1819 ([ABCDEFGIZ\`ade]) 1820 local arg=0 1821 [[ $param =~ ^[0-9]+$ ]] && ((arg=10#0$param)) 1822 ((arg==0&&(arg=1))) 1823 1824 local ox=$x oy=$y 1825 if [[ $char == A ]]; then 1826 # CUU "CSI A" 1827 ((y-=arg,y<0&&(y=0))) 1828 ((!flag_clip&&y<oy)) && ble/canvas/put-cuu.draw "$((oy-y))" 1829 elif [[ $char == [Be] ]]; then 1830 # CUD "CSI B" 1831 # VPR "CSI e" 1832 ((y+=arg,y>=lines&&(y=lines-1))) 1833 ((!flag_clip&&y>oy)) && ble/canvas/put-cud.draw "$((y-oy))" 1834 elif [[ $char == [Ca] ]]; then 1835 # CUF "CSI C" 1836 # HPR "CSI a" 1837 ((x+=arg,x>=cols&&(x=cols-1))) 1838 ((!flag_clip&&x>ox)) && ble/canvas/put-cuf.draw "$((x-ox))" 1839 elif [[ $char == D ]]; then 1840 # CUB "CSI D" 1841 ((x-=arg,x<0&&(x=0))) 1842 ((!flag_clip&&x<ox)) && ble/canvas/put-cub.draw "$((ox-x))" 1843 elif [[ $char == E ]]; then 1844 # CNL "CSI E" 1845 ((y+=arg,y>=lines&&(y=lines-1),x=0)) 1846 if [[ ! $flag_clip ]]; then 1847 ((y>oy)) && ble/canvas/put-cud.draw "$((y-oy))" 1848 ble/canvas/put.draw "$_ble_term_cr" 1849 fi 1850 elif [[ $char == F ]]; then 1851 # CPL "CSI F" 1852 ((y-=arg,y<0&&(y=0),x=0)) 1853 if [[ ! $flag_clip ]]; then 1854 ((y<oy)) && ble/canvas/put-cuu.draw "$((oy-y))" 1855 ble/canvas/put.draw "$_ble_term_cr" 1856 fi 1857 elif [[ $char == [G\`] ]]; then 1858 # CHA "CSI G" 1859 # HPA "CSI `" 1860 ((x=arg-1,x<0&&(x=0),x>=cols&&(x=cols-1))) 1861 if [[ ! $flag_clip ]]; then 1862 if [[ $opt_relative ]]; then 1863 ble/canvas/put-move-x.draw "$((x-ox))" 1864 else 1865 ble/canvas/put-hpa.draw "$((x+1))" 1866 fi 1867 fi 1868 elif [[ $char == d ]]; then 1869 # VPA "CSI d" 1870 ((y=arg-1,y<0&&(y=0),y>=lines&&(y=lines-1))) 1871 if [[ ! $flag_clip ]]; then 1872 if [[ $opt_relative ]]; then 1873 ble/canvas/put-move-y.draw "$((y-oy))" 1874 else 1875 ble/canvas/put-vpa.draw "$((y+1))" 1876 fi 1877 fi 1878 elif [[ $char == I ]]; then 1879 # CHT "CSI I" 1880 local tx 1881 ((tx=(x/it+arg)*it, 1882 tx>=cols&&(tx=cols-1))) 1883 if ((tx>x)); then 1884 [[ $flag_clip ]] || ble/canvas/put-cuf.draw "$((tx-x))" 1885 ((x=tx)) 1886 fi 1887 elif [[ $char == Z ]]; then 1888 # CHB "CSI Z" 1889 local tx 1890 ((tx=((x+it-1)/it-arg)*it, 1891 tx<0&&(tx=0))) 1892 if ((tx<x)); then 1893 [[ $flag_clip ]] || ble/canvas/put-cub.draw "$((x-tx))" 1894 ((x=tx)) 1895 fi 1896 fi 1897 ble/canvas/trace/.measure-point 1898 lc=-1 lg=0 1899 return 0 ;; 1900 ([Hf]) 1901 # CUP "CSI H" 1902 # HVP "CSI f" 1903 local -a params 1904 ble/string#split-words params "${param//[^0-9]/ }" 1905 params=("${params[@]/#/10#0}") # WA #D1570 checked (is-array) 1906 local dstx dsty 1907 ((dstx=params[1]-1)) 1908 ((dsty=params[0]-1)) 1909 ((dstx<0&&(dstx=0),dstx>=cols&&(dstx=cols-1), 1910 dsty<0&&(dsty=0),dsty>=lines&&(dsty=lines-1))) 1911 ble/canvas/trace/.goto "$dstx" "$dsty" 1912 lc=-1 lg=0 1913 return 0 ;; 1914 ([su]) # SCOSC SCORC 1915 if [[ $char == s ]]; then 1916 ble/canvas/trace/.scosc 1917 else 1918 ble/canvas/trace/.scorc 1919 fi 1920 return 0 ;; 1921 # ■その他色々? 1922 # ([JPX@MKL]) # 挿入削除→カーソルの位置は不変 lc? 1923 # ([hl]) # SM RM DECSM DECRM 1924 esac 1925 fi 1926 1927 ble/canvas/put.draw "$seq" 1928 } 1929 function ble/canvas/trace/.process-esc-sequence { 1930 local seq=$1 char=${1:1} 1931 case $char in 1932 (7) # DECSC 1933 ble/canvas/trace/.decsc 1934 return 0 ;; 1935 (8) # DECRC 1936 ble/canvas/trace/.decrc 1937 return 0 ;; 1938 (D) # IND 1939 [[ $opt_nooverflow ]] && ((y+1>=lines)) && return 0 1940 if [[ $flag_clip || $opt_relative || $flag_justify ]]; then 1941 ((y+1>=lines)) && return 0 1942 ((y++)) 1943 [[ $flag_clip ]] || 1944 ble/canvas/put-cud.draw 1 1945 else 1946 ((y++)) 1947 ble/canvas/put.draw "$_ble_term_ind" 1948 [[ $_ble_term_ind != $'\eD' ]] && 1949 ble/canvas/put-hpa.draw "$((x+1))" # tput ind が唯の改行の時がある 1950 fi 1951 lc=-1 lg=0 1952 ble/canvas/trace/.measure-point 1953 return 0 ;; 1954 (M) # RI 1955 [[ $opt_nooverflow ]] && ((y==0)) && return 0 1956 if [[ $flag_clip || $opt_relative || $flag_justify ]]; then 1957 ((y==0)) && return 0 1958 ((y--)) 1959 [[ $flag_clip ]] || 1960 ble/canvas/put-cuu.draw 1 1961 else 1962 ((y--)) 1963 ble/canvas/put.draw "$_ble_term_ri" 1964 fi 1965 lc=-1 lg=0 1966 ble/canvas/trace/.measure-point 1967 return 0 ;; 1968 (E) # NEL 1969 ble/canvas/trace/.NEL 1970 return 0 ;; 1971 # (H) # HTS 面倒だから無視。 1972 # ([KL]) PLD PLU 1973 # 上付き・下付き文字 (端末における実装は色々) 1974 esac 1975 1976 ble/canvas/put.draw "$seq" 1977 } 1978 1979 function ble/canvas/trace/.impl { 1980 local text=$1 opts=$2 1981 1982 # cygwin では LC_COLLATE=C にしないと 1983 # 正規表現の range expression が期待通りに動かない。 1984 local LC_ALL= LC_COLLATE=C 1985 1986 # constants 1987 local cols=${COLUMNS:-80} lines=${LINES:-25} 1988 local it=${bleopt_tab_width:-$_ble_term_it} xenl=$_ble_term_xenl 1989 ble/string#reserve-prototype "$it" 1990 1991 # Note: 文字符号化方式によっては対応する文字が存在しない可能性がある。 1992 # その時は st='\u009C' になるはず。2文字以上のとき変換に失敗したと見做す。 1993 local ret rex 1994 ble/util/c2s 156; local st=$ret # (ST) 1995 ((${#st}>=2)) && st= 1996 1997 #------------------------------------- 1998 # Options 1999 2000 local xinit=$x yinit=$y ginit=$g 2001 2002 # @var trace_flags 2003 # R relative 2004 # B measure-bbox 2005 # G measure-gbox 2006 # J justify, right, center 2007 # C clip 2008 # 2009 local trace_flags= 2010 2011 local opt_nooverflow=; [[ :$opts: == *:truncate:* || :$opts: == *:confine:* ]] && opt_nooverflow=1 2012 local opt_relative=; [[ :$opts: == *:relative:* ]] && trace_flags=R$trace_flags opt_relative=1 2013 [[ :$opts: == *:measure-bbox:* ]] && trace_flags=B$trace_flags 2014 [[ :$opts: == *:measure-gbox:* ]] && trace_flags=G$trace_flags 2015 [[ :$opts: == *:left-char:* ]] && trace_flags=L$trace_flags 2016 local opt_terminfo=; [[ :$opts: == *:terminfo:* ]] && opt_terminfo=1 2017 2018 if local rex=':(justify(=[^:]+)?|center|right):'; [[ :$opts: =~ $rex ]]; then 2019 trace_flags=J$trace_flags 2020 local jx0=$x jy0=$y 2021 local justify_sep= justify_align= 2022 local -a justify_buff=() 2023 local -a justify_fields=() 2024 case ${BASH_REMATCH[1]} in 2025 (justify*) justify_sep=${BASH_REMATCH[2]:1}${BASH_REMATCH[2]:-' '} ;; 2026 (center) justify_align=c ;; 2027 (right) justify_align=r ;; 2028 esac 2029 fi 2030 2031 if local rex=':clip=([0-9]*),([0-9]*)([-+])([0-9]*),([0-9]*):'; [[ :$opts: =~ $rex ]]; then 2032 local cx1 cy1 cx2 cy2 cx cy cg 2033 trace_flags=C$trace_flags 2034 cx1=${BASH_REMATCH[1]} cy1=${BASH_REMATCH[2]} 2035 cx2=${BASH_REMATCH[4]} cy2=${BASH_REMATCH[5]} 2036 [[ ${BASH_REMATCH[3]} == + ]] && ((cx2+=cx1,cy2+=cy1)) 2037 ((cx1<=cx2)) || local cx1=$cx2 cx2=$cx1 2038 ((cy1<=cy2)) || local cy1=$cy2 cy2=$cy1 2039 ((cx1<0)) && cx1=0 2040 ((cy1<0)) && cy1=0 2041 ((cols<cx2)) && cx2=$cols 2042 ((lines<cy2)) && cy2=$lines 2043 local cx=$cx1 cy=$cy1 cg= 2044 fi 2045 2046 local trace_g2sgr=ble/color/g2sgr 2047 [[ :$opts: == *:ansi:* || $trace_flags == *C*J* ]] && 2048 trace_g2sgr=ble/color/g2sgr-ansi 2049 2050 local opt_g0= opt_sgr0=$_ble_term_sgr0 2051 if rex=':g0=([^:]+):'; [[ :$opts: =~ $rex ]]; then 2052 opt_g0=${BASH_REMATCH[1]} 2053 elif rex=':face0=([^:]+):'; [[ :$opts: =~ $rex ]]; then 2054 ble/color/face2g "${BASH_REMATCH[1]}"; opt_g0=$ret 2055 fi 2056 if [[ $opt_g0 ]]; then 2057 "$trace_g2sgr" "$opt_g0"; opt_sgr0=$ret 2058 ble/canvas/put.draw "$opt_sgr0" 2059 g=$opt_g0 2060 fi 2061 2062 #------------------------------------- 2063 2064 # CSI 2065 local rex_csi='^\[[ -?]*[@-~]' 2066 # OSC, DCS, SOS, PM, APC Sequences + "GNU screen ESC k" 2067 local rex_osc='^([]PX^_k])([^'$st']|+[^\'$st'])*(\\|'${st:+'|'}$st'|$)' 2068 # ISO-2022 関係 (3byte以上の物) 2069 local rex_2022='^[ -/]+[@-~]' 2070 # ESC ? 2071 local rex_esc='^[ -~]' 2072 2073 # states 2074 local trace_sclevel=0 2075 local -a trace_brack=() 2076 local -a trace_scosc=() 2077 local -a trace_decsc=() 2078 2079 local flag_lchar= 2080 if [[ $trace_flags == *L* ]]; then 2081 flag_lchar=1 2082 else 2083 local lc=32 lg=0 2084 fi 2085 2086 # prepare measure 2087 local flag_bbox= flag_gbox= 2088 if [[ $trace_flags == *[BJ]* ]]; then 2089 flag_bbox=1 2090 [[ $trace_flags != *B* ]] && local x1 x2 y1 y2 2091 [[ $trace_flags != *G* ]] && local gx1= gx2= gy1= gy2= 2092 ((x1=x2=x,y1=y2=y)) 2093 2094 [[ $trace_flags == *J*B* ]] && 2095 local jx1=$x jy1=$y jx2=$x jy2=$y 2096 fi 2097 if [[ $trace_flags == *G* ]]; then 2098 ((flag_gbox=1)) 2099 gx1= gx2= gy1= gy2= 2100 [[ $trace_flags == *J* ]] && 2101 local jgx1= jgy1= jgx2= jgy2= 2102 fi 2103 2104 # flag_clip: justify 処理が入っている時は後で clip を処理する。 2105 local flag_clip= 2106 [[ $trace_flags == *C* && $trace_flags != *J* ]] && flag_clip=1 2107 2108 # opt_relative の時には右端に接触しない前提。justify の時には、後の再配置の時 2109 # に xenl について処理するので、フィールド内追跡では xenl は気にしなくて良い。 2110 local xenl=$_ble_term_xenl 2111 [[ $opt_relative || $trace_flags == *J* ]] && xenl=1 2112 local xlimit=$((xenl?cols:cols-1)) 2113 2114 local flag_justify= 2115 if [[ $trace_flags == *J* ]]; then 2116 flag_justify=1 2117 ble/canvas/trace/.justify/begin-line 2118 fi 2119 2120 local i=0 iN=${#text} 2121 while ((i<iN)); do 2122 local tail=${text:i} 2123 local is_overflow= 2124 if [[ $flag_justify && $justify_sep && $tail == ["$justify_sep"]* ]]; then 2125 ble/canvas/trace/.justify/next-field "${tail::1}" 2126 ((i++)) 2127 elif [[ $tail == [-]* ]]; then 2128 local s=${tail::1} 2129 ((i++)) 2130 case $s in 2131 ($'\e') 2132 if [[ $tail =~ $rex_osc ]]; then 2133 # 各種メッセージ (素通り) 2134 s=$BASH_REMATCH 2135 [[ ${BASH_REMATCH[3]} ]] || s="$s\\" # 終端の追加 2136 ((i+=${#BASH_REMATCH}-1)) 2137 ble/canvas/trace/.put-atomic.draw "$s" 0 2138 elif [[ $tail =~ $rex_csi ]]; then 2139 # Control sequences 2140 ((i+=${#BASH_REMATCH}-1)) 2141 ble/canvas/trace/.process-csi-sequence "$BASH_REMATCH" 2142 elif [[ $tail =~ $rex_2022 ]]; then 2143 # ISO-2022 (素通り) 2144 ble/canvas/trace/.put-atomic.draw "$BASH_REMATCH" 0 2145 ((i+=${#BASH_REMATCH}-1)) 2146 elif [[ $tail =~ $rex_esc ]]; then 2147 ((i+=${#BASH_REMATCH}-1)) 2148 ble/canvas/trace/.process-esc-sequence "$BASH_REMATCH" 2149 else 2150 ble/canvas/trace/.put-atomic.draw "$s" 0 2151 fi ;; 2152 ($'\b') # BS 2153 if ((x>0)); then 2154 [[ $flag_clip ]] || ble/canvas/put.draw "$s" 2155 ((x--,lc=32,lg=g)) 2156 ble/canvas/trace/.measure-point 2157 fi ;; 2158 ($'\t') # HT 2159 local tx 2160 ((tx=(x+it)/it*it, 2161 tx>=cols&&(tx=cols-1))) 2162 if ((x<tx)); then 2163 ((lc=32,lg=g)) 2164 ble/canvas/trace/.put-ascii.draw "${_ble_string_prototype::tx-x}" 2165 fi ;; 2166 ($'\n') # LF = CR+LF 2167 ble/canvas/trace/.NEL ;; 2168 ($'\v') # VT 2169 if ((y+1<lines||!opt_nooverflow)); then 2170 if [[ $flag_clip || $opt_relative || $flag_justify ]]; then 2171 if ((y+1<lines)); then 2172 [[ $flag_clip ]] || 2173 ble/canvas/put-cud.draw 1 2174 ((y++,lc=32,lg=0)) 2175 fi 2176 else 2177 ble/canvas/put.draw "$_ble_term_cr" 2178 ble/canvas/put.draw "$_ble_term_nl" 2179 ((x)) && ble/canvas/put-cuf.draw "$x" 2180 ((y++,lc=32,lg=0)) 2181 fi 2182 ble/canvas/trace/.measure-point 2183 fi ;; 2184 ($'\r') # CR ^M 2185 local ox=$x 2186 ((x=0,lc=-1,lg=0)) 2187 if [[ ! $flag_clip ]]; then 2188 if [[ $flag_justify ]]; then 2189 ble/canvas/put-move-x.draw "$((jx0-ox))" 2190 ((x=jx0)) 2191 elif [[ $opt_relative ]]; then 2192 ble/canvas/put-cub.draw "$ox" 2193 else 2194 ble/canvas/put.draw "$_ble_term_cr" 2195 fi 2196 fi 2197 ble/canvas/trace/.measure-point ;; 2198 # Note: \001 (^A) 及び \002 (^B) は PS1 の処理で \[ \] を意味するそうだ。#D1074 2199 ($'\001') [[ :$opts: == *:prompt:* ]] && ble/canvas/trace/.ps1sc ;; 2200 ($'\002') [[ :$opts: == *:prompt:* ]] && ble/canvas/trace/.ps1rc ;; 2201 # その他の制御文字は (BEL) (FF) も含めてゼロ幅と解釈する 2202 (*) ble/canvas/put.draw "$s" ;; 2203 esac 2204 elif ble/util/isprint+ "$tail"; then 2205 local s=$BASH_REMATCH 2206 [[ $flag_justify && $justify_sep ]] && s=${s%%["$justify_sep"]*} 2207 local w=${#s} 2208 if [[ $opt_nooverflow ]]; then 2209 local wmax=$((lines*cols-(y*cols+x))) 2210 ((xenl||wmax--,wmax<0&&(wmax=0))) 2211 ((w>wmax)) && w=$wmax is_overflow=1 2212 fi 2213 2214 local t=${s::w} 2215 if [[ $flag_clip || $opt_relative || $flag_justify ]]; then 2216 local tlen=$w len=$((cols-x)) 2217 if ((tlen>len)); then 2218 while ((tlen>len)); do 2219 ble/canvas/trace/.put-ascii.draw "${t::len}" 2220 t=${t:len} 2221 ((x=cols,tlen-=len,len=cols)) 2222 ble/canvas/trace/.NEL 2223 done 2224 w=${#t} 2225 fi 2226 fi 2227 2228 if [[ $flag_lchar ]]; then 2229 local ret 2230 ble/util/s2c "${s:w-1:1}" 2231 lc=$ret lg=$g 2232 fi 2233 ble/canvas/trace/.put-ascii.draw "$t" 2234 ((i+=${#s})) 2235 2236 if local extend; ble/unicode/GraphemeCluster/extend-ascii "$text" "$i"; then 2237 ble/canvas/trace/.put-atomic.draw "${text:i:extend}" 0 2238 ((i+=extend)) 2239 fi 2240 2241 else 2242 local c w cs cb extend 2243 ble/unicode/GraphemeCluster/match "$text" "$i" R 2244 if [[ $opt_nooverflow ]] && ! ((x+w<=xlimit||y+1<lines&&w<=cols)); then 2245 is_overflow=1 2246 else 2247 if ((x+w>cols)); then 2248 if [[ $flag_clip || $opt_relative || $flag_justify ]]; then 2249 ble/canvas/trace/.NEL 2250 else 2251 # 行に入りきらない場合の調整 2252 ble/canvas/trace/.put-ascii.draw "${_ble_string_prototype::cols-x}" 2253 fi 2254 fi 2255 lc=$c lg=$g 2256 ble/canvas/trace/.put-atomic.draw "$cs" "$w" 2257 fi 2258 ((i+=1+extend)) 2259 fi 2260 2261 [[ $is_overflow ]] && ble/canvas/trace/.process-overflow 2262 done 2263 2264 if [[ $trace_flags == *J* ]]; then 2265 if [[ ! $flag_justify ]]; then 2266 # 各種 sc により一時的に justify が無効化されていたとしても、強制的に rc 2267 # を出力して閉じる。 2268 [[ ${trace_scosc[5]} ]] && ble/canvas/trace/.scorc 2269 [[ ${trace_decsc[5]} ]] && ble/canvas/trace/.decrc 2270 while [[ ${trace_brack[0]} ]]; do ble/canvas/trace/.ps1rc; done 2271 fi 2272 ble/canvas/trace/.justify/end-line 2273 DRAW_BUFF=("${justify_buff[@]}") 2274 2275 [[ $trace_flags == *B* ]] && 2276 ((x1=jx1,y1=jy1,x2=jx2,y2=jy2)) 2277 [[ $trace_flags == *G* ]] && 2278 gx1=$jgx1 gy1=$jgy1 gx2=$jgx2 gy2=$jgy2 2279 2280 if [[ $trace_flags == *C* ]]; then 2281 ble/canvas/sflush.draw 2282 x=$xinit y=$yinit g=$ginit 2283 local trace_opts=clip=$cx1,$cy1-$cx2,$cy2 2284 [[ :$opts: == *:ansi:* ]] && trace_opts=$trace_opts:ansi 2285 ble/canvas/trace/.impl "$ret" "$trace_opts" 2286 cx=$x cy=$y cg=$g 2287 fi 2288 fi 2289 2290 [[ $trace_flags == *B* ]] && ((y2++)) 2291 [[ $trace_flags == *G* ]] && ((gy2++)) 2292 if [[ $trace_flags == *C* ]]; then 2293 x=$cx y=$cy g=$cg 2294 if [[ $trace_flags == *B* ]]; then 2295 ((x1<cx1)) && x1=$cx1 2296 ((x1>cx2)) && x1=$cx2 2297 ((x2<cx1)) && x2=$cx1 2298 ((x2>cx2)) && x2=$cx2 2299 ((y1<cy1)) && y1=$cy1 2300 ((y1>cy2)) && y1=$cy2 2301 ((y2<cy1)) && y2=$cy1 2302 ((y2>cy2)) && y2=$cy2 2303 fi 2304 if [[ $trace_flags == *G* ]]; then 2305 if ((gx2<=cx1||cx2<=gx1||gy2<=cy1||cy2<=gy1)); then 2306 gx1= gx2= gy1= gy2= 2307 else 2308 ((gx1<cx1)) && gx1=$cx1 2309 ((gx2>cx2)) && gx2=$cx2 2310 ((gy1<cy1)) && gy1=$cy1 2311 ((gy2>cy2)) && gy2=$cy2 2312 fi 2313 fi 2314 fi 2315 } 2316 function ble/canvas/trace.draw { 2317 ble/canvas/trace/.impl "$@" 2>/dev/null # Note: suppress LC_COLLATE errors #D1205 #D1341 #D1440 2318 } 2319 function ble/canvas/trace { 2320 local -a DRAW_BUFF=() 2321 ble/canvas/trace/.impl "$@" 2>/dev/null # Note: suppress LC_COLLATE errors #D1205 #D1341 #D1440 2322 ble/canvas/sflush.draw # -> ret 2323 } 2324 2325 #------------------------------------------------------------------------------ 2326 # ble/canvas/construct-text 2327 2328 ## @fn ble/canvas/trace-text/.put-atomic nchar text 2329 ## 指定した文字列を out に追加しつつ、現在位置を更新します。 2330 ## 文字列は幅 1 の文字で構成されていると仮定します。 2331 ## @var[in,out] x y out 2332 ## @var[in] cols lines 2333 ## 2334 function ble/canvas/trace-text/.put-simple { 2335 local nchar=$1 2336 ((nchar)) || return 0 2337 2338 local nput=$((cols*lines-!_ble_term_xenl-(y*cols+x))) 2339 ((nput>0)) || return 1 2340 ((nput>nchar)) && nput=$nchar 2341 out=$out${2::nput} 2342 ((x+=nput,y+=x/cols,x%=cols)) 2343 ((_ble_term_xenl&&x==0&&(y--,x=cols))) 2344 ((nput==nchar)); return "$?" 2345 } 2346 ## @fn x y cols out ; ble/canvas/trace-text/.put-atomic ( w char )+ ; x y out 2347 ## 指定した文字を out に追加しつつ、現在位置を更新します。 2348 ## 範囲に収まり切らない時に失敗します。 2349 function ble/canvas/trace-text/.put-atomic { 2350 local w=$1 c=$2 2351 2352 # 収まらない時は skip 2353 ((y*cols+x+w<=cols*lines-!_ble_term_xenl)) || return 1 2354 2355 # その行に入りきらない文字は次の行へ (幅 w が2以上の文字) 2356 if ((x<cols&&cols<x+w)); then 2357 if [[ :$opts: == *:nonewline:* ]]; then 2358 ble/string#reserve-prototype "$((cols-x))" 2359 out=$out${_ble_string_prototype::cols-x} 2360 ((x=cols)) 2361 else 2362 out=$out$'\n' 2363 ((y++,x=0)) 2364 fi 2365 fi 2366 2367 # w!=0 のとき行末にいたら次の行へ暗黙移動 2368 ((w&&x==cols&&(y++,x=0))) 2369 2370 # 改行しても尚行内に収まらない時は ## で代用 2371 local limit=$((cols-(y+1==lines&&!_ble_term_xenl))) 2372 if ((x+w>limit)); then 2373 ble/string#reserve-prototype "$((limit-x))" 2374 local pad=${_ble_string_prototype::limit-x} 2375 out=$out$sgr1${pad//?/'#'}$sgr0 2376 x=$limit 2377 ((y+1<lines)); return "$?" 2378 fi 2379 2380 out=$out$c 2381 ((x+=w,!_ble_term_xenl&&x==cols&&(y++,x=0))) 2382 return 0 2383 } 2384 ## @fn x y cols out ; ble/canvas/trace-text/.put-nl-if-eol ; x y out 2385 ## 行末にいる場合次の行へ移動します。 2386 function ble/canvas/trace-text/.put-nl-if-eol { 2387 if ((x==cols&&y+1<lines)); then 2388 [[ :$opts: == *:nonewline:* ]] && return 0 2389 ((_ble_term_xenl)) && out=$out$'\n' 2390 ((y++,x=0)) 2391 fi 2392 } 2393 2394 ## @fn ble/canvas/trace-text text opts 2395 ## 指定した文字列を表示する為の制御系列に変換します。 2396 ## @param[in] text 2397 ## @param[in] opts 2398 ## nonewline 2399 ## 2400 ## external-sgr 2401 ## @var[in] sgr0 sgr1 2402 ## 特殊文字の強調に用いる SGR シーケンスを外部から提供します。 2403 ## sgr0 に通常文字の表示に用いる SGR を、 2404 ## sgr1 に特殊文字の表示に用いる SGR を指定します。 2405 ## 2406 ## @var[in] cols lines 2407 ## @var[in,out] x y 2408 ## @var[out] ret 2409 ## @exit 2410 ## 指定した範囲に文字列が収まった時に成功します。 2411 function ble/canvas/trace-text { 2412 local LC_ALL= LC_COLLATE=C 2413 2414 local out= glob='*[! -~]*' 2415 local opts=$2 flag_overflow= 2416 [[ :$opts: == *:external-sgr:* ]] || 2417 local sgr0=$_ble_term_sgr0 sgr1=$_ble_term_rev 2418 if [[ $1 != $glob ]]; then 2419 # G0 だけで構成された文字列は先に単純に処理する 2420 ble/canvas/trace-text/.put-simple "${#1}" "$1" 2421 else 2422 local glob='[ -~]*' globx='[! -~]*' 2423 local i iN=${#1} text=$1 2424 for ((i=0;i<iN;)); do 2425 local tail=${text:i} 2426 if [[ $tail == $glob ]]; then 2427 local span=${tail%%$globx}; ((i+=${#span})) 2428 ble/canvas/trace-text/.put-simple "${#span}" "$span" 2429 if local extend; ble/unicode/GraphemeCluster/extend-ascii "$text" "$i"; then 2430 out=$out${text:i:extend} 2431 ((i+=extend)) 2432 fi 2433 else 2434 local c w cs cb extend 2435 ble/unicode/GraphemeCluster/match "$text" "$i" 2436 ((i+=1+extend)) 2437 ((cb==_ble_unicode_GraphemeClusterBreak_Control)) && 2438 cs=$sgr1$cs$sgr0 2439 ble/canvas/trace-text/.put-atomic "$w" "$cs" 2440 fi && ((y*cols+x<lines*cols)) || 2441 { flag_overflow=1; break; } 2442 done 2443 fi 2444 2445 ble/canvas/trace-text/.put-nl-if-eol 2446 ret=$out 2447 2448 # 収まったかどうか 2449 [[ ! $flag_overflow ]] 2450 } 2451 # Note: suppress LC_COLLATE errors #D1205 #D1262 #1341 #D1440 2452 ble/function#suppress-stderr ble/canvas/trace-text 2453 2454 #------------------------------------------------------------------------------ 2455 # ble/textmap 2456 2457 _ble_textmap_VARNAMES=( 2458 _ble_textmap_cols 2459 _ble_textmap_length 2460 _ble_textmap_begx 2461 _ble_textmap_begy 2462 _ble_textmap_endx 2463 _ble_textmap_endy 2464 2465 _ble_textmap_pos 2466 _ble_textmap_glyph 2467 _ble_textmap_ichg 2468 2469 _ble_textmap_dbeg 2470 _ble_textmap_dend 2471 _ble_textmap_dend0 2472 _ble_textmap_umin 2473 _ble_textmap_umax) 2474 2475 ## 文字列の配置計算に関する情報 2476 ## 2477 ## 前回の配置計算の前提と結果を保持する変数群を以下に説明します。 2478 ## 以下は配置計算の前提になる情報です。 2479 ## 2480 ## @var _ble_textmap_cols 2481 ## 配置幅を保持します。 2482 ## @var _ble_textmap_begx 2483 ## @var _ble_textmap_begy 2484 ## 配置の開始位置を保持します。 2485 ## @var _ble_textmap_length 2486 ## 配置文字列の長さを保持します。 2487 ## 2488 ## 以下は配置計算の結果を保持します。 2489 ## 2490 ## @arr _ble_textmap_pos[] 2491 ## 各文字の表示位置を保持します。 2492 ## @arr _ble_textmap_glyph[] 2493 ## 各文字の表現を保持します。 2494 ## 例えば、制御文字は ^C や M-^C などと表されます。 2495 ## タブは表示開始位置に応じて異なる個数の空白で表現されます。 2496 ## 行送りされた全角文字は前にパディングの空白が付加されます。 2497 ## @arr _ble_textmap_ichg[] 2498 ## タブや行送りなどによって標準的な表現と異なる文字 2499 ## のインデックスのリストです。 2500 ## 標準的な表現は ble/highlight/layer:plain/update/.getch で規定されます。 2501 ## @var _ble_textmap_endx 2502 ## @var _ble_textmap_endy 2503 ## 最後の文字の右端の座標を保持します。 2504 ## 2505 ## 以下は前回の配置計算以降の更新範囲を保持する変数です。 2506 ## 部分更新をするために使用します。 2507 ## 2508 ## @var _ble_textmap_dbeg 2509 ## @var _ble_textmap_dend 2510 ## @var _ble_textmap_dend0 2511 ## 2512 _ble_textmap_cols=80 2513 _ble_textmap_length= 2514 _ble_textmap_begx=0 2515 _ble_textmap_begy=0 2516 _ble_textmap_endx=0 2517 _ble_textmap_endy=0 2518 _ble_textmap_pos=() 2519 _ble_textmap_glyph=() 2520 _ble_textmap_ichg=() 2521 _ble_textmap_dbeg=-1 2522 _ble_textmap_dend=-1 2523 _ble_textmap_dend0=-1 2524 _ble_textmap_umin=-1 2525 _ble_textmap_umax=-1 2526 2527 function ble/textmap#update-dirty-range { 2528 ble/dirty-range#update --prefix=_ble_textmap_d "$@" 2529 } 2530 function ble/textmap#save { 2531 local name prefix=$1 2532 ble/util/save-vars "$prefix" "${_ble_textmap_VARNAMES[@]}" 2533 } 2534 function ble/textmap#restore { 2535 local name prefix=$1 2536 ble/util/restore-vars "$prefix" "${_ble_textmap_VARNAMES[@]}" 2537 } 2538 2539 ## @fn ble/textmap#update/.wrap 2540 ## @var[in,out] cs x y changed 2541 function ble/textmap#update/.wrap { 2542 if [[ :$opts: == *:relative:* ]]; then 2543 ((x)) && cs=$cs${_ble_term_cub//'%d'/$x} 2544 cs=$cs${_ble_term_cud//'%d'/1} 2545 changed=1 2546 elif ((xenl)); then 2547 # Note #D1745: 自動改行は CR で表現する事にする。この CR は実際の 2548 # 出力時に LF または空文字列に置換する。 2549 cs=$cs$_ble_term_cr 2550 changed=1 2551 fi 2552 ((y++,x=0)) 2553 } 2554 2555 ## @fn ble/textmap#update text [opts] 2556 ## @param[in] text 2557 ## @param[in,opt] opts 2558 ## @var[in,out] x y 2559 ## @var[in,out] _ble_textmap_* 2560 function ble/textmap#update { 2561 local IFS=$_ble_term_IFS 2562 local dbeg dend dend0 2563 ((dbeg=_ble_textmap_dbeg, 2564 dend=_ble_textmap_dend, 2565 dend0=_ble_textmap_dend0)) 2566 ble/dirty-range#clear --prefix=_ble_textmap_d 2567 2568 local text=$1 opts=$2 2569 local iN=${#text} 2570 2571 # 初期位置 x y 2572 local pos0="$x $y" 2573 _ble_textmap_begx=$x 2574 _ble_textmap_begy=$y 2575 2576 # ※現在は COLUMNS で決定しているが将来的には変更可能にする? 2577 local cols=${COLUMNS-80} xenl=$_ble_term_xenl 2578 ((COLUMNS&&cols<COLUMNS&&(xenl=1))) 2579 ble/string#reserve-prototype "$cols" 2580 # local cols=80 xenl=1 2581 2582 local it=${bleopt_tab_width:-$_ble_term_it} 2583 ble/string#reserve-prototype "$it" 2584 2585 if ((cols!=_ble_textmap_cols)); then 2586 # 表示幅が変化したときは全部再計算 2587 ((dbeg=0,dend0=_ble_textmap_length,dend=iN)) 2588 _ble_textmap_pos[0]=$pos0 2589 elif [[ ${_ble_textmap_pos[0]} != "$pos0" ]]; then 2590 # 初期位置の変更がある場合は初めから計算し直し 2591 ((dbeg<0&&(dend=dend0=0), 2592 dbeg=0)) 2593 _ble_textmap_pos[0]=$pos0 2594 else 2595 if ((dbeg<0)); then 2596 # 表示幅も初期位置も内容も変更がない場合はOK 2597 local pos 2598 ble/string#split-words pos "${_ble_textmap_pos[iN]}" 2599 ((x=pos[0])) 2600 ((y=pos[1])) 2601 _ble_textmap_endx=$x 2602 _ble_textmap_endy=$y 2603 return 0 2604 elif ((dbeg>0)); then 2605 # 途中から計算を再開 2606 local ret 2607 ble/unicode/GraphemeCluster/find-previous-boundary "$text" "$dbeg"; dbeg=$ret 2608 local pos 2609 ble/string#split-words pos "${_ble_textmap_pos[dbeg]}" 2610 ((x=pos[0])) 2611 ((y=pos[1])) 2612 fi 2613 fi 2614 2615 _ble_textmap_cols=$cols 2616 _ble_textmap_length=$iN 2617 2618 #%if !release 2619 ble/util/assert '((dbeg<0||(dbeg<=dend&&dbeg<=dend0)))' "($dbeg $dend $dend0) <- ($_ble_textmap_dbeg $_ble_textmap_dend $_ble_textmap_dend0)" 2620 #%end 2621 2622 # shift cached data 2623 ble/array#reserve-prototype "$iN" 2624 local -a old_pos old_ichg 2625 old_pos=("${_ble_textmap_pos[@]:dend0:iN-dend+1}") 2626 old_ichg=("${_ble_textmap_ichg[@]}") 2627 _ble_textmap_pos=( 2628 "${_ble_textmap_pos[@]::dbeg+1}" 2629 "${_ble_array_prototype[@]::dend-dbeg}" 2630 "${_ble_textmap_pos[@]:dend0+1:iN-dend}") 2631 _ble_textmap_glyph=( 2632 "${_ble_textmap_glyph[@]::dbeg}" 2633 "${_ble_array_prototype[@]::dend-dbeg}" 2634 "${_ble_textmap_glyph[@]:dend0:iN-dend}") 2635 _ble_textmap_ichg=() 2636 2637 ble/urange#shift --prefix=_ble_textmap_ "$dbeg" "$dend" "$dend0" 2638 2639 local i extend 2640 for ((i=dbeg;i<iN;)); do 2641 if ble/util/isprint+ "${text:i}"; then 2642 local w=${#BASH_REMATCH} 2643 local n 2644 for ((n=i+w;i<n;i++)); do 2645 local cs=${text:i:1} 2646 if ((++x==cols)); then 2647 local changed=0 2648 ble/textmap#update/.wrap 2649 ((changed)) && ble/array#push _ble_textmap_ichg "$i" 2650 fi 2651 _ble_textmap_glyph[i]=$cs 2652 _ble_textmap_pos[i+1]="$x $y 0" 2653 done 2654 ble/unicode/GraphemeCluster/extend-ascii "$text" "$i" 2655 else 2656 local c w cs cb extend changed=0 2657 ble/unicode/GraphemeCluster/match "$text" "$i" 2658 if ((c<32)); then 2659 if ((c==9)); then 2660 if ((x+1>=cols)); then 2661 cs=' ' w=0 2662 ble/textmap#update/.wrap 2663 else 2664 local x2 2665 ((x2=(x/it+1)*it, 2666 x2>=cols&&(x2=cols-1), 2667 w=x2-x, 2668 w!=it&&(changed=1))) 2669 cs=${_ble_string_prototype::w} 2670 fi 2671 elif ((c==10)); then 2672 w=0 2673 if [[ :$opts: == *:relative:* ]]; then 2674 local pad=$((cols-x)) eraser= 2675 if ((pad)); then 2676 if [[ $_ble_term_ech ]]; then 2677 eraser=${_ble_term_ech//'%d'/$pad} 2678 else 2679 eraser=${_ble_string_prototype::pad} 2680 ((x=cols)) 2681 fi 2682 fi 2683 local move=${_ble_term_cub//'%d'/$x}${_ble_term_cud//'%d'/1} 2684 cs=$eraser$move 2685 changed=1 2686 else 2687 cs=$_ble_term_el$_ble_term_nl 2688 fi 2689 ((y++,x=0)) 2690 fi 2691 fi 2692 2693 local wrapping=0 2694 if ((w>0)); then 2695 if ((x<cols&&cols<x+w)); then 2696 if [[ :$opts: == *:relative:* ]]; then 2697 cs=${_ble_term_cub//'%d'/$cols}${_ble_term_cud//'%d'/1}$cs 2698 elif ((xenl)); then 2699 # Note #D1745: 自動改行は CR で表現する事にする。この CR 2700 # は実際の出力時に LF または空文字列に置換する。 2701 cs=$_ble_term_cr$cs 2702 fi 2703 local pad=$((cols-x)) 2704 if ((pad)); then 2705 if [[ $_ble_term_ech ]]; then 2706 cs=${_ble_term_ech//'%d'/$pad}$cs 2707 else 2708 cs=${_ble_string_prototype::pad}$cs 2709 fi 2710 fi 2711 ((x=cols,changed=1,wrapping=1)) 2712 fi 2713 2714 ((x+=w)) 2715 while ((x>cols)); do 2716 ((y++,x-=cols)) 2717 done 2718 if ((x==cols)); then 2719 ble/textmap#update/.wrap 2720 fi 2721 fi 2722 2723 _ble_textmap_glyph[i]=$cs 2724 ((changed)) && ble/array#push _ble_textmap_ichg "$i" 2725 _ble_textmap_pos[i+1]="$x $y $wrapping" 2726 ((i++)) 2727 fi 2728 while ((extend--)); do 2729 _ble_textmap_glyph[i]= 2730 _ble_textmap_pos[++i]="$x $y 0" 2731 done 2732 2733 if ((i>=dend)); then 2734 # 後は同じなので計算を省略 2735 [[ ${old_pos[i-dend]} == "${_ble_textmap_pos[i]}" ]] && break 2736 2737 # x 座標が同じならば、以降は最後まで y 座標だけずらす 2738 if [[ ${old_pos[i-dend]%%[$IFS]*} == "${_ble_textmap_pos[i]%%[$IFS]*}" ]]; then 2739 local -a opos npos pos 2740 opos=(${old_pos[i-dend]}) 2741 npos=(${_ble_textmap_pos[i]}) 2742 local ydelta=$((npos[1]-opos[1])) 2743 while ((i<iN)); do 2744 ((i++)) 2745 pos=(${_ble_textmap_pos[i]}) 2746 ((pos[1]+=ydelta)) 2747 _ble_textmap_pos[i]="${pos[*]}" 2748 done 2749 pos=(${_ble_textmap_pos[iN]}) 2750 x=${pos[0]} y=${pos[1]} 2751 break 2752 fi 2753 fi 2754 done 2755 2756 if ((i<iN)); then 2757 # 途中で一致して中断した場合は、前の iN 番目の位置を読む 2758 local -a pos 2759 pos=(${_ble_textmap_pos[iN]}) 2760 x=${pos[0]} y=${pos[1]} 2761 fi 2762 2763 # 前回までの文字修正位置を shift&add 2764 local j jN ichg 2765 for ((j=0,jN=${#old_ichg[@]};j<jN;j++)); do 2766 if ((ichg=old_ichg[j], 2767 (ichg>=dend0)&&(ichg+=dend-dend0), 2768 (0<=ichg&&ichg<dbeg||dend<=i&&ichg<iN))) 2769 then 2770 ble/array#push _ble_textmap_ichg "$ichg" 2771 fi 2772 done 2773 2774 ((dbeg<i)) && ble/urange#update --prefix=_ble_textmap_ "$dbeg" "$i" 2775 2776 _ble_textmap_endx=$x 2777 _ble_textmap_endy=$y 2778 } 2779 2780 function ble/textmap#is-up-to-date { 2781 ((_ble_textmap_dbeg==-1)) 2782 } 2783 ## @fn ble/textmap#assert-up-to-date 2784 ## 編集文字列の文字の配置情報が最新であることを確認します。 2785 ## 以下の変数を参照する場合に事前に呼び出します。 2786 ## 2787 ## _ble_textmap_pos 2788 ## _ble_textmap_length 2789 ## 2790 function ble/textmap#assert-up-to-date { 2791 ble/util/assert 'ble/textmap#is-up-to-date' 'dirty text positions' 2792 } 2793 2794 ## @fn ble/textmap#getxy.out index 2795 ## index 番目の文字の出力開始位置を取得します。 2796 ## 2797 ## @var[out] x y 2798 ## 2799 ## 行末に収まらない文字の場合は行末のスペースを埋める為に 2800 ## 配列 _ble_textmap_glyph において空白文字が文字本体の前に追加されます。 2801 ## その場合には、追加される空白文字の前の位置を返すことに注意して下さい。 2802 ## 実用上は境界 index の左側の文字の終端位置と解釈できます。 2803 ## 2804 function ble/textmap#getxy.out { 2805 ble/textmap#assert-up-to-date 2806 local _ble_local_prefix= 2807 if [[ $1 == --prefix=* ]]; then 2808 _ble_local_prefix=${1#--prefix=} 2809 shift 2810 fi 2811 2812 local -a pos 2813 ble/string#split-words pos "${_ble_textmap_pos[$1]}" 2814 ((${_ble_local_prefix}x=pos[0])) 2815 ((${_ble_local_prefix}y=pos[1])) 2816 } 2817 2818 ## @fn ble/textmap#getxy.cur index 2819 ## index 番目の文字の表示開始位置を取得します。 2820 ## 2821 ## @var[out] x y 2822 ## 2823 ## ble/textmap#getxy.out の異なり前置される空白は考えずに、 2824 ## 文字本体が開始する位置を取得します。 2825 ## 実用上は境界 index の右側の文字の開始位置と解釈できます。 2826 ## 2827 function ble/textmap#getxy.cur { 2828 ble/textmap#assert-up-to-date 2829 local _ble_local_prefix= 2830 if [[ $1 == --prefix=* ]]; then 2831 _ble_local_prefix=${1#--prefix=} 2832 shift 2833 fi 2834 2835 local -a pos 2836 ble/string#split-words pos "${_ble_textmap_pos[$1]}" 2837 2838 # 追い出しされたか check 2839 if (($1<_ble_textmap_length)); then 2840 local -a eoc 2841 ble/string#split-words eoc "${_ble_textmap_pos[$1+1]}" 2842 ((eoc[2])) && ((pos[0]=0,pos[1]++)) 2843 fi 2844 2845 ((${_ble_local_prefix}x=pos[0])) 2846 ((${_ble_local_prefix}y=pos[1])) 2847 } 2848 2849 ## @fn ble/textmap#get-index-at [-v varname] x y 2850 ## 指定した位置 x y に対応する index を求めます。 2851 function ble/textmap#get-index-at { 2852 ble/textmap#assert-up-to-date 2853 local __ble_var=index 2854 if [[ $1 == -v ]]; then 2855 __ble_var=$2 2856 shift 2 2857 fi 2858 2859 local __ble_x=$1 __ble_y=$2 2860 if ((__ble_y>_ble_textmap_endy)); then 2861 (($__ble_var=_ble_textmap_length)) 2862 elif ((__ble_y<_ble_textmap_begy)); then 2863 (($__ble_var=0)) 2864 else 2865 # 2分法 2866 local __ble_l=0 __ble_u=$((_ble_textmap_length+1)) 2867 local m mx my 2868 while ((__ble_l+1<__ble_u)); do 2869 ble/textmap#getxy.cur --prefix=m "$((m=(__ble_l+__ble_u)/2))" 2870 (((__ble_y<my||__ble_y==my&&__ble_x<mx)?(__ble_u=m):(__ble_l=m))) 2871 done 2872 ble/util/unlocal m mx my 2873 (($__ble_var=__ble_l)) 2874 fi 2875 } 2876 2877 ## @fn ble/textmap#hit/.getxy.out index 2878 ## @fn ble/textmap#hit/.getxy.cur index 2879 ## @var[in,out] pos 2880 function ble/textmap#hit/.getxy.out { 2881 local a 2882 ble/string#split-words a "${_ble_textmap_pos[$1]}" 2883 x=${a[0]} y=${a[1]} 2884 } 2885 function ble/textmap#hit/.getxy.cur { 2886 local index=$1 a 2887 ble/string#split-words a "${_ble_textmap_pos[index]}" 2888 x=${a[0]} y=${a[1]} 2889 if ((index<_ble_textmap_length)); then 2890 ble/string#split-words a "${_ble_textmap_pos[index+1]}" 2891 ((a[2])) && ((x=0,y++)) 2892 fi 2893 } 2894 2895 ## @fn ble/textmap#hit type xh yh [beg [end]] 2896 ## 指定した座標に対応する境界 index を取得します。 2897 ## 指定した座標以前の最も近い境界を求めます。 2898 ## 探索範囲に対応する境界がないときは最初の境界 beg を返します。 2899 ## 2900 ## @param[in] type 2901 ## 探索する点の種類を指定します。out または cur を指定します。 2902 ## out を指定したときは文字終端境界を探索します。 2903 ## cur を指定したときは文字開始境界(行送りを考慮に入れたもの)を探索します。 2904 ## @param[in] xh yh 2905 ## 探索する点を指定します。 2906 ## @param[in] beg end 2907 ## 探索する index の範囲を指定します。 2908 ## beg を省略したときは最初の境界位置が使用されます。 2909 ## end を省略したときは最後の境界位置が使用されます。 2910 ## 2911 ## @var[out] index 2912 ## 見つかった境界の番号を返します。 2913 ## @var[out] lx ly 2914 ## 見つかった境界の座標を返します。 2915 ## @var[out] rx ry 2916 ## 指定した座標以後の最も近い境界を返します。 2917 ## index が探索範囲の最後の境界のとき、または、 2918 ## lx ly が指定した座標と一致するとき lx ly と同一です。 2919 ## 2920 function ble/textmap#hit { 2921 ble/textmap#assert-up-to-date 2922 local getxy=ble/textmap#hit/.getxy.$1 2923 local xh=$2 yh=$3 beg=${4:-0} end=${5:-$_ble_textmap_length} 2924 2925 local -a pos 2926 if "$getxy" "$end"; ((yh>y||yh==y&&xh>x)); then 2927 index=$end 2928 lx=$x ly=$y 2929 rx=$x ry=$y 2930 elif "$getxy" "$beg"; ((yh<y||yh==y&&xh<x)); then 2931 index=$beg 2932 lx=$x ly=$y 2933 rx=$x ry=$y 2934 else 2935 # 2分法 2936 local l=0 u=$((end+1)) m 2937 while ((l+1<u)); do 2938 "$getxy" "$((m=(l+u)/2))" 2939 (((yh<y||yh==y&&xh<x)?(u=m):(l=m))) 2940 done 2941 "$getxy" "$((index=l))" 2942 lx=$x ly=$y 2943 (((ly<yh||ly==yh&&lx<xh)&&index<end)) && "$getxy" "$((index+1))" 2944 rx=$x ry=$y 2945 fi 2946 } 2947 2948 #------------------------------------------------------------------------------ 2949 # ble/canvas/goto.draw 2950 2951 ## @var _ble_canvas_x 2952 ## @var _ble_canvas_y 2953 ## 現在の (描画の為に動き回る) カーソル位置を保持します。 2954 _ble_canvas_x=0 2955 _ble_canvas_y=0 2956 _ble_canvas_excursion= 2957 2958 ## @fn ble/canvas/goto.draw x y opts 2959 ## 現在位置を指定した座標へ移動する制御系列を生成します。 2960 ## @param[in] x y 2961 ## 移動先のカーソルの座標を指定します。 2962 ## プロンプト原点が x=0 y=0 に対応します。 2963 function ble/canvas/goto.draw { 2964 local x=$1 y=$2 opts=$3 2965 2966 # Note #D1392: mc (midnight commander) は 2967 # sgr0 単体でもプロンプトと勘違いするので、 2968 # プロンプト更新もカーソル移動も不要の時は、 2969 # sgr0 も含めて何も出力しない。 2970 [[ :$opts: != *:sgr0:* ]] && 2971 ((x==_ble_canvas_x&&y==_ble_canvas_y)) && return 0 2972 2973 ble/canvas/put.draw "$_ble_term_sgr0" 2974 2975 ble/canvas/put-move-y.draw "$((y-_ble_canvas_y))" 2976 2977 local dx=$((x-_ble_canvas_x)) 2978 if ((dx!=0)); then 2979 if ((x==0)); then 2980 ble/canvas/put.draw "$_ble_term_cr" 2981 else 2982 ble/canvas/put-move-x.draw "$dx" 2983 fi 2984 fi 2985 2986 _ble_canvas_x=$x _ble_canvas_y=$y 2987 } 2988 2989 _ble_canvas_excursion_x= 2990 _ble_canvas_excursion_y= 2991 function ble/canvas/excursion-start.draw { 2992 [[ $_ble_canvas_excursion ]] && return 1 2993 _ble_canvas_excursion=1 2994 _ble_canvas_excursion_x=$_ble_canvas_x 2995 _ble_canvas_excursion_y=$_ble_canvas_y 2996 ble/canvas/put.draw "$_ble_term_sc" 2997 } 2998 function ble/canvas/excursion-end.draw { 2999 [[ $_ble_canvas_excursion ]] || return 1 3000 _ble_canvas_excursion= 3001 ble/canvas/put.draw "$_ble_term_rc" 3002 _ble_canvas_x=$_ble_canvas_excursion_x 3003 _ble_canvas_y=$_ble_canvas_excursion_y 3004 } 3005 3006 #------------------------------------------------------------------------------ 3007 # ble/canvas/panel 3008 3009 ## @arr _ble_canvas_panel_class 3010 ## 各パネルを管理する関数接頭辞を保持する。 3011 ## 3012 ## @arr _ble_canvas_panel_height 3013 ## 各パネルの高さを保持する。 3014 ## 現在 panel 0 が textarea で panel 2 が info に対応する。 3015 ## 3016 ## 開始した瞬間にキー入力をすると画面に echo されてしまうので、 3017 ## それを削除するために最初の編集文字列の行数を 1 とする。 3018 ## 3019 ## @var _ble_canvas_panel_focus 3020 ## 現在 focus のあるパネルの番号を保持する。 3021 ## 端末の現在位置はこのパネルの render が設定した位置に置かれる。 3022 ## 3023 ## @var _ble_canvas_panel_vfill 3024 ## 下部に寄せて表示されるパネルの開始番号を保持する。 3025 ## この変数が空文字列の時は全てのパネルは上部に表示される。 3026 _ble_canvas_panel_class=() 3027 _ble_canvas_panel_height=(1 0 0) 3028 _ble_canvas_panel_focus= 3029 _ble_canvas_panel_vfill= 3030 _ble_canvas_panel_bottom= # 現在下部に居るかどうか 3031 _ble_canvas_panel_tmargin='LINES!=1?1:0' # for visible-bell 3032 3033 ## @fn ble/canvas/panel/layout/.extract-heights 3034 ## @arr[out] mins maxs 3035 function ble/canvas/panel/layout/.extract-heights { 3036 local i n=${#_ble_canvas_panel_class[@]} 3037 for ((i=0;i<n;i++)); do 3038 local height=0:0 3039 ble/function#try "${_ble_canvas_panel_class[i]}#panel::getHeight" "$i" 3040 mins[i]=${height%:*} 3041 maxs[i]=${height#*:} 3042 done 3043 } 3044 3045 ## @fn ble/canvas/panel/layout/.determine-heights 3046 ## 最小高さ mins と希望高さ maxs から実際の高さ heights を決定します。 3047 ## @var[in] lines 3048 ## @arr[in] mins maxs 3049 ## @arr[out] heights 3050 function ble/canvas/panel/layout/.determine-heights { 3051 local i n=${#_ble_canvas_panel_class[@]} ret 3052 ble/arithmetic/sum "${mins[@]}"; local min=$ret 3053 ble/arithmetic/sum "${maxs[@]}"; local max=$ret 3054 if ((max<=lines)); then 3055 heights=("${maxs[@]}") 3056 elif ((min<=lines)); then 3057 local room=$((lines-min)) 3058 heights=("${mins[@]}") 3059 while ((room)); do 3060 local count=0 min_delta=-1 delta 3061 for ((i=0;i<n;i++)); do 3062 ((delta=maxs[i]-heights[i],delta>0)) || continue 3063 ((count++)) 3064 ((min_delta<0||min_delta>delta)) && min_delta=$delta 3065 done 3066 ((count==0)) && break 3067 3068 if ((count*min_delta<=room)); then 3069 for ((i=0;i<n;i++)); do 3070 ((maxs[i]-heights[i]>0)) || continue 3071 ((heights[i]+=min_delta)) 3072 done 3073 ((room-=count*min_delta)) 3074 else 3075 local delta=$((room/count)) rem=$((room%count)) count=0 3076 for ((i=0;i<n;i++)); do 3077 ((maxs[i]-heights[i]>0)) || continue 3078 ((heights[i]+=delta)) 3079 ((count++<rem)) && ((heights[i]++)) 3080 done 3081 ((room=0)) 3082 fi 3083 done 3084 else 3085 heights=("${mins[@]}") 3086 local excess=$((min-lines)) 3087 for ((i=n-1;i>=0;i--)); do 3088 local sub=$((heights[i]-heights[i]*lines/min)) 3089 if ((sub<excess)); then 3090 ((excess-=sub)) 3091 ((heights[i]-=sub)) 3092 else 3093 ((heights[i]-=excess)) 3094 break 3095 fi 3096 done 3097 fi 3098 } 3099 3100 ## @fn ble/canvas/panel/layout/.get-available-height index 3101 ## @var[out] ret 3102 function ble/canvas/panel/layout/.get-available-height { 3103 local index=$1 3104 local lines=$((${LINES:-25}-_ble_canvas_panel_tmargin)) 3105 local -a mins=() maxs=() 3106 ble/canvas/panel/layout/.extract-heights 3107 maxs[index]=${LINES:-25} 3108 local -a heights=() 3109 ble/canvas/panel/layout/.determine-heights 3110 ret=${heights[index]} 3111 } 3112 3113 function ble/canvas/panel/reallocate-height.draw { 3114 local lines=$((${LINES:-25}-_ble_canvas_panel_tmargin)) 3115 3116 local i n=${#_ble_canvas_panel_class[@]} 3117 local -a mins=() maxs=() 3118 ble/canvas/panel/layout/.extract-heights 3119 3120 local -a heights=() 3121 ble/canvas/panel/layout/.determine-heights 3122 3123 # shrink 3124 for ((i=0;i<n;i++)); do 3125 ((heights[i]<_ble_canvas_panel_height[i])) && 3126 ble/canvas/panel#set-height.draw "$i" "${heights[i]}" 3127 done 3128 3129 # expand 3130 for ((i=0;i<n;i++)); do 3131 ((heights[i]>_ble_canvas_panel_height[i])) && 3132 ble/canvas/panel#set-height.draw "$i" "${heights[i]}" 3133 done 3134 } 3135 function ble/canvas/panel/is-last-line { 3136 local ret 3137 ble/arithmetic/sum "${_ble_canvas_panel_height[@]}" 3138 ((_ble_canvas_y==ret-1)) 3139 } 3140 3141 function ble/canvas/panel/goto-bottom-dock.draw { 3142 if [[ ! $_ble_canvas_panel_bottom ]]; then 3143 _ble_canvas_panel_bottom=1 3144 ble/canvas/excursion-start.draw 3145 ble/canvas/put-cup.draw "$LINES" 0 # 一番下の行に移動 3146 ble/arithmetic/sum "${_ble_canvas_panel_height[@]}" 3147 ((_ble_canvas_x=0,_ble_canvas_y=ret-1)) 3148 fi 3149 } 3150 function ble/canvas/panel/goto-top-dock.draw { 3151 if [[ $_ble_canvas_panel_bottom ]]; then 3152 _ble_canvas_panel_bottom= 3153 ble/canvas/excursion-end.draw 3154 fi 3155 } 3156 function ble/canvas/panel/goto-vfill.draw { 3157 ble/canvas/panel/has-bottom-dock || return 1 3158 local ret 3159 ble/canvas/panel/goto-top-dock.draw 3160 ble/arithmetic/sum "${_ble_canvas_panel_height[@]::_ble_canvas_panel_vfill}" 3161 ble/canvas/goto.draw 0 "$ret" sgr0 3162 return 0 3163 } 3164 ## @fn ble/canvas/panel/save-position opts 3165 ## @var[out] ret 3166 function ble/canvas/panel/save-position { 3167 ret=$_ble_canvas_x:$_ble_canvas_y:$_ble_canvas_panel_bottom 3168 [[ :$2: == *:goto-top-dock:* ]] && 3169 ble/canvas/panel/goto-top-dock.draw 3170 } 3171 ## @fn ble/canvas/panel/load-position x:y:bottom 3172 ## ble/canvas/panel/save-position で記録した情報を元に 3173 ## 元の位置に戻ります。 3174 function ble/canvas/panel/load-position { 3175 local -a DRAW_BUFF=() 3176 ble/canvas/panel/load-position.draw "$@" 3177 ble/canvas/bflush.draw 3178 } 3179 function ble/canvas/panel/load-position.draw { 3180 local data=$1 3181 local x=${data%%:*}; data=${data#*:} 3182 local y=${data%%:*}; data=${data#*:} 3183 local bottom=$data 3184 if [[ $bottom ]]; then 3185 ble/canvas/panel/goto-bottom-dock.draw 3186 else 3187 ble/canvas/panel/goto-top-dock.draw 3188 fi 3189 ble/canvas/goto.draw "$x" "$y" 3190 } 3191 3192 function ble/canvas/panel/has-bottom-dock { 3193 local ret; ble/canvas/panel/bottom-dock#height 3194 ((ret)) 3195 } 3196 function ble/canvas/panel/bottom-dock#height { 3197 ret=0 3198 [[ $_ble_canvas_panel_vfill && $_ble_term_rc ]] || return 0 3199 ble/arithmetic/sum "${_ble_canvas_panel_height[@]:_ble_canvas_panel_vfill}" 3200 } 3201 function ble/canvas/panel/top-dock#height { 3202 if [[ $_ble_canvas_panel_vfill && $_ble_term_rc ]]; then 3203 ble/arithmetic/sum "${_ble_canvas_panel_height[@]::_ble_canvas_panel_vfill}" 3204 else 3205 ble/arithmetic/sum "${_ble_canvas_panel_height[@]}" 3206 fi 3207 } 3208 ## @fn ble/canvas/panel/bottom-dock#invalidate 3209 ## Invalidate all bottom panels (with non-zero height) 3210 function ble/canvas/panel/bottom-dock#invalidate { 3211 [[ $_ble_canvas_panel_vfill && $_ble_term_rc ]] || return 0 3212 local index n=${#_ble_canvas_panel_class[@]} 3213 for ((index=_ble_canvas_panel_vfill;index<n;index++)); do 3214 local panel_class=${_ble_canvas_panel_class[index]} 3215 local panel_height=${_ble_canvas_panel_height[index]} 3216 ((panel_height)) && 3217 ble/function#try "$panel_class#panel::invalidate" "$index" 0 "$panel_height" 3218 done 3219 } 3220 function ble/canvas/panel#is-bottom { 3221 [[ $_ble_canvas_panel_vfill && $_ble_term_rc ]] && (($1>=_ble_canvas_panel_vfill)) 3222 } 3223 3224 ## @fn ble/canvas/panel#get-origin 3225 ## @var[out] x y 3226 function ble/canvas/panel#get-origin { 3227 local ret index=$1 prefix= 3228 [[ $2 == --prefix=* ]] && prefix=${2#*=} 3229 ble/arithmetic/sum "${_ble_canvas_panel_height[@]::index}" 3230 ((${prefix}x=0,${prefix}y=ret)) 3231 } 3232 function ble/canvas/panel#goto.draw { 3233 local index=$1 x=${2-0} y=${3-0} opts=$4 ret 3234 if ble/canvas/panel#is-bottom "$index"; then 3235 ble/canvas/panel/goto-bottom-dock.draw 3236 else 3237 ble/canvas/panel/goto-top-dock.draw 3238 fi 3239 ble/arithmetic/sum "${_ble_canvas_panel_height[@]::index}" 3240 ble/canvas/goto.draw "$x" "$((ret+y))" "$opts" 3241 } 3242 ## @fn ble/canvas/panel#put.draw panel text x y 3243 function ble/canvas/panel#put.draw { 3244 ble/canvas/put.draw "$2" 3245 ble/canvas/panel#report-cursor-position "$1" "$3" "$4" 3246 } 3247 function ble/canvas/panel#report-cursor-position { 3248 local index=$1 x=${2-0} y=${3-0} ret 3249 ble/arithmetic/sum "${_ble_canvas_panel_height[@]::index}" 3250 ((_ble_canvas_x=x,_ble_canvas_y=ret+y)) 3251 } 3252 3253 function ble/canvas/panel/increase-total-height.draw { 3254 local delta=$1 3255 ((delta>0)) || return 1 3256 3257 local ret 3258 ble/canvas/panel/top-dock#height; local top_height=$ret 3259 ble/canvas/panel/bottom-dock#height; local bottom_height=$ret 3260 if ((bottom_height)); then 3261 ble/canvas/panel/goto-top-dock.draw 3262 if [[ $_ble_term_DECSTBM ]]; then 3263 ble/canvas/excursion-start.draw 3264 ble/canvas/put.draw $'\e[1;'$((LINES-bottom_height))'r' 3265 ble/canvas/excursion-end.draw 3266 ble/canvas/goto.draw 0 "$((top_height==0?0:top_height-1))" sgr0 3267 ble/canvas/put-ind.draw "$((top_height-1+delta-_ble_canvas_y))" 3268 ((_ble_canvas_y=top_height-1+delta)) 3269 ble/canvas/excursion-start.draw 3270 ble/canvas/put.draw "$_ble_term_DECSTBM_reset" 3271 ble/canvas/excursion-end.draw 3272 return 0 3273 else 3274 ble/canvas/panel/bottom-dock#invalidate 3275 fi 3276 fi 3277 3278 local old_height=$((top_height+bottom_height)) 3279 local new_height=$((old_height+delta)) 3280 ble/canvas/goto.draw 0 "$((top_height==0?0:top_height-1))" sgr0 3281 ble/canvas/put-ind.draw "$((new_height-1-_ble_canvas_y))"; ((_ble_canvas_y=new_height-1)) 3282 ble/canvas/panel/goto-vfill.draw && 3283 ble/canvas/put-il.draw "$delta" vfill 3284 } 3285 3286 ## @fn ble/canvas/panel#set-height.draw panel height opts 3287 ## @param[in] opts 3288 ## shift ... 範囲の先頭で行を追加・削除します。 3289 function ble/canvas/panel#set-height.draw { 3290 local index=$1 new_height=$2 opts=$3 3291 ((new_height<0)) && new_height=0 3292 local old_height=${_ble_canvas_panel_height[index]} 3293 local delta=$((new_height-old_height)) 3294 3295 if ((delta==0)); then 3296 if [[ :$opts: == *:clear:* ]]; then 3297 ble/canvas/panel#clear.draw "$index" 3298 return "$?" 3299 else 3300 return 1 3301 fi 3302 elif ((delta>0)); then 3303 # 新しく行を挿入 3304 ble/canvas/panel/increase-total-height.draw "$delta" 3305 ble/canvas/panel/goto-vfill.draw && 3306 ble/canvas/put-dl.draw "$delta" vfill 3307 ((_ble_canvas_panel_height[index]=new_height)) 3308 3309 case :$opts: in 3310 (*:clear:*) 3311 ble/canvas/panel#goto.draw "$index" 0 0 sgr0 3312 ble/canvas/put-clear-lines.draw "$old_height" "$new_height" panel ;; 3313 (*:shift:*) # 先頭に行挿入 3314 ble/canvas/panel#goto.draw "$index" 0 0 sgr0 3315 ble/canvas/put-il.draw "$delta" panel ;; 3316 (*) # 末尾に行挿入 3317 ble/canvas/panel#goto.draw "$index" 0 "$old_height" sgr0 3318 ble/canvas/put-il.draw "$delta" panel ;; 3319 esac 3320 3321 else 3322 ((delta=-delta)) 3323 3324 case :$opts: in 3325 (*:clear:*) 3326 ble/canvas/panel#goto.draw "$index" 0 0 sgr0 3327 ble/canvas/put-clear-lines.draw "$old_height" "$new_height" panel ;; 3328 (*:shift:*) # 先頭を削除 3329 ble/canvas/panel#goto.draw "$index" 0 0 sgr0 3330 ble/canvas/put-dl.draw "$delta" panel ;; 3331 (*) # 末尾を削除 3332 ble/canvas/panel#goto.draw "$index" 0 "$new_height" sgr0 3333 ble/canvas/put-dl.draw "$delta" panel ;; 3334 esac 3335 3336 ((_ble_canvas_panel_height[index]=new_height)) 3337 ble/canvas/panel/goto-vfill.draw && 3338 ble/canvas/put-il.draw "$delta" vfill 3339 fi 3340 ble/function#try "${_ble_canvas_panel_class[index]}#panel::onHeightChange" "$index" 3341 3342 return 0 3343 } 3344 function ble/canvas/panel#increase-height.draw { 3345 local index=$1 delta=$2 opts=$3 3346 ble/canvas/panel#set-height.draw "$index" "$((_ble_canvas_panel_height[index]+delta))" "$opts" 3347 } 3348 3349 function ble/canvas/panel#set-height-and-clear.draw { 3350 local index=$1 new_height=$2 3351 ble/canvas/panel#set-height.draw "$index" "$new_height" clear 3352 } 3353 3354 function ble/canvas/panel#clear.draw { 3355 local index=$1 3356 local height=${_ble_canvas_panel_height[index]} 3357 if ((height)); then 3358 ble/canvas/panel#goto.draw "$index" 0 0 sgr0 3359 ble/canvas/put-clear-lines.draw "$height" 3360 fi 3361 } 3362 function ble/canvas/panel#clear-after.draw { 3363 local index=$1 x=$2 y=$3 3364 local height=${_ble_canvas_panel_height[index]} 3365 ((y<height)) || return 1 3366 3367 ble/canvas/panel#goto.draw "$index" "$x" "$y" sgr0 3368 ble/canvas/put.draw "$_ble_term_el" 3369 local rest_lines=$((height-(y+1))) 3370 if ((rest_lines)); then 3371 ble/canvas/put.draw "$_ble_term_ind" 3372 [[ $_ble_term_ind != $'\eD' ]] && 3373 ble/canvas/put-hpa.draw "$((x+1))" 3374 ble/canvas/put-clear-lines.draw "$rest_lines" 3375 ble/canvas/put-cuu.draw 1 3376 fi 3377 } 3378 3379 ## @fn ble/canvas/panel/invalidate 3380 ## Invalidate all panels (with non-zero height) 3381 function ble/canvas/panel/clear { 3382 local -a DRAW_BUFF=() 3383 local index n=${#_ble_canvas_panel_class[@]} 3384 for ((index=0;index<n;index++)); do 3385 local panel_class=${_ble_canvas_panel_class[index]} 3386 local panel_height=${_ble_canvas_panel_height[index]} 3387 ((panel_height)) || continue 3388 ble/canvas/panel#clear.draw "$index" 3389 ble/function#try "$panel_class#panel::invalidate" "$index" 0 "$panel_height" 3390 done 3391 ble/canvas/bflush.draw 3392 } 3393 function ble/canvas/panel/invalidate { 3394 local opts=$1 3395 if [[ :$opts: == *:height:* ]]; then 3396 local -a DRAW_BUFF=() 3397 ble/canvas/excursion-end.draw 3398 ble/canvas/put.draw "$_ble_term_cr$_ble_term_ed" 3399 _ble_canvas_x=0 _ble_canvas_y=0 3400 ble/array#fill-range _ble_canvas_panel_height 0 "${#_ble_canvas_panel_height[@]}" 0 3401 ble/canvas/panel/reallocate-height.draw 3402 ble/canvas/bflush.draw 3403 fi 3404 3405 local index n=${#_ble_canvas_panel_class[@]} 3406 for ((index=0;index<n;index++)); do 3407 local panel_class=${_ble_canvas_panel_class[index]} 3408 local panel_height=${_ble_canvas_panel_height[index]} 3409 ((panel_height)) || continue 3410 ble/function#try "$panel_class#panel::invalidate" "$index" 0 "$panel_height" 3411 done 3412 } 3413 function ble/canvas/panel/render { 3414 local index n=${#_ble_canvas_panel_class[@]} pos= 3415 for ((index=0;index<n;index++)); do 3416 local panel_class=${_ble_canvas_panel_class[index]} 3417 local panel_height=${_ble_canvas_panel_height[index]} 3418 # Note: panel::render の中で高さを更新するので panel_height==0 で 3419 # あっても panel::render を呼び出す。 3420 ble/function#try "$panel_class#panel::render" "$index" 0 "$panel_height" 3421 if [[ $_ble_canvas_panel_focus ]] && ((index==_ble_canvas_panel_focus)); then 3422 local ret; ble/canvas/panel/save-position; local pos=$ret 3423 fi 3424 done 3425 [[ $pos ]] && ble/canvas/panel/load-position "$pos" 3426 return 0 3427 } 3428 ## @fn ble/canvas/panel/ensure-terminal-top-line 3429 ## visible-bell で使う為 3430 function ble/canvas/panel/ensure-tmargin.draw { 3431 local tmargin=$((_ble_canvas_panel_tmargin)) 3432 ((tmargin>LINES)) && tmargin=$LINES 3433 ((tmargin>0)) || return 0 3434 3435 local ret 3436 ble/canvas/panel/save-position; local pos=$ret 3437 ble/canvas/panel/goto-top-dock.draw 3438 3439 ble/canvas/panel/top-dock#height; local top_height=$ret 3440 ble/canvas/panel/bottom-dock#height; local bottom_height=$ret 3441 if ((bottom_height)); then 3442 if [[ $_ble_term_DECSTBM ]]; then 3443 ble/canvas/excursion-start.draw 3444 ble/canvas/put.draw $'\e[1;'$((LINES-bottom_height))'r' 3445 ble/canvas/excursion-end.draw 3446 ble/canvas/goto.draw 0 0 sgr0 3447 if [[ $_ble_term_ri ]]; then 3448 ble/canvas/put-ri.draw "$tmargin" 3449 ble/canvas/put-cud.draw "$tmargin" 3450 else 3451 # RI がない時 3452 ble/canvas/put-ind.draw "$((top_height-1+tmargin))" 3453 ble/canvas/put-cuu.draw "$((top_height-1+tmargin))" 3454 ble/canvas/excursion-start.draw 3455 ble/canvas/put-cup.draw 1 1 3456 ble/canvas/put-il.draw "$tmargin" no-lastline 3457 ble/canvas/excursion-end.draw 3458 fi 3459 ble/canvas/excursion-start.draw 3460 ble/canvas/put.draw "$_ble_term_DECSTBM_reset" 3461 ble/canvas/excursion-end.draw 3462 ble/canvas/panel/load-position.draw "$pos" 3463 return 0 3464 else 3465 ble/canvas/panel/bottom-dock#invalidate 3466 fi 3467 fi 3468 3469 ble/canvas/goto.draw 0 0 sgr0 3470 if [[ $_ble_term_ri ]]; then 3471 ble/canvas/put-ri.draw "$tmargin" 3472 ble/canvas/put-cud.draw "$tmargin" 3473 else 3474 # RI がない時 3475 local total_height=$((top_height+bottom_height)) 3476 ble/canvas/put-ind.draw "$((total_height-1+tmargin))" 3477 ble/canvas/put-cuu.draw "$((total_height-1+tmargin))" 3478 if [[ $_ble_term_rc ]]; then 3479 ble/canvas/excursion-start.draw 3480 ble/canvas/put-cup.draw 1 1 3481 ble/canvas/put-il.draw "$tmargin" no-lastline 3482 ble/canvas/excursion-end.draw 3483 else 3484 ble/canvas/put-il.draw "$tmargin" no-lastline 3485 fi 3486 ble/canvas/put-cud.draw "$tmargin" 3487 fi 3488 ble/canvas/panel/load-position.draw "$pos" 3489 }