-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
canvas.sh
2768 lines (2564 loc) · 85.2 KB
/
canvas.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
## @bleopt tab_width
## タブの表示幅を指定します。
##
## bleopt_tab_width= (既定)
## 空文字列を指定したときは $(tput it) を用います。
## bleopt_tab_width=NUM
## 数字を指定したときはその値をタブの幅として用います。
bleopt/declare -v tab_width ''
function bleopt/check:tab_width {
if [[ $value ]] && (((value=value)<=0)); then
ble/util/print "bleopt: an empty string or a positive value is required for tab_width." >&2
return 1
fi
}
#------------------------------------------------------------------------------
# ble/arithmetic
## ble/arithmetic/sum integer...
## @var[out] ret
function ble/arithmetic/sum {
IFS=+ builtin eval 'let "ret=$*+0"'
}
#------------------------------------------------------------------------------
# ble/util/c2w
# ※注意 [ -~] の範囲の文字は全て幅1であるという事を仮定したコードが幾らかある
# もしこれらの範囲の文字を幅1以外で表示する端末が有ればそれらのコードを実装し
# 直す必要がある。その様な変な端末があるとは思えないが。
## @bleopt char_width_mode
## 文字の表示幅の計算方法を指定します。
## bleopt_char_width_mode=east
## Unicode East_Asian_Width=A (Ambiguous) の文字幅を全て 2 とします
## bleopt_char_width_mode=west
## Unicode East_Asian_Width=A (Ambiguous) の文字幅を全て 1 とします
## bleopt_char_width_mode=auto
## east または west を自動判定します。
## bleopt_char_width_mode=emacs
## emacs で用いられている既定の文字幅の設定です
## 定義 ble/util/c2w+$bleopt_char_width_mode
bleopt/declare -n char_width_mode auto
function bleopt/check:char_width_mode {
if ! ble/is-function "ble/util/c2w+$value"; then
ble/util/print "bleopt: Invalid value char_width_mode='$value'. A function 'ble/util/c2w+$value' is not defined." >&2
return 1
fi
if [[ $_ble_attached && $value == auto ]]; then
ble/util/c2w+auto/update.buff first-line
ble/util/buffer.flush >&2
fi
}
## @fn ble/util/c2w ccode
## @var[out] ret
function ble/util/c2w {
"ble/util/c2w+$bleopt_char_width_mode" "$1"
}
## @fn ble/util/c2w-edit ccode
## 編集画面での表示上の文字幅を返します。
## @var[out] ret
function ble/util/c2w-edit {
if (($1<32||127<=$1&&$1<160)); then
# 制御文字は ^? と表示される。
ret=2
# TAB は???
# 128-159: M-^?
((128<=$1&&(ret=4)))
else
ble/util/c2w "$1"
fi
}
# ## @fn ble/util/c2w-edit ccode
# ## @var[out] ret
# function ble/util/s2w {
# ble/util/s2c "${1:$2:1}"
# "ble/util/c2w+$bleopt_char_width_mode" "$ret"
# }
# ---- 文字種判定 ----
## @arr _ble_util_c2w_except
_ble_util_c2w_zenkaku_min=11904 # 0x2E80
_ble_util_c2w_zenkaku_max=42192 # 0xA4D0
_ble_util_c2w_except=(
# 0x2E80..0xA4D0 の範囲内で飛び地になっている全角とは限らない文字
[0x303F]=1 # 半角スペース
[0x3030]=-2 [0x303d]=-2 [0x3297]=-2 [0x3299]=-2 # 絵文字
)
## @fn ble/util/c2w/.determine-unambiguous
## @var[out] ret
function ble/util/c2w/.determine-unambiguous {
local code=$1
if ((code<0xA0)); then
ret=1
return 0
fi
# 取り敢えず曖昧
ret=-1
# 以下は全角に確定している範囲
if ((code<0xFB00)); then
((_ble_util_c2w_zenkaku_min<=code&&code<_ble_util_c2w_zenkaku_max&&!_ble_util_c2w_except[code]||
0xAC00<=code&&code<0xD7A4||
0xF900<=code||
0x1100<=code&&code<0x1160||
code==0x2329||code==0x232A)) && ret=2
elif ((code<0x10000)); then
((0xFF00<=code&&code<0xFF61||
0xFE30<=code&&code<0xFE70||
0xFFE0<=code&&code<0xFFE7)) && ret=2
else
((0x20000<=code&&code<0x2FFFE||
0x30000<=code&&code<0x3FFFE)) && ret=2
fi
}
## @var _ble_canvas_emoji_expr_maybe
## @arr _ble_canvas_emoji_database
## @arr _ble_canvas_emoji_database_????
## @bleopt emoji_version
##
## ファイル src/canvas.emoji.sh は以下のコマンドで生成する。
## $ ./make_command.sh update-emoji-database
##
#%< canvas.emoji.sh
bleopt/declare -n emoji_width 2
function bleopt/check:emoji_version {
local rex='^0*([0-9]+)\.0*([0-9]+)$'
if ! [[ $value =~ $rex ]]; then
ble/util/print "bleopt: Invalid value for emoji_version: '$value'." >&2
return 1
fi
local src
ble/util/sprintf src _ble_canvas_emoji_database_%04d $((BASH_REMATCH[1]*100+BASH_REMATCH[2]))
if ! ble/is-array "$src"; then
ble/util/print "bleopt: Unsupported emoji_version '$value'." >&2
return 1
fi
builtin eval -- "_ble_canvas_emoji_database=(\"\${$src[@]}\")"
return 0
}
## @fn ble/util/c2w/is-emoji code
## @param[in] code
function ble/util/c2w/is-emoji {
local code=$1
((_ble_canvas_emoji_expr_maybe)) || return 1
local l=0 u=${#_ble_canvas_emoji_database[@]} m
while ((l+1<u)); do
((_ble_canvas_emoji_database[m=(l+u)/2]<=code?(l=m):(u=m)))
done
(((l&1)==0)); return "$?"
}
# ---- char_width_mode ----
## @fn ble/util/c2w+emacs
## emacs-24.2.1 default char-width-table
## @var[out] ret
_ble_util_c2w_emacs_wranges=(
162 164 167 169 172 173 176 178 180 181 182 183 215 216 247 248 272 273 276 279
280 282 284 286 288 290 293 295 304 305 306 308 315 316 515 516 534 535 545 546
555 556 608 618 656 660 722 723 724 725 768 769 770 772 775 777 779 780 785 787
794 795 797 801 805 806 807 813 814 815 820 822 829 830 850 851 864 866 870 872
874 876 898 900 902 904 933 934 959 960 1042 1043 1065 1067 1376 1396 1536 1540 1548 1549
1551 1553 1555 1557 1559 1561 1563 1566 1568 1569 1571 1574 1576 1577 1579 1581 1583 1585 1587 1589
1591 1593 1595 1597 1599 1600 1602 1603 1611 1612 1696 1698 1714 1716 1724 1726 1734 1736 1739 1740
1742 1744 1775 1776 1797 1799 1856 1857 1858 1859 1898 1899 1901 1902 1903 1904)
function ble/util/c2w+emacs {
local code=$1 al=0 ah=0 tIndex=
# bash-4.0 bug workaround
# 中で使用している変数に日本語などの文字列が入っているとエラーになる。
# その値を参照していなくても、その分岐に入らなくても関係ない。
# なので ret に予め適当な値を設定しておく事にする。
ret=1
((code<0xA0)) && return 0
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then
((ret=bleopt_emoji_width))
return 0
fi
((
0x3100<=code&&code<0xA4D0||0xAC00<=code&&code<0xD7A4?(
ret=2
):(0x2000<=code&&code<0x2700?(
tIndex=0x0100+code-0x2000
):(
al=code&0xFF,
ah=code/256,
ah==0x00?(
tIndex=al
):(ah==0x03?(
ret=0xFF&((al-0x91)&~0x20),
ret=ret<25&&ret!=17?2:1
):(ah==0x04?(
ret=al==1||0x10<=al&&al<=0x50||al==0x51?2:1
):(ah==0x11?(
ret=al<0x60?2:1
):(ah==0x2e?(
ret=al>=0x80?2:1
):(ah==0x2f?(
ret=2
):(ah==0x30?(
ret=al!=0x3f?2:1
):(ah==0xf9||ah==0xfa?(
ret=2
):(ah==0xfe?(
ret=0x30<=al&&al<0x70?2:1
):(ah==0xff?(
ret=0x01<=al&&al<0x61||0xE0<=al&&al<=0xE7?2:1
):(ret=1))))))))))
))
))
[[ $tIndex ]] || return 0
if ((tIndex<_ble_util_c2w_emacs_wranges[0])); then
ret=1
return 0
fi
local l=0 u=${#_ble_util_c2w_emacs_wranges[@]} m
while ((l+1<u)); do
((_ble_util_c2w_emacs_wranges[m=(l+u)/2]<=tIndex?(l=m):(u=m)))
done
((ret=((l&1)==0)?2:1))
return 0
}
## @fn ble/util/c2w+west
## @var[out] ret
function ble/util/c2w+west {
ble/util/c2w/.determine-unambiguous "$1"
if ((ret<0)); then
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then
((ret=bleopt_emoji_width))
elif ((_ble_util_c2w_except[$1]==-2)); then
ret=2 # (絵文字の可能性があったため曖昧だった) 全角
else
ret=1
fi
fi
}
## @fn ble/util/c2w+east
## @var[out] ret
_ble_util_c2w_east_wranges=(
161 162 164 165 167 169 170 171 174 175 176 181 182 187 188 192 198 199 208 209
215 217 222 226 230 231 232 235 236 238 240 241 242 244 247 251 252 253 254 255
257 258 273 274 275 276 283 284 294 296 299 300 305 308 312 313 319 323 324 325
328 332 333 334 338 340 358 360 363 364 462 463 464 465 466 467 468 469 470 471
472 473 474 475 476 477 593 594 609 610 708 709 711 712 713 716 717 718 720 721
728 732 733 734 735 736 913 930 931 938 945 962 963 970 1025 1026 1040 1104 1105 1106
8208 8209 8211 8215 8216 8218 8220 8222 8224 8227 8228 8232 8240 8241 8242 8244 8245 8246 8251 8252
8254 8255 8308 8309 8319 8320 8321 8325 8364 8365 8451 8452 8453 8454 8457 8458 8467 8468 8470 8471
8481 8483 8486 8487 8491 8492 8531 8533 8539 8543 8544 8556 8560 8570 8592 8602 8632 8634 8658 8659
8660 8661 8679 8680 8704 8705 8706 8708 8711 8713 8715 8716 8719 8720 8721 8722 8725 8726 8730 8731
8733 8737 8739 8740 8741 8742 8743 8749 8750 8751 8756 8760 8764 8766 8776 8777 8780 8781 8786 8787
8800 8802 8804 8808 8810 8812 8814 8816 8834 8836 8838 8840 8853 8854 8857 8858 8869 8870 8895 8896
8978 8979 9312 9450 9451 9548 9552 9588 9600 9616 9618 9622 9632 9634 9635 9642 9650 9652 9654 9656
9660 9662 9664 9666 9670 9673 9675 9676 9678 9682 9698 9702 9711 9712 9733 9735 9737 9738 9742 9744
9748 9750 9756 9757 9758 9759 9792 9793 9794 9795 9824 9826 9827 9830 9831 9835 9836 9838 9839 9840
10045 10046 10102 10112 57344 63744 65533 65534 983040 1048574 1048576 1114110)
function ble/util/c2w+east {
ble/util/c2w/.determine-unambiguous "$1"
((ret>=0)) && return 0
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then
((ret=bleopt_emoji_width))
return 0
elif ((_ble_util_c2w_except[$1]==-2)); then
ret=2 # (絵文字の可能性があったため曖昧だった) 全角
return 0
fi
local code=$1
if ((code<_ble_util_c2w_east_wranges[0])); then
ret=1
return 0
fi
local l=0 u=${#_ble_util_c2w_east_wranges[@]} m
while ((l+1<u)); do
((_ble_util_c2w_east_wranges[m=(l+u)/2]<=code?(l=m):(u=m)))
done
((ret=((l&1)==0)?2:1))
return 0
}
_ble_util_c2w_auto_width=1
_ble_util_c2w_auto_update_x0=0
function ble/util/c2w+auto {
ble/util/c2w/.determine-unambiguous "$1"
((ret>=0)) && return 0
if ((_ble_util_c2w_auto_width==1)); then
ble/util/c2w+west "$1"
else
ble/util/c2w+east "$1"
((ret==2&&(ret=_ble_util_c2w_auto_width)))
fi
}
function ble/util/c2w+auto/update.buff {
local opts=$1
local -a DRAW_BUFF=()
local ret
[[ $_ble_attached ]] && ble/canvas/panel/save-position goto-top-dock
ble/canvas/put.draw "$_ble_term_sc"
if ble/util/is-unicode-output; then
local achar='▽'
if [[ :$opts: == *:first-line:* ]]; then
# 画面の右上で判定を行います。
local cols=${COLUMNS:-80}
local x0=$((cols-4)); ((x0<0)) && x0=0
_ble_util_c2w_auto_update_x0=$x0
ble/canvas/put-cup.draw 1 $((x0+1))
ble/canvas/put.draw "$achar"
ble/term/CPR/request.draw ble/util/c2w+auto/update.hook
ble/canvas/put-cup.draw 1 $((x0+1))
ble/canvas/put.draw "$_ble_term_el"
else
_ble_util_c2w_auto_update_x0=0
ble/canvas/put.draw "$_ble_term_cr$achar"
ble/term/CPR/request.draw ble/util/c2w+auto/update.hook
fi
fi
ble/canvas/put.draw "$_ble_term_rc"
[[ $_ble_attached ]] && ble/canvas/panel/load-position.draw "$ret"
ble/canvas/bflush.draw
}
function ble/util/c2w+auto/update.hook {
local l=$1 c=$2
local w=$((c-1-_ble_util_c2w_auto_update_x0))
((_ble_util_c2w_auto_width=w==1?1:2))
}
#------------------------------------------------------------------------------
# ble/canvas/attach
function ble/canvas/attach {
[[ $bleopt_char_width_mode == auto ]] &&
ble/util/c2w+auto/update.buff
}
#------------------------------------------------------------------------------
# ble/canvas
function ble/canvas/put.draw {
DRAW_BUFF[${#DRAW_BUFF[*]}]=$1
}
function ble/canvas/put-ind.draw {
local count=${1-1} ind=$_ble_term_ind
[[ :$2: == *:true-ind:* ]] && ind=$'\eD'
local ret; ble/string#repeat "$ind" "$count"
DRAW_BUFF[${#DRAW_BUFF[*]}]=$ret
}
function ble/canvas/put-ri.draw {
local count=${1-1}
local ret; ble/string#repeat "$_ble_term_ri" "$count"
DRAW_BUFF[${#DRAW_BUFF[*]}]=$ret
}
## @fn ble/canvas/put-il.draw [nline] [opts]
## @fn ble/canvas/put-dl.draw [nline] [opts]
## @param[in,opt] nline
## 消去・挿入する行数を指定します。
## 省略した場合は 1 と解釈されます。
## @param[in,opt] opts
## panel
## vfill
## no-lastline
## Cygwin console 最終行バグ判定用の情報です。
function ble/canvas/put-il.draw {
local value=${1-1}
((value>0)) || return 0
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_il//'%d'/$value}
DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux
}
function ble/canvas/put-dl.draw {
local value=${1-1}
((value>0)) || return 0
DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_dl//'%d'/$value}
}
# Cygwin console (pcon) では最終行で IL/DL すると画面全体がクリアされるバグの対策 (#D1482)
if ((_ble_bash>=40000)) && [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $TERM == xterm-256color ]]; then
function ble/canvas/.is-il-workaround-required {
local value=$1 opts=$2
# Cygwin console 以外の端末ではそもそも対策不要。
[[ ! $_ble_term_DA2R ]] || return 1
# 複数行挿入・削除する場合は現在位置は最終行ではない筈。
((value==1)) || return 1
# 対策不要と明示されている場合は対策不要。
[[ :$opts: == *:vfill:* || :$opts: == *:no-lastline:* ]] && return 1
# ble/canvas/panel 内部で移動中の時は opts=panel が指定される。
# panel 集合の最終行にいない場合は対策不要。
[[ :$opts: == *:panel:* ]] &&
! ble/canvas/panel/is-last-line &&
return 1
return 0
}
function ble/canvas/put-il.draw {
local value=${1-1} opts=$2
((value>0)) || return 0
if ble/canvas/.is-il-workaround-required "$value" "$2"; then
if [[ :$opts: == *:panel:* ]]; then
DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2
else
DRAW_BUFF[${#DRAW_BUFF[*]}]=$'\e[S\e[A\e[L\e[B\e[T'
fi
else
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_il//'%d'/$value}
DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux
fi
}
function ble/canvas/put-dl.draw {
local value=${1-1} opts=$2
((value>0)) || return 0
if ble/canvas/.is-il-workaround-required "$value" "$2"; then
if [[ :$opts: == *:panel:* ]]; then
DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2
else
DRAW_BUFF[${#DRAW_BUFF[*]}]=$'\e[S\e[A\e[M\e[B\e[T'
fi
else
DRAW_BUFF[${#DRAW_BUFF[*]}]=$_ble_term_el2 # Note #D1214: 最終行対策 cygwin, linux
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_dl//'%d'/$value}
fi
}
fi
function ble/canvas/put-cuu.draw {
local value=${1-1}
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cuu//'%d'/$value}
}
function ble/canvas/put-cud.draw {
local value=${1-1}
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cud//'%d'/$value}
}
function ble/canvas/put-cuf.draw {
local value=${1-1}
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cuf//'%d'/$value}
}
function ble/canvas/put-cub.draw {
local value=${1-1}
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_cub//'%d'/$value}
}
function ble/canvas/put-cup.draw {
local l=${1-1} c=${2-1}
local out=$_ble_term_cup
out=${out//'%l'/$l}
out=${out//'%c'/$c}
out=${out//'%y'/$((l-1))}
out=${out//'%x'/$((c-1))}
DRAW_BUFF[${#DRAW_BUFF[*]}]=$out
}
function ble/canvas/put-hpa.draw {
local c=${1-1}
local out=$_ble_term_hpa
out=${out//'%c'/$c}
out=${out//'%x'/$((c-1))}
DRAW_BUFF[${#DRAW_BUFF[*]}]=$out
}
function ble/canvas/put-vpa.draw {
local l=${1-1}
local out=$_ble_term_vpa
out=${out//'%l'/$l}
out=${out//'%y'/$((l-1))}
DRAW_BUFF[${#DRAW_BUFF[*]}]=$out
}
function ble/canvas/put-ech.draw {
local value=${1:-1} esc
if [[ $_ble_term_ech ]]; then
esc=${_ble_term_ech/'%d'/$value}
else
ble/string#reserve-prototype "$value"
esc=${_ble_string_prototype::value}${_ble_term_cub/'%d'/$value}
fi
DRAW_BUFF[${#DRAW_BUFF[*]}]=$esc
}
function ble/canvas/put-spaces.draw {
local value=${1:-1}
ble/string#reserve-prototype "$value"
DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_string_prototype::value}
}
function ble/canvas/put-move-x.draw {
local dx=$1
((dx)) || return 1
if ((dx>0)); then
ble/canvas/put-cuf.draw "$dx"
else
ble/canvas/put-cub.draw $((-dx))
fi
}
function ble/canvas/put-move-y.draw {
local dy=$1
((dy)) || return 1
if ((dy>0)); then
if [[ $MC_SID == $$ ]]; then
# Note #D1392: mc (midnight commander) の中だと layout が破壊されるので、
# 必ずしも CUD で想定した行だけ移動できると限らない。
ble/canvas/put-ind.draw "$dy" true-ind
else
ble/canvas/put-cud.draw "$dy"
fi
else
ble/canvas/put-cuu.draw $((-dy))
fi
}
function ble/canvas/put-move.draw {
ble/canvas/put-move-x.draw "$1"
ble/canvas/put-move-y.draw "$2"
}
function ble/canvas/flush.draw {
IFS= builtin eval 'ble/util/put "${DRAW_BUFF[*]}"'
DRAW_BUFF=()
}
## @fn ble/canvas/sflush.draw [-v var]
## @param[in] var
## 出力先の変数名を指定します。
## @var[out] !var
function ble/canvas/sflush.draw {
local _var=ret
[[ $1 == -v ]] && _var=$2
IFS= builtin eval "$_var=\"\${DRAW_BUFF[*]}\""
DRAW_BUFF=()
}
function ble/canvas/bflush.draw {
IFS= builtin eval 'ble/util/buffer "${DRAW_BUFF[*]}"'
DRAW_BUFF=()
}
## @fn ble/canvas/put-clear-lines.draw [old] [new] [opts]
## @param[in,opt] old new
## 消去前と消去後の行数を指定します。
## old を省略した場合は 1 が使われます。
## new を省略した場合は old が使われます。
## @param[in,opt] opts
## panel
## vfill
## no-lastline
function ble/canvas/put-clear-lines.draw {
local old=${1:-1}
local new=${2:-$old}
if ((old==1&&new==1)); then
ble/canvas/put.draw "$_ble_term_el2"
else
ble/canvas/put-dl.draw "$old" "$3"
ble/canvas/put-il.draw "$new" "$3"
fi
}
#------------------------------------------------------------------------------
# ble/canvas/trace.draw
# ble/canvas/trace
## @fn ble/canvas/trace.draw text [opts]
## @fn ble/canvas/trace text [opts]
## 制御シーケンスを含む文字列を出力すると共にカーソル位置の移動を計算します。
##
## @param[in] text
## 出力する (制御シーケンスを含む) 文字列を指定します。
##
## @param[in,opt] opts
## コロン区切りのオプションの列を指定します。
##
## truncate
## LINES COLUMNS で指定される範囲外に出た時、処理を中断します。
##
## confine
## LINES COLUMNS の範囲外に文字出力・移動を行いません。
## 制御シーケンスにより範囲内に戻る可能性もあります。
##
## ellipsis
## LINES COLUMNS の範囲外に文字を出力しようとした時に、
## 三点リーダを末尾に上書きします。
##
## relative
## x y を相対位置と考えて移動を行います。
## 改行などの制御は全て座標に基づいた移動に変換されます。
##
## clip=X1xY1,X2xY2
## clip=XxY+WxH
## @param[in] X1 Y1 X2 Y2
## @param[in] X Y W H
## 指定した矩形範囲内の描画内容だけを抽出します。
## 矩形の左上の点が出力の描画開始点であると想定します。
##
## justify
## justify=SEPSPEC
##
## measure-bbox
## @var[out] x1 x2 y1 y2
## 描画範囲を x1 x2 y1 y2 に返します。
##
## left-char
## @var[in,out] lc lg
## bleopt_internal_suppress_bash_output= の時、
## 出力開始時のカーソル左の文字コードを指定します。
## 出力終了時のカーソル左の文字コードが分かる場合にそれを返します。
##
## terminfo
## ANSI制御シーケンスではなく現在の端末のシーケンスとして
## 制御機能SGRを解釈します。
##
## g0 face0
## 背景色・既定属性として用いる属性値または描画設定を指定します。
## 両方指定された場合は g0 を優先させます。
##
## @var[in,out] DRAW_BUFF[]
## ble/canvas/trace.draw の出力先の配列です。
## @var[out] ret
## ble/canvas/trace の結果の格納先の変数です。
##
## @var[in,out] x y g
## 出力の開始位置を指定します。出力終了時の位置を返します。
##
## 以下のシーケンスを認識します
##
## - Control Characters (C0 の文字 及び DEL)
## BS HT LF VT CR はカーソル位置の変更を行います。
## それ以外の文字はカーソル位置の変更は行いません。
##
## - CSI Sequence (Control Sequence)
## | CUU CSI A | CHB CSI Z |
## | CUD CSI B | HPR CSI a |
## | CUF CSI C | VPR CSI e |
## | CUB CSI D | HPA CSI ` |
## | CNL CSI E | VPA CSI d |
## | CPL CSI F | HVP CSI f |
## | CHA CSI G | SGR CSI m |
## | CUP CSI H | SCOSC CSI s |
## | CHT CSI I | SCORC CSI u |
## 上記のシーケンスはカーソル位置の計算に含め、
## また、端末 (TERM) に応じた出力を実施します。
## 上記以外のシーケンスはカーソル位置を変更しません。
##
## - SOS, DCS, SOS, PM, APC, ESC k ~ ESC \
## - ISO-2022 に含まれる 3 byte 以上のシーケンス
## これらはそのまま通します。位置計算の考慮には入れません。
##
## - ESC Sequence
## DECSC DECRC IND RI NEL はカーソル位置の変更を行います。
## それ以外はカーソル位置の変更は行いません。
##
## 内部実装で用いている変数を整理する
##
## @var[local] xinit yinit ginit
## 初期カーソル状態を格納する。
##
## @var x1 x2 y1 y2
## これは measure-bbox または justify を指定した時に描画範囲を追跡するのに使っている。
##
## @var[local] cx cy cg
## clip 時に DRAW_BUFF 出力済みの内容のカーソル状態を追跡する変数。
## clip 時は x y g は仮想的に clip していない時のカーソル状態を追跡している。
## @var[local] cx1 cy1 cx2 cy2
## clip 範囲を保持する変数
##
##
function ble/canvas/trace/.put-sgr.draw {
local ret g=$1
if ((g==0)); then
ble/canvas/put.draw "$opt_sgr0"
else
ble/color/g#compose "$opt_g0" "$g"
ble/color/g2sgr "$g"
ble/canvas/put.draw "$ret"
fi
}
function ble/canvas/trace/.measure-point {
if [[ $flag_bbox ]]; then
((x<x1?(x1=x):(x2<x&&(x2=x))))
((y<y1?(y1=y):(y2<y&&(y2=y))))
fi
}
## @fn ble/canvas/trace/.goto x1 y1
## @var[in,out] x y
## Note: lc lg の面倒は呼び出し元で見る。
function ble/canvas/trace/.goto {
local dstx=$1 dsty=$2
if [[ ! $flag_clip ]]; then
if [[ $trace_flags == *[RJ]* ]]; then
ble/canvas/put-move.draw $((dstx-x)) $((dsty-y))
else
ble/canvas/put-cup.draw $((dsty+1)) $((dstx+1))
fi
fi
((x=dstx,y=dsty))
ble/canvas/trace/.measure-point
}
function ble/canvas/trace/.implicit-move {
local w=$1 type=$2
# gbox は開始点と終了点を記録する。bbox の開始点は既に記録されている
# 前提。終了点及び行折返しが発生した時の極値を此処で記録する。
((w>0)) || return 0
if [[ $flag_gbox ]]; then
if [[ ! $gx1 ]]; then
((gx1=gx2=x,gy1=gy2=y))
else
((x<gx1?(gx1=x):(gx2<x&&(gx2=x))))
((y<gy1?(gy1=y):(gy2<y&&(gy2=y))))
fi
fi
((x+=w))
if ((x<=cols)); then
# 行内に収まった時
[[ $flag_bbox ]] && ((x>x2)) && x2=$x
[[ $flag_gbox ]] && ((x>gx2)) && gx2=$x
if ((x==cols&&!xenl)); then
((y++,x=0))
if [[ $flag_bbox ]]; then
((x<x1)) && x1=0
((y>y2)) && y2=$y
fi
fi
else
# 端末による折り返し
if [[ $type == atomic ]]; then
# [Note: 文字が横幅より大きい場合は取り敢えず次の行が
# 一杯になると仮定しているが端末による]
((y++,x=w<xlimit?w:xlimit))
else
((x+=w,y+=x/cols,x%=cols,
xenl&&x==0&&(y--,x=cols)))
fi
if [[ $flag_bbox ]]; then
((x1>0&&(x1=0)))
((x2<cols&&(x2=cols)))
((y>y2)) && y2=$y
fi
if [[ $flag_gbox ]]; then
((gx1>0&&(gx1=0)))
((gx2<cols&&(gx2=cols)))
((y>gy2)) && gy2=$y
fi
fi
((x==0&&(lc=32,lg=0)))
return 0
}
function ble/canvas/trace/.put-atomic.draw {
local c=$1 w=$2
if [[ $flag_clip ]]; then
((cy1<=y&&y<cy2&&cx1<=x&&x<cx2&&x+w<=cx2)) || return 0
if [[ $cg != "$g" ]]; then
ble/canvas/trace/.put-sgr.draw "$g"
cg=$g
fi
ble/canvas/put-move.draw $((x-cx)) $((y-cy))
ble/canvas/put.draw "$c"
((cx+=x+w,cy=y))
else
ble/canvas/put.draw "$c"
fi
ble/canvas/trace/.implicit-move "$w" atomic
}
function ble/canvas/trace/.put-ascii.draw {
local value=$1 w=${#1}
[[ $value ]] || return
if [[ $flag_clip ]]; then
local xL=$x xR=$((x+w))
((xR<=cx1||cx2<=xL||y+1<=cy1||cy2<=y)) && return 0
if [[ $cg != "$g" ]]; then
ble/canvas/trace/.put-sgr.draw "$g"
cg=$g
fi
((xL<cx1)) && value=${value:cx1-xL} xL=$cx1
((xR>cx2)) && value=${value::${#value}-(xR-cx2)} xR=$cx2
ble/canvas/put-move.draw $((x-cx)) $((y-cy))
ble/canvas/put.draw "$value"
((cx=xR,cy=y))
else
ble/canvas/put.draw "$value"
fi
ble/canvas/trace/.implicit-move "$w"
}
function ble/canvas/trace/.process-overflow {
[[ :$opts: == *:truncate:* ]] && i=$iN # stop
if [[ :$opts: == *:ellipsis:* ]]; then
if ble/util/is-unicode-output; then
local ellipsis='…' ret
ble/util/s2c "$ellipsis"; ble/util/c2w "$ret"; local w=$ret
else
local ellipsis=... w=3
fi
local ox=$x oy=$y
ble/canvas/trace/.goto $((xlimit-w)) $((lines-1))
ble/canvas/trace/.put-atomic.draw "$ellipsis" "$w"
ble/canvas/trace/.goto "$ox" "$oy"
fi
}
#--------------------------------------
## (trace 内部変数) justify 関連
##
## @var[local] justify_sep
## @arr[local] justify_fields
## @arr[local] justify_buff
## @arr[local] justify_out
## @var[local] jx0 jy0
## 各フィールドの開始カーソル位置を保持する。
## @var[local] jx1 jy1 jx2 jy2
## measure-bbox も指定されていた時に、
## justify 後の描画範囲追跡に用いている。
## justify 処理中は x1 y1 x2 y2 は align 前のフィールドの描画範囲追跡に使っている。
## 関数の一番最後で jx1 jy1 jx2 jy2 で x1 y1 x2 y2 を上書きする。
##
function ble/canvas/trace/.justify/inc-quote {
[[ $trace_flags == *J* ]] || return 0
((trace_sclevel++))
flag_justify=
}
function ble/canvas/trace/.justify/dec-quote {
[[ $trace_flags == *J* ]] || return 0
((--trace_sclevel)) || flag_justify=1
}
## @fn ble/canvas/trace/.justify/begin-line
## @var[out] jx0 jy0 x1 y1 x2 y2
function ble/canvas/trace/.justify/begin-line {
((jx0=x1=x2=x,jy0=y1=y2=y))
gx1= gx2= gy1= gy2=
[[ $justify_align == *[cr]* ]] &&
ble/canvas/trace/.justify/next-field
}
## @fn ble/canvas/trace/.justify/next-field [sep]
## @param[in,opt] sep
## 省略時は最後のフィールドを意味する。
## @var[out] jx0 jy0 x1 y1 x2 y2
## @var[in,out] DRAW_BUFF justify_fields
function ble/canvas/trace/.justify/next-field {
local sep=$1 wmin=0
local esc; ble/canvas/sflush.draw -v esc
[[ $sep == ' ' ]] && wmin=1
ble/array#push justify_fields "${sep:-\$}:$wmin:$jx0,$jy0,$x,$y:$x1,$y1,$x2,$y2:$gx1,$gy1,$gx2,$gy2:$esc"
((x+=wmin,jx0=x1=x2=x,jy0=y1=y2=y))
}
## @fn ble/canvas/trace/.justify/unpack packed_data
## @var[out] sep wmin xI yI xF yF x1 y1 x2 y2 esc
function ble/canvas/trace/.justify/unpack {
local data=$1
sep=${data::1}; data=${data:2}
wmin=${data%%:*}; data=${data#*:}
ble/string#split buff , "${data%%:*}"; data=${data#*:}
xI=${buff[0]} yI=${buff[1]} xF=${buff[2]} yF=${buff[3]}
ble/string#split buff , "${data%%:*}"; data=${data#*:}
x1=${buff[0]} y1=${buff[1]} x2=${buff[2]} y2=${buff[3]}
ble/string#split buff , "${data%%:*}"; data=${data#*:}
gx1=${buff[0]} gy1=${buff[1]} gx2=${buff[2]} gy2=${buff[3]}
esc=$data
}
## @fn ble/canvas/trace/.justify/end-line
## これまでに justify_fields に記録した各フィールドの esc を align しつつ結合
## する。
## @var[in,out] justify_fields DRAW_BUFF justify_buff
function ble/canvas/trace/.justify/end-line {
# Note: 行内容がなかった場合でも行の高さだけは記録する
# (NEL で新しい行が形成される事に注意)。
if [[ $trace_flags == *B* ]]; then
((y<jy1&&(jy1=y)))
((y>jy2&&(jy2=y)))
fi
((${#justify_fields[@]}||${#DRAW_BUFF[@]})) || return 0
# 最後のフィールドを justify_fields に移動。
ble/canvas/trace/.justify/next-field
[[ $justify_align == *c* ]] &&
ble/canvas/trace/.justify/next-field
local i width=0 ispan=0 has_content=
for ((i=0;i<${#justify_fields[@]};i++)); do
local sep wmin xI yI xF yF x1 y1 x2 y2 gx1 gy1 gx2 gy2 esc
ble/canvas/trace/.justify/unpack "${justify_fields[i]}"
((width+=xF-xI))
[[ $esc ]] && has_content=1
# Note: 最後の要素の次には余白はない。
((i+1==${#justify_fields[@]})) && break
((width+=wmin))
((ispan++))
done
[[ $has_content ]] || return 0
local nspan=$ispan
local -a DRAW_BUFF=()
# fill に使える余白を計算する。
# Note: _ble_term_xenl 及び opt_relative の時には本当の端末の右端には接触しな
# いと想定して範囲の右端まで使用する。
local xlimit=$cols
[[ $_ble_term_xenl$opt_relative ]] || ((xlimit--))
local span=$((xlimit-width))
x= y=
local ispan=0 vx=0 spanx=0
for ((i=0;i<${#justify_fields[@]};i++)); do
local sep wmin xI yI xF yF x1 y1 x2 y2 gx1 gy1 gx2 gy2 esc
ble/canvas/trace/.justify/unpack "${justify_fields[i]}"
if [[ ! $x ]]; then
x=$xI y=$yI
if [[ $justify_align == right ]]; then
ble/canvas/put-move-x.draw $((cols-1-x))
((x=cols-1))
fi
fi
if [[ $esc ]]; then
local delta=0
((vx+x1-xI<0)) && ((delta=-(vx+x1-xI)))
((vx+x2-xI>xlimit)) && ((delta=xlimit-(vx+x2-xI)))
ble/canvas/put-move-x.draw $((vx+delta-x))
((x=vx+delta))
ble/canvas/put.draw "$esc"
if [[ $trace_flags == *B* ]]; then
((x+x1-xI<jx1&&(jx1=x+x1-xI)))
((y+y1-yI<jy1&&(jy1=y+y1-yI)))
((x+x2-xI>jx2&&(jx2=x+x2-xI)))
((y+y2-yI>jy2&&(jy2=y+y2-yI)))
fi
if [[ $flag_gbox && $gx1 ]]; then
((gx1+=x-xI,gx2+=x-xI))
((gy1+=y-yI,gy2+=y-yI))
if [[ ! $jgx1 ]]; then
((jgx1=gx1,jgy1=gy1,jgx2=gx2,jgy2=gy2))
else
((gx1<jgx1&&(jgx1=gx1)))
((gy1<jgy1&&(jgy1=gy1)))
((gx2>jgx2&&(jgx2=gx2)))
((gy2>jgy2&&(jgy2=gy2)))
fi
fi
((x+=xF-xI,y+=yF-yI,vx+=xF-xI))
fi
((i+1==${#justify_fields[@]})) && break
local new_spanx=$((span*++ispan/nspan))
local wfill=$((wmin+new_spanx-spanx))
((vx+=wfill,spanx=new_spanx))
# fillchar: 取り敢えず現在の実装では空白で fill
if [[ $sep == ' ' ]]; then
ble/string#reserve-prototype "$wfill"
ble/canvas/put.draw "${_ble_string_prototype::wfill}"
((x+=wfill))
fi
done
local ret
ble/canvas/sflush.draw
ble/array#push justify_buff "$ret"
justify_fields=()
}
#--------------------------------------
## (trace 内部変数) sc/rc 関連
##
## @arr[local] trace_decsc
## @arr[local] trace_scosc
## @arr[local] trace_brack
##
function ble/canvas/trace/.decsc {
[[ ${trace_decsc[5]} ]] || ble/canvas/trace/.justify/inc-quote
trace_decsc=("$x" "$y" "$g" "$lc" "$lg" active)
if [[ ! $flag_clip ]]; then