UNPKG

16.2 kBapplication/x-shView Raw
1#!/usr/bin/env bash
2
3#
4# Bash completion generated for 'suman' at Tue Mar 28 2017 15:07:37 GMT-0700 (PDT).
5#
6# The original template lives here:
7# https://github.com/trentm/node-dashdash/blob/master/etc/dashdash.bash_completion.in
8#
9
10##
11## This is a Bash completion file for the 'suman' command. You can install
12## with either:
13##
14## cp FILE /usr/local/etc/bash_completion.d/suman # Mac
15## cp FILE /etc/bash_completion.d/suman # Linux
16##
17## or:
18##
19## cp FILE > ~/.suman.completion
20## echo "source ~/.suman.completion" >> ~/.bashrc
21##
22
23
24#
25# Copyright 2016 Trent Mick
26# Copyright 2016 Joyent, Inc.
27#
28#
29# A generic Bash completion driver script.
30#
31# This is meant to provide a re-usable chunk of Bash to use for
32# "etc/bash_completion.d/" files for individual tools. Only the "Configuration"
33# section with tool-specific info need differ. Features:
34#
35# - support for short and long opts
36# - support for knowing which options take arguments
37# - support for subcommands (e.g. 'git log <TAB>' to show just options for the
38# log subcommand)
39# - does the right thing with "--" to stop options
40# - custom optarg and arg types for custom completions
41# - (TODO) support for shells other than Bash (tcsh, zsh, fish?, etc.)
42#
43#
44# Examples/design:
45#
46# 1. Bash "default" completion. By default Bash's 'complete -o default' is
47# enabled. That means when there are no completions (e.g. if no opts match
48# the current word), then you'll get Bash's default completion. Most notably
49# that means you get filename completion. E.g.:
50# $ tool ./<TAB>
51# $ tool READ<TAB>
52#
53# 2. all opts and subcmds:
54# $ tool <TAB>
55# $ tool -v <TAB> # assuming '-v' doesn't take an arg
56# $ tool -<TAB> # matching opts
57# $ git lo<TAB> # matching subcmds
58#
59# Long opt completions are given *without* the '=', i.e. we prefer space
60# separated because that's easier for good completions.
61#
62# 3. long opt arg with '='
63# $ tool --file=<TAB>
64# $ tool --file=./d<TAB>
65# We maintain the "--file=" prefix. Limitation: With the attached prefix
66# the 'complete -o filenames' doesn't know to do dirname '/' suffixing. Meh.
67#
68# 4. envvars:
69# $ tool $<TAB>
70# $ tool $P<TAB>
71# Limitation: Currently only getting exported vars, so we miss "PS1" and
72# others.
73#
74# 5. Defer to other completion in a subshell:
75# $ tool --file $(cat ./<TAB>
76# We get this from 'complete -o default ...'.
77#
78# 6. Custom completion types from a provided bash function.
79# $ tool --profile <TAB> # complete available "profiles"
80#
81#
82# Dev Notes:
83# - compgen notes, from http://unix.stackexchange.com/questions/151118/understand-compgen-builtin-command
84# - https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html
85#
86
87
88# Debugging this completion:
89# 1. Uncomment the "_suman_log_file=..." line.
90# 2. 'tail -f /var/tmp/dashdash-completion.log' in one terminal.
91# 3. Re-source this bash completion file.
92#_suman_log=/var/tmp/dashdash-completion.log
93
94function _suman_completer {
95
96 # ---- cmd definition
97
98 local cmd_shortopts="-a -b -d -f -h -p -r -s -t -v -w"
99local cmd_longopts="--all --allow-reuse-image --append-match-all --append-match-any --append-match-none --babel-register --bail --cfg --cnvt --completion --concurrency --config --convert --coverage --create --cwd-is-root --cwd-is-tfd --daemon --dest --diagnostics --errors-only --fast --ff --fforce --force --force-cwd-root --force-cwd-to-be-project-root --force-cwd-to-test-file-dir --fst --full-stack-traces --groups --help --home --ignore-break --ignore-uncaught-exceptions --ignore-unhandled-rejections --inherit-stdio --init --install-globals --interactive --iue --iur --match-all --match-any --match-none --max-depth --no-allow-reuse-image --no-babel-register --no-color --no-colors --no-coverage-report --no-report --no-run --no-runner-lock --no-stream-to-console --no-stream-to-file --no-tables --no-transpile --no-use-babel-register --no-use-container --nt --pipe --postinstall --processes --procs --rand --random --recursive --remove-babel --repair --reporter-paths --reporters --rm-babel --rnr --runner --runner-lock --safe --server --shd --silent --sort-by-millis --src --stdout-silent --suman-helpers-dir --tail --touch --transpile --uninstall --uninstall-babel --use-babel --use-babel-register --use-container --use-istanbul --use-server --use-tap --use-tap-output --verbosity --version --vn --watch"
100local cmd_optargs="--append-match-all=arrayOfString --append-match-any=arrayOfString --append-match-none=arrayOfString --cfg=string --concurrency=integer --config=string --create=arrayOfString --dest=string --match-all=arrayOfString --match-any=arrayOfString --match-none=arrayOfString --max-depth=integer --processes=integer --procs=integer --reporter-paths=arrayOfString --reporters=arrayOfString --shd=string --src=string --suman-helpers-dir=string --verbosity=integer -v=integer"
101
102
103 # ---- locals
104
105 declare -a argv
106
107
108 # ---- support functions
109
110 function trace {
111 [[ -n "$_suman_log" ]] && echo "$*" >&2
112 }
113
114 function _dashdash_complete {
115 local idx context
116 idx=$1
117 context=$2
118
119 local shortopts longopts optargs subcmds allsubcmds argtypes
120 shortopts="$(eval "echo \${cmd${context}_shortopts}")"
121 longopts="$(eval "echo \${cmd${context}_longopts}")"
122 optargs="$(eval "echo \${cmd${context}_optargs}")"
123 subcmds="$(eval "echo \${cmd${context}_subcmds}")"
124 allsubcmds="$(eval "echo \${cmd${context}_allsubcmds}")"
125 IFS=', ' read -r -a argtypes <<< "$(eval "echo \${cmd${context}_argtypes}")"
126
127 trace ""
128 trace "_dashdash_complete(idx=$idx, context=$context)"
129 trace " shortopts: $shortopts"
130 trace " longopts: $longopts"
131 trace " optargs: $optargs"
132 trace " subcmds: $subcmds"
133 trace " allsubcmds: $allsubcmds"
134
135 # Get 'state' of option parsing at this COMP_POINT.
136 # Copying "dashdash.js#parse()" behaviour here.
137 local state=
138 local nargs=0
139 local i=$idx
140 local argtype
141 local optname
142 local prefix
143 local word
144 local dashdashseen=
145 while [[ $i -lt $len && $i -le $COMP_CWORD ]]; do
146 argtype=
147 optname=
148 prefix=
149 word=
150
151 arg=${argv[$i]}
152 trace " consider argv[$i]: '$arg'"
153
154 if [[ "$arg" == "--" && $i -lt $COMP_CWORD ]]; then
155 trace " dashdash seen"
156 dashdashseen=yes
157 state=arg
158 word=$arg
159 elif [[ -z "$dashdashseen" && "${arg:0:2}" == "--" ]]; then
160 arg=${arg:2}
161 if [[ "$arg" == *"="* ]]; then
162 optname=${arg%%=*}
163 val=${arg##*=}
164 trace " long opt: optname='$optname' val='$val'"
165 state=arg
166 argtype=$(echo "$optargs" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
167 word=$val
168 prefix="--$optname="
169 else
170 optname=$arg
171 val=
172 trace " long opt: optname='$optname'"
173 state=longopt
174 word=--$optname
175
176 if [[ "$optargs" == *"-$optname="* && $i -lt $COMP_CWORD ]]; then
177 i=$(( $i + 1 ))
178 state=arg
179 argtype=$(echo "$optargs" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
180 word=${argv[$i]}
181 trace " takes arg (consume argv[$i], word='$word')"
182 fi
183 fi
184 elif [[ -z "$dashdashseen" && "${arg:0:1}" == "-" ]]; then
185 trace " short opt group"
186 state=shortopt
187 word=$arg
188
189 local j=1
190 while [[ $j -lt ${#arg} ]]; do
191 optname=${arg:$j:1}
192 trace " consider index $j: optname '$optname'"
193
194 if [[ "$optargs" == *"-$optname="* ]]; then
195 argtype=$(echo "$optargs" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
196 if [[ $(( $j + 1 )) -lt ${#arg} ]]; then
197 state=arg
198 word=${arg:$(( $j + 1 ))}
199 trace " takes arg (rest of this arg, word='$word', argtype='$argtype')"
200 elif [[ $i -lt $COMP_CWORD ]]; then
201 state=arg
202 i=$(( $i + 1 ))
203 word=${argv[$i]}
204 trace " takes arg (word='$word', argtype='$argtype')"
205 fi
206 break
207 fi
208
209 j=$(( $j + 1 ))
210 done
211 elif [[ $i -lt $COMP_CWORD && -n "$arg" ]] && $(echo "$allsubcmds" | grep -w "$arg" >/dev/null); then
212 trace " complete subcmd: recurse _dashdash_complete"
213 _dashdash_complete $(( $i + 1 )) "${context}__${arg/-/_}"
214 return
215 else
216 trace " not an opt or a complete subcmd"
217 state=arg
218 word=$arg
219 nargs=$(( $nargs + 1 ))
220 if [[ ${#argtypes[@]} -gt 0 ]]; then
221 argtype="${argtypes[$(( $nargs - 1 ))]}"
222 if [[ -z "$argtype" ]]; then
223 # If we have more args than argtypes, we use the
224 # last type.
225 argtype="${argtypes[@]: -1:1}"
226 fi
227 fi
228 fi
229
230 trace " state=$state prefix='$prefix' word='$word'"
231 i=$(( $i + 1 ))
232 done
233
234 trace " parsed: state=$state optname='$optname' argtype='$argtype' prefix='$prefix' word='$word' dashdashseen=$dashdashseen"
235 local compgen_opts=
236 if [[ -n "$prefix" ]]; then
237 compgen_opts="$compgen_opts -P $prefix"
238 fi
239
240 case $state in
241 shortopt)
242 compgen $compgen_opts -W "$shortopts $longopts" -- "$word"
243 ;;
244 longopt)
245 compgen $compgen_opts -W "$longopts" -- "$word"
246 ;;
247 arg)
248 # If we don't know what completion to do, then emit nothing. We
249 # expect that we are running with:
250 # complete -o default ...
251 # where "default" means: "Use Readline's default completion if
252 # the compspec generates no matches." This gives us the good filename
253 # completion, completion in subshells/backticks.
254 #
255 # We cannot support an argtype="directory" because
256 # compgen -S '/' -A directory -- "$word"
257 # doesn't give a satisfying result. It doesn't stop at the trailing '/'
258 # so you cannot descend into dirs.
259 if [[ "${word:0:1}" == '$' ]]; then
260 # By default, Bash will complete '$<TAB>' to all envvars. Apparently
261 # 'complete -o default' does *not* give us that. The following
262 # gets *close* to the same completions: '-A export' misses envvars
263 # like "PS1".
264 trace " completing envvars"
265 compgen $compgen_opts -P '$' -A export -- "${word:1}"
266 elif [[ -z "$argtype" ]]; then
267 # Only include opts in completions if $word is not empty.
268 # This is to avoid completing the leading '-', which foils
269 # using 'default' completion.
270 if [[ -n "$dashdashseen" ]]; then
271 trace " completing subcmds, if any (no argtype, dashdash seen)"
272 compgen $compgen_opts -W "$subcmds" -- "$word"
273 elif [[ -z "$word" ]]; then
274 trace " completing subcmds, if any (no argtype, empty word)"
275 compgen $compgen_opts -W "$subcmds" -- "$word"
276 else
277 trace " completing opts & subcmds (no argtype)"
278 compgen $compgen_opts -W "$shortopts $longopts $subcmds" -- "$word"
279 fi
280 elif [[ $argtype == "none" ]]; then
281 # We want *no* completions, i.e. some way to get the active
282 # 'complete -o default' to not do filename completion.
283 trace " completing 'none' (hack to imply no completions)"
284 echo "##-no-completion- -results-##"
285 elif [[ $argtype == "file" ]]; then
286 # 'complete -o default' gives the best filename completion, at least
287 # on Mac.
288 trace " completing 'file' (let 'complete -o default' handle it)"
289 echo ""
290 elif ! type complete_$argtype 2>/dev/null >/dev/null; then
291 trace " completing '$argtype' (fallback to default b/c complete_$argtype is unknown)"
292 echo ""
293 else
294 trace " completing custom '$argtype'"
295 completions=$(complete_$argtype "$word")
296 if [[ -z "$completions" ]]; then
297 trace " no custom '$argtype' completions"
298 # These are in ascii and "dictionary" order so they sort
299 # correctly.
300 echo "##-no-completion- -results-##"
301 else
302 echo $completions
303 fi
304 fi
305 ;;
306 *)
307 trace " unknown state: $state"
308 ;;
309 esac
310 }
311
312
313 trace ""
314 trace "-- $(date)"
315 #trace "\$IFS: '$IFS'"
316 #trace "\$@: '$@'"
317 #trace "COMP_WORDBREAKS: '$COMP_WORDBREAKS'"
318 trace "COMP_CWORD: '$COMP_CWORD'"
319 trace "COMP_LINE: '$COMP_LINE'"
320 trace "COMP_POINT: $COMP_POINT"
321
322 # Guard against negative COMP_CWORD. This is a Bash bug at least on
323 # Mac 10.10.4's bash. See
324 # <https://lists.gnu.org/archive/html/bug-bash/2009-07/msg00125.html>.
325 if [[ $COMP_CWORD -lt 0 ]]; then
326 trace "abort on negative COMP_CWORD"
327 exit 1;
328 fi
329
330 # I don't know how to do array manip on argv vars,
331 # so copy over to argv array to work on them.
332 shift # the leading '--'
333 i=0
334 len=$#
335 while [[ $# -gt 0 ]]; do
336 argv[$i]=$1
337 shift;
338 i=$(( $i + 1 ))
339 done
340 trace "argv: '${argv[@]}'"
341 trace "argv[COMP_CWORD-1]: '${argv[$(( $COMP_CWORD - 1 ))]}'"
342 trace "argv[COMP_CWORD]: '${argv[$COMP_CWORD]}'"
343 trace "argv len: '$len'"
344
345 _dashdash_complete 1 ""
346}
347
348
349# ---- mainline
350
351# Note: This if-block to help work with 'compdef' and 'compctl' is
352# adapted from 'npm completion'.
353if type complete &>/dev/null; then
354 function _suman_completion {
355 local _log_file=/dev/null
356 [[ -z "$_suman_log" ]] || _log_file="$_suman_log"
357 COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
358 COMP_LINE="$COMP_LINE" \
359 COMP_POINT="$COMP_POINT" \
360 _suman_completer -- "${COMP_WORDS[@]}" \
361 2>$_log_file)) || return $?
362 }
363 complete -o default -F _suman_completion suman
364elif type compdef &>/dev/null; then
365 function _suman_completion {
366 local _log_file=/dev/null
367 [[ -z "$_suman_log" ]] || _log_file="$_suman_log"
368 compadd -- $(COMP_CWORD=$((CURRENT-1)) \
369 COMP_LINE=$BUFFER \
370 COMP_POINT=0 \
371 _suman_completer -- "${words[@]}" \
372 2>$_log_file)
373 }
374 compdef _suman_completion suman
375elif type compctl &>/dev/null; then
376 function _suman_completion {
377 local cword line point words si
378 read -Ac words
379 read -cn cword
380 let cword-=1
381 read -l line
382 read -ln point
383 local _log_file=/dev/null
384 [[ -z "$_suman_log" ]] || _log_file="$_suman_log"
385 reply=($(COMP_CWORD="$cword" \
386 COMP_LINE="$line" \
387 COMP_POINT="$point" \
388 _suman_completer -- "${words[@]}" \
389 2>$_log_file)) || return $?
390 }
391 compctl -K _suman_completion suman
392fi
393