-
-
Notifications
You must be signed in to change notification settings - Fork 84
/
core-complete.sh
5054 lines (4518 loc) · 166 KB
/
core-complete.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
ble/util/import "$_ble_base/lib/core-syntax.sh"
## 関数 ble/complete/string#search-longest-suffix-in needle haystack
## @var[out] ret
function ble/complete/string#search-longest-suffix-in {
local needle=$1 haystack=$2
local l=0 u=${#needle}
while ((l<u)); do
local m=$(((l+u)/2))
if [[ $haystack == *"${needle:m}"* ]]; then
u=$m
else
l=$((m+1))
fi
done
ret=${needle:l}
}
## 関数 ble/complete/string#common-suffix-prefix lhs rhs
## @var[out] ret
function ble/complete/string#common-suffix-prefix {
local lhs=$1 rhs=$2
if ((${#lhs}<${#rhs})); then
local i n=${#lhs}
for ((i=0;i<n;i++)); do
ret=${lhs:i}
[[ $rhs == "$ret"* ]] && return
done
ret=
else
local j m=${#rhs}
for ((j=m;j>0;j--)); do
ret=${rhs::j}
[[ $lhs == *"$ret" ]] && return
done
ret=
fi
}
## ble/complete 内で共通で使われるローカル変数
##
## @var COMP1 COMP2 COMPS COMPV
## COMP1-COMP2 は補完対象の範囲を指定します。
## COMPS は COMP1-COMP2 にある文字列を表し、
## COMPV は COMPS の評価値 (クォート除去、簡単なパラメータ展開をした値) を表します。
## COMPS に複雑な構造が含まれていて即時評価ができない場合は
## COMPV は unset になります。必要な場合は [[ $comps_flags == *v* ]] で判定して下さい。
## ※ [[ -v COMPV ]] は bash-4.2 以降です。
##
## @var comp_type
## 候補生成の方法を制御します。
## 以下のオプションのコロン区切りの組み合わせからなる文字列です。
##
## a 曖昧補完に用いる候補を生成する。
## 曖昧一致するかどうかは呼び出し元で判定されるので、
## 曖昧一致する可能性のある候補をできるだけ多く生成すれば良い。
## m 曖昧補完 (中間部分に一致)
## A 曖昧補完 (部分列・最初の文字も一致しなくて良い)
##
## i (rlvar completion-ignore-case)
## 大文字小文字を区別しない補完候補生成を行う。
## vstat (rlvar visible-stats)
## ファイル名末尾にファイルの種類を示す記号を付加する。
## markdir (rlvar mark-directories)
## ディレクトリ名の補完後に / を付加する。
##
## sync
## ユーザの入力があっても中断しない事を表す。
## raw
## COMPV としてシェル評価前の文字列を使用します。
##
function ble/complete/check-cancel {
[[ :$comp_type: != *:sync:* ]] && ble-decode/has-input
}
#==============================================================================
# action
## 既存の action
##
## ble/complete/action:plain
## ble/complete/action:word
## ble/complete/action:file
## ble/complete/action:progcomp
## ble/complete/action:command
## ble/complete/action:variable
##
## action の実装
##
## 関数 ble/complete/action:$ACTION/initialize
## 基本的に INSERT を設定すれば良い
## @var[in ] CAND
## @var[in,out] ACTION
## @var[in,out] DATA
## @var[in,out] INSERT
## COMP1-COMP2 を置き換える文字列を指定します
##
## @var[in] COMP1 COMP2 COMPS COMPV comp_type
##
## @var[in ] COMP_PREFIX
##
## @var[in ] comps_flags
## 以下のフラグ文字からなる文字列です。
##
## p パラメータ展開の直後に於ける補完である事を表します。
## 直後に識別子を構成する文字を追記する時に対処が必要です。
##
## v COMPV が利用可能である事を表します。
##
## S クォート '' の中にいる事を表します。
## E クォート $'' の中にいる事を表します。
## D クォート "" の中にいる事を表します。
## I クォート $"" の中にいる事を表します。
## B クォート \ の直後にいる事を表します。
## x ブレース展開の中にいる事を表します。
##
## Note: shopt -s nocaseglob のため、フラグ文字は
## 大文字・小文字でも重複しないように定義する必要がある。
##
## @var[in ] comps_fixed
## 補完対象がブレース展開を含む場合に ibrace:value の形式になります。
## それ以外の場合は空文字列です。
## ibrace はブレース展開の構造を保持するのに必要な COMPS 接頭辞の長さです。
## value は ${COMPS::ibrace} のブレース展開を実行した結果の最後の単語の評価結果です。
##
## 関数 ble/complete/action:$ACTION/complete
## 一意確定時に、挿入文字列・範囲に対する加工を行います。
## 例えばディレクトリ名の場合に / を後に付け加える等です。
##
## @var[in] CAND
## @var[in] ACTION
## @var[in] DATA
## @var[in] COMP1 COMP2 COMPS COMPV comp_type comps_flags
##
## @var[in,out] insert suffix
## 補完によって挿入される文字列を指定します。
## 加工後の挿入する文字列を返します。
##
## @var[in] insert_beg insert_end
## 補完によって置換される範囲を指定します。
##
## @var[in,out] insert_flags
## 以下のフラグ文字の組み合わせの文字列です。
##
## r [in] 既存の部分を保持したまま補完が実行される事を表します。
## それ以外の時、既存の入力部分も含めて置換されます。
## m [out] 候補一覧 (menu) の表示を要求する事を表します。
## n [out] 再度補完を試み (確定せずに) 候補一覧を表示する事を要求します。
##
function ble/complete/string#escape-for-completion-context {
local str=$1
case $comps_flags in
(*S*) ble/string#escape-for-bash-single-quote "$str" ;;
(*E*) ble/string#escape-for-bash-escape-string "$str" ;;
(*[DI]*) ble/string#escape-for-bash-double-quote "$str" ;;
(*)
if [[ $comps_fixed ]]; then
ble/string#escape-for-bash-specialchars "$str" b
else
ble/string#escape-for-bash-specialchars "$str"
fi ;;
esac
}
function ble/complete/action/util/complete.addtail {
suffix=$suffix$1
}
function ble/complete/action/util/complete.mark-directory {
[[ :$comp_type: == *:markdir:* && $CAND != */ ]] &&
[[ ! -h $CAND || ( $insert == "$COMPS" || :$comp_type: == *:marksymdir:* ) ]] &&
ble/complete/action/util/complete.addtail /
}
function ble/complete/action/util/complete.close-quotation {
case $comps_flags in
(*[SE]*) ble/complete/action/util/complete.addtail \' ;;
(*[DI]*) ble/complete/action/util/complete.addtail \" ;;
esac
}
function ble/complete/action/util/quote-insert {
if [[ $comps_flags == *v* && $CAND == "$COMPV"* ]]; then
local ins=${CAND:${#COMPV}} ret
# 単語内の文脈に応じたエスケープ
ble/complete/string#escape-for-completion-context "$ins"; ins=$ret
# 直前にパラメータ展開があればエスケープ
if [[ $comps_flags == *p* && $ins == [_a-zA-Z0-9]* ]]; then
case $comps_flags in
(*[DI]*)
if [[ $COMPS =~ $rex_raw_paramx ]]; then
local rematch1=${BASH_REMATCH[1]}
INSERT=$rematch1'${'${COMPS:${#rematch1}+1}'}'$ins
return
else
ins='""'$ins
fi ;;
(*) ins='\'$ins ;;
esac
fi
# backslash が前置している時は二重クォートを防ぐ為に削除
[[ $comps_flags == *B* && $COMPS == *'\' && $ins == '\'* ]] && ins=${ins:1}
INSERT=$COMPS$ins
else
local ins=$CAND comps_fixed_part= compv_fixed_part=
if [[ $comps_fixed && $CAND == "${comps_fixed#*:}"* ]]; then
comps_fixed_part=${COMPS::${comps_fixed%%:*}}
compv_fixed_part=${comps_fixed#*:}
ins=${CAND:${#compv_fixed_part}}
fi
local ret; ble/complete/string#escape-for-completion-context "$ins"; ins=$ret
case $comps_flags in
(*S*) ins=\'$ins ;;
(*E*) ins=\$\'$ins ;;
(*D*) ins=\"$ins ;;
(*I*) ins=\$\"$ins ;;
esac
INSERT=$comps_fixed_part$ins
fi
}
function ble/complete/action/inherit-from {
local dst=$1 src=$2
local member srcfunc dstfunc
for member in initialize complete getg get-desc; do
srcfunc=ble/complete/action:$src/$member
dstfunc=ble/complete/action:$dst/$member
ble/is-function "$srcfunc" && eval "function $dstfunc { $srcfunc; }"
done
}
#------------------------------------------------------------------------------
# action:plain
#
function ble/complete/action:plain/initialize {
ble/complete/action/util/quote-insert
}
function ble/complete/action:plain/complete { :; }
# action:literal-substr
function ble/complete/action:literal-substr/initialize { :; }
function ble/complete/action:literal-substr/complete { :; }
# action:substr (equivalent to plain)
function ble/complete/action:substr/initialize {
ble/complete/action/util/quote-insert
}
function ble/complete/action:substr/complete { :; }
# action:literal-word
function ble/complete/action:literal-word/initialize { :; }
function ble/complete/action:literal-word/complete {
if [[ $comps_flags == *x* ]]; then
ble/complete/action/util/complete.addtail ','
else
ble/complete/action/util/complete.addtail ' '
fi
}
# action:word
#
# DATA ... 候補の説明として使用する文字列を指定します
#
function ble/complete/action:word/initialize {
ble/complete/action/util/quote-insert
}
function ble/complete/action:word/complete {
ble/complete/action/util/complete.close-quotation
ble/complete/action:literal-word/complete
}
function ble/complete/action:word/get-desc {
[[ $DATA ]] && desc=$DATA
}
# action:file
#
function ble/complete/action:file/initialize {
ble/complete/action/util/quote-insert
}
function ble/complete/action:file/complete {
if [[ -e $CAND || -h $CAND ]]; then
if [[ -d $CAND ]]; then
ble/complete/action/util/complete.mark-directory
else
ble/complete/action:word/complete
fi
fi
}
function ble/complete/action:file/init-menu-item {
ble/syntax/highlight/getg-from-filename "$CAND"
[[ $g ]] || ble/color/face2g filename_warning
if [[ :$comp_type: == *:vstat:* ]]; then
if [[ -h $CAND ]]; then
suffix='@'
elif [[ -d $CAND ]]; then
suffix='/'
elif [[ -x $CAND ]]; then
suffix='*'
fi
fi
}
_ble_complete_action_file_desc[_ble_attr_FILE_LINK]='symbolic link'
_ble_complete_action_file_desc[_ble_attr_FILE_ORPHAN]='symbolic link (orphan)'
_ble_complete_action_file_desc[_ble_attr_FILE_DIR]='directory'
_ble_complete_action_file_desc[_ble_attr_FILE_STICKY]='directory (sticky)'
_ble_complete_action_file_desc[_ble_attr_FILE_SETUID]='file (setuid)'
_ble_complete_action_file_desc[_ble_attr_FILE_SETGID]='file (setgid)'
_ble_complete_action_file_desc[_ble_attr_FILE_EXEC]='file (executable)'
_ble_complete_action_file_desc[_ble_attr_FILE_FILE]='file'
_ble_complete_action_file_desc[_ble_attr_FILE_CHR]='character device'
_ble_complete_action_file_desc[_ble_attr_FILE_FIFO]='named pipe'
_ble_complete_action_file_desc[_ble_attr_FILE_SOCK]='socket'
_ble_complete_action_file_desc[_ble_attr_FILE_BLK]='block device'
function ble/complete/action:file/get-desc {
local type; ble/syntax/highlight/filetype "$CAND"
desc=${_ble_complete_action_file_desc[type]:-'file (???)'}
}
# action:progcomp
#
# DATA ... compopt 互換のオプションをコロン区切りで指定します
#
function ble/complete/action:progcomp/initialize {
[[ $DATA == *:noquote:* ]] && return
# bash-completion には compopt -o nospace として、
# 自分でスペースを付加する補完関数がある。この時クォートすると問題。
[[ $DATA == *:nospace:* && $CAND == *' ' && ! -f $CAND ]] && return
ble/complete/action/util/quote-insert
}
function ble/complete/action:progcomp/complete {
if [[ $DATA == *:filenames:* ]]; then
ble/complete/action:file/complete
else
if [[ $DATA != *:no-mark-directories:* && -d $CAND ]]; then
ble/complete/action/util/complete.mark-directory
else
ble/complete/action:word/complete
fi
fi
[[ $DATA == *:nospace:* ]] && suffix=${suffix%' '}
}
function ble/complete/action:progcomp/init-menu-item {
if [[ $DATA == *:filenames:* ]]; then
ble/complete/action:file/init-menu-item
fi
}
function ble/complete/action:progcomp/get-desc {
if [[ $DATA == *:filenames:* ]]; then
ble/complete/action:file/get-desc
fi
}
# action:command
#
function ble/complete/action:command/initialize {
ble/complete/action/util/quote-insert
}
function ble/complete/action:command/complete {
if [[ -d $CAND ]]; then
ble/complete/action/util/complete.mark-directory
elif ! type "$CAND" &>/dev/null; then
# 関数名について縮約されたもので一意確定した時。
#
# Note: 関数名について縮約されている時、
# 本来は一意確定でなくても一意確定として此処に来ることがある。
# そのコマンドが存在していない時に、縮約されていると判定する。
#
if [[ $CAND == */ ]]; then
# 縮約されていると想定し続きの補完候補を出す。
insert_flags=${insert_flags}n
fi
else
ble/complete/action:word/complete
fi
}
function ble/complete/action:command/init-menu-item {
if [[ -d $CAND ]]; then
ble/color/face2g filename_directory
else
local type
if [[ $CAND != "$INSERT" ]]; then
ble/syntax/highlight/cmdtype "$CAND" "$INSERT"
else
# Note: ble/syntax/highlight/cmdtype はキャッシュ機能がついているが、
# キーワードに対して呼び出さない前提なのでキーワードを渡すと
# _ble_attr_ERR を返してしまう。
local type; ble/util/type type "$CAND"
ble/syntax/highlight/cmdtype1 "$type" "$CAND"
if [[ $CAND == */ ]] && ((type==_ble_attr_ERR)); then
type=_ble_attr_CMD_FUNCTION
fi
fi
ble/syntax/attr2g "$type"
fi
}
_ble_complete_action_command_desc[_ble_attr_CMD_BOLD]=builtin
_ble_complete_action_command_desc[_ble_attr_CMD_BUILTIN]=builtin
_ble_complete_action_command_desc[_ble_attr_CMD_ALIAS]=alias
_ble_complete_action_command_desc[_ble_attr_CMD_FUNCTION]=function
_ble_complete_action_command_desc[_ble_attr_CMD_FILE]=file
_ble_complete_action_command_desc[_ble_attr_KEYWORD]=keyword
_ble_complete_action_command_desc[_ble_attr_CMD_JOBS]=job
_ble_complete_action_command_desc[_ble_attr_ERR]='???'
_ble_complete_action_command_desc[_ble_attr_CMD_DIR]=directory
function ble/complete/action:command/get-desc {
if [[ -d $CAND ]]; then
desc=directory
else
local type; ble/util/type type "$CAND"
ble/syntax/highlight/cmdtype1 "$type" "$CAND"
if [[ $CAND == */ ]] && ((type==_ble_attr_ERR)); then
type=_ble_attr_CMD_FUNCTION
fi
desc=${_ble_complete_action_command_desc[type]:-'???'}
fi
}
# action:variable
#
# DATA ... 変数名の文脈を指定します。
# assignment braced word arithmetic の何れかです。
#
function ble/complete/action:variable/initialize { ble/complete/action/util/quote-insert; }
function ble/complete/action:variable/complete {
case $DATA in
(assignment)
# var= 等に於いて = を挿入
ble/complete/action/util/complete.addtail '=' ;;
(braced)
# ${var 等に於いて } を挿入
ble/complete/action/util/complete.addtail '}' ;;
(word) ble/complete/action:word/complete ;;
(arithmetic) ;; # do nothing
esac
}
function ble/complete/action:variable/init-menu-item {
ble/color/face2g syntax_varname
}
#==============================================================================
# source
## 関数 ble/complete/source/reduce-compv-for-ambiguous-match
## 曖昧補完の為に擬似的な COMPV と COMPS を生成・設定します。
## @var[in,out] COMPS COMPV
function ble/complete/source/reduce-compv-for-ambiguous-match {
[[ :$comp_type: == *:[amA]:* ]] || return 0
local comps=$COMPS compv=$COMPV
local comps_prefix= compv_prefix=
if [[ $comps_fixed ]]; then
comps_prefix=${comps::${comps_fixed%%:*}}
compv_prefix=${comps_fixed#*:}
compv=${COMPV:${#compv_prefix}}
fi
case $comps_flags in
(*S*) comps_prefix=$comps_prefix\' ;;
(*E*) comps_prefix=$comps_prefix\$\' ;;
(*D*) comps_prefix=$comps_prefix\" ;;
(*I*) comps_prefix=$comps_prefix\$\" ;;
esac
if [[ $compv && :$comp_type: == *:a:* ]]; then
compv=${compv::1}
ble/complete/string#escape-for-completion-context "$compv"
comps=$ret
else
compv= comps=
fi
COMPV=$compv_prefix$compv
COMPS=$comps_prefix$comps
}
## 関数 ble/complete/cand/yield ACTION CAND DATA
## @param[in] ACTION
## @param[in] CAND
## @param[in] DATA
## @var[in] COMP_PREFIX
function ble/complete/cand/yield {
local ACTION=$1 CAND=$2 DATA=$3
[[ $flag_force_fignore ]] && ! ble/complete/.fignore/filter "$CAND" && return
local PREFIX_LEN=0
[[ $CAND == "$COMP_PREFIX"* ]] && PREFIX_LEN=${#COMP_PREFIX}
local INSERT=$CAND
ble/complete/action:"$ACTION"/initialize || return "$?"
local icand
((icand=cand_count++))
cand_cand[icand]=$CAND
cand_word[icand]=$INSERT
cand_pack[icand]=$ACTION:${#CAND},${#INSERT},$PREFIX_LEN:$CAND$INSERT$DATA
}
function ble/complete/cand/yield-filenames {
local action=$1; shift
local rex_hidden=
[[ :$comp_type: != *:match-hidden:* ]] &&
rex_hidden=${COMPV:+'.{'${#COMPV}'}'}'(^|/)\.[^/]*$'
local cand
for cand; do
((cand_iloop++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148
[[ $rex_hidden && $cand =~ $rex_hidden ]] && continue
[[ $FIGNORE ]] && ! ble/complete/.fignore/filter "$cand" && continue
ble/complete/cand/yield "$action" "$cand"
done
}
_ble_complete_cand_varnames=(ACTION CAND INSERT DATA PREFIX_LEN)
## 関数 ble/complete/cand/unpack data
## @param[in] data
## ACTION:ncand,ninsert,PREFIX_LEN:$CAND$INSERT$DATA
## @var[out] ACTION CAND INSERT DATA PREFIX_LEN
function ble/complete/cand/unpack {
local pack=$1
ACTION=${pack%%:*} pack=${pack#*:}
local text=${pack#*:}
IFS=, eval 'pack=(${pack%%:*})'
CAND=${text::pack[0]}
INSERT=${text:pack[0]:pack[1]}
DATA=${text:pack[0]+pack[1]}
PREFIX_LEN=${pack[2]}
}
## 定義されている source
##
## source:wordlist
## source:command
## source:file
## source:dir
## source:argument
## source:variable
##
## source の実装
##
## 関数 ble/complete/source:$name args...
## @param[in] args...
## ble/syntax/completion-context/generate で設定されるユーザ定義の引数。
##
## @var[in] COMP1 COMP2 COMPS COMPV comp_type
## @var[in] comp_filter_type
## @var[out] COMP_PREFIX
## ble/complete/cand/yield で参照される一時変数。
##
# source:none
function ble/complete/source:none { return 0; }
# source:wordlist
#
# -r 指定された単語をエスケープせずにそのまま挿入する
# -W 補完完了時に空白を挿入しない
#
function ble/complete/source:wordlist {
[[ $comps_flags == *v* ]] || return 1
local COMPS=$COMPS COMPV=$COMPV
ble/complete/source/reduce-compv-for-ambiguous-match
[[ $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]}
# process options
local opt_raw= opt_noword=
while (($#)) && [[ $1 == -* ]]; do
local arg=$1; shift
case $arg in
(--) break ;;
(--*) ;; # ignore
(-*)
local i iN=${#arg}
for ((i=1;i<iN;i++)); do
case ${arg:i:1} in
(r) opt_raw=1 ;;
(W) opt_noword=1 ;;
(*) ;; # ignore
esac
done ;;
esac
done
local action=word
[[ $opt_noword ]] && action=substr
[[ $opt_raw ]] && action=literal-$action
local cand
for cand; do
[[ $cand == "$COMPV"* ]] && ble/complete/cand/yield "$action" "$cand"
done
}
# source:command
function ble/complete/source:command/.contract-by-slashes {
local slashes=${COMPV//[!'/']}
ble/bin/awk -F / -v baseNF="${#slashes}" '
function initialize_common() {
common_NF = NF;
for (i = 1; i <= NF; i++) common[i] = $i;
common_degeneracy = 1;
common0_NF = NF;
common0_str = $0;
}
function print_common(_, output) {
if (!common_NF) return;
if (common_degeneracy == 1) {
print common0_str;
common_NF = 0;
return;
}
output = common[1];
for (i = 2; i <= common_NF; i++)
output = output "/" common[i];
# Note:
# For candidates `a/b/c/1` and `a/b/c/2`, prints `a/b/c/`.
# For candidates `a/b/c` and `a/b/c/1`, prints `a/b/c` and `a/b/c/1`.
if (common_NF == common0_NF) print output;
print output "/";
common_NF = 0;
}
{
if (NF <= baseNF + 1) {
print_common();
print $0;
} else if (!common_NF) {
initialize_common();
} else {
n = common_NF < NF ? common_NF : NF;
for (i = baseNF + 1; i <= n; i++)
if (common[i] != $i) break;
matched_length = i - 1;
if (matched_length <= baseNF) {
print_common();
initialize_common();
} else {
common_NF = matched_length;
common_degeneracy++;
}
}
}
END { print_common(); }
'
}
## @fn ble/complete/source:command/gen.1
function ble/complete/source:command/gen.1 {
# Note #D1922: パス名コマンドの曖昧補完は compgen -c ではなく自前で処理する。
# ディレクトリ名に関しては ble/complete/source:command/gen の側で生成されるの
# でここでは生成しない。
if [[ $COMPV == */* && :$comp_type: == *:[maA]:* ]]; then
local ret
ble/complete/source:file/.construct-pathname-pattern "$COMPV"
ble/complete/util/eval-pathname-expansion "$ret"; (($?==148)) && return 148
ble/array#filter ret '[[ ! -d $1 && -x $1 ]]'
((${#ret[@]})) && printf '%s\n' "${ret[@]}"
local COMPS=$COMPS COMPV=$COMPV
ble/complete/source/reduce-compv-for-ambiguous-match
else
local COMPS=$COMPS COMPV=$COMPV
ble/complete/source/reduce-compv-for-ambiguous-match
# Note: cygwin では cyg,x86,i68 等で始まる場合にとても遅い。他の環境でも空
# の補完を実行すると遅くなる可能性がある。
local slow_compgen=
if [[ ! $COMPV ]]; then
slow_compgen=1
elif [[ $OSTYPE == cygwin* ]]; then
case $COMPV in
(?|cy*|x8*|i6*)
slow_compgen=1 ;;
esac
fi
# Note: 何故か compgen -A command はクォート除去が実行されない。compgen -A
# function はクォート除去が実行される。従って、compgen -A command には直
# 接 COMPV を渡し、compgen -A function には compv_quoted を渡す。
if [[ $slow_compgen ]]; then
shopt -q no_empty_cmd_completion && return 0
ble/util/conditional-sync \
'builtin compgen -c -- "$COMPV"' \
'! ble/complete/check-cancel' 128 progressive-weight
else
builtin compgen -c -- "$COMPV"
fi
fi
if [[ $COMPV == */* ]]; then
local q="'" Q="'\''"
local compv_quoted="'${COMPV//$q/$Q}'"
compgen -A function -- "$compv_quoted"
fi
}
function ble/complete/source:command/gen {
if [[ :$comp_type: != *:[amA]:* && $bleopt_complete_contract_function_names ]]; then
ble/complete/source:command/gen.1 |
ble/complete/source:command/.contract-by-slashes
else
ble/complete/source:command/gen.1
fi
# ディレクトリ名列挙 (/ 付きで生成する)
#
# Note: shopt -q autocd &>/dev/null かどうかに拘らず列挙する。
#
# Note: compgen -A directory (以下のコード参照) はバグがあって、
# bash-4.3 以降でクォート除去が実行されないので使わない (#D0714 #M0009)
#
# [[ :$comp_type: == *:a:* ]] && local COMPS=${COMPS::1} COMPV=${COMPV::1}
# compgen -A directory -S / -- "$compv_quoted"
#
if [[ $arg != *D* ]]; then
local ret
ble/complete/source:file/.construct-pathname-pattern "$COMPV"
ble/complete/util/eval-pathname-expansion "$ret/"
((${#ret[@]})) && printf '%s\n' "${ret[@]}"
fi
}
## ble/complete/source:command arg
## @param[in] arg
## arg に D が含まれている時、
## ディレクトリ名の列挙を抑制する事を表します。
function ble/complete/source:command {
[[ $comps_flags == *v* ]] || return 1
[[ ! $COMPV ]] && shopt -q no_empty_cmd_completion && return 1
[[ $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]}
local arg=$1
# Try progcomp by "complete -I"
if ((_ble_bash>=50000)); then
# first filter candidates to obtain effective 'cand_count'
ble/complete/candidates/filter:"$comp_filter_type"/filter
(($?==148)) && return 148
local old_cand_count=$cand_count
local comp_opts=:
ble/complete/source:argument/.generate-user-defined-completion initial; local ext=$?
((ext==148)) && return "$ext"
if ((ext==0)); then
# check 'cand_count' to see if any valid candidates are generated.
ble/complete/candidates/filter:"$comp_filter_type"/filter
(($?==148)) && return 148
((cand_count>old_cand_count)) && return "$ext"
fi
fi
local cand arr i=0
local compgen
ble/util/assign compgen 'ble/complete/source:command/gen "$arg"'
[[ $compgen ]] || return 1
ble/util/assign-array arr 'ble/bin/sort -u <<< "$compgen"' # 1 fork/exec
# keyword 判定用
local rex_keyword=
[[ $COMPS != $COMPV ]] &&
local rex_keyword='^(if|then|else|elif|fi|case|esac|for|select|while|until|do|done|function|time|[{}]|\[\[|coproc)$'
for cand in "${arr[@]}"; do
((i++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148
# workaround: 何故か compgen -c -- "$compv_quoted" で
# 厳密一致のディレクトリ名が混入するので削除する。
[[ $cand != */ && -d $cand ]] && ! type "$cand" &>/dev/null && continue
# #D1691 keyword は quote されている場合には無効
if [[ $rex_keyword && $cand =~ $rex_keyword ]]; then
local type; ble/util/type type "$cand"
((${#type[@]}==1)) && continue
fi
ble/complete/cand/yield command "$cand"
done
}
# source:file, source:dir
## 関数 ble/complete/util/eval-pathname-expansion pattern
## @var[out] ret
function ble/complete/util/eval-pathname-expansion {
local pattern=$1
local -a dtor=()
if [[ -o noglob ]]; then
set +f
ble/array#push dtor 'set -f'
fi
if ! shopt -q nullglob; then
shopt -s nullglob
ble/array#push dtor 'shopt -u nullglob'
fi
if ! shopt -q dotglob; then
shopt -s dotglob
ble/array#push dtor 'shopt -u dotglob'
else
# GLOBIGNORE に触ると設定が変わるので
# dotglob は明示的に保存・復元する。
ble/array#push dtor 'shopt -s dotglob'
fi
if ! shopt -q extglob; then
shopt -s extglob
ble/array#push dtor 'shopt -u extglob'
fi
if [[ :$comp_type: == *:i:* ]]; then
if ! shopt -q nocaseglob; then
shopt -s nocaseglob
ble/array#push dtor 'shopt -u nocaseglob'
fi
else
if shopt -q nocaseglob; then
shopt -u nocaseglob
ble/array#push dtor 'shopt -s nocaseglob'
fi
fi
if ble/util/is-cygwin-slow-glob "$pattern"; then # Note: #D1168
if shopt -q failglob &>/dev/null || shopt -q nullglob &>/dev/null; then
pattern=
else
set -f
ble/array#push dtor 'set +f'
fi
fi
if [[ $GLOBIGNORE ]]; then
local GLOBIGNORE_save=$GLOBIGNORE
GLOBIGNORE=
ble/array#push dtor 'GLOBIGNORE=$GLOBIGNORE_save'
fi
IFS= builtin eval "ret=(); ret=($pattern)" 2>/dev/null
ble/array#reverse dtor
ble/util/invoke-hook dtor
}
## 関数 ble/complete/source:file/.construct-ambiguous-pathname-pattern path
## 指定された path に対応する曖昧一致パターンを生成します。
## 例えば alpha/beta/gamma に対して a*/b*/g* でファイル名を生成します。
## 但し "../" や "./" については (".*.*/" や ".*/" 等に変換せず) そのままにします。
##
## @param[in] path
## @var[out] ret
##
## @remarks
## 当初は a*/b*/g* で生成して、後のフィルタに一致しないものの除外を一任していたが遅い。
## 従って、a*l*p*h*a*/b*e*t*a*/g*a*m*m*a* の様なパターンを生成する様に変更した。
##
function ble/complete/source:file/.construct-ambiguous-pathname-pattern {
local path=$1 fixlen=${2:-1}
local pattern= i=0 j
local names; ble/string#split names / "$1"
local name
for name in "${names[@]}"; do
((i++)) && pattern=$pattern/
if [[ $name == .. || $name == . && i -lt ${#names[@]} ]]; then
pattern=$pattern$name
elif [[ $name ]]; then
ble/string#quote-word "${name::fixlen}"
pattern=$pattern$ret*
for ((j=fixlen;j<${#name};j++)); do
ble/string#quote-word "${name:j:1}"
if [[ $pattern == *\* ]]; then
# * を extglob *([!ch]) に変換 #D1389
pattern=$pattern'([!'$ret'])'
fi
pattern=$pattern$ret*
done
fi
done
[[ $pattern ]] || pattern="*"
ret=$pattern
}
## 関数 ble/complete/source:file/.construct-pathname-pattern path
## @param[in] path
## @var[out] ret
function ble/complete/source:file/.construct-pathname-pattern {
local path=$1
if [[ :$comp_type: == *:a:* ]]; then
ble/complete/source:file/.construct-ambiguous-pathname-pattern "$path"; local pattern=$ret
elif [[ :$comp_type: == *:[mA]:* ]]; then
ble/complete/source:file/.construct-ambiguous-pathname-pattern "$path" 0; local pattern=$ret
else
ble/string#quote-word "$path"; local pattern=$ret*
fi
ret=$pattern
}
function ble/complete/source:file/.impl {
local opts=$1
[[ $comps_flags == *v* ]] || return 1
[[ :$comp_type: != *:[amA]:* && $COMPV =~ ^.+/ ]] && COMP_PREFIX=${BASH_REMATCH[0]}
# Note: compgen -A file/directory (以下のコード参照) はバグがあって、
# bash-4.0 と 4.1 でクォート除去が実行されないので使わない (#D0714 #M0009)
#
# local q="'" Q="'\''"; local compv_quoted="'${COMPV//$q/$Q}'"
# local candidates; ble/util/assign-array candidates 'compgen -A file -- "$compv_quoted"'
ble/complete/source:tilde; local ext=$?
((ext==148||ext==0)) && return "$ext"
# filenames
local action=file
local ret
ble/complete/source:file/.construct-pathname-pattern "$COMPV"
[[ :$opts: == *:directory:* ]] && ret=$ret/
ble/complete/util/eval-pathname-expansion "$ret"
local -a candidates=()
if [[ :$opts: == *:directory:* ]]; then
candidates=("${ret[@]%/}")
else
candidates=("${ret[@]}")
fi
local rex_hidden=
[[ :$comp_type: != *:match-hidden:* ]] &&
rex_hidden=${COMPV:+'.{'${#COMPV}'}'}'(^|/)\.[^/]*$'
local cand i=0
for cand in "${candidates[@]}"; do
((i++%bleopt_complete_polling_cycle==0)) && ble/complete/check-cancel && return 148
[[ $rex_hidden && $cand =~ $rex_hidden ]] && continue
[[ $FIGNORE ]] && ! ble/complete/.fignore/filter "$cand" && continue
ble/complete/cand/yield "$action" "$cand"
done
}
function ble/complete/source:file {
ble/complete/source:file/.impl
}
function ble/complete/source:dir {
ble/complete/source:file/.impl directory
}
# source:rhs
function ble/complete/source:rhs { ble/complete/source:file; }
#------------------------------------------------------------------------------
# source:tilde
function ble/complete/action:tilde/initialize {
# チルダは quote しない
CAND=${CAND#\~} ble/complete/action/util/quote-insert
INSERT=\~$INSERT
# Note: Windows 等でチルダ展開の無効なユーザー名があるのでチェック
local rex='^~[^/'\''"$`\!:]*$'; [[ $INSERT =~ $rex ]]
}
function ble/complete/action:tilde/complete {
ble/complete/action/util/complete.mark-directory
}
function ble/complete/action:tilde/init-menu-item {
ble/color/face2g filename_directory
}
function ble/complete/action:tilde/get-desc {
if [[ $CAND == '~+' ]]; then
desc='current directory (tilde expansion)'
elif [[ $CAND == '~-' ]]; then
desc='previous directory (tilde expansion)'
elif local rex='^~[0-9]$'; [[ $CAND =~ $rex ]]; then
desc='DIRSTACK directory (tilde expansion)'
else
desc='user directory (tilde expansion)'
fi
}
function ble/complete/source:tilde/.generate {
# generate user directories
local pattern=${COMPS#\~}