color.sh (70865B)
1 #!/bin/bash 2 3 # gflags 4 5 _ble_color_gflags_Bold=0x01 6 _ble_color_gflags_Italic=0x02 7 _ble_color_gflags_Underline=0x04 8 _ble_color_gflags_Revert=0x08 9 _ble_color_gflags_Invisible=0x10 10 _ble_color_gflags_Strike=0x20 11 _ble_color_gflags_Blink=0x40 12 13 _ble_color_gflags_DecorationMask=0x77 14 _ble_color_gflags_FgMask=0x00000000FFFFFF00 15 _ble_color_gflags_BgMask=0x00FFFFFF00000000 16 _ble_color_gflags_FgShift=8 17 _ble_color_gflags_BgShift=32 18 _ble_color_gflags_FgIndexed=0x0100000000000000 19 _ble_color_gflags_BgIndexed=0x0200000000000000 20 21 _ble_color_index_colors_default=$_ble_term_colors 22 if [[ $TERM == xterm* || $TERM == *-256color || $TERM == kterm* ]]; then 23 _ble_color_index_colors_default=256 24 elif [[ $TERM == *-88color ]]; then 25 _ble_color_index_colors_default=88 26 fi 27 28 bleopt/declare -v term_true_colors semicolon 29 bleopt/declare -v term_index_colors auto 30 31 function bleopt/check:term_true_colors { 32 ble/color/g2sgr/.clear-cache 33 return 0 34 } 35 function bleopt/check:term_index_colors { 36 ble/color/g2sgr/.clear-cache 37 return 0 38 } 39 function ble/color/initialize-term-colors { 40 local fields 41 ble/string#split fields \; "$_ble_term_DA2R" 42 if [[ $bleopt_term_true_colors == auto ]]; then 43 # truecolor support 自動判定 (暫定実装) 44 local value= 45 if [[ $TERM == *-24bit || $TERM == *-direct ]]; then 46 value=colon 47 elif [[ $TERM == *-24bits || $TERM == *-truecolor || $COLORTERM == *24bit* || $COLORTERM == *truecolor* ]]; then 48 value=semicolon 49 else 50 case ${fields[0]} in 51 (83) # screen (truecolor on にしている必要がある。判定方法は不明) 52 if ((fields[1]>=49900)); then 53 value=semicolon 54 fi ;; 55 (67) 56 if ((fields[1]>=100000)); then 57 : # cygwin terminal 58 else 59 # contra 60 value=colon 61 fi ;; 62 esac 63 fi 64 [[ $value ]] && 65 bleopt term_true_colors="$value" 66 fi 67 } 68 blehook term_DA2R!=ble/color/initialize-term-colors 69 70 71 function ble-color-show { 72 if (($#)); then 73 ble/base/print-usage-for-no-argument-command 'Update and reload ble.sh.' "$@" 74 return "$?" 75 fi 76 77 local cols=$(((${COLUMNS:-80}-1)/4)) 78 ((cols<1?(cols=1):(cols>16&&(cols=16)))) 79 local bg bg0 bgN ret gflags=$((_ble_color_gflags_BgIndexed|_ble_color_gflags_FgIndexed)) 80 for ((bg0=0;bg0<256;bg0+=cols)); do 81 ((bgN=bg0+cols,bgN<256||(bgN=256))) 82 for ((bg=bg0;bg<bgN;bg++)); do 83 ble/color/g2sgr "$((gflags|bg<<_ble_color_gflags_BgShift))" 84 printf '%s%03d ' "$ret" "$bg" 85 done 86 printf '%s\n' "$_ble_term_sgr0" 87 for ((bg=bg0;bg<bgN;bg++)); do 88 ble/color/g2sgr "$((gflags|bg<<_ble_color_gflags_BgShift|15<<_ble_color_gflags_FgShift))" 89 printf '%s%03d ' "$ret" "$bg" 90 done 91 printf '%s\n' "$_ble_term_sgr0" 92 done 93 } 94 function ble-palette { 95 if (($#)); then 96 ble/base/print-usage-for-no-argument-command 'Update and reload ble.sh.' "$@" 97 return "$?" 98 fi 99 100 if ((${COLUMNS:-80}<80)); then 101 ble-color-show 102 return 0 103 fi 104 105 local ret gflags=$((_ble_color_gflags_BgIndexed|_ble_color_gflags_FgIndexed)) 106 local l c bg 107 for ((l=0;l<2;l++)); do 108 for ((c=0;c<16;c++)); do 109 ((bg=l/2*8+c)) 110 ble/color/g2sgr "$((gflags|bg<<_ble_color_gflags_BgShift|(l%2?15:0)<<_ble_color_gflags_FgShift))" 111 printf '%s%03d ' "$ret" "$bg" 112 done 113 printf '%s\n' "$_ble_term_sgr0" 114 done 115 116 local l p B G R 117 for ((l=0;l<24;l++)); do 118 ((G=l%12/2)) 119 for ((p=0;p<3;p++)); do 120 ((R=l>=12?3+p:p)) 121 for ((B=0;B<6;B++)); do 122 ((bg=16+R*36+G*6+B)) 123 ble/color/g2sgr "$((gflags|bg<<_ble_color_gflags_BgShift|(l%2?15:0)<<_ble_color_gflags_FgShift))" 124 printf '%s%03d ' "$ret" "$bg" 125 done 126 if ((p+1<3)); then 127 printf '%s ' "$_ble_term_sgr0" 128 else 129 printf '%s\n' "$_ble_term_sgr0" 130 fi 131 done 132 done 133 134 local l c K 135 for ((l=0;l<4;l++)); do 136 for ((c=0;c<12;c++)); do 137 ((K=l/2*12+c)) 138 ((bg=232+K)) 139 ble/color/g2sgr "$((gflags|bg<<_ble_color_gflags_BgShift|(l%2?15:0)<<_ble_color_gflags_FgShift))" 140 printf '%s%03d ' "$ret" "$bg" 141 done 142 printf '%s\n' "$_ble_term_sgr0" 143 done 144 } 145 146 147 ## @fn ble/color/g2sgr g 148 ## @fn ble/color/g2sgr-ansi g 149 ## @param[in] g 150 ## @var[out] ret 151 ## 152 # 153 # Note: もし SGR 以外の制御機能を使って (tput 等の出力を用いて) 描画シー 154 # ケンスを構築する様に拡張する場合には、 155 # ble/textarea#slice-text-buffer に於いて行っている CR LF の組の検出 156 # において、間に許容する制御機能の種類に注意する。もし考慮に入れてい 157 # ない物をここで使いたい時には、それを 158 # ble/textarea#slice-text-buffer の正規表現に追加しなければならない。 159 # 160 _ble_color_g2sgr_version=0 161 _ble_color_g2sgr=() 162 _ble_color_g2sgr_ansi=() 163 function ble/color/g2sgr/.impl { 164 local g=$(($1)) 165 166 local sgr=0 167 ((g&_ble_color_gflags_Bold)) && sgr="$sgr;${_ble_term_sgr_bold:-1}" 168 ((g&_ble_color_gflags_Italic)) && sgr="$sgr;${_ble_term_sgr_sitm:-3}" 169 ((g&_ble_color_gflags_Underline)) && sgr="$sgr;${_ble_term_sgr_smul:-4}" 170 ((g&_ble_color_gflags_Blink)) && sgr="$sgr;${_ble_term_sgr_blink:-5}" 171 ((g&_ble_color_gflags_Revert)) && sgr="$sgr;${_ble_term_sgr_rev:-7}" 172 ((g&_ble_color_gflags_Invisible)) && sgr="$sgr;${_ble_term_sgr_invis:-8}" 173 ((g&_ble_color_gflags_Strike)) && sgr="$sgr;${_ble_term_sgr_strike:-9}" 174 if ((g&_ble_color_gflags_FgIndexed)); then 175 local fg=$((g>>8&0xFF)) 176 ble/color/.color2sgrfg "$fg" 177 sgr="$sgr;$ret" 178 elif ((g&_ble_color_gflags_FgMask)); then 179 local rgb=$((1<<24|g>>8&0xFFFFFF)) 180 ble/color/.color2sgrfg "$rgb" 181 sgr="$sgr;$ret" 182 fi 183 if ((g&_ble_color_gflags_BgIndexed)); then 184 local bg=$((g>>32&0xFF)) 185 ble/color/.color2sgrbg "$bg" 186 sgr="$sgr;$ret" 187 elif ((g&_ble_color_gflags_BgMask)); then 188 local rgb=$((1<<24|g>>32&0xFFFFFF)) 189 ble/color/.color2sgrbg "$rgb" 190 sgr="$sgr;$ret" 191 fi 192 193 ret=$'\e['$sgr'm' 194 _ble_color_g2sgr[$1]=$ret 195 } 196 function ble/color/g2sgr/.clear-cache { 197 _ble_color_g2sgr=() 198 ((_ble_color_g2sgr_version++)) 199 } 200 function ble/color/g2sgr { 201 ret=${_ble_color_g2sgr[$1]} 202 [[ $ret ]] || ble/color/g2sgr/.impl "$1" 203 } 204 function ble/color/g2sgr-ansi/.impl { 205 local g=$(($1)) 206 207 local sgr=0 208 ((g&_ble_color_gflags_Bold)) && sgr="$sgr;1" 209 ((g&_ble_color_gflags_Italic)) && sgr="$sgr;3" 210 ((g&_ble_color_gflags_Underline)) && sgr="$sgr;4" 211 ((g&_ble_color_gflags_Blink)) && sgr="$sgr;5" 212 ((g&_ble_color_gflags_Revert)) && sgr="$sgr;7" 213 ((g&_ble_color_gflags_Invisible)) && sgr="$sgr;8" 214 ((g&_ble_color_gflags_Strike)) && sgr="$sgr;9" 215 if ((g&_ble_color_gflags_FgIndexed)); then 216 local fg=$((g>>8&0xFF)) 217 sgr="$sgr;38:5:$fg" 218 elif ((g&_ble_color_gflags_FgMask)); then 219 local rgb=$((1<<24|g>>8&0xFFFFFF)) 220 local R=$((rgb>>16&0xFF)) G=$((rgb>>8&0xFF)) B=$((rgb&0xFF)) 221 sgr="$sgr;38:2::$R:$G:$B" 222 fi 223 if ((g&_ble_color_gflags_BgIndexed)); then 224 local bg=$((g>>32&0xFF)) 225 sgr="$sgr;48:5:$bg" 226 elif ((g&_ble_color_gflags_BgMask)); then 227 local rgb=$((1<<24|g>>32&0xFFFFFF)) 228 local R=$((rgb>>16&0xFF)) G=$((rgb>>8&0xFF)) B=$((rgb&0xFF)) 229 sgr="$sgr;48:2::$R:$G:$B" 230 fi 231 232 ret=$'\e['$sgr'm' 233 _ble_color_g2sgr_ansi[$1]=$ret 234 } 235 function ble/color/g2sgr-ansi { 236 ret=${_ble_color_g2sgr_ansi[$1]} 237 [[ $ret ]] || ble/color/g2sgr-ansi/.impl "$1" 238 } 239 240 function ble/color/g#setfg-clear { 241 (($1&=~(_ble_color_gflags_FgIndexed|_ble_color_gflags_FgMask))) 242 } 243 function ble/color/g#setbg-clear { 244 (($1&=~(_ble_color_gflags_BgIndexed|_ble_color_gflags_BgMask))) 245 } 246 function ble/color/g#setfg-index { 247 local _ble_local_color=$2 248 (($1=$1&~_ble_color_gflags_FgMask|_ble_color_gflags_FgIndexed|(_ble_local_color&0xFF)<<8)) # index color 249 } 250 function ble/color/g#setbg-index { 251 local _ble_local_color=$2 252 (($1=$1&~_ble_color_gflags_BgMask|_ble_color_gflags_BgIndexed|(_ble_local_color&0xFF)<<32)) # index color 253 } 254 function ble/color/g#setfg-rgb { 255 local _ble_local_R=$2 256 local _ble_local_G=$3 257 local _ble_local_B=$4 258 ((_ble_local_R&=0xFF,_ble_local_G&=0xFF,_ble_local_B&=0xFF)) 259 if ((_ble_local_R==0&&_ble_local_G==0&&_ble_local_B==0)); then 260 ble/color/g#setfg-index "$1" 16 261 else 262 (($1=$1&~(_ble_color_gflags_FgIndexed|_ble_color_gflags_FgMask)|_ble_local_R<<24|_ble_local_G<<16|_ble_local_B<<8)) # true color 263 fi 264 } 265 function ble/color/g#setbg-rgb { 266 local _ble_local_R=$2 267 local _ble_local_G=$3 268 local _ble_local_B=$4 269 ((_ble_local_R&=0xFF,_ble_local_G&=0xFF,_ble_local_B&=0xFF)) 270 if ((_ble_local_R==0&&_ble_local_G==0&&_ble_local_B==0)); then 271 ble/color/g#setbg-index "$1" 16 272 else 273 (($1=$1&~(_ble_color_gflags_BgIndexed|_ble_color_gflags_BgMask)|_ble_local_R<<48|_ble_local_G<<40|_ble_local_B<<32)) # true color 274 fi 275 } 276 function ble/color/g#setfg-cmyk { 277 local _ble_local_C=$2 278 local _ble_local_M=$3 279 local _ble_local_Y=$4 280 local _ble_local_K=${5:-0} 281 ((_ble_local_K=~_ble_local_K&0xFF, 282 _ble_local_C=(~_ble_local_C&0xFF)*_ble_local_K/255, 283 _ble_local_M=(~_ble_local_M&0xFF)*_ble_local_K/255, 284 _ble_local_Y=(~_ble_local_Y&0xFF)*_ble_local_K/255)) 285 ble/color/g#setfg-rgb "$_ble_local_C" "$_ble_local_M" "$_ble_local_Y" 286 } 287 function ble/color/g#setbg-cmyk { 288 local _ble_local_C=$2 289 local _ble_local_M=$3 290 local _ble_local_Y=$4 291 local _ble_local_K=${5:-0} 292 ((_ble_local_K=~_ble_local_K&0xFF, 293 _ble_local_C=(~_ble_local_C&0xFF)*_ble_local_K/255, 294 _ble_local_M=(~_ble_local_M&0xFF)*_ble_local_K/255, 295 _ble_local_Y=(~_ble_local_Y&0xFF)*_ble_local_K/255)) 296 ble/color/g#setbg-rgb "$1" "$_ble_local_C" "$_ble_local_M" "$_ble_local_Y" 297 } 298 function ble/color/g#setfg { 299 local _ble_local_color=$2 300 if ((_ble_local_color<0)); then 301 ble/color/g#setfg-clear "$1" 302 elif ((_ble_local_color>=0x1000000)); then 303 if ((_ble_local_color==0x1000000)); then 304 ble/color/g#setfg-index "$1" 16 305 else 306 (($1=$1&~(_ble_color_gflags_FgIndexed|_ble_color_gflags_FgMask)|(_ble_local_color&0xFFFFFF)<<8)) # true color 307 fi 308 else 309 ble/color/g#setfg-index "$1" "$_ble_local_color" 310 fi 311 } 312 function ble/color/g#setbg { 313 local _ble_local_color=$2 314 if ((_ble_local_color<0)); then 315 ble/color/g#setbg-clear "$1" 316 elif ((_ble_local_color>=0x1000000)); then 317 if ((_ble_local_color==0x1000000)); then 318 ble/color/g#setbg-index "$1" 16 319 else 320 (($1=$1&~(_ble_color_gflags_BgIndexed|_ble_color_gflags_BgMask)|(_ble_local_color&0xFFFFFF)<<32)) # true color 321 fi 322 else 323 ble/color/g#setbg-index "$1" "$_ble_local_color" 324 fi 325 } 326 ## @fn ble/color/g#append g g2 327 ## g に描画属性 g2 を上書きします。 328 ## @param[in,out] g 329 ## @param[in] g2 330 function ble/color/g#append { 331 local _ble_local_g2=$2 332 ((_ble_local_g2&(_ble_color_gflags_FgMask|_ble_color_gflags_FgIndexed))) && 333 (($1&=~(_ble_color_gflags_FgMask|_ble_color_gflags_FgIndexed))) 334 ((_ble_local_g2&(_ble_color_gflags_BgMask|_ble_color_gflags_BgIndexed))) && 335 (($1&=~(_ble_color_gflags_BgMask|_ble_color_gflags_BgIndexed))) 336 (($1|=_ble_local_g2)) 337 } 338 function ble/color/g#compose { 339 (($1=($2))) 340 local _ble_local_g2 341 for _ble_local_g2 in "${@:3}"; do 342 ble/color/g#append "$1" "$_ble_local_g2" 343 done 344 } 345 function ble/color/g.setfg { ble/color/g#setfg g "$@"; } 346 function ble/color/g.setbg { ble/color/g#setbg g "$@"; } 347 function ble/color/g.setfg-clear { ble/color/g#setfg-clear g "$@"; } 348 function ble/color/g.setbg-clear { ble/color/g#setbg-clear g "$@"; } 349 function ble/color/g.setfg-index { ble/color/g#setfg-index g "$@"; } 350 function ble/color/g.setbg-index { ble/color/g#setbg-index g "$@"; } 351 function ble/color/g.setfg-rgb { ble/color/g#setfg-rgb g "$@"; } 352 function ble/color/g.setbg-rgb { ble/color/g#setbg-rgb g "$@"; } 353 function ble/color/g.setfg-cmyk { ble/color/g#setfg-cmyk g "$@"; } 354 function ble/color/g.setbg-cmyk { ble/color/g#setbg-cmyk g "$@"; } 355 function ble/color/g.append { ble/color/g#append g "$@"; } 356 function ble/color/g.compose { ble/color/g#compose g "$@"; } 357 358 function ble/color/g#getfg { 359 local g=$1 360 if ((g&_ble_color_gflags_FgIndexed)); then 361 ((ret=g>>8&0xFF)) 362 elif ((g&_ble_color_gflags_FgMask)); then 363 ((ret=0x1000000|(g>>8&0xFFFFFF))) 364 else 365 ((ret=-1)) 366 fi 367 } 368 function ble/color/g#getbg { 369 local g=$1 370 if ((g&_ble_color_gflags_BgIndexed)); then 371 ((ret=g>>32&0xFF)) 372 elif ((g&_ble_color_gflags_BgMask)); then 373 ((ret=0x1000000|(g>>32&0xFFFFFF))) 374 else 375 ((ret=-1)) 376 fi 377 } 378 function ble/color/g#compute-fg { 379 local g=$1 380 if ((g&_ble_color_gflags_Invisible)); then 381 ble/color/g#compute-bg "$g" 382 elif ((g&_ble_color_gflags_Revert)); then 383 ble/color/g#getbg "$g" 384 else 385 ble/color/g#getfg "$g" 386 fi 387 } 388 function ble/color/g#compute-bg { 389 local g=$1 390 if ((g&_ble_color_gflags_Revert)); then 391 ble/color/g#getfg "$g" 392 else 393 ble/color/g#getbg "$g" 394 fi 395 } 396 397 ## @fn ble/color/gspec2g gspec 398 ## @param[in] gspec 399 ## @var[out] ret 400 function ble/color/gspec2g { 401 local g=0 entry 402 for entry in ${1//,/ }; do 403 case $entry in 404 (bold) ((g|=_ble_color_gflags_Bold)) ;; 405 (underline) ((g|=_ble_color_gflags_Underline)) ;; 406 (blink) ((g|=_ble_color_gflags_Blink)) ;; 407 (invis) ((g|=_ble_color_gflags_Invisible)) ;; 408 (reverse) ((g|=_ble_color_gflags_Revert)) ;; 409 (strike) ((g|=_ble_color_gflags_Strike)) ;; 410 (italic) ((g|=_ble_color_gflags_Italic)) ;; 411 (standout) ((g|=_ble_color_gflags_Revert|_ble_color_gflags_Bold)) ;; 412 (fg=*) 413 ble/color/.name2color "${entry:3}" 414 ble/color/g.setfg "$ret" ;; 415 (bg=*) 416 ble/color/.name2color "${entry:3}" 417 ble/color/g.setbg "$ret" ;; 418 (none) 419 g=0 ;; 420 esac 421 done 422 ret=$g 423 } 424 ## @fn ble/color/g2gspec g 425 ## @var[out] ret 426 function ble/color/g2gspec { 427 local g=$1 gspec= 428 if ((g&_ble_color_gflags_FgIndexed)); then 429 local fg=$((g>>8&0xFF)) 430 ble/color/.color2name "$fg" 431 gspec=$gspec,fg=$ret 432 elif ((g&_ble_color_gflags_FgMask)); then 433 local rgb=$((1<<24|g>>8&0xFFFFFF)) 434 ble/color/.color2name "$rgb" 435 gspec=$gspec,fg=$ret 436 fi 437 if ((g&_ble_color_gflags_BgIndexed)); then 438 local bg=$((g>>32&0xFF)) 439 ble/color/.color2name "$bg" 440 gspec=$gspec,bg=$ret 441 elif ((g&_ble_color_gflags_BgMask)); then 442 local rgb=$((1<<24|g>>32&0xFFFFFF)) 443 ble/color/.color2name "$rgb" 444 gspec=$gspec,bg=$ret 445 fi 446 ((g&_ble_color_gflags_Bold)) && gspec=$gspec,bold 447 ((g&_ble_color_gflags_Underline)) && gspec=$gspec,underline 448 ((g&_ble_color_gflags_Blink)) && gspec=$gspec,blink 449 ((g&_ble_color_gflags_Invisible)) && gspec=$gspec,invis 450 ((g&_ble_color_gflags_Revert)) && gspec=$gspec,reverse 451 ((g&_ble_color_gflags_Strike)) && gspec=$gspec,strike 452 ((g&_ble_color_gflags_Italic)) && gspec=$gspec,italic 453 gspec=${gspec#,} 454 ret=${gspec:-none} 455 } 456 457 ## @fn ble/color/gspec2sgr gspec 458 ## @param[in] gspec 459 ## @var[out] ret 460 function ble/color/gspec2sgr { 461 local sgr=0 entry 462 463 for entry in ${1//,/ }; do 464 case $entry in 465 (bold) sgr="$sgr;${_ble_term_sgr_bold:-1}" ;; 466 (underline) sgr="$sgr;${_ble_term_sgr_smul:-4}" ;; 467 (blink) sgr="$sgr;${_ble_term_sgr_blink:-5}" ;; 468 (invis) sgr="$sgr;${_ble_term_sgr_invis:-8}" ;; 469 (reverse) sgr="$sgr;${_ble_term_sgr_rev:-7}" ;; 470 (strike) sgr="$sgr;${_ble_term_sgr_strike:-9}" ;; 471 (italic) sgr="$sgr;${_ble_term_sgr_sitm:-3}" ;; 472 (standout) sgr="$sgr;${_ble_term_sgr_bold:-1};${_ble_term_sgr_rev:-7}" ;; 473 (fg=*) 474 ble/color/.name2color "${entry:3}" 475 ble/color/.color2sgrfg "$ret" 476 sgr="$sgr;$ret" ;; 477 (bg=*) 478 ble/color/.name2color "${entry:3}" 479 ble/color/.color2sgrbg "$ret" 480 sgr="$sgr;$ret" ;; 481 (none) 482 sgr=0 ;; 483 esac 484 done 485 486 ret="[${sgr}m" 487 } 488 489 function ble/color/.name2color/.clamp { 490 local text=$1 max=$2 491 if [[ $text == *% ]]; then 492 ((ret=10#0${text%'%'}*max/100)) 493 else 494 ((ret=10#0$text)) 495 fi 496 ((ret>max)) && ret=max 497 } 498 function ble/color/.name2color/.wrap { 499 local text=$1 max=$2 500 if [[ $text == *% ]]; then 501 ((ret=10#0${text%'%'}*max/100)) 502 else 503 ((ret=10#0$text)) 504 fi 505 ((ret%=max)) 506 } 507 function ble/color/.hxx2color { 508 local H=$1 Min=$2 Range=$3 Unit=$4 509 local h1 h2 x=$Min y=$Min z=$Min 510 ((h1=H%120,h2=120-h1, 511 x+=Range*(h2<60?h2:60)/60, 512 y+=Range*(h1<60?h1:60)/60)) 513 ((x=x*255/Unit, 514 y=y*255/Unit, 515 z=z*255/Unit)) 516 case $((H/120)) in 517 (0) local R=$x G=$y B=$z ;; 518 (1) local R=$z G=$x B=$y ;; 519 (2) local R=$y G=$z B=$x ;; 520 esac 521 ((ret=1<<24|R<<16|G<<8|B)) 522 } 523 function ble/color/.hsl2color { 524 local H=$1 S=$2 L=$3 Unit=$4 525 local Range=$((2*(L<=Unit/2?L:Unit-L)*S/Unit)) 526 local Min=$((L-Range/2)) 527 ble/color/.hxx2color "$H" "$Min" "$Range" "$Unit" 528 } 529 function ble/color/.hsb2color { 530 local H=$1 S=$2 B=$3 Unit=$4 531 local Range=$((B*S/Unit)) 532 local Min=$((B-Range)) 533 ble/color/.hxx2color "$H" "$Min" "$Range" "$Unit" 534 } 535 ## @fn ble/color/.name2color colorName 536 ## @var[out] ret 537 function ble/color/.name2color { 538 local colorName=$1 539 if [[ ! ${colorName//[0-9]} ]]; then 540 ((ret=10#0$colorName&255)) 541 elif [[ $colorName == '#'* ]]; then 542 if local rex='^#[0-9a-fA-F]{3}$'; [[ $colorName =~ $rex ]]; then 543 let "ret=1<<24|16#${colorName:1:1}*0x11<<16|16#${colorName:2:1}*0x11<<8|16#${colorName:3:1}*0x11" 544 elif rex='^#[0-9a-fA-F]{6}$'; [[ $colorName =~ $rex ]]; then 545 let "ret=1<<24|16#${colorName:1:2}<<16|16#${colorName:3:2}<<8|16#${colorName:5:2}" 546 else 547 ret=-1 548 fi 549 elif [[ $colorName == *:* ]]; then 550 if local rex='^rgb:([0-9]+%?)/([0-9]+%?)/([0-9]+%?)$'; [[ $colorName =~ $rex ]]; then 551 ble/color/.name2color/.clamp "${BASH_REMATCH[1]}" 255; local R=$ret 552 ble/color/.name2color/.clamp "${BASH_REMATCH[2]}" 255; local G=$ret 553 ble/color/.name2color/.clamp "${BASH_REMATCH[3]}" 255; local B=$ret 554 ((ret=1<<24|R<<16|G<<8|B)) 555 elif 556 local rex1='^cmy:([0-9]+%?)/([0-9]+%?)/([0-9]+%?)$' 557 local rex2='^cmyk:([0-9]+%?)/([0-9]+%?)/([0-9]+%?)/([0-9]+%?)$' 558 [[ $colorName =~ $rex1 || $colorName =~ $rex2 ]] 559 then 560 ble/color/.name2color/.clamp "${BASH_REMATCH[1]}" 255; local C=$ret 561 ble/color/.name2color/.clamp "${BASH_REMATCH[2]}" 255; local M=$ret 562 ble/color/.name2color/.clamp "${BASH_REMATCH[3]}" 255; local Y=$ret 563 ble/color/.name2color/.clamp "${BASH_REMATCH[4]:-0}" 255; local K=$ret 564 local K=$((~K&0xFF)) 565 local R=$(((~C&0xFF)*K/255)) 566 local G=$(((~M&0xFF)*K/255)) 567 local B=$(((~Y&0xFF)*K/255)) 568 ((ret=1<<24|R<<16|G<<8|B)) 569 elif rex='^hs[lvb]:([0-9]+)/([0-9]+%)/([0-9]+%)$'; [[ $colorName =~ $rex ]]; then 570 ble/color/.name2color/.wrap "${BASH_REMATCH[1]}" 360; local H=$ret 571 ble/color/.name2color/.clamp "${BASH_REMATCH[2]}" 1000; local S=$ret 572 ble/color/.name2color/.clamp "${BASH_REMATCH[3]}" 1000; local X=$ret 573 if [[ $colorName == hsl:* ]]; then 574 ble/color/.hsl2color "$H" "$S" "$X" 1000 575 else 576 ble/color/.hsb2color "$H" "$S" "$X" 1000 577 fi 578 else 579 ret=-1 580 fi 581 else 582 case $colorName in 583 (black) ret=0 ;; 584 (brown) ret=1 ;; 585 (green) ret=2 ;; 586 (olive) ret=3 ;; 587 (navy) ret=4 ;; 588 (purple) ret=5 ;; 589 (teal) ret=6 ;; 590 (silver) ret=7 ;; 591 592 (gr[ae]y) ret=8 ;; 593 (red) ret=9 ;; 594 (lime) ret=10 ;; 595 (yellow) ret=11 ;; 596 (blue) ret=12 ;; 597 (magenta) ret=13 ;; 598 (cyan) ret=14 ;; 599 (white) ret=15 ;; 600 601 (orange) ret=202 ;; 602 (transparent|default) ret=-1 ;; 603 (*) ret=-1 ;; 604 esac 605 fi 606 } 607 function ble/color/.color2name { 608 if (($1>=0x1000000)); then 609 ble/util/sprintf ret '#%06x' "$(($1&0xFFFFFF))" 610 return 0 611 fi 612 613 ((ret=(10#0$1&255))) 614 case $ret in 615 (0) ret=black ;; 616 (1) ret=brown ;; 617 (2) ret=green ;; 618 (3) ret=olive ;; 619 (4) ret=navy ;; 620 (5) ret=purple ;; 621 (6) ret=teal ;; 622 (7) ret=silver ;; 623 (8) ret=gray ;; 624 (9) ret=red ;; 625 (10) ret=lime ;; 626 (11) ret=yellow ;; 627 (12) ret=blue ;; 628 (13) ret=magenta ;; 629 (14) ret=cyan ;; 630 (15) ret=white ;; 631 (202) ret=orange ;; 632 esac 633 } 634 635 ## @fn ble/color/convert-color88-to-color256 color 636 ## @param[in] color 637 ## @var[out] ret 638 function ble/color/convert-color88-to-color256 { 639 local color=$1 640 if ((color>=16)); then 641 if ((color>=80)); then 642 local L=$((((color-80+1)*25+4)/9)) 643 ((color=L==0?16:(L==25?231:232+(L-1)))) 644 else 645 ((color-=16)) 646 local R=$((color/16)) G=$((color/4%4)) B=$((color%4)) 647 ((R=(R*5+1)/3,G=(G*5+1)/3,B=(B*5+1)/3, 648 color=16+R*36+G*6+B)) 649 fi 650 fi 651 ret=$color 652 } 653 ## @fn ble/color/convert-color256-to-color88 color 654 ## @param[in] color 655 ## @var[out] ret 656 function ble/color/convert-color256-to-color88 { 657 local color=$1 658 if ((color>=16)); then 659 if ((color>=232)); then 660 local L=$((((color-232+1)*9+12)/25)) 661 ((color=L==0?16:(L==9?79:80+(L-1)))) 662 else 663 ((color-=16)) 664 local R=$((color/36)) G=$((color/6%6)) B=$((color%6)) 665 ((R=(R*3+2)/5,G=(G*3+2)/5,B=(B*3+2)/5, 666 color=16+R*16+G*4+B)) 667 fi 668 fi 669 ret=$color 670 } 671 ## @fn ble/color/convert-rgb24-to-color256 R G B 672 ## @param[in] R G B 673 ## 0..255 の階調値 674 ## @var[out] ret 675 function ble/color/convert-rgb24-to-color256 { 676 local R=$1 G=$2 B=$3 677 if ((R==G&&G==B)); then 678 # xterm 24 grayscale: 10k+8 (0..238) 679 if ((R<=3)); then 680 # 6x6x6 cube (0,0,0) 681 ret=16 682 elif ((R>=247)); then 683 # 6x6x6 cube (5,5,5) 684 ret=231 685 elif ((R>=92&&(R-92)%40<5)); then 686 # 6x6x6 cube (1,1,1)-(4,4,4) 687 ((ret=59+43*(R-92)/40)) 688 else 689 local level=$(((R-3)/10)) 690 ((ret=232+(level<=23?level:23))) 691 fi 692 else 693 # xterm 6x6x6 cube: k?55+40k:0 694 ((R=R<=47?0:(R<=95?1:(R-35)/40))) 695 ((G=G<=47?0:(G<=95?1:(G-35)/40))) 696 ((B=B<=47?0:(B<=95?1:(B-35)/40))) 697 ((ret=16+36*R+6*G+B)) 698 fi 699 } 700 ## @fn ble/color/convert-rgb24-to-color88 R G B 701 ## @param[in] R G B 702 ## 0..255 の階調値 703 ## @var[out] ret 704 function ble/color/convert-rgb24-to-color88 { 705 local R=$1 G=$2 B=$3 706 if ((R==G&&G==B)); then 707 # xterm 8 grayscale: 46+25k = 46,71,96,121,146,171,196,221 708 if ((R<=22)); then 709 ret=16 # 4x4x4 cube (0,0,0)=0:0:0 710 elif ((R>=239)); then 711 ret=79 # 4x4x4 cube (3,3,3)=255:255:255 712 elif ((131<=R&&R<=142)); then 713 ret=37 # 4x4x4 cube (1,1,1)=139:139:139 714 elif ((197<=R&&R<=208)); then 715 ret=58 # 4x4x4 cube (2,2,2)=197:197:197 716 else 717 local level=$(((R-34)/25)) 718 ((ret=80+(level<=7?level:7))) 719 fi 720 else 721 # xterm 4x4x4 cube: (k?81+58k:0) = 0,139,197,255 722 ((R=R<=69?0:(R<=168?1:(R-52)/58))) 723 ((G=G<=69?0:(G<=168?1:(G-52)/58))) 724 ((B=B<=69?0:(B<=168?1:(B-52)/58))) 725 ((ret=16+16*R+4*G+B)) 726 fi 727 } 728 729 _ble_color_color2sgr_filter= 730 ## @fn ble/color/.color2sgrfg color 731 ## @fn ble/color/.color2sgrbg color 732 ## @param[in] color 733 ## 0-255 の値は index color を表します。 734 ## 1XXXXXX の値は 24bit color を表します。 735 ## @var[out] ret 736 function ble/color/.color2sgr-impl { 737 local ccode=$1 prefix=$2 # 3 for fg, 4 for bg 738 builtin eval -- "$_ble_color_color2sgr_filter" 739 if ((ccode<0)); then 740 ret=${prefix}9 741 elif ((ccode<16&&ccode<_ble_term_colors)); then 742 if ((prefix==4)); then 743 ret=${_ble_term_sgr_ab[ccode]} 744 else 745 ret=${_ble_term_sgr_af[ccode]} 746 fi 747 elif ((ccode<256)); then 748 local index_colors=$_ble_color_index_colors_default 749 [[ $bleopt_term_index_colors == auto ]] || ((index_colors=bleopt_term_index_colors)) 750 if ((index_colors>=256)); then 751 ret="${prefix}8;5;$ccode" 752 elif ((index_colors>=88)); then 753 ble/color/convert-color256-to-color88 "$ccode" 754 ret="${prefix}8;5;$ret" 755 elif ((ccode<index_colors)); then 756 ret="${prefix}8;5;$ccode" 757 elif ((_ble_term_colors>=16||_ble_term_colors==8)); then 758 if ((ccode>=16)); then 759 if ((ccode>=232)); then 760 local L=$((((ccode-232+1)*3+12)/25)) 761 ((ccode=L==0?0:(L==1?8:(L==2?7:15)))) 762 else 763 ((ccode-=16)) 764 local R=$((ccode/36)) G=$((ccode/6%6)) B=$((ccode%6)) 765 if ((R==G&&G==B)); then 766 local L=$(((R*3+2)/5)) 767 ((ccode=L==0?0:(L==1?8:(L==2?7:15)))) 768 else 769 local min max 770 ((R<G?(min=R,max=G):(min=G,max=R), 771 B<min?(min=B):(B>max&&(max=B)))) 772 local Range=$((max-min)) 773 ((R=(R-min+Range/2)/Range, 774 G=(G-min+Range/2)/Range, 775 B=(B-min+Range/2)/Range, 776 ccode=R+G*2+B*4+(min+max>=5?8:0))) 777 fi 778 fi 779 fi 780 ((_ble_term_colors==8&&ccode>=8&&(ccode-=8))) 781 782 if ((prefix==4)); then 783 ret=${_ble_term_sgr_ab[ccode]} 784 else 785 ret=${_ble_term_sgr_af[ccode]} 786 fi 787 else 788 ret=${prefix}9 789 fi 790 elif ((0x1000000<=ccode&&ccode<0x2000000)); then 791 # 24bit True Colors 792 local R=$((ccode>>16&0xFF)) G=$((ccode>>8&0xFF)) B=$((ccode&0xFF)) 793 if [[ $bleopt_term_true_colors == semicolon ]]; then 794 ret="${prefix}8;2;$R;$G;$B" 795 elif [[ $bleopt_term_true_colors == colon ]]; then 796 ret="${prefix}8:2::$R:$G:$B" 797 else 798 local index_colors=$_ble_color_index_colors_default 799 [[ $bleopt_term_index_colors == auto ]] || ((index_colors=bleopt_term_index_colors)) 800 local index= 801 if ((index_colors>=256)); then 802 ble/color/convert-rgb24-to-color256 "$R" "$G" "$B" 803 index=$ret 804 elif ((index_colors>=88)); then 805 ble/color/convert-rgb24-to-color88 "$R" "$G" "$B" 806 index=$ret 807 else 808 ble/color/convert-rgb24-to-color256 "$R" "$G" "$B" 809 if ((ret<index_colors)); then 810 index=$ret 811 else 812 ble/color/.color2sgr-impl "$ret" "$prefix" 813 fi 814 fi 815 [[ $index ]] && ret="${prefix}8;5;$index" 816 fi 817 else 818 ret=${prefix}9 819 fi 820 } 821 822 ## @fn ble/color/.color2sgrfg color_code 823 ## @var[out] ret 824 function ble/color/.color2sgrfg { 825 ble/color/.color2sgr-impl "$1" 3 826 } 827 ## @fn ble/color/.color2sgrbg color_code 828 ## @var[out] ret 829 function ble/color/.color2sgrbg { 830 ble/color/.color2sgr-impl "$1" 4 831 } 832 833 #------------------------------------------------------------------------------ 834 835 ## @fn ble/color/read-sgrspec/.arg-next 836 ## @var[in ] fields 837 ## @var[in,out] j 838 ## @var[ out] arg 839 function ble/color/read-sgrspec/.arg-next { 840 local _ble_local_var=arg _ble_local_ret 841 if [[ $1 == -v ]]; then 842 _ble_local_var=$2 843 shift 2 844 fi 845 846 if ((j<${#fields[*]})); then 847 ((_ble_local_ret=10#0${fields[j++]})) 848 else 849 ((i++)) 850 ((_ble_local_ret=10#0${specs[i]%%:*})) 851 fi 852 853 (($_ble_local_var=_ble_local_ret)) 854 } 855 856 ## @fn ble-color/read-sgrspec sgrspec opts 857 ## @param[in] sgrspec 858 ## @var[in,out] g 859 function ble/color/read-sgrspec { 860 local specs i iN 861 ble/string#split specs \; "$1" 862 for ((i=0,iN=${#specs[@]};i<iN;i++)); do 863 local spec=${specs[i]} fields 864 ble/string#split fields : "$spec" 865 local arg=$((10#0${fields[0]})) 866 if ((arg==0)); then 867 g=0 868 continue 869 elif [[ :$opts: != *:ansi:* ]]; then 870 [[ ${_ble_term_sgr_term2ansi[arg]} ]] && 871 arg=${_ble_term_sgr_term2ansi[arg]} 872 fi 873 874 if ((30<=arg&&arg<50)); then 875 # colors 876 if ((30<=arg&&arg<38)); then 877 local color=$((arg-30)) 878 ble/color/g.setfg-index "$color" 879 elif ((40<=arg&&arg<48)); then 880 local color=$((arg-40)) 881 ble/color/g.setbg-index "$color" 882 elif ((arg==38)); then 883 local j=1 color cspace 884 ble/color/read-sgrspec/.arg-next -v cspace 885 if ((cspace==5)); then 886 ble/color/read-sgrspec/.arg-next -v color 887 if [[ :$opts: != *:ansi:* ]] && ((bleopt_term_index_colors==88)); then 888 local ret; ble/color/convert-color88-to-color256 "$color"; color=$ret 889 fi 890 ble/color/g.setfg-index "$color" 891 elif ((cspace==2)); then 892 local S R G B 893 ((${#fields[@]}>5)) && 894 ble/color/read-sgrspec/.arg-next -v S 895 ble/color/read-sgrspec/.arg-next -v R 896 ble/color/read-sgrspec/.arg-next -v G 897 ble/color/read-sgrspec/.arg-next -v B 898 ble/color/g.setfg-rgb "$R" "$G" "$B" 899 elif ((cspace==3||cspace==4)); then 900 local S C M Y K=0 901 ((${#fields[@]}>2+cspace)) && 902 ble/color/read-sgrspec/.arg-next -v S 903 ble/color/read-sgrspec/.arg-next -v C 904 ble/color/read-sgrspec/.arg-next -v M 905 ble/color/read-sgrspec/.arg-next -v Y 906 ((cspace==4)) && 907 ble/color/read-sgrspec/.arg-next -v K 908 ble/color/g.setfg-cmyk "$C" "$M" "$Y" "$K" 909 else 910 ble/color/g.setfg-clear 911 fi 912 elif ((arg==48)); then 913 local j=1 color cspace 914 ble/color/read-sgrspec/.arg-next -v cspace 915 if ((cspace==5)); then 916 ble/color/read-sgrspec/.arg-next -v color 917 if [[ :$opts: != *:ansi:* ]] && ((bleopt_term_index_colors==88)); then 918 local ret; ble/color/convert-color88-to-color256 "$color"; color=$ret 919 fi 920 ble/color/g.setbg-index "$color" 921 elif ((cspace==2)); then 922 local S R G B 923 ((${#fields[@]}>5)) && 924 ble/color/read-sgrspec/.arg-next -v S 925 ble/color/read-sgrspec/.arg-next -v R 926 ble/color/read-sgrspec/.arg-next -v G 927 ble/color/read-sgrspec/.arg-next -v B 928 ble/color/g.setbg-rgb "$R" "$G" "$B" 929 elif ((cspace==3||cspace==4)); then 930 local S C M Y K=0 931 ((${#fields[@]}>2+cspace)) && 932 ble/color/read-sgrspec/.arg-next -v S 933 ble/color/read-sgrspec/.arg-next -v C 934 ble/color/read-sgrspec/.arg-next -v M 935 ble/color/read-sgrspec/.arg-next -v Y 936 ((cspace==4)) && 937 ble/color/read-sgrspec/.arg-next -v K 938 ble/color/g.setbg-cmyk "$C" "$M" "$Y" "$K" 939 else 940 ble/color/g.setbg-clear 941 fi 942 elif ((arg==39)); then 943 ble/color/g.setfg-clear 944 elif ((arg==49)); then 945 ble/color/g.setbg-clear 946 fi 947 elif ((90<=arg&&arg<98)); then 948 local color=$((arg-90+8)) 949 ble/color/g.setfg-index "$color" 950 elif ((100<=arg&&arg<108)); then 951 local color=$((arg-100+8)) 952 ble/color/g.setbg-index "$color" 953 else 954 case $arg in 955 (1) ((g|=_ble_color_gflags_Bold)) ;; 956 (22) ((g&=~_ble_color_gflags_Bold)) ;; 957 (4) ((g|=_ble_color_gflags_Underline)) ;; 958 (24) ((g&=~_ble_color_gflags_Underline)) ;; 959 (7) ((g|=_ble_color_gflags_Revert)) ;; 960 (27) ((g&=~_ble_color_gflags_Revert)) ;; 961 (9807) ((g^=_ble_color_gflags_Revert)) ;; # toggle (for internal use) 962 (3) ((g|=_ble_color_gflags_Italic)) ;; 963 (23) ((g&=~_ble_color_gflags_Italic)) ;; 964 (5) ((g|=_ble_color_gflags_Blink)) ;; 965 (25) ((g&=~_ble_color_gflags_Blink)) ;; 966 (8) ((g|=_ble_color_gflags_Invisible)) ;; 967 (28) ((g&=~_ble_color_gflags_Invisible)) ;; 968 (9) ((g|=_ble_color_gflags_Strike)) ;; 969 (29) ((g&=~_ble_color_gflags_Strike)) ;; 970 esac 971 fi 972 done 973 } 974 975 ## @fn ble/color/sgrspec2g str 976 ## SGRに対する引数から描画属性を構築します。 977 ## @var[out] ret 978 function ble/color/sgrspec2g { 979 local g=0 980 ble/color/read-sgrspec "$1" 981 ret=$g 982 } 983 984 ## @fn ble/color/ansi2g str 985 ## ANSI制御シーケンスから描画属性を構築します。 986 ## Note: canvas.sh を読み込んで以降でないと使えません。 987 ## @var[out] ret 988 function ble/color/ansi2g { 989 local x=0 y=0 g=0 990 ble/function#try ble/canvas/trace "$1" # -> ret 991 ret=$g 992 } 993 994 #------------------------------------------------------------------------------ 995 # _ble_faces 996 997 # 遅延初期化登録 998 # @hook color_defface_load (defined in src/def.sh) 999 # @hook color_setface_load (defined in src/def.sh) 1000 1001 # 遅延初期化 1002 if [[ ! ${_ble_faces_count-} ]]; then # reload #D0875 1003 _ble_faces_count=0 1004 _ble_faces=() 1005 fi 1006 1007 ## @fn ble/color/setface/.check-argument 1008 ## @var[out] ext 1009 function ble/color/setface/.check-argument { 1010 local rex='^[_a-zA-Z0-9]+$' 1011 [[ $# == 2 && $1 =~ $rex && $2 ]] && return 0 1012 1013 local flags=a 1014 while (($#)); do 1015 local arg=$1; shift 1016 case $arg in 1017 (--help) flags=H$flags ;; 1018 (--color|--color=always) flags=c${flags//[ac]} ;; 1019 (--color=auto) flags=a${flags//[ac]} ;; 1020 (--color=never) flags=${flags//[ac]} ;; 1021 (-*) 1022 ble/util/print "${FUNCNAME[1]}: unrecognized option '$arg'." >&2 1023 flags=E$flags ;; 1024 (*) 1025 ble/util/print "${FUNCNAME[1]}: unrecognized argument '$arg'." >&2 1026 flags=E$flags ;; 1027 esac 1028 done 1029 1030 if [[ $flags == *E* ]]; then 1031 ext=2; return 1 1032 elif [[ $flags == *H* ]]; then 1033 ble/util/print-lines \ 1034 "usage: $name FACE_NAME [TYPE:]SPEC" \ 1035 ' Set face.' \ 1036 '' \ 1037 ' TYPE Specifies the format of SPEC. The following values are available.' \ 1038 ' gspec Comma separated graphic attribute list' \ 1039 ' g Integer value' \ 1040 ' ref Face name or id (reference)' \ 1041 ' copy Face name or id (copy value)' \ 1042 ' sgrspec Parameters to the control function SGR' \ 1043 ' ansi ANSI Sequences' >&2 1044 ext=0; return 1 1045 fi 1046 1047 local opts= 1048 [[ $flags == *c* || $flags == *a* && -t 1 ]] && opts=$opts:color 1049 ble/color/list-faces "$opts"; ext=$?; return 1 1050 } 1051 function ble-color-defface { 1052 local ext; ble/color/setface/.check-argument "$@" || return "$ext" 1053 ble/color/defface "$@" 1054 } 1055 function ble-color-setface { 1056 local ext; ble/color/setface/.check-argument "$@" || return "$ext" 1057 ble/color/setface "$@" 1058 } 1059 1060 # 遅延関数 (後で上書き) 1061 function ble/color/defface { local q=\' Q="'\''"; blehook color_defface_load+="ble/color/defface '${1//$q/$Q}' '${2//$q/$Q}'"; } 1062 function ble/color/setface { local q=\' Q="'\''"; blehook color_setface_load+="ble/color/setface '${1//$q/$Q}' '${2//$q/$Q}'"; } 1063 function ble/color/face2g { ble/color/initialize-faces && ble/color/face2g "$@"; } 1064 function ble/color/face2sgr { ble/color/initialize-faces && ble/color/face2sgr "$@"; } 1065 function ble/color/iface2g { ble/color/initialize-faces && ble/color/iface2g "$@"; } 1066 function ble/color/iface2sgr { ble/color/initialize-faces && ble/color/iface2sgr "$@"; } 1067 function ble/color/spec2g { ble/color/initialize-faces && ble/color/spec2g "$@"; } 1068 1069 function ble/color/face2sgr-ansi { ble/color/initialize-faces && ble/color/face2sgr "$@"; } 1070 1071 # 遅延初期化子 1072 _ble_color_faces_initialized= 1073 function ble/color/initialize-faces { 1074 local _ble_color_faces_initializing=1 1075 local -a _ble_color_faces_errors=() 1076 1077 ## @fn ble/color/face2g face 1078 ## @var[out] ret 1079 function ble/color/face2g { 1080 ((ret=_ble_faces[_ble_faces__$1])) 1081 } 1082 ## @fn ble/color/face2sgr face 1083 ## @var[out] ret 1084 function ble/color/face2sgr { ble/color/g2sgr "$((_ble_faces[_ble_faces__$1]))"; } 1085 function ble/color/face2sgr-ansi { ble/color/g2sgr-ansi "$((_ble_faces[_ble_faces__$1]))"; } 1086 ## @fn ble/color/iface2g iface 1087 ## @var[out] ret 1088 function ble/color/iface2g { 1089 ((ret=_ble_faces[$1])) 1090 } 1091 ## @fn ble/color/iface2sgr iface 1092 ## @var[out] ret 1093 function ble/color/iface2sgr { 1094 ble/color/g2sgr "$((_ble_faces[$1]))" 1095 } 1096 ## @fn ble/color/spec2g [TYPE:]SPEC 1097 function ble/color/spec2g { 1098 ble/color/setface/.spec2gexpr "$@" prefix-face 1099 ((ret=ret)) 1100 } 1101 1102 ## @fn ble/color/setface/.spec2gexpr spec 1103 ## @var[out] ret 1104 function ble/color/setface/.spec2gexpr { 1105 local spec=$1 value=${1#*:} opts=$2 1106 case $spec in 1107 (gspec:*) ble/color/gspec2g "$value" ;; 1108 (g:*) ret=$(($value)) ;; 1109 (ref:*) 1110 if [[ ! ${value//[0-9]} ]]; then 1111 ret=_ble_faces[$((value))] 1112 else 1113 ret=_ble_faces[_ble_faces__$value] 1114 fi ;; 1115 (copy:*|face:*|iface:*) 1116 # `face:*' and `iface:*' are obsoleted forms. 1117 [[ $spec == copy:* || $spec == face:* && :$opts: == *:prefix-face:* ]] || 1118 ble/util/print "ble-face: \"${spec%%:*}:*\" is obsoleted. Use \"copy:*\" instead." >&2 1119 if [[ ! ${value//[0-9]} ]]; then 1120 ble/color/iface2g "$value" 1121 else 1122 ble/color/face2g "$value" 1123 fi ;; 1124 (sgrspec:*) ble/color/sgrspec2g "$value" ;; 1125 (ansi:*) ble/color/ansi2g "$value" ;; 1126 (*) ble/color/gspec2g "$spec" ;; 1127 esac 1128 } 1129 1130 function ble/color/defface { 1131 local name=_ble_faces__$1 spec=$2 ret 1132 (($name)) && return 0 1133 (($name=++_ble_faces_count)) 1134 ble/color/setface/.spec2gexpr "$spec" 1135 _ble_faces[$name]=$ret 1136 _ble_faces_def[$name]=$ret 1137 } 1138 function ble/color/setface { 1139 local name=_ble_faces__$1 spec=$2 ret 1140 if [[ ${!name} ]]; then 1141 ble/color/setface/.spec2gexpr "$spec"; _ble_faces[$name]=$ret 1142 else 1143 local message="ble.sh: the specified face \`$1' is not defined." 1144 if [[ $_ble_color_faces_initializing ]]; then 1145 ble/array#push _ble_color_faces_errors "$message" 1146 else 1147 ble/util/print "$message" >&2 1148 fi 1149 return 1 1150 fi 1151 } 1152 1153 _ble_color_faces_initialized=1 1154 blehook/invoke color_defface_load 1155 blehook/invoke color_setface_load 1156 blehook color_defface_load= 1157 blehook color_setface_load= 1158 1159 if ((${#_ble_color_faces_errors[@]})); then 1160 if ((_ble_edit_attached)) && [[ ! $_ble_textarea_invalidated && $_ble_term_state == internal ]]; then 1161 IFS=$'\n' builtin eval 'local message="${_ble_color_faces_errors[*]}"' 1162 ble/widget/print "$message" 1163 else 1164 printf '%s\n' "${_ble_color_faces_errors[@]}" >&2 1165 fi 1166 return 1 1167 else 1168 return 0 1169 fi 1170 } 1171 ble/function#try ble/util/idle.push ble/color/initialize-faces 1172 1173 ## @fn ble/color/list-faces opts 1174 function ble/color/list-faces { 1175 local flags= 1176 [[ :$1: == *:color:* ]] && flags=c 1177 1178 local ret sgr0= sgr1= sgr2= 1179 if [[ $flags == *c* ]]; then 1180 sgr0=$_ble_term_sgr0 1181 ble/color/face2sgr command_function; sgr1=$ret 1182 ble/color/face2sgr syntax_varname; sgr2=$ret 1183 fi 1184 1185 local key 1186 for key in "${!_ble_faces__@}"; do 1187 ble-face/.print-face "$key" 1188 done 1189 } 1190 1191 function ble-face/.read-arguments/process-set { 1192 local o=$1 face=$2 value=$3 1193 if local rex='^[_a-zA-Z0-9@][_a-zA-Z0-9@]*$'; ! [[ $face =~ $rex ]]; then 1194 ble/util/print "ble-face: invalid face name '$face'." >&2 1195 flags=E$flags 1196 return 1 1197 elif [[ $o == '-d' && $face == *@* ]]; then 1198 ble/util/print "ble-face: wildcards cannot be used in the face name '$face' for definition." >&2 1199 flags=E$flags 1200 return 1 1201 fi 1202 1203 local assign='=' 1204 [[ $o == -d ]] && assign=':=' 1205 ble/array#push setface "$face$assign$value" 1206 } 1207 1208 ## @fn ble-face/.read-arguments args... 1209 ## @var[out] flags 1210 ## H = help 1211 ## E = error 1212 ## L = literal 1213 ## c = color 1214 ## r = reset 1215 ## u = changed 1216 function ble-face/.read-arguments { 1217 flags= setface=() print=() 1218 local opt_color=auto 1219 local args iarg narg=$#; args=("$@") 1220 for ((iarg=0;iarg<narg;)); do 1221 local arg=${args[iarg++]} 1222 if [[ $arg == -* ]]; then 1223 if [[ $flags == *L* ]]; then 1224 ble/util/print "ble-face: unrecognized argument '$arg'." >&2 1225 flags=E$flags 1226 else 1227 case $arg in 1228 (--help) flags=H$flags ;; 1229 (--color) 1230 opt_color=always ;; 1231 (--color=always|--color=auto|--color=never) 1232 opt_color=${arg#*=} ;; 1233 (--color=*) 1234 ble/util/print "ble-face: '${arg#*=}': unrecognized option argument for '--color'." >&2 1235 flags=E$flags ;; 1236 (--reset) flags=r$flags ;; 1237 (--changed) flags=u$flags ;; 1238 (--) flags=L$flags ;; 1239 (--*) 1240 ble/util/print "ble-face: unrecognized long option '$arg'." >&2 1241 flags=E$flags ;; 1242 (-?*) 1243 local i c 1244 for ((i=1;i<${#arg};i++)); do 1245 c=${arg:i:1} 1246 case $c in 1247 ([ru]) flags=$c$flags ;; 1248 ([sd]) 1249 if ((i+1<${#arg})); then 1250 local lhs=${arg:i+1} 1251 else 1252 local lhs=${args[iarg++]} 1253 fi 1254 local rhs=${args[iarg++]} 1255 if ((iarg>narg)); then 1256 ble/util/print "ble-face: missing option argument for '-$c FACE SPEC'." >&2 1257 flags=E$flags 1258 continue 1259 fi 1260 ble-face/.read-arguments/process-set "${arg::2}" "$lhs" "$rhs" 1261 break ;; 1262 (*) 1263 ble/util/print "ble-face: unrecognized option '-$c'." >&2 1264 flags=E$flags ;; 1265 esac 1266 done ;; 1267 (-) 1268 ble/util/print "ble-face: unrecognized argument '$arg'." >&2 1269 flags=E$flags ;; 1270 esac 1271 fi 1272 1273 elif [[ $arg == *=* ]]; then 1274 if local rex='^[_a-zA-Z@][_a-zA-Z0-9@]*:?='; [[ $arg =~ $rex ]]; then 1275 ble/array#push setface "$arg" 1276 else 1277 local lhs=${arg%%=*}; lhs=${lhs%:} 1278 ble/util/print "ble-face: invalid left-hand side '$lhs' ($arg)." >&2 1279 flags=E$flags 1280 fi 1281 1282 else 1283 if local rex='^[_a-zA-Z@][_a-zA-Z0-9@]*$'; [[ $arg =~ $rex ]]; then 1284 ble/array#push print "$arg" 1285 else 1286 ble/util/print "ble-face: unrecognized form of argument '$arg'." >&2 1287 flags=E$flags 1288 fi 1289 fi 1290 done 1291 1292 [[ $opt_color == auto && -t 1 || $opt_color == always ]] && flags=c$flags 1293 [[ $flags != *E* ]] 1294 } 1295 function ble-face/.print-help { 1296 ble/util/print-lines >&2 \ 1297 'ble-face --help' \ 1298 'ble-face [FACEPAT[:=|=][TYPE:]SPEC | -[sd] FACEPAT [TYPE:]SPEC]]...' \ 1299 'ble-face [-ur|--color[=WHEN]] [FACE...]' \ 1300 '' \ 1301 ' OPTIONS/ARGUMENTS' \ 1302 '' \ 1303 ' FACEPAT=[TYPE:]SPEC' \ 1304 ' -s FACEPAT [TYPE:]SPEC' \ 1305 ' Set a face. FACEPAT can include a wildcard @ which matches one or' \ 1306 ' more characters.' \ 1307 '' \ 1308 ' FACE:=[TYPE:]SPEC' \ 1309 ' -d FACE [TYPE:]SPEC' \ 1310 ' Define a face' \ 1311 '' \ 1312 ' [-u | --color[=always|never|auto]]... FACEPAT...' \ 1313 ' Print faces. If faces are not specified, all faces are selected.' \ 1314 ' If -u is specified, only the faces with different values from their' \ 1315 ' default will be printed. The option "--color" controls the output' \ 1316 ' color settings. The default is "auto".' \ 1317 '' \ 1318 ' -r FACEPAT...' \ 1319 ' Reset faces. If faces are not specified, all faces are selected.' \ 1320 '' \ 1321 ' FACEPAT Specifies a face name. The character @ in the face name is treated' \ 1322 ' as a wildcard.' \ 1323 '' \ 1324 ' FACE Specifies a face name. Wildcard @ cannot be used.' \ 1325 '' \ 1326 ' TYPE Specifies the format of SPEC. The following values are available.' \ 1327 ' gspec Comma separated graphic attribute list' \ 1328 ' g Integer value' \ 1329 ' ref Face name or id (reference)' \ 1330 ' copy Face name or id (copy value)' \ 1331 ' sgrspec Parameters to the control function SGR' \ 1332 ' ansi ANSI Sequences' \ 1333 '' 1334 return 0 1335 } 1336 ## @fn ble/color/.print-face key 1337 ## @param[in] key 1338 ## @var[in] flags sgr0 sgr1 sgr2 1339 function ble-face/.print-face { 1340 local key=$1 ret 1341 local name=${key#_ble_faces__} 1342 local cur=${_ble_faces[key]} 1343 if [[ $flags == *u* ]]; then 1344 local def=_ble_faces_def[key] 1345 [[ ${!def+set} && $cur == "${!def}" ]] && return 0 1346 fi 1347 local def=${_ble_faces[key]} 1348 if [[ $cur == '_ble_faces['*']' ]]; then 1349 cur=${cur#'_ble_faces['} 1350 cur=${cur%']'} 1351 cur=ref:${cur#_ble_faces__} 1352 else 1353 ble/color/g2gspec "$((cur))"; cur=$ret 1354 fi 1355 if [[ $flags == *c* ]]; then 1356 ble/color/iface2sgr "$((key))" 1357 cur=$ret$cur$_ble_term_sgr0 1358 fi 1359 printf '%s %s=%s\n' "${sgr1}ble-face$sgr0" "$sgr2$name$sgr0" "$cur" 1360 } 1361 ## @fn ble/color/.print-face key 1362 ## @param[in] key 1363 ## @var[in] flags sgr0 sgr1 sgr2 1364 function ble-face/.reset-face { 1365 local key=$1 ret 1366 [[ ${_ble_faces_def[key]+set} ]] && 1367 _ble_faces[key]=${_ble_faces_def[key]} 1368 } 1369 function ble-face { 1370 local flags setface print 1371 ble-face/.read-arguments "$@" 1372 if [[ $flags == *H* ]]; then 1373 ble-face/.print-help 1374 return 2 1375 elif [[ $flags == *E* ]]; then 1376 return 2 1377 fi 1378 1379 if ((!${#print[@]}&&!${#setface[@]})); then 1380 print=(@) 1381 fi 1382 1383 ((${#print[@]})) && ble/color/initialize-faces 1384 if [[ ! $_ble_color_faces_initialized ]]; then 1385 local ret 1386 ble/string#quote-command ble-face "${setface[@]}" 1387 blehook color_setface_load+="$ret" 1388 return 0 1389 fi 1390 1391 local spec 1392 for spec in "${setface[@]}"; do 1393 if local rex='^([_a-zA-Z@][_a-zA-Z0-9@]*)(:?=)(.*)$'; ! [[ $spec =~ $rex ]]; then 1394 ble/util/print "ble-face: unrecognized setting '$spec'" >&2 1395 flags=E$flags 1396 continue 1397 fi 1398 1399 local var=${BASH_REMATCH[1]} 1400 local type=${BASH_REMATCH[2]} 1401 local value=${BASH_REMATCH[3]} 1402 if [[ $type == ':=' ]]; then 1403 if [[ $var == *@* ]]; then 1404 ble/util/print "ble-face: wild card @ cannot be used for face definition ($spec)." >&2 1405 flags=E$flags 1406 else 1407 ble/color/defface "$var" "$value" 1408 fi 1409 else 1410 local ret face 1411 if bleopt/expand-variable-pattern "_ble_faces__$var"; then 1412 for face in "${ret[@]}"; do 1413 ble/color/setface "${face#_ble_faces__}" "$value" 1414 done 1415 else 1416 ble/util/print "ble-face: face '$var' not found" >&2 1417 flags=E$flags 1418 fi 1419 fi 1420 done 1421 1422 if ((${#print[@]})); then 1423 # initialize 1424 local ret sgr0= sgr1= sgr2= 1425 if [[ $flags == *c* ]]; then 1426 sgr0=$_ble_term_sgr0 1427 ble/color/face2sgr command_function; sgr1=$ret 1428 ble/color/face2sgr syntax_varname; sgr2=$ret 1429 fi 1430 1431 local spec 1432 for spec in "${print[@]}"; do 1433 local ret face 1434 if bleopt/expand-variable-pattern "_ble_faces__$spec"; then 1435 if [[ $flags == *r* ]]; then 1436 for face in "${ret[@]}"; do 1437 ble-face/.reset-face "$face" 1438 done 1439 else 1440 for face in "${ret[@]}"; do 1441 ble-face/.print-face "$face" 1442 done 1443 fi 1444 else 1445 ble/util/print "ble-face: face '$spec' not found" >&2 1446 flags=E$flags 1447 fi 1448 done 1449 fi 1450 [[ $flags != *E* ]] 1451 } 1452 1453 #------------------------------------------------------------------------------ 1454 # ble/highlight/layer 1455 1456 _ble_highlight_layer_list=(plain) 1457 1458 ## @fn ble/highlight/layer/update text opts [DMIN DMAX DMAX0] 1459 ## @param[in] text opts DMIN DMAX DMAX0 1460 ## @var[out] HIGHLIGHT_BUFF 1461 ## @var[out] HIGHLIGHT_UMIN 1462 ## @var[out] HIGHLIGHT_UMAX 1463 function ble/highlight/layer/update { 1464 local text=$1 iN=${#1} opts=$2 1465 local DMIN=${3:-0} DMAX=${4:-$iN} DMAX0=${5:-0} 1466 1467 local PREV_BUFF=_ble_highlight_layer_plain_buff 1468 local PREV_UMIN=-1 1469 local PREV_UMAX=-1 1470 local layer player=plain LEVEL 1471 local nlevel=${#_ble_highlight_layer_list[@]} 1472 for ((LEVEL=0;LEVEL<nlevel;LEVEL++)); do 1473 layer=${_ble_highlight_layer_list[LEVEL]} 1474 1475 "ble/highlight/layer:$layer/update" "$text" "$player" 1476 # echo "PREV($LEVEL) $PREV_UMIN $PREV_UMAX" >> 1.tmp 1477 1478 player=$layer 1479 done 1480 1481 HIGHLIGHT_BUFF=$PREV_BUFF 1482 HIGHLIGHT_UMIN=$PREV_UMIN 1483 HIGHLIGHT_UMAX=$PREV_UMAX 1484 } 1485 1486 function ble/highlight/layer/update/add-urange { 1487 local umin=$1 umax=$2 1488 (((PREV_UMIN<0||PREV_UMIN>umin)&&(PREV_UMIN=umin), 1489 (PREV_UMAX<0||PREV_UMAX<umax)&&(PREV_UMAX=umax))) 1490 } 1491 function ble/highlight/layer/update/shift { 1492 local _ble_local_dstarr=$1 1493 local _ble_local_srcarr=${2:-$_ble_local_dstarr} 1494 if ((DMIN>=0)); then 1495 ble/array#reserve-prototype "$((DMAX-DMIN))" 1496 builtin eval " 1497 $_ble_local_dstarr=( 1498 \"\${$_ble_local_srcarr[@]::DMIN}\" 1499 \"\${_ble_array_prototype[@]::DMAX-DMIN}\" 1500 \"\${$_ble_local_srcarr[@]:DMAX0}\")" 1501 else 1502 [[ $_ble_local_dstarr != "$_ble_local_srcarr" ]] && 1503 builtin eval -- "$_ble_local_dstarr=(\"\${$_ble_local_srcarr[@]}\")" 1504 fi 1505 } 1506 1507 function ble/highlight/layer/update/getg { 1508 g= 1509 local LEVEL=$LEVEL 1510 while ((--LEVEL>=0)); do 1511 "ble/highlight/layer:${_ble_highlight_layer_list[LEVEL]}/getg" "$1" 1512 [[ $g ]] && return 0 1513 done 1514 g=0 1515 } 1516 1517 ## @fn ble/highlight/layer/getg index 1518 ## @param[in] index 1519 ## @var[out] g 1520 function ble/highlight/layer/getg { 1521 LEVEL=${#_ble_highlight_layer_list[*]} ble/highlight/layer/update/getg "$1" 1522 } 1523 1524 ## レイヤーの実装 1525 ## 先ず作成するレイヤーの名前を決めます。ここでは <layerName> とします。 1526 ## 次に、以下の配列変数と二つの関数を用意します。 1527 ## 1528 ## @arr _ble_highlight_layer_<layerName>_VARNAMES 1529 ## レイヤーの動的な状態を保持する変数の一覧です。ble/textarea#save-state で参 1530 ## 照されます。もしこの配列が定義されていない場合は、代わりに 1531 ## _ble_highlight_layer_<layerName>_ で始まる変数名を全て記録します。 1532 ## 1533 ## @arr _ble_highlight_layer_<layerName>_buff=() 1534 ## グローバルに定義する配列変数です。 1535 ## 後述の ble/highlight/layer:<layerName>/update が呼ばれた時に更新します。 1536 ## 1537 ## 各要素は編集文字列の各文字に対応しています。 1538 ## 各要素は "<SGR指定><表示文字>" の形式になります。 1539 ## 1540 ## "SGR指定" には描画属性を指定するエスケープシーケンスを指定します。 1541 ## "SGR指定" は前の文字と同じ描画属性の場合には省略可能です。 1542 ## この描画属性は現在のレイヤーとその下層にある全てのレイヤーの結果を総合した物になります。 1543 ## この描画属性は後述する ble/highlight/layer/getg 関数によって得られる 1544 ## g 値と対応している必要があります。 1545 ## 1546 ## "<表示文字>" は編集文字列中の文字に対応する、予め定められた文字列です。 1547 ## 基本レイヤーである plain の _ble_highlight_layer_plain_buff 配列に 1548 ## 対応する "<表示文字>" が (SGR属性無しで) 格納されているのでこれを使用して下さい。 1549 ## 表示文字の内容は基本的に、その文字自身と同一の物になります。 1550 ## 但し、改行を除く制御文字の場合には、文字自身とは異なる "<表示文字>" になります。 1551 ## ASCII code 1-8, 11-31 の文字については "^A" ~ "^_" という2文字になります。 1552 ## ASCII code 9 (TAB) の場合には、空白が幾つか (端末の設定に応じた数だけ) 並んだ物になります。 1553 ## ASCII code 127 (DEL) については "^?" という2文字の表現になります。 1554 ## 通常は _ble_highlight_layer_plain_buff に格納されている値をそのまま使えば良いので、 1555 ## これらの "<表示文字>" の詳細について考慮に入れる必要はありません。 1556 ## 1557 ## @fn ble/highlight/layer:<layerName>/update text player 1558 ## _ble_highlight_layer_<layerName>_buff の内容を更新します。 1559 ## 1560 ## @param[in] text 1561 ## @var [in] DMIN DMAX DMAX0 1562 ## 第一引数 text には現在の編集文字列が指定されます。 1563 ## シェル変数 DMIN DMAX DMAX0 には前回の呼出の後の編集文字列の変更位置が指定されます。 1564 ## DMIN<0 の時は前回の呼出から text が変わっていない事を表します。 1565 ## DMIN>=0 の時は、現在の text の DMIN から DMAX までが変更された部分になります。 1566 ## DMAX0 は、DMAX の編集前の対応位置を表します。幾つか例を挙げます: 1567 ## - aaaa の 境界2 に挿入があって aaxxaa となった場合、DMIN DMAX DMAX0 は 2 4 2 となります。 1568 ## - aaxxaa から xx を削除して aaaa になった場合、DMIN DMAX DMAX0 はそれぞれ 2 2 4 となります。 1569 ## - aaxxaa が aayyyaa となった場合 DMIN DMAX DMAX0 は 2 5 4 となります。 1570 ## - aaxxaa が aazzaa となった場合 DMIN DMAX DMAX0 は 2 4 4 となります。 1571 ## 1572 ## @param[in] player 1573 ## @var [in,out] LAYER_UMIN (unused) 1574 ## @var [in,out] LAYER_UMAX (unused) 1575 ## @param[in] PREV_BUFF 1576 ## @var [in,out] PREV_UMIN 1577 ## @var [in,out] PREV_UMAX 1578 ## player には現在のレイヤーの一つ下にあるレイヤーの名前が指定されます。 1579 ## 通常 _ble_highlight_layer_<layerName>_buff は 1580 ## _ble_highlight_layer_<player>_buff の値を上書きする形で実装します。 1581 ## LAYER_UMIN, LAYER_UMAX は _ble_highlight_layer_<player>_buff において、 1582 ## 前回の呼び出し以来、変更のあった範囲が指定されます。 1583 ## 1584 ## @param[in,out] _ble_highlight_layer_<layerName>_buff 1585 ## 前回の呼出の時の状態で関数が呼び出されます。 1586 ## DMIN DMAX DMAX0, LAYER_UMIN, LAYER_UMAX を元に 1587 ## 前回から描画属性の変化がない部分については、 1588 ## 呼出時に入っている値を再利用する事ができます。 1589 ## ble/highlight/layer/update/shift 関数も参照して下さい。 1590 ## 1591 ## @fn ble/highlight/layer:<layerName>/getg index 1592 ## 指定した index に対応する描画属性の値を g 値で取得します。 1593 ## 前回の ble/highlight/layer:<layerName>/update の呼出に基づく描画属性です。 1594 ## @var[out] g 1595 ## 結果は変数 g に設定する事によって返します。 1596 ## より下層のレイヤーの値を引き継ぐ場合には空文字列を設定します: g= 1597 ## 1598 1599 #------------------------------------------------------------------------------ 1600 # ble/highlight/layer:plain 1601 1602 _ble_highlight_layer_plain_VARNAMES=( 1603 _ble_highlight_layer_plain_buff) 1604 function ble/highlight/layer:plain/initialize-vars { 1605 _ble_highlight_layer_plain_buff=() 1606 } 1607 ble/highlight/layer:plain/initialize-vars 1608 1609 ## @fn ble/highlight/layer:plain/update/.getch 1610 ## @var[in,out] ch 1611 function ble/highlight/layer:plain/update/.getch { 1612 [[ $ch == [' '-'~'] ]] && return 0 1613 if [[ $ch == [$'\t\n\177'] ]]; then 1614 if [[ $ch == $'\t' ]]; then 1615 ch=${_ble_string_prototype::it} 1616 elif [[ $ch == $'\n' ]]; then 1617 ch=$_ble_term_el$_ble_term_nl 1618 elif [[ $ch == $'\177' ]]; then 1619 ch='^?' 1620 fi 1621 else 1622 local ret; ble/util/s2c "$ch" 1623 local cs=${_ble_unicode_GraphemeCluster_ControlRepresentation[ret]} 1624 if [[ $cs ]]; then 1625 ch=$cs 1626 elif ((ret<0x20)); then 1627 ble/util/c2s "$((ret+64))" 1628 ch="^$ret" 1629 elif ((0x80<=ret&&ret<=0x9F)); then 1630 # C1 characters 1631 ble/util/c2s "$((ret-64))" 1632 ch="M-^$ret" 1633 fi 1634 fi 1635 } 1636 1637 ## @fn ble/highlight/layer:<layerName>/update text pbuff 1638 function ble/highlight/layer:plain/update { 1639 if ((DMIN>=0)); then 1640 ble/highlight/layer/update/shift _ble_highlight_layer_plain_buff 1641 1642 local i text=$1 ch 1643 local it=$_ble_term_it 1644 for ((i=DMIN;i<DMAX;i++)); do 1645 ch=${text:i:1} 1646 1647 # LC_COLLATE for cygwin collation 1648 local LC_ALL= LC_COLLATE=C 1649 ble/highlight/layer:plain/update/.getch 1650 1651 _ble_highlight_layer_plain_buff[i]=$ch 1652 done 1653 fi 1654 1655 PREV_BUFF=_ble_highlight_layer_plain_buff 1656 ((PREV_UMIN=DMIN,PREV_UMAX=DMAX)) 1657 } 1658 # Note: suppress LC_COLLATE errors #D1205 #D1440 1659 ble/function#suppress-stderr ble/highlight/layer:plain/update 1660 1661 ## @fn ble/highlight/layer:plain/getg index 1662 ## @var[out] g 1663 function ble/highlight/layer:plain/getg { 1664 g=0 1665 } 1666 1667 #------------------------------------------------------------------------------ 1668 # abstract layer {selection} 1669 1670 # This layer supports multiple selections with different gflags. 1671 1672 function ble/highlight/layer:{selection}/declare { 1673 local layer_name=$1 1674 local layer_prefix=_ble_highlight_layer_${layer_name}_ 1675 builtin eval -- " 1676 ${layer_prefix}VARNAMES=( 1677 ${layer_prefix}buff 1678 ${layer_prefix}osel 1679 ${layer_prefix}ogflags)" 1680 ble/highlight/layer:{selection}/initialize-vars "$layer_name" 1681 } 1682 1683 ## @fn ble/highlight/layer:{selection}/initialize-vars layer_name 1684 ## レイヤーで内部使用する配列を初期化します。 1685 ## @arr[out] _ble_highlight_layer_<layer_name>_buff 1686 ## @arr[out] _ble_highlight_layer_<layer_name>_osel 1687 ## 前回の選択範囲の端点を保持する配列です。 1688 ## @arr[out] _ble_highlight_layer_<layer_name>_ogflags 1689 ## 前回の選択範囲の着色を保持します。 1690 ## 1691 function ble/highlight/layer:{selection}/initialize-vars { 1692 local layer_name=$1 1693 local layer_prefix=_ble_highlight_layer_${layer_name}_ 1694 builtin eval -- " 1695 ${layer_prefix}buff=() 1696 ${layer_prefix}osel=() 1697 ${layer_prefix}ogflags=()" 1698 } 1699 1700 ## @fn ble/highlight/layer:{selection}/.invalidate a b 1701 ## Include the range [a, b) (or [b, a) for the reversed range) in the dirty 1702 ## range (i.e., the range of the command line that needs to be re-rendered 1703 ## because of the updates). 1704 ## 1705 ## @param[in] a b 1706 ## @var[in,out] umin umax 1707 function ble/highlight/layer:{selection}/.invalidate { 1708 local a=$1 b=$2 p q 1709 ((a==b)) && return 0 1710 (((a<b?(p=a,q=b):(p=b,q=a)), 1711 (umin<0||umin>p)&&(umin=p), 1712 (umax<0||umax<q)&&(umax=q))) 1713 } 1714 1715 ## @fn ble/highlight/layer:{selection}/update layer_name text 1716 ## 1717 ## @param[in] layer_name 1718 ## This is used to save/restore the layer information. All the related 1719 ## data are stored in the variables of the names: 1720 ## `_ble_highlight_layer_${layer_name}_${name}`. 1721 ## @arr[in] sel gflags 1722 ## The caller should prepare the list of the selection and the 1723 ## corresponding gflags. The array `sel` contains two elements for each 1724 ## selection while `gflags` contains one for each. The content of `sel` is 1725 ## (<selection1-begin> <selection1-end> <selection2-begin> <selection2-end> 1726 ## ... <selectionN-begin> <selectionN-end>) where <selectionX-begin/end> 1727 ## are integers specifying indices in the command-line string. The content 1728 ## of `gflags` is (<selection1-gflags> <selection2-gflags> 1729 ## ... <selectionN-gflags>) where <selectionX-gflags> are integers 1730 ## specifying the highlighting style in gflags. 1731 ## 1732 ## When `sel` is set to -1, it means that the selections do not change from 1733 ## its previous state. 1734 function ble/highlight/layer:{selection}/update { 1735 local layer_name=$1 1736 local layer_prefix=_ble_highlight_layer_${layer_name}_ 1737 shift 1738 1739 local IFS=$_ble_term_IFS 1740 1741 # Retrieve the previous selections and adjust positions by the insertion and 1742 # deletion of substrings in the command line. 1743 local omin=-1 omax=-1 osel ogflags olen 1744 ble/util/restore-vars "$layer_prefix" osel ogflags 1745 olen=${#osel[@]} 1746 if ((olen)); then 1747 if ((DMIN>=0)); then 1748 local k 1749 for ((k=0;k<olen;k++)); do 1750 if ((DMAX0<=osel[k])); then 1751 ((osel[k]+=DMAX-DMAX0)) 1752 elif ((DMIN<osel[k])); then 1753 ((osel[k]=DMIN)) 1754 fi 1755 done 1756 fi 1757 omin=${osel[0]} 1758 omax=${osel[olen-1]} 1759 fi 1760 1761 # Retrieve the new selections. The array `sel` and `gflags` are supposed to 1762 # be specified by the caller. 1763 if ((sel==-1)); then 1764 sel=("${osel[@]}") 1765 gflags=("${ogflags[@]}") 1766 fi 1767 local rlen=${#sel[@]} 1768 1769 # 変更がない時はそのまま通過 1770 if ((DMIN<0&&(PREV_UMIN<0||rlen>=2&&sel[0]<=PREV_UMIN&&PREV_UMAX<=sel[1]))); then 1771 if [[ ${sel[*]} == "${osel[*]}" && ${gflags[*]} == "${ogflags[*]}" ]]; then 1772 [[ ${sel[*]} ]] && PREV_BUFF=${layer_prefix}buff 1773 return 0 1774 fi 1775 else 1776 [[ ! ${sel[*]} && ! ${osel[*]} ]] && return 0 1777 fi 1778 1779 local umin=-1 umax=-1 1780 if ((rlen)); then 1781 # 選択範囲がある時 1782 local rmin=${sel[0]} 1783 local rmax=${sel[rlen-1]} 1784 1785 # 描画文字配列の更新 1786 local -a buff=() 1787 local g ret 1788 local k=0 inext iprev=0 1789 for inext in "${sel[@]}"; do 1790 if ((inext>iprev)); then 1791 if ((k==0)); then 1792 ble/array#push buff "\"\${$PREV_BUFF[@]::$inext}\"" 1793 elif ((k%2)); then 1794 ble/color/g2sgr "${gflags[k/2]}" 1795 ble/array#push buff "\"$ret\${_ble_highlight_layer_plain_buff[@]:$iprev:$((inext-iprev))}\"" 1796 else 1797 ble/highlight/layer/update/getg "$iprev" 1798 ble/color/g2sgr "$g" 1799 ble/array#push buff "\"$ret\${$PREV_BUFF[@]:$iprev:$((inext-iprev))}\"" 1800 fi 1801 fi 1802 ((iprev=inext,k++)) 1803 done 1804 ble/highlight/layer/update/getg "$iprev" 1805 ble/color/g2sgr "$g" 1806 ble/array#push buff "\"$ret\${$PREV_BUFF[@]:$iprev}\"" 1807 builtin eval -- "${layer_prefix}buff=(${buff[*]})" 1808 PREV_BUFF=${layer_prefix}buff 1809 1810 # (Dirty range 1) DMIN-DMAX の間 1811 if ((DMIN>=0)); then 1812 ble/highlight/layer:{selection}/.invalidate "$DMIN" "$DMAX" 1813 fi 1814 1815 # (Dirty range 2) 選択範囲の変更 1816 if ((olen==2&&rlen==2)); then 1817 # Optimized code for the case where both osel and sel are single 1818 # selections (i.e., the next `if ((omin>=0))` branch is general and 1819 # should also work for this specific case). 1820 # 1821 # Note: The following two branches are currently equivalent because 1822 # `.invalidate` manages only a single covering range [umin, umax), but 1823 # these are semantically different when multiple ranges would be managed 1824 # by `.invalidate`. 1825 if [[ ${gflags[0]} != "${ogflags[0]}" ]]; then 1826 # 色が変化する場合 1827 ble/highlight/layer:{selection}/.invalidate "$omin" "$omax" 1828 ble/highlight/layer:{selection}/.invalidate "$rmin" "$rmax" 1829 else 1830 # 端点の移動による再描画 1831 ble/highlight/layer:{selection}/.invalidate "$omin" "$rmin" 1832 ble/highlight/layer:{selection}/.invalidate "$omax" "$rmax" 1833 fi 1834 elif ((omin>=0)); then 1835 # Find the first and last non-matching selection boundaries and update 1836 # the dirty range. 1837 local k m 1838 local min_len=$((olen<rlen?olen:rlen)) 1839 local max_len=$((olen>rlen?olen:rlen)) 1840 for ((k=0;k<max_len;k++)); do 1841 # Compare each selection in `sel` and `osel` and advance `k` as far as 1842 # the selections match. If there is no corresponding selection or the 1843 # color is different or the boundary is different, process the later 1844 # part of the loop. Otherwise, skip this loop and try next `k`. 1845 if ((k<min_len)); then 1846 [[ k%2 -eq 0 && ${gflags[k/2]} != "${ogflags[k/2]}" ]] || 1847 ((sel[k]!=osel[k])) || 1848 continue 1849 fi 1850 local smin=$((sel[k]<osel[k]?sel[k]:osel[k])) 1851 1852 for ((m=0;m<max_len;m++)); do 1853 local rind=$((rlen-m-1)) oind=$((olen-m-1)) 1854 # Compare each selection from the end of the list (where `m` is the 1855 # offset from the end) and increment `m` as far as the selections 1856 # match. If there is no correponding selection or the color is 1857 # different or the boundary is different, process the later part of 1858 # the loop. Otherwise, skip this loop and try next `m`. 1859 if ((m==min_len)); then 1860 [[ m%2 -eq 0 && ${gflags[rind/2]} != "${ogflags[oind/2]}" ]] || 1861 ((sel[rind]!=osel[oind])) || 1862 continue 1863 fi 1864 local smax=$((sel[rind]>osel[oind]?sel[rind]:osel[oind])) 1865 1866 ((smin<smax)) && 1867 ble/highlight/layer:{selection}/.invalidate "$smin" "$smax" 1868 break 1869 done 1870 break 1871 done 1872 else 1873 # 新規選択 1874 ble/highlight/layer:{selection}/.invalidate "$rmin" "$rmax" 1875 fi 1876 1877 # (Dirty range 3) 下層の変更 (rmin ~ rmax は表には反映されない) 1878 local pmin=$PREV_UMIN pmax=$PREV_UMAX 1879 if ((rlen==2)); then 1880 # Optimized code for the single-selection case (i.e., the next `if 1881 # ((rlen))` branch is general and should also work for this specific 1882 # case). 1883 ((rmin<=pmin&&pmin<rmax&&(pmin=rmax), 1884 rmin<pmax&&pmax<=rmax&&(pmax=rmin))) 1885 elif ((rlen)); then 1886 # この層の選択範囲で隠されている部分は省略可能 1887 local k 1888 for ((k=0;k<rlen;k+=2)); do 1889 if ((pmin<sel[k])); then 1890 break 1891 elif ((sel[k]<=pmin&&pmin<sel[k+1])); then 1892 pmin=${sel[k+1]} 1893 fi 1894 done 1895 1896 for ((k=rlen-2;k>=0;k-=2)); do 1897 if ((sel[k+1]<pmax)); then 1898 break 1899 elif ((sel[k]<pmax&&pmax<=sel[k+1])); then 1900 pmax=${sel[k]} 1901 fi 1902 done 1903 fi 1904 ble/highlight/layer:{selection}/.invalidate "$pmin" "$pmax" 1905 else 1906 # 選択範囲がない時 1907 1908 # 下層の変更 1909 umin=$PREV_UMIN umax=$PREV_UMAX 1910 1911 # 選択解除の範囲 1912 ble/highlight/layer:{selection}/.invalidate "$omin" "$omax" 1913 fi 1914 1915 osel=("${sel[@]}") 1916 ogflags=("${gflags[@]}") 1917 ble/util/save-vars "$layer_prefix" osel ogflags 1918 ((PREV_UMIN=umin,PREV_UMAX=umax)) 1919 } 1920 1921 function ble/highlight/layer:{selection}/getg { 1922 local layer_name=$1 1923 local layer_prefix=_ble_highlight_layer_${layer_name}_ 1924 shift 1925 1926 local index=$1 1927 1928 local osel olen 1929 ble/util/restore-vars "$layer_prefix" osel 1930 olen=${#osel[@]} 1931 ((olen)) || return 1 1932 ((osel[0]<=index&&index<osel[olen-1])) || return 1 1933 1934 local isel= 1935 if ((olen>=4)); then 1936 # When there are multiple selections, we identify the position of `index` 1937 # using bisection. 1938 local l=0 u=$((olen-1)) m 1939 while ((l+1<u)); do 1940 ((osel[m=(l+u)/2]<=index?(l=m):(u=m))) 1941 done 1942 1943 # When `l` sits at the end of a selection, check if the next selection 1944 # immediately starts. If it is the case, we increment `l` to pick the 1945 # gflags of the next selection. 1946 ((l%2&&l+1<olen&&osel[l]==osel[l+1]&&l++)) 1947 1948 ((l%2==0)) && ((isel=l/2)) 1949 else 1950 # When there is only a single selection, the position `index` is always 1951 # inside the selection because otherwise we already returned from the 1952 # function by an earlier check. 1953 isel=0 1954 fi 1955 1956 if [[ $isel ]]; then 1957 local ref=${layer_prefix}ogflags[isel] 1958 g=${!ref} 1959 fi 1960 } 1961 1962 #------------------------------------------------------------------------------ 1963 # ble/highlight/layer:region 1964 1965 function ble/color/defface.onload { 1966 ble/color/defface region bg=60,fg=white 1967 ble/color/defface region_target bg=153,fg=black 1968 ble/color/defface region_match bg=55,fg=white 1969 ble/color/defface region_insert fg=12,bg=252 1970 ble/color/defface disabled fg=242 1971 ble/color/defface overwrite_mode fg=black,bg=51 1972 } 1973 blehook color_defface_load+=ble/color/defface.onload 1974 1975 ## @arr _ble_highlight_layer_region_buff 1976 ## 1977 ## @arr _ble_highlight_layer_region_osel 1978 ## 前回の選択範囲の端点を保持する配列です。 1979 ## 1980 ## @var _ble_highlight_layer_region_ogflags 1981 ## 前回の選択範囲の着色を保持します。 1982 ## 1983 ble/highlight/layer:{selection}/declare region 1984 1985 function ble/highlight/layer:region/update { 1986 local -a sel=() gflags=() 1987 if [[ $_ble_edit_mark_active ]]; then 1988 # 外部定義の選択範囲があるか確認 1989 # vi-mode のビジュアルモード (文字選択、行選択、矩形選択) の実装で使用する。 1990 local -a selection=() 1991 if ! ble/function#try ble/highlight/layer:region/mark:"$_ble_edit_mark_active"/get-selection; then 1992 if ((_ble_edit_mark>_ble_edit_ind)); then 1993 selection=("$_ble_edit_ind" "$_ble_edit_mark") 1994 elif ((_ble_edit_mark<_ble_edit_ind)); then 1995 selection=("$_ble_edit_mark" "$_ble_edit_ind") 1996 fi 1997 fi 1998 1999 # gflags の決定 2000 local face=region 2001 ble/function#try ble/highlight/layer:region/mark:"$_ble_edit_mark_active"/get-face 2002 local ret; ble/color/face2g "$face"; local g=$ret 2003 2004 sel=("${selection[@]}") 2005 ble/array#fill-range gflags 0 "$((${#selection[@]}/2))" "$g" 2006 fi 2007 2008 ble/highlight/layer:{selection}/update region "$@" 2009 } 2010 2011 function ble/highlight/layer:region/getg { 2012 ble/highlight/layer:{selection}/getg region "$@" 2013 } 2014 2015 #------------------------------------------------------------------------------ 2016 # ble/highlight/layer:disabled 2017 2018 _ble_highlight_layer_disabled_VARNAMES=( 2019 _ble_highlight_layer_disabled_prev 2020 _ble_highlight_layer_disabled_buff) 2021 function ble/highlight/layer:disabled/initialize-vars { 2022 _ble_highlight_layer_disabled_prev= 2023 _ble_highlight_layer_disabled_buff=() 2024 } 2025 ble/highlight/layer:disabled/initialize-vars 2026 2027 function ble/highlight/layer:disabled/update { 2028 if [[ $_ble_edit_line_disabled ]]; then 2029 if ((DMIN>=0)) || [[ ! $_ble_highlight_layer_disabled_prev ]]; then 2030 local ret; ble/color/face2sgr disabled; local sgr=$ret 2031 _ble_highlight_layer_disabled_buff=("$sgr""${_ble_highlight_layer_plain_buff[@]}") 2032 fi 2033 PREV_BUFF=_ble_highlight_layer_disabled_buff 2034 2035 if [[ $_ble_highlight_layer_disabled_prev ]]; then 2036 PREV_UMIN=$DMIN PREV_UMAX=$DMAX 2037 else 2038 PREV_UMIN=0 PREV_UMAX=${#1} 2039 fi 2040 else 2041 if [[ $_ble_highlight_layer_disabled_prev ]]; then 2042 PREV_UMIN=0 PREV_UMAX=${#1} 2043 fi 2044 fi 2045 2046 _ble_highlight_layer_disabled_prev=$_ble_edit_line_disabled 2047 } 2048 2049 function ble/highlight/layer:disabled/getg { 2050 if [[ $_ble_highlight_layer_disabled_prev ]]; then 2051 local ret; ble/color/face2g disabled; g=$ret 2052 fi 2053 } 2054 2055 #------------------------------------------------------------------------------ 2056 # ble/highlight/layer:overwrite_mode 2057 2058 _ble_highlight_layer_overwrite_mode_VARNAMES=( 2059 _ble_highlight_layer_overwrite_mode_index 2060 _ble_highlight_layer_overwrite_mode_buff) 2061 function ble/highlight/layer:overwrite_mode/initialize-vars { 2062 _ble_highlight_layer_overwrite_mode_index=-1 2063 _ble_highlight_layer_overwrite_mode_buff=() 2064 } 2065 ble/highlight/layer:overwrite_mode/initialize-vars 2066 2067 function ble/highlight/layer:overwrite_mode/update { 2068 local oindex=$_ble_highlight_layer_overwrite_mode_index 2069 if ((DMIN>=0)); then 2070 if ((oindex>=DMAX0)); then 2071 ((oindex+=DMAX-DMAX0)) 2072 elif ((oindex>=DMIN)); then 2073 oindex=-1 2074 fi 2075 fi 2076 2077 local index=-1 2078 if [[ $_ble_edit_overwrite_mode && ! $_ble_edit_mark_active ]]; then 2079 local next=${_ble_edit_str:_ble_edit_ind:1} 2080 if [[ $next && $next != [$'\n\t'] ]]; then 2081 index=$_ble_edit_ind 2082 2083 local g ret 2084 2085 # PREV_BUFF の内容をロード 2086 if ((PREV_UMIN<0&&oindex>=0)); then 2087 # 前回の結果が残っている場合 2088 ble/highlight/layer/update/getg "$oindex" 2089 ble/color/g2sgr "$g" 2090 _ble_highlight_layer_overwrite_mode_buff[oindex]=$ret${_ble_highlight_layer_plain_buff[oindex]} 2091 else 2092 # コピーした方が速い場合 2093 builtin eval "_ble_highlight_layer_overwrite_mode_buff=(\"\${$PREV_BUFF[@]}\")" 2094 fi 2095 PREV_BUFF=_ble_highlight_layer_overwrite_mode_buff 2096 2097 # 1文字着色 2098 # ble/highlight/layer/update/getg "$index" 2099 # ((g^=_ble_color_gflags_Revert)) 2100 ble/color/face2g overwrite_mode 2101 ble/color/g2sgr "$ret" 2102 _ble_highlight_layer_overwrite_mode_buff[index]=$ret${_ble_highlight_layer_plain_buff[index]} 2103 if ((index+1<${#1})); then 2104 ble/highlight/layer/update/getg "$((index+1))" 2105 ble/color/g2sgr "$g" 2106 _ble_highlight_layer_overwrite_mode_buff[index+1]=$ret${_ble_highlight_layer_plain_buff[index+1]} 2107 fi 2108 fi 2109 fi 2110 2111 if ((index>=0)); then 2112 ble/term/cursor-state/hide 2113 else 2114 ble/term/cursor-state/reveal 2115 fi 2116 2117 if ((index!=oindex)); then 2118 ((oindex>=0)) && ble/highlight/layer/update/add-urange "$oindex" "$((oindex+1))" 2119 ((index>=0)) && ble/highlight/layer/update/add-urange "$index" "$((index+1))" 2120 fi 2121 2122 _ble_highlight_layer_overwrite_mode_index=$index 2123 } 2124 function ble/highlight/layer:overwrite_mode/getg { 2125 local index=$_ble_highlight_layer_overwrite_mode_index 2126 if ((index>=0&&index==$1)); then 2127 # ble/highlight/layer/update/getg "$1" 2128 # ((g^=_ble_color_gflags_Revert)) 2129 local ret; ble/color/face2g overwrite_mode; g=$ret 2130 fi 2131 } 2132 2133 #------------------------------------------------------------------------------ 2134 2135 _ble_highlight_layer_list=(plain syntax region overwrite_mode disabled)