forked from akinomyoga/ble.sh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathedit.sh
10157 lines (9214 loc) · 340 KB
/
edit.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
# **** sections ****
#
# @line.ps1
# @line.text
# @line.info
# @edit.content
# @edit.ps1
# @textarea
# @textarea.buffer
# @textarea.render
# @widget.clear
# @widget.mark
# @edit.bell
# @edit.insert
# @edit.delete
# @edit.cursor
# @edit.word
# @edit.exec
# @edit.accept
# @history
# @history.widget
# @history.isearch
# @comp
# @bind
# @bind.bind
#
# 現在の ble/canvas/panel 構成
# 0 command-line
# 1 追加入力欄
# 2 infobar
## @bleopt edit_vbell
## 編集時の visible bell の有効・無効を設定します。
## bleopt_edit_vbell=1
## 有効です。
## bleopt_edit_vbell=
## 無効です。
bleopt/declare -v edit_vbell ''
## @bleopt edit_abell
## 編集時の audible bell (BEL 文字出力) の有効・無効を設定します。
## bleopt_edit_abell=1
## 有効です。
## bleopt_edit_abell=
## 無効です。
bleopt/declare -v edit_abell 1
## @bleopt history_lazyload
## bleopt_history_lazyload=1
## ble-attach 後、初めて必要になった時に履歴の読込を行います。
## bleopt_history_lazyload=
## ble-attach 時に履歴の読込を行います。
##
## bash-3.1 未満では history -s が思い通りに動作しないので、
## このオプションの値に関係なく ble-attach の時に履歴の読み込みを行います。
bleopt/declare -v history_lazyload 1
## @bleopt delete_selection_mode
## 文字挿入時に選択範囲をどうするかについて設定します。
## bleopt_delete_selection_mode=1 (既定)
## 選択範囲の内容を新しい文字で置き換えます。
## bleopt_delete_selection_mode=
## 選択範囲を解除して現在位置に新しい文字を挿入します。
bleopt/declare -v delete_selection_mode 1
## @bleopt indent_offset
## シェルのインデント幅を指定します。既定では 4 です。
bleopt/declare -n indent_offset 4
## @bleopt indent_tabs
## インデントにタブを使用するかどうかを指定します。
## 0 を指定するとインデントに空白だけを用います。
## それ以外の場合はインデントにタブを使用します。
bleopt/declare -n indent_tabs 1
## @bleopt undo_point
## undo/redo 実行直後のカーソル位置を設定します。
##
## undo_point=beg
## undo/redo によって変化のあった範囲の先頭に移動します。
## undo_point=end
## undo/redo によって変化のあった範囲の末端に移動します。
## その他の時
## undo/redo 後の状態が記録された時のカーソル位置を復元します。
##
bleopt/declare -v undo_point end
## @bleopt edit_forced_textmap
## 1 が設定されているとき、矩形選択に先立って配置計算を強制します。
## 0 が設定されているとき、配置情報があるときにそれを使い、
## 配置情報がないときは論理行・論理列による矩形選択にフォールバックします。
##
bleopt/declare -n edit_forced_textmap 1
function ble/edit/use-textmap {
ble/textmap#is-up-to-date && return 0
((bleopt_edit_forced_textmap)) || return 1
ble/widget/.update-textmap
return 0
}
## @bleopt edit_line_type
## 行頭・行末への移動などの操作を行う時の行の解釈を指定します。
## "logical" が設定されている時、論理行で解釈します。
## つまり編集文字列内の改行文字で区切られた行頭・行末を使用して操作を行います。
## "graphical" が設定されている時、表示行で解釈します。
## つまり端末内での現在行の行頭・行末を使用して操作を行います。
bleopt/declare -n edit_line_type logical
function bleopt/check:edit_line_type {
if [[ $value != logical && $value != graphical ]]; then
ble/util/print "bleopt edit_line_type: Unexpected value '$value'. 'logical' or 'graphical' is expected." >&2
return 1
fi
}
function ble/edit/performs-on-graphical-line {
[[ $edit_line_type == graphical ]] || return 1
ble/textmap#is-up-to-date && return 0
((bleopt_edit_forced_textmap)) || return 1
ble/widget/.update-textmap
return 0
}
bleopt/declare -n info_display top
function bleopt/check:info_display {
case $value in
(top)
[[ $_ble_canvas_panel_vfill == 3 ]] && return 0
_ble_canvas_panel_vfill=3
[[ $_ble_attached ]] && ble/canvas/panel/clear
return 0 ;;
(bottom)
[[ $_ble_canvas_panel_vfill == 2 ]] && return 0
_ble_canvas_panel_vfill=2
[[ $_ble_attached ]] && ble/canvas/panel/clear
return 0 ;;
(*)
ble/util/print "bleopt: Invalid value for 'info_display': $value"
return 1 ;;
esac
}
## プロンプトオプション
bleopt/declare -v prompt_ps1_final ''
bleopt/declare -v prompt_ps1_transient ''
bleopt/declare -v prompt_rps1 ''
bleopt/declare -v prompt_rps1_final ''
bleopt/declare -v prompt_rps1_transient ''
bleopt/declare -v prompt_xterm_title ''
bleopt/declare -v prompt_screen_title ''
bleopt/declare -v prompt_term_status ''
# obsoleted options
bleopt/declare -o rps1 prompt_rps1
bleopt/declare -o rps1_transient prompt_rps1_transient
bleopt/declare -v prompt_eol_mark $'\e[94m[ble: EOF]\e[m'
bleopt/declare -v prompt_ruler ''
bleopt/declare -v prompt_status_line ''
bleopt/declare -n prompt_status_align $'justify=\r'
ble/color/defface prompt_status_line fg=231,bg=240
bleopt/declare -v prompt_command_changes_layout ''
function bleopt/check:prompt_status_align {
case $value in
(left|right|center|justify|justify=?*)
ble/prompt/unit#clear _ble_prompt_status hash
return 0 ;;
(*)
ble/util/print "bleopt prompt_status_align: unsupported value: '$value'" >&2
return 1 ;;
esac
}
## @bleopt internal_exec_type (内部使用)
## コマンドの実行の方法を指定します。
##
## internal_exec_type=exec [廃止]
## 関数内で実行します (削除されました)
## internal_exec_type=gexec
## グローバルな文脈で実行します (新しい方法です)
##
## 要件: 関数 ble-edit/exec:$bleopt_internal_exec_type/process が定義されていること。
bleopt/declare -n internal_exec_type gexec
function bleopt/check:internal_exec_type {
if ! ble/is-function "ble-edit/exec:$value/process"; then
ble/util/print "bleopt: Invalid value internal_exec_type='$value'. A function 'ble-edit/exec:$value/process' is not defined." >&2
return 1
fi
}
## @bleopt internal_suppress_bash_output (内部使用)
## bash 自体の出力を抑制するかどうかを指定します。
## bleopt_internal_suppress_bash_output=1
## 抑制します。bash のエラーメッセージは visible-bell で表示します。
## bleopt_internal_suppress_bash_output=
## 抑制しません。bash のメッセージは全て端末に出力されます。
## これはデバグ用の設定です。bash の出力を制御するためにちらつきが発生する事があります。
## bash-3 ではこの設定では C-d を捕捉できません。
bleopt/declare -v internal_suppress_bash_output 1
## @bleopt internal_ignoreeof_trap (内部使用)
## bash-3.0 の時に使用します。C-d を捕捉するのに用いるメッセージです。
## これは自分の bash の設定に合わせる必要があります。
bleopt/declare -n internal_ignoreeof_trap 'Use "exit" to leave the shell.'
## @bleopt allow_exit_with_jobs
## この変数に空文字列が設定されている時、
## ジョブが残っている時には ble/widget/exit からシェルは終了しません。
## この変数に空文字列以外が設定されている時、
## ジョブがある場合でも条件を満たした時に exit を実行します。
## 停止中のジョブがある場合、または、shopt -s checkjobs かつ実行中のジョブが存在する時は、
## 二回連続で同じ widget から exit を呼び出した時にシェルを終了します。
## それ以外の場合は常にシェルを終了します。
## 既定値は空文字列です。
bleopt/declare -v allow_exit_with_jobs ''
## @bleopt history_share
## この変数に空文字列が設定されている時、履歴を共有します。
bleopt/declare -v history_share ''
## @bleopt accept_line_threshold
## 編集関数 accept-single-line-or-newline の単一行モードにおける振る舞いを制御します。
## この変数が負の整数の時、常にコマンドを実行します。
## この変数が 0 の時、ユーザの入力がある場合は改行を挿入して複数行モードに入ります。
## 正の整数 n の時、未処理のユーザ入力が n 以上の時に改行を挿入して複数行モードに入ります。
bleopt/declare -v accept_line_threshold 5
## @bleopt exec_errexit_mark
## 終了ステータスが非零の時に表示するマークの書式を指定します。
## この変数が空の時、終了ステータスは表示しません。
bleopt/declare -v exec_errexit_mark $'\e[91m[ble: exit %d]\e[m'
bleopt/declare -v exec_elapsed_mark $'\e[94m[ble: elapsed %s (CPU %s%%)]\e[m'
bleopt/declare -v exec_elapsed_enabled 'usr+sys>=10000'
## @bleopt line_limit_length
## 一括挿入時のコマンドライン文字数の上限を指定します。
## 0以下の値は文字数に制限を与えない事を示します。
bleopt/declare -v line_limit_length 10000
## @bleopt line_limit_type
## 一括挿入で文字数を超過した時の動作を指定します。
bleopt/declare -v line_limit_type none
#
#------------------------------------------------------------------------------
# **** Application ****
_ble_app_render_mode=panel
_ble_app_winsize=()
_ble_app_render_Processing=
function ble/application/.set-up-render-mode {
[[ $1 == "$_ble_app_render_mode" ]] && return 0
case $1 in
(panel)
ble/term/leave-altscr
ble/canvas/panel/invalidate ;;
(forms:*)
ble/term/enter-altscr
ble/util/buffer "$_ble_term_clear"
ble/util/buffer $'\e[H'
_ble_canvas_x=0 _ble_canvas_y=0 ;;
(*)
ble/util/print "ble/edit: unrecognized render mode '$1'."
return 1 ;;
esac
}
function ble/application/push-render-mode {
ble/application/.set-up-render-mode "$1" || return 1
ble/array#unshift _ble_app_render_mode "$1"
}
function ble/application/pop-render-mode {
[[ ${_ble_app_render_mode[1]} ]] || return 1
ble/application/.set-up-render-mode "${_ble_app_render_mode[1]}"
ble/array#shift _ble_app_render_mode
}
function ble/application/render {
local _ble_app_render_Processing=1
{
local render=$_ble_app_render_mode
case $render in
(panel)
local _ble_prompt_update=owner
ble/prompt/update
ble/canvas/panel/render ;;
(forms:*)
ble/forms/render "${render#*:}" ;;
esac
_ble_app_winsize=("$COLUMNS" "$LINES")
ble/util/buffer.flush >&2
}
ble/util/unlocal _ble_app_render_Processing
if [[ $_ble_application_render_winch ]]; then
_ble_application_render_winch=
ble/application/onwinch
fi
}
function ble/application/onwinch {
if [[ $_ble_app_render_Processing || $_ble_decode_hook_Processing == body || $_ble_decode_hook_Processing == prologue ]]; then
# Note #D1762: 別の処理が走っている途中に描画更新すると中途半端な
# データに対して処理が実行されてデータが破壊されるので後で処理する。
#
# ble_decode_hook_body=1 の時は EPILOGUE が後で必ず呼び出されるの
# でその時に ble/application/render が呼び出される。その中で
# _ble_application_render_winch がチェックされて改めてこの関数が呼
# び出される。_ble_app_render_Processing=1 の時には、
# ble/application/render の末尾でやはりチェックが走ると期待する。
_ble_application_render_winch=1
return 0
fi
_ble_textmap_pos=()
# 処理中に届いた WINCH は失われる様だ。連続的サイズ変化を通知す
# る端末の場合、途中のサイズの WINCH の処理中に最終的なサイズの
# WINCH を逃して表示が乱れたままになる。対策として描画終了時に処
# 理中にサイズ変化が起こっていないか確認する。
local old_size= i
for ((i=0;i<20;i++)); do
# 次の待つと共にサブシェルで checkwinsize を誘発
(ble/util/msleep 50)
# trap 中だと bash のバグでジョブが溜まるので逐次捌く
ble/util/joblist.check ignore-volatile-jobs
local size=$LINES:$COLUMNS
[[ $size == "$old_size" ]] && break
local _ble_app_render_Processing=1
{
old_size=$size
case $bleopt_canvas_winch_action in
(clear)
# 全消去して一番上から再描画
_ble_prompt_trim_opwd=
ble/util/buffer "$_ble_term_clear" ;;
(redraw-here)
# 現在位置から再描画 (幅が減った時は前のコマンドの出力結果を破壊しな
# いので戻る)
if ((COLUMNS<_ble_app_winsize[0])); then
local -a DRAW_BUFF=()
ble/canvas/panel#goto.draw 0 0 0
ble/canvas/bflush.draw
fi ;;
(redraw-prev)
# 前回の開始相対位置が変化していないと仮定して戻って再描画
local -a DRAW_BUFF=()
ble/canvas/panel#goto.draw 0 0 0
ble/canvas/bflush.draw ;;
esac
ble/canvas/panel/invalidate height # 高さの再確保も含めて。
}
ble/util/unlocal _ble_app_render_Processing
ble/application/render
done
}
# canvas.sh 設定
_ble_canvas_panel_focus=0
_ble_canvas_panel_class=(ble/textarea ble/textarea ble/edit/info ble/prompt/status)
_ble_canvas_panel_vfill=3
_ble_edit_command_layout_level=0
function ble/edit/enter-command-layout {
((_ble_edit_command_layout_level++==0)) || return 0
# 一時的に info 及び status を消去する。
ble/edit/info#collapse "$_ble_edit_info_panel"
ble/prompt/status#collapse
}
function ble/edit/leave-command-layout {
((_ble_edit_command_layout_level>0&&
--_ble_edit_command_layout_level==0)) || return 0
# 抑制した info を改めて表示し直す。一時的に表示していた内容は消去して
# default の内容を表示する。
blehook/invoke info_reveal
ble/edit/info/default
}
function ble/edit/clear-command-layout {
((_ble_edit_command_layout_level>0)) || return 0
_ble_edit_command_layout_level=1
ble/edit/leave-command-layout
}
function ble/edit/is-command-layout {
((_ble_edit_command_layout_level>0))
}
#
#------------------------------------------------------------------------------
# **** ble/prompt/status **** @prompt.status
_ble_prompt_status_panel=3
_ble_prompt_status_dirty=
_ble_prompt_status_data=()
_ble_prompt_status_bbox=()
# Note: 高さは 0 か 1 のどちらかである事を前提に設計してある。より多くの行を表
# 示したい場合には _ble_prompt_status_data を計算する時点で調整が必要になる。
function ble/prompt/status#panel::invalidate {
_ble_prompt_status_dirty=1
}
function ble/prompt/status#panel::render {
[[ $_ble_prompt_status_dirty ]] || return 0
_ble_prompt_status_dirty=
# 表示内容がない場合は何もせず抜ける (高さは既に調整されている前提)
local index=$1
local height; ble/prompt/status#panel::getHeight "$index"
[[ ${height#*:} == 1 ]] || return 0
local -a DRAW_BUFF=()
# 高さが一致していない場合は取り敢えず再配置を要求してみる。
# 高さを取得できなければ諦める。
height=$3
if ((height!=1)); then
ble/canvas/panel/reallocate-height.draw
ble/canvas/bflush.draw
height=${_ble_canvas_panel_height[index]}
((height==0)) && return 0
fi
local esc=${_ble_prompt_status_data[10]}
if [[ $esc ]]; then
local prox=${_ble_prompt_status_data[11]}
local proy=${_ble_prompt_status_data[12]}
ble/canvas/panel#goto.draw "$_ble_prompt_status_panel"
ble/canvas/panel#put.draw "$_ble_prompt_status_panel" "$esc" "$prox" "$proy"
else
ble/canvas/panel#clear.draw "$_ble_prompt_status_panel"
fi
ble/canvas/bflush.draw
}
function ble/prompt/status#panel::getHeight {
if ble/edit/is-command-layout || [[ ! ${_ble_prompt_status_data[10]} ]]; then
height=0:0
else
height=0:1
fi
}
function ble/prompt/status#panel::onHeightChange {
ble/prompt/status#panel::invalidate
}
function ble/prompt/status#collapse {
local -a DRAW_BUFF=()
ble/canvas/panel#set-height.draw "$_ble_prompt_status_panel" 0
ble/canvas/bflush.draw
}
#
#------------------------------------------------------------------------------
# **** prompt **** @line.ps1
## @var _ble_prompt_version
## ble/prompt/update でのプロンプト更新の度にインクリメントする変数
_ble_prompt_hash=
_ble_prompt_version=0
function ble/prompt/.escape-control-characters {
ret=$1
local ctrl=$'\001-\037\177'
case $_ble_util_locale_encoding in
(UTF-8) ctrl=$ctrl$'\302\200-\302\237' ;;
(C) ctrl=$ctrl$'\200-\237' ;;
esac
local LC_ALL= LC_COLLATE=C glob_ctrl=[$ctrl]
[[ $ret == *$glob_ctrl* ]] || return 0
local out= head tail=$ret cs
while head=${tail%%$glob_ctrl*}; [[ $head != "$tail" ]]; do
ble/util/s2c "${tail:${#head}:1}"
ble/unicode/GraphemeCluster/.get-ascii-rep "$ret" # -> cs
out=$out$head$'\e[9807m'$cs$'\e[9807m'
tail=${tail#*$glob_ctrl}
done
ret=$out$tail
}
ble/function#suppress-stderr ble/prompt/.escape-control-characters # LC_COLLATE
## @fn ble/prompt/.initialize-constant ps defeval [opts]
## @param ps
## 初期化に使用する prompt シーケンスを指定します。
## @param defeval
## 初期化に使用するコマンドを指定します。ret に結果を格納します。
## @param[opt] opts
## コロン区切りのオプションリストです。escape が指定されている時、
## 展開結果に含まれる制御文字をエスケープします。
function ble/prompt/.initialize-constant {
local __ps=$1 __defeval=$2 __opts=$3
if ((_ble_bash>=40400)); then
ret=${__ps@P}
else
builtin eval -- "$__defeval"
fi
if [[ $__opts == *:escape:* ]]; then
if ((_ble_bash>=50200)); then
# bash-5.2 以上では bash が escape を行うが、反転などの処理が実
# 装されていないので、制御文字が含まれている場合には ble.sh の側
# で処理を行う。
if [[ $ret == *\^['A'-'Z[\]^_?']* ]]; then
builtin eval -- "$__defeval"
ble/prompt/.escape-control-characters "$ret"
elif [[ $ret == *$'\t'* ]]; then
ble/prompt/.escape-control-characters "$ret"
fi
else
ble/prompt/.escape-control-characters "$_ble_prompt_const_s"
fi
fi
}
## called by ble-edit/initialize
function ble/prompt/initialize {
local ret
# hostname
ble/prompt/.initialize-constant '\H' 'ret=$HOSTNAME' escape
_ble_prompt_const_H=$ret
if local rex='^[0-9]+(\.[0-9]){3}$'; [[ $HOSTNAME =~ $rex ]]; then
# IPv4 の形式の場合には省略しない
_ble_prompt_const_h=$_ble_prompt_const_H
else
_ble_prompt_const_h=${_ble_prompt_const_H%%.*}
fi
# tty basename
ble/prompt/.initialize-constant '\l' 'ble/util/assign ret "ble/bin/tty 2>/dev/null";ret=${ret##*/}'
_ble_prompt_const_l=$ret
# command name
ble/prompt/.initialize-constant '\s' 'ret=${0##*/}' escape
_ble_prompt_const_s=$ret
# user
ble/prompt/.initialize-constant '\s' 'ret=$USER' escape
_ble_prompt_const_u=$ret
# bash versions
ble/util/sprintf _ble_prompt_const_v '%d.%d' "${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}"
ble/util/sprintf _ble_prompt_const_V '%d.%d.%d' "${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}" "${BASH_VERSINFO[2]}"
# uid
if [[ $EUID -eq 0 ]]; then
_ble_prompt_const_root='#'
else
_ble_prompt_const_root='$'
fi
if [[ $OSTYPE == cygwin* ]]; then
local windir=/cygdrive/c/Windows
if [[ $WINDIR == [a-zA-Z]:\\* ]]; then
local bsl='\' sl=/
local c=${WINDIR::1} path=${WINDIR:3}
if [[ $c == [A-Z] ]]; then
if ((_ble_bash>=40000)); then
c=${c,?}
else
local ret
ble/util/s2c "$c"
ble/util/c2s "$((ret+32))"
c=$ret
fi
fi
windir=/cygdrive/$c/${path//"$bsl"/"$sl"}
fi
if [[ -e $windir && -w $windir ]]; then
_ble_prompt_const_root='#'
fi
elif [[ $OSTYPE == msys* ]]; then
# msys64/etc/bash.bashrc に倣う
if ble/bin#has id getent; then
local id getent
ble/util/assign id 'id -G'
ble/util/assign getent 'getent -w group S-1-16-12288'
ble/string#split getent : "$getent"
[[ " $id " == *" ${getent[1]} "* ]] &&
_ble_prompt_const_root='#'
fi
fi
}
## @arr PREFIX_data
## プロンプトに表示するデータの単位です。
## 他のデータに対する依存性等を管理する機能を有します。
##
## @var PREFIX_data[0] version
## prompt 情報の更新回数を保持します。
## @var PREFIX_data[1] hashref
## @var PREFIX_data[2] hash
## 依存性追跡に使われる変数です。
##
## @fn ble/prompt/unit#update TYPE PREFIX ARGS...
## 依存性を追跡しつつデータを更新します。
##
## @fn[in] ble/prompt/unit:TYPE/update
## データの更新をします。データに変化があった場合に 0 を返します。
## それ以外の場合に 1 を返します。
##
## @var[in] prompt_unit
## @var[in,out] prompt_unit_changed
## @var[in] prompt_unit_expired
## @var[in,out] prompt_hashref_dep
## @var[in,out] prompt_hashref_var
##
## @var[in,opt] prompt_hashref_base
##
## @var[in] prompt_unit
## ble/prompt/unit:PREFIX/update が入れ子で呼び出される時に設定される変数です。
## 親プロンプトの PREFIX を保持します。
## prompt 間の依存性を追跡する為に呼び出し元の以下の変数を更新します。
##
## @var[ref,opt] prompt_hashref_dep
##
function ble/prompt/unit#update {
local unit=$1
local prompt_unit_changed=
local prompt_unit_expired=
local ohashref=${unit}_data[1]; ohashref=${!ohashref-}
if [[ ! $ohashref ]]; then
prompt_unit_expired=1
else
ble/prompt/unit#update/.update-dependencies "$ohashref"
local ohash=${unit}_data[2]; ohash=${!ohash}
builtin eval -- "local nhash=\"$ohashref\"" 2>/dev/null
[[ $nhash != "$ohash" ]] && prompt_unit_expired=1
fi
if [[ $prompt_unit_expired ]]; then
local prompt_unit=$unit
local prompt_hashref_dep= # プロンプト間依存性
local prompt_hashref_var= # 変数に対する依存性
ble/prompt/unit:"$unit"/update "$unit" &&
((prompt_unit_changed=1,${unit}_data[0]++))
local hashref=${prompt_hashref_base-'$_ble_prompt_version'}:$prompt_hashref_dep:$prompt_hashref_var
builtin eval -- "${unit}_data[1]=\$hashref"
builtin eval -- "${unit}_data[2]=\"$hashref\"" 2>/dev/null
ble/util/unlocal prompt_unit prompt_hashref_dep
fi
# 呼び出し元 prompt_hashref_dep の更新 (依存性登録)
if [[ $prompt_unit ]]; then
local ref1='$'$unit'_data'
[[ ,$prompt_hashref_dep, != *,"$ref1",* ]] &&
prompt_hashref_dep=$prompt_hashref_dep${prompt_hashref_dep:+,}$ref1
fi
[[ $prompt_unit_changed ]]
}
function ble/prompt/unit#update/.update-dependencies {
local ohashref=$1
local otree=${ohashref#*:}; otree=${otree%%:*}
if [[ $otree ]]; then
ble/string#split otree , "$otree"
if [[ ! $ble_prompt_unit_processing ]]; then
local ble_prompt_unit_processing=1
"${_ble_util_set_declare[@]//NAME/ble_prompt_unit_mark}" # WA #D1570 checked
elif ble/set#contains ble_prompt_unit_mark "$unit"; then
ble/util/print "ble/prompt: FATAL: detected cyclic dependency ($unit required by $ble_prompt_unit_parent)" >/dev/tty
return 1
fi
local ble_prompt_unit_parent=$unit
ble/set#add ble_prompt_unit_mark "$unit"
local prompt_unit= # 依存関係の登録はしない
local child
for child in "${otree[@]}"; do
[[ $child == '$'?*'_data' ]] || continue
child=${child:1:${#child}-6}
ble/is-function ble/prompt/unit:"$child"/update &&
ble/prompt/unit#update "$child"
done
ble/set#remove ble_prompt_unit_mark "$unit"
fi
}
function ble/prompt/unit#clear {
local prefix=$1
builtin eval -- "${prefix}_data[2]="
}
function ble/prompt/unit/assign {
local var=$1 value=$2
[[ $value == "${!var}" ]] && return 1
prompt_unit_changed=1
builtin eval -- "$var=\$value"
}
## @fn ble/prompt/unit/add-hash hashref
## プロンプトの更新検出に用いるシェル単語を指定します。
function ble/prompt/unit/add-hash {
[[ $prompt_unit && ,$prompt_hashref_var, != *,"$1",* ]] &&
prompt_hashref_var=$prompt_hashref_var${prompt_hashref_var:+,}$1
return 0
}
## @var _ble_prompt_ps1_data
## @var _ble_prompt_rps1_data
## @var _ble_prompt_status_data
## @var _ble_prompt_xterm_title_data
## @var _ble_prompt_screen_title_data
## @var _ble_prompt_term_status_data
## 構築した prompt の情報をキャッシュします。
##
## @var PREFIX_data[3..5] x y g
## prompt を表示し終わった時のカーソルの位置と描画属性を表します。
## @var PREFIX_data[6..7] lc lg
## bleopt_internal_suppress_bash_output= の時、
## prompt を表示し終わった時の左側にある文字とその描画属性を表します。
## それ以外の時はこの値は使われません。
## @var PREFIX_data[8] ps1out (esc)
## prompt を表示する為に出力する制御シーケンスを含んだ文字列です。
## @var PREFIX_data[9] trace_hash
## COLUMNS:ps1esc の形式の文字列です。
## 調整前の ps1out を格納します。
## ps1out の計算 (trace) を省略する為に使用します。
##
## @var PREFIX_data[10...] tailored
## ps1out の結果を加工して得られるデータ。
## 加工だけを後で再実行する事もあるので統一的に管理する。
##
_ble_prompt_ps1_dirty=
_ble_prompt_ps1_data=(0 '' '' 0 0 0 32 0 '' '')
_ble_prompt_rps1_dirty=
_ble_prompt_rps1_data=()
_ble_prompt_rps1_gbox=()
_ble_prompt_rps1_shown=
_ble_prompt_xterm_title_dirty=
_ble_prompt_xterm_title_data=()
_ble_prompt_screen_title_dirty=
_ble_prompt_screen_title_data=()
_ble_prompt_term_status_dirty=
_ble_prompt_term_status_data=()
## @fn ble/prompt/print text
## プロンプト構築中に呼び出す関数です。
## 指定された文字列を、後の評価に対するエスケープをして出力します。
## @param[in] text
## エスケープされる文字列を指定します。
## @var[out] DRAW_BUFF[]
## 出力先の配列です。
function ble/prompt/print {
local ret=$1
[[ $prompt_noesc ]] ||
ble/string#escape-characters "$ret" '\$"`'
ble/canvas/put.draw "$ret"
}
## @fn ble/prompt/process-prompt-string prompt_string
## プロンプト構築中に呼び出す関数です。
## 指定した引数を PS1 と同様の形式と解釈して処理します。
## @param[in] prompt_string
## @arr[in,out] DRAW_BUFF
function ble/prompt/process-prompt-string {
local ps1=$1
local i=0 iN=${#ps1}
local rex_letters='^[^\]+|\\$'
while ((i<iN)); do
local tail=${ps1:i}
if [[ $tail == '\'?* ]]; then
ble/prompt/.process-backslash
elif [[ $tail =~ $rex_letters ]]; then
ble/canvas/put.draw "$BASH_REMATCH"
((i+=${#BASH_REMATCH}))
else
# ? ここには本来来ないはず。
ble/canvas/put.draw "${tail::1}"
((i++))
fi
done
}
## @fn ble/prompt/.process-backslash
## @var[in] tail
## @arr[in.out] DRAW_BUFF
function ble/prompt/.process-backslash {
((i+=2))
# \\ の次の文字
local c=${tail:1:1} pat='][#!$\'
if [[ $c == ["$pat"] ]]; then
case "$c" in
(\[) ble/canvas/put.draw $'\001' ;; # \[ \] は後処理の為、適当な識別用の文字列を出力する。
(\]) ble/canvas/put.draw $'\002' ;;
('#') # コマンド番号 (本当は history に入らない物もある…)
ble/prompt/unit/add-hash '$_ble_edit_CMD'
ble/canvas/put.draw "$_ble_edit_CMD" ;;
(\!) # 編集行の履歴番号
local count
ble/history/get-count -v count
ble/canvas/put.draw "$((count+1))" ;;
('$') # # or $
ble/prompt/print "$_ble_prompt_const_root" ;;
(\\)
# '\\' は '\' と出力された後に、更に "" 内で評価された時に次の文字をエスケープする。
# 例えば '\\$' は一旦 '\$' となり、更に展開されて '$' となる。'\\\\' も同様に '\' になる。
ble/canvas/put.draw '\' ;;
esac
elif ble/is-function ble/prompt/backslash:"$c"; then
ble/function#try ble/prompt/backslash:"$c"
elif ble/is-function ble-edit/prompt/backslash:"$c"; then # deprecated name
ble/function#try ble-edit/prompt/backslash:"$c"
else
# その他の文字はそのまま出力される。
# - '\"' '\`' はそのまま出力された後に "" 内で評価され '"' '`' となる。
# - それ以外の場合は '\?' がそのまま出力された後に、"" 内で評価されても変わらず '\?' 等となる。
ble/canvas/put.draw "\\$c"
fi
}
## @fn[custom] ble/prompt/backslash:*
## プロンプト PS1 内で使用するバックスラッシュシーケンスを定義します。
## 内部では ble/canvas/put.draw escaped_text もしくは
## ble/prompt/print unescaped_text を用いて
## シーケンスの展開結果を追記します。
##
## @exit
## 対応する文字列を出力した時に成功します。
## 0 以外の終了ステータスを返した場合、
## シーケンスが処理されなかったと見做され、
## 呼び出し元によって \c (c: 文字) が代わりに書き込まれます。
##
function ble/prompt/backslash:0 { # 8進表現
local rex='^\\[0-7]{1,3}'
if [[ $tail =~ $rex ]]; then
local seq=${BASH_REMATCH[0]}
((i+=${#seq}-2))
builtin eval "c=\$'$seq'"
fi
ble/prompt/print "$c"
return 0
}
function ble/prompt/backslash:1 { ble/prompt/backslash:0; }
function ble/prompt/backslash:2 { ble/prompt/backslash:0; }
function ble/prompt/backslash:3 { ble/prompt/backslash:0; }
function ble/prompt/backslash:4 { ble/prompt/backslash:0; }
function ble/prompt/backslash:5 { ble/prompt/backslash:0; }
function ble/prompt/backslash:6 { ble/prompt/backslash:0; }
function ble/prompt/backslash:7 { ble/prompt/backslash:0; }
function ble/prompt/backslash:a { # 0 BEL
ble/canvas/put.draw ""
return 0
}
function ble/prompt/backslash:e {
ble/canvas/put.draw $'\e'
return 0
}
function ble/prompt/backslash:n {
ble/canvas/put.draw $'\n'
return 0
}
function ble/prompt/backslash:r {
ble/canvas/put.draw "$_ble_term_cr"
return 0
}
_ble_prompt_cache_vars=(
prompt_cache_d
prompt_cache_t
prompt_cache_A
prompt_cache_T
prompt_cache_at
prompt_cache_j
prompt_cache_wd
)
function ble/prompt/backslash:d { # ? 日付
[[ $prompt_cache_d ]] || ble/util/strftime -v prompt_cache_d '%a %b %d'
ble/prompt/print "$prompt_cache_d"
return 0
}
function ble/prompt/backslash:t { # 8 時刻
[[ $prompt_cache_t ]] || ble/util/strftime -v prompt_cache_t '%H:%M:%S'
ble/prompt/print "$prompt_cache_t"
return 0
}
function ble/prompt/backslash:A { # 5 時刻
[[ $prompt_cache_A ]] || ble/util/strftime -v prompt_cache_A '%H:%M'
ble/prompt/print "$prompt_cache_A"
return 0
}
function ble/prompt/backslash:T { # 8 時刻
[[ $prompt_cache_T ]] || ble/util/strftime -v prompt_cache_T '%I:%M:%S'
ble/prompt/print "$prompt_cache_T"
return 0
}
function ble/prompt/backslash:@ { # ? 時刻
[[ $prompt_cache_at ]] || ble/util/strftime -v prompt_cache_at '%I:%M %p'
ble/prompt/print "$prompt_cache_at"
return 0
}
function ble/prompt/backslash:D {
local rex='^\\D\{([^{}]*)\}' cache_D
if [[ $tail =~ $rex ]]; then
ble/util/strftime -v cache_D "${BASH_REMATCH[1]}"
ble/prompt/print "$cache_D"
((i+=${#BASH_REMATCH}-2))
else
ble/prompt/print "\\$c"
fi
return 0
}
function ble/prompt/backslash:h { # = ホスト名
ble/prompt/print "$_ble_prompt_const_h"
return 0
}
function ble/prompt/backslash:H { # = ホスト名
ble/prompt/print "$_ble_prompt_const_H"
return 0
}
function ble/prompt/backslash:j { # ジョブの数
if [[ ! $prompt_cache_j ]]; then
local joblist
ble/util/joblist
prompt_cache_j=${#joblist[@]}
fi
ble/canvas/put.draw "$prompt_cache_j"
return 0
}
function ble/prompt/backslash:l { # tty basename
ble/prompt/print "$_ble_prompt_const_l"
return 0
}
function ble/prompt/backslash:s { # 4 "bash"
ble/prompt/print "$_ble_prompt_const_s"
return 0
}
function ble/prompt/backslash:u { # = ユーザ名
ble/prompt/print "$_ble_prompt_const_u"
return 0
}
function ble/prompt/backslash:v { # = bash version %d.%d
ble/prompt/print "$_ble_prompt_const_v"
return 0
}
function ble/prompt/backslash:V { # = bash version %d.%d.%d
ble/prompt/print "$_ble_prompt_const_V"
return 0
}
function ble/prompt/backslash:w { # PWD
ble/prompt/unit/add-hash '$PWD'
ble/prompt/.update-working-directory
local ret
ble/prompt/.escape-control-characters "$prompt_cache_wd"
ble/prompt/print "$ret"
return 0
}
function ble/prompt/backslash:W { # PWD短縮
ble/prompt/unit/add-hash '$PWD'
if [[ ! ${PWD//'/'} ]]; then
ble/prompt/print "$PWD"
else
ble/prompt/.update-working-directory
local ret
ble/prompt/.escape-control-characters "${prompt_cache_wd##*/}"
ble/prompt/print "$ret"
fi
return 0
}
# \q{name} (ble.sh extension)
function ble/prompt/backslash:q {
local rex='^\{([^{}]*)\}'
if [[ ${tail:2} =~ $rex ]]; then
local rematch=$BASH_REMATCH
((i+=${#rematch}))
local word; ble/string#split-words word "${BASH_REMATCH[1]}"
if [[ $word ]] && ble/is-function ble/prompt/backslash:"$word"; then
ble/util/joblist.check
ble/prompt/backslash:"${word[@]}"; local ext=$?
ble/util/joblist.check ignore-volatile-jobs
return "$?"
else
if [[ ! $word ]]; then
ble/term/visible-bell "ble/prompt: invalid sequence \\q$rematch"
elif ! ble/is-function ble/prompt/backslash:"$word"; then
ble/term/visible-bell "ble/propmt: undefined named sequence \\q{$word}"
fi
ble/prompt/print "\\q$BASH_REMATCH"
return 2
fi
else
ble/prompt/print "\\$c"