OILS / devtools / completion.bash View on Github | oilshell.org

190 lines, 98 significant
1# devtools/completion.bash: bash (and OSH!) completion for TASK FILES.
2#
3# Task are simply shell functions:
4#
5# build-foo() {
6# rm foo; make foo
7# }
8#
9# run-task "$@" # dispatch to task; in devtools/run-task.sh
10
11# This script also supports completing unit tests files and
12# TestClass.testMethod names.
13#
14# $ test/unit.sh unit <TAB>
15# $ test/unit.sh unit frontend/args_test.py <TAB>
16#
17# TODO:
18#
19# - Remove dep on ~/git/oilshell/bash-completion/osh_completion, see below
20#
21# - Query the binary for more advanced completions? (e.g. flag completions)
22# Maybe it could a --completions flag.
23#
24# Most binaries will response with a exit code 1 in that case. But if it
25# prints a spec, then you could use that to find flags.
26
27# Note: Bash completion is bizarre.
28#
29# - Use -X '!*_test.py' to remove everything EXCEPT *_test.py. -G for glob
30# does NOT do what you want. This confusing and bizarrely undocumented.
31
32# Test for default distro completion:
33#
34# bash --norc --noprofile
35# . /etc/bash_completion
36# apt-get <TAB> -> You see actions.
37
38log() {
39 echo "$@" >&2
40}
41
42_debug() {
43 log "$COMP_CWORD - ${COMP_WORDS[@]}"
44}
45
46readonly THIS_DIR=$(dirname ${BASH_SOURCE[0]})
47
48_completion_py() {
49 "$THIS_DIR/completion.py" "$@"
50}
51
52# default completion
53# if $0 ends with .sh, then try scanning with completion.py
54# otherwise, do the default filename/dir completion
55_my_default_completion() {
56 # This seems be what the default completion is, for the -1, 0, and positive
57 # cases
58
59 case "$COMP_CWORD" in
60
61 # Fall back if there's nothing there
62 -1) ;;
63 # Fall back to complete a partial command ($0)
64 # NOTE: not getting this to happen?
65 0) ;;
66
67 *)
68 local cur="${COMP_WORDS[COMP_CWORD]}"
69 local script="${COMP_WORDS[0]}"
70
71 case $script in
72 # Special completion for run.sh/test.sh scripts. Auto is also supported.
73 # unit action, and then unit tests
74 # test.sh: new convention for test runner setting PYTHONPATH, etc.
75 *run.sh|*test.sh|*unit.sh|*Auto)
76 case "$COMP_CWORD" in
77 # Complete the action first
78 1)
79 local script="${COMP_WORDS[0]}"
80 local actions=$(_completion_py bash "$script")
81 COMPREPLY=( $(compgen -W "$actions" -- "$cur") )
82 return
83 ;;
84 # Complete *_test.py files
85 2)
86 local word1="${COMP_WORDS[1]}"
87 if test "$word1" = 'unit' || test "$word1" = 'py-unit'; then
88 # BUG: dirs don't have slashes here?
89 COMPREPLY=( $(compgen -A file -o plusdirs -X '!*_test.py' -- "$cur") )
90 return
91 fi
92 ;;
93 # Complete Class.testMethod within the foo_test.py file
94 3)
95 local word1="${COMP_WORDS[1]}"
96 if test "$word1" = 'unit' || test "$word1" = 'py-unit'; then
97 local test_file="${COMP_WORDS[2]}"
98 local tests=$(_completion_py pyunit "$test_file")
99 if test -z "$tests"; then
100 COMPREPLY=( NOTESTS )
101 else
102 COMPREPLY=( $(compgen -W "$tests" -- "$cur") )
103 fi
104 return
105 fi
106 ;;
107 esac
108 ;;
109
110 *.sh)
111 # For the first word, try to complete actions in shell scripts
112 case "$COMP_CWORD" in
113 1)
114 local actions=$(_completion_py bash "$script")
115 COMPREPLY=( $(compgen -W "$actions" -- "$cur") )
116 return
117 ;;
118 esac
119 ;;
120
121 *_test.py)
122 case "$COMP_CWORD" in
123 # Complete Class.testMethod within the foo_test.py file
124 1)
125 local test_file="${COMP_WORDS[0]}"
126 local tests=$(_completion_py pyunit "$test_file")
127 # Show a dummy error result, so we aren't confused by the
128 # directory name completion
129 if test -z "$tests"; then
130 COMPREPLY=( NOTESTS )
131 else
132 COMPREPLY=( $(compgen -W "$tests" -- "$cur") )
133 fi
134 return
135 ;;
136 esac
137 ;;
138 esac # script
139 ;;
140 esac # $COMP_CWORD
141
142 # Need this for for ./run.sh action <filename> There is an "if" in that clause.
143 test -n "$_comp_fallback" && "$_comp_fallback" "$@"
144}
145
146# global that is mutated
147_comp_fallback=''
148
149# _comp_fallback is invoked by my _my_default_completion, with the same 3 args
150# as a completion function, i.e. -- "$@".
151_maybe_set_comp_fallback() {
152 local _distro_script
153 if test -n "$BASH_VERSION"; then
154 # running under bash
155 _distro_script='/etc/bash_completion'
156 else
157 # running under OSH
158 _distro_script=~/git/oilshell/bash-completion/osh_completion
159 fi
160 local _distro_function=_completion_loader
161
162 if test -f $_distro_script; then
163 source $_distro_script
164 if test $(type -t $_distro_function) = 'function'; then
165 _comp_fallback=$_distro_function
166 fi
167 else
168 # log "Warning: $_distro_script not found; no completion fallback"
169 _comp_fallback=''
170 fi
171}
172
173_install_completion() {
174 # Fallback on distro completion so we don't clobber it.
175 _maybe_set_comp_fallback
176
177 # Fix: add "-o bashdefault" to fix completion of variable names (e.g. $HO ->
178 # HOME). When there is no completion produced by my function, bash will fall
179 # back on its defaults.
180 # -o filenames: it makes it so that directories get a trailing slash.
181 #
182 # Formula for completing a subset of filenames:
183 # 1) complete -o filenames ...
184 # 2) compgen -A file -o plusdirs -X '!*.sh'
185
186 complete -F _my_default_completion -o bashdefault -o filenames -D
187}
188
189_install_completion
190