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 |
|
38 | log() {
|
39 | echo "$@" >&2
|
40 | }
|
41 |
|
42 | _debug() {
|
43 | log "$COMP_CWORD - ${COMP_WORDS[@]}"
|
44 | }
|
45 |
|
46 | readonly 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 |
|