1 | #!/bin/bash
|
2 | #
|
3 | # Demo of the bash completion API.
|
4 | #
|
5 | # It's used by core/completion_test.py, and you can run it manually.
|
6 | #
|
7 | # The reason we use unit tests is that some of the postprocessing in GNU
|
8 | # readline is untestable from the "outside".
|
9 | #
|
10 | # Usage:
|
11 | # source testdata/completion/osh-unit.bash
|
12 |
|
13 | # For testing if ls completion works AUTOMATICALLY
|
14 |
|
15 | complete -W 'one two' ls
|
16 |
|
17 | alias ll='ls -l'
|
18 | alias ll_classify='ll --classify' # a second level of alias expansion
|
19 | alias ll_trailing='ls -l ' # trailing space shouldn't make a difference
|
20 |
|
21 | alias ll_own_completion='ls -l'
|
22 | complete -W 'own words' ll_own_completion
|
23 |
|
24 |
|
25 | argv() {
|
26 | spec/bin/argv.py "$@"
|
27 | }
|
28 |
|
29 | # Complete a fixed set of words
|
30 | complete_mywords() {
|
31 | local first=$1
|
32 | local cur=$2
|
33 | local prev=$3
|
34 | for candidate in one two three bin; do
|
35 | if [[ $candidate == $cur* ]]; then
|
36 | COMPREPLY+=("$candidate")
|
37 | fi
|
38 | done
|
39 | }
|
40 |
|
41 | foo() { argv foo "$@"; }
|
42 | complete_foo() {
|
43 | local first=$1
|
44 | local cur=$2
|
45 | local prev=$3
|
46 |
|
47 | echo
|
48 | argv args "$@"
|
49 |
|
50 | # NOTE: If you pass foo 'hi', then the words are quoted! This is a mistake!
|
51 | # Also true for \hi and "hi".
|
52 | # If you pass foo $x, you get literally $x as a word! It's BEFORE
|
53 | # evaluation rather than AFTER evaluation!
|
54 | #
|
55 | # This is asking the completion function to do something impossible!!!
|
56 |
|
57 | argv COMP_WORDS "${COMP_WORDS[@]}"
|
58 | argv COMP_CWORD "${COMP_CWORD}"
|
59 | argv COMP_LINE "${COMP_LINE}"
|
60 | # Somehow completion doesn't trigger in the middle of a word, so this is
|
61 | # always equal to ${#COMP_LINE} ?
|
62 | argv COMP_POINT "${COMP_POINT}"
|
63 |
|
64 | # This value is used in main bash_completion script.
|
65 |
|
66 | argv source "${BASH_SOURCE[@]}"
|
67 | argv 'source[0]' "${BASH_SOURCE[0]}"
|
68 |
|
69 | # Test for prefix
|
70 | # bin is a dir
|
71 | for candidate in one two three bin; do
|
72 | if [[ $candidate == $cur* ]]; then
|
73 | COMPREPLY+=("$candidate")
|
74 | fi
|
75 | done
|
76 | }
|
77 | # dirnames: add dirs if nothing matches
|
78 | # plusdirs: always add dirs
|
79 | # filenames: adds trailing slash if it is a directory
|
80 | complete -F complete_foo -o dirnames -o filenames foo
|
81 | complete -F complete_foo -o nospace foo2
|
82 |
|
83 | complete_filedir() {
|
84 | local first=$1
|
85 | local cur=$2
|
86 | local prev=$3
|
87 | COMPREPLY=( $( compgen -d "$cur" ) )
|
88 | }
|
89 | # from _filedir
|
90 | complete -F complete_filedir filedir
|
91 |
|
92 | complete_bug() {
|
93 | # Regression for issue where readline swallows SystemExit.
|
94 | comsub=$(echo comsub)
|
95 |
|
96 | COMPREPLY=(one two three $comsub)
|
97 | }
|
98 | # isolated bug
|
99 | complete -F complete_bug bug
|
100 |
|
101 | optdemo() { argv "$@"; }
|
102 | complete_optdemo() {
|
103 | local first=$1
|
104 | local cur=$2
|
105 | local prev=$3
|
106 |
|
107 | # Turn this off DYNAMICALLY, so we WILL get a space.
|
108 | # TODO: Add unit tests for this case. I only tested it manually.
|
109 | compopt +o nospace
|
110 |
|
111 | # -o nospace doesn't work here, but it's accepted!
|
112 | COMPREPLY=( $( compgen -o nospace -d "$cur" ) )
|
113 | }
|
114 | # Test how the options work. git uses nospace.
|
115 | complete -F complete_optdemo -o nospace optdemo
|
116 |
|
117 | optdynamic() { argv optdynamic "$@"; }
|
118 | complete_optdynamic() {
|
119 | local first=$1
|
120 | local cur=$2
|
121 | local prev=$3
|
122 |
|
123 | # Suppress space on anything that starts with b
|
124 | if [[ "$cur" == b* ]]; then
|
125 | compopt -o nospace
|
126 | fi
|
127 | COMPREPLY=( $( compgen -A file "$cur" ) )
|
128 | }
|
129 | complete -F complete_optdynamic optdynamic
|
130 |
|
131 | files_func() { argv "$@"; }
|
132 | complete_files_func() {
|
133 | local first=$1
|
134 | local cur=$2
|
135 | local prev=$3
|
136 |
|
137 | # This MESSES up the trailing slashes. Need -o filenames.
|
138 | COMPREPLY=( $( compgen -A file "$cur" ) )
|
139 | }
|
140 | # everything else completes files
|
141 | #complete -D -A file
|
142 | complete -F complete_files_func files_func # messes up trailing slashes
|
143 |
|
144 | # Check trailing backslashes for the 'fileuser' command
|
145 | # Hm somehow it knows to distinguish. Gah.
|
146 | fu_builtin() { argv "$@"; }
|
147 | complete -A user -A file fu_builtin
|
148 |
|
149 | # This behaves the same way as above because of -o filenames.
|
150 | # If you have both a dir and a user named root, it gets a trailing slash, which
|
151 | # makes sense.
|
152 | fu_func() { argv "$@"; }
|
153 | complete_users_files() {
|
154 | local first=$1
|
155 | local cur=$2
|
156 |
|
157 | COMPREPLY=( $( compgen -A user -A file "$cur" ) )
|
158 | }
|
159 | complete -F complete_users_files -o filenames fu_func
|
160 |
|
161 | complete_op_chars() {
|
162 | local first=$1
|
163 | local cur=$2
|
164 |
|
165 | for candidate in '$$$' '(((' '```' ';;;'; do
|
166 | if [[ $candidate == $cur* ]]; then
|
167 | COMPREPLY+=("$candidate")
|
168 | fi
|
169 | done
|
170 | }
|
171 |
|
172 | # Bash does NOT quote these! So they do not get sent into commands. It is
|
173 | # conflating shell syntax and tool syntax.
|
174 | op_chars() { argv "$@"; }
|
175 | complete -F complete_op_chars op_chars
|
176 |
|
177 | # Aha but this does the right thing! I think OSH should do this all the time!
|
178 | # User-defined functions shouldn't be responsible for quoting.
|
179 | op_chars_filenames() { argv "$@"; }
|
180 | complete -F complete_op_chars -o filenames op_chars_filenames
|
181 |
|
182 | # This shows that x is evaluated at RUNTIME, not at registration time!
|
183 | W_runtime() { argv "$@"; }
|
184 | complete -W 'foo $x $(echo --$x)' W_runtime
|
185 |
|
186 | W_runtime_error() { argv "$@"; }
|
187 | complete -W 'foo $(( 1 / 0 )) bar' W_runtime_error
|
188 |
|
189 | F_runtime_error() { argv "$@"; }
|
190 | complete_F_runtime_error() {
|
191 | COMPREPLY=( foo bar )
|
192 | COMPREPLY+=( $(( 1 / 0 )) ) # FATAL, but we still have candidates
|
193 | }
|
194 | complete -F complete_F_runtime_error F_runtime_error
|
195 |
|
196 | unset_compreply() { argv "$@"; }
|
197 | complete_unset_compreply() {
|
198 | unset COMPREPLY
|
199 | }
|
200 | complete -F complete_unset_compreply unset_compreply
|
201 |
|
202 | badexit() { argv "$@"; }
|
203 | complete_badexit() {
|
204 | COMPREPLY=(one two three)
|
205 | exit 42
|
206 | }
|
207 | complete -F complete_badexit badexit
|
208 |
|
209 | #
|
210 | # Test out the "124" protocol for lazy loading of completions.
|
211 | #
|
212 |
|
213 | # https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html
|
214 | _completion_loader()
|
215 | {
|
216 | # If the file exists, we will return 124 and reload it.
|
217 | # TODO: It would be nice to have a debug_f builtin to trace this!
|
218 | . "testdata/completion/${1}_completion.bash" >/dev/null 2>&1 && return 124
|
219 | }
|
220 | complete -D -F _completion_loader
|
221 |
|
222 | #
|
223 | # Unit tests use this
|
224 | #
|
225 |
|
226 | # For testing interactively
|
227 | flagX() { argv "$@"; }
|
228 | flagX_bang() { argv "$@"; }
|
229 | flagX_prefix() { argv "$@"; }
|
230 | prefix_plusdirs() { argv "$@"; }
|
231 | flagX_plusdirs() { argv "$@"; }
|
232 | prefix_dirnames() { argv "$@"; }
|
233 |
|
234 | complete -F complete_mywords mywords
|
235 | complete -F complete_mywords -o nospace mywords_nospace
|
236 |
|
237 | # This REMOVES two of them. 'three' should not be removed
|
238 | # since it's not an exact match.
|
239 | # Hm filtering comes BEFORE the prefix.
|
240 | complete -X '@(two|bin|thre)' -F complete_mywords flagX
|
241 |
|
242 | # Silly negation syntax
|
243 | complete -X '!@(two|bin)' -F complete_mywords flagX_bang
|
244 |
|
245 | complete -P __ -X '@(two|bin|thre)' -F complete_mywords flagX_prefix
|
246 |
|
247 | complete -P __ -o plusdirs -F complete_mywords prefix_plusdirs
|
248 |
|
249 | # Filter out bin, is it added back? Yes, it appears to work.
|
250 | complete -X '@(two|bin)' -o plusdirs -F complete_mywords flagX_plusdirs
|
251 |
|
252 | complete -P __ -o dirnames prefix_dirnames
|
253 |
|