OILS / testdata / completion / osh-unit.bash View on Github | oilshell.org

253 lines, 142 significant
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
15complete -W 'one two' ls
16
17alias ll='ls -l'
18alias ll_classify='ll --classify' # a second level of alias expansion
19alias ll_trailing='ls -l ' # trailing space shouldn't make a difference
20
21alias ll_own_completion='ls -l'
22complete -W 'own words' ll_own_completion
23
24
25argv() {
26 spec/bin/argv.py "$@"
27}
28
29# Complete a fixed set of words
30complete_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
41foo() { argv foo "$@"; }
42complete_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
80complete -F complete_foo -o dirnames -o filenames foo
81complete -F complete_foo -o nospace foo2
82
83complete_filedir() {
84 local first=$1
85 local cur=$2
86 local prev=$3
87 COMPREPLY=( $( compgen -d "$cur" ) )
88}
89# from _filedir
90complete -F complete_filedir filedir
91
92complete_bug() {
93 # Regression for issue where readline swallows SystemExit.
94 comsub=$(echo comsub)
95
96 COMPREPLY=(one two three $comsub)
97}
98# isolated bug
99complete -F complete_bug bug
100
101optdemo() { argv "$@"; }
102complete_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.
115complete -F complete_optdemo -o nospace optdemo
116
117optdynamic() { argv optdynamic "$@"; }
118complete_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}
129complete -F complete_optdynamic optdynamic
130
131files_func() { argv "$@"; }
132complete_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
142complete -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.
146fu_builtin() { argv "$@"; }
147complete -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.
152fu_func() { argv "$@"; }
153complete_users_files() {
154 local first=$1
155 local cur=$2
156
157 COMPREPLY=( $( compgen -A user -A file "$cur" ) )
158}
159complete -F complete_users_files -o filenames fu_func
160
161complete_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.
174op_chars() { argv "$@"; }
175complete -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.
179op_chars_filenames() { argv "$@"; }
180complete -F complete_op_chars -o filenames op_chars_filenames
181
182# This shows that x is evaluated at RUNTIME, not at registration time!
183W_runtime() { argv "$@"; }
184complete -W 'foo $x $(echo --$x)' W_runtime
185
186W_runtime_error() { argv "$@"; }
187complete -W 'foo $(( 1 / 0 )) bar' W_runtime_error
188
189F_runtime_error() { argv "$@"; }
190complete_F_runtime_error() {
191 COMPREPLY=( foo bar )
192 COMPREPLY+=( $(( 1 / 0 )) ) # FATAL, but we still have candidates
193}
194complete -F complete_F_runtime_error F_runtime_error
195
196unset_compreply() { argv "$@"; }
197complete_unset_compreply() {
198 unset COMPREPLY
199}
200complete -F complete_unset_compreply unset_compreply
201
202badexit() { argv "$@"; }
203complete_badexit() {
204 COMPREPLY=(one two three)
205 exit 42
206}
207complete -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}
220complete -D -F _completion_loader
221
222#
223# Unit tests use this
224#
225
226# For testing interactively
227flagX() { argv "$@"; }
228flagX_bang() { argv "$@"; }
229flagX_prefix() { argv "$@"; }
230prefix_plusdirs() { argv "$@"; }
231flagX_plusdirs() { argv "$@"; }
232prefix_dirnames() { argv "$@"; }
233
234complete -F complete_mywords mywords
235complete -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.
240complete -X '@(two|bin|thre)' -F complete_mywords flagX
241
242# Silly negation syntax
243complete -X '!@(two|bin)' -F complete_mywords flagX_bang
244
245complete -P __ -X '@(two|bin|thre)' -F complete_mywords flagX_prefix
246
247complete -P __ -o plusdirs -F complete_mywords prefix_plusdirs
248
249# Filter out bin, is it added back? Yes, it appears to work.
250complete -X '@(two|bin)' -o plusdirs -F complete_mywords flagX_plusdirs
251
252complete -P __ -o dirnames prefix_dirnames
253