OILS / testdata / completion / quoting.bash View on Github | oilshell.org

262 lines, 123 significant
1# Test bash and OSH handling of spaces
2
3# This works, you have to escape it FIRST.
4
5# So COMPREPLY takes SHELL-ESCAPED strings
6#
7# bash does NOT quote COMPREPLY -- it expects it to be already quoted.
8#
9# But YSH might do so, because it's a better interface.
10#
11# shopt --set ysh_quotes_compreply
12#
13# If the user plugin doesn't quote it, then the shell is responsible for it.
14
15# This is like 'printf %q' 'cha spaces'
16#declare -a commands=( cherry checkout 'cha\ spaces')
17
18# Test apostrophe
19#
20# Newline works! It's because printf %q works.
21
22# The $ gets quoted
23# BUG: bash shows \$file_not_var as a completion candidate, but you can't select it
24#
25# - If you type $, you get $'one\ntwo'
26# - If you type \$, you get $'one\ntwo' as well
27# This is probably GNU readline evaluation
28#
29# This is why YSH distinguishes between completing the shell language, and
30# completing a command arg. The latter must be shell-quoted.
31
32declare -a commands=(
33 cherry checkout
34 'ch with space' # you have to type 'ch\ ' to select this completion
35 "can't" # apostrophe
36 $'one\ntwo' # newline
37 '$file_not_var'
38 $'mu \u03bc \u4e09 \U0001f618 unicode'
39)
40
41# This has problems because 'check' is a prefix of two things.
42# You might need to add quotes
43#declare -a commands=( cherry checkout 'check\ spaces' )
44
45__printf_q() {
46 #argv "$@"
47
48 local cur=$2
49
50 local -a results
51
52 # Literal spaces don't work, you have to escape them beforehand
53 for cmd in "${commands[@]}"; do
54 case $cmd in
55 $cur*)
56
57 # So COMPREPLY is a literal list of SHELL strings, not string argv
58 # words
59
60 #local quoted=$(printf %q "$cmd")
61
62 # More efficient version
63 local quoted
64 printf -v quoted %q "$cmd"
65
66 # This extra space isn't treated as part of the word. But it does
67 # result in an extra space.
68 #results+=( "$quoted " )
69
70 results+=( "$quoted" )
71
72 ;;
73 esac
74 done
75
76 COMPREPLY=( "${results[@]}" )
77}
78
79# This works too
80
81__sq() {
82 # Important: cur does NOT include the single quote
83 local cur=$2
84
85 local -a results
86
87 for cmd in "${commands[@]}"; do
88 case $cmd in
89 $cur*)
90 # BUG when there's a single quote!
91 local quoted="'$cmd'"
92 #local quoted="$cmd"
93 results+=( "$quoted" )
94 ;;
95 esac
96 done
97
98 COMPREPLY=( "${results[@]}" )
99}
100
101# TODO: demonstrate how to get it from an external process
102# Well I think the easiest thing is obviously to implement %q on their side,
103# and '\n'
104
105argv() {
106 python3 -c 'import sys; print(sys.argv[1:])' "$@"
107}
108
109pq-argv() {
110 argv "$@"
111}
112
113sq-argv() {
114 argv "$@"
115}
116
117w1-argv() {
118 argv "$@"
119}
120
121w2-argv() {
122 argv "$@"
123}
124
125c-argv() {
126 argv "$@"
127}
128
129cw-argv() {
130 argv "$@"
131}
132
133q2-argv() {
134 argv "$@"
135}
136
137q3-argv() {
138 argv "$@"
139}
140
141v-argv() {
142 argv "$@"
143}
144
145complete -F __printf_q pq-argv
146complete -F __sq sq-argv
147
148# Hm this doesn't work. It comes across as one candidate.
149# But it doesn't get shell escaping
150#complete -W 'word\ with\ spaces w2' w-argv
151
152# It comes across as one candidate
153complete -W "'word with spaces' w2" w1-argv
154
155# This works! I think there is a double-eval
156complete -W "'word\ with\ spaces' w2" w2-argv
157
158print-comps() {
159 local cur=$2
160
161 for cmd in "${commands[@]}"; do
162 case $cmd in
163 $cur*)
164 # More efficient version
165 local quoted
166 printf -v quoted %q "$cmd"
167 echo "$quoted"
168 ;;
169 esac
170 done
171}
172
173complete -C print-comps c-argv
174
175#
176# Try to figure out what -W does
177#
178
179# This doesn't work
180complete -W '$(print-comps)' cw-argv
181
182# Doesn't work either
183#complete -W "$(print-comps)" cw-argv
184
185print-comps-q2() {
186 print-comps | while read -r line; do
187 printf '%q\n' "$line"
188 done
189}
190
191# This works except you have to press $ for $'one\ntwo'
192complete -W "$(print-comps-q2)" q2-argv
193
194# -W is first split by IFS, and then each word is evaluated?
195
196print-comps-q3() {
197 ### Complex alternative to printf %q that also works
198
199 print-comps | while read -r line; do
200 # This is wrong
201 #echo "'$line'"
202
203 # replace ' --> '\''
204 echo "'${line//"'"/"'\\''"}'"
205 done
206}
207
208test-words() {
209 echo "$(print-comps)"
210 echo
211 echo "$(print-comps-q2)"
212 echo
213 echo "$(print-comps-q3)"
214 echo
215
216 echo 'Unquoted command sub, with word splitting'
217 echo
218
219 echo $(print-comps-q2)
220 echo
221
222 echo $(print-comps-q3)
223 echo
224}
225
226# This works except you have to press $ for $'one\ntwo'
227complete -W "$(print-comps-q3)" q3-argv
228
229
230print-vars-with-dollar() {
231 local prefix=$1
232 compgen -A variable "$prefix" | while read -r line; do
233 echo '$'$line
234 done
235}
236
237__complete-vars() {
238 #argv "$@"
239 local cur=$2
240
241 local -a results
242
243 case $cur in
244 '$'*)
245 local prefix=${cur:1}
246 #echo "prefix=$prefix"
247
248 # Variables that start with prefix
249 COMPREPLY=( $(print-vars-with-dollar "$prefix") )
250 ;;
251 esac
252}
253
254complete -F __complete-vars v-argv
255
256
257# For testing print-comps
258
259if test "$(basename -- $0)" = 'quoting.bash'; then
260 "$@"
261fi
262