forked from akinomyoga/ble.sh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcanvas.sh
3387 lines (3138 loc) · 108 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以外で表示する端末が有ればそれらのコードを実装し
# 直す必要がある。その様な変な端末があるとは思えないが。
_ble_util_c2w=()
_ble_util_c2w_cache=()
function ble/util/c2w/clear-cache {
_ble_util_c2w_cache=()
}
## @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
case $value in
(auto)
_ble_unicode_c2w_ambiguous=1
ble && ble/util/c2w:auto/test.buff first-line ;;
(west) _ble_unicode_c2w_ambiguous=1 ;;
(east) _ble_unicode_c2w_ambiguous=2 ;;
esac
((_ble_prompt_version++))
ble/util/c2w/clear-cache
}
## @fn ble/util/c2w ccode
## @var[out] ret
function ble/util/c2w {
ret=${_ble_util_c2w_cache[$1]:-${_ble_util_c2w[$1]}}
if [[ ! $ret ]]; then
"ble/util/c2w:$bleopt_char_width_mode" "$1"
_ble_util_c2w_cache[$1]=$ret
fi
}
## @fn ble/util/c2w-edit ccode
## 編集画面での表示上の文字幅を返します。
## @var[out] ret
function ble/util/c2w-edit {
local cs=${_ble_unicode_GraphemeCluster_ControlRepresentation[$1]}
if [[ $cs ]]; then
ret=${#cs}
elif (($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/s2w text
## @fn ble/util/s2w-edit text [opts]
## @param[in] text
## @var[out] ret
function ble/util/s2w-edit {
local text=$1 iN=${#1} flags=$2 i
ret=0
for ((i=0;i<iN;i++)); do
local c w cs cb extend
ble/unicode/GraphemeCluster/match "$text" "$i" "$flags"
((ret+=w,i+=extend))
done
}
function ble/util/s2w {
ble/util/s2w-edit "$1" R
}
# ---- 文字種判定 ----
#%< canvas.c2w.sh
_ble_unicode_c2w_version=14
_ble_unicode_c2w_ambiguous=1
_ble_unicode_c2w_invalid=1
_ble_unicode_c2w_custom=()
bleopt/declare -n char_width_version auto
function bleopt/check:char_width_version {
if [[ $value == auto ]]; then
ble && ble/util/c2w:auto/test.buff first-line
((_ble_prompt_version++))
ble/util/c2w/clear-cache
return 0
elif local ret; ble/unicode/c2w/version2index "$value"; then
_ble_unicode_c2w_version=$ret
((_ble_prompt_version++))
ble/util/c2w/clear-cache
return 0
else
ble/util/print "bleopt: char_width_version: invalid value '$value'." >&2
return 1
fi
}
# wcwdith 例外 (Unicode 特性からは予想できない値を持っている物)
# この表は make/canvas.c2w.wcwidth.exe compare_eaw の出力より。
_ble_unicode_c2w_custom[173]=1 # U+00ad Cf A SHY(soft-hyphen)
let '_ble_unicode_c2w_custom['{1536..1541}']=1' # U+0600..0605 Cf 1 アラブの数字?
_ble_unicode_c2w_custom[1757]=1 # U+06dd Cf 1 ARABIC END OF AYAH
_ble_unicode_c2w_custom[1807]=1 # U+070f Cf 1 SYRIAC ABBREVIATION MARK
_ble_unicode_c2w_custom[2274]=1 # U+08e2 Cf 1 ARABIC DISPUTED END OF AYAH
_ble_unicode_c2w_custom[69821]=1 # U+110bd Cf 1 KAITHI NUMBER SIGN
_ble_unicode_c2w_custom[69837]=1 # U+110cd Cf 1 KAITHI NUMBER SIGN ABOVE
let '_ble_unicode_c2w_custom['{12872..12879}']=2' # U+3248..324f No A 囲み文字10-80 (8字)
let '_ble_unicode_c2w_custom['{19904..19967}']=2' # U+4dc0..4dff So 1 易経記号 (6字)
let '_ble_unicode_c2w_custom['{4448..4607}']=0' # U+1160..11ff Lo 1 HANGUL JAMO (160字)
let '_ble_unicode_c2w_custom['{55216..55238}']=0' # U+d7b0..d7c6 Lo 1 HANGUL JAMO EXTENDED-B (1) (23字)
let '_ble_unicode_c2w_custom['{55243..55291}']=0' # U+d7cb..d7fb Lo 1 HANGUL JAMO EXTENDED-B (2) (49字)
function ble/unicode/c2w {
local c=$1
ret=${_ble_unicode_c2w_custom[c]}
[[ $ret ]] && return 0
ret=${_ble_unicode_c2w[c]}
if [[ ! $ret ]]; then
ret=${_ble_unicode_c2w_index[c<0x20000?c>>8:((c>>12)-32+512)]}
if [[ $ret == *:* ]]; then
local l=${ret%:*} u=${ret#*:} m
while ((l+1<u)); do
((m=(l+u)/2))
if ((_ble_unicode_c2w_ranges[m]<=c)); then
l=$m
else
u=$m
fi
done
ret=${_ble_unicode_c2w[_ble_unicode_c2w_ranges[l]]}
fi
fi
ret=${_ble_unicode_c2w_UnicodeVersionMapping[ret*_ble_unicode_c2w_UnicodeVersionCount+_ble_unicode_c2w_version]}
((ret<0)) && ret=${_ble_unicode_c2w_invalid:-$((-ret))}
((ret==3)) &&
ret=${_ble_unicode_c2w_ambiguous:-1}
return 0
}
## @const _ble_unicode_EmojiStatus_*
##
## @var _ble_unicode_EmojiStatus_xmaybe
## @arr _ble_unicode_EmojiStatus
## @arr _ble_unicode_EmojiStatus_ranges
## @var _ble_unicode_EmojiStatus_version
## @bleopt emoji_version
##
## ファイル src/canvas.emoji.sh は以下のコマンドで生成する。
## $ ./make_command.sh update-emoji-database
##
#%< canvas.emoji.sh
bleopt/declare -v emoji_width 2
bleopt/declare -v emoji_opts ri
function bleopt/check:emoji_version {
local ret
if ! ble/unicode/EmojiStatus/version2index "$value"; then
local rex='^0*([0-9]+)\.0*([0-9]+)$'
if ! [[ $value =~ $rex ]]; then
ble/util/print "bleopt: Invalid format for emoji_version: '$value'." >&2
return 1
else
ble/util/print "bleopt: Unsupported emoji_version: '$value'." >&2
return 1
fi
fi
_ble_unicode_EmojiStatus_version=$ret
((_ble_prompt_version++))
ble/util/c2w/clear-cache
return 0
}
function bleopt/check:emoji_width { ble/util/c2w/clear-cache; }
# 2021-06-18 unqualified は絵文字に含めない。多くの場合は既定では通常文字で
# EPVS によって絵文字として表示する様である。component は肌の色(Extend) と髪
# (Pictographic) の2種類がある。取り敢えず幅2で計算する。
_ble_unicode_EmojiStatus_xIsEmoji='ret&&ret!=_ble_unicode_EmojiStatus_Unqualified'
function bleopt/check:emoji_opts {
_ble_unicode_EmojiStatus_xIsEmoji='ret'
[[ :$value: != *:unqualified:* ]] &&
_ble_unicode_EmojiStatus_xIsEmoji=$_ble_unicode_EmojiStatus_xIsEmoji'&&ret!=_ble_unicode_EmojiStatus_Unqualified'
local rex=':min=U\+([0-9a-fA-F]+):'
[[ :$value: =~ $rex ]] &&
_ble_unicode_EmojiStatus_xIsEmoji=$_ble_unicode_EmojiStatus_xIsEmoji'&&code>=0x'${BASH_REMATCH[1]}
((_ble_prompt_version++))
ble/util/c2w/clear-cache
return 0
}
function ble/unicode/EmojiStatus {
local code=$1 V=$_ble_unicode_EmojiStatus_version
ret=${_ble_unicode_EmojiStatus[code]}
if [[ ! $ret ]]; then
ret=$_ble_unicode_EmojiStatus_None
if ((_ble_unicode_EmojiStatus_xmaybe)); then
local l=0 u=${#_ble_unicode_EmojiStatus_ranges[@]} m
while ((l+1<u)); do
((_ble_unicode_EmojiStatus_ranges[m=(l+u)/2]<=code?(l=m):(u=m)))
done
ret=${_ble_unicode_EmojiStatus[_ble_unicode_EmojiStatus_ranges[l]]:-0}
fi
_ble_unicode_EmojiStatus[code]=$ret
fi
((ret=ret))
return 0
}
## @fn ble/util/c2w/is-emoji code
## @param[in] code
function ble/util/c2w/is-emoji {
local code=$1 ret
ble/unicode/EmojiStatus "$code"
((_ble_unicode_EmojiStatus_xIsEmoji))
}
# ---- char_width_mode ----
function ble/util/c2w:west {
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then
((ret=bleopt_emoji_width))
else
ble/unicode/c2w "$1"
fi
}
function ble/util/c2w:east {
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then
((ret=bleopt_emoji_width))
else
ble/unicode/c2w "$1"
fi
}
## @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
# bash-4.0 bug workaround
# 中で使用している変数に日本語などの文字列が入っているとエラーになる。
# その値を参照していなくても、その分岐に入らなくても関係ない。
# なので ret に予め適当な値を設定しておく事にする。
ret=1
((code<0xA0)) && return 0
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$code"; then
((ret=bleopt_emoji_width))
return 0
fi
# Note: ble/unicode/c2w を使うとずれる。考えてみれば emacs は各端末
# で同じテーブルを使って実装しているので ble/unicode/c2w 等外部の物
# を参照せずに実装するべきなのであった。
#ble/unicode/c2w "$1"
#((ret==3)) || return 0
# 実は EastAsianWidth=A だけ考えれば良いので下の条件式は単純化できる筈
local al=0 ah=0 tIndex=
((
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
}
#%< canvas.c2w.musl.sh
function ble/util/c2w:musl {
local code=$1
ret=1
((code&&code<0x300)) && return 0
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$code"; then
((ret=bleopt_emoji_width))
return 0
fi
local l=0 u=${#_ble_util_c2w_musl_ranges[@]} m
while ((l+1<u)); do
((_ble_util_c2w_musl_ranges[m=(l+u)/2]<=code?(l=m):(u=m)))
done
ret=${_ble_util_c2w_musl[_ble_util_c2w_musl_ranges[l]]}
}
_ble_util_c2w_auto_update_x0=0
_ble_util_c2w_auto_update_result=()
_ble_util_c2w_auto_update_processing=0
function ble/util/c2w:auto {
if [[ $bleopt_emoji_width ]] && ble/util/c2w/is-emoji "$1"; then
((ret=bleopt_emoji_width))
else
ble/unicode/c2w "$1"
fi
}
function ble/util/c2w:auto/check {
[[ $bleopt_char_width_mode == auto || $bleopt_char_width_version == auto ]] &&
ble/util/c2w:auto/test.buff
return 0
}
function ble/util/c2w:auto/test.buff {
local opts=$1
local -a DRAW_BUFF=()
local ret saved_pos=
# 現在既に処理中の場合 DSR は省略。char_width_@=auto 等で一括して要
# 求した時などに一回だけ実行する為。
((_ble_util_c2w_auto_update_processing)) && return 0
[[ $_ble_attached ]] && { ble/canvas/panel/save-position goto-top-dock; saved_pos=$ret; }
ble/canvas/put.draw "$_ble_term_sc"
if ble/util/is-unicode-output; then
local -a codes=(
# index=0,1 [EastAsianWidth=A 判定]
0x25bd 0x25b6
# index=2..15 [Unicode version 判定] #D1645 #D1668
# 判定用の文字コードは "source
# make/canvas.c2w.list-ucsver-detection-codes.sh" を用いて生
# 成されたリストから選択した。新しい Unicode version が出たら
# 再びこれを実行して判定コードを書く事になる。
0x9FBC 0x9FC4 0x31B8 0xD7B0 0x3099
0x9FCD 0x1F93B 0x312E 0x312F 0x16FE2
0x32FF 0x31BB 0x9FFD 0x1B132)
_ble_util_c2w_auto_update_processing=${#codes[@]}
_ble_util_c2w_auto_update_result=()
if [[ :$opts: == *:first-line:* ]]; then
# 画面の右上で判定を行います。
local cols=${COLUMNS:-80}
local x0=$((cols-4)); ((x0<0)) && x0=0
_ble_util_c2w_auto_update_x0=$x0
local code index=0
for code in "${codes[@]}"; do
ble/canvas/put-cup.draw 1 "$((x0+1))"
ble/canvas/put.draw "$_ble_term_el"
ble/util/c2s "$((code))"
ble/canvas/put.draw "$ret"
ble/term/CPR/request.draw "ble/util/c2w/test.hook $((index++))"
done
ble/canvas/put-cup.draw 1 "$((x0+1))"
ble/canvas/put.draw "$_ble_term_el"
else
_ble_util_c2w_auto_update_x0=2
local code index=0
for code in "${codes[@]}"; do
ble/util/c2s "$((code))"
ble/canvas/put.draw "$_ble_term_cr$_ble_term_el[$ret]"
ble/term/CPR/request.draw "ble/util/c2w/test.hook $((index++))"
done
ble/canvas/put.draw "$_ble_term_cr$_ble_term_el"
fi
fi
ble/canvas/put.draw "$_ble_term_rc"
[[ $_ble_attached ]] && ble/canvas/panel/load-position.draw "$saved_pos"
ble/canvas/bflush.draw
}
function ble/util/c2w/test.hook {
local index=$1 l=$2 c=$3
local w=$((c-1-_ble_util_c2w_auto_update_x0))
_ble_util_c2w_auto_update_result[index]=$w
((index==_ble_util_c2w_auto_update_processing-1)) || return 0
_ble_util_c2w_auto_update_processing=0
local ws
if [[ $bleopt_char_width_mode == auto ]]; then
IFS=: builtin eval 'ws="${_ble_util_c2w_auto_update_result[*]::2}:${_ble_util_c2w_auto_update_result[*]:5:2}"'
case $ws in
(2:2:*:*) bleopt char_width_mode=east ;;
(2:1:*:*) bleopt char_width_mode=emacs ;;
(1:1:2:0) bleopt char_width_mode=musl ;;
(*) bleopt char_width_mode=west ;;
esac
fi
if [[ $bleopt_char_width_version == auto ]]; then
ws=("${_ble_util_c2w_auto_update_result[@]:2}")
if ((ws[13]==2)); then
bleopt char_width_version=15.0
elif ((ws[11]==2)); then
if ((ws[12]==2)); then
bleopt char_width_version=14.0
else
bleopt char_width_version=13.0
fi
elif ((ws[10]==2)); then
bleopt char_width_version=12.1
elif ((ws[9]==2)); then
bleopt char_width_version=12.0
elif ((ws[8]==2)); then
bleopt char_width_version=11.0
elif ((ws[7]==2)); then
bleopt char_width_version=10.0
elif ((ws[6]==2)); then
bleopt char_width_version=9.0
elif ((ws[4]==0)); then
if ((ws[5]==2)); then
bleopt char_width_version=8.0
else
bleopt char_width_version=7.0
fi
elif ((ws[3]==1&&ws[1]==2)); then
bleopt char_width_version=6.3 # or 6.2
elif ((ws[2]==2)); then
bleopt char_width_version=6.1 # or 6.0
elif ((ws[1]==2)); then
bleopt char_width_version=5.2
elif ((ws[0]==2)); then
bleopt char_width_version=5.0
else
bleopt char_width_version=4.1
fi
fi
return 0
}
bleopt/declare -v grapheme_cluster extended
function bleopt/check:grapheme_cluster {
case $value in
(extended|legacy|'') return 0 ;;
(*)
ble/util/print "bleopt: invalid value for grapheme_cluster: '$value'." >&2
return 1 ;;
esac
}
#%< canvas.GraphemeClusterBreak.sh
function ble/unicode/GraphemeCluster/c2break {
local code=$1
ret=${_ble_unicode_GraphemeClusterBreak[code]}
[[ $ret ]] && return 0
((ret>_ble_unicode_GraphemeClusterBreak_MaxCode)) && { ret=0; return 0; }
local l=0 u=${#_ble_unicode_GraphemeClusterBreak_ranges[@]} m
while ((l+1<u)); do
((_ble_unicode_GraphemeClusterBreak_ranges[m=(l+u)/2]<=code?(l=m):(u=m)))
done
ret=${_ble_unicode_GraphemeClusterBreak[_ble_unicode_GraphemeClusterBreak_ranges[l]]:-0}
_ble_unicode_GraphemeClusterBreak[code]=$ret
return 0
}
_ble_unicode_GraphemeCluster_bomlen=1
_ble_unicode_GraphemeCluster_ucs4len=1
function ble/unicode/GraphemeCluster/s2break/.initialize {
local LC_ALL=C.UTF-8
builtin eval "local v1=\$'\\uFE0F' v2=\$'\\U1F6D1'"
_ble_unicode_GraphemeCluster_bomlen=${#v1}
_ble_unicode_GraphemeCluster_ucs4len=${#v2}
ble/util/unlocal LC_ALL
builtin unset -f "$FUNCNAME"
} 2>/dev/null # suppress locale error #D1440
ble/unicode/GraphemeCluster/s2break/.initialize
## @fn ble/unicode/GraphemeCluster/s2break/.combine-surrogate code1 code2 str
## @var[out] c
function ble/unicode/GraphemeCluster/s2break/.combine-surrogate {
local code1=$1 code2=$2 s=$3
if ((0xDC00<=code2&&code2<=0xDFFF)); then
((c=0x10000+(code1-0xD800)*1024+(code2&0x3FF)))
else
local ret
ble/util/s2bytes "$s"
ble/encoding:UTF-8/b2c "${ret[@]}"
c=$ret
fi
}
## @fn ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF code
## (#D1881) Bash 4.3, 4.4 [sizeof(wchar_t) == 2] で $'\uE000'.. $'\uFFFF' が
## 壊れたサロゲートになるバグに対する対策。この時、前半サロゲートは不正な値
## U+D7F8..D7FF になるが、これはハングル字母などと被る。U+D7F8..D7FF の時は、
## 次の文字が後半サロゲートの時に限り前半サロゲートとして取り扱う。
##
## @param[in] code
## 壊れた前半サロゲータの可能性がある文字コード
## @var[in,out] ret
## 調整前後の GraphemeClusterBreak 値
## @exit
## 調整が行われた時に成功です (0)。それ以外の時は失敗 (1) です。
##
if ((_ble_unicode_GraphemeCluster_bomlen==2&&40300<=_ble_bash&&_ble_bash<50000)); then
function ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF {
local code=$1
((0xD7F8<=code&&code<0xD800)) && ble/util/is-unicode-output &&
ret=$_ble_unicode_GraphemeClusterBreak_HighSurrogate
}
else
function ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF { ((0)); }
fi
## @fn ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG code
## (#D1881) Cygwin では UCS-2 に入らないコードポイントの後半サロゲートをs2cで
## 取ろうとしても 0 になってしまう (Bash 5.0 以降では 4-byte UTF-8 の最後のバ
## イト値) ので、後半について code == 0 の場合も前半サロゲートをチェックする。
##
## @param[in] code
## UCS-4 の後半サロゲートの可能性がある文字コード
## @var[in,out] ret
## 調整前後の GraphemeClusterBreak 値
## @exit
## 調整が行われた時に成功です (0)。それ以外の時は失敗 (1) です。
##
if ((_ble_unicode_GraphemeCluster_ucs4len==2)); then
if ((_ble_bash<50000)); then
function ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG {
local code=$1
((code==0)) && ble/util/is-unicode-output &&
ret=$_ble_unicode_GraphemeClusterBreak_LowSurrogate
}
else
function ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG {
local code=$1
((0x80<=code&&code<0xC0)) && ble/util/is-unicode-output &&
ret=$_ble_unicode_GraphemeClusterBreak_LowSurrogate
}
fi
else
function ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG { ((0)); }
fi
## @fn ble/unicode/GraphemeCluster/s2break-left str index [opts]
## @fn ble/unicode/GraphemeCluster/s2break-right str index [opts]
## 指定した文字列の指定した境界の左右の code point の GraphemeCulsterBreak 値
## を求めます。単に bash の文字単位ではなく、サロゲートペアも考慮に入れたコー
## ドポイント単位で処理を行います。
##
## @param str
## @param index
## @param[opt] opts
## @var[out] ret
## GraphemeCulsterBreak 値を返します。
## @var[out,opt] shift
## opts に shift が指定された時に対象の code point の文字数を返します。
## surrogate pair の時に 2 になります。それ以外の時は 1 です。
## @var[out,opt] code
## opts に code が指定された時に対象の code point を返します。
##
## * Note2 (#D1881): ${s:i-1:2} 等として 2 文字切り出すのは、Cygwin では
## ${s:i-1:1} として最初の文字を切り出そうとすると UCS-2 に入らない code
## point の文字が破壊されてしまって surrogate 前半すら取り出せなくなる為。少
## なくとも wchar_t*2 の分だけ渡せば printf %d '$1 で surrogate 前半の code
## point を取り出す事ができる。
function ble/unicode/GraphemeCluster/s2break-left {
ret=0
local s=$1 N=${#1} i=$2 opts=$3 sh=1
((i>0)) && ble/util/s2c "${s:i-1:2}"; local c=$ret code2=$ret # Note2 (上述)
ble/unicode/GraphemeCluster/c2break "$code2"; local break=$ret
# process surrogate pairs
((i-1<N)) && ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG "$code2"
if ((i-2>=0&&ret==_ble_unicode_GraphemeClusterBreak_LowSurrogate)); then
ble/util/s2c "${s:i-2:2}"; local code1=$ret # Note2 (上述)
ble/unicode/GraphemeCluster/c2break "$code1"
ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code1"
if ((ret==_ble_unicode_GraphemeClusterBreak_HighSurrogate)); then
ble/unicode/GraphemeCluster/s2break/.combine-surrogate "$code1" "$code2" "${s:i-2:2}"
ble/unicode/GraphemeCluster/c2break "$c"
break=$ret
sh=2
fi
elif ((i<N)) && ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code2"; then
# 壊れた前半サロゲートの可能性があるので次の文字を確認して break を確定する。
# (Note: 壊れたサロゲートペアの場合には UTF-8 4B 表現になる事はないので
# Cygwin で code_next==0 になる可能性は考えなくて良い。)
ble/util/s2c "${s:i:1}"; local code_next=$ret
ble/unicode/GraphemeCluster/c2break "$code_next"
((ret==_ble_unicode_GraphemeClusterBreak_LowSurrogate)) &&
break=$_ble_unicode_GraphemeClusterBreak_HighSurrogate
fi
[[ :$opts: == *:shift:* ]] && shift=$sh
[[ :$opts: == *:code:* ]] && code=$c
ret=$break
}
function ble/unicode/GraphemeCluster/s2break-right {
ret=0
local s=$1 N=${#1} i=$2 opts=$3 sh=1
ble/util/s2c "${s:i:2}"; local c=$ret code1=$ret # Note2 (上述)
ble/unicode/GraphemeCluster/c2break "$code1"; local break=$ret
# process surrogate pairs
ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code1"
if ((i+1<N&&ret==_ble_unicode_GraphemeClusterBreak_HighSurrogate)); then
ble/util/s2c "${s:i+1:1}"; local code2=$ret
ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG "$code2" ||
ble/unicode/GraphemeCluster/c2break "$code2"
if ((ret==_ble_unicode_GraphemeClusterBreak_LowSurrogate)); then
ble/unicode/GraphemeCluster/s2break/.combine-surrogate "$code1" "$code2" "${s:i:2}"
ble/unicode/GraphemeCluster/c2break "$c"
break=$ret
sh=2
fi
elif ((0<i&&i<N)) && ble/unicode/GraphemeCluster/s2break/.wa-cygwin-LSG "$code1"; then
# Note #D1881: Cygwin では UCS-2 に入らない code point の surrogate 後半を
# s2c で取ろうとしても 0 になってしまうので code1==0 の時は念入りに調べる。
# 前に HighSurrogate がない時は通常文字と同様に取り扱って問題ない。
ble/util/s2c "${s:i-1:1}"; local code_prev=$ret
ble/unicode/GraphemeCluster/c2break "$code_prev"
ble/unicode/GraphemeCluster/s2break/.wa-bash43bug-uFFFF "$code_prev"
if ((ret==_ble_unicode_GraphemeClusterBreak_HighSurrogate)); then
break=$_ble_unicode_GraphemeClusterBreak_LowSurrogate
if [[ :$opts: == *:code:* ]]; then
ble/util/s2bytes "${s:i-1:2}"
ble/encoding:UTF-8/b2c "${ret[@]}"
((c=0xDC00|ret&0x3FF))
else
c=0
fi
fi
fi
[[ :$opts: == *:shift:* ]] && shift=$sh
[[ :$opts: == *:code:* ]] && code=$c
ret=$break
}
## @fn ble/unicode/GraphemeCluster/find-previous-boundary/.ZWJ
## @var[in] text i
## @var[out] ret
function ble/unicode/GraphemeCluster/find-previous-boundary/.ZWJ {
if [[ :$bleopt_emoji_opts: != *:zwj:* ]]; then
((ret=i))
return 0
fi
local j=$((i-1)) shift=1
for ((j=i-1;j>0;j-=shift)); do
ble/unicode/GraphemeCluster/s2break-left "$text" "$j" shift
((ret==_ble_unicode_GraphemeClusterBreak_Extend)) || break
done
if ((j==0||ret!=_ble_unicode_GraphemeClusterBreak_Pictographic)); then
# sot | Extend* ZWJ | Pictographic
# [^Pictographic] | Extend* ZWJ | Pictographic
# ^--- j ^--- i
((ret=i))
return 0
else
# Pictographic | Extend* ZWJ | Pictographic
# ^--- j ^--- i
((i=j-shift,b1=ret))
return 1
fi
}
## @fn ble/unicode/GraphemeCluster/find-previous-boundary/.RI
## @var[in] text i shift
## @var[out] ret
function ble/unicode/GraphemeCluster/find-previous-boundary/.RI {
if [[ :$bleopt_emoji_opts: != *:ri:* ]]; then
((ret=i))
return 0
fi
local j1=$((i-shift))
local j shift=1 countRI=1
for ((j=j1;j>0;j-=shift,countRI++)); do
ble/unicode/GraphemeCluster/s2break-left "$text" "$j" shift
((ret==_ble_unicode_GraphemeClusterBreak_Regional_Indicator)) || break
done
if ((j==j1)); then
((i=j,b1=_ble_unicode_GraphemeClusterBreak_Regional_Indicator))
return 1
else
((ret=countRI%2==1?j1:i))
return 0
fi
}
function ble/unicode/GraphemeCluster/find-previous-boundary {
local text=$1 i=$2 shift
if [[ $bleopt_grapheme_cluster ]] && ((i&&--i)); then
ble/unicode/GraphemeCluster/s2break-right "$text" "$i" shift; local b1=$ret
while ((i>0)); do
local b2=$b1
ble/unicode/GraphemeCluster/s2break-left "$text" "$i" shift; local b1=$ret
case ${_ble_unicode_GraphemeClusterBreak_rule[b1*_ble_unicode_GraphemeClusterBreak_Count+b2]} in
(0) break ;;
(1) ((i-=shift)) ;;
(2) [[ $bleopt_grapheme_cluster != extended ]] && break; ((i-=shift)) ;;
(3) ble/unicode/GraphemeCluster/find-previous-boundary/.ZWJ && return 0 ;;
(4) ble/unicode/GraphemeCluster/find-previous-boundary/.RI && return 0 ;;
(5)
# surrogate pair の間にいた時は GraphemeClusterBreak を取得し直す
((i-=shift))
ble/unicode/GraphemeCluster/s2break-right "$text" "$i"; b1=$ret ;;
esac
done
fi
ret=$i
return 0
}
_ble_unicode_GraphemeClusterBreak_isCore=()
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Other]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Control]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Regional_Indicator]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_L]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_V]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_T]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_LV]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_LVT]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_Pictographic]=1
_ble_unicode_GraphemeClusterBreak_isCore[_ble_unicode_GraphemeClusterBreak_HighSurrogate]=1
## @fn ble/unicode/GraphemeCluster/extend-ascii text i
## @var[out] extend
function ble/unicode/GraphemeCluster/extend-ascii {
extend=0
[[ $_ble_util_locale_encoding != UTF-8 || ! $bleopt_grapheme_cluster ]] && return 1
local text=$1 iN=${#1} i=$2 ret shift=1
for ((;i<iN;i+=shift,extend+=shift)); do
ble/unicode/GraphemeCluster/s2break-right "$text" "$i" shift
case $ret in
("$_ble_unicode_GraphemeClusterBreak_Extend"|"$_ble_unicode_GraphemeClusterBreak_ZWJ") ;;
("$_ble_unicode_GraphemeClusterBreak_SpacingMark")
[[ $bleopt_grapheme_cluster == extended ]] || break ;;
(*) break ;;
esac
done
((extend))
}
_ble_unicode_GraphemeCluster_ControlRepresentation=()
function ble/unicode/GraphemeCluster/.get-ascii-rep {
local c=$1
cs=${_ble_unicode_GraphemeCluster_ControlRepresentation[c]}
if [[ ! $cs ]]; then
if ((c<32)); then
ble/util/c2s "$((c+64))"
cs=^$ret
elif ((c==127)); then
cs=^?
elif ((128<=c&&c<160)); then
ble/util/c2s "$((c-64))"
cs=M-^$ret
else
ble/util/sprintf cs 'U+%X' "$c"
fi
_ble_unicode_GraphemeCluster_ControlRepresentation[c]=$cs
fi
}
## @fn ble/unicode/GraphemeCluster/match text i flags
## @param[in] text i
## @param[in] flags
## R が含まれている時制御文字を (ASCII 表現ではなく) そのまま cs に格納しま
## す。幅は 0 で換算されます。
## @var[out] c w cs cb extend
function ble/unicode/GraphemeCluster/match {
local text=$1 iN=${#1} i=$2 j=$2 flags=$3 ret
if ((i>=iN)); then
c=0 w=0 cs= cb= extend=0
return 1
elif ! ble/util/is-unicode-output || [[ ! $bleopt_grapheme_cluster ]]; then
cs=${text:i:1}
ble/util/s2c "$cs"; c=$ret
if [[ $flags != *R* ]] && {
ble/unicode/GraphemeCluster/c2break "$c"
((ret==_ble_unicode_GraphemeClusterBreak_Control)); }; then
ble/unicode/GraphemeCluster/.get-ascii-rep "$c"
w=${#cs}
else
ble/util/c2w "$c"; w=$ret
fi
extend=0
return 0
fi
local b0 b1 b2 c0 c2 shift code
ble/unicode/GraphemeCluster/s2break-right "$text" "$i" code:shift; c0=$code b0=$ret
local coreb= corec= npre=0 vs= ri=
c2=$c0 b2=$b0
while ((j<iN)); do
if ((_ble_unicode_GraphemeClusterBreak_isCore[b2])); then
[[ $coreb ]] || coreb=$b2 corec=$c2
elif ((b2==_ble_unicode_GraphemeClusterBreak_Prepend)); then
((npre++))
elif ((c2==0xFE0E)); then # Variation selector TPVS
vs=tpvs
elif ((c2==0xFE0F)); then # Variation selector EPVS
vs=epvs
fi
((j+=shift))
b1=$b2
ble/unicode/GraphemeCluster/s2break-right "$text" "$j" code:shift; c2=$code b2=$ret
case ${_ble_unicode_GraphemeClusterBreak_rule[b1*_ble_unicode_GraphemeClusterBreak_Count+b2]} in
(0) break ;;
(1) continue ;;
(2) [[ $bleopt_grapheme_cluster != extended ]] && break ;;
(3) [[ :$bleopt_emoji_opts: == *:zwj:* ]] &&
((coreb==_ble_unicode_GraphemeClusterBreak_Pictographic)) || break ;;
(4) [[ :$bleopt_emoji_opts: == *:ri:* && ! $ri ]] || break; ri=1 ;;
(5)
# surrogate pair の間にいた時は GraphemeClusterBreak を取得し直す
ble/unicode/GraphemeCluster/s2break-left "$text" "$((j+shift))" code; c2=$code b2=$ret ;;
esac
done
c=$corec cb=$coreb cs=${text:i:j-i}
((extend=j-i-1))
if [[ ! $corec ]]; then
if [[ $flags != *R* ]]; then
((c=c0,cb=0,corec=0x25CC)) # 基底が存在しない時は点線円
ble/util/c2s "$corec"
cs=${text:i:npre}$ret${text:i+npre:j-i-npre}
else
local code
ble/unicode/GraphemeCluster/s2break-right "$cs" 0 code
c=$code corec=$code cb=$ret
fi
fi
if ((cb==_ble_unicode_GraphemeClusterBreak_Control)); then
if [[ $flags != *R* ]]; then
ble/unicode/GraphemeCluster/.get-ascii-rep "$c"
w=${#cs}
else
# ToDo: 全ての制御文字が幅0とは限らない。というより色々処理が必要。
w=0
fi
else
# 幅の計算 (Variation Selector を考慮に入れる)
if [[ $vs == tpvs && :$bleopt_emoji_opts: == *:tpvs:* ]]; then
bleopt_emoji_width= ble/util/c2w "$corec"; w=$ret
elif [[ $vs == epvs && :$bleopt_emoji_opts: == *:epvs:* ]]; then
w=${bleopt_emoji_width:-2}
else
ble/util/c2w "$corec"; w=$ret
fi
fi
return 0
}
#------------------------------------------------------------------------------
# ble/canvas/attach
function ble/canvas/attach {
ble/util/c2w:auto/check
}
#------------------------------------------------------------------------------
# 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 が指定される。